backtest-kit 9.7.0 → 9.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (6) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +1871 -1871
  3. package/build/index.cjs +283 -180
  4. package/build/index.mjs +284 -181
  5. package/package.json +86 -86
  6. package/types.d.ts +100 -28
package/build/index.cjs CHANGED
@@ -36127,72 +36127,41 @@ const PRINT_PROGRESS_FN = (fetched, total, symbol, interval) => {
36127
36127
  }
36128
36128
  };
36129
36129
  /**
36130
- * Checks cached candle timestamps for correct interval alignment.
36131
- * Reads JSON files directly from persist storage without using abstractions.
36130
+ * Checks cached candle presence via the persist adapter.
36131
+ * Issues one ranged read; adapter-side `hasValue` covers each expected timestamp,
36132
+ * so a single missing or unaligned candle yields a miss without loading the whole dataset.
36132
36133
  *
36133
36134
  * @param params - Validation parameters
36134
36135
  */
36135
36136
  async function checkCandles(params) {
36136
- const { symbol, exchangeName, interval, from, to, baseDir = "./dump/data/candle" } = params;
36137
+ const { symbol, exchangeName, interval, from, to } = params;
36137
36138
  backtest.loggerService.info(CHECK_CANDLES_METHOD_NAME, params);
36138
36139
  const step = INTERVAL_MINUTES$3[interval];
36139
36140
  if (!step) {
36140
36141
  throw new Error(`checkCandles: unsupported interval=${interval}`);
36141
36142
  }
36142
36143
  const stepMs = step * MS_PER_MINUTE$3;
36143
- const dir = path.join(baseDir, exchangeName, symbol, interval);
36144
36144
  const fromTs = ALIGN_TO_INTERVAL_FN(from.getTime(), step);
36145
36145
  const toTs = ALIGN_TO_INTERVAL_FN(to.getTime(), step);
36146
- try {
36147
- await fs.stat(dir);
36148
- }
36149
- catch {
36150
- throw new Error(`checkCandles: cache directory not found: ${dir}`);
36151
- }
36152
- // Collect only filenames (strings) in range via async iterator — no full readdir in memory
36153
- const files = [];
36154
- for await (const entry of await fs.opendir(dir)) {
36155
- if (!entry.isFile() || !entry.name.endsWith(".json")) {
36156
- continue;
36157
- }
36158
- const ts = Number(entry.name.replace(".json", ""));
36159
- if (ts >= fromTs && ts < toTs) {
36160
- files.push(entry.name);
36161
- }
36162
- }
36163
- if (files.length === 0) {
36164
- throw new Error(`checkCandles: no cached candles in range [${fromTs}, ${toTs}) in ${dir}`);
36146
+ const totalCandles = Math.floor((toTs - fromTs) / stepMs);
36147
+ if (totalCandles <= 0) {
36148
+ throw new Error(`checkCandles: empty range [${fromTs}, ${toTs}) for ${symbol} ${interval}`);
36165
36149
  }
36166
- files.sort();
36167
- let prevTimestamp = null;
36168
- let prevName = null;
36169
- PRINT_PROGRESS_FN(0, files.length, symbol, interval);
36170
- for (let i = 0; i < files.length; i++) {
36171
- const filePath = path.join(dir, files[i]);
36172
- const raw = await fs.readFile(filePath, "utf-8");
36173
- let candle;
36174
- try {
36175
- candle = JSON.parse(raw);
36176
- }
36177
- catch {
36178
- throw new Error(`checkCandles: ${files[i]} contains invalid JSON`);
36179
- }
36180
- const { timestamp } = candle;
36181
- const aligned = ALIGN_TO_INTERVAL_FN(timestamp, step);
36182
- if (timestamp !== aligned) {
36183
- throw new Error(`checkCandles: ${files[i]} timestamp not aligned to ${interval} boundary (actual=${timestamp}, expected=${aligned})`);
36184
- }
36185
- if (prevTimestamp !== null) {
36186
- const gap = timestamp - prevTimestamp;
36187
- if (gap !== stepMs) {
36188
- throw new Error(`checkCandles: gap between ${prevName} and ${files[i]} (actual=${gap}ms, expected=${stepMs}ms)`);
36189
- }
36150
+ let checked = 0;
36151
+ let currentSince = fromTs;
36152
+ PRINT_PROGRESS_FN(checked, totalCandles, symbol, interval);
36153
+ while (checked < totalCandles) {
36154
+ const chunkLimit = Math.min(totalCandles - checked, GLOBAL_CONFIG.CC_MAX_CANDLES_PER_REQUEST);
36155
+ const chunkUntil = currentSince + chunkLimit * stepMs;
36156
+ const candles = await PersistCandleAdapter.readCandlesData(symbol, interval, exchangeName, chunkLimit, currentSince, chunkUntil);
36157
+ if (!candles) {
36158
+ throw new Error(`checkCandles: cache miss for ${symbol} ${interval} [${currentSince}, ${chunkUntil})`);
36190
36159
  }
36191
- prevTimestamp = timestamp;
36192
- prevName = files[i];
36193
- PRINT_PROGRESS_FN(i + 1, files.length, symbol, interval);
36160
+ checked += chunkLimit;
36161
+ currentSince = chunkUntil;
36162
+ PRINT_PROGRESS_FN(checked, totalCandles, symbol, interval);
36194
36163
  }
36195
- console.log(`checkCandles: OK ${files.length} candles ${symbol} ${interval}`);
36164
+ console.log(`checkCandles: OK ${totalCandles} candles ${symbol} ${interval}`);
36196
36165
  }
36197
36166
  /**
36198
36167
  * Pre-caches candles for a date range into persist storage.
@@ -37443,7 +37412,19 @@ class BrokerProxy {
37443
37412
  */
37444
37413
  class BrokerAdapter {
37445
37414
  constructor() {
37446
- this._brokerInstance = null;
37415
+ /** Factory producing the active `BrokerProxy` instance */
37416
+ this._brokerFactory = () => null;
37417
+ /**
37418
+ * Lazily constructs the `BrokerProxy` from the registered factory and
37419
+ * memoizes the result via `singleshot`.
37420
+ *
37421
+ * The proxy is built on the first call and cached for all subsequent calls.
37422
+ * Returns `null` when no adapter has been registered via `useBrokerAdapter()`.
37423
+ *
37424
+ * Reset via `clear()` so the next call rebuilds from the current factory
37425
+ * (e.g. when `process.cwd()` changes between strategy iterations).
37426
+ */
37427
+ this.getInstance = functoolsKit.singleshot(() => this._brokerFactory());
37447
37428
  /**
37448
37429
  * Forwards a signal-open event to the registered broker adapter.
37449
37430
  *
@@ -37477,7 +37458,10 @@ class BrokerAdapter {
37477
37458
  if (payload.backtest) {
37478
37459
  return;
37479
37460
  }
37480
- await this._brokerInstance?.onSignalOpenCommit(payload);
37461
+ const instance = this.getInstance();
37462
+ if (instance) {
37463
+ await instance.onSignalOpenCommit(payload);
37464
+ }
37481
37465
  };
37482
37466
  /**
37483
37467
  * Forwards a signal-close event to the registered broker adapter.
@@ -37515,7 +37499,10 @@ class BrokerAdapter {
37515
37499
  if (payload.backtest) {
37516
37500
  return;
37517
37501
  }
37518
- await this._brokerInstance?.onSignalCloseCommit(payload);
37502
+ const instance = this.getInstance();
37503
+ if (instance) {
37504
+ await instance.onSignalCloseCommit(payload);
37505
+ }
37519
37506
  };
37520
37507
  /**
37521
37508
  * Intercepts a partial-profit close before DI-core mutation.
@@ -37551,7 +37538,10 @@ class BrokerAdapter {
37551
37538
  if (payload.backtest) {
37552
37539
  return;
37553
37540
  }
37554
- await this._brokerInstance?.onPartialProfitCommit(payload);
37541
+ const instance = this.getInstance();
37542
+ if (instance) {
37543
+ await instance.onPartialProfitCommit(payload);
37544
+ }
37555
37545
  };
37556
37546
  /**
37557
37547
  * Intercepts a partial-loss close before DI-core mutation.
@@ -37587,7 +37577,10 @@ class BrokerAdapter {
37587
37577
  if (payload.backtest) {
37588
37578
  return;
37589
37579
  }
37590
- await this._brokerInstance?.onPartialLossCommit(payload);
37580
+ const instance = this.getInstance();
37581
+ if (instance) {
37582
+ await instance.onPartialLossCommit(payload);
37583
+ }
37591
37584
  };
37592
37585
  /**
37593
37586
  * Intercepts a trailing stop-loss update before DI-core mutation.
@@ -37623,7 +37616,10 @@ class BrokerAdapter {
37623
37616
  if (payload.backtest) {
37624
37617
  return;
37625
37618
  }
37626
- await this._brokerInstance?.onTrailingStopCommit(payload);
37619
+ const instance = this.getInstance();
37620
+ if (instance) {
37621
+ await instance.onTrailingStopCommit(payload);
37622
+ }
37627
37623
  };
37628
37624
  /**
37629
37625
  * Intercepts a trailing take-profit update before DI-core mutation.
@@ -37659,7 +37655,10 @@ class BrokerAdapter {
37659
37655
  if (payload.backtest) {
37660
37656
  return;
37661
37657
  }
37662
- await this._brokerInstance?.onTrailingTakeCommit(payload);
37658
+ const instance = this.getInstance();
37659
+ if (instance) {
37660
+ await instance.onTrailingTakeCommit(payload);
37661
+ }
37663
37662
  };
37664
37663
  /**
37665
37664
  * Intercepts a breakeven operation before DI-core mutation.
@@ -37696,7 +37695,10 @@ class BrokerAdapter {
37696
37695
  if (payload.backtest) {
37697
37696
  return;
37698
37697
  }
37699
- await this._brokerInstance?.onBreakevenCommit(payload);
37698
+ const instance = this.getInstance();
37699
+ if (instance) {
37700
+ await instance.onBreakevenCommit(payload);
37701
+ }
37700
37702
  };
37701
37703
  /**
37702
37704
  * Intercepts a DCA average-buy entry before DI-core mutation.
@@ -37732,7 +37734,10 @@ class BrokerAdapter {
37732
37734
  if (payload.backtest) {
37733
37735
  return;
37734
37736
  }
37735
- await this._brokerInstance?.onAverageBuyCommit(payload);
37737
+ const instance = this.getInstance();
37738
+ if (instance) {
37739
+ await instance.onAverageBuyCommit(payload);
37740
+ }
37736
37741
  };
37737
37742
  /**
37738
37743
  * Registers a broker adapter instance or constructor to receive commit* callbacks.
@@ -37756,11 +37761,12 @@ class BrokerAdapter {
37756
37761
  this.useBrokerAdapter = (broker) => {
37757
37762
  backtest.loggerService.info(BROKER_METHOD_NAME_USE_BROKER_ADAPTER, {});
37758
37763
  if (typeof broker === "function") {
37759
- const instance = Reflect.construct(broker, []);
37760
- this._brokerInstance = new BrokerProxy(instance);
37761
- return;
37764
+ this._brokerFactory = () => new BrokerProxy(Reflect.construct(broker, []));
37765
+ }
37766
+ else {
37767
+ this._brokerFactory = () => new BrokerProxy(broker);
37762
37768
  }
37763
- this._brokerInstance = new BrokerProxy(broker);
37769
+ this.getInstance.clear();
37764
37770
  };
37765
37771
  /**
37766
37772
  * Activates the broker: subscribes to syncSubject for signal-open / signal-close routing.
@@ -37787,7 +37793,8 @@ class BrokerAdapter {
37787
37793
  */
37788
37794
  this.enable = functoolsKit.singleshot(() => {
37789
37795
  backtest.loggerService.info(BROKER_METHOD_NAME_ENABLE, {});
37790
- if (!this._brokerInstance) {
37796
+ const instance = this.getInstance();
37797
+ if (!instance) {
37791
37798
  this.enable.clear();
37792
37799
  throw new Error("No broker instance provided. Call Broker.useBrokerAdapter first.");
37793
37800
  }
@@ -37875,7 +37882,7 @@ class BrokerAdapter {
37875
37882
  */
37876
37883
  this.clear = () => {
37877
37884
  backtest.loggerService.info(BROKER_METHOD_NAME_CLEAR, {});
37878
- this._brokerInstance = null;
37885
+ this.getInstance.clear();
37879
37886
  this.enable.clear();
37880
37887
  };
37881
37888
  }
@@ -48895,8 +48902,16 @@ class RecentMemoryLiveUtils {
48895
48902
  */
48896
48903
  class RecentBacktestAdapter {
48897
48904
  constructor() {
48898
- /** Internal storage utils instance */
48899
- this._recentBacktestUtils = new RecentMemoryBacktestUtils();
48905
+ /** Factory producing the active storage utils instance */
48906
+ this._recentBacktestFactory = () => new RecentMemoryBacktestUtils();
48907
+ /**
48908
+ * Lazily constructs the storage utils from the registered factory and memoizes
48909
+ * the result via `singleshot`.
48910
+ *
48911
+ * The instance is built on the first call and cached for all subsequent calls.
48912
+ * Reset via `clear()` so the next call rebuilds from the current factory.
48913
+ */
48914
+ this.getInstance = functoolsKit.singleshot(() => this._recentBacktestFactory());
48900
48915
  /**
48901
48916
  * Handles active ping event.
48902
48917
  * Proxies call to the underlying storage adapter.
@@ -48906,7 +48921,7 @@ class RecentBacktestAdapter {
48906
48921
  backtest.loggerService.info(RECENT_BACKTEST_ADAPTER_METHOD_NAME_HANDLE_ACTIVE_PING, {
48907
48922
  signalId: event.data.id,
48908
48923
  });
48909
- return await this._recentBacktestUtils.handleActivePing(event);
48924
+ return await this.getInstance().handleActivePing(event);
48910
48925
  };
48911
48926
  /**
48912
48927
  * Retrieves the latest signal for the given context.
@@ -48927,7 +48942,7 @@ class RecentBacktestAdapter {
48927
48942
  frameName,
48928
48943
  backtest: backtest$1,
48929
48944
  });
48930
- return await this._recentBacktestUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1, when);
48945
+ return await this.getInstance().getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1, when);
48931
48946
  };
48932
48947
  /**
48933
48948
  * Returns the number of whole minutes elapsed since the latest signal's creation timestamp.
@@ -48951,7 +48966,7 @@ class RecentBacktestAdapter {
48951
48966
  backtest: backtest$1,
48952
48967
  timestamp,
48953
48968
  });
48954
- const signal = await this._recentBacktestUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1, new Date(timestamp));
48969
+ const signal = await this.getInstance().getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1, new Date(timestamp));
48955
48970
  if (!signal) {
48956
48971
  return null;
48957
48972
  }
@@ -48964,7 +48979,8 @@ class RecentBacktestAdapter {
48964
48979
  */
48965
48980
  this.useRecentAdapter = (Ctor) => {
48966
48981
  backtest.loggerService.info(RECENT_BACKTEST_ADAPTER_METHOD_NAME_USE_ADAPTER);
48967
- this._recentBacktestUtils = Reflect.construct(Ctor, []);
48982
+ this._recentBacktestFactory = () => Reflect.construct(Ctor, []);
48983
+ this.getInstance.clear();
48968
48984
  };
48969
48985
  /**
48970
48986
  * Switches to persistent storage adapter.
@@ -48972,7 +48988,8 @@ class RecentBacktestAdapter {
48972
48988
  */
48973
48989
  this.usePersist = () => {
48974
48990
  backtest.loggerService.info(RECENT_BACKTEST_ADAPTER_METHOD_NAME_USE_PERSIST);
48975
- this._recentBacktestUtils = new RecentPersistBacktestUtils();
48991
+ this._recentBacktestFactory = () => new RecentPersistBacktestUtils();
48992
+ this.getInstance.clear();
48976
48993
  };
48977
48994
  /**
48978
48995
  * Switches to in-memory storage adapter (default).
@@ -48980,14 +48997,17 @@ class RecentBacktestAdapter {
48980
48997
  */
48981
48998
  this.useMemory = () => {
48982
48999
  backtest.loggerService.info(RECENT_BACKTEST_ADAPTER_METHOD_NAME_USE_MEMORY);
48983
- this._recentBacktestUtils = new RecentMemoryBacktestUtils();
49000
+ this._recentBacktestFactory = () => new RecentMemoryBacktestUtils();
49001
+ this.getInstance.clear();
48984
49002
  };
48985
49003
  /**
48986
- * Clears the cached utils instance by resetting to the default in-memory adapter.
49004
+ * Clears the memoized utils instance.
49005
+ * Call this when process.cwd() changes between strategy iterations
49006
+ * so a new instance is created with the updated base path.
48987
49007
  */
48988
49008
  this.clear = () => {
48989
49009
  backtest.loggerService.info(RECENT_BACKTEST_ADAPTER_METHOD_NAME_CLEAR);
48990
- this._recentBacktestUtils = new RecentMemoryBacktestUtils();
49010
+ this.getInstance.clear();
48991
49011
  };
48992
49012
  }
48993
49013
  }
@@ -49002,8 +49022,16 @@ class RecentBacktestAdapter {
49002
49022
  */
49003
49023
  class RecentLiveAdapter {
49004
49024
  constructor() {
49005
- /** Internal storage utils instance */
49006
- this._recentLiveUtils = new RecentPersistLiveUtils();
49025
+ /** Factory producing the active storage utils instance */
49026
+ this._recentLiveFactory = () => new RecentPersistLiveUtils();
49027
+ /**
49028
+ * Lazily constructs the storage utils from the registered factory and memoizes
49029
+ * the result via `singleshot`.
49030
+ *
49031
+ * The instance is built on the first call and cached for all subsequent calls.
49032
+ * Reset via `clear()` so the next call rebuilds from the current factory.
49033
+ */
49034
+ this.getInstance = functoolsKit.singleshot(() => this._recentLiveFactory());
49007
49035
  /**
49008
49036
  * Handles active ping event.
49009
49037
  * Proxies call to the underlying storage adapter.
@@ -49013,7 +49041,7 @@ class RecentLiveAdapter {
49013
49041
  backtest.loggerService.info(RECENT_LIVE_ADAPTER_METHOD_NAME_HANDLE_ACTIVE_PING, {
49014
49042
  signalId: event.data.id,
49015
49043
  });
49016
- return await this._recentLiveUtils.handleActivePing(event);
49044
+ return await this.getInstance().handleActivePing(event);
49017
49045
  };
49018
49046
  /**
49019
49047
  * Retrieves the latest signal for the given context.
@@ -49034,7 +49062,7 @@ class RecentLiveAdapter {
49034
49062
  frameName,
49035
49063
  backtest: backtest$1,
49036
49064
  });
49037
- return await this._recentLiveUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1, when);
49065
+ return await this.getInstance().getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1, when);
49038
49066
  };
49039
49067
  /**
49040
49068
  * Returns the number of whole minutes elapsed since the latest signal's creation timestamp.
@@ -49058,7 +49086,7 @@ class RecentLiveAdapter {
49058
49086
  backtest: backtest$1,
49059
49087
  timestamp,
49060
49088
  });
49061
- const signal = await this._recentLiveUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1, new Date(timestamp));
49089
+ const signal = await this.getInstance().getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1, new Date(timestamp));
49062
49090
  if (!signal) {
49063
49091
  return null;
49064
49092
  }
@@ -49071,7 +49099,8 @@ class RecentLiveAdapter {
49071
49099
  */
49072
49100
  this.useRecentAdapter = (Ctor) => {
49073
49101
  backtest.loggerService.info(RECENT_LIVE_ADAPTER_METHOD_NAME_USE_ADAPTER);
49074
- this._recentLiveUtils = Reflect.construct(Ctor, []);
49102
+ this._recentLiveFactory = () => Reflect.construct(Ctor, []);
49103
+ this.getInstance.clear();
49075
49104
  };
49076
49105
  /**
49077
49106
  * Switches to persistent storage adapter (default).
@@ -49079,7 +49108,8 @@ class RecentLiveAdapter {
49079
49108
  */
49080
49109
  this.usePersist = () => {
49081
49110
  backtest.loggerService.info(RECENT_LIVE_ADAPTER_METHOD_NAME_USE_PERSIST);
49082
- this._recentLiveUtils = new RecentPersistLiveUtils();
49111
+ this._recentLiveFactory = () => new RecentPersistLiveUtils();
49112
+ this.getInstance.clear();
49083
49113
  };
49084
49114
  /**
49085
49115
  * Switches to in-memory storage adapter.
@@ -49087,14 +49117,17 @@ class RecentLiveAdapter {
49087
49117
  */
49088
49118
  this.useMemory = () => {
49089
49119
  backtest.loggerService.info(RECENT_LIVE_ADAPTER_METHOD_NAME_USE_MEMORY);
49090
- this._recentLiveUtils = new RecentMemoryLiveUtils();
49120
+ this._recentLiveFactory = () => new RecentMemoryLiveUtils();
49121
+ this.getInstance.clear();
49091
49122
  };
49092
49123
  /**
49093
- * Clears the cached utils instance by resetting to the default persistent adapter.
49124
+ * Clears the memoized utils instance.
49125
+ * Call this when process.cwd() changes between strategy iterations
49126
+ * so a new instance is created with the updated base path.
49094
49127
  */
49095
49128
  this.clear = () => {
49096
49129
  backtest.loggerService.info(RECENT_LIVE_ADAPTER_METHOD_NAME_CLEAR);
49097
- this._recentLiveUtils = new RecentPersistLiveUtils();
49130
+ this.getInstance.clear();
49098
49131
  };
49099
49132
  }
49100
49133
  }
@@ -54419,16 +54452,26 @@ class LogDummyUtils {
54419
54452
  */
54420
54453
  class LogAdapter {
54421
54454
  constructor() {
54422
- /** Internal log utils instance */
54423
- this._log = new LogMemoryUtils();
54455
+ /** Factory producing the active log utils instance */
54456
+ this._logFactory = () => new LogMemoryUtils();
54457
+ /**
54458
+ * Lazily constructs the log utils from the registered factory and memoizes
54459
+ * the result via `singleshot`.
54460
+ *
54461
+ * The instance is built on the first call and cached for all subsequent calls.
54462
+ * Reset via `clear()` so the next call rebuilds from the current factory
54463
+ * (e.g. when `process.cwd()` changes between strategy iterations).
54464
+ */
54465
+ this.getInstance = functoolsKit.singleshot(() => this._logFactory());
54424
54466
  /**
54425
54467
  * Lists all stored log entries.
54426
54468
  * Proxies call to the underlying log adapter.
54427
54469
  * @returns Array of all log entries
54428
54470
  */
54429
54471
  this.getList = async () => {
54430
- if (this._log.getList) {
54431
- return await this._log.getList();
54472
+ const log = this.getInstance();
54473
+ if (log.getList) {
54474
+ return await log.getList();
54432
54475
  }
54433
54476
  return [];
54434
54477
  };
@@ -54439,8 +54482,9 @@ class LogAdapter {
54439
54482
  * @param args - Additional arguments
54440
54483
  */
54441
54484
  this.log = (topic, ...args) => {
54442
- if (this._log.log) {
54443
- this._log.log(topic, ...args);
54485
+ const log = this.getInstance();
54486
+ if (log.log) {
54487
+ log.log(topic, ...args);
54444
54488
  }
54445
54489
  };
54446
54490
  /**
@@ -54450,8 +54494,9 @@ class LogAdapter {
54450
54494
  * @param args - Additional arguments
54451
54495
  */
54452
54496
  this.debug = (topic, ...args) => {
54453
- if (this._log.debug) {
54454
- this._log.debug(topic, ...args);
54497
+ const log = this.getInstance();
54498
+ if (log.debug) {
54499
+ log.debug(topic, ...args);
54455
54500
  }
54456
54501
  };
54457
54502
  /**
@@ -54461,8 +54506,9 @@ class LogAdapter {
54461
54506
  * @param args - Additional arguments
54462
54507
  */
54463
54508
  this.info = (topic, ...args) => {
54464
- if (this._log.info) {
54465
- this._log.info(topic, ...args);
54509
+ const log = this.getInstance();
54510
+ if (log.info) {
54511
+ log.info(topic, ...args);
54466
54512
  }
54467
54513
  };
54468
54514
  /**
@@ -54472,8 +54518,9 @@ class LogAdapter {
54472
54518
  * @param args - Additional arguments
54473
54519
  */
54474
54520
  this.warn = (topic, ...args) => {
54475
- if (this._log.warn) {
54476
- this._log.warn(topic, ...args);
54521
+ const log = this.getInstance();
54522
+ if (log.warn) {
54523
+ log.warn(topic, ...args);
54477
54524
  }
54478
54525
  };
54479
54526
  /**
@@ -54483,7 +54530,8 @@ class LogAdapter {
54483
54530
  */
54484
54531
  this.useLogger = (Ctor) => {
54485
54532
  backtest.loggerService.info(LOG_ADAPTER_METHOD_NAME_USE_LOGGER);
54486
- this._log = Reflect.construct(Ctor, []);
54533
+ this._logFactory = () => Reflect.construct(Ctor, []);
54534
+ this.getInstance.clear();
54487
54535
  };
54488
54536
  /**
54489
54537
  * Switches to persistent log adapter.
@@ -54491,7 +54539,8 @@ class LogAdapter {
54491
54539
  */
54492
54540
  this.usePersist = () => {
54493
54541
  backtest.loggerService.info(LOG_ADAPTER_METHOD_NAME_USE_PERSIST);
54494
- this._log = new LogPersistUtils();
54542
+ this._logFactory = () => new LogPersistUtils();
54543
+ this.getInstance.clear();
54495
54544
  };
54496
54545
  /**
54497
54546
  * Switches to in-memory log adapter (default).
@@ -54499,7 +54548,8 @@ class LogAdapter {
54499
54548
  */
54500
54549
  this.useMemory = () => {
54501
54550
  backtest.loggerService.info(LOG_ADAPTER_METHOD_NAME_USE_MEMORY);
54502
- this._log = new LogMemoryUtils();
54551
+ this._logFactory = () => new LogMemoryUtils();
54552
+ this.getInstance.clear();
54503
54553
  };
54504
54554
  /**
54505
54555
  * Switches to dummy log adapter.
@@ -54507,7 +54557,8 @@ class LogAdapter {
54507
54557
  */
54508
54558
  this.useDummy = () => {
54509
54559
  backtest.loggerService.info(LOG_ADAPTER_METHOD_NAME_USE_DUMMY);
54510
- this._log = new LogDummyUtils();
54560
+ this._logFactory = () => new LogDummyUtils();
54561
+ this.getInstance.clear();
54511
54562
  };
54512
54563
  /**
54513
54564
  * Switches to JSONL file log adapter.
@@ -54517,18 +54568,22 @@ class LogAdapter {
54517
54568
  * @param fileName - Base file name without extension (default: "log")
54518
54569
  * @param dirName - Directory for the JSONL file (default: ./dump/log)
54519
54570
  */
54520
- this.useJsonl = (fileName = "log.jsonl", dirName = path.join(process.cwd(), "./dump/log")) => {
54571
+ this.useJsonl = (fileName = "log.jsonl", dirName) => {
54521
54572
  backtest.loggerService.info(LOG_ADAPTER_METHOD_NAME_USE_JSONL);
54522
- this._log = new LogJsonlUtils(fileName, dirName);
54573
+ this._logFactory = () => {
54574
+ const dir = dirName || path.join(process.cwd(), "./dump/log");
54575
+ return new LogJsonlUtils(fileName, dir);
54576
+ };
54577
+ this.getInstance.clear();
54523
54578
  };
54524
54579
  /**
54525
- * Clears the cached log instance by resetting to the default in-memory adapter.
54580
+ * Clears the memoized log instance.
54526
54581
  * Call this when process.cwd() changes between strategy iterations
54527
54582
  * so a new adapter instance is created with the updated base path.
54528
54583
  */
54529
54584
  this.clear = () => {
54530
54585
  backtest.loggerService.info(LOG_ADAPTER_METHOD_NAME_CLEAR);
54531
- this._log = new LogMemoryUtils();
54586
+ this.getInstance.clear();
54532
54587
  };
54533
54588
  }
54534
54589
  }
@@ -58420,15 +58475,23 @@ class StorageDummyLiveUtils {
58420
58475
  */
58421
58476
  class StorageBacktestAdapter {
58422
58477
  constructor() {
58423
- /** Internal storage utils instance */
58424
- this._signalBacktestUtils = new StorageMemoryBacktestUtils();
58478
+ /** Factory producing the active storage utils instance */
58479
+ this._signalBacktestFactory = () => new StorageMemoryBacktestUtils();
58480
+ /**
58481
+ * Lazily constructs the storage utils from the registered factory and memoizes
58482
+ * the result via `singleshot`.
58483
+ *
58484
+ * The instance is built on the first call and cached for all subsequent calls.
58485
+ * Reset via `clear()` so the next call rebuilds from the current factory.
58486
+ */
58487
+ this.getInstance = functoolsKit.singleshot(() => this._signalBacktestFactory());
58425
58488
  /**
58426
58489
  * Handles signal opened event.
58427
58490
  * Proxies call to the underlying storage adapter.
58428
58491
  * @param tick - The opened signal tick data
58429
58492
  */
58430
58493
  this.handleOpened = async (tick) => {
58431
- return await this._signalBacktestUtils.handleOpened(tick);
58494
+ return await this.getInstance().handleOpened(tick);
58432
58495
  };
58433
58496
  /**
58434
58497
  * Handles signal closed event.
@@ -58436,7 +58499,7 @@ class StorageBacktestAdapter {
58436
58499
  * @param tick - The closed signal tick data
58437
58500
  */
58438
58501
  this.handleClosed = async (tick) => {
58439
- return await this._signalBacktestUtils.handleClosed(tick);
58502
+ return await this.getInstance().handleClosed(tick);
58440
58503
  };
58441
58504
  /**
58442
58505
  * Handles signal scheduled event.
@@ -58444,7 +58507,7 @@ class StorageBacktestAdapter {
58444
58507
  * @param tick - The scheduled signal tick data
58445
58508
  */
58446
58509
  this.handleScheduled = async (tick) => {
58447
- return await this._signalBacktestUtils.handleScheduled(tick);
58510
+ return await this.getInstance().handleScheduled(tick);
58448
58511
  };
58449
58512
  /**
58450
58513
  * Handles signal cancelled event.
@@ -58452,7 +58515,7 @@ class StorageBacktestAdapter {
58452
58515
  * @param tick - The cancelled signal tick data
58453
58516
  */
58454
58517
  this.handleCancelled = async (tick) => {
58455
- return await this._signalBacktestUtils.handleCancelled(tick);
58518
+ return await this.getInstance().handleCancelled(tick);
58456
58519
  };
58457
58520
  /**
58458
58521
  * Finds a signal by its ID.
@@ -58461,7 +58524,7 @@ class StorageBacktestAdapter {
58461
58524
  * @returns The signal row or null if not found
58462
58525
  */
58463
58526
  this.findById = async (id) => {
58464
- return await this._signalBacktestUtils.findById(id);
58527
+ return await this.getInstance().findById(id);
58465
58528
  };
58466
58529
  /**
58467
58530
  * Lists all stored signals.
@@ -58469,13 +58532,13 @@ class StorageBacktestAdapter {
58469
58532
  * @returns Array of all signal rows
58470
58533
  */
58471
58534
  this.list = async () => {
58472
- return await this._signalBacktestUtils.list();
58535
+ return await this.getInstance().list();
58473
58536
  };
58474
58537
  this.handleActivePing = async (event) => {
58475
- return await this._signalBacktestUtils.handleActivePing(event);
58538
+ return await this.getInstance().handleActivePing(event);
58476
58539
  };
58477
58540
  this.handleSchedulePing = async (event) => {
58478
- return await this._signalBacktestUtils.handleSchedulePing(event);
58541
+ return await this.getInstance().handleSchedulePing(event);
58479
58542
  };
58480
58543
  /**
58481
58544
  * Sets the storage adapter constructor.
@@ -58485,7 +58548,8 @@ class StorageBacktestAdapter {
58485
58548
  */
58486
58549
  this.useStorageAdapter = (Ctor) => {
58487
58550
  backtest.loggerService.info(STORAGE_BACKTEST_ADAPTER_METHOD_NAME_USE_ADAPTER);
58488
- this._signalBacktestUtils = Reflect.construct(Ctor, []);
58551
+ this._signalBacktestFactory = () => Reflect.construct(Ctor, []);
58552
+ this.getInstance.clear();
58489
58553
  };
58490
58554
  /**
58491
58555
  * Switches to dummy storage adapter.
@@ -58493,7 +58557,8 @@ class StorageBacktestAdapter {
58493
58557
  */
58494
58558
  this.useDummy = () => {
58495
58559
  backtest.loggerService.info(STORAGE_BACKTEST_ADAPTER_METHOD_NAME_USE_DUMMY);
58496
- this._signalBacktestUtils = new StorageDummyBacktestUtils();
58560
+ this._signalBacktestFactory = () => new StorageDummyBacktestUtils();
58561
+ this.getInstance.clear();
58497
58562
  };
58498
58563
  /**
58499
58564
  * Switches to persistent storage adapter (default).
@@ -58501,7 +58566,8 @@ class StorageBacktestAdapter {
58501
58566
  */
58502
58567
  this.usePersist = () => {
58503
58568
  backtest.loggerService.info(STORAGE_BACKTEST_ADAPTER_METHOD_NAME_USE_PERSIST);
58504
- this._signalBacktestUtils = new StoragePersistBacktestUtils();
58569
+ this._signalBacktestFactory = () => new StoragePersistBacktestUtils();
58570
+ this.getInstance.clear();
58505
58571
  };
58506
58572
  /**
58507
58573
  * Switches to in-memory storage adapter.
@@ -58509,16 +58575,17 @@ class StorageBacktestAdapter {
58509
58575
  */
58510
58576
  this.useMemory = () => {
58511
58577
  backtest.loggerService.info(STORAGE_BACKTEST_ADAPTER_METHOD_NAME_USE_MEMORY);
58512
- this._signalBacktestUtils = new StorageMemoryBacktestUtils();
58578
+ this._signalBacktestFactory = () => new StorageMemoryBacktestUtils();
58579
+ this.getInstance.clear();
58513
58580
  };
58514
58581
  /**
58515
- * Clears the cached utils instance by resetting to the default in-memory adapter.
58582
+ * Clears the memoized utils instance.
58516
58583
  * Call this when process.cwd() changes between strategy iterations
58517
58584
  * so a new instance is created with the updated base path.
58518
58585
  */
58519
58586
  this.clear = () => {
58520
58587
  backtest.loggerService.info(STORAGE_BACKTEST_ADAPTER_METHOD_NAME_CLEAR);
58521
- this._signalBacktestUtils = new StorageMemoryBacktestUtils();
58588
+ this.getInstance.clear();
58522
58589
  };
58523
58590
  }
58524
58591
  }
@@ -58533,15 +58600,23 @@ class StorageBacktestAdapter {
58533
58600
  */
58534
58601
  class StorageLiveAdapter {
58535
58602
  constructor() {
58536
- /** Internal storage utils instance */
58537
- this._signalLiveUtils = new StoragePersistLiveUtils();
58603
+ /** Factory producing the active storage utils instance */
58604
+ this._signalLiveFactory = () => new StoragePersistLiveUtils();
58605
+ /**
58606
+ * Lazily constructs the storage utils from the registered factory and memoizes
58607
+ * the result via `singleshot`.
58608
+ *
58609
+ * The instance is built on the first call and cached for all subsequent calls.
58610
+ * Reset via `clear()` so the next call rebuilds from the current factory.
58611
+ */
58612
+ this.getInstance = functoolsKit.singleshot(() => this._signalLiveFactory());
58538
58613
  /**
58539
58614
  * Handles signal opened event.
58540
58615
  * Proxies call to the underlying storage adapter.
58541
58616
  * @param tick - The opened signal tick data
58542
58617
  */
58543
58618
  this.handleOpened = async (tick) => {
58544
- return await this._signalLiveUtils.handleOpened(tick);
58619
+ return await this.getInstance().handleOpened(tick);
58545
58620
  };
58546
58621
  /**
58547
58622
  * Handles signal closed event.
@@ -58549,7 +58624,7 @@ class StorageLiveAdapter {
58549
58624
  * @param tick - The closed signal tick data
58550
58625
  */
58551
58626
  this.handleClosed = async (tick) => {
58552
- return await this._signalLiveUtils.handleClosed(tick);
58627
+ return await this.getInstance().handleClosed(tick);
58553
58628
  };
58554
58629
  /**
58555
58630
  * Handles signal scheduled event.
@@ -58557,7 +58632,7 @@ class StorageLiveAdapter {
58557
58632
  * @param tick - The scheduled signal tick data
58558
58633
  */
58559
58634
  this.handleScheduled = async (tick) => {
58560
- return await this._signalLiveUtils.handleScheduled(tick);
58635
+ return await this.getInstance().handleScheduled(tick);
58561
58636
  };
58562
58637
  /**
58563
58638
  * Handles signal cancelled event.
@@ -58565,7 +58640,7 @@ class StorageLiveAdapter {
58565
58640
  * @param tick - The cancelled signal tick data
58566
58641
  */
58567
58642
  this.handleCancelled = async (tick) => {
58568
- return await this._signalLiveUtils.handleCancelled(tick);
58643
+ return await this.getInstance().handleCancelled(tick);
58569
58644
  };
58570
58645
  /**
58571
58646
  * Finds a signal by its ID.
@@ -58574,7 +58649,7 @@ class StorageLiveAdapter {
58574
58649
  * @returns The signal row or null if not found
58575
58650
  */
58576
58651
  this.findById = async (id) => {
58577
- return await this._signalLiveUtils.findById(id);
58652
+ return await this.getInstance().findById(id);
58578
58653
  };
58579
58654
  /**
58580
58655
  * Lists all stored signals.
@@ -58582,13 +58657,13 @@ class StorageLiveAdapter {
58582
58657
  * @returns Array of all signal rows
58583
58658
  */
58584
58659
  this.list = async () => {
58585
- return await this._signalLiveUtils.list();
58660
+ return await this.getInstance().list();
58586
58661
  };
58587
58662
  this.handleActivePing = async (event) => {
58588
- return await this._signalLiveUtils.handleActivePing(event);
58663
+ return await this.getInstance().handleActivePing(event);
58589
58664
  };
58590
58665
  this.handleSchedulePing = async (event) => {
58591
- return await this._signalLiveUtils.handleSchedulePing(event);
58666
+ return await this.getInstance().handleSchedulePing(event);
58592
58667
  };
58593
58668
  /**
58594
58669
  * Sets the storage adapter constructor.
@@ -58598,7 +58673,8 @@ class StorageLiveAdapter {
58598
58673
  */
58599
58674
  this.useStorageAdapter = (Ctor) => {
58600
58675
  backtest.loggerService.info(STORAGE_LIVE_ADAPTER_METHOD_NAME_USE_ADAPTER);
58601
- this._signalLiveUtils = Reflect.construct(Ctor, []);
58676
+ this._signalLiveFactory = () => Reflect.construct(Ctor, []);
58677
+ this.getInstance.clear();
58602
58678
  };
58603
58679
  /**
58604
58680
  * Switches to dummy storage adapter.
@@ -58606,7 +58682,8 @@ class StorageLiveAdapter {
58606
58682
  */
58607
58683
  this.useDummy = () => {
58608
58684
  backtest.loggerService.info(STORAGE_LIVE_ADAPTER_METHOD_NAME_USE_DUMMY);
58609
- this._signalLiveUtils = new StorageDummyLiveUtils();
58685
+ this._signalLiveFactory = () => new StorageDummyLiveUtils();
58686
+ this.getInstance.clear();
58610
58687
  };
58611
58688
  /**
58612
58689
  * Switches to persistent storage adapter (default).
@@ -58614,7 +58691,8 @@ class StorageLiveAdapter {
58614
58691
  */
58615
58692
  this.usePersist = () => {
58616
58693
  backtest.loggerService.info(STORAGE_LIVE_ADAPTER_METHOD_NAME_USE_PERSIST);
58617
- this._signalLiveUtils = new StoragePersistLiveUtils();
58694
+ this._signalLiveFactory = () => new StoragePersistLiveUtils();
58695
+ this.getInstance.clear();
58618
58696
  };
58619
58697
  /**
58620
58698
  * Switches to in-memory storage adapter.
@@ -58622,16 +58700,17 @@ class StorageLiveAdapter {
58622
58700
  */
58623
58701
  this.useMemory = () => {
58624
58702
  backtest.loggerService.info(STORAGE_LIVE_ADAPTER_METHOD_NAME_USE_MEMORY);
58625
- this._signalLiveUtils = new StorageMemoryLiveUtils();
58703
+ this._signalLiveFactory = () => new StorageMemoryLiveUtils();
58704
+ this.getInstance.clear();
58626
58705
  };
58627
58706
  /**
58628
- * Clears the cached utils instance by resetting to the default persistent adapter.
58707
+ * Clears the memoized utils instance.
58629
58708
  * Call this when process.cwd() changes between strategy iterations
58630
58709
  * so a new instance is created with the updated base path.
58631
58710
  */
58632
58711
  this.clear = () => {
58633
58712
  backtest.loggerService.info(STORAGE_LIVE_ADAPTER_METHOD_NAME_CLEAR);
58634
- this._signalLiveUtils = new StoragePersistLiveUtils();
58713
+ this.getInstance.clear();
58635
58714
  };
58636
58715
  }
58637
58716
  }
@@ -60747,18 +60826,26 @@ class NotificationPersistLiveUtils {
60747
60826
  */
60748
60827
  class NotificationBacktestAdapter {
60749
60828
  constructor() {
60750
- /** Internal notification utils instance */
60751
- this._notificationBacktestUtils = new NotificationMemoryBacktestUtils();
60829
+ /** Factory producing the active notification utils instance */
60830
+ this._notificationBacktestFactory = () => new NotificationMemoryBacktestUtils();
60831
+ /**
60832
+ * Lazily constructs the notification utils from the registered factory and
60833
+ * memoizes the result via `singleshot`.
60834
+ *
60835
+ * The instance is built on the first call and cached for all subsequent calls.
60836
+ * Reset via `clear()` so the next call rebuilds from the current factory.
60837
+ */
60838
+ this.getInstance = functoolsKit.singleshot(() => this._notificationBacktestFactory());
60752
60839
  /**
60753
60840
  * Handles signal events.
60754
60841
  * Proxies call to the underlying notification adapter.
60755
60842
  * @param data - The strategy tick result data
60756
60843
  */
60757
60844
  this.handleSignal = async (data) => {
60758
- return await this._notificationBacktestUtils.handleSignal(data);
60845
+ return await this.getInstance().handleSignal(data);
60759
60846
  };
60760
60847
  this.handleSignalNotify = async (data) => {
60761
- return await this._notificationBacktestUtils.handleSignalNotify(data);
60848
+ return await this.getInstance().handleSignalNotify(data);
60762
60849
  };
60763
60850
  /**
60764
60851
  * Handles partial profit availability event.
@@ -60766,7 +60853,7 @@ class NotificationBacktestAdapter {
60766
60853
  * @param data - The partial profit contract data
60767
60854
  */
60768
60855
  this.handlePartialProfit = async (data) => {
60769
- return await this._notificationBacktestUtils.handlePartialProfit(data);
60856
+ return await this.getInstance().handlePartialProfit(data);
60770
60857
  };
60771
60858
  /**
60772
60859
  * Handles partial loss availability event.
@@ -60774,7 +60861,7 @@ class NotificationBacktestAdapter {
60774
60861
  * @param data - The partial loss contract data
60775
60862
  */
60776
60863
  this.handlePartialLoss = async (data) => {
60777
- return await this._notificationBacktestUtils.handlePartialLoss(data);
60864
+ return await this.getInstance().handlePartialLoss(data);
60778
60865
  };
60779
60866
  /**
60780
60867
  * Handles breakeven availability event.
@@ -60782,7 +60869,7 @@ class NotificationBacktestAdapter {
60782
60869
  * @param data - The breakeven contract data
60783
60870
  */
60784
60871
  this.handleBreakeven = async (data) => {
60785
- return await this._notificationBacktestUtils.handleBreakeven(data);
60872
+ return await this.getInstance().handleBreakeven(data);
60786
60873
  };
60787
60874
  /**
60788
60875
  * Handles strategy commit events.
@@ -60790,7 +60877,7 @@ class NotificationBacktestAdapter {
60790
60877
  * @param data - The strategy commit contract data
60791
60878
  */
60792
60879
  this.handleStrategyCommit = async (data) => {
60793
- return await this._notificationBacktestUtils.handleStrategyCommit(data);
60880
+ return await this.getInstance().handleStrategyCommit(data);
60794
60881
  };
60795
60882
  /**
60796
60883
  * Handles signal sync events (signal-open, signal-close).
@@ -60798,7 +60885,7 @@ class NotificationBacktestAdapter {
60798
60885
  * @param data - The signal sync contract data
60799
60886
  */
60800
60887
  this.handleSync = functoolsKit.trycatch(async (data) => {
60801
- return await this._notificationBacktestUtils.handleSync(data);
60888
+ return await this.getInstance().handleSync(data);
60802
60889
  }, {
60803
60890
  defaultValue: null,
60804
60891
  });
@@ -60808,7 +60895,7 @@ class NotificationBacktestAdapter {
60808
60895
  * @param data - The risk contract data
60809
60896
  */
60810
60897
  this.handleRisk = async (data) => {
60811
- return await this._notificationBacktestUtils.handleRisk(data);
60898
+ return await this.getInstance().handleRisk(data);
60812
60899
  };
60813
60900
  /**
60814
60901
  * Handles error event.
@@ -60816,7 +60903,7 @@ class NotificationBacktestAdapter {
60816
60903
  * @param error - The error object
60817
60904
  */
60818
60905
  this.handleError = async (error) => {
60819
- return await this._notificationBacktestUtils.handleError(error);
60906
+ return await this.getInstance().handleError(error);
60820
60907
  };
60821
60908
  /**
60822
60909
  * Handles critical error event.
@@ -60824,7 +60911,7 @@ class NotificationBacktestAdapter {
60824
60911
  * @param error - The error object
60825
60912
  */
60826
60913
  this.handleCriticalError = async (error) => {
60827
- return await this._notificationBacktestUtils.handleCriticalError(error);
60914
+ return await this.getInstance().handleCriticalError(error);
60828
60915
  };
60829
60916
  /**
60830
60917
  * Handles validation error event.
@@ -60832,7 +60919,7 @@ class NotificationBacktestAdapter {
60832
60919
  * @param error - The error object
60833
60920
  */
60834
60921
  this.handleValidationError = async (error) => {
60835
- return await this._notificationBacktestUtils.handleValidationError(error);
60922
+ return await this.getInstance().handleValidationError(error);
60836
60923
  };
60837
60924
  /**
60838
60925
  * Gets all stored notifications.
@@ -60840,14 +60927,14 @@ class NotificationBacktestAdapter {
60840
60927
  * @returns Array of all notification models
60841
60928
  */
60842
60929
  this.getData = async () => {
60843
- return await this._notificationBacktestUtils.getData();
60930
+ return await this.getInstance().getData();
60844
60931
  };
60845
60932
  /**
60846
60933
  * Clears all stored notifications.
60847
60934
  * Proxies call to the underlying notification adapter.
60848
60935
  */
60849
60936
  this.dispose = async () => {
60850
- return await this._notificationBacktestUtils.dispose();
60937
+ return await this.getInstance().dispose();
60851
60938
  };
60852
60939
  /**
60853
60940
  * Sets the notification adapter constructor.
@@ -60857,7 +60944,8 @@ class NotificationBacktestAdapter {
60857
60944
  */
60858
60945
  this.useNotificationAdapter = (Ctor) => {
60859
60946
  backtest.loggerService.info(NOTIFICATION_BACKTEST_ADAPTER_METHOD_NAME_USE_ADAPTER);
60860
- this._notificationBacktestUtils = Reflect.construct(Ctor, []);
60947
+ this._notificationBacktestFactory = () => Reflect.construct(Ctor, []);
60948
+ this.getInstance.clear();
60861
60949
  };
60862
60950
  /**
60863
60951
  * Switches to dummy notification adapter.
@@ -60865,7 +60953,8 @@ class NotificationBacktestAdapter {
60865
60953
  */
60866
60954
  this.useDummy = () => {
60867
60955
  backtest.loggerService.info(NOTIFICATION_BACKTEST_ADAPTER_METHOD_NAME_USE_DUMMY);
60868
- this._notificationBacktestUtils = new NotificationDummyBacktestUtils();
60956
+ this._notificationBacktestFactory = () => new NotificationDummyBacktestUtils();
60957
+ this.getInstance.clear();
60869
60958
  };
60870
60959
  /**
60871
60960
  * Switches to in-memory notification adapter (default).
@@ -60873,7 +60962,8 @@ class NotificationBacktestAdapter {
60873
60962
  */
60874
60963
  this.useMemory = () => {
60875
60964
  backtest.loggerService.info(NOTIFICATION_BACKTEST_ADAPTER_METHOD_NAME_USE_MEMORY);
60876
- this._notificationBacktestUtils = new NotificationMemoryBacktestUtils();
60965
+ this._notificationBacktestFactory = () => new NotificationMemoryBacktestUtils();
60966
+ this.getInstance.clear();
60877
60967
  };
60878
60968
  /**
60879
60969
  * Switches to persistent notification adapter.
@@ -60881,16 +60971,17 @@ class NotificationBacktestAdapter {
60881
60971
  */
60882
60972
  this.usePersist = () => {
60883
60973
  backtest.loggerService.info(NOTIFICATION_BACKTEST_ADAPTER_METHOD_NAME_USE_PERSIST);
60884
- this._notificationBacktestUtils = new NotificationPersistBacktestUtils();
60974
+ this._notificationBacktestFactory = () => new NotificationPersistBacktestUtils();
60975
+ this.getInstance.clear();
60885
60976
  };
60886
60977
  /**
60887
- * Resets the cached utils instance to the default in-memory adapter.
60978
+ * Clears the memoized utils instance.
60888
60979
  * Call this when process.cwd() changes between strategy iterations
60889
60980
  * so a new instance is created with the updated base path.
60890
60981
  */
60891
60982
  this.clear = () => {
60892
60983
  backtest.loggerService.info(NOTIFICATION_BACKTEST_ADAPTER_METHOD_NAME_CLEAR);
60893
- this._notificationBacktestUtils = new NotificationMemoryBacktestUtils();
60984
+ this.getInstance.clear();
60894
60985
  };
60895
60986
  }
60896
60987
  }
@@ -60905,18 +60996,26 @@ class NotificationBacktestAdapter {
60905
60996
  */
60906
60997
  class NotificationLiveAdapter {
60907
60998
  constructor() {
60908
- /** Internal notification utils instance */
60909
- this._notificationLiveUtils = new NotificationMemoryLiveUtils();
60999
+ /** Factory producing the active notification utils instance */
61000
+ this._notificationLiveFactory = () => new NotificationMemoryLiveUtils();
61001
+ /**
61002
+ * Lazily constructs the notification utils from the registered factory and
61003
+ * memoizes the result via `singleshot`.
61004
+ *
61005
+ * The instance is built on the first call and cached for all subsequent calls.
61006
+ * Reset via `clear()` so the next call rebuilds from the current factory.
61007
+ */
61008
+ this.getInstance = functoolsKit.singleshot(() => this._notificationLiveFactory());
60910
61009
  /**
60911
61010
  * Handles signal events.
60912
61011
  * Proxies call to the underlying notification adapter.
60913
61012
  * @param data - The strategy tick result data
60914
61013
  */
60915
61014
  this.handleSignal = async (data) => {
60916
- return await this._notificationLiveUtils.handleSignal(data);
61015
+ return await this.getInstance().handleSignal(data);
60917
61016
  };
60918
61017
  this.handleSignalNotify = async (data) => {
60919
- return await this._notificationLiveUtils.handleSignalNotify(data);
61018
+ return await this.getInstance().handleSignalNotify(data);
60920
61019
  };
60921
61020
  /**
60922
61021
  * Handles partial profit availability event.
@@ -60924,7 +61023,7 @@ class NotificationLiveAdapter {
60924
61023
  * @param data - The partial profit contract data
60925
61024
  */
60926
61025
  this.handlePartialProfit = async (data) => {
60927
- return await this._notificationLiveUtils.handlePartialProfit(data);
61026
+ return await this.getInstance().handlePartialProfit(data);
60928
61027
  };
60929
61028
  /**
60930
61029
  * Handles partial loss availability event.
@@ -60932,7 +61031,7 @@ class NotificationLiveAdapter {
60932
61031
  * @param data - The partial loss contract data
60933
61032
  */
60934
61033
  this.handlePartialLoss = async (data) => {
60935
- return await this._notificationLiveUtils.handlePartialLoss(data);
61034
+ return await this.getInstance().handlePartialLoss(data);
60936
61035
  };
60937
61036
  /**
60938
61037
  * Handles breakeven availability event.
@@ -60940,7 +61039,7 @@ class NotificationLiveAdapter {
60940
61039
  * @param data - The breakeven contract data
60941
61040
  */
60942
61041
  this.handleBreakeven = async (data) => {
60943
- return await this._notificationLiveUtils.handleBreakeven(data);
61042
+ return await this.getInstance().handleBreakeven(data);
60944
61043
  };
60945
61044
  /**
60946
61045
  * Handles strategy commit events.
@@ -60948,7 +61047,7 @@ class NotificationLiveAdapter {
60948
61047
  * @param data - The strategy commit contract data
60949
61048
  */
60950
61049
  this.handleStrategyCommit = async (data) => {
60951
- return await this._notificationLiveUtils.handleStrategyCommit(data);
61050
+ return await this.getInstance().handleStrategyCommit(data);
60952
61051
  };
60953
61052
  /**
60954
61053
  * Handles signal sync events (signal-open, signal-close).
@@ -60956,7 +61055,7 @@ class NotificationLiveAdapter {
60956
61055
  * @param data - The signal sync contract data
60957
61056
  */
60958
61057
  this.handleSync = functoolsKit.trycatch(async (data) => {
60959
- return await this._notificationLiveUtils.handleSync(data);
61058
+ return await this.getInstance().handleSync(data);
60960
61059
  }, {
60961
61060
  defaultValue: null,
60962
61061
  });
@@ -60966,7 +61065,7 @@ class NotificationLiveAdapter {
60966
61065
  * @param data - The risk contract data
60967
61066
  */
60968
61067
  this.handleRisk = async (data) => {
60969
- return await this._notificationLiveUtils.handleRisk(data);
61068
+ return await this.getInstance().handleRisk(data);
60970
61069
  };
60971
61070
  /**
60972
61071
  * Handles error event.
@@ -60974,7 +61073,7 @@ class NotificationLiveAdapter {
60974
61073
  * @param error - The error object
60975
61074
  */
60976
61075
  this.handleError = async (error) => {
60977
- return await this._notificationLiveUtils.handleError(error);
61076
+ return await this.getInstance().handleError(error);
60978
61077
  };
60979
61078
  /**
60980
61079
  * Handles critical error event.
@@ -60982,7 +61081,7 @@ class NotificationLiveAdapter {
60982
61081
  * @param error - The error object
60983
61082
  */
60984
61083
  this.handleCriticalError = async (error) => {
60985
- return await this._notificationLiveUtils.handleCriticalError(error);
61084
+ return await this.getInstance().handleCriticalError(error);
60986
61085
  };
60987
61086
  /**
60988
61087
  * Handles validation error event.
@@ -60990,7 +61089,7 @@ class NotificationLiveAdapter {
60990
61089
  * @param error - The error object
60991
61090
  */
60992
61091
  this.handleValidationError = async (error) => {
60993
- return await this._notificationLiveUtils.handleValidationError(error);
61092
+ return await this.getInstance().handleValidationError(error);
60994
61093
  };
60995
61094
  /**
60996
61095
  * Gets all stored notifications.
@@ -60998,14 +61097,14 @@ class NotificationLiveAdapter {
60998
61097
  * @returns Array of all notification models
60999
61098
  */
61000
61099
  this.getData = async () => {
61001
- return await this._notificationLiveUtils.getData();
61100
+ return await this.getInstance().getData();
61002
61101
  };
61003
61102
  /**
61004
61103
  * Clears all stored notifications.
61005
61104
  * Proxies call to the underlying notification adapter.
61006
61105
  */
61007
61106
  this.dispose = async () => {
61008
- return await this._notificationLiveUtils.dispose();
61107
+ return await this.getInstance().dispose();
61009
61108
  };
61010
61109
  /**
61011
61110
  * Sets the notification adapter constructor.
@@ -61015,7 +61114,8 @@ class NotificationLiveAdapter {
61015
61114
  */
61016
61115
  this.useNotificationAdapter = (Ctor) => {
61017
61116
  backtest.loggerService.info(NOTIFICATION_LIVE_ADAPTER_METHOD_NAME_USE_ADAPTER);
61018
- this._notificationLiveUtils = Reflect.construct(Ctor, []);
61117
+ this._notificationLiveFactory = () => Reflect.construct(Ctor, []);
61118
+ this.getInstance.clear();
61019
61119
  };
61020
61120
  /**
61021
61121
  * Switches to dummy notification adapter.
@@ -61023,7 +61123,8 @@ class NotificationLiveAdapter {
61023
61123
  */
61024
61124
  this.useDummy = () => {
61025
61125
  backtest.loggerService.info(NOTIFICATION_LIVE_ADAPTER_METHOD_NAME_USE_DUMMY);
61026
- this._notificationLiveUtils = new NotificationDummyLiveUtils();
61126
+ this._notificationLiveFactory = () => new NotificationDummyLiveUtils();
61127
+ this.getInstance.clear();
61027
61128
  };
61028
61129
  /**
61029
61130
  * Switches to in-memory notification adapter (default).
@@ -61031,7 +61132,8 @@ class NotificationLiveAdapter {
61031
61132
  */
61032
61133
  this.useMemory = () => {
61033
61134
  backtest.loggerService.info(NOTIFICATION_LIVE_ADAPTER_METHOD_NAME_USE_MEMORY);
61034
- this._notificationLiveUtils = new NotificationMemoryLiveUtils();
61135
+ this._notificationLiveFactory = () => new NotificationMemoryLiveUtils();
61136
+ this.getInstance.clear();
61035
61137
  };
61036
61138
  /**
61037
61139
  * Switches to persistent notification adapter.
@@ -61039,16 +61141,17 @@ class NotificationLiveAdapter {
61039
61141
  */
61040
61142
  this.usePersist = () => {
61041
61143
  backtest.loggerService.info(NOTIFICATION_LIVE_ADAPTER_METHOD_NAME_USE_PERSIST);
61042
- this._notificationLiveUtils = new NotificationPersistLiveUtils();
61144
+ this._notificationLiveFactory = () => new NotificationPersistLiveUtils();
61145
+ this.getInstance.clear();
61043
61146
  };
61044
61147
  /**
61045
- * Resets the cached utils instance to the default in-memory adapter.
61148
+ * Clears the memoized utils instance.
61046
61149
  * Call this when process.cwd() changes between strategy iterations
61047
61150
  * so a new instance is created with the updated base path.
61048
61151
  */
61049
61152
  this.clear = () => {
61050
61153
  backtest.loggerService.info(NOTIFICATION_LIVE_ADAPTER_METHOD_NAME_CLEAR);
61051
- this._notificationLiveUtils = new NotificationMemoryLiveUtils();
61154
+ this.getInstance.clear();
61052
61155
  };
61053
61156
  }
61054
61157
  }