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.cjs
CHANGED
|
@@ -428,6 +428,12 @@ const GLOBAL_CONFIG = {
|
|
|
428
428
|
* Default: 20 levels
|
|
429
429
|
*/
|
|
430
430
|
CC_ORDER_BOOK_MAX_DEPTH_LEVELS: 1000,
|
|
431
|
+
/**
|
|
432
|
+
* Maximum minutes of aggregated trades to fetch when no limit is provided.
|
|
433
|
+
* If limit is not specified, the system will fetch aggregated trades for this many minutes starting from the current time minus the offset.
|
|
434
|
+
* Binance requirement
|
|
435
|
+
*/
|
|
436
|
+
CC_AGGREGATED_TRADES_MAX_MINUTES: 60,
|
|
431
437
|
/**
|
|
432
438
|
* Maximum number of notifications to keep in storage.
|
|
433
439
|
* Older notifications are removed when this limit is exceeded.
|
|
@@ -442,6 +448,14 @@ const GLOBAL_CONFIG = {
|
|
|
442
448
|
* Default: 50 signals
|
|
443
449
|
*/
|
|
444
450
|
CC_MAX_SIGNALS: 50,
|
|
451
|
+
/**
|
|
452
|
+
* Maximum number of log lines to keep in storage.
|
|
453
|
+
* Older log lines are removed when this limit is exceeded.
|
|
454
|
+
* This helps prevent unbounded log growth which can consume memory and degrade performance over time.
|
|
455
|
+
*
|
|
456
|
+
* Default: 1000 log lines
|
|
457
|
+
*/
|
|
458
|
+
CC_MAX_LOG_LINES: 1000,
|
|
445
459
|
/**
|
|
446
460
|
* Enables mutex locking for candle fetching to prevent concurrent fetches of the same candles.
|
|
447
461
|
* This can help avoid redundant API calls and ensure data consistency when multiple processes/threads attempt to fetch candles simultaneously.
|
|
@@ -776,6 +790,11 @@ const PERSIST_NOTIFICATION_UTILS_METHOD_NAME_WRITE_DATA = "PersistNotificationUt
|
|
|
776
790
|
const PERSIST_NOTIFICATION_UTILS_METHOD_NAME_USE_JSON = "PersistNotificationUtils.useJson";
|
|
777
791
|
const PERSIST_NOTIFICATION_UTILS_METHOD_NAME_USE_DUMMY = "PersistNotificationUtils.useDummy";
|
|
778
792
|
const PERSIST_NOTIFICATION_UTILS_METHOD_NAME_USE_PERSIST_NOTIFICATION_ADAPTER = "PersistNotificationUtils.usePersistNotificationAdapter";
|
|
793
|
+
const PERSIST_LOG_UTILS_METHOD_NAME_READ_DATA = "PersistLogUtils.readLogData";
|
|
794
|
+
const PERSIST_LOG_UTILS_METHOD_NAME_WRITE_DATA = "PersistLogUtils.writeLogData";
|
|
795
|
+
const PERSIST_LOG_UTILS_METHOD_NAME_USE_JSON = "PersistLogUtils.useJson";
|
|
796
|
+
const PERSIST_LOG_UTILS_METHOD_NAME_USE_DUMMY = "PersistLogUtils.useDummy";
|
|
797
|
+
const PERSIST_LOG_UTILS_METHOD_NAME_USE_PERSIST_LOG_ADAPTER = "PersistLogUtils.usePersistLogAdapter";
|
|
779
798
|
const BASE_WAIT_FOR_INIT_FN_METHOD_NAME = "PersistBase.waitForInitFn";
|
|
780
799
|
const BASE_UNLINK_RETRY_COUNT = 5;
|
|
781
800
|
const BASE_UNLINK_RETRY_DELAY = 1000;
|
|
@@ -1946,6 +1965,106 @@ class PersistNotificationUtils {
|
|
|
1946
1965
|
* Used by NotificationPersistLiveUtils/NotificationPersistBacktestUtils for notification persistence.
|
|
1947
1966
|
*/
|
|
1948
1967
|
const PersistNotificationAdapter = new PersistNotificationUtils();
|
|
1968
|
+
/**
|
|
1969
|
+
* Utility class for managing log entry persistence.
|
|
1970
|
+
*
|
|
1971
|
+
* Features:
|
|
1972
|
+
* - Memoized storage instance
|
|
1973
|
+
* - Custom adapter support
|
|
1974
|
+
* - Atomic read/write operations for LogData
|
|
1975
|
+
* - Each log entry stored as separate file keyed by id
|
|
1976
|
+
* - Crash-safe log state management
|
|
1977
|
+
*
|
|
1978
|
+
* Used by LogPersistUtils for log entry persistence.
|
|
1979
|
+
*/
|
|
1980
|
+
class PersistLogUtils {
|
|
1981
|
+
constructor() {
|
|
1982
|
+
this.PersistLogFactory = PersistBase;
|
|
1983
|
+
this._logStorage = null;
|
|
1984
|
+
/**
|
|
1985
|
+
* Reads persisted log entries.
|
|
1986
|
+
*
|
|
1987
|
+
* Called by LogPersistUtils.waitForInit() to restore state.
|
|
1988
|
+
* Uses keys() from PersistBase to iterate over all stored entries.
|
|
1989
|
+
* Returns empty array if no entries exist.
|
|
1990
|
+
*
|
|
1991
|
+
* @returns Promise resolving to array of log entries
|
|
1992
|
+
*/
|
|
1993
|
+
this.readLogData = async () => {
|
|
1994
|
+
bt.loggerService.info(PERSIST_LOG_UTILS_METHOD_NAME_READ_DATA);
|
|
1995
|
+
const isInitial = !this._logStorage;
|
|
1996
|
+
const stateStorage = this.getLogStorage();
|
|
1997
|
+
await stateStorage.waitForInit(isInitial);
|
|
1998
|
+
const entries = [];
|
|
1999
|
+
for await (const entryId of stateStorage.keys()) {
|
|
2000
|
+
const entry = await stateStorage.readValue(entryId);
|
|
2001
|
+
entries.push(entry);
|
|
2002
|
+
}
|
|
2003
|
+
return entries;
|
|
2004
|
+
};
|
|
2005
|
+
/**
|
|
2006
|
+
* Writes log entries to disk with atomic file writes.
|
|
2007
|
+
*
|
|
2008
|
+
* Called by LogPersistUtils after each log call to persist state.
|
|
2009
|
+
* Uses entry.id as the storage key for individual file storage.
|
|
2010
|
+
* Uses atomic writes to prevent corruption on crashes.
|
|
2011
|
+
*
|
|
2012
|
+
* @param logData - Log entries to persist
|
|
2013
|
+
* @returns Promise that resolves when write is complete
|
|
2014
|
+
*/
|
|
2015
|
+
this.writeLogData = async (logData) => {
|
|
2016
|
+
bt.loggerService.info(PERSIST_LOG_UTILS_METHOD_NAME_WRITE_DATA);
|
|
2017
|
+
const isInitial = !this._logStorage;
|
|
2018
|
+
const stateStorage = this.getLogStorage();
|
|
2019
|
+
await stateStorage.waitForInit(isInitial);
|
|
2020
|
+
for (const entry of logData) {
|
|
2021
|
+
if (await stateStorage.hasValue(entry.id)) {
|
|
2022
|
+
continue;
|
|
2023
|
+
}
|
|
2024
|
+
await stateStorage.writeValue(entry.id, entry);
|
|
2025
|
+
}
|
|
2026
|
+
};
|
|
2027
|
+
}
|
|
2028
|
+
getLogStorage() {
|
|
2029
|
+
if (!this._logStorage) {
|
|
2030
|
+
this._logStorage = Reflect.construct(this.PersistLogFactory, [
|
|
2031
|
+
`log`,
|
|
2032
|
+
`./dump/data/log/`,
|
|
2033
|
+
]);
|
|
2034
|
+
}
|
|
2035
|
+
return this._logStorage;
|
|
2036
|
+
}
|
|
2037
|
+
/**
|
|
2038
|
+
* Registers a custom persistence adapter.
|
|
2039
|
+
*
|
|
2040
|
+
* @param Ctor - Custom PersistBase constructor
|
|
2041
|
+
*/
|
|
2042
|
+
usePersistLogAdapter(Ctor) {
|
|
2043
|
+
bt.loggerService.info(PERSIST_LOG_UTILS_METHOD_NAME_USE_PERSIST_LOG_ADAPTER);
|
|
2044
|
+
this.PersistLogFactory = Ctor;
|
|
2045
|
+
}
|
|
2046
|
+
/**
|
|
2047
|
+
* Switches to the default JSON persist adapter.
|
|
2048
|
+
* All future persistence writes will use JSON storage.
|
|
2049
|
+
*/
|
|
2050
|
+
useJson() {
|
|
2051
|
+
bt.loggerService.log(PERSIST_LOG_UTILS_METHOD_NAME_USE_JSON);
|
|
2052
|
+
this.usePersistLogAdapter(PersistBase);
|
|
2053
|
+
}
|
|
2054
|
+
/**
|
|
2055
|
+
* Switches to a dummy persist adapter that discards all writes.
|
|
2056
|
+
* All future persistence writes will be no-ops.
|
|
2057
|
+
*/
|
|
2058
|
+
useDummy() {
|
|
2059
|
+
bt.loggerService.log(PERSIST_LOG_UTILS_METHOD_NAME_USE_DUMMY);
|
|
2060
|
+
this.usePersistLogAdapter(PersistDummy);
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
/**
|
|
2064
|
+
* Global singleton instance of PersistLogUtils.
|
|
2065
|
+
* Used by LogPersistUtils for log entry persistence.
|
|
2066
|
+
*/
|
|
2067
|
+
const PersistLogAdapter = new PersistLogUtils();
|
|
1949
2068
|
|
|
1950
2069
|
var _a$2, _b$2;
|
|
1951
2070
|
const BUSY_DELAY = 100;
|
|
@@ -2687,6 +2806,55 @@ class ClientExchange {
|
|
|
2687
2806
|
GLOBAL_CONFIG.CC_ORDER_BOOK_TIME_OFFSET_MINUTES * MS_PER_MINUTE$5);
|
|
2688
2807
|
return await this.params.getOrderBook(symbol, depth, from, to, this.params.execution.context.backtest);
|
|
2689
2808
|
}
|
|
2809
|
+
/**
|
|
2810
|
+
* Fetches aggregated trades backwards from execution context time.
|
|
2811
|
+
*
|
|
2812
|
+
* Algorithm:
|
|
2813
|
+
* 1. Align when down to the nearest minute boundary (1-minute granularity)
|
|
2814
|
+
* 2. If limit is not specified: fetch one window of CC_AGGREGATED_TRADES_MAX_MINUTES
|
|
2815
|
+
* 3. If limit is specified: paginate backwards in CC_AGGREGATED_TRADES_MAX_MINUTES
|
|
2816
|
+
* chunks until at least limit trades are collected, then slice to limit
|
|
2817
|
+
*
|
|
2818
|
+
* Look-ahead bias prevention:
|
|
2819
|
+
* - `to` is always aligned down to the minute (never exceeds current when)
|
|
2820
|
+
* - Each pagination window goes strictly backwards from alignedWhen
|
|
2821
|
+
*
|
|
2822
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
2823
|
+
* @param limit - Optional maximum number of trades to return. If not specified,
|
|
2824
|
+
* returns all trades within the last CC_AGGREGATED_TRADES_MAX_MINUTES window.
|
|
2825
|
+
* @returns Promise resolving to array of aggregated trade data
|
|
2826
|
+
*/
|
|
2827
|
+
async getAggregatedTrades(symbol, limit) {
|
|
2828
|
+
this.params.logger.debug("ClientExchange getAggregatedTrades", {
|
|
2829
|
+
symbol,
|
|
2830
|
+
limit,
|
|
2831
|
+
});
|
|
2832
|
+
const whenTimestamp = this.params.execution.context.when.getTime();
|
|
2833
|
+
// Align to 1-minute boundary to prevent look-ahead bias
|
|
2834
|
+
const alignedTo = ALIGN_TO_INTERVAL_FN$2(whenTimestamp, 1);
|
|
2835
|
+
const windowMs = GLOBAL_CONFIG.CC_AGGREGATED_TRADES_MAX_MINUTES * MS_PER_MINUTE$5 - MS_PER_MINUTE$5;
|
|
2836
|
+
// No limit: fetch a single window and return as-is
|
|
2837
|
+
if (limit === undefined) {
|
|
2838
|
+
const to = new Date(alignedTo);
|
|
2839
|
+
const from = new Date(alignedTo - windowMs);
|
|
2840
|
+
return await this.params.getAggregatedTrades(symbol, from, to, this.params.execution.context.backtest);
|
|
2841
|
+
}
|
|
2842
|
+
// With limit: paginate backwards until we have enough trades
|
|
2843
|
+
const result = [];
|
|
2844
|
+
let windowEnd = alignedTo;
|
|
2845
|
+
while (result.length < limit) {
|
|
2846
|
+
const windowStart = windowEnd - windowMs;
|
|
2847
|
+
const to = new Date(windowEnd);
|
|
2848
|
+
const from = new Date(windowStart);
|
|
2849
|
+
const chunk = await this.params.getAggregatedTrades(symbol, from, to, this.params.execution.context.backtest);
|
|
2850
|
+
// Prepend chunk (older data goes first)
|
|
2851
|
+
result.unshift(...chunk);
|
|
2852
|
+
// Move window backwards
|
|
2853
|
+
windowEnd = windowStart;
|
|
2854
|
+
}
|
|
2855
|
+
// Slice to requested limit (most recent trades)
|
|
2856
|
+
return result.slice(-limit);
|
|
2857
|
+
}
|
|
2690
2858
|
}
|
|
2691
2859
|
|
|
2692
2860
|
/**
|
|
@@ -2723,6 +2891,18 @@ const DEFAULT_FORMAT_PRICE_FN$1 = async (_symbol, price, _backtest) => {
|
|
|
2723
2891
|
const DEFAULT_GET_ORDER_BOOK_FN$1 = async (_symbol, _depth, _from, _to, _backtest) => {
|
|
2724
2892
|
throw new Error(`getOrderBook is not implemented for this exchange`);
|
|
2725
2893
|
};
|
|
2894
|
+
/**
|
|
2895
|
+
* Default implementation for getAggregatedTrades.
|
|
2896
|
+
* Throws an error indicating the method is not implemented.
|
|
2897
|
+
*
|
|
2898
|
+
* @param _symbol - Trading pair symbol (unused)
|
|
2899
|
+
* @param _from - Start of time range (unused - can be ignored in live implementations)
|
|
2900
|
+
* @param _to - End of time range (unused - can be ignored in live implementations)
|
|
2901
|
+
* @param _backtest - Whether running in backtest mode (unused)
|
|
2902
|
+
*/
|
|
2903
|
+
const DEFAULT_GET_AGGREGATED_TRADES_FN$1 = async (_symbol, _from, _to, _backtest) => {
|
|
2904
|
+
throw new Error(`getAggregatedTrades is not implemented for this exchange`);
|
|
2905
|
+
};
|
|
2726
2906
|
/**
|
|
2727
2907
|
* Connection service routing exchange operations to correct ClientExchange instance.
|
|
2728
2908
|
*
|
|
@@ -2761,7 +2941,7 @@ class ExchangeConnectionService {
|
|
|
2761
2941
|
* @returns Configured ClientExchange instance
|
|
2762
2942
|
*/
|
|
2763
2943
|
this.getExchange = functoolsKit.memoize(([exchangeName]) => `${exchangeName}`, (exchangeName) => {
|
|
2764
|
-
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);
|
|
2944
|
+
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);
|
|
2765
2945
|
return new ClientExchange({
|
|
2766
2946
|
execution: this.executionContextService,
|
|
2767
2947
|
logger: this.loggerService,
|
|
@@ -2770,6 +2950,7 @@ class ExchangeConnectionService {
|
|
|
2770
2950
|
formatPrice,
|
|
2771
2951
|
formatQuantity,
|
|
2772
2952
|
getOrderBook,
|
|
2953
|
+
getAggregatedTrades,
|
|
2773
2954
|
callbacks,
|
|
2774
2955
|
});
|
|
2775
2956
|
});
|
|
@@ -2875,6 +3056,22 @@ class ExchangeConnectionService {
|
|
|
2875
3056
|
});
|
|
2876
3057
|
return await this.getExchange(this.methodContextService.context.exchangeName).getOrderBook(symbol, depth);
|
|
2877
3058
|
};
|
|
3059
|
+
/**
|
|
3060
|
+
* Fetches aggregated trades for a trading pair using configured exchange.
|
|
3061
|
+
*
|
|
3062
|
+
* Routes to exchange determined by methodContextService.context.exchangeName.
|
|
3063
|
+
*
|
|
3064
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
3065
|
+
* @param limit - Optional maximum number of trades to fetch. If empty returns one window of data.
|
|
3066
|
+
* @returns Promise resolving to array of aggregated trade data
|
|
3067
|
+
*/
|
|
3068
|
+
this.getAggregatedTrades = async (symbol, limit) => {
|
|
3069
|
+
this.loggerService.log("exchangeConnectionService getAggregatedTrades", {
|
|
3070
|
+
symbol,
|
|
3071
|
+
limit,
|
|
3072
|
+
});
|
|
3073
|
+
return await this.getExchange(this.methodContextService.context.exchangeName).getAggregatedTrades(symbol, limit);
|
|
3074
|
+
};
|
|
2878
3075
|
/**
|
|
2879
3076
|
* Fetches raw candles with flexible date/limit parameters.
|
|
2880
3077
|
*
|
|
@@ -11248,6 +11445,34 @@ class ExchangeCoreService {
|
|
|
11248
11445
|
backtest,
|
|
11249
11446
|
});
|
|
11250
11447
|
};
|
|
11448
|
+
/**
|
|
11449
|
+
* Fetches aggregated trades with execution context.
|
|
11450
|
+
*
|
|
11451
|
+
* @param symbol - Trading pair symbol
|
|
11452
|
+
* @param when - Timestamp for context (used in backtest mode)
|
|
11453
|
+
* @param backtest - Whether running in backtest mode
|
|
11454
|
+
* @param limit - Optional maximum number of trades to fetch
|
|
11455
|
+
* @returns Promise resolving to array of aggregated trade data
|
|
11456
|
+
*/
|
|
11457
|
+
this.getAggregatedTrades = async (symbol, when, backtest, limit) => {
|
|
11458
|
+
this.loggerService.log("exchangeCoreService getAggregatedTrades", {
|
|
11459
|
+
symbol,
|
|
11460
|
+
when,
|
|
11461
|
+
backtest,
|
|
11462
|
+
limit,
|
|
11463
|
+
});
|
|
11464
|
+
if (!MethodContextService.hasContext()) {
|
|
11465
|
+
throw new Error("exchangeCoreService getAggregatedTrades requires a method context");
|
|
11466
|
+
}
|
|
11467
|
+
await this.validate(this.methodContextService.context.exchangeName);
|
|
11468
|
+
return await ExecutionContextService.runInContext(async () => {
|
|
11469
|
+
return await this.exchangeConnectionService.getAggregatedTrades(symbol, limit);
|
|
11470
|
+
}, {
|
|
11471
|
+
symbol,
|
|
11472
|
+
when,
|
|
11473
|
+
backtest,
|
|
11474
|
+
});
|
|
11475
|
+
};
|
|
11251
11476
|
/**
|
|
11252
11477
|
* Fetches raw candles with flexible date/limit parameters and execution context.
|
|
11253
11478
|
*
|
|
@@ -26667,6 +26892,7 @@ const EXCHANGE_METHOD_NAME_FORMAT_QUANTITY = "ExchangeUtils.formatQuantity";
|
|
|
26667
26892
|
const EXCHANGE_METHOD_NAME_FORMAT_PRICE = "ExchangeUtils.formatPrice";
|
|
26668
26893
|
const EXCHANGE_METHOD_NAME_GET_ORDER_BOOK = "ExchangeUtils.getOrderBook";
|
|
26669
26894
|
const EXCHANGE_METHOD_NAME_GET_RAW_CANDLES = "ExchangeUtils.getRawCandles";
|
|
26895
|
+
const EXCHANGE_METHOD_NAME_GET_AGGREGATED_TRADES = "ExchangeUtils.getAggregatedTrades";
|
|
26670
26896
|
const MS_PER_MINUTE$3 = 60000;
|
|
26671
26897
|
/**
|
|
26672
26898
|
* Gets current timestamp from execution context if available.
|
|
@@ -26722,6 +26948,18 @@ const DEFAULT_FORMAT_PRICE_FN = async (_symbol, price, _backtest) => {
|
|
|
26722
26948
|
const DEFAULT_GET_ORDER_BOOK_FN = async (_symbol, _depth, _from, _to, _backtest) => {
|
|
26723
26949
|
throw new Error(`getOrderBook is not implemented for this exchange`);
|
|
26724
26950
|
};
|
|
26951
|
+
/**
|
|
26952
|
+
* Default implementation for getAggregatedTrades.
|
|
26953
|
+
* Throws an error indicating the method is not implemented.
|
|
26954
|
+
*
|
|
26955
|
+
* @param _symbol - Trading pair symbol (unused)
|
|
26956
|
+
* @param _from - Start of time range (unused - can be ignored in live implementations)
|
|
26957
|
+
* @param _to - End of time range (unused - can be ignored in live implementations)
|
|
26958
|
+
* @param _backtest - Whether running in backtest mode (unused)
|
|
26959
|
+
*/
|
|
26960
|
+
const DEFAULT_GET_AGGREGATED_TRADES_FN = async (_symbol, _from, _to, _backtest) => {
|
|
26961
|
+
throw new Error(`getAggregatedTrades is not implemented for this exchange`);
|
|
26962
|
+
};
|
|
26725
26963
|
const INTERVAL_MINUTES$3 = {
|
|
26726
26964
|
"1m": 1,
|
|
26727
26965
|
"3m": 3,
|
|
@@ -26767,11 +27005,13 @@ const CREATE_EXCHANGE_INSTANCE_FN = (schema) => {
|
|
|
26767
27005
|
const formatQuantity = schema.formatQuantity ?? DEFAULT_FORMAT_QUANTITY_FN;
|
|
26768
27006
|
const formatPrice = schema.formatPrice ?? DEFAULT_FORMAT_PRICE_FN;
|
|
26769
27007
|
const getOrderBook = schema.getOrderBook ?? DEFAULT_GET_ORDER_BOOK_FN;
|
|
27008
|
+
const getAggregatedTrades = schema.getAggregatedTrades ?? DEFAULT_GET_AGGREGATED_TRADES_FN;
|
|
26770
27009
|
return {
|
|
26771
27010
|
getCandles,
|
|
26772
27011
|
formatQuantity,
|
|
26773
27012
|
formatPrice,
|
|
26774
27013
|
getOrderBook,
|
|
27014
|
+
getAggregatedTrades,
|
|
26775
27015
|
};
|
|
26776
27016
|
};
|
|
26777
27017
|
/**
|
|
@@ -27083,6 +27323,58 @@ class ExchangeInstance {
|
|
|
27083
27323
|
const isBacktest = await GET_BACKTEST_FN();
|
|
27084
27324
|
return await this._methods.getOrderBook(symbol, depth, from, to, isBacktest);
|
|
27085
27325
|
};
|
|
27326
|
+
/**
|
|
27327
|
+
* Fetch aggregated trades for a trading pair.
|
|
27328
|
+
*
|
|
27329
|
+
* Calculates time range backwards from current timestamp (or execution context when).
|
|
27330
|
+
* Aligns `to` to 1-minute boundary to prevent look-ahead bias.
|
|
27331
|
+
* If limit is not specified, returns all trades within one CC_AGGREGATED_TRADES_MAX_MINUTES window.
|
|
27332
|
+
* If limit is specified, paginates backwards until at least limit trades are collected.
|
|
27333
|
+
*
|
|
27334
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
27335
|
+
* @param limit - Optional maximum number of trades to return
|
|
27336
|
+
* @returns Promise resolving to array of aggregated trade data
|
|
27337
|
+
*
|
|
27338
|
+
* @example
|
|
27339
|
+
* ```typescript
|
|
27340
|
+
* const instance = new ExchangeInstance("binance");
|
|
27341
|
+
* const trades = await instance.getAggregatedTrades("BTCUSDT");
|
|
27342
|
+
* const lastN = await instance.getAggregatedTrades("BTCUSDT", 500);
|
|
27343
|
+
* ```
|
|
27344
|
+
*/
|
|
27345
|
+
this.getAggregatedTrades = async (symbol, limit) => {
|
|
27346
|
+
bt.loggerService.info(EXCHANGE_METHOD_NAME_GET_AGGREGATED_TRADES, {
|
|
27347
|
+
exchangeName: this.exchangeName,
|
|
27348
|
+
symbol,
|
|
27349
|
+
limit,
|
|
27350
|
+
});
|
|
27351
|
+
const when = await GET_TIMESTAMP_FN();
|
|
27352
|
+
// Align to 1-minute boundary to prevent look-ahead bias
|
|
27353
|
+
const alignedTo = ALIGN_TO_INTERVAL_FN$1(when.getTime(), 1);
|
|
27354
|
+
const windowMs = GLOBAL_CONFIG.CC_AGGREGATED_TRADES_MAX_MINUTES * MS_PER_MINUTE$3 - MS_PER_MINUTE$3;
|
|
27355
|
+
const isBacktest = await GET_BACKTEST_FN();
|
|
27356
|
+
// No limit: fetch a single window and return as-is
|
|
27357
|
+
if (limit === undefined) {
|
|
27358
|
+
const to = new Date(alignedTo);
|
|
27359
|
+
const from = new Date(alignedTo - windowMs);
|
|
27360
|
+
return await this._methods.getAggregatedTrades(symbol, from, to, isBacktest);
|
|
27361
|
+
}
|
|
27362
|
+
// With limit: paginate backwards until we have enough trades
|
|
27363
|
+
const result = [];
|
|
27364
|
+
let windowEnd = alignedTo;
|
|
27365
|
+
while (result.length < limit) {
|
|
27366
|
+
const windowStart = windowEnd - windowMs;
|
|
27367
|
+
const to = new Date(windowEnd);
|
|
27368
|
+
const from = new Date(windowStart);
|
|
27369
|
+
const chunk = await this._methods.getAggregatedTrades(symbol, from, to, isBacktest);
|
|
27370
|
+
// Prepend chunk (older data goes first)
|
|
27371
|
+
result.unshift(...chunk);
|
|
27372
|
+
// Move window backwards
|
|
27373
|
+
windowEnd = windowStart;
|
|
27374
|
+
}
|
|
27375
|
+
// Slice to requested limit (most recent trades)
|
|
27376
|
+
return result.slice(-limit);
|
|
27377
|
+
};
|
|
27086
27378
|
/**
|
|
27087
27379
|
* Fetches raw candles with flexible date/limit parameters.
|
|
27088
27380
|
*
|
|
@@ -27352,6 +27644,19 @@ class ExchangeUtils {
|
|
|
27352
27644
|
const instance = this._getInstance(context.exchangeName);
|
|
27353
27645
|
return await instance.getOrderBook(symbol, depth);
|
|
27354
27646
|
};
|
|
27647
|
+
/**
|
|
27648
|
+
* Fetch aggregated trades for a trading pair.
|
|
27649
|
+
*
|
|
27650
|
+
* @param symbol - Trading pair symbol
|
|
27651
|
+
* @param context - Execution context with exchange name
|
|
27652
|
+
* @param limit - Optional maximum number of trades to return
|
|
27653
|
+
* @returns Promise resolving to array of aggregated trade data
|
|
27654
|
+
*/
|
|
27655
|
+
this.getAggregatedTrades = async (symbol, context, limit) => {
|
|
27656
|
+
bt.exchangeValidationService.validate(context.exchangeName, EXCHANGE_METHOD_NAME_GET_AGGREGATED_TRADES);
|
|
27657
|
+
const instance = this._getInstance(context.exchangeName);
|
|
27658
|
+
return await instance.getAggregatedTrades(symbol, limit);
|
|
27659
|
+
};
|
|
27355
27660
|
/**
|
|
27356
27661
|
* Fetches raw candles with flexible date/limit parameters.
|
|
27357
27662
|
*
|
|
@@ -27891,6 +28196,7 @@ const HAS_TRADE_CONTEXT_METHOD_NAME = "exchange.hasTradeContext";
|
|
|
27891
28196
|
const GET_ORDER_BOOK_METHOD_NAME = "exchange.getOrderBook";
|
|
27892
28197
|
const GET_RAW_CANDLES_METHOD_NAME = "exchange.getRawCandles";
|
|
27893
28198
|
const GET_NEXT_CANDLES_METHOD_NAME = "exchange.getNextCandles";
|
|
28199
|
+
const GET_AGGREGATED_TRADES_METHOD_NAME = "exchange.getAggregatedTrades";
|
|
27894
28200
|
/**
|
|
27895
28201
|
* Checks if trade context is active (execution and method contexts).
|
|
27896
28202
|
*
|
|
@@ -28219,6 +28525,41 @@ async function getNextCandles(symbol, interval, limit) {
|
|
|
28219
28525
|
}
|
|
28220
28526
|
return await bt.exchangeConnectionService.getNextCandles(symbol, interval, limit);
|
|
28221
28527
|
}
|
|
28528
|
+
/**
|
|
28529
|
+
* Fetches aggregated trades for a trading pair from the registered exchange.
|
|
28530
|
+
*
|
|
28531
|
+
* Trades are fetched backwards from the current execution context time.
|
|
28532
|
+
* If limit is not specified, returns all trades within one CC_AGGREGATED_TRADES_MAX_MINUTES window.
|
|
28533
|
+
* If limit is specified, paginates backwards until at least limit trades are collected.
|
|
28534
|
+
*
|
|
28535
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
28536
|
+
* @param limit - Optional maximum number of trades to fetch
|
|
28537
|
+
* @returns Promise resolving to array of aggregated trade data
|
|
28538
|
+
* @throws Error if execution or method context is missing
|
|
28539
|
+
*
|
|
28540
|
+
* @example
|
|
28541
|
+
* ```typescript
|
|
28542
|
+
* // Fetch last hour of trades
|
|
28543
|
+
* const trades = await getAggregatedTrades("BTCUSDT");
|
|
28544
|
+
*
|
|
28545
|
+
* // Fetch last 500 trades
|
|
28546
|
+
* const lastTrades = await getAggregatedTrades("BTCUSDT", 500);
|
|
28547
|
+
* console.log(lastTrades[0]); // { id, price, qty, timestamp, isBuyerMaker }
|
|
28548
|
+
* ```
|
|
28549
|
+
*/
|
|
28550
|
+
async function getAggregatedTrades(symbol, limit) {
|
|
28551
|
+
bt.loggerService.info(GET_AGGREGATED_TRADES_METHOD_NAME, {
|
|
28552
|
+
symbol,
|
|
28553
|
+
limit,
|
|
28554
|
+
});
|
|
28555
|
+
if (!ExecutionContextService.hasContext()) {
|
|
28556
|
+
throw new Error("getAggregatedTrades requires an execution context");
|
|
28557
|
+
}
|
|
28558
|
+
if (!MethodContextService.hasContext()) {
|
|
28559
|
+
throw new Error("getAggregatedTrades requires a method context");
|
|
28560
|
+
}
|
|
28561
|
+
return await bt.exchangeConnectionService.getAggregatedTrades(symbol, limit);
|
|
28562
|
+
}
|
|
28222
28563
|
|
|
28223
28564
|
const CANCEL_SCHEDULED_METHOD_NAME = "strategy.commitCancelScheduled";
|
|
28224
28565
|
const CLOSE_PENDING_METHOD_NAME = "strategy.commitClosePending";
|
|
@@ -30882,6 +31223,416 @@ async function dumpMessages(resultId, history, result, outputDir = "./dump/strat
|
|
|
30882
31223
|
}
|
|
30883
31224
|
}
|
|
30884
31225
|
|
|
31226
|
+
const LOG_PERSIST_METHOD_NAME_WAIT_FOR_INIT = "LogPersistUtils.waitForInit";
|
|
31227
|
+
const LOG_PERSIST_METHOD_NAME_LOG = "LogPersistUtils.log";
|
|
31228
|
+
const LOG_PERSIST_METHOD_NAME_DEBUG = "LogPersistUtils.debug";
|
|
31229
|
+
const LOG_PERSIST_METHOD_NAME_INFO = "LogPersistUtils.info";
|
|
31230
|
+
const LOG_PERSIST_METHOD_NAME_WARN = "LogPersistUtils.warn";
|
|
31231
|
+
const LOG_PERSIST_METHOD_NAME_GET_LIST = "LogPersistUtils.getList";
|
|
31232
|
+
const LOG_MEMORY_METHOD_NAME_LOG = "LogMemoryUtils.log";
|
|
31233
|
+
const LOG_MEMORY_METHOD_NAME_DEBUG = "LogMemoryUtils.debug";
|
|
31234
|
+
const LOG_MEMORY_METHOD_NAME_INFO = "LogMemoryUtils.info";
|
|
31235
|
+
const LOG_MEMORY_METHOD_NAME_WARN = "LogMemoryUtils.warn";
|
|
31236
|
+
const LOG_MEMORY_METHOD_NAME_GET_LIST = "LogMemoryUtils.getList";
|
|
31237
|
+
const LOG_ADAPTER_METHOD_NAME_USE_LOGGER = "LogAdapter.useLogger";
|
|
31238
|
+
const LOG_ADAPTER_METHOD_NAME_USE_PERSIST = "LogAdapter.usePersist";
|
|
31239
|
+
const LOG_ADAPTER_METHOD_NAME_USE_MEMORY = "LogAdapter.useMemory";
|
|
31240
|
+
const LOG_ADAPTER_METHOD_NAME_USE_DUMMY = "LogAdapter.useDummy";
|
|
31241
|
+
/**
|
|
31242
|
+
* Backtest execution time retrieval function.
|
|
31243
|
+
* Returns the 'when' timestamp from the execution context if available, otherwise returns the current time.
|
|
31244
|
+
* 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.
|
|
31245
|
+
*/
|
|
31246
|
+
const GET_DATE_FN = async () => {
|
|
31247
|
+
if (ExecutionContextService.hasContext()) {
|
|
31248
|
+
return new Date(bt.executionContextService.context.when);
|
|
31249
|
+
}
|
|
31250
|
+
return new Date();
|
|
31251
|
+
};
|
|
31252
|
+
/**
|
|
31253
|
+
* Persistent log adapter.
|
|
31254
|
+
*
|
|
31255
|
+
* Features:
|
|
31256
|
+
* - Persists log entries to disk using PersistLogAdapter
|
|
31257
|
+
* - Lazy initialization with singleshot pattern
|
|
31258
|
+
* - Maintains up to CC_MAX_LOG_LINES most recent entries
|
|
31259
|
+
* - Each entry stored individually keyed by its id
|
|
31260
|
+
*
|
|
31261
|
+
* Use this adapter (default) for log persistence across sessions.
|
|
31262
|
+
*/
|
|
31263
|
+
class LogPersistUtils {
|
|
31264
|
+
constructor() {
|
|
31265
|
+
/** Array of log entries */
|
|
31266
|
+
this._entries = [];
|
|
31267
|
+
/**
|
|
31268
|
+
* Singleshot initialization function that loads entries from disk.
|
|
31269
|
+
* Protected by singleshot to ensure one-time execution.
|
|
31270
|
+
*/
|
|
31271
|
+
this.waitForInit = functoolsKit.singleshot(async () => {
|
|
31272
|
+
bt.loggerService.info(LOG_PERSIST_METHOD_NAME_WAIT_FOR_INIT);
|
|
31273
|
+
const list = await PersistLogAdapter.readLogData();
|
|
31274
|
+
list.sort((a, b) => a.timestamp - b.timestamp);
|
|
31275
|
+
this._entries = list.slice(-GLOBAL_CONFIG.CC_MAX_LOG_LINES);
|
|
31276
|
+
});
|
|
31277
|
+
/**
|
|
31278
|
+
* Logs a general-purpose message.
|
|
31279
|
+
* Persists entry to disk after appending.
|
|
31280
|
+
* @param topic - The log topic / method name
|
|
31281
|
+
* @param args - Additional arguments
|
|
31282
|
+
*/
|
|
31283
|
+
this.log = async (topic, ...args) => {
|
|
31284
|
+
bt.loggerService.info(LOG_PERSIST_METHOD_NAME_LOG, { topic });
|
|
31285
|
+
await this.waitForInit();
|
|
31286
|
+
const date = await GET_DATE_FN();
|
|
31287
|
+
this._entries.push({
|
|
31288
|
+
id: functoolsKit.randomString(),
|
|
31289
|
+
type: "log",
|
|
31290
|
+
timestamp: Date.now(),
|
|
31291
|
+
createdAt: date.toISOString(),
|
|
31292
|
+
topic,
|
|
31293
|
+
args,
|
|
31294
|
+
});
|
|
31295
|
+
this._enforceLimit();
|
|
31296
|
+
await PersistLogAdapter.writeLogData(this._entries);
|
|
31297
|
+
};
|
|
31298
|
+
/**
|
|
31299
|
+
* Logs a debug-level message.
|
|
31300
|
+
* Persists entry to disk after appending.
|
|
31301
|
+
* @param topic - The log topic / method name
|
|
31302
|
+
* @param args - Additional arguments
|
|
31303
|
+
*/
|
|
31304
|
+
this.debug = async (topic, ...args) => {
|
|
31305
|
+
bt.loggerService.info(LOG_PERSIST_METHOD_NAME_DEBUG, { topic });
|
|
31306
|
+
await this.waitForInit();
|
|
31307
|
+
const date = await GET_DATE_FN();
|
|
31308
|
+
this._entries.push({
|
|
31309
|
+
id: functoolsKit.randomString(),
|
|
31310
|
+
type: "debug",
|
|
31311
|
+
timestamp: Date.now(),
|
|
31312
|
+
createdAt: date.toISOString(),
|
|
31313
|
+
topic,
|
|
31314
|
+
args,
|
|
31315
|
+
});
|
|
31316
|
+
this._enforceLimit();
|
|
31317
|
+
await PersistLogAdapter.writeLogData(this._entries);
|
|
31318
|
+
};
|
|
31319
|
+
/**
|
|
31320
|
+
* Logs an info-level message.
|
|
31321
|
+
* Persists entry to disk after appending.
|
|
31322
|
+
* @param topic - The log topic / method name
|
|
31323
|
+
* @param args - Additional arguments
|
|
31324
|
+
*/
|
|
31325
|
+
this.info = async (topic, ...args) => {
|
|
31326
|
+
bt.loggerService.info(LOG_PERSIST_METHOD_NAME_INFO, { topic });
|
|
31327
|
+
await this.waitForInit();
|
|
31328
|
+
const date = await GET_DATE_FN();
|
|
31329
|
+
this._entries.push({
|
|
31330
|
+
id: functoolsKit.randomString(),
|
|
31331
|
+
type: "info",
|
|
31332
|
+
timestamp: Date.now(),
|
|
31333
|
+
createdAt: date.toISOString(),
|
|
31334
|
+
topic,
|
|
31335
|
+
args,
|
|
31336
|
+
});
|
|
31337
|
+
this._enforceLimit();
|
|
31338
|
+
await PersistLogAdapter.writeLogData(this._entries);
|
|
31339
|
+
};
|
|
31340
|
+
/**
|
|
31341
|
+
* Logs a warning-level message.
|
|
31342
|
+
* Persists entry to disk after appending.
|
|
31343
|
+
* @param topic - The log topic / method name
|
|
31344
|
+
* @param args - Additional arguments
|
|
31345
|
+
*/
|
|
31346
|
+
this.warn = async (topic, ...args) => {
|
|
31347
|
+
bt.loggerService.info(LOG_PERSIST_METHOD_NAME_WARN, { topic });
|
|
31348
|
+
await this.waitForInit();
|
|
31349
|
+
const date = await GET_DATE_FN();
|
|
31350
|
+
this._entries.push({
|
|
31351
|
+
id: functoolsKit.randomString(),
|
|
31352
|
+
type: "warn",
|
|
31353
|
+
timestamp: Date.now(),
|
|
31354
|
+
createdAt: date.toISOString(),
|
|
31355
|
+
topic,
|
|
31356
|
+
args,
|
|
31357
|
+
});
|
|
31358
|
+
this._enforceLimit();
|
|
31359
|
+
await PersistLogAdapter.writeLogData(this._entries);
|
|
31360
|
+
};
|
|
31361
|
+
/**
|
|
31362
|
+
* Lists all stored log entries.
|
|
31363
|
+
* @returns Array of all log entries
|
|
31364
|
+
*/
|
|
31365
|
+
this.getList = async () => {
|
|
31366
|
+
bt.loggerService.info(LOG_PERSIST_METHOD_NAME_GET_LIST);
|
|
31367
|
+
await this.waitForInit();
|
|
31368
|
+
return [...this._entries];
|
|
31369
|
+
};
|
|
31370
|
+
}
|
|
31371
|
+
/**
|
|
31372
|
+
* Removes oldest entries if limit is exceeded.
|
|
31373
|
+
*/
|
|
31374
|
+
_enforceLimit() {
|
|
31375
|
+
if (this._entries.length > GLOBAL_CONFIG.CC_MAX_LOG_LINES) {
|
|
31376
|
+
this._entries.splice(0, this._entries.length - GLOBAL_CONFIG.CC_MAX_LOG_LINES);
|
|
31377
|
+
}
|
|
31378
|
+
}
|
|
31379
|
+
}
|
|
31380
|
+
/**
|
|
31381
|
+
* In-memory log adapter.
|
|
31382
|
+
*
|
|
31383
|
+
* Features:
|
|
31384
|
+
* - Stores log entries in memory only (no persistence)
|
|
31385
|
+
* - Maintains up to CC_MAX_LOG_LINES most recent entries
|
|
31386
|
+
* - Data is lost when application restarts
|
|
31387
|
+
* - Handles all log levels (log, debug, info, warn)
|
|
31388
|
+
*
|
|
31389
|
+
* Use this adapter for testing or when persistence is not required.
|
|
31390
|
+
*/
|
|
31391
|
+
class LogMemoryUtils {
|
|
31392
|
+
constructor() {
|
|
31393
|
+
/** Array of log entries */
|
|
31394
|
+
this._entries = [];
|
|
31395
|
+
/**
|
|
31396
|
+
* Logs a general-purpose message.
|
|
31397
|
+
* Appends entry to in-memory array.
|
|
31398
|
+
* @param topic - The log topic / method name
|
|
31399
|
+
* @param args - Additional arguments
|
|
31400
|
+
*/
|
|
31401
|
+
this.log = async (topic, ...args) => {
|
|
31402
|
+
bt.loggerService.info(LOG_MEMORY_METHOD_NAME_LOG, { topic });
|
|
31403
|
+
const date = await GET_DATE_FN();
|
|
31404
|
+
this._entries.push({
|
|
31405
|
+
id: functoolsKit.randomString(),
|
|
31406
|
+
type: "log",
|
|
31407
|
+
timestamp: Date.now(),
|
|
31408
|
+
createdAt: date.toISOString(),
|
|
31409
|
+
topic,
|
|
31410
|
+
args,
|
|
31411
|
+
});
|
|
31412
|
+
this._enforceLimit();
|
|
31413
|
+
};
|
|
31414
|
+
/**
|
|
31415
|
+
* Logs a debug-level message.
|
|
31416
|
+
* Appends entry to in-memory array.
|
|
31417
|
+
* @param topic - The log topic / method name
|
|
31418
|
+
* @param args - Additional arguments
|
|
31419
|
+
*/
|
|
31420
|
+
this.debug = async (topic, ...args) => {
|
|
31421
|
+
bt.loggerService.info(LOG_MEMORY_METHOD_NAME_DEBUG, { topic });
|
|
31422
|
+
const date = await GET_DATE_FN();
|
|
31423
|
+
this._entries.push({
|
|
31424
|
+
id: functoolsKit.randomString(),
|
|
31425
|
+
type: "debug",
|
|
31426
|
+
timestamp: Date.now(),
|
|
31427
|
+
createdAt: date.toISOString(),
|
|
31428
|
+
topic,
|
|
31429
|
+
args,
|
|
31430
|
+
});
|
|
31431
|
+
this._enforceLimit();
|
|
31432
|
+
};
|
|
31433
|
+
/**
|
|
31434
|
+
* Logs an info-level message.
|
|
31435
|
+
* Appends entry to in-memory array.
|
|
31436
|
+
* @param topic - The log topic / method name
|
|
31437
|
+
* @param args - Additional arguments
|
|
31438
|
+
*/
|
|
31439
|
+
this.info = async (topic, ...args) => {
|
|
31440
|
+
bt.loggerService.info(LOG_MEMORY_METHOD_NAME_INFO, { topic });
|
|
31441
|
+
const date = await GET_DATE_FN();
|
|
31442
|
+
this._entries.push({
|
|
31443
|
+
id: functoolsKit.randomString(),
|
|
31444
|
+
type: "info",
|
|
31445
|
+
timestamp: Date.now(),
|
|
31446
|
+
createdAt: date.toISOString(),
|
|
31447
|
+
topic,
|
|
31448
|
+
args,
|
|
31449
|
+
});
|
|
31450
|
+
this._enforceLimit();
|
|
31451
|
+
};
|
|
31452
|
+
/**
|
|
31453
|
+
* Logs a warning-level message.
|
|
31454
|
+
* Appends entry to in-memory array.
|
|
31455
|
+
* @param topic - The log topic / method name
|
|
31456
|
+
* @param args - Additional arguments
|
|
31457
|
+
*/
|
|
31458
|
+
this.warn = async (topic, ...args) => {
|
|
31459
|
+
bt.loggerService.info(LOG_MEMORY_METHOD_NAME_WARN, { topic });
|
|
31460
|
+
const date = await GET_DATE_FN();
|
|
31461
|
+
this._entries.push({
|
|
31462
|
+
id: functoolsKit.randomString(),
|
|
31463
|
+
type: "warn",
|
|
31464
|
+
timestamp: Date.now(),
|
|
31465
|
+
createdAt: date.toISOString(),
|
|
31466
|
+
topic,
|
|
31467
|
+
args,
|
|
31468
|
+
});
|
|
31469
|
+
this._enforceLimit();
|
|
31470
|
+
};
|
|
31471
|
+
/**
|
|
31472
|
+
* Lists all stored log entries.
|
|
31473
|
+
* @returns Array of all log entries
|
|
31474
|
+
*/
|
|
31475
|
+
this.getList = async () => {
|
|
31476
|
+
bt.loggerService.info(LOG_MEMORY_METHOD_NAME_GET_LIST);
|
|
31477
|
+
return [...this._entries];
|
|
31478
|
+
};
|
|
31479
|
+
}
|
|
31480
|
+
/**
|
|
31481
|
+
* Removes oldest entries if limit is exceeded.
|
|
31482
|
+
*/
|
|
31483
|
+
_enforceLimit() {
|
|
31484
|
+
if (this._entries.length > GLOBAL_CONFIG.CC_MAX_LOG_LINES) {
|
|
31485
|
+
this._entries.splice(0, this._entries.length - GLOBAL_CONFIG.CC_MAX_LOG_LINES);
|
|
31486
|
+
}
|
|
31487
|
+
}
|
|
31488
|
+
}
|
|
31489
|
+
/**
|
|
31490
|
+
* Dummy log adapter that discards all writes.
|
|
31491
|
+
*
|
|
31492
|
+
* Features:
|
|
31493
|
+
* - No-op implementation for all methods
|
|
31494
|
+
* - getList always returns empty array
|
|
31495
|
+
*
|
|
31496
|
+
* Use this adapter to disable log storage completely.
|
|
31497
|
+
*/
|
|
31498
|
+
class LogDummyUtils {
|
|
31499
|
+
/**
|
|
31500
|
+
* Always returns empty array (no storage).
|
|
31501
|
+
* @returns Empty array
|
|
31502
|
+
*/
|
|
31503
|
+
async getList() {
|
|
31504
|
+
return [];
|
|
31505
|
+
}
|
|
31506
|
+
/**
|
|
31507
|
+
* No-op handler for general-purpose log.
|
|
31508
|
+
*/
|
|
31509
|
+
log() {
|
|
31510
|
+
}
|
|
31511
|
+
/**
|
|
31512
|
+
* No-op handler for debug-level log.
|
|
31513
|
+
*/
|
|
31514
|
+
debug() {
|
|
31515
|
+
}
|
|
31516
|
+
/**
|
|
31517
|
+
* No-op handler for info-level log.
|
|
31518
|
+
*/
|
|
31519
|
+
info() {
|
|
31520
|
+
}
|
|
31521
|
+
/**
|
|
31522
|
+
* No-op handler for warning-level log.
|
|
31523
|
+
*/
|
|
31524
|
+
warn() {
|
|
31525
|
+
}
|
|
31526
|
+
}
|
|
31527
|
+
/**
|
|
31528
|
+
* Log adapter with pluggable storage backend.
|
|
31529
|
+
*
|
|
31530
|
+
* Features:
|
|
31531
|
+
* - Adapter pattern for swappable log implementations
|
|
31532
|
+
* - Default adapter: LogMemoryUtils (in-memory storage)
|
|
31533
|
+
* - Alternative adapters: LogPersistUtils, LogDummyUtils
|
|
31534
|
+
* - Convenience methods: usePersist(), useMemory(), useDummy()
|
|
31535
|
+
*/
|
|
31536
|
+
class LogAdapter {
|
|
31537
|
+
constructor() {
|
|
31538
|
+
/** Internal log utils instance */
|
|
31539
|
+
this._log = new LogMemoryUtils();
|
|
31540
|
+
/**
|
|
31541
|
+
* Lists all stored log entries.
|
|
31542
|
+
* Proxies call to the underlying log adapter.
|
|
31543
|
+
* @returns Array of all log entries
|
|
31544
|
+
*/
|
|
31545
|
+
this.getList = async () => {
|
|
31546
|
+
if (this._log.getList) {
|
|
31547
|
+
return await this._log.getList();
|
|
31548
|
+
}
|
|
31549
|
+
return [];
|
|
31550
|
+
};
|
|
31551
|
+
/**
|
|
31552
|
+
* Logs a general-purpose message.
|
|
31553
|
+
* Proxies call to the underlying log adapter.
|
|
31554
|
+
* @param topic - The log topic / method name
|
|
31555
|
+
* @param args - Additional arguments
|
|
31556
|
+
*/
|
|
31557
|
+
this.log = (topic, ...args) => {
|
|
31558
|
+
if (this._log.log) {
|
|
31559
|
+
this._log.log(topic, ...args);
|
|
31560
|
+
}
|
|
31561
|
+
};
|
|
31562
|
+
/**
|
|
31563
|
+
* Logs a debug-level message.
|
|
31564
|
+
* Proxies call to the underlying log adapter.
|
|
31565
|
+
* @param topic - The log topic / method name
|
|
31566
|
+
* @param args - Additional arguments
|
|
31567
|
+
*/
|
|
31568
|
+
this.debug = (topic, ...args) => {
|
|
31569
|
+
if (this._log.debug) {
|
|
31570
|
+
this._log.debug(topic, ...args);
|
|
31571
|
+
}
|
|
31572
|
+
};
|
|
31573
|
+
/**
|
|
31574
|
+
* Logs an info-level message.
|
|
31575
|
+
* Proxies call to the underlying log adapter.
|
|
31576
|
+
* @param topic - The log topic / method name
|
|
31577
|
+
* @param args - Additional arguments
|
|
31578
|
+
*/
|
|
31579
|
+
this.info = (topic, ...args) => {
|
|
31580
|
+
if (this._log.info) {
|
|
31581
|
+
this._log.info(topic, ...args);
|
|
31582
|
+
}
|
|
31583
|
+
};
|
|
31584
|
+
/**
|
|
31585
|
+
* Logs a warning-level message.
|
|
31586
|
+
* Proxies call to the underlying log adapter.
|
|
31587
|
+
* @param topic - The log topic / method name
|
|
31588
|
+
* @param args - Additional arguments
|
|
31589
|
+
*/
|
|
31590
|
+
this.warn = (topic, ...args) => {
|
|
31591
|
+
if (this._log.warn) {
|
|
31592
|
+
this._log.warn(topic, ...args);
|
|
31593
|
+
}
|
|
31594
|
+
};
|
|
31595
|
+
/**
|
|
31596
|
+
* Sets the log adapter constructor.
|
|
31597
|
+
* All future log operations will use this adapter.
|
|
31598
|
+
* @param Ctor - Constructor for log adapter
|
|
31599
|
+
*/
|
|
31600
|
+
this.useLogger = (Ctor) => {
|
|
31601
|
+
bt.loggerService.info(LOG_ADAPTER_METHOD_NAME_USE_LOGGER);
|
|
31602
|
+
this._log = Reflect.construct(Ctor, []);
|
|
31603
|
+
};
|
|
31604
|
+
/**
|
|
31605
|
+
* Switches to persistent log adapter.
|
|
31606
|
+
* Log entries will be persisted to disk.
|
|
31607
|
+
*/
|
|
31608
|
+
this.usePersist = () => {
|
|
31609
|
+
bt.loggerService.info(LOG_ADAPTER_METHOD_NAME_USE_PERSIST);
|
|
31610
|
+
this._log = new LogPersistUtils();
|
|
31611
|
+
};
|
|
31612
|
+
/**
|
|
31613
|
+
* Switches to in-memory log adapter (default).
|
|
31614
|
+
* Log entries will be stored in memory only.
|
|
31615
|
+
*/
|
|
31616
|
+
this.useMemory = () => {
|
|
31617
|
+
bt.loggerService.info(LOG_ADAPTER_METHOD_NAME_USE_MEMORY);
|
|
31618
|
+
this._log = new LogMemoryUtils();
|
|
31619
|
+
};
|
|
31620
|
+
/**
|
|
31621
|
+
* Switches to dummy log adapter.
|
|
31622
|
+
* All future log writes will be no-ops.
|
|
31623
|
+
*/
|
|
31624
|
+
this.useDummy = () => {
|
|
31625
|
+
bt.loggerService.info(LOG_ADAPTER_METHOD_NAME_USE_DUMMY);
|
|
31626
|
+
this._log = new LogDummyUtils();
|
|
31627
|
+
};
|
|
31628
|
+
}
|
|
31629
|
+
}
|
|
31630
|
+
/**
|
|
31631
|
+
* Global singleton instance of LogAdapter.
|
|
31632
|
+
* Provides unified log management with pluggable backends.
|
|
31633
|
+
*/
|
|
31634
|
+
const Log = new LogAdapter();
|
|
31635
|
+
|
|
30885
31636
|
const BACKTEST_METHOD_NAME_RUN = "BacktestUtils.run";
|
|
30886
31637
|
const BACKTEST_METHOD_NAME_BACKGROUND = "BacktestUtils.background";
|
|
30887
31638
|
const BACKTEST_METHOD_NAME_STOP = "BacktestUtils.stop";
|
|
@@ -38289,6 +39040,7 @@ exports.Exchange = Exchange;
|
|
|
38289
39040
|
exports.ExecutionContextService = ExecutionContextService;
|
|
38290
39041
|
exports.Heat = Heat;
|
|
38291
39042
|
exports.Live = Live;
|
|
39043
|
+
exports.Log = Log;
|
|
38292
39044
|
exports.Markdown = Markdown;
|
|
38293
39045
|
exports.MarkdownFileBase = MarkdownFileBase;
|
|
38294
39046
|
exports.MarkdownFolderBase = MarkdownFolderBase;
|
|
@@ -38301,6 +39053,7 @@ exports.Performance = Performance;
|
|
|
38301
39053
|
exports.PersistBase = PersistBase;
|
|
38302
39054
|
exports.PersistBreakevenAdapter = PersistBreakevenAdapter;
|
|
38303
39055
|
exports.PersistCandleAdapter = PersistCandleAdapter;
|
|
39056
|
+
exports.PersistLogAdapter = PersistLogAdapter;
|
|
38304
39057
|
exports.PersistNotificationAdapter = PersistNotificationAdapter;
|
|
38305
39058
|
exports.PersistPartialAdapter = PersistPartialAdapter;
|
|
38306
39059
|
exports.PersistRiskAdapter = PersistRiskAdapter;
|
|
@@ -38341,6 +39094,7 @@ exports.formatPrice = formatPrice;
|
|
|
38341
39094
|
exports.formatQuantity = formatQuantity;
|
|
38342
39095
|
exports.get = get;
|
|
38343
39096
|
exports.getActionSchema = getActionSchema;
|
|
39097
|
+
exports.getAggregatedTrades = getAggregatedTrades;
|
|
38344
39098
|
exports.getAveragePrice = getAveragePrice;
|
|
38345
39099
|
exports.getBacktestTimeframe = getBacktestTimeframe;
|
|
38346
39100
|
exports.getCandles = getCandles;
|