backtest-kit 3.5.2 → 3.7.0
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 +1 -1
- package/build/index.cjs +324 -43
- package/build/index.mjs +323 -44
- package/package.json +1 -1
- package/types.d.ts +149 -7
package/build/index.cjs
CHANGED
|
@@ -801,6 +801,11 @@ const PERSIST_LOG_UTILS_METHOD_NAME_WRITE_DATA = "PersistLogUtils.writeLogData";
|
|
|
801
801
|
const PERSIST_LOG_UTILS_METHOD_NAME_USE_JSON = "PersistLogUtils.useJson";
|
|
802
802
|
const PERSIST_LOG_UTILS_METHOD_NAME_USE_DUMMY = "PersistLogUtils.useDummy";
|
|
803
803
|
const PERSIST_LOG_UTILS_METHOD_NAME_USE_PERSIST_LOG_ADAPTER = "PersistLogUtils.usePersistLogAdapter";
|
|
804
|
+
const PERSIST_MEASURE_UTILS_METHOD_NAME_READ_DATA = "PersistMeasureUtils.readMeasureData";
|
|
805
|
+
const PERSIST_MEASURE_UTILS_METHOD_NAME_WRITE_DATA = "PersistMeasureUtils.writeMeasureData";
|
|
806
|
+
const PERSIST_MEASURE_UTILS_METHOD_NAME_USE_JSON = "PersistMeasureUtils.useJson";
|
|
807
|
+
const PERSIST_MEASURE_UTILS_METHOD_NAME_USE_DUMMY = "PersistMeasureUtils.useDummy";
|
|
808
|
+
const PERSIST_MEASURE_UTILS_METHOD_NAME_USE_PERSIST_MEASURE_ADAPTER = "PersistMeasureUtils.usePersistMeasureAdapter";
|
|
804
809
|
const BASE_WAIT_FOR_INIT_FN_METHOD_NAME = "PersistBase.waitForInitFn";
|
|
805
810
|
const BASE_UNLINK_RETRY_COUNT = 5;
|
|
806
811
|
const BASE_UNLINK_RETRY_DELAY = 1000;
|
|
@@ -2071,6 +2076,92 @@ class PersistLogUtils {
|
|
|
2071
2076
|
* Used by LogPersistUtils for log entry persistence.
|
|
2072
2077
|
*/
|
|
2073
2078
|
const PersistLogAdapter = new PersistLogUtils();
|
|
2079
|
+
/**
|
|
2080
|
+
* Utility class for managing external API response cache persistence.
|
|
2081
|
+
*
|
|
2082
|
+
* Features:
|
|
2083
|
+
* - Memoized storage instances per cache bucket (aligned timestamp + symbol)
|
|
2084
|
+
* - Custom adapter support
|
|
2085
|
+
* - Atomic read/write operations
|
|
2086
|
+
* - Crash-safe cache state management
|
|
2087
|
+
*
|
|
2088
|
+
* Used by Cache.file for persistent caching of external API responses.
|
|
2089
|
+
*/
|
|
2090
|
+
class PersistMeasureUtils {
|
|
2091
|
+
constructor() {
|
|
2092
|
+
this.PersistMeasureFactory = PersistBase;
|
|
2093
|
+
this.getMeasureStorage = functoolsKit.memoize(([bucket]) => bucket, (bucket) => Reflect.construct(this.PersistMeasureFactory, [
|
|
2094
|
+
bucket,
|
|
2095
|
+
`./dump/data/measure/`,
|
|
2096
|
+
]));
|
|
2097
|
+
/**
|
|
2098
|
+
* Reads cached measure data for a given bucket and key.
|
|
2099
|
+
*
|
|
2100
|
+
* @param bucket - Storage bucket (e.g. aligned timestamp + symbol)
|
|
2101
|
+
* @param key - Dynamic cache key within the bucket
|
|
2102
|
+
* @returns Promise resolving to cached value or null if not found
|
|
2103
|
+
*/
|
|
2104
|
+
this.readMeasureData = async (bucket, key) => {
|
|
2105
|
+
bt.loggerService.info(PERSIST_MEASURE_UTILS_METHOD_NAME_READ_DATA, {
|
|
2106
|
+
bucket,
|
|
2107
|
+
key,
|
|
2108
|
+
});
|
|
2109
|
+
const isInitial = !this.getMeasureStorage.has(bucket);
|
|
2110
|
+
const stateStorage = this.getMeasureStorage(bucket);
|
|
2111
|
+
await stateStorage.waitForInit(isInitial);
|
|
2112
|
+
if (await stateStorage.hasValue(key)) {
|
|
2113
|
+
return await stateStorage.readValue(key);
|
|
2114
|
+
}
|
|
2115
|
+
return null;
|
|
2116
|
+
};
|
|
2117
|
+
/**
|
|
2118
|
+
* Writes measure data to disk with atomic file writes.
|
|
2119
|
+
*
|
|
2120
|
+
* @param data - Data to cache
|
|
2121
|
+
* @param bucket - Storage bucket (e.g. aligned timestamp + symbol)
|
|
2122
|
+
* @param key - Dynamic cache key within the bucket
|
|
2123
|
+
* @returns Promise that resolves when write is complete
|
|
2124
|
+
*/
|
|
2125
|
+
this.writeMeasureData = async (data, bucket, key) => {
|
|
2126
|
+
bt.loggerService.info(PERSIST_MEASURE_UTILS_METHOD_NAME_WRITE_DATA, {
|
|
2127
|
+
bucket,
|
|
2128
|
+
key,
|
|
2129
|
+
});
|
|
2130
|
+
const isInitial = !this.getMeasureStorage.has(bucket);
|
|
2131
|
+
const stateStorage = this.getMeasureStorage(bucket);
|
|
2132
|
+
await stateStorage.waitForInit(isInitial);
|
|
2133
|
+
await stateStorage.writeValue(key, data);
|
|
2134
|
+
};
|
|
2135
|
+
}
|
|
2136
|
+
/**
|
|
2137
|
+
* Registers a custom persistence adapter.
|
|
2138
|
+
*
|
|
2139
|
+
* @param Ctor - Custom PersistBase constructor
|
|
2140
|
+
*/
|
|
2141
|
+
usePersistMeasureAdapter(Ctor) {
|
|
2142
|
+
bt.loggerService.info(PERSIST_MEASURE_UTILS_METHOD_NAME_USE_PERSIST_MEASURE_ADAPTER);
|
|
2143
|
+
this.PersistMeasureFactory = Ctor;
|
|
2144
|
+
}
|
|
2145
|
+
/**
|
|
2146
|
+
* Switches to the default JSON persist adapter.
|
|
2147
|
+
*/
|
|
2148
|
+
useJson() {
|
|
2149
|
+
bt.loggerService.log(PERSIST_MEASURE_UTILS_METHOD_NAME_USE_JSON);
|
|
2150
|
+
this.usePersistMeasureAdapter(PersistBase);
|
|
2151
|
+
}
|
|
2152
|
+
/**
|
|
2153
|
+
* Switches to a dummy persist adapter that discards all writes.
|
|
2154
|
+
*/
|
|
2155
|
+
useDummy() {
|
|
2156
|
+
bt.loggerService.log(PERSIST_MEASURE_UTILS_METHOD_NAME_USE_DUMMY);
|
|
2157
|
+
this.usePersistMeasureAdapter(PersistDummy);
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
/**
|
|
2161
|
+
* Global singleton instance of PersistMeasureUtils.
|
|
2162
|
+
* Used by Cache.file for persistent caching of external API responses.
|
|
2163
|
+
*/
|
|
2164
|
+
const PersistMeasureAdapter = new PersistMeasureUtils();
|
|
2074
2165
|
|
|
2075
2166
|
var _a$3, _b$3;
|
|
2076
2167
|
const BUSY_DELAY = 100;
|
|
@@ -10371,7 +10462,7 @@ class ActionBase {
|
|
|
10371
10462
|
* @example
|
|
10372
10463
|
* ```typescript
|
|
10373
10464
|
* pingScheduled(event: SchedulePingContract) {
|
|
10374
|
-
* const waitTime =
|
|
10465
|
+
* const waitTime = getTimestamp() - event.data.timestampScheduled;
|
|
10375
10466
|
* const waitMinutes = Math.floor(waitTime / 60000);
|
|
10376
10467
|
* console.log(`Scheduled signal waiting ${waitMinutes} minutes`);
|
|
10377
10468
|
* }
|
|
@@ -10400,7 +10491,7 @@ class ActionBase {
|
|
|
10400
10491
|
* @example
|
|
10401
10492
|
* ```typescript
|
|
10402
10493
|
* pingActive(event: ActivePingContract) {
|
|
10403
|
-
* const holdTime =
|
|
10494
|
+
* const holdTime = getTimestamp() - event.data.pendingAt;
|
|
10404
10495
|
* const holdMinutes = Math.floor(holdTime / 60000);
|
|
10405
10496
|
* console.log(`Active signal holding ${holdMinutes} minutes`);
|
|
10406
10497
|
* }
|
|
@@ -16102,6 +16193,20 @@ const COLUMN_CONFIG = {
|
|
|
16102
16193
|
*/
|
|
16103
16194
|
const DEFAULT_COLUMNS = Object.freeze({ ...COLUMN_CONFIG });
|
|
16104
16195
|
|
|
16196
|
+
/**
|
|
16197
|
+
* Retrieves the current timestamp based on the execution context.
|
|
16198
|
+
* If an execution context is active (e.g., during a backtest), it returns the timestamp from the context to ensure consistency with the simulated time.
|
|
16199
|
+
* If no execution context is active (e.g., during live operation), it returns the current real-world timestamp.
|
|
16200
|
+
* This function helps maintain accurate timing for logs, metrics, and other time-sensitive operations across both live and backtest modes.
|
|
16201
|
+
* @return {number} The current timestamp in milliseconds, either from the execution context or the real-world clock.
|
|
16202
|
+
*/
|
|
16203
|
+
const getContextTimestamp = () => {
|
|
16204
|
+
if (ExecutionContextService.hasContext()) {
|
|
16205
|
+
return bt.executionContextService.context.when.getTime();
|
|
16206
|
+
}
|
|
16207
|
+
return Date.now();
|
|
16208
|
+
};
|
|
16209
|
+
|
|
16105
16210
|
var _a$2, _b$2;
|
|
16106
16211
|
const MARKDOWN_METHOD_NAME_ENABLE = "MarkdownUtils.enable";
|
|
16107
16212
|
const MARKDOWN_METHOD_NAME_DISABLE = "MarkdownUtils.disable";
|
|
@@ -16235,7 +16340,7 @@ class MarkdownFileBase {
|
|
|
16235
16340
|
markdownName: this.markdownName,
|
|
16236
16341
|
data,
|
|
16237
16342
|
...searchFlags,
|
|
16238
|
-
timestamp:
|
|
16343
|
+
timestamp: getContextTimestamp(),
|
|
16239
16344
|
}) + "\n";
|
|
16240
16345
|
const status = await this[WRITE_SAFE_SYMBOL$2](line);
|
|
16241
16346
|
if (status === functoolsKit.TIMEOUT_SYMBOL) {
|
|
@@ -16779,7 +16884,7 @@ let ReportStorage$7 = class ReportStorage {
|
|
|
16779
16884
|
*/
|
|
16780
16885
|
async dump(strategyName, path = "./dump/backtest", columns = COLUMN_CONFIG.backtest_columns) {
|
|
16781
16886
|
const markdown = await this.getReport(strategyName, columns);
|
|
16782
|
-
const timestamp =
|
|
16887
|
+
const timestamp = getContextTimestamp();
|
|
16783
16888
|
const filename = CREATE_FILE_NAME_FN$9(this.symbol, strategyName, this.exchangeName, this.frameName, timestamp);
|
|
16784
16889
|
await Markdown.writeData("backtest", markdown, {
|
|
16785
16890
|
path,
|
|
@@ -17114,7 +17219,7 @@ let ReportStorage$6 = class ReportStorage {
|
|
|
17114
17219
|
*/
|
|
17115
17220
|
addIdleEvent(currentPrice) {
|
|
17116
17221
|
const newEvent = {
|
|
17117
|
-
timestamp:
|
|
17222
|
+
timestamp: getContextTimestamp(),
|
|
17118
17223
|
action: "idle",
|
|
17119
17224
|
currentPrice,
|
|
17120
17225
|
};
|
|
@@ -17170,7 +17275,7 @@ let ReportStorage$6 = class ReportStorage {
|
|
|
17170
17275
|
*/
|
|
17171
17276
|
addActiveEvent(data) {
|
|
17172
17277
|
const newEvent = {
|
|
17173
|
-
timestamp:
|
|
17278
|
+
timestamp: getContextTimestamp(),
|
|
17174
17279
|
action: "active",
|
|
17175
17280
|
symbol: data.signal.symbol,
|
|
17176
17281
|
signalId: data.signal.id,
|
|
@@ -17272,7 +17377,7 @@ let ReportStorage$6 = class ReportStorage {
|
|
|
17272
17377
|
*/
|
|
17273
17378
|
addWaitingEvent(data) {
|
|
17274
17379
|
const newEvent = {
|
|
17275
|
-
timestamp:
|
|
17380
|
+
timestamp: getContextTimestamp(),
|
|
17276
17381
|
action: "waiting",
|
|
17277
17382
|
symbol: data.signal.symbol,
|
|
17278
17383
|
signalId: data.signal.id,
|
|
@@ -17466,7 +17571,7 @@ let ReportStorage$6 = class ReportStorage {
|
|
|
17466
17571
|
*/
|
|
17467
17572
|
async dump(strategyName, path = "./dump/live", columns = COLUMN_CONFIG.live_columns) {
|
|
17468
17573
|
const markdown = await this.getReport(strategyName, columns);
|
|
17469
|
-
const timestamp =
|
|
17574
|
+
const timestamp = getContextTimestamp();
|
|
17470
17575
|
const filename = CREATE_FILE_NAME_FN$8(this.symbol, strategyName, this.exchangeName, this.frameName, timestamp);
|
|
17471
17576
|
await Markdown.writeData("live", markdown, {
|
|
17472
17577
|
path,
|
|
@@ -17998,7 +18103,7 @@ let ReportStorage$5 = class ReportStorage {
|
|
|
17998
18103
|
*/
|
|
17999
18104
|
async dump(strategyName, path = "./dump/schedule", columns = COLUMN_CONFIG.schedule_columns) {
|
|
18000
18105
|
const markdown = await this.getReport(strategyName, columns);
|
|
18001
|
-
const timestamp =
|
|
18106
|
+
const timestamp = getContextTimestamp();
|
|
18002
18107
|
const filename = CREATE_FILE_NAME_FN$7(this.symbol, strategyName, this.exchangeName, this.frameName, timestamp);
|
|
18003
18108
|
await Markdown.writeData("schedule", markdown, {
|
|
18004
18109
|
path,
|
|
@@ -18463,7 +18568,7 @@ class PerformanceStorage {
|
|
|
18463
18568
|
*/
|
|
18464
18569
|
async dump(strategyName, path = "./dump/performance", columns = COLUMN_CONFIG.performance_columns) {
|
|
18465
18570
|
const markdown = await this.getReport(strategyName, columns);
|
|
18466
|
-
const timestamp =
|
|
18571
|
+
const timestamp = getContextTimestamp();
|
|
18467
18572
|
const filename = CREATE_FILE_NAME_FN$6(this.symbol, strategyName, this.exchangeName, this.frameName, timestamp);
|
|
18468
18573
|
await Markdown.writeData("performance", markdown, {
|
|
18469
18574
|
path,
|
|
@@ -18914,7 +19019,7 @@ let ReportStorage$4 = class ReportStorage {
|
|
|
18914
19019
|
*/
|
|
18915
19020
|
async dump(symbol, metric, context, path = "./dump/walker", strategyColumns = COLUMN_CONFIG.walker_strategy_columns, pnlColumns = COLUMN_CONFIG.walker_pnl_columns) {
|
|
18916
19021
|
const markdown = await this.getReport(symbol, metric, context, strategyColumns, pnlColumns);
|
|
18917
|
-
const timestamp =
|
|
19022
|
+
const timestamp = getContextTimestamp();
|
|
18918
19023
|
const filename = CREATE_FILE_NAME_FN$5(this.walkerName, timestamp);
|
|
18919
19024
|
await Markdown.writeData("walker", markdown, {
|
|
18920
19025
|
path,
|
|
@@ -19468,7 +19573,7 @@ class HeatmapStorage {
|
|
|
19468
19573
|
*/
|
|
19469
19574
|
async dump(strategyName, path = "./dump/heatmap", columns = COLUMN_CONFIG.heat_columns) {
|
|
19470
19575
|
const markdown = await this.getReport(strategyName, columns);
|
|
19471
|
-
const timestamp =
|
|
19576
|
+
const timestamp = getContextTimestamp();
|
|
19472
19577
|
const filename = CREATE_FILE_NAME_FN$4(strategyName, this.exchangeName, this.frameName, timestamp);
|
|
19473
19578
|
await Markdown.writeData("heat", markdown, {
|
|
19474
19579
|
path,
|
|
@@ -21148,7 +21253,7 @@ let ReportStorage$3 = class ReportStorage {
|
|
|
21148
21253
|
*/
|
|
21149
21254
|
async dump(symbol, strategyName, path = "./dump/partial", columns = COLUMN_CONFIG.partial_columns) {
|
|
21150
21255
|
const markdown = await this.getReport(symbol, strategyName, columns);
|
|
21151
|
-
const timestamp =
|
|
21256
|
+
const timestamp = getContextTimestamp();
|
|
21152
21257
|
const filename = CREATE_FILE_NAME_FN$3(this.symbol, strategyName, this.exchangeName, this.frameName, timestamp);
|
|
21153
21258
|
await Markdown.writeData("partial", markdown, {
|
|
21154
21259
|
path,
|
|
@@ -22241,7 +22346,7 @@ let ReportStorage$2 = class ReportStorage {
|
|
|
22241
22346
|
*/
|
|
22242
22347
|
async dump(symbol, strategyName, path = "./dump/breakeven", columns = COLUMN_CONFIG.breakeven_columns) {
|
|
22243
22348
|
const markdown = await this.getReport(symbol, strategyName, columns);
|
|
22244
|
-
const timestamp =
|
|
22349
|
+
const timestamp = getContextTimestamp();
|
|
22245
22350
|
const filename = CREATE_FILE_NAME_FN$2(this.symbol, strategyName, this.exchangeName, this.frameName, timestamp);
|
|
22246
22351
|
await Markdown.writeData("breakeven", markdown, {
|
|
22247
22352
|
path,
|
|
@@ -22909,7 +23014,7 @@ let ReportStorage$1 = class ReportStorage {
|
|
|
22909
23014
|
*/
|
|
22910
23015
|
async dump(symbol, strategyName, path = "./dump/risk", columns = COLUMN_CONFIG.risk_columns) {
|
|
22911
23016
|
const markdown = await this.getReport(symbol, strategyName, columns);
|
|
22912
|
-
const timestamp =
|
|
23017
|
+
const timestamp = getContextTimestamp();
|
|
22913
23018
|
const filename = CREATE_FILE_NAME_FN$1(this.symbol, strategyName, this.exchangeName, this.frameName, timestamp);
|
|
22914
23019
|
await Markdown.writeData("risk", markdown, {
|
|
22915
23020
|
path,
|
|
@@ -23393,7 +23498,7 @@ class ReportBase {
|
|
|
23393
23498
|
reportName: this.reportName,
|
|
23394
23499
|
data,
|
|
23395
23500
|
...searchFlags,
|
|
23396
|
-
timestamp:
|
|
23501
|
+
timestamp: getContextTimestamp(),
|
|
23397
23502
|
}) + "\n";
|
|
23398
23503
|
const status = await this[WRITE_SAFE_SYMBOL$1](line);
|
|
23399
23504
|
if (status === functoolsKit.TIMEOUT_SYMBOL) {
|
|
@@ -23732,7 +23837,7 @@ class BacktestReportService {
|
|
|
23732
23837
|
this.tick = async (data) => {
|
|
23733
23838
|
this.loggerService.log(BACKTEST_REPORT_METHOD_NAME_TICK, { data });
|
|
23734
23839
|
const baseEvent = {
|
|
23735
|
-
timestamp:
|
|
23840
|
+
timestamp: getContextTimestamp(),
|
|
23736
23841
|
action: data.action,
|
|
23737
23842
|
symbol: data.symbol,
|
|
23738
23843
|
strategyName: data.strategyName,
|
|
@@ -23911,7 +24016,7 @@ class LiveReportService {
|
|
|
23911
24016
|
this.tick = async (data) => {
|
|
23912
24017
|
this.loggerService.log(LIVE_REPORT_METHOD_NAME_TICK, { data });
|
|
23913
24018
|
const baseEvent = {
|
|
23914
|
-
timestamp:
|
|
24019
|
+
timestamp: getContextTimestamp(),
|
|
23915
24020
|
action: data.action,
|
|
23916
24021
|
symbol: data.symbol,
|
|
23917
24022
|
strategyName: data.strategyName,
|
|
@@ -24434,7 +24539,7 @@ class WalkerReportService {
|
|
|
24434
24539
|
this.tick = async (data) => {
|
|
24435
24540
|
this.loggerService.log(WALKER_REPORT_METHOD_NAME_TICK, { data });
|
|
24436
24541
|
await Report.writeData("walker", {
|
|
24437
|
-
timestamp:
|
|
24542
|
+
timestamp: getContextTimestamp(),
|
|
24438
24543
|
walkerName: data.walkerName,
|
|
24439
24544
|
symbol: data.symbol,
|
|
24440
24545
|
exchangeName: data.exchangeName,
|
|
@@ -24561,7 +24666,7 @@ class HeatReportService {
|
|
|
24561
24666
|
return;
|
|
24562
24667
|
}
|
|
24563
24668
|
await Report.writeData("heat", {
|
|
24564
|
-
timestamp:
|
|
24669
|
+
timestamp: getContextTimestamp(),
|
|
24565
24670
|
action: data.action,
|
|
24566
24671
|
symbol: data.symbol,
|
|
24567
24672
|
strategyName: data.strategyName,
|
|
@@ -25883,7 +25988,7 @@ class ReportStorage {
|
|
|
25883
25988
|
*/
|
|
25884
25989
|
async dump(symbol, strategyName, path = "./dump/strategy", columns = COLUMN_CONFIG.strategy_columns) {
|
|
25885
25990
|
const markdown = await this.getReport(symbol, strategyName, columns);
|
|
25886
|
-
const timestamp =
|
|
25991
|
+
const timestamp = getContextTimestamp();
|
|
25887
25992
|
const filename = CREATE_FILE_NAME_FN(this.symbol, strategyName, this.exchangeName, this.frameName, timestamp);
|
|
25888
25993
|
await Markdown.writeData("strategy", markdown, {
|
|
25889
25994
|
path,
|
|
@@ -28199,6 +28304,7 @@ const GET_AVERAGE_PRICE_METHOD_NAME = "exchange.getAveragePrice";
|
|
|
28199
28304
|
const FORMAT_PRICE_METHOD_NAME = "exchange.formatPrice";
|
|
28200
28305
|
const FORMAT_QUANTITY_METHOD_NAME = "exchange.formatQuantity";
|
|
28201
28306
|
const GET_DATE_METHOD_NAME = "exchange.getDate";
|
|
28307
|
+
const GET_TIMESTAMP_METHOD_NAME = "exchange.getTimestamp";
|
|
28202
28308
|
const GET_MODE_METHOD_NAME = "exchange.getMode";
|
|
28203
28309
|
const GET_SYMBOL_METHOD_NAME = "exchange.getSymbol";
|
|
28204
28310
|
const GET_CONTEXT_METHOD_NAME = "exchange.getContext";
|
|
@@ -28364,6 +28470,26 @@ async function getDate() {
|
|
|
28364
28470
|
const { when } = bt.executionContextService.context;
|
|
28365
28471
|
return new Date(when.getTime());
|
|
28366
28472
|
}
|
|
28473
|
+
/**
|
|
28474
|
+
* Gets the current timestamp from execution context.
|
|
28475
|
+
*
|
|
28476
|
+
* In backtest mode: returns the current timeframe timestamp being processed
|
|
28477
|
+
* In live mode: returns current real-time timestamp
|
|
28478
|
+
*
|
|
28479
|
+
* @returns Promise resolving to current execution context timestamp in milliseconds
|
|
28480
|
+
* @example
|
|
28481
|
+
* ```typescript
|
|
28482
|
+
* const timestamp = await getTimestamp();
|
|
28483
|
+
* console.log(timestamp); // 1700000000000
|
|
28484
|
+
* ```
|
|
28485
|
+
*/
|
|
28486
|
+
async function getTimestamp() {
|
|
28487
|
+
bt.loggerService.info(GET_TIMESTAMP_METHOD_NAME);
|
|
28488
|
+
if (!ExecutionContextService.hasContext()) {
|
|
28489
|
+
throw new Error("getTimestamp requires an execution context");
|
|
28490
|
+
}
|
|
28491
|
+
return getContextTimestamp();
|
|
28492
|
+
}
|
|
28367
28493
|
/**
|
|
28368
28494
|
* Gets the current execution mode.
|
|
28369
28495
|
*
|
|
@@ -32561,7 +32687,6 @@ const ADD_ACTION_METHOD_NAME = "add.addActionSchema";
|
|
|
32561
32687
|
* priceTakeProfit: 51000,
|
|
32562
32688
|
* priceStopLoss: 49000,
|
|
32563
32689
|
* minuteEstimatedTime: 60,
|
|
32564
|
-
* timestamp: Date.now(),
|
|
32565
32690
|
* }),
|
|
32566
32691
|
* callbacks: {
|
|
32567
32692
|
* onOpen: (symbol, signal, currentPrice, backtest) => console.log("Signal opened"),
|
|
@@ -33488,8 +33613,8 @@ const WAIT_FOR_INIT_SYMBOL = Symbol("wait-for-init");
|
|
|
33488
33613
|
const WRITE_SAFE_SYMBOL = Symbol("write-safe");
|
|
33489
33614
|
/**
|
|
33490
33615
|
* Backtest execution time retrieval function.
|
|
33491
|
-
* Returns the 'when'
|
|
33492
|
-
* This allows log entries to be
|
|
33616
|
+
* Returns the 'when' priority from the execution context if available, otherwise returns the current time.
|
|
33617
|
+
* This allows log entries to be priorityed according to the backtest timeline rather than real-world time, improving log relevance and user experience during backtest analysis.
|
|
33493
33618
|
*/
|
|
33494
33619
|
const GET_DATE_FN = () => {
|
|
33495
33620
|
if (ExecutionContextService.hasContext()) {
|
|
@@ -33511,7 +33636,7 @@ const GET_METHOD_CONTEXT_FN = () => {
|
|
|
33511
33636
|
/**
|
|
33512
33637
|
* Execution context retrieval function.
|
|
33513
33638
|
* Returns the current execution context from ExecutionContextService if available, otherwise returns null.
|
|
33514
|
-
* This allows log entries to include contextual information about the symbol,
|
|
33639
|
+
* This allows log entries to include contextual information about the symbol, priority, and backtest mode associated with the log event, providing additional insights into the execution environment when analyzing logs.
|
|
33515
33640
|
*/
|
|
33516
33641
|
const GET_EXECUTION_CONTEXT_FN = () => {
|
|
33517
33642
|
if (ExecutionContextService.hasContext()) {
|
|
@@ -33541,7 +33666,7 @@ class LogPersistUtils {
|
|
|
33541
33666
|
this.waitForInit = functoolsKit.singleshot(async () => {
|
|
33542
33667
|
bt.loggerService.info(LOG_PERSIST_METHOD_NAME_WAIT_FOR_INIT);
|
|
33543
33668
|
const list = await PersistLogAdapter.readLogData();
|
|
33544
|
-
list.sort((a, b) => a.
|
|
33669
|
+
list.sort((a, b) => a.priority - b.priority);
|
|
33545
33670
|
this._entries = list.slice(-GLOBAL_CONFIG.CC_MAX_LOG_LINES);
|
|
33546
33671
|
});
|
|
33547
33672
|
/**
|
|
@@ -33557,7 +33682,8 @@ class LogPersistUtils {
|
|
|
33557
33682
|
this._entries.push({
|
|
33558
33683
|
id: functoolsKit.randomString(),
|
|
33559
33684
|
type: "log",
|
|
33560
|
-
|
|
33685
|
+
priority: Date.now(),
|
|
33686
|
+
timestamp: getContextTimestamp(),
|
|
33561
33687
|
createdAt: date.toISOString(),
|
|
33562
33688
|
methodContext: GET_METHOD_CONTEXT_FN(),
|
|
33563
33689
|
executionContext: GET_EXECUTION_CONTEXT_FN(),
|
|
@@ -33580,7 +33706,8 @@ class LogPersistUtils {
|
|
|
33580
33706
|
this._entries.push({
|
|
33581
33707
|
id: functoolsKit.randomString(),
|
|
33582
33708
|
type: "debug",
|
|
33583
|
-
|
|
33709
|
+
priority: Date.now(),
|
|
33710
|
+
timestamp: getContextTimestamp(),
|
|
33584
33711
|
createdAt: date.toISOString(),
|
|
33585
33712
|
methodContext: GET_METHOD_CONTEXT_FN(),
|
|
33586
33713
|
executionContext: GET_EXECUTION_CONTEXT_FN(),
|
|
@@ -33603,7 +33730,8 @@ class LogPersistUtils {
|
|
|
33603
33730
|
this._entries.push({
|
|
33604
33731
|
id: functoolsKit.randomString(),
|
|
33605
33732
|
type: "info",
|
|
33606
|
-
|
|
33733
|
+
priority: Date.now(),
|
|
33734
|
+
timestamp: getContextTimestamp(),
|
|
33607
33735
|
createdAt: date.toISOString(),
|
|
33608
33736
|
methodContext: GET_METHOD_CONTEXT_FN(),
|
|
33609
33737
|
executionContext: GET_EXECUTION_CONTEXT_FN(),
|
|
@@ -33626,7 +33754,8 @@ class LogPersistUtils {
|
|
|
33626
33754
|
this._entries.push({
|
|
33627
33755
|
id: functoolsKit.randomString(),
|
|
33628
33756
|
type: "warn",
|
|
33629
|
-
|
|
33757
|
+
priority: Date.now(),
|
|
33758
|
+
timestamp: getContextTimestamp(),
|
|
33630
33759
|
createdAt: date.toISOString(),
|
|
33631
33760
|
methodContext: GET_METHOD_CONTEXT_FN(),
|
|
33632
33761
|
executionContext: GET_EXECUTION_CONTEXT_FN(),
|
|
@@ -33682,7 +33811,8 @@ class LogMemoryUtils {
|
|
|
33682
33811
|
this._entries.push({
|
|
33683
33812
|
id: functoolsKit.randomString(),
|
|
33684
33813
|
type: "log",
|
|
33685
|
-
|
|
33814
|
+
priority: Date.now(),
|
|
33815
|
+
timestamp: getContextTimestamp(),
|
|
33686
33816
|
createdAt: date.toISOString(),
|
|
33687
33817
|
methodContext: GET_METHOD_CONTEXT_FN(),
|
|
33688
33818
|
executionContext: GET_EXECUTION_CONTEXT_FN(),
|
|
@@ -33703,7 +33833,8 @@ class LogMemoryUtils {
|
|
|
33703
33833
|
this._entries.push({
|
|
33704
33834
|
id: functoolsKit.randomString(),
|
|
33705
33835
|
type: "debug",
|
|
33706
|
-
|
|
33836
|
+
priority: Date.now(),
|
|
33837
|
+
timestamp: getContextTimestamp(),
|
|
33707
33838
|
createdAt: date.toISOString(),
|
|
33708
33839
|
methodContext: GET_METHOD_CONTEXT_FN(),
|
|
33709
33840
|
executionContext: GET_EXECUTION_CONTEXT_FN(),
|
|
@@ -33724,7 +33855,8 @@ class LogMemoryUtils {
|
|
|
33724
33855
|
this._entries.push({
|
|
33725
33856
|
id: functoolsKit.randomString(),
|
|
33726
33857
|
type: "info",
|
|
33727
|
-
|
|
33858
|
+
priority: Date.now(),
|
|
33859
|
+
timestamp: getContextTimestamp(),
|
|
33728
33860
|
createdAt: date.toISOString(),
|
|
33729
33861
|
methodContext: GET_METHOD_CONTEXT_FN(),
|
|
33730
33862
|
executionContext: GET_EXECUTION_CONTEXT_FN(),
|
|
@@ -33745,7 +33877,8 @@ class LogMemoryUtils {
|
|
|
33745
33877
|
this._entries.push({
|
|
33746
33878
|
id: functoolsKit.randomString(),
|
|
33747
33879
|
type: "warn",
|
|
33748
|
-
|
|
33880
|
+
priority: Date.now(),
|
|
33881
|
+
timestamp: getContextTimestamp(),
|
|
33749
33882
|
createdAt: date.toISOString(),
|
|
33750
33883
|
methodContext: GET_METHOD_CONTEXT_FN(),
|
|
33751
33884
|
executionContext: GET_EXECUTION_CONTEXT_FN(),
|
|
@@ -33859,7 +33992,8 @@ class LogJsonlUtils {
|
|
|
33859
33992
|
await this._append({
|
|
33860
33993
|
id: functoolsKit.randomString(),
|
|
33861
33994
|
type: "log",
|
|
33862
|
-
|
|
33995
|
+
priority: Date.now(),
|
|
33996
|
+
timestamp: getContextTimestamp(),
|
|
33863
33997
|
createdAt: date.toISOString(),
|
|
33864
33998
|
methodContext: GET_METHOD_CONTEXT_FN(),
|
|
33865
33999
|
executionContext: GET_EXECUTION_CONTEXT_FN(),
|
|
@@ -33878,7 +34012,8 @@ class LogJsonlUtils {
|
|
|
33878
34012
|
await this._append({
|
|
33879
34013
|
id: functoolsKit.randomString(),
|
|
33880
34014
|
type: "debug",
|
|
33881
|
-
|
|
34015
|
+
priority: Date.now(),
|
|
34016
|
+
timestamp: getContextTimestamp(),
|
|
33882
34017
|
createdAt: date.toISOString(),
|
|
33883
34018
|
methodContext: GET_METHOD_CONTEXT_FN(),
|
|
33884
34019
|
executionContext: GET_EXECUTION_CONTEXT_FN(),
|
|
@@ -33897,7 +34032,8 @@ class LogJsonlUtils {
|
|
|
33897
34032
|
await this._append({
|
|
33898
34033
|
id: functoolsKit.randomString(),
|
|
33899
34034
|
type: "info",
|
|
33900
|
-
|
|
34035
|
+
priority: Date.now(),
|
|
34036
|
+
timestamp: getContextTimestamp(),
|
|
33901
34037
|
createdAt: date.toISOString(),
|
|
33902
34038
|
methodContext: GET_METHOD_CONTEXT_FN(),
|
|
33903
34039
|
executionContext: GET_EXECUTION_CONTEXT_FN(),
|
|
@@ -33916,7 +34052,8 @@ class LogJsonlUtils {
|
|
|
33916
34052
|
await this._append({
|
|
33917
34053
|
id: functoolsKit.randomString(),
|
|
33918
34054
|
type: "warn",
|
|
33919
|
-
|
|
34055
|
+
priority: Date.now(),
|
|
34056
|
+
timestamp: getContextTimestamp(),
|
|
33920
34057
|
createdAt: date.toISOString(),
|
|
33921
34058
|
methodContext: GET_METHOD_CONTEXT_FN(),
|
|
33922
34059
|
executionContext: GET_EXECUTION_CONTEXT_FN(),
|
|
@@ -38343,6 +38480,8 @@ const CACHE_METHOD_NAME_CLEAR = "CacheInstance.clear";
|
|
|
38343
38480
|
const CACHE_METHOD_NAME_RUN = "CacheInstance.run";
|
|
38344
38481
|
const CACHE_METHOD_NAME_GC = "CacheInstance.gc";
|
|
38345
38482
|
const CACHE_METHOD_NAME_FN = "CacheUtils.fn";
|
|
38483
|
+
const CACHE_METHOD_NAME_FILE = "CacheUtils.file";
|
|
38484
|
+
const CACHE_FILE_INSTANCE_METHOD_NAME_RUN = "CacheFileInstance.run";
|
|
38346
38485
|
const MS_PER_MINUTE$1 = 60000;
|
|
38347
38486
|
const INTERVAL_MINUTES$1 = {
|
|
38348
38487
|
"1m": 1,
|
|
@@ -38564,6 +38703,95 @@ class CacheInstance {
|
|
|
38564
38703
|
};
|
|
38565
38704
|
}
|
|
38566
38705
|
}
|
|
38706
|
+
/**
|
|
38707
|
+
* Instance class for caching async function results in persistent file storage.
|
|
38708
|
+
*
|
|
38709
|
+
* Provides automatic cache invalidation based on candle intervals.
|
|
38710
|
+
* Cache key = `${alignedTimestamp}_${symbol}` (bucket) + dynamic key (file within bucket).
|
|
38711
|
+
* On cache hit reads from disk, on miss calls the function and writes the result.
|
|
38712
|
+
*
|
|
38713
|
+
* @template T - Async function type to cache
|
|
38714
|
+
* @template K - Dynamic key type
|
|
38715
|
+
*
|
|
38716
|
+
* @example
|
|
38717
|
+
* ```typescript
|
|
38718
|
+
* const instance = new CacheFileInstance(fetchFromApi, "1h");
|
|
38719
|
+
* const result = await instance.run("BTCUSDT", extraArg);
|
|
38720
|
+
* ```
|
|
38721
|
+
*/
|
|
38722
|
+
class CacheFileInstance {
|
|
38723
|
+
/**
|
|
38724
|
+
* Allocates a new unique index. Called once in the constructor to give each
|
|
38725
|
+
* CacheFileInstance its own namespace in the persistent key space.
|
|
38726
|
+
*/
|
|
38727
|
+
static createIndex() {
|
|
38728
|
+
return CacheFileInstance._indexCounter++;
|
|
38729
|
+
}
|
|
38730
|
+
/**
|
|
38731
|
+
* Creates a new CacheFileInstance.
|
|
38732
|
+
*
|
|
38733
|
+
* @param fn - Async function to cache
|
|
38734
|
+
* @param interval - Candle interval for cache invalidation
|
|
38735
|
+
* @param name - Human-readable bucket name used as the directory key (replaces symbol in bucket path)
|
|
38736
|
+
* @param key - Dynamic key generator; receives all args, must return a string.
|
|
38737
|
+
* Default: `([symbol, alignMs]) => \`${symbol}_${alignMs}\``
|
|
38738
|
+
*/
|
|
38739
|
+
constructor(fn, interval, name, key = ([symbol, alignMs]) => `${symbol}_${alignMs}`) {
|
|
38740
|
+
this.fn = fn;
|
|
38741
|
+
this.interval = interval;
|
|
38742
|
+
this.name = name;
|
|
38743
|
+
this.key = key;
|
|
38744
|
+
/**
|
|
38745
|
+
* Execute async function with persistent file caching.
|
|
38746
|
+
*
|
|
38747
|
+
* Algorithm:
|
|
38748
|
+
* 1. Build bucket = `${name}_${interval}_${index}` — fixed per instance, used as directory name
|
|
38749
|
+
* 2. Align execution context `when` to interval boundary → `alignedTs`
|
|
38750
|
+
* 3. Build entity key from the key generator (receives `[symbol, alignedTs, ...rest]`)
|
|
38751
|
+
* 4. Try to read from PersistMeasureAdapter using (bucket, entityKey)
|
|
38752
|
+
* 5. On hit — return cached value
|
|
38753
|
+
* 6. On miss — call fn, write result to disk, return result
|
|
38754
|
+
*
|
|
38755
|
+
* Cache invalidation happens through the entity key: the default key embeds `alignedTs`,
|
|
38756
|
+
* so each new candle interval produces a new file name while the bucket directory stays the same.
|
|
38757
|
+
*
|
|
38758
|
+
* Requires active execution context (symbol, when) and method context.
|
|
38759
|
+
*
|
|
38760
|
+
* @param args - Arguments forwarded to the wrapped function
|
|
38761
|
+
* @returns Cached or freshly computed result
|
|
38762
|
+
*/
|
|
38763
|
+
this.run = async (...args) => {
|
|
38764
|
+
bt.loggerService.debug(CACHE_FILE_INSTANCE_METHOD_NAME_RUN, { args });
|
|
38765
|
+
const step = INTERVAL_MINUTES$1[this.interval];
|
|
38766
|
+
{
|
|
38767
|
+
if (!MethodContextService.hasContext()) {
|
|
38768
|
+
throw new Error("CacheFileInstance run requires method context");
|
|
38769
|
+
}
|
|
38770
|
+
if (!ExecutionContextService.hasContext()) {
|
|
38771
|
+
throw new Error("CacheFileInstance run requires execution context");
|
|
38772
|
+
}
|
|
38773
|
+
if (!step) {
|
|
38774
|
+
throw new Error(`CacheFileInstance unknown cache ttl interval=${this.interval}`);
|
|
38775
|
+
}
|
|
38776
|
+
}
|
|
38777
|
+
const [symbol, ...rest] = args;
|
|
38778
|
+
const { when } = bt.executionContextService.context;
|
|
38779
|
+
const alignedTs = align(when.getTime(), this.interval);
|
|
38780
|
+
const bucket = `${this.name}_${this.interval}_${this.index}`;
|
|
38781
|
+
const entityKey = this.key([symbol, alignedTs, ...rest]);
|
|
38782
|
+
const cached = await PersistMeasureAdapter.readMeasureData(bucket, entityKey);
|
|
38783
|
+
if (cached !== null) {
|
|
38784
|
+
return cached;
|
|
38785
|
+
}
|
|
38786
|
+
const result = await this.fn.call(null, ...args);
|
|
38787
|
+
await PersistMeasureAdapter.writeMeasureData(result, bucket, entityKey);
|
|
38788
|
+
return result;
|
|
38789
|
+
};
|
|
38790
|
+
this.index = CacheFileInstance.createIndex();
|
|
38791
|
+
}
|
|
38792
|
+
}
|
|
38793
|
+
/** Global counter — incremented once per CacheFileInstance construction */
|
|
38794
|
+
CacheFileInstance._indexCounter = 0;
|
|
38567
38795
|
/**
|
|
38568
38796
|
* Utility class for function caching with timeframe-based invalidation.
|
|
38569
38797
|
*
|
|
@@ -38585,7 +38813,12 @@ class CacheUtils {
|
|
|
38585
38813
|
* Memoized function to get or create CacheInstance for a function.
|
|
38586
38814
|
* Each function gets its own isolated cache instance.
|
|
38587
38815
|
*/
|
|
38588
|
-
this.
|
|
38816
|
+
this._getFnInstance = functoolsKit.memoize(([run]) => run, (run, interval, key) => new CacheInstance(run, interval, key));
|
|
38817
|
+
/**
|
|
38818
|
+
* Memoized function to get or create CacheFileInstance for an async function.
|
|
38819
|
+
* Each function gets its own isolated file-cache instance.
|
|
38820
|
+
*/
|
|
38821
|
+
this._getFileInstance = functoolsKit.memoize(([run]) => run, (run, interval, name, key) => new CacheFileInstance(run, interval, name, key));
|
|
38589
38822
|
/**
|
|
38590
38823
|
* Wrap a function with caching based on timeframe intervals.
|
|
38591
38824
|
*
|
|
@@ -38624,11 +38857,57 @@ class CacheUtils {
|
|
|
38624
38857
|
context,
|
|
38625
38858
|
});
|
|
38626
38859
|
const wrappedFn = (...args) => {
|
|
38627
|
-
const instance = this.
|
|
38860
|
+
const instance = this._getFnInstance(run, context.interval, context.key);
|
|
38628
38861
|
return instance.run(...args).value;
|
|
38629
38862
|
};
|
|
38630
38863
|
return wrappedFn;
|
|
38631
38864
|
};
|
|
38865
|
+
/**
|
|
38866
|
+
* Wrap an async function with persistent file-based caching.
|
|
38867
|
+
*
|
|
38868
|
+
* Returns a wrapped version of the function that reads from disk on cache hit
|
|
38869
|
+
* and writes the result to disk on cache miss. Files are stored under
|
|
38870
|
+
* `./dump/data/measure/{name}_{interval}_{index}/`.
|
|
38871
|
+
*
|
|
38872
|
+
* The `run` function reference is used as the memoization key for the underlying
|
|
38873
|
+
* `CacheFileInstance`, so each unique function reference gets its own isolated instance.
|
|
38874
|
+
* Pass the same function reference each time to reuse the same cache.
|
|
38875
|
+
*
|
|
38876
|
+
* @template T - Async function type to cache
|
|
38877
|
+
* @param run - Async function to wrap with file caching
|
|
38878
|
+
* @param context.interval - Candle interval for cache invalidation
|
|
38879
|
+
* @param context.name - Human-readable bucket name; becomes the directory prefix
|
|
38880
|
+
* @param context.key - Optional entity key generator. Receives `[symbol, alignMs, ...rest]`
|
|
38881
|
+
* where `alignMs` is the timestamp aligned to `interval`.
|
|
38882
|
+
* Default: `([symbol, alignMs]) => \`${symbol}_${alignMs}\``
|
|
38883
|
+
* @returns Wrapped async function with automatic persistent caching
|
|
38884
|
+
*
|
|
38885
|
+
* @example
|
|
38886
|
+
* ```typescript
|
|
38887
|
+
* const fetchData = async (symbol: string, period: number) => {
|
|
38888
|
+
* return await externalApi.fetch(symbol, period);
|
|
38889
|
+
* };
|
|
38890
|
+
*
|
|
38891
|
+
* // Default key — one cache file per symbol per aligned candle
|
|
38892
|
+
* const cachedFetch = Cache.file(fetchData, { interval: "1h", name: "fetchData" });
|
|
38893
|
+
*
|
|
38894
|
+
* // Custom key — one cache file per symbol + period combination
|
|
38895
|
+
* const cachedFetch = Cache.file(fetchData, {
|
|
38896
|
+
* interval: "1h",
|
|
38897
|
+
* name: "fetchData",
|
|
38898
|
+
* key: ([symbol, alignMs, period]) => `${symbol}_${alignMs}_${period}`,
|
|
38899
|
+
* });
|
|
38900
|
+
* const result = await cachedFetch("BTCUSDT", 14);
|
|
38901
|
+
* ```
|
|
38902
|
+
*/
|
|
38903
|
+
this.file = (run, context) => {
|
|
38904
|
+
bt.loggerService.info(CACHE_METHOD_NAME_FILE, { context });
|
|
38905
|
+
const wrappedFn = (...args) => {
|
|
38906
|
+
const instance = this._getFileInstance(run, context.interval, context.name, context.key);
|
|
38907
|
+
return instance.run(...args);
|
|
38908
|
+
};
|
|
38909
|
+
return wrappedFn;
|
|
38910
|
+
};
|
|
38632
38911
|
/**
|
|
38633
38912
|
* Flush (remove) cached CacheInstance for a specific function or all functions.
|
|
38634
38913
|
*
|
|
@@ -38662,7 +38941,7 @@ class CacheUtils {
|
|
|
38662
38941
|
bt.loggerService.info(CACHE_METHOD_NAME_FLUSH, {
|
|
38663
38942
|
run,
|
|
38664
38943
|
});
|
|
38665
|
-
this.
|
|
38944
|
+
this._getFnInstance.clear(run);
|
|
38666
38945
|
};
|
|
38667
38946
|
/**
|
|
38668
38947
|
* Clear cached value for current execution context of a specific function.
|
|
@@ -38705,7 +38984,7 @@ class CacheUtils {
|
|
|
38705
38984
|
console.warn(`${CACHE_METHOD_NAME_CLEAR} called without execution context, skipping clear`);
|
|
38706
38985
|
return;
|
|
38707
38986
|
}
|
|
38708
|
-
this.
|
|
38987
|
+
this._getFnInstance.get(run).clear();
|
|
38709
38988
|
};
|
|
38710
38989
|
/**
|
|
38711
38990
|
* Garbage collect expired cache entries for a specific function.
|
|
@@ -38737,7 +39016,7 @@ class CacheUtils {
|
|
|
38737
39016
|
console.warn(`${CACHE_METHOD_NAME_GC} called without execution context, skipping garbage collection`);
|
|
38738
39017
|
return;
|
|
38739
39018
|
}
|
|
38740
|
-
return this.
|
|
39019
|
+
return this._getFnInstance.get(run).gc();
|
|
38741
39020
|
};
|
|
38742
39021
|
}
|
|
38743
39022
|
}
|
|
@@ -39330,6 +39609,7 @@ exports.PersistBase = PersistBase;
|
|
|
39330
39609
|
exports.PersistBreakevenAdapter = PersistBreakevenAdapter;
|
|
39331
39610
|
exports.PersistCandleAdapter = PersistCandleAdapter;
|
|
39332
39611
|
exports.PersistLogAdapter = PersistLogAdapter;
|
|
39612
|
+
exports.PersistMeasureAdapter = PersistMeasureAdapter;
|
|
39333
39613
|
exports.PersistNotificationAdapter = PersistNotificationAdapter;
|
|
39334
39614
|
exports.PersistPartialAdapter = PersistPartialAdapter;
|
|
39335
39615
|
exports.PersistRiskAdapter = PersistRiskAdapter;
|
|
@@ -39390,6 +39670,7 @@ exports.getRiskSchema = getRiskSchema;
|
|
|
39390
39670
|
exports.getSizingSchema = getSizingSchema;
|
|
39391
39671
|
exports.getStrategySchema = getStrategySchema;
|
|
39392
39672
|
exports.getSymbol = getSymbol;
|
|
39673
|
+
exports.getTimestamp = getTimestamp;
|
|
39393
39674
|
exports.getWalkerSchema = getWalkerSchema;
|
|
39394
39675
|
exports.hasTradeContext = hasTradeContext;
|
|
39395
39676
|
exports.lib = backtest;
|