backtest-kit 6.11.0 → 6.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.cjs CHANGED
@@ -6593,7 +6593,7 @@ const RETURN_IDLE_FN = async (self, currentPrice) => {
6593
6593
  await CALL_TICK_CALLBACKS_FN(self, self.params.execution.context.symbol, result, currentTime, self.params.execution.context.backtest);
6594
6594
  return result;
6595
6595
  };
6596
- const CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN = async (self, scheduled, averagePrice, closeTimestamp, reason, cancelId) => {
6596
+ const CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN = async (self, scheduled, averagePrice, closeTimestamp, reason, cancelId, cancelNote) => {
6597
6597
  self.params.logger.info("ClientStrategy backtest scheduled signal cancelled", {
6598
6598
  symbol: self.params.execution.context.symbol,
6599
6599
  signalId: scheduled.id,
@@ -6618,7 +6618,7 @@ const CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN = async (self, scheduled, averagePr
6618
6618
  totalPartials: scheduled._partial?.length ?? 0,
6619
6619
  originalPriceOpen: scheduled.priceOpen,
6620
6620
  pnl: toProfitLossDto(scheduled, averagePrice),
6621
- note: scheduled.note,
6621
+ note: cancelNote ?? scheduled.note,
6622
6622
  });
6623
6623
  }
6624
6624
  await CALL_CANCEL_CALLBACKS_FN(self, self.params.execution.context.symbol, scheduled, averagePrice, closeTimestamp, self.params.execution.context.backtest);
@@ -6785,7 +6785,7 @@ const CLOSE_USER_PENDING_SIGNAL_IN_BACKTEST_FN = async (self, closedSignal, aver
6785
6785
  totalPartials: closedSignal._partial?.length ?? 0,
6786
6786
  originalPriceOpen: closedSignal.priceOpen,
6787
6787
  pnl: toProfitLossDto(closedSignal, averagePrice),
6788
- note: closedSignal.note,
6788
+ note: closedSignal.closeNote ?? closedSignal.note,
6789
6789
  });
6790
6790
  await CALL_CLOSE_CALLBACKS_FN(self, self.params.execution.context.symbol, closedSignal, averagePrice, closeTimestamp, self.params.execution.context.backtest);
6791
6791
  await CALL_PARTIAL_CLEAR_FN(self, self.params.execution.context.symbol, closedSignal, averagePrice, closeTimestamp, self.params.execution.context.backtest);
@@ -6832,7 +6832,8 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles, fra
6832
6832
  if (self._cancelledSignal) {
6833
6833
  // Сигнал был отменен через cancel() в onSchedulePing
6834
6834
  const cancelId = self._cancelledSignal.cancelId;
6835
- const result = await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, averagePrice, candle.timestamp, "user", cancelId);
6835
+ const cancelNote = self._cancelledSignal.cancelNote;
6836
+ const result = await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, averagePrice, candle.timestamp, "user", cancelId, cancelNote);
6836
6837
  return { outcome: "cancelled", result };
6837
6838
  }
6838
6839
  // КРИТИЧНО: Проверяем был ли сигнал активирован пользователем через activateScheduled()
@@ -6886,7 +6887,7 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles, fra
6886
6887
  totalPartials: activatedSignal._partial?.length ?? 0,
6887
6888
  originalPriceOpen: activatedSignal.priceOpen,
6888
6889
  pnl: toProfitLossDto(activatedSignal, averagePrice),
6889
- note: activatedSignal.note,
6890
+ note: activatedSignal.activateNote ?? activatedSignal.note,
6890
6891
  });
6891
6892
  return { outcome: "pending" };
6892
6893
  }
@@ -6918,7 +6919,7 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles, fra
6918
6919
  pendingAt: publicSignalForCommit.pendingAt,
6919
6920
  totalEntries: publicSignalForCommit.totalEntries,
6920
6921
  totalPartials: publicSignalForCommit.totalPartials,
6921
- note: publicSignalForCommit.note,
6922
+ note: activatedSignal.activateNote ?? publicSignalForCommit.note,
6922
6923
  });
6923
6924
  await CALL_OPEN_CALLBACKS_FN(self, self.params.execution.context.symbol, pendingSignal, pendingSignal.priceOpen, candle.timestamp, self.params.execution.context.backtest);
6924
6925
  await CALL_BACKTEST_SCHEDULE_OPEN_FN(self, self.params.execution.context.symbol, pendingSignal, candle.timestamp, self.params.execution.context.backtest);
@@ -8039,6 +8040,44 @@ class ClientStrategy {
8039
8040
  const currentPnl = toProfitLossDto(this._pendingSignal, currentPrice);
8040
8041
  return Math.max(0, currentPnl.pnlCost - this._pendingSignal._fall.pnlCost);
8041
8042
  }
8043
+ /**
8044
+ * Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
8045
+ *
8046
+ * Measures the total swing from the stored `_peak.pnlPercentage` to the stored `_fall.pnlPercentage`.
8047
+ * Computed as: max(0, peakPnlPercentage - fallPnlPercentage).
8048
+ *
8049
+ * Returns null if no pending signal exists.
8050
+ *
8051
+ * @param symbol - Trading pair symbol
8052
+ * @param currentPrice - Current market price
8053
+ * @returns Promise resolving to peak-to-trough PnL percentage distance (≥ 0) or null
8054
+ */
8055
+ async getMaxDrawdownDistancePnlPercentage(symbol, currentPrice) {
8056
+ this.params.logger.debug("ClientStrategy getMaxDrawdownDistancePnlPercentage", { symbol, currentPrice });
8057
+ if (!this._pendingSignal) {
8058
+ return null;
8059
+ }
8060
+ return Math.max(0, this._pendingSignal._peak.pnlPercentage - this._pendingSignal._fall.pnlPercentage);
8061
+ }
8062
+ /**
8063
+ * Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
8064
+ *
8065
+ * Measures the total swing from the stored `_peak.pnlCost` to the stored `_fall.pnlCost`.
8066
+ * Computed as: max(0, peakPnlCost - fallPnlCost).
8067
+ *
8068
+ * Returns null if no pending signal exists.
8069
+ *
8070
+ * @param symbol - Trading pair symbol
8071
+ * @param currentPrice - Current market price
8072
+ * @returns Promise resolving to peak-to-trough PnL cost distance (≥ 0) or null
8073
+ */
8074
+ async getMaxDrawdownDistancePnlCost(symbol, currentPrice) {
8075
+ this.params.logger.debug("ClientStrategy getMaxDrawdownDistancePnlCost", { symbol, currentPrice });
8076
+ if (!this._pendingSignal) {
8077
+ return null;
8078
+ }
8079
+ return Math.max(0, this._pendingSignal._peak.pnlCost - this._pendingSignal._fall.pnlCost);
8080
+ }
8042
8081
  /**
8043
8082
  * Performs a single tick of strategy execution.
8044
8083
  *
@@ -8103,7 +8142,7 @@ class ClientStrategy {
8103
8142
  totalPartials: cancelledSignal._partial?.length ?? 0,
8104
8143
  originalPriceOpen: cancelledSignal.priceOpen,
8105
8144
  pnl: toProfitLossDto(cancelledSignal, currentPrice),
8106
- note: cancelledSignal.note,
8145
+ note: cancelledSignal.cancelNote ?? cancelledSignal.note,
8107
8146
  });
8108
8147
  // Call onCancel callback
8109
8148
  await CALL_CANCEL_CALLBACKS_FN(this, this.params.execution.context.symbol, cancelledSignal, currentPrice, currentTime, this.params.execution.context.backtest);
@@ -8157,7 +8196,7 @@ class ClientStrategy {
8157
8196
  totalPartials: closedSignal._partial?.length ?? 0,
8158
8197
  originalPriceOpen: closedSignal.priceOpen,
8159
8198
  pnl: toProfitLossDto(closedSignal, currentPrice),
8160
- note: closedSignal.note,
8199
+ note: closedSignal.closeNote ?? closedSignal.note,
8161
8200
  });
8162
8201
  // Call onClose callback
8163
8202
  await CALL_CLOSE_CALLBACKS_FN(this, this.params.execution.context.symbol, closedSignal, currentPrice, currentTime, this.params.execution.context.backtest);
@@ -8239,7 +8278,7 @@ class ClientStrategy {
8239
8278
  totalPartials: activatedSignal._partial?.length ?? 0,
8240
8279
  originalPriceOpen: activatedSignal.priceOpen,
8241
8280
  pnl: toProfitLossDto(activatedSignal, currentPrice),
8242
- note: activatedSignal.note,
8281
+ note: activatedSignal.activateNote ?? activatedSignal.note,
8243
8282
  });
8244
8283
  return await RETURN_IDLE_FN(this, currentPrice);
8245
8284
  }
@@ -8270,7 +8309,7 @@ class ClientStrategy {
8270
8309
  pendingAt: publicSignalForCommit.pendingAt,
8271
8310
  totalEntries: publicSignalForCommit.totalEntries,
8272
8311
  totalPartials: publicSignalForCommit.totalPartials,
8273
- note: publicSignalForCommit.note,
8312
+ note: activatedSignal.activateNote ?? publicSignalForCommit.note,
8274
8313
  });
8275
8314
  // Call onOpen callback
8276
8315
  await CALL_OPEN_CALLBACKS_FN(this, this.params.execution.context.symbol, pendingSignal, currentPrice, currentTime, this.params.execution.context.backtest);
@@ -8406,7 +8445,7 @@ class ClientStrategy {
8406
8445
  totalPartials: cancelledSignal._partial?.length ?? 0,
8407
8446
  originalPriceOpen: cancelledSignal.priceOpen,
8408
8447
  pnl: toProfitLossDto(cancelledSignal, currentPrice),
8409
- note: cancelledSignal.note,
8448
+ note: cancelledSignal.cancelNote ?? cancelledSignal.note,
8410
8449
  });
8411
8450
  await CALL_CANCEL_CALLBACKS_FN(this, this.params.execution.context.symbol, cancelledSignal, currentPrice, closeTimestamp, this.params.execution.context.backtest);
8412
8451
  const cancelledResult = {
@@ -8461,7 +8500,7 @@ class ClientStrategy {
8461
8500
  totalPartials: closedSignal._partial?.length ?? 0,
8462
8501
  originalPriceOpen: closedSignal.priceOpen,
8463
8502
  pnl: toProfitLossDto(closedSignal, currentPrice),
8464
- note: closedSignal.note,
8503
+ note: closedSignal.closeNote ?? closedSignal.note,
8465
8504
  });
8466
8505
  await CALL_CLOSE_CALLBACKS_FN(this, this.params.execution.context.symbol, closedSignal, currentPrice, closeTimestamp, this.params.execution.context.backtest);
8467
8506
  // КРИТИЧНО: Очищаем состояние ClientPartial при закрытии позиции
@@ -8645,7 +8684,8 @@ class ClientStrategy {
8645
8684
  * // Strategy continues, can generate new signals
8646
8685
  * ```
8647
8686
  */
8648
- async cancelScheduled(symbol, backtest, cancelId) {
8687
+ async cancelScheduled(symbol, backtest, payload) {
8688
+ const cancelId = payload.id;
8649
8689
  this.params.logger.debug("ClientStrategy cancelScheduled", {
8650
8690
  symbol,
8651
8691
  hasScheduledSignal: this._scheduledSignal !== null,
@@ -8657,6 +8697,7 @@ class ClientStrategy {
8657
8697
  if (this._scheduledSignal) {
8658
8698
  this._cancelledSignal = Object.assign({}, this._scheduledSignal, {
8659
8699
  cancelId,
8700
+ cancelNote: payload.note,
8660
8701
  });
8661
8702
  this._scheduledSignal = null;
8662
8703
  }
@@ -8688,7 +8729,8 @@ class ClientStrategy {
8688
8729
  * // Scheduled signal becomes pending signal immediately
8689
8730
  * ```
8690
8731
  */
8691
- async activateScheduled(symbol, backtest, activateId) {
8732
+ async activateScheduled(symbol, backtest, payload) {
8733
+ const activateId = payload.id;
8692
8734
  this.params.logger.debug("ClientStrategy activateScheduled", {
8693
8735
  symbol,
8694
8736
  hasScheduledSignal: this._scheduledSignal !== null,
@@ -8706,6 +8748,7 @@ class ClientStrategy {
8706
8748
  if (this._scheduledSignal) {
8707
8749
  this._activatedSignal = Object.assign({}, this._scheduledSignal, {
8708
8750
  activateId,
8751
+ activateNote: payload.note,
8709
8752
  });
8710
8753
  this._scheduledSignal = null;
8711
8754
  }
@@ -8737,7 +8780,8 @@ class ClientStrategy {
8737
8780
  * // Strategy continues, can generate new signals
8738
8781
  * ```
8739
8782
  */
8740
- async closePending(symbol, backtest, closeId) {
8783
+ async closePending(symbol, backtest, payload) {
8784
+ const closeId = payload.id;
8741
8785
  this.params.logger.debug("ClientStrategy closePending", {
8742
8786
  symbol,
8743
8787
  hasPendingSignal: this._pendingSignal !== null,
@@ -8748,6 +8792,7 @@ class ClientStrategy {
8748
8792
  if (this._pendingSignal) {
8749
8793
  this._closedSignal = Object.assign({}, this._pendingSignal, {
8750
8794
  closeId,
8795
+ closeNote: payload.note,
8751
8796
  });
8752
8797
  this._pendingSignal = null;
8753
8798
  }
@@ -11238,6 +11283,48 @@ class StrategyConnectionService {
11238
11283
  const currentPrice = await this.priceMetaService.getCurrentPrice(symbol, context, backtest);
11239
11284
  return await strategy.getPositionHighestMaxDrawdownPnlCost(symbol, currentPrice);
11240
11285
  };
11286
+ /**
11287
+ * Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
11288
+ *
11289
+ * Resolves current price via priceMetaService and delegates to
11290
+ * ClientStrategy.getMaxDrawdownDistancePnlPercentage().
11291
+ * Returns null if no pending signal exists.
11292
+ *
11293
+ * @param backtest - Whether running in backtest mode
11294
+ * @param symbol - Trading pair symbol
11295
+ * @param context - Execution context with strategyName, exchangeName, frameName
11296
+ * @returns Promise resolving to peak-to-trough PnL percentage distance (≥ 0) or null
11297
+ */
11298
+ this.getMaxDrawdownDistancePnlPercentage = async (backtest, symbol, context) => {
11299
+ this.loggerService.log("strategyConnectionService getMaxDrawdownDistancePnlPercentage", {
11300
+ symbol,
11301
+ context,
11302
+ });
11303
+ const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
11304
+ const currentPrice = await this.priceMetaService.getCurrentPrice(symbol, context, backtest);
11305
+ return await strategy.getMaxDrawdownDistancePnlPercentage(symbol, currentPrice);
11306
+ };
11307
+ /**
11308
+ * Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
11309
+ *
11310
+ * Resolves current price via priceMetaService and delegates to
11311
+ * ClientStrategy.getMaxDrawdownDistancePnlCost().
11312
+ * Returns null if no pending signal exists.
11313
+ *
11314
+ * @param backtest - Whether running in backtest mode
11315
+ * @param symbol - Trading pair symbol
11316
+ * @param context - Execution context with strategyName, exchangeName, frameName
11317
+ * @returns Promise resolving to peak-to-trough PnL cost distance (≥ 0) or null
11318
+ */
11319
+ this.getMaxDrawdownDistancePnlCost = async (backtest, symbol, context) => {
11320
+ this.loggerService.log("strategyConnectionService getMaxDrawdownDistancePnlCost", {
11321
+ symbol,
11322
+ context,
11323
+ });
11324
+ const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
11325
+ const currentPrice = await this.priceMetaService.getCurrentPrice(symbol, context, backtest);
11326
+ return await strategy.getMaxDrawdownDistancePnlCost(symbol, currentPrice);
11327
+ };
11241
11328
  /**
11242
11329
  * Disposes the ClientStrategy instance for the given context.
11243
11330
  *
@@ -11297,17 +11384,17 @@ class StrategyConnectionService {
11297
11384
  * @param backtest - Whether running in backtest mode
11298
11385
  * @param symbol - Trading pair symbol
11299
11386
  * @param ctx - Context with strategyName, exchangeName, frameName
11300
- * @param cancelId - Optional cancellation ID for user-initiated cancellations
11387
+ * @param payload - Optional commit payload with id and note
11301
11388
  * @returns Promise that resolves when scheduled signal is cancelled
11302
11389
  */
11303
- this.cancelScheduled = async (backtest, symbol, context, cancelId) => {
11390
+ this.cancelScheduled = async (backtest, symbol, context, payload = {}) => {
11304
11391
  this.loggerService.log("strategyConnectionService cancelScheduled", {
11305
11392
  symbol,
11306
11393
  context,
11307
- cancelId,
11394
+ payload,
11308
11395
  });
11309
11396
  const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
11310
- await strategy.cancelScheduled(symbol, backtest, cancelId);
11397
+ await strategy.cancelScheduled(symbol, backtest, payload);
11311
11398
  };
11312
11399
  /**
11313
11400
  * Closes the pending signal without stopping the strategy.
@@ -11322,17 +11409,17 @@ class StrategyConnectionService {
11322
11409
  * @param backtest - Whether running in backtest mode
11323
11410
  * @param symbol - Trading pair symbol
11324
11411
  * @param context - Context with strategyName, exchangeName, frameName
11325
- * @param closeId - Optional close ID for user-initiated closes
11412
+ * @param payload - Optional commit payload with id and note
11326
11413
  * @returns Promise that resolves when pending signal is closed
11327
11414
  */
11328
- this.closePending = async (backtest, symbol, context, closeId) => {
11415
+ this.closePending = async (backtest, symbol, context, payload = {}) => {
11329
11416
  this.loggerService.log("strategyConnectionService closePending", {
11330
11417
  symbol,
11331
11418
  context,
11332
- closeId,
11419
+ payload,
11333
11420
  });
11334
11421
  const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
11335
- await strategy.closePending(symbol, backtest, closeId);
11422
+ await strategy.closePending(symbol, backtest, payload);
11336
11423
  };
11337
11424
  /**
11338
11425
  * Checks whether `partialProfit` would succeed without executing it.
@@ -11611,7 +11698,7 @@ class StrategyConnectionService {
11611
11698
  * @param backtest - Whether running in backtest mode
11612
11699
  * @param symbol - Trading pair symbol
11613
11700
  * @param context - Execution context with strategyName, exchangeName, frameName
11614
- * @param activateId - Optional identifier for the activation reason
11701
+ * @param payload - Optional commit payload with id and note
11615
11702
  * @returns Promise that resolves when activation flag is set
11616
11703
  *
11617
11704
  * @example
@@ -11621,19 +11708,19 @@ class StrategyConnectionService {
11621
11708
  * false,
11622
11709
  * "BTCUSDT",
11623
11710
  * { strategyName: "my-strategy", exchangeName: "binance", frameName: "" },
11624
- * "manual-activation"
11711
+ * { id: "manual-activation" }
11625
11712
  * );
11626
11713
  * ```
11627
11714
  */
11628
- this.activateScheduled = async (backtest, symbol, context, activateId) => {
11715
+ this.activateScheduled = async (backtest, symbol, context, payload = {}) => {
11629
11716
  this.loggerService.log("strategyConnectionService activateScheduled", {
11630
11717
  symbol,
11631
11718
  context,
11632
11719
  backtest,
11633
- activateId,
11720
+ payload,
11634
11721
  });
11635
11722
  const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
11636
- return await strategy.activateScheduled(symbol, backtest, activateId);
11723
+ return await strategy.activateScheduled(symbol, backtest, payload);
11637
11724
  };
11638
11725
  /**
11639
11726
  * Checks whether `averageBuy` would succeed without executing it.
@@ -14668,18 +14755,18 @@ class StrategyCoreService {
14668
14755
  * @param backtest - Whether running in backtest mode
14669
14756
  * @param symbol - Trading pair symbol
14670
14757
  * @param ctx - Context with strategyName, exchangeName, frameName
14671
- * @param cancelId - Optional cancellation ID for user-initiated cancellations
14758
+ * @param payload - Optional commit payload with id and note
14672
14759
  * @returns Promise that resolves when scheduled signal is cancelled
14673
14760
  */
14674
- this.cancelScheduled = async (backtest, symbol, context, cancelId) => {
14761
+ this.cancelScheduled = async (backtest, symbol, context, payload = {}) => {
14675
14762
  this.loggerService.log("strategyCoreService cancelScheduled", {
14676
14763
  symbol,
14677
14764
  context,
14678
14765
  backtest,
14679
- cancelId,
14766
+ payload,
14680
14767
  });
14681
14768
  await this.validate(context);
14682
- return await this.strategyConnectionService.cancelScheduled(backtest, symbol, context, cancelId);
14769
+ return await this.strategyConnectionService.cancelScheduled(backtest, symbol, context, payload);
14683
14770
  };
14684
14771
  /**
14685
14772
  * Closes the pending signal without stopping the strategy.
@@ -14695,18 +14782,18 @@ class StrategyCoreService {
14695
14782
  * @param backtest - Whether running in backtest mode
14696
14783
  * @param symbol - Trading pair symbol
14697
14784
  * @param context - Context with strategyName, exchangeName, frameName
14698
- * @param closeId - Optional close ID for user-initiated closes
14785
+ * @param payload - Optional commit payload with id and note
14699
14786
  * @returns Promise that resolves when pending signal is closed
14700
14787
  */
14701
- this.closePending = async (backtest, symbol, context, closeId) => {
14788
+ this.closePending = async (backtest, symbol, context, payload = {}) => {
14702
14789
  this.loggerService.log("strategyCoreService closePending", {
14703
14790
  symbol,
14704
14791
  context,
14705
14792
  backtest,
14706
- closeId,
14793
+ payload,
14707
14794
  });
14708
14795
  await this.validate(context);
14709
- return await this.strategyConnectionService.closePending(backtest, symbol, context, closeId);
14796
+ return await this.strategyConnectionService.closePending(backtest, symbol, context, payload);
14710
14797
  };
14711
14798
  /**
14712
14799
  * Disposes the ClientStrategy instance for the given context.
@@ -15051,7 +15138,7 @@ class StrategyCoreService {
15051
15138
  * @param backtest - Whether running in backtest mode
15052
15139
  * @param symbol - Trading pair symbol
15053
15140
  * @param context - Execution context with strategyName, exchangeName, frameName
15054
- * @param activateId - Optional identifier for the activation reason
15141
+ * @param payload - Optional commit payload with id and note
15055
15142
  * @returns Promise that resolves when activation flag is set
15056
15143
  *
15057
15144
  * @example
@@ -15061,19 +15148,19 @@ class StrategyCoreService {
15061
15148
  * false,
15062
15149
  * "BTCUSDT",
15063
15150
  * { strategyName: "my-strategy", exchangeName: "binance", frameName: "" },
15064
- * "manual-activation"
15151
+ * { id: "manual-activation" }
15065
15152
  * );
15066
15153
  * ```
15067
15154
  */
15068
- this.activateScheduled = async (backtest, symbol, context, activateId) => {
15155
+ this.activateScheduled = async (backtest, symbol, context, payload = {}) => {
15069
15156
  this.loggerService.log("strategyCoreService activateScheduled", {
15070
15157
  symbol,
15071
15158
  context,
15072
15159
  backtest,
15073
- activateId,
15160
+ payload,
15074
15161
  });
15075
15162
  await this.validate(context);
15076
- return await this.strategyConnectionService.activateScheduled(backtest, symbol, context, activateId);
15163
+ return await this.strategyConnectionService.activateScheduled(backtest, symbol, context, payload);
15077
15164
  };
15078
15165
  /**
15079
15166
  * Checks whether `averageBuy` would succeed without executing it.
@@ -15475,6 +15562,44 @@ class StrategyCoreService {
15475
15562
  await this.validate(context);
15476
15563
  return await this.strategyConnectionService.getPositionHighestMaxDrawdownPnlCost(backtest, symbol, context);
15477
15564
  };
15565
+ /**
15566
+ * Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
15567
+ *
15568
+ * Delegates to StrategyConnectionService.getMaxDrawdownDistancePnlPercentage().
15569
+ * Returns null if no pending signal exists.
15570
+ *
15571
+ * @param backtest - Whether running in backtest mode
15572
+ * @param symbol - Trading pair symbol
15573
+ * @param context - Execution context with strategyName, exchangeName, frameName
15574
+ * @returns Promise resolving to peak-to-trough PnL percentage distance (≥ 0) or null
15575
+ */
15576
+ this.getMaxDrawdownDistancePnlPercentage = async (backtest, symbol, context) => {
15577
+ this.loggerService.log("strategyCoreService getMaxDrawdownDistancePnlPercentage", {
15578
+ symbol,
15579
+ context,
15580
+ });
15581
+ await this.validate(context);
15582
+ return await this.strategyConnectionService.getMaxDrawdownDistancePnlPercentage(backtest, symbol, context);
15583
+ };
15584
+ /**
15585
+ * Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
15586
+ *
15587
+ * Delegates to StrategyConnectionService.getMaxDrawdownDistancePnlCost().
15588
+ * Returns null if no pending signal exists.
15589
+ *
15590
+ * @param backtest - Whether running in backtest mode
15591
+ * @param symbol - Trading pair symbol
15592
+ * @param context - Execution context with strategyName, exchangeName, frameName
15593
+ * @returns Promise resolving to peak-to-trough PnL cost distance (≥ 0) or null
15594
+ */
15595
+ this.getMaxDrawdownDistancePnlCost = async (backtest, symbol, context) => {
15596
+ this.loggerService.log("strategyCoreService getMaxDrawdownDistancePnlCost", {
15597
+ symbol,
15598
+ context,
15599
+ });
15600
+ await this.validate(context);
15601
+ return await this.strategyConnectionService.getMaxDrawdownDistancePnlCost(backtest, symbol, context);
15602
+ };
15478
15603
  }
15479
15604
  }
15480
15605
 
@@ -29074,7 +29199,7 @@ class StrategyReportService {
29074
29199
  /**
29075
29200
  * Logs a cancel-scheduled event when a scheduled signal is cancelled.
29076
29201
  */
29077
- this.cancelScheduled = async (symbol, isBacktest, context, timestamp, signalId, pnl, totalPartials, cancelId) => {
29202
+ this.cancelScheduled = async (symbol, isBacktest, context, timestamp, signalId, pnl, totalPartials, cancelId, note) => {
29078
29203
  this.loggerService.log("strategyReportService cancelScheduled", {
29079
29204
  symbol,
29080
29205
  isBacktest,
@@ -29087,6 +29212,7 @@ class StrategyReportService {
29087
29212
  await ReportWriter.writeData("strategy", {
29088
29213
  action: "cancel-scheduled",
29089
29214
  cancelId,
29215
+ note,
29090
29216
  symbol,
29091
29217
  timestamp,
29092
29218
  createdAt,
@@ -29108,7 +29234,7 @@ class StrategyReportService {
29108
29234
  /**
29109
29235
  * Logs a close-pending event when a pending signal is closed.
29110
29236
  */
29111
- this.closePending = async (symbol, isBacktest, context, timestamp, signalId, pnl, totalPartials, closeId) => {
29237
+ this.closePending = async (symbol, isBacktest, context, timestamp, signalId, pnl, totalPartials, closeId, note) => {
29112
29238
  this.loggerService.log("strategyReportService closePending", {
29113
29239
  symbol,
29114
29240
  isBacktest,
@@ -29121,6 +29247,7 @@ class StrategyReportService {
29121
29247
  await ReportWriter.writeData("strategy", {
29122
29248
  action: "close-pending",
29123
29249
  closeId,
29250
+ note,
29124
29251
  symbol,
29125
29252
  timestamp,
29126
29253
  createdAt,
@@ -29370,7 +29497,7 @@ class StrategyReportService {
29370
29497
  /**
29371
29498
  * Logs an activate-scheduled event when a scheduled signal is activated early.
29372
29499
  */
29373
- this.activateScheduled = async (symbol, currentPrice, isBacktest, context, timestamp, signalId, pnl, totalPartials, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen, activateId) => {
29500
+ this.activateScheduled = async (symbol, currentPrice, isBacktest, context, timestamp, signalId, pnl, totalPartials, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen, activateId, note) => {
29374
29501
  this.loggerService.log("strategyReportService activateScheduled", {
29375
29502
  symbol,
29376
29503
  currentPrice,
@@ -29384,6 +29511,7 @@ class StrategyReportService {
29384
29511
  await ReportWriter.writeData("strategy", {
29385
29512
  action: "activate-scheduled",
29386
29513
  activateId,
29514
+ note,
29387
29515
  currentPrice,
29388
29516
  symbol,
29389
29517
  timestamp,
@@ -29477,14 +29605,14 @@ class StrategyReportService {
29477
29605
  exchangeName: event.exchangeName,
29478
29606
  frameName: event.frameName,
29479
29607
  strategyName: event.strategyName,
29480
- }, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.cancelId));
29608
+ }, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.cancelId, event.note));
29481
29609
  const unClosePending = strategyCommitSubject
29482
29610
  .filter(({ action }) => action === "close-pending")
29483
29611
  .connect(async (event) => await this.closePending(event.symbol, event.backtest, {
29484
29612
  exchangeName: event.exchangeName,
29485
29613
  frameName: event.frameName,
29486
29614
  strategyName: event.strategyName,
29487
- }, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.closeId));
29615
+ }, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.closeId, event.note));
29488
29616
  const unPartialProfit = strategyCommitSubject
29489
29617
  .filter(({ action }) => action === "partial-profit")
29490
29618
  .connect(async (event) => await this.partialProfit(event.symbol, event.percentToClose, event.currentPrice, event.backtest, {
@@ -29526,7 +29654,7 @@ class StrategyReportService {
29526
29654
  exchangeName: event.exchangeName,
29527
29655
  frameName: event.frameName,
29528
29656
  strategyName: event.strategyName,
29529
- }, 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));
29657
+ }, 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));
29530
29658
  const unAverageBuy = strategyCommitSubject
29531
29659
  .filter(({ action }) => action === "average-buy")
29532
29660
  .connect(async (event) => await this.averageBuy(event.symbol, event.currentPrice, event.effectivePriceOpen, event.totalEntries, event.backtest, {
@@ -30065,8 +30193,9 @@ class StrategyMarkdownService {
30065
30193
  * @param context - Strategy context with strategyName, exchangeName, frameName
30066
30194
  * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
30067
30195
  * @param cancelId - Optional identifier for the cancellation reason
30196
+ * @param note - Optional note from commit payload
30068
30197
  */
30069
- this.cancelScheduled = async (symbol, isBacktest, context, timestamp, signalId, pnl, cancelId) => {
30198
+ this.cancelScheduled = async (symbol, isBacktest, context, timestamp, signalId, pnl, cancelId, note) => {
30070
30199
  this.loggerService.log("strategyMarkdownService cancelScheduled", {
30071
30200
  symbol,
30072
30201
  isBacktest,
@@ -30087,6 +30216,7 @@ class StrategyMarkdownService {
30087
30216
  action: "cancel-scheduled",
30088
30217
  pnl,
30089
30218
  cancelId,
30219
+ note,
30090
30220
  createdAt,
30091
30221
  backtest: isBacktest,
30092
30222
  });
@@ -30099,8 +30229,9 @@ class StrategyMarkdownService {
30099
30229
  * @param context - Strategy context with strategyName, exchangeName, frameName
30100
30230
  * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
30101
30231
  * @param closeId - Optional identifier for the close reason
30232
+ * @param note - Optional note from commit payload
30102
30233
  */
30103
- this.closePending = async (symbol, isBacktest, context, timestamp, signalId, pnl, closeId) => {
30234
+ this.closePending = async (symbol, isBacktest, context, timestamp, signalId, pnl, closeId, note) => {
30104
30235
  this.loggerService.log("strategyMarkdownService closePending", {
30105
30236
  symbol,
30106
30237
  isBacktest,
@@ -30121,6 +30252,7 @@ class StrategyMarkdownService {
30121
30252
  action: "close-pending",
30122
30253
  pnl,
30123
30254
  closeId,
30255
+ note,
30124
30256
  createdAt,
30125
30257
  backtest: isBacktest,
30126
30258
  });
@@ -30419,8 +30551,9 @@ class StrategyMarkdownService {
30419
30551
  * @param scheduledAt - Signal creation timestamp in milliseconds
30420
30552
  * @param pendingAt - Pending timestamp in milliseconds
30421
30553
  * @param activateId - Optional identifier for the activation reason
30554
+ * @param note - Optional note from commit payload
30422
30555
  */
30423
- this.activateScheduled = async (symbol, currentPrice, isBacktest, context, timestamp, signalId, pnl, totalPartials, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen, activateId) => {
30556
+ this.activateScheduled = async (symbol, currentPrice, isBacktest, context, timestamp, signalId, pnl, totalPartials, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen, activateId, note) => {
30424
30557
  this.loggerService.log("strategyMarkdownService activateScheduled", {
30425
30558
  symbol,
30426
30559
  currentPrice,
@@ -30443,6 +30576,7 @@ class StrategyMarkdownService {
30443
30576
  pnl,
30444
30577
  totalPartials,
30445
30578
  activateId,
30579
+ note,
30446
30580
  currentPrice,
30447
30581
  createdAt,
30448
30582
  backtest: isBacktest,
@@ -30647,14 +30781,14 @@ class StrategyMarkdownService {
30647
30781
  exchangeName: event.exchangeName,
30648
30782
  frameName: event.frameName,
30649
30783
  strategyName: event.strategyName,
30650
- }, event.timestamp, event.signalId, event.pnl, event.cancelId));
30784
+ }, event.timestamp, event.signalId, event.pnl, event.cancelId, event.note));
30651
30785
  const unClosePending = strategyCommitSubject
30652
30786
  .filter(({ action }) => action === "close-pending")
30653
30787
  .connect(async (event) => await this.closePending(event.symbol, event.backtest, {
30654
30788
  exchangeName: event.exchangeName,
30655
30789
  frameName: event.frameName,
30656
30790
  strategyName: event.strategyName,
30657
- }, event.timestamp, event.signalId, event.pnl, event.closeId));
30791
+ }, event.timestamp, event.signalId, event.pnl, event.closeId, event.note));
30658
30792
  const unPartialProfit = strategyCommitSubject
30659
30793
  .filter(({ action }) => action === "partial-profit")
30660
30794
  .connect(async (event) => await this.partialProfit(event.symbol, event.percentToClose, event.currentPrice, event.backtest, {
@@ -30696,7 +30830,7 @@ class StrategyMarkdownService {
30696
30830
  exchangeName: event.exchangeName,
30697
30831
  frameName: event.frameName,
30698
30832
  strategyName: event.strategyName,
30699
- }, 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));
30833
+ }, 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));
30700
30834
  const unAverageBuy = strategyCommitSubject
30701
30835
  .filter(({ action }) => action === "average-buy")
30702
30836
  .connect(async (event) => await this.averageBuy(event.symbol, event.currentPrice, event.effectivePriceOpen, event.totalEntries, event.backtest, {
@@ -35181,6 +35315,8 @@ const GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE_METHOD_NAME = "strateg
35181
35315
  const GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST_METHOD_NAME = "strategy.getPositionHighestProfitDistancePnlCost";
35182
35316
  const GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE_METHOD_NAME = "strategy.getPositionHighestMaxDrawdownPnlPercentage";
35183
35317
  const GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST_METHOD_NAME = "strategy.getPositionHighestMaxDrawdownPnlCost";
35318
+ const GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE_METHOD_NAME = "strategy.getMaxDrawdownDistancePnlPercentage";
35319
+ const GET_MAX_DRAWDOWN_DISTANCE_PNL_COST_METHOD_NAME = "strategy.getMaxDrawdownDistancePnlCost";
35184
35320
  const GET_POSITION_ENTRY_OVERLAP_METHOD_NAME = "strategy.getPositionEntryOverlap";
35185
35321
  const GET_POSITION_PARTIAL_OVERLAP_METHOD_NAME = "strategy.getPositionPartialOverlap";
35186
35322
  const HAS_NO_PENDING_SIGNAL_METHOD_NAME = "strategy.hasNoPendingSignal";
@@ -35196,7 +35332,7 @@ const HAS_NO_SCHEDULED_SIGNAL_METHOD_NAME = "strategy.hasNoScheduledSignal";
35196
35332
  *
35197
35333
  * @param symbol - Trading pair symbol
35198
35334
  * @param strategyName - Strategy name
35199
- * @param cancelId - Optional cancellation ID for tracking user-initiated cancellations
35335
+ * @param payload - Optional commit payload with id and note
35200
35336
  * @returns Promise that resolves when scheduled signal is cancelled
35201
35337
  *
35202
35338
  * @example
@@ -35204,13 +35340,13 @@ const HAS_NO_SCHEDULED_SIGNAL_METHOD_NAME = "strategy.hasNoScheduledSignal";
35204
35340
  * import { commitCancelScheduled } from "backtest-kit";
35205
35341
  *
35206
35342
  * // Cancel scheduled signal with custom ID
35207
- * await commitCancelScheduled("BTCUSDT", "manual-cancel-001");
35343
+ * await commitCancelScheduled("BTCUSDT", { id: "manual-cancel-001" });
35208
35344
  * ```
35209
35345
  */
35210
- async function commitCancelScheduled(symbol, cancelId) {
35346
+ async function commitCancelScheduled(symbol, payload = {}) {
35211
35347
  backtest.loggerService.info(CANCEL_SCHEDULED_METHOD_NAME, {
35212
35348
  symbol,
35213
- cancelId,
35349
+ payload,
35214
35350
  });
35215
35351
  if (!ExecutionContextService.hasContext()) {
35216
35352
  throw new Error("commitCancelScheduled requires an execution context");
@@ -35220,7 +35356,7 @@ async function commitCancelScheduled(symbol, cancelId) {
35220
35356
  }
35221
35357
  const { backtest: isBacktest } = backtest.executionContextService.context;
35222
35358
  const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
35223
- await backtest.strategyCoreService.cancelScheduled(isBacktest, symbol, { exchangeName, frameName, strategyName }, cancelId);
35359
+ await backtest.strategyCoreService.cancelScheduled(isBacktest, symbol, { exchangeName, frameName, strategyName }, payload);
35224
35360
  }
35225
35361
  /**
35226
35362
  * Closes the pending signal without stopping the strategy.
@@ -35232,7 +35368,7 @@ async function commitCancelScheduled(symbol, cancelId) {
35232
35368
  * Automatically detects backtest/live mode from execution context.
35233
35369
  *
35234
35370
  * @param symbol - Trading pair symbol
35235
- * @param closeId - Optional close ID for tracking user-initiated closes
35371
+ * @param payload - Optional commit payload with id and note
35236
35372
  * @returns Promise that resolves when pending signal is closed
35237
35373
  *
35238
35374
  * @example
@@ -35240,13 +35376,13 @@ async function commitCancelScheduled(symbol, cancelId) {
35240
35376
  * import { commitClosePending } from "backtest-kit";
35241
35377
  *
35242
35378
  * // Close pending signal with custom ID
35243
- * await commitClosePending("BTCUSDT", "manual-close-001");
35379
+ * await commitClosePending("BTCUSDT", { id: "manual-close-001" });
35244
35380
  * ```
35245
35381
  */
35246
- async function commitClosePending(symbol, closeId) {
35382
+ async function commitClosePending(symbol, payload = {}) {
35247
35383
  backtest.loggerService.info(CLOSE_PENDING_METHOD_NAME, {
35248
35384
  symbol,
35249
- closeId,
35385
+ payload,
35250
35386
  });
35251
35387
  if (!ExecutionContextService.hasContext()) {
35252
35388
  throw new Error("commitClosePending requires an execution context");
@@ -35256,7 +35392,7 @@ async function commitClosePending(symbol, closeId) {
35256
35392
  }
35257
35393
  const { backtest: isBacktest } = backtest.executionContextService.context;
35258
35394
  const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
35259
- await backtest.strategyCoreService.closePending(isBacktest, symbol, { exchangeName, frameName, strategyName }, closeId);
35395
+ await backtest.strategyCoreService.closePending(isBacktest, symbol, { exchangeName, frameName, strategyName }, payload);
35260
35396
  }
35261
35397
  /**
35262
35398
  * Executes partial close at profit level (moving toward TP).
@@ -35719,7 +35855,7 @@ async function commitBreakeven(symbol) {
35719
35855
  * Automatically detects backtest/live mode from execution context.
35720
35856
  *
35721
35857
  * @param symbol - Trading pair symbol
35722
- * @param activateId - Optional activation ID for tracking user-initiated activations
35858
+ * @param payload - Optional commit payload with id and note
35723
35859
  * @returns Promise that resolves when activation flag is set
35724
35860
  *
35725
35861
  * @example
@@ -35727,13 +35863,13 @@ async function commitBreakeven(symbol) {
35727
35863
  * import { commitActivateScheduled } from "backtest-kit";
35728
35864
  *
35729
35865
  * // Activate scheduled signal early with custom ID
35730
- * await commitActivateScheduled("BTCUSDT", "manual-activate-001");
35866
+ * await commitActivateScheduled("BTCUSDT", { id: "manual-activate-001" });
35731
35867
  * ```
35732
35868
  */
35733
- async function commitActivateScheduled(symbol, activateId) {
35869
+ async function commitActivateScheduled(symbol, payload = {}) {
35734
35870
  backtest.loggerService.info(ACTIVATE_SCHEDULED_METHOD_NAME, {
35735
35871
  symbol,
35736
- activateId,
35872
+ payload,
35737
35873
  });
35738
35874
  if (!ExecutionContextService.hasContext()) {
35739
35875
  throw new Error("commitActivateScheduled requires an execution context");
@@ -35743,7 +35879,7 @@ async function commitActivateScheduled(symbol, activateId) {
35743
35879
  }
35744
35880
  const { backtest: isBacktest } = backtest.executionContextService.context;
35745
35881
  const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
35746
- await backtest.strategyCoreService.activateScheduled(isBacktest, symbol, { exchangeName, frameName, strategyName }, activateId);
35882
+ await backtest.strategyCoreService.activateScheduled(isBacktest, symbol, { exchangeName, frameName, strategyName }, payload);
35747
35883
  }
35748
35884
  /**
35749
35885
  * Adds a new DCA entry to the active pending signal.
@@ -36931,6 +37067,64 @@ async function getPositionHighestMaxDrawdownPnlCost(symbol) {
36931
37067
  const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
36932
37068
  return await backtest.strategyCoreService.getPositionHighestMaxDrawdownPnlCost(isBacktest, symbol, { exchangeName, frameName, strategyName });
36933
37069
  }
37070
+ /**
37071
+ * Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
37072
+ *
37073
+ * Computed as: max(0, peakPnlPercentage - fallPnlPercentage).
37074
+ * Returns null if no pending signal exists.
37075
+ *
37076
+ * @param symbol - Trading pair symbol
37077
+ * @returns Promise resolving to peak-to-trough PnL percentage distance (≥ 0) or null
37078
+ *
37079
+ * @example
37080
+ * ```typescript
37081
+ * import { getMaxDrawdownDistancePnlPercentage } from "backtest-kit";
37082
+ *
37083
+ * const dist = await getMaxDrawdownDistancePnlPercentage("BTCUSDT");
37084
+ * // e.g. 3.5 (peak was +3.5% above trough)
37085
+ * ```
37086
+ */
37087
+ async function getMaxDrawdownDistancePnlPercentage(symbol) {
37088
+ backtest.loggerService.info(GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE_METHOD_NAME, { symbol });
37089
+ if (!ExecutionContextService.hasContext()) {
37090
+ throw new Error("getMaxDrawdownDistancePnlPercentage requires an execution context");
37091
+ }
37092
+ if (!MethodContextService.hasContext()) {
37093
+ throw new Error("getMaxDrawdownDistancePnlPercentage requires a method context");
37094
+ }
37095
+ const { backtest: isBacktest } = backtest.executionContextService.context;
37096
+ const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
37097
+ return await backtest.strategyCoreService.getMaxDrawdownDistancePnlPercentage(isBacktest, symbol, { exchangeName, frameName, strategyName });
37098
+ }
37099
+ /**
37100
+ * Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
37101
+ *
37102
+ * Computed as: max(0, peakPnlCost - fallPnlCost).
37103
+ * Returns null if no pending signal exists.
37104
+ *
37105
+ * @param symbol - Trading pair symbol
37106
+ * @returns Promise resolving to peak-to-trough PnL cost distance (≥ 0) or null
37107
+ *
37108
+ * @example
37109
+ * ```typescript
37110
+ * import { getMaxDrawdownDistancePnlCost } from "backtest-kit";
37111
+ *
37112
+ * const dist = await getMaxDrawdownDistancePnlCost("BTCUSDT");
37113
+ * // e.g. 7.2 (peak was $7.2 above trough)
37114
+ * ```
37115
+ */
37116
+ async function getMaxDrawdownDistancePnlCost(symbol) {
37117
+ backtest.loggerService.info(GET_MAX_DRAWDOWN_DISTANCE_PNL_COST_METHOD_NAME, { symbol });
37118
+ if (!ExecutionContextService.hasContext()) {
37119
+ throw new Error("getMaxDrawdownDistancePnlCost requires an execution context");
37120
+ }
37121
+ if (!MethodContextService.hasContext()) {
37122
+ throw new Error("getMaxDrawdownDistancePnlCost requires a method context");
37123
+ }
37124
+ const { backtest: isBacktest } = backtest.executionContextService.context;
37125
+ const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
37126
+ return await backtest.strategyCoreService.getMaxDrawdownDistancePnlCost(isBacktest, symbol, { exchangeName, frameName, strategyName });
37127
+ }
36934
37128
  /**
36935
37129
  * Checks whether the current price falls within the tolerance zone of any existing DCA entry level.
36936
37130
  * Use this to prevent duplicate DCA entries at the same price area.
@@ -38604,6 +38798,8 @@ const BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE =
38604
38798
  const BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST = "BacktestUtils.getPositionHighestProfitDistancePnlCost";
38605
38799
  const BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE = "BacktestUtils.getPositionHighestMaxDrawdownPnlPercentage";
38606
38800
  const BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST = "BacktestUtils.getPositionHighestMaxDrawdownPnlCost";
38801
+ const BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE = "BacktestUtils.getMaxDrawdownDistancePnlPercentage";
38802
+ const BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST = "BacktestUtils.getMaxDrawdownDistancePnlCost";
38607
38803
  const BACKTEST_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP = "BacktestUtils.getPositionEntryOverlap";
38608
38804
  const BACKTEST_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP = "BacktestUtils.getPositionPartialOverlap";
38609
38805
  const BACKTEST_METHOD_NAME_BREAKEVEN = "Backtest.commitBreakeven";
@@ -39985,6 +40181,62 @@ class BacktestUtils {
39985
40181
  }
39986
40182
  return await backtest.strategyCoreService.getPositionHighestMaxDrawdownPnlCost(true, symbol, context);
39987
40183
  };
40184
+ /**
40185
+ * Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
40186
+ *
40187
+ * Computed as: max(0, peakPnlPercentage - fallPnlPercentage).
40188
+ * Returns null if no pending signal exists.
40189
+ *
40190
+ * @param symbol - Trading pair symbol
40191
+ * @param context - Execution context with strategyName, exchangeName, and frameName
40192
+ * @returns peak-to-trough PnL percentage distance (≥ 0) or null if no active position
40193
+ */
40194
+ this.getMaxDrawdownDistancePnlPercentage = async (symbol, context) => {
40195
+ backtest.loggerService.info(BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE, {
40196
+ symbol,
40197
+ context,
40198
+ });
40199
+ backtest.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
40200
+ backtest.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
40201
+ {
40202
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
40203
+ riskName &&
40204
+ backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
40205
+ riskList &&
40206
+ riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE));
40207
+ actions &&
40208
+ actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE));
40209
+ }
40210
+ return await backtest.strategyCoreService.getMaxDrawdownDistancePnlPercentage(true, symbol, context);
40211
+ };
40212
+ /**
40213
+ * Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
40214
+ *
40215
+ * Computed as: max(0, peakPnlCost - fallPnlCost).
40216
+ * Returns null if no pending signal exists.
40217
+ *
40218
+ * @param symbol - Trading pair symbol
40219
+ * @param context - Execution context with strategyName, exchangeName, and frameName
40220
+ * @returns peak-to-trough PnL cost distance (≥ 0) or null if no active position
40221
+ */
40222
+ this.getMaxDrawdownDistancePnlCost = async (symbol, context) => {
40223
+ backtest.loggerService.info(BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST, {
40224
+ symbol,
40225
+ context,
40226
+ });
40227
+ backtest.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
40228
+ backtest.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
40229
+ {
40230
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
40231
+ riskName &&
40232
+ backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
40233
+ riskList &&
40234
+ riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST));
40235
+ actions &&
40236
+ actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST));
40237
+ }
40238
+ return await backtest.strategyCoreService.getMaxDrawdownDistancePnlCost(true, symbol, context);
40239
+ };
39988
40240
  /**
39989
40241
  * Checks whether the current price falls within the tolerance zone of any existing DCA entry level.
39990
40242
  * Use this to prevent duplicate DCA entries at the same price area.
@@ -40119,7 +40371,7 @@ class BacktestUtils {
40119
40371
  * @param symbol - Trading pair symbol
40120
40372
  * @param strategyName - Strategy name
40121
40373
  * @param context - Execution context with exchangeName and frameName
40122
- * @param cancelId - Optional cancellation ID for tracking user-initiated cancellations
40374
+ * @param payload - Optional commit payload with id and note
40123
40375
  * @returns Promise that resolves when scheduled signal is cancelled
40124
40376
  *
40125
40377
  * @example
@@ -40129,14 +40381,14 @@ class BacktestUtils {
40129
40381
  * exchangeName: "binance",
40130
40382
  * frameName: "frame1",
40131
40383
  * strategyName: "my-strategy"
40132
- * }, "manual-cancel-001");
40384
+ * }, { id: "manual-cancel-001" });
40133
40385
  * ```
40134
40386
  */
40135
- this.commitCancelScheduled = async (symbol, context, cancelId) => {
40387
+ this.commitCancelScheduled = async (symbol, context, payload = {}) => {
40136
40388
  backtest.loggerService.info(BACKTEST_METHOD_NAME_CANCEL_SCHEDULED, {
40137
40389
  symbol,
40138
40390
  context,
40139
- cancelId,
40391
+ payload,
40140
40392
  });
40141
40393
  backtest.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_CANCEL_SCHEDULED);
40142
40394
  backtest.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_CANCEL_SCHEDULED);
@@ -40149,7 +40401,7 @@ class BacktestUtils {
40149
40401
  actions &&
40150
40402
  actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_CANCEL_SCHEDULED));
40151
40403
  }
40152
- await backtest.strategyCoreService.cancelScheduled(true, symbol, context, cancelId);
40404
+ await backtest.strategyCoreService.cancelScheduled(true, symbol, context, payload);
40153
40405
  };
40154
40406
  /**
40155
40407
  * Closes the pending signal without stopping the strategy.
@@ -40160,7 +40412,7 @@ class BacktestUtils {
40160
40412
  *
40161
40413
  * @param symbol - Trading pair symbol
40162
40414
  * @param context - Execution context with strategyName, exchangeName, and frameName
40163
- * @param closeId - Optional close ID for user-initiated closes
40415
+ * @param payload - Optional commit payload with id and note
40164
40416
  * @returns Promise that resolves when pending signal is closed
40165
40417
  *
40166
40418
  * @example
@@ -40170,14 +40422,14 @@ class BacktestUtils {
40170
40422
  * exchangeName: "binance",
40171
40423
  * strategyName: "my-strategy",
40172
40424
  * frameName: "1m"
40173
- * }, "manual-close-001");
40425
+ * }, { id: "manual-close-001" });
40174
40426
  * ```
40175
40427
  */
40176
- this.commitClosePending = async (symbol, context, closeId) => {
40428
+ this.commitClosePending = async (symbol, context, payload = {}) => {
40177
40429
  backtest.loggerService.info(BACKTEST_METHOD_NAME_CLOSE_PENDING, {
40178
40430
  symbol,
40179
40431
  context,
40180
- closeId,
40432
+ payload,
40181
40433
  });
40182
40434
  backtest.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_CLOSE_PENDING);
40183
40435
  backtest.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_CLOSE_PENDING);
@@ -40190,7 +40442,7 @@ class BacktestUtils {
40190
40442
  actions &&
40191
40443
  actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_CLOSE_PENDING));
40192
40444
  }
40193
- await backtest.strategyCoreService.closePending(true, symbol, context, closeId);
40445
+ await backtest.strategyCoreService.closePending(true, symbol, context, payload);
40194
40446
  };
40195
40447
  /**
40196
40448
  * Executes partial close at profit level (moving toward TP).
@@ -40826,7 +41078,7 @@ class BacktestUtils {
40826
41078
  *
40827
41079
  * @param symbol - Trading pair symbol
40828
41080
  * @param context - Execution context with strategyName, exchangeName, and frameName
40829
- * @param activateId - Optional activation ID for tracking user-initiated activations
41081
+ * @param payload - Optional commit payload with id and note
40830
41082
  * @returns Promise that resolves when activation flag is set
40831
41083
  *
40832
41084
  * @example
@@ -40836,14 +41088,14 @@ class BacktestUtils {
40836
41088
  * strategyName: "my-strategy",
40837
41089
  * exchangeName: "binance",
40838
41090
  * frameName: "1h"
40839
- * }, "manual-activate-001");
41091
+ * }, { id: "manual-activate-001" });
40840
41092
  * ```
40841
41093
  */
40842
- this.commitActivateScheduled = async (symbol, context, activateId) => {
41094
+ this.commitActivateScheduled = async (symbol, context, payload = {}) => {
40843
41095
  backtest.loggerService.info(BACKTEST_METHOD_NAME_ACTIVATE_SCHEDULED, {
40844
41096
  symbol,
40845
41097
  context,
40846
- activateId,
41098
+ payload,
40847
41099
  });
40848
41100
  backtest.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_ACTIVATE_SCHEDULED);
40849
41101
  backtest.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_ACTIVATE_SCHEDULED);
@@ -40856,7 +41108,7 @@ class BacktestUtils {
40856
41108
  actions &&
40857
41109
  actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_ACTIVATE_SCHEDULED));
40858
41110
  }
40859
- await backtest.strategyCoreService.activateScheduled(true, symbol, context, activateId);
41111
+ await backtest.strategyCoreService.activateScheduled(true, symbol, context, payload);
40860
41112
  };
40861
41113
  /**
40862
41114
  * Adds a new DCA entry to the active pending signal.
@@ -41114,6 +41366,8 @@ const LIVE_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE = "Li
41114
41366
  const LIVE_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST = "LiveUtils.getPositionHighestProfitDistancePnlCost";
41115
41367
  const LIVE_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE = "LiveUtils.getPositionHighestMaxDrawdownPnlPercentage";
41116
41368
  const LIVE_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST = "LiveUtils.getPositionHighestMaxDrawdownPnlCost";
41369
+ const LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE = "LiveUtils.getMaxDrawdownDistancePnlPercentage";
41370
+ const LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST = "LiveUtils.getMaxDrawdownDistancePnlCost";
41117
41371
  const LIVE_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP = "LiveUtils.getPositionEntryOverlap";
41118
41372
  const LIVE_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP = "LiveUtils.getPositionPartialOverlap";
41119
41373
  const LIVE_METHOD_NAME_BREAKEVEN = "Live.commitBreakeven";
@@ -42638,6 +42892,70 @@ class LiveUtils {
42638
42892
  frameName: "",
42639
42893
  });
42640
42894
  };
42895
+ /**
42896
+ * Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
42897
+ *
42898
+ * Computed as: max(0, peakPnlPercentage - fallPnlPercentage).
42899
+ * Returns null if no pending signal exists.
42900
+ *
42901
+ * @param symbol - Trading pair symbol
42902
+ * @param context - Execution context with strategyName and exchangeName
42903
+ * @returns peak-to-trough PnL percentage distance (≥ 0) or null if no active position
42904
+ */
42905
+ this.getMaxDrawdownDistancePnlPercentage = async (symbol, context) => {
42906
+ backtest.loggerService.info(LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE, {
42907
+ symbol,
42908
+ context,
42909
+ });
42910
+ backtest.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
42911
+ backtest.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
42912
+ {
42913
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
42914
+ riskName &&
42915
+ backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
42916
+ riskList &&
42917
+ riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE));
42918
+ actions &&
42919
+ actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE));
42920
+ }
42921
+ return await backtest.strategyCoreService.getMaxDrawdownDistancePnlPercentage(false, symbol, {
42922
+ strategyName: context.strategyName,
42923
+ exchangeName: context.exchangeName,
42924
+ frameName: "",
42925
+ });
42926
+ };
42927
+ /**
42928
+ * Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
42929
+ *
42930
+ * Computed as: max(0, peakPnlCost - fallPnlCost).
42931
+ * Returns null if no pending signal exists.
42932
+ *
42933
+ * @param symbol - Trading pair symbol
42934
+ * @param context - Execution context with strategyName and exchangeName
42935
+ * @returns peak-to-trough PnL cost distance (≥ 0) or null if no active position
42936
+ */
42937
+ this.getMaxDrawdownDistancePnlCost = async (symbol, context) => {
42938
+ backtest.loggerService.info(LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST, {
42939
+ symbol,
42940
+ context,
42941
+ });
42942
+ backtest.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
42943
+ backtest.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
42944
+ {
42945
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
42946
+ riskName &&
42947
+ backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
42948
+ riskList &&
42949
+ riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST));
42950
+ actions &&
42951
+ actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST));
42952
+ }
42953
+ return await backtest.strategyCoreService.getMaxDrawdownDistancePnlCost(false, symbol, {
42954
+ strategyName: context.strategyName,
42955
+ exchangeName: context.exchangeName,
42956
+ frameName: "",
42957
+ });
42958
+ };
42641
42959
  /**
42642
42960
  * Checks whether the current price falls within the tolerance zone of any existing DCA entry level.
42643
42961
  * Use this to prevent duplicate DCA entries at the same price area.
@@ -42779,7 +43097,7 @@ class LiveUtils {
42779
43097
  * @param symbol - Trading pair symbol
42780
43098
  * @param strategyName - Strategy name
42781
43099
  * @param context - Execution context with exchangeName and frameName
42782
- * @param cancelId - Optional cancellation ID for tracking user-initiated cancellations
43100
+ * @param payload - Optional commit payload with id and note
42783
43101
  * @returns Promise that resolves when scheduled signal is cancelled
42784
43102
  *
42785
43103
  * @example
@@ -42789,14 +43107,14 @@ class LiveUtils {
42789
43107
  * exchangeName: "binance",
42790
43108
  * frameName: "",
42791
43109
  * strategyName: "my-strategy"
42792
- * }, "manual-cancel-001");
43110
+ * }, { id: "manual-cancel-001" });
42793
43111
  * ```
42794
43112
  */
42795
- this.commitCancelScheduled = async (symbol, context, cancelId) => {
43113
+ this.commitCancelScheduled = async (symbol, context, payload = {}) => {
42796
43114
  backtest.loggerService.info(LIVE_METHOD_NAME_CANCEL_SCHEDULED, {
42797
43115
  symbol,
42798
43116
  context,
42799
- cancelId,
43117
+ payload,
42800
43118
  });
42801
43119
  backtest.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_CANCEL_SCHEDULED);
42802
43120
  backtest.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_CANCEL_SCHEDULED);
@@ -42813,7 +43131,7 @@ class LiveUtils {
42813
43131
  strategyName: context.strategyName,
42814
43132
  exchangeName: context.exchangeName,
42815
43133
  frameName: "",
42816
- }, cancelId);
43134
+ }, payload);
42817
43135
  };
42818
43136
  /**
42819
43137
  * Closes the pending signal without stopping the strategy.
@@ -42824,7 +43142,7 @@ class LiveUtils {
42824
43142
  *
42825
43143
  * @param symbol - Trading pair symbol
42826
43144
  * @param context - Execution context with strategyName and exchangeName
42827
- * @param closeId - Optional close ID for user-initiated closes
43145
+ * @param payload - Optional commit payload with id and note
42828
43146
  * @returns Promise that resolves when pending signal is closed
42829
43147
  *
42830
43148
  * @example
@@ -42833,14 +43151,14 @@ class LiveUtils {
42833
43151
  * await Live.commitClose("BTCUSDT", {
42834
43152
  * exchangeName: "binance",
42835
43153
  * strategyName: "my-strategy"
42836
- * }, "manual-close-001");
43154
+ * }, { id: "manual-close-001" });
42837
43155
  * ```
42838
43156
  */
42839
- this.commitClosePending = async (symbol, context, closeId) => {
43157
+ this.commitClosePending = async (symbol, context, payload = {}) => {
42840
43158
  backtest.loggerService.info(LIVE_METHOD_NAME_CLOSE_PENDING, {
42841
43159
  symbol,
42842
43160
  context,
42843
- closeId,
43161
+ payload,
42844
43162
  });
42845
43163
  backtest.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_CLOSE_PENDING);
42846
43164
  backtest.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_CLOSE_PENDING);
@@ -42857,7 +43175,7 @@ class LiveUtils {
42857
43175
  strategyName: context.strategyName,
42858
43176
  exchangeName: context.exchangeName,
42859
43177
  frameName: "",
42860
- }, closeId);
43178
+ }, payload);
42861
43179
  };
42862
43180
  /**
42863
43181
  * Executes partial close at profit level (moving toward TP).
@@ -43651,7 +43969,7 @@ class LiveUtils {
43651
43969
  *
43652
43970
  * @param symbol - Trading pair symbol
43653
43971
  * @param context - Execution context with strategyName and exchangeName
43654
- * @param activateId - Optional activation ID for tracking user-initiated activations
43972
+ * @param payload - Optional commit payload with id and note
43655
43973
  * @returns Promise that resolves when activation flag is set
43656
43974
  *
43657
43975
  * @example
@@ -43660,14 +43978,14 @@ class LiveUtils {
43660
43978
  * await Live.commitActivateScheduled("BTCUSDT", {
43661
43979
  * strategyName: "my-strategy",
43662
43980
  * exchangeName: "binance"
43663
- * }, "manual-activate-001");
43981
+ * }, { id: "manual-activate-001" });
43664
43982
  * ```
43665
43983
  */
43666
- this.commitActivateScheduled = async (symbol, context, activateId) => {
43984
+ this.commitActivateScheduled = async (symbol, context, payload = {}) => {
43667
43985
  backtest.loggerService.info(LIVE_METHOD_NAME_ACTIVATE_SCHEDULED, {
43668
43986
  symbol,
43669
43987
  context,
43670
- activateId,
43988
+ payload,
43671
43989
  });
43672
43990
  backtest.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_ACTIVATE_SCHEDULED);
43673
43991
  backtest.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_ACTIVATE_SCHEDULED);
@@ -43684,7 +44002,7 @@ class LiveUtils {
43684
44002
  strategyName: context.strategyName,
43685
44003
  exchangeName: context.exchangeName,
43686
44004
  frameName: "",
43687
- }, activateId);
44005
+ }, payload);
43688
44006
  };
43689
44007
  /**
43690
44008
  * Adds a new DCA entry to the active pending signal.
@@ -49755,6 +50073,741 @@ class MaxDrawdownUtils {
49755
50073
  */
49756
50074
  const MaxDrawdown = new MaxDrawdownUtils();
49757
50075
 
50076
+ const REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT = "ReflectUtils.getPositionPnlPercent";
50077
+ const REFLECT_METHOD_NAME_GET_POSITION_PNL_COST = "ReflectUtils.getPositionPnlCost";
50078
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE = "ReflectUtils.getPositionHighestProfitPrice";
50079
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP = "ReflectUtils.getPositionHighestProfitTimestamp";
50080
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE = "ReflectUtils.getPositionHighestPnlPercentage";
50081
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST = "ReflectUtils.getPositionHighestPnlCost";
50082
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN = "ReflectUtils.getPositionHighestProfitBreakeven";
50083
+ const REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES = "ReflectUtils.getPositionDrawdownMinutes";
50084
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES = "ReflectUtils.getPositionHighestProfitMinutes";
50085
+ const REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES = "ReflectUtils.getPositionMaxDrawdownMinutes";
50086
+ const REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE = "ReflectUtils.getPositionMaxDrawdownPrice";
50087
+ const REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP = "ReflectUtils.getPositionMaxDrawdownTimestamp";
50088
+ const REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE = "ReflectUtils.getPositionMaxDrawdownPnlPercentage";
50089
+ const REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST = "ReflectUtils.getPositionMaxDrawdownPnlCost";
50090
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE = "ReflectUtils.getPositionHighestProfitDistancePnlPercentage";
50091
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST = "ReflectUtils.getPositionHighestProfitDistancePnlCost";
50092
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE = "ReflectUtils.getPositionHighestMaxDrawdownPnlPercentage";
50093
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST = "ReflectUtils.getPositionHighestMaxDrawdownPnlCost";
50094
+ const REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE = "ReflectUtils.getMaxDrawdownDistancePnlPercentage";
50095
+ const REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST = "ReflectUtils.getMaxDrawdownDistancePnlCost";
50096
+ /**
50097
+ * Utility class for real-time position reflection: PNL, peak profit, and drawdown queries.
50098
+ *
50099
+ * Provides unified access to strategyCoreService position state methods with logging
50100
+ * and full validation (strategy, exchange, frame, risk, actions).
50101
+ * Works for both live and backtest modes via the `backtest` parameter.
50102
+ * Exported as singleton instance for convenient usage.
50103
+ *
50104
+ * @example
50105
+ * ```typescript
50106
+ * import { Reflect } from "backtest-kit";
50107
+ *
50108
+ * // Get current unrealized PNL percentage
50109
+ * const pnl = await Reflect.getPositionPnlPercent(
50110
+ * "BTCUSDT",
50111
+ * 45000,
50112
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50113
+ * );
50114
+ * console.log(`PNL: ${pnl}%`);
50115
+ *
50116
+ * // Get peak profit reached
50117
+ * const peakPnl = await Reflect.getPositionHighestPnlPercentage(
50118
+ * "BTCUSDT",
50119
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50120
+ * );
50121
+ * console.log(`Peak PNL: ${peakPnl}%`);
50122
+ * ```
50123
+ */
50124
+ class ReflectUtils {
50125
+ constructor() {
50126
+ /**
50127
+ * Returns the unrealized PNL percentage for the current pending signal at currentPrice.
50128
+ *
50129
+ * Accounts for partial closes, DCA entries, slippage and fees.
50130
+ * Returns null if no pending signal exists.
50131
+ *
50132
+ * @param symbol - Trading pair symbol
50133
+ * @param currentPrice - Current market price
50134
+ * @param context - Execution context with strategyName, exchangeName and frameName
50135
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50136
+ * @returns Promise resolving to PNL percentage or null
50137
+ *
50138
+ * @example
50139
+ * ```typescript
50140
+ * const pnl = await Reflect.getPositionPnlPercent(
50141
+ * "BTCUSDT",
50142
+ * 45000,
50143
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50144
+ * );
50145
+ * console.log(`PNL: ${pnl}%`);
50146
+ * ```
50147
+ */
50148
+ this.getPositionPnlPercent = async (symbol, currentPrice, context, backtest$1 = false) => {
50149
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT, { symbol, currentPrice, context });
50150
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT);
50151
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT);
50152
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT);
50153
+ {
50154
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50155
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT);
50156
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT));
50157
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT));
50158
+ }
50159
+ return await backtest.strategyCoreService.getPositionPnlPercent(backtest$1, symbol, currentPrice, context);
50160
+ };
50161
+ /**
50162
+ * Returns the unrealized PNL in dollars for the current pending signal at currentPrice.
50163
+ *
50164
+ * Calculated as: pnlPercentage / 100 × totalInvestedCost.
50165
+ * Accounts for partial closes, DCA entries, slippage and fees.
50166
+ * Returns null if no pending signal exists.
50167
+ *
50168
+ * @param symbol - Trading pair symbol
50169
+ * @param currentPrice - Current market price
50170
+ * @param context - Execution context with strategyName, exchangeName and frameName
50171
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50172
+ * @returns Promise resolving to PNL in dollars or null
50173
+ *
50174
+ * @example
50175
+ * ```typescript
50176
+ * const pnlCost = await Reflect.getPositionPnlCost(
50177
+ * "BTCUSDT",
50178
+ * 45000,
50179
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50180
+ * );
50181
+ * console.log(`PNL: $${pnlCost}`);
50182
+ * ```
50183
+ */
50184
+ this.getPositionPnlCost = async (symbol, currentPrice, context, backtest$1 = false) => {
50185
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_PNL_COST, { symbol, currentPrice, context });
50186
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_PNL_COST);
50187
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_PNL_COST);
50188
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_PNL_COST);
50189
+ {
50190
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50191
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_PNL_COST);
50192
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_PNL_COST));
50193
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_PNL_COST));
50194
+ }
50195
+ return await backtest.strategyCoreService.getPositionPnlCost(backtest$1, symbol, currentPrice, context);
50196
+ };
50197
+ /**
50198
+ * Returns the best price reached in the profit direction during this position's life.
50199
+ *
50200
+ * Returns null if no pending signal exists.
50201
+ *
50202
+ * @param symbol - Trading pair symbol
50203
+ * @param context - Execution context with strategyName, exchangeName and frameName
50204
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50205
+ * @returns Promise resolving to price or null
50206
+ *
50207
+ * @example
50208
+ * ```typescript
50209
+ * const peakPrice = await Reflect.getPositionHighestProfitPrice(
50210
+ * "BTCUSDT",
50211
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50212
+ * );
50213
+ * console.log(`Peak price: ${peakPrice}`);
50214
+ * ```
50215
+ */
50216
+ this.getPositionHighestProfitPrice = async (symbol, context, backtest$1 = false) => {
50217
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE, { symbol, context });
50218
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE);
50219
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE);
50220
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE);
50221
+ {
50222
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50223
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE);
50224
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE));
50225
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE));
50226
+ }
50227
+ return await backtest.strategyCoreService.getPositionHighestProfitPrice(backtest$1, symbol, context);
50228
+ };
50229
+ /**
50230
+ * Returns the timestamp when the best profit price was recorded during this position's life.
50231
+ *
50232
+ * Returns null if no pending signal exists.
50233
+ *
50234
+ * @param symbol - Trading pair symbol
50235
+ * @param context - Execution context with strategyName, exchangeName and frameName
50236
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50237
+ * @returns Promise resolving to timestamp in milliseconds or null
50238
+ *
50239
+ * @example
50240
+ * ```typescript
50241
+ * const ts = await Reflect.getPositionHighestProfitTimestamp(
50242
+ * "BTCUSDT",
50243
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50244
+ * );
50245
+ * console.log(`Peak at: ${new Date(ts).toISOString()}`);
50246
+ * ```
50247
+ */
50248
+ this.getPositionHighestProfitTimestamp = async (symbol, context, backtest$1 = false) => {
50249
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP, { symbol, context });
50250
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP);
50251
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP);
50252
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP);
50253
+ {
50254
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50255
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP);
50256
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP));
50257
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP));
50258
+ }
50259
+ return await backtest.strategyCoreService.getPositionHighestProfitTimestamp(backtest$1, symbol, context);
50260
+ };
50261
+ /**
50262
+ * Returns the PnL percentage at the moment the best profit price was recorded during this position's life.
50263
+ *
50264
+ * Returns null if no pending signal exists.
50265
+ *
50266
+ * @param symbol - Trading pair symbol
50267
+ * @param context - Execution context with strategyName, exchangeName and frameName
50268
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50269
+ * @returns Promise resolving to PnL percentage or null
50270
+ *
50271
+ * @example
50272
+ * ```typescript
50273
+ * const peakPnl = await Reflect.getPositionHighestPnlPercentage(
50274
+ * "BTCUSDT",
50275
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50276
+ * );
50277
+ * console.log(`Peak PNL: ${peakPnl}%`);
50278
+ * ```
50279
+ */
50280
+ this.getPositionHighestPnlPercentage = async (symbol, context, backtest$1 = false) => {
50281
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE, { symbol, context });
50282
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE);
50283
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE);
50284
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE);
50285
+ {
50286
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50287
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE);
50288
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE));
50289
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE));
50290
+ }
50291
+ return await backtest.strategyCoreService.getPositionHighestPnlPercentage(backtest$1, symbol, context);
50292
+ };
50293
+ /**
50294
+ * Returns the PnL cost (in quote currency) at the moment the best profit price was recorded during this position's life.
50295
+ *
50296
+ * Returns null if no pending signal exists.
50297
+ *
50298
+ * @param symbol - Trading pair symbol
50299
+ * @param context - Execution context with strategyName, exchangeName and frameName
50300
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50301
+ * @returns Promise resolving to PnL cost in quote currency or null
50302
+ *
50303
+ * @example
50304
+ * ```typescript
50305
+ * const peakCost = await Reflect.getPositionHighestPnlCost(
50306
+ * "BTCUSDT",
50307
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50308
+ * );
50309
+ * console.log(`Peak PNL: $${peakCost}`);
50310
+ * ```
50311
+ */
50312
+ this.getPositionHighestPnlCost = async (symbol, context, backtest$1 = false) => {
50313
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST, { symbol, context });
50314
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST);
50315
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST);
50316
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST);
50317
+ {
50318
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50319
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST);
50320
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST));
50321
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST));
50322
+ }
50323
+ return await backtest.strategyCoreService.getPositionHighestPnlCost(backtest$1, symbol, context);
50324
+ };
50325
+ /**
50326
+ * Returns whether breakeven was mathematically reachable at the highest profit price.
50327
+ *
50328
+ * Returns null if no pending signal exists.
50329
+ *
50330
+ * @param symbol - Trading pair symbol
50331
+ * @param context - Execution context with strategyName, exchangeName and frameName
50332
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50333
+ * @returns Promise resolving to true if breakeven was reachable at peak, false otherwise, or null
50334
+ *
50335
+ * @example
50336
+ * ```typescript
50337
+ * const wasReachable = await Reflect.getPositionHighestProfitBreakeven(
50338
+ * "BTCUSDT",
50339
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50340
+ * );
50341
+ * console.log(`Breakeven reachable at peak: ${wasReachable}`);
50342
+ * ```
50343
+ */
50344
+ this.getPositionHighestProfitBreakeven = async (symbol, context, backtest$1 = false) => {
50345
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN, { symbol, context });
50346
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN);
50347
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN);
50348
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN);
50349
+ {
50350
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50351
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN);
50352
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN));
50353
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN));
50354
+ }
50355
+ return await backtest.strategyCoreService.getPositionHighestProfitBreakeven(backtest$1, symbol, context);
50356
+ };
50357
+ /**
50358
+ * Returns the number of minutes elapsed since the highest profit price was recorded.
50359
+ *
50360
+ * Returns null if no pending signal exists.
50361
+ *
50362
+ * @param symbol - Trading pair symbol
50363
+ * @param context - Execution context with strategyName, exchangeName and frameName
50364
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50365
+ * @returns Promise resolving to minutes since highest profit price was recorded, or null
50366
+ *
50367
+ * @example
50368
+ * ```typescript
50369
+ * const minutes = await Reflect.getPositionDrawdownMinutes(
50370
+ * "BTCUSDT",
50371
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50372
+ * );
50373
+ * console.log(`Pulling back from peak for ${minutes} minutes`);
50374
+ * ```
50375
+ */
50376
+ this.getPositionDrawdownMinutes = async (symbol, context, backtest$1 = false) => {
50377
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES, { symbol, context });
50378
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES);
50379
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES);
50380
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES);
50381
+ {
50382
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50383
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES);
50384
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES));
50385
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES));
50386
+ }
50387
+ return await backtest.strategyCoreService.getPositionDrawdownMinutes(backtest$1, symbol, context);
50388
+ };
50389
+ /**
50390
+ * Returns the number of minutes elapsed since the highest profit price was recorded.
50391
+ *
50392
+ * Alias for getPositionDrawdownMinutes — measures how long the position has been
50393
+ * pulling back from its peak profit level.
50394
+ * Returns null if no pending signal exists.
50395
+ *
50396
+ * @param symbol - Trading pair symbol
50397
+ * @param context - Execution context with strategyName, exchangeName and frameName
50398
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50399
+ * @returns Promise resolving to minutes since last profit peak or null
50400
+ *
50401
+ * @example
50402
+ * ```typescript
50403
+ * const minutes = await Reflect.getPositionHighestProfitMinutes(
50404
+ * "BTCUSDT",
50405
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50406
+ * );
50407
+ * console.log(`Pulling back from peak for ${minutes} minutes`);
50408
+ * ```
50409
+ */
50410
+ this.getPositionHighestProfitMinutes = async (symbol, context, backtest$1 = false) => {
50411
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES, { symbol, context });
50412
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES);
50413
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES);
50414
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES);
50415
+ {
50416
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50417
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES);
50418
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES));
50419
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES));
50420
+ }
50421
+ return await backtest.strategyCoreService.getPositionHighestProfitMinutes(backtest$1, symbol, context);
50422
+ };
50423
+ /**
50424
+ * Returns the number of minutes elapsed since the worst loss price was recorded.
50425
+ *
50426
+ * Measures how long ago the deepest drawdown point occurred.
50427
+ * Zero when called at the exact moment the trough was set.
50428
+ * Returns null if no pending signal exists.
50429
+ *
50430
+ * @param symbol - Trading pair symbol
50431
+ * @param context - Execution context with strategyName, exchangeName and frameName
50432
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50433
+ * @returns Promise resolving to minutes since last drawdown trough or null
50434
+ *
50435
+ * @example
50436
+ * ```typescript
50437
+ * const minutes = await Reflect.getPositionMaxDrawdownMinutes(
50438
+ * "BTCUSDT",
50439
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50440
+ * );
50441
+ * console.log(`Drawdown trough was ${minutes} minutes ago`);
50442
+ * ```
50443
+ */
50444
+ this.getPositionMaxDrawdownMinutes = async (symbol, context, backtest$1 = false) => {
50445
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES, { symbol, context });
50446
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES);
50447
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES);
50448
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES);
50449
+ {
50450
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50451
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES);
50452
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES));
50453
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES));
50454
+ }
50455
+ return await backtest.strategyCoreService.getPositionMaxDrawdownMinutes(backtest$1, symbol, context);
50456
+ };
50457
+ /**
50458
+ * Returns the worst price reached in the loss direction during this position's life.
50459
+ *
50460
+ * Returns null if no pending signal exists.
50461
+ *
50462
+ * @param symbol - Trading pair symbol
50463
+ * @param context - Execution context with strategyName, exchangeName and frameName
50464
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50465
+ * @returns Promise resolving to price or null
50466
+ *
50467
+ * @example
50468
+ * ```typescript
50469
+ * const troughPrice = await Reflect.getPositionMaxDrawdownPrice(
50470
+ * "BTCUSDT",
50471
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50472
+ * );
50473
+ * console.log(`Worst price: ${troughPrice}`);
50474
+ * ```
50475
+ */
50476
+ this.getPositionMaxDrawdownPrice = async (symbol, context, backtest$1 = false) => {
50477
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE, { symbol, context });
50478
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE);
50479
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE);
50480
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE);
50481
+ {
50482
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50483
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE);
50484
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE));
50485
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE));
50486
+ }
50487
+ return await backtest.strategyCoreService.getPositionMaxDrawdownPrice(backtest$1, symbol, context);
50488
+ };
50489
+ /**
50490
+ * Returns the timestamp when the worst loss price was recorded during this position's life.
50491
+ *
50492
+ * Returns null if no pending signal exists.
50493
+ *
50494
+ * @param symbol - Trading pair symbol
50495
+ * @param context - Execution context with strategyName, exchangeName and frameName
50496
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50497
+ * @returns Promise resolving to timestamp in milliseconds or null
50498
+ *
50499
+ * @example
50500
+ * ```typescript
50501
+ * const ts = await Reflect.getPositionMaxDrawdownTimestamp(
50502
+ * "BTCUSDT",
50503
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50504
+ * );
50505
+ * console.log(`Worst drawdown at: ${new Date(ts).toISOString()}`);
50506
+ * ```
50507
+ */
50508
+ this.getPositionMaxDrawdownTimestamp = async (symbol, context, backtest$1 = false) => {
50509
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP, { symbol, context });
50510
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP);
50511
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP);
50512
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP);
50513
+ {
50514
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50515
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP);
50516
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP));
50517
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP));
50518
+ }
50519
+ return await backtest.strategyCoreService.getPositionMaxDrawdownTimestamp(backtest$1, symbol, context);
50520
+ };
50521
+ /**
50522
+ * Returns the PnL percentage at the moment the worst loss price was recorded during this position's life.
50523
+ *
50524
+ * Returns null if no pending signal exists.
50525
+ *
50526
+ * @param symbol - Trading pair symbol
50527
+ * @param context - Execution context with strategyName, exchangeName and frameName
50528
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50529
+ * @returns Promise resolving to PnL percentage or null
50530
+ *
50531
+ * @example
50532
+ * ```typescript
50533
+ * const worstPnl = await Reflect.getPositionMaxDrawdownPnlPercentage(
50534
+ * "BTCUSDT",
50535
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50536
+ * );
50537
+ * console.log(`Worst PNL: ${worstPnl}%`);
50538
+ * ```
50539
+ */
50540
+ this.getPositionMaxDrawdownPnlPercentage = async (symbol, context, backtest$1 = false) => {
50541
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE, { symbol, context });
50542
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE);
50543
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE);
50544
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE);
50545
+ {
50546
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50547
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE);
50548
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE));
50549
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE));
50550
+ }
50551
+ return await backtest.strategyCoreService.getPositionMaxDrawdownPnlPercentage(backtest$1, symbol, context);
50552
+ };
50553
+ /**
50554
+ * Returns the PnL cost (in quote currency) at the moment the worst loss price was recorded during this position's life.
50555
+ *
50556
+ * Returns null if no pending signal exists.
50557
+ *
50558
+ * @param symbol - Trading pair symbol
50559
+ * @param context - Execution context with strategyName, exchangeName and frameName
50560
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50561
+ * @returns Promise resolving to PnL cost in quote currency or null
50562
+ *
50563
+ * @example
50564
+ * ```typescript
50565
+ * const worstCost = await Reflect.getPositionMaxDrawdownPnlCost(
50566
+ * "BTCUSDT",
50567
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50568
+ * );
50569
+ * console.log(`Worst PNL: $${worstCost}`);
50570
+ * ```
50571
+ */
50572
+ this.getPositionMaxDrawdownPnlCost = async (symbol, context, backtest$1 = false) => {
50573
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST, { symbol, context });
50574
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST);
50575
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST);
50576
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST);
50577
+ {
50578
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50579
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST);
50580
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST));
50581
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST));
50582
+ }
50583
+ return await backtest.strategyCoreService.getPositionMaxDrawdownPnlCost(backtest$1, symbol, context);
50584
+ };
50585
+ /**
50586
+ * Returns the distance in PnL percentage between the current price and the highest profit peak.
50587
+ *
50588
+ * Result is ≥ 0. Returns null if no pending signal exists.
50589
+ *
50590
+ * @param symbol - Trading pair symbol
50591
+ * @param context - Execution context with strategyName, exchangeName and frameName
50592
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50593
+ * @returns Promise resolving to drawdown distance in PnL% (≥ 0) or null
50594
+ *
50595
+ * @example
50596
+ * ```typescript
50597
+ * const distance = await Reflect.getPositionHighestProfitDistancePnlPercentage(
50598
+ * "BTCUSDT",
50599
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50600
+ * );
50601
+ * console.log(`Dropped ${distance}% from peak`);
50602
+ * ```
50603
+ */
50604
+ this.getPositionHighestProfitDistancePnlPercentage = async (symbol, context, backtest$1 = false) => {
50605
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE, { symbol, context });
50606
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE);
50607
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE);
50608
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE);
50609
+ {
50610
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50611
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE);
50612
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE));
50613
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE));
50614
+ }
50615
+ return await backtest.strategyCoreService.getPositionHighestProfitDistancePnlPercentage(backtest$1, symbol, context);
50616
+ };
50617
+ /**
50618
+ * Returns the distance in PnL cost between the current price and the highest profit peak.
50619
+ *
50620
+ * Result is ≥ 0. Returns null if no pending signal exists.
50621
+ *
50622
+ * @param symbol - Trading pair symbol
50623
+ * @param context - Execution context with strategyName, exchangeName and frameName
50624
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50625
+ * @returns Promise resolving to drawdown distance in PnL cost (≥ 0) or null
50626
+ *
50627
+ * @example
50628
+ * ```typescript
50629
+ * const distance = await Reflect.getPositionHighestProfitDistancePnlCost(
50630
+ * "BTCUSDT",
50631
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50632
+ * );
50633
+ * console.log(`Dropped $${distance} from peak`);
50634
+ * ```
50635
+ */
50636
+ this.getPositionHighestProfitDistancePnlCost = async (symbol, context, backtest$1 = false) => {
50637
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST, { symbol, context });
50638
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST);
50639
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST);
50640
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST);
50641
+ {
50642
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50643
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST);
50644
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST));
50645
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST));
50646
+ }
50647
+ return await backtest.strategyCoreService.getPositionHighestProfitDistancePnlCost(backtest$1, symbol, context);
50648
+ };
50649
+ /**
50650
+ * Returns the distance in PnL percentage between the current price and the worst drawdown trough.
50651
+ *
50652
+ * Result is ≥ 0. Returns null if no pending signal exists.
50653
+ *
50654
+ * @param symbol - Trading pair symbol
50655
+ * @param context - Execution context with strategyName, exchangeName and frameName
50656
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50657
+ * @returns Promise resolving to recovery distance from worst drawdown trough in PnL% (≥ 0) or null
50658
+ *
50659
+ * @example
50660
+ * ```typescript
50661
+ * const distance = await Reflect.getPositionHighestMaxDrawdownPnlPercentage(
50662
+ * "BTCUSDT",
50663
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50664
+ * );
50665
+ * console.log(`${distance}% above worst trough`);
50666
+ * ```
50667
+ */
50668
+ this.getPositionHighestMaxDrawdownPnlPercentage = async (symbol, context, backtest$1 = false) => {
50669
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE, { symbol, context });
50670
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE);
50671
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE);
50672
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE);
50673
+ {
50674
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50675
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE);
50676
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE));
50677
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE));
50678
+ }
50679
+ return await backtest.strategyCoreService.getPositionHighestMaxDrawdownPnlPercentage(backtest$1, symbol, context);
50680
+ };
50681
+ /**
50682
+ * Returns the distance in PnL cost between the current price and the worst drawdown trough.
50683
+ *
50684
+ * Result is ≥ 0. Returns null if no pending signal exists.
50685
+ *
50686
+ * @param symbol - Trading pair symbol
50687
+ * @param context - Execution context with strategyName, exchangeName and frameName
50688
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50689
+ * @returns Promise resolving to recovery distance from worst drawdown trough in PnL cost (≥ 0) or null
50690
+ *
50691
+ * @example
50692
+ * ```typescript
50693
+ * const distance = await Reflect.getPositionHighestMaxDrawdownPnlCost(
50694
+ * "BTCUSDT",
50695
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50696
+ * );
50697
+ * console.log(`$${distance} above worst trough`);
50698
+ * ```
50699
+ */
50700
+ this.getPositionHighestMaxDrawdownPnlCost = async (symbol, context, backtest$1 = false) => {
50701
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST, { symbol, context });
50702
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST);
50703
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST);
50704
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST);
50705
+ {
50706
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50707
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST);
50708
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST));
50709
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST));
50710
+ }
50711
+ return await backtest.strategyCoreService.getPositionHighestMaxDrawdownPnlCost(backtest$1, symbol, context);
50712
+ };
50713
+ /**
50714
+ * Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
50715
+ *
50716
+ * Result is ≥ 0. Returns null if no pending signal exists.
50717
+ *
50718
+ * @param symbol - Trading pair symbol
50719
+ * @param context - Execution context with strategyName, exchangeName and frameName
50720
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50721
+ * @returns Promise resolving to peak-to-trough PnL percentage distance (≥ 0) or null
50722
+ *
50723
+ * @example
50724
+ * ```typescript
50725
+ * const distance = await Reflect.getMaxDrawdownDistancePnlPercentage(
50726
+ * "BTCUSDT",
50727
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50728
+ * );
50729
+ * console.log(`Peak-to-trough: ${distance}%`);
50730
+ * ```
50731
+ */
50732
+ this.getMaxDrawdownDistancePnlPercentage = async (symbol, context, backtest$1 = false) => {
50733
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE, { symbol, context });
50734
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
50735
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
50736
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
50737
+ {
50738
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50739
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
50740
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE));
50741
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE));
50742
+ }
50743
+ return await backtest.strategyCoreService.getMaxDrawdownDistancePnlPercentage(backtest$1, symbol, context);
50744
+ };
50745
+ /**
50746
+ * Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
50747
+ *
50748
+ * Result is ≥ 0. Returns null if no pending signal exists.
50749
+ *
50750
+ * @param symbol - Trading pair symbol
50751
+ * @param context - Execution context with strategyName, exchangeName and frameName
50752
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50753
+ * @returns Promise resolving to peak-to-trough PnL cost distance (≥ 0) or null
50754
+ *
50755
+ * @example
50756
+ * ```typescript
50757
+ * const distance = await Reflect.getMaxDrawdownDistancePnlCost(
50758
+ * "BTCUSDT",
50759
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50760
+ * );
50761
+ * console.log(`Peak-to-trough: $${distance}`);
50762
+ * ```
50763
+ */
50764
+ this.getMaxDrawdownDistancePnlCost = async (symbol, context, backtest$1 = false) => {
50765
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST, { symbol, context });
50766
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
50767
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
50768
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
50769
+ {
50770
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50771
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
50772
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST));
50773
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST));
50774
+ }
50775
+ return await backtest.strategyCoreService.getMaxDrawdownDistancePnlCost(backtest$1, symbol, context);
50776
+ };
50777
+ }
50778
+ }
50779
+ /**
50780
+ * Singleton instance of ReflectUtils for convenient position state queries.
50781
+ *
50782
+ * @example
50783
+ * ```typescript
50784
+ * import { Reflect } from "backtest-kit";
50785
+ *
50786
+ * // Real-time PNL
50787
+ * const pnl = await Reflect.getPositionPnlPercent(
50788
+ * "BTCUSDT",
50789
+ * 45000,
50790
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50791
+ * );
50792
+ * console.log(`PNL: ${pnl}%`);
50793
+ *
50794
+ * // Peak profit
50795
+ * const peakPnl = await Reflect.getPositionHighestPnlPercentage(
50796
+ * "BTCUSDT",
50797
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50798
+ * );
50799
+ * console.log(`Peak PNL: ${peakPnl}%`);
50800
+ *
50801
+ * // Drawdown from peak
50802
+ * const drawdown = await Reflect.getPositionHighestProfitDistancePnlPercentage(
50803
+ * "BTCUSDT",
50804
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50805
+ * );
50806
+ * console.log(`Dropped ${drawdown}% from peak`);
50807
+ * ```
50808
+ */
50809
+ const Reflect$1 = new ReflectUtils();
50810
+
49758
50811
  /**
49759
50812
  * Utility class containing predefined trading constants for take-profit and stop-loss levels.
49760
50813
  *
@@ -55864,6 +56917,7 @@ exports.PersistSignalAdapter = PersistSignalAdapter;
55864
56917
  exports.PersistStorageAdapter = PersistStorageAdapter;
55865
56918
  exports.Position = Position;
55866
56919
  exports.PositionSize = PositionSize;
56920
+ exports.Reflect = Reflect$1;
55867
56921
  exports.Report = Report;
55868
56922
  exports.ReportBase = ReportBase;
55869
56923
  exports.ReportWriter = ReportWriter;
@@ -55922,6 +56976,8 @@ exports.getDefaultConfig = getDefaultConfig;
55922
56976
  exports.getEffectivePriceOpen = getEffectivePriceOpen;
55923
56977
  exports.getExchangeSchema = getExchangeSchema;
55924
56978
  exports.getFrameSchema = getFrameSchema;
56979
+ exports.getMaxDrawdownDistancePnlCost = getMaxDrawdownDistancePnlCost;
56980
+ exports.getMaxDrawdownDistancePnlPercentage = getMaxDrawdownDistancePnlPercentage;
55925
56981
  exports.getMode = getMode;
55926
56982
  exports.getNextCandles = getNextCandles;
55927
56983
  exports.getOrderBook = getOrderBook;