backtest-kit 6.12.0 → 6.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.mjs CHANGED
@@ -982,6 +982,12 @@ const PERSIST_MEMORY_UTILS_METHOD_NAME_LIST_DATA = "PersistMemoryUtils.listMemor
982
982
  const PERSIST_MEMORY_UTILS_METHOD_NAME_HAS_DATA = "PersistMemoryUtils.hasMemoryData";
983
983
  const PERSIST_MEMORY_UTILS_METHOD_NAME_CLEAR = "PersistMemoryUtils.clear";
984
984
  const PERSIST_MEMORY_UTILS_METHOD_NAME_DISPOSE = "PersistMemoryUtils.dispose";
985
+ const PERSIST_RECENT_UTILS_METHOD_NAME_USE_PERSIST_RECENT_ADAPTER = "PersistRecentUtils.usePersistRecentAdapter";
986
+ const PERSIST_RECENT_UTILS_METHOD_NAME_READ_DATA = "PersistRecentUtils.readRecentData";
987
+ const PERSIST_RECENT_UTILS_METHOD_NAME_WRITE_DATA = "PersistRecentUtils.writeRecentData";
988
+ const PERSIST_RECENT_UTILS_METHOD_NAME_USE_JSON = "PersistRecentUtils.useJson";
989
+ const PERSIST_RECENT_UTILS_METHOD_NAME_USE_DUMMY = "PersistRecentUtils.useDummy";
990
+ const PERSIST_RECENT_UTILS_METHOD_NAME_CLEAR = "PersistRecentUtils.clear";
985
991
  const BASE_WAIT_FOR_INIT_FN_METHOD_NAME = "PersistBase.waitForInitFn";
986
992
  const BASE_UNLINK_RETRY_COUNT = 5;
987
993
  const BASE_UNLINK_RETRY_DELAY = 1000;
@@ -2828,6 +2834,115 @@ class PersistMemoryUtils {
2828
2834
  * ```
2829
2835
  */
2830
2836
  const PersistMemoryAdapter = new PersistMemoryUtils();
2837
+ /**
2838
+ * Utility class for managing recent signal persistence.
2839
+ *
2840
+ * Features:
2841
+ * - Memoized storage instances per (symbol, strategyName, exchangeName, frameName) context
2842
+ * - Custom adapter support
2843
+ * - Atomic read/write operations
2844
+ * - Crash-safe recent signal state management
2845
+ *
2846
+ * Used by RecentPersistBacktestUtils/RecentPersistLiveUtils for recent signal persistence.
2847
+ */
2848
+ class PersistRecentUtils {
2849
+ constructor() {
2850
+ this.PersistRecentFactory = PersistBase;
2851
+ this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => this.createKeyParts(symbol, strategyName, exchangeName, frameName, backtest).join(":"), (symbol, strategyName, exchangeName, frameName, backtest) => Reflect.construct(this.PersistRecentFactory, [
2852
+ this.createKeyParts(symbol, strategyName, exchangeName, frameName, backtest).join("_"),
2853
+ `./dump/data/recent/`,
2854
+ ]));
2855
+ /**
2856
+ * Reads the latest persisted recent signal for a given context.
2857
+ *
2858
+ * Returns null if no recent signal exists.
2859
+ *
2860
+ * @param symbol - Trading pair symbol
2861
+ * @param strategyName - Strategy identifier
2862
+ * @param exchangeName - Exchange identifier
2863
+ * @param frameName - Frame identifier
2864
+ * @returns Promise resolving to recent signal or null
2865
+ */
2866
+ this.readRecentData = async (symbol, strategyName, exchangeName, frameName, backtest) => {
2867
+ LOGGER_SERVICE$7.info(PERSIST_RECENT_UTILS_METHOD_NAME_READ_DATA);
2868
+ const key = this.createKeyParts(symbol, strategyName, exchangeName, frameName, backtest).join(":");
2869
+ const isInitial = !this.getStorage.has(key);
2870
+ const stateStorage = this.getStorage(symbol, strategyName, exchangeName, frameName, backtest);
2871
+ await stateStorage.waitForInit(isInitial);
2872
+ if (await stateStorage.hasValue(symbol)) {
2873
+ return await stateStorage.readValue(symbol);
2874
+ }
2875
+ return null;
2876
+ };
2877
+ /**
2878
+ * Writes the latest recent signal to disk with atomic file writes.
2879
+ *
2880
+ * Uses symbol as the entity ID within the per-context storage instance.
2881
+ * Uses atomic writes to prevent corruption on crashes.
2882
+ *
2883
+ * @param signalRow - Recent signal data to persist
2884
+ * @param symbol - Trading pair symbol
2885
+ * @param strategyName - Strategy identifier
2886
+ * @param exchangeName - Exchange identifier
2887
+ * @param frameName - Frame identifier
2888
+ * @returns Promise that resolves when write is complete
2889
+ */
2890
+ this.writeRecentData = async (signalRow, symbol, strategyName, exchangeName, frameName, backtest) => {
2891
+ LOGGER_SERVICE$7.info(PERSIST_RECENT_UTILS_METHOD_NAME_WRITE_DATA);
2892
+ const key = this.createKeyParts(symbol, strategyName, exchangeName, frameName, backtest).join(":");
2893
+ const isInitial = !this.getStorage.has(key);
2894
+ const stateStorage = this.getStorage(symbol, strategyName, exchangeName, frameName, backtest);
2895
+ await stateStorage.waitForInit(isInitial);
2896
+ await stateStorage.writeValue(symbol, signalRow);
2897
+ };
2898
+ }
2899
+ createKeyParts(symbol, strategyName, exchangeName, frameName, backtest) {
2900
+ const parts = [symbol, strategyName, exchangeName];
2901
+ if (frameName)
2902
+ parts.push(frameName);
2903
+ parts.push(backtest ? "backtest" : "live");
2904
+ return parts;
2905
+ }
2906
+ /**
2907
+ * Registers a custom persistence adapter.
2908
+ *
2909
+ * @param Ctor - Custom PersistBase constructor
2910
+ */
2911
+ usePersistRecentAdapter(Ctor) {
2912
+ LOGGER_SERVICE$7.info(PERSIST_RECENT_UTILS_METHOD_NAME_USE_PERSIST_RECENT_ADAPTER);
2913
+ this.PersistRecentFactory = Ctor;
2914
+ }
2915
+ /**
2916
+ * Clears the memoized storage cache.
2917
+ * Call this when process.cwd() changes between strategy iterations
2918
+ * so new storage instances are created with the updated base path.
2919
+ */
2920
+ clear() {
2921
+ LOGGER_SERVICE$7.log(PERSIST_RECENT_UTILS_METHOD_NAME_CLEAR);
2922
+ this.getStorage.clear();
2923
+ }
2924
+ /**
2925
+ * Switches to the default JSON persist adapter.
2926
+ * All future persistence writes will use JSON storage.
2927
+ */
2928
+ useJson() {
2929
+ LOGGER_SERVICE$7.log(PERSIST_RECENT_UTILS_METHOD_NAME_USE_JSON);
2930
+ this.usePersistRecentAdapter(PersistBase);
2931
+ }
2932
+ /**
2933
+ * Switches to a dummy persist adapter that discards all writes.
2934
+ * All future persistence writes will be no-ops.
2935
+ */
2936
+ useDummy() {
2937
+ LOGGER_SERVICE$7.log(PERSIST_RECENT_UTILS_METHOD_NAME_USE_DUMMY);
2938
+ this.usePersistRecentAdapter(PersistDummy);
2939
+ }
2940
+ }
2941
+ /**
2942
+ * Global singleton instance of PersistRecentUtils.
2943
+ * Used by RecentPersistBacktestUtils/RecentPersistLiveUtils for recent signal persistence.
2944
+ */
2945
+ const PersistRecentAdapter = new PersistRecentUtils();
2831
2946
 
2832
2947
  var _a$2, _b$2;
2833
2948
  const BUSY_DELAY = 100;
@@ -10155,7 +10270,7 @@ const GET_RISK_FN = (dto, backtest, exchangeName, frameName, self) => {
10155
10270
  * @param backtest - Whether running in backtest mode
10156
10271
  * @returns Unique string key for memoization
10157
10272
  */
10158
- const CREATE_KEY_FN$t = (symbol, strategyName, exchangeName, frameName, backtest) => {
10273
+ const CREATE_KEY_FN$u = (symbol, strategyName, exchangeName, frameName, backtest) => {
10159
10274
  const parts = [symbol, strategyName, exchangeName];
10160
10275
  if (frameName)
10161
10276
  parts.push(frameName);
@@ -10422,7 +10537,7 @@ class StrategyConnectionService {
10422
10537
  * @param backtest - Whether running in backtest mode
10423
10538
  * @returns Configured ClientStrategy instance
10424
10539
  */
10425
- this.getStrategy = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$t(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => {
10540
+ this.getStrategy = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$u(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => {
10426
10541
  const { riskName = "", riskList = [], getSignal, interval = STRATEGY_DEFAULT_INTERVAL, callbacks, } = this.strategySchemaService.get(strategyName);
10427
10542
  return new ClientStrategy({
10428
10543
  symbol,
@@ -11343,7 +11458,7 @@ class StrategyConnectionService {
11343
11458
  }
11344
11459
  return;
11345
11460
  }
11346
- const key = CREATE_KEY_FN$t(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
11461
+ const key = CREATE_KEY_FN$u(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
11347
11462
  if (!this.getStrategy.has(key)) {
11348
11463
  return;
11349
11464
  }
@@ -12512,7 +12627,7 @@ class ClientRisk {
12512
12627
  * @param backtest - Whether running in backtest mode
12513
12628
  * @returns Unique string key for memoization
12514
12629
  */
12515
- const CREATE_KEY_FN$s = (riskName, exchangeName, frameName, backtest) => {
12630
+ const CREATE_KEY_FN$t = (riskName, exchangeName, frameName, backtest) => {
12516
12631
  const parts = [riskName, exchangeName];
12517
12632
  if (frameName)
12518
12633
  parts.push(frameName);
@@ -12612,7 +12727,7 @@ class RiskConnectionService {
12612
12727
  * @param backtest - True if backtest mode, false if live mode
12613
12728
  * @returns Configured ClientRisk instance
12614
12729
  */
12615
- this.getRisk = memoize(([riskName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$s(riskName, exchangeName, frameName, backtest), (riskName, exchangeName, frameName, backtest) => {
12730
+ this.getRisk = memoize(([riskName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$t(riskName, exchangeName, frameName, backtest), (riskName, exchangeName, frameName, backtest) => {
12616
12731
  const schema = this.riskSchemaService.get(riskName);
12617
12732
  return new ClientRisk({
12618
12733
  ...schema,
@@ -12681,7 +12796,7 @@ class RiskConnectionService {
12681
12796
  payload,
12682
12797
  });
12683
12798
  if (payload) {
12684
- const key = CREATE_KEY_FN$s(payload.riskName, payload.exchangeName, payload.frameName, payload.backtest);
12799
+ const key = CREATE_KEY_FN$t(payload.riskName, payload.exchangeName, payload.frameName, payload.backtest);
12685
12800
  this.getRisk.clear(key);
12686
12801
  }
12687
12802
  else {
@@ -13725,7 +13840,7 @@ class ClientAction {
13725
13840
  * @param backtest - Whether running in backtest mode
13726
13841
  * @returns Unique string key for memoization
13727
13842
  */
13728
- const CREATE_KEY_FN$r = (actionName, strategyName, exchangeName, frameName, backtest) => {
13843
+ const CREATE_KEY_FN$s = (actionName, strategyName, exchangeName, frameName, backtest) => {
13729
13844
  const parts = [actionName, strategyName, exchangeName];
13730
13845
  if (frameName)
13731
13846
  parts.push(frameName);
@@ -13777,7 +13892,7 @@ class ActionConnectionService {
13777
13892
  * @param backtest - True if backtest mode, false if live mode
13778
13893
  * @returns Configured ClientAction instance
13779
13894
  */
13780
- this.getAction = memoize(([actionName, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$r(actionName, strategyName, exchangeName, frameName, backtest), (actionName, strategyName, exchangeName, frameName, backtest) => {
13895
+ this.getAction = memoize(([actionName, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$s(actionName, strategyName, exchangeName, frameName, backtest), (actionName, strategyName, exchangeName, frameName, backtest) => {
13781
13896
  const schema = this.actionSchemaService.get(actionName);
13782
13897
  return new ClientAction({
13783
13898
  ...schema,
@@ -13988,7 +14103,7 @@ class ActionConnectionService {
13988
14103
  await Promise.all(actions.map(async (action) => await action.dispose()));
13989
14104
  return;
13990
14105
  }
13991
- const key = CREATE_KEY_FN$r(payload.actionName, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
14106
+ const key = CREATE_KEY_FN$s(payload.actionName, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
13992
14107
  if (!this.getAction.has(key)) {
13993
14108
  return;
13994
14109
  }
@@ -14006,7 +14121,7 @@ const METHOD_NAME_VALIDATE$2 = "exchangeCoreService validate";
14006
14121
  * @param exchangeName - Exchange name
14007
14122
  * @returns Unique string key for memoization
14008
14123
  */
14009
- const CREATE_KEY_FN$q = (exchangeName) => {
14124
+ const CREATE_KEY_FN$r = (exchangeName) => {
14010
14125
  return exchangeName;
14011
14126
  };
14012
14127
  /**
@@ -14030,7 +14145,7 @@ class ExchangeCoreService {
14030
14145
  * @param exchangeName - Name of the exchange to validate
14031
14146
  * @returns Promise that resolves when validation is complete
14032
14147
  */
14033
- this.validate = memoize(([exchangeName]) => CREATE_KEY_FN$q(exchangeName), async (exchangeName) => {
14148
+ this.validate = memoize(([exchangeName]) => CREATE_KEY_FN$r(exchangeName), async (exchangeName) => {
14034
14149
  this.loggerService.log(METHOD_NAME_VALIDATE$2, {
14035
14150
  exchangeName,
14036
14151
  });
@@ -14282,7 +14397,7 @@ const METHOD_NAME_VALIDATE$1 = "strategyCoreService validate";
14282
14397
  * @param context - Execution context with strategyName, exchangeName, frameName
14283
14398
  * @returns Unique string key for memoization
14284
14399
  */
14285
- const CREATE_KEY_FN$p = (context) => {
14400
+ const CREATE_KEY_FN$q = (context) => {
14286
14401
  const parts = [context.strategyName, context.exchangeName];
14287
14402
  if (context.frameName)
14288
14403
  parts.push(context.frameName);
@@ -14314,7 +14429,7 @@ class StrategyCoreService {
14314
14429
  * @param context - Execution context with strategyName, exchangeName, frameName
14315
14430
  * @returns Promise that resolves when validation is complete
14316
14431
  */
14317
- this.validate = memoize(([context]) => CREATE_KEY_FN$p(context), async (context) => {
14432
+ this.validate = memoize(([context]) => CREATE_KEY_FN$q(context), async (context) => {
14318
14433
  this.loggerService.log(METHOD_NAME_VALIDATE$1, {
14319
14434
  context,
14320
14435
  });
@@ -15652,7 +15767,7 @@ class SizingGlobalService {
15652
15767
  * @param context - Context with riskName, exchangeName, frameName
15653
15768
  * @returns Unique string key for memoization
15654
15769
  */
15655
- const CREATE_KEY_FN$o = (context) => {
15770
+ const CREATE_KEY_FN$p = (context) => {
15656
15771
  const parts = [context.riskName, context.exchangeName];
15657
15772
  if (context.frameName)
15658
15773
  parts.push(context.frameName);
@@ -15678,7 +15793,7 @@ class RiskGlobalService {
15678
15793
  * @param payload - Payload with riskName, exchangeName and frameName
15679
15794
  * @returns Promise that resolves when validation is complete
15680
15795
  */
15681
- this.validate = memoize(([context]) => CREATE_KEY_FN$o(context), async (context) => {
15796
+ this.validate = memoize(([context]) => CREATE_KEY_FN$p(context), async (context) => {
15682
15797
  this.loggerService.log("riskGlobalService validate", {
15683
15798
  context,
15684
15799
  });
@@ -15756,7 +15871,7 @@ const METHOD_NAME_VALIDATE = "actionCoreService validate";
15756
15871
  * @param context - Execution context with strategyName, exchangeName, frameName
15757
15872
  * @returns Unique string key for memoization
15758
15873
  */
15759
- const CREATE_KEY_FN$n = (context) => {
15874
+ const CREATE_KEY_FN$o = (context) => {
15760
15875
  const parts = [context.strategyName, context.exchangeName];
15761
15876
  if (context.frameName)
15762
15877
  parts.push(context.frameName);
@@ -15800,7 +15915,7 @@ class ActionCoreService {
15800
15915
  * @param context - Strategy execution context with strategyName, exchangeName and frameName
15801
15916
  * @returns Promise that resolves when all validations complete
15802
15917
  */
15803
- this.validate = memoize(([context]) => CREATE_KEY_FN$n(context), async (context) => {
15918
+ this.validate = memoize(([context]) => CREATE_KEY_FN$o(context), async (context) => {
15804
15919
  this.loggerService.log(METHOD_NAME_VALIDATE, {
15805
15920
  context,
15806
15921
  });
@@ -20843,7 +20958,7 @@ const ReportWriter = new ReportWriterAdapter();
20843
20958
  * @param backtest - Whether running in backtest mode
20844
20959
  * @returns Unique string key for memoization
20845
20960
  */
20846
- const CREATE_KEY_FN$m = (symbol, strategyName, exchangeName, frameName, backtest) => {
20961
+ const CREATE_KEY_FN$n = (symbol, strategyName, exchangeName, frameName, backtest) => {
20847
20962
  const parts = [symbol, strategyName, exchangeName];
20848
20963
  if (frameName)
20849
20964
  parts.push(frameName);
@@ -21089,7 +21204,7 @@ class BacktestMarkdownService {
21089
21204
  * Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
21090
21205
  * Each combination gets its own isolated storage instance.
21091
21206
  */
21092
- this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$m(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$a(symbol, strategyName, exchangeName, frameName));
21207
+ this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$n(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$a(symbol, strategyName, exchangeName, frameName));
21093
21208
  /**
21094
21209
  * Processes tick events and accumulates closed signals.
21095
21210
  * Should be called from IStrategyCallbacks.onTick.
@@ -21246,7 +21361,7 @@ class BacktestMarkdownService {
21246
21361
  payload,
21247
21362
  });
21248
21363
  if (payload) {
21249
- const key = CREATE_KEY_FN$m(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
21364
+ const key = CREATE_KEY_FN$n(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
21250
21365
  this.getStorage.clear(key);
21251
21366
  }
21252
21367
  else {
@@ -21308,7 +21423,7 @@ class BacktestMarkdownService {
21308
21423
  * @param backtest - Whether running in backtest mode
21309
21424
  * @returns Unique string key for memoization
21310
21425
  */
21311
- const CREATE_KEY_FN$l = (symbol, strategyName, exchangeName, frameName, backtest) => {
21426
+ const CREATE_KEY_FN$m = (symbol, strategyName, exchangeName, frameName, backtest) => {
21312
21427
  const parts = [symbol, strategyName, exchangeName];
21313
21428
  if (frameName)
21314
21429
  parts.push(frameName);
@@ -21803,7 +21918,7 @@ class LiveMarkdownService {
21803
21918
  * Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
21804
21919
  * Each combination gets its own isolated storage instance.
21805
21920
  */
21806
- this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$l(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$9(symbol, strategyName, exchangeName, frameName));
21921
+ this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$m(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$9(symbol, strategyName, exchangeName, frameName));
21807
21922
  /**
21808
21923
  * Subscribes to live signal emitter to receive tick events.
21809
21924
  * Protected against multiple subscriptions.
@@ -22021,7 +22136,7 @@ class LiveMarkdownService {
22021
22136
  payload,
22022
22137
  });
22023
22138
  if (payload) {
22024
- const key = CREATE_KEY_FN$l(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
22139
+ const key = CREATE_KEY_FN$m(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
22025
22140
  this.getStorage.clear(key);
22026
22141
  }
22027
22142
  else {
@@ -22041,7 +22156,7 @@ class LiveMarkdownService {
22041
22156
  * @param backtest - Whether running in backtest mode
22042
22157
  * @returns Unique string key for memoization
22043
22158
  */
22044
- const CREATE_KEY_FN$k = (symbol, strategyName, exchangeName, frameName, backtest) => {
22159
+ const CREATE_KEY_FN$l = (symbol, strategyName, exchangeName, frameName, backtest) => {
22045
22160
  const parts = [symbol, strategyName, exchangeName];
22046
22161
  if (frameName)
22047
22162
  parts.push(frameName);
@@ -22330,7 +22445,7 @@ class ScheduleMarkdownService {
22330
22445
  * Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
22331
22446
  * Each combination gets its own isolated storage instance.
22332
22447
  */
22333
- this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$k(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$8(symbol, strategyName, exchangeName, frameName));
22448
+ this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$l(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$8(symbol, strategyName, exchangeName, frameName));
22334
22449
  /**
22335
22450
  * Subscribes to signal emitter to receive scheduled signal events.
22336
22451
  * Protected against multiple subscriptions.
@@ -22533,7 +22648,7 @@ class ScheduleMarkdownService {
22533
22648
  payload,
22534
22649
  });
22535
22650
  if (payload) {
22536
- const key = CREATE_KEY_FN$k(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
22651
+ const key = CREATE_KEY_FN$l(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
22537
22652
  this.getStorage.clear(key);
22538
22653
  }
22539
22654
  else {
@@ -22553,7 +22668,7 @@ class ScheduleMarkdownService {
22553
22668
  * @param backtest - Whether running in backtest mode
22554
22669
  * @returns Unique string key for memoization
22555
22670
  */
22556
- const CREATE_KEY_FN$j = (symbol, strategyName, exchangeName, frameName, backtest) => {
22671
+ const CREATE_KEY_FN$k = (symbol, strategyName, exchangeName, frameName, backtest) => {
22557
22672
  const parts = [symbol, strategyName, exchangeName];
22558
22673
  if (frameName)
22559
22674
  parts.push(frameName);
@@ -22798,7 +22913,7 @@ class PerformanceMarkdownService {
22798
22913
  * Memoized function to get or create PerformanceStorage for a symbol-strategy-exchange-frame-backtest combination.
22799
22914
  * Each combination gets its own isolated storage instance.
22800
22915
  */
22801
- this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$j(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new PerformanceStorage(symbol, strategyName, exchangeName, frameName));
22916
+ this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$k(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new PerformanceStorage(symbol, strategyName, exchangeName, frameName));
22802
22917
  /**
22803
22918
  * Subscribes to performance emitter to receive performance events.
22804
22919
  * Protected against multiple subscriptions.
@@ -22965,7 +23080,7 @@ class PerformanceMarkdownService {
22965
23080
  payload,
22966
23081
  });
22967
23082
  if (payload) {
22968
- const key = CREATE_KEY_FN$j(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
23083
+ const key = CREATE_KEY_FN$k(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
22969
23084
  this.getStorage.clear(key);
22970
23085
  }
22971
23086
  else {
@@ -23444,7 +23559,7 @@ class WalkerMarkdownService {
23444
23559
  * @param backtest - Whether running in backtest mode
23445
23560
  * @returns Unique string key for memoization
23446
23561
  */
23447
- const CREATE_KEY_FN$i = (exchangeName, frameName, backtest) => {
23562
+ const CREATE_KEY_FN$j = (exchangeName, frameName, backtest) => {
23448
23563
  const parts = [exchangeName];
23449
23564
  if (frameName)
23450
23565
  parts.push(frameName);
@@ -23891,7 +24006,7 @@ class HeatMarkdownService {
23891
24006
  * Memoized function to get or create HeatmapStorage for exchange, frame and backtest mode.
23892
24007
  * Each exchangeName + frameName + backtest mode combination gets its own isolated heatmap storage instance.
23893
24008
  */
23894
- this.getStorage = memoize(([exchangeName, frameName, backtest]) => CREATE_KEY_FN$i(exchangeName, frameName, backtest), (exchangeName, frameName, backtest) => new HeatmapStorage(exchangeName, frameName, backtest));
24009
+ this.getStorage = memoize(([exchangeName, frameName, backtest]) => CREATE_KEY_FN$j(exchangeName, frameName, backtest), (exchangeName, frameName, backtest) => new HeatmapStorage(exchangeName, frameName, backtest));
23895
24010
  /**
23896
24011
  * Subscribes to signal emitter to receive tick events.
23897
24012
  * Protected against multiple subscriptions.
@@ -24109,7 +24224,7 @@ class HeatMarkdownService {
24109
24224
  payload,
24110
24225
  });
24111
24226
  if (payload) {
24112
- const key = CREATE_KEY_FN$i(payload.exchangeName, payload.frameName, payload.backtest);
24227
+ const key = CREATE_KEY_FN$j(payload.exchangeName, payload.frameName, payload.backtest);
24113
24228
  this.getStorage.clear(key);
24114
24229
  }
24115
24230
  else {
@@ -25140,7 +25255,7 @@ class ClientPartial {
25140
25255
  * @param backtest - Whether running in backtest mode
25141
25256
  * @returns Unique string key for memoization
25142
25257
  */
25143
- const CREATE_KEY_FN$h = (signalId, backtest) => `${signalId}:${backtest ? "backtest" : "live"}`;
25258
+ const CREATE_KEY_FN$i = (signalId, backtest) => `${signalId}:${backtest ? "backtest" : "live"}`;
25144
25259
  /**
25145
25260
  * Creates a callback function for emitting profit events to partialProfitSubject.
25146
25261
  *
@@ -25262,7 +25377,7 @@ class PartialConnectionService {
25262
25377
  * Key format: "signalId:backtest" or "signalId:live"
25263
25378
  * Value: ClientPartial instance with logger and event emitters
25264
25379
  */
25265
- this.getPartial = memoize(([signalId, backtest]) => CREATE_KEY_FN$h(signalId, backtest), (signalId, backtest) => {
25380
+ this.getPartial = memoize(([signalId, backtest]) => CREATE_KEY_FN$i(signalId, backtest), (signalId, backtest) => {
25266
25381
  return new ClientPartial({
25267
25382
  signalId,
25268
25383
  logger: this.loggerService,
@@ -25352,7 +25467,7 @@ class PartialConnectionService {
25352
25467
  const partial = this.getPartial(data.id, backtest);
25353
25468
  await partial.waitForInit(symbol, data.strategyName, data.exchangeName, backtest);
25354
25469
  await partial.clear(symbol, data, priceClose, backtest);
25355
- const key = CREATE_KEY_FN$h(data.id, backtest);
25470
+ const key = CREATE_KEY_FN$i(data.id, backtest);
25356
25471
  this.getPartial.clear(key);
25357
25472
  };
25358
25473
  }
@@ -25368,7 +25483,7 @@ class PartialConnectionService {
25368
25483
  * @param backtest - Whether running in backtest mode
25369
25484
  * @returns Unique string key for memoization
25370
25485
  */
25371
- const CREATE_KEY_FN$g = (symbol, strategyName, exchangeName, frameName, backtest) => {
25486
+ const CREATE_KEY_FN$h = (symbol, strategyName, exchangeName, frameName, backtest) => {
25372
25487
  const parts = [symbol, strategyName, exchangeName];
25373
25488
  if (frameName)
25374
25489
  parts.push(frameName);
@@ -25591,7 +25706,7 @@ class PartialMarkdownService {
25591
25706
  * Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
25592
25707
  * Each combination gets its own isolated storage instance.
25593
25708
  */
25594
- this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$g(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$6(symbol, strategyName, exchangeName, frameName));
25709
+ this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$h(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$6(symbol, strategyName, exchangeName, frameName));
25595
25710
  /**
25596
25711
  * Subscribes to partial profit/loss signal emitters to receive events.
25597
25712
  * Protected against multiple subscriptions.
@@ -25801,7 +25916,7 @@ class PartialMarkdownService {
25801
25916
  payload,
25802
25917
  });
25803
25918
  if (payload) {
25804
- const key = CREATE_KEY_FN$g(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
25919
+ const key = CREATE_KEY_FN$h(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
25805
25920
  this.getStorage.clear(key);
25806
25921
  }
25807
25922
  else {
@@ -25817,7 +25932,7 @@ class PartialMarkdownService {
25817
25932
  * @param context - Context with strategyName, exchangeName, frameName
25818
25933
  * @returns Unique string key for memoization
25819
25934
  */
25820
- const CREATE_KEY_FN$f = (context) => {
25935
+ const CREATE_KEY_FN$g = (context) => {
25821
25936
  const parts = [context.strategyName, context.exchangeName];
25822
25937
  if (context.frameName)
25823
25938
  parts.push(context.frameName);
@@ -25891,7 +26006,7 @@ class PartialGlobalService {
25891
26006
  * @param context - Context with strategyName, exchangeName and frameName
25892
26007
  * @param methodName - Name of the calling method for error tracking
25893
26008
  */
25894
- this.validate = memoize(([context]) => CREATE_KEY_FN$f(context), (context, methodName) => {
26009
+ this.validate = memoize(([context]) => CREATE_KEY_FN$g(context), (context, methodName) => {
25895
26010
  this.loggerService.log("partialGlobalService validate", {
25896
26011
  context,
25897
26012
  methodName,
@@ -26346,7 +26461,7 @@ class ClientBreakeven {
26346
26461
  * @param backtest - Whether running in backtest mode
26347
26462
  * @returns Unique string key for memoization
26348
26463
  */
26349
- const CREATE_KEY_FN$e = (signalId, backtest) => `${signalId}:${backtest ? "backtest" : "live"}`;
26464
+ const CREATE_KEY_FN$f = (signalId, backtest) => `${signalId}:${backtest ? "backtest" : "live"}`;
26350
26465
  /**
26351
26466
  * Creates a callback function for emitting breakeven events to breakevenSubject.
26352
26467
  *
@@ -26432,7 +26547,7 @@ class BreakevenConnectionService {
26432
26547
  * Key format: "signalId:backtest" or "signalId:live"
26433
26548
  * Value: ClientBreakeven instance with logger and event emitter
26434
26549
  */
26435
- this.getBreakeven = memoize(([signalId, backtest]) => CREATE_KEY_FN$e(signalId, backtest), (signalId, backtest) => {
26550
+ this.getBreakeven = memoize(([signalId, backtest]) => CREATE_KEY_FN$f(signalId, backtest), (signalId, backtest) => {
26436
26551
  return new ClientBreakeven({
26437
26552
  signalId,
26438
26553
  logger: this.loggerService,
@@ -26493,7 +26608,7 @@ class BreakevenConnectionService {
26493
26608
  const breakeven = this.getBreakeven(data.id, backtest);
26494
26609
  await breakeven.waitForInit(symbol, data.strategyName, data.exchangeName, backtest);
26495
26610
  await breakeven.clear(symbol, data, priceClose, backtest);
26496
- const key = CREATE_KEY_FN$e(data.id, backtest);
26611
+ const key = CREATE_KEY_FN$f(data.id, backtest);
26497
26612
  this.getBreakeven.clear(key);
26498
26613
  };
26499
26614
  }
@@ -26509,7 +26624,7 @@ class BreakevenConnectionService {
26509
26624
  * @param backtest - Whether running in backtest mode
26510
26625
  * @returns Unique string key for memoization
26511
26626
  */
26512
- const CREATE_KEY_FN$d = (symbol, strategyName, exchangeName, frameName, backtest) => {
26627
+ const CREATE_KEY_FN$e = (symbol, strategyName, exchangeName, frameName, backtest) => {
26513
26628
  const parts = [symbol, strategyName, exchangeName];
26514
26629
  if (frameName)
26515
26630
  parts.push(frameName);
@@ -26684,7 +26799,7 @@ class BreakevenMarkdownService {
26684
26799
  * Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
26685
26800
  * Each combination gets its own isolated storage instance.
26686
26801
  */
26687
- this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$d(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$5(symbol, strategyName, exchangeName, frameName));
26802
+ this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$e(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$5(symbol, strategyName, exchangeName, frameName));
26688
26803
  /**
26689
26804
  * Subscribes to breakeven signal emitter to receive events.
26690
26805
  * Protected against multiple subscriptions.
@@ -26873,7 +26988,7 @@ class BreakevenMarkdownService {
26873
26988
  payload,
26874
26989
  });
26875
26990
  if (payload) {
26876
- const key = CREATE_KEY_FN$d(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
26991
+ const key = CREATE_KEY_FN$e(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
26877
26992
  this.getStorage.clear(key);
26878
26993
  }
26879
26994
  else {
@@ -26889,7 +27004,7 @@ class BreakevenMarkdownService {
26889
27004
  * @param context - Context with strategyName, exchangeName, frameName
26890
27005
  * @returns Unique string key for memoization
26891
27006
  */
26892
- const CREATE_KEY_FN$c = (context) => {
27007
+ const CREATE_KEY_FN$d = (context) => {
26893
27008
  const parts = [context.strategyName, context.exchangeName];
26894
27009
  if (context.frameName)
26895
27010
  parts.push(context.frameName);
@@ -26963,7 +27078,7 @@ class BreakevenGlobalService {
26963
27078
  * @param context - Context with strategyName, exchangeName and frameName
26964
27079
  * @param methodName - Name of the calling method for error tracking
26965
27080
  */
26966
- this.validate = memoize(([context]) => CREATE_KEY_FN$c(context), (context, methodName) => {
27081
+ this.validate = memoize(([context]) => CREATE_KEY_FN$d(context), (context, methodName) => {
26967
27082
  this.loggerService.log("breakevenGlobalService validate", {
26968
27083
  context,
26969
27084
  methodName,
@@ -27184,7 +27299,7 @@ class ConfigValidationService {
27184
27299
  * @param backtest - Whether running in backtest mode
27185
27300
  * @returns Unique string key for memoization
27186
27301
  */
27187
- const CREATE_KEY_FN$b = (symbol, strategyName, exchangeName, frameName, backtest) => {
27302
+ const CREATE_KEY_FN$c = (symbol, strategyName, exchangeName, frameName, backtest) => {
27188
27303
  const parts = [symbol, strategyName, exchangeName];
27189
27304
  if (frameName)
27190
27305
  parts.push(frameName);
@@ -27351,7 +27466,7 @@ class RiskMarkdownService {
27351
27466
  * Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
27352
27467
  * Each combination gets its own isolated storage instance.
27353
27468
  */
27354
- this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$b(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$4(symbol, strategyName, exchangeName, frameName));
27469
+ this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$c(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$4(symbol, strategyName, exchangeName, frameName));
27355
27470
  /**
27356
27471
  * Subscribes to risk rejection emitter to receive rejection events.
27357
27472
  * Protected against multiple subscriptions.
@@ -27540,7 +27655,7 @@ class RiskMarkdownService {
27540
27655
  payload,
27541
27656
  });
27542
27657
  if (payload) {
27543
- const key = CREATE_KEY_FN$b(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
27658
+ const key = CREATE_KEY_FN$c(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
27544
27659
  this.getStorage.clear(key);
27545
27660
  }
27546
27661
  else {
@@ -29922,7 +30037,7 @@ class HighestProfitReportService {
29922
30037
  * @returns Colon-separated key string for memoization
29923
30038
  * @internal
29924
30039
  */
29925
- const CREATE_KEY_FN$a = (symbol, strategyName, exchangeName, frameName, backtest) => {
30040
+ const CREATE_KEY_FN$b = (symbol, strategyName, exchangeName, frameName, backtest) => {
29926
30041
  const parts = [symbol, strategyName, exchangeName];
29927
30042
  if (frameName)
29928
30043
  parts.push(frameName);
@@ -30164,7 +30279,7 @@ class StrategyMarkdownService {
30164
30279
  *
30165
30280
  * @internal
30166
30281
  */
30167
- this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$a(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$3(symbol, strategyName, exchangeName, frameName));
30282
+ this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$b(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$3(symbol, strategyName, exchangeName, frameName));
30168
30283
  /**
30169
30284
  * Records a cancel-scheduled event when a scheduled signal is cancelled.
30170
30285
  *
@@ -30738,7 +30853,7 @@ class StrategyMarkdownService {
30738
30853
  this.clear = async (payload) => {
30739
30854
  this.loggerService.log("strategyMarkdownService clear", { payload });
30740
30855
  if (payload) {
30741
- const key = CREATE_KEY_FN$a(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
30856
+ const key = CREATE_KEY_FN$b(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
30742
30857
  this.getStorage.clear(key);
30743
30858
  }
30744
30859
  else {
@@ -30846,7 +30961,7 @@ class StrategyMarkdownService {
30846
30961
  * Creates a unique key for memoizing ReportStorage instances.
30847
30962
  * Key format: "symbol:strategyName:exchangeName[:frameName]:backtest|live"
30848
30963
  */
30849
- const CREATE_KEY_FN$9 = (symbol, strategyName, exchangeName, frameName, backtest) => {
30964
+ const CREATE_KEY_FN$a = (symbol, strategyName, exchangeName, frameName, backtest) => {
30850
30965
  const parts = [symbol, strategyName, exchangeName];
30851
30966
  if (frameName)
30852
30967
  parts.push(frameName);
@@ -31039,7 +31154,7 @@ let ReportStorage$2 = class ReportStorage {
31039
31154
  class SyncMarkdownService {
31040
31155
  constructor() {
31041
31156
  this.loggerService = inject(TYPES.loggerService);
31042
- this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$9(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$2(symbol, strategyName, exchangeName, frameName, backtest));
31157
+ this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$a(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$2(symbol, strategyName, exchangeName, frameName, backtest));
31043
31158
  /**
31044
31159
  * Subscribes to `syncSubject` to start receiving `SignalSyncContract` events.
31045
31160
  * Protected against multiple subscriptions via `singleshot` — subsequent calls
@@ -31235,7 +31350,7 @@ class SyncMarkdownService {
31235
31350
  this.clear = async (payload) => {
31236
31351
  this.loggerService.log("syncMarkdownService clear", { payload });
31237
31352
  if (payload) {
31238
- const key = CREATE_KEY_FN$9(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
31353
+ const key = CREATE_KEY_FN$a(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
31239
31354
  this.getStorage.clear(key);
31240
31355
  }
31241
31356
  else {
@@ -31248,7 +31363,7 @@ class SyncMarkdownService {
31248
31363
  /**
31249
31364
  * Creates a unique memoization key for a symbol-strategy-exchange-frame-backtest combination.
31250
31365
  */
31251
- const CREATE_KEY_FN$8 = (symbol, strategyName, exchangeName, frameName, backtest) => {
31366
+ const CREATE_KEY_FN$9 = (symbol, strategyName, exchangeName, frameName, backtest) => {
31252
31367
  const parts = [symbol, strategyName, exchangeName];
31253
31368
  if (frameName)
31254
31369
  parts.push(frameName);
@@ -31424,7 +31539,7 @@ let ReportStorage$1 = class ReportStorage {
31424
31539
  class HighestProfitMarkdownService {
31425
31540
  constructor() {
31426
31541
  this.loggerService = inject(TYPES.loggerService);
31427
- this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$8(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$1(symbol, strategyName, exchangeName, frameName));
31542
+ this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$9(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$1(symbol, strategyName, exchangeName, frameName));
31428
31543
  /**
31429
31544
  * Subscribes to `highestProfitSubject` to start receiving `HighestProfitContract`
31430
31545
  * events. Protected against multiple subscriptions via `singleshot` — subsequent
@@ -31590,7 +31705,7 @@ class HighestProfitMarkdownService {
31590
31705
  this.clear = async (payload) => {
31591
31706
  this.loggerService.log("highestProfitMarkdownService clear", { payload });
31592
31707
  if (payload) {
31593
- const key = CREATE_KEY_FN$8(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
31708
+ const key = CREATE_KEY_FN$9(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
31594
31709
  this.getStorage.clear(key);
31595
31710
  }
31596
31711
  else {
@@ -31612,7 +31727,7 @@ const LISTEN_TIMEOUT$1 = 120000;
31612
31727
  * @param backtest - Whether running in backtest mode
31613
31728
  * @returns Unique string key for memoization
31614
31729
  */
31615
- const CREATE_KEY_FN$7 = (symbol, strategyName, exchangeName, frameName, backtest) => {
31730
+ const CREATE_KEY_FN$8 = (symbol, strategyName, exchangeName, frameName, backtest) => {
31616
31731
  const parts = [symbol, strategyName, exchangeName];
31617
31732
  if (frameName)
31618
31733
  parts.push(frameName);
@@ -31655,7 +31770,7 @@ class PriceMetaService {
31655
31770
  * Each subject holds the latest currentPrice emitted by the strategy iterator for that key.
31656
31771
  * Instances are cached until clear() is called.
31657
31772
  */
31658
- this.getSource = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$7(symbol, strategyName, exchangeName, frameName, backtest), () => new BehaviorSubject());
31773
+ this.getSource = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$8(symbol, strategyName, exchangeName, frameName, backtest), () => new BehaviorSubject());
31659
31774
  /**
31660
31775
  * Returns the current market price for the given symbol and context.
31661
31776
  *
@@ -31684,10 +31799,10 @@ class PriceMetaService {
31684
31799
  if (source.data) {
31685
31800
  return source.data;
31686
31801
  }
31687
- console.warn(`PriceMetaService: No currentPrice available for ${CREATE_KEY_FN$7(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}. Trying to fetch from strategy iterator as a fallback...`);
31802
+ console.warn(`PriceMetaService: No currentPrice available for ${CREATE_KEY_FN$8(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}. Trying to fetch from strategy iterator as a fallback...`);
31688
31803
  const currentPrice = await waitForNext(source, (data) => !!data, LISTEN_TIMEOUT$1);
31689
31804
  if (typeof currentPrice === "symbol") {
31690
- throw new Error(`PriceMetaService: Timeout while waiting for currentPrice for ${CREATE_KEY_FN$7(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}`);
31805
+ throw new Error(`PriceMetaService: Timeout while waiting for currentPrice for ${CREATE_KEY_FN$8(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}`);
31691
31806
  }
31692
31807
  return currentPrice;
31693
31808
  };
@@ -31729,7 +31844,7 @@ class PriceMetaService {
31729
31844
  this.getSource.clear();
31730
31845
  return;
31731
31846
  }
31732
- const key = CREATE_KEY_FN$7(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
31847
+ const key = CREATE_KEY_FN$8(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
31733
31848
  this.getSource.clear(key);
31734
31849
  };
31735
31850
  }
@@ -31747,7 +31862,7 @@ const LISTEN_TIMEOUT = 120000;
31747
31862
  * @param backtest - Whether running in backtest mode
31748
31863
  * @returns Unique string key for memoization
31749
31864
  */
31750
- const CREATE_KEY_FN$6 = (symbol, strategyName, exchangeName, frameName, backtest) => {
31865
+ const CREATE_KEY_FN$7 = (symbol, strategyName, exchangeName, frameName, backtest) => {
31751
31866
  const parts = [symbol, strategyName, exchangeName];
31752
31867
  if (frameName)
31753
31868
  parts.push(frameName);
@@ -31790,7 +31905,7 @@ class TimeMetaService {
31790
31905
  * Each subject holds the latest createdAt timestamp emitted by the strategy iterator for that key.
31791
31906
  * Instances are cached until clear() is called.
31792
31907
  */
31793
- this.getSource = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$6(symbol, strategyName, exchangeName, frameName, backtest), () => new BehaviorSubject());
31908
+ this.getSource = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$7(symbol, strategyName, exchangeName, frameName, backtest), () => new BehaviorSubject());
31794
31909
  /**
31795
31910
  * Returns the current candle timestamp (in milliseconds) for the given symbol and context.
31796
31911
  *
@@ -31818,10 +31933,10 @@ class TimeMetaService {
31818
31933
  if (source.data) {
31819
31934
  return source.data;
31820
31935
  }
31821
- console.warn(`TimeMetaService: No timestamp available for ${CREATE_KEY_FN$6(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}. Trying to fetch from strategy iterator as a fallback...`);
31936
+ console.warn(`TimeMetaService: No timestamp available for ${CREATE_KEY_FN$7(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}. Trying to fetch from strategy iterator as a fallback...`);
31822
31937
  const timestamp = await waitForNext(source, (data) => !!data, LISTEN_TIMEOUT);
31823
31938
  if (typeof timestamp === "symbol") {
31824
- throw new Error(`TimeMetaService: Timeout while waiting for timestamp for ${CREATE_KEY_FN$6(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}`);
31939
+ throw new Error(`TimeMetaService: Timeout while waiting for timestamp for ${CREATE_KEY_FN$7(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}`);
31825
31940
  }
31826
31941
  return timestamp;
31827
31942
  };
@@ -31863,7 +31978,7 @@ class TimeMetaService {
31863
31978
  this.getSource.clear();
31864
31979
  return;
31865
31980
  }
31866
- const key = CREATE_KEY_FN$6(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
31981
+ const key = CREATE_KEY_FN$7(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
31867
31982
  this.getSource.clear(key);
31868
31983
  };
31869
31984
  }
@@ -31959,7 +32074,7 @@ class MaxDrawdownReportService {
31959
32074
  /**
31960
32075
  * Creates a unique memoization key for a symbol-strategy-exchange-frame-backtest combination.
31961
32076
  */
31962
- const CREATE_KEY_FN$5 = (symbol, strategyName, exchangeName, frameName, backtest) => {
32077
+ const CREATE_KEY_FN$6 = (symbol, strategyName, exchangeName, frameName, backtest) => {
31963
32078
  const parts = [symbol, strategyName, exchangeName];
31964
32079
  if (frameName)
31965
32080
  parts.push(frameName);
@@ -32083,7 +32198,7 @@ class ReportStorage {
32083
32198
  class MaxDrawdownMarkdownService {
32084
32199
  constructor() {
32085
32200
  this.loggerService = inject(TYPES.loggerService);
32086
- this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$5(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage(symbol, strategyName, exchangeName, frameName));
32201
+ this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$6(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage(symbol, strategyName, exchangeName, frameName));
32087
32202
  /**
32088
32203
  * Subscribes to `maxDrawdownSubject` to start receiving `MaxDrawdownContract`
32089
32204
  * events. Protected against multiple subscriptions via `singleshot`.
@@ -32162,7 +32277,7 @@ class MaxDrawdownMarkdownService {
32162
32277
  this.clear = async (payload) => {
32163
32278
  this.loggerService.log("maxDrawdownMarkdownService clear", { payload });
32164
32279
  if (payload) {
32165
- const key = CREATE_KEY_FN$5(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
32280
+ const key = CREATE_KEY_FN$6(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
32166
32281
  this.getStorage.clear(key);
32167
32282
  }
32168
32283
  else {
@@ -45228,6 +45343,503 @@ async function listRiskSchema() {
45228
45343
  return await backtest.riskValidationService.list();
45229
45344
  }
45230
45345
 
45346
+ const RECENT_PERSIST_BACKTEST_METHOD_NAME_HANDLE_ACTIVE_PING = "RecentPersistBacktestUtils.handleActivePing";
45347
+ const RECENT_PERSIST_BACKTEST_METHOD_NAME_GET_LATEST_SIGNAL = "RecentPersistBacktestUtils.getLatestSignal";
45348
+ const RECENT_PERSIST_LIVE_METHOD_NAME_HANDLE_ACTIVE_PING = "RecentPersistLiveUtils.handleActivePing";
45349
+ const RECENT_PERSIST_LIVE_METHOD_NAME_GET_LATEST_SIGNAL = "RecentPersistLiveUtils.getLatestSignal";
45350
+ const RECENT_MEMORY_BACKTEST_METHOD_NAME_HANDLE_ACTIVE_PING = "RecentMemoryBacktestUtils.handleActivePing";
45351
+ const RECENT_MEMORY_BACKTEST_METHOD_NAME_GET_LATEST_SIGNAL = "RecentMemoryBacktestUtils.getLatestSignal";
45352
+ const RECENT_MEMORY_LIVE_METHOD_NAME_HANDLE_ACTIVE_PING = "RecentMemoryLiveUtils.handleActivePing";
45353
+ const RECENT_MEMORY_LIVE_METHOD_NAME_GET_LATEST_SIGNAL = "RecentMemoryLiveUtils.getLatestSignal";
45354
+ const RECENT_BACKTEST_ADAPTER_METHOD_NAME_HANDLE_ACTIVE_PING = "RecentBacktestAdapter.handleActivePing";
45355
+ const RECENT_BACKTEST_ADAPTER_METHOD_NAME_GET_LATEST_SIGNAL = "RecentBacktestAdapter.getLatestSignal";
45356
+ const RECENT_BACKTEST_ADAPTER_METHOD_NAME_USE_ADAPTER = "RecentBacktestAdapter.useRecentAdapter";
45357
+ const RECENT_BACKTEST_ADAPTER_METHOD_NAME_USE_PERSIST = "RecentBacktestAdapter.usePersist";
45358
+ const RECENT_BACKTEST_ADAPTER_METHOD_NAME_USE_MEMORY = "RecentBacktestAdapter.useMemory";
45359
+ const RECENT_BACKTEST_ADAPTER_METHOD_NAME_CLEAR = "RecentBacktestAdapter.clear";
45360
+ const RECENT_LIVE_ADAPTER_METHOD_NAME_HANDLE_ACTIVE_PING = "RecentLiveAdapter.handleActivePing";
45361
+ const RECENT_LIVE_ADAPTER_METHOD_NAME_GET_LATEST_SIGNAL = "RecentLiveAdapter.getLatestSignal";
45362
+ const RECENT_LIVE_ADAPTER_METHOD_NAME_USE_ADAPTER = "RecentLiveAdapter.useRecentAdapter";
45363
+ const RECENT_LIVE_ADAPTER_METHOD_NAME_USE_PERSIST = "RecentLiveAdapter.usePersist";
45364
+ const RECENT_LIVE_ADAPTER_METHOD_NAME_USE_MEMORY = "RecentLiveAdapter.useMemory";
45365
+ const RECENT_LIVE_ADAPTER_METHOD_NAME_CLEAR = "RecentLiveAdapter.clear";
45366
+ const RECENT_ADAPTER_METHOD_NAME_ENABLE = "RecentAdapter.enable";
45367
+ const RECENT_ADAPTER_METHOD_NAME_DISABLE = "RecentAdapter.disable";
45368
+ const RECENT_ADAPTER_METHOD_NAME_GET_LATEST_SIGNAL = "RecentAdapter.getLatestSignal";
45369
+ /**
45370
+ * Builds a composite storage key from context parts.
45371
+ * Includes backtest flag as the last segment to prevent live/backtest collisions.
45372
+ * @param symbol - Trading pair symbol
45373
+ * @param strategyName - Strategy identifier
45374
+ * @param exchangeName - Exchange identifier
45375
+ * @param frameName - Frame identifier
45376
+ * @param backtest - Flag indicating if the context is backtest or live
45377
+ * @returns Composite key string
45378
+ */
45379
+ const CREATE_KEY_FN$5 = (symbol, strategyName, exchangeName, frameName, backtest) => {
45380
+ const parts = [symbol, strategyName, exchangeName];
45381
+ if (frameName)
45382
+ parts.push(frameName);
45383
+ parts.push(backtest ? "backtest" : "live");
45384
+ return parts.join(":");
45385
+ };
45386
+ /**
45387
+ * Persistent storage adapter for backtest recent signals.
45388
+ *
45389
+ * Features:
45390
+ * - Persists the latest active signal per context to disk using PersistRecentAdapter
45391
+ * - Handles active ping events only
45392
+ *
45393
+ * Use this adapter for backtest recent signal persistence across sessions.
45394
+ */
45395
+ class RecentPersistBacktestUtils {
45396
+ constructor() {
45397
+ /**
45398
+ * Handles active ping event.
45399
+ * Persists the latest signal to disk via PersistRecentAdapter.
45400
+ * @param event - Active ping contract with signal data and backtest flag
45401
+ */
45402
+ this.handleActivePing = async (event) => {
45403
+ backtest.loggerService.info(RECENT_PERSIST_BACKTEST_METHOD_NAME_HANDLE_ACTIVE_PING, {
45404
+ signalId: event.data.id,
45405
+ });
45406
+ await PersistRecentAdapter.writeRecentData(event.data, event.symbol, event.strategyName, event.exchangeName, event.data.frameName, event.backtest);
45407
+ };
45408
+ /**
45409
+ * Retrieves the latest persisted signal for the given context.
45410
+ * @param symbol - Trading pair symbol
45411
+ * @param strategyName - Strategy identifier
45412
+ * @param exchangeName - Exchange identifier
45413
+ * @param frameName - Frame identifier
45414
+ * @param backtest - Flag indicating if the context is backtest or live
45415
+ * @returns The latest signal or null if not found
45416
+ */
45417
+ this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
45418
+ backtest.loggerService.info(RECENT_PERSIST_BACKTEST_METHOD_NAME_GET_LATEST_SIGNAL, {
45419
+ symbol,
45420
+ strategyName,
45421
+ exchangeName,
45422
+ frameName,
45423
+ backtest: backtest$1,
45424
+ });
45425
+ return await PersistRecentAdapter.readRecentData(symbol, strategyName, exchangeName, frameName, backtest$1);
45426
+ };
45427
+ }
45428
+ }
45429
+ /**
45430
+ * In-memory storage adapter for backtest recent signals.
45431
+ *
45432
+ * Features:
45433
+ * - Stores the latest active signal per context key in memory only
45434
+ * - Fast read/write operations
45435
+ * - Data is lost when application restarts
45436
+ *
45437
+ * Use this adapter for testing or when persistence is not required.
45438
+ */
45439
+ class RecentMemoryBacktestUtils {
45440
+ constructor() {
45441
+ /** Map of composite context keys to the latest signal */
45442
+ this._signals = new Map();
45443
+ /**
45444
+ * Handles active ping event.
45445
+ * Stores the latest signal in memory under the composite context key.
45446
+ * @param event - Active ping contract with signal data and backtest flag
45447
+ */
45448
+ this.handleActivePing = async (event) => {
45449
+ backtest.loggerService.info(RECENT_MEMORY_BACKTEST_METHOD_NAME_HANDLE_ACTIVE_PING, {
45450
+ signalId: event.data.id,
45451
+ });
45452
+ const key = CREATE_KEY_FN$5(event.symbol, event.strategyName, event.exchangeName, event.data.frameName, event.backtest);
45453
+ this._signals.set(key, event.data);
45454
+ };
45455
+ /**
45456
+ * Retrieves the latest in-memory signal for the given context.
45457
+ * @param symbol - Trading pair symbol
45458
+ * @param strategyName - Strategy identifier
45459
+ * @param exchangeName - Exchange identifier
45460
+ * @param frameName - Frame identifier
45461
+ * @param backtest - Flag indicating if the context is backtest or live
45462
+ * @returns The latest signal or null if not found
45463
+ */
45464
+ this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
45465
+ const key = CREATE_KEY_FN$5(symbol, strategyName, exchangeName, frameName, backtest$1);
45466
+ backtest.loggerService.info(RECENT_MEMORY_BACKTEST_METHOD_NAME_GET_LATEST_SIGNAL, { key });
45467
+ return this._signals.get(key) ?? null;
45468
+ };
45469
+ }
45470
+ }
45471
+ /**
45472
+ * Persistent storage adapter for live recent signals.
45473
+ *
45474
+ * Features:
45475
+ * - Persists the latest active signal per context to disk using PersistRecentAdapter
45476
+ * - Handles active ping events only
45477
+ *
45478
+ * Use this adapter (default) for live recent signal persistence across sessions.
45479
+ */
45480
+ class RecentPersistLiveUtils {
45481
+ constructor() {
45482
+ /**
45483
+ * Handles active ping event.
45484
+ * Persists the latest signal to disk via PersistRecentAdapter.
45485
+ * @param event - Active ping contract with signal data and backtest flag
45486
+ */
45487
+ this.handleActivePing = async (event) => {
45488
+ backtest.loggerService.info(RECENT_PERSIST_LIVE_METHOD_NAME_HANDLE_ACTIVE_PING, {
45489
+ signalId: event.data.id,
45490
+ });
45491
+ await PersistRecentAdapter.writeRecentData(event.data, event.symbol, event.strategyName, event.exchangeName, event.data.frameName, event.backtest);
45492
+ };
45493
+ /**
45494
+ * Retrieves the latest persisted signal for the given context.
45495
+ * @param symbol - Trading pair symbol
45496
+ * @param strategyName - Strategy identifier
45497
+ * @param exchangeName - Exchange identifier
45498
+ * @param frameName - Frame identifier
45499
+ * @param backtest - Flag indicating if the context is backtest or live
45500
+ * @returns The latest signal or null if not found
45501
+ */
45502
+ this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
45503
+ backtest.loggerService.info(RECENT_PERSIST_LIVE_METHOD_NAME_GET_LATEST_SIGNAL, {
45504
+ symbol,
45505
+ strategyName,
45506
+ exchangeName,
45507
+ frameName,
45508
+ backtest: backtest$1,
45509
+ });
45510
+ return await PersistRecentAdapter.readRecentData(symbol, strategyName, exchangeName, frameName, backtest$1);
45511
+ };
45512
+ }
45513
+ }
45514
+ /**
45515
+ * In-memory storage adapter for live recent signals.
45516
+ *
45517
+ * Features:
45518
+ * - Stores the latest active signal per context key in memory only
45519
+ * - Fast read/write operations
45520
+ * - Data is lost when application restarts
45521
+ *
45522
+ * Use this adapter for testing or when persistence is not required.
45523
+ */
45524
+ class RecentMemoryLiveUtils {
45525
+ constructor() {
45526
+ /** Map of composite context keys to the latest signal */
45527
+ this._signals = new Map();
45528
+ /**
45529
+ * Handles active ping event.
45530
+ * Stores the latest signal in memory under the composite context key.
45531
+ * @param event - Active ping contract with signal data and backtest flag
45532
+ */
45533
+ this.handleActivePing = async (event) => {
45534
+ backtest.loggerService.info(RECENT_MEMORY_LIVE_METHOD_NAME_HANDLE_ACTIVE_PING, {
45535
+ signalId: event.data.id,
45536
+ });
45537
+ const key = CREATE_KEY_FN$5(event.symbol, event.strategyName, event.exchangeName, event.data.frameName, event.backtest);
45538
+ this._signals.set(key, event.data);
45539
+ };
45540
+ /**
45541
+ * Retrieves the latest in-memory signal for the given context.
45542
+ * @param symbol - Trading pair symbol
45543
+ * @param strategyName - Strategy identifier
45544
+ * @param exchangeName - Exchange identifier
45545
+ * @param frameName - Frame identifier
45546
+ * @param backtest - Flag indicating if the context is backtest or live
45547
+ * @returns The latest signal or null if not found
45548
+ */
45549
+ this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
45550
+ const key = CREATE_KEY_FN$5(symbol, strategyName, exchangeName, frameName, backtest$1);
45551
+ backtest.loggerService.info(RECENT_MEMORY_LIVE_METHOD_NAME_GET_LATEST_SIGNAL, { key });
45552
+ return this._signals.get(key) ?? null;
45553
+ };
45554
+ }
45555
+ }
45556
+ /**
45557
+ * Backtest recent signal adapter with pluggable storage backend.
45558
+ *
45559
+ * Features:
45560
+ * - Adapter pattern for swappable storage implementations
45561
+ * - Default adapter: RecentMemoryBacktestUtils (in-memory storage)
45562
+ * - Alternative adapter: RecentPersistBacktestUtils
45563
+ * - Convenience methods: usePersist(), useMemory()
45564
+ */
45565
+ class RecentBacktestAdapter {
45566
+ constructor() {
45567
+ /** Internal storage utils instance */
45568
+ this._recentBacktestUtils = new RecentMemoryBacktestUtils();
45569
+ /**
45570
+ * Handles active ping event.
45571
+ * Proxies call to the underlying storage adapter.
45572
+ * @param event - Active ping contract with signal data
45573
+ */
45574
+ this.handleActivePing = async (event) => {
45575
+ backtest.loggerService.info(RECENT_BACKTEST_ADAPTER_METHOD_NAME_HANDLE_ACTIVE_PING, {
45576
+ signalId: event.data.id,
45577
+ });
45578
+ return await this._recentBacktestUtils.handleActivePing(event);
45579
+ };
45580
+ /**
45581
+ * Retrieves the latest signal for the given context.
45582
+ * Proxies call to the underlying storage adapter.
45583
+ * @param symbol - Trading pair symbol
45584
+ * @param strategyName - Strategy identifier
45585
+ * @param exchangeName - Exchange identifier
45586
+ * @param frameName - Frame identifier
45587
+ * @param backtest - Flag indicating if the context is backtest or live
45588
+ * @returns The latest signal or null if not found
45589
+ */
45590
+ this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
45591
+ backtest.loggerService.info(RECENT_BACKTEST_ADAPTER_METHOD_NAME_GET_LATEST_SIGNAL, {
45592
+ symbol,
45593
+ strategyName,
45594
+ exchangeName,
45595
+ frameName,
45596
+ backtest: backtest$1,
45597
+ });
45598
+ return await this._recentBacktestUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1);
45599
+ };
45600
+ /**
45601
+ * Sets the storage adapter constructor.
45602
+ * All future storage operations will use this adapter.
45603
+ * @param Ctor - Constructor for recent adapter
45604
+ */
45605
+ this.useRecentAdapter = (Ctor) => {
45606
+ backtest.loggerService.info(RECENT_BACKTEST_ADAPTER_METHOD_NAME_USE_ADAPTER);
45607
+ this._recentBacktestUtils = Reflect.construct(Ctor, []);
45608
+ };
45609
+ /**
45610
+ * Switches to persistent storage adapter.
45611
+ * Signals will be persisted to disk.
45612
+ */
45613
+ this.usePersist = () => {
45614
+ backtest.loggerService.info(RECENT_BACKTEST_ADAPTER_METHOD_NAME_USE_PERSIST);
45615
+ this._recentBacktestUtils = new RecentPersistBacktestUtils();
45616
+ };
45617
+ /**
45618
+ * Switches to in-memory storage adapter (default).
45619
+ * Signals will be stored in memory only.
45620
+ */
45621
+ this.useMemory = () => {
45622
+ backtest.loggerService.info(RECENT_BACKTEST_ADAPTER_METHOD_NAME_USE_MEMORY);
45623
+ this._recentBacktestUtils = new RecentMemoryBacktestUtils();
45624
+ };
45625
+ /**
45626
+ * Clears the cached utils instance by resetting to the default in-memory adapter.
45627
+ */
45628
+ this.clear = () => {
45629
+ backtest.loggerService.info(RECENT_BACKTEST_ADAPTER_METHOD_NAME_CLEAR);
45630
+ this._recentBacktestUtils = new RecentMemoryBacktestUtils();
45631
+ };
45632
+ }
45633
+ }
45634
+ /**
45635
+ * Live recent signal adapter with pluggable storage backend.
45636
+ *
45637
+ * Features:
45638
+ * - Adapter pattern for swappable storage implementations
45639
+ * - Default adapter: RecentPersistLiveUtils (persistent storage)
45640
+ * - Alternative adapter: RecentMemoryLiveUtils
45641
+ * - Convenience methods: usePersist(), useMemory()
45642
+ */
45643
+ class RecentLiveAdapter {
45644
+ constructor() {
45645
+ /** Internal storage utils instance */
45646
+ this._recentLiveUtils = new RecentPersistLiveUtils();
45647
+ /**
45648
+ * Handles active ping event.
45649
+ * Proxies call to the underlying storage adapter.
45650
+ * @param event - Active ping contract with signal data
45651
+ */
45652
+ this.handleActivePing = async (event) => {
45653
+ backtest.loggerService.info(RECENT_LIVE_ADAPTER_METHOD_NAME_HANDLE_ACTIVE_PING, {
45654
+ signalId: event.data.id,
45655
+ });
45656
+ return await this._recentLiveUtils.handleActivePing(event);
45657
+ };
45658
+ /**
45659
+ * Retrieves the latest signal for the given context.
45660
+ * Proxies call to the underlying storage adapter.
45661
+ * @param symbol - Trading pair symbol
45662
+ * @param strategyName - Strategy identifier
45663
+ * @param exchangeName - Exchange identifier
45664
+ * @param frameName - Frame identifier
45665
+ * @param backtest - Flag indicating if the context is backtest or live
45666
+ * @returns The latest signal or null if not found
45667
+ */
45668
+ this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
45669
+ backtest.loggerService.info(RECENT_LIVE_ADAPTER_METHOD_NAME_GET_LATEST_SIGNAL, {
45670
+ symbol,
45671
+ strategyName,
45672
+ exchangeName,
45673
+ frameName,
45674
+ backtest: backtest$1,
45675
+ });
45676
+ return await this._recentLiveUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1);
45677
+ };
45678
+ /**
45679
+ * Sets the storage adapter constructor.
45680
+ * All future storage operations will use this adapter.
45681
+ * @param Ctor - Constructor for recent adapter
45682
+ */
45683
+ this.useRecentAdapter = (Ctor) => {
45684
+ backtest.loggerService.info(RECENT_LIVE_ADAPTER_METHOD_NAME_USE_ADAPTER);
45685
+ this._recentLiveUtils = Reflect.construct(Ctor, []);
45686
+ };
45687
+ /**
45688
+ * Switches to persistent storage adapter (default).
45689
+ * Signals will be persisted to disk.
45690
+ */
45691
+ this.usePersist = () => {
45692
+ backtest.loggerService.info(RECENT_LIVE_ADAPTER_METHOD_NAME_USE_PERSIST);
45693
+ this._recentLiveUtils = new RecentPersistLiveUtils();
45694
+ };
45695
+ /**
45696
+ * Switches to in-memory storage adapter.
45697
+ * Signals will be stored in memory only.
45698
+ */
45699
+ this.useMemory = () => {
45700
+ backtest.loggerService.info(RECENT_LIVE_ADAPTER_METHOD_NAME_USE_MEMORY);
45701
+ this._recentLiveUtils = new RecentMemoryLiveUtils();
45702
+ };
45703
+ /**
45704
+ * Clears the cached utils instance by resetting to the default persistent adapter.
45705
+ */
45706
+ this.clear = () => {
45707
+ backtest.loggerService.info(RECENT_LIVE_ADAPTER_METHOD_NAME_CLEAR);
45708
+ this._recentLiveUtils = new RecentPersistLiveUtils();
45709
+ };
45710
+ }
45711
+ }
45712
+ /**
45713
+ * Main recent signal adapter that manages both backtest and live recent signal storage.
45714
+ *
45715
+ * Features:
45716
+ * - Subscribes to activePingSubject for automatic storage updates
45717
+ * - Provides unified access to the latest signal for any context
45718
+ * - Singleshot enable pattern prevents duplicate subscriptions
45719
+ * - Cleanup function for proper unsubscription
45720
+ */
45721
+ class RecentAdapter {
45722
+ constructor() {
45723
+ /**
45724
+ * Enables recent signal storage by subscribing to activePingSubject.
45725
+ * Uses singleshot to ensure one-time subscription.
45726
+ *
45727
+ * @returns Cleanup function that unsubscribes from all emitters
45728
+ */
45729
+ this.enable = singleshot(() => {
45730
+ backtest.loggerService.info(RECENT_ADAPTER_METHOD_NAME_ENABLE);
45731
+ let unBacktest;
45732
+ let unLive;
45733
+ {
45734
+ const unBacktestPingActive = activePingSubject
45735
+ .filter(({ backtest }) => backtest)
45736
+ .connect((event) => RecentBacktest.handleActivePing(event));
45737
+ unBacktest = compose(() => unBacktestPingActive());
45738
+ }
45739
+ {
45740
+ const unLivePingActive = activePingSubject
45741
+ .filter(({ backtest }) => !backtest)
45742
+ .connect((event) => RecentLive.handleActivePing(event));
45743
+ unLive = compose(() => unLivePingActive());
45744
+ }
45745
+ const unEnable = () => this.enable.clear();
45746
+ return compose(() => unBacktest(), () => unLive(), () => unEnable());
45747
+ });
45748
+ /**
45749
+ * Disables recent signal storage by unsubscribing from all emitters.
45750
+ * Safe to call multiple times.
45751
+ */
45752
+ this.disable = () => {
45753
+ backtest.loggerService.info(RECENT_ADAPTER_METHOD_NAME_DISABLE);
45754
+ if (this.enable.hasValue()) {
45755
+ const lastSubscription = this.enable();
45756
+ lastSubscription();
45757
+ }
45758
+ };
45759
+ /**
45760
+ * Retrieves the latest active signal for the given symbol and context.
45761
+ * Searches backtest storage first, then live storage.
45762
+ *
45763
+ * @param symbol - Trading pair symbol
45764
+ * @param context - Execution context with strategyName, exchangeName, and frameName
45765
+ * @param backtest - Flag indicating if the context is backtest or live
45766
+ * @returns The latest signal or null if not found
45767
+ * @throws Error if RecentAdapter is not enabled
45768
+ */
45769
+ this.getLatestSignal = async (symbol, context) => {
45770
+ backtest.loggerService.info(RECENT_ADAPTER_METHOD_NAME_GET_LATEST_SIGNAL, {
45771
+ symbol,
45772
+ context,
45773
+ });
45774
+ if (!this.enable.hasValue()) {
45775
+ throw new Error("RecentAdapter is not enabled. Call enable() first.");
45776
+ }
45777
+ let result = null;
45778
+ if (result = await RecentBacktest.getLatestSignal(symbol, context.strategyName, context.exchangeName, context.frameName, true)) {
45779
+ return result;
45780
+ }
45781
+ if (result = await RecentLive.getLatestSignal(symbol, context.strategyName, context.exchangeName, context.frameName, false)) {
45782
+ return result;
45783
+ }
45784
+ return null;
45785
+ };
45786
+ }
45787
+ }
45788
+ /**
45789
+ * Global singleton instance of RecentAdapter.
45790
+ * Provides unified recent signal management for backtest and live trading.
45791
+ */
45792
+ const Recent = new RecentAdapter();
45793
+ /**
45794
+ * Global singleton instance of RecentLiveAdapter.
45795
+ * Provides live trading recent signal storage with pluggable backends.
45796
+ */
45797
+ const RecentLive = new RecentLiveAdapter();
45798
+ /**
45799
+ * Global singleton instance of RecentBacktestAdapter.
45800
+ * Provides backtest recent signal storage with pluggable backends.
45801
+ */
45802
+ const RecentBacktest = new RecentBacktestAdapter();
45803
+
45804
+ const GET_LATEST_SIGNAL_METHOD_NAME = "signal.getLatestSignal";
45805
+ /**
45806
+ * Returns the latest signal (pending or closed) for the current strategy context.
45807
+ *
45808
+ * Does not distinguish between active and closed signals — returns whichever
45809
+ * was recorded last. Useful for cooldown logic: e.g. skip opening a new position
45810
+ * for 4 hours after a stop-loss by checking the timestamp of the latest signal
45811
+ * regardless of its outcome.
45812
+ *
45813
+ * Searches backtest storage first, then live storage.
45814
+ * Returns null if no signal exists at all.
45815
+ *
45816
+ * Automatically detects backtest/live mode from execution context.
45817
+ *
45818
+ * @param symbol - Trading pair symbol
45819
+ * @returns Promise resolving to the latest signal or null
45820
+ *
45821
+ * @example
45822
+ * ```typescript
45823
+ * import { getLatestSignal } from "backtest-kit";
45824
+ *
45825
+ * const latest = await getLatestSignal("BTCUSDT");
45826
+ * if (latest && Date.now() - latest.closedAt < 4 * 60 * 60 * 1000) {
45827
+ * return; // cooldown after SL — skip new signal for 4 hours
45828
+ * }
45829
+ * ```
45830
+ */
45831
+ async function getLatestSignal(symbol) {
45832
+ backtest.loggerService.info(GET_LATEST_SIGNAL_METHOD_NAME, { symbol });
45833
+ if (!ExecutionContextService.hasContext()) {
45834
+ throw new Error("getLatestSignal requires an execution context");
45835
+ }
45836
+ if (!MethodContextService.hasContext()) {
45837
+ throw new Error("getLatestSignal requires a method context");
45838
+ }
45839
+ const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
45840
+ return await Recent.getLatestSignal(symbol, { exchangeName, frameName, strategyName });
45841
+ }
45842
+
45231
45843
  const DEFAULT_BM25_K1 = 1.5;
45232
45844
  const DEFAULT_BM25_B = 0.75;
45233
45845
  const DEFAULT_BM25_SCORE = 0.5;
@@ -48478,6 +49090,67 @@ class LogAdapter {
48478
49090
  */
48479
49091
  const Log = new LogAdapter();
48480
49092
 
49093
+ const METHOD_NAME_CREATE_SNAPSHOT = "SessionUtils.createSnapshot";
49094
+ /** List of all global subjects whose listeners should be snapshotted for session isolation */
49095
+ const SUBJECT_ISOLATION_LIST = [
49096
+ activePingSubject,
49097
+ backtestScheduleOpenSubject,
49098
+ breakevenSubject,
49099
+ doneBacktestSubject,
49100
+ doneLiveSubject,
49101
+ errorEmitter,
49102
+ exitEmitter,
49103
+ highestProfitSubject,
49104
+ maxDrawdownSubject,
49105
+ partialLossSubject,
49106
+ partialProfitSubject,
49107
+ performanceEmitter,
49108
+ progressBacktestEmitter,
49109
+ riskSubject,
49110
+ schedulePingSubject,
49111
+ shutdownEmitter,
49112
+ signalBacktestEmitter,
49113
+ signalEmitter,
49114
+ signalLiveEmitter,
49115
+ strategyCommitSubject,
49116
+ syncSubject,
49117
+ validationSubject,
49118
+ ];
49119
+ /**
49120
+ * Creates a snapshot function for a given subject by clearing its internal
49121
+ * events map and returning a restore function that can put the original listeners back.
49122
+ * @param subject The subject to snapshot
49123
+ * @returns A function that restores the subject's original listeners when called
49124
+ */
49125
+ const CREATE_SUBJECT_SNAPSHOT_FN = (subject) => {
49126
+ const emitter = subject["_emitter"];
49127
+ const events = emitter["_events"];
49128
+ emitter["_events"] = {};
49129
+ return () => {
49130
+ emitter["_events"] = events;
49131
+ };
49132
+ };
49133
+ /**
49134
+ * Manages isolation of global event-bus state between backtest sessions.
49135
+ * Allows temporarily detaching all subject subscriptions so that one session
49136
+ * does not interfere with another, then restoring them afterwards.
49137
+ */
49138
+ class SessionUtils {
49139
+ constructor() {
49140
+ /**
49141
+ * Snapshots the current listener state of every global subject by replacing
49142
+ * their internal `_events` map with an empty object.
49143
+ * @returns A restore function that, when called, puts all original listeners back.
49144
+ */
49145
+ this.createSnapshot = () => {
49146
+ backtest.loggerService.log(METHOD_NAME_CREATE_SNAPSHOT);
49147
+ const snapshotList = SUBJECT_ISOLATION_LIST.map(CREATE_SUBJECT_SNAPSHOT_FN);
49148
+ return compose(...snapshotList);
49149
+ };
49150
+ }
49151
+ }
49152
+ const Session = new SessionUtils();
49153
+
48481
49154
  const SCHEDULE_METHOD_NAME_GET_DATA = "ScheduleUtils.getData";
48482
49155
  const SCHEDULE_METHOD_NAME_GET_REPORT = "ScheduleUtils.getReport";
48483
49156
  const SCHEDULE_METHOD_NAME_DUMP = "ScheduleUtils.dump";
@@ -54715,12 +55388,15 @@ const CACHE_METHOD_NAME_RUN = "CacheFnInstance.run";
54715
55388
  const CACHE_METHOD_NAME_FN = "CacheUtils.fn";
54716
55389
  const CACHE_METHOD_NAME_FN_CLEAR = "CacheUtils.fn.clear";
54717
55390
  const CACHE_METHOD_NAME_FN_GC = "CacheUtils.fn.gc";
55391
+ const CACHE_METHOD_NAME_FN_HAS_VALUE = "CacheUtils.fn.hasValue";
54718
55392
  const CACHE_METHOD_NAME_FILE = "CacheUtils.file";
54719
55393
  const CACHE_METHOD_NAME_FILE_CLEAR = "CacheUtils.file.clear";
55394
+ const CACHE_METHOD_NAME_FILE_HAS_VALUE = "CacheUtils.file.hasValue";
54720
55395
  const CACHE_METHOD_NAME_DISPOSE = "CacheUtils.dispose";
54721
55396
  const CACHE_METHOD_NAME_CLEAR = "CacheUtils.clear";
54722
55397
  const CACHE_METHOD_NAME_RESET_COUNTER = "CacheUtils.resetCounter";
54723
55398
  const CACHE_FILE_INSTANCE_METHOD_NAME_RUN = "CacheFileInstance.run";
55399
+ const CACHE_FILE_INSTANCE_METHOD_NAME_HAS_VALUE = "CacheFileInstance.hasValue";
54724
55400
  const MS_PER_MINUTE$1 = 60000;
54725
55401
  const INTERVAL_MINUTES$1 = {
54726
55402
  "1m": 1,
@@ -54910,6 +55586,36 @@ class CacheFnInstance {
54910
55586
  }
54911
55587
  }
54912
55588
  };
55589
+ /**
55590
+ * Check whether a valid (non-expired) cache entry exists for the current context and arguments.
55591
+ *
55592
+ * Returns `true` if a cached value exists and its interval is still current.
55593
+ * Returns `false` if there is no entry or the cached entry has expired.
55594
+ *
55595
+ * Requires active execution context and method context.
55596
+ *
55597
+ * @param args - Arguments to look up in the cache
55598
+ * @returns `true` if a fresh cached value exists, `false` otherwise
55599
+ */
55600
+ this.hasValue = (...args) => {
55601
+ if (!MethodContextService.hasContext()) {
55602
+ throw new Error("CacheFnInstance hasValue requires method context");
55603
+ }
55604
+ if (!ExecutionContextService.hasContext()) {
55605
+ throw new Error("CacheFnInstance hasValue requires execution context");
55606
+ }
55607
+ const contextKey = CREATE_KEY_FN$1(backtest.methodContextService.context.strategyName, backtest.methodContextService.context.exchangeName, backtest.methodContextService.context.frameName, backtest.executionContextService.context.backtest);
55608
+ const argKey = String(this.key(args));
55609
+ const key = `${contextKey}:${argKey}`;
55610
+ const cached = this._cacheMap.get(key);
55611
+ if (!cached) {
55612
+ return false;
55613
+ }
55614
+ const currentWhen = backtest.executionContextService.context.when;
55615
+ const currentAligned = align$1(currentWhen.getTime(), this.interval);
55616
+ const cachedAligned = align$1(cached.when.getTime(), this.interval);
55617
+ return currentAligned === cachedAligned;
55618
+ };
54913
55619
  /**
54914
55620
  * Garbage collect expired cache entries.
54915
55621
  *
@@ -55034,6 +55740,33 @@ class CacheFileInstance {
55034
55740
  await PersistMeasureAdapter.writeMeasureData({ id: entityKey, data: result, removed: false }, bucket, entityKey);
55035
55741
  return result;
55036
55742
  };
55743
+ /**
55744
+ * Check whether a cached value exists on disk for the given arguments and current interval.
55745
+ *
55746
+ * Returns `true` if a persisted record exists for the current aligned timestamp.
55747
+ * Returns `false` if no record is found.
55748
+ *
55749
+ * Requires active execution context and method context.
55750
+ *
55751
+ * @param args - Arguments forwarded to the key generator
55752
+ * @returns `true` if a cached record exists, `false` otherwise
55753
+ */
55754
+ this.hasValue = async (...args) => {
55755
+ backtest.loggerService.debug(CACHE_FILE_INSTANCE_METHOD_NAME_HAS_VALUE, { args });
55756
+ if (!MethodContextService.hasContext()) {
55757
+ throw new Error("CacheFileInstance hasValue requires method context");
55758
+ }
55759
+ if (!ExecutionContextService.hasContext()) {
55760
+ throw new Error("CacheFileInstance hasValue requires execution context");
55761
+ }
55762
+ const [symbol, ...rest] = args;
55763
+ const { when } = backtest.executionContextService.context;
55764
+ const alignedTs = align$1(when.getTime(), this.interval);
55765
+ const bucket = `${this.name}_${this.interval}_${this.index}`;
55766
+ const entityKey = this.key([symbol, alignedTs, ...rest]);
55767
+ const cached = await PersistMeasureAdapter.readMeasureData(bucket, entityKey);
55768
+ return cached !== null;
55769
+ };
55037
55770
  /**
55038
55771
  * Soft-delete all persisted records for this instance's bucket.
55039
55772
  * After this call the next `run()` will recompute and re-cache the value.
@@ -55120,23 +55853,30 @@ class CacheUtils {
55120
55853
  wrappedFn.clear = () => {
55121
55854
  backtest.loggerService.info(CACHE_METHOD_NAME_FN_CLEAR);
55122
55855
  if (!MethodContextService.hasContext()) {
55123
- backtest.loggerService.warn(`${CACHE_METHOD_NAME_FN_CLEAR} called without method context, skipping`);
55124
- return;
55856
+ throw new Error(`${CACHE_METHOD_NAME_FN_CLEAR} requires method context`);
55125
55857
  }
55126
55858
  if (!ExecutionContextService.hasContext()) {
55127
- backtest.loggerService.warn(`${CACHE_METHOD_NAME_FN_CLEAR} called without execution context, skipping`);
55128
- return;
55859
+ throw new Error(`${CACHE_METHOD_NAME_FN_CLEAR} requires execution context`);
55129
55860
  }
55130
55861
  this._getFnInstance.get(run)?.clear();
55131
55862
  };
55132
55863
  wrappedFn.gc = () => {
55133
55864
  backtest.loggerService.info(CACHE_METHOD_NAME_FN_GC);
55134
55865
  if (!ExecutionContextService.hasContext()) {
55135
- backtest.loggerService.warn(`${CACHE_METHOD_NAME_FN_GC} called without execution context, skipping`);
55136
- return;
55866
+ throw new Error(`${CACHE_METHOD_NAME_FN_GC} requires execution context`);
55137
55867
  }
55138
55868
  return this._getFnInstance.get(run)?.gc();
55139
55869
  };
55870
+ wrappedFn.hasValue = (...args) => {
55871
+ backtest.loggerService.info(CACHE_METHOD_NAME_FN_HAS_VALUE);
55872
+ if (!MethodContextService.hasContext()) {
55873
+ throw new Error(`${CACHE_METHOD_NAME_FN_HAS_VALUE} requires method context`);
55874
+ }
55875
+ if (!ExecutionContextService.hasContext()) {
55876
+ throw new Error(`${CACHE_METHOD_NAME_FN_HAS_VALUE} requires execution context`);
55877
+ }
55878
+ return this._getFnInstance.get(run)?.hasValue(...args) ?? false;
55879
+ };
55140
55880
  return wrappedFn;
55141
55881
  };
55142
55882
  /**
@@ -55188,8 +55928,25 @@ class CacheUtils {
55188
55928
  };
55189
55929
  wrappedFn.clear = async () => {
55190
55930
  backtest.loggerService.info(CACHE_METHOD_NAME_FILE_CLEAR);
55931
+ if (!MethodContextService.hasContext()) {
55932
+ throw new Error(`${CACHE_METHOD_NAME_FILE_CLEAR} requires method context`);
55933
+ }
55934
+ if (!ExecutionContextService.hasContext()) {
55935
+ throw new Error(`${CACHE_METHOD_NAME_FILE_CLEAR} requires execution context`);
55936
+ }
55191
55937
  await this._getFileInstance.get(run)?.clear();
55192
55938
  };
55939
+ wrappedFn.hasValue = async (...args) => {
55940
+ backtest.loggerService.info(CACHE_METHOD_NAME_FILE_HAS_VALUE);
55941
+ if (!MethodContextService.hasContext()) {
55942
+ throw new Error(`${CACHE_METHOD_NAME_FILE_HAS_VALUE} requires method context`);
55943
+ }
55944
+ if (!ExecutionContextService.hasContext()) {
55945
+ throw new Error(`${CACHE_METHOD_NAME_FILE_HAS_VALUE} requires execution context`);
55946
+ }
55947
+ const instance = this._getFileInstance(run, context.interval, context.name, context.key);
55948
+ return await instance.hasValue(...args);
55949
+ };
55193
55950
  return wrappedFn;
55194
55951
  };
55195
55952
  /**
@@ -55258,11 +56015,14 @@ const Cache = new CacheUtils();
55258
56015
 
55259
56016
  const INTERVAL_METHOD_NAME_RUN = "IntervalFnInstance.run";
55260
56017
  const INTERVAL_FILE_INSTANCE_METHOD_NAME_RUN = "IntervalFileInstance.run";
56018
+ const INTERVAL_FILE_INSTANCE_METHOD_NAME_HAS_VALUE = "IntervalFileInstance.hasValue";
55261
56019
  const INTERVAL_METHOD_NAME_FN = "IntervalUtils.fn";
55262
56020
  const INTERVAL_METHOD_NAME_FN_CLEAR = "IntervalUtils.fn.clear";
55263
56021
  const INTERVAL_METHOD_NAME_FN_GC = "IntervalUtils.fn.gc";
56022
+ const INTERVAL_METHOD_NAME_FN_HAS_VALUE = "IntervalUtils.fn.hasValue";
55264
56023
  const INTERVAL_METHOD_NAME_FILE = "IntervalUtils.file";
55265
56024
  const INTERVAL_METHOD_NAME_FILE_CLEAR = "IntervalUtils.file.clear";
56025
+ const INTERVAL_METHOD_NAME_FILE_HAS_VALUE = "IntervalUtils.file.hasValue";
55266
56026
  const INTERVAL_METHOD_NAME_DISPOSE = "IntervalUtils.dispose";
55267
56027
  const INTERVAL_METHOD_NAME_CLEAR = "IntervalUtils.clear";
55268
56028
  const INTERVAL_METHOD_NAME_RESET_COUNTER = "IntervalUtils.resetCounter";
@@ -55420,6 +56180,31 @@ class IntervalFnInstance {
55420
56180
  }
55421
56181
  }
55422
56182
  };
56183
+ /**
56184
+ * Check whether the function has already fired for the current interval and context.
56185
+ *
56186
+ * Returns `true` if the function fired (non-null result) within the current interval boundary.
56187
+ * Returns `false` if there is no recorded firing for this interval.
56188
+ *
56189
+ * Requires active method context and execution context.
56190
+ *
56191
+ * @param args - Arguments to look up in the state map
56192
+ * @returns `true` if the function has already fired this interval, `false` otherwise
56193
+ */
56194
+ this.hasValue = (...args) => {
56195
+ if (!MethodContextService.hasContext()) {
56196
+ throw new Error("IntervalFnInstance hasValue requires method context");
56197
+ }
56198
+ if (!ExecutionContextService.hasContext()) {
56199
+ throw new Error("IntervalFnInstance hasValue requires execution context");
56200
+ }
56201
+ const contextKey = CREATE_KEY_FN(backtest.methodContextService.context.strategyName, backtest.methodContextService.context.exchangeName, backtest.methodContextService.context.frameName, backtest.executionContextService.context.backtest);
56202
+ const currentWhen = backtest.executionContextService.context.when;
56203
+ const currentAligned = align(currentWhen.getTime(), this.interval);
56204
+ const argKey = this.key(args);
56205
+ const stateKey = `${contextKey}:${argKey}`;
56206
+ return this._stateMap.get(stateKey) === currentAligned;
56207
+ };
55423
56208
  /**
55424
56209
  * Garbage collect expired state entries.
55425
56210
  *
@@ -55541,6 +56326,33 @@ class IntervalFileInstance {
55541
56326
  }
55542
56327
  return result;
55543
56328
  };
56329
+ /**
56330
+ * Check whether the function has already fired for the current interval on disk.
56331
+ *
56332
+ * Returns `true` if a persisted record exists for the current aligned timestamp.
56333
+ * Returns `false` if no record is found.
56334
+ *
56335
+ * Requires active execution context and method context.
56336
+ *
56337
+ * @param args - Arguments forwarded to the key generator
56338
+ * @returns `true` if a fired record exists, `false` otherwise
56339
+ */
56340
+ this.hasValue = async (...args) => {
56341
+ backtest.loggerService.debug(INTERVAL_FILE_INSTANCE_METHOD_NAME_HAS_VALUE, { args });
56342
+ if (!MethodContextService.hasContext()) {
56343
+ throw new Error("IntervalFileInstance hasValue requires method context");
56344
+ }
56345
+ if (!ExecutionContextService.hasContext()) {
56346
+ throw new Error("IntervalFileInstance hasValue requires execution context");
56347
+ }
56348
+ const [symbol, ...rest] = args;
56349
+ const { when } = backtest.executionContextService.context;
56350
+ const alignedMs = align(when.getTime(), this.interval);
56351
+ const bucket = `${this.name}_${this.interval}_${this.index}`;
56352
+ const entityKey = this.key([symbol, alignedMs, ...rest]);
56353
+ const cached = await PersistIntervalAdapter.readIntervalData(bucket, entityKey);
56354
+ return cached !== null;
56355
+ };
55544
56356
  /**
55545
56357
  * Soft-delete all persisted records for this instance's bucket.
55546
56358
  * After this call the function will fire again on the next `run()`.
@@ -55621,23 +56433,30 @@ class IntervalUtils {
55621
56433
  wrappedFn.clear = () => {
55622
56434
  backtest.loggerService.info(INTERVAL_METHOD_NAME_FN_CLEAR);
55623
56435
  if (!MethodContextService.hasContext()) {
55624
- backtest.loggerService.warn(`${INTERVAL_METHOD_NAME_FN_CLEAR} called without method context, skipping`);
55625
- return;
56436
+ throw new Error(`${INTERVAL_METHOD_NAME_FN_CLEAR} requires method context`);
55626
56437
  }
55627
56438
  if (!ExecutionContextService.hasContext()) {
55628
- backtest.loggerService.warn(`${INTERVAL_METHOD_NAME_FN_CLEAR} called without execution context, skipping`);
55629
- return;
56439
+ throw new Error(`${INTERVAL_METHOD_NAME_FN_CLEAR} requires execution context`);
55630
56440
  }
55631
56441
  this._getInstance.get(run)?.clear();
55632
56442
  };
55633
56443
  wrappedFn.gc = () => {
55634
56444
  backtest.loggerService.info(INTERVAL_METHOD_NAME_FN_GC);
55635
56445
  if (!ExecutionContextService.hasContext()) {
55636
- backtest.loggerService.warn(`${INTERVAL_METHOD_NAME_FN_GC} called without execution context, skipping`);
55637
- return;
56446
+ throw new Error(`${INTERVAL_METHOD_NAME_FN_GC} requires execution context`);
55638
56447
  }
55639
56448
  return this._getInstance.get(run)?.gc();
55640
56449
  };
56450
+ wrappedFn.hasValue = (...args) => {
56451
+ backtest.loggerService.info(INTERVAL_METHOD_NAME_FN_HAS_VALUE);
56452
+ if (!MethodContextService.hasContext()) {
56453
+ throw new Error(`${INTERVAL_METHOD_NAME_FN_HAS_VALUE} requires method context`);
56454
+ }
56455
+ if (!ExecutionContextService.hasContext()) {
56456
+ throw new Error(`${INTERVAL_METHOD_NAME_FN_HAS_VALUE} requires execution context`);
56457
+ }
56458
+ return this._getInstance.get(run)?.hasValue(...args) ?? false;
56459
+ };
55641
56460
  return wrappedFn;
55642
56461
  };
55643
56462
  /**
@@ -55680,8 +56499,25 @@ class IntervalUtils {
55680
56499
  };
55681
56500
  wrappedFn.clear = async () => {
55682
56501
  backtest.loggerService.info(INTERVAL_METHOD_NAME_FILE_CLEAR);
56502
+ if (!MethodContextService.hasContext()) {
56503
+ throw new Error(`${INTERVAL_METHOD_NAME_FILE_CLEAR} requires method context`);
56504
+ }
56505
+ if (!ExecutionContextService.hasContext()) {
56506
+ throw new Error(`${INTERVAL_METHOD_NAME_FILE_CLEAR} requires execution context`);
56507
+ }
55683
56508
  await this._getFileInstance.get(run)?.clear();
55684
56509
  };
56510
+ wrappedFn.hasValue = async (...args) => {
56511
+ backtest.loggerService.info(INTERVAL_METHOD_NAME_FILE_HAS_VALUE);
56512
+ if (!MethodContextService.hasContext()) {
56513
+ throw new Error(`${INTERVAL_METHOD_NAME_FILE_HAS_VALUE} requires method context`);
56514
+ }
56515
+ if (!ExecutionContextService.hasContext()) {
56516
+ throw new Error(`${INTERVAL_METHOD_NAME_FILE_HAS_VALUE} requires execution context`);
56517
+ }
56518
+ const instance = this._getFileInstance(run, context.interval, context.name, context.key);
56519
+ return await instance.hasValue(...args);
56520
+ };
55685
56521
  return wrappedFn;
55686
56522
  };
55687
56523
  /**
@@ -56855,4 +57691,4 @@ const validateSignal = (signal, currentPrice) => {
56855
57691
  return !errors.length;
56856
57692
  };
56857
57693
 
56858
- export { ActionBase, Backtest, Breakeven, Broker, BrokerBase, Cache, Constant, Dump, Exchange, ExecutionContextService, Heat, HighestProfit, Interval, Live, Log, Markdown, MarkdownFileBase, MarkdownFolderBase, MarkdownWriter, MaxDrawdown, Memory, MethodContextService, Notification, NotificationBacktest, NotificationLive, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistCandleAdapter, PersistIntervalAdapter, PersistLogAdapter, PersistMeasureAdapter, PersistMemoryAdapter, PersistNotificationAdapter, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PersistStorageAdapter, Position, PositionSize, Reflect$1 as Reflect, Report, ReportBase, ReportWriter, Risk, Schedule, Storage, StorageBacktest, StorageLive, Strategy, Sync, Walker, addActionSchema, addExchangeSchema, addFrameSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, alignToInterval, checkCandles, commitActivateScheduled, commitAverageBuy, commitBreakeven, commitCancelScheduled, commitClosePending, commitPartialLoss, commitPartialLossCost, commitPartialProfit, commitPartialProfitCost, commitTrailingStop, commitTrailingStopCost, commitTrailingTake, commitTrailingTakeCost, dumpAgentAnswer, dumpError, dumpJson, dumpRecord, dumpTable, dumpText, emitters, formatPrice, formatQuantity, get, getActionSchema, getAggregatedTrades, getAveragePrice, getBacktestTimeframe, getBreakeven, getCandles, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getEffectivePriceOpen, getExchangeSchema, getFrameSchema, getMaxDrawdownDistancePnlCost, getMaxDrawdownDistancePnlPercentage, getMode, getNextCandles, getOrderBook, getPendingSignal, 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, getRawCandles, getRiskSchema, getScheduledSignal, getSizingSchema, getStrategySchema, getSymbol, getTimestamp, getTotalClosed, getTotalCostClosed, getTotalPercentClosed, getWalkerSchema, hasNoPendingSignal, hasNoScheduledSignal, hasTradeContext, investedCostToPercent, backtest as lib, listExchangeSchema, listFrameSchema, listMemory, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenBacktestProgress, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenHighestProfit, listenHighestProfitOnce, listenMaxDrawdown, listenMaxDrawdownOnce, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, 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, shutdown, slPercentShiftToPrice, slPriceToPercentShift, stopStrategy, toProfitLossDto, tpPercentShiftToPrice, tpPriceToPercentShift, validate, validateCommonSignal, validatePendingSignal, validateScheduledSignal, validateSignal, waitForCandle, warmCandles, writeMemory };
57694
+ export { ActionBase, Backtest, Breakeven, Broker, BrokerBase, Cache, Constant, Dump, Exchange, ExecutionContextService, Heat, HighestProfit, Interval, Live, Log, Markdown, MarkdownFileBase, MarkdownFolderBase, MarkdownWriter, MaxDrawdown, Memory, MethodContextService, Notification, NotificationBacktest, NotificationLive, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistCandleAdapter, PersistIntervalAdapter, PersistLogAdapter, PersistMeasureAdapter, PersistMemoryAdapter, PersistNotificationAdapter, PersistPartialAdapter, PersistRecentAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PersistStorageAdapter, Position, PositionSize, Recent, RecentBacktest, RecentLive, Reflect$1 as Reflect, Report, ReportBase, ReportWriter, Risk, Schedule, Session, Storage, StorageBacktest, StorageLive, Strategy, Sync, Walker, addActionSchema, addExchangeSchema, addFrameSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, alignToInterval, checkCandles, commitActivateScheduled, commitAverageBuy, commitBreakeven, commitCancelScheduled, commitClosePending, commitPartialLoss, commitPartialLossCost, commitPartialProfit, commitPartialProfitCost, commitTrailingStop, commitTrailingStopCost, commitTrailingTake, commitTrailingTakeCost, dumpAgentAnswer, dumpError, dumpJson, dumpRecord, dumpTable, dumpText, emitters, formatPrice, formatQuantity, get, getActionSchema, getAggregatedTrades, getAveragePrice, getBacktestTimeframe, getBreakeven, getCandles, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getEffectivePriceOpen, getExchangeSchema, getFrameSchema, getLatestSignal, getMaxDrawdownDistancePnlCost, getMaxDrawdownDistancePnlPercentage, getMode, getNextCandles, getOrderBook, getPendingSignal, 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, getRawCandles, getRiskSchema, getScheduledSignal, getSizingSchema, getStrategySchema, getSymbol, getTimestamp, getTotalClosed, getTotalCostClosed, getTotalPercentClosed, getWalkerSchema, hasNoPendingSignal, hasNoScheduledSignal, hasTradeContext, investedCostToPercent, backtest as lib, listExchangeSchema, listFrameSchema, listMemory, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenBacktestProgress, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenHighestProfit, listenHighestProfitOnce, listenMaxDrawdown, listenMaxDrawdownOnce, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, 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, shutdown, slPercentShiftToPrice, slPriceToPercentShift, stopStrategy, toProfitLossDto, tpPercentShiftToPrice, tpPriceToPercentShift, validate, validateCommonSignal, validatePendingSignal, validateScheduledSignal, validateSignal, waitForCandle, warmCandles, writeMemory };