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.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$9 = {
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$7 = 60000;
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$9[this.interval] * MS_PER_MINUTE$7;
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$9[this.interval] * MS_PER_MINUTE$7;
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$6 = 60000;
4613
- const INTERVAL_MINUTES$8 = {
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$6;
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$8[dto.interval];
4798
+ const step = INTERVAL_MINUTES$9[dto.interval];
4799
4799
  const sinceTimestamp = since.getTime();
4800
- const untilTimestamp = sinceTimestamp + dto.limit * step * MS_PER_MINUTE$6;
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$8[interval];
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$6;
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$8[interval];
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$6;
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$8[interval];
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$6;
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$6);
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$6 - MS_PER_MINUTE$6;
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$7 = {
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$7[self.params.interval];
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$6 = {
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$6[interval];
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$5 = 60000;
14136
- const INTERVAL_MINUTES$5 = {
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$5[interval];
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$5;
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$4 = 60000;
19654
- const INTERVAL_MINUTES$4 = {
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$4[interval] * MS_PER_MINUTE$4;
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$3 = 60000;
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$3 = {
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$3;
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$3[interval];
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$3;
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$3);
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$3 - MS_PER_MINUTE$3;
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$3[interval];
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$3;
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$2 = 60000;
36043
- const INTERVAL_MINUTES$2 = {
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$2;
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$2[interval];
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$2;
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$2[interval];
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$2;
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
- * @returns The latest signal or null if not found
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
- return await PersistRecentAdapter.readRecentData(symbol, strategyName, exchangeName, frameName, backtest$1);
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
- * @param timestamp - Current timestamp in milliseconds
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
- * @returns The latest signal or null if not found
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
- return this._signals.get(key) ?? null;
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
- * @param timestamp - Current timestamp in milliseconds
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
- * @returns The latest signal or null if not found
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
- return await PersistRecentAdapter.readRecentData(symbol, strategyName, exchangeName, frameName, backtest$1);
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
- * @param timestamp - Current timestamp in milliseconds
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
- * @returns The latest signal or null if not found
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
- return this._signals.get(key) ?? null;
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
- * @param timestamp - Current timestamp in milliseconds
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
- * @returns The latest signal or null if not found
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
- * @param timestamp - Current timestamp in milliseconds
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
- * @returns The latest signal or null if not found
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
- * @param timestamp - Current timestamp in milliseconds
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 backtest - Flag indicating if the context is backtest or live
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
- * @param timestamp - Current timestamp in milliseconds
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
- * @returns Whole minutes since the latest signal was created, or null if no signal found
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
- const timestamp = await backtest.timeMetaService.getTimestamp(symbol, context, true);
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
- const timestamp = await backtest.timeMetaService.getTimestamp(symbol, context, false);
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._value = await dispatch(this._value);
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._value = await dispatch(this._value);
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
- * @returns Current session value, or null if not set
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
- * @returns Current session value, or null if not set
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
- * @returns Current session value, or null if not set
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
- * @returns Current session value, or null if not set
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
- * @returns Current session value, or null if not set
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
- const read = (id) => docs.get(id)?.content;
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
- const list = () => Array.from(docs.entries())
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
- const search = (query, settings = DEFAULT_SETTINGS) => {
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 = JSON.stringify(value)) {
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
- await PersistMemoryAdapter.writeMemoryData({ data: value, priority, removed: false, index }, this.signalId, this.bucketName, memoryId);
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$1 = 60000;
60979
- const INTERVAL_MINUTES$1 = {
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$1[interval];
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$1;
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$1[this.interval];
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$1[this.interval];
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 };