backtest-kit 6.11.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.
Files changed (4) hide show
  1. package/build/index.cjs +2081 -183
  2. package/build/index.mjs +2073 -184
  3. package/package.json +2 -2
  4. package/types.d.ts +1136 -85
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;
@@ -6573,7 +6688,7 @@ const RETURN_IDLE_FN = async (self, currentPrice) => {
6573
6688
  await CALL_TICK_CALLBACKS_FN(self, self.params.execution.context.symbol, result, currentTime, self.params.execution.context.backtest);
6574
6689
  return result;
6575
6690
  };
6576
- const CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN = async (self, scheduled, averagePrice, closeTimestamp, reason, cancelId) => {
6691
+ const CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN = async (self, scheduled, averagePrice, closeTimestamp, reason, cancelId, cancelNote) => {
6577
6692
  self.params.logger.info("ClientStrategy backtest scheduled signal cancelled", {
6578
6693
  symbol: self.params.execution.context.symbol,
6579
6694
  signalId: scheduled.id,
@@ -6598,7 +6713,7 @@ const CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN = async (self, scheduled, averagePr
6598
6713
  totalPartials: scheduled._partial?.length ?? 0,
6599
6714
  originalPriceOpen: scheduled.priceOpen,
6600
6715
  pnl: toProfitLossDto(scheduled, averagePrice),
6601
- note: scheduled.note,
6716
+ note: cancelNote ?? scheduled.note,
6602
6717
  });
6603
6718
  }
6604
6719
  await CALL_CANCEL_CALLBACKS_FN(self, self.params.execution.context.symbol, scheduled, averagePrice, closeTimestamp, self.params.execution.context.backtest);
@@ -6765,7 +6880,7 @@ const CLOSE_USER_PENDING_SIGNAL_IN_BACKTEST_FN = async (self, closedSignal, aver
6765
6880
  totalPartials: closedSignal._partial?.length ?? 0,
6766
6881
  originalPriceOpen: closedSignal.priceOpen,
6767
6882
  pnl: toProfitLossDto(closedSignal, averagePrice),
6768
- note: closedSignal.note,
6883
+ note: closedSignal.closeNote ?? closedSignal.note,
6769
6884
  });
6770
6885
  await CALL_CLOSE_CALLBACKS_FN(self, self.params.execution.context.symbol, closedSignal, averagePrice, closeTimestamp, self.params.execution.context.backtest);
6771
6886
  await CALL_PARTIAL_CLEAR_FN(self, self.params.execution.context.symbol, closedSignal, averagePrice, closeTimestamp, self.params.execution.context.backtest);
@@ -6812,7 +6927,8 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles, fra
6812
6927
  if (self._cancelledSignal) {
6813
6928
  // Сигнал был отменен через cancel() в onSchedulePing
6814
6929
  const cancelId = self._cancelledSignal.cancelId;
6815
- const result = await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, averagePrice, candle.timestamp, "user", cancelId);
6930
+ const cancelNote = self._cancelledSignal.cancelNote;
6931
+ const result = await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, averagePrice, candle.timestamp, "user", cancelId, cancelNote);
6816
6932
  return { outcome: "cancelled", result };
6817
6933
  }
6818
6934
  // КРИТИЧНО: Проверяем был ли сигнал активирован пользователем через activateScheduled()
@@ -6866,7 +6982,7 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles, fra
6866
6982
  totalPartials: activatedSignal._partial?.length ?? 0,
6867
6983
  originalPriceOpen: activatedSignal.priceOpen,
6868
6984
  pnl: toProfitLossDto(activatedSignal, averagePrice),
6869
- note: activatedSignal.note,
6985
+ note: activatedSignal.activateNote ?? activatedSignal.note,
6870
6986
  });
6871
6987
  return { outcome: "pending" };
6872
6988
  }
@@ -6898,7 +7014,7 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles, fra
6898
7014
  pendingAt: publicSignalForCommit.pendingAt,
6899
7015
  totalEntries: publicSignalForCommit.totalEntries,
6900
7016
  totalPartials: publicSignalForCommit.totalPartials,
6901
- note: publicSignalForCommit.note,
7017
+ note: activatedSignal.activateNote ?? publicSignalForCommit.note,
6902
7018
  });
6903
7019
  await CALL_OPEN_CALLBACKS_FN(self, self.params.execution.context.symbol, pendingSignal, pendingSignal.priceOpen, candle.timestamp, self.params.execution.context.backtest);
6904
7020
  await CALL_BACKTEST_SCHEDULE_OPEN_FN(self, self.params.execution.context.symbol, pendingSignal, candle.timestamp, self.params.execution.context.backtest);
@@ -8019,6 +8135,44 @@ class ClientStrategy {
8019
8135
  const currentPnl = toProfitLossDto(this._pendingSignal, currentPrice);
8020
8136
  return Math.max(0, currentPnl.pnlCost - this._pendingSignal._fall.pnlCost);
8021
8137
  }
8138
+ /**
8139
+ * Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
8140
+ *
8141
+ * Measures the total swing from the stored `_peak.pnlPercentage` to the stored `_fall.pnlPercentage`.
8142
+ * Computed as: max(0, peakPnlPercentage - fallPnlPercentage).
8143
+ *
8144
+ * Returns null if no pending signal exists.
8145
+ *
8146
+ * @param symbol - Trading pair symbol
8147
+ * @param currentPrice - Current market price
8148
+ * @returns Promise resolving to peak-to-trough PnL percentage distance (≥ 0) or null
8149
+ */
8150
+ async getMaxDrawdownDistancePnlPercentage(symbol, currentPrice) {
8151
+ this.params.logger.debug("ClientStrategy getMaxDrawdownDistancePnlPercentage", { symbol, currentPrice });
8152
+ if (!this._pendingSignal) {
8153
+ return null;
8154
+ }
8155
+ return Math.max(0, this._pendingSignal._peak.pnlPercentage - this._pendingSignal._fall.pnlPercentage);
8156
+ }
8157
+ /**
8158
+ * Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
8159
+ *
8160
+ * Measures the total swing from the stored `_peak.pnlCost` to the stored `_fall.pnlCost`.
8161
+ * Computed as: max(0, peakPnlCost - fallPnlCost).
8162
+ *
8163
+ * Returns null if no pending signal exists.
8164
+ *
8165
+ * @param symbol - Trading pair symbol
8166
+ * @param currentPrice - Current market price
8167
+ * @returns Promise resolving to peak-to-trough PnL cost distance (≥ 0) or null
8168
+ */
8169
+ async getMaxDrawdownDistancePnlCost(symbol, currentPrice) {
8170
+ this.params.logger.debug("ClientStrategy getMaxDrawdownDistancePnlCost", { symbol, currentPrice });
8171
+ if (!this._pendingSignal) {
8172
+ return null;
8173
+ }
8174
+ return Math.max(0, this._pendingSignal._peak.pnlCost - this._pendingSignal._fall.pnlCost);
8175
+ }
8022
8176
  /**
8023
8177
  * Performs a single tick of strategy execution.
8024
8178
  *
@@ -8083,7 +8237,7 @@ class ClientStrategy {
8083
8237
  totalPartials: cancelledSignal._partial?.length ?? 0,
8084
8238
  originalPriceOpen: cancelledSignal.priceOpen,
8085
8239
  pnl: toProfitLossDto(cancelledSignal, currentPrice),
8086
- note: cancelledSignal.note,
8240
+ note: cancelledSignal.cancelNote ?? cancelledSignal.note,
8087
8241
  });
8088
8242
  // Call onCancel callback
8089
8243
  await CALL_CANCEL_CALLBACKS_FN(this, this.params.execution.context.symbol, cancelledSignal, currentPrice, currentTime, this.params.execution.context.backtest);
@@ -8137,7 +8291,7 @@ class ClientStrategy {
8137
8291
  totalPartials: closedSignal._partial?.length ?? 0,
8138
8292
  originalPriceOpen: closedSignal.priceOpen,
8139
8293
  pnl: toProfitLossDto(closedSignal, currentPrice),
8140
- note: closedSignal.note,
8294
+ note: closedSignal.closeNote ?? closedSignal.note,
8141
8295
  });
8142
8296
  // Call onClose callback
8143
8297
  await CALL_CLOSE_CALLBACKS_FN(this, this.params.execution.context.symbol, closedSignal, currentPrice, currentTime, this.params.execution.context.backtest);
@@ -8219,7 +8373,7 @@ class ClientStrategy {
8219
8373
  totalPartials: activatedSignal._partial?.length ?? 0,
8220
8374
  originalPriceOpen: activatedSignal.priceOpen,
8221
8375
  pnl: toProfitLossDto(activatedSignal, currentPrice),
8222
- note: activatedSignal.note,
8376
+ note: activatedSignal.activateNote ?? activatedSignal.note,
8223
8377
  });
8224
8378
  return await RETURN_IDLE_FN(this, currentPrice);
8225
8379
  }
@@ -8250,7 +8404,7 @@ class ClientStrategy {
8250
8404
  pendingAt: publicSignalForCommit.pendingAt,
8251
8405
  totalEntries: publicSignalForCommit.totalEntries,
8252
8406
  totalPartials: publicSignalForCommit.totalPartials,
8253
- note: publicSignalForCommit.note,
8407
+ note: activatedSignal.activateNote ?? publicSignalForCommit.note,
8254
8408
  });
8255
8409
  // Call onOpen callback
8256
8410
  await CALL_OPEN_CALLBACKS_FN(this, this.params.execution.context.symbol, pendingSignal, currentPrice, currentTime, this.params.execution.context.backtest);
@@ -8386,7 +8540,7 @@ class ClientStrategy {
8386
8540
  totalPartials: cancelledSignal._partial?.length ?? 0,
8387
8541
  originalPriceOpen: cancelledSignal.priceOpen,
8388
8542
  pnl: toProfitLossDto(cancelledSignal, currentPrice),
8389
- note: cancelledSignal.note,
8543
+ note: cancelledSignal.cancelNote ?? cancelledSignal.note,
8390
8544
  });
8391
8545
  await CALL_CANCEL_CALLBACKS_FN(this, this.params.execution.context.symbol, cancelledSignal, currentPrice, closeTimestamp, this.params.execution.context.backtest);
8392
8546
  const cancelledResult = {
@@ -8441,7 +8595,7 @@ class ClientStrategy {
8441
8595
  totalPartials: closedSignal._partial?.length ?? 0,
8442
8596
  originalPriceOpen: closedSignal.priceOpen,
8443
8597
  pnl: toProfitLossDto(closedSignal, currentPrice),
8444
- note: closedSignal.note,
8598
+ note: closedSignal.closeNote ?? closedSignal.note,
8445
8599
  });
8446
8600
  await CALL_CLOSE_CALLBACKS_FN(this, this.params.execution.context.symbol, closedSignal, currentPrice, closeTimestamp, this.params.execution.context.backtest);
8447
8601
  // КРИТИЧНО: Очищаем состояние ClientPartial при закрытии позиции
@@ -8625,7 +8779,8 @@ class ClientStrategy {
8625
8779
  * // Strategy continues, can generate new signals
8626
8780
  * ```
8627
8781
  */
8628
- async cancelScheduled(symbol, backtest, cancelId) {
8782
+ async cancelScheduled(symbol, backtest, payload) {
8783
+ const cancelId = payload.id;
8629
8784
  this.params.logger.debug("ClientStrategy cancelScheduled", {
8630
8785
  symbol,
8631
8786
  hasScheduledSignal: this._scheduledSignal !== null,
@@ -8637,6 +8792,7 @@ class ClientStrategy {
8637
8792
  if (this._scheduledSignal) {
8638
8793
  this._cancelledSignal = Object.assign({}, this._scheduledSignal, {
8639
8794
  cancelId,
8795
+ cancelNote: payload.note,
8640
8796
  });
8641
8797
  this._scheduledSignal = null;
8642
8798
  }
@@ -8668,7 +8824,8 @@ class ClientStrategy {
8668
8824
  * // Scheduled signal becomes pending signal immediately
8669
8825
  * ```
8670
8826
  */
8671
- async activateScheduled(symbol, backtest, activateId) {
8827
+ async activateScheduled(symbol, backtest, payload) {
8828
+ const activateId = payload.id;
8672
8829
  this.params.logger.debug("ClientStrategy activateScheduled", {
8673
8830
  symbol,
8674
8831
  hasScheduledSignal: this._scheduledSignal !== null,
@@ -8686,6 +8843,7 @@ class ClientStrategy {
8686
8843
  if (this._scheduledSignal) {
8687
8844
  this._activatedSignal = Object.assign({}, this._scheduledSignal, {
8688
8845
  activateId,
8846
+ activateNote: payload.note,
8689
8847
  });
8690
8848
  this._scheduledSignal = null;
8691
8849
  }
@@ -8717,7 +8875,8 @@ class ClientStrategy {
8717
8875
  * // Strategy continues, can generate new signals
8718
8876
  * ```
8719
8877
  */
8720
- async closePending(symbol, backtest, closeId) {
8878
+ async closePending(symbol, backtest, payload) {
8879
+ const closeId = payload.id;
8721
8880
  this.params.logger.debug("ClientStrategy closePending", {
8722
8881
  symbol,
8723
8882
  hasPendingSignal: this._pendingSignal !== null,
@@ -8728,6 +8887,7 @@ class ClientStrategy {
8728
8887
  if (this._pendingSignal) {
8729
8888
  this._closedSignal = Object.assign({}, this._pendingSignal, {
8730
8889
  closeId,
8890
+ closeNote: payload.note,
8731
8891
  });
8732
8892
  this._pendingSignal = null;
8733
8893
  }
@@ -10110,7 +10270,7 @@ const GET_RISK_FN = (dto, backtest, exchangeName, frameName, self) => {
10110
10270
  * @param backtest - Whether running in backtest mode
10111
10271
  * @returns Unique string key for memoization
10112
10272
  */
10113
- const CREATE_KEY_FN$t = (symbol, strategyName, exchangeName, frameName, backtest) => {
10273
+ const CREATE_KEY_FN$u = (symbol, strategyName, exchangeName, frameName, backtest) => {
10114
10274
  const parts = [symbol, strategyName, exchangeName];
10115
10275
  if (frameName)
10116
10276
  parts.push(frameName);
@@ -10377,7 +10537,7 @@ class StrategyConnectionService {
10377
10537
  * @param backtest - Whether running in backtest mode
10378
10538
  * @returns Configured ClientStrategy instance
10379
10539
  */
10380
- 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) => {
10381
10541
  const { riskName = "", riskList = [], getSignal, interval = STRATEGY_DEFAULT_INTERVAL, callbacks, } = this.strategySchemaService.get(strategyName);
10382
10542
  return new ClientStrategy({
10383
10543
  symbol,
@@ -11218,6 +11378,48 @@ class StrategyConnectionService {
11218
11378
  const currentPrice = await this.priceMetaService.getCurrentPrice(symbol, context, backtest);
11219
11379
  return await strategy.getPositionHighestMaxDrawdownPnlCost(symbol, currentPrice);
11220
11380
  };
11381
+ /**
11382
+ * Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
11383
+ *
11384
+ * Resolves current price via priceMetaService and delegates to
11385
+ * ClientStrategy.getMaxDrawdownDistancePnlPercentage().
11386
+ * Returns null if no pending signal exists.
11387
+ *
11388
+ * @param backtest - Whether running in backtest mode
11389
+ * @param symbol - Trading pair symbol
11390
+ * @param context - Execution context with strategyName, exchangeName, frameName
11391
+ * @returns Promise resolving to peak-to-trough PnL percentage distance (≥ 0) or null
11392
+ */
11393
+ this.getMaxDrawdownDistancePnlPercentage = async (backtest, symbol, context) => {
11394
+ this.loggerService.log("strategyConnectionService getMaxDrawdownDistancePnlPercentage", {
11395
+ symbol,
11396
+ context,
11397
+ });
11398
+ const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
11399
+ const currentPrice = await this.priceMetaService.getCurrentPrice(symbol, context, backtest);
11400
+ return await strategy.getMaxDrawdownDistancePnlPercentage(symbol, currentPrice);
11401
+ };
11402
+ /**
11403
+ * Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
11404
+ *
11405
+ * Resolves current price via priceMetaService and delegates to
11406
+ * ClientStrategy.getMaxDrawdownDistancePnlCost().
11407
+ * Returns null if no pending signal exists.
11408
+ *
11409
+ * @param backtest - Whether running in backtest mode
11410
+ * @param symbol - Trading pair symbol
11411
+ * @param context - Execution context with strategyName, exchangeName, frameName
11412
+ * @returns Promise resolving to peak-to-trough PnL cost distance (≥ 0) or null
11413
+ */
11414
+ this.getMaxDrawdownDistancePnlCost = async (backtest, symbol, context) => {
11415
+ this.loggerService.log("strategyConnectionService getMaxDrawdownDistancePnlCost", {
11416
+ symbol,
11417
+ context,
11418
+ });
11419
+ const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
11420
+ const currentPrice = await this.priceMetaService.getCurrentPrice(symbol, context, backtest);
11421
+ return await strategy.getMaxDrawdownDistancePnlCost(symbol, currentPrice);
11422
+ };
11221
11423
  /**
11222
11424
  * Disposes the ClientStrategy instance for the given context.
11223
11425
  *
@@ -11256,7 +11458,7 @@ class StrategyConnectionService {
11256
11458
  }
11257
11459
  return;
11258
11460
  }
11259
- 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);
11260
11462
  if (!this.getStrategy.has(key)) {
11261
11463
  return;
11262
11464
  }
@@ -11277,17 +11479,17 @@ class StrategyConnectionService {
11277
11479
  * @param backtest - Whether running in backtest mode
11278
11480
  * @param symbol - Trading pair symbol
11279
11481
  * @param ctx - Context with strategyName, exchangeName, frameName
11280
- * @param cancelId - Optional cancellation ID for user-initiated cancellations
11482
+ * @param payload - Optional commit payload with id and note
11281
11483
  * @returns Promise that resolves when scheduled signal is cancelled
11282
11484
  */
11283
- this.cancelScheduled = async (backtest, symbol, context, cancelId) => {
11485
+ this.cancelScheduled = async (backtest, symbol, context, payload = {}) => {
11284
11486
  this.loggerService.log("strategyConnectionService cancelScheduled", {
11285
11487
  symbol,
11286
11488
  context,
11287
- cancelId,
11489
+ payload,
11288
11490
  });
11289
11491
  const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
11290
- await strategy.cancelScheduled(symbol, backtest, cancelId);
11492
+ await strategy.cancelScheduled(symbol, backtest, payload);
11291
11493
  };
11292
11494
  /**
11293
11495
  * Closes the pending signal without stopping the strategy.
@@ -11302,17 +11504,17 @@ class StrategyConnectionService {
11302
11504
  * @param backtest - Whether running in backtest mode
11303
11505
  * @param symbol - Trading pair symbol
11304
11506
  * @param context - Context with strategyName, exchangeName, frameName
11305
- * @param closeId - Optional close ID for user-initiated closes
11507
+ * @param payload - Optional commit payload with id and note
11306
11508
  * @returns Promise that resolves when pending signal is closed
11307
11509
  */
11308
- this.closePending = async (backtest, symbol, context, closeId) => {
11510
+ this.closePending = async (backtest, symbol, context, payload = {}) => {
11309
11511
  this.loggerService.log("strategyConnectionService closePending", {
11310
11512
  symbol,
11311
11513
  context,
11312
- closeId,
11514
+ payload,
11313
11515
  });
11314
11516
  const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
11315
- await strategy.closePending(symbol, backtest, closeId);
11517
+ await strategy.closePending(symbol, backtest, payload);
11316
11518
  };
11317
11519
  /**
11318
11520
  * Checks whether `partialProfit` would succeed without executing it.
@@ -11591,7 +11793,7 @@ class StrategyConnectionService {
11591
11793
  * @param backtest - Whether running in backtest mode
11592
11794
  * @param symbol - Trading pair symbol
11593
11795
  * @param context - Execution context with strategyName, exchangeName, frameName
11594
- * @param activateId - Optional identifier for the activation reason
11796
+ * @param payload - Optional commit payload with id and note
11595
11797
  * @returns Promise that resolves when activation flag is set
11596
11798
  *
11597
11799
  * @example
@@ -11601,19 +11803,19 @@ class StrategyConnectionService {
11601
11803
  * false,
11602
11804
  * "BTCUSDT",
11603
11805
  * { strategyName: "my-strategy", exchangeName: "binance", frameName: "" },
11604
- * "manual-activation"
11806
+ * { id: "manual-activation" }
11605
11807
  * );
11606
11808
  * ```
11607
11809
  */
11608
- this.activateScheduled = async (backtest, symbol, context, activateId) => {
11810
+ this.activateScheduled = async (backtest, symbol, context, payload = {}) => {
11609
11811
  this.loggerService.log("strategyConnectionService activateScheduled", {
11610
11812
  symbol,
11611
11813
  context,
11612
11814
  backtest,
11613
- activateId,
11815
+ payload,
11614
11816
  });
11615
11817
  const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
11616
- return await strategy.activateScheduled(symbol, backtest, activateId);
11818
+ return await strategy.activateScheduled(symbol, backtest, payload);
11617
11819
  };
11618
11820
  /**
11619
11821
  * Checks whether `averageBuy` would succeed without executing it.
@@ -12425,7 +12627,7 @@ class ClientRisk {
12425
12627
  * @param backtest - Whether running in backtest mode
12426
12628
  * @returns Unique string key for memoization
12427
12629
  */
12428
- const CREATE_KEY_FN$s = (riskName, exchangeName, frameName, backtest) => {
12630
+ const CREATE_KEY_FN$t = (riskName, exchangeName, frameName, backtest) => {
12429
12631
  const parts = [riskName, exchangeName];
12430
12632
  if (frameName)
12431
12633
  parts.push(frameName);
@@ -12525,7 +12727,7 @@ class RiskConnectionService {
12525
12727
  * @param backtest - True if backtest mode, false if live mode
12526
12728
  * @returns Configured ClientRisk instance
12527
12729
  */
12528
- 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) => {
12529
12731
  const schema = this.riskSchemaService.get(riskName);
12530
12732
  return new ClientRisk({
12531
12733
  ...schema,
@@ -12594,7 +12796,7 @@ class RiskConnectionService {
12594
12796
  payload,
12595
12797
  });
12596
12798
  if (payload) {
12597
- 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);
12598
12800
  this.getRisk.clear(key);
12599
12801
  }
12600
12802
  else {
@@ -13638,7 +13840,7 @@ class ClientAction {
13638
13840
  * @param backtest - Whether running in backtest mode
13639
13841
  * @returns Unique string key for memoization
13640
13842
  */
13641
- const CREATE_KEY_FN$r = (actionName, strategyName, exchangeName, frameName, backtest) => {
13843
+ const CREATE_KEY_FN$s = (actionName, strategyName, exchangeName, frameName, backtest) => {
13642
13844
  const parts = [actionName, strategyName, exchangeName];
13643
13845
  if (frameName)
13644
13846
  parts.push(frameName);
@@ -13690,7 +13892,7 @@ class ActionConnectionService {
13690
13892
  * @param backtest - True if backtest mode, false if live mode
13691
13893
  * @returns Configured ClientAction instance
13692
13894
  */
13693
- 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) => {
13694
13896
  const schema = this.actionSchemaService.get(actionName);
13695
13897
  return new ClientAction({
13696
13898
  ...schema,
@@ -13901,7 +14103,7 @@ class ActionConnectionService {
13901
14103
  await Promise.all(actions.map(async (action) => await action.dispose()));
13902
14104
  return;
13903
14105
  }
13904
- 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);
13905
14107
  if (!this.getAction.has(key)) {
13906
14108
  return;
13907
14109
  }
@@ -13919,7 +14121,7 @@ const METHOD_NAME_VALIDATE$2 = "exchangeCoreService validate";
13919
14121
  * @param exchangeName - Exchange name
13920
14122
  * @returns Unique string key for memoization
13921
14123
  */
13922
- const CREATE_KEY_FN$q = (exchangeName) => {
14124
+ const CREATE_KEY_FN$r = (exchangeName) => {
13923
14125
  return exchangeName;
13924
14126
  };
13925
14127
  /**
@@ -13943,7 +14145,7 @@ class ExchangeCoreService {
13943
14145
  * @param exchangeName - Name of the exchange to validate
13944
14146
  * @returns Promise that resolves when validation is complete
13945
14147
  */
13946
- this.validate = memoize(([exchangeName]) => CREATE_KEY_FN$q(exchangeName), async (exchangeName) => {
14148
+ this.validate = memoize(([exchangeName]) => CREATE_KEY_FN$r(exchangeName), async (exchangeName) => {
13947
14149
  this.loggerService.log(METHOD_NAME_VALIDATE$2, {
13948
14150
  exchangeName,
13949
14151
  });
@@ -14195,7 +14397,7 @@ const METHOD_NAME_VALIDATE$1 = "strategyCoreService validate";
14195
14397
  * @param context - Execution context with strategyName, exchangeName, frameName
14196
14398
  * @returns Unique string key for memoization
14197
14399
  */
14198
- const CREATE_KEY_FN$p = (context) => {
14400
+ const CREATE_KEY_FN$q = (context) => {
14199
14401
  const parts = [context.strategyName, context.exchangeName];
14200
14402
  if (context.frameName)
14201
14403
  parts.push(context.frameName);
@@ -14227,7 +14429,7 @@ class StrategyCoreService {
14227
14429
  * @param context - Execution context with strategyName, exchangeName, frameName
14228
14430
  * @returns Promise that resolves when validation is complete
14229
14431
  */
14230
- this.validate = memoize(([context]) => CREATE_KEY_FN$p(context), async (context) => {
14432
+ this.validate = memoize(([context]) => CREATE_KEY_FN$q(context), async (context) => {
14231
14433
  this.loggerService.log(METHOD_NAME_VALIDATE$1, {
14232
14434
  context,
14233
14435
  });
@@ -14648,18 +14850,18 @@ class StrategyCoreService {
14648
14850
  * @param backtest - Whether running in backtest mode
14649
14851
  * @param symbol - Trading pair symbol
14650
14852
  * @param ctx - Context with strategyName, exchangeName, frameName
14651
- * @param cancelId - Optional cancellation ID for user-initiated cancellations
14853
+ * @param payload - Optional commit payload with id and note
14652
14854
  * @returns Promise that resolves when scheduled signal is cancelled
14653
14855
  */
14654
- this.cancelScheduled = async (backtest, symbol, context, cancelId) => {
14856
+ this.cancelScheduled = async (backtest, symbol, context, payload = {}) => {
14655
14857
  this.loggerService.log("strategyCoreService cancelScheduled", {
14656
14858
  symbol,
14657
14859
  context,
14658
14860
  backtest,
14659
- cancelId,
14861
+ payload,
14660
14862
  });
14661
14863
  await this.validate(context);
14662
- return await this.strategyConnectionService.cancelScheduled(backtest, symbol, context, cancelId);
14864
+ return await this.strategyConnectionService.cancelScheduled(backtest, symbol, context, payload);
14663
14865
  };
14664
14866
  /**
14665
14867
  * Closes the pending signal without stopping the strategy.
@@ -14675,18 +14877,18 @@ class StrategyCoreService {
14675
14877
  * @param backtest - Whether running in backtest mode
14676
14878
  * @param symbol - Trading pair symbol
14677
14879
  * @param context - Context with strategyName, exchangeName, frameName
14678
- * @param closeId - Optional close ID for user-initiated closes
14880
+ * @param payload - Optional commit payload with id and note
14679
14881
  * @returns Promise that resolves when pending signal is closed
14680
14882
  */
14681
- this.closePending = async (backtest, symbol, context, closeId) => {
14883
+ this.closePending = async (backtest, symbol, context, payload = {}) => {
14682
14884
  this.loggerService.log("strategyCoreService closePending", {
14683
14885
  symbol,
14684
14886
  context,
14685
14887
  backtest,
14686
- closeId,
14888
+ payload,
14687
14889
  });
14688
14890
  await this.validate(context);
14689
- return await this.strategyConnectionService.closePending(backtest, symbol, context, closeId);
14891
+ return await this.strategyConnectionService.closePending(backtest, symbol, context, payload);
14690
14892
  };
14691
14893
  /**
14692
14894
  * Disposes the ClientStrategy instance for the given context.
@@ -15031,7 +15233,7 @@ class StrategyCoreService {
15031
15233
  * @param backtest - Whether running in backtest mode
15032
15234
  * @param symbol - Trading pair symbol
15033
15235
  * @param context - Execution context with strategyName, exchangeName, frameName
15034
- * @param activateId - Optional identifier for the activation reason
15236
+ * @param payload - Optional commit payload with id and note
15035
15237
  * @returns Promise that resolves when activation flag is set
15036
15238
  *
15037
15239
  * @example
@@ -15041,19 +15243,19 @@ class StrategyCoreService {
15041
15243
  * false,
15042
15244
  * "BTCUSDT",
15043
15245
  * { strategyName: "my-strategy", exchangeName: "binance", frameName: "" },
15044
- * "manual-activation"
15246
+ * { id: "manual-activation" }
15045
15247
  * );
15046
15248
  * ```
15047
15249
  */
15048
- this.activateScheduled = async (backtest, symbol, context, activateId) => {
15250
+ this.activateScheduled = async (backtest, symbol, context, payload = {}) => {
15049
15251
  this.loggerService.log("strategyCoreService activateScheduled", {
15050
15252
  symbol,
15051
15253
  context,
15052
15254
  backtest,
15053
- activateId,
15255
+ payload,
15054
15256
  });
15055
15257
  await this.validate(context);
15056
- return await this.strategyConnectionService.activateScheduled(backtest, symbol, context, activateId);
15258
+ return await this.strategyConnectionService.activateScheduled(backtest, symbol, context, payload);
15057
15259
  };
15058
15260
  /**
15059
15261
  * Checks whether `averageBuy` would succeed without executing it.
@@ -15455,6 +15657,44 @@ class StrategyCoreService {
15455
15657
  await this.validate(context);
15456
15658
  return await this.strategyConnectionService.getPositionHighestMaxDrawdownPnlCost(backtest, symbol, context);
15457
15659
  };
15660
+ /**
15661
+ * Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
15662
+ *
15663
+ * Delegates to StrategyConnectionService.getMaxDrawdownDistancePnlPercentage().
15664
+ * Returns null if no pending signal exists.
15665
+ *
15666
+ * @param backtest - Whether running in backtest mode
15667
+ * @param symbol - Trading pair symbol
15668
+ * @param context - Execution context with strategyName, exchangeName, frameName
15669
+ * @returns Promise resolving to peak-to-trough PnL percentage distance (≥ 0) or null
15670
+ */
15671
+ this.getMaxDrawdownDistancePnlPercentage = async (backtest, symbol, context) => {
15672
+ this.loggerService.log("strategyCoreService getMaxDrawdownDistancePnlPercentage", {
15673
+ symbol,
15674
+ context,
15675
+ });
15676
+ await this.validate(context);
15677
+ return await this.strategyConnectionService.getMaxDrawdownDistancePnlPercentage(backtest, symbol, context);
15678
+ };
15679
+ /**
15680
+ * Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
15681
+ *
15682
+ * Delegates to StrategyConnectionService.getMaxDrawdownDistancePnlCost().
15683
+ * Returns null if no pending signal exists.
15684
+ *
15685
+ * @param backtest - Whether running in backtest mode
15686
+ * @param symbol - Trading pair symbol
15687
+ * @param context - Execution context with strategyName, exchangeName, frameName
15688
+ * @returns Promise resolving to peak-to-trough PnL cost distance (≥ 0) or null
15689
+ */
15690
+ this.getMaxDrawdownDistancePnlCost = async (backtest, symbol, context) => {
15691
+ this.loggerService.log("strategyCoreService getMaxDrawdownDistancePnlCost", {
15692
+ symbol,
15693
+ context,
15694
+ });
15695
+ await this.validate(context);
15696
+ return await this.strategyConnectionService.getMaxDrawdownDistancePnlCost(backtest, symbol, context);
15697
+ };
15458
15698
  }
15459
15699
  }
15460
15700
 
@@ -15527,7 +15767,7 @@ class SizingGlobalService {
15527
15767
  * @param context - Context with riskName, exchangeName, frameName
15528
15768
  * @returns Unique string key for memoization
15529
15769
  */
15530
- const CREATE_KEY_FN$o = (context) => {
15770
+ const CREATE_KEY_FN$p = (context) => {
15531
15771
  const parts = [context.riskName, context.exchangeName];
15532
15772
  if (context.frameName)
15533
15773
  parts.push(context.frameName);
@@ -15553,7 +15793,7 @@ class RiskGlobalService {
15553
15793
  * @param payload - Payload with riskName, exchangeName and frameName
15554
15794
  * @returns Promise that resolves when validation is complete
15555
15795
  */
15556
- this.validate = memoize(([context]) => CREATE_KEY_FN$o(context), async (context) => {
15796
+ this.validate = memoize(([context]) => CREATE_KEY_FN$p(context), async (context) => {
15557
15797
  this.loggerService.log("riskGlobalService validate", {
15558
15798
  context,
15559
15799
  });
@@ -15631,7 +15871,7 @@ const METHOD_NAME_VALIDATE = "actionCoreService validate";
15631
15871
  * @param context - Execution context with strategyName, exchangeName, frameName
15632
15872
  * @returns Unique string key for memoization
15633
15873
  */
15634
- const CREATE_KEY_FN$n = (context) => {
15874
+ const CREATE_KEY_FN$o = (context) => {
15635
15875
  const parts = [context.strategyName, context.exchangeName];
15636
15876
  if (context.frameName)
15637
15877
  parts.push(context.frameName);
@@ -15675,7 +15915,7 @@ class ActionCoreService {
15675
15915
  * @param context - Strategy execution context with strategyName, exchangeName and frameName
15676
15916
  * @returns Promise that resolves when all validations complete
15677
15917
  */
15678
- this.validate = memoize(([context]) => CREATE_KEY_FN$n(context), async (context) => {
15918
+ this.validate = memoize(([context]) => CREATE_KEY_FN$o(context), async (context) => {
15679
15919
  this.loggerService.log(METHOD_NAME_VALIDATE, {
15680
15920
  context,
15681
15921
  });
@@ -20718,7 +20958,7 @@ const ReportWriter = new ReportWriterAdapter();
20718
20958
  * @param backtest - Whether running in backtest mode
20719
20959
  * @returns Unique string key for memoization
20720
20960
  */
20721
- const CREATE_KEY_FN$m = (symbol, strategyName, exchangeName, frameName, backtest) => {
20961
+ const CREATE_KEY_FN$n = (symbol, strategyName, exchangeName, frameName, backtest) => {
20722
20962
  const parts = [symbol, strategyName, exchangeName];
20723
20963
  if (frameName)
20724
20964
  parts.push(frameName);
@@ -20964,7 +21204,7 @@ class BacktestMarkdownService {
20964
21204
  * Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
20965
21205
  * Each combination gets its own isolated storage instance.
20966
21206
  */
20967
- 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));
20968
21208
  /**
20969
21209
  * Processes tick events and accumulates closed signals.
20970
21210
  * Should be called from IStrategyCallbacks.onTick.
@@ -21121,7 +21361,7 @@ class BacktestMarkdownService {
21121
21361
  payload,
21122
21362
  });
21123
21363
  if (payload) {
21124
- 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);
21125
21365
  this.getStorage.clear(key);
21126
21366
  }
21127
21367
  else {
@@ -21183,7 +21423,7 @@ class BacktestMarkdownService {
21183
21423
  * @param backtest - Whether running in backtest mode
21184
21424
  * @returns Unique string key for memoization
21185
21425
  */
21186
- const CREATE_KEY_FN$l = (symbol, strategyName, exchangeName, frameName, backtest) => {
21426
+ const CREATE_KEY_FN$m = (symbol, strategyName, exchangeName, frameName, backtest) => {
21187
21427
  const parts = [symbol, strategyName, exchangeName];
21188
21428
  if (frameName)
21189
21429
  parts.push(frameName);
@@ -21678,7 +21918,7 @@ class LiveMarkdownService {
21678
21918
  * Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
21679
21919
  * Each combination gets its own isolated storage instance.
21680
21920
  */
21681
- 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));
21682
21922
  /**
21683
21923
  * Subscribes to live signal emitter to receive tick events.
21684
21924
  * Protected against multiple subscriptions.
@@ -21896,7 +22136,7 @@ class LiveMarkdownService {
21896
22136
  payload,
21897
22137
  });
21898
22138
  if (payload) {
21899
- 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);
21900
22140
  this.getStorage.clear(key);
21901
22141
  }
21902
22142
  else {
@@ -21916,7 +22156,7 @@ class LiveMarkdownService {
21916
22156
  * @param backtest - Whether running in backtest mode
21917
22157
  * @returns Unique string key for memoization
21918
22158
  */
21919
- const CREATE_KEY_FN$k = (symbol, strategyName, exchangeName, frameName, backtest) => {
22159
+ const CREATE_KEY_FN$l = (symbol, strategyName, exchangeName, frameName, backtest) => {
21920
22160
  const parts = [symbol, strategyName, exchangeName];
21921
22161
  if (frameName)
21922
22162
  parts.push(frameName);
@@ -22205,7 +22445,7 @@ class ScheduleMarkdownService {
22205
22445
  * Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
22206
22446
  * Each combination gets its own isolated storage instance.
22207
22447
  */
22208
- 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));
22209
22449
  /**
22210
22450
  * Subscribes to signal emitter to receive scheduled signal events.
22211
22451
  * Protected against multiple subscriptions.
@@ -22408,7 +22648,7 @@ class ScheduleMarkdownService {
22408
22648
  payload,
22409
22649
  });
22410
22650
  if (payload) {
22411
- 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);
22412
22652
  this.getStorage.clear(key);
22413
22653
  }
22414
22654
  else {
@@ -22428,7 +22668,7 @@ class ScheduleMarkdownService {
22428
22668
  * @param backtest - Whether running in backtest mode
22429
22669
  * @returns Unique string key for memoization
22430
22670
  */
22431
- const CREATE_KEY_FN$j = (symbol, strategyName, exchangeName, frameName, backtest) => {
22671
+ const CREATE_KEY_FN$k = (symbol, strategyName, exchangeName, frameName, backtest) => {
22432
22672
  const parts = [symbol, strategyName, exchangeName];
22433
22673
  if (frameName)
22434
22674
  parts.push(frameName);
@@ -22673,7 +22913,7 @@ class PerformanceMarkdownService {
22673
22913
  * Memoized function to get or create PerformanceStorage for a symbol-strategy-exchange-frame-backtest combination.
22674
22914
  * Each combination gets its own isolated storage instance.
22675
22915
  */
22676
- 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));
22677
22917
  /**
22678
22918
  * Subscribes to performance emitter to receive performance events.
22679
22919
  * Protected against multiple subscriptions.
@@ -22840,7 +23080,7 @@ class PerformanceMarkdownService {
22840
23080
  payload,
22841
23081
  });
22842
23082
  if (payload) {
22843
- 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);
22844
23084
  this.getStorage.clear(key);
22845
23085
  }
22846
23086
  else {
@@ -23319,7 +23559,7 @@ class WalkerMarkdownService {
23319
23559
  * @param backtest - Whether running in backtest mode
23320
23560
  * @returns Unique string key for memoization
23321
23561
  */
23322
- const CREATE_KEY_FN$i = (exchangeName, frameName, backtest) => {
23562
+ const CREATE_KEY_FN$j = (exchangeName, frameName, backtest) => {
23323
23563
  const parts = [exchangeName];
23324
23564
  if (frameName)
23325
23565
  parts.push(frameName);
@@ -23766,7 +24006,7 @@ class HeatMarkdownService {
23766
24006
  * Memoized function to get or create HeatmapStorage for exchange, frame and backtest mode.
23767
24007
  * Each exchangeName + frameName + backtest mode combination gets its own isolated heatmap storage instance.
23768
24008
  */
23769
- 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));
23770
24010
  /**
23771
24011
  * Subscribes to signal emitter to receive tick events.
23772
24012
  * Protected against multiple subscriptions.
@@ -23984,7 +24224,7 @@ class HeatMarkdownService {
23984
24224
  payload,
23985
24225
  });
23986
24226
  if (payload) {
23987
- 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);
23988
24228
  this.getStorage.clear(key);
23989
24229
  }
23990
24230
  else {
@@ -25015,7 +25255,7 @@ class ClientPartial {
25015
25255
  * @param backtest - Whether running in backtest mode
25016
25256
  * @returns Unique string key for memoization
25017
25257
  */
25018
- const CREATE_KEY_FN$h = (signalId, backtest) => `${signalId}:${backtest ? "backtest" : "live"}`;
25258
+ const CREATE_KEY_FN$i = (signalId, backtest) => `${signalId}:${backtest ? "backtest" : "live"}`;
25019
25259
  /**
25020
25260
  * Creates a callback function for emitting profit events to partialProfitSubject.
25021
25261
  *
@@ -25137,7 +25377,7 @@ class PartialConnectionService {
25137
25377
  * Key format: "signalId:backtest" or "signalId:live"
25138
25378
  * Value: ClientPartial instance with logger and event emitters
25139
25379
  */
25140
- 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) => {
25141
25381
  return new ClientPartial({
25142
25382
  signalId,
25143
25383
  logger: this.loggerService,
@@ -25227,7 +25467,7 @@ class PartialConnectionService {
25227
25467
  const partial = this.getPartial(data.id, backtest);
25228
25468
  await partial.waitForInit(symbol, data.strategyName, data.exchangeName, backtest);
25229
25469
  await partial.clear(symbol, data, priceClose, backtest);
25230
- const key = CREATE_KEY_FN$h(data.id, backtest);
25470
+ const key = CREATE_KEY_FN$i(data.id, backtest);
25231
25471
  this.getPartial.clear(key);
25232
25472
  };
25233
25473
  }
@@ -25243,7 +25483,7 @@ class PartialConnectionService {
25243
25483
  * @param backtest - Whether running in backtest mode
25244
25484
  * @returns Unique string key for memoization
25245
25485
  */
25246
- const CREATE_KEY_FN$g = (symbol, strategyName, exchangeName, frameName, backtest) => {
25486
+ const CREATE_KEY_FN$h = (symbol, strategyName, exchangeName, frameName, backtest) => {
25247
25487
  const parts = [symbol, strategyName, exchangeName];
25248
25488
  if (frameName)
25249
25489
  parts.push(frameName);
@@ -25466,7 +25706,7 @@ class PartialMarkdownService {
25466
25706
  * Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
25467
25707
  * Each combination gets its own isolated storage instance.
25468
25708
  */
25469
- 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));
25470
25710
  /**
25471
25711
  * Subscribes to partial profit/loss signal emitters to receive events.
25472
25712
  * Protected against multiple subscriptions.
@@ -25676,7 +25916,7 @@ class PartialMarkdownService {
25676
25916
  payload,
25677
25917
  });
25678
25918
  if (payload) {
25679
- 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);
25680
25920
  this.getStorage.clear(key);
25681
25921
  }
25682
25922
  else {
@@ -25692,7 +25932,7 @@ class PartialMarkdownService {
25692
25932
  * @param context - Context with strategyName, exchangeName, frameName
25693
25933
  * @returns Unique string key for memoization
25694
25934
  */
25695
- const CREATE_KEY_FN$f = (context) => {
25935
+ const CREATE_KEY_FN$g = (context) => {
25696
25936
  const parts = [context.strategyName, context.exchangeName];
25697
25937
  if (context.frameName)
25698
25938
  parts.push(context.frameName);
@@ -25766,7 +26006,7 @@ class PartialGlobalService {
25766
26006
  * @param context - Context with strategyName, exchangeName and frameName
25767
26007
  * @param methodName - Name of the calling method for error tracking
25768
26008
  */
25769
- this.validate = memoize(([context]) => CREATE_KEY_FN$f(context), (context, methodName) => {
26009
+ this.validate = memoize(([context]) => CREATE_KEY_FN$g(context), (context, methodName) => {
25770
26010
  this.loggerService.log("partialGlobalService validate", {
25771
26011
  context,
25772
26012
  methodName,
@@ -26221,7 +26461,7 @@ class ClientBreakeven {
26221
26461
  * @param backtest - Whether running in backtest mode
26222
26462
  * @returns Unique string key for memoization
26223
26463
  */
26224
- const CREATE_KEY_FN$e = (signalId, backtest) => `${signalId}:${backtest ? "backtest" : "live"}`;
26464
+ const CREATE_KEY_FN$f = (signalId, backtest) => `${signalId}:${backtest ? "backtest" : "live"}`;
26225
26465
  /**
26226
26466
  * Creates a callback function for emitting breakeven events to breakevenSubject.
26227
26467
  *
@@ -26307,7 +26547,7 @@ class BreakevenConnectionService {
26307
26547
  * Key format: "signalId:backtest" or "signalId:live"
26308
26548
  * Value: ClientBreakeven instance with logger and event emitter
26309
26549
  */
26310
- 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) => {
26311
26551
  return new ClientBreakeven({
26312
26552
  signalId,
26313
26553
  logger: this.loggerService,
@@ -26368,7 +26608,7 @@ class BreakevenConnectionService {
26368
26608
  const breakeven = this.getBreakeven(data.id, backtest);
26369
26609
  await breakeven.waitForInit(symbol, data.strategyName, data.exchangeName, backtest);
26370
26610
  await breakeven.clear(symbol, data, priceClose, backtest);
26371
- const key = CREATE_KEY_FN$e(data.id, backtest);
26611
+ const key = CREATE_KEY_FN$f(data.id, backtest);
26372
26612
  this.getBreakeven.clear(key);
26373
26613
  };
26374
26614
  }
@@ -26384,7 +26624,7 @@ class BreakevenConnectionService {
26384
26624
  * @param backtest - Whether running in backtest mode
26385
26625
  * @returns Unique string key for memoization
26386
26626
  */
26387
- const CREATE_KEY_FN$d = (symbol, strategyName, exchangeName, frameName, backtest) => {
26627
+ const CREATE_KEY_FN$e = (symbol, strategyName, exchangeName, frameName, backtest) => {
26388
26628
  const parts = [symbol, strategyName, exchangeName];
26389
26629
  if (frameName)
26390
26630
  parts.push(frameName);
@@ -26559,7 +26799,7 @@ class BreakevenMarkdownService {
26559
26799
  * Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
26560
26800
  * Each combination gets its own isolated storage instance.
26561
26801
  */
26562
- 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));
26563
26803
  /**
26564
26804
  * Subscribes to breakeven signal emitter to receive events.
26565
26805
  * Protected against multiple subscriptions.
@@ -26748,7 +26988,7 @@ class BreakevenMarkdownService {
26748
26988
  payload,
26749
26989
  });
26750
26990
  if (payload) {
26751
- 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);
26752
26992
  this.getStorage.clear(key);
26753
26993
  }
26754
26994
  else {
@@ -26764,7 +27004,7 @@ class BreakevenMarkdownService {
26764
27004
  * @param context - Context with strategyName, exchangeName, frameName
26765
27005
  * @returns Unique string key for memoization
26766
27006
  */
26767
- const CREATE_KEY_FN$c = (context) => {
27007
+ const CREATE_KEY_FN$d = (context) => {
26768
27008
  const parts = [context.strategyName, context.exchangeName];
26769
27009
  if (context.frameName)
26770
27010
  parts.push(context.frameName);
@@ -26838,7 +27078,7 @@ class BreakevenGlobalService {
26838
27078
  * @param context - Context with strategyName, exchangeName and frameName
26839
27079
  * @param methodName - Name of the calling method for error tracking
26840
27080
  */
26841
- this.validate = memoize(([context]) => CREATE_KEY_FN$c(context), (context, methodName) => {
27081
+ this.validate = memoize(([context]) => CREATE_KEY_FN$d(context), (context, methodName) => {
26842
27082
  this.loggerService.log("breakevenGlobalService validate", {
26843
27083
  context,
26844
27084
  methodName,
@@ -27059,7 +27299,7 @@ class ConfigValidationService {
27059
27299
  * @param backtest - Whether running in backtest mode
27060
27300
  * @returns Unique string key for memoization
27061
27301
  */
27062
- const CREATE_KEY_FN$b = (symbol, strategyName, exchangeName, frameName, backtest) => {
27302
+ const CREATE_KEY_FN$c = (symbol, strategyName, exchangeName, frameName, backtest) => {
27063
27303
  const parts = [symbol, strategyName, exchangeName];
27064
27304
  if (frameName)
27065
27305
  parts.push(frameName);
@@ -27226,7 +27466,7 @@ class RiskMarkdownService {
27226
27466
  * Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
27227
27467
  * Each combination gets its own isolated storage instance.
27228
27468
  */
27229
- 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));
27230
27470
  /**
27231
27471
  * Subscribes to risk rejection emitter to receive rejection events.
27232
27472
  * Protected against multiple subscriptions.
@@ -27415,7 +27655,7 @@ class RiskMarkdownService {
27415
27655
  payload,
27416
27656
  });
27417
27657
  if (payload) {
27418
- 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);
27419
27659
  this.getStorage.clear(key);
27420
27660
  }
27421
27661
  else {
@@ -29054,7 +29294,7 @@ class StrategyReportService {
29054
29294
  /**
29055
29295
  * Logs a cancel-scheduled event when a scheduled signal is cancelled.
29056
29296
  */
29057
- this.cancelScheduled = async (symbol, isBacktest, context, timestamp, signalId, pnl, totalPartials, cancelId) => {
29297
+ this.cancelScheduled = async (symbol, isBacktest, context, timestamp, signalId, pnl, totalPartials, cancelId, note) => {
29058
29298
  this.loggerService.log("strategyReportService cancelScheduled", {
29059
29299
  symbol,
29060
29300
  isBacktest,
@@ -29067,6 +29307,7 @@ class StrategyReportService {
29067
29307
  await ReportWriter.writeData("strategy", {
29068
29308
  action: "cancel-scheduled",
29069
29309
  cancelId,
29310
+ note,
29070
29311
  symbol,
29071
29312
  timestamp,
29072
29313
  createdAt,
@@ -29088,7 +29329,7 @@ class StrategyReportService {
29088
29329
  /**
29089
29330
  * Logs a close-pending event when a pending signal is closed.
29090
29331
  */
29091
- this.closePending = async (symbol, isBacktest, context, timestamp, signalId, pnl, totalPartials, closeId) => {
29332
+ this.closePending = async (symbol, isBacktest, context, timestamp, signalId, pnl, totalPartials, closeId, note) => {
29092
29333
  this.loggerService.log("strategyReportService closePending", {
29093
29334
  symbol,
29094
29335
  isBacktest,
@@ -29101,6 +29342,7 @@ class StrategyReportService {
29101
29342
  await ReportWriter.writeData("strategy", {
29102
29343
  action: "close-pending",
29103
29344
  closeId,
29345
+ note,
29104
29346
  symbol,
29105
29347
  timestamp,
29106
29348
  createdAt,
@@ -29350,7 +29592,7 @@ class StrategyReportService {
29350
29592
  /**
29351
29593
  * Logs an activate-scheduled event when a scheduled signal is activated early.
29352
29594
  */
29353
- this.activateScheduled = async (symbol, currentPrice, isBacktest, context, timestamp, signalId, pnl, totalPartials, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen, activateId) => {
29595
+ this.activateScheduled = async (symbol, currentPrice, isBacktest, context, timestamp, signalId, pnl, totalPartials, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen, activateId, note) => {
29354
29596
  this.loggerService.log("strategyReportService activateScheduled", {
29355
29597
  symbol,
29356
29598
  currentPrice,
@@ -29364,6 +29606,7 @@ class StrategyReportService {
29364
29606
  await ReportWriter.writeData("strategy", {
29365
29607
  action: "activate-scheduled",
29366
29608
  activateId,
29609
+ note,
29367
29610
  currentPrice,
29368
29611
  symbol,
29369
29612
  timestamp,
@@ -29457,14 +29700,14 @@ class StrategyReportService {
29457
29700
  exchangeName: event.exchangeName,
29458
29701
  frameName: event.frameName,
29459
29702
  strategyName: event.strategyName,
29460
- }, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.cancelId));
29703
+ }, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.cancelId, event.note));
29461
29704
  const unClosePending = strategyCommitSubject
29462
29705
  .filter(({ action }) => action === "close-pending")
29463
29706
  .connect(async (event) => await this.closePending(event.symbol, event.backtest, {
29464
29707
  exchangeName: event.exchangeName,
29465
29708
  frameName: event.frameName,
29466
29709
  strategyName: event.strategyName,
29467
- }, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.closeId));
29710
+ }, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.closeId, event.note));
29468
29711
  const unPartialProfit = strategyCommitSubject
29469
29712
  .filter(({ action }) => action === "partial-profit")
29470
29713
  .connect(async (event) => await this.partialProfit(event.symbol, event.percentToClose, event.currentPrice, event.backtest, {
@@ -29506,7 +29749,7 @@ class StrategyReportService {
29506
29749
  exchangeName: event.exchangeName,
29507
29750
  frameName: event.frameName,
29508
29751
  strategyName: event.strategyName,
29509
- }, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.totalEntries, event.originalPriceOpen, event.activateId));
29752
+ }, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.totalEntries, event.originalPriceOpen, event.activateId, event.note));
29510
29753
  const unAverageBuy = strategyCommitSubject
29511
29754
  .filter(({ action }) => action === "average-buy")
29512
29755
  .connect(async (event) => await this.averageBuy(event.symbol, event.currentPrice, event.effectivePriceOpen, event.totalEntries, event.backtest, {
@@ -29794,7 +30037,7 @@ class HighestProfitReportService {
29794
30037
  * @returns Colon-separated key string for memoization
29795
30038
  * @internal
29796
30039
  */
29797
- const CREATE_KEY_FN$a = (symbol, strategyName, exchangeName, frameName, backtest) => {
30040
+ const CREATE_KEY_FN$b = (symbol, strategyName, exchangeName, frameName, backtest) => {
29798
30041
  const parts = [symbol, strategyName, exchangeName];
29799
30042
  if (frameName)
29800
30043
  parts.push(frameName);
@@ -30036,7 +30279,7 @@ class StrategyMarkdownService {
30036
30279
  *
30037
30280
  * @internal
30038
30281
  */
30039
- 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));
30040
30283
  /**
30041
30284
  * Records a cancel-scheduled event when a scheduled signal is cancelled.
30042
30285
  *
@@ -30045,8 +30288,9 @@ class StrategyMarkdownService {
30045
30288
  * @param context - Strategy context with strategyName, exchangeName, frameName
30046
30289
  * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
30047
30290
  * @param cancelId - Optional identifier for the cancellation reason
30291
+ * @param note - Optional note from commit payload
30048
30292
  */
30049
- this.cancelScheduled = async (symbol, isBacktest, context, timestamp, signalId, pnl, cancelId) => {
30293
+ this.cancelScheduled = async (symbol, isBacktest, context, timestamp, signalId, pnl, cancelId, note) => {
30050
30294
  this.loggerService.log("strategyMarkdownService cancelScheduled", {
30051
30295
  symbol,
30052
30296
  isBacktest,
@@ -30067,6 +30311,7 @@ class StrategyMarkdownService {
30067
30311
  action: "cancel-scheduled",
30068
30312
  pnl,
30069
30313
  cancelId,
30314
+ note,
30070
30315
  createdAt,
30071
30316
  backtest: isBacktest,
30072
30317
  });
@@ -30079,8 +30324,9 @@ class StrategyMarkdownService {
30079
30324
  * @param context - Strategy context with strategyName, exchangeName, frameName
30080
30325
  * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
30081
30326
  * @param closeId - Optional identifier for the close reason
30327
+ * @param note - Optional note from commit payload
30082
30328
  */
30083
- this.closePending = async (symbol, isBacktest, context, timestamp, signalId, pnl, closeId) => {
30329
+ this.closePending = async (symbol, isBacktest, context, timestamp, signalId, pnl, closeId, note) => {
30084
30330
  this.loggerService.log("strategyMarkdownService closePending", {
30085
30331
  symbol,
30086
30332
  isBacktest,
@@ -30101,6 +30347,7 @@ class StrategyMarkdownService {
30101
30347
  action: "close-pending",
30102
30348
  pnl,
30103
30349
  closeId,
30350
+ note,
30104
30351
  createdAt,
30105
30352
  backtest: isBacktest,
30106
30353
  });
@@ -30399,8 +30646,9 @@ class StrategyMarkdownService {
30399
30646
  * @param scheduledAt - Signal creation timestamp in milliseconds
30400
30647
  * @param pendingAt - Pending timestamp in milliseconds
30401
30648
  * @param activateId - Optional identifier for the activation reason
30649
+ * @param note - Optional note from commit payload
30402
30650
  */
30403
- this.activateScheduled = async (symbol, currentPrice, isBacktest, context, timestamp, signalId, pnl, totalPartials, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen, activateId) => {
30651
+ this.activateScheduled = async (symbol, currentPrice, isBacktest, context, timestamp, signalId, pnl, totalPartials, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen, activateId, note) => {
30404
30652
  this.loggerService.log("strategyMarkdownService activateScheduled", {
30405
30653
  symbol,
30406
30654
  currentPrice,
@@ -30423,6 +30671,7 @@ class StrategyMarkdownService {
30423
30671
  pnl,
30424
30672
  totalPartials,
30425
30673
  activateId,
30674
+ note,
30426
30675
  currentPrice,
30427
30676
  createdAt,
30428
30677
  backtest: isBacktest,
@@ -30604,7 +30853,7 @@ class StrategyMarkdownService {
30604
30853
  this.clear = async (payload) => {
30605
30854
  this.loggerService.log("strategyMarkdownService clear", { payload });
30606
30855
  if (payload) {
30607
- 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);
30608
30857
  this.getStorage.clear(key);
30609
30858
  }
30610
30859
  else {
@@ -30627,14 +30876,14 @@ class StrategyMarkdownService {
30627
30876
  exchangeName: event.exchangeName,
30628
30877
  frameName: event.frameName,
30629
30878
  strategyName: event.strategyName,
30630
- }, event.timestamp, event.signalId, event.pnl, event.cancelId));
30879
+ }, event.timestamp, event.signalId, event.pnl, event.cancelId, event.note));
30631
30880
  const unClosePending = strategyCommitSubject
30632
30881
  .filter(({ action }) => action === "close-pending")
30633
30882
  .connect(async (event) => await this.closePending(event.symbol, event.backtest, {
30634
30883
  exchangeName: event.exchangeName,
30635
30884
  frameName: event.frameName,
30636
30885
  strategyName: event.strategyName,
30637
- }, event.timestamp, event.signalId, event.pnl, event.closeId));
30886
+ }, event.timestamp, event.signalId, event.pnl, event.closeId, event.note));
30638
30887
  const unPartialProfit = strategyCommitSubject
30639
30888
  .filter(({ action }) => action === "partial-profit")
30640
30889
  .connect(async (event) => await this.partialProfit(event.symbol, event.percentToClose, event.currentPrice, event.backtest, {
@@ -30676,7 +30925,7 @@ class StrategyMarkdownService {
30676
30925
  exchangeName: event.exchangeName,
30677
30926
  frameName: event.frameName,
30678
30927
  strategyName: event.strategyName,
30679
- }, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.totalEntries, event.originalPriceOpen, event.activateId));
30928
+ }, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.totalEntries, event.originalPriceOpen, event.activateId, event.note));
30680
30929
  const unAverageBuy = strategyCommitSubject
30681
30930
  .filter(({ action }) => action === "average-buy")
30682
30931
  .connect(async (event) => await this.averageBuy(event.symbol, event.currentPrice, event.effectivePriceOpen, event.totalEntries, event.backtest, {
@@ -30712,7 +30961,7 @@ class StrategyMarkdownService {
30712
30961
  * Creates a unique key for memoizing ReportStorage instances.
30713
30962
  * Key format: "symbol:strategyName:exchangeName[:frameName]:backtest|live"
30714
30963
  */
30715
- const CREATE_KEY_FN$9 = (symbol, strategyName, exchangeName, frameName, backtest) => {
30964
+ const CREATE_KEY_FN$a = (symbol, strategyName, exchangeName, frameName, backtest) => {
30716
30965
  const parts = [symbol, strategyName, exchangeName];
30717
30966
  if (frameName)
30718
30967
  parts.push(frameName);
@@ -30905,7 +31154,7 @@ let ReportStorage$2 = class ReportStorage {
30905
31154
  class SyncMarkdownService {
30906
31155
  constructor() {
30907
31156
  this.loggerService = inject(TYPES.loggerService);
30908
- 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));
30909
31158
  /**
30910
31159
  * Subscribes to `syncSubject` to start receiving `SignalSyncContract` events.
30911
31160
  * Protected against multiple subscriptions via `singleshot` — subsequent calls
@@ -31101,7 +31350,7 @@ class SyncMarkdownService {
31101
31350
  this.clear = async (payload) => {
31102
31351
  this.loggerService.log("syncMarkdownService clear", { payload });
31103
31352
  if (payload) {
31104
- 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);
31105
31354
  this.getStorage.clear(key);
31106
31355
  }
31107
31356
  else {
@@ -31114,7 +31363,7 @@ class SyncMarkdownService {
31114
31363
  /**
31115
31364
  * Creates a unique memoization key for a symbol-strategy-exchange-frame-backtest combination.
31116
31365
  */
31117
- const CREATE_KEY_FN$8 = (symbol, strategyName, exchangeName, frameName, backtest) => {
31366
+ const CREATE_KEY_FN$9 = (symbol, strategyName, exchangeName, frameName, backtest) => {
31118
31367
  const parts = [symbol, strategyName, exchangeName];
31119
31368
  if (frameName)
31120
31369
  parts.push(frameName);
@@ -31290,7 +31539,7 @@ let ReportStorage$1 = class ReportStorage {
31290
31539
  class HighestProfitMarkdownService {
31291
31540
  constructor() {
31292
31541
  this.loggerService = inject(TYPES.loggerService);
31293
- 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));
31294
31543
  /**
31295
31544
  * Subscribes to `highestProfitSubject` to start receiving `HighestProfitContract`
31296
31545
  * events. Protected against multiple subscriptions via `singleshot` — subsequent
@@ -31456,7 +31705,7 @@ class HighestProfitMarkdownService {
31456
31705
  this.clear = async (payload) => {
31457
31706
  this.loggerService.log("highestProfitMarkdownService clear", { payload });
31458
31707
  if (payload) {
31459
- 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);
31460
31709
  this.getStorage.clear(key);
31461
31710
  }
31462
31711
  else {
@@ -31478,7 +31727,7 @@ const LISTEN_TIMEOUT$1 = 120000;
31478
31727
  * @param backtest - Whether running in backtest mode
31479
31728
  * @returns Unique string key for memoization
31480
31729
  */
31481
- const CREATE_KEY_FN$7 = (symbol, strategyName, exchangeName, frameName, backtest) => {
31730
+ const CREATE_KEY_FN$8 = (symbol, strategyName, exchangeName, frameName, backtest) => {
31482
31731
  const parts = [symbol, strategyName, exchangeName];
31483
31732
  if (frameName)
31484
31733
  parts.push(frameName);
@@ -31521,7 +31770,7 @@ class PriceMetaService {
31521
31770
  * Each subject holds the latest currentPrice emitted by the strategy iterator for that key.
31522
31771
  * Instances are cached until clear() is called.
31523
31772
  */
31524
- 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());
31525
31774
  /**
31526
31775
  * Returns the current market price for the given symbol and context.
31527
31776
  *
@@ -31550,10 +31799,10 @@ class PriceMetaService {
31550
31799
  if (source.data) {
31551
31800
  return source.data;
31552
31801
  }
31553
- 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...`);
31554
31803
  const currentPrice = await waitForNext(source, (data) => !!data, LISTEN_TIMEOUT$1);
31555
31804
  if (typeof currentPrice === "symbol") {
31556
- 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)}`);
31557
31806
  }
31558
31807
  return currentPrice;
31559
31808
  };
@@ -31595,7 +31844,7 @@ class PriceMetaService {
31595
31844
  this.getSource.clear();
31596
31845
  return;
31597
31846
  }
31598
- 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);
31599
31848
  this.getSource.clear(key);
31600
31849
  };
31601
31850
  }
@@ -31613,7 +31862,7 @@ const LISTEN_TIMEOUT = 120000;
31613
31862
  * @param backtest - Whether running in backtest mode
31614
31863
  * @returns Unique string key for memoization
31615
31864
  */
31616
- const CREATE_KEY_FN$6 = (symbol, strategyName, exchangeName, frameName, backtest) => {
31865
+ const CREATE_KEY_FN$7 = (symbol, strategyName, exchangeName, frameName, backtest) => {
31617
31866
  const parts = [symbol, strategyName, exchangeName];
31618
31867
  if (frameName)
31619
31868
  parts.push(frameName);
@@ -31656,7 +31905,7 @@ class TimeMetaService {
31656
31905
  * Each subject holds the latest createdAt timestamp emitted by the strategy iterator for that key.
31657
31906
  * Instances are cached until clear() is called.
31658
31907
  */
31659
- 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());
31660
31909
  /**
31661
31910
  * Returns the current candle timestamp (in milliseconds) for the given symbol and context.
31662
31911
  *
@@ -31684,10 +31933,10 @@ class TimeMetaService {
31684
31933
  if (source.data) {
31685
31934
  return source.data;
31686
31935
  }
31687
- 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...`);
31688
31937
  const timestamp = await waitForNext(source, (data) => !!data, LISTEN_TIMEOUT);
31689
31938
  if (typeof timestamp === "symbol") {
31690
- 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)}`);
31691
31940
  }
31692
31941
  return timestamp;
31693
31942
  };
@@ -31729,7 +31978,7 @@ class TimeMetaService {
31729
31978
  this.getSource.clear();
31730
31979
  return;
31731
31980
  }
31732
- 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);
31733
31982
  this.getSource.clear(key);
31734
31983
  };
31735
31984
  }
@@ -31825,7 +32074,7 @@ class MaxDrawdownReportService {
31825
32074
  /**
31826
32075
  * Creates a unique memoization key for a symbol-strategy-exchange-frame-backtest combination.
31827
32076
  */
31828
- const CREATE_KEY_FN$5 = (symbol, strategyName, exchangeName, frameName, backtest) => {
32077
+ const CREATE_KEY_FN$6 = (symbol, strategyName, exchangeName, frameName, backtest) => {
31829
32078
  const parts = [symbol, strategyName, exchangeName];
31830
32079
  if (frameName)
31831
32080
  parts.push(frameName);
@@ -31949,7 +32198,7 @@ class ReportStorage {
31949
32198
  class MaxDrawdownMarkdownService {
31950
32199
  constructor() {
31951
32200
  this.loggerService = inject(TYPES.loggerService);
31952
- 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));
31953
32202
  /**
31954
32203
  * Subscribes to `maxDrawdownSubject` to start receiving `MaxDrawdownContract`
31955
32204
  * events. Protected against multiple subscriptions via `singleshot`.
@@ -32028,7 +32277,7 @@ class MaxDrawdownMarkdownService {
32028
32277
  this.clear = async (payload) => {
32029
32278
  this.loggerService.log("maxDrawdownMarkdownService clear", { payload });
32030
32279
  if (payload) {
32031
- 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);
32032
32281
  this.getStorage.clear(key);
32033
32282
  }
32034
32283
  else {
@@ -35161,6 +35410,8 @@ const GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE_METHOD_NAME = "strateg
35161
35410
  const GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST_METHOD_NAME = "strategy.getPositionHighestProfitDistancePnlCost";
35162
35411
  const GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE_METHOD_NAME = "strategy.getPositionHighestMaxDrawdownPnlPercentage";
35163
35412
  const GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST_METHOD_NAME = "strategy.getPositionHighestMaxDrawdownPnlCost";
35413
+ const GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE_METHOD_NAME = "strategy.getMaxDrawdownDistancePnlPercentage";
35414
+ const GET_MAX_DRAWDOWN_DISTANCE_PNL_COST_METHOD_NAME = "strategy.getMaxDrawdownDistancePnlCost";
35164
35415
  const GET_POSITION_ENTRY_OVERLAP_METHOD_NAME = "strategy.getPositionEntryOverlap";
35165
35416
  const GET_POSITION_PARTIAL_OVERLAP_METHOD_NAME = "strategy.getPositionPartialOverlap";
35166
35417
  const HAS_NO_PENDING_SIGNAL_METHOD_NAME = "strategy.hasNoPendingSignal";
@@ -35176,7 +35427,7 @@ const HAS_NO_SCHEDULED_SIGNAL_METHOD_NAME = "strategy.hasNoScheduledSignal";
35176
35427
  *
35177
35428
  * @param symbol - Trading pair symbol
35178
35429
  * @param strategyName - Strategy name
35179
- * @param cancelId - Optional cancellation ID for tracking user-initiated cancellations
35430
+ * @param payload - Optional commit payload with id and note
35180
35431
  * @returns Promise that resolves when scheduled signal is cancelled
35181
35432
  *
35182
35433
  * @example
@@ -35184,13 +35435,13 @@ const HAS_NO_SCHEDULED_SIGNAL_METHOD_NAME = "strategy.hasNoScheduledSignal";
35184
35435
  * import { commitCancelScheduled } from "backtest-kit";
35185
35436
  *
35186
35437
  * // Cancel scheduled signal with custom ID
35187
- * await commitCancelScheduled("BTCUSDT", "manual-cancel-001");
35438
+ * await commitCancelScheduled("BTCUSDT", { id: "manual-cancel-001" });
35188
35439
  * ```
35189
35440
  */
35190
- async function commitCancelScheduled(symbol, cancelId) {
35441
+ async function commitCancelScheduled(symbol, payload = {}) {
35191
35442
  backtest.loggerService.info(CANCEL_SCHEDULED_METHOD_NAME, {
35192
35443
  symbol,
35193
- cancelId,
35444
+ payload,
35194
35445
  });
35195
35446
  if (!ExecutionContextService.hasContext()) {
35196
35447
  throw new Error("commitCancelScheduled requires an execution context");
@@ -35200,7 +35451,7 @@ async function commitCancelScheduled(symbol, cancelId) {
35200
35451
  }
35201
35452
  const { backtest: isBacktest } = backtest.executionContextService.context;
35202
35453
  const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
35203
- await backtest.strategyCoreService.cancelScheduled(isBacktest, symbol, { exchangeName, frameName, strategyName }, cancelId);
35454
+ await backtest.strategyCoreService.cancelScheduled(isBacktest, symbol, { exchangeName, frameName, strategyName }, payload);
35204
35455
  }
35205
35456
  /**
35206
35457
  * Closes the pending signal without stopping the strategy.
@@ -35212,7 +35463,7 @@ async function commitCancelScheduled(symbol, cancelId) {
35212
35463
  * Automatically detects backtest/live mode from execution context.
35213
35464
  *
35214
35465
  * @param symbol - Trading pair symbol
35215
- * @param closeId - Optional close ID for tracking user-initiated closes
35466
+ * @param payload - Optional commit payload with id and note
35216
35467
  * @returns Promise that resolves when pending signal is closed
35217
35468
  *
35218
35469
  * @example
@@ -35220,13 +35471,13 @@ async function commitCancelScheduled(symbol, cancelId) {
35220
35471
  * import { commitClosePending } from "backtest-kit";
35221
35472
  *
35222
35473
  * // Close pending signal with custom ID
35223
- * await commitClosePending("BTCUSDT", "manual-close-001");
35474
+ * await commitClosePending("BTCUSDT", { id: "manual-close-001" });
35224
35475
  * ```
35225
35476
  */
35226
- async function commitClosePending(symbol, closeId) {
35477
+ async function commitClosePending(symbol, payload = {}) {
35227
35478
  backtest.loggerService.info(CLOSE_PENDING_METHOD_NAME, {
35228
35479
  symbol,
35229
- closeId,
35480
+ payload,
35230
35481
  });
35231
35482
  if (!ExecutionContextService.hasContext()) {
35232
35483
  throw new Error("commitClosePending requires an execution context");
@@ -35236,7 +35487,7 @@ async function commitClosePending(symbol, closeId) {
35236
35487
  }
35237
35488
  const { backtest: isBacktest } = backtest.executionContextService.context;
35238
35489
  const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
35239
- await backtest.strategyCoreService.closePending(isBacktest, symbol, { exchangeName, frameName, strategyName }, closeId);
35490
+ await backtest.strategyCoreService.closePending(isBacktest, symbol, { exchangeName, frameName, strategyName }, payload);
35240
35491
  }
35241
35492
  /**
35242
35493
  * Executes partial close at profit level (moving toward TP).
@@ -35699,7 +35950,7 @@ async function commitBreakeven(symbol) {
35699
35950
  * Automatically detects backtest/live mode from execution context.
35700
35951
  *
35701
35952
  * @param symbol - Trading pair symbol
35702
- * @param activateId - Optional activation ID for tracking user-initiated activations
35953
+ * @param payload - Optional commit payload with id and note
35703
35954
  * @returns Promise that resolves when activation flag is set
35704
35955
  *
35705
35956
  * @example
@@ -35707,13 +35958,13 @@ async function commitBreakeven(symbol) {
35707
35958
  * import { commitActivateScheduled } from "backtest-kit";
35708
35959
  *
35709
35960
  * // Activate scheduled signal early with custom ID
35710
- * await commitActivateScheduled("BTCUSDT", "manual-activate-001");
35961
+ * await commitActivateScheduled("BTCUSDT", { id: "manual-activate-001" });
35711
35962
  * ```
35712
35963
  */
35713
- async function commitActivateScheduled(symbol, activateId) {
35964
+ async function commitActivateScheduled(symbol, payload = {}) {
35714
35965
  backtest.loggerService.info(ACTIVATE_SCHEDULED_METHOD_NAME, {
35715
35966
  symbol,
35716
- activateId,
35967
+ payload,
35717
35968
  });
35718
35969
  if (!ExecutionContextService.hasContext()) {
35719
35970
  throw new Error("commitActivateScheduled requires an execution context");
@@ -35723,7 +35974,7 @@ async function commitActivateScheduled(symbol, activateId) {
35723
35974
  }
35724
35975
  const { backtest: isBacktest } = backtest.executionContextService.context;
35725
35976
  const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
35726
- await backtest.strategyCoreService.activateScheduled(isBacktest, symbol, { exchangeName, frameName, strategyName }, activateId);
35977
+ await backtest.strategyCoreService.activateScheduled(isBacktest, symbol, { exchangeName, frameName, strategyName }, payload);
35727
35978
  }
35728
35979
  /**
35729
35980
  * Adds a new DCA entry to the active pending signal.
@@ -36911,6 +37162,64 @@ async function getPositionHighestMaxDrawdownPnlCost(symbol) {
36911
37162
  const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
36912
37163
  return await backtest.strategyCoreService.getPositionHighestMaxDrawdownPnlCost(isBacktest, symbol, { exchangeName, frameName, strategyName });
36913
37164
  }
37165
+ /**
37166
+ * Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
37167
+ *
37168
+ * Computed as: max(0, peakPnlPercentage - fallPnlPercentage).
37169
+ * Returns null if no pending signal exists.
37170
+ *
37171
+ * @param symbol - Trading pair symbol
37172
+ * @returns Promise resolving to peak-to-trough PnL percentage distance (≥ 0) or null
37173
+ *
37174
+ * @example
37175
+ * ```typescript
37176
+ * import { getMaxDrawdownDistancePnlPercentage } from "backtest-kit";
37177
+ *
37178
+ * const dist = await getMaxDrawdownDistancePnlPercentage("BTCUSDT");
37179
+ * // e.g. 3.5 (peak was +3.5% above trough)
37180
+ * ```
37181
+ */
37182
+ async function getMaxDrawdownDistancePnlPercentage(symbol) {
37183
+ backtest.loggerService.info(GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE_METHOD_NAME, { symbol });
37184
+ if (!ExecutionContextService.hasContext()) {
37185
+ throw new Error("getMaxDrawdownDistancePnlPercentage requires an execution context");
37186
+ }
37187
+ if (!MethodContextService.hasContext()) {
37188
+ throw new Error("getMaxDrawdownDistancePnlPercentage requires a method context");
37189
+ }
37190
+ const { backtest: isBacktest } = backtest.executionContextService.context;
37191
+ const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
37192
+ return await backtest.strategyCoreService.getMaxDrawdownDistancePnlPercentage(isBacktest, symbol, { exchangeName, frameName, strategyName });
37193
+ }
37194
+ /**
37195
+ * Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
37196
+ *
37197
+ * Computed as: max(0, peakPnlCost - fallPnlCost).
37198
+ * Returns null if no pending signal exists.
37199
+ *
37200
+ * @param symbol - Trading pair symbol
37201
+ * @returns Promise resolving to peak-to-trough PnL cost distance (≥ 0) or null
37202
+ *
37203
+ * @example
37204
+ * ```typescript
37205
+ * import { getMaxDrawdownDistancePnlCost } from "backtest-kit";
37206
+ *
37207
+ * const dist = await getMaxDrawdownDistancePnlCost("BTCUSDT");
37208
+ * // e.g. 7.2 (peak was $7.2 above trough)
37209
+ * ```
37210
+ */
37211
+ async function getMaxDrawdownDistancePnlCost(symbol) {
37212
+ backtest.loggerService.info(GET_MAX_DRAWDOWN_DISTANCE_PNL_COST_METHOD_NAME, { symbol });
37213
+ if (!ExecutionContextService.hasContext()) {
37214
+ throw new Error("getMaxDrawdownDistancePnlCost requires an execution context");
37215
+ }
37216
+ if (!MethodContextService.hasContext()) {
37217
+ throw new Error("getMaxDrawdownDistancePnlCost requires a method context");
37218
+ }
37219
+ const { backtest: isBacktest } = backtest.executionContextService.context;
37220
+ const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
37221
+ return await backtest.strategyCoreService.getMaxDrawdownDistancePnlCost(isBacktest, symbol, { exchangeName, frameName, strategyName });
37222
+ }
36914
37223
  /**
36915
37224
  * Checks whether the current price falls within the tolerance zone of any existing DCA entry level.
36916
37225
  * Use this to prevent duplicate DCA entries at the same price area.
@@ -38584,6 +38893,8 @@ const BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE =
38584
38893
  const BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST = "BacktestUtils.getPositionHighestProfitDistancePnlCost";
38585
38894
  const BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE = "BacktestUtils.getPositionHighestMaxDrawdownPnlPercentage";
38586
38895
  const BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST = "BacktestUtils.getPositionHighestMaxDrawdownPnlCost";
38896
+ const BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE = "BacktestUtils.getMaxDrawdownDistancePnlPercentage";
38897
+ const BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST = "BacktestUtils.getMaxDrawdownDistancePnlCost";
38587
38898
  const BACKTEST_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP = "BacktestUtils.getPositionEntryOverlap";
38588
38899
  const BACKTEST_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP = "BacktestUtils.getPositionPartialOverlap";
38589
38900
  const BACKTEST_METHOD_NAME_BREAKEVEN = "Backtest.commitBreakeven";
@@ -39965,6 +40276,62 @@ class BacktestUtils {
39965
40276
  }
39966
40277
  return await backtest.strategyCoreService.getPositionHighestMaxDrawdownPnlCost(true, symbol, context);
39967
40278
  };
40279
+ /**
40280
+ * Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
40281
+ *
40282
+ * Computed as: max(0, peakPnlPercentage - fallPnlPercentage).
40283
+ * Returns null if no pending signal exists.
40284
+ *
40285
+ * @param symbol - Trading pair symbol
40286
+ * @param context - Execution context with strategyName, exchangeName, and frameName
40287
+ * @returns peak-to-trough PnL percentage distance (≥ 0) or null if no active position
40288
+ */
40289
+ this.getMaxDrawdownDistancePnlPercentage = async (symbol, context) => {
40290
+ backtest.loggerService.info(BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE, {
40291
+ symbol,
40292
+ context,
40293
+ });
40294
+ backtest.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
40295
+ backtest.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
40296
+ {
40297
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
40298
+ riskName &&
40299
+ backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
40300
+ riskList &&
40301
+ riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE));
40302
+ actions &&
40303
+ actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE));
40304
+ }
40305
+ return await backtest.strategyCoreService.getMaxDrawdownDistancePnlPercentage(true, symbol, context);
40306
+ };
40307
+ /**
40308
+ * Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
40309
+ *
40310
+ * Computed as: max(0, peakPnlCost - fallPnlCost).
40311
+ * Returns null if no pending signal exists.
40312
+ *
40313
+ * @param symbol - Trading pair symbol
40314
+ * @param context - Execution context with strategyName, exchangeName, and frameName
40315
+ * @returns peak-to-trough PnL cost distance (≥ 0) or null if no active position
40316
+ */
40317
+ this.getMaxDrawdownDistancePnlCost = async (symbol, context) => {
40318
+ backtest.loggerService.info(BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST, {
40319
+ symbol,
40320
+ context,
40321
+ });
40322
+ backtest.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
40323
+ backtest.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
40324
+ {
40325
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
40326
+ riskName &&
40327
+ backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
40328
+ riskList &&
40329
+ riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST));
40330
+ actions &&
40331
+ actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST));
40332
+ }
40333
+ return await backtest.strategyCoreService.getMaxDrawdownDistancePnlCost(true, symbol, context);
40334
+ };
39968
40335
  /**
39969
40336
  * Checks whether the current price falls within the tolerance zone of any existing DCA entry level.
39970
40337
  * Use this to prevent duplicate DCA entries at the same price area.
@@ -40099,7 +40466,7 @@ class BacktestUtils {
40099
40466
  * @param symbol - Trading pair symbol
40100
40467
  * @param strategyName - Strategy name
40101
40468
  * @param context - Execution context with exchangeName and frameName
40102
- * @param cancelId - Optional cancellation ID for tracking user-initiated cancellations
40469
+ * @param payload - Optional commit payload with id and note
40103
40470
  * @returns Promise that resolves when scheduled signal is cancelled
40104
40471
  *
40105
40472
  * @example
@@ -40109,14 +40476,14 @@ class BacktestUtils {
40109
40476
  * exchangeName: "binance",
40110
40477
  * frameName: "frame1",
40111
40478
  * strategyName: "my-strategy"
40112
- * }, "manual-cancel-001");
40479
+ * }, { id: "manual-cancel-001" });
40113
40480
  * ```
40114
40481
  */
40115
- this.commitCancelScheduled = async (symbol, context, cancelId) => {
40482
+ this.commitCancelScheduled = async (symbol, context, payload = {}) => {
40116
40483
  backtest.loggerService.info(BACKTEST_METHOD_NAME_CANCEL_SCHEDULED, {
40117
40484
  symbol,
40118
40485
  context,
40119
- cancelId,
40486
+ payload,
40120
40487
  });
40121
40488
  backtest.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_CANCEL_SCHEDULED);
40122
40489
  backtest.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_CANCEL_SCHEDULED);
@@ -40129,7 +40496,7 @@ class BacktestUtils {
40129
40496
  actions &&
40130
40497
  actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_CANCEL_SCHEDULED));
40131
40498
  }
40132
- await backtest.strategyCoreService.cancelScheduled(true, symbol, context, cancelId);
40499
+ await backtest.strategyCoreService.cancelScheduled(true, symbol, context, payload);
40133
40500
  };
40134
40501
  /**
40135
40502
  * Closes the pending signal without stopping the strategy.
@@ -40140,7 +40507,7 @@ class BacktestUtils {
40140
40507
  *
40141
40508
  * @param symbol - Trading pair symbol
40142
40509
  * @param context - Execution context with strategyName, exchangeName, and frameName
40143
- * @param closeId - Optional close ID for user-initiated closes
40510
+ * @param payload - Optional commit payload with id and note
40144
40511
  * @returns Promise that resolves when pending signal is closed
40145
40512
  *
40146
40513
  * @example
@@ -40150,14 +40517,14 @@ class BacktestUtils {
40150
40517
  * exchangeName: "binance",
40151
40518
  * strategyName: "my-strategy",
40152
40519
  * frameName: "1m"
40153
- * }, "manual-close-001");
40520
+ * }, { id: "manual-close-001" });
40154
40521
  * ```
40155
40522
  */
40156
- this.commitClosePending = async (symbol, context, closeId) => {
40523
+ this.commitClosePending = async (symbol, context, payload = {}) => {
40157
40524
  backtest.loggerService.info(BACKTEST_METHOD_NAME_CLOSE_PENDING, {
40158
40525
  symbol,
40159
40526
  context,
40160
- closeId,
40527
+ payload,
40161
40528
  });
40162
40529
  backtest.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_CLOSE_PENDING);
40163
40530
  backtest.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_CLOSE_PENDING);
@@ -40170,7 +40537,7 @@ class BacktestUtils {
40170
40537
  actions &&
40171
40538
  actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_CLOSE_PENDING));
40172
40539
  }
40173
- await backtest.strategyCoreService.closePending(true, symbol, context, closeId);
40540
+ await backtest.strategyCoreService.closePending(true, symbol, context, payload);
40174
40541
  };
40175
40542
  /**
40176
40543
  * Executes partial close at profit level (moving toward TP).
@@ -40806,7 +41173,7 @@ class BacktestUtils {
40806
41173
  *
40807
41174
  * @param symbol - Trading pair symbol
40808
41175
  * @param context - Execution context with strategyName, exchangeName, and frameName
40809
- * @param activateId - Optional activation ID for tracking user-initiated activations
41176
+ * @param payload - Optional commit payload with id and note
40810
41177
  * @returns Promise that resolves when activation flag is set
40811
41178
  *
40812
41179
  * @example
@@ -40816,14 +41183,14 @@ class BacktestUtils {
40816
41183
  * strategyName: "my-strategy",
40817
41184
  * exchangeName: "binance",
40818
41185
  * frameName: "1h"
40819
- * }, "manual-activate-001");
41186
+ * }, { id: "manual-activate-001" });
40820
41187
  * ```
40821
41188
  */
40822
- this.commitActivateScheduled = async (symbol, context, activateId) => {
41189
+ this.commitActivateScheduled = async (symbol, context, payload = {}) => {
40823
41190
  backtest.loggerService.info(BACKTEST_METHOD_NAME_ACTIVATE_SCHEDULED, {
40824
41191
  symbol,
40825
41192
  context,
40826
- activateId,
41193
+ payload,
40827
41194
  });
40828
41195
  backtest.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_ACTIVATE_SCHEDULED);
40829
41196
  backtest.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_ACTIVATE_SCHEDULED);
@@ -40836,7 +41203,7 @@ class BacktestUtils {
40836
41203
  actions &&
40837
41204
  actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_ACTIVATE_SCHEDULED));
40838
41205
  }
40839
- await backtest.strategyCoreService.activateScheduled(true, symbol, context, activateId);
41206
+ await backtest.strategyCoreService.activateScheduled(true, symbol, context, payload);
40840
41207
  };
40841
41208
  /**
40842
41209
  * Adds a new DCA entry to the active pending signal.
@@ -41094,6 +41461,8 @@ const LIVE_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE = "Li
41094
41461
  const LIVE_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST = "LiveUtils.getPositionHighestProfitDistancePnlCost";
41095
41462
  const LIVE_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE = "LiveUtils.getPositionHighestMaxDrawdownPnlPercentage";
41096
41463
  const LIVE_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST = "LiveUtils.getPositionHighestMaxDrawdownPnlCost";
41464
+ const LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE = "LiveUtils.getMaxDrawdownDistancePnlPercentage";
41465
+ const LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST = "LiveUtils.getMaxDrawdownDistancePnlCost";
41097
41466
  const LIVE_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP = "LiveUtils.getPositionEntryOverlap";
41098
41467
  const LIVE_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP = "LiveUtils.getPositionPartialOverlap";
41099
41468
  const LIVE_METHOD_NAME_BREAKEVEN = "Live.commitBreakeven";
@@ -42618,6 +42987,70 @@ class LiveUtils {
42618
42987
  frameName: "",
42619
42988
  });
42620
42989
  };
42990
+ /**
42991
+ * Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
42992
+ *
42993
+ * Computed as: max(0, peakPnlPercentage - fallPnlPercentage).
42994
+ * Returns null if no pending signal exists.
42995
+ *
42996
+ * @param symbol - Trading pair symbol
42997
+ * @param context - Execution context with strategyName and exchangeName
42998
+ * @returns peak-to-trough PnL percentage distance (≥ 0) or null if no active position
42999
+ */
43000
+ this.getMaxDrawdownDistancePnlPercentage = async (symbol, context) => {
43001
+ backtest.loggerService.info(LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE, {
43002
+ symbol,
43003
+ context,
43004
+ });
43005
+ backtest.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
43006
+ backtest.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
43007
+ {
43008
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
43009
+ riskName &&
43010
+ backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
43011
+ riskList &&
43012
+ riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE));
43013
+ actions &&
43014
+ actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE));
43015
+ }
43016
+ return await backtest.strategyCoreService.getMaxDrawdownDistancePnlPercentage(false, symbol, {
43017
+ strategyName: context.strategyName,
43018
+ exchangeName: context.exchangeName,
43019
+ frameName: "",
43020
+ });
43021
+ };
43022
+ /**
43023
+ * Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
43024
+ *
43025
+ * Computed as: max(0, peakPnlCost - fallPnlCost).
43026
+ * Returns null if no pending signal exists.
43027
+ *
43028
+ * @param symbol - Trading pair symbol
43029
+ * @param context - Execution context with strategyName and exchangeName
43030
+ * @returns peak-to-trough PnL cost distance (≥ 0) or null if no active position
43031
+ */
43032
+ this.getMaxDrawdownDistancePnlCost = async (symbol, context) => {
43033
+ backtest.loggerService.info(LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST, {
43034
+ symbol,
43035
+ context,
43036
+ });
43037
+ backtest.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
43038
+ backtest.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
43039
+ {
43040
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
43041
+ riskName &&
43042
+ backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
43043
+ riskList &&
43044
+ riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST));
43045
+ actions &&
43046
+ actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST));
43047
+ }
43048
+ return await backtest.strategyCoreService.getMaxDrawdownDistancePnlCost(false, symbol, {
43049
+ strategyName: context.strategyName,
43050
+ exchangeName: context.exchangeName,
43051
+ frameName: "",
43052
+ });
43053
+ };
42621
43054
  /**
42622
43055
  * Checks whether the current price falls within the tolerance zone of any existing DCA entry level.
42623
43056
  * Use this to prevent duplicate DCA entries at the same price area.
@@ -42759,7 +43192,7 @@ class LiveUtils {
42759
43192
  * @param symbol - Trading pair symbol
42760
43193
  * @param strategyName - Strategy name
42761
43194
  * @param context - Execution context with exchangeName and frameName
42762
- * @param cancelId - Optional cancellation ID for tracking user-initiated cancellations
43195
+ * @param payload - Optional commit payload with id and note
42763
43196
  * @returns Promise that resolves when scheduled signal is cancelled
42764
43197
  *
42765
43198
  * @example
@@ -42769,14 +43202,14 @@ class LiveUtils {
42769
43202
  * exchangeName: "binance",
42770
43203
  * frameName: "",
42771
43204
  * strategyName: "my-strategy"
42772
- * }, "manual-cancel-001");
43205
+ * }, { id: "manual-cancel-001" });
42773
43206
  * ```
42774
43207
  */
42775
- this.commitCancelScheduled = async (symbol, context, cancelId) => {
43208
+ this.commitCancelScheduled = async (symbol, context, payload = {}) => {
42776
43209
  backtest.loggerService.info(LIVE_METHOD_NAME_CANCEL_SCHEDULED, {
42777
43210
  symbol,
42778
43211
  context,
42779
- cancelId,
43212
+ payload,
42780
43213
  });
42781
43214
  backtest.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_CANCEL_SCHEDULED);
42782
43215
  backtest.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_CANCEL_SCHEDULED);
@@ -42793,7 +43226,7 @@ class LiveUtils {
42793
43226
  strategyName: context.strategyName,
42794
43227
  exchangeName: context.exchangeName,
42795
43228
  frameName: "",
42796
- }, cancelId);
43229
+ }, payload);
42797
43230
  };
42798
43231
  /**
42799
43232
  * Closes the pending signal without stopping the strategy.
@@ -42804,7 +43237,7 @@ class LiveUtils {
42804
43237
  *
42805
43238
  * @param symbol - Trading pair symbol
42806
43239
  * @param context - Execution context with strategyName and exchangeName
42807
- * @param closeId - Optional close ID for user-initiated closes
43240
+ * @param payload - Optional commit payload with id and note
42808
43241
  * @returns Promise that resolves when pending signal is closed
42809
43242
  *
42810
43243
  * @example
@@ -42813,14 +43246,14 @@ class LiveUtils {
42813
43246
  * await Live.commitClose("BTCUSDT", {
42814
43247
  * exchangeName: "binance",
42815
43248
  * strategyName: "my-strategy"
42816
- * }, "manual-close-001");
43249
+ * }, { id: "manual-close-001" });
42817
43250
  * ```
42818
43251
  */
42819
- this.commitClosePending = async (symbol, context, closeId) => {
43252
+ this.commitClosePending = async (symbol, context, payload = {}) => {
42820
43253
  backtest.loggerService.info(LIVE_METHOD_NAME_CLOSE_PENDING, {
42821
43254
  symbol,
42822
43255
  context,
42823
- closeId,
43256
+ payload,
42824
43257
  });
42825
43258
  backtest.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_CLOSE_PENDING);
42826
43259
  backtest.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_CLOSE_PENDING);
@@ -42837,7 +43270,7 @@ class LiveUtils {
42837
43270
  strategyName: context.strategyName,
42838
43271
  exchangeName: context.exchangeName,
42839
43272
  frameName: "",
42840
- }, closeId);
43273
+ }, payload);
42841
43274
  };
42842
43275
  /**
42843
43276
  * Executes partial close at profit level (moving toward TP).
@@ -43631,7 +44064,7 @@ class LiveUtils {
43631
44064
  *
43632
44065
  * @param symbol - Trading pair symbol
43633
44066
  * @param context - Execution context with strategyName and exchangeName
43634
- * @param activateId - Optional activation ID for tracking user-initiated activations
44067
+ * @param payload - Optional commit payload with id and note
43635
44068
  * @returns Promise that resolves when activation flag is set
43636
44069
  *
43637
44070
  * @example
@@ -43640,14 +44073,14 @@ class LiveUtils {
43640
44073
  * await Live.commitActivateScheduled("BTCUSDT", {
43641
44074
  * strategyName: "my-strategy",
43642
44075
  * exchangeName: "binance"
43643
- * }, "manual-activate-001");
44076
+ * }, { id: "manual-activate-001" });
43644
44077
  * ```
43645
44078
  */
43646
- this.commitActivateScheduled = async (symbol, context, activateId) => {
44079
+ this.commitActivateScheduled = async (symbol, context, payload = {}) => {
43647
44080
  backtest.loggerService.info(LIVE_METHOD_NAME_ACTIVATE_SCHEDULED, {
43648
44081
  symbol,
43649
44082
  context,
43650
- activateId,
44083
+ payload,
43651
44084
  });
43652
44085
  backtest.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_ACTIVATE_SCHEDULED);
43653
44086
  backtest.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_ACTIVATE_SCHEDULED);
@@ -43664,7 +44097,7 @@ class LiveUtils {
43664
44097
  strategyName: context.strategyName,
43665
44098
  exchangeName: context.exchangeName,
43666
44099
  frameName: "",
43667
- }, activateId);
44100
+ }, payload);
43668
44101
  };
43669
44102
  /**
43670
44103
  * Adds a new DCA entry to the active pending signal.
@@ -44910,6 +45343,503 @@ async function listRiskSchema() {
44910
45343
  return await backtest.riskValidationService.list();
44911
45344
  }
44912
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
+
44913
45843
  const DEFAULT_BM25_K1 = 1.5;
44914
45844
  const DEFAULT_BM25_B = 0.75;
44915
45845
  const DEFAULT_BM25_SCORE = 0.5;
@@ -48160,6 +49090,67 @@ class LogAdapter {
48160
49090
  */
48161
49091
  const Log = new LogAdapter();
48162
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
+
48163
49154
  const SCHEDULE_METHOD_NAME_GET_DATA = "ScheduleUtils.getData";
48164
49155
  const SCHEDULE_METHOD_NAME_GET_REPORT = "ScheduleUtils.getReport";
48165
49156
  const SCHEDULE_METHOD_NAME_DUMP = "ScheduleUtils.dump";
@@ -49735,6 +50726,741 @@ class MaxDrawdownUtils {
49735
50726
  */
49736
50727
  const MaxDrawdown = new MaxDrawdownUtils();
49737
50728
 
50729
+ const REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT = "ReflectUtils.getPositionPnlPercent";
50730
+ const REFLECT_METHOD_NAME_GET_POSITION_PNL_COST = "ReflectUtils.getPositionPnlCost";
50731
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE = "ReflectUtils.getPositionHighestProfitPrice";
50732
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP = "ReflectUtils.getPositionHighestProfitTimestamp";
50733
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE = "ReflectUtils.getPositionHighestPnlPercentage";
50734
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST = "ReflectUtils.getPositionHighestPnlCost";
50735
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN = "ReflectUtils.getPositionHighestProfitBreakeven";
50736
+ const REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES = "ReflectUtils.getPositionDrawdownMinutes";
50737
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES = "ReflectUtils.getPositionHighestProfitMinutes";
50738
+ const REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES = "ReflectUtils.getPositionMaxDrawdownMinutes";
50739
+ const REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE = "ReflectUtils.getPositionMaxDrawdownPrice";
50740
+ const REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP = "ReflectUtils.getPositionMaxDrawdownTimestamp";
50741
+ const REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE = "ReflectUtils.getPositionMaxDrawdownPnlPercentage";
50742
+ const REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST = "ReflectUtils.getPositionMaxDrawdownPnlCost";
50743
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE = "ReflectUtils.getPositionHighestProfitDistancePnlPercentage";
50744
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST = "ReflectUtils.getPositionHighestProfitDistancePnlCost";
50745
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE = "ReflectUtils.getPositionHighestMaxDrawdownPnlPercentage";
50746
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST = "ReflectUtils.getPositionHighestMaxDrawdownPnlCost";
50747
+ const REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE = "ReflectUtils.getMaxDrawdownDistancePnlPercentage";
50748
+ const REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST = "ReflectUtils.getMaxDrawdownDistancePnlCost";
50749
+ /**
50750
+ * Utility class for real-time position reflection: PNL, peak profit, and drawdown queries.
50751
+ *
50752
+ * Provides unified access to strategyCoreService position state methods with logging
50753
+ * and full validation (strategy, exchange, frame, risk, actions).
50754
+ * Works for both live and backtest modes via the `backtest` parameter.
50755
+ * Exported as singleton instance for convenient usage.
50756
+ *
50757
+ * @example
50758
+ * ```typescript
50759
+ * import { Reflect } from "backtest-kit";
50760
+ *
50761
+ * // Get current unrealized PNL percentage
50762
+ * const pnl = await Reflect.getPositionPnlPercent(
50763
+ * "BTCUSDT",
50764
+ * 45000,
50765
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50766
+ * );
50767
+ * console.log(`PNL: ${pnl}%`);
50768
+ *
50769
+ * // Get peak profit reached
50770
+ * const peakPnl = await Reflect.getPositionHighestPnlPercentage(
50771
+ * "BTCUSDT",
50772
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50773
+ * );
50774
+ * console.log(`Peak PNL: ${peakPnl}%`);
50775
+ * ```
50776
+ */
50777
+ class ReflectUtils {
50778
+ constructor() {
50779
+ /**
50780
+ * Returns the unrealized PNL percentage for the current pending signal at currentPrice.
50781
+ *
50782
+ * Accounts for partial closes, DCA entries, slippage and fees.
50783
+ * Returns null if no pending signal exists.
50784
+ *
50785
+ * @param symbol - Trading pair symbol
50786
+ * @param currentPrice - Current market price
50787
+ * @param context - Execution context with strategyName, exchangeName and frameName
50788
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50789
+ * @returns Promise resolving to PNL percentage or null
50790
+ *
50791
+ * @example
50792
+ * ```typescript
50793
+ * const pnl = await Reflect.getPositionPnlPercent(
50794
+ * "BTCUSDT",
50795
+ * 45000,
50796
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50797
+ * );
50798
+ * console.log(`PNL: ${pnl}%`);
50799
+ * ```
50800
+ */
50801
+ this.getPositionPnlPercent = async (symbol, currentPrice, context, backtest$1 = false) => {
50802
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT, { symbol, currentPrice, context });
50803
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT);
50804
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT);
50805
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT);
50806
+ {
50807
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50808
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT);
50809
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT));
50810
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT));
50811
+ }
50812
+ return await backtest.strategyCoreService.getPositionPnlPercent(backtest$1, symbol, currentPrice, context);
50813
+ };
50814
+ /**
50815
+ * Returns the unrealized PNL in dollars for the current pending signal at currentPrice.
50816
+ *
50817
+ * Calculated as: pnlPercentage / 100 × totalInvestedCost.
50818
+ * Accounts for partial closes, DCA entries, slippage and fees.
50819
+ * Returns null if no pending signal exists.
50820
+ *
50821
+ * @param symbol - Trading pair symbol
50822
+ * @param currentPrice - Current market price
50823
+ * @param context - Execution context with strategyName, exchangeName and frameName
50824
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50825
+ * @returns Promise resolving to PNL in dollars or null
50826
+ *
50827
+ * @example
50828
+ * ```typescript
50829
+ * const pnlCost = await Reflect.getPositionPnlCost(
50830
+ * "BTCUSDT",
50831
+ * 45000,
50832
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50833
+ * );
50834
+ * console.log(`PNL: $${pnlCost}`);
50835
+ * ```
50836
+ */
50837
+ this.getPositionPnlCost = async (symbol, currentPrice, context, backtest$1 = false) => {
50838
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_PNL_COST, { symbol, currentPrice, context });
50839
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_PNL_COST);
50840
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_PNL_COST);
50841
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_PNL_COST);
50842
+ {
50843
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50844
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_PNL_COST);
50845
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_PNL_COST));
50846
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_PNL_COST));
50847
+ }
50848
+ return await backtest.strategyCoreService.getPositionPnlCost(backtest$1, symbol, currentPrice, context);
50849
+ };
50850
+ /**
50851
+ * Returns the best price reached in the profit direction during this position's life.
50852
+ *
50853
+ * Returns null if no pending signal exists.
50854
+ *
50855
+ * @param symbol - Trading pair symbol
50856
+ * @param context - Execution context with strategyName, exchangeName and frameName
50857
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50858
+ * @returns Promise resolving to price or null
50859
+ *
50860
+ * @example
50861
+ * ```typescript
50862
+ * const peakPrice = await Reflect.getPositionHighestProfitPrice(
50863
+ * "BTCUSDT",
50864
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50865
+ * );
50866
+ * console.log(`Peak price: ${peakPrice}`);
50867
+ * ```
50868
+ */
50869
+ this.getPositionHighestProfitPrice = async (symbol, context, backtest$1 = false) => {
50870
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE, { symbol, context });
50871
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE);
50872
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE);
50873
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE);
50874
+ {
50875
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50876
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE);
50877
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE));
50878
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE));
50879
+ }
50880
+ return await backtest.strategyCoreService.getPositionHighestProfitPrice(backtest$1, symbol, context);
50881
+ };
50882
+ /**
50883
+ * Returns the timestamp when the best profit price was recorded during this position's life.
50884
+ *
50885
+ * Returns null if no pending signal exists.
50886
+ *
50887
+ * @param symbol - Trading pair symbol
50888
+ * @param context - Execution context with strategyName, exchangeName and frameName
50889
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50890
+ * @returns Promise resolving to timestamp in milliseconds or null
50891
+ *
50892
+ * @example
50893
+ * ```typescript
50894
+ * const ts = await Reflect.getPositionHighestProfitTimestamp(
50895
+ * "BTCUSDT",
50896
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50897
+ * );
50898
+ * console.log(`Peak at: ${new Date(ts).toISOString()}`);
50899
+ * ```
50900
+ */
50901
+ this.getPositionHighestProfitTimestamp = async (symbol, context, backtest$1 = false) => {
50902
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP, { symbol, context });
50903
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP);
50904
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP);
50905
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP);
50906
+ {
50907
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50908
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP);
50909
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP));
50910
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP));
50911
+ }
50912
+ return await backtest.strategyCoreService.getPositionHighestProfitTimestamp(backtest$1, symbol, context);
50913
+ };
50914
+ /**
50915
+ * Returns the PnL percentage at the moment the best profit price was recorded during this position's life.
50916
+ *
50917
+ * Returns null if no pending signal exists.
50918
+ *
50919
+ * @param symbol - Trading pair symbol
50920
+ * @param context - Execution context with strategyName, exchangeName and frameName
50921
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50922
+ * @returns Promise resolving to PnL percentage or null
50923
+ *
50924
+ * @example
50925
+ * ```typescript
50926
+ * const peakPnl = await Reflect.getPositionHighestPnlPercentage(
50927
+ * "BTCUSDT",
50928
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50929
+ * );
50930
+ * console.log(`Peak PNL: ${peakPnl}%`);
50931
+ * ```
50932
+ */
50933
+ this.getPositionHighestPnlPercentage = async (symbol, context, backtest$1 = false) => {
50934
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE, { symbol, context });
50935
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE);
50936
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE);
50937
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE);
50938
+ {
50939
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50940
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE);
50941
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE));
50942
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE));
50943
+ }
50944
+ return await backtest.strategyCoreService.getPositionHighestPnlPercentage(backtest$1, symbol, context);
50945
+ };
50946
+ /**
50947
+ * Returns the PnL cost (in quote currency) at the moment the best profit price was recorded during this position's life.
50948
+ *
50949
+ * Returns null if no pending signal exists.
50950
+ *
50951
+ * @param symbol - Trading pair symbol
50952
+ * @param context - Execution context with strategyName, exchangeName and frameName
50953
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50954
+ * @returns Promise resolving to PnL cost in quote currency or null
50955
+ *
50956
+ * @example
50957
+ * ```typescript
50958
+ * const peakCost = await Reflect.getPositionHighestPnlCost(
50959
+ * "BTCUSDT",
50960
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50961
+ * );
50962
+ * console.log(`Peak PNL: $${peakCost}`);
50963
+ * ```
50964
+ */
50965
+ this.getPositionHighestPnlCost = async (symbol, context, backtest$1 = false) => {
50966
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST, { symbol, context });
50967
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST);
50968
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST);
50969
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST);
50970
+ {
50971
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50972
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST);
50973
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST));
50974
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST));
50975
+ }
50976
+ return await backtest.strategyCoreService.getPositionHighestPnlCost(backtest$1, symbol, context);
50977
+ };
50978
+ /**
50979
+ * Returns whether breakeven was mathematically reachable at the highest profit price.
50980
+ *
50981
+ * Returns null if no pending signal exists.
50982
+ *
50983
+ * @param symbol - Trading pair symbol
50984
+ * @param context - Execution context with strategyName, exchangeName and frameName
50985
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50986
+ * @returns Promise resolving to true if breakeven was reachable at peak, false otherwise, or null
50987
+ *
50988
+ * @example
50989
+ * ```typescript
50990
+ * const wasReachable = await Reflect.getPositionHighestProfitBreakeven(
50991
+ * "BTCUSDT",
50992
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50993
+ * );
50994
+ * console.log(`Breakeven reachable at peak: ${wasReachable}`);
50995
+ * ```
50996
+ */
50997
+ this.getPositionHighestProfitBreakeven = async (symbol, context, backtest$1 = false) => {
50998
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN, { symbol, context });
50999
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN);
51000
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN);
51001
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN);
51002
+ {
51003
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
51004
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN);
51005
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN));
51006
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN));
51007
+ }
51008
+ return await backtest.strategyCoreService.getPositionHighestProfitBreakeven(backtest$1, symbol, context);
51009
+ };
51010
+ /**
51011
+ * Returns the number of minutes elapsed since the highest profit price was recorded.
51012
+ *
51013
+ * Returns null if no pending signal exists.
51014
+ *
51015
+ * @param symbol - Trading pair symbol
51016
+ * @param context - Execution context with strategyName, exchangeName and frameName
51017
+ * @param backtest - True if backtest mode, false if live mode (default: false)
51018
+ * @returns Promise resolving to minutes since highest profit price was recorded, or null
51019
+ *
51020
+ * @example
51021
+ * ```typescript
51022
+ * const minutes = await Reflect.getPositionDrawdownMinutes(
51023
+ * "BTCUSDT",
51024
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
51025
+ * );
51026
+ * console.log(`Pulling back from peak for ${minutes} minutes`);
51027
+ * ```
51028
+ */
51029
+ this.getPositionDrawdownMinutes = async (symbol, context, backtest$1 = false) => {
51030
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES, { symbol, context });
51031
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES);
51032
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES);
51033
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES);
51034
+ {
51035
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
51036
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES);
51037
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES));
51038
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES));
51039
+ }
51040
+ return await backtest.strategyCoreService.getPositionDrawdownMinutes(backtest$1, symbol, context);
51041
+ };
51042
+ /**
51043
+ * Returns the number of minutes elapsed since the highest profit price was recorded.
51044
+ *
51045
+ * Alias for getPositionDrawdownMinutes — measures how long the position has been
51046
+ * pulling back from its peak profit level.
51047
+ * Returns null if no pending signal exists.
51048
+ *
51049
+ * @param symbol - Trading pair symbol
51050
+ * @param context - Execution context with strategyName, exchangeName and frameName
51051
+ * @param backtest - True if backtest mode, false if live mode (default: false)
51052
+ * @returns Promise resolving to minutes since last profit peak or null
51053
+ *
51054
+ * @example
51055
+ * ```typescript
51056
+ * const minutes = await Reflect.getPositionHighestProfitMinutes(
51057
+ * "BTCUSDT",
51058
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
51059
+ * );
51060
+ * console.log(`Pulling back from peak for ${minutes} minutes`);
51061
+ * ```
51062
+ */
51063
+ this.getPositionHighestProfitMinutes = async (symbol, context, backtest$1 = false) => {
51064
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES, { symbol, context });
51065
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES);
51066
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES);
51067
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES);
51068
+ {
51069
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
51070
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES);
51071
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES));
51072
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES));
51073
+ }
51074
+ return await backtest.strategyCoreService.getPositionHighestProfitMinutes(backtest$1, symbol, context);
51075
+ };
51076
+ /**
51077
+ * Returns the number of minutes elapsed since the worst loss price was recorded.
51078
+ *
51079
+ * Measures how long ago the deepest drawdown point occurred.
51080
+ * Zero when called at the exact moment the trough was set.
51081
+ * Returns null if no pending signal exists.
51082
+ *
51083
+ * @param symbol - Trading pair symbol
51084
+ * @param context - Execution context with strategyName, exchangeName and frameName
51085
+ * @param backtest - True if backtest mode, false if live mode (default: false)
51086
+ * @returns Promise resolving to minutes since last drawdown trough or null
51087
+ *
51088
+ * @example
51089
+ * ```typescript
51090
+ * const minutes = await Reflect.getPositionMaxDrawdownMinutes(
51091
+ * "BTCUSDT",
51092
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
51093
+ * );
51094
+ * console.log(`Drawdown trough was ${minutes} minutes ago`);
51095
+ * ```
51096
+ */
51097
+ this.getPositionMaxDrawdownMinutes = async (symbol, context, backtest$1 = false) => {
51098
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES, { symbol, context });
51099
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES);
51100
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES);
51101
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES);
51102
+ {
51103
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
51104
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES);
51105
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES));
51106
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES));
51107
+ }
51108
+ return await backtest.strategyCoreService.getPositionMaxDrawdownMinutes(backtest$1, symbol, context);
51109
+ };
51110
+ /**
51111
+ * Returns the worst price reached in the loss direction during this position's life.
51112
+ *
51113
+ * Returns null if no pending signal exists.
51114
+ *
51115
+ * @param symbol - Trading pair symbol
51116
+ * @param context - Execution context with strategyName, exchangeName and frameName
51117
+ * @param backtest - True if backtest mode, false if live mode (default: false)
51118
+ * @returns Promise resolving to price or null
51119
+ *
51120
+ * @example
51121
+ * ```typescript
51122
+ * const troughPrice = await Reflect.getPositionMaxDrawdownPrice(
51123
+ * "BTCUSDT",
51124
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
51125
+ * );
51126
+ * console.log(`Worst price: ${troughPrice}`);
51127
+ * ```
51128
+ */
51129
+ this.getPositionMaxDrawdownPrice = async (symbol, context, backtest$1 = false) => {
51130
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE, { symbol, context });
51131
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE);
51132
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE);
51133
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE);
51134
+ {
51135
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
51136
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE);
51137
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE));
51138
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE));
51139
+ }
51140
+ return await backtest.strategyCoreService.getPositionMaxDrawdownPrice(backtest$1, symbol, context);
51141
+ };
51142
+ /**
51143
+ * Returns the timestamp when the worst loss price was recorded during this position's life.
51144
+ *
51145
+ * Returns null if no pending signal exists.
51146
+ *
51147
+ * @param symbol - Trading pair symbol
51148
+ * @param context - Execution context with strategyName, exchangeName and frameName
51149
+ * @param backtest - True if backtest mode, false if live mode (default: false)
51150
+ * @returns Promise resolving to timestamp in milliseconds or null
51151
+ *
51152
+ * @example
51153
+ * ```typescript
51154
+ * const ts = await Reflect.getPositionMaxDrawdownTimestamp(
51155
+ * "BTCUSDT",
51156
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
51157
+ * );
51158
+ * console.log(`Worst drawdown at: ${new Date(ts).toISOString()}`);
51159
+ * ```
51160
+ */
51161
+ this.getPositionMaxDrawdownTimestamp = async (symbol, context, backtest$1 = false) => {
51162
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP, { symbol, context });
51163
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP);
51164
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP);
51165
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP);
51166
+ {
51167
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
51168
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP);
51169
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP));
51170
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP));
51171
+ }
51172
+ return await backtest.strategyCoreService.getPositionMaxDrawdownTimestamp(backtest$1, symbol, context);
51173
+ };
51174
+ /**
51175
+ * Returns the PnL percentage at the moment the worst loss price was recorded during this position's life.
51176
+ *
51177
+ * Returns null if no pending signal exists.
51178
+ *
51179
+ * @param symbol - Trading pair symbol
51180
+ * @param context - Execution context with strategyName, exchangeName and frameName
51181
+ * @param backtest - True if backtest mode, false if live mode (default: false)
51182
+ * @returns Promise resolving to PnL percentage or null
51183
+ *
51184
+ * @example
51185
+ * ```typescript
51186
+ * const worstPnl = await Reflect.getPositionMaxDrawdownPnlPercentage(
51187
+ * "BTCUSDT",
51188
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
51189
+ * );
51190
+ * console.log(`Worst PNL: ${worstPnl}%`);
51191
+ * ```
51192
+ */
51193
+ this.getPositionMaxDrawdownPnlPercentage = async (symbol, context, backtest$1 = false) => {
51194
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE, { symbol, context });
51195
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE);
51196
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE);
51197
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE);
51198
+ {
51199
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
51200
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE);
51201
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE));
51202
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE));
51203
+ }
51204
+ return await backtest.strategyCoreService.getPositionMaxDrawdownPnlPercentage(backtest$1, symbol, context);
51205
+ };
51206
+ /**
51207
+ * Returns the PnL cost (in quote currency) at the moment the worst loss price was recorded during this position's life.
51208
+ *
51209
+ * Returns null if no pending signal exists.
51210
+ *
51211
+ * @param symbol - Trading pair symbol
51212
+ * @param context - Execution context with strategyName, exchangeName and frameName
51213
+ * @param backtest - True if backtest mode, false if live mode (default: false)
51214
+ * @returns Promise resolving to PnL cost in quote currency or null
51215
+ *
51216
+ * @example
51217
+ * ```typescript
51218
+ * const worstCost = await Reflect.getPositionMaxDrawdownPnlCost(
51219
+ * "BTCUSDT",
51220
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
51221
+ * );
51222
+ * console.log(`Worst PNL: $${worstCost}`);
51223
+ * ```
51224
+ */
51225
+ this.getPositionMaxDrawdownPnlCost = async (symbol, context, backtest$1 = false) => {
51226
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST, { symbol, context });
51227
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST);
51228
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST);
51229
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST);
51230
+ {
51231
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
51232
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST);
51233
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST));
51234
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST));
51235
+ }
51236
+ return await backtest.strategyCoreService.getPositionMaxDrawdownPnlCost(backtest$1, symbol, context);
51237
+ };
51238
+ /**
51239
+ * Returns the distance in PnL percentage between the current price and the highest profit peak.
51240
+ *
51241
+ * Result is ≥ 0. Returns null if no pending signal exists.
51242
+ *
51243
+ * @param symbol - Trading pair symbol
51244
+ * @param context - Execution context with strategyName, exchangeName and frameName
51245
+ * @param backtest - True if backtest mode, false if live mode (default: false)
51246
+ * @returns Promise resolving to drawdown distance in PnL% (≥ 0) or null
51247
+ *
51248
+ * @example
51249
+ * ```typescript
51250
+ * const distance = await Reflect.getPositionHighestProfitDistancePnlPercentage(
51251
+ * "BTCUSDT",
51252
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
51253
+ * );
51254
+ * console.log(`Dropped ${distance}% from peak`);
51255
+ * ```
51256
+ */
51257
+ this.getPositionHighestProfitDistancePnlPercentage = async (symbol, context, backtest$1 = false) => {
51258
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE, { symbol, context });
51259
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE);
51260
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE);
51261
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE);
51262
+ {
51263
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
51264
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE);
51265
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE));
51266
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE));
51267
+ }
51268
+ return await backtest.strategyCoreService.getPositionHighestProfitDistancePnlPercentage(backtest$1, symbol, context);
51269
+ };
51270
+ /**
51271
+ * Returns the distance in PnL cost between the current price and the highest profit peak.
51272
+ *
51273
+ * Result is ≥ 0. Returns null if no pending signal exists.
51274
+ *
51275
+ * @param symbol - Trading pair symbol
51276
+ * @param context - Execution context with strategyName, exchangeName and frameName
51277
+ * @param backtest - True if backtest mode, false if live mode (default: false)
51278
+ * @returns Promise resolving to drawdown distance in PnL cost (≥ 0) or null
51279
+ *
51280
+ * @example
51281
+ * ```typescript
51282
+ * const distance = await Reflect.getPositionHighestProfitDistancePnlCost(
51283
+ * "BTCUSDT",
51284
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
51285
+ * );
51286
+ * console.log(`Dropped $${distance} from peak`);
51287
+ * ```
51288
+ */
51289
+ this.getPositionHighestProfitDistancePnlCost = async (symbol, context, backtest$1 = false) => {
51290
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST, { symbol, context });
51291
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST);
51292
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST);
51293
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST);
51294
+ {
51295
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
51296
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST);
51297
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST));
51298
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST));
51299
+ }
51300
+ return await backtest.strategyCoreService.getPositionHighestProfitDistancePnlCost(backtest$1, symbol, context);
51301
+ };
51302
+ /**
51303
+ * Returns the distance in PnL percentage between the current price and the worst drawdown trough.
51304
+ *
51305
+ * Result is ≥ 0. Returns null if no pending signal exists.
51306
+ *
51307
+ * @param symbol - Trading pair symbol
51308
+ * @param context - Execution context with strategyName, exchangeName and frameName
51309
+ * @param backtest - True if backtest mode, false if live mode (default: false)
51310
+ * @returns Promise resolving to recovery distance from worst drawdown trough in PnL% (≥ 0) or null
51311
+ *
51312
+ * @example
51313
+ * ```typescript
51314
+ * const distance = await Reflect.getPositionHighestMaxDrawdownPnlPercentage(
51315
+ * "BTCUSDT",
51316
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
51317
+ * );
51318
+ * console.log(`${distance}% above worst trough`);
51319
+ * ```
51320
+ */
51321
+ this.getPositionHighestMaxDrawdownPnlPercentage = async (symbol, context, backtest$1 = false) => {
51322
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE, { symbol, context });
51323
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE);
51324
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE);
51325
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE);
51326
+ {
51327
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
51328
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE);
51329
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE));
51330
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE));
51331
+ }
51332
+ return await backtest.strategyCoreService.getPositionHighestMaxDrawdownPnlPercentage(backtest$1, symbol, context);
51333
+ };
51334
+ /**
51335
+ * Returns the distance in PnL cost between the current price and the worst drawdown trough.
51336
+ *
51337
+ * Result is ≥ 0. Returns null if no pending signal exists.
51338
+ *
51339
+ * @param symbol - Trading pair symbol
51340
+ * @param context - Execution context with strategyName, exchangeName and frameName
51341
+ * @param backtest - True if backtest mode, false if live mode (default: false)
51342
+ * @returns Promise resolving to recovery distance from worst drawdown trough in PnL cost (≥ 0) or null
51343
+ *
51344
+ * @example
51345
+ * ```typescript
51346
+ * const distance = await Reflect.getPositionHighestMaxDrawdownPnlCost(
51347
+ * "BTCUSDT",
51348
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
51349
+ * );
51350
+ * console.log(`$${distance} above worst trough`);
51351
+ * ```
51352
+ */
51353
+ this.getPositionHighestMaxDrawdownPnlCost = async (symbol, context, backtest$1 = false) => {
51354
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST, { symbol, context });
51355
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST);
51356
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST);
51357
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST);
51358
+ {
51359
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
51360
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST);
51361
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST));
51362
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST));
51363
+ }
51364
+ return await backtest.strategyCoreService.getPositionHighestMaxDrawdownPnlCost(backtest$1, symbol, context);
51365
+ };
51366
+ /**
51367
+ * Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
51368
+ *
51369
+ * Result is ≥ 0. Returns null if no pending signal exists.
51370
+ *
51371
+ * @param symbol - Trading pair symbol
51372
+ * @param context - Execution context with strategyName, exchangeName and frameName
51373
+ * @param backtest - True if backtest mode, false if live mode (default: false)
51374
+ * @returns Promise resolving to peak-to-trough PnL percentage distance (≥ 0) or null
51375
+ *
51376
+ * @example
51377
+ * ```typescript
51378
+ * const distance = await Reflect.getMaxDrawdownDistancePnlPercentage(
51379
+ * "BTCUSDT",
51380
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
51381
+ * );
51382
+ * console.log(`Peak-to-trough: ${distance}%`);
51383
+ * ```
51384
+ */
51385
+ this.getMaxDrawdownDistancePnlPercentage = async (symbol, context, backtest$1 = false) => {
51386
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE, { symbol, context });
51387
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
51388
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
51389
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
51390
+ {
51391
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
51392
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
51393
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE));
51394
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE));
51395
+ }
51396
+ return await backtest.strategyCoreService.getMaxDrawdownDistancePnlPercentage(backtest$1, symbol, context);
51397
+ };
51398
+ /**
51399
+ * Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
51400
+ *
51401
+ * Result is ≥ 0. Returns null if no pending signal exists.
51402
+ *
51403
+ * @param symbol - Trading pair symbol
51404
+ * @param context - Execution context with strategyName, exchangeName and frameName
51405
+ * @param backtest - True if backtest mode, false if live mode (default: false)
51406
+ * @returns Promise resolving to peak-to-trough PnL cost distance (≥ 0) or null
51407
+ *
51408
+ * @example
51409
+ * ```typescript
51410
+ * const distance = await Reflect.getMaxDrawdownDistancePnlCost(
51411
+ * "BTCUSDT",
51412
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
51413
+ * );
51414
+ * console.log(`Peak-to-trough: $${distance}`);
51415
+ * ```
51416
+ */
51417
+ this.getMaxDrawdownDistancePnlCost = async (symbol, context, backtest$1 = false) => {
51418
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST, { symbol, context });
51419
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
51420
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
51421
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
51422
+ {
51423
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
51424
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
51425
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST));
51426
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST));
51427
+ }
51428
+ return await backtest.strategyCoreService.getMaxDrawdownDistancePnlCost(backtest$1, symbol, context);
51429
+ };
51430
+ }
51431
+ }
51432
+ /**
51433
+ * Singleton instance of ReflectUtils for convenient position state queries.
51434
+ *
51435
+ * @example
51436
+ * ```typescript
51437
+ * import { Reflect } from "backtest-kit";
51438
+ *
51439
+ * // Real-time PNL
51440
+ * const pnl = await Reflect.getPositionPnlPercent(
51441
+ * "BTCUSDT",
51442
+ * 45000,
51443
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
51444
+ * );
51445
+ * console.log(`PNL: ${pnl}%`);
51446
+ *
51447
+ * // Peak profit
51448
+ * const peakPnl = await Reflect.getPositionHighestPnlPercentage(
51449
+ * "BTCUSDT",
51450
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
51451
+ * );
51452
+ * console.log(`Peak PNL: ${peakPnl}%`);
51453
+ *
51454
+ * // Drawdown from peak
51455
+ * const drawdown = await Reflect.getPositionHighestProfitDistancePnlPercentage(
51456
+ * "BTCUSDT",
51457
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
51458
+ * );
51459
+ * console.log(`Dropped ${drawdown}% from peak`);
51460
+ * ```
51461
+ */
51462
+ const Reflect$1 = new ReflectUtils();
51463
+
49738
51464
  /**
49739
51465
  * Utility class containing predefined trading constants for take-profit and stop-loss levels.
49740
51466
  *
@@ -53662,12 +55388,15 @@ const CACHE_METHOD_NAME_RUN = "CacheFnInstance.run";
53662
55388
  const CACHE_METHOD_NAME_FN = "CacheUtils.fn";
53663
55389
  const CACHE_METHOD_NAME_FN_CLEAR = "CacheUtils.fn.clear";
53664
55390
  const CACHE_METHOD_NAME_FN_GC = "CacheUtils.fn.gc";
55391
+ const CACHE_METHOD_NAME_FN_HAS_VALUE = "CacheUtils.fn.hasValue";
53665
55392
  const CACHE_METHOD_NAME_FILE = "CacheUtils.file";
53666
55393
  const CACHE_METHOD_NAME_FILE_CLEAR = "CacheUtils.file.clear";
55394
+ const CACHE_METHOD_NAME_FILE_HAS_VALUE = "CacheUtils.file.hasValue";
53667
55395
  const CACHE_METHOD_NAME_DISPOSE = "CacheUtils.dispose";
53668
55396
  const CACHE_METHOD_NAME_CLEAR = "CacheUtils.clear";
53669
55397
  const CACHE_METHOD_NAME_RESET_COUNTER = "CacheUtils.resetCounter";
53670
55398
  const CACHE_FILE_INSTANCE_METHOD_NAME_RUN = "CacheFileInstance.run";
55399
+ const CACHE_FILE_INSTANCE_METHOD_NAME_HAS_VALUE = "CacheFileInstance.hasValue";
53671
55400
  const MS_PER_MINUTE$1 = 60000;
53672
55401
  const INTERVAL_MINUTES$1 = {
53673
55402
  "1m": 1,
@@ -53857,6 +55586,36 @@ class CacheFnInstance {
53857
55586
  }
53858
55587
  }
53859
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
+ };
53860
55619
  /**
53861
55620
  * Garbage collect expired cache entries.
53862
55621
  *
@@ -53981,6 +55740,33 @@ class CacheFileInstance {
53981
55740
  await PersistMeasureAdapter.writeMeasureData({ id: entityKey, data: result, removed: false }, bucket, entityKey);
53982
55741
  return result;
53983
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
+ };
53984
55770
  /**
53985
55771
  * Soft-delete all persisted records for this instance's bucket.
53986
55772
  * After this call the next `run()` will recompute and re-cache the value.
@@ -54067,23 +55853,30 @@ class CacheUtils {
54067
55853
  wrappedFn.clear = () => {
54068
55854
  backtest.loggerService.info(CACHE_METHOD_NAME_FN_CLEAR);
54069
55855
  if (!MethodContextService.hasContext()) {
54070
- backtest.loggerService.warn(`${CACHE_METHOD_NAME_FN_CLEAR} called without method context, skipping`);
54071
- return;
55856
+ throw new Error(`${CACHE_METHOD_NAME_FN_CLEAR} requires method context`);
54072
55857
  }
54073
55858
  if (!ExecutionContextService.hasContext()) {
54074
- backtest.loggerService.warn(`${CACHE_METHOD_NAME_FN_CLEAR} called without execution context, skipping`);
54075
- return;
55859
+ throw new Error(`${CACHE_METHOD_NAME_FN_CLEAR} requires execution context`);
54076
55860
  }
54077
55861
  this._getFnInstance.get(run)?.clear();
54078
55862
  };
54079
55863
  wrappedFn.gc = () => {
54080
55864
  backtest.loggerService.info(CACHE_METHOD_NAME_FN_GC);
54081
55865
  if (!ExecutionContextService.hasContext()) {
54082
- backtest.loggerService.warn(`${CACHE_METHOD_NAME_FN_GC} called without execution context, skipping`);
54083
- return;
55866
+ throw new Error(`${CACHE_METHOD_NAME_FN_GC} requires execution context`);
54084
55867
  }
54085
55868
  return this._getFnInstance.get(run)?.gc();
54086
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
+ };
54087
55880
  return wrappedFn;
54088
55881
  };
54089
55882
  /**
@@ -54135,8 +55928,25 @@ class CacheUtils {
54135
55928
  };
54136
55929
  wrappedFn.clear = async () => {
54137
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
+ }
54138
55937
  await this._getFileInstance.get(run)?.clear();
54139
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
+ };
54140
55950
  return wrappedFn;
54141
55951
  };
54142
55952
  /**
@@ -54205,11 +56015,14 @@ const Cache = new CacheUtils();
54205
56015
 
54206
56016
  const INTERVAL_METHOD_NAME_RUN = "IntervalFnInstance.run";
54207
56017
  const INTERVAL_FILE_INSTANCE_METHOD_NAME_RUN = "IntervalFileInstance.run";
56018
+ const INTERVAL_FILE_INSTANCE_METHOD_NAME_HAS_VALUE = "IntervalFileInstance.hasValue";
54208
56019
  const INTERVAL_METHOD_NAME_FN = "IntervalUtils.fn";
54209
56020
  const INTERVAL_METHOD_NAME_FN_CLEAR = "IntervalUtils.fn.clear";
54210
56021
  const INTERVAL_METHOD_NAME_FN_GC = "IntervalUtils.fn.gc";
56022
+ const INTERVAL_METHOD_NAME_FN_HAS_VALUE = "IntervalUtils.fn.hasValue";
54211
56023
  const INTERVAL_METHOD_NAME_FILE = "IntervalUtils.file";
54212
56024
  const INTERVAL_METHOD_NAME_FILE_CLEAR = "IntervalUtils.file.clear";
56025
+ const INTERVAL_METHOD_NAME_FILE_HAS_VALUE = "IntervalUtils.file.hasValue";
54213
56026
  const INTERVAL_METHOD_NAME_DISPOSE = "IntervalUtils.dispose";
54214
56027
  const INTERVAL_METHOD_NAME_CLEAR = "IntervalUtils.clear";
54215
56028
  const INTERVAL_METHOD_NAME_RESET_COUNTER = "IntervalUtils.resetCounter";
@@ -54367,6 +56180,31 @@ class IntervalFnInstance {
54367
56180
  }
54368
56181
  }
54369
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
+ };
54370
56208
  /**
54371
56209
  * Garbage collect expired state entries.
54372
56210
  *
@@ -54488,6 +56326,33 @@ class IntervalFileInstance {
54488
56326
  }
54489
56327
  return result;
54490
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
+ };
54491
56356
  /**
54492
56357
  * Soft-delete all persisted records for this instance's bucket.
54493
56358
  * After this call the function will fire again on the next `run()`.
@@ -54568,23 +56433,30 @@ class IntervalUtils {
54568
56433
  wrappedFn.clear = () => {
54569
56434
  backtest.loggerService.info(INTERVAL_METHOD_NAME_FN_CLEAR);
54570
56435
  if (!MethodContextService.hasContext()) {
54571
- backtest.loggerService.warn(`${INTERVAL_METHOD_NAME_FN_CLEAR} called without method context, skipping`);
54572
- return;
56436
+ throw new Error(`${INTERVAL_METHOD_NAME_FN_CLEAR} requires method context`);
54573
56437
  }
54574
56438
  if (!ExecutionContextService.hasContext()) {
54575
- backtest.loggerService.warn(`${INTERVAL_METHOD_NAME_FN_CLEAR} called without execution context, skipping`);
54576
- return;
56439
+ throw new Error(`${INTERVAL_METHOD_NAME_FN_CLEAR} requires execution context`);
54577
56440
  }
54578
56441
  this._getInstance.get(run)?.clear();
54579
56442
  };
54580
56443
  wrappedFn.gc = () => {
54581
56444
  backtest.loggerService.info(INTERVAL_METHOD_NAME_FN_GC);
54582
56445
  if (!ExecutionContextService.hasContext()) {
54583
- backtest.loggerService.warn(`${INTERVAL_METHOD_NAME_FN_GC} called without execution context, skipping`);
54584
- return;
56446
+ throw new Error(`${INTERVAL_METHOD_NAME_FN_GC} requires execution context`);
54585
56447
  }
54586
56448
  return this._getInstance.get(run)?.gc();
54587
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
+ };
54588
56460
  return wrappedFn;
54589
56461
  };
54590
56462
  /**
@@ -54627,8 +56499,25 @@ class IntervalUtils {
54627
56499
  };
54628
56500
  wrappedFn.clear = async () => {
54629
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
+ }
54630
56508
  await this._getFileInstance.get(run)?.clear();
54631
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
+ };
54632
56521
  return wrappedFn;
54633
56522
  };
54634
56523
  /**
@@ -55802,4 +57691,4 @@ const validateSignal = (signal, currentPrice) => {
55802
57691
  return !errors.length;
55803
57692
  };
55804
57693
 
55805
- 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, 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, 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 };