backtest-kit 9.8.1 → 9.8.3

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.
Files changed (6) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +1898 -1871
  3. package/build/index.cjs +949 -282
  4. package/build/index.mjs +944 -283
  5. package/package.json +86 -86
  6. package/types.d.ts +551 -151
package/build/index.mjs CHANGED
@@ -554,6 +554,22 @@ const GLOBAL_CONFIG = {
554
554
  * Default: true (mutex locking enabled for candle fetching)
555
555
  */
556
556
  CC_ENABLE_CANDLE_FETCH_MUTEX: true,
557
+ /**
558
+ * Enables cooperative interleaving of concurrently running backtests after each candle fetch.
559
+ *
560
+ * Mechanism (implemented in `Candle.spinLock`):
561
+ * - After `getNextCandles` resolves, the current backtest awaits
562
+ * `Promise.race([_spin.toPromise(), sleep(50)])`, where `_spin` is emitted whenever
563
+ * another caller acquires the candle-fetch mutex.
564
+ * - This hands the event loop to a peer backtest waiting on the same mutex, so multiple
565
+ * parallel `Backtest.run` / `Walker` workloads progress in round-robin fashion instead
566
+ * of one monopolizing the event loop until completion.
567
+ * - The spin is skipped entirely when `Lookup.isParallel` is `false` (single active workload —
568
+ * no peer to yield to) or when `CC_ENABLE_CANDLE_FETCH_MUTEX` is disabled.
569
+ *
570
+ * Default: true (parallel backtests are interleaved on each candle fetch boundary)
571
+ */
572
+ CC_ENABLE_BACKTEST_PARALLEL_SPIN: true,
557
573
  /**
558
574
  * Enables DCA (Dollar-Cost Averaging) logic even if antirecord is not broken.
559
575
  * Allows to commitAverageBuy if currentPrice is not the lowest price since entry, but still lower than priceOpen.
@@ -772,11 +788,23 @@ const maxDrawdownSubject = new Subject();
772
788
  * Emits when a strategy calls commitSignalInfo() to broadcast a custom annotation.
773
789
  */
774
790
  const signalNotifySubject = new Subject();
791
+ /**
792
+ * Before start emitter for strategy initialization events.
793
+ * Emits when the engine is about to start a new strategy execution.
794
+ */
795
+ const beforeStartSubject = new Subject();
796
+ /**
797
+ * After end emitter for strategy completion events.
798
+ * Emits when the engine has completed processing a signal.
799
+ */
800
+ const afterEndSubject = new Subject();
775
801
 
776
802
  var emitters = /*#__PURE__*/Object.freeze({
777
803
  __proto__: null,
778
804
  activePingSubject: activePingSubject,
805
+ afterEndSubject: afterEndSubject,
779
806
  backtestScheduleOpenSubject: backtestScheduleOpenSubject,
807
+ beforeStartSubject: beforeStartSubject,
780
808
  breakevenSubject: breakevenSubject,
781
809
  doneBacktestSubject: doneBacktestSubject,
782
810
  doneLiveSubject: doneLiveSubject,
@@ -914,7 +942,7 @@ async function writeFileAtomic(file, data, options = {}) {
914
942
 
915
943
  var _a$3;
916
944
  /** Logger service injected as DI singleton */
917
- const LOGGER_SERVICE$7 = new LoggerService();
945
+ const LOGGER_SERVICE$8 = new LoggerService();
918
946
  /** Symbol key for the singleshot waitForInit function on PersistBase instances. */
919
947
  const BASE_WAIT_FOR_INIT_SYMBOL = Symbol("wait-for-init");
920
948
  // Calculate step in milliseconds for candle close time validation
@@ -1037,7 +1065,7 @@ const BASE_WAIT_FOR_INIT_FN_METHOD_NAME = "PersistBase.waitForInitFn";
1037
1065
  const BASE_UNLINK_RETRY_COUNT = 5;
1038
1066
  const BASE_UNLINK_RETRY_DELAY = 1000;
1039
1067
  const BASE_WAIT_FOR_INIT_FN = async (self) => {
1040
- LOGGER_SERVICE$7.debug(BASE_WAIT_FOR_INIT_FN_METHOD_NAME, {
1068
+ LOGGER_SERVICE$8.debug(BASE_WAIT_FOR_INIT_FN_METHOD_NAME, {
1041
1069
  entityName: self.entityName,
1042
1070
  directory: self._directory,
1043
1071
  });
@@ -1095,7 +1123,7 @@ class PersistBase {
1095
1123
  this.entityName = entityName;
1096
1124
  this.baseDir = baseDir;
1097
1125
  this[_a$3] = singleshot(async () => await BASE_WAIT_FOR_INIT_FN(this));
1098
- LOGGER_SERVICE$7.debug(PERSIST_BASE_METHOD_NAME_CTOR, {
1126
+ LOGGER_SERVICE$8.debug(PERSIST_BASE_METHOD_NAME_CTOR, {
1099
1127
  entityName: this.entityName,
1100
1128
  baseDir,
1101
1129
  });
@@ -1111,14 +1139,14 @@ class PersistBase {
1111
1139
  return join(this.baseDir, this.entityName, `${entityId}.json`);
1112
1140
  }
1113
1141
  async waitForInit(initial) {
1114
- LOGGER_SERVICE$7.debug(PERSIST_BASE_METHOD_NAME_WAIT_FOR_INIT, {
1142
+ LOGGER_SERVICE$8.debug(PERSIST_BASE_METHOD_NAME_WAIT_FOR_INIT, {
1115
1143
  entityName: this.entityName,
1116
1144
  initial,
1117
1145
  });
1118
1146
  await this[BASE_WAIT_FOR_INIT_SYMBOL]();
1119
1147
  }
1120
1148
  async readValue(entityId) {
1121
- LOGGER_SERVICE$7.debug(PERSIST_BASE_METHOD_NAME_READ_VALUE, {
1149
+ LOGGER_SERVICE$8.debug(PERSIST_BASE_METHOD_NAME_READ_VALUE, {
1122
1150
  entityName: this.entityName,
1123
1151
  entityId,
1124
1152
  });
@@ -1135,7 +1163,7 @@ class PersistBase {
1135
1163
  }
1136
1164
  }
1137
1165
  async hasValue(entityId) {
1138
- LOGGER_SERVICE$7.debug(PERSIST_BASE_METHOD_NAME_HAS_VALUE, {
1166
+ LOGGER_SERVICE$8.debug(PERSIST_BASE_METHOD_NAME_HAS_VALUE, {
1139
1167
  entityName: this.entityName,
1140
1168
  entityId,
1141
1169
  });
@@ -1152,7 +1180,7 @@ class PersistBase {
1152
1180
  }
1153
1181
  }
1154
1182
  async writeValue(entityId, entity) {
1155
- LOGGER_SERVICE$7.debug(PERSIST_BASE_METHOD_NAME_WRITE_VALUE, {
1183
+ LOGGER_SERVICE$8.debug(PERSIST_BASE_METHOD_NAME_WRITE_VALUE, {
1156
1184
  entityName: this.entityName,
1157
1185
  entityId,
1158
1186
  });
@@ -1174,7 +1202,7 @@ class PersistBase {
1174
1202
  * @throws Error if reading fails
1175
1203
  */
1176
1204
  async *keys() {
1177
- LOGGER_SERVICE$7.debug(PERSIST_BASE_METHOD_NAME_KEYS, {
1205
+ LOGGER_SERVICE$8.debug(PERSIST_BASE_METHOD_NAME_KEYS, {
1178
1206
  entityName: this.entityName,
1179
1207
  });
1180
1208
  try {
@@ -1319,7 +1347,7 @@ class PersistSignalUtils {
1319
1347
  * @returns Promise resolving to signal or null if none persisted
1320
1348
  */
1321
1349
  this.readSignalData = async (symbol, strategyName, exchangeName) => {
1322
- LOGGER_SERVICE$7.info(PERSIST_SIGNAL_UTILS_METHOD_NAME_READ_DATA);
1350
+ LOGGER_SERVICE$8.info(PERSIST_SIGNAL_UTILS_METHOD_NAME_READ_DATA);
1323
1351
  const key = `${symbol}:${strategyName}:${exchangeName}`;
1324
1352
  const isInitial = !this.getStorage.has(key);
1325
1353
  const instance = this.getStorage(symbol, strategyName, exchangeName);
@@ -1337,7 +1365,7 @@ class PersistSignalUtils {
1337
1365
  * @returns Promise that resolves when write is complete
1338
1366
  */
1339
1367
  this.writeSignalData = async (signalRow, symbol, strategyName, exchangeName) => {
1340
- LOGGER_SERVICE$7.info(PERSIST_SIGNAL_UTILS_METHOD_NAME_WRITE_DATA);
1368
+ LOGGER_SERVICE$8.info(PERSIST_SIGNAL_UTILS_METHOD_NAME_WRITE_DATA);
1341
1369
  const key = `${symbol}:${strategyName}:${exchangeName}`;
1342
1370
  const isInitial = !this.getStorage.has(key);
1343
1371
  const instance = this.getStorage(symbol, strategyName, exchangeName);
@@ -1352,7 +1380,7 @@ class PersistSignalUtils {
1352
1380
  * @param Ctor - Custom IPersistSignalInstance constructor
1353
1381
  */
1354
1382
  usePersistSignalAdapter(Ctor) {
1355
- LOGGER_SERVICE$7.info(PERSIST_SIGNAL_UTILS_METHOD_NAME_USE_PERSIST_SIGNAL_ADAPTER);
1383
+ LOGGER_SERVICE$8.info(PERSIST_SIGNAL_UTILS_METHOD_NAME_USE_PERSIST_SIGNAL_ADAPTER);
1356
1384
  this.PersistSignalInstanceCtor = Ctor;
1357
1385
  this.getStorage.clear();
1358
1386
  }
@@ -1361,21 +1389,21 @@ class PersistSignalUtils {
1361
1389
  * Call when process.cwd() changes between strategy iterations.
1362
1390
  */
1363
1391
  clear() {
1364
- LOGGER_SERVICE$7.log(PERSIST_SIGNAL_UTILS_METHOD_NAME_CLEAR);
1392
+ LOGGER_SERVICE$8.log(PERSIST_SIGNAL_UTILS_METHOD_NAME_CLEAR);
1365
1393
  this.getStorage.clear();
1366
1394
  }
1367
1395
  /**
1368
1396
  * Switches to the default file-based PersistSignalInstance.
1369
1397
  */
1370
1398
  useJson() {
1371
- LOGGER_SERVICE$7.log(PERSIST_SIGNAL_UTILS_METHOD_NAME_USE_JSON);
1399
+ LOGGER_SERVICE$8.log(PERSIST_SIGNAL_UTILS_METHOD_NAME_USE_JSON);
1372
1400
  this.usePersistSignalAdapter(PersistSignalInstance);
1373
1401
  }
1374
1402
  /**
1375
1403
  * Switches to PersistSignalDummyInstance (all operations are no-ops).
1376
1404
  */
1377
1405
  useDummy() {
1378
- LOGGER_SERVICE$7.log(PERSIST_SIGNAL_UTILS_METHOD_NAME_USE_DUMMY);
1406
+ LOGGER_SERVICE$8.log(PERSIST_SIGNAL_UTILS_METHOD_NAME_USE_DUMMY);
1379
1407
  this.usePersistSignalAdapter(PersistSignalDummyInstance);
1380
1408
  }
1381
1409
  }
@@ -1515,7 +1543,7 @@ class PersistRiskUtils {
1515
1543
  * @returns Promise resolving to position entries (empty array if none)
1516
1544
  */
1517
1545
  this.readPositionData = async (riskName, exchangeName, when) => {
1518
- LOGGER_SERVICE$7.info(PERSIST_RISK_UTILS_METHOD_NAME_READ_DATA);
1546
+ LOGGER_SERVICE$8.info(PERSIST_RISK_UTILS_METHOD_NAME_READ_DATA);
1519
1547
  const key = `${riskName}:${exchangeName}`;
1520
1548
  const isInitial = !this.getRiskStorage.has(key);
1521
1549
  const instance = this.getRiskStorage(riskName, exchangeName);
@@ -1533,7 +1561,7 @@ class PersistRiskUtils {
1533
1561
  * @returns Promise that resolves when write is complete
1534
1562
  */
1535
1563
  this.writePositionData = async (riskRow, riskName, exchangeName, when) => {
1536
- LOGGER_SERVICE$7.info(PERSIST_RISK_UTILS_METHOD_NAME_WRITE_DATA);
1564
+ LOGGER_SERVICE$8.info(PERSIST_RISK_UTILS_METHOD_NAME_WRITE_DATA);
1537
1565
  const key = `${riskName}:${exchangeName}`;
1538
1566
  const isInitial = !this.getRiskStorage.has(key);
1539
1567
  const instance = this.getRiskStorage(riskName, exchangeName);
@@ -1548,7 +1576,7 @@ class PersistRiskUtils {
1548
1576
  * @param Ctor - Custom IPersistRiskInstance constructor
1549
1577
  */
1550
1578
  usePersistRiskAdapter(Ctor) {
1551
- LOGGER_SERVICE$7.info(PERSIST_RISK_UTILS_METHOD_NAME_USE_PERSIST_RISK_ADAPTER);
1579
+ LOGGER_SERVICE$8.info(PERSIST_RISK_UTILS_METHOD_NAME_USE_PERSIST_RISK_ADAPTER);
1552
1580
  this.PersistRiskInstanceCtor = Ctor;
1553
1581
  this.getRiskStorage.clear();
1554
1582
  }
@@ -1557,21 +1585,21 @@ class PersistRiskUtils {
1557
1585
  * Call when process.cwd() changes between strategy iterations.
1558
1586
  */
1559
1587
  clear() {
1560
- LOGGER_SERVICE$7.log(PERSIST_RISK_UTILS_METHOD_NAME_CLEAR);
1588
+ LOGGER_SERVICE$8.log(PERSIST_RISK_UTILS_METHOD_NAME_CLEAR);
1561
1589
  this.getRiskStorage.clear();
1562
1590
  }
1563
1591
  /**
1564
1592
  * Switches to the default file-based PersistRiskInstance.
1565
1593
  */
1566
1594
  useJson() {
1567
- LOGGER_SERVICE$7.log(PERSIST_RISK_UTILS_METHOD_NAME_USE_JSON);
1595
+ LOGGER_SERVICE$8.log(PERSIST_RISK_UTILS_METHOD_NAME_USE_JSON);
1568
1596
  this.usePersistRiskAdapter(PersistRiskInstance);
1569
1597
  }
1570
1598
  /**
1571
1599
  * Switches to PersistRiskDummyInstance (all operations are no-ops).
1572
1600
  */
1573
1601
  useDummy() {
1574
- LOGGER_SERVICE$7.log(PERSIST_RISK_UTILS_METHOD_NAME_USE_DUMMY);
1602
+ LOGGER_SERVICE$8.log(PERSIST_RISK_UTILS_METHOD_NAME_USE_DUMMY);
1575
1603
  this.usePersistRiskAdapter(PersistRiskDummyInstance);
1576
1604
  }
1577
1605
  }
@@ -1710,7 +1738,7 @@ class PersistScheduleUtils {
1710
1738
  * @returns Promise resolving to scheduled signal or null if none persisted
1711
1739
  */
1712
1740
  this.readScheduleData = async (symbol, strategyName, exchangeName) => {
1713
- LOGGER_SERVICE$7.info(PERSIST_SCHEDULE_UTILS_METHOD_NAME_READ_DATA);
1741
+ LOGGER_SERVICE$8.info(PERSIST_SCHEDULE_UTILS_METHOD_NAME_READ_DATA);
1714
1742
  const key = `${symbol}:${strategyName}:${exchangeName}`;
1715
1743
  const isInitial = !this.getScheduleStorage.has(key);
1716
1744
  const instance = this.getScheduleStorage(symbol, strategyName, exchangeName);
@@ -1728,7 +1756,7 @@ class PersistScheduleUtils {
1728
1756
  * @returns Promise that resolves when write is complete
1729
1757
  */
1730
1758
  this.writeScheduleData = async (scheduledSignalRow, symbol, strategyName, exchangeName) => {
1731
- LOGGER_SERVICE$7.info(PERSIST_SCHEDULE_UTILS_METHOD_NAME_WRITE_DATA);
1759
+ LOGGER_SERVICE$8.info(PERSIST_SCHEDULE_UTILS_METHOD_NAME_WRITE_DATA);
1732
1760
  const key = `${symbol}:${strategyName}:${exchangeName}`;
1733
1761
  const isInitial = !this.getScheduleStorage.has(key);
1734
1762
  const instance = this.getScheduleStorage(symbol, strategyName, exchangeName);
@@ -1743,7 +1771,7 @@ class PersistScheduleUtils {
1743
1771
  * @param Ctor - Custom IPersistScheduleInstance constructor
1744
1772
  */
1745
1773
  usePersistScheduleAdapter(Ctor) {
1746
- LOGGER_SERVICE$7.info(PERSIST_SCHEDULE_UTILS_METHOD_NAME_USE_PERSIST_SCHEDULE_ADAPTER);
1774
+ LOGGER_SERVICE$8.info(PERSIST_SCHEDULE_UTILS_METHOD_NAME_USE_PERSIST_SCHEDULE_ADAPTER);
1747
1775
  this.PersistScheduleInstanceCtor = Ctor;
1748
1776
  this.getScheduleStorage.clear();
1749
1777
  }
@@ -1752,21 +1780,21 @@ class PersistScheduleUtils {
1752
1780
  * Call when process.cwd() changes between strategy iterations.
1753
1781
  */
1754
1782
  clear() {
1755
- LOGGER_SERVICE$7.log(PERSIST_SCHEDULE_UTILS_METHOD_NAME_CLEAR);
1783
+ LOGGER_SERVICE$8.log(PERSIST_SCHEDULE_UTILS_METHOD_NAME_CLEAR);
1756
1784
  this.getScheduleStorage.clear();
1757
1785
  }
1758
1786
  /**
1759
1787
  * Switches to the default file-based PersistScheduleInstance.
1760
1788
  */
1761
1789
  useJson() {
1762
- LOGGER_SERVICE$7.log(PERSIST_SCHEDULE_UTILS_METHOD_NAME_USE_JSON);
1790
+ LOGGER_SERVICE$8.log(PERSIST_SCHEDULE_UTILS_METHOD_NAME_USE_JSON);
1763
1791
  this.usePersistScheduleAdapter(PersistScheduleInstance);
1764
1792
  }
1765
1793
  /**
1766
1794
  * Switches to PersistScheduleDummyInstance (all operations are no-ops).
1767
1795
  */
1768
1796
  useDummy() {
1769
- LOGGER_SERVICE$7.log(PERSIST_SCHEDULE_UTILS_METHOD_NAME_USE_DUMMY);
1797
+ LOGGER_SERVICE$8.log(PERSIST_SCHEDULE_UTILS_METHOD_NAME_USE_DUMMY);
1770
1798
  this.usePersistScheduleAdapter(PersistScheduleDummyInstance);
1771
1799
  }
1772
1800
  }
@@ -1911,7 +1939,7 @@ class PersistPartialUtils {
1911
1939
  * @returns Promise resolving to partial data record (empty object if none)
1912
1940
  */
1913
1941
  this.readPartialData = async (symbol, strategyName, signalId, exchangeName, when) => {
1914
- LOGGER_SERVICE$7.info(PERSIST_PARTIAL_UTILS_METHOD_NAME_READ_DATA);
1942
+ LOGGER_SERVICE$8.info(PERSIST_PARTIAL_UTILS_METHOD_NAME_READ_DATA);
1915
1943
  const key = `${symbol}:${strategyName}:${exchangeName}`;
1916
1944
  const isInitial = !this.getPartialStorage.has(key);
1917
1945
  const instance = this.getPartialStorage(symbol, strategyName, exchangeName);
@@ -1931,7 +1959,7 @@ class PersistPartialUtils {
1931
1959
  * @returns Promise that resolves when write is complete
1932
1960
  */
1933
1961
  this.writePartialData = async (partialData, symbol, strategyName, signalId, exchangeName, when) => {
1934
- LOGGER_SERVICE$7.info(PERSIST_PARTIAL_UTILS_METHOD_NAME_WRITE_DATA);
1962
+ LOGGER_SERVICE$8.info(PERSIST_PARTIAL_UTILS_METHOD_NAME_WRITE_DATA);
1935
1963
  const key = `${symbol}:${strategyName}:${exchangeName}`;
1936
1964
  const isInitial = !this.getPartialStorage.has(key);
1937
1965
  const instance = this.getPartialStorage(symbol, strategyName, exchangeName);
@@ -1946,7 +1974,7 @@ class PersistPartialUtils {
1946
1974
  * @param Ctor - Custom IPersistPartialInstance constructor
1947
1975
  */
1948
1976
  usePersistPartialAdapter(Ctor) {
1949
- LOGGER_SERVICE$7.info(PERSIST_PARTIAL_UTILS_METHOD_NAME_USE_PERSIST_PARTIAL_ADAPTER);
1977
+ LOGGER_SERVICE$8.info(PERSIST_PARTIAL_UTILS_METHOD_NAME_USE_PERSIST_PARTIAL_ADAPTER);
1950
1978
  this.PersistPartialInstanceCtor = Ctor;
1951
1979
  this.getPartialStorage.clear();
1952
1980
  }
@@ -1955,21 +1983,21 @@ class PersistPartialUtils {
1955
1983
  * Call when process.cwd() changes between strategy iterations.
1956
1984
  */
1957
1985
  clear() {
1958
- LOGGER_SERVICE$7.log(PERSIST_PARTIAL_UTILS_METHOD_NAME_CLEAR);
1986
+ LOGGER_SERVICE$8.log(PERSIST_PARTIAL_UTILS_METHOD_NAME_CLEAR);
1959
1987
  this.getPartialStorage.clear();
1960
1988
  }
1961
1989
  /**
1962
1990
  * Switches to the default file-based PersistPartialInstance.
1963
1991
  */
1964
1992
  useJson() {
1965
- LOGGER_SERVICE$7.log(PERSIST_PARTIAL_UTILS_METHOD_NAME_USE_JSON);
1993
+ LOGGER_SERVICE$8.log(PERSIST_PARTIAL_UTILS_METHOD_NAME_USE_JSON);
1966
1994
  this.usePersistPartialAdapter(PersistPartialInstance);
1967
1995
  }
1968
1996
  /**
1969
1997
  * Switches to PersistPartialDummyInstance (all operations are no-ops).
1970
1998
  */
1971
1999
  useDummy() {
1972
- LOGGER_SERVICE$7.log(PERSIST_PARTIAL_UTILS_METHOD_NAME_USE_DUMMY);
2000
+ LOGGER_SERVICE$8.log(PERSIST_PARTIAL_UTILS_METHOD_NAME_USE_DUMMY);
1973
2001
  this.usePersistPartialAdapter(PersistPartialDummyInstance);
1974
2002
  }
1975
2003
  }
@@ -2134,7 +2162,7 @@ class PersistBreakevenUtils {
2134
2162
  * @returns Promise resolving to breakeven data record (empty object if none)
2135
2163
  */
2136
2164
  this.readBreakevenData = async (symbol, strategyName, signalId, exchangeName, when) => {
2137
- LOGGER_SERVICE$7.info(PERSIST_BREAKEVEN_UTILS_METHOD_NAME_READ_DATA);
2165
+ LOGGER_SERVICE$8.info(PERSIST_BREAKEVEN_UTILS_METHOD_NAME_READ_DATA);
2138
2166
  const key = `${symbol}:${strategyName}:${exchangeName}`;
2139
2167
  const isInitial = !this.getBreakevenStorage.has(key);
2140
2168
  const instance = this.getBreakevenStorage(symbol, strategyName, exchangeName);
@@ -2154,7 +2182,7 @@ class PersistBreakevenUtils {
2154
2182
  * @returns Promise that resolves when write is complete
2155
2183
  */
2156
2184
  this.writeBreakevenData = async (breakevenData, symbol, strategyName, signalId, exchangeName, when) => {
2157
- LOGGER_SERVICE$7.info(PERSIST_BREAKEVEN_UTILS_METHOD_NAME_WRITE_DATA);
2185
+ LOGGER_SERVICE$8.info(PERSIST_BREAKEVEN_UTILS_METHOD_NAME_WRITE_DATA);
2158
2186
  const key = `${symbol}:${strategyName}:${exchangeName}`;
2159
2187
  const isInitial = !this.getBreakevenStorage.has(key);
2160
2188
  const instance = this.getBreakevenStorage(symbol, strategyName, exchangeName);
@@ -2169,7 +2197,7 @@ class PersistBreakevenUtils {
2169
2197
  * @param Ctor - Custom IPersistBreakevenInstance constructor
2170
2198
  */
2171
2199
  usePersistBreakevenAdapter(Ctor) {
2172
- LOGGER_SERVICE$7.info(PERSIST_BREAKEVEN_UTILS_METHOD_NAME_USE_PERSIST_BREAKEVEN_ADAPTER);
2200
+ LOGGER_SERVICE$8.info(PERSIST_BREAKEVEN_UTILS_METHOD_NAME_USE_PERSIST_BREAKEVEN_ADAPTER);
2173
2201
  this.PersistBreakevenInstanceCtor = Ctor;
2174
2202
  this.getBreakevenStorage.clear();
2175
2203
  }
@@ -2178,21 +2206,21 @@ class PersistBreakevenUtils {
2178
2206
  * Call when process.cwd() changes between strategy iterations.
2179
2207
  */
2180
2208
  clear() {
2181
- LOGGER_SERVICE$7.log(PERSIST_BREAKEVEN_UTILS_METHOD_NAME_CLEAR);
2209
+ LOGGER_SERVICE$8.log(PERSIST_BREAKEVEN_UTILS_METHOD_NAME_CLEAR);
2182
2210
  this.getBreakevenStorage.clear();
2183
2211
  }
2184
2212
  /**
2185
2213
  * Switches to the default file-based PersistBreakevenInstance.
2186
2214
  */
2187
2215
  useJson() {
2188
- LOGGER_SERVICE$7.log(PERSIST_BREAKEVEN_UTILS_METHOD_NAME_USE_JSON);
2216
+ LOGGER_SERVICE$8.log(PERSIST_BREAKEVEN_UTILS_METHOD_NAME_USE_JSON);
2189
2217
  this.usePersistBreakevenAdapter(PersistBreakevenInstance);
2190
2218
  }
2191
2219
  /**
2192
2220
  * Switches to PersistBreakevenDummyInstance (all operations are no-ops).
2193
2221
  */
2194
2222
  useDummy() {
2195
- LOGGER_SERVICE$7.log(PERSIST_BREAKEVEN_UTILS_METHOD_NAME_USE_DUMMY);
2223
+ LOGGER_SERVICE$8.log(PERSIST_BREAKEVEN_UTILS_METHOD_NAME_USE_DUMMY);
2196
2224
  this.usePersistBreakevenAdapter(PersistBreakevenDummyInstance);
2197
2225
  }
2198
2226
  }
@@ -2283,7 +2311,7 @@ class PersistCandleInstance {
2283
2311
  error: errorData(error),
2284
2312
  message: getErrorMessage(error),
2285
2313
  };
2286
- LOGGER_SERVICE$7.warn(message, payload);
2314
+ LOGGER_SERVICE$8.warn(message, payload);
2287
2315
  console.warn(message, payload);
2288
2316
  errorEmitter.next(error);
2289
2317
  return null;
@@ -2305,7 +2333,7 @@ class PersistCandleInstance {
2305
2333
  for (const candle of candles) {
2306
2334
  const candleCloseTime = candle.timestamp + stepMs;
2307
2335
  if (candleCloseTime > now) {
2308
- LOGGER_SERVICE$7.debug("PersistCandleInstance.writeCandlesData: skipping incomplete candle", {
2336
+ LOGGER_SERVICE$8.debug("PersistCandleInstance.writeCandlesData: skipping incomplete candle", {
2309
2337
  symbol: this.symbol,
2310
2338
  interval: this.interval,
2311
2339
  exchangeName: this.exchangeName,
@@ -2382,7 +2410,7 @@ class PersistCandleUtils {
2382
2410
  * @returns Promise resolving to candles in order, or null on cache miss
2383
2411
  */
2384
2412
  this.readCandlesData = async (symbol, interval, exchangeName, limit, sinceTimestamp, untilTimestamp) => {
2385
- LOGGER_SERVICE$7.info("PersistCandleUtils.readCandlesData", {
2413
+ LOGGER_SERVICE$8.info("PersistCandleUtils.readCandlesData", {
2386
2414
  symbol,
2387
2415
  interval,
2388
2416
  exchangeName,
@@ -2406,7 +2434,7 @@ class PersistCandleUtils {
2406
2434
  * @returns Promise that resolves when all writes are complete
2407
2435
  */
2408
2436
  this.writeCandlesData = async (candles, symbol, interval, exchangeName) => {
2409
- LOGGER_SERVICE$7.info("PersistCandleUtils.writeCandlesData", {
2437
+ LOGGER_SERVICE$8.info("PersistCandleUtils.writeCandlesData", {
2410
2438
  symbol,
2411
2439
  interval,
2412
2440
  exchangeName,
@@ -2426,7 +2454,7 @@ class PersistCandleUtils {
2426
2454
  * @param Ctor - Custom IPersistCandleInstance constructor
2427
2455
  */
2428
2456
  usePersistCandleAdapter(Ctor) {
2429
- LOGGER_SERVICE$7.info("PersistCandleUtils.usePersistCandleAdapter");
2457
+ LOGGER_SERVICE$8.info("PersistCandleUtils.usePersistCandleAdapter");
2430
2458
  this.PersistCandleInstanceCtor = Ctor;
2431
2459
  this.getCandlesStorage.clear();
2432
2460
  }
@@ -2435,21 +2463,21 @@ class PersistCandleUtils {
2435
2463
  * Call when process.cwd() changes between strategy iterations.
2436
2464
  */
2437
2465
  clear() {
2438
- LOGGER_SERVICE$7.log(PERSIST_CANDLE_UTILS_METHOD_NAME_CLEAR);
2466
+ LOGGER_SERVICE$8.log(PERSIST_CANDLE_UTILS_METHOD_NAME_CLEAR);
2439
2467
  this.getCandlesStorage.clear();
2440
2468
  }
2441
2469
  /**
2442
2470
  * Switches to the default file-based PersistCandleInstance.
2443
2471
  */
2444
2472
  useJson() {
2445
- LOGGER_SERVICE$7.log("PersistCandleUtils.useJson");
2473
+ LOGGER_SERVICE$8.log("PersistCandleUtils.useJson");
2446
2474
  this.usePersistCandleAdapter(PersistCandleInstance);
2447
2475
  }
2448
2476
  /**
2449
2477
  * Switches to PersistCandleDummyInstance (always returns null on read, discards writes).
2450
2478
  */
2451
2479
  useDummy() {
2452
- LOGGER_SERVICE$7.log("PersistCandleUtils.useDummy");
2480
+ LOGGER_SERVICE$8.log("PersistCandleUtils.useDummy");
2453
2481
  this.usePersistCandleAdapter(PersistCandleDummyInstance);
2454
2482
  }
2455
2483
  }
@@ -2587,7 +2615,7 @@ class PersistStorageUtils {
2587
2615
  * @returns Promise resolving to array of signal entries
2588
2616
  */
2589
2617
  this.readStorageData = async (backtest) => {
2590
- LOGGER_SERVICE$7.info(PERSIST_STORAGE_UTILS_METHOD_NAME_READ_DATA);
2618
+ LOGGER_SERVICE$8.info(PERSIST_STORAGE_UTILS_METHOD_NAME_READ_DATA);
2591
2619
  const key = backtest ? `backtest` : `live`;
2592
2620
  const isInitial = !this.getStorage.has(key);
2593
2621
  const instance = this.getStorage(backtest);
@@ -2603,7 +2631,7 @@ class PersistStorageUtils {
2603
2631
  * @returns Promise that resolves when write is complete
2604
2632
  */
2605
2633
  this.writeStorageData = async (signalData, backtest) => {
2606
- LOGGER_SERVICE$7.info(PERSIST_STORAGE_UTILS_METHOD_NAME_WRITE_DATA);
2634
+ LOGGER_SERVICE$8.info(PERSIST_STORAGE_UTILS_METHOD_NAME_WRITE_DATA);
2607
2635
  const key = backtest ? `backtest` : `live`;
2608
2636
  const isInitial = !this.getStorage.has(key);
2609
2637
  const instance = this.getStorage(backtest);
@@ -2618,7 +2646,7 @@ class PersistStorageUtils {
2618
2646
  * @param Ctor - Custom IPersistStorageInstance constructor
2619
2647
  */
2620
2648
  usePersistStorageAdapter(Ctor) {
2621
- LOGGER_SERVICE$7.info(PERSIST_STORAGE_UTILS_METHOD_NAME_USE_PERSIST_STORAGE_ADAPTER);
2649
+ LOGGER_SERVICE$8.info(PERSIST_STORAGE_UTILS_METHOD_NAME_USE_PERSIST_STORAGE_ADAPTER);
2622
2650
  this.PersistStorageInstanceCtor = Ctor;
2623
2651
  this.getStorage.clear();
2624
2652
  }
@@ -2627,21 +2655,21 @@ class PersistStorageUtils {
2627
2655
  * Call when process.cwd() changes between strategy iterations.
2628
2656
  */
2629
2657
  clear() {
2630
- LOGGER_SERVICE$7.log(PERSIST_STORAGE_UTILS_METHOD_NAME_CLEAR);
2658
+ LOGGER_SERVICE$8.log(PERSIST_STORAGE_UTILS_METHOD_NAME_CLEAR);
2631
2659
  this.getStorage.clear();
2632
2660
  }
2633
2661
  /**
2634
2662
  * Switches to the default file-based PersistStorageInstance.
2635
2663
  */
2636
2664
  useJson() {
2637
- LOGGER_SERVICE$7.log(PERSIST_STORAGE_UTILS_METHOD_NAME_USE_JSON);
2665
+ LOGGER_SERVICE$8.log(PERSIST_STORAGE_UTILS_METHOD_NAME_USE_JSON);
2638
2666
  this.usePersistStorageAdapter(PersistStorageInstance);
2639
2667
  }
2640
2668
  /**
2641
2669
  * Switches to PersistStorageDummyInstance (all operations are no-ops).
2642
2670
  */
2643
2671
  useDummy() {
2644
- LOGGER_SERVICE$7.log(PERSIST_STORAGE_UTILS_METHOD_NAME_USE_DUMMY);
2672
+ LOGGER_SERVICE$8.log(PERSIST_STORAGE_UTILS_METHOD_NAME_USE_DUMMY);
2645
2673
  this.usePersistStorageAdapter(PersistStorageDummyInstance);
2646
2674
  }
2647
2675
  }
@@ -2768,7 +2796,7 @@ class PersistNotificationUtils {
2768
2796
  * @returns Promise resolving to array of notification entries
2769
2797
  */
2770
2798
  this.readNotificationData = async (backtest) => {
2771
- LOGGER_SERVICE$7.info(PERSIST_NOTIFICATION_UTILS_METHOD_NAME_READ_DATA);
2799
+ LOGGER_SERVICE$8.info(PERSIST_NOTIFICATION_UTILS_METHOD_NAME_READ_DATA);
2772
2800
  const key = backtest ? `backtest` : `live`;
2773
2801
  const isInitial = !this.getNotificationStorage.has(key);
2774
2802
  const instance = this.getNotificationStorage(backtest);
@@ -2784,7 +2812,7 @@ class PersistNotificationUtils {
2784
2812
  * @returns Promise that resolves when write is complete
2785
2813
  */
2786
2814
  this.writeNotificationData = async (notificationData, backtest) => {
2787
- LOGGER_SERVICE$7.info(PERSIST_NOTIFICATION_UTILS_METHOD_NAME_WRITE_DATA);
2815
+ LOGGER_SERVICE$8.info(PERSIST_NOTIFICATION_UTILS_METHOD_NAME_WRITE_DATA);
2788
2816
  const key = backtest ? `backtest` : `live`;
2789
2817
  const isInitial = !this.getNotificationStorage.has(key);
2790
2818
  const instance = this.getNotificationStorage(backtest);
@@ -2799,7 +2827,7 @@ class PersistNotificationUtils {
2799
2827
  * @param Ctor - Custom IPersistNotificationInstance constructor
2800
2828
  */
2801
2829
  usePersistNotificationAdapter(Ctor) {
2802
- LOGGER_SERVICE$7.info(PERSIST_NOTIFICATION_UTILS_METHOD_NAME_USE_PERSIST_NOTIFICATION_ADAPTER);
2830
+ LOGGER_SERVICE$8.info(PERSIST_NOTIFICATION_UTILS_METHOD_NAME_USE_PERSIST_NOTIFICATION_ADAPTER);
2803
2831
  this.PersistNotificationInstanceCtor = Ctor;
2804
2832
  this.getNotificationStorage.clear();
2805
2833
  }
@@ -2809,21 +2837,21 @@ class PersistNotificationUtils {
2809
2837
  * instances are created with the updated base path.
2810
2838
  */
2811
2839
  clear() {
2812
- LOGGER_SERVICE$7.log(PERSIST_NOTIFICATION_UTILS_METHOD_NAME_CLEAR);
2840
+ LOGGER_SERVICE$8.log(PERSIST_NOTIFICATION_UTILS_METHOD_NAME_CLEAR);
2813
2841
  this.getNotificationStorage.clear();
2814
2842
  }
2815
2843
  /**
2816
2844
  * Switches to the default file-based PersistNotificationInstance.
2817
2845
  */
2818
2846
  useJson() {
2819
- LOGGER_SERVICE$7.log(PERSIST_NOTIFICATION_UTILS_METHOD_NAME_USE_JSON);
2847
+ LOGGER_SERVICE$8.log(PERSIST_NOTIFICATION_UTILS_METHOD_NAME_USE_JSON);
2820
2848
  this.usePersistNotificationAdapter(PersistNotificationInstance);
2821
2849
  }
2822
2850
  /**
2823
2851
  * Switches to PersistNotificationDummyInstance (all operations are no-ops).
2824
2852
  */
2825
2853
  useDummy() {
2826
- LOGGER_SERVICE$7.log(PERSIST_NOTIFICATION_UTILS_METHOD_NAME_USE_DUMMY);
2854
+ LOGGER_SERVICE$8.log(PERSIST_NOTIFICATION_UTILS_METHOD_NAME_USE_DUMMY);
2827
2855
  this.usePersistNotificationAdapter(PersistNotificationDummyInstance);
2828
2856
  }
2829
2857
  }
@@ -2947,7 +2975,7 @@ class PersistLogUtils {
2947
2975
  * @returns Promise resolving to array of log entries
2948
2976
  */
2949
2977
  this.readLogData = async () => {
2950
- LOGGER_SERVICE$7.info(PERSIST_LOG_UTILS_METHOD_NAME_READ_DATA);
2978
+ LOGGER_SERVICE$8.info(PERSIST_LOG_UTILS_METHOD_NAME_READ_DATA);
2951
2979
  const isInitial = !this._logInstance;
2952
2980
  const instance = this.getLogInstance();
2953
2981
  await instance.waitForInit(isInitial);
@@ -2961,7 +2989,7 @@ class PersistLogUtils {
2961
2989
  * @returns Promise that resolves when write is complete
2962
2990
  */
2963
2991
  this.writeLogData = async (logData) => {
2964
- LOGGER_SERVICE$7.info(PERSIST_LOG_UTILS_METHOD_NAME_WRITE_DATA);
2992
+ LOGGER_SERVICE$8.info(PERSIST_LOG_UTILS_METHOD_NAME_WRITE_DATA);
2965
2993
  const isInitial = !this._logInstance;
2966
2994
  const instance = this.getLogInstance();
2967
2995
  await instance.waitForInit(isInitial);
@@ -2986,7 +3014,7 @@ class PersistLogUtils {
2986
3014
  * @param Ctor - Custom IPersistLogInstance constructor
2987
3015
  */
2988
3016
  usePersistLogAdapter(Ctor) {
2989
- LOGGER_SERVICE$7.info(PERSIST_LOG_UTILS_METHOD_NAME_USE_PERSIST_LOG_ADAPTER);
3017
+ LOGGER_SERVICE$8.info(PERSIST_LOG_UTILS_METHOD_NAME_USE_PERSIST_LOG_ADAPTER);
2990
3018
  this.PersistLogInstanceCtor = Ctor;
2991
3019
  this._logInstance = null;
2992
3020
  }
@@ -2995,21 +3023,21 @@ class PersistLogUtils {
2995
3023
  * Call when process.cwd() changes between strategy iterations.
2996
3024
  */
2997
3025
  clear() {
2998
- LOGGER_SERVICE$7.log(PERSIST_LOG_UTILS_METHOD_NAME_CLEAR);
3026
+ LOGGER_SERVICE$8.log(PERSIST_LOG_UTILS_METHOD_NAME_CLEAR);
2999
3027
  this._logInstance = null;
3000
3028
  }
3001
3029
  /**
3002
3030
  * Switches to the default file-based PersistLogInstance.
3003
3031
  */
3004
3032
  useJson() {
3005
- LOGGER_SERVICE$7.log(PERSIST_LOG_UTILS_METHOD_NAME_USE_JSON);
3033
+ LOGGER_SERVICE$8.log(PERSIST_LOG_UTILS_METHOD_NAME_USE_JSON);
3006
3034
  this.usePersistLogAdapter(PersistLogInstance);
3007
3035
  }
3008
3036
  /**
3009
3037
  * Switches to PersistLogDummyInstance (all operations are no-ops).
3010
3038
  */
3011
3039
  useDummy() {
3012
- LOGGER_SERVICE$7.log(PERSIST_LOG_UTILS_METHOD_NAME_USE_DUMMY);
3040
+ LOGGER_SERVICE$8.log(PERSIST_LOG_UTILS_METHOD_NAME_USE_DUMMY);
3013
3041
  this.usePersistLogAdapter(PersistLogDummyInstance);
3014
3042
  }
3015
3043
  }
@@ -3171,7 +3199,7 @@ class PersistMeasureUtils {
3171
3199
  * @returns Promise resolving to cached value, or null if not found / soft-deleted
3172
3200
  */
3173
3201
  this.readMeasureData = async (bucket, key) => {
3174
- LOGGER_SERVICE$7.info(PERSIST_MEASURE_UTILS_METHOD_NAME_READ_DATA, { bucket, key });
3202
+ LOGGER_SERVICE$8.info(PERSIST_MEASURE_UTILS_METHOD_NAME_READ_DATA, { bucket, key });
3175
3203
  const isInitial = !this.getMeasureStorage.has(bucket);
3176
3204
  const instance = this.getMeasureStorage(bucket);
3177
3205
  await instance.waitForInit(isInitial);
@@ -3187,7 +3215,7 @@ class PersistMeasureUtils {
3187
3215
  * @returns Promise that resolves when write is complete
3188
3216
  */
3189
3217
  this.writeMeasureData = async (data, bucket, key, when) => {
3190
- LOGGER_SERVICE$7.info(PERSIST_MEASURE_UTILS_METHOD_NAME_WRITE_DATA, { bucket, key });
3218
+ LOGGER_SERVICE$8.info(PERSIST_MEASURE_UTILS_METHOD_NAME_WRITE_DATA, { bucket, key });
3191
3219
  const isInitial = !this.getMeasureStorage.has(bucket);
3192
3220
  const instance = this.getMeasureStorage(bucket);
3193
3221
  await instance.waitForInit(isInitial);
@@ -3202,7 +3230,7 @@ class PersistMeasureUtils {
3202
3230
  * @returns Promise that resolves when removal is complete
3203
3231
  */
3204
3232
  this.removeMeasureData = async (bucket, key) => {
3205
- LOGGER_SERVICE$7.info(PERSIST_MEASURE_UTILS_METHOD_NAME_REMOVE_DATA, { bucket, key });
3233
+ LOGGER_SERVICE$8.info(PERSIST_MEASURE_UTILS_METHOD_NAME_REMOVE_DATA, { bucket, key });
3206
3234
  const isInitial = !this.getMeasureStorage.has(bucket);
3207
3235
  const instance = this.getMeasureStorage(bucket);
3208
3236
  await instance.waitForInit(isInitial);
@@ -3216,7 +3244,7 @@ class PersistMeasureUtils {
3216
3244
  * @param Ctor - Custom IPersistMeasureInstance constructor
3217
3245
  */
3218
3246
  usePersistMeasureAdapter(Ctor) {
3219
- LOGGER_SERVICE$7.info(PERSIST_MEASURE_UTILS_METHOD_NAME_USE_PERSIST_MEASURE_ADAPTER);
3247
+ LOGGER_SERVICE$8.info(PERSIST_MEASURE_UTILS_METHOD_NAME_USE_PERSIST_MEASURE_ADAPTER);
3220
3248
  this.PersistMeasureInstanceCtor = Ctor;
3221
3249
  this.getMeasureStorage.clear();
3222
3250
  }
@@ -3228,7 +3256,7 @@ class PersistMeasureUtils {
3228
3256
  * @returns AsyncGenerator yielding entry keys
3229
3257
  */
3230
3258
  async *listMeasureData(bucket) {
3231
- LOGGER_SERVICE$7.info(PERSIST_MEASURE_UTILS_METHOD_NAME_LIST_DATA, { bucket });
3259
+ LOGGER_SERVICE$8.info(PERSIST_MEASURE_UTILS_METHOD_NAME_LIST_DATA, { bucket });
3232
3260
  const isInitial = !this.getMeasureStorage.has(bucket);
3233
3261
  const instance = this.getMeasureStorage(bucket);
3234
3262
  await instance.waitForInit(isInitial);
@@ -3239,21 +3267,21 @@ class PersistMeasureUtils {
3239
3267
  * Call when process.cwd() changes between strategy iterations.
3240
3268
  */
3241
3269
  clear() {
3242
- LOGGER_SERVICE$7.log(PERSIST_MEASURE_UTILS_METHOD_NAME_CLEAR);
3270
+ LOGGER_SERVICE$8.log(PERSIST_MEASURE_UTILS_METHOD_NAME_CLEAR);
3243
3271
  this.getMeasureStorage.clear();
3244
3272
  }
3245
3273
  /**
3246
3274
  * Switches to the default file-based PersistMeasureInstance.
3247
3275
  */
3248
3276
  useJson() {
3249
- LOGGER_SERVICE$7.log(PERSIST_MEASURE_UTILS_METHOD_NAME_USE_JSON);
3277
+ LOGGER_SERVICE$8.log(PERSIST_MEASURE_UTILS_METHOD_NAME_USE_JSON);
3250
3278
  this.usePersistMeasureAdapter(PersistMeasureInstance);
3251
3279
  }
3252
3280
  /**
3253
3281
  * Switches to PersistMeasureDummyInstance (all operations are no-ops).
3254
3282
  */
3255
3283
  useDummy() {
3256
- LOGGER_SERVICE$7.log(PERSIST_MEASURE_UTILS_METHOD_NAME_USE_DUMMY);
3284
+ LOGGER_SERVICE$8.log(PERSIST_MEASURE_UTILS_METHOD_NAME_USE_DUMMY);
3257
3285
  this.usePersistMeasureAdapter(PersistMeasureDummyInstance);
3258
3286
  }
3259
3287
  }
@@ -3412,7 +3440,7 @@ class PersistIntervalUtils {
3412
3440
  * @returns Promise resolving to marker data, or null if not found / soft-deleted
3413
3441
  */
3414
3442
  this.readIntervalData = async (bucket, key) => {
3415
- LOGGER_SERVICE$7.info(PERSIST_INTERVAL_UTILS_METHOD_NAME_READ_DATA, { bucket, key });
3443
+ LOGGER_SERVICE$8.info(PERSIST_INTERVAL_UTILS_METHOD_NAME_READ_DATA, { bucket, key });
3416
3444
  const isInitial = !this.getIntervalStorage.has(bucket);
3417
3445
  const instance = this.getIntervalStorage(bucket);
3418
3446
  await instance.waitForInit(isInitial);
@@ -3428,7 +3456,7 @@ class PersistIntervalUtils {
3428
3456
  * @returns Promise that resolves when write is complete
3429
3457
  */
3430
3458
  this.writeIntervalData = async (data, bucket, key, when) => {
3431
- LOGGER_SERVICE$7.info(PERSIST_INTERVAL_UTILS_METHOD_NAME_WRITE_DATA, { bucket, key });
3459
+ LOGGER_SERVICE$8.info(PERSIST_INTERVAL_UTILS_METHOD_NAME_WRITE_DATA, { bucket, key });
3432
3460
  const isInitial = !this.getIntervalStorage.has(bucket);
3433
3461
  const instance = this.getIntervalStorage(bucket);
3434
3462
  await instance.waitForInit(isInitial);
@@ -3443,7 +3471,7 @@ class PersistIntervalUtils {
3443
3471
  * @returns Promise that resolves when removal is complete
3444
3472
  */
3445
3473
  this.removeIntervalData = async (bucket, key) => {
3446
- LOGGER_SERVICE$7.info(PERSIST_INTERVAL_UTILS_METHOD_NAME_REMOVE_DATA, { bucket, key });
3474
+ LOGGER_SERVICE$8.info(PERSIST_INTERVAL_UTILS_METHOD_NAME_REMOVE_DATA, { bucket, key });
3447
3475
  const isInitial = !this.getIntervalStorage.has(bucket);
3448
3476
  const instance = this.getIntervalStorage(bucket);
3449
3477
  await instance.waitForInit(isInitial);
@@ -3457,7 +3485,7 @@ class PersistIntervalUtils {
3457
3485
  * @param Ctor - Custom IPersistIntervalInstance constructor
3458
3486
  */
3459
3487
  usePersistIntervalAdapter(Ctor) {
3460
- LOGGER_SERVICE$7.info(PERSIST_INTERVAL_UTILS_METHOD_NAME_USE_PERSIST_INTERVAL_ADAPTER);
3488
+ LOGGER_SERVICE$8.info(PERSIST_INTERVAL_UTILS_METHOD_NAME_USE_PERSIST_INTERVAL_ADAPTER);
3461
3489
  this.PersistIntervalInstanceCtor = Ctor;
3462
3490
  this.getIntervalStorage.clear();
3463
3491
  }
@@ -3469,7 +3497,7 @@ class PersistIntervalUtils {
3469
3497
  * @returns AsyncGenerator yielding marker keys
3470
3498
  */
3471
3499
  async *listIntervalData(bucket) {
3472
- LOGGER_SERVICE$7.info(PERSIST_INTERVAL_UTILS_METHOD_NAME_LIST_DATA, { bucket });
3500
+ LOGGER_SERVICE$8.info(PERSIST_INTERVAL_UTILS_METHOD_NAME_LIST_DATA, { bucket });
3473
3501
  const isInitial = !this.getIntervalStorage.has(bucket);
3474
3502
  const instance = this.getIntervalStorage(bucket);
3475
3503
  await instance.waitForInit(isInitial);
@@ -3480,21 +3508,21 @@ class PersistIntervalUtils {
3480
3508
  * Call when process.cwd() changes between strategy iterations.
3481
3509
  */
3482
3510
  clear() {
3483
- LOGGER_SERVICE$7.log(PERSIST_INTERVAL_UTILS_METHOD_NAME_CLEAR);
3511
+ LOGGER_SERVICE$8.log(PERSIST_INTERVAL_UTILS_METHOD_NAME_CLEAR);
3484
3512
  this.getIntervalStorage.clear();
3485
3513
  }
3486
3514
  /**
3487
3515
  * Switches to the default file-based PersistIntervalInstance.
3488
3516
  */
3489
3517
  useJson() {
3490
- LOGGER_SERVICE$7.log(PERSIST_INTERVAL_UTILS_METHOD_NAME_USE_JSON);
3518
+ LOGGER_SERVICE$8.log(PERSIST_INTERVAL_UTILS_METHOD_NAME_USE_JSON);
3491
3519
  this.usePersistIntervalAdapter(PersistIntervalInstance);
3492
3520
  }
3493
3521
  /**
3494
3522
  * Switches to PersistIntervalDummyInstance (all operations are no-ops).
3495
3523
  */
3496
3524
  useDummy() {
3497
- LOGGER_SERVICE$7.log(PERSIST_INTERVAL_UTILS_METHOD_NAME_USE_DUMMY);
3525
+ LOGGER_SERVICE$8.log(PERSIST_INTERVAL_UTILS_METHOD_NAME_USE_DUMMY);
3498
3526
  this.usePersistIntervalAdapter(PersistIntervalDummyInstance);
3499
3527
  }
3500
3528
  }
@@ -3700,7 +3728,7 @@ class PersistMemoryUtils {
3700
3728
  * @returns Promise resolving to entry data, or null if not found / soft-deleted
3701
3729
  */
3702
3730
  this.readMemoryData = async (signalId, bucketName, memoryId) => {
3703
- LOGGER_SERVICE$7.info(PERSIST_MEMORY_UTILS_METHOD_NAME_READ_DATA, { signalId, bucketName, memoryId });
3731
+ LOGGER_SERVICE$8.info(PERSIST_MEMORY_UTILS_METHOD_NAME_READ_DATA, { signalId, bucketName, memoryId });
3704
3732
  const key = `${signalId}:${bucketName}`;
3705
3733
  const isInitial = !this.getMemoryStorage.has(key);
3706
3734
  const instance = this.getMemoryStorage(signalId, bucketName);
@@ -3717,7 +3745,7 @@ class PersistMemoryUtils {
3717
3745
  * @returns Promise resolving to true if entry exists
3718
3746
  */
3719
3747
  this.hasMemoryData = async (signalId, bucketName, memoryId) => {
3720
- LOGGER_SERVICE$7.info(PERSIST_MEMORY_UTILS_METHOD_NAME_HAS_DATA, { signalId, bucketName, memoryId });
3748
+ LOGGER_SERVICE$8.info(PERSIST_MEMORY_UTILS_METHOD_NAME_HAS_DATA, { signalId, bucketName, memoryId });
3721
3749
  const key = `${signalId}:${bucketName}`;
3722
3750
  const isInitial = !this.getMemoryStorage.has(key);
3723
3751
  const instance = this.getMemoryStorage(signalId, bucketName);
@@ -3736,7 +3764,7 @@ class PersistMemoryUtils {
3736
3764
  * @returns Promise that resolves when write is complete
3737
3765
  */
3738
3766
  this.writeMemoryData = async (data, signalId, bucketName, memoryId, when) => {
3739
- LOGGER_SERVICE$7.info(PERSIST_MEMORY_UTILS_METHOD_NAME_WRITE_DATA, { signalId, bucketName, memoryId });
3767
+ LOGGER_SERVICE$8.info(PERSIST_MEMORY_UTILS_METHOD_NAME_WRITE_DATA, { signalId, bucketName, memoryId });
3740
3768
  const key = `${signalId}:${bucketName}`;
3741
3769
  const isInitial = !this.getMemoryStorage.has(key);
3742
3770
  const instance = this.getMemoryStorage(signalId, bucketName);
@@ -3753,7 +3781,7 @@ class PersistMemoryUtils {
3753
3781
  * @returns Promise that resolves when removal is complete
3754
3782
  */
3755
3783
  this.removeMemoryData = async (signalId, bucketName, memoryId) => {
3756
- LOGGER_SERVICE$7.info(PERSIST_MEMORY_UTILS_METHOD_NAME_REMOVE_DATA, { signalId, bucketName, memoryId });
3784
+ LOGGER_SERVICE$8.info(PERSIST_MEMORY_UTILS_METHOD_NAME_REMOVE_DATA, { signalId, bucketName, memoryId });
3757
3785
  const key = `${signalId}:${bucketName}`;
3758
3786
  const isInitial = !this.getMemoryStorage.has(key);
3759
3787
  const instance = this.getMemoryStorage(signalId, bucketName);
@@ -3765,7 +3793,7 @@ class PersistMemoryUtils {
3765
3793
  * Call when process.cwd() changes between strategy iterations.
3766
3794
  */
3767
3795
  this.clear = () => {
3768
- LOGGER_SERVICE$7.info(PERSIST_MEMORY_UTILS_METHOD_NAME_CLEAR);
3796
+ LOGGER_SERVICE$8.info(PERSIST_MEMORY_UTILS_METHOD_NAME_CLEAR);
3769
3797
  this.getMemoryStorage.clear();
3770
3798
  };
3771
3799
  /**
@@ -3776,7 +3804,7 @@ class PersistMemoryUtils {
3776
3804
  * @param bucketName - Bucket name
3777
3805
  */
3778
3806
  this.dispose = (signalId, bucketName) => {
3779
- LOGGER_SERVICE$7.info(PERSIST_MEMORY_UTILS_METHOD_NAME_DISPOSE);
3807
+ LOGGER_SERVICE$8.info(PERSIST_MEMORY_UTILS_METHOD_NAME_DISPOSE);
3780
3808
  const key = `${signalId}:${bucketName}`;
3781
3809
  this.getMemoryStorage.clear(key);
3782
3810
  };
@@ -3788,7 +3816,7 @@ class PersistMemoryUtils {
3788
3816
  * @param Ctor - Custom IPersistMemoryInstance constructor
3789
3817
  */
3790
3818
  usePersistMemoryAdapter(Ctor) {
3791
- LOGGER_SERVICE$7.info(PERSIST_MEMORY_UTILS_METHOD_NAME_USE_PERSIST_MEMORY_ADAPTER);
3819
+ LOGGER_SERVICE$8.info(PERSIST_MEMORY_UTILS_METHOD_NAME_USE_PERSIST_MEMORY_ADAPTER);
3792
3820
  this.PersistMemoryInstanceCtor = Ctor;
3793
3821
  this.getMemoryStorage.clear();
3794
3822
  }
@@ -3802,7 +3830,7 @@ class PersistMemoryUtils {
3802
3830
  * @returns AsyncGenerator yielding `{ memoryId, data }` tuples
3803
3831
  */
3804
3832
  async *listMemoryData(signalId, bucketName) {
3805
- LOGGER_SERVICE$7.info(PERSIST_MEMORY_UTILS_METHOD_NAME_LIST_DATA, { signalId, bucketName });
3833
+ LOGGER_SERVICE$8.info(PERSIST_MEMORY_UTILS_METHOD_NAME_LIST_DATA, { signalId, bucketName });
3806
3834
  const key = `${signalId}:${bucketName}`;
3807
3835
  const isInitial = !this.getMemoryStorage.has(key);
3808
3836
  const instance = this.getMemoryStorage(signalId, bucketName);
@@ -3813,14 +3841,14 @@ class PersistMemoryUtils {
3813
3841
  * Switches to the default file-based PersistMemoryInstance.
3814
3842
  */
3815
3843
  useJson() {
3816
- LOGGER_SERVICE$7.log(PERSIST_SIGNAL_UTILS_METHOD_NAME_USE_JSON);
3844
+ LOGGER_SERVICE$8.log(PERSIST_SIGNAL_UTILS_METHOD_NAME_USE_JSON);
3817
3845
  this.usePersistMemoryAdapter(PersistMemoryInstance);
3818
3846
  }
3819
3847
  /**
3820
3848
  * Switches to PersistMemoryDummyInstance (all operations are no-ops).
3821
3849
  */
3822
3850
  useDummy() {
3823
- LOGGER_SERVICE$7.log(PERSIST_SIGNAL_UTILS_METHOD_NAME_USE_DUMMY);
3851
+ LOGGER_SERVICE$8.log(PERSIST_SIGNAL_UTILS_METHOD_NAME_USE_DUMMY);
3824
3852
  this.usePersistMemoryAdapter(PersistMemoryDummyInstance);
3825
3853
  }
3826
3854
  }
@@ -3969,7 +3997,7 @@ class PersistRecentUtils {
3969
3997
  * @returns Promise resolving to recent signal or null if none persisted
3970
3998
  */
3971
3999
  this.readRecentData = async (symbol, strategyName, exchangeName, frameName, backtest) => {
3972
- LOGGER_SERVICE$7.info(PERSIST_RECENT_UTILS_METHOD_NAME_READ_DATA);
4000
+ LOGGER_SERVICE$8.info(PERSIST_RECENT_UTILS_METHOD_NAME_READ_DATA);
3973
4001
  const key = this.createKey(symbol, strategyName, exchangeName, frameName, backtest);
3974
4002
  const isInitial = !this.getStorage.has(key);
3975
4003
  const instance = this.getStorage(symbol, strategyName, exchangeName, frameName, backtest);
@@ -3990,7 +4018,7 @@ class PersistRecentUtils {
3990
4018
  * @returns Promise that resolves when write is complete
3991
4019
  */
3992
4020
  this.writeRecentData = async (signalRow, symbol, strategyName, exchangeName, frameName, backtest, when) => {
3993
- LOGGER_SERVICE$7.info(PERSIST_RECENT_UTILS_METHOD_NAME_WRITE_DATA);
4021
+ LOGGER_SERVICE$8.info(PERSIST_RECENT_UTILS_METHOD_NAME_WRITE_DATA);
3994
4022
  const key = this.createKey(symbol, strategyName, exchangeName, frameName, backtest);
3995
4023
  const isInitial = !this.getStorage.has(key);
3996
4024
  const instance = this.getStorage(symbol, strategyName, exchangeName, frameName, backtest);
@@ -4023,7 +4051,7 @@ class PersistRecentUtils {
4023
4051
  * @param Ctor - Custom IPersistRecentInstance constructor
4024
4052
  */
4025
4053
  usePersistRecentAdapter(Ctor) {
4026
- LOGGER_SERVICE$7.info(PERSIST_RECENT_UTILS_METHOD_NAME_USE_PERSIST_RECENT_ADAPTER);
4054
+ LOGGER_SERVICE$8.info(PERSIST_RECENT_UTILS_METHOD_NAME_USE_PERSIST_RECENT_ADAPTER);
4027
4055
  this.PersistRecentInstanceCtor = Ctor;
4028
4056
  this.getStorage.clear();
4029
4057
  }
@@ -4032,21 +4060,21 @@ class PersistRecentUtils {
4032
4060
  * Call when process.cwd() changes between strategy iterations.
4033
4061
  */
4034
4062
  clear() {
4035
- LOGGER_SERVICE$7.log(PERSIST_RECENT_UTILS_METHOD_NAME_CLEAR);
4063
+ LOGGER_SERVICE$8.log(PERSIST_RECENT_UTILS_METHOD_NAME_CLEAR);
4036
4064
  this.getStorage.clear();
4037
4065
  }
4038
4066
  /**
4039
4067
  * Switches to the default file-based PersistRecentInstance.
4040
4068
  */
4041
4069
  useJson() {
4042
- LOGGER_SERVICE$7.log(PERSIST_RECENT_UTILS_METHOD_NAME_USE_JSON);
4070
+ LOGGER_SERVICE$8.log(PERSIST_RECENT_UTILS_METHOD_NAME_USE_JSON);
4043
4071
  this.usePersistRecentAdapter(PersistRecentInstance);
4044
4072
  }
4045
4073
  /**
4046
4074
  * Switches to PersistRecentDummyInstance (all operations are no-ops).
4047
4075
  */
4048
4076
  useDummy() {
4049
- LOGGER_SERVICE$7.log(PERSIST_RECENT_UTILS_METHOD_NAME_USE_DUMMY);
4077
+ LOGGER_SERVICE$8.log(PERSIST_RECENT_UTILS_METHOD_NAME_USE_DUMMY);
4050
4078
  this.usePersistRecentAdapter(PersistRecentDummyInstance);
4051
4079
  }
4052
4080
  }
@@ -4181,7 +4209,7 @@ class PersistStateUtils {
4181
4209
  * @returns Promise that resolves when initialization is complete
4182
4210
  */
4183
4211
  this.waitForInit = async (signalId, bucketName, initial) => {
4184
- LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_WAIT_FOR_INIT, { signalId, bucketName, initial });
4212
+ LOGGER_SERVICE$8.info(PERSIST_STATE_UTILS_METHOD_NAME_WAIT_FOR_INIT, { signalId, bucketName, initial });
4185
4213
  const key = `${signalId}:${bucketName}`;
4186
4214
  const isInitial = initial && !this.getStateStorage.has(key);
4187
4215
  const instance = this.getStateStorage(signalId, bucketName);
@@ -4196,7 +4224,7 @@ class PersistStateUtils {
4196
4224
  * @returns Promise resolving to state data or null if none persisted
4197
4225
  */
4198
4226
  this.readStateData = async (signalId, bucketName) => {
4199
- LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_READ_DATA, { signalId, bucketName });
4227
+ LOGGER_SERVICE$8.info(PERSIST_STATE_UTILS_METHOD_NAME_READ_DATA, { signalId, bucketName });
4200
4228
  const key = `${signalId}:${bucketName}`;
4201
4229
  const isInitial = !this.getStateStorage.has(key);
4202
4230
  const instance = this.getStateStorage(signalId, bucketName);
@@ -4214,7 +4242,7 @@ class PersistStateUtils {
4214
4242
  * @returns Promise that resolves when write is complete
4215
4243
  */
4216
4244
  this.writeStateData = async (data, signalId, bucketName, when) => {
4217
- LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_WRITE_DATA, { signalId, bucketName });
4245
+ LOGGER_SERVICE$8.info(PERSIST_STATE_UTILS_METHOD_NAME_WRITE_DATA, { signalId, bucketName });
4218
4246
  const key = `${signalId}:${bucketName}`;
4219
4247
  const isInitial = !this.getStateStorage.has(key);
4220
4248
  const instance = this.getStateStorage(signalId, bucketName);
@@ -4225,14 +4253,14 @@ class PersistStateUtils {
4225
4253
  * Switches to PersistStateDummyInstance (all operations are no-ops).
4226
4254
  */
4227
4255
  this.useDummy = () => {
4228
- LOGGER_SERVICE$7.log(PERSIST_STATE_UTILS_METHOD_NAME_USE_DUMMY);
4256
+ LOGGER_SERVICE$8.log(PERSIST_STATE_UTILS_METHOD_NAME_USE_DUMMY);
4229
4257
  this.usePersistStateAdapter(PersistStateDummyInstance);
4230
4258
  };
4231
4259
  /**
4232
4260
  * Switches to the default file-based PersistStateInstance.
4233
4261
  */
4234
4262
  this.useJson = () => {
4235
- LOGGER_SERVICE$7.log(PERSIST_STATE_UTILS_METHOD_NAME_USE_JSON);
4263
+ LOGGER_SERVICE$8.log(PERSIST_STATE_UTILS_METHOD_NAME_USE_JSON);
4236
4264
  this.usePersistStateAdapter(PersistStateInstance);
4237
4265
  };
4238
4266
  /**
@@ -4240,7 +4268,7 @@ class PersistStateUtils {
4240
4268
  * Call when process.cwd() changes between strategy iterations.
4241
4269
  */
4242
4270
  this.clear = () => {
4243
- LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_CLEAR);
4271
+ LOGGER_SERVICE$8.info(PERSIST_STATE_UTILS_METHOD_NAME_CLEAR);
4244
4272
  this.getStateStorage.clear();
4245
4273
  };
4246
4274
  /**
@@ -4251,7 +4279,7 @@ class PersistStateUtils {
4251
4279
  * @param bucketName - Bucket name
4252
4280
  */
4253
4281
  this.dispose = (signalId, bucketName) => {
4254
- LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_DISPOSE);
4282
+ LOGGER_SERVICE$8.info(PERSIST_STATE_UTILS_METHOD_NAME_DISPOSE);
4255
4283
  const key = `${signalId}:${bucketName}`;
4256
4284
  this.getStateStorage.clear(key);
4257
4285
  };
@@ -4263,7 +4291,7 @@ class PersistStateUtils {
4263
4291
  * @param Ctor - Custom IPersistStateInstance constructor
4264
4292
  */
4265
4293
  usePersistStateAdapter(Ctor) {
4266
- LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_USE_PERSIST_STATE_ADAPTER);
4294
+ LOGGER_SERVICE$8.info(PERSIST_STATE_UTILS_METHOD_NAME_USE_PERSIST_STATE_ADAPTER);
4267
4295
  this.PersistStateInstanceCtor = Ctor;
4268
4296
  this.getStateStorage.clear();
4269
4297
  }
@@ -4403,7 +4431,7 @@ class PersistSessionUtils {
4403
4431
  * @returns Promise that resolves when initialization is complete
4404
4432
  */
4405
4433
  this.waitForInit = async (strategyName, exchangeName, frameName, initial) => {
4406
- LOGGER_SERVICE$7.info(PERSIST_SESSION_UTILS_METHOD_NAME_WAIT_FOR_INIT, { strategyName, exchangeName, frameName, initial });
4434
+ LOGGER_SERVICE$8.info(PERSIST_SESSION_UTILS_METHOD_NAME_WAIT_FOR_INIT, { strategyName, exchangeName, frameName, initial });
4407
4435
  const key = `${strategyName}:${exchangeName}:${frameName}`;
4408
4436
  const isInitial = initial && !this.getSessionStorage.has(key);
4409
4437
  const instance = this.getSessionStorage(strategyName, exchangeName, frameName);
@@ -4419,7 +4447,7 @@ class PersistSessionUtils {
4419
4447
  * @returns Promise resolving to session data or null if none persisted
4420
4448
  */
4421
4449
  this.readSessionData = async (strategyName, exchangeName, frameName) => {
4422
- LOGGER_SERVICE$7.info(PERSIST_SESSION_UTILS_METHOD_NAME_READ_DATA, { strategyName, exchangeName, frameName });
4450
+ LOGGER_SERVICE$8.info(PERSIST_SESSION_UTILS_METHOD_NAME_READ_DATA, { strategyName, exchangeName, frameName });
4423
4451
  const key = `${strategyName}:${exchangeName}:${frameName}`;
4424
4452
  const isInitial = !this.getSessionStorage.has(key);
4425
4453
  const instance = this.getSessionStorage(strategyName, exchangeName, frameName);
@@ -4438,7 +4466,7 @@ class PersistSessionUtils {
4438
4466
  * @returns Promise that resolves when write is complete
4439
4467
  */
4440
4468
  this.writeSessionData = async (data, strategyName, exchangeName, frameName, when) => {
4441
- LOGGER_SERVICE$7.info(PERSIST_SESSION_UTILS_METHOD_NAME_WRITE_DATA, { strategyName, exchangeName, frameName });
4469
+ LOGGER_SERVICE$8.info(PERSIST_SESSION_UTILS_METHOD_NAME_WRITE_DATA, { strategyName, exchangeName, frameName });
4442
4470
  const key = `${strategyName}:${exchangeName}:${frameName}`;
4443
4471
  const isInitial = !this.getSessionStorage.has(key);
4444
4472
  const instance = this.getSessionStorage(strategyName, exchangeName, frameName);
@@ -4449,14 +4477,14 @@ class PersistSessionUtils {
4449
4477
  * Switches to PersistSessionDummyInstance (all operations are no-ops).
4450
4478
  */
4451
4479
  this.useDummy = () => {
4452
- LOGGER_SERVICE$7.log(PERSIST_SESSION_UTILS_METHOD_NAME_USE_DUMMY);
4480
+ LOGGER_SERVICE$8.log(PERSIST_SESSION_UTILS_METHOD_NAME_USE_DUMMY);
4453
4481
  this.usePersistSessionAdapter(PersistSessionDummyInstance);
4454
4482
  };
4455
4483
  /**
4456
4484
  * Switches to the default file-based PersistSessionInstance.
4457
4485
  */
4458
4486
  this.useJson = () => {
4459
- LOGGER_SERVICE$7.log(PERSIST_SESSION_UTILS_METHOD_NAME_USE_JSON);
4487
+ LOGGER_SERVICE$8.log(PERSIST_SESSION_UTILS_METHOD_NAME_USE_JSON);
4460
4488
  this.usePersistSessionAdapter(PersistSessionInstance);
4461
4489
  };
4462
4490
  /**
@@ -4464,7 +4492,7 @@ class PersistSessionUtils {
4464
4492
  * Call when process.cwd() changes between strategy iterations.
4465
4493
  */
4466
4494
  this.clear = () => {
4467
- LOGGER_SERVICE$7.info(PERSIST_SESSION_UTILS_METHOD_NAME_CLEAR);
4495
+ LOGGER_SERVICE$8.info(PERSIST_SESSION_UTILS_METHOD_NAME_CLEAR);
4468
4496
  this.getSessionStorage.clear();
4469
4497
  };
4470
4498
  /**
@@ -4476,7 +4504,7 @@ class PersistSessionUtils {
4476
4504
  * @param frameName - Frame identifier
4477
4505
  */
4478
4506
  this.dispose = (strategyName, exchangeName, frameName) => {
4479
- LOGGER_SERVICE$7.info(PERSIST_SESSION_UTILS_METHOD_NAME_DISPOSE);
4507
+ LOGGER_SERVICE$8.info(PERSIST_SESSION_UTILS_METHOD_NAME_DISPOSE);
4480
4508
  const key = `${strategyName}:${exchangeName}:${frameName}`;
4481
4509
  this.getSessionStorage.clear(key);
4482
4510
  };
@@ -4488,7 +4516,7 @@ class PersistSessionUtils {
4488
4516
  * @param Ctor - Custom IPersistSessionInstance constructor
4489
4517
  */
4490
4518
  usePersistSessionAdapter(Ctor) {
4491
- LOGGER_SERVICE$7.info(PERSIST_SESSION_UTILS_METHOD_NAME_USE_PERSIST_SESSION_ADAPTER);
4519
+ LOGGER_SERVICE$8.info(PERSIST_SESSION_UTILS_METHOD_NAME_USE_PERSIST_SESSION_ADAPTER);
4492
4520
  this.PersistSessionInstanceCtor = Ctor;
4493
4521
  this.getSessionStorage.clear();
4494
4522
  }
@@ -4500,28 +4528,39 @@ class PersistSessionUtils {
4500
4528
  const PersistSessionAdapter = new PersistSessionUtils();
4501
4529
 
4502
4530
  var _a$2, _b$2;
4503
- const BUSY_DELAY = 100;
4504
4531
  const SET_BUSY_SYMBOL = Symbol("setBusy");
4505
4532
  const GET_BUSY_SYMBOL = Symbol("getBusy");
4506
4533
  const ACQUIRE_LOCK_SYMBOL = Symbol("acquireLock");
4507
4534
  const RELEASE_LOCK_SYMBOL = Symbol("releaseLock");
4535
+ /**
4536
+ * Body of the queued acquire operation.
4537
+ *
4538
+ * Parks the caller on `self._tick` whenever the lock is already busy: each
4539
+ * `releaseLock` emits on `_tick`, waking exactly the next queued acquirer
4540
+ * instead of polling on a fixed delay. The busy counter is bumped only after
4541
+ * the loop exits, so re-entry checks remain coherent under contention.
4542
+ *
4543
+ * @param self - The owning {@link Lock} instance.
4544
+ */
4508
4545
  const ACQUIRE_LOCK_FN = async (self) => {
4509
4546
  while (self[GET_BUSY_SYMBOL]()) {
4510
- await sleep(BUSY_DELAY);
4547
+ // @ts-ignore
4548
+ await self._tick.toPromise();
4511
4549
  }
4512
4550
  self[SET_BUSY_SYMBOL](true);
4513
4551
  };
4514
4552
  /**
4515
4553
  * Mutual exclusion primitive for async TypeScript code.
4516
4554
  *
4517
- * Provides a reentrant-safe, queued lock that serializes access to a critical
4518
- * section across concurrent async callers. Internally tracks a busy counter so
4519
- * nested acquire/release pairs are detected and mis-matched releases throw
4520
- * immediately.
4555
+ * Provides a queued lock that serializes access to a critical section across
4556
+ * concurrent async callers. Wake-ups are event-driven (via an internal
4557
+ * `_tick` subject emitted on every `releaseLock`) rather than polling,
4558
+ * so contention does not incur a fixed delay.
4521
4559
  *
4522
- * Three usage styles are supported:
4560
+ * The busy counter detects mis-matched releases and throws immediately on
4561
+ * extra `releaseLock` calls.
4523
4562
  *
4524
- * **Manual acquire / release**
4563
+ * **Usage**
4525
4564
  * ```ts
4526
4565
  * await lock.acquireLock();
4527
4566
  * try {
@@ -4536,7 +4575,18 @@ const ACQUIRE_LOCK_FN = async (self) => {
4536
4575
  */
4537
4576
  class Lock {
4538
4577
  constructor() {
4578
+ /**
4579
+ * Outstanding acquires that have not yet been released.
4580
+ * Incremented in `[SET_BUSY_SYMBOL](true)`, decremented in `[SET_BUSY_SYMBOL](false)`.
4581
+ * A negative value indicates an extra `releaseLock` and throws on detection.
4582
+ */
4539
4583
  this._isBusy = 0;
4584
+ /**
4585
+ * Wake-up channel for {@link ACQUIRE_LOCK_FN}.
4586
+ * Every {@link releaseLock} emits a single tick that unblocks the next
4587
+ * queued acquirer parked on `toPromise()`.
4588
+ */
4589
+ this._tick = new Subject();
4540
4590
  this[_a$2] = queued(ACQUIRE_LOCK_FN);
4541
4591
  this[_b$2] = () => this[SET_BUSY_SYMBOL](false);
4542
4592
  /**
@@ -4557,16 +4607,19 @@ class Lock {
4557
4607
  await this[ACQUIRE_LOCK_SYMBOL](this);
4558
4608
  };
4559
4609
  /**
4560
- * Releases the lock previously acquired with {@link acquireLock}.
4610
+ * Releases the lock previously acquired with {@link acquireLock} and emits
4611
+ * on the internal `_tick` subject to wake the next queued acquirer.
4612
+ *
4561
4613
  * Must be called exactly once per successful {@link acquireLock} call,
4562
- * typically inside a `finally` block. Throws if called more times
4563
- * than the lock was acquired.
4614
+ * typically inside a `finally` block. Throws if called more times than
4615
+ * the lock was acquired.
4564
4616
  *
4565
4617
  * @returns {Promise<void>} Resolves once the lock has been released.
4566
4618
  * @throws {Error} If the lock is released more times than it was acquired.
4567
4619
  */
4568
4620
  this.releaseLock = async () => {
4569
4621
  await this[RELEASE_LOCK_SYMBOL]();
4622
+ await this._tick.next();
4570
4623
  };
4571
4624
  }
4572
4625
  [SET_BUSY_SYMBOL](isBusy) {
@@ -4581,18 +4634,166 @@ class Lock {
4581
4634
  }
4582
4635
  _a$2 = ACQUIRE_LOCK_SYMBOL, _b$2 = RELEASE_LOCK_SYMBOL;
4583
4636
 
4637
+ const METHOD_NAME_ADD_ACTIVITY = "LookupUtils.addActivity";
4638
+ const METHOD_NAME_REMOVE_ACTIVITY = "LookupUtils.removeActivity";
4639
+ const METHOD_NAME_LIST_ACTIVITY = "LookupUtils.listActivity";
4640
+ /** Logger service injected as DI singleton */
4641
+ const LOGGER_SERVICE$7 = new LoggerService();
4642
+ /**
4643
+ * Builds the composite {@link Key} used to register an activity in `_lookupMap`.
4644
+ *
4645
+ * Mirrors the {@link Key} type construction: appends `frameName` only when provided
4646
+ * (typical for backtest), then a `"backtest"` / `"live"` discriminator suffix.
4647
+ *
4648
+ * @param symbol - Trading pair symbol.
4649
+ * @param strategyName - Strategy schema name.
4650
+ * @param exchangeName - Exchange schema name.
4651
+ * @param frameName - Frame schema name; omitted from the key when falsy.
4652
+ * @param backtest - `true` for backtest, `false` for live.
4653
+ * @returns Colon-joined composite key.
4654
+ */
4655
+ const CREATE_KEY_FN$y = (symbol, strategyName, exchangeName, frameName, backtest) => {
4656
+ const parts = [symbol, strategyName, exchangeName];
4657
+ if (frameName)
4658
+ parts.push(frameName);
4659
+ parts.push(backtest ? "backtest" : "live");
4660
+ return parts.join(":");
4661
+ };
4662
+ /**
4663
+ * In-memory registry of currently running backtest and live activities.
4664
+ *
4665
+ * Purpose:
4666
+ * - Each `Backtest.run` / `Live.run` / per-strategy walker iteration registers an
4667
+ * {@link IActivityEntry} on start and removes it on completion.
4668
+ * - `Candle.spinLock` consults {@link isParallel} to decide whether the event-loop
4669
+ * hand-off (post-candle-fetch spin) is worth performing. With a single active
4670
+ * workload there is no peer to yield to, so the spin is skipped entirely.
4671
+ *
4672
+ * Exposed as the `Lookup` singleton; no constructor parameters.
4673
+ *
4674
+ * @example
4675
+ * ```typescript
4676
+ * Lookup.addActivity({ symbol: "BTCUSDT", context, backtest: true });
4677
+ * try {
4678
+ * for await (const _ of run(symbol, context)) { ... }
4679
+ * } finally {
4680
+ * Lookup.removeActivity({ symbol: "BTCUSDT", context, backtest: true });
4681
+ * }
4682
+ * ```
4683
+ */
4684
+ class LookupUtils {
4685
+ constructor() {
4686
+ /** Active entries keyed by their composite {@link Key}. */
4687
+ this._lookupMap = new Map();
4688
+ /**
4689
+ * Registers a backtest or live activity in the lookup map.
4690
+ * Idempotent for identical keys — duplicate calls overwrite the existing entry.
4691
+ *
4692
+ * @param activity - Activity descriptor identifying the running workload.
4693
+ */
4694
+ this.addActivity = (activity) => {
4695
+ LOGGER_SERVICE$7.info(METHOD_NAME_ADD_ACTIVITY, {
4696
+ activity,
4697
+ });
4698
+ const key = CREATE_KEY_FN$y(activity.symbol, activity.context.strategyName, activity.context.exchangeName, activity.context.frameName, activity.backtest);
4699
+ this._lookupMap.set(key, activity);
4700
+ };
4701
+ /**
4702
+ * Removes a previously registered activity from the lookup map.
4703
+ * Must be paired with a prior {@link addActivity}, typically in a `finally` block,
4704
+ * so a thrown error in the underlying run does not leave a stale entry behind.
4705
+ *
4706
+ * @param activity - Activity descriptor matching the one passed to {@link addActivity}.
4707
+ */
4708
+ this.removeActivity = (activity) => {
4709
+ LOGGER_SERVICE$7.info(METHOD_NAME_REMOVE_ACTIVITY, {
4710
+ activity,
4711
+ });
4712
+ const key = CREATE_KEY_FN$y(activity.symbol, activity.context.strategyName, activity.context.exchangeName, activity.context.frameName, activity.backtest);
4713
+ this._lookupMap.delete(key);
4714
+ };
4715
+ /**
4716
+ * Returns a snapshot of currently active entries.
4717
+ *
4718
+ * @returns Array of all activities present in the lookup map at call time.
4719
+ */
4720
+ this.listActivity = () => {
4721
+ LOGGER_SERVICE$7.info(METHOD_NAME_LIST_ACTIVITY);
4722
+ return Array.from(this._lookupMap.values());
4723
+ };
4724
+ }
4725
+ /**
4726
+ * `true` when more than one activity is currently registered.
4727
+ * Used by `Candle.spinLock` to decide whether yielding the event loop is useful.
4728
+ */
4729
+ get isParallel() {
4730
+ return this._lookupMap.size > 1;
4731
+ }
4732
+ }
4733
+ /**
4734
+ * Process-wide singleton instance of {@link LookupUtils}.
4735
+ * Imported by `Backtest`, `Live`, `WalkerLogicPrivateService` (registration sites)
4736
+ * and by `Candle` (read-only consumer via `isParallel`).
4737
+ */
4738
+ const Lookup = new LookupUtils();
4739
+
4584
4740
  const METHOD_NAME_ACQUIRE_LOCK = "CandleUtils.acquireLock";
4585
4741
  const METHOD_NAME_RELEASE_LOCK = "CandleUtils.releaseLock";
4742
+ const METHOD_NAME_SPIN_LOCK = "CandleUtils.spinLock";
4743
+ /**
4744
+ * Upper bound (ms) on how long `spinLock` may park before falling through.
4745
+ * If no peer backtest acquires the candle-fetch mutex within this window,
4746
+ * the spinner proceeds without further yielding.
4747
+ */
4748
+ const ROTATE_DELAY = 50;
4586
4749
  /** Logger service injected as DI singleton */
4587
4750
  const LOGGER_SERVICE$6 = new LoggerService();
4751
+ /**
4752
+ * Process-wide coordinator for candle-fetch serialization and cooperative
4753
+ * yielding between parallel backtests.
4754
+ *
4755
+ * Two complementary primitives are exposed:
4756
+ * - **Mutex** via {@link acquireLock} / {@link releaseLock}: prevents concurrent
4757
+ * candle fetches from racing on the same exchange.
4758
+ * - **Spin hand-off** via {@link spinLock}: invoked after a fetch completes to
4759
+ * give peer backtests waiting on the mutex a chance to run, so multiple
4760
+ * `Backtest.run` workloads interleave instead of one monopolizing the loop.
4761
+ *
4762
+ * All three operations are no-ops when `CC_ENABLE_CANDLE_FETCH_MUTEX` is `false`.
4763
+ * The spin additionally requires `CC_ENABLE_BACKTEST_PARALLEL_SPIN` and at least
4764
+ * two registered activities in `Lookup` (see `Lookup.isParallel`).
4765
+ *
4766
+ * @example
4767
+ * ```typescript
4768
+ * await Candle.acquireLock("ClientExchange GET_CANDLES_FN");
4769
+ * try {
4770
+ * const candles = await fetchFromExchange(...);
4771
+ * return candles;
4772
+ * } finally {
4773
+ * await Candle.releaseLock("ClientExchange GET_CANDLES_FN");
4774
+ * }
4775
+ * // Elsewhere, after a fetch completes inside a backtest loop:
4776
+ * await Candle.spinLock("BacktestLogicPrivateService GET_CANDLES_FN");
4777
+ * ```
4778
+ */
4588
4779
  class CandleUtils {
4589
4780
  constructor() {
4781
+ /** Underlying mutex serializing candle fetches across concurrent callers. */
4590
4782
  this._lock = new Lock();
4591
4783
  /**
4592
- * Acquires the candle fetch mutex if CC_ENABLE_CANDLE_FETCH_MUTEX is enabled.
4784
+ * Emits whenever {@link acquireLock} successfully takes the mutex.
4785
+ * Awaited by {@link spinLock} to detect that a peer backtest has just
4786
+ * started its own fetch — the signal that yielding now will be productive.
4787
+ */
4788
+ this._spin = new Subject();
4789
+ /**
4790
+ * Acquires the candle fetch mutex if `CC_ENABLE_CANDLE_FETCH_MUTEX` is enabled.
4593
4791
  * Prevents concurrent candle fetches from the same exchange.
4594
4792
  *
4595
- * @param source - Caller identifier for logging
4793
+ * On successful acquisition, emits on the internal spin subject so any
4794
+ * peer parked inside {@link spinLock} can wake up and proceed.
4795
+ *
4796
+ * @param source - Caller identifier for logging.
4596
4797
  */
4597
4798
  this.acquireLock = async (source) => {
4598
4799
  LOGGER_SERVICE$6.info(METHOD_NAME_ACQUIRE_LOCK, {
@@ -4601,13 +4802,14 @@ class CandleUtils {
4601
4802
  if (!GLOBAL_CONFIG.CC_ENABLE_CANDLE_FETCH_MUTEX) {
4602
4803
  return;
4603
4804
  }
4604
- return await this._lock.acquireLock();
4805
+ await this._lock.acquireLock();
4806
+ await this._spin.next();
4605
4807
  };
4606
4808
  /**
4607
- * Releases the candle fetch mutex if CC_ENABLE_CANDLE_FETCH_MUTEX is enabled.
4608
- * Must be called after acquireLock, typically in a finally block.
4809
+ * Releases the candle fetch mutex if `CC_ENABLE_CANDLE_FETCH_MUTEX` is enabled.
4810
+ * Must be called after {@link acquireLock}, typically in a `finally` block.
4609
4811
  *
4610
- * @param source - Caller identifier for logging
4812
+ * @param source - Caller identifier for logging.
4611
4813
  */
4612
4814
  this.releaseLock = async (source) => {
4613
4815
  LOGGER_SERVICE$6.info(METHOD_NAME_RELEASE_LOCK, {
@@ -4618,8 +4820,47 @@ class CandleUtils {
4618
4820
  }
4619
4821
  return await this._lock.releaseLock();
4620
4822
  };
4823
+ /**
4824
+ * Cooperative event-loop hand-off invoked by `BacktestLogicPrivateService`
4825
+ * after a successful `getNextCandles`. Allows peer backtests waiting on the
4826
+ * candle-fetch mutex to run before the current backtest fetches the next chunk.
4827
+ *
4828
+ * Waits for one of:
4829
+ * - a peer calling {@link acquireLock} (signalled via the spin subject), or
4830
+ * - a `ROTATE_DELAY` ms timeout, so the caller never parks indefinitely.
4831
+ *
4832
+ * Returns immediately as a no-op when any of these is true:
4833
+ * - `CC_ENABLE_CANDLE_FETCH_MUTEX` is disabled (mutex is off entirely),
4834
+ * - `CC_ENABLE_BACKTEST_PARALLEL_SPIN` is disabled (cooperative yielding off),
4835
+ * - `Lookup.isParallel` is `false` (only one active workload — no peer to yield to).
4836
+ *
4837
+ * @param source - Caller identifier for logging.
4838
+ */
4839
+ this.spinLock = async (source) => {
4840
+ LOGGER_SERVICE$6.info(METHOD_NAME_SPIN_LOCK, {
4841
+ source,
4842
+ });
4843
+ if (!GLOBAL_CONFIG.CC_ENABLE_CANDLE_FETCH_MUTEX) {
4844
+ return;
4845
+ }
4846
+ if (!GLOBAL_CONFIG.CC_ENABLE_BACKTEST_PARALLEL_SPIN) {
4847
+ return;
4848
+ }
4849
+ if (!Lookup.isParallel) {
4850
+ return;
4851
+ }
4852
+ await Promise.race([
4853
+ this._spin.toPromise(),
4854
+ sleep(ROTATE_DELAY),
4855
+ ]);
4856
+ };
4621
4857
  }
4622
4858
  }
4859
+ /**
4860
+ * Process-wide singleton instance of {@link CandleUtils}.
4861
+ * Imported by `ClientExchange` (mutex around exchange fetches) and by
4862
+ * `BacktestLogicPrivateService` (spin hand-off between parallel backtests).
4863
+ */
4623
4864
  const Candle = new CandleUtils();
4624
4865
 
4625
4866
  const MS_PER_MINUTE$7 = 60000;
@@ -19153,9 +19394,28 @@ const TICK_FN = async (self, symbol, when) => {
19153
19394
  return { type: "error", __error__: SYMBOL_FN_ERROR, reason: "TICK_FN", message: getErrorMessage(error) };
19154
19395
  }
19155
19396
  };
19397
+ /**
19398
+ * Wraps `exchangeCoreService.getNextCandles` with error capture and a cooperative
19399
+ * event-loop hand-off after a successful fetch.
19400
+ *
19401
+ * Calls `Candle.spinLock(...)` on success: when multiple backtests run in parallel
19402
+ * (`Lookup.isParallel === true`), this yields the event loop so a peer waiting on
19403
+ * the candle-fetch mutex can take its turn, producing round-robin interleaving
19404
+ * instead of one backtest monopolizing the loop until completion. The spin is a
19405
+ * no-op for single-workload runs.
19406
+ *
19407
+ * @param self - Owning service instance, used for logging and exchange access.
19408
+ * @param symbol - Trading pair symbol.
19409
+ * @param candlesNeeded - Number of 1m candles to request.
19410
+ * @param bufferStartTime - Inclusive start time for the fetch window.
19411
+ * @param logMeta - Extra structured fields appended to the warn log on failure.
19412
+ * @returns Fetched candles, or a {@link TFnError} discriminated union on failure.
19413
+ */
19156
19414
  const GET_CANDLES_FN = async (self, symbol, candlesNeeded, bufferStartTime, logMeta) => {
19157
19415
  try {
19158
- return await self.exchangeCoreService.getNextCandles(symbol, "1m", candlesNeeded, bufferStartTime, true);
19416
+ const result = await self.exchangeCoreService.getNextCandles(symbol, "1m", candlesNeeded, bufferStartTime, true);
19417
+ await Candle.spinLock("BacktestLogicPrivateService GET_CANDLES_FN");
19418
+ return result;
19159
19419
  }
19160
19420
  catch (error) {
19161
19421
  console.error(`backtestLogicPrivateService getNextCandles failed symbol=${symbol} strategyName=${self.methodContextService.context.strategyName} exchangeName=${self.methodContextService.context.exchangeName}`);
@@ -20041,6 +20301,15 @@ class WalkerLogicPrivateService {
20041
20301
  exchangeName: context.exchangeName,
20042
20302
  frameName: context.frameName,
20043
20303
  });
20304
+ Lookup.addActivity({
20305
+ symbol,
20306
+ context: {
20307
+ strategyName,
20308
+ exchangeName: context.exchangeName,
20309
+ frameName: context.frameName,
20310
+ },
20311
+ backtest: true,
20312
+ });
20044
20313
  try {
20045
20314
  await resolveDocuments(iterator);
20046
20315
  }
@@ -20056,6 +20325,17 @@ class WalkerLogicPrivateService {
20056
20325
  await CALL_STRATEGY_ERROR_CALLBACKS_FN(this, walkerSchema, strategyName, symbol, error);
20057
20326
  continue;
20058
20327
  }
20328
+ finally {
20329
+ Lookup.removeActivity({
20330
+ symbol,
20331
+ context: {
20332
+ strategyName,
20333
+ exchangeName: context.exchangeName,
20334
+ frameName: context.frameName,
20335
+ },
20336
+ backtest: true,
20337
+ });
20338
+ }
20059
20339
  this.loggerService.info("walkerLogicPrivateService backtest complete", {
20060
20340
  strategyName,
20061
20341
  symbol,
@@ -20132,6 +20412,110 @@ class WalkerLogicPrivateService {
20132
20412
  }
20133
20413
  }
20134
20414
 
20415
+ /**
20416
+ * Run iterator function for backtest logic.
20417
+ *
20418
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
20419
+ * @param context - Execution context with strategy, exchange, and frame names
20420
+ * @param self - Instance of BacktestLogicPublicService
20421
+ * @returns Async iterator for backtest results
20422
+ */
20423
+ const RUN_ITERATOR_FN$1 = (self, symbol, context) => {
20424
+ return MethodContextService.runAsyncIterator(self.backtestLogicPrivateService.run(symbol), {
20425
+ exchangeName: context.exchangeName,
20426
+ strategyName: context.strategyName,
20427
+ frameName: context.frameName,
20428
+ });
20429
+ };
20430
+ /**
20431
+ * Call before start execution for backtest logic.
20432
+ * This function is responsible for triggering the beforeStartSubject
20433
+ * with the appropriate context and symbol information.
20434
+ */
20435
+ const CALL_BEFORE_START_FN$1 = trycatch(async (self, symbol, context) => {
20436
+ const { startDate } = self.frameSchemaService.get(context.frameName);
20437
+ const when = alignToInterval(startDate, "1m");
20438
+ await MethodContextService.runInContext(async () => {
20439
+ await ExecutionContextService.runInContext(async () => {
20440
+ const currentPrice = await self.exchangeConnectionService.getAveragePrice(symbol);
20441
+ await beforeStartSubject.next({
20442
+ symbol,
20443
+ exchangeName: context.exchangeName,
20444
+ strategyName: context.strategyName,
20445
+ frameName: context.frameName,
20446
+ backtest: true,
20447
+ when,
20448
+ timestamp: when.getTime(),
20449
+ currentPrice,
20450
+ });
20451
+ }, {
20452
+ symbol,
20453
+ when,
20454
+ backtest: true,
20455
+ });
20456
+ }, {
20457
+ exchangeName: context.exchangeName,
20458
+ strategyName: context.strategyName,
20459
+ frameName: context.frameName,
20460
+ });
20461
+ }, {
20462
+ fallback: (error, self) => {
20463
+ const message = "BacktestLogicPublicService CALL_BEFORE_START_FN thrown";
20464
+ const payload = {
20465
+ error: errorData(error),
20466
+ message: getErrorMessage(error),
20467
+ };
20468
+ self.loggerService.warn(message, payload);
20469
+ console.error(message, payload);
20470
+ errorEmitter.next(error);
20471
+ },
20472
+ });
20473
+ /**
20474
+ * Call after end execution for backtest logic.
20475
+ * This function is responsible for triggering the afterEndSubject
20476
+ * with the appropriate context and symbol information.
20477
+ */
20478
+ const CALL_AFTER_END_FN$1 = trycatch(async (self, symbol, context) => {
20479
+ const { startDate } = self.frameSchemaService.get(context.frameName);
20480
+ const timestamp = self.timeMetaService.hasTimestamp(symbol, context, true)
20481
+ ? await self.timeMetaService.getTimestamp(symbol, context, true)
20482
+ : startDate.getTime();
20483
+ const when = new Date(timestamp);
20484
+ await MethodContextService.runInContext(async () => {
20485
+ await ExecutionContextService.runInContext(async () => {
20486
+ const currentPrice = await self.exchangeConnectionService.getAveragePrice(symbol);
20487
+ await afterEndSubject.next({
20488
+ symbol,
20489
+ exchangeName: context.exchangeName,
20490
+ strategyName: context.strategyName,
20491
+ frameName: context.frameName,
20492
+ backtest: true,
20493
+ when,
20494
+ timestamp,
20495
+ currentPrice,
20496
+ });
20497
+ }, {
20498
+ symbol,
20499
+ when,
20500
+ backtest: true,
20501
+ });
20502
+ }, {
20503
+ exchangeName: context.exchangeName,
20504
+ strategyName: context.strategyName,
20505
+ frameName: context.frameName,
20506
+ });
20507
+ }, {
20508
+ fallback: (error, self) => {
20509
+ const message = "BacktestLogicPublicService CALL_AFTER_END_FN thrown";
20510
+ const payload = {
20511
+ error: errorData(error),
20512
+ message: getErrorMessage(error),
20513
+ };
20514
+ self.loggerService.warn(message, payload);
20515
+ console.error(message, payload);
20516
+ errorEmitter.next(error);
20517
+ },
20518
+ });
20135
20519
  /**
20136
20520
  * Public service for backtest orchestration with context management.
20137
20521
  *
@@ -20160,30 +20544,134 @@ class BacktestLogicPublicService {
20160
20544
  constructor() {
20161
20545
  this.loggerService = inject(TYPES.loggerService);
20162
20546
  this.backtestLogicPrivateService = inject(TYPES.backtestLogicPrivateService);
20163
- /**
20164
- * Runs backtest for a symbol with context propagation.
20165
- *
20166
- * Streams closed signals as async generator. Context is automatically
20167
- * injected into all framework functions called during iteration.
20168
- *
20169
- * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
20170
- * @param context - Execution context with strategy, exchange, and frame names
20171
- * @returns Async generator yielding closed signals with PNL
20172
- */
20173
- this.run = (symbol, context) => {
20174
- this.loggerService.log("backtestLogicPublicService run", {
20547
+ this.timeMetaService = inject(TYPES.timeMetaService);
20548
+ this.frameSchemaService = inject(TYPES.frameSchemaService);
20549
+ this.exchangeConnectionService = inject(TYPES.exchangeConnectionService);
20550
+ }
20551
+ /**
20552
+ * Runs backtest for a symbol with context propagation.
20553
+ *
20554
+ * Streams closed signals as async generator. Context is automatically
20555
+ * injected into all framework functions called during iteration.
20556
+ *
20557
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
20558
+ * @param context - Execution context with strategy, exchange, and frame names
20559
+ * @returns Async generator yielding closed signals with PNL
20560
+ */
20561
+ async *run(symbol, context) {
20562
+ this.loggerService.log("backtestLogicPublicService run", {
20563
+ symbol,
20564
+ context,
20565
+ });
20566
+ await CALL_BEFORE_START_FN$1(this, symbol, context);
20567
+ try {
20568
+ yield* RUN_ITERATOR_FN$1(this, symbol, context);
20569
+ }
20570
+ finally {
20571
+ await CALL_AFTER_END_FN$1(this, symbol, context);
20572
+ }
20573
+ }
20574
+ }
20575
+
20576
+ /**
20577
+ * Run iterator function for live logic.
20578
+ *
20579
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
20580
+ * @param context - Execution context with strategy and exchange names
20581
+ * @param self - Instance of LiveLogicPublicService
20582
+ * @returns Async iterator for live trading results
20583
+ */
20584
+ const RUN_ITERATOR_FN = (self, symbol, context) => {
20585
+ return MethodContextService.runAsyncIterator(self.liveLogicPrivateService.run(symbol), {
20586
+ exchangeName: context.exchangeName,
20587
+ strategyName: context.strategyName,
20588
+ frameName: "",
20589
+ });
20590
+ };
20591
+ /**
20592
+ * Call before start execution for live logic.
20593
+ * This function is responsible for triggering the beforeStartSubject
20594
+ * with the appropriate context and symbol information.
20595
+ */
20596
+ const CALL_BEFORE_START_FN = trycatch(async (self, symbol, context) => {
20597
+ const when = alignToInterval(new Date(), "1m");
20598
+ await MethodContextService.runInContext(async () => {
20599
+ await ExecutionContextService.runInContext(async () => {
20600
+ const currentPrice = await self.exchangeConnectionService.getAveragePrice(symbol);
20601
+ await beforeStartSubject.next({
20175
20602
  symbol,
20176
- context,
20603
+ exchangeName: context.exchangeName,
20604
+ strategyName: context.strategyName,
20605
+ frameName: "",
20606
+ backtest: false,
20607
+ currentPrice,
20608
+ when,
20609
+ timestamp: when.getTime(),
20177
20610
  });
20178
- return MethodContextService.runAsyncIterator(this.backtestLogicPrivateService.run(symbol), {
20611
+ }, {
20612
+ symbol,
20613
+ when,
20614
+ backtest: false,
20615
+ });
20616
+ }, {
20617
+ exchangeName: context.exchangeName,
20618
+ strategyName: context.strategyName,
20619
+ frameName: "",
20620
+ });
20621
+ }, {
20622
+ fallback: (error, self) => {
20623
+ const message = "LiveLogicPublicService CALL_BEFORE_START_FN thrown";
20624
+ const payload = {
20625
+ error: errorData(error),
20626
+ message: getErrorMessage(error),
20627
+ };
20628
+ self.loggerService.warn(message, payload);
20629
+ console.error(message, payload);
20630
+ errorEmitter.next(error);
20631
+ },
20632
+ });
20633
+ /**
20634
+ * Call after end execution for live logic.
20635
+ * This function is responsible for triggering the afterEndSubject
20636
+ * with the appropriate context and symbol information.
20637
+ */
20638
+ const CALL_AFTER_END_FN = trycatch(async (self, symbol, context) => {
20639
+ const when = alignToInterval(new Date(), "1m");
20640
+ await MethodContextService.runInContext(async () => {
20641
+ await ExecutionContextService.runInContext(async () => {
20642
+ const currentPrice = await self.exchangeConnectionService.getAveragePrice(symbol);
20643
+ await afterEndSubject.next({
20644
+ symbol,
20179
20645
  exchangeName: context.exchangeName,
20180
20646
  strategyName: context.strategyName,
20181
- frameName: context.frameName,
20647
+ frameName: "",
20648
+ backtest: false,
20649
+ currentPrice,
20650
+ when,
20651
+ timestamp: when.getTime(),
20182
20652
  });
20653
+ }, {
20654
+ symbol,
20655
+ when,
20656
+ backtest: false,
20657
+ });
20658
+ }, {
20659
+ exchangeName: context.exchangeName,
20660
+ strategyName: context.strategyName,
20661
+ frameName: "",
20662
+ });
20663
+ }, {
20664
+ fallback: (error, self) => {
20665
+ const message = "LiveLogicPublicService CALL_AFTER_END_FN thrown";
20666
+ const payload = {
20667
+ error: errorData(error),
20668
+ message: getErrorMessage(error),
20183
20669
  };
20184
- }
20185
- }
20186
-
20670
+ self.loggerService.warn(message, payload);
20671
+ console.error(message, payload);
20672
+ errorEmitter.next(error);
20673
+ },
20674
+ });
20187
20675
  /**
20188
20676
  * Public service for live trading orchestration with context management.
20189
20677
  *
@@ -20219,28 +20707,31 @@ class LiveLogicPublicService {
20219
20707
  constructor() {
20220
20708
  this.loggerService = inject(TYPES.loggerService);
20221
20709
  this.liveLogicPrivateService = inject(TYPES.liveLogicPrivateService);
20222
- /**
20223
- * Runs live trading for a symbol with context propagation.
20224
- *
20225
- * Streams opened and closed signals as infinite async generator.
20226
- * Context is automatically injected into all framework functions.
20227
- * Process can crash and restart - state will be recovered from disk.
20228
- *
20229
- * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
20230
- * @param context - Execution context with strategy and exchange names
20231
- * @returns Infinite async generator yielding opened and closed signals
20232
- */
20233
- this.run = (symbol, context) => {
20234
- this.loggerService.log("liveLogicPublicService run", {
20235
- symbol,
20236
- context,
20237
- });
20238
- return MethodContextService.runAsyncIterator(this.liveLogicPrivateService.run(symbol), {
20239
- exchangeName: context.exchangeName,
20240
- strategyName: context.strategyName,
20241
- frameName: "",
20242
- });
20243
- };
20710
+ this.exchangeConnectionService = inject(TYPES.exchangeConnectionService);
20711
+ }
20712
+ /**
20713
+ * Runs live trading for a symbol with context propagation.
20714
+ *
20715
+ * Streams opened and closed signals as infinite async generator.
20716
+ * Context is automatically injected into all framework functions.
20717
+ * Process can crash and restart - state will be recovered from disk.
20718
+ *
20719
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
20720
+ * @param context - Execution context with strategy and exchange names
20721
+ * @returns Infinite async generator yielding opened and closed signals
20722
+ */
20723
+ async *run(symbol, context) {
20724
+ this.loggerService.log("liveLogicPublicService run", {
20725
+ symbol,
20726
+ context,
20727
+ });
20728
+ await CALL_BEFORE_START_FN(this, symbol, context);
20729
+ try {
20730
+ yield* RUN_ITERATOR_FN(this, symbol, context);
20731
+ }
20732
+ finally {
20733
+ await CALL_AFTER_END_FN(this, symbol, context);
20734
+ }
20244
20735
  }
20245
20736
  }
20246
20737
 
@@ -34444,6 +34935,21 @@ class TimeMetaService {
34444
34935
  * Instances are cached until clear() is called.
34445
34936
  */
34446
34937
  this.getSource = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$a(symbol, strategyName, exchangeName, frameName, backtest), () => new BehaviorSubject());
34938
+ /**
34939
+ * Checks if a timestamp exists for the given symbol and context.
34940
+ *
34941
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
34942
+ * @param context - Strategy, exchange, and frame identifiers
34943
+ * @param backtest - True if backtest mode, false if live mode
34944
+ * @returns True if a timestamp is available, false otherwise
34945
+ */
34946
+ this.hasTimestamp = (symbol, context, backtest) => {
34947
+ const key = CREATE_KEY_FN$a(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
34948
+ if (!this.getSource.has(key)) {
34949
+ return false;
34950
+ }
34951
+ return !!this.getSource.get(key)?.data;
34952
+ };
34447
34953
  /**
34448
34954
  * Returns the current candle timestamp (in milliseconds) for the given symbol and context.
34449
34955
  *
@@ -36075,6 +36581,7 @@ const Exchange = new ExchangeUtils();
36075
36581
 
36076
36582
  const WARM_CANDLES_METHOD_NAME = "cache.warmCandles";
36077
36583
  const CHECK_CANDLES_METHOD_NAME = "cache.checkCandles";
36584
+ const CACHE_CANDLES_METHOD_NAME = "cache.cacheCandles";
36078
36585
  const MS_PER_MINUTE$3 = 60000;
36079
36586
  const INTERVAL_MINUTES$3 = {
36080
36587
  "1m": 1,
@@ -36106,6 +36613,34 @@ const PRINT_PROGRESS_FN = (fetched, total, symbol, interval) => {
36106
36613
  process.stdout.write("\n");
36107
36614
  }
36108
36615
  };
36616
+ /**
36617
+ * Retry-wrapped pipeline: validates the cache via `checkCandles` and, on miss,
36618
+ * fills it via `warmCandles` and rethrows to trigger a retry pass that
36619
+ * re-validates the freshly cached range. Limited to 2 attempts.
36620
+ */
36621
+ const CACHE_CANDLES_FN = retry(async (interval, dto, onWarmStart, onCheckStart) => {
36622
+ try {
36623
+ onCheckStart && onCheckStart(dto.symbol, interval, dto.from, dto.to);
36624
+ await checkCandles({
36625
+ exchangeName: dto.exchangeName,
36626
+ from: dto.from,
36627
+ to: dto.to,
36628
+ symbol: dto.symbol,
36629
+ interval: interval,
36630
+ });
36631
+ }
36632
+ catch (error) {
36633
+ onWarmStart && onWarmStart(dto.symbol, interval, dto.from, dto.to);
36634
+ await warmCandles({
36635
+ symbol: dto.symbol,
36636
+ exchangeName: dto.exchangeName,
36637
+ from: dto.from,
36638
+ to: dto.to,
36639
+ interval: interval,
36640
+ });
36641
+ throw error;
36642
+ }
36643
+ }, 2);
36109
36644
  /**
36110
36645
  * Checks cached candle presence via the persist adapter.
36111
36646
  * Issues one ranged read; adapter-side `hasValue` covers each expected timestamp,
@@ -36181,6 +36716,34 @@ async function warmCandles(params) {
36181
36716
  PRINT_PROGRESS_FN(fetched, totalCandles, symbol, interval);
36182
36717
  }
36183
36718
  }
36719
+ /**
36720
+ * Ensures candles for the given range are present in persist storage.
36721
+ * Runs a check-then-warm pipeline with one retry: validates the cache first
36722
+ * and, on a miss, downloads the missing data and re-validates.
36723
+ *
36724
+ * @param params - Combined cache parameters with optional lifecycle callbacks
36725
+ */
36726
+ async function cacheCandles({ symbol, interval, from, to, exchangeName, onCheckStart = (symbol, interval, from, to) => {
36727
+ process.stdout.write("\n");
36728
+ process.stdout.write(`Checking candles cache for ${symbol} ${interval} from ${from} to ${to}\n`);
36729
+ }, onWarmStart = (symbol, interval, from, to) => {
36730
+ process.stdout.write("\n\n");
36731
+ process.stdout.write(`Caching candles for ${symbol} ${interval} from ${from} to ${to}\n`);
36732
+ }, }) {
36733
+ backtest.loggerService.info(CACHE_CANDLES_METHOD_NAME, {
36734
+ symbol,
36735
+ exchangeName,
36736
+ interval,
36737
+ from,
36738
+ to,
36739
+ });
36740
+ await CACHE_CANDLES_FN(interval, {
36741
+ exchangeName,
36742
+ from,
36743
+ to,
36744
+ symbol,
36745
+ }, onWarmStart, onCheckStart);
36746
+ }
36184
36747
 
36185
36748
  const METHOD_NAME = "validate.validate";
36186
36749
  /**
@@ -36600,11 +37163,6 @@ const GET_AVERAGE_PRICE_METHOD_NAME = "exchange.getAveragePrice";
36600
37163
  const GET_CLOSE_PRICE_METHOD_NAME = "exchange.getClosePrice";
36601
37164
  const FORMAT_PRICE_METHOD_NAME = "exchange.formatPrice";
36602
37165
  const FORMAT_QUANTITY_METHOD_NAME = "exchange.formatQuantity";
36603
- const GET_DATE_METHOD_NAME = "exchange.getDate";
36604
- const GET_TIMESTAMP_METHOD_NAME = "exchange.getTimestamp";
36605
- const GET_MODE_METHOD_NAME = "exchange.getMode";
36606
- const GET_SYMBOL_METHOD_NAME = "exchange.getSymbol";
36607
- const GET_CONTEXT_METHOD_NAME = "exchange.getContext";
36608
37166
  const HAS_TRADE_CONTEXT_METHOD_NAME = "exchange.hasTradeContext";
36609
37167
  const GET_ORDER_BOOK_METHOD_NAME = "exchange.getOrderBook";
36610
37168
  const GET_RAW_CANDLES_METHOD_NAME = "exchange.getRawCandles";
@@ -36771,113 +37329,6 @@ async function formatQuantity(symbol, quantity) {
36771
37329
  }
36772
37330
  return await backtest.exchangeConnectionService.formatQuantity(symbol, quantity);
36773
37331
  }
36774
- /**
36775
- * Gets the current date from execution context.
36776
- *
36777
- * In backtest mode: returns the current timeframe date being processed
36778
- * In live mode: returns current real-time date
36779
- *
36780
- * @returns Promise resolving to current execution context date
36781
- *
36782
- * @example
36783
- * ```typescript
36784
- * const date = await getDate();
36785
- * console.log(date); // 2024-01-01T12:00:00.000Z
36786
- * ```
36787
- */
36788
- async function getDate() {
36789
- backtest.loggerService.info(GET_DATE_METHOD_NAME);
36790
- if (!ExecutionContextService.hasContext()) {
36791
- throw new Error("getDate requires an execution context");
36792
- }
36793
- const { when } = backtest.executionContextService.context;
36794
- return new Date(when.getTime());
36795
- }
36796
- /**
36797
- * Gets the current timestamp from execution context.
36798
- *
36799
- * In backtest mode: returns the current timeframe timestamp being processed
36800
- * In live mode: returns current real-time timestamp
36801
- *
36802
- * @returns Promise resolving to current execution context timestamp in milliseconds
36803
- * @example
36804
- * ```typescript
36805
- * const timestamp = await getTimestamp();
36806
- * console.log(timestamp); // 1700000000000
36807
- * ```
36808
- */
36809
- async function getTimestamp() {
36810
- backtest.loggerService.info(GET_TIMESTAMP_METHOD_NAME);
36811
- if (!ExecutionContextService.hasContext()) {
36812
- throw new Error("getTimestamp requires an execution context");
36813
- }
36814
- return getContextTimestamp();
36815
- }
36816
- /**
36817
- * Gets the current execution mode.
36818
- *
36819
- * @returns Promise resolving to "backtest" or "live"
36820
- *
36821
- * @example
36822
- * ```typescript
36823
- * const mode = await getMode();
36824
- * if (mode === "backtest") {
36825
- * console.log("Running in backtest mode");
36826
- * } else {
36827
- * console.log("Running in live mode");
36828
- * }
36829
- * ```
36830
- */
36831
- async function getMode() {
36832
- backtest.loggerService.info(GET_MODE_METHOD_NAME);
36833
- if (!ExecutionContextService.hasContext()) {
36834
- throw new Error("getMode requires an execution context");
36835
- }
36836
- const { backtest: bt } = backtest.executionContextService.context;
36837
- return bt ? "backtest" : "live";
36838
- }
36839
- /**
36840
- * Gets the current trading symbol from execution context.
36841
- *
36842
- * @returns Promise resolving to the current trading symbol (e.g., "BTCUSDT")
36843
- * @throws Error if execution context is not active
36844
- *
36845
- * @example
36846
- * ```typescript
36847
- * const symbol = await getSymbol();
36848
- * console.log(symbol); // "BTCUSDT"
36849
- * ```
36850
- */
36851
- async function getSymbol() {
36852
- backtest.loggerService.info(GET_SYMBOL_METHOD_NAME);
36853
- if (!ExecutionContextService.hasContext()) {
36854
- throw new Error("getSymbol requires an execution context");
36855
- }
36856
- const { symbol } = backtest.executionContextService.context;
36857
- return symbol;
36858
- }
36859
- /**
36860
- * Gets the current method context.
36861
- *
36862
- * Returns the context object from the method context service, which contains
36863
- * information about the current method execution environment.
36864
- *
36865
- * @returns Promise resolving to the current method context object
36866
- * @throws Error if method context is not active
36867
- *
36868
- * @example
36869
- * ```typescript
36870
- * const context = await getContext();
36871
- * console.log(context); // { ...method context data... }
36872
- * ```
36873
- */
36874
- async function getContext() {
36875
- backtest.loggerService.info(GET_CONTEXT_METHOD_NAME);
36876
- if (!MethodContextService.hasContext()) {
36877
- throw new Error("getContext requires a method context");
36878
- }
36879
- return backtest.methodContextService.context;
36880
- }
36881
37332
  /**
36882
37333
  * Fetches order book for a trading pair from the registered exchange.
36883
37334
  *
@@ -40418,6 +40869,10 @@ const LISTEN_MAX_DRAWDOWN_METHOD_NAME = "event.listenMaxDrawdown";
40418
40869
  const LISTEN_MAX_DRAWDOWN_ONCE_METHOD_NAME = "event.listenMaxDrawdownOnce";
40419
40870
  const LISTEN_SIGNAL_NOTIFY_METHOD_NAME = "event.listenSignalNotify";
40420
40871
  const LISTEN_SIGNAL_NOTIFY_ONCE_METHOD_NAME = "event.listenSignalNotifyOnce";
40872
+ const LISTEN_BEFORE_START_METHOD_NAME = "event.listenBeforeStart";
40873
+ const LISTEN_BEFORE_START_ONCE_METHOD_NAME = "event.listenBeforeStartOnce";
40874
+ const LISTEN_AFTER_END_METHOD_NAME = "event.listenAfterEnd";
40875
+ const LISTEN_AFTER_END_ONCE_METHOD_NAME = "event.listenAfterEndOnce";
40421
40876
  /**
40422
40877
  * Subscribes to all signal events with queued async processing.
40423
40878
  *
@@ -41888,6 +42343,68 @@ function listenSignalNotifyOnce(filterFn, fn) {
41888
42343
  };
41889
42344
  return disposeFn = listenSignalNotify(wrappedFn);
41890
42345
  }
42346
+ /**
42347
+ * Subscribes to before start events with queued async processing.
42348
+ * Emits when the engine is about to start a new strategy execution for a symbol.
42349
+ * Events are processed sequentially in order received, even if callback is async.
42350
+ * Uses queued wrapper to prevent concurrent execution of the callback.
42351
+ * @param fn - Callback function to handle before start events
42352
+ * @return Unsubscribe function to stop listening to events
42353
+ */
42354
+ function listenBeforeStart(fn) {
42355
+ backtest.loggerService.log(LISTEN_BEFORE_START_METHOD_NAME);
42356
+ return beforeStartSubject.subscribe(queued(async (event) => fn(event)));
42357
+ }
42358
+ /**
42359
+ * Subscribes to filtered before start events with one-time execution.
42360
+ * Listens for events matching the filter predicate, then executes callback once
42361
+ * and automatically unsubscribes.
42362
+ * @param filterFn - Predicate to filter which events trigger the callback
42363
+ * @param fn - Callback function to handle the filtered event (called only once)
42364
+ * @return Unsubscribe function to cancel the listener before it fires
42365
+ */
42366
+ function listenBeforeStartOnce(filterFn, fn) {
42367
+ backtest.loggerService.log(LISTEN_BEFORE_START_ONCE_METHOD_NAME);
42368
+ let disposeFn;
42369
+ const wrappedFn = async (event) => {
42370
+ if (filterFn(event)) {
42371
+ await fn(event);
42372
+ disposeFn && disposeFn();
42373
+ }
42374
+ };
42375
+ return disposeFn = listenBeforeStart(wrappedFn);
42376
+ }
42377
+ /**
42378
+ * Subscribes to after end events with queued async processing.
42379
+ * Emits when the engine has completed processing a strategy execution for a symbol.
42380
+ * Events are processed sequentially in order received, even if callback is async.
42381
+ * Uses queued wrapper to prevent concurrent execution of the callback.
42382
+ * @param fn - Callback function to handle after end events
42383
+ * @return Unsubscribe function to stop listening to events
42384
+ */
42385
+ function listenAfterEnd(fn) {
42386
+ backtest.loggerService.log(LISTEN_AFTER_END_METHOD_NAME);
42387
+ return afterEndSubject.subscribe(queued(async (event) => fn(event)));
42388
+ }
42389
+ /**
42390
+ * Subscribes to filtered after end events with one-time execution.
42391
+ * Listens for events matching the filter predicate, then executes callback once
42392
+ * and automatically unsubscribes.
42393
+ * @param filterFn - Predicate to filter which events trigger the callback
42394
+ * @param fn - Callback function to handle the filtered event (called only once)
42395
+ * @return Unsubscribe function to cancel the listener before it fires
42396
+ */
42397
+ function listenAfterEndOnce(filterFn, fn) {
42398
+ backtest.loggerService.log(LISTEN_AFTER_END_ONCE_METHOD_NAME);
42399
+ let disposeFn;
42400
+ const wrappedFn = async (event) => {
42401
+ if (filterFn(event)) {
42402
+ await fn(event);
42403
+ disposeFn && disposeFn();
42404
+ }
42405
+ };
42406
+ return disposeFn = listenAfterEnd(wrappedFn);
42407
+ }
41891
42408
 
41892
42409
  const BACKTEST_METHOD_NAME_RUN = "BacktestUtils.run";
41893
42410
  const BACKTEST_METHOD_NAME_BACKGROUND = "BacktestUtils.background";
@@ -41966,11 +42483,21 @@ const INSTANCE_TASK_FN$2 = async (symbol, context, self) => {
41966
42483
  self._isStopped = false;
41967
42484
  self._isDone = false;
41968
42485
  }
42486
+ Lookup.addActivity({
42487
+ symbol,
42488
+ context,
42489
+ backtest: true,
42490
+ });
41969
42491
  for await (const _ of self.run(symbol, context)) {
41970
42492
  if (self._isStopped) {
41971
42493
  break;
41972
42494
  }
41973
42495
  }
42496
+ Lookup.removeActivity({
42497
+ symbol,
42498
+ context,
42499
+ backtest: true,
42500
+ });
41974
42501
  if (!self._isDone) {
41975
42502
  await doneBacktestSubject.next({
41976
42503
  exchangeName: context.exchangeName,
@@ -44617,11 +45144,21 @@ const INSTANCE_TASK_FN$1 = async (symbol, context, self) => {
44617
45144
  self._isStopped = false;
44618
45145
  self._isDone = false;
44619
45146
  }
45147
+ Lookup.addActivity({
45148
+ symbol,
45149
+ context,
45150
+ backtest: false,
45151
+ });
44620
45152
  for await (const signal of self.run(symbol, context)) {
44621
45153
  if (signal?.action === "closed" && self._isStopped) {
44622
45154
  break;
44623
45155
  }
44624
45156
  }
45157
+ Lookup.removeActivity({
45158
+ symbol,
45159
+ context,
45160
+ backtest: false,
45161
+ });
44625
45162
  if (!self._isDone) {
44626
45163
  await doneLiveSubject.next({
44627
45164
  exchangeName: context.exchangeName,
@@ -48556,6 +49093,128 @@ async function listRiskSchema() {
48556
49093
  return await backtest.riskValidationService.list();
48557
49094
  }
48558
49095
 
49096
+ const GET_DATE_METHOD_NAME = "meta.getDate";
49097
+ const GET_TIMESTAMP_METHOD_NAME = "meta.getTimestamp";
49098
+ const GET_MODE_METHOD_NAME = "meta.getMode";
49099
+ const GET_SYMBOL_METHOD_NAME = "meta.getSymbol";
49100
+ const GET_CONTEXT_METHOD_NAME = "meta.getContext";
49101
+ /**
49102
+ * Gets the current date from execution context.
49103
+ *
49104
+ * In backtest mode: returns the current timeframe date being processed
49105
+ * In live mode: returns current real-time date
49106
+ *
49107
+ * @returns Promise resolving to current execution context date
49108
+ *
49109
+ * @example
49110
+ * ```typescript
49111
+ * const date = await getDate();
49112
+ * console.log(date); // 2024-01-01T12:00:00.000Z
49113
+ * ```
49114
+ */
49115
+ async function getDate() {
49116
+ backtest.loggerService.info(GET_DATE_METHOD_NAME);
49117
+ if (!ExecutionContextService.hasContext()) {
49118
+ throw new Error("getDate requires an execution context");
49119
+ }
49120
+ const { when } = backtest.executionContextService.context;
49121
+ return new Date(when.getTime());
49122
+ }
49123
+ /**
49124
+ * Gets the current timestamp from execution context.
49125
+ *
49126
+ * In backtest mode: returns the current timeframe timestamp being processed
49127
+ * In live mode: returns current real-time timestamp
49128
+ *
49129
+ * @returns Promise resolving to current execution context timestamp in milliseconds
49130
+ * @example
49131
+ * ```typescript
49132
+ * const timestamp = await getTimestamp();
49133
+ * console.log(timestamp); // 1700000000000
49134
+ * ```
49135
+ */
49136
+ async function getTimestamp() {
49137
+ backtest.loggerService.info(GET_TIMESTAMP_METHOD_NAME);
49138
+ if (!ExecutionContextService.hasContext()) {
49139
+ throw new Error("getTimestamp requires an execution context");
49140
+ }
49141
+ if (!MethodContextService.hasContext()) {
49142
+ throw new Error("getTimestamp requires a method context");
49143
+ }
49144
+ const { symbol, backtest: isBacktest } = backtest.executionContextService.context;
49145
+ const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
49146
+ return backtest.timeMetaService.getTimestamp(symbol, {
49147
+ exchangeName,
49148
+ frameName,
49149
+ strategyName,
49150
+ }, isBacktest);
49151
+ }
49152
+ /**
49153
+ * Gets the current execution mode.
49154
+ *
49155
+ * @returns Promise resolving to "backtest" or "live"
49156
+ *
49157
+ * @example
49158
+ * ```typescript
49159
+ * const mode = await getMode();
49160
+ * if (mode === "backtest") {
49161
+ * console.log("Running in backtest mode");
49162
+ * } else {
49163
+ * console.log("Running in live mode");
49164
+ * }
49165
+ * ```
49166
+ */
49167
+ async function getMode() {
49168
+ backtest.loggerService.info(GET_MODE_METHOD_NAME);
49169
+ if (!ExecutionContextService.hasContext()) {
49170
+ throw new Error("getMode requires an execution context");
49171
+ }
49172
+ const { backtest: bt } = backtest.executionContextService.context;
49173
+ return bt ? "backtest" : "live";
49174
+ }
49175
+ /**
49176
+ * Gets the current trading symbol from execution context.
49177
+ *
49178
+ * @returns Promise resolving to the current trading symbol (e.g., "BTCUSDT")
49179
+ * @throws Error if execution context is not active
49180
+ *
49181
+ * @example
49182
+ * ```typescript
49183
+ * const symbol = await getSymbol();
49184
+ * console.log(symbol); // "BTCUSDT"
49185
+ * ```
49186
+ */
49187
+ async function getSymbol() {
49188
+ backtest.loggerService.info(GET_SYMBOL_METHOD_NAME);
49189
+ if (!ExecutionContextService.hasContext()) {
49190
+ throw new Error("getSymbol requires an execution context");
49191
+ }
49192
+ const { symbol } = backtest.executionContextService.context;
49193
+ return symbol;
49194
+ }
49195
+ /**
49196
+ * Gets the current method context.
49197
+ *
49198
+ * Returns the context object from the method context service, which contains
49199
+ * information about the current method execution environment.
49200
+ *
49201
+ * @returns Promise resolving to the current method context object
49202
+ * @throws Error if method context is not active
49203
+ *
49204
+ * @example
49205
+ * ```typescript
49206
+ * const context = await getContext();
49207
+ * console.log(context); // { ...method context data... }
49208
+ * ```
49209
+ */
49210
+ async function getContext() {
49211
+ backtest.loggerService.info(GET_CONTEXT_METHOD_NAME);
49212
+ if (!MethodContextService.hasContext()) {
49213
+ throw new Error("getContext requires a method context");
49214
+ }
49215
+ return backtest.methodContextService.context;
49216
+ }
49217
+
48559
49218
  const RECENT_PERSIST_BACKTEST_METHOD_NAME_HANDLE_ACTIVE_PING = "RecentPersistBacktestUtils.handleActivePing";
48560
49219
  const RECENT_PERSIST_BACKTEST_METHOD_NAME_GET_LATEST_SIGNAL = "RecentPersistBacktestUtils.getLatestSignal";
48561
49220
  const RECENT_PERSIST_LIVE_METHOD_NAME_HANDLE_ACTIVE_PING = "RecentPersistLiveUtils.handleActivePing";
@@ -54600,6 +55259,8 @@ const SUBJECT_ISOLATION_LIST = [
54600
55259
  syncSubject,
54601
55260
  validationSubject,
54602
55261
  signalNotifySubject,
55262
+ beforeStartSubject,
55263
+ afterEndSubject
54603
55264
  ];
54604
55265
  /**
54605
55266
  * Creates a snapshot function for a given subject by clearing its internal
@@ -63732,4 +64393,4 @@ const validateSignal = (signal, currentPrice) => {
63732
64393
  return !errors.length;
63733
64394
  };
63734
64395
 
63735
- 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, waitForReady, warmCandles, writeMemory };
64396
+ export { ActionBase, Backtest, Breakeven, Broker, BrokerBase, Cache, Constant, Dump, Exchange, ExecutionContextService, Heat, HighestProfit, Interval, Live, Log, Lookup, 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, cacheCandles, 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, listenAfterEnd, listenAfterEndOnce, listenBacktestProgress, listenBeforeStart, listenBeforeStartOnce, 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, waitForReady, warmCandles, writeMemory };