backtest-kit 3.4.0 → 3.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -22
- package/build/index.cjs +755 -1
- package/build/index.mjs +753 -2
- package/package.json +2 -2
- package/types.d.ts +333 -2
package/build/index.mjs
CHANGED
|
@@ -408,6 +408,12 @@ const GLOBAL_CONFIG = {
|
|
|
408
408
|
* Default: 20 levels
|
|
409
409
|
*/
|
|
410
410
|
CC_ORDER_BOOK_MAX_DEPTH_LEVELS: 1000,
|
|
411
|
+
/**
|
|
412
|
+
* Maximum minutes of aggregated trades to fetch when no limit is provided.
|
|
413
|
+
* If limit is not specified, the system will fetch aggregated trades for this many minutes starting from the current time minus the offset.
|
|
414
|
+
* Binance requirement
|
|
415
|
+
*/
|
|
416
|
+
CC_AGGREGATED_TRADES_MAX_MINUTES: 60,
|
|
411
417
|
/**
|
|
412
418
|
* Maximum number of notifications to keep in storage.
|
|
413
419
|
* Older notifications are removed when this limit is exceeded.
|
|
@@ -422,6 +428,14 @@ const GLOBAL_CONFIG = {
|
|
|
422
428
|
* Default: 50 signals
|
|
423
429
|
*/
|
|
424
430
|
CC_MAX_SIGNALS: 50,
|
|
431
|
+
/**
|
|
432
|
+
* Maximum number of log lines to keep in storage.
|
|
433
|
+
* Older log lines are removed when this limit is exceeded.
|
|
434
|
+
* This helps prevent unbounded log growth which can consume memory and degrade performance over time.
|
|
435
|
+
*
|
|
436
|
+
* Default: 1000 log lines
|
|
437
|
+
*/
|
|
438
|
+
CC_MAX_LOG_LINES: 1000,
|
|
425
439
|
/**
|
|
426
440
|
* Enables mutex locking for candle fetching to prevent concurrent fetches of the same candles.
|
|
427
441
|
* This can help avoid redundant API calls and ensure data consistency when multiple processes/threads attempt to fetch candles simultaneously.
|
|
@@ -756,6 +770,11 @@ const PERSIST_NOTIFICATION_UTILS_METHOD_NAME_WRITE_DATA = "PersistNotificationUt
|
|
|
756
770
|
const PERSIST_NOTIFICATION_UTILS_METHOD_NAME_USE_JSON = "PersistNotificationUtils.useJson";
|
|
757
771
|
const PERSIST_NOTIFICATION_UTILS_METHOD_NAME_USE_DUMMY = "PersistNotificationUtils.useDummy";
|
|
758
772
|
const PERSIST_NOTIFICATION_UTILS_METHOD_NAME_USE_PERSIST_NOTIFICATION_ADAPTER = "PersistNotificationUtils.usePersistNotificationAdapter";
|
|
773
|
+
const PERSIST_LOG_UTILS_METHOD_NAME_READ_DATA = "PersistLogUtils.readLogData";
|
|
774
|
+
const PERSIST_LOG_UTILS_METHOD_NAME_WRITE_DATA = "PersistLogUtils.writeLogData";
|
|
775
|
+
const PERSIST_LOG_UTILS_METHOD_NAME_USE_JSON = "PersistLogUtils.useJson";
|
|
776
|
+
const PERSIST_LOG_UTILS_METHOD_NAME_USE_DUMMY = "PersistLogUtils.useDummy";
|
|
777
|
+
const PERSIST_LOG_UTILS_METHOD_NAME_USE_PERSIST_LOG_ADAPTER = "PersistLogUtils.usePersistLogAdapter";
|
|
759
778
|
const BASE_WAIT_FOR_INIT_FN_METHOD_NAME = "PersistBase.waitForInitFn";
|
|
760
779
|
const BASE_UNLINK_RETRY_COUNT = 5;
|
|
761
780
|
const BASE_UNLINK_RETRY_DELAY = 1000;
|
|
@@ -1926,6 +1945,106 @@ class PersistNotificationUtils {
|
|
|
1926
1945
|
* Used by NotificationPersistLiveUtils/NotificationPersistBacktestUtils for notification persistence.
|
|
1927
1946
|
*/
|
|
1928
1947
|
const PersistNotificationAdapter = new PersistNotificationUtils();
|
|
1948
|
+
/**
|
|
1949
|
+
* Utility class for managing log entry persistence.
|
|
1950
|
+
*
|
|
1951
|
+
* Features:
|
|
1952
|
+
* - Memoized storage instance
|
|
1953
|
+
* - Custom adapter support
|
|
1954
|
+
* - Atomic read/write operations for LogData
|
|
1955
|
+
* - Each log entry stored as separate file keyed by id
|
|
1956
|
+
* - Crash-safe log state management
|
|
1957
|
+
*
|
|
1958
|
+
* Used by LogPersistUtils for log entry persistence.
|
|
1959
|
+
*/
|
|
1960
|
+
class PersistLogUtils {
|
|
1961
|
+
constructor() {
|
|
1962
|
+
this.PersistLogFactory = PersistBase;
|
|
1963
|
+
this._logStorage = null;
|
|
1964
|
+
/**
|
|
1965
|
+
* Reads persisted log entries.
|
|
1966
|
+
*
|
|
1967
|
+
* Called by LogPersistUtils.waitForInit() to restore state.
|
|
1968
|
+
* Uses keys() from PersistBase to iterate over all stored entries.
|
|
1969
|
+
* Returns empty array if no entries exist.
|
|
1970
|
+
*
|
|
1971
|
+
* @returns Promise resolving to array of log entries
|
|
1972
|
+
*/
|
|
1973
|
+
this.readLogData = async () => {
|
|
1974
|
+
bt.loggerService.info(PERSIST_LOG_UTILS_METHOD_NAME_READ_DATA);
|
|
1975
|
+
const isInitial = !this._logStorage;
|
|
1976
|
+
const stateStorage = this.getLogStorage();
|
|
1977
|
+
await stateStorage.waitForInit(isInitial);
|
|
1978
|
+
const entries = [];
|
|
1979
|
+
for await (const entryId of stateStorage.keys()) {
|
|
1980
|
+
const entry = await stateStorage.readValue(entryId);
|
|
1981
|
+
entries.push(entry);
|
|
1982
|
+
}
|
|
1983
|
+
return entries;
|
|
1984
|
+
};
|
|
1985
|
+
/**
|
|
1986
|
+
* Writes log entries to disk with atomic file writes.
|
|
1987
|
+
*
|
|
1988
|
+
* Called by LogPersistUtils after each log call to persist state.
|
|
1989
|
+
* Uses entry.id as the storage key for individual file storage.
|
|
1990
|
+
* Uses atomic writes to prevent corruption on crashes.
|
|
1991
|
+
*
|
|
1992
|
+
* @param logData - Log entries to persist
|
|
1993
|
+
* @returns Promise that resolves when write is complete
|
|
1994
|
+
*/
|
|
1995
|
+
this.writeLogData = async (logData) => {
|
|
1996
|
+
bt.loggerService.info(PERSIST_LOG_UTILS_METHOD_NAME_WRITE_DATA);
|
|
1997
|
+
const isInitial = !this._logStorage;
|
|
1998
|
+
const stateStorage = this.getLogStorage();
|
|
1999
|
+
await stateStorage.waitForInit(isInitial);
|
|
2000
|
+
for (const entry of logData) {
|
|
2001
|
+
if (await stateStorage.hasValue(entry.id)) {
|
|
2002
|
+
continue;
|
|
2003
|
+
}
|
|
2004
|
+
await stateStorage.writeValue(entry.id, entry);
|
|
2005
|
+
}
|
|
2006
|
+
};
|
|
2007
|
+
}
|
|
2008
|
+
getLogStorage() {
|
|
2009
|
+
if (!this._logStorage) {
|
|
2010
|
+
this._logStorage = Reflect.construct(this.PersistLogFactory, [
|
|
2011
|
+
`log`,
|
|
2012
|
+
`./dump/data/log/`,
|
|
2013
|
+
]);
|
|
2014
|
+
}
|
|
2015
|
+
return this._logStorage;
|
|
2016
|
+
}
|
|
2017
|
+
/**
|
|
2018
|
+
* Registers a custom persistence adapter.
|
|
2019
|
+
*
|
|
2020
|
+
* @param Ctor - Custom PersistBase constructor
|
|
2021
|
+
*/
|
|
2022
|
+
usePersistLogAdapter(Ctor) {
|
|
2023
|
+
bt.loggerService.info(PERSIST_LOG_UTILS_METHOD_NAME_USE_PERSIST_LOG_ADAPTER);
|
|
2024
|
+
this.PersistLogFactory = Ctor;
|
|
2025
|
+
}
|
|
2026
|
+
/**
|
|
2027
|
+
* Switches to the default JSON persist adapter.
|
|
2028
|
+
* All future persistence writes will use JSON storage.
|
|
2029
|
+
*/
|
|
2030
|
+
useJson() {
|
|
2031
|
+
bt.loggerService.log(PERSIST_LOG_UTILS_METHOD_NAME_USE_JSON);
|
|
2032
|
+
this.usePersistLogAdapter(PersistBase);
|
|
2033
|
+
}
|
|
2034
|
+
/**
|
|
2035
|
+
* Switches to a dummy persist adapter that discards all writes.
|
|
2036
|
+
* All future persistence writes will be no-ops.
|
|
2037
|
+
*/
|
|
2038
|
+
useDummy() {
|
|
2039
|
+
bt.loggerService.log(PERSIST_LOG_UTILS_METHOD_NAME_USE_DUMMY);
|
|
2040
|
+
this.usePersistLogAdapter(PersistDummy);
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
/**
|
|
2044
|
+
* Global singleton instance of PersistLogUtils.
|
|
2045
|
+
* Used by LogPersistUtils for log entry persistence.
|
|
2046
|
+
*/
|
|
2047
|
+
const PersistLogAdapter = new PersistLogUtils();
|
|
1929
2048
|
|
|
1930
2049
|
var _a$2, _b$2;
|
|
1931
2050
|
const BUSY_DELAY = 100;
|
|
@@ -2667,6 +2786,55 @@ class ClientExchange {
|
|
|
2667
2786
|
GLOBAL_CONFIG.CC_ORDER_BOOK_TIME_OFFSET_MINUTES * MS_PER_MINUTE$5);
|
|
2668
2787
|
return await this.params.getOrderBook(symbol, depth, from, to, this.params.execution.context.backtest);
|
|
2669
2788
|
}
|
|
2789
|
+
/**
|
|
2790
|
+
* Fetches aggregated trades backwards from execution context time.
|
|
2791
|
+
*
|
|
2792
|
+
* Algorithm:
|
|
2793
|
+
* 1. Align when down to the nearest minute boundary (1-minute granularity)
|
|
2794
|
+
* 2. If limit is not specified: fetch one window of CC_AGGREGATED_TRADES_MAX_MINUTES
|
|
2795
|
+
* 3. If limit is specified: paginate backwards in CC_AGGREGATED_TRADES_MAX_MINUTES
|
|
2796
|
+
* chunks until at least limit trades are collected, then slice to limit
|
|
2797
|
+
*
|
|
2798
|
+
* Look-ahead bias prevention:
|
|
2799
|
+
* - `to` is always aligned down to the minute (never exceeds current when)
|
|
2800
|
+
* - Each pagination window goes strictly backwards from alignedWhen
|
|
2801
|
+
*
|
|
2802
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
2803
|
+
* @param limit - Optional maximum number of trades to return. If not specified,
|
|
2804
|
+
* returns all trades within the last CC_AGGREGATED_TRADES_MAX_MINUTES window.
|
|
2805
|
+
* @returns Promise resolving to array of aggregated trade data
|
|
2806
|
+
*/
|
|
2807
|
+
async getAggregatedTrades(symbol, limit) {
|
|
2808
|
+
this.params.logger.debug("ClientExchange getAggregatedTrades", {
|
|
2809
|
+
symbol,
|
|
2810
|
+
limit,
|
|
2811
|
+
});
|
|
2812
|
+
const whenTimestamp = this.params.execution.context.when.getTime();
|
|
2813
|
+
// Align to 1-minute boundary to prevent look-ahead bias
|
|
2814
|
+
const alignedTo = ALIGN_TO_INTERVAL_FN$2(whenTimestamp, 1);
|
|
2815
|
+
const windowMs = GLOBAL_CONFIG.CC_AGGREGATED_TRADES_MAX_MINUTES * MS_PER_MINUTE$5 - MS_PER_MINUTE$5;
|
|
2816
|
+
// No limit: fetch a single window and return as-is
|
|
2817
|
+
if (limit === undefined) {
|
|
2818
|
+
const to = new Date(alignedTo);
|
|
2819
|
+
const from = new Date(alignedTo - windowMs);
|
|
2820
|
+
return await this.params.getAggregatedTrades(symbol, from, to, this.params.execution.context.backtest);
|
|
2821
|
+
}
|
|
2822
|
+
// With limit: paginate backwards until we have enough trades
|
|
2823
|
+
const result = [];
|
|
2824
|
+
let windowEnd = alignedTo;
|
|
2825
|
+
while (result.length < limit) {
|
|
2826
|
+
const windowStart = windowEnd - windowMs;
|
|
2827
|
+
const to = new Date(windowEnd);
|
|
2828
|
+
const from = new Date(windowStart);
|
|
2829
|
+
const chunk = await this.params.getAggregatedTrades(symbol, from, to, this.params.execution.context.backtest);
|
|
2830
|
+
// Prepend chunk (older data goes first)
|
|
2831
|
+
result.unshift(...chunk);
|
|
2832
|
+
// Move window backwards
|
|
2833
|
+
windowEnd = windowStart;
|
|
2834
|
+
}
|
|
2835
|
+
// Slice to requested limit (most recent trades)
|
|
2836
|
+
return result.slice(-limit);
|
|
2837
|
+
}
|
|
2670
2838
|
}
|
|
2671
2839
|
|
|
2672
2840
|
/**
|
|
@@ -2703,6 +2871,18 @@ const DEFAULT_FORMAT_PRICE_FN$1 = async (_symbol, price, _backtest) => {
|
|
|
2703
2871
|
const DEFAULT_GET_ORDER_BOOK_FN$1 = async (_symbol, _depth, _from, _to, _backtest) => {
|
|
2704
2872
|
throw new Error(`getOrderBook is not implemented for this exchange`);
|
|
2705
2873
|
};
|
|
2874
|
+
/**
|
|
2875
|
+
* Default implementation for getAggregatedTrades.
|
|
2876
|
+
* Throws an error indicating the method is not implemented.
|
|
2877
|
+
*
|
|
2878
|
+
* @param _symbol - Trading pair symbol (unused)
|
|
2879
|
+
* @param _from - Start of time range (unused - can be ignored in live implementations)
|
|
2880
|
+
* @param _to - End of time range (unused - can be ignored in live implementations)
|
|
2881
|
+
* @param _backtest - Whether running in backtest mode (unused)
|
|
2882
|
+
*/
|
|
2883
|
+
const DEFAULT_GET_AGGREGATED_TRADES_FN$1 = async (_symbol, _from, _to, _backtest) => {
|
|
2884
|
+
throw new Error(`getAggregatedTrades is not implemented for this exchange`);
|
|
2885
|
+
};
|
|
2706
2886
|
/**
|
|
2707
2887
|
* Connection service routing exchange operations to correct ClientExchange instance.
|
|
2708
2888
|
*
|
|
@@ -2741,7 +2921,7 @@ class ExchangeConnectionService {
|
|
|
2741
2921
|
* @returns Configured ClientExchange instance
|
|
2742
2922
|
*/
|
|
2743
2923
|
this.getExchange = memoize(([exchangeName]) => `${exchangeName}`, (exchangeName) => {
|
|
2744
|
-
const { getCandles = DEFAULT_GET_CANDLES_FN$1, formatPrice = DEFAULT_FORMAT_PRICE_FN$1, formatQuantity = DEFAULT_FORMAT_QUANTITY_FN$1, getOrderBook = DEFAULT_GET_ORDER_BOOK_FN$1, callbacks } = this.exchangeSchemaService.get(exchangeName);
|
|
2924
|
+
const { getCandles = DEFAULT_GET_CANDLES_FN$1, formatPrice = DEFAULT_FORMAT_PRICE_FN$1, formatQuantity = DEFAULT_FORMAT_QUANTITY_FN$1, getOrderBook = DEFAULT_GET_ORDER_BOOK_FN$1, getAggregatedTrades = DEFAULT_GET_AGGREGATED_TRADES_FN$1, callbacks } = this.exchangeSchemaService.get(exchangeName);
|
|
2745
2925
|
return new ClientExchange({
|
|
2746
2926
|
execution: this.executionContextService,
|
|
2747
2927
|
logger: this.loggerService,
|
|
@@ -2750,6 +2930,7 @@ class ExchangeConnectionService {
|
|
|
2750
2930
|
formatPrice,
|
|
2751
2931
|
formatQuantity,
|
|
2752
2932
|
getOrderBook,
|
|
2933
|
+
getAggregatedTrades,
|
|
2753
2934
|
callbacks,
|
|
2754
2935
|
});
|
|
2755
2936
|
});
|
|
@@ -2855,6 +3036,22 @@ class ExchangeConnectionService {
|
|
|
2855
3036
|
});
|
|
2856
3037
|
return await this.getExchange(this.methodContextService.context.exchangeName).getOrderBook(symbol, depth);
|
|
2857
3038
|
};
|
|
3039
|
+
/**
|
|
3040
|
+
* Fetches aggregated trades for a trading pair using configured exchange.
|
|
3041
|
+
*
|
|
3042
|
+
* Routes to exchange determined by methodContextService.context.exchangeName.
|
|
3043
|
+
*
|
|
3044
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
3045
|
+
* @param limit - Optional maximum number of trades to fetch. If empty returns one window of data.
|
|
3046
|
+
* @returns Promise resolving to array of aggregated trade data
|
|
3047
|
+
*/
|
|
3048
|
+
this.getAggregatedTrades = async (symbol, limit) => {
|
|
3049
|
+
this.loggerService.log("exchangeConnectionService getAggregatedTrades", {
|
|
3050
|
+
symbol,
|
|
3051
|
+
limit,
|
|
3052
|
+
});
|
|
3053
|
+
return await this.getExchange(this.methodContextService.context.exchangeName).getAggregatedTrades(symbol, limit);
|
|
3054
|
+
};
|
|
2858
3055
|
/**
|
|
2859
3056
|
* Fetches raw candles with flexible date/limit parameters.
|
|
2860
3057
|
*
|
|
@@ -11228,6 +11425,34 @@ class ExchangeCoreService {
|
|
|
11228
11425
|
backtest,
|
|
11229
11426
|
});
|
|
11230
11427
|
};
|
|
11428
|
+
/**
|
|
11429
|
+
* Fetches aggregated trades with execution context.
|
|
11430
|
+
*
|
|
11431
|
+
* @param symbol - Trading pair symbol
|
|
11432
|
+
* @param when - Timestamp for context (used in backtest mode)
|
|
11433
|
+
* @param backtest - Whether running in backtest mode
|
|
11434
|
+
* @param limit - Optional maximum number of trades to fetch
|
|
11435
|
+
* @returns Promise resolving to array of aggregated trade data
|
|
11436
|
+
*/
|
|
11437
|
+
this.getAggregatedTrades = async (symbol, when, backtest, limit) => {
|
|
11438
|
+
this.loggerService.log("exchangeCoreService getAggregatedTrades", {
|
|
11439
|
+
symbol,
|
|
11440
|
+
when,
|
|
11441
|
+
backtest,
|
|
11442
|
+
limit,
|
|
11443
|
+
});
|
|
11444
|
+
if (!MethodContextService.hasContext()) {
|
|
11445
|
+
throw new Error("exchangeCoreService getAggregatedTrades requires a method context");
|
|
11446
|
+
}
|
|
11447
|
+
await this.validate(this.methodContextService.context.exchangeName);
|
|
11448
|
+
return await ExecutionContextService.runInContext(async () => {
|
|
11449
|
+
return await this.exchangeConnectionService.getAggregatedTrades(symbol, limit);
|
|
11450
|
+
}, {
|
|
11451
|
+
symbol,
|
|
11452
|
+
when,
|
|
11453
|
+
backtest,
|
|
11454
|
+
});
|
|
11455
|
+
};
|
|
11231
11456
|
/**
|
|
11232
11457
|
* Fetches raw candles with flexible date/limit parameters and execution context.
|
|
11233
11458
|
*
|
|
@@ -26647,6 +26872,7 @@ const EXCHANGE_METHOD_NAME_FORMAT_QUANTITY = "ExchangeUtils.formatQuantity";
|
|
|
26647
26872
|
const EXCHANGE_METHOD_NAME_FORMAT_PRICE = "ExchangeUtils.formatPrice";
|
|
26648
26873
|
const EXCHANGE_METHOD_NAME_GET_ORDER_BOOK = "ExchangeUtils.getOrderBook";
|
|
26649
26874
|
const EXCHANGE_METHOD_NAME_GET_RAW_CANDLES = "ExchangeUtils.getRawCandles";
|
|
26875
|
+
const EXCHANGE_METHOD_NAME_GET_AGGREGATED_TRADES = "ExchangeUtils.getAggregatedTrades";
|
|
26650
26876
|
const MS_PER_MINUTE$3 = 60000;
|
|
26651
26877
|
/**
|
|
26652
26878
|
* Gets current timestamp from execution context if available.
|
|
@@ -26702,6 +26928,18 @@ const DEFAULT_FORMAT_PRICE_FN = async (_symbol, price, _backtest) => {
|
|
|
26702
26928
|
const DEFAULT_GET_ORDER_BOOK_FN = async (_symbol, _depth, _from, _to, _backtest) => {
|
|
26703
26929
|
throw new Error(`getOrderBook is not implemented for this exchange`);
|
|
26704
26930
|
};
|
|
26931
|
+
/**
|
|
26932
|
+
* Default implementation for getAggregatedTrades.
|
|
26933
|
+
* Throws an error indicating the method is not implemented.
|
|
26934
|
+
*
|
|
26935
|
+
* @param _symbol - Trading pair symbol (unused)
|
|
26936
|
+
* @param _from - Start of time range (unused - can be ignored in live implementations)
|
|
26937
|
+
* @param _to - End of time range (unused - can be ignored in live implementations)
|
|
26938
|
+
* @param _backtest - Whether running in backtest mode (unused)
|
|
26939
|
+
*/
|
|
26940
|
+
const DEFAULT_GET_AGGREGATED_TRADES_FN = async (_symbol, _from, _to, _backtest) => {
|
|
26941
|
+
throw new Error(`getAggregatedTrades is not implemented for this exchange`);
|
|
26942
|
+
};
|
|
26705
26943
|
const INTERVAL_MINUTES$3 = {
|
|
26706
26944
|
"1m": 1,
|
|
26707
26945
|
"3m": 3,
|
|
@@ -26747,11 +26985,13 @@ const CREATE_EXCHANGE_INSTANCE_FN = (schema) => {
|
|
|
26747
26985
|
const formatQuantity = schema.formatQuantity ?? DEFAULT_FORMAT_QUANTITY_FN;
|
|
26748
26986
|
const formatPrice = schema.formatPrice ?? DEFAULT_FORMAT_PRICE_FN;
|
|
26749
26987
|
const getOrderBook = schema.getOrderBook ?? DEFAULT_GET_ORDER_BOOK_FN;
|
|
26988
|
+
const getAggregatedTrades = schema.getAggregatedTrades ?? DEFAULT_GET_AGGREGATED_TRADES_FN;
|
|
26750
26989
|
return {
|
|
26751
26990
|
getCandles,
|
|
26752
26991
|
formatQuantity,
|
|
26753
26992
|
formatPrice,
|
|
26754
26993
|
getOrderBook,
|
|
26994
|
+
getAggregatedTrades,
|
|
26755
26995
|
};
|
|
26756
26996
|
};
|
|
26757
26997
|
/**
|
|
@@ -27063,6 +27303,58 @@ class ExchangeInstance {
|
|
|
27063
27303
|
const isBacktest = await GET_BACKTEST_FN();
|
|
27064
27304
|
return await this._methods.getOrderBook(symbol, depth, from, to, isBacktest);
|
|
27065
27305
|
};
|
|
27306
|
+
/**
|
|
27307
|
+
* Fetch aggregated trades for a trading pair.
|
|
27308
|
+
*
|
|
27309
|
+
* Calculates time range backwards from current timestamp (or execution context when).
|
|
27310
|
+
* Aligns `to` to 1-minute boundary to prevent look-ahead bias.
|
|
27311
|
+
* If limit is not specified, returns all trades within one CC_AGGREGATED_TRADES_MAX_MINUTES window.
|
|
27312
|
+
* If limit is specified, paginates backwards until at least limit trades are collected.
|
|
27313
|
+
*
|
|
27314
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
27315
|
+
* @param limit - Optional maximum number of trades to return
|
|
27316
|
+
* @returns Promise resolving to array of aggregated trade data
|
|
27317
|
+
*
|
|
27318
|
+
* @example
|
|
27319
|
+
* ```typescript
|
|
27320
|
+
* const instance = new ExchangeInstance("binance");
|
|
27321
|
+
* const trades = await instance.getAggregatedTrades("BTCUSDT");
|
|
27322
|
+
* const lastN = await instance.getAggregatedTrades("BTCUSDT", 500);
|
|
27323
|
+
* ```
|
|
27324
|
+
*/
|
|
27325
|
+
this.getAggregatedTrades = async (symbol, limit) => {
|
|
27326
|
+
bt.loggerService.info(EXCHANGE_METHOD_NAME_GET_AGGREGATED_TRADES, {
|
|
27327
|
+
exchangeName: this.exchangeName,
|
|
27328
|
+
symbol,
|
|
27329
|
+
limit,
|
|
27330
|
+
});
|
|
27331
|
+
const when = await GET_TIMESTAMP_FN();
|
|
27332
|
+
// Align to 1-minute boundary to prevent look-ahead bias
|
|
27333
|
+
const alignedTo = ALIGN_TO_INTERVAL_FN$1(when.getTime(), 1);
|
|
27334
|
+
const windowMs = GLOBAL_CONFIG.CC_AGGREGATED_TRADES_MAX_MINUTES * MS_PER_MINUTE$3 - MS_PER_MINUTE$3;
|
|
27335
|
+
const isBacktest = await GET_BACKTEST_FN();
|
|
27336
|
+
// No limit: fetch a single window and return as-is
|
|
27337
|
+
if (limit === undefined) {
|
|
27338
|
+
const to = new Date(alignedTo);
|
|
27339
|
+
const from = new Date(alignedTo - windowMs);
|
|
27340
|
+
return await this._methods.getAggregatedTrades(symbol, from, to, isBacktest);
|
|
27341
|
+
}
|
|
27342
|
+
// With limit: paginate backwards until we have enough trades
|
|
27343
|
+
const result = [];
|
|
27344
|
+
let windowEnd = alignedTo;
|
|
27345
|
+
while (result.length < limit) {
|
|
27346
|
+
const windowStart = windowEnd - windowMs;
|
|
27347
|
+
const to = new Date(windowEnd);
|
|
27348
|
+
const from = new Date(windowStart);
|
|
27349
|
+
const chunk = await this._methods.getAggregatedTrades(symbol, from, to, isBacktest);
|
|
27350
|
+
// Prepend chunk (older data goes first)
|
|
27351
|
+
result.unshift(...chunk);
|
|
27352
|
+
// Move window backwards
|
|
27353
|
+
windowEnd = windowStart;
|
|
27354
|
+
}
|
|
27355
|
+
// Slice to requested limit (most recent trades)
|
|
27356
|
+
return result.slice(-limit);
|
|
27357
|
+
};
|
|
27066
27358
|
/**
|
|
27067
27359
|
* Fetches raw candles with flexible date/limit parameters.
|
|
27068
27360
|
*
|
|
@@ -27332,6 +27624,19 @@ class ExchangeUtils {
|
|
|
27332
27624
|
const instance = this._getInstance(context.exchangeName);
|
|
27333
27625
|
return await instance.getOrderBook(symbol, depth);
|
|
27334
27626
|
};
|
|
27627
|
+
/**
|
|
27628
|
+
* Fetch aggregated trades for a trading pair.
|
|
27629
|
+
*
|
|
27630
|
+
* @param symbol - Trading pair symbol
|
|
27631
|
+
* @param context - Execution context with exchange name
|
|
27632
|
+
* @param limit - Optional maximum number of trades to return
|
|
27633
|
+
* @returns Promise resolving to array of aggregated trade data
|
|
27634
|
+
*/
|
|
27635
|
+
this.getAggregatedTrades = async (symbol, context, limit) => {
|
|
27636
|
+
bt.exchangeValidationService.validate(context.exchangeName, EXCHANGE_METHOD_NAME_GET_AGGREGATED_TRADES);
|
|
27637
|
+
const instance = this._getInstance(context.exchangeName);
|
|
27638
|
+
return await instance.getAggregatedTrades(symbol, limit);
|
|
27639
|
+
};
|
|
27335
27640
|
/**
|
|
27336
27641
|
* Fetches raw candles with flexible date/limit parameters.
|
|
27337
27642
|
*
|
|
@@ -27871,6 +28176,7 @@ const HAS_TRADE_CONTEXT_METHOD_NAME = "exchange.hasTradeContext";
|
|
|
27871
28176
|
const GET_ORDER_BOOK_METHOD_NAME = "exchange.getOrderBook";
|
|
27872
28177
|
const GET_RAW_CANDLES_METHOD_NAME = "exchange.getRawCandles";
|
|
27873
28178
|
const GET_NEXT_CANDLES_METHOD_NAME = "exchange.getNextCandles";
|
|
28179
|
+
const GET_AGGREGATED_TRADES_METHOD_NAME = "exchange.getAggregatedTrades";
|
|
27874
28180
|
/**
|
|
27875
28181
|
* Checks if trade context is active (execution and method contexts).
|
|
27876
28182
|
*
|
|
@@ -28199,6 +28505,41 @@ async function getNextCandles(symbol, interval, limit) {
|
|
|
28199
28505
|
}
|
|
28200
28506
|
return await bt.exchangeConnectionService.getNextCandles(symbol, interval, limit);
|
|
28201
28507
|
}
|
|
28508
|
+
/**
|
|
28509
|
+
* Fetches aggregated trades for a trading pair from the registered exchange.
|
|
28510
|
+
*
|
|
28511
|
+
* Trades are fetched backwards from the current execution context time.
|
|
28512
|
+
* If limit is not specified, returns all trades within one CC_AGGREGATED_TRADES_MAX_MINUTES window.
|
|
28513
|
+
* If limit is specified, paginates backwards until at least limit trades are collected.
|
|
28514
|
+
*
|
|
28515
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
28516
|
+
* @param limit - Optional maximum number of trades to fetch
|
|
28517
|
+
* @returns Promise resolving to array of aggregated trade data
|
|
28518
|
+
* @throws Error if execution or method context is missing
|
|
28519
|
+
*
|
|
28520
|
+
* @example
|
|
28521
|
+
* ```typescript
|
|
28522
|
+
* // Fetch last hour of trades
|
|
28523
|
+
* const trades = await getAggregatedTrades("BTCUSDT");
|
|
28524
|
+
*
|
|
28525
|
+
* // Fetch last 500 trades
|
|
28526
|
+
* const lastTrades = await getAggregatedTrades("BTCUSDT", 500);
|
|
28527
|
+
* console.log(lastTrades[0]); // { id, price, qty, timestamp, isBuyerMaker }
|
|
28528
|
+
* ```
|
|
28529
|
+
*/
|
|
28530
|
+
async function getAggregatedTrades(symbol, limit) {
|
|
28531
|
+
bt.loggerService.info(GET_AGGREGATED_TRADES_METHOD_NAME, {
|
|
28532
|
+
symbol,
|
|
28533
|
+
limit,
|
|
28534
|
+
});
|
|
28535
|
+
if (!ExecutionContextService.hasContext()) {
|
|
28536
|
+
throw new Error("getAggregatedTrades requires an execution context");
|
|
28537
|
+
}
|
|
28538
|
+
if (!MethodContextService.hasContext()) {
|
|
28539
|
+
throw new Error("getAggregatedTrades requires a method context");
|
|
28540
|
+
}
|
|
28541
|
+
return await bt.exchangeConnectionService.getAggregatedTrades(symbol, limit);
|
|
28542
|
+
}
|
|
28202
28543
|
|
|
28203
28544
|
const CANCEL_SCHEDULED_METHOD_NAME = "strategy.commitCancelScheduled";
|
|
28204
28545
|
const CLOSE_PENDING_METHOD_NAME = "strategy.commitClosePending";
|
|
@@ -30862,6 +31203,416 @@ async function dumpMessages(resultId, history, result, outputDir = "./dump/strat
|
|
|
30862
31203
|
}
|
|
30863
31204
|
}
|
|
30864
31205
|
|
|
31206
|
+
const LOG_PERSIST_METHOD_NAME_WAIT_FOR_INIT = "LogPersistUtils.waitForInit";
|
|
31207
|
+
const LOG_PERSIST_METHOD_NAME_LOG = "LogPersistUtils.log";
|
|
31208
|
+
const LOG_PERSIST_METHOD_NAME_DEBUG = "LogPersistUtils.debug";
|
|
31209
|
+
const LOG_PERSIST_METHOD_NAME_INFO = "LogPersistUtils.info";
|
|
31210
|
+
const LOG_PERSIST_METHOD_NAME_WARN = "LogPersistUtils.warn";
|
|
31211
|
+
const LOG_PERSIST_METHOD_NAME_GET_LIST = "LogPersistUtils.getList";
|
|
31212
|
+
const LOG_MEMORY_METHOD_NAME_LOG = "LogMemoryUtils.log";
|
|
31213
|
+
const LOG_MEMORY_METHOD_NAME_DEBUG = "LogMemoryUtils.debug";
|
|
31214
|
+
const LOG_MEMORY_METHOD_NAME_INFO = "LogMemoryUtils.info";
|
|
31215
|
+
const LOG_MEMORY_METHOD_NAME_WARN = "LogMemoryUtils.warn";
|
|
31216
|
+
const LOG_MEMORY_METHOD_NAME_GET_LIST = "LogMemoryUtils.getList";
|
|
31217
|
+
const LOG_ADAPTER_METHOD_NAME_USE_LOGGER = "LogAdapter.useLogger";
|
|
31218
|
+
const LOG_ADAPTER_METHOD_NAME_USE_PERSIST = "LogAdapter.usePersist";
|
|
31219
|
+
const LOG_ADAPTER_METHOD_NAME_USE_MEMORY = "LogAdapter.useMemory";
|
|
31220
|
+
const LOG_ADAPTER_METHOD_NAME_USE_DUMMY = "LogAdapter.useDummy";
|
|
31221
|
+
/**
|
|
31222
|
+
* Backtest execution time retrieval function.
|
|
31223
|
+
* Returns the 'when' timestamp from the execution context if available, otherwise returns the current time.
|
|
31224
|
+
* This allows log entries to be timestamped according to the backtest timeline rather than real-world time, improving log relevance and user experience during backtest analysis.
|
|
31225
|
+
*/
|
|
31226
|
+
const GET_DATE_FN = async () => {
|
|
31227
|
+
if (ExecutionContextService.hasContext()) {
|
|
31228
|
+
return new Date(bt.executionContextService.context.when);
|
|
31229
|
+
}
|
|
31230
|
+
return new Date();
|
|
31231
|
+
};
|
|
31232
|
+
/**
|
|
31233
|
+
* Persistent log adapter.
|
|
31234
|
+
*
|
|
31235
|
+
* Features:
|
|
31236
|
+
* - Persists log entries to disk using PersistLogAdapter
|
|
31237
|
+
* - Lazy initialization with singleshot pattern
|
|
31238
|
+
* - Maintains up to CC_MAX_LOG_LINES most recent entries
|
|
31239
|
+
* - Each entry stored individually keyed by its id
|
|
31240
|
+
*
|
|
31241
|
+
* Use this adapter (default) for log persistence across sessions.
|
|
31242
|
+
*/
|
|
31243
|
+
class LogPersistUtils {
|
|
31244
|
+
constructor() {
|
|
31245
|
+
/** Array of log entries */
|
|
31246
|
+
this._entries = [];
|
|
31247
|
+
/**
|
|
31248
|
+
* Singleshot initialization function that loads entries from disk.
|
|
31249
|
+
* Protected by singleshot to ensure one-time execution.
|
|
31250
|
+
*/
|
|
31251
|
+
this.waitForInit = singleshot(async () => {
|
|
31252
|
+
bt.loggerService.info(LOG_PERSIST_METHOD_NAME_WAIT_FOR_INIT);
|
|
31253
|
+
const list = await PersistLogAdapter.readLogData();
|
|
31254
|
+
list.sort((a, b) => a.timestamp - b.timestamp);
|
|
31255
|
+
this._entries = list.slice(-GLOBAL_CONFIG.CC_MAX_LOG_LINES);
|
|
31256
|
+
});
|
|
31257
|
+
/**
|
|
31258
|
+
* Logs a general-purpose message.
|
|
31259
|
+
* Persists entry to disk after appending.
|
|
31260
|
+
* @param topic - The log topic / method name
|
|
31261
|
+
* @param args - Additional arguments
|
|
31262
|
+
*/
|
|
31263
|
+
this.log = async (topic, ...args) => {
|
|
31264
|
+
bt.loggerService.info(LOG_PERSIST_METHOD_NAME_LOG, { topic });
|
|
31265
|
+
await this.waitForInit();
|
|
31266
|
+
const date = await GET_DATE_FN();
|
|
31267
|
+
this._entries.push({
|
|
31268
|
+
id: randomString(),
|
|
31269
|
+
type: "log",
|
|
31270
|
+
timestamp: Date.now(),
|
|
31271
|
+
createdAt: date.toISOString(),
|
|
31272
|
+
topic,
|
|
31273
|
+
args,
|
|
31274
|
+
});
|
|
31275
|
+
this._enforceLimit();
|
|
31276
|
+
await PersistLogAdapter.writeLogData(this._entries);
|
|
31277
|
+
};
|
|
31278
|
+
/**
|
|
31279
|
+
* Logs a debug-level message.
|
|
31280
|
+
* Persists entry to disk after appending.
|
|
31281
|
+
* @param topic - The log topic / method name
|
|
31282
|
+
* @param args - Additional arguments
|
|
31283
|
+
*/
|
|
31284
|
+
this.debug = async (topic, ...args) => {
|
|
31285
|
+
bt.loggerService.info(LOG_PERSIST_METHOD_NAME_DEBUG, { topic });
|
|
31286
|
+
await this.waitForInit();
|
|
31287
|
+
const date = await GET_DATE_FN();
|
|
31288
|
+
this._entries.push({
|
|
31289
|
+
id: randomString(),
|
|
31290
|
+
type: "debug",
|
|
31291
|
+
timestamp: Date.now(),
|
|
31292
|
+
createdAt: date.toISOString(),
|
|
31293
|
+
topic,
|
|
31294
|
+
args,
|
|
31295
|
+
});
|
|
31296
|
+
this._enforceLimit();
|
|
31297
|
+
await PersistLogAdapter.writeLogData(this._entries);
|
|
31298
|
+
};
|
|
31299
|
+
/**
|
|
31300
|
+
* Logs an info-level message.
|
|
31301
|
+
* Persists entry to disk after appending.
|
|
31302
|
+
* @param topic - The log topic / method name
|
|
31303
|
+
* @param args - Additional arguments
|
|
31304
|
+
*/
|
|
31305
|
+
this.info = async (topic, ...args) => {
|
|
31306
|
+
bt.loggerService.info(LOG_PERSIST_METHOD_NAME_INFO, { topic });
|
|
31307
|
+
await this.waitForInit();
|
|
31308
|
+
const date = await GET_DATE_FN();
|
|
31309
|
+
this._entries.push({
|
|
31310
|
+
id: randomString(),
|
|
31311
|
+
type: "info",
|
|
31312
|
+
timestamp: Date.now(),
|
|
31313
|
+
createdAt: date.toISOString(),
|
|
31314
|
+
topic,
|
|
31315
|
+
args,
|
|
31316
|
+
});
|
|
31317
|
+
this._enforceLimit();
|
|
31318
|
+
await PersistLogAdapter.writeLogData(this._entries);
|
|
31319
|
+
};
|
|
31320
|
+
/**
|
|
31321
|
+
* Logs a warning-level message.
|
|
31322
|
+
* Persists entry to disk after appending.
|
|
31323
|
+
* @param topic - The log topic / method name
|
|
31324
|
+
* @param args - Additional arguments
|
|
31325
|
+
*/
|
|
31326
|
+
this.warn = async (topic, ...args) => {
|
|
31327
|
+
bt.loggerService.info(LOG_PERSIST_METHOD_NAME_WARN, { topic });
|
|
31328
|
+
await this.waitForInit();
|
|
31329
|
+
const date = await GET_DATE_FN();
|
|
31330
|
+
this._entries.push({
|
|
31331
|
+
id: randomString(),
|
|
31332
|
+
type: "warn",
|
|
31333
|
+
timestamp: Date.now(),
|
|
31334
|
+
createdAt: date.toISOString(),
|
|
31335
|
+
topic,
|
|
31336
|
+
args,
|
|
31337
|
+
});
|
|
31338
|
+
this._enforceLimit();
|
|
31339
|
+
await PersistLogAdapter.writeLogData(this._entries);
|
|
31340
|
+
};
|
|
31341
|
+
/**
|
|
31342
|
+
* Lists all stored log entries.
|
|
31343
|
+
* @returns Array of all log entries
|
|
31344
|
+
*/
|
|
31345
|
+
this.getList = async () => {
|
|
31346
|
+
bt.loggerService.info(LOG_PERSIST_METHOD_NAME_GET_LIST);
|
|
31347
|
+
await this.waitForInit();
|
|
31348
|
+
return [...this._entries];
|
|
31349
|
+
};
|
|
31350
|
+
}
|
|
31351
|
+
/**
|
|
31352
|
+
* Removes oldest entries if limit is exceeded.
|
|
31353
|
+
*/
|
|
31354
|
+
_enforceLimit() {
|
|
31355
|
+
if (this._entries.length > GLOBAL_CONFIG.CC_MAX_LOG_LINES) {
|
|
31356
|
+
this._entries.splice(0, this._entries.length - GLOBAL_CONFIG.CC_MAX_LOG_LINES);
|
|
31357
|
+
}
|
|
31358
|
+
}
|
|
31359
|
+
}
|
|
31360
|
+
/**
|
|
31361
|
+
* In-memory log adapter.
|
|
31362
|
+
*
|
|
31363
|
+
* Features:
|
|
31364
|
+
* - Stores log entries in memory only (no persistence)
|
|
31365
|
+
* - Maintains up to CC_MAX_LOG_LINES most recent entries
|
|
31366
|
+
* - Data is lost when application restarts
|
|
31367
|
+
* - Handles all log levels (log, debug, info, warn)
|
|
31368
|
+
*
|
|
31369
|
+
* Use this adapter for testing or when persistence is not required.
|
|
31370
|
+
*/
|
|
31371
|
+
class LogMemoryUtils {
|
|
31372
|
+
constructor() {
|
|
31373
|
+
/** Array of log entries */
|
|
31374
|
+
this._entries = [];
|
|
31375
|
+
/**
|
|
31376
|
+
* Logs a general-purpose message.
|
|
31377
|
+
* Appends entry to in-memory array.
|
|
31378
|
+
* @param topic - The log topic / method name
|
|
31379
|
+
* @param args - Additional arguments
|
|
31380
|
+
*/
|
|
31381
|
+
this.log = async (topic, ...args) => {
|
|
31382
|
+
bt.loggerService.info(LOG_MEMORY_METHOD_NAME_LOG, { topic });
|
|
31383
|
+
const date = await GET_DATE_FN();
|
|
31384
|
+
this._entries.push({
|
|
31385
|
+
id: randomString(),
|
|
31386
|
+
type: "log",
|
|
31387
|
+
timestamp: Date.now(),
|
|
31388
|
+
createdAt: date.toISOString(),
|
|
31389
|
+
topic,
|
|
31390
|
+
args,
|
|
31391
|
+
});
|
|
31392
|
+
this._enforceLimit();
|
|
31393
|
+
};
|
|
31394
|
+
/**
|
|
31395
|
+
* Logs a debug-level message.
|
|
31396
|
+
* Appends entry to in-memory array.
|
|
31397
|
+
* @param topic - The log topic / method name
|
|
31398
|
+
* @param args - Additional arguments
|
|
31399
|
+
*/
|
|
31400
|
+
this.debug = async (topic, ...args) => {
|
|
31401
|
+
bt.loggerService.info(LOG_MEMORY_METHOD_NAME_DEBUG, { topic });
|
|
31402
|
+
const date = await GET_DATE_FN();
|
|
31403
|
+
this._entries.push({
|
|
31404
|
+
id: randomString(),
|
|
31405
|
+
type: "debug",
|
|
31406
|
+
timestamp: Date.now(),
|
|
31407
|
+
createdAt: date.toISOString(),
|
|
31408
|
+
topic,
|
|
31409
|
+
args,
|
|
31410
|
+
});
|
|
31411
|
+
this._enforceLimit();
|
|
31412
|
+
};
|
|
31413
|
+
/**
|
|
31414
|
+
* Logs an info-level message.
|
|
31415
|
+
* Appends entry to in-memory array.
|
|
31416
|
+
* @param topic - The log topic / method name
|
|
31417
|
+
* @param args - Additional arguments
|
|
31418
|
+
*/
|
|
31419
|
+
this.info = async (topic, ...args) => {
|
|
31420
|
+
bt.loggerService.info(LOG_MEMORY_METHOD_NAME_INFO, { topic });
|
|
31421
|
+
const date = await GET_DATE_FN();
|
|
31422
|
+
this._entries.push({
|
|
31423
|
+
id: randomString(),
|
|
31424
|
+
type: "info",
|
|
31425
|
+
timestamp: Date.now(),
|
|
31426
|
+
createdAt: date.toISOString(),
|
|
31427
|
+
topic,
|
|
31428
|
+
args,
|
|
31429
|
+
});
|
|
31430
|
+
this._enforceLimit();
|
|
31431
|
+
};
|
|
31432
|
+
/**
|
|
31433
|
+
* Logs a warning-level message.
|
|
31434
|
+
* Appends entry to in-memory array.
|
|
31435
|
+
* @param topic - The log topic / method name
|
|
31436
|
+
* @param args - Additional arguments
|
|
31437
|
+
*/
|
|
31438
|
+
this.warn = async (topic, ...args) => {
|
|
31439
|
+
bt.loggerService.info(LOG_MEMORY_METHOD_NAME_WARN, { topic });
|
|
31440
|
+
const date = await GET_DATE_FN();
|
|
31441
|
+
this._entries.push({
|
|
31442
|
+
id: randomString(),
|
|
31443
|
+
type: "warn",
|
|
31444
|
+
timestamp: Date.now(),
|
|
31445
|
+
createdAt: date.toISOString(),
|
|
31446
|
+
topic,
|
|
31447
|
+
args,
|
|
31448
|
+
});
|
|
31449
|
+
this._enforceLimit();
|
|
31450
|
+
};
|
|
31451
|
+
/**
|
|
31452
|
+
* Lists all stored log entries.
|
|
31453
|
+
* @returns Array of all log entries
|
|
31454
|
+
*/
|
|
31455
|
+
this.getList = async () => {
|
|
31456
|
+
bt.loggerService.info(LOG_MEMORY_METHOD_NAME_GET_LIST);
|
|
31457
|
+
return [...this._entries];
|
|
31458
|
+
};
|
|
31459
|
+
}
|
|
31460
|
+
/**
|
|
31461
|
+
* Removes oldest entries if limit is exceeded.
|
|
31462
|
+
*/
|
|
31463
|
+
_enforceLimit() {
|
|
31464
|
+
if (this._entries.length > GLOBAL_CONFIG.CC_MAX_LOG_LINES) {
|
|
31465
|
+
this._entries.splice(0, this._entries.length - GLOBAL_CONFIG.CC_MAX_LOG_LINES);
|
|
31466
|
+
}
|
|
31467
|
+
}
|
|
31468
|
+
}
|
|
31469
|
+
/**
|
|
31470
|
+
* Dummy log adapter that discards all writes.
|
|
31471
|
+
*
|
|
31472
|
+
* Features:
|
|
31473
|
+
* - No-op implementation for all methods
|
|
31474
|
+
* - getList always returns empty array
|
|
31475
|
+
*
|
|
31476
|
+
* Use this adapter to disable log storage completely.
|
|
31477
|
+
*/
|
|
31478
|
+
class LogDummyUtils {
|
|
31479
|
+
/**
|
|
31480
|
+
* Always returns empty array (no storage).
|
|
31481
|
+
* @returns Empty array
|
|
31482
|
+
*/
|
|
31483
|
+
async getList() {
|
|
31484
|
+
return [];
|
|
31485
|
+
}
|
|
31486
|
+
/**
|
|
31487
|
+
* No-op handler for general-purpose log.
|
|
31488
|
+
*/
|
|
31489
|
+
log() {
|
|
31490
|
+
}
|
|
31491
|
+
/**
|
|
31492
|
+
* No-op handler for debug-level log.
|
|
31493
|
+
*/
|
|
31494
|
+
debug() {
|
|
31495
|
+
}
|
|
31496
|
+
/**
|
|
31497
|
+
* No-op handler for info-level log.
|
|
31498
|
+
*/
|
|
31499
|
+
info() {
|
|
31500
|
+
}
|
|
31501
|
+
/**
|
|
31502
|
+
* No-op handler for warning-level log.
|
|
31503
|
+
*/
|
|
31504
|
+
warn() {
|
|
31505
|
+
}
|
|
31506
|
+
}
|
|
31507
|
+
/**
|
|
31508
|
+
* Log adapter with pluggable storage backend.
|
|
31509
|
+
*
|
|
31510
|
+
* Features:
|
|
31511
|
+
* - Adapter pattern for swappable log implementations
|
|
31512
|
+
* - Default adapter: LogMemoryUtils (in-memory storage)
|
|
31513
|
+
* - Alternative adapters: LogPersistUtils, LogDummyUtils
|
|
31514
|
+
* - Convenience methods: usePersist(), useMemory(), useDummy()
|
|
31515
|
+
*/
|
|
31516
|
+
class LogAdapter {
|
|
31517
|
+
constructor() {
|
|
31518
|
+
/** Internal log utils instance */
|
|
31519
|
+
this._log = new LogMemoryUtils();
|
|
31520
|
+
/**
|
|
31521
|
+
* Lists all stored log entries.
|
|
31522
|
+
* Proxies call to the underlying log adapter.
|
|
31523
|
+
* @returns Array of all log entries
|
|
31524
|
+
*/
|
|
31525
|
+
this.getList = async () => {
|
|
31526
|
+
if (this._log.getList) {
|
|
31527
|
+
return await this._log.getList();
|
|
31528
|
+
}
|
|
31529
|
+
return [];
|
|
31530
|
+
};
|
|
31531
|
+
/**
|
|
31532
|
+
* Logs a general-purpose message.
|
|
31533
|
+
* Proxies call to the underlying log adapter.
|
|
31534
|
+
* @param topic - The log topic / method name
|
|
31535
|
+
* @param args - Additional arguments
|
|
31536
|
+
*/
|
|
31537
|
+
this.log = (topic, ...args) => {
|
|
31538
|
+
if (this._log.log) {
|
|
31539
|
+
this._log.log(topic, ...args);
|
|
31540
|
+
}
|
|
31541
|
+
};
|
|
31542
|
+
/**
|
|
31543
|
+
* Logs a debug-level message.
|
|
31544
|
+
* Proxies call to the underlying log adapter.
|
|
31545
|
+
* @param topic - The log topic / method name
|
|
31546
|
+
* @param args - Additional arguments
|
|
31547
|
+
*/
|
|
31548
|
+
this.debug = (topic, ...args) => {
|
|
31549
|
+
if (this._log.debug) {
|
|
31550
|
+
this._log.debug(topic, ...args);
|
|
31551
|
+
}
|
|
31552
|
+
};
|
|
31553
|
+
/**
|
|
31554
|
+
* Logs an info-level message.
|
|
31555
|
+
* Proxies call to the underlying log adapter.
|
|
31556
|
+
* @param topic - The log topic / method name
|
|
31557
|
+
* @param args - Additional arguments
|
|
31558
|
+
*/
|
|
31559
|
+
this.info = (topic, ...args) => {
|
|
31560
|
+
if (this._log.info) {
|
|
31561
|
+
this._log.info(topic, ...args);
|
|
31562
|
+
}
|
|
31563
|
+
};
|
|
31564
|
+
/**
|
|
31565
|
+
* Logs a warning-level message.
|
|
31566
|
+
* Proxies call to the underlying log adapter.
|
|
31567
|
+
* @param topic - The log topic / method name
|
|
31568
|
+
* @param args - Additional arguments
|
|
31569
|
+
*/
|
|
31570
|
+
this.warn = (topic, ...args) => {
|
|
31571
|
+
if (this._log.warn) {
|
|
31572
|
+
this._log.warn(topic, ...args);
|
|
31573
|
+
}
|
|
31574
|
+
};
|
|
31575
|
+
/**
|
|
31576
|
+
* Sets the log adapter constructor.
|
|
31577
|
+
* All future log operations will use this adapter.
|
|
31578
|
+
* @param Ctor - Constructor for log adapter
|
|
31579
|
+
*/
|
|
31580
|
+
this.useLogger = (Ctor) => {
|
|
31581
|
+
bt.loggerService.info(LOG_ADAPTER_METHOD_NAME_USE_LOGGER);
|
|
31582
|
+
this._log = Reflect.construct(Ctor, []);
|
|
31583
|
+
};
|
|
31584
|
+
/**
|
|
31585
|
+
* Switches to persistent log adapter.
|
|
31586
|
+
* Log entries will be persisted to disk.
|
|
31587
|
+
*/
|
|
31588
|
+
this.usePersist = () => {
|
|
31589
|
+
bt.loggerService.info(LOG_ADAPTER_METHOD_NAME_USE_PERSIST);
|
|
31590
|
+
this._log = new LogPersistUtils();
|
|
31591
|
+
};
|
|
31592
|
+
/**
|
|
31593
|
+
* Switches to in-memory log adapter (default).
|
|
31594
|
+
* Log entries will be stored in memory only.
|
|
31595
|
+
*/
|
|
31596
|
+
this.useMemory = () => {
|
|
31597
|
+
bt.loggerService.info(LOG_ADAPTER_METHOD_NAME_USE_MEMORY);
|
|
31598
|
+
this._log = new LogMemoryUtils();
|
|
31599
|
+
};
|
|
31600
|
+
/**
|
|
31601
|
+
* Switches to dummy log adapter.
|
|
31602
|
+
* All future log writes will be no-ops.
|
|
31603
|
+
*/
|
|
31604
|
+
this.useDummy = () => {
|
|
31605
|
+
bt.loggerService.info(LOG_ADAPTER_METHOD_NAME_USE_DUMMY);
|
|
31606
|
+
this._log = new LogDummyUtils();
|
|
31607
|
+
};
|
|
31608
|
+
}
|
|
31609
|
+
}
|
|
31610
|
+
/**
|
|
31611
|
+
* Global singleton instance of LogAdapter.
|
|
31612
|
+
* Provides unified log management with pluggable backends.
|
|
31613
|
+
*/
|
|
31614
|
+
const Log = new LogAdapter();
|
|
31615
|
+
|
|
30865
31616
|
const BACKTEST_METHOD_NAME_RUN = "BacktestUtils.run";
|
|
30866
31617
|
const BACKTEST_METHOD_NAME_BACKGROUND = "BacktestUtils.background";
|
|
30867
31618
|
const BACKTEST_METHOD_NAME_STOP = "BacktestUtils.stop";
|
|
@@ -38260,4 +39011,4 @@ const set = (object, path, value) => {
|
|
|
38260
39011
|
}
|
|
38261
39012
|
};
|
|
38262
39013
|
|
|
38263
|
-
export { ActionBase, Backtest, Breakeven, Cache, Constant, Exchange, ExecutionContextService, Heat, Live, Markdown, MarkdownFileBase, MarkdownFolderBase, MethodContextService, Notification, NotificationBacktest, NotificationLive, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistCandleAdapter, PersistNotificationAdapter, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PersistStorageAdapter, PositionSize, Report, ReportBase, Risk, Schedule, Storage, StorageBacktest, StorageLive, Strategy, Walker, addActionSchema, addExchangeSchema, addFrameSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, alignToInterval, checkCandles, commitActivateScheduled, commitAverageBuy, commitBreakeven, commitCancelScheduled, commitClosePending, commitPartialLoss, commitPartialProfit, commitTrailingStop, commitTrailingTake, dumpMessages, emitters, formatPrice, formatQuantity, get, getActionSchema, getAveragePrice, getBacktestTimeframe, getCandles, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getExchangeSchema, getFrameSchema, getMode, getNextCandles, getOrderBook, getRawCandles, getRiskSchema, getSizingSchema, getStrategySchema, getSymbol, getWalkerSchema, hasTradeContext, backtest as lib, listExchangeSchema, listFrameSchema, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenBacktestProgress, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenStrategyCommit, listenStrategyCommitOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideActionSchema, overrideExchangeSchema, overrideFrameSchema, overrideRiskSchema, overrideSizingSchema, overrideStrategySchema, overrideWalkerSchema, parseArgs, roundTicks, set, setColumns, setConfig, setLogger, stopStrategy, validate, waitForCandle, warmCandles };
|
|
39014
|
+
export { ActionBase, Backtest, Breakeven, Cache, Constant, Exchange, ExecutionContextService, Heat, Live, Log, Markdown, MarkdownFileBase, MarkdownFolderBase, MethodContextService, Notification, NotificationBacktest, NotificationLive, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistCandleAdapter, PersistLogAdapter, PersistNotificationAdapter, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PersistStorageAdapter, PositionSize, Report, ReportBase, Risk, Schedule, Storage, StorageBacktest, StorageLive, Strategy, Walker, addActionSchema, addExchangeSchema, addFrameSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, alignToInterval, checkCandles, commitActivateScheduled, commitAverageBuy, commitBreakeven, commitCancelScheduled, commitClosePending, commitPartialLoss, commitPartialProfit, commitTrailingStop, commitTrailingTake, dumpMessages, emitters, formatPrice, formatQuantity, get, getActionSchema, getAggregatedTrades, getAveragePrice, getBacktestTimeframe, getCandles, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getExchangeSchema, getFrameSchema, getMode, getNextCandles, getOrderBook, getRawCandles, getRiskSchema, getSizingSchema, getStrategySchema, getSymbol, getWalkerSchema, hasTradeContext, backtest as lib, listExchangeSchema, listFrameSchema, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenBacktestProgress, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenStrategyCommit, listenStrategyCommitOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideActionSchema, overrideExchangeSchema, overrideFrameSchema, overrideRiskSchema, overrideSizingSchema, overrideStrategySchema, overrideWalkerSchema, parseArgs, roundTicks, set, setColumns, setConfig, setLogger, stopStrategy, validate, waitForCandle, warmCandles };
|