backtest-kit 9.0.0 → 9.0.1
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/build/index.cjs +429 -208
- package/build/index.mjs +429 -209
- package/package.json +1 -1
- package/types.d.ts +144 -45
package/build/index.mjs
CHANGED
|
@@ -918,7 +918,7 @@ const LOGGER_SERVICE$7 = new LoggerService();
|
|
|
918
918
|
/** Symbol key for the singleshot waitForInit function on PersistBase instances. */
|
|
919
919
|
const BASE_WAIT_FOR_INIT_SYMBOL = Symbol("wait-for-init");
|
|
920
920
|
// Calculate step in milliseconds for candle close time validation
|
|
921
|
-
const INTERVAL_MINUTES$
|
|
921
|
+
const INTERVAL_MINUTES$a = {
|
|
922
922
|
"1m": 1,
|
|
923
923
|
"3m": 3,
|
|
924
924
|
"5m": 5,
|
|
@@ -931,7 +931,7 @@ const INTERVAL_MINUTES$9 = {
|
|
|
931
931
|
"8h": 480,
|
|
932
932
|
"1d": 1440,
|
|
933
933
|
};
|
|
934
|
-
const MS_PER_MINUTE$
|
|
934
|
+
const MS_PER_MINUTE$8 = 60000;
|
|
935
935
|
const PERSIST_SIGNAL_UTILS_METHOD_NAME_USE_PERSIST_SIGNAL_ADAPTER = "PersistSignalUtils.usePersistSignalAdapter";
|
|
936
936
|
const PERSIST_SIGNAL_UTILS_METHOD_NAME_READ_DATA = "PersistSignalUtils.readSignalData";
|
|
937
937
|
const PERSIST_SIGNAL_UTILS_METHOD_NAME_WRITE_DATA = "PersistSignalUtils.writeSignalData";
|
|
@@ -2256,7 +2256,7 @@ class PersistCandleInstance {
|
|
|
2256
2256
|
* @returns Promise resolving to candles in order, or null on cache miss
|
|
2257
2257
|
*/
|
|
2258
2258
|
async readCandlesData(limit, sinceTimestamp, _untilTimestamp) {
|
|
2259
|
-
const stepMs = INTERVAL_MINUTES$
|
|
2259
|
+
const stepMs = INTERVAL_MINUTES$a[this.interval] * MS_PER_MINUTE$8;
|
|
2260
2260
|
const cachedCandles = [];
|
|
2261
2261
|
for (let i = 0; i < limit; i++) {
|
|
2262
2262
|
const expectedTimestamp = sinceTimestamp + i * stepMs;
|
|
@@ -2291,7 +2291,7 @@ class PersistCandleInstance {
|
|
|
2291
2291
|
* @returns Promise that resolves when all writes are complete
|
|
2292
2292
|
*/
|
|
2293
2293
|
async writeCandlesData(candles) {
|
|
2294
|
-
const stepMs = INTERVAL_MINUTES$
|
|
2294
|
+
const stepMs = INTERVAL_MINUTES$a[this.interval] * MS_PER_MINUTE$8;
|
|
2295
2295
|
const now = Date.now();
|
|
2296
2296
|
for (const candle of candles) {
|
|
2297
2297
|
const candleCloseTime = candle.timestamp + stepMs;
|
|
@@ -3177,12 +3177,12 @@ class PersistMeasureUtils {
|
|
|
3177
3177
|
* @param key - Cache key within the bucket
|
|
3178
3178
|
* @returns Promise that resolves when write is complete
|
|
3179
3179
|
*/
|
|
3180
|
-
this.writeMeasureData = async (data, bucket, key) => {
|
|
3180
|
+
this.writeMeasureData = async (data, bucket, key, when) => {
|
|
3181
3181
|
LOGGER_SERVICE$7.info(PERSIST_MEASURE_UTILS_METHOD_NAME_WRITE_DATA, { bucket, key });
|
|
3182
3182
|
const isInitial = !this.getMeasureStorage.has(bucket);
|
|
3183
3183
|
const instance = this.getMeasureStorage(bucket);
|
|
3184
3184
|
await instance.waitForInit(isInitial);
|
|
3185
|
-
return instance.writeMeasureData(data, key);
|
|
3185
|
+
return instance.writeMeasureData(data, key, when);
|
|
3186
3186
|
};
|
|
3187
3187
|
/**
|
|
3188
3188
|
* Soft-deletes a measure entry in the given bucket by setting `removed: true`.
|
|
@@ -3418,12 +3418,12 @@ class PersistIntervalUtils {
|
|
|
3418
3418
|
* @param key - Marker key within the bucket
|
|
3419
3419
|
* @returns Promise that resolves when write is complete
|
|
3420
3420
|
*/
|
|
3421
|
-
this.writeIntervalData = async (data, bucket, key) => {
|
|
3421
|
+
this.writeIntervalData = async (data, bucket, key, when) => {
|
|
3422
3422
|
LOGGER_SERVICE$7.info(PERSIST_INTERVAL_UTILS_METHOD_NAME_WRITE_DATA, { bucket, key });
|
|
3423
3423
|
const isInitial = !this.getIntervalStorage.has(bucket);
|
|
3424
3424
|
const instance = this.getIntervalStorage(bucket);
|
|
3425
3425
|
await instance.waitForInit(isInitial);
|
|
3426
|
-
return instance.writeIntervalData(data, key);
|
|
3426
|
+
return instance.writeIntervalData(data, key, when);
|
|
3427
3427
|
};
|
|
3428
3428
|
/**
|
|
3429
3429
|
* Soft-deletes a marker in the given bucket by setting `removed: true`.
|
|
@@ -4609,8 +4609,8 @@ class CandleUtils {
|
|
|
4609
4609
|
}
|
|
4610
4610
|
const Candle = new CandleUtils();
|
|
4611
4611
|
|
|
4612
|
-
const MS_PER_MINUTE$
|
|
4613
|
-
const INTERVAL_MINUTES$
|
|
4612
|
+
const MS_PER_MINUTE$7 = 60000;
|
|
4613
|
+
const INTERVAL_MINUTES$9 = {
|
|
4614
4614
|
"1m": 1,
|
|
4615
4615
|
"3m": 3,
|
|
4616
4616
|
"5m": 5,
|
|
@@ -4641,7 +4641,7 @@ const INTERVAL_MINUTES$8 = {
|
|
|
4641
4641
|
* @returns Aligned timestamp rounded down to interval boundary
|
|
4642
4642
|
*/
|
|
4643
4643
|
const ALIGN_TO_INTERVAL_FN$2 = (timestamp, intervalMinutes) => {
|
|
4644
|
-
const intervalMs = intervalMinutes * MS_PER_MINUTE$
|
|
4644
|
+
const intervalMs = intervalMinutes * MS_PER_MINUTE$7;
|
|
4645
4645
|
return Math.floor(timestamp / intervalMs) * intervalMs;
|
|
4646
4646
|
};
|
|
4647
4647
|
/**
|
|
@@ -4795,9 +4795,9 @@ const WRITE_CANDLES_CACHE_FN$1 = trycatch(queued(async (candles, dto, self) => {
|
|
|
4795
4795
|
* @returns Promise resolving to array of candle data
|
|
4796
4796
|
*/
|
|
4797
4797
|
const GET_CANDLES_FN$1 = async (dto, since, self) => {
|
|
4798
|
-
const step = INTERVAL_MINUTES$
|
|
4798
|
+
const step = INTERVAL_MINUTES$9[dto.interval];
|
|
4799
4799
|
const sinceTimestamp = since.getTime();
|
|
4800
|
-
const untilTimestamp = sinceTimestamp + dto.limit * step * MS_PER_MINUTE$
|
|
4800
|
+
const untilTimestamp = sinceTimestamp + dto.limit * step * MS_PER_MINUTE$7;
|
|
4801
4801
|
await Candle.acquireLock(`ClientExchange GET_CANDLES_FN symbol=${dto.symbol} interval=${dto.interval} limit=${dto.limit}`);
|
|
4802
4802
|
try {
|
|
4803
4803
|
// Try to read from cache first
|
|
@@ -4911,11 +4911,11 @@ class ClientExchange {
|
|
|
4911
4911
|
interval,
|
|
4912
4912
|
limit,
|
|
4913
4913
|
});
|
|
4914
|
-
const step = INTERVAL_MINUTES$
|
|
4914
|
+
const step = INTERVAL_MINUTES$9[interval];
|
|
4915
4915
|
if (!step) {
|
|
4916
4916
|
throw new Error(`ClientExchange unknown interval=${interval}`);
|
|
4917
4917
|
}
|
|
4918
|
-
const stepMs = step * MS_PER_MINUTE$
|
|
4918
|
+
const stepMs = step * MS_PER_MINUTE$7;
|
|
4919
4919
|
// Align when down to interval boundary
|
|
4920
4920
|
const whenTimestamp = this.params.execution.context.when.getTime();
|
|
4921
4921
|
const alignedWhen = ALIGN_TO_INTERVAL_FN$2(whenTimestamp, step);
|
|
@@ -4992,11 +4992,11 @@ class ClientExchange {
|
|
|
4992
4992
|
if (!this.params.execution.context.backtest) {
|
|
4993
4993
|
throw new Error(`ClientExchange getNextCandles: cannot fetch future candles in live mode`);
|
|
4994
4994
|
}
|
|
4995
|
-
const step = INTERVAL_MINUTES$
|
|
4995
|
+
const step = INTERVAL_MINUTES$9[interval];
|
|
4996
4996
|
if (!step) {
|
|
4997
4997
|
throw new Error(`ClientExchange getNextCandles: unknown interval=${interval}`);
|
|
4998
4998
|
}
|
|
4999
|
-
const stepMs = step * MS_PER_MINUTE$
|
|
4999
|
+
const stepMs = step * MS_PER_MINUTE$7;
|
|
5000
5000
|
const now = Date.now();
|
|
5001
5001
|
// Align when down to interval boundary
|
|
5002
5002
|
const whenTimestamp = this.params.execution.context.when.getTime();
|
|
@@ -5181,11 +5181,11 @@ class ClientExchange {
|
|
|
5181
5181
|
sDate,
|
|
5182
5182
|
eDate,
|
|
5183
5183
|
});
|
|
5184
|
-
const step = INTERVAL_MINUTES$
|
|
5184
|
+
const step = INTERVAL_MINUTES$9[interval];
|
|
5185
5185
|
if (!step) {
|
|
5186
5186
|
throw new Error(`ClientExchange getRawCandles: unknown interval=${interval}`);
|
|
5187
5187
|
}
|
|
5188
|
-
const stepMs = step * MS_PER_MINUTE$
|
|
5188
|
+
const stepMs = step * MS_PER_MINUTE$7;
|
|
5189
5189
|
const whenTimestamp = this.params.execution.context.when.getTime();
|
|
5190
5190
|
const alignedWhen = ALIGN_TO_INTERVAL_FN$2(whenTimestamp, step);
|
|
5191
5191
|
let sinceTimestamp;
|
|
@@ -5314,7 +5314,7 @@ class ClientExchange {
|
|
|
5314
5314
|
const alignedTo = ALIGN_TO_INTERVAL_FN$2(whenTimestamp, GLOBAL_CONFIG.CC_ORDER_BOOK_TIME_OFFSET_MINUTES);
|
|
5315
5315
|
const to = new Date(alignedTo);
|
|
5316
5316
|
const from = new Date(alignedTo -
|
|
5317
|
-
GLOBAL_CONFIG.CC_ORDER_BOOK_TIME_OFFSET_MINUTES * MS_PER_MINUTE$
|
|
5317
|
+
GLOBAL_CONFIG.CC_ORDER_BOOK_TIME_OFFSET_MINUTES * MS_PER_MINUTE$7);
|
|
5318
5318
|
return await this.params.getOrderBook(symbol, depth, from, to, this.params.execution.context.backtest);
|
|
5319
5319
|
}
|
|
5320
5320
|
/**
|
|
@@ -5343,7 +5343,7 @@ class ClientExchange {
|
|
|
5343
5343
|
const whenTimestamp = this.params.execution.context.when.getTime();
|
|
5344
5344
|
// Align to 1-minute boundary to prevent look-ahead bias
|
|
5345
5345
|
const alignedTo = ALIGN_TO_INTERVAL_FN$2(whenTimestamp, 1);
|
|
5346
|
-
const windowMs = GLOBAL_CONFIG.CC_AGGREGATED_TRADES_MAX_MINUTES * MS_PER_MINUTE$
|
|
5346
|
+
const windowMs = GLOBAL_CONFIG.CC_AGGREGATED_TRADES_MAX_MINUTES * MS_PER_MINUTE$7 - MS_PER_MINUTE$7;
|
|
5347
5347
|
// No limit: fetch a single window and return as-is
|
|
5348
5348
|
if (limit === undefined) {
|
|
5349
5349
|
const to = new Date(alignedTo);
|
|
@@ -6297,7 +6297,7 @@ const validateScheduledSignal = (signal, currentPrice) => {
|
|
|
6297
6297
|
}
|
|
6298
6298
|
};
|
|
6299
6299
|
|
|
6300
|
-
const INTERVAL_MINUTES$
|
|
6300
|
+
const INTERVAL_MINUTES$8 = {
|
|
6301
6301
|
"1m": 1,
|
|
6302
6302
|
"3m": 3,
|
|
6303
6303
|
"5m": 5,
|
|
@@ -6718,7 +6718,7 @@ const GET_SIGNAL_FN = trycatch(async (self) => {
|
|
|
6718
6718
|
}
|
|
6719
6719
|
const currentTime = self.params.execution.context.when.getTime();
|
|
6720
6720
|
{
|
|
6721
|
-
const intervalMinutes = INTERVAL_MINUTES$
|
|
6721
|
+
const intervalMinutes = INTERVAL_MINUTES$8[self.params.interval];
|
|
6722
6722
|
const intervalMs = intervalMinutes * 60 * 1000;
|
|
6723
6723
|
const alignedTime = Math.floor(currentTime / intervalMs) * intervalMs;
|
|
6724
6724
|
// Проверяем что наступил новый интервал (по aligned timestamp)
|
|
@@ -13713,7 +13713,7 @@ class StrategyConnectionService {
|
|
|
13713
13713
|
* Maps FrameInterval to minutes for timestamp calculation.
|
|
13714
13714
|
* Used to generate timeframe arrays with proper spacing.
|
|
13715
13715
|
*/
|
|
13716
|
-
const INTERVAL_MINUTES$
|
|
13716
|
+
const INTERVAL_MINUTES$7 = {
|
|
13717
13717
|
"1m": 1,
|
|
13718
13718
|
"3m": 3,
|
|
13719
13719
|
"5m": 5,
|
|
@@ -13767,7 +13767,7 @@ const GET_TIMEFRAME_FN = async (symbol, self) => {
|
|
|
13767
13767
|
symbol,
|
|
13768
13768
|
});
|
|
13769
13769
|
const { interval, startDate, endDate } = self.params;
|
|
13770
|
-
const intervalMinutes = INTERVAL_MINUTES$
|
|
13770
|
+
const intervalMinutes = INTERVAL_MINUTES$7[interval];
|
|
13771
13771
|
if (!intervalMinutes) {
|
|
13772
13772
|
throw new Error(`ClientFrame unknown interval: ${interval}`);
|
|
13773
13773
|
}
|
|
@@ -14132,8 +14132,8 @@ const get = (object, path) => {
|
|
|
14132
14132
|
return pathArrayFlat.reduce((obj, key) => obj && obj[key], object);
|
|
14133
14133
|
};
|
|
14134
14134
|
|
|
14135
|
-
const MS_PER_MINUTE$
|
|
14136
|
-
const INTERVAL_MINUTES$
|
|
14135
|
+
const MS_PER_MINUTE$6 = 60000;
|
|
14136
|
+
const INTERVAL_MINUTES$6 = {
|
|
14137
14137
|
"1m": 1,
|
|
14138
14138
|
"3m": 3,
|
|
14139
14139
|
"5m": 5,
|
|
@@ -14164,11 +14164,11 @@ const INTERVAL_MINUTES$5 = {
|
|
|
14164
14164
|
* @returns New Date aligned down to interval boundary
|
|
14165
14165
|
*/
|
|
14166
14166
|
const alignToInterval = (date, interval) => {
|
|
14167
|
-
const minutes = INTERVAL_MINUTES$
|
|
14167
|
+
const minutes = INTERVAL_MINUTES$6[interval];
|
|
14168
14168
|
if (minutes === undefined) {
|
|
14169
14169
|
throw new Error(`alignToInterval: unknown interval=${interval}`);
|
|
14170
14170
|
}
|
|
14171
|
-
const intervalMs = minutes * MS_PER_MINUTE$
|
|
14171
|
+
const intervalMs = minutes * MS_PER_MINUTE$6;
|
|
14172
14172
|
return new Date(Math.floor(date.getTime() / intervalMs) * intervalMs);
|
|
14173
14173
|
};
|
|
14174
14174
|
|
|
@@ -19650,8 +19650,8 @@ class BacktestLogicPrivateService {
|
|
|
19650
19650
|
}
|
|
19651
19651
|
|
|
19652
19652
|
const EMITTER_CHECK_INTERVAL = 5000;
|
|
19653
|
-
const MS_PER_MINUTE$
|
|
19654
|
-
const INTERVAL_MINUTES$
|
|
19653
|
+
const MS_PER_MINUTE$5 = 60000;
|
|
19654
|
+
const INTERVAL_MINUTES$5 = {
|
|
19655
19655
|
"1m": 1,
|
|
19656
19656
|
"3m": 3,
|
|
19657
19657
|
"5m": 5,
|
|
@@ -19666,7 +19666,7 @@ const INTERVAL_MINUTES$4 = {
|
|
|
19666
19666
|
};
|
|
19667
19667
|
const createEmitter = memoize(([interval]) => `${interval}`, (interval) => {
|
|
19668
19668
|
const tickSubject = new Subject();
|
|
19669
|
-
const intervalMs = INTERVAL_MINUTES$
|
|
19669
|
+
const intervalMs = INTERVAL_MINUTES$5[interval] * MS_PER_MINUTE$5;
|
|
19670
19670
|
{
|
|
19671
19671
|
let lastAligned = Math.floor(Date.now() / intervalMs) * intervalMs;
|
|
19672
19672
|
Source.fromInterval(EMITTER_CHECK_INTERVAL)
|
|
@@ -35172,7 +35172,7 @@ const EXCHANGE_METHOD_NAME_FORMAT_PRICE = "ExchangeUtils.formatPrice";
|
|
|
35172
35172
|
const EXCHANGE_METHOD_NAME_GET_ORDER_BOOK = "ExchangeUtils.getOrderBook";
|
|
35173
35173
|
const EXCHANGE_METHOD_NAME_GET_RAW_CANDLES = "ExchangeUtils.getRawCandles";
|
|
35174
35174
|
const EXCHANGE_METHOD_NAME_GET_AGGREGATED_TRADES = "ExchangeUtils.getAggregatedTrades";
|
|
35175
|
-
const MS_PER_MINUTE$
|
|
35175
|
+
const MS_PER_MINUTE$4 = 60000;
|
|
35176
35176
|
/**
|
|
35177
35177
|
* Gets current timestamp from execution context if available.
|
|
35178
35178
|
* Returns current Date() if no execution context exists (non-trading GUI).
|
|
@@ -35239,7 +35239,7 @@ const DEFAULT_GET_ORDER_BOOK_FN = async (_symbol, _depth, _from, _to, _backtest)
|
|
|
35239
35239
|
const DEFAULT_GET_AGGREGATED_TRADES_FN = async (_symbol, _from, _to, _backtest) => {
|
|
35240
35240
|
throw new Error(`getAggregatedTrades is not implemented for this exchange`);
|
|
35241
35241
|
};
|
|
35242
|
-
const INTERVAL_MINUTES$
|
|
35242
|
+
const INTERVAL_MINUTES$4 = {
|
|
35243
35243
|
"1m": 1,
|
|
35244
35244
|
"3m": 3,
|
|
35245
35245
|
"5m": 5,
|
|
@@ -35270,7 +35270,7 @@ const INTERVAL_MINUTES$3 = {
|
|
|
35270
35270
|
* @returns Aligned timestamp rounded down to interval boundary
|
|
35271
35271
|
*/
|
|
35272
35272
|
const ALIGN_TO_INTERVAL_FN$1 = (timestamp, intervalMinutes) => {
|
|
35273
|
-
const intervalMs = intervalMinutes * MS_PER_MINUTE$
|
|
35273
|
+
const intervalMs = intervalMinutes * MS_PER_MINUTE$4;
|
|
35274
35274
|
return Math.floor(timestamp / intervalMs) * intervalMs;
|
|
35275
35275
|
};
|
|
35276
35276
|
/**
|
|
@@ -35410,11 +35410,11 @@ class ExchangeInstance {
|
|
|
35410
35410
|
limit,
|
|
35411
35411
|
});
|
|
35412
35412
|
const getCandles = this._methods.getCandles;
|
|
35413
|
-
const step = INTERVAL_MINUTES$
|
|
35413
|
+
const step = INTERVAL_MINUTES$4[interval];
|
|
35414
35414
|
if (!step) {
|
|
35415
35415
|
throw new Error(`ExchangeInstance unknown interval=${interval}`);
|
|
35416
35416
|
}
|
|
35417
|
-
const stepMs = step * MS_PER_MINUTE$
|
|
35417
|
+
const stepMs = step * MS_PER_MINUTE$4;
|
|
35418
35418
|
// Align when down to interval boundary
|
|
35419
35419
|
const when = await GET_TIMESTAMP_FN();
|
|
35420
35420
|
const whenTimestamp = when.getTime();
|
|
@@ -35628,7 +35628,7 @@ class ExchangeInstance {
|
|
|
35628
35628
|
const when = await GET_TIMESTAMP_FN();
|
|
35629
35629
|
const alignedTo = ALIGN_TO_INTERVAL_FN$1(when.getTime(), GLOBAL_CONFIG.CC_ORDER_BOOK_TIME_OFFSET_MINUTES);
|
|
35630
35630
|
const to = new Date(alignedTo);
|
|
35631
|
-
const from = new Date(alignedTo - GLOBAL_CONFIG.CC_ORDER_BOOK_TIME_OFFSET_MINUTES * MS_PER_MINUTE$
|
|
35631
|
+
const from = new Date(alignedTo - GLOBAL_CONFIG.CC_ORDER_BOOK_TIME_OFFSET_MINUTES * MS_PER_MINUTE$4);
|
|
35632
35632
|
const isBacktest = await GET_BACKTEST_FN();
|
|
35633
35633
|
return await this._methods.getOrderBook(symbol, depth, from, to, isBacktest);
|
|
35634
35634
|
};
|
|
@@ -35660,7 +35660,7 @@ class ExchangeInstance {
|
|
|
35660
35660
|
const when = await GET_TIMESTAMP_FN();
|
|
35661
35661
|
// Align to 1-minute boundary to prevent look-ahead bias
|
|
35662
35662
|
const alignedTo = ALIGN_TO_INTERVAL_FN$1(when.getTime(), 1);
|
|
35663
|
-
const windowMs = GLOBAL_CONFIG.CC_AGGREGATED_TRADES_MAX_MINUTES * MS_PER_MINUTE$
|
|
35663
|
+
const windowMs = GLOBAL_CONFIG.CC_AGGREGATED_TRADES_MAX_MINUTES * MS_PER_MINUTE$4 - MS_PER_MINUTE$4;
|
|
35664
35664
|
const isBacktest = await GET_BACKTEST_FN();
|
|
35665
35665
|
// No limit: fetch a single window and return as-is
|
|
35666
35666
|
if (limit === undefined) {
|
|
@@ -35723,11 +35723,11 @@ class ExchangeInstance {
|
|
|
35723
35723
|
sDate,
|
|
35724
35724
|
eDate,
|
|
35725
35725
|
});
|
|
35726
|
-
const step = INTERVAL_MINUTES$
|
|
35726
|
+
const step = INTERVAL_MINUTES$4[interval];
|
|
35727
35727
|
if (!step) {
|
|
35728
35728
|
throw new Error(`ExchangeInstance getRawCandles: unknown interval=${interval}`);
|
|
35729
35729
|
}
|
|
35730
|
-
const stepMs = step * MS_PER_MINUTE$
|
|
35730
|
+
const stepMs = step * MS_PER_MINUTE$4;
|
|
35731
35731
|
const when = await GET_TIMESTAMP_FN();
|
|
35732
35732
|
const nowTimestamp = when.getTime();
|
|
35733
35733
|
const alignedNow = ALIGN_TO_INTERVAL_FN$1(nowTimestamp, step);
|
|
@@ -36039,8 +36039,8 @@ const Exchange = new ExchangeUtils();
|
|
|
36039
36039
|
|
|
36040
36040
|
const WARM_CANDLES_METHOD_NAME = "cache.warmCandles";
|
|
36041
36041
|
const CHECK_CANDLES_METHOD_NAME = "cache.checkCandles";
|
|
36042
|
-
const MS_PER_MINUTE$
|
|
36043
|
-
const INTERVAL_MINUTES$
|
|
36042
|
+
const MS_PER_MINUTE$3 = 60000;
|
|
36043
|
+
const INTERVAL_MINUTES$3 = {
|
|
36044
36044
|
"1m": 1,
|
|
36045
36045
|
"3m": 3,
|
|
36046
36046
|
"5m": 5,
|
|
@@ -36054,7 +36054,7 @@ const INTERVAL_MINUTES$2 = {
|
|
|
36054
36054
|
"1d": 1440,
|
|
36055
36055
|
};
|
|
36056
36056
|
const ALIGN_TO_INTERVAL_FN = (timestamp, intervalMinutes) => {
|
|
36057
|
-
const intervalMs = intervalMinutes * MS_PER_MINUTE$
|
|
36057
|
+
const intervalMs = intervalMinutes * MS_PER_MINUTE$3;
|
|
36058
36058
|
return Math.floor(timestamp / intervalMs) * intervalMs;
|
|
36059
36059
|
};
|
|
36060
36060
|
const BAR_LENGTH = 30;
|
|
@@ -36079,11 +36079,11 @@ const PRINT_PROGRESS_FN = (fetched, total, symbol, interval) => {
|
|
|
36079
36079
|
async function checkCandles(params) {
|
|
36080
36080
|
const { symbol, exchangeName, interval, from, to, baseDir = "./dump/data/candle" } = params;
|
|
36081
36081
|
backtest.loggerService.info(CHECK_CANDLES_METHOD_NAME, params);
|
|
36082
|
-
const step = INTERVAL_MINUTES$
|
|
36082
|
+
const step = INTERVAL_MINUTES$3[interval];
|
|
36083
36083
|
if (!step) {
|
|
36084
36084
|
throw new Error(`checkCandles: unsupported interval=${interval}`);
|
|
36085
36085
|
}
|
|
36086
|
-
const stepMs = step * MS_PER_MINUTE$
|
|
36086
|
+
const stepMs = step * MS_PER_MINUTE$3;
|
|
36087
36087
|
const dir = join(baseDir, exchangeName, symbol, interval);
|
|
36088
36088
|
const fromTs = ALIGN_TO_INTERVAL_FN(from.getTime(), step);
|
|
36089
36089
|
const toTs = ALIGN_TO_INTERVAL_FN(to.getTime(), step);
|
|
@@ -36153,11 +36153,11 @@ async function warmCandles(params) {
|
|
|
36153
36153
|
from,
|
|
36154
36154
|
to,
|
|
36155
36155
|
});
|
|
36156
|
-
const step = INTERVAL_MINUTES$
|
|
36156
|
+
const step = INTERVAL_MINUTES$3[interval];
|
|
36157
36157
|
if (!step) {
|
|
36158
36158
|
throw new Error(`warmCandles: unsupported interval=${interval}`);
|
|
36159
36159
|
}
|
|
36160
|
-
const stepMs = step * MS_PER_MINUTE$
|
|
36160
|
+
const stepMs = step * MS_PER_MINUTE$3;
|
|
36161
36161
|
const instance = new ExchangeInstance(exchangeName);
|
|
36162
36162
|
const sinceTimestamp = ALIGN_TO_INTERVAL_FN(from.getTime(), step);
|
|
36163
36163
|
const untilTimestamp = ALIGN_TO_INTERVAL_FN(to.getTime(), step);
|
|
@@ -48504,14 +48504,17 @@ class RecentPersistBacktestUtils {
|
|
|
48504
48504
|
};
|
|
48505
48505
|
/**
|
|
48506
48506
|
* Retrieves the latest persisted signal for the given context.
|
|
48507
|
+
* Returns null if the stored signal's `timestamp` is greater than the requested `when`
|
|
48508
|
+
* (look-ahead bias protection).
|
|
48507
48509
|
* @param symbol - Trading pair symbol
|
|
48508
48510
|
* @param strategyName - Strategy identifier
|
|
48509
48511
|
* @param exchangeName - Exchange identifier
|
|
48510
48512
|
* @param frameName - Frame identifier
|
|
48511
48513
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
48512
|
-
* @
|
|
48514
|
+
* @param when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
48515
|
+
* @returns The latest signal or null if not found / shadowed by look-ahead
|
|
48513
48516
|
*/
|
|
48514
|
-
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
48517
|
+
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1, when) => {
|
|
48515
48518
|
backtest.loggerService.info(RECENT_PERSIST_BACKTEST_METHOD_NAME_GET_LATEST_SIGNAL, {
|
|
48516
48519
|
symbol,
|
|
48517
48520
|
strategyName,
|
|
@@ -48519,20 +48522,26 @@ class RecentPersistBacktestUtils {
|
|
|
48519
48522
|
frameName,
|
|
48520
48523
|
backtest: backtest$1,
|
|
48521
48524
|
});
|
|
48522
|
-
|
|
48525
|
+
const signal = await PersistRecentAdapter.readRecentData(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
48526
|
+
if (!signal || signal.timestamp > when.getTime()) {
|
|
48527
|
+
return null;
|
|
48528
|
+
}
|
|
48529
|
+
return signal;
|
|
48523
48530
|
};
|
|
48524
48531
|
/**
|
|
48525
48532
|
* Returns the number of whole minutes elapsed since the latest signal's creation timestamp.
|
|
48526
|
-
*
|
|
48533
|
+
* `timestamp` doubles as the look-ahead cutoff — a signal whose `timestamp` exceeds
|
|
48534
|
+
* the requested one is treated as not yet visible.
|
|
48535
|
+
* @param timestamp - Current timestamp in milliseconds (also serves as look-ahead cutoff)
|
|
48527
48536
|
* @param symbol - Trading pair symbol
|
|
48528
48537
|
* @param strategyName - Strategy identifier
|
|
48529
48538
|
* @param exchangeName - Exchange identifier
|
|
48530
48539
|
* @param frameName - Frame identifier
|
|
48531
48540
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
48532
|
-
* @returns Whole minutes since the latest signal was created, or null if no signal found
|
|
48541
|
+
* @returns Whole minutes since the latest signal was created, or null if no signal found / shadowed by look-ahead
|
|
48533
48542
|
*/
|
|
48534
48543
|
this.getMinutesSinceLatestSignalCreated = async (timestamp, symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
48535
|
-
const signal = await this.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest);
|
|
48544
|
+
const signal = await this.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest, new Date(timestamp));
|
|
48536
48545
|
if (!signal) {
|
|
48537
48546
|
return null;
|
|
48538
48547
|
}
|
|
@@ -48568,30 +48577,39 @@ class RecentMemoryBacktestUtils {
|
|
|
48568
48577
|
};
|
|
48569
48578
|
/**
|
|
48570
48579
|
* Retrieves the latest in-memory signal for the given context.
|
|
48580
|
+
* Returns null if the stored signal's `timestamp` is greater than the requested `when`
|
|
48581
|
+
* (look-ahead bias protection).
|
|
48571
48582
|
* @param symbol - Trading pair symbol
|
|
48572
48583
|
* @param strategyName - Strategy identifier
|
|
48573
48584
|
* @param exchangeName - Exchange identifier
|
|
48574
48585
|
* @param frameName - Frame identifier
|
|
48575
48586
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
48576
|
-
* @
|
|
48587
|
+
* @param when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
48588
|
+
* @returns The latest signal or null if not found / shadowed by look-ahead
|
|
48577
48589
|
*/
|
|
48578
|
-
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
48590
|
+
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1, when) => {
|
|
48579
48591
|
const key = CREATE_KEY_FN$7(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
48580
48592
|
backtest.loggerService.info(RECENT_MEMORY_BACKTEST_METHOD_NAME_GET_LATEST_SIGNAL, { key });
|
|
48581
|
-
|
|
48593
|
+
const signal = this._signals.get(key) ?? null;
|
|
48594
|
+
if (!signal || signal.timestamp > when.getTime()) {
|
|
48595
|
+
return null;
|
|
48596
|
+
}
|
|
48597
|
+
return signal;
|
|
48582
48598
|
};
|
|
48583
48599
|
/**
|
|
48584
48600
|
* Returns the number of whole minutes elapsed since the latest signal's creation timestamp.
|
|
48585
|
-
*
|
|
48601
|
+
* `timestamp` doubles as the look-ahead cutoff — a signal whose `timestamp` exceeds
|
|
48602
|
+
* the requested one is treated as not yet visible.
|
|
48603
|
+
* @param timestamp - Current timestamp in milliseconds (also serves as look-ahead cutoff)
|
|
48586
48604
|
* @param symbol - Trading pair symbol
|
|
48587
48605
|
* @param strategyName - Strategy identifier
|
|
48588
48606
|
* @param exchangeName - Exchange identifier
|
|
48589
48607
|
* @param frameName - Frame identifier
|
|
48590
48608
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
48591
|
-
* @returns Whole minutes since the latest signal was created, or null if no signal found
|
|
48609
|
+
* @returns Whole minutes since the latest signal was created, or null if no signal found / shadowed by look-ahead
|
|
48592
48610
|
*/
|
|
48593
48611
|
this.getMinutesSinceLatestSignalCreated = async (timestamp, symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
48594
|
-
const signal = await this.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest);
|
|
48612
|
+
const signal = await this.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest, new Date(timestamp));
|
|
48595
48613
|
if (!signal) {
|
|
48596
48614
|
return null;
|
|
48597
48615
|
}
|
|
@@ -48623,14 +48641,17 @@ class RecentPersistLiveUtils {
|
|
|
48623
48641
|
};
|
|
48624
48642
|
/**
|
|
48625
48643
|
* Retrieves the latest persisted signal for the given context.
|
|
48644
|
+
* Returns null if the stored signal's `timestamp` is greater than the requested `when`
|
|
48645
|
+
* (look-ahead bias protection).
|
|
48626
48646
|
* @param symbol - Trading pair symbol
|
|
48627
48647
|
* @param strategyName - Strategy identifier
|
|
48628
48648
|
* @param exchangeName - Exchange identifier
|
|
48629
48649
|
* @param frameName - Frame identifier
|
|
48630
48650
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
48631
|
-
* @
|
|
48651
|
+
* @param when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
48652
|
+
* @returns The latest signal or null if not found / shadowed by look-ahead
|
|
48632
48653
|
*/
|
|
48633
|
-
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
48654
|
+
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1, when) => {
|
|
48634
48655
|
backtest.loggerService.info(RECENT_PERSIST_LIVE_METHOD_NAME_GET_LATEST_SIGNAL, {
|
|
48635
48656
|
symbol,
|
|
48636
48657
|
strategyName,
|
|
@@ -48638,20 +48659,26 @@ class RecentPersistLiveUtils {
|
|
|
48638
48659
|
frameName,
|
|
48639
48660
|
backtest: backtest$1,
|
|
48640
48661
|
});
|
|
48641
|
-
|
|
48662
|
+
const signal = await PersistRecentAdapter.readRecentData(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
48663
|
+
if (!signal || signal.timestamp > when.getTime()) {
|
|
48664
|
+
return null;
|
|
48665
|
+
}
|
|
48666
|
+
return signal;
|
|
48642
48667
|
};
|
|
48643
48668
|
/**
|
|
48644
48669
|
* Returns the number of whole minutes elapsed since the latest signal's creation timestamp.
|
|
48645
|
-
*
|
|
48670
|
+
* `timestamp` doubles as the look-ahead cutoff — a signal whose `timestamp` exceeds
|
|
48671
|
+
* the requested one is treated as not yet visible.
|
|
48672
|
+
* @param timestamp - Current timestamp in milliseconds (also serves as look-ahead cutoff)
|
|
48646
48673
|
* @param symbol - Trading pair symbol
|
|
48647
48674
|
* @param strategyName - Strategy identifier
|
|
48648
48675
|
* @param exchangeName - Exchange identifier
|
|
48649
48676
|
* @param frameName - Frame identifier
|
|
48650
48677
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
48651
|
-
* @returns Whole minutes since the latest signal was created, or null if no signal found
|
|
48678
|
+
* @returns Whole minutes since the latest signal was created, or null if no signal found / shadowed by look-ahead
|
|
48652
48679
|
*/
|
|
48653
48680
|
this.getMinutesSinceLatestSignalCreated = async (timestamp, symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
48654
|
-
const signal = await this.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest);
|
|
48681
|
+
const signal = await this.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest, new Date(timestamp));
|
|
48655
48682
|
if (!signal) {
|
|
48656
48683
|
return null;
|
|
48657
48684
|
}
|
|
@@ -48687,30 +48714,39 @@ class RecentMemoryLiveUtils {
|
|
|
48687
48714
|
};
|
|
48688
48715
|
/**
|
|
48689
48716
|
* Retrieves the latest in-memory signal for the given context.
|
|
48717
|
+
* Returns null if the stored signal's `timestamp` is greater than the requested `when`
|
|
48718
|
+
* (look-ahead bias protection).
|
|
48690
48719
|
* @param symbol - Trading pair symbol
|
|
48691
48720
|
* @param strategyName - Strategy identifier
|
|
48692
48721
|
* @param exchangeName - Exchange identifier
|
|
48693
48722
|
* @param frameName - Frame identifier
|
|
48694
48723
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
48695
|
-
* @
|
|
48724
|
+
* @param when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
48725
|
+
* @returns The latest signal or null if not found / shadowed by look-ahead
|
|
48696
48726
|
*/
|
|
48697
|
-
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
48727
|
+
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1, when) => {
|
|
48698
48728
|
const key = CREATE_KEY_FN$7(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
48699
48729
|
backtest.loggerService.info(RECENT_MEMORY_LIVE_METHOD_NAME_GET_LATEST_SIGNAL, { key });
|
|
48700
|
-
|
|
48730
|
+
const signal = this._signals.get(key) ?? null;
|
|
48731
|
+
if (!signal || signal.timestamp > when.getTime()) {
|
|
48732
|
+
return null;
|
|
48733
|
+
}
|
|
48734
|
+
return signal;
|
|
48701
48735
|
};
|
|
48702
48736
|
/**
|
|
48703
48737
|
* Returns the number of whole minutes elapsed since the latest signal's creation timestamp.
|
|
48704
|
-
*
|
|
48738
|
+
* `timestamp` doubles as the look-ahead cutoff — a signal whose `timestamp` exceeds
|
|
48739
|
+
* the requested one is treated as not yet visible.
|
|
48740
|
+
* @param timestamp - Current timestamp in milliseconds (also serves as look-ahead cutoff)
|
|
48705
48741
|
* @param symbol - Trading pair symbol
|
|
48706
48742
|
* @param strategyName - Strategy identifier
|
|
48707
48743
|
* @param exchangeName - Exchange identifier
|
|
48708
48744
|
* @param frameName - Frame identifier
|
|
48709
48745
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
48710
|
-
* @returns Whole minutes since the latest signal was created, or null if no signal found
|
|
48746
|
+
* @returns Whole minutes since the latest signal was created, or null if no signal found / shadowed by look-ahead
|
|
48711
48747
|
*/
|
|
48712
48748
|
this.getMinutesSinceLatestSignalCreated = async (timestamp, symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
48713
|
-
const signal = await this.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest);
|
|
48749
|
+
const signal = await this.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest, new Date(timestamp));
|
|
48714
48750
|
if (!signal) {
|
|
48715
48751
|
return null;
|
|
48716
48752
|
}
|
|
@@ -48750,9 +48786,10 @@ class RecentBacktestAdapter {
|
|
|
48750
48786
|
* @param exchangeName - Exchange identifier
|
|
48751
48787
|
* @param frameName - Frame identifier
|
|
48752
48788
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
48753
|
-
* @
|
|
48789
|
+
* @param when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
48790
|
+
* @returns The latest signal or null if not found / shadowed by look-ahead
|
|
48754
48791
|
*/
|
|
48755
|
-
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
48792
|
+
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1, when) => {
|
|
48756
48793
|
backtest.loggerService.info(RECENT_BACKTEST_ADAPTER_METHOD_NAME_GET_LATEST_SIGNAL, {
|
|
48757
48794
|
symbol,
|
|
48758
48795
|
strategyName,
|
|
@@ -48760,18 +48797,20 @@ class RecentBacktestAdapter {
|
|
|
48760
48797
|
frameName,
|
|
48761
48798
|
backtest: backtest$1,
|
|
48762
48799
|
});
|
|
48763
|
-
return await this._recentBacktestUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
48800
|
+
return await this._recentBacktestUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1, when);
|
|
48764
48801
|
};
|
|
48765
48802
|
/**
|
|
48766
48803
|
* Returns the number of whole minutes elapsed since the latest signal's creation timestamp.
|
|
48767
|
-
* Proxies call to the underlying storage adapter.
|
|
48768
|
-
*
|
|
48804
|
+
* Proxies call to the underlying storage adapter. `timestamp` doubles as the
|
|
48805
|
+
* look-ahead cutoff — a signal whose `timestamp` exceeds the requested one is
|
|
48806
|
+
* treated as not yet visible.
|
|
48807
|
+
* @param timestamp - Current timestamp in milliseconds (also serves as look-ahead cutoff)
|
|
48769
48808
|
* @param symbol - Trading pair symbol
|
|
48770
48809
|
* @param strategyName - Strategy identifier
|
|
48771
48810
|
* @param exchangeName - Exchange identifier
|
|
48772
48811
|
* @param frameName - Frame identifier
|
|
48773
48812
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
48774
|
-
* @returns Whole minutes since the latest signal was created, or null if no signal found
|
|
48813
|
+
* @returns Whole minutes since the latest signal was created, or null if no signal found / shadowed by look-ahead
|
|
48775
48814
|
*/
|
|
48776
48815
|
this.getMinutesSinceLatestSignalCreated = async (timestamp, symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
48777
48816
|
backtest.loggerService.info(RECENT_BACKTEST_ADAPTER_METHOD_NAME_GET_LATEST_SIGNAL, {
|
|
@@ -48782,7 +48821,7 @@ class RecentBacktestAdapter {
|
|
|
48782
48821
|
backtest: backtest$1,
|
|
48783
48822
|
timestamp,
|
|
48784
48823
|
});
|
|
48785
|
-
const signal = await this._recentBacktestUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
48824
|
+
const signal = await this._recentBacktestUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1, new Date(timestamp));
|
|
48786
48825
|
if (!signal) {
|
|
48787
48826
|
return null;
|
|
48788
48827
|
}
|
|
@@ -48854,9 +48893,10 @@ class RecentLiveAdapter {
|
|
|
48854
48893
|
* @param exchangeName - Exchange identifier
|
|
48855
48894
|
* @param frameName - Frame identifier
|
|
48856
48895
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
48857
|
-
* @
|
|
48896
|
+
* @param when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
48897
|
+
* @returns The latest signal or null if not found / shadowed by look-ahead
|
|
48858
48898
|
*/
|
|
48859
|
-
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
48899
|
+
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1, when) => {
|
|
48860
48900
|
backtest.loggerService.info(RECENT_LIVE_ADAPTER_METHOD_NAME_GET_LATEST_SIGNAL, {
|
|
48861
48901
|
symbol,
|
|
48862
48902
|
strategyName,
|
|
@@ -48864,18 +48904,20 @@ class RecentLiveAdapter {
|
|
|
48864
48904
|
frameName,
|
|
48865
48905
|
backtest: backtest$1,
|
|
48866
48906
|
});
|
|
48867
|
-
return await this._recentLiveUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
48907
|
+
return await this._recentLiveUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1, when);
|
|
48868
48908
|
};
|
|
48869
48909
|
/**
|
|
48870
48910
|
* Returns the number of whole minutes elapsed since the latest signal's creation timestamp.
|
|
48871
|
-
* Proxies call to the underlying storage adapter.
|
|
48872
|
-
*
|
|
48911
|
+
* Proxies call to the underlying storage adapter. `timestamp` doubles as the
|
|
48912
|
+
* look-ahead cutoff — a signal whose `timestamp` exceeds the requested one is
|
|
48913
|
+
* treated as not yet visible.
|
|
48914
|
+
* @param timestamp - Current timestamp in milliseconds (also serves as look-ahead cutoff)
|
|
48873
48915
|
* @param symbol - Trading pair symbol
|
|
48874
48916
|
* @param strategyName - Strategy identifier
|
|
48875
48917
|
* @param exchangeName - Exchange identifier
|
|
48876
48918
|
* @param frameName - Frame identifier
|
|
48877
48919
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
48878
|
-
* @returns Whole minutes since the latest signal was created, or null if no signal found
|
|
48920
|
+
* @returns Whole minutes since the latest signal was created, or null if no signal found / shadowed by look-ahead
|
|
48879
48921
|
*/
|
|
48880
48922
|
this.getMinutesSinceLatestSignalCreated = async (timestamp, symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
48881
48923
|
backtest.loggerService.info(RECENT_LIVE_ADAPTER_METHOD_NAME_GET_LATEST_SIGNAL, {
|
|
@@ -48886,7 +48928,7 @@ class RecentLiveAdapter {
|
|
|
48886
48928
|
backtest: backtest$1,
|
|
48887
48929
|
timestamp,
|
|
48888
48930
|
});
|
|
48889
|
-
const signal = await this._recentLiveUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
48931
|
+
const signal = await this._recentLiveUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1, new Date(timestamp));
|
|
48890
48932
|
if (!signal) {
|
|
48891
48933
|
return null;
|
|
48892
48934
|
}
|
|
@@ -48976,14 +49018,16 @@ class RecentAdapter {
|
|
|
48976
49018
|
/**
|
|
48977
49019
|
* Retrieves the latest active signal for the given symbol and context.
|
|
48978
49020
|
* Searches backtest storage first, then live storage.
|
|
49021
|
+
* Returns null if the stored signal's `timestamp` is greater than the requested `when`
|
|
49022
|
+
* (look-ahead bias protection).
|
|
48979
49023
|
*
|
|
48980
49024
|
* @param symbol - Trading pair symbol
|
|
48981
49025
|
* @param context - Execution context with strategyName, exchangeName, and frameName
|
|
48982
|
-
* @param
|
|
48983
|
-
* @returns The latest signal or null if not found
|
|
49026
|
+
* @param when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
49027
|
+
* @returns The latest signal or null if not found / shadowed by look-ahead
|
|
48984
49028
|
* @throws Error if RecentAdapter is not enabled
|
|
48985
49029
|
*/
|
|
48986
|
-
this.getLatestSignal = async (symbol, context) => {
|
|
49030
|
+
this.getLatestSignal = async (symbol, context, when) => {
|
|
48987
49031
|
backtest.loggerService.info(RECENT_ADAPTER_METHOD_NAME_GET_LATEST_SIGNAL, {
|
|
48988
49032
|
symbol,
|
|
48989
49033
|
context,
|
|
@@ -48992,10 +49036,10 @@ class RecentAdapter {
|
|
|
48992
49036
|
throw new Error("RecentAdapter is not enabled. Call enable() first.");
|
|
48993
49037
|
}
|
|
48994
49038
|
let result = null;
|
|
48995
|
-
if (result = await RecentBacktest.getLatestSignal(symbol, context.strategyName, context.exchangeName, context.frameName, true)) {
|
|
49039
|
+
if (result = await RecentBacktest.getLatestSignal(symbol, context.strategyName, context.exchangeName, context.frameName, true, when)) {
|
|
48996
49040
|
return result;
|
|
48997
49041
|
}
|
|
48998
|
-
if (result = await RecentLive.getLatestSignal(symbol, context.strategyName, context.exchangeName, context.frameName, false)) {
|
|
49042
|
+
if (result = await RecentLive.getLatestSignal(symbol, context.strategyName, context.exchangeName, context.frameName, false, when)) {
|
|
48999
49043
|
return result;
|
|
49000
49044
|
}
|
|
49001
49045
|
return null;
|
|
@@ -49003,13 +49047,16 @@ class RecentAdapter {
|
|
|
49003
49047
|
/**
|
|
49004
49048
|
* Returns the number of whole minutes elapsed since the latest signal's creation timestamp.
|
|
49005
49049
|
* Searches backtest storage first, then live storage.
|
|
49006
|
-
*
|
|
49050
|
+
* `when` doubles as the look-ahead cutoff — a signal whose `timestamp` exceeds
|
|
49051
|
+
* `when.getTime()` is treated as not yet visible — and as the "now" against
|
|
49052
|
+
* which elapsed minutes are computed.
|
|
49007
49053
|
* @param symbol - Trading pair symbol
|
|
49008
49054
|
* @param context - Execution context with strategyName, exchangeName, and frameName
|
|
49009
|
-
* @
|
|
49055
|
+
* @param when - Logical timestamp at which the read is happening (look-ahead cutoff + "now")
|
|
49056
|
+
* @returns Whole minutes since the latest signal was created, or null if no signal found / shadowed by look-ahead
|
|
49010
49057
|
* @throws Error if RecentAdapter is not enabled
|
|
49011
49058
|
*/
|
|
49012
|
-
this.getMinutesSinceLatestSignalCreated = async (symbol, context) => {
|
|
49059
|
+
this.getMinutesSinceLatestSignalCreated = async (symbol, context, when) => {
|
|
49013
49060
|
backtest.loggerService.info(RECENT_ADAPTER_METHOD_NAME_GET_MINUTES_SINCE_LATEST_SIGNAL, {
|
|
49014
49061
|
symbol,
|
|
49015
49062
|
context,
|
|
@@ -49018,13 +49065,11 @@ class RecentAdapter {
|
|
|
49018
49065
|
throw new Error("RecentAdapter is not enabled. Call enable() first.");
|
|
49019
49066
|
}
|
|
49020
49067
|
let signal = null;
|
|
49021
|
-
if (signal = await RecentBacktest.getLatestSignal(symbol, context.strategyName, context.exchangeName, context.frameName, true)) {
|
|
49022
|
-
|
|
49023
|
-
return Math.floor((timestamp - signal.timestamp) / (1000 * 60));
|
|
49068
|
+
if (signal = await RecentBacktest.getLatestSignal(symbol, context.strategyName, context.exchangeName, context.frameName, true, when)) {
|
|
49069
|
+
return Math.floor((when.getTime() - signal.timestamp) / (1000 * 60));
|
|
49024
49070
|
}
|
|
49025
|
-
if (signal = await RecentLive.getLatestSignal(symbol, context.strategyName, context.exchangeName, context.frameName, false)) {
|
|
49026
|
-
|
|
49027
|
-
return Math.floor((timestamp - signal.timestamp) / (1000 * 60));
|
|
49071
|
+
if (signal = await RecentLive.getLatestSignal(symbol, context.strategyName, context.exchangeName, context.frameName, false, when)) {
|
|
49072
|
+
return Math.floor((when.getTime() - signal.timestamp) / (1000 * 60));
|
|
49028
49073
|
}
|
|
49029
49074
|
return null;
|
|
49030
49075
|
};
|
|
@@ -49089,41 +49134,54 @@ class StateLocalInstance {
|
|
|
49089
49134
|
this.initialValue = initialValue;
|
|
49090
49135
|
this.signalId = signalId;
|
|
49091
49136
|
this.bucketName = bucketName;
|
|
49137
|
+
this._when = 0;
|
|
49092
49138
|
/**
|
|
49093
49139
|
* Initializes _value from initialValue - local state needs no async setup.
|
|
49094
49140
|
* @returns Promise that resolves immediately
|
|
49095
49141
|
*/
|
|
49096
49142
|
this.waitForInit = singleshot(async (_initial) => {
|
|
49097
49143
|
this._value = this.initialValue;
|
|
49144
|
+
this._when = 0;
|
|
49098
49145
|
});
|
|
49099
49146
|
/**
|
|
49100
49147
|
* Update the in-memory state value.
|
|
49148
|
+
* Records `when` so future reads with a smaller `when` see `initialValue`.
|
|
49149
|
+
* The dispatch updater receives the look-ahead-guarded current value.
|
|
49101
49150
|
* @param dispatch - New value or updater function receiving current value
|
|
49151
|
+
* @param when - Logical timestamp this value belongs to
|
|
49102
49152
|
* @returns Updated state value
|
|
49103
49153
|
*/
|
|
49104
|
-
this.setState = queued(async (dispatch) => {
|
|
49154
|
+
this.setState = queued(async (dispatch, when) => {
|
|
49105
49155
|
backtest.loggerService.debug(STATE_LOCAL_INSTANCE_METHOD_NAME_SET, {
|
|
49106
49156
|
signalId: this.signalId,
|
|
49107
49157
|
bucketName: this.bucketName,
|
|
49108
49158
|
});
|
|
49109
49159
|
if (typeof dispatch === "function") {
|
|
49110
|
-
this.
|
|
49160
|
+
const prev = this._when > when.getTime() ? this.initialValue : this._value;
|
|
49161
|
+
this._value = await dispatch(prev);
|
|
49111
49162
|
}
|
|
49112
49163
|
else {
|
|
49113
49164
|
this._value = dispatch;
|
|
49114
49165
|
}
|
|
49166
|
+
this._when = when.getTime();
|
|
49115
49167
|
return this._value;
|
|
49116
49168
|
});
|
|
49117
49169
|
}
|
|
49118
49170
|
/**
|
|
49119
49171
|
* Read the current in-memory state value.
|
|
49172
|
+
* Returns `initialValue` when the stored `when` is greater than the requested `when`
|
|
49173
|
+
* (look-ahead bias protection).
|
|
49174
|
+
* @param when - Logical timestamp at which the read is happening
|
|
49120
49175
|
* @returns Current state value
|
|
49121
49176
|
*/
|
|
49122
|
-
async getState() {
|
|
49177
|
+
async getState(when) {
|
|
49123
49178
|
backtest.loggerService.debug(STATE_LOCAL_INSTANCE_METHOD_NAME_GET, {
|
|
49124
49179
|
signalId: this.signalId,
|
|
49125
49180
|
bucketName: this.bucketName,
|
|
49126
49181
|
});
|
|
49182
|
+
if (this._when > when.getTime()) {
|
|
49183
|
+
return this.initialValue;
|
|
49184
|
+
}
|
|
49127
49185
|
return this._value;
|
|
49128
49186
|
}
|
|
49129
49187
|
/** Releases resources held by this instance. */
|
|
@@ -49157,14 +49215,14 @@ class StateDummyInstance {
|
|
|
49157
49215
|
* No-op read - always returns initialValue.
|
|
49158
49216
|
* @returns initialValue
|
|
49159
49217
|
*/
|
|
49160
|
-
async getState() {
|
|
49218
|
+
async getState(_when) {
|
|
49161
49219
|
return this.initialValue;
|
|
49162
49220
|
}
|
|
49163
49221
|
/**
|
|
49164
49222
|
* No-op write - discards the value and returns initialValue.
|
|
49165
49223
|
* @returns initialValue
|
|
49166
49224
|
*/
|
|
49167
|
-
async setState(_dispatch) {
|
|
49225
|
+
async setState(_dispatch, _when) {
|
|
49168
49226
|
return this.initialValue;
|
|
49169
49227
|
}
|
|
49170
49228
|
/** No-op. */
|
|
@@ -49190,6 +49248,7 @@ class StatePersistInstance {
|
|
|
49190
49248
|
this.initialValue = initialValue;
|
|
49191
49249
|
this.signalId = signalId;
|
|
49192
49250
|
this.bucketName = bucketName;
|
|
49251
|
+
this._when = 0;
|
|
49193
49252
|
/**
|
|
49194
49253
|
* Initialize persistence storage and restore state from disk.
|
|
49195
49254
|
* @param initial - Whether this is the first initialization
|
|
@@ -49204,40 +49263,54 @@ class StatePersistInstance {
|
|
|
49204
49263
|
const data = await PersistStateAdapter.readStateData(this.signalId, this.bucketName);
|
|
49205
49264
|
if (data) {
|
|
49206
49265
|
this._value = data.data;
|
|
49266
|
+
this._when = data.when;
|
|
49207
49267
|
return;
|
|
49208
49268
|
}
|
|
49209
49269
|
this._value = this.initialValue;
|
|
49270
|
+
this._when = 0;
|
|
49210
49271
|
});
|
|
49211
49272
|
/**
|
|
49212
49273
|
* Update state and persist to disk atomically.
|
|
49274
|
+
* A write with a smaller `when` overwrites an existing record — that lets a
|
|
49275
|
+
* restarted backtest reset live-written state without breaking live.
|
|
49276
|
+
* The dispatch updater receives the look-ahead-guarded current value.
|
|
49213
49277
|
* @param dispatch - New value or updater function receiving current value
|
|
49278
|
+
* @param when - Logical timestamp this value belongs to
|
|
49214
49279
|
* @returns Updated state value
|
|
49215
49280
|
*/
|
|
49216
|
-
this.setState = queued(async (dispatch) => {
|
|
49281
|
+
this.setState = queued(async (dispatch, when) => {
|
|
49217
49282
|
backtest.loggerService.debug(STATE_PERSIST_INSTANCE_METHOD_NAME_SET, {
|
|
49218
49283
|
signalId: this.signalId,
|
|
49219
49284
|
bucketName: this.bucketName,
|
|
49220
49285
|
});
|
|
49221
49286
|
if (typeof dispatch === "function") {
|
|
49222
|
-
this.
|
|
49287
|
+
const prev = this._when > when.getTime() ? this.initialValue : this._value;
|
|
49288
|
+
this._value = await dispatch(prev);
|
|
49223
49289
|
}
|
|
49224
49290
|
else {
|
|
49225
49291
|
this._value = dispatch;
|
|
49226
49292
|
}
|
|
49293
|
+
this._when = when.getTime();
|
|
49227
49294
|
const id = CREATE_KEY_FN$6(this.signalId, this.bucketName);
|
|
49228
|
-
await PersistStateAdapter.writeStateData({ id, data: this._value }, this.signalId, this.bucketName);
|
|
49295
|
+
await PersistStateAdapter.writeStateData({ id, data: this._value, when: this._when }, this.signalId, this.bucketName);
|
|
49229
49296
|
return this._value;
|
|
49230
49297
|
});
|
|
49231
49298
|
}
|
|
49232
49299
|
/**
|
|
49233
49300
|
* Read the current persisted state value.
|
|
49301
|
+
* Returns `initialValue` when the stored `when` is greater than the requested `when`
|
|
49302
|
+
* (look-ahead bias protection).
|
|
49303
|
+
* @param when - Logical timestamp at which the read is happening
|
|
49234
49304
|
* @returns Current state value
|
|
49235
49305
|
*/
|
|
49236
|
-
async getState() {
|
|
49306
|
+
async getState(when) {
|
|
49237
49307
|
backtest.loggerService.debug(STATE_PERSIST_INSTANCE_METHOD_NAME_GET, {
|
|
49238
49308
|
signalId: this.signalId,
|
|
49239
49309
|
bucketName: this.bucketName,
|
|
49240
49310
|
});
|
|
49311
|
+
if (this._when > when.getTime()) {
|
|
49312
|
+
return this.initialValue;
|
|
49313
|
+
}
|
|
49241
49314
|
return this._value;
|
|
49242
49315
|
}
|
|
49243
49316
|
/** Releases resources held by this instance. */
|
|
@@ -49290,6 +49363,7 @@ class StateBacktestAdapter {
|
|
|
49290
49363
|
* @param dto.signalId - Signal identifier
|
|
49291
49364
|
* @param dto.bucketName - Bucket name
|
|
49292
49365
|
* @param dto.initialValue - Default value when no persisted state exists
|
|
49366
|
+
* @param dto.when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
49293
49367
|
* @returns Current state value
|
|
49294
49368
|
*/
|
|
49295
49369
|
this.getState = async (dto) => {
|
|
@@ -49301,7 +49375,7 @@ class StateBacktestAdapter {
|
|
|
49301
49375
|
const isInitial = !this.getInstance.has(key);
|
|
49302
49376
|
const instance = this.getInstance(dto.signalId, dto.bucketName, dto.initialValue);
|
|
49303
49377
|
await instance.waitForInit(isInitial);
|
|
49304
|
-
return await instance.getState();
|
|
49378
|
+
return await instance.getState(dto.when);
|
|
49305
49379
|
};
|
|
49306
49380
|
/**
|
|
49307
49381
|
* Update the state value for a signal.
|
|
@@ -49309,6 +49383,7 @@ class StateBacktestAdapter {
|
|
|
49309
49383
|
* @param dto.signalId - Signal identifier
|
|
49310
49384
|
* @param dto.bucketName - Bucket name
|
|
49311
49385
|
* @param dto.initialValue - Default value when no persisted state exists
|
|
49386
|
+
* @param dto.when - Logical timestamp this value belongs to
|
|
49312
49387
|
* @returns Updated state value
|
|
49313
49388
|
*/
|
|
49314
49389
|
this.setState = async (dispatch, dto) => {
|
|
@@ -49320,7 +49395,7 @@ class StateBacktestAdapter {
|
|
|
49320
49395
|
const isInitial = !this.getInstance.has(key);
|
|
49321
49396
|
const instance = this.getInstance(dto.signalId, dto.bucketName, dto.initialValue);
|
|
49322
49397
|
await instance.waitForInit(isInitial);
|
|
49323
|
-
return await instance.setState(dispatch);
|
|
49398
|
+
return await instance.setState(dispatch, dto.when);
|
|
49324
49399
|
};
|
|
49325
49400
|
/**
|
|
49326
49401
|
* Switches to in-memory adapter (default).
|
|
@@ -49405,6 +49480,7 @@ class StateLiveAdapter {
|
|
|
49405
49480
|
* @param dto.signalId - Signal identifier
|
|
49406
49481
|
* @param dto.bucketName - Bucket name
|
|
49407
49482
|
* @param dto.initialValue - Default value when no persisted state exists
|
|
49483
|
+
* @param dto.when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
49408
49484
|
* @returns Current state value
|
|
49409
49485
|
*/
|
|
49410
49486
|
this.getState = async (dto) => {
|
|
@@ -49416,7 +49492,7 @@ class StateLiveAdapter {
|
|
|
49416
49492
|
const isInitial = !this.getInstance.has(key);
|
|
49417
49493
|
const instance = this.getInstance(dto.signalId, dto.bucketName, dto.initialValue);
|
|
49418
49494
|
await instance.waitForInit(isInitial);
|
|
49419
|
-
return await instance.getState();
|
|
49495
|
+
return await instance.getState(dto.when);
|
|
49420
49496
|
};
|
|
49421
49497
|
/**
|
|
49422
49498
|
* Update the state value for a signal.
|
|
@@ -49424,6 +49500,7 @@ class StateLiveAdapter {
|
|
|
49424
49500
|
* @param dto.signalId - Signal identifier
|
|
49425
49501
|
* @param dto.bucketName - Bucket name
|
|
49426
49502
|
* @param dto.initialValue - Default value when no persisted state exists
|
|
49503
|
+
* @param dto.when - Logical timestamp this value belongs to
|
|
49427
49504
|
* @returns Updated state value
|
|
49428
49505
|
*/
|
|
49429
49506
|
this.setState = async (dispatch, dto) => {
|
|
@@ -49435,7 +49512,7 @@ class StateLiveAdapter {
|
|
|
49435
49512
|
const isInitial = !this.getInstance.has(key);
|
|
49436
49513
|
const instance = this.getInstance(dto.signalId, dto.bucketName, dto.initialValue);
|
|
49437
49514
|
await instance.waitForInit(isInitial);
|
|
49438
|
-
return await instance.setState(dispatch);
|
|
49515
|
+
return await instance.setState(dispatch, dto.when);
|
|
49439
49516
|
};
|
|
49440
49517
|
/**
|
|
49441
49518
|
* Switches to in-memory adapter.
|
|
@@ -49532,6 +49609,7 @@ class StateAdapter {
|
|
|
49532
49609
|
* @param dto.bucketName - Bucket name
|
|
49533
49610
|
* @param dto.initialValue - Default value when no persisted state exists
|
|
49534
49611
|
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
49612
|
+
* @param dto.when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
49535
49613
|
* @returns Current state value
|
|
49536
49614
|
* @throws Error if adapter is not enabled
|
|
49537
49615
|
*/
|
|
@@ -49557,6 +49635,7 @@ class StateAdapter {
|
|
|
49557
49635
|
* @param dto.bucketName - Bucket name
|
|
49558
49636
|
* @param dto.initialValue - Default value when no persisted state exists
|
|
49559
49637
|
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
49638
|
+
* @param dto.when - Logical timestamp this value belongs to
|
|
49560
49639
|
* @returns Updated state value
|
|
49561
49640
|
* @throws Error if adapter is not enabled
|
|
49562
49641
|
*/
|
|
@@ -49630,8 +49709,9 @@ async function getLatestSignal(symbol) {
|
|
|
49630
49709
|
if (!MethodContextService.hasContext()) {
|
|
49631
49710
|
throw new Error("getLatestSignal requires a method context");
|
|
49632
49711
|
}
|
|
49712
|
+
const { when } = backtest.executionContextService.context;
|
|
49633
49713
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
49634
|
-
return await Recent.getLatestSignal(symbol, { exchangeName, frameName, strategyName });
|
|
49714
|
+
return await Recent.getLatestSignal(symbol, { exchangeName, frameName, strategyName }, when);
|
|
49635
49715
|
}
|
|
49636
49716
|
/**
|
|
49637
49717
|
* Returns the number of whole minutes elapsed since the latest signal's creation timestamp.
|
|
@@ -49666,8 +49746,9 @@ async function getMinutesSinceLatestSignalCreated(symbol) {
|
|
|
49666
49746
|
if (!MethodContextService.hasContext()) {
|
|
49667
49747
|
throw new Error("getMinutesSinceLatestSignalCreated requires a method context");
|
|
49668
49748
|
}
|
|
49749
|
+
const { when } = backtest.executionContextService.context;
|
|
49669
49750
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
49670
|
-
return await Recent.getMinutesSinceLatestSignalCreated(symbol, { exchangeName, frameName, strategyName });
|
|
49751
|
+
return await Recent.getMinutesSinceLatestSignalCreated(symbol, { exchangeName, frameName, strategyName }, when);
|
|
49671
49752
|
}
|
|
49672
49753
|
/**
|
|
49673
49754
|
* Reads the state value scoped to the current active signal.
|
|
@@ -49712,7 +49793,7 @@ async function getSignalState(symbol, dto) {
|
|
|
49712
49793
|
if (!MethodContextService.hasContext()) {
|
|
49713
49794
|
throw new Error("getSignalState requires a method context");
|
|
49714
49795
|
}
|
|
49715
|
-
const { backtest: isBacktest } = backtest.executionContextService.context;
|
|
49796
|
+
const { backtest: isBacktest, when } = backtest.executionContextService.context;
|
|
49716
49797
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
49717
49798
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
49718
49799
|
let signal;
|
|
@@ -49722,6 +49803,7 @@ async function getSignalState(symbol, dto) {
|
|
|
49722
49803
|
bucketName,
|
|
49723
49804
|
initialValue,
|
|
49724
49805
|
backtest: isBacktest,
|
|
49806
|
+
when,
|
|
49725
49807
|
});
|
|
49726
49808
|
}
|
|
49727
49809
|
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
@@ -49730,6 +49812,7 @@ async function getSignalState(symbol, dto) {
|
|
|
49730
49812
|
bucketName,
|
|
49731
49813
|
initialValue,
|
|
49732
49814
|
backtest: isBacktest,
|
|
49815
|
+
when,
|
|
49733
49816
|
});
|
|
49734
49817
|
}
|
|
49735
49818
|
throw new Error(`getSignalState requires a pending or scheduled signal for symbol=${symbol} bucketName=${bucketName}`);
|
|
@@ -49781,7 +49864,7 @@ async function setSignalState(symbol, dispatch, dto) {
|
|
|
49781
49864
|
if (!MethodContextService.hasContext()) {
|
|
49782
49865
|
throw new Error("setSignalState requires a method context");
|
|
49783
49866
|
}
|
|
49784
|
-
const { backtest: isBacktest } = backtest.executionContextService.context;
|
|
49867
|
+
const { backtest: isBacktest, when } = backtest.executionContextService.context;
|
|
49785
49868
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
49786
49869
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
49787
49870
|
let signal;
|
|
@@ -49791,6 +49874,7 @@ async function setSignalState(symbol, dispatch, dto) {
|
|
|
49791
49874
|
bucketName,
|
|
49792
49875
|
initialValue,
|
|
49793
49876
|
backtest: isBacktest,
|
|
49877
|
+
when,
|
|
49794
49878
|
});
|
|
49795
49879
|
}
|
|
49796
49880
|
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
@@ -49799,6 +49883,7 @@ async function setSignalState(symbol, dispatch, dto) {
|
|
|
49799
49883
|
bucketName,
|
|
49800
49884
|
initialValue,
|
|
49801
49885
|
backtest: isBacktest,
|
|
49886
|
+
when,
|
|
49802
49887
|
});
|
|
49803
49888
|
}
|
|
49804
49889
|
throw new Error(`setSignalState requires a pending or scheduled signal for symbol=${symbol} bucketName=${bucketName}`);
|
|
@@ -49852,36 +49937,47 @@ class SessionLocalInstance {
|
|
|
49852
49937
|
this.frameName = frameName;
|
|
49853
49938
|
this.backtest = backtest$1;
|
|
49854
49939
|
this._data = null;
|
|
49940
|
+
this._when = 0;
|
|
49855
49941
|
/**
|
|
49856
49942
|
* Initializes _data to null — local session needs no async setup.
|
|
49857
49943
|
* @returns Promise that resolves immediately
|
|
49858
49944
|
*/
|
|
49859
49945
|
this.waitForInit = singleshot(async (_initial) => {
|
|
49860
49946
|
this._data = null;
|
|
49947
|
+
this._when = 0;
|
|
49861
49948
|
});
|
|
49862
49949
|
/**
|
|
49863
49950
|
* Read the current in-memory session value.
|
|
49864
|
-
*
|
|
49951
|
+
* Returns null if the stored `when` is greater than the requested `when`
|
|
49952
|
+
* (look-ahead bias protection).
|
|
49953
|
+
* @param when - Logical timestamp at which the read is happening
|
|
49954
|
+
* @returns Current session value, or null
|
|
49865
49955
|
*/
|
|
49866
|
-
this.getData = async () => {
|
|
49956
|
+
this.getData = async (when) => {
|
|
49867
49957
|
backtest.loggerService.debug(SESSION_LOCAL_INSTANCE_METHOD_NAME_GET, {
|
|
49868
49958
|
strategyName: this.strategyName,
|
|
49869
49959
|
exchangeName: this.exchangeName,
|
|
49870
49960
|
frameName: this.frameName,
|
|
49871
49961
|
});
|
|
49962
|
+
if (this._when > when.getTime()) {
|
|
49963
|
+
return null;
|
|
49964
|
+
}
|
|
49872
49965
|
return this._data;
|
|
49873
49966
|
};
|
|
49874
49967
|
/**
|
|
49875
49968
|
* Update the in-memory session value.
|
|
49969
|
+
* Records `when` so future reads with a smaller `when` see no value.
|
|
49876
49970
|
* @param value - New value or null to clear
|
|
49971
|
+
* @param when - Logical timestamp this value belongs to
|
|
49877
49972
|
*/
|
|
49878
|
-
this.setData = async (value) => {
|
|
49973
|
+
this.setData = async (value, when) => {
|
|
49879
49974
|
backtest.loggerService.debug(SESSION_LOCAL_INSTANCE_METHOD_NAME_SET, {
|
|
49880
49975
|
strategyName: this.strategyName,
|
|
49881
49976
|
exchangeName: this.exchangeName,
|
|
49882
49977
|
frameName: this.frameName,
|
|
49883
49978
|
});
|
|
49884
49979
|
this._data = value;
|
|
49980
|
+
this._when = when.getTime();
|
|
49885
49981
|
};
|
|
49886
49982
|
}
|
|
49887
49983
|
/** Releases resources held by this instance. */
|
|
@@ -49917,13 +50013,13 @@ class SessionDummyInstance {
|
|
|
49917
50013
|
* No-op read — always returns null.
|
|
49918
50014
|
* @returns null
|
|
49919
50015
|
*/
|
|
49920
|
-
this.getData = async () => {
|
|
50016
|
+
this.getData = async (_when) => {
|
|
49921
50017
|
return null;
|
|
49922
50018
|
};
|
|
49923
50019
|
/**
|
|
49924
50020
|
* No-op write — discards the value.
|
|
49925
50021
|
*/
|
|
49926
|
-
this.setData = async (_value) => {
|
|
50022
|
+
this.setData = async (_value, _when) => {
|
|
49927
50023
|
};
|
|
49928
50024
|
}
|
|
49929
50025
|
/** No-op. */
|
|
@@ -49949,6 +50045,7 @@ class SessionPersistInstance {
|
|
|
49949
50045
|
this.frameName = frameName;
|
|
49950
50046
|
this.backtest = backtest$1;
|
|
49951
50047
|
this._data = null;
|
|
50048
|
+
this._when = 0;
|
|
49952
50049
|
/**
|
|
49953
50050
|
* Initialize persistence storage and restore session from disk.
|
|
49954
50051
|
* @param initial - Whether this is the first initialization
|
|
@@ -49964,35 +50061,47 @@ class SessionPersistInstance {
|
|
|
49964
50061
|
const data = await PersistSessionAdapter.readSessionData(this.strategyName, this.exchangeName, this.frameName);
|
|
49965
50062
|
if (data) {
|
|
49966
50063
|
this._data = data.data;
|
|
50064
|
+
this._when = data.when;
|
|
49967
50065
|
return;
|
|
49968
50066
|
}
|
|
49969
50067
|
this._data = null;
|
|
50068
|
+
this._when = 0;
|
|
49970
50069
|
});
|
|
49971
50070
|
/**
|
|
49972
50071
|
* Read the current persisted session value.
|
|
49973
|
-
*
|
|
50072
|
+
* Returns null if the stored `when` is greater than the requested `when`
|
|
50073
|
+
* (look-ahead bias protection).
|
|
50074
|
+
* @param when - Logical timestamp at which the read is happening
|
|
50075
|
+
* @returns Current session value, or null
|
|
49974
50076
|
*/
|
|
49975
|
-
this.getData = async () => {
|
|
50077
|
+
this.getData = async (when) => {
|
|
49976
50078
|
backtest.loggerService.debug(SESSION_PERSIST_INSTANCE_METHOD_NAME_GET, {
|
|
49977
50079
|
strategyName: this.strategyName,
|
|
49978
50080
|
exchangeName: this.exchangeName,
|
|
49979
50081
|
frameName: this.frameName,
|
|
49980
50082
|
});
|
|
50083
|
+
if (this._when > when.getTime()) {
|
|
50084
|
+
return null;
|
|
50085
|
+
}
|
|
49981
50086
|
return this._data;
|
|
49982
50087
|
};
|
|
49983
50088
|
/**
|
|
49984
50089
|
* Update session value and persist to disk atomically.
|
|
50090
|
+
* A write with a smaller `when` overwrites an existing record — that lets
|
|
50091
|
+
* a restarted backtest reset live-written state without breaking live.
|
|
49985
50092
|
* @param value - New value or null to clear
|
|
50093
|
+
* @param when - Logical timestamp this value belongs to
|
|
49986
50094
|
*/
|
|
49987
|
-
this.setData = async (value) => {
|
|
50095
|
+
this.setData = async (value, when) => {
|
|
49988
50096
|
backtest.loggerService.debug(SESSION_PERSIST_INSTANCE_METHOD_NAME_SET, {
|
|
49989
50097
|
strategyName: this.strategyName,
|
|
49990
50098
|
exchangeName: this.exchangeName,
|
|
49991
50099
|
frameName: this.frameName,
|
|
49992
50100
|
});
|
|
49993
50101
|
this._data = value;
|
|
50102
|
+
this._when = when.getTime();
|
|
49994
50103
|
const id = CREATE_KEY_FN$5(this.symbol, this.strategyName, this.exchangeName, this.frameName, this.backtest);
|
|
49995
|
-
await PersistSessionAdapter.writeSessionData({ id, data: value }, this.strategyName, this.exchangeName, this.frameName);
|
|
50104
|
+
await PersistSessionAdapter.writeSessionData({ id, data: value, when: this._when }, this.strategyName, this.exchangeName, this.frameName);
|
|
49996
50105
|
};
|
|
49997
50106
|
}
|
|
49998
50107
|
/** Releases resources held by this instance. */
|
|
@@ -50025,9 +50134,10 @@ class SessionBacktestAdapter {
|
|
|
50025
50134
|
* @param context.strategyName - Strategy identifier
|
|
50026
50135
|
* @param context.exchangeName - Exchange identifier
|
|
50027
50136
|
* @param context.frameName - Frame identifier
|
|
50028
|
-
* @
|
|
50137
|
+
* @param when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
50138
|
+
* @returns Current session value, or null if not set / look-ahead
|
|
50029
50139
|
*/
|
|
50030
|
-
this.getData = async (symbol, context) => {
|
|
50140
|
+
this.getData = async (symbol, context, when) => {
|
|
50031
50141
|
backtest.loggerService.debug(SESSION_BACKTEST_ADAPTER_METHOD_NAME_GET, {
|
|
50032
50142
|
strategyName: context.strategyName,
|
|
50033
50143
|
exchangeName: context.exchangeName,
|
|
@@ -50037,7 +50147,7 @@ class SessionBacktestAdapter {
|
|
|
50037
50147
|
const isInitial = !this.getInstance.has(key);
|
|
50038
50148
|
const instance = this.getInstance(symbol, context.strategyName, context.exchangeName, context.frameName, true);
|
|
50039
50149
|
await instance.waitForInit(isInitial);
|
|
50040
|
-
return await instance.getData();
|
|
50150
|
+
return await instance.getData(when);
|
|
50041
50151
|
};
|
|
50042
50152
|
/**
|
|
50043
50153
|
* Update the session value for a backtest run.
|
|
@@ -50046,8 +50156,9 @@ class SessionBacktestAdapter {
|
|
|
50046
50156
|
* @param context.strategyName - Strategy identifier
|
|
50047
50157
|
* @param context.exchangeName - Exchange identifier
|
|
50048
50158
|
* @param context.frameName - Frame identifier
|
|
50159
|
+
* @param when - Logical timestamp this value belongs to
|
|
50049
50160
|
*/
|
|
50050
|
-
this.setData = async (symbol, value, context) => {
|
|
50161
|
+
this.setData = async (symbol, value, context, when) => {
|
|
50051
50162
|
backtest.loggerService.debug(SESSION_BACKTEST_ADAPTER_METHOD_NAME_SET, {
|
|
50052
50163
|
strategyName: context.strategyName,
|
|
50053
50164
|
exchangeName: context.exchangeName,
|
|
@@ -50057,7 +50168,7 @@ class SessionBacktestAdapter {
|
|
|
50057
50168
|
const isInitial = !this.getInstance.has(key);
|
|
50058
50169
|
const instance = this.getInstance(symbol, context.strategyName, context.exchangeName, context.frameName, true);
|
|
50059
50170
|
await instance.waitForInit(isInitial);
|
|
50060
|
-
return await instance.setData(value);
|
|
50171
|
+
return await instance.setData(value, when);
|
|
50061
50172
|
};
|
|
50062
50173
|
/**
|
|
50063
50174
|
* Switches to in-memory adapter (default).
|
|
@@ -50121,9 +50232,10 @@ class SessionLiveAdapter {
|
|
|
50121
50232
|
* @param context.strategyName - Strategy identifier
|
|
50122
50233
|
* @param context.exchangeName - Exchange identifier
|
|
50123
50234
|
* @param context.frameName - Frame identifier
|
|
50124
|
-
* @
|
|
50235
|
+
* @param when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
50236
|
+
* @returns Current session value, or null if not set / look-ahead
|
|
50125
50237
|
*/
|
|
50126
|
-
this.getData = async (symbol, context) => {
|
|
50238
|
+
this.getData = async (symbol, context, when) => {
|
|
50127
50239
|
backtest.loggerService.debug(SESSION_LIVE_ADAPTER_METHOD_NAME_GET, {
|
|
50128
50240
|
strategyName: context.strategyName,
|
|
50129
50241
|
exchangeName: context.exchangeName,
|
|
@@ -50133,7 +50245,7 @@ class SessionLiveAdapter {
|
|
|
50133
50245
|
const isInitial = !this.getInstance.has(key);
|
|
50134
50246
|
const instance = this.getInstance(symbol, context.strategyName, context.exchangeName, context.frameName, false);
|
|
50135
50247
|
await instance.waitForInit(isInitial);
|
|
50136
|
-
return await instance.getData();
|
|
50248
|
+
return await instance.getData(when);
|
|
50137
50249
|
};
|
|
50138
50250
|
/**
|
|
50139
50251
|
* Update the session value for a live run.
|
|
@@ -50142,8 +50254,9 @@ class SessionLiveAdapter {
|
|
|
50142
50254
|
* @param context.strategyName - Strategy identifier
|
|
50143
50255
|
* @param context.exchangeName - Exchange identifier
|
|
50144
50256
|
* @param context.frameName - Frame identifier
|
|
50257
|
+
* @param when - Logical timestamp this value belongs to
|
|
50145
50258
|
*/
|
|
50146
|
-
this.setData = async (symbol, value, context) => {
|
|
50259
|
+
this.setData = async (symbol, value, context, when) => {
|
|
50147
50260
|
backtest.loggerService.debug(SESSION_LIVE_ADAPTER_METHOD_NAME_SET, {
|
|
50148
50261
|
strategyName: context.strategyName,
|
|
50149
50262
|
exchangeName: context.exchangeName,
|
|
@@ -50153,7 +50266,7 @@ class SessionLiveAdapter {
|
|
|
50153
50266
|
const isInitial = !this.getInstance.has(key);
|
|
50154
50267
|
const instance = this.getInstance(symbol, context.strategyName, context.exchangeName, context.frameName, false);
|
|
50155
50268
|
await instance.waitForInit(isInitial);
|
|
50156
|
-
return await instance.setData(value);
|
|
50269
|
+
return await instance.setData(value, when);
|
|
50157
50270
|
};
|
|
50158
50271
|
/**
|
|
50159
50272
|
* Switches to in-memory adapter.
|
|
@@ -50213,9 +50326,10 @@ class SessionAdapter {
|
|
|
50213
50326
|
* @param context.exchangeName - Exchange identifier
|
|
50214
50327
|
* @param context.frameName - Frame identifier
|
|
50215
50328
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
50216
|
-
* @
|
|
50329
|
+
* @param when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
50330
|
+
* @returns Current session value, or null if not set / look-ahead
|
|
50217
50331
|
*/
|
|
50218
|
-
this.getData = async (symbol, context, backtest$1) => {
|
|
50332
|
+
this.getData = async (symbol, context, backtest$1, when) => {
|
|
50219
50333
|
backtest.loggerService.debug(SESSION_ADAPTER_METHOD_NAME_GET, {
|
|
50220
50334
|
strategyName: context.strategyName,
|
|
50221
50335
|
exchangeName: context.exchangeName,
|
|
@@ -50223,9 +50337,9 @@ class SessionAdapter {
|
|
|
50223
50337
|
backtest: backtest$1,
|
|
50224
50338
|
});
|
|
50225
50339
|
if (backtest$1) {
|
|
50226
|
-
return await SessionBacktest.getData(symbol, context);
|
|
50340
|
+
return await SessionBacktest.getData(symbol, context, when);
|
|
50227
50341
|
}
|
|
50228
|
-
return await SessionLive.getData(symbol, context);
|
|
50342
|
+
return await SessionLive.getData(symbol, context, when);
|
|
50229
50343
|
};
|
|
50230
50344
|
/**
|
|
50231
50345
|
* Update the session value for a signal.
|
|
@@ -50236,8 +50350,9 @@ class SessionAdapter {
|
|
|
50236
50350
|
* @param context.exchangeName - Exchange identifier
|
|
50237
50351
|
* @param context.frameName - Frame identifier
|
|
50238
50352
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
50353
|
+
* @param when - Logical timestamp this value belongs to
|
|
50239
50354
|
*/
|
|
50240
|
-
this.setData = async (symbol, value, context, backtest$1) => {
|
|
50355
|
+
this.setData = async (symbol, value, context, backtest$1, when) => {
|
|
50241
50356
|
backtest.loggerService.debug(SESSION_ADAPTER_METHOD_NAME_SET, {
|
|
50242
50357
|
strategyName: context.strategyName,
|
|
50243
50358
|
exchangeName: context.exchangeName,
|
|
@@ -50245,9 +50360,9 @@ class SessionAdapter {
|
|
|
50245
50360
|
backtest: backtest$1,
|
|
50246
50361
|
});
|
|
50247
50362
|
if (backtest$1) {
|
|
50248
|
-
return await SessionBacktest.setData(symbol, value, context);
|
|
50363
|
+
return await SessionBacktest.setData(symbol, value, context, when);
|
|
50249
50364
|
}
|
|
50250
|
-
return await SessionLive.setData(symbol, value, context);
|
|
50365
|
+
return await SessionLive.setData(symbol, value, context, when);
|
|
50251
50366
|
};
|
|
50252
50367
|
}
|
|
50253
50368
|
}
|
|
@@ -50299,9 +50414,9 @@ async function getSessionData(symbol) {
|
|
|
50299
50414
|
if (!MethodContextService.hasContext()) {
|
|
50300
50415
|
throw new Error("getSession requires a method context");
|
|
50301
50416
|
}
|
|
50302
|
-
const { backtest: isBacktest } = backtest.executionContextService.context;
|
|
50417
|
+
const { backtest: isBacktest, when } = backtest.executionContextService.context;
|
|
50303
50418
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
50304
|
-
return await Session.getData(symbol, { exchangeName, frameName, strategyName }, isBacktest);
|
|
50419
|
+
return await Session.getData(symbol, { exchangeName, frameName, strategyName }, isBacktest, when);
|
|
50305
50420
|
}
|
|
50306
50421
|
/**
|
|
50307
50422
|
* Writes a session value scoped to the current (symbol, strategy, exchange, frame) context.
|
|
@@ -50333,9 +50448,9 @@ async function setSessionData(symbol, value) {
|
|
|
50333
50448
|
if (!MethodContextService.hasContext()) {
|
|
50334
50449
|
throw new Error("setSession requires a method context");
|
|
50335
50450
|
}
|
|
50336
|
-
const { backtest: isBacktest } = backtest.executionContextService.context;
|
|
50451
|
+
const { backtest: isBacktest, when } = backtest.executionContextService.context;
|
|
50337
50452
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
50338
|
-
await Session.setData(symbol, value, { exchangeName, frameName, strategyName }, isBacktest);
|
|
50453
|
+
await Session.setData(symbol, value, { exchangeName, frameName, strategyName }, isBacktest, when);
|
|
50339
50454
|
}
|
|
50340
50455
|
|
|
50341
50456
|
const CREATE_SIGNAL_STATE_METHOD_NAME = "state.createSignalState";
|
|
@@ -50346,7 +50461,7 @@ const CREATE_SET_STATE_FN = (params) => async (symbol, dispatch) => {
|
|
|
50346
50461
|
if (!MethodContextService.hasContext()) {
|
|
50347
50462
|
throw new Error("createSignalState requires a method context");
|
|
50348
50463
|
}
|
|
50349
|
-
const { backtest: isBacktest } = backtest.executionContextService.context;
|
|
50464
|
+
const { backtest: isBacktest, when } = backtest.executionContextService.context;
|
|
50350
50465
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
50351
50466
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
50352
50467
|
let signal;
|
|
@@ -50356,6 +50471,7 @@ const CREATE_SET_STATE_FN = (params) => async (symbol, dispatch) => {
|
|
|
50356
50471
|
bucketName: params.bucketName,
|
|
50357
50472
|
initialValue: params.initialValue,
|
|
50358
50473
|
signalId: signal.id,
|
|
50474
|
+
when,
|
|
50359
50475
|
});
|
|
50360
50476
|
}
|
|
50361
50477
|
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
@@ -50364,6 +50480,7 @@ const CREATE_SET_STATE_FN = (params) => async (symbol, dispatch) => {
|
|
|
50364
50480
|
bucketName: params.bucketName,
|
|
50365
50481
|
initialValue: params.initialValue,
|
|
50366
50482
|
signalId: signal.id,
|
|
50483
|
+
when,
|
|
50367
50484
|
});
|
|
50368
50485
|
}
|
|
50369
50486
|
throw new Error(`createSignalState requires a pending or scheduled signal for symbol=${symbol} bucketName=${params.bucketName}`);
|
|
@@ -50375,7 +50492,7 @@ const CREATE_GET_STATE_FN = (params) => async (symbol) => {
|
|
|
50375
50492
|
if (!MethodContextService.hasContext()) {
|
|
50376
50493
|
throw new Error("createSignalState requires a method context");
|
|
50377
50494
|
}
|
|
50378
|
-
const { backtest: isBacktest } = backtest.executionContextService.context;
|
|
50495
|
+
const { backtest: isBacktest, when } = backtest.executionContextService.context;
|
|
50379
50496
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
50380
50497
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
50381
50498
|
let signal;
|
|
@@ -50385,6 +50502,7 @@ const CREATE_GET_STATE_FN = (params) => async (symbol) => {
|
|
|
50385
50502
|
bucketName: params.bucketName,
|
|
50386
50503
|
initialValue: params.initialValue,
|
|
50387
50504
|
signalId: signal.id,
|
|
50505
|
+
when,
|
|
50388
50506
|
});
|
|
50389
50507
|
}
|
|
50390
50508
|
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
@@ -50393,6 +50511,7 @@ const CREATE_GET_STATE_FN = (params) => async (symbol) => {
|
|
|
50393
50511
|
bucketName: params.bucketName,
|
|
50394
50512
|
initialValue: params.initialValue,
|
|
50395
50513
|
signalId: signal.id,
|
|
50514
|
+
when,
|
|
50396
50515
|
});
|
|
50397
50516
|
}
|
|
50398
50517
|
throw new Error(`createSignalState requires a pending or scheduled signal for symbol=${symbol} bucketName=${params.bucketName}`);
|
|
@@ -50475,7 +50594,7 @@ const createSearchIndex = () => {
|
|
|
50475
50594
|
df.set(term, count);
|
|
50476
50595
|
});
|
|
50477
50596
|
};
|
|
50478
|
-
const upsert = ({ id, content, index = JSON.stringify(content), priority = Date.now(), }) => {
|
|
50597
|
+
const upsert = ({ id, content, when, index = JSON.stringify(content), priority = Date.now(), }) => {
|
|
50479
50598
|
const existing = docs.get(id);
|
|
50480
50599
|
{
|
|
50481
50600
|
existing && subtractDf(existing.tf);
|
|
@@ -50484,12 +50603,21 @@ const createSearchIndex = () => {
|
|
|
50484
50603
|
const tf = new Map();
|
|
50485
50604
|
for (const t of tokens)
|
|
50486
50605
|
tf.set(t, (tf.get(t) ?? 0) + 1);
|
|
50487
|
-
docs.set(id, { tf, len: tokens.length, content, priority });
|
|
50606
|
+
docs.set(id, { tf, len: tokens.length, content, priority, when });
|
|
50488
50607
|
{
|
|
50489
50608
|
addDf(tf);
|
|
50490
50609
|
}
|
|
50491
50610
|
};
|
|
50492
|
-
|
|
50611
|
+
/**
|
|
50612
|
+
* Read a document by id. Returns undefined when the document was written
|
|
50613
|
+
* with a `when` greater than the requested `when` (look-ahead guard).
|
|
50614
|
+
*/
|
|
50615
|
+
const read = (id, when) => {
|
|
50616
|
+
const doc = docs.get(id);
|
|
50617
|
+
if (!doc || doc.when > when)
|
|
50618
|
+
return undefined;
|
|
50619
|
+
return doc.content;
|
|
50620
|
+
};
|
|
50493
50621
|
const remove = (id) => {
|
|
50494
50622
|
{
|
|
50495
50623
|
const existing = docs.get(id);
|
|
@@ -50497,16 +50625,30 @@ const createSearchIndex = () => {
|
|
|
50497
50625
|
}
|
|
50498
50626
|
docs.delete(id);
|
|
50499
50627
|
};
|
|
50500
|
-
|
|
50628
|
+
/**
|
|
50629
|
+
* List documents whose `when` is less than or equal to the requested `when`
|
|
50630
|
+
* (look-ahead guard), sorted by priority.
|
|
50631
|
+
*/
|
|
50632
|
+
const list = (when) => Array.from(docs.entries())
|
|
50633
|
+
.filter(([, doc]) => doc.when <= when)
|
|
50501
50634
|
.sort(([, a], [, b]) => a.priority - b.priority)
|
|
50502
50635
|
.map(([id, { content }]) => ({ id, content }));
|
|
50503
|
-
|
|
50636
|
+
/**
|
|
50637
|
+
* BM25 search over documents whose `when` is less than or equal to the
|
|
50638
|
+
* requested `when` (look-ahead guard).
|
|
50639
|
+
*
|
|
50640
|
+
* Document frequency (df) is computed across the whole index — the time-cut
|
|
50641
|
+
* is applied only to the candidate set, so scores stay comparable across
|
|
50642
|
+
* different `when` values.
|
|
50643
|
+
*/
|
|
50644
|
+
const search = (query, when, settings = DEFAULT_SETTINGS) => {
|
|
50504
50645
|
const terms = tokenize(query);
|
|
50505
50646
|
if (!terms.length || !docs.size)
|
|
50506
50647
|
return [];
|
|
50507
50648
|
const N = docs.size;
|
|
50508
50649
|
const avgLen = [...docs.values()].reduce((s, d) => s + d.len, 0) / N;
|
|
50509
50650
|
return [...docs.entries()]
|
|
50651
|
+
.filter(([, doc]) => doc.when <= when)
|
|
50510
50652
|
.map(([id, doc]) => {
|
|
50511
50653
|
let score = 0;
|
|
50512
50654
|
for (const term of terms) {
|
|
@@ -50603,8 +50745,9 @@ class MemoryLocalInstance {
|
|
|
50603
50745
|
* @param memoryId - Unique entry identifier
|
|
50604
50746
|
* @param value - Value to store and index
|
|
50605
50747
|
* @param description - BM25 index string
|
|
50748
|
+
* @param when - Logical timestamp this entry belongs to (look-ahead guard)
|
|
50606
50749
|
*/
|
|
50607
|
-
async writeMemory(memoryId, value, description) {
|
|
50750
|
+
async writeMemory(memoryId, value, description, when) {
|
|
50608
50751
|
backtest.loggerService.debug(MEMORY_LOCAL_INSTANCE_METHOD_NAME_WRITE, {
|
|
50609
50752
|
signalId: this.signalId,
|
|
50610
50753
|
bucketName: this.bucketName,
|
|
@@ -50615,21 +50758,24 @@ class MemoryLocalInstance {
|
|
|
50615
50758
|
content: value,
|
|
50616
50759
|
index: description,
|
|
50617
50760
|
priority: Date.now(),
|
|
50761
|
+
when: when.getTime(),
|
|
50618
50762
|
});
|
|
50619
50763
|
}
|
|
50620
50764
|
/**
|
|
50621
50765
|
* Read a single entry from the in-memory index.
|
|
50766
|
+
* Behaves as not-found if the stored `when` is greater than the requested `when`.
|
|
50622
50767
|
* @param memoryId - Unique entry identifier
|
|
50768
|
+
* @param when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
50623
50769
|
* @returns Parsed entry value
|
|
50624
|
-
* @throws Error if entry not found
|
|
50770
|
+
* @throws Error if entry not found (or shadowed by look-ahead)
|
|
50625
50771
|
*/
|
|
50626
|
-
async readMemory(memoryId) {
|
|
50772
|
+
async readMemory(memoryId, when) {
|
|
50627
50773
|
backtest.loggerService.debug(MEMORY_LOCAL_INSTANCE_METHOD_NAME_READ, {
|
|
50628
50774
|
signalId: this.signalId,
|
|
50629
50775
|
bucketName: this.bucketName,
|
|
50630
50776
|
memoryId,
|
|
50631
50777
|
});
|
|
50632
|
-
const value = this._index.read(memoryId);
|
|
50778
|
+
const value = this._index.read(memoryId, when.getTime());
|
|
50633
50779
|
if (!value) {
|
|
50634
50780
|
throw new Error(`MemoryLocalInstance value not found memoryId=${memoryId}`);
|
|
50635
50781
|
}
|
|
@@ -50637,33 +50783,38 @@ class MemoryLocalInstance {
|
|
|
50637
50783
|
}
|
|
50638
50784
|
/**
|
|
50639
50785
|
* Search entries using BM25 full-text scoring.
|
|
50786
|
+
* Filters out entries whose `when` is greater than the requested `when`.
|
|
50640
50787
|
* @param query - Search query string
|
|
50788
|
+
* @param when - Logical timestamp at which the search is happening (look-ahead guard)
|
|
50641
50789
|
* @returns Matching entries sorted by relevance score
|
|
50642
50790
|
*/
|
|
50643
|
-
async searchMemory(query, settings) {
|
|
50791
|
+
async searchMemory(query, when, settings) {
|
|
50644
50792
|
backtest.loggerService.debug(MEMORY_LOCAL_INSTANCE_METHOD_NAME_SEARCH, {
|
|
50645
50793
|
signalId: this.signalId,
|
|
50646
50794
|
bucketName: this.bucketName,
|
|
50647
50795
|
query,
|
|
50648
50796
|
});
|
|
50649
|
-
return this._index.search(query, settings).map(SEARCH_MEMORY_FN);
|
|
50797
|
+
return this._index.search(query, when.getTime(), settings).map(SEARCH_MEMORY_FN);
|
|
50650
50798
|
}
|
|
50651
50799
|
/**
|
|
50652
50800
|
* List all entries stored in the in-memory index.
|
|
50801
|
+
* Filters out entries whose `when` is greater than the requested `when`.
|
|
50802
|
+
* @param when - Logical timestamp at which the list is happening (look-ahead guard)
|
|
50653
50803
|
* @returns Array of all stored entries
|
|
50654
50804
|
*/
|
|
50655
|
-
async listMemory() {
|
|
50805
|
+
async listMemory(when) {
|
|
50656
50806
|
backtest.loggerService.debug(MEMORY_LOCAL_INSTANCE_METHOD_NAME_LIST, {
|
|
50657
50807
|
signalId: this.signalId,
|
|
50658
50808
|
bucketName: this.bucketName,
|
|
50659
50809
|
});
|
|
50660
|
-
return this._index.list().map(LIST_MEMORY_FN);
|
|
50810
|
+
return this._index.list(when.getTime()).map(LIST_MEMORY_FN);
|
|
50661
50811
|
}
|
|
50662
50812
|
/**
|
|
50663
50813
|
* Remove an entry from the in-memory index.
|
|
50664
50814
|
* @param memoryId - Unique entry identifier
|
|
50815
|
+
* @param when - Logical timestamp (kept for API consistency; removal is by UUID)
|
|
50665
50816
|
*/
|
|
50666
|
-
async removeMemory(memoryId) {
|
|
50817
|
+
async removeMemory(memoryId, _when) {
|
|
50667
50818
|
backtest.loggerService.debug(MEMORY_LOCAL_INSTANCE_METHOD_NAME_REMOVE, {
|
|
50668
50819
|
signalId: this.signalId,
|
|
50669
50820
|
bucketName: this.bucketName,
|
|
@@ -50709,12 +50860,13 @@ class MemoryPersistInstance {
|
|
|
50709
50860
|
initial,
|
|
50710
50861
|
});
|
|
50711
50862
|
await PersistMemoryAdapter.waitForInit(this.signalId, this.bucketName, initial);
|
|
50712
|
-
for await (const { memoryId, data: { data, index, priority } } of PersistMemoryAdapter.listMemoryData(this.signalId, this.bucketName)) {
|
|
50863
|
+
for await (const { memoryId, data: { data, index, priority, when } } of PersistMemoryAdapter.listMemoryData(this.signalId, this.bucketName)) {
|
|
50713
50864
|
this._index.upsert({
|
|
50714
50865
|
id: memoryId,
|
|
50715
50866
|
content: data,
|
|
50716
50867
|
index,
|
|
50717
50868
|
priority,
|
|
50869
|
+
when,
|
|
50718
50870
|
});
|
|
50719
50871
|
}
|
|
50720
50872
|
}
|
|
@@ -50723,69 +50875,79 @@ class MemoryPersistInstance {
|
|
|
50723
50875
|
* @param memoryId - Unique entry identifier
|
|
50724
50876
|
* @param value - Value to persist and index
|
|
50725
50877
|
* @param index - BM25 index string; defaults to JSON.stringify(value)
|
|
50878
|
+
* @param when - Logical timestamp this entry belongs to (look-ahead guard)
|
|
50726
50879
|
*/
|
|
50727
|
-
async writeMemory(memoryId, value, index
|
|
50880
|
+
async writeMemory(memoryId, value, index, when) {
|
|
50728
50881
|
backtest.loggerService.debug(MEMORY_PERSIST_INSTANCE_METHOD_NAME_WRITE, {
|
|
50729
50882
|
signalId: this.signalId,
|
|
50730
50883
|
bucketName: this.bucketName,
|
|
50731
50884
|
memoryId,
|
|
50732
50885
|
});
|
|
50733
50886
|
const priority = Date.now();
|
|
50734
|
-
|
|
50887
|
+
const whenMs = when.getTime();
|
|
50888
|
+
await PersistMemoryAdapter.writeMemoryData({ data: value, priority, removed: false, index, when: whenMs }, this.signalId, this.bucketName, memoryId);
|
|
50735
50889
|
this._index.upsert({
|
|
50736
50890
|
id: memoryId,
|
|
50737
50891
|
content: value,
|
|
50738
50892
|
index,
|
|
50739
50893
|
priority,
|
|
50894
|
+
when: whenMs,
|
|
50740
50895
|
});
|
|
50741
50896
|
}
|
|
50742
50897
|
/**
|
|
50743
50898
|
* Read a single entry from disk.
|
|
50899
|
+
* Behaves as not-found if the stored `when` is greater than the requested `when`.
|
|
50744
50900
|
* @param memoryId - Unique entry identifier
|
|
50901
|
+
* @param when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
50745
50902
|
* @returns Entry value
|
|
50746
|
-
* @throws Error if entry not found
|
|
50903
|
+
* @throws Error if entry not found (or shadowed by look-ahead)
|
|
50747
50904
|
*/
|
|
50748
|
-
async readMemory(memoryId) {
|
|
50905
|
+
async readMemory(memoryId, when) {
|
|
50749
50906
|
backtest.loggerService.debug(MEMORY_PERSIST_INSTANCE_METHOD_NAME_READ, {
|
|
50750
50907
|
signalId: this.signalId,
|
|
50751
50908
|
bucketName: this.bucketName,
|
|
50752
50909
|
memoryId,
|
|
50753
50910
|
});
|
|
50754
50911
|
const data = await PersistMemoryAdapter.readMemoryData(this.signalId, this.bucketName, memoryId);
|
|
50755
|
-
if (!data) {
|
|
50912
|
+
if (!data || data.when > when.getTime()) {
|
|
50756
50913
|
throw new Error(`MemoryPersistInstance value not found memoryId=${memoryId}`);
|
|
50757
50914
|
}
|
|
50758
50915
|
return data.data;
|
|
50759
50916
|
}
|
|
50760
50917
|
/**
|
|
50761
50918
|
* Search entries using BM25 index rebuilt from disk on init.
|
|
50919
|
+
* Filters out entries whose `when` is greater than the requested `when`.
|
|
50762
50920
|
* @param query - Search query string
|
|
50921
|
+
* @param when - Logical timestamp at which the search is happening (look-ahead guard)
|
|
50763
50922
|
* @returns Matching entries sorted by relevance score
|
|
50764
50923
|
*/
|
|
50765
|
-
async searchMemory(query, settings) {
|
|
50924
|
+
async searchMemory(query, when, settings) {
|
|
50766
50925
|
backtest.loggerService.debug(MEMORY_PERSIST_INSTANCE_METHOD_NAME_SEARCH, {
|
|
50767
50926
|
signalId: this.signalId,
|
|
50768
50927
|
bucketName: this.bucketName,
|
|
50769
50928
|
query,
|
|
50770
50929
|
});
|
|
50771
|
-
return this._index.search(query, settings).map(SEARCH_MEMORY_FN);
|
|
50930
|
+
return this._index.search(query, when.getTime(), settings).map(SEARCH_MEMORY_FN);
|
|
50772
50931
|
}
|
|
50773
50932
|
/**
|
|
50774
50933
|
* List all entries from the in-memory index (populated from disk on init).
|
|
50934
|
+
* Filters out entries whose `when` is greater than the requested `when`.
|
|
50935
|
+
* @param when - Logical timestamp at which the list is happening (look-ahead guard)
|
|
50775
50936
|
* @returns Array of all stored entries
|
|
50776
50937
|
*/
|
|
50777
|
-
async listMemory() {
|
|
50938
|
+
async listMemory(when) {
|
|
50778
50939
|
backtest.loggerService.debug(MEMORY_PERSIST_INSTANCE_METHOD_NAME_LIST, {
|
|
50779
50940
|
signalId: this.signalId,
|
|
50780
50941
|
bucketName: this.bucketName,
|
|
50781
50942
|
});
|
|
50782
|
-
return this._index.list().map(LIST_MEMORY_FN);
|
|
50943
|
+
return this._index.list(when.getTime()).map(LIST_MEMORY_FN);
|
|
50783
50944
|
}
|
|
50784
50945
|
/**
|
|
50785
50946
|
* Remove an entry from disk and from the BM25 index.
|
|
50786
50947
|
* @param memoryId - Unique entry identifier
|
|
50948
|
+
* @param when - Logical timestamp (kept for API consistency; removal is by UUID)
|
|
50787
50949
|
*/
|
|
50788
|
-
async removeMemory(memoryId) {
|
|
50950
|
+
async removeMemory(memoryId, _when) {
|
|
50789
50951
|
backtest.loggerService.debug(MEMORY_PERSIST_INSTANCE_METHOD_NAME_REMOVE, {
|
|
50790
50952
|
signalId: this.signalId,
|
|
50791
50953
|
bucketName: this.bucketName,
|
|
@@ -50822,34 +50984,34 @@ class MemoryDummyInstance {
|
|
|
50822
50984
|
* No-op write - discards the value.
|
|
50823
50985
|
* @returns Promise that resolves immediately
|
|
50824
50986
|
*/
|
|
50825
|
-
async writeMemory() {
|
|
50987
|
+
async writeMemory(_memoryId, _value, _description, _when) {
|
|
50826
50988
|
}
|
|
50827
50989
|
/**
|
|
50828
50990
|
* No-op read - always throws.
|
|
50829
50991
|
* @throws Error always
|
|
50830
50992
|
*/
|
|
50831
|
-
async readMemory(_memoryId) {
|
|
50993
|
+
async readMemory(_memoryId, _when) {
|
|
50832
50994
|
throw new Error("MemoryDummyInstance: readMemory not supported");
|
|
50833
50995
|
}
|
|
50834
50996
|
/**
|
|
50835
50997
|
* No-op search - returns empty array.
|
|
50836
50998
|
* @returns Empty array
|
|
50837
50999
|
*/
|
|
50838
|
-
async searchMemory() {
|
|
51000
|
+
async searchMemory(_query, _when, _settings) {
|
|
50839
51001
|
return [];
|
|
50840
51002
|
}
|
|
50841
51003
|
/**
|
|
50842
51004
|
* No-op list - returns empty array.
|
|
50843
51005
|
* @returns Empty array
|
|
50844
51006
|
*/
|
|
50845
|
-
async listMemory() {
|
|
51007
|
+
async listMemory(_when) {
|
|
50846
51008
|
return [];
|
|
50847
51009
|
}
|
|
50848
51010
|
/**
|
|
50849
51011
|
* No-op remove.
|
|
50850
51012
|
* @returns Promise that resolves immediately
|
|
50851
51013
|
*/
|
|
50852
|
-
async removeMemory() {
|
|
51014
|
+
async removeMemory(_memoryId, _when) {
|
|
50853
51015
|
}
|
|
50854
51016
|
/** No-op. */
|
|
50855
51017
|
dispose() {
|
|
@@ -50893,6 +51055,7 @@ class MemoryBacktestAdapter {
|
|
|
50893
51055
|
* @param dto.signalId - Signal identifier
|
|
50894
51056
|
* @param dto.bucketName - Bucket name
|
|
50895
51057
|
* @param dto.description - BM25 index string; defaults to JSON.stringify(value)
|
|
51058
|
+
* @param dto.when - Logical timestamp this entry belongs to (look-ahead guard)
|
|
50896
51059
|
*/
|
|
50897
51060
|
this.writeMemory = async (dto) => {
|
|
50898
51061
|
backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_WRITE, {
|
|
@@ -50904,13 +51067,14 @@ class MemoryBacktestAdapter {
|
|
|
50904
51067
|
const isInitial = !this.getInstance.has(key);
|
|
50905
51068
|
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
50906
51069
|
await instance.waitForInit(isInitial);
|
|
50907
|
-
return await instance.writeMemory(dto.memoryId, dto.value, dto.description);
|
|
51070
|
+
return await instance.writeMemory(dto.memoryId, dto.value, dto.description, dto.when);
|
|
50908
51071
|
};
|
|
50909
51072
|
/**
|
|
50910
51073
|
* Search memory using BM25 full-text scoring.
|
|
50911
51074
|
* @param dto.query - Search query string
|
|
50912
51075
|
* @param dto.signalId - Signal identifier
|
|
50913
51076
|
* @param dto.bucketName - Bucket name
|
|
51077
|
+
* @param dto.when - Logical timestamp at which the search is happening (look-ahead guard)
|
|
50914
51078
|
* @returns Matching entries sorted by relevance score
|
|
50915
51079
|
*/
|
|
50916
51080
|
this.searchMemory = async (dto) => {
|
|
@@ -50923,12 +51087,13 @@ class MemoryBacktestAdapter {
|
|
|
50923
51087
|
const isInitial = !this.getInstance.has(key);
|
|
50924
51088
|
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
50925
51089
|
await instance.waitForInit(isInitial);
|
|
50926
|
-
return await instance.searchMemory(dto.query, dto.settings);
|
|
51090
|
+
return await instance.searchMemory(dto.query, dto.when, dto.settings);
|
|
50927
51091
|
};
|
|
50928
51092
|
/**
|
|
50929
51093
|
* List all entries in memory.
|
|
50930
51094
|
* @param dto.signalId - Signal identifier
|
|
50931
51095
|
* @param dto.bucketName - Bucket name
|
|
51096
|
+
* @param dto.when - Logical timestamp at which the list is happening (look-ahead guard)
|
|
50932
51097
|
* @returns Array of all stored entries
|
|
50933
51098
|
*/
|
|
50934
51099
|
this.listMemory = async (dto) => {
|
|
@@ -50940,13 +51105,14 @@ class MemoryBacktestAdapter {
|
|
|
50940
51105
|
const isInitial = !this.getInstance.has(key);
|
|
50941
51106
|
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
50942
51107
|
await instance.waitForInit(isInitial);
|
|
50943
|
-
return await instance.listMemory();
|
|
51108
|
+
return await instance.listMemory(dto.when);
|
|
50944
51109
|
};
|
|
50945
51110
|
/**
|
|
50946
51111
|
* Remove an entry from memory.
|
|
50947
51112
|
* @param dto.memoryId - Unique entry identifier
|
|
50948
51113
|
* @param dto.signalId - Signal identifier
|
|
50949
51114
|
* @param dto.bucketName - Bucket name
|
|
51115
|
+
* @param dto.when - Logical timestamp (kept for API consistency; removal is by UUID)
|
|
50950
51116
|
*/
|
|
50951
51117
|
this.removeMemory = async (dto) => {
|
|
50952
51118
|
backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_REMOVE, {
|
|
@@ -50958,13 +51124,14 @@ class MemoryBacktestAdapter {
|
|
|
50958
51124
|
const isInitial = !this.getInstance.has(key);
|
|
50959
51125
|
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
50960
51126
|
await instance.waitForInit(isInitial);
|
|
50961
|
-
return await instance.removeMemory(dto.memoryId);
|
|
51127
|
+
return await instance.removeMemory(dto.memoryId, dto.when);
|
|
50962
51128
|
};
|
|
50963
51129
|
/**
|
|
50964
51130
|
* Read a single entry from memory.
|
|
50965
51131
|
* @param dto.memoryId - Unique entry identifier
|
|
50966
51132
|
* @param dto.signalId - Signal identifier
|
|
50967
51133
|
* @param dto.bucketName - Bucket name
|
|
51134
|
+
* @param dto.when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
50968
51135
|
* @returns Entry value
|
|
50969
51136
|
* @throws Error if entry not found
|
|
50970
51137
|
*/
|
|
@@ -50978,7 +51145,7 @@ class MemoryBacktestAdapter {
|
|
|
50978
51145
|
const isInitial = !this.getInstance.has(key);
|
|
50979
51146
|
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
50980
51147
|
await instance.waitForInit(isInitial);
|
|
50981
|
-
return await instance.readMemory(dto.memoryId);
|
|
51148
|
+
return await instance.readMemory(dto.memoryId, dto.when);
|
|
50982
51149
|
};
|
|
50983
51150
|
/**
|
|
50984
51151
|
* Switches to in-memory BM25 adapter (default).
|
|
@@ -51060,6 +51227,7 @@ class MemoryLiveAdapter {
|
|
|
51060
51227
|
* @param dto.signalId - Signal identifier
|
|
51061
51228
|
* @param dto.bucketName - Bucket name
|
|
51062
51229
|
* @param dto.description - BM25 index string; defaults to JSON.stringify(value)
|
|
51230
|
+
* @param dto.when - Logical timestamp this entry belongs to (look-ahead guard)
|
|
51063
51231
|
*/
|
|
51064
51232
|
this.writeMemory = async (dto) => {
|
|
51065
51233
|
backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_WRITE, {
|
|
@@ -51071,13 +51239,14 @@ class MemoryLiveAdapter {
|
|
|
51071
51239
|
const isInitial = !this.getInstance.has(key);
|
|
51072
51240
|
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
51073
51241
|
await instance.waitForInit(isInitial);
|
|
51074
|
-
return await instance.writeMemory(dto.memoryId, dto.value, dto.description);
|
|
51242
|
+
return await instance.writeMemory(dto.memoryId, dto.value, dto.description, dto.when);
|
|
51075
51243
|
};
|
|
51076
51244
|
/**
|
|
51077
51245
|
* Search memory using BM25 full-text scoring.
|
|
51078
51246
|
* @param dto.query - Search query string
|
|
51079
51247
|
* @param dto.signalId - Signal identifier
|
|
51080
51248
|
* @param dto.bucketName - Bucket name
|
|
51249
|
+
* @param dto.when - Logical timestamp at which the search is happening (look-ahead guard)
|
|
51081
51250
|
* @returns Matching entries sorted by relevance score
|
|
51082
51251
|
*/
|
|
51083
51252
|
this.searchMemory = async (dto) => {
|
|
@@ -51090,12 +51259,13 @@ class MemoryLiveAdapter {
|
|
|
51090
51259
|
const isInitial = !this.getInstance.has(key);
|
|
51091
51260
|
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
51092
51261
|
await instance.waitForInit(isInitial);
|
|
51093
|
-
return await instance.searchMemory(dto.query, dto.settings);
|
|
51262
|
+
return await instance.searchMemory(dto.query, dto.when, dto.settings);
|
|
51094
51263
|
};
|
|
51095
51264
|
/**
|
|
51096
51265
|
* List all entries in memory.
|
|
51097
51266
|
* @param dto.signalId - Signal identifier
|
|
51098
51267
|
* @param dto.bucketName - Bucket name
|
|
51268
|
+
* @param dto.when - Logical timestamp at which the list is happening (look-ahead guard)
|
|
51099
51269
|
* @returns Array of all stored entries
|
|
51100
51270
|
*/
|
|
51101
51271
|
this.listMemory = async (dto) => {
|
|
@@ -51107,13 +51277,14 @@ class MemoryLiveAdapter {
|
|
|
51107
51277
|
const isInitial = !this.getInstance.has(key);
|
|
51108
51278
|
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
51109
51279
|
await instance.waitForInit(isInitial);
|
|
51110
|
-
return await instance.listMemory();
|
|
51280
|
+
return await instance.listMemory(dto.when);
|
|
51111
51281
|
};
|
|
51112
51282
|
/**
|
|
51113
51283
|
* Remove an entry from memory.
|
|
51114
51284
|
* @param dto.memoryId - Unique entry identifier
|
|
51115
51285
|
* @param dto.signalId - Signal identifier
|
|
51116
51286
|
* @param dto.bucketName - Bucket name
|
|
51287
|
+
* @param dto.when - Logical timestamp (kept for API consistency; removal is by UUID)
|
|
51117
51288
|
*/
|
|
51118
51289
|
this.removeMemory = async (dto) => {
|
|
51119
51290
|
backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_REMOVE, {
|
|
@@ -51125,13 +51296,14 @@ class MemoryLiveAdapter {
|
|
|
51125
51296
|
const isInitial = !this.getInstance.has(key);
|
|
51126
51297
|
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
51127
51298
|
await instance.waitForInit(isInitial);
|
|
51128
|
-
return await instance.removeMemory(dto.memoryId);
|
|
51299
|
+
return await instance.removeMemory(dto.memoryId, dto.when);
|
|
51129
51300
|
};
|
|
51130
51301
|
/**
|
|
51131
51302
|
* Read a single entry from memory.
|
|
51132
51303
|
* @param dto.memoryId - Unique entry identifier
|
|
51133
51304
|
* @param dto.signalId - Signal identifier
|
|
51134
51305
|
* @param dto.bucketName - Bucket name
|
|
51306
|
+
* @param dto.when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
51135
51307
|
* @returns Entry value
|
|
51136
51308
|
* @throws Error if entry not found
|
|
51137
51309
|
*/
|
|
@@ -51145,7 +51317,7 @@ class MemoryLiveAdapter {
|
|
|
51145
51317
|
const isInitial = !this.getInstance.has(key);
|
|
51146
51318
|
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
51147
51319
|
await instance.waitForInit(isInitial);
|
|
51148
|
-
return await instance.readMemory(dto.memoryId);
|
|
51320
|
+
return await instance.readMemory(dto.memoryId, dto.when);
|
|
51149
51321
|
};
|
|
51150
51322
|
/**
|
|
51151
51323
|
* Switches to in-memory BM25 adapter.
|
|
@@ -51244,6 +51416,7 @@ class MemoryAdapter {
|
|
|
51244
51416
|
* @param dto.bucketName - Bucket name
|
|
51245
51417
|
* @param dto.description - BM25 index string; defaults to JSON.stringify(value)
|
|
51246
51418
|
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
51419
|
+
* @param dto.when - Logical timestamp this entry belongs to (look-ahead guard)
|
|
51247
51420
|
*/
|
|
51248
51421
|
this.writeMemory = async (dto) => {
|
|
51249
51422
|
if (!this.enable.hasValue()) {
|
|
@@ -51267,6 +51440,7 @@ class MemoryAdapter {
|
|
|
51267
51440
|
* @param dto.signalId - Signal identifier
|
|
51268
51441
|
* @param dto.bucketName - Bucket name
|
|
51269
51442
|
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
51443
|
+
* @param dto.when - Logical timestamp at which the search is happening (look-ahead guard)
|
|
51270
51444
|
* @returns Matching entries sorted by relevance score
|
|
51271
51445
|
*/
|
|
51272
51446
|
this.searchMemory = async (dto) => {
|
|
@@ -51290,6 +51464,7 @@ class MemoryAdapter {
|
|
|
51290
51464
|
* @param dto.signalId - Signal identifier
|
|
51291
51465
|
* @param dto.bucketName - Bucket name
|
|
51292
51466
|
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
51467
|
+
* @param dto.when - Logical timestamp at which the list is happening (look-ahead guard)
|
|
51293
51468
|
* @returns Array of all stored entries
|
|
51294
51469
|
*/
|
|
51295
51470
|
this.listMemory = async (dto) => {
|
|
@@ -51313,6 +51488,7 @@ class MemoryAdapter {
|
|
|
51313
51488
|
* @param dto.signalId - Signal identifier
|
|
51314
51489
|
* @param dto.bucketName - Bucket name
|
|
51315
51490
|
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
51491
|
+
* @param dto.when - Logical timestamp (kept for API consistency; removal is by UUID)
|
|
51316
51492
|
*/
|
|
51317
51493
|
this.removeMemory = async (dto) => {
|
|
51318
51494
|
if (!this.enable.hasValue()) {
|
|
@@ -51336,6 +51512,7 @@ class MemoryAdapter {
|
|
|
51336
51512
|
* @param dto.signalId - Signal identifier
|
|
51337
51513
|
* @param dto.bucketName - Bucket name
|
|
51338
51514
|
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
51515
|
+
* @param dto.when - Logical timestamp at which the read is happening (look-ahead guard)
|
|
51339
51516
|
* @returns Entry value
|
|
51340
51517
|
* @throws Error if entry not found
|
|
51341
51518
|
*/
|
|
@@ -51408,7 +51585,7 @@ async function writeMemory(dto) {
|
|
|
51408
51585
|
if (!MethodContextService.hasContext()) {
|
|
51409
51586
|
throw new Error("writeMemory requires a method context");
|
|
51410
51587
|
}
|
|
51411
|
-
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
51588
|
+
const { backtest: isBacktest, symbol, when } = backtest.executionContextService.context;
|
|
51412
51589
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
51413
51590
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
51414
51591
|
let signal;
|
|
@@ -51420,6 +51597,7 @@ async function writeMemory(dto) {
|
|
|
51420
51597
|
bucketName,
|
|
51421
51598
|
description,
|
|
51422
51599
|
backtest: isBacktest,
|
|
51600
|
+
when,
|
|
51423
51601
|
});
|
|
51424
51602
|
return;
|
|
51425
51603
|
}
|
|
@@ -51431,6 +51609,7 @@ async function writeMemory(dto) {
|
|
|
51431
51609
|
bucketName,
|
|
51432
51610
|
description,
|
|
51433
51611
|
backtest: isBacktest,
|
|
51612
|
+
when,
|
|
51434
51613
|
});
|
|
51435
51614
|
return;
|
|
51436
51615
|
}
|
|
@@ -51466,7 +51645,7 @@ async function readMemory(dto) {
|
|
|
51466
51645
|
if (!MethodContextService.hasContext()) {
|
|
51467
51646
|
throw new Error("readMemory requires a method context");
|
|
51468
51647
|
}
|
|
51469
|
-
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
51648
|
+
const { backtest: isBacktest, symbol, when } = backtest.executionContextService.context;
|
|
51470
51649
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
51471
51650
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
51472
51651
|
let signal;
|
|
@@ -51476,6 +51655,7 @@ async function readMemory(dto) {
|
|
|
51476
51655
|
signalId: signal.id,
|
|
51477
51656
|
bucketName,
|
|
51478
51657
|
backtest: isBacktest,
|
|
51658
|
+
when,
|
|
51479
51659
|
});
|
|
51480
51660
|
}
|
|
51481
51661
|
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
@@ -51484,6 +51664,7 @@ async function readMemory(dto) {
|
|
|
51484
51664
|
signalId: signal.id,
|
|
51485
51665
|
bucketName,
|
|
51486
51666
|
backtest: isBacktest,
|
|
51667
|
+
when,
|
|
51487
51668
|
});
|
|
51488
51669
|
}
|
|
51489
51670
|
throw new Error(`readMemory requires a pending or scheduled signal for symbol=${symbol} memoryId=${memoryId}`);
|
|
@@ -51518,7 +51699,7 @@ async function searchMemory(dto) {
|
|
|
51518
51699
|
if (!MethodContextService.hasContext()) {
|
|
51519
51700
|
throw new Error("searchMemory requires a method context");
|
|
51520
51701
|
}
|
|
51521
|
-
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
51702
|
+
const { backtest: isBacktest, symbol, when } = backtest.executionContextService.context;
|
|
51522
51703
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
51523
51704
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
51524
51705
|
let signal;
|
|
@@ -51528,6 +51709,7 @@ async function searchMemory(dto) {
|
|
|
51528
51709
|
signalId: signal.id,
|
|
51529
51710
|
bucketName,
|
|
51530
51711
|
backtest: isBacktest,
|
|
51712
|
+
when,
|
|
51531
51713
|
});
|
|
51532
51714
|
}
|
|
51533
51715
|
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
@@ -51536,6 +51718,7 @@ async function searchMemory(dto) {
|
|
|
51536
51718
|
signalId: signal.id,
|
|
51537
51719
|
bucketName,
|
|
51538
51720
|
backtest: isBacktest,
|
|
51721
|
+
when,
|
|
51539
51722
|
});
|
|
51540
51723
|
}
|
|
51541
51724
|
throw new Error(`searchMemory requires a pending or scheduled signal for symbol=${symbol} query=${query}`);
|
|
@@ -51568,7 +51751,7 @@ async function listMemory(dto) {
|
|
|
51568
51751
|
if (!MethodContextService.hasContext()) {
|
|
51569
51752
|
throw new Error("listMemory requires a method context");
|
|
51570
51753
|
}
|
|
51571
|
-
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
51754
|
+
const { backtest: isBacktest, symbol, when } = backtest.executionContextService.context;
|
|
51572
51755
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
51573
51756
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
51574
51757
|
let signal;
|
|
@@ -51577,6 +51760,7 @@ async function listMemory(dto) {
|
|
|
51577
51760
|
signalId: signal.id,
|
|
51578
51761
|
bucketName,
|
|
51579
51762
|
backtest: isBacktest,
|
|
51763
|
+
when,
|
|
51580
51764
|
});
|
|
51581
51765
|
}
|
|
51582
51766
|
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
@@ -51584,6 +51768,7 @@ async function listMemory(dto) {
|
|
|
51584
51768
|
signalId: signal.id,
|
|
51585
51769
|
bucketName,
|
|
51586
51770
|
backtest: isBacktest,
|
|
51771
|
+
when,
|
|
51587
51772
|
});
|
|
51588
51773
|
}
|
|
51589
51774
|
throw new Error(`listMemory requires a pending or scheduled signal for symbol=${symbol} bucketName=${bucketName}`);
|
|
@@ -51618,7 +51803,7 @@ async function removeMemory(dto) {
|
|
|
51618
51803
|
if (!MethodContextService.hasContext()) {
|
|
51619
51804
|
throw new Error("removeMemory requires a method context");
|
|
51620
51805
|
}
|
|
51621
|
-
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
51806
|
+
const { backtest: isBacktest, symbol, when } = backtest.executionContextService.context;
|
|
51622
51807
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
51623
51808
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
51624
51809
|
let signal;
|
|
@@ -51628,6 +51813,7 @@ async function removeMemory(dto) {
|
|
|
51628
51813
|
signalId: signal.id,
|
|
51629
51814
|
bucketName,
|
|
51630
51815
|
backtest: isBacktest,
|
|
51816
|
+
when,
|
|
51631
51817
|
});
|
|
51632
51818
|
return;
|
|
51633
51819
|
}
|
|
@@ -51637,6 +51823,7 @@ async function removeMemory(dto) {
|
|
|
51637
51823
|
signalId: signal.id,
|
|
51638
51824
|
bucketName,
|
|
51639
51825
|
backtest: isBacktest,
|
|
51826
|
+
when,
|
|
51640
51827
|
});
|
|
51641
51828
|
return;
|
|
51642
51829
|
}
|
|
@@ -51902,6 +52089,9 @@ class DumpMemoryInstance {
|
|
|
51902
52089
|
value: { messages },
|
|
51903
52090
|
description,
|
|
51904
52091
|
backtest: this.backtest,
|
|
52092
|
+
// when=0: dumps are UI artifacts (agent transcripts, markdown reports);
|
|
52093
|
+
// they must stay visible regardless of the reader's logical time.
|
|
52094
|
+
when: new Date(0)
|
|
51905
52095
|
});
|
|
51906
52096
|
}
|
|
51907
52097
|
/**
|
|
@@ -51924,6 +52114,7 @@ class DumpMemoryInstance {
|
|
|
51924
52114
|
value: record,
|
|
51925
52115
|
description,
|
|
51926
52116
|
backtest: this.backtest,
|
|
52117
|
+
when: new Date(0)
|
|
51927
52118
|
});
|
|
51928
52119
|
}
|
|
51929
52120
|
/**
|
|
@@ -51947,6 +52138,7 @@ class DumpMemoryInstance {
|
|
|
51947
52138
|
value: { rows },
|
|
51948
52139
|
description,
|
|
51949
52140
|
backtest: this.backtest,
|
|
52141
|
+
when: new Date(0)
|
|
51950
52142
|
});
|
|
51951
52143
|
}
|
|
51952
52144
|
/**
|
|
@@ -51969,6 +52161,7 @@ class DumpMemoryInstance {
|
|
|
51969
52161
|
value: { content },
|
|
51970
52162
|
description,
|
|
51971
52163
|
backtest: this.backtest,
|
|
52164
|
+
when: new Date(0)
|
|
51972
52165
|
});
|
|
51973
52166
|
}
|
|
51974
52167
|
/**
|
|
@@ -51991,6 +52184,7 @@ class DumpMemoryInstance {
|
|
|
51991
52184
|
value: { content },
|
|
51992
52185
|
description,
|
|
51993
52186
|
backtest: this.backtest,
|
|
52187
|
+
when: new Date(0)
|
|
51994
52188
|
});
|
|
51995
52189
|
}
|
|
51996
52190
|
/**
|
|
@@ -52014,6 +52208,7 @@ class DumpMemoryInstance {
|
|
|
52014
52208
|
value: json,
|
|
52015
52209
|
description,
|
|
52016
52210
|
backtest: this.backtest,
|
|
52211
|
+
when: new Date(0)
|
|
52017
52212
|
});
|
|
52018
52213
|
}
|
|
52019
52214
|
/** Releases resources held by this instance. */
|
|
@@ -60975,8 +61170,8 @@ const CACHE_METHOD_NAME_CLEAR = "CacheUtils.clear";
|
|
|
60975
61170
|
const CACHE_METHOD_NAME_RESET_COUNTER = "CacheUtils.resetCounter";
|
|
60976
61171
|
const CACHE_FILE_INSTANCE_METHOD_NAME_RUN = "CacheFileInstance.run";
|
|
60977
61172
|
const CACHE_FILE_INSTANCE_METHOD_NAME_HAS_VALUE = "CacheFileInstance.hasValue";
|
|
60978
|
-
const MS_PER_MINUTE$
|
|
60979
|
-
const INTERVAL_MINUTES$
|
|
61173
|
+
const MS_PER_MINUTE$2 = 60000;
|
|
61174
|
+
const INTERVAL_MINUTES$2 = {
|
|
60980
61175
|
"1m": 1,
|
|
60981
61176
|
"3m": 3,
|
|
60982
61177
|
"5m": 5,
|
|
@@ -61010,11 +61205,11 @@ const INTERVAL_MINUTES$1 = {
|
|
|
61010
61205
|
* ```
|
|
61011
61206
|
*/
|
|
61012
61207
|
const align$1 = (timestamp, interval) => {
|
|
61013
|
-
const intervalMinutes = INTERVAL_MINUTES$
|
|
61208
|
+
const intervalMinutes = INTERVAL_MINUTES$2[interval];
|
|
61014
61209
|
if (!intervalMinutes) {
|
|
61015
61210
|
throw new Error(`align: unknown interval=${interval}`);
|
|
61016
61211
|
}
|
|
61017
|
-
const intervalMs = intervalMinutes * MS_PER_MINUTE$
|
|
61212
|
+
const intervalMs = intervalMinutes * MS_PER_MINUTE$2;
|
|
61018
61213
|
return Math.floor(timestamp / intervalMs) * intervalMs;
|
|
61019
61214
|
};
|
|
61020
61215
|
/**
|
|
@@ -61103,7 +61298,7 @@ class CacheFnInstance {
|
|
|
61103
61298
|
*/
|
|
61104
61299
|
this.run = (...args) => {
|
|
61105
61300
|
backtest.loggerService.debug(CACHE_METHOD_NAME_RUN, { args });
|
|
61106
|
-
const step = INTERVAL_MINUTES$
|
|
61301
|
+
const step = INTERVAL_MINUTES$2[this.interval];
|
|
61107
61302
|
{
|
|
61108
61303
|
if (!MethodContextService.hasContext()) {
|
|
61109
61304
|
throw new Error("CacheFnInstance run requires method context");
|
|
@@ -61298,7 +61493,7 @@ class CacheFileInstance {
|
|
|
61298
61493
|
*/
|
|
61299
61494
|
this.run = async (...args) => {
|
|
61300
61495
|
backtest.loggerService.debug(CACHE_FILE_INSTANCE_METHOD_NAME_RUN, { args });
|
|
61301
|
-
const step = INTERVAL_MINUTES$
|
|
61496
|
+
const step = INTERVAL_MINUTES$2[this.interval];
|
|
61302
61497
|
{
|
|
61303
61498
|
if (!MethodContextService.hasContext()) {
|
|
61304
61499
|
throw new Error("CacheFileInstance run requires method context");
|
|
@@ -61320,7 +61515,7 @@ class CacheFileInstance {
|
|
|
61320
61515
|
return cached.data;
|
|
61321
61516
|
}
|
|
61322
61517
|
const result = await this.fn.call(null, ...args);
|
|
61323
|
-
await PersistMeasureAdapter.writeMeasureData({ id: entityKey, data: result, removed: false }, bucket, entityKey);
|
|
61518
|
+
await PersistMeasureAdapter.writeMeasureData({ id: entityKey, data: result, removed: false }, bucket, entityKey, when);
|
|
61324
61519
|
return result;
|
|
61325
61520
|
};
|
|
61326
61521
|
/**
|
|
@@ -61609,8 +61804,8 @@ const INTERVAL_METHOD_NAME_FILE_HAS_VALUE = "IntervalUtils.file.hasValue";
|
|
|
61609
61804
|
const INTERVAL_METHOD_NAME_DISPOSE = "IntervalUtils.dispose";
|
|
61610
61805
|
const INTERVAL_METHOD_NAME_CLEAR = "IntervalUtils.clear";
|
|
61611
61806
|
const INTERVAL_METHOD_NAME_RESET_COUNTER = "IntervalUtils.resetCounter";
|
|
61612
|
-
const MS_PER_MINUTE = 60000;
|
|
61613
|
-
const INTERVAL_MINUTES = {
|
|
61807
|
+
const MS_PER_MINUTE$1 = 60000;
|
|
61808
|
+
const INTERVAL_MINUTES$1 = {
|
|
61614
61809
|
"1m": 1,
|
|
61615
61810
|
"3m": 3,
|
|
61616
61811
|
"5m": 5,
|
|
@@ -61640,11 +61835,11 @@ const INTERVAL_MINUTES = {
|
|
|
61640
61835
|
* ```
|
|
61641
61836
|
*/
|
|
61642
61837
|
const align = (timestamp, interval) => {
|
|
61643
|
-
const intervalMinutes = INTERVAL_MINUTES[interval];
|
|
61838
|
+
const intervalMinutes = INTERVAL_MINUTES$1[interval];
|
|
61644
61839
|
if (!intervalMinutes) {
|
|
61645
61840
|
throw new Error(`align: unknown interval=${interval}`);
|
|
61646
61841
|
}
|
|
61647
|
-
const intervalMs = intervalMinutes * MS_PER_MINUTE;
|
|
61842
|
+
const intervalMs = intervalMinutes * MS_PER_MINUTE$1;
|
|
61648
61843
|
return Math.floor(timestamp / intervalMs) * intervalMs;
|
|
61649
61844
|
};
|
|
61650
61845
|
/**
|
|
@@ -61719,7 +61914,7 @@ class IntervalFnInstance {
|
|
|
61719
61914
|
*/
|
|
61720
61915
|
this.run = (...args) => {
|
|
61721
61916
|
backtest.loggerService.debug(INTERVAL_METHOD_NAME_RUN, { args });
|
|
61722
|
-
const step = INTERVAL_MINUTES[this.interval];
|
|
61917
|
+
const step = INTERVAL_MINUTES$1[this.interval];
|
|
61723
61918
|
{
|
|
61724
61919
|
if (!MethodContextService.hasContext()) {
|
|
61725
61920
|
throw new Error("IntervalFnInstance run requires method context");
|
|
@@ -61886,7 +62081,7 @@ class IntervalFileInstance {
|
|
|
61886
62081
|
*/
|
|
61887
62082
|
this.run = async (...args) => {
|
|
61888
62083
|
backtest.loggerService.debug(INTERVAL_FILE_INSTANCE_METHOD_NAME_RUN, { args });
|
|
61889
|
-
const step = INTERVAL_MINUTES[this.interval];
|
|
62084
|
+
const step = INTERVAL_MINUTES$1[this.interval];
|
|
61890
62085
|
{
|
|
61891
62086
|
if (!MethodContextService.hasContext()) {
|
|
61892
62087
|
throw new Error("IntervalFileInstance run requires method context");
|
|
@@ -61909,7 +62104,7 @@ class IntervalFileInstance {
|
|
|
61909
62104
|
}
|
|
61910
62105
|
const result = await this.fn.call(null, ...args);
|
|
61911
62106
|
if (result !== null) {
|
|
61912
|
-
await PersistIntervalAdapter.writeIntervalData({ id: entityKey, data: result, removed: false }, bucket, entityKey);
|
|
62107
|
+
await PersistIntervalAdapter.writeIntervalData({ id: entityKey, data: result, when, removed: false }, bucket, entityKey, when);
|
|
61913
62108
|
}
|
|
61914
62109
|
return result;
|
|
61915
62110
|
};
|
|
@@ -63033,6 +63228,31 @@ class ActionBase {
|
|
|
63033
63228
|
// @ts-ignore
|
|
63034
63229
|
ActionBase = makeExtendable(ActionBase);
|
|
63035
63230
|
|
|
63231
|
+
const MS_PER_MINUTE = 60000;
|
|
63232
|
+
const INTERVAL_MINUTES = {
|
|
63233
|
+
"1m": 1,
|
|
63234
|
+
"3m": 3,
|
|
63235
|
+
"5m": 5,
|
|
63236
|
+
"15m": 15,
|
|
63237
|
+
"30m": 30,
|
|
63238
|
+
"1h": 60,
|
|
63239
|
+
"2h": 120,
|
|
63240
|
+
"4h": 240,
|
|
63241
|
+
"6h": 360,
|
|
63242
|
+
"8h": 480,
|
|
63243
|
+
"1d": 1440,
|
|
63244
|
+
};
|
|
63245
|
+
/**
|
|
63246
|
+
* Returns the step in milliseconds for a given candle interval.
|
|
63247
|
+
* For example, for "15m" interval, it returns 900000 (15 * 60 * 1000).
|
|
63248
|
+
*
|
|
63249
|
+
* @param interval - Candle interval (e.g., "1m", "15m", "1h")
|
|
63250
|
+
* @returns Step in milliseconds corresponding to the interval
|
|
63251
|
+
*/
|
|
63252
|
+
const intervalStepMs = (interval) => {
|
|
63253
|
+
return INTERVAL_MINUTES[interval] * MS_PER_MINUTE;
|
|
63254
|
+
};
|
|
63255
|
+
|
|
63036
63256
|
/**
|
|
63037
63257
|
* Rounds a price to the appropriate precision based on the tick size.
|
|
63038
63258
|
*
|
|
@@ -63299,4 +63519,4 @@ const validateSignal = (signal, currentPrice) => {
|
|
|
63299
63519
|
return !errors.length;
|
|
63300
63520
|
};
|
|
63301
63521
|
|
|
63302
|
-
export { ActionBase, Backtest, Breakeven, Broker, BrokerBase, Cache, Constant, Dump, Exchange, ExecutionContextService, Heat, HighestProfit, Interval, Live, Log, Markdown, MarkdownFileBase, MarkdownFolderBase, MarkdownWriter, MaxDrawdown, Memory, MemoryBacktest, MemoryBacktestAdapter, MemoryLive, MemoryLiveAdapter, MethodContextService, Notification, NotificationBacktest, NotificationLive, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistBreakevenInstance, PersistCandleAdapter, PersistCandleInstance, PersistIntervalAdapter, PersistIntervalInstance, PersistLogAdapter, PersistLogInstance, PersistMeasureAdapter, PersistMeasureInstance, PersistMemoryAdapter, PersistMemoryInstance, PersistNotificationAdapter, PersistNotificationInstance, PersistPartialAdapter, PersistPartialInstance, PersistRecentAdapter, PersistRecentInstance, PersistRiskAdapter, PersistRiskInstance, PersistScheduleAdapter, PersistScheduleInstance, PersistSessionAdapter, PersistSessionInstance, PersistSignalAdapter, PersistSignalInstance, PersistStateAdapter, PersistStateInstance, PersistStorageAdapter, PersistStorageInstance, Position, PositionSize, Recent, RecentBacktest, RecentLive, Reflect$1 as Reflect, Report, ReportBase, ReportWriter, Risk, Schedule, Session, SessionBacktest, SessionLive, State, StateBacktest, StateBacktestAdapter, StateLive, StateLiveAdapter, Storage, StorageBacktest, StorageLive, Strategy, Sync, System, Walker, addActionSchema, addExchangeSchema, addFrameSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, alignToInterval, checkCandles, commitActivateScheduled, commitAverageBuy, commitBreakeven, commitCancelScheduled, commitClosePending, commitPartialLoss, commitPartialLossCost, commitPartialProfit, commitPartialProfitCost, commitSignalNotify, commitTrailingStop, commitTrailingStopCost, commitTrailingTake, commitTrailingTakeCost, createSignalState, dumpAgentAnswer, dumpError, dumpJson, dumpRecord, dumpTable, dumpText, emitters, formatPrice, formatQuantity, get, getActionSchema, getAggregatedTrades, getAveragePrice, getBacktestTimeframe, getBreakeven, getCandles, getClosePrice, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getEffectivePriceOpen, getExchangeSchema, getFrameSchema, getLatestSignal, getMaxDrawdownDistancePnlCost, getMaxDrawdownDistancePnlPercentage, getMinutesSinceLatestSignalCreated, getMode, getNextCandles, getOrderBook, getPendingSignal, getPositionActiveMinutes, getPositionCountdownMinutes, getPositionDrawdownMinutes, getPositionEffectivePrice, getPositionEntries, getPositionEntryOverlap, getPositionEstimateMinutes, getPositionHighestMaxDrawdownPnlCost, getPositionHighestMaxDrawdownPnlPercentage, getPositionHighestPnlCost, getPositionHighestPnlPercentage, getPositionHighestProfitBreakeven, getPositionHighestProfitDistancePnlCost, getPositionHighestProfitDistancePnlPercentage, getPositionHighestProfitMinutes, getPositionHighestProfitPrice, getPositionHighestProfitTimestamp, getPositionInvestedCost, getPositionInvestedCount, getPositionLevels, getPositionMaxDrawdownMinutes, getPositionMaxDrawdownPnlCost, getPositionMaxDrawdownPnlPercentage, getPositionMaxDrawdownPrice, getPositionMaxDrawdownTimestamp, getPositionPartialOverlap, getPositionPartials, getPositionPnlCost, getPositionPnlPercent, getPositionWaitingMinutes, getRawCandles, getRiskSchema, getScheduledSignal, getSessionData, getSignalState, getSizingSchema, getStrategySchema, getSymbol, getTimestamp, getTotalClosed, getTotalCostClosed, getTotalPercentClosed, getWalkerSchema, hasNoPendingSignal, hasNoScheduledSignal, hasTradeContext, investedCostToPercent, backtest as lib, listExchangeSchema, listFrameSchema, listMemory, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenBacktestProgress, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenHighestProfit, listenHighestProfitOnce, listenIdlePing, listenIdlePingOnce, listenMaxDrawdown, listenMaxDrawdownOnce, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalNotify, listenSignalNotifyOnce, listenSignalOnce, listenStrategyCommit, listenStrategyCommitOnce, listenSync, listenSyncOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideActionSchema, overrideExchangeSchema, overrideFrameSchema, overrideRiskSchema, overrideSizingSchema, overrideStrategySchema, overrideWalkerSchema, parseArgs, percentDiff, percentToCloseCost, percentValue, readMemory, removeMemory, roundTicks, runInMockContext, searchMemory, set, setColumns, setConfig, setLogger, setSessionData, setSignalState, shutdown, slPercentShiftToPrice, slPriceToPercentShift, stopStrategy, toProfitLossDto, tpPercentShiftToPrice, tpPriceToPercentShift, validate, validateCommonSignal, validatePendingSignal, validateScheduledSignal, validateSignal, waitForCandle, warmCandles, writeMemory };
|
|
63522
|
+
export { ActionBase, Backtest, Breakeven, Broker, BrokerBase, Cache, Constant, Dump, Exchange, ExecutionContextService, Heat, HighestProfit, Interval, Live, Log, Markdown, MarkdownFileBase, MarkdownFolderBase, MarkdownWriter, MaxDrawdown, Memory, MemoryBacktest, MemoryBacktestAdapter, MemoryLive, MemoryLiveAdapter, MethodContextService, Notification, NotificationBacktest, NotificationLive, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistBreakevenInstance, PersistCandleAdapter, PersistCandleInstance, PersistIntervalAdapter, PersistIntervalInstance, PersistLogAdapter, PersistLogInstance, PersistMeasureAdapter, PersistMeasureInstance, PersistMemoryAdapter, PersistMemoryInstance, PersistNotificationAdapter, PersistNotificationInstance, PersistPartialAdapter, PersistPartialInstance, PersistRecentAdapter, PersistRecentInstance, PersistRiskAdapter, PersistRiskInstance, PersistScheduleAdapter, PersistScheduleInstance, PersistSessionAdapter, PersistSessionInstance, PersistSignalAdapter, PersistSignalInstance, PersistStateAdapter, PersistStateInstance, PersistStorageAdapter, PersistStorageInstance, Position, PositionSize, Recent, RecentBacktest, RecentLive, Reflect$1 as Reflect, Report, ReportBase, ReportWriter, Risk, Schedule, Session, SessionBacktest, SessionLive, State, StateBacktest, StateBacktestAdapter, StateLive, StateLiveAdapter, Storage, StorageBacktest, StorageLive, Strategy, Sync, System, Walker, addActionSchema, addExchangeSchema, addFrameSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, alignToInterval, checkCandles, commitActivateScheduled, commitAverageBuy, commitBreakeven, commitCancelScheduled, commitClosePending, commitPartialLoss, commitPartialLossCost, commitPartialProfit, commitPartialProfitCost, commitSignalNotify, commitTrailingStop, commitTrailingStopCost, commitTrailingTake, commitTrailingTakeCost, createSignalState, dumpAgentAnswer, dumpError, dumpJson, dumpRecord, dumpTable, dumpText, emitters, formatPrice, formatQuantity, get, getActionSchema, getAggregatedTrades, getAveragePrice, getBacktestTimeframe, getBreakeven, getCandles, getClosePrice, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getEffectivePriceOpen, getExchangeSchema, getFrameSchema, getLatestSignal, getMaxDrawdownDistancePnlCost, getMaxDrawdownDistancePnlPercentage, getMinutesSinceLatestSignalCreated, getMode, getNextCandles, getOrderBook, getPendingSignal, getPositionActiveMinutes, getPositionCountdownMinutes, getPositionDrawdownMinutes, getPositionEffectivePrice, getPositionEntries, getPositionEntryOverlap, getPositionEstimateMinutes, getPositionHighestMaxDrawdownPnlCost, getPositionHighestMaxDrawdownPnlPercentage, getPositionHighestPnlCost, getPositionHighestPnlPercentage, getPositionHighestProfitBreakeven, getPositionHighestProfitDistancePnlCost, getPositionHighestProfitDistancePnlPercentage, getPositionHighestProfitMinutes, getPositionHighestProfitPrice, getPositionHighestProfitTimestamp, getPositionInvestedCost, getPositionInvestedCount, getPositionLevels, getPositionMaxDrawdownMinutes, getPositionMaxDrawdownPnlCost, getPositionMaxDrawdownPnlPercentage, getPositionMaxDrawdownPrice, getPositionMaxDrawdownTimestamp, getPositionPartialOverlap, getPositionPartials, getPositionPnlCost, getPositionPnlPercent, getPositionWaitingMinutes, getRawCandles, getRiskSchema, getScheduledSignal, getSessionData, getSignalState, getSizingSchema, getStrategySchema, getSymbol, getTimestamp, getTotalClosed, getTotalCostClosed, getTotalPercentClosed, getWalkerSchema, hasNoPendingSignal, hasNoScheduledSignal, hasTradeContext, intervalStepMs, investedCostToPercent, backtest as lib, listExchangeSchema, listFrameSchema, listMemory, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenBacktestProgress, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenHighestProfit, listenHighestProfitOnce, listenIdlePing, listenIdlePingOnce, listenMaxDrawdown, listenMaxDrawdownOnce, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalNotify, listenSignalNotifyOnce, listenSignalOnce, listenStrategyCommit, listenStrategyCommitOnce, listenSync, listenSyncOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideActionSchema, overrideExchangeSchema, overrideFrameSchema, overrideRiskSchema, overrideSizingSchema, overrideStrategySchema, overrideWalkerSchema, parseArgs, percentDiff, percentToCloseCost, percentValue, readMemory, removeMemory, roundTicks, runInMockContext, searchMemory, set, setColumns, setConfig, setLogger, setSessionData, setSignalState, shutdown, slPercentShiftToPrice, slPriceToPercentShift, stopStrategy, toProfitLossDto, tpPercentShiftToPrice, tpPriceToPercentShift, validate, validateCommonSignal, validatePendingSignal, validateScheduledSignal, validateSignal, waitForCandle, warmCandles, writeMemory };
|