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.mjs CHANGED
@@ -6573,7 +6573,7 @@ const RETURN_IDLE_FN = async (self, currentPrice) => {
6573
6573
  await CALL_TICK_CALLBACKS_FN(self, self.params.execution.context.symbol, result, currentTime, self.params.execution.context.backtest);
6574
6574
  return result;
6575
6575
  };
6576
- const CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN = async (self, scheduled, averagePrice, closeTimestamp, reason, cancelId) => {
6576
+ const CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN = async (self, scheduled, averagePrice, closeTimestamp, reason, cancelId, cancelNote) => {
6577
6577
  self.params.logger.info("ClientStrategy backtest scheduled signal cancelled", {
6578
6578
  symbol: self.params.execution.context.symbol,
6579
6579
  signalId: scheduled.id,
@@ -6598,7 +6598,7 @@ const CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN = async (self, scheduled, averagePr
6598
6598
  totalPartials: scheduled._partial?.length ?? 0,
6599
6599
  originalPriceOpen: scheduled.priceOpen,
6600
6600
  pnl: toProfitLossDto(scheduled, averagePrice),
6601
- note: scheduled.note,
6601
+ note: cancelNote ?? scheduled.note,
6602
6602
  });
6603
6603
  }
6604
6604
  await CALL_CANCEL_CALLBACKS_FN(self, self.params.execution.context.symbol, scheduled, averagePrice, closeTimestamp, self.params.execution.context.backtest);
@@ -6765,7 +6765,7 @@ const CLOSE_USER_PENDING_SIGNAL_IN_BACKTEST_FN = async (self, closedSignal, aver
6765
6765
  totalPartials: closedSignal._partial?.length ?? 0,
6766
6766
  originalPriceOpen: closedSignal.priceOpen,
6767
6767
  pnl: toProfitLossDto(closedSignal, averagePrice),
6768
- note: closedSignal.note,
6768
+ note: closedSignal.closeNote ?? closedSignal.note,
6769
6769
  });
6770
6770
  await CALL_CLOSE_CALLBACKS_FN(self, self.params.execution.context.symbol, closedSignal, averagePrice, closeTimestamp, self.params.execution.context.backtest);
6771
6771
  await CALL_PARTIAL_CLEAR_FN(self, self.params.execution.context.symbol, closedSignal, averagePrice, closeTimestamp, self.params.execution.context.backtest);
@@ -6812,7 +6812,8 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles, fra
6812
6812
  if (self._cancelledSignal) {
6813
6813
  // Сигнал был отменен через cancel() в onSchedulePing
6814
6814
  const cancelId = self._cancelledSignal.cancelId;
6815
- const result = await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, averagePrice, candle.timestamp, "user", cancelId);
6815
+ const cancelNote = self._cancelledSignal.cancelNote;
6816
+ const result = await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, averagePrice, candle.timestamp, "user", cancelId, cancelNote);
6816
6817
  return { outcome: "cancelled", result };
6817
6818
  }
6818
6819
  // КРИТИЧНО: Проверяем был ли сигнал активирован пользователем через activateScheduled()
@@ -6866,7 +6867,7 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles, fra
6866
6867
  totalPartials: activatedSignal._partial?.length ?? 0,
6867
6868
  originalPriceOpen: activatedSignal.priceOpen,
6868
6869
  pnl: toProfitLossDto(activatedSignal, averagePrice),
6869
- note: activatedSignal.note,
6870
+ note: activatedSignal.activateNote ?? activatedSignal.note,
6870
6871
  });
6871
6872
  return { outcome: "pending" };
6872
6873
  }
@@ -6898,7 +6899,7 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles, fra
6898
6899
  pendingAt: publicSignalForCommit.pendingAt,
6899
6900
  totalEntries: publicSignalForCommit.totalEntries,
6900
6901
  totalPartials: publicSignalForCommit.totalPartials,
6901
- note: publicSignalForCommit.note,
6902
+ note: activatedSignal.activateNote ?? publicSignalForCommit.note,
6902
6903
  });
6903
6904
  await CALL_OPEN_CALLBACKS_FN(self, self.params.execution.context.symbol, pendingSignal, pendingSignal.priceOpen, candle.timestamp, self.params.execution.context.backtest);
6904
6905
  await CALL_BACKTEST_SCHEDULE_OPEN_FN(self, self.params.execution.context.symbol, pendingSignal, candle.timestamp, self.params.execution.context.backtest);
@@ -8019,6 +8020,44 @@ class ClientStrategy {
8019
8020
  const currentPnl = toProfitLossDto(this._pendingSignal, currentPrice);
8020
8021
  return Math.max(0, currentPnl.pnlCost - this._pendingSignal._fall.pnlCost);
8021
8022
  }
8023
+ /**
8024
+ * Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
8025
+ *
8026
+ * Measures the total swing from the stored `_peak.pnlPercentage` to the stored `_fall.pnlPercentage`.
8027
+ * Computed as: max(0, peakPnlPercentage - fallPnlPercentage).
8028
+ *
8029
+ * Returns null if no pending signal exists.
8030
+ *
8031
+ * @param symbol - Trading pair symbol
8032
+ * @param currentPrice - Current market price
8033
+ * @returns Promise resolving to peak-to-trough PnL percentage distance (≥ 0) or null
8034
+ */
8035
+ async getMaxDrawdownDistancePnlPercentage(symbol, currentPrice) {
8036
+ this.params.logger.debug("ClientStrategy getMaxDrawdownDistancePnlPercentage", { symbol, currentPrice });
8037
+ if (!this._pendingSignal) {
8038
+ return null;
8039
+ }
8040
+ return Math.max(0, this._pendingSignal._peak.pnlPercentage - this._pendingSignal._fall.pnlPercentage);
8041
+ }
8042
+ /**
8043
+ * Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
8044
+ *
8045
+ * Measures the total swing from the stored `_peak.pnlCost` to the stored `_fall.pnlCost`.
8046
+ * Computed as: max(0, peakPnlCost - fallPnlCost).
8047
+ *
8048
+ * Returns null if no pending signal exists.
8049
+ *
8050
+ * @param symbol - Trading pair symbol
8051
+ * @param currentPrice - Current market price
8052
+ * @returns Promise resolving to peak-to-trough PnL cost distance (≥ 0) or null
8053
+ */
8054
+ async getMaxDrawdownDistancePnlCost(symbol, currentPrice) {
8055
+ this.params.logger.debug("ClientStrategy getMaxDrawdownDistancePnlCost", { symbol, currentPrice });
8056
+ if (!this._pendingSignal) {
8057
+ return null;
8058
+ }
8059
+ return Math.max(0, this._pendingSignal._peak.pnlCost - this._pendingSignal._fall.pnlCost);
8060
+ }
8022
8061
  /**
8023
8062
  * Performs a single tick of strategy execution.
8024
8063
  *
@@ -8083,7 +8122,7 @@ class ClientStrategy {
8083
8122
  totalPartials: cancelledSignal._partial?.length ?? 0,
8084
8123
  originalPriceOpen: cancelledSignal.priceOpen,
8085
8124
  pnl: toProfitLossDto(cancelledSignal, currentPrice),
8086
- note: cancelledSignal.note,
8125
+ note: cancelledSignal.cancelNote ?? cancelledSignal.note,
8087
8126
  });
8088
8127
  // Call onCancel callback
8089
8128
  await CALL_CANCEL_CALLBACKS_FN(this, this.params.execution.context.symbol, cancelledSignal, currentPrice, currentTime, this.params.execution.context.backtest);
@@ -8137,7 +8176,7 @@ class ClientStrategy {
8137
8176
  totalPartials: closedSignal._partial?.length ?? 0,
8138
8177
  originalPriceOpen: closedSignal.priceOpen,
8139
8178
  pnl: toProfitLossDto(closedSignal, currentPrice),
8140
- note: closedSignal.note,
8179
+ note: closedSignal.closeNote ?? closedSignal.note,
8141
8180
  });
8142
8181
  // Call onClose callback
8143
8182
  await CALL_CLOSE_CALLBACKS_FN(this, this.params.execution.context.symbol, closedSignal, currentPrice, currentTime, this.params.execution.context.backtest);
@@ -8219,7 +8258,7 @@ class ClientStrategy {
8219
8258
  totalPartials: activatedSignal._partial?.length ?? 0,
8220
8259
  originalPriceOpen: activatedSignal.priceOpen,
8221
8260
  pnl: toProfitLossDto(activatedSignal, currentPrice),
8222
- note: activatedSignal.note,
8261
+ note: activatedSignal.activateNote ?? activatedSignal.note,
8223
8262
  });
8224
8263
  return await RETURN_IDLE_FN(this, currentPrice);
8225
8264
  }
@@ -8250,7 +8289,7 @@ class ClientStrategy {
8250
8289
  pendingAt: publicSignalForCommit.pendingAt,
8251
8290
  totalEntries: publicSignalForCommit.totalEntries,
8252
8291
  totalPartials: publicSignalForCommit.totalPartials,
8253
- note: publicSignalForCommit.note,
8292
+ note: activatedSignal.activateNote ?? publicSignalForCommit.note,
8254
8293
  });
8255
8294
  // Call onOpen callback
8256
8295
  await CALL_OPEN_CALLBACKS_FN(this, this.params.execution.context.symbol, pendingSignal, currentPrice, currentTime, this.params.execution.context.backtest);
@@ -8386,7 +8425,7 @@ class ClientStrategy {
8386
8425
  totalPartials: cancelledSignal._partial?.length ?? 0,
8387
8426
  originalPriceOpen: cancelledSignal.priceOpen,
8388
8427
  pnl: toProfitLossDto(cancelledSignal, currentPrice),
8389
- note: cancelledSignal.note,
8428
+ note: cancelledSignal.cancelNote ?? cancelledSignal.note,
8390
8429
  });
8391
8430
  await CALL_CANCEL_CALLBACKS_FN(this, this.params.execution.context.symbol, cancelledSignal, currentPrice, closeTimestamp, this.params.execution.context.backtest);
8392
8431
  const cancelledResult = {
@@ -8441,7 +8480,7 @@ class ClientStrategy {
8441
8480
  totalPartials: closedSignal._partial?.length ?? 0,
8442
8481
  originalPriceOpen: closedSignal.priceOpen,
8443
8482
  pnl: toProfitLossDto(closedSignal, currentPrice),
8444
- note: closedSignal.note,
8483
+ note: closedSignal.closeNote ?? closedSignal.note,
8445
8484
  });
8446
8485
  await CALL_CLOSE_CALLBACKS_FN(this, this.params.execution.context.symbol, closedSignal, currentPrice, closeTimestamp, this.params.execution.context.backtest);
8447
8486
  // КРИТИЧНО: Очищаем состояние ClientPartial при закрытии позиции
@@ -8625,7 +8664,8 @@ class ClientStrategy {
8625
8664
  * // Strategy continues, can generate new signals
8626
8665
  * ```
8627
8666
  */
8628
- async cancelScheduled(symbol, backtest, cancelId) {
8667
+ async cancelScheduled(symbol, backtest, payload) {
8668
+ const cancelId = payload.id;
8629
8669
  this.params.logger.debug("ClientStrategy cancelScheduled", {
8630
8670
  symbol,
8631
8671
  hasScheduledSignal: this._scheduledSignal !== null,
@@ -8637,6 +8677,7 @@ class ClientStrategy {
8637
8677
  if (this._scheduledSignal) {
8638
8678
  this._cancelledSignal = Object.assign({}, this._scheduledSignal, {
8639
8679
  cancelId,
8680
+ cancelNote: payload.note,
8640
8681
  });
8641
8682
  this._scheduledSignal = null;
8642
8683
  }
@@ -8668,7 +8709,8 @@ class ClientStrategy {
8668
8709
  * // Scheduled signal becomes pending signal immediately
8669
8710
  * ```
8670
8711
  */
8671
- async activateScheduled(symbol, backtest, activateId) {
8712
+ async activateScheduled(symbol, backtest, payload) {
8713
+ const activateId = payload.id;
8672
8714
  this.params.logger.debug("ClientStrategy activateScheduled", {
8673
8715
  symbol,
8674
8716
  hasScheduledSignal: this._scheduledSignal !== null,
@@ -8686,6 +8728,7 @@ class ClientStrategy {
8686
8728
  if (this._scheduledSignal) {
8687
8729
  this._activatedSignal = Object.assign({}, this._scheduledSignal, {
8688
8730
  activateId,
8731
+ activateNote: payload.note,
8689
8732
  });
8690
8733
  this._scheduledSignal = null;
8691
8734
  }
@@ -8717,7 +8760,8 @@ class ClientStrategy {
8717
8760
  * // Strategy continues, can generate new signals
8718
8761
  * ```
8719
8762
  */
8720
- async closePending(symbol, backtest, closeId) {
8763
+ async closePending(symbol, backtest, payload) {
8764
+ const closeId = payload.id;
8721
8765
  this.params.logger.debug("ClientStrategy closePending", {
8722
8766
  symbol,
8723
8767
  hasPendingSignal: this._pendingSignal !== null,
@@ -8728,6 +8772,7 @@ class ClientStrategy {
8728
8772
  if (this._pendingSignal) {
8729
8773
  this._closedSignal = Object.assign({}, this._pendingSignal, {
8730
8774
  closeId,
8775
+ closeNote: payload.note,
8731
8776
  });
8732
8777
  this._pendingSignal = null;
8733
8778
  }
@@ -11218,6 +11263,48 @@ class StrategyConnectionService {
11218
11263
  const currentPrice = await this.priceMetaService.getCurrentPrice(symbol, context, backtest);
11219
11264
  return await strategy.getPositionHighestMaxDrawdownPnlCost(symbol, currentPrice);
11220
11265
  };
11266
+ /**
11267
+ * Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
11268
+ *
11269
+ * Resolves current price via priceMetaService and delegates to
11270
+ * ClientStrategy.getMaxDrawdownDistancePnlPercentage().
11271
+ * Returns null if no pending signal exists.
11272
+ *
11273
+ * @param backtest - Whether running in backtest mode
11274
+ * @param symbol - Trading pair symbol
11275
+ * @param context - Execution context with strategyName, exchangeName, frameName
11276
+ * @returns Promise resolving to peak-to-trough PnL percentage distance (≥ 0) or null
11277
+ */
11278
+ this.getMaxDrawdownDistancePnlPercentage = async (backtest, symbol, context) => {
11279
+ this.loggerService.log("strategyConnectionService getMaxDrawdownDistancePnlPercentage", {
11280
+ symbol,
11281
+ context,
11282
+ });
11283
+ const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
11284
+ const currentPrice = await this.priceMetaService.getCurrentPrice(symbol, context, backtest);
11285
+ return await strategy.getMaxDrawdownDistancePnlPercentage(symbol, currentPrice);
11286
+ };
11287
+ /**
11288
+ * Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
11289
+ *
11290
+ * Resolves current price via priceMetaService and delegates to
11291
+ * ClientStrategy.getMaxDrawdownDistancePnlCost().
11292
+ * Returns null if no pending signal exists.
11293
+ *
11294
+ * @param backtest - Whether running in backtest mode
11295
+ * @param symbol - Trading pair symbol
11296
+ * @param context - Execution context with strategyName, exchangeName, frameName
11297
+ * @returns Promise resolving to peak-to-trough PnL cost distance (≥ 0) or null
11298
+ */
11299
+ this.getMaxDrawdownDistancePnlCost = async (backtest, symbol, context) => {
11300
+ this.loggerService.log("strategyConnectionService getMaxDrawdownDistancePnlCost", {
11301
+ symbol,
11302
+ context,
11303
+ });
11304
+ const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
11305
+ const currentPrice = await this.priceMetaService.getCurrentPrice(symbol, context, backtest);
11306
+ return await strategy.getMaxDrawdownDistancePnlCost(symbol, currentPrice);
11307
+ };
11221
11308
  /**
11222
11309
  * Disposes the ClientStrategy instance for the given context.
11223
11310
  *
@@ -11277,17 +11364,17 @@ class StrategyConnectionService {
11277
11364
  * @param backtest - Whether running in backtest mode
11278
11365
  * @param symbol - Trading pair symbol
11279
11366
  * @param ctx - Context with strategyName, exchangeName, frameName
11280
- * @param cancelId - Optional cancellation ID for user-initiated cancellations
11367
+ * @param payload - Optional commit payload with id and note
11281
11368
  * @returns Promise that resolves when scheduled signal is cancelled
11282
11369
  */
11283
- this.cancelScheduled = async (backtest, symbol, context, cancelId) => {
11370
+ this.cancelScheduled = async (backtest, symbol, context, payload = {}) => {
11284
11371
  this.loggerService.log("strategyConnectionService cancelScheduled", {
11285
11372
  symbol,
11286
11373
  context,
11287
- cancelId,
11374
+ payload,
11288
11375
  });
11289
11376
  const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
11290
- await strategy.cancelScheduled(symbol, backtest, cancelId);
11377
+ await strategy.cancelScheduled(symbol, backtest, payload);
11291
11378
  };
11292
11379
  /**
11293
11380
  * Closes the pending signal without stopping the strategy.
@@ -11302,17 +11389,17 @@ class StrategyConnectionService {
11302
11389
  * @param backtest - Whether running in backtest mode
11303
11390
  * @param symbol - Trading pair symbol
11304
11391
  * @param context - Context with strategyName, exchangeName, frameName
11305
- * @param closeId - Optional close ID for user-initiated closes
11392
+ * @param payload - Optional commit payload with id and note
11306
11393
  * @returns Promise that resolves when pending signal is closed
11307
11394
  */
11308
- this.closePending = async (backtest, symbol, context, closeId) => {
11395
+ this.closePending = async (backtest, symbol, context, payload = {}) => {
11309
11396
  this.loggerService.log("strategyConnectionService closePending", {
11310
11397
  symbol,
11311
11398
  context,
11312
- closeId,
11399
+ payload,
11313
11400
  });
11314
11401
  const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
11315
- await strategy.closePending(symbol, backtest, closeId);
11402
+ await strategy.closePending(symbol, backtest, payload);
11316
11403
  };
11317
11404
  /**
11318
11405
  * Checks whether `partialProfit` would succeed without executing it.
@@ -11591,7 +11678,7 @@ class StrategyConnectionService {
11591
11678
  * @param backtest - Whether running in backtest mode
11592
11679
  * @param symbol - Trading pair symbol
11593
11680
  * @param context - Execution context with strategyName, exchangeName, frameName
11594
- * @param activateId - Optional identifier for the activation reason
11681
+ * @param payload - Optional commit payload with id and note
11595
11682
  * @returns Promise that resolves when activation flag is set
11596
11683
  *
11597
11684
  * @example
@@ -11601,19 +11688,19 @@ class StrategyConnectionService {
11601
11688
  * false,
11602
11689
  * "BTCUSDT",
11603
11690
  * { strategyName: "my-strategy", exchangeName: "binance", frameName: "" },
11604
- * "manual-activation"
11691
+ * { id: "manual-activation" }
11605
11692
  * );
11606
11693
  * ```
11607
11694
  */
11608
- this.activateScheduled = async (backtest, symbol, context, activateId) => {
11695
+ this.activateScheduled = async (backtest, symbol, context, payload = {}) => {
11609
11696
  this.loggerService.log("strategyConnectionService activateScheduled", {
11610
11697
  symbol,
11611
11698
  context,
11612
11699
  backtest,
11613
- activateId,
11700
+ payload,
11614
11701
  });
11615
11702
  const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
11616
- return await strategy.activateScheduled(symbol, backtest, activateId);
11703
+ return await strategy.activateScheduled(symbol, backtest, payload);
11617
11704
  };
11618
11705
  /**
11619
11706
  * Checks whether `averageBuy` would succeed without executing it.
@@ -14648,18 +14735,18 @@ class StrategyCoreService {
14648
14735
  * @param backtest - Whether running in backtest mode
14649
14736
  * @param symbol - Trading pair symbol
14650
14737
  * @param ctx - Context with strategyName, exchangeName, frameName
14651
- * @param cancelId - Optional cancellation ID for user-initiated cancellations
14738
+ * @param payload - Optional commit payload with id and note
14652
14739
  * @returns Promise that resolves when scheduled signal is cancelled
14653
14740
  */
14654
- this.cancelScheduled = async (backtest, symbol, context, cancelId) => {
14741
+ this.cancelScheduled = async (backtest, symbol, context, payload = {}) => {
14655
14742
  this.loggerService.log("strategyCoreService cancelScheduled", {
14656
14743
  symbol,
14657
14744
  context,
14658
14745
  backtest,
14659
- cancelId,
14746
+ payload,
14660
14747
  });
14661
14748
  await this.validate(context);
14662
- return await this.strategyConnectionService.cancelScheduled(backtest, symbol, context, cancelId);
14749
+ return await this.strategyConnectionService.cancelScheduled(backtest, symbol, context, payload);
14663
14750
  };
14664
14751
  /**
14665
14752
  * Closes the pending signal without stopping the strategy.
@@ -14675,18 +14762,18 @@ class StrategyCoreService {
14675
14762
  * @param backtest - Whether running in backtest mode
14676
14763
  * @param symbol - Trading pair symbol
14677
14764
  * @param context - Context with strategyName, exchangeName, frameName
14678
- * @param closeId - Optional close ID for user-initiated closes
14765
+ * @param payload - Optional commit payload with id and note
14679
14766
  * @returns Promise that resolves when pending signal is closed
14680
14767
  */
14681
- this.closePending = async (backtest, symbol, context, closeId) => {
14768
+ this.closePending = async (backtest, symbol, context, payload = {}) => {
14682
14769
  this.loggerService.log("strategyCoreService closePending", {
14683
14770
  symbol,
14684
14771
  context,
14685
14772
  backtest,
14686
- closeId,
14773
+ payload,
14687
14774
  });
14688
14775
  await this.validate(context);
14689
- return await this.strategyConnectionService.closePending(backtest, symbol, context, closeId);
14776
+ return await this.strategyConnectionService.closePending(backtest, symbol, context, payload);
14690
14777
  };
14691
14778
  /**
14692
14779
  * Disposes the ClientStrategy instance for the given context.
@@ -15031,7 +15118,7 @@ class StrategyCoreService {
15031
15118
  * @param backtest - Whether running in backtest mode
15032
15119
  * @param symbol - Trading pair symbol
15033
15120
  * @param context - Execution context with strategyName, exchangeName, frameName
15034
- * @param activateId - Optional identifier for the activation reason
15121
+ * @param payload - Optional commit payload with id and note
15035
15122
  * @returns Promise that resolves when activation flag is set
15036
15123
  *
15037
15124
  * @example
@@ -15041,19 +15128,19 @@ class StrategyCoreService {
15041
15128
  * false,
15042
15129
  * "BTCUSDT",
15043
15130
  * { strategyName: "my-strategy", exchangeName: "binance", frameName: "" },
15044
- * "manual-activation"
15131
+ * { id: "manual-activation" }
15045
15132
  * );
15046
15133
  * ```
15047
15134
  */
15048
- this.activateScheduled = async (backtest, symbol, context, activateId) => {
15135
+ this.activateScheduled = async (backtest, symbol, context, payload = {}) => {
15049
15136
  this.loggerService.log("strategyCoreService activateScheduled", {
15050
15137
  symbol,
15051
15138
  context,
15052
15139
  backtest,
15053
- activateId,
15140
+ payload,
15054
15141
  });
15055
15142
  await this.validate(context);
15056
- return await this.strategyConnectionService.activateScheduled(backtest, symbol, context, activateId);
15143
+ return await this.strategyConnectionService.activateScheduled(backtest, symbol, context, payload);
15057
15144
  };
15058
15145
  /**
15059
15146
  * Checks whether `averageBuy` would succeed without executing it.
@@ -15455,6 +15542,44 @@ class StrategyCoreService {
15455
15542
  await this.validate(context);
15456
15543
  return await this.strategyConnectionService.getPositionHighestMaxDrawdownPnlCost(backtest, symbol, context);
15457
15544
  };
15545
+ /**
15546
+ * Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
15547
+ *
15548
+ * Delegates to StrategyConnectionService.getMaxDrawdownDistancePnlPercentage().
15549
+ * Returns null if no pending signal exists.
15550
+ *
15551
+ * @param backtest - Whether running in backtest mode
15552
+ * @param symbol - Trading pair symbol
15553
+ * @param context - Execution context with strategyName, exchangeName, frameName
15554
+ * @returns Promise resolving to peak-to-trough PnL percentage distance (≥ 0) or null
15555
+ */
15556
+ this.getMaxDrawdownDistancePnlPercentage = async (backtest, symbol, context) => {
15557
+ this.loggerService.log("strategyCoreService getMaxDrawdownDistancePnlPercentage", {
15558
+ symbol,
15559
+ context,
15560
+ });
15561
+ await this.validate(context);
15562
+ return await this.strategyConnectionService.getMaxDrawdownDistancePnlPercentage(backtest, symbol, context);
15563
+ };
15564
+ /**
15565
+ * Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
15566
+ *
15567
+ * Delegates to StrategyConnectionService.getMaxDrawdownDistancePnlCost().
15568
+ * Returns null if no pending signal exists.
15569
+ *
15570
+ * @param backtest - Whether running in backtest mode
15571
+ * @param symbol - Trading pair symbol
15572
+ * @param context - Execution context with strategyName, exchangeName, frameName
15573
+ * @returns Promise resolving to peak-to-trough PnL cost distance (≥ 0) or null
15574
+ */
15575
+ this.getMaxDrawdownDistancePnlCost = async (backtest, symbol, context) => {
15576
+ this.loggerService.log("strategyCoreService getMaxDrawdownDistancePnlCost", {
15577
+ symbol,
15578
+ context,
15579
+ });
15580
+ await this.validate(context);
15581
+ return await this.strategyConnectionService.getMaxDrawdownDistancePnlCost(backtest, symbol, context);
15582
+ };
15458
15583
  }
15459
15584
  }
15460
15585
 
@@ -29054,7 +29179,7 @@ class StrategyReportService {
29054
29179
  /**
29055
29180
  * Logs a cancel-scheduled event when a scheduled signal is cancelled.
29056
29181
  */
29057
- this.cancelScheduled = async (symbol, isBacktest, context, timestamp, signalId, pnl, totalPartials, cancelId) => {
29182
+ this.cancelScheduled = async (symbol, isBacktest, context, timestamp, signalId, pnl, totalPartials, cancelId, note) => {
29058
29183
  this.loggerService.log("strategyReportService cancelScheduled", {
29059
29184
  symbol,
29060
29185
  isBacktest,
@@ -29067,6 +29192,7 @@ class StrategyReportService {
29067
29192
  await ReportWriter.writeData("strategy", {
29068
29193
  action: "cancel-scheduled",
29069
29194
  cancelId,
29195
+ note,
29070
29196
  symbol,
29071
29197
  timestamp,
29072
29198
  createdAt,
@@ -29088,7 +29214,7 @@ class StrategyReportService {
29088
29214
  /**
29089
29215
  * Logs a close-pending event when a pending signal is closed.
29090
29216
  */
29091
- this.closePending = async (symbol, isBacktest, context, timestamp, signalId, pnl, totalPartials, closeId) => {
29217
+ this.closePending = async (symbol, isBacktest, context, timestamp, signalId, pnl, totalPartials, closeId, note) => {
29092
29218
  this.loggerService.log("strategyReportService closePending", {
29093
29219
  symbol,
29094
29220
  isBacktest,
@@ -29101,6 +29227,7 @@ class StrategyReportService {
29101
29227
  await ReportWriter.writeData("strategy", {
29102
29228
  action: "close-pending",
29103
29229
  closeId,
29230
+ note,
29104
29231
  symbol,
29105
29232
  timestamp,
29106
29233
  createdAt,
@@ -29350,7 +29477,7 @@ class StrategyReportService {
29350
29477
  /**
29351
29478
  * Logs an activate-scheduled event when a scheduled signal is activated early.
29352
29479
  */
29353
- this.activateScheduled = async (symbol, currentPrice, isBacktest, context, timestamp, signalId, pnl, totalPartials, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen, activateId) => {
29480
+ this.activateScheduled = async (symbol, currentPrice, isBacktest, context, timestamp, signalId, pnl, totalPartials, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen, activateId, note) => {
29354
29481
  this.loggerService.log("strategyReportService activateScheduled", {
29355
29482
  symbol,
29356
29483
  currentPrice,
@@ -29364,6 +29491,7 @@ class StrategyReportService {
29364
29491
  await ReportWriter.writeData("strategy", {
29365
29492
  action: "activate-scheduled",
29366
29493
  activateId,
29494
+ note,
29367
29495
  currentPrice,
29368
29496
  symbol,
29369
29497
  timestamp,
@@ -29457,14 +29585,14 @@ class StrategyReportService {
29457
29585
  exchangeName: event.exchangeName,
29458
29586
  frameName: event.frameName,
29459
29587
  strategyName: event.strategyName,
29460
- }, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.cancelId));
29588
+ }, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.cancelId, event.note));
29461
29589
  const unClosePending = strategyCommitSubject
29462
29590
  .filter(({ action }) => action === "close-pending")
29463
29591
  .connect(async (event) => await this.closePending(event.symbol, event.backtest, {
29464
29592
  exchangeName: event.exchangeName,
29465
29593
  frameName: event.frameName,
29466
29594
  strategyName: event.strategyName,
29467
- }, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.closeId));
29595
+ }, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.closeId, event.note));
29468
29596
  const unPartialProfit = strategyCommitSubject
29469
29597
  .filter(({ action }) => action === "partial-profit")
29470
29598
  .connect(async (event) => await this.partialProfit(event.symbol, event.percentToClose, event.currentPrice, event.backtest, {
@@ -29506,7 +29634,7 @@ class StrategyReportService {
29506
29634
  exchangeName: event.exchangeName,
29507
29635
  frameName: event.frameName,
29508
29636
  strategyName: event.strategyName,
29509
- }, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.totalEntries, event.originalPriceOpen, event.activateId));
29637
+ }, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.totalEntries, event.originalPriceOpen, event.activateId, event.note));
29510
29638
  const unAverageBuy = strategyCommitSubject
29511
29639
  .filter(({ action }) => action === "average-buy")
29512
29640
  .connect(async (event) => await this.averageBuy(event.symbol, event.currentPrice, event.effectivePriceOpen, event.totalEntries, event.backtest, {
@@ -30045,8 +30173,9 @@ class StrategyMarkdownService {
30045
30173
  * @param context - Strategy context with strategyName, exchangeName, frameName
30046
30174
  * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
30047
30175
  * @param cancelId - Optional identifier for the cancellation reason
30176
+ * @param note - Optional note from commit payload
30048
30177
  */
30049
- this.cancelScheduled = async (symbol, isBacktest, context, timestamp, signalId, pnl, cancelId) => {
30178
+ this.cancelScheduled = async (symbol, isBacktest, context, timestamp, signalId, pnl, cancelId, note) => {
30050
30179
  this.loggerService.log("strategyMarkdownService cancelScheduled", {
30051
30180
  symbol,
30052
30181
  isBacktest,
@@ -30067,6 +30196,7 @@ class StrategyMarkdownService {
30067
30196
  action: "cancel-scheduled",
30068
30197
  pnl,
30069
30198
  cancelId,
30199
+ note,
30070
30200
  createdAt,
30071
30201
  backtest: isBacktest,
30072
30202
  });
@@ -30079,8 +30209,9 @@ class StrategyMarkdownService {
30079
30209
  * @param context - Strategy context with strategyName, exchangeName, frameName
30080
30210
  * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
30081
30211
  * @param closeId - Optional identifier for the close reason
30212
+ * @param note - Optional note from commit payload
30082
30213
  */
30083
- this.closePending = async (symbol, isBacktest, context, timestamp, signalId, pnl, closeId) => {
30214
+ this.closePending = async (symbol, isBacktest, context, timestamp, signalId, pnl, closeId, note) => {
30084
30215
  this.loggerService.log("strategyMarkdownService closePending", {
30085
30216
  symbol,
30086
30217
  isBacktest,
@@ -30101,6 +30232,7 @@ class StrategyMarkdownService {
30101
30232
  action: "close-pending",
30102
30233
  pnl,
30103
30234
  closeId,
30235
+ note,
30104
30236
  createdAt,
30105
30237
  backtest: isBacktest,
30106
30238
  });
@@ -30399,8 +30531,9 @@ class StrategyMarkdownService {
30399
30531
  * @param scheduledAt - Signal creation timestamp in milliseconds
30400
30532
  * @param pendingAt - Pending timestamp in milliseconds
30401
30533
  * @param activateId - Optional identifier for the activation reason
30534
+ * @param note - Optional note from commit payload
30402
30535
  */
30403
- this.activateScheduled = async (symbol, currentPrice, isBacktest, context, timestamp, signalId, pnl, totalPartials, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen, activateId) => {
30536
+ this.activateScheduled = async (symbol, currentPrice, isBacktest, context, timestamp, signalId, pnl, totalPartials, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen, activateId, note) => {
30404
30537
  this.loggerService.log("strategyMarkdownService activateScheduled", {
30405
30538
  symbol,
30406
30539
  currentPrice,
@@ -30423,6 +30556,7 @@ class StrategyMarkdownService {
30423
30556
  pnl,
30424
30557
  totalPartials,
30425
30558
  activateId,
30559
+ note,
30426
30560
  currentPrice,
30427
30561
  createdAt,
30428
30562
  backtest: isBacktest,
@@ -30627,14 +30761,14 @@ class StrategyMarkdownService {
30627
30761
  exchangeName: event.exchangeName,
30628
30762
  frameName: event.frameName,
30629
30763
  strategyName: event.strategyName,
30630
- }, event.timestamp, event.signalId, event.pnl, event.cancelId));
30764
+ }, event.timestamp, event.signalId, event.pnl, event.cancelId, event.note));
30631
30765
  const unClosePending = strategyCommitSubject
30632
30766
  .filter(({ action }) => action === "close-pending")
30633
30767
  .connect(async (event) => await this.closePending(event.symbol, event.backtest, {
30634
30768
  exchangeName: event.exchangeName,
30635
30769
  frameName: event.frameName,
30636
30770
  strategyName: event.strategyName,
30637
- }, event.timestamp, event.signalId, event.pnl, event.closeId));
30771
+ }, event.timestamp, event.signalId, event.pnl, event.closeId, event.note));
30638
30772
  const unPartialProfit = strategyCommitSubject
30639
30773
  .filter(({ action }) => action === "partial-profit")
30640
30774
  .connect(async (event) => await this.partialProfit(event.symbol, event.percentToClose, event.currentPrice, event.backtest, {
@@ -30676,7 +30810,7 @@ class StrategyMarkdownService {
30676
30810
  exchangeName: event.exchangeName,
30677
30811
  frameName: event.frameName,
30678
30812
  strategyName: event.strategyName,
30679
- }, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.totalEntries, event.originalPriceOpen, event.activateId));
30813
+ }, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.totalEntries, event.originalPriceOpen, event.activateId, event.note));
30680
30814
  const unAverageBuy = strategyCommitSubject
30681
30815
  .filter(({ action }) => action === "average-buy")
30682
30816
  .connect(async (event) => await this.averageBuy(event.symbol, event.currentPrice, event.effectivePriceOpen, event.totalEntries, event.backtest, {
@@ -35161,6 +35295,8 @@ const GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE_METHOD_NAME = "strateg
35161
35295
  const GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST_METHOD_NAME = "strategy.getPositionHighestProfitDistancePnlCost";
35162
35296
  const GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE_METHOD_NAME = "strategy.getPositionHighestMaxDrawdownPnlPercentage";
35163
35297
  const GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST_METHOD_NAME = "strategy.getPositionHighestMaxDrawdownPnlCost";
35298
+ const GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE_METHOD_NAME = "strategy.getMaxDrawdownDistancePnlPercentage";
35299
+ const GET_MAX_DRAWDOWN_DISTANCE_PNL_COST_METHOD_NAME = "strategy.getMaxDrawdownDistancePnlCost";
35164
35300
  const GET_POSITION_ENTRY_OVERLAP_METHOD_NAME = "strategy.getPositionEntryOverlap";
35165
35301
  const GET_POSITION_PARTIAL_OVERLAP_METHOD_NAME = "strategy.getPositionPartialOverlap";
35166
35302
  const HAS_NO_PENDING_SIGNAL_METHOD_NAME = "strategy.hasNoPendingSignal";
@@ -35176,7 +35312,7 @@ const HAS_NO_SCHEDULED_SIGNAL_METHOD_NAME = "strategy.hasNoScheduledSignal";
35176
35312
  *
35177
35313
  * @param symbol - Trading pair symbol
35178
35314
  * @param strategyName - Strategy name
35179
- * @param cancelId - Optional cancellation ID for tracking user-initiated cancellations
35315
+ * @param payload - Optional commit payload with id and note
35180
35316
  * @returns Promise that resolves when scheduled signal is cancelled
35181
35317
  *
35182
35318
  * @example
@@ -35184,13 +35320,13 @@ const HAS_NO_SCHEDULED_SIGNAL_METHOD_NAME = "strategy.hasNoScheduledSignal";
35184
35320
  * import { commitCancelScheduled } from "backtest-kit";
35185
35321
  *
35186
35322
  * // Cancel scheduled signal with custom ID
35187
- * await commitCancelScheduled("BTCUSDT", "manual-cancel-001");
35323
+ * await commitCancelScheduled("BTCUSDT", { id: "manual-cancel-001" });
35188
35324
  * ```
35189
35325
  */
35190
- async function commitCancelScheduled(symbol, cancelId) {
35326
+ async function commitCancelScheduled(symbol, payload = {}) {
35191
35327
  backtest.loggerService.info(CANCEL_SCHEDULED_METHOD_NAME, {
35192
35328
  symbol,
35193
- cancelId,
35329
+ payload,
35194
35330
  });
35195
35331
  if (!ExecutionContextService.hasContext()) {
35196
35332
  throw new Error("commitCancelScheduled requires an execution context");
@@ -35200,7 +35336,7 @@ async function commitCancelScheduled(symbol, cancelId) {
35200
35336
  }
35201
35337
  const { backtest: isBacktest } = backtest.executionContextService.context;
35202
35338
  const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
35203
- await backtest.strategyCoreService.cancelScheduled(isBacktest, symbol, { exchangeName, frameName, strategyName }, cancelId);
35339
+ await backtest.strategyCoreService.cancelScheduled(isBacktest, symbol, { exchangeName, frameName, strategyName }, payload);
35204
35340
  }
35205
35341
  /**
35206
35342
  * Closes the pending signal without stopping the strategy.
@@ -35212,7 +35348,7 @@ async function commitCancelScheduled(symbol, cancelId) {
35212
35348
  * Automatically detects backtest/live mode from execution context.
35213
35349
  *
35214
35350
  * @param symbol - Trading pair symbol
35215
- * @param closeId - Optional close ID for tracking user-initiated closes
35351
+ * @param payload - Optional commit payload with id and note
35216
35352
  * @returns Promise that resolves when pending signal is closed
35217
35353
  *
35218
35354
  * @example
@@ -35220,13 +35356,13 @@ async function commitCancelScheduled(symbol, cancelId) {
35220
35356
  * import { commitClosePending } from "backtest-kit";
35221
35357
  *
35222
35358
  * // Close pending signal with custom ID
35223
- * await commitClosePending("BTCUSDT", "manual-close-001");
35359
+ * await commitClosePending("BTCUSDT", { id: "manual-close-001" });
35224
35360
  * ```
35225
35361
  */
35226
- async function commitClosePending(symbol, closeId) {
35362
+ async function commitClosePending(symbol, payload = {}) {
35227
35363
  backtest.loggerService.info(CLOSE_PENDING_METHOD_NAME, {
35228
35364
  symbol,
35229
- closeId,
35365
+ payload,
35230
35366
  });
35231
35367
  if (!ExecutionContextService.hasContext()) {
35232
35368
  throw new Error("commitClosePending requires an execution context");
@@ -35236,7 +35372,7 @@ async function commitClosePending(symbol, closeId) {
35236
35372
  }
35237
35373
  const { backtest: isBacktest } = backtest.executionContextService.context;
35238
35374
  const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
35239
- await backtest.strategyCoreService.closePending(isBacktest, symbol, { exchangeName, frameName, strategyName }, closeId);
35375
+ await backtest.strategyCoreService.closePending(isBacktest, symbol, { exchangeName, frameName, strategyName }, payload);
35240
35376
  }
35241
35377
  /**
35242
35378
  * Executes partial close at profit level (moving toward TP).
@@ -35699,7 +35835,7 @@ async function commitBreakeven(symbol) {
35699
35835
  * Automatically detects backtest/live mode from execution context.
35700
35836
  *
35701
35837
  * @param symbol - Trading pair symbol
35702
- * @param activateId - Optional activation ID for tracking user-initiated activations
35838
+ * @param payload - Optional commit payload with id and note
35703
35839
  * @returns Promise that resolves when activation flag is set
35704
35840
  *
35705
35841
  * @example
@@ -35707,13 +35843,13 @@ async function commitBreakeven(symbol) {
35707
35843
  * import { commitActivateScheduled } from "backtest-kit";
35708
35844
  *
35709
35845
  * // Activate scheduled signal early with custom ID
35710
- * await commitActivateScheduled("BTCUSDT", "manual-activate-001");
35846
+ * await commitActivateScheduled("BTCUSDT", { id: "manual-activate-001" });
35711
35847
  * ```
35712
35848
  */
35713
- async function commitActivateScheduled(symbol, activateId) {
35849
+ async function commitActivateScheduled(symbol, payload = {}) {
35714
35850
  backtest.loggerService.info(ACTIVATE_SCHEDULED_METHOD_NAME, {
35715
35851
  symbol,
35716
- activateId,
35852
+ payload,
35717
35853
  });
35718
35854
  if (!ExecutionContextService.hasContext()) {
35719
35855
  throw new Error("commitActivateScheduled requires an execution context");
@@ -35723,7 +35859,7 @@ async function commitActivateScheduled(symbol, activateId) {
35723
35859
  }
35724
35860
  const { backtest: isBacktest } = backtest.executionContextService.context;
35725
35861
  const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
35726
- await backtest.strategyCoreService.activateScheduled(isBacktest, symbol, { exchangeName, frameName, strategyName }, activateId);
35862
+ await backtest.strategyCoreService.activateScheduled(isBacktest, symbol, { exchangeName, frameName, strategyName }, payload);
35727
35863
  }
35728
35864
  /**
35729
35865
  * Adds a new DCA entry to the active pending signal.
@@ -36911,6 +37047,64 @@ async function getPositionHighestMaxDrawdownPnlCost(symbol) {
36911
37047
  const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
36912
37048
  return await backtest.strategyCoreService.getPositionHighestMaxDrawdownPnlCost(isBacktest, symbol, { exchangeName, frameName, strategyName });
36913
37049
  }
37050
+ /**
37051
+ * Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
37052
+ *
37053
+ * Computed as: max(0, peakPnlPercentage - fallPnlPercentage).
37054
+ * Returns null if no pending signal exists.
37055
+ *
37056
+ * @param symbol - Trading pair symbol
37057
+ * @returns Promise resolving to peak-to-trough PnL percentage distance (≥ 0) or null
37058
+ *
37059
+ * @example
37060
+ * ```typescript
37061
+ * import { getMaxDrawdownDistancePnlPercentage } from "backtest-kit";
37062
+ *
37063
+ * const dist = await getMaxDrawdownDistancePnlPercentage("BTCUSDT");
37064
+ * // e.g. 3.5 (peak was +3.5% above trough)
37065
+ * ```
37066
+ */
37067
+ async function getMaxDrawdownDistancePnlPercentage(symbol) {
37068
+ backtest.loggerService.info(GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE_METHOD_NAME, { symbol });
37069
+ if (!ExecutionContextService.hasContext()) {
37070
+ throw new Error("getMaxDrawdownDistancePnlPercentage requires an execution context");
37071
+ }
37072
+ if (!MethodContextService.hasContext()) {
37073
+ throw new Error("getMaxDrawdownDistancePnlPercentage requires a method context");
37074
+ }
37075
+ const { backtest: isBacktest } = backtest.executionContextService.context;
37076
+ const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
37077
+ return await backtest.strategyCoreService.getMaxDrawdownDistancePnlPercentage(isBacktest, symbol, { exchangeName, frameName, strategyName });
37078
+ }
37079
+ /**
37080
+ * Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
37081
+ *
37082
+ * Computed as: max(0, peakPnlCost - fallPnlCost).
37083
+ * Returns null if no pending signal exists.
37084
+ *
37085
+ * @param symbol - Trading pair symbol
37086
+ * @returns Promise resolving to peak-to-trough PnL cost distance (≥ 0) or null
37087
+ *
37088
+ * @example
37089
+ * ```typescript
37090
+ * import { getMaxDrawdownDistancePnlCost } from "backtest-kit";
37091
+ *
37092
+ * const dist = await getMaxDrawdownDistancePnlCost("BTCUSDT");
37093
+ * // e.g. 7.2 (peak was $7.2 above trough)
37094
+ * ```
37095
+ */
37096
+ async function getMaxDrawdownDistancePnlCost(symbol) {
37097
+ backtest.loggerService.info(GET_MAX_DRAWDOWN_DISTANCE_PNL_COST_METHOD_NAME, { symbol });
37098
+ if (!ExecutionContextService.hasContext()) {
37099
+ throw new Error("getMaxDrawdownDistancePnlCost requires an execution context");
37100
+ }
37101
+ if (!MethodContextService.hasContext()) {
37102
+ throw new Error("getMaxDrawdownDistancePnlCost requires a method context");
37103
+ }
37104
+ const { backtest: isBacktest } = backtest.executionContextService.context;
37105
+ const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
37106
+ return await backtest.strategyCoreService.getMaxDrawdownDistancePnlCost(isBacktest, symbol, { exchangeName, frameName, strategyName });
37107
+ }
36914
37108
  /**
36915
37109
  * Checks whether the current price falls within the tolerance zone of any existing DCA entry level.
36916
37110
  * Use this to prevent duplicate DCA entries at the same price area.
@@ -38584,6 +38778,8 @@ const BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE =
38584
38778
  const BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST = "BacktestUtils.getPositionHighestProfitDistancePnlCost";
38585
38779
  const BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE = "BacktestUtils.getPositionHighestMaxDrawdownPnlPercentage";
38586
38780
  const BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST = "BacktestUtils.getPositionHighestMaxDrawdownPnlCost";
38781
+ const BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE = "BacktestUtils.getMaxDrawdownDistancePnlPercentage";
38782
+ const BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST = "BacktestUtils.getMaxDrawdownDistancePnlCost";
38587
38783
  const BACKTEST_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP = "BacktestUtils.getPositionEntryOverlap";
38588
38784
  const BACKTEST_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP = "BacktestUtils.getPositionPartialOverlap";
38589
38785
  const BACKTEST_METHOD_NAME_BREAKEVEN = "Backtest.commitBreakeven";
@@ -39965,6 +40161,62 @@ class BacktestUtils {
39965
40161
  }
39966
40162
  return await backtest.strategyCoreService.getPositionHighestMaxDrawdownPnlCost(true, symbol, context);
39967
40163
  };
40164
+ /**
40165
+ * Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
40166
+ *
40167
+ * Computed as: max(0, peakPnlPercentage - fallPnlPercentage).
40168
+ * Returns null if no pending signal exists.
40169
+ *
40170
+ * @param symbol - Trading pair symbol
40171
+ * @param context - Execution context with strategyName, exchangeName, and frameName
40172
+ * @returns peak-to-trough PnL percentage distance (≥ 0) or null if no active position
40173
+ */
40174
+ this.getMaxDrawdownDistancePnlPercentage = async (symbol, context) => {
40175
+ backtest.loggerService.info(BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE, {
40176
+ symbol,
40177
+ context,
40178
+ });
40179
+ backtest.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
40180
+ backtest.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
40181
+ {
40182
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
40183
+ riskName &&
40184
+ backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
40185
+ riskList &&
40186
+ riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE));
40187
+ actions &&
40188
+ actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE));
40189
+ }
40190
+ return await backtest.strategyCoreService.getMaxDrawdownDistancePnlPercentage(true, symbol, context);
40191
+ };
40192
+ /**
40193
+ * Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
40194
+ *
40195
+ * Computed as: max(0, peakPnlCost - fallPnlCost).
40196
+ * Returns null if no pending signal exists.
40197
+ *
40198
+ * @param symbol - Trading pair symbol
40199
+ * @param context - Execution context with strategyName, exchangeName, and frameName
40200
+ * @returns peak-to-trough PnL cost distance (≥ 0) or null if no active position
40201
+ */
40202
+ this.getMaxDrawdownDistancePnlCost = async (symbol, context) => {
40203
+ backtest.loggerService.info(BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST, {
40204
+ symbol,
40205
+ context,
40206
+ });
40207
+ backtest.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
40208
+ backtest.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
40209
+ {
40210
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
40211
+ riskName &&
40212
+ backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
40213
+ riskList &&
40214
+ riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST));
40215
+ actions &&
40216
+ actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST));
40217
+ }
40218
+ return await backtest.strategyCoreService.getMaxDrawdownDistancePnlCost(true, symbol, context);
40219
+ };
39968
40220
  /**
39969
40221
  * Checks whether the current price falls within the tolerance zone of any existing DCA entry level.
39970
40222
  * Use this to prevent duplicate DCA entries at the same price area.
@@ -40099,7 +40351,7 @@ class BacktestUtils {
40099
40351
  * @param symbol - Trading pair symbol
40100
40352
  * @param strategyName - Strategy name
40101
40353
  * @param context - Execution context with exchangeName and frameName
40102
- * @param cancelId - Optional cancellation ID for tracking user-initiated cancellations
40354
+ * @param payload - Optional commit payload with id and note
40103
40355
  * @returns Promise that resolves when scheduled signal is cancelled
40104
40356
  *
40105
40357
  * @example
@@ -40109,14 +40361,14 @@ class BacktestUtils {
40109
40361
  * exchangeName: "binance",
40110
40362
  * frameName: "frame1",
40111
40363
  * strategyName: "my-strategy"
40112
- * }, "manual-cancel-001");
40364
+ * }, { id: "manual-cancel-001" });
40113
40365
  * ```
40114
40366
  */
40115
- this.commitCancelScheduled = async (symbol, context, cancelId) => {
40367
+ this.commitCancelScheduled = async (symbol, context, payload = {}) => {
40116
40368
  backtest.loggerService.info(BACKTEST_METHOD_NAME_CANCEL_SCHEDULED, {
40117
40369
  symbol,
40118
40370
  context,
40119
- cancelId,
40371
+ payload,
40120
40372
  });
40121
40373
  backtest.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_CANCEL_SCHEDULED);
40122
40374
  backtest.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_CANCEL_SCHEDULED);
@@ -40129,7 +40381,7 @@ class BacktestUtils {
40129
40381
  actions &&
40130
40382
  actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_CANCEL_SCHEDULED));
40131
40383
  }
40132
- await backtest.strategyCoreService.cancelScheduled(true, symbol, context, cancelId);
40384
+ await backtest.strategyCoreService.cancelScheduled(true, symbol, context, payload);
40133
40385
  };
40134
40386
  /**
40135
40387
  * Closes the pending signal without stopping the strategy.
@@ -40140,7 +40392,7 @@ class BacktestUtils {
40140
40392
  *
40141
40393
  * @param symbol - Trading pair symbol
40142
40394
  * @param context - Execution context with strategyName, exchangeName, and frameName
40143
- * @param closeId - Optional close ID for user-initiated closes
40395
+ * @param payload - Optional commit payload with id and note
40144
40396
  * @returns Promise that resolves when pending signal is closed
40145
40397
  *
40146
40398
  * @example
@@ -40150,14 +40402,14 @@ class BacktestUtils {
40150
40402
  * exchangeName: "binance",
40151
40403
  * strategyName: "my-strategy",
40152
40404
  * frameName: "1m"
40153
- * }, "manual-close-001");
40405
+ * }, { id: "manual-close-001" });
40154
40406
  * ```
40155
40407
  */
40156
- this.commitClosePending = async (symbol, context, closeId) => {
40408
+ this.commitClosePending = async (symbol, context, payload = {}) => {
40157
40409
  backtest.loggerService.info(BACKTEST_METHOD_NAME_CLOSE_PENDING, {
40158
40410
  symbol,
40159
40411
  context,
40160
- closeId,
40412
+ payload,
40161
40413
  });
40162
40414
  backtest.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_CLOSE_PENDING);
40163
40415
  backtest.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_CLOSE_PENDING);
@@ -40170,7 +40422,7 @@ class BacktestUtils {
40170
40422
  actions &&
40171
40423
  actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_CLOSE_PENDING));
40172
40424
  }
40173
- await backtest.strategyCoreService.closePending(true, symbol, context, closeId);
40425
+ await backtest.strategyCoreService.closePending(true, symbol, context, payload);
40174
40426
  };
40175
40427
  /**
40176
40428
  * Executes partial close at profit level (moving toward TP).
@@ -40806,7 +41058,7 @@ class BacktestUtils {
40806
41058
  *
40807
41059
  * @param symbol - Trading pair symbol
40808
41060
  * @param context - Execution context with strategyName, exchangeName, and frameName
40809
- * @param activateId - Optional activation ID for tracking user-initiated activations
41061
+ * @param payload - Optional commit payload with id and note
40810
41062
  * @returns Promise that resolves when activation flag is set
40811
41063
  *
40812
41064
  * @example
@@ -40816,14 +41068,14 @@ class BacktestUtils {
40816
41068
  * strategyName: "my-strategy",
40817
41069
  * exchangeName: "binance",
40818
41070
  * frameName: "1h"
40819
- * }, "manual-activate-001");
41071
+ * }, { id: "manual-activate-001" });
40820
41072
  * ```
40821
41073
  */
40822
- this.commitActivateScheduled = async (symbol, context, activateId) => {
41074
+ this.commitActivateScheduled = async (symbol, context, payload = {}) => {
40823
41075
  backtest.loggerService.info(BACKTEST_METHOD_NAME_ACTIVATE_SCHEDULED, {
40824
41076
  symbol,
40825
41077
  context,
40826
- activateId,
41078
+ payload,
40827
41079
  });
40828
41080
  backtest.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_ACTIVATE_SCHEDULED);
40829
41081
  backtest.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_ACTIVATE_SCHEDULED);
@@ -40836,7 +41088,7 @@ class BacktestUtils {
40836
41088
  actions &&
40837
41089
  actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_ACTIVATE_SCHEDULED));
40838
41090
  }
40839
- await backtest.strategyCoreService.activateScheduled(true, symbol, context, activateId);
41091
+ await backtest.strategyCoreService.activateScheduled(true, symbol, context, payload);
40840
41092
  };
40841
41093
  /**
40842
41094
  * Adds a new DCA entry to the active pending signal.
@@ -41094,6 +41346,8 @@ const LIVE_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE = "Li
41094
41346
  const LIVE_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST = "LiveUtils.getPositionHighestProfitDistancePnlCost";
41095
41347
  const LIVE_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE = "LiveUtils.getPositionHighestMaxDrawdownPnlPercentage";
41096
41348
  const LIVE_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST = "LiveUtils.getPositionHighestMaxDrawdownPnlCost";
41349
+ const LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE = "LiveUtils.getMaxDrawdownDistancePnlPercentage";
41350
+ const LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST = "LiveUtils.getMaxDrawdownDistancePnlCost";
41097
41351
  const LIVE_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP = "LiveUtils.getPositionEntryOverlap";
41098
41352
  const LIVE_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP = "LiveUtils.getPositionPartialOverlap";
41099
41353
  const LIVE_METHOD_NAME_BREAKEVEN = "Live.commitBreakeven";
@@ -42618,6 +42872,70 @@ class LiveUtils {
42618
42872
  frameName: "",
42619
42873
  });
42620
42874
  };
42875
+ /**
42876
+ * Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
42877
+ *
42878
+ * Computed as: max(0, peakPnlPercentage - fallPnlPercentage).
42879
+ * Returns null if no pending signal exists.
42880
+ *
42881
+ * @param symbol - Trading pair symbol
42882
+ * @param context - Execution context with strategyName and exchangeName
42883
+ * @returns peak-to-trough PnL percentage distance (≥ 0) or null if no active position
42884
+ */
42885
+ this.getMaxDrawdownDistancePnlPercentage = async (symbol, context) => {
42886
+ backtest.loggerService.info(LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE, {
42887
+ symbol,
42888
+ context,
42889
+ });
42890
+ backtest.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
42891
+ backtest.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
42892
+ {
42893
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
42894
+ riskName &&
42895
+ backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
42896
+ riskList &&
42897
+ riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE));
42898
+ actions &&
42899
+ actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE));
42900
+ }
42901
+ return await backtest.strategyCoreService.getMaxDrawdownDistancePnlPercentage(false, symbol, {
42902
+ strategyName: context.strategyName,
42903
+ exchangeName: context.exchangeName,
42904
+ frameName: "",
42905
+ });
42906
+ };
42907
+ /**
42908
+ * Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
42909
+ *
42910
+ * Computed as: max(0, peakPnlCost - fallPnlCost).
42911
+ * Returns null if no pending signal exists.
42912
+ *
42913
+ * @param symbol - Trading pair symbol
42914
+ * @param context - Execution context with strategyName and exchangeName
42915
+ * @returns peak-to-trough PnL cost distance (≥ 0) or null if no active position
42916
+ */
42917
+ this.getMaxDrawdownDistancePnlCost = async (symbol, context) => {
42918
+ backtest.loggerService.info(LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST, {
42919
+ symbol,
42920
+ context,
42921
+ });
42922
+ backtest.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
42923
+ backtest.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
42924
+ {
42925
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
42926
+ riskName &&
42927
+ backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
42928
+ riskList &&
42929
+ riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST));
42930
+ actions &&
42931
+ actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST));
42932
+ }
42933
+ return await backtest.strategyCoreService.getMaxDrawdownDistancePnlCost(false, symbol, {
42934
+ strategyName: context.strategyName,
42935
+ exchangeName: context.exchangeName,
42936
+ frameName: "",
42937
+ });
42938
+ };
42621
42939
  /**
42622
42940
  * Checks whether the current price falls within the tolerance zone of any existing DCA entry level.
42623
42941
  * Use this to prevent duplicate DCA entries at the same price area.
@@ -42759,7 +43077,7 @@ class LiveUtils {
42759
43077
  * @param symbol - Trading pair symbol
42760
43078
  * @param strategyName - Strategy name
42761
43079
  * @param context - Execution context with exchangeName and frameName
42762
- * @param cancelId - Optional cancellation ID for tracking user-initiated cancellations
43080
+ * @param payload - Optional commit payload with id and note
42763
43081
  * @returns Promise that resolves when scheduled signal is cancelled
42764
43082
  *
42765
43083
  * @example
@@ -42769,14 +43087,14 @@ class LiveUtils {
42769
43087
  * exchangeName: "binance",
42770
43088
  * frameName: "",
42771
43089
  * strategyName: "my-strategy"
42772
- * }, "manual-cancel-001");
43090
+ * }, { id: "manual-cancel-001" });
42773
43091
  * ```
42774
43092
  */
42775
- this.commitCancelScheduled = async (symbol, context, cancelId) => {
43093
+ this.commitCancelScheduled = async (symbol, context, payload = {}) => {
42776
43094
  backtest.loggerService.info(LIVE_METHOD_NAME_CANCEL_SCHEDULED, {
42777
43095
  symbol,
42778
43096
  context,
42779
- cancelId,
43097
+ payload,
42780
43098
  });
42781
43099
  backtest.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_CANCEL_SCHEDULED);
42782
43100
  backtest.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_CANCEL_SCHEDULED);
@@ -42793,7 +43111,7 @@ class LiveUtils {
42793
43111
  strategyName: context.strategyName,
42794
43112
  exchangeName: context.exchangeName,
42795
43113
  frameName: "",
42796
- }, cancelId);
43114
+ }, payload);
42797
43115
  };
42798
43116
  /**
42799
43117
  * Closes the pending signal without stopping the strategy.
@@ -42804,7 +43122,7 @@ class LiveUtils {
42804
43122
  *
42805
43123
  * @param symbol - Trading pair symbol
42806
43124
  * @param context - Execution context with strategyName and exchangeName
42807
- * @param closeId - Optional close ID for user-initiated closes
43125
+ * @param payload - Optional commit payload with id and note
42808
43126
  * @returns Promise that resolves when pending signal is closed
42809
43127
  *
42810
43128
  * @example
@@ -42813,14 +43131,14 @@ class LiveUtils {
42813
43131
  * await Live.commitClose("BTCUSDT", {
42814
43132
  * exchangeName: "binance",
42815
43133
  * strategyName: "my-strategy"
42816
- * }, "manual-close-001");
43134
+ * }, { id: "manual-close-001" });
42817
43135
  * ```
42818
43136
  */
42819
- this.commitClosePending = async (symbol, context, closeId) => {
43137
+ this.commitClosePending = async (symbol, context, payload = {}) => {
42820
43138
  backtest.loggerService.info(LIVE_METHOD_NAME_CLOSE_PENDING, {
42821
43139
  symbol,
42822
43140
  context,
42823
- closeId,
43141
+ payload,
42824
43142
  });
42825
43143
  backtest.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_CLOSE_PENDING);
42826
43144
  backtest.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_CLOSE_PENDING);
@@ -42837,7 +43155,7 @@ class LiveUtils {
42837
43155
  strategyName: context.strategyName,
42838
43156
  exchangeName: context.exchangeName,
42839
43157
  frameName: "",
42840
- }, closeId);
43158
+ }, payload);
42841
43159
  };
42842
43160
  /**
42843
43161
  * Executes partial close at profit level (moving toward TP).
@@ -43631,7 +43949,7 @@ class LiveUtils {
43631
43949
  *
43632
43950
  * @param symbol - Trading pair symbol
43633
43951
  * @param context - Execution context with strategyName and exchangeName
43634
- * @param activateId - Optional activation ID for tracking user-initiated activations
43952
+ * @param payload - Optional commit payload with id and note
43635
43953
  * @returns Promise that resolves when activation flag is set
43636
43954
  *
43637
43955
  * @example
@@ -43640,14 +43958,14 @@ class LiveUtils {
43640
43958
  * await Live.commitActivateScheduled("BTCUSDT", {
43641
43959
  * strategyName: "my-strategy",
43642
43960
  * exchangeName: "binance"
43643
- * }, "manual-activate-001");
43961
+ * }, { id: "manual-activate-001" });
43644
43962
  * ```
43645
43963
  */
43646
- this.commitActivateScheduled = async (symbol, context, activateId) => {
43964
+ this.commitActivateScheduled = async (symbol, context, payload = {}) => {
43647
43965
  backtest.loggerService.info(LIVE_METHOD_NAME_ACTIVATE_SCHEDULED, {
43648
43966
  symbol,
43649
43967
  context,
43650
- activateId,
43968
+ payload,
43651
43969
  });
43652
43970
  backtest.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_ACTIVATE_SCHEDULED);
43653
43971
  backtest.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_ACTIVATE_SCHEDULED);
@@ -43664,7 +43982,7 @@ class LiveUtils {
43664
43982
  strategyName: context.strategyName,
43665
43983
  exchangeName: context.exchangeName,
43666
43984
  frameName: "",
43667
- }, activateId);
43985
+ }, payload);
43668
43986
  };
43669
43987
  /**
43670
43988
  * Adds a new DCA entry to the active pending signal.
@@ -49735,6 +50053,741 @@ class MaxDrawdownUtils {
49735
50053
  */
49736
50054
  const MaxDrawdown = new MaxDrawdownUtils();
49737
50055
 
50056
+ const REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT = "ReflectUtils.getPositionPnlPercent";
50057
+ const REFLECT_METHOD_NAME_GET_POSITION_PNL_COST = "ReflectUtils.getPositionPnlCost";
50058
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE = "ReflectUtils.getPositionHighestProfitPrice";
50059
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP = "ReflectUtils.getPositionHighestProfitTimestamp";
50060
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE = "ReflectUtils.getPositionHighestPnlPercentage";
50061
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST = "ReflectUtils.getPositionHighestPnlCost";
50062
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN = "ReflectUtils.getPositionHighestProfitBreakeven";
50063
+ const REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES = "ReflectUtils.getPositionDrawdownMinutes";
50064
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES = "ReflectUtils.getPositionHighestProfitMinutes";
50065
+ const REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES = "ReflectUtils.getPositionMaxDrawdownMinutes";
50066
+ const REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE = "ReflectUtils.getPositionMaxDrawdownPrice";
50067
+ const REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP = "ReflectUtils.getPositionMaxDrawdownTimestamp";
50068
+ const REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE = "ReflectUtils.getPositionMaxDrawdownPnlPercentage";
50069
+ const REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST = "ReflectUtils.getPositionMaxDrawdownPnlCost";
50070
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE = "ReflectUtils.getPositionHighestProfitDistancePnlPercentage";
50071
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST = "ReflectUtils.getPositionHighestProfitDistancePnlCost";
50072
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE = "ReflectUtils.getPositionHighestMaxDrawdownPnlPercentage";
50073
+ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST = "ReflectUtils.getPositionHighestMaxDrawdownPnlCost";
50074
+ const REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE = "ReflectUtils.getMaxDrawdownDistancePnlPercentage";
50075
+ const REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST = "ReflectUtils.getMaxDrawdownDistancePnlCost";
50076
+ /**
50077
+ * Utility class for real-time position reflection: PNL, peak profit, and drawdown queries.
50078
+ *
50079
+ * Provides unified access to strategyCoreService position state methods with logging
50080
+ * and full validation (strategy, exchange, frame, risk, actions).
50081
+ * Works for both live and backtest modes via the `backtest` parameter.
50082
+ * Exported as singleton instance for convenient usage.
50083
+ *
50084
+ * @example
50085
+ * ```typescript
50086
+ * import { Reflect } from "backtest-kit";
50087
+ *
50088
+ * // Get current unrealized PNL percentage
50089
+ * const pnl = await Reflect.getPositionPnlPercent(
50090
+ * "BTCUSDT",
50091
+ * 45000,
50092
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50093
+ * );
50094
+ * console.log(`PNL: ${pnl}%`);
50095
+ *
50096
+ * // Get peak profit reached
50097
+ * const peakPnl = await Reflect.getPositionHighestPnlPercentage(
50098
+ * "BTCUSDT",
50099
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50100
+ * );
50101
+ * console.log(`Peak PNL: ${peakPnl}%`);
50102
+ * ```
50103
+ */
50104
+ class ReflectUtils {
50105
+ constructor() {
50106
+ /**
50107
+ * Returns the unrealized PNL percentage for the current pending signal at currentPrice.
50108
+ *
50109
+ * Accounts for partial closes, DCA entries, slippage and fees.
50110
+ * Returns null if no pending signal exists.
50111
+ *
50112
+ * @param symbol - Trading pair symbol
50113
+ * @param currentPrice - Current market price
50114
+ * @param context - Execution context with strategyName, exchangeName and frameName
50115
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50116
+ * @returns Promise resolving to PNL percentage or null
50117
+ *
50118
+ * @example
50119
+ * ```typescript
50120
+ * const pnl = await Reflect.getPositionPnlPercent(
50121
+ * "BTCUSDT",
50122
+ * 45000,
50123
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50124
+ * );
50125
+ * console.log(`PNL: ${pnl}%`);
50126
+ * ```
50127
+ */
50128
+ this.getPositionPnlPercent = async (symbol, currentPrice, context, backtest$1 = false) => {
50129
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT, { symbol, currentPrice, context });
50130
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT);
50131
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT);
50132
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT);
50133
+ {
50134
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50135
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT);
50136
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT));
50137
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT));
50138
+ }
50139
+ return await backtest.strategyCoreService.getPositionPnlPercent(backtest$1, symbol, currentPrice, context);
50140
+ };
50141
+ /**
50142
+ * Returns the unrealized PNL in dollars for the current pending signal at currentPrice.
50143
+ *
50144
+ * Calculated as: pnlPercentage / 100 × totalInvestedCost.
50145
+ * Accounts for partial closes, DCA entries, slippage and fees.
50146
+ * Returns null if no pending signal exists.
50147
+ *
50148
+ * @param symbol - Trading pair symbol
50149
+ * @param currentPrice - Current market price
50150
+ * @param context - Execution context with strategyName, exchangeName and frameName
50151
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50152
+ * @returns Promise resolving to PNL in dollars or null
50153
+ *
50154
+ * @example
50155
+ * ```typescript
50156
+ * const pnlCost = await Reflect.getPositionPnlCost(
50157
+ * "BTCUSDT",
50158
+ * 45000,
50159
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50160
+ * );
50161
+ * console.log(`PNL: $${pnlCost}`);
50162
+ * ```
50163
+ */
50164
+ this.getPositionPnlCost = async (symbol, currentPrice, context, backtest$1 = false) => {
50165
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_PNL_COST, { symbol, currentPrice, context });
50166
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_PNL_COST);
50167
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_PNL_COST);
50168
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_PNL_COST);
50169
+ {
50170
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50171
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_PNL_COST);
50172
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_PNL_COST));
50173
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_PNL_COST));
50174
+ }
50175
+ return await backtest.strategyCoreService.getPositionPnlCost(backtest$1, symbol, currentPrice, context);
50176
+ };
50177
+ /**
50178
+ * Returns the best price reached in the profit direction during this position's life.
50179
+ *
50180
+ * Returns null if no pending signal exists.
50181
+ *
50182
+ * @param symbol - Trading pair symbol
50183
+ * @param context - Execution context with strategyName, exchangeName and frameName
50184
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50185
+ * @returns Promise resolving to price or null
50186
+ *
50187
+ * @example
50188
+ * ```typescript
50189
+ * const peakPrice = await Reflect.getPositionHighestProfitPrice(
50190
+ * "BTCUSDT",
50191
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50192
+ * );
50193
+ * console.log(`Peak price: ${peakPrice}`);
50194
+ * ```
50195
+ */
50196
+ this.getPositionHighestProfitPrice = async (symbol, context, backtest$1 = false) => {
50197
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE, { symbol, context });
50198
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE);
50199
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE);
50200
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE);
50201
+ {
50202
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50203
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE);
50204
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE));
50205
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE));
50206
+ }
50207
+ return await backtest.strategyCoreService.getPositionHighestProfitPrice(backtest$1, symbol, context);
50208
+ };
50209
+ /**
50210
+ * Returns the timestamp when the best profit price was recorded during this position's life.
50211
+ *
50212
+ * Returns null if no pending signal exists.
50213
+ *
50214
+ * @param symbol - Trading pair symbol
50215
+ * @param context - Execution context with strategyName, exchangeName and frameName
50216
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50217
+ * @returns Promise resolving to timestamp in milliseconds or null
50218
+ *
50219
+ * @example
50220
+ * ```typescript
50221
+ * const ts = await Reflect.getPositionHighestProfitTimestamp(
50222
+ * "BTCUSDT",
50223
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50224
+ * );
50225
+ * console.log(`Peak at: ${new Date(ts).toISOString()}`);
50226
+ * ```
50227
+ */
50228
+ this.getPositionHighestProfitTimestamp = async (symbol, context, backtest$1 = false) => {
50229
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP, { symbol, context });
50230
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP);
50231
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP);
50232
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP);
50233
+ {
50234
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50235
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP);
50236
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP));
50237
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP));
50238
+ }
50239
+ return await backtest.strategyCoreService.getPositionHighestProfitTimestamp(backtest$1, symbol, context);
50240
+ };
50241
+ /**
50242
+ * Returns the PnL percentage at the moment the best profit price was recorded during this position's life.
50243
+ *
50244
+ * Returns null if no pending signal exists.
50245
+ *
50246
+ * @param symbol - Trading pair symbol
50247
+ * @param context - Execution context with strategyName, exchangeName and frameName
50248
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50249
+ * @returns Promise resolving to PnL percentage or null
50250
+ *
50251
+ * @example
50252
+ * ```typescript
50253
+ * const peakPnl = await Reflect.getPositionHighestPnlPercentage(
50254
+ * "BTCUSDT",
50255
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50256
+ * );
50257
+ * console.log(`Peak PNL: ${peakPnl}%`);
50258
+ * ```
50259
+ */
50260
+ this.getPositionHighestPnlPercentage = async (symbol, context, backtest$1 = false) => {
50261
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE, { symbol, context });
50262
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE);
50263
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE);
50264
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE);
50265
+ {
50266
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50267
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE);
50268
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE));
50269
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE));
50270
+ }
50271
+ return await backtest.strategyCoreService.getPositionHighestPnlPercentage(backtest$1, symbol, context);
50272
+ };
50273
+ /**
50274
+ * Returns the PnL cost (in quote currency) at the moment the best profit price was recorded during this position's life.
50275
+ *
50276
+ * Returns null if no pending signal exists.
50277
+ *
50278
+ * @param symbol - Trading pair symbol
50279
+ * @param context - Execution context with strategyName, exchangeName and frameName
50280
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50281
+ * @returns Promise resolving to PnL cost in quote currency or null
50282
+ *
50283
+ * @example
50284
+ * ```typescript
50285
+ * const peakCost = await Reflect.getPositionHighestPnlCost(
50286
+ * "BTCUSDT",
50287
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50288
+ * );
50289
+ * console.log(`Peak PNL: $${peakCost}`);
50290
+ * ```
50291
+ */
50292
+ this.getPositionHighestPnlCost = async (symbol, context, backtest$1 = false) => {
50293
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST, { symbol, context });
50294
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST);
50295
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST);
50296
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST);
50297
+ {
50298
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50299
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST);
50300
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST));
50301
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST));
50302
+ }
50303
+ return await backtest.strategyCoreService.getPositionHighestPnlCost(backtest$1, symbol, context);
50304
+ };
50305
+ /**
50306
+ * Returns whether breakeven was mathematically reachable at the highest profit price.
50307
+ *
50308
+ * Returns null if no pending signal exists.
50309
+ *
50310
+ * @param symbol - Trading pair symbol
50311
+ * @param context - Execution context with strategyName, exchangeName and frameName
50312
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50313
+ * @returns Promise resolving to true if breakeven was reachable at peak, false otherwise, or null
50314
+ *
50315
+ * @example
50316
+ * ```typescript
50317
+ * const wasReachable = await Reflect.getPositionHighestProfitBreakeven(
50318
+ * "BTCUSDT",
50319
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50320
+ * );
50321
+ * console.log(`Breakeven reachable at peak: ${wasReachable}`);
50322
+ * ```
50323
+ */
50324
+ this.getPositionHighestProfitBreakeven = async (symbol, context, backtest$1 = false) => {
50325
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN, { symbol, context });
50326
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN);
50327
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN);
50328
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN);
50329
+ {
50330
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50331
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN);
50332
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN));
50333
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN));
50334
+ }
50335
+ return await backtest.strategyCoreService.getPositionHighestProfitBreakeven(backtest$1, symbol, context);
50336
+ };
50337
+ /**
50338
+ * Returns the number of minutes elapsed since the highest profit price was recorded.
50339
+ *
50340
+ * Returns null if no pending signal exists.
50341
+ *
50342
+ * @param symbol - Trading pair symbol
50343
+ * @param context - Execution context with strategyName, exchangeName and frameName
50344
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50345
+ * @returns Promise resolving to minutes since highest profit price was recorded, or null
50346
+ *
50347
+ * @example
50348
+ * ```typescript
50349
+ * const minutes = await Reflect.getPositionDrawdownMinutes(
50350
+ * "BTCUSDT",
50351
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50352
+ * );
50353
+ * console.log(`Pulling back from peak for ${minutes} minutes`);
50354
+ * ```
50355
+ */
50356
+ this.getPositionDrawdownMinutes = async (symbol, context, backtest$1 = false) => {
50357
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES, { symbol, context });
50358
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES);
50359
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES);
50360
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES);
50361
+ {
50362
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50363
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES);
50364
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES));
50365
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES));
50366
+ }
50367
+ return await backtest.strategyCoreService.getPositionDrawdownMinutes(backtest$1, symbol, context);
50368
+ };
50369
+ /**
50370
+ * Returns the number of minutes elapsed since the highest profit price was recorded.
50371
+ *
50372
+ * Alias for getPositionDrawdownMinutes — measures how long the position has been
50373
+ * pulling back from its peak profit level.
50374
+ * Returns null if no pending signal exists.
50375
+ *
50376
+ * @param symbol - Trading pair symbol
50377
+ * @param context - Execution context with strategyName, exchangeName and frameName
50378
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50379
+ * @returns Promise resolving to minutes since last profit peak or null
50380
+ *
50381
+ * @example
50382
+ * ```typescript
50383
+ * const minutes = await Reflect.getPositionHighestProfitMinutes(
50384
+ * "BTCUSDT",
50385
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50386
+ * );
50387
+ * console.log(`Pulling back from peak for ${minutes} minutes`);
50388
+ * ```
50389
+ */
50390
+ this.getPositionHighestProfitMinutes = async (symbol, context, backtest$1 = false) => {
50391
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES, { symbol, context });
50392
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES);
50393
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES);
50394
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES);
50395
+ {
50396
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50397
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES);
50398
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES));
50399
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES));
50400
+ }
50401
+ return await backtest.strategyCoreService.getPositionHighestProfitMinutes(backtest$1, symbol, context);
50402
+ };
50403
+ /**
50404
+ * Returns the number of minutes elapsed since the worst loss price was recorded.
50405
+ *
50406
+ * Measures how long ago the deepest drawdown point occurred.
50407
+ * Zero when called at the exact moment the trough was set.
50408
+ * Returns null if no pending signal exists.
50409
+ *
50410
+ * @param symbol - Trading pair symbol
50411
+ * @param context - Execution context with strategyName, exchangeName and frameName
50412
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50413
+ * @returns Promise resolving to minutes since last drawdown trough or null
50414
+ *
50415
+ * @example
50416
+ * ```typescript
50417
+ * const minutes = await Reflect.getPositionMaxDrawdownMinutes(
50418
+ * "BTCUSDT",
50419
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50420
+ * );
50421
+ * console.log(`Drawdown trough was ${minutes} minutes ago`);
50422
+ * ```
50423
+ */
50424
+ this.getPositionMaxDrawdownMinutes = async (symbol, context, backtest$1 = false) => {
50425
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES, { symbol, context });
50426
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES);
50427
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES);
50428
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES);
50429
+ {
50430
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50431
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES);
50432
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES));
50433
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES));
50434
+ }
50435
+ return await backtest.strategyCoreService.getPositionMaxDrawdownMinutes(backtest$1, symbol, context);
50436
+ };
50437
+ /**
50438
+ * Returns the worst price reached in the loss direction during this position's life.
50439
+ *
50440
+ * Returns null if no pending signal exists.
50441
+ *
50442
+ * @param symbol - Trading pair symbol
50443
+ * @param context - Execution context with strategyName, exchangeName and frameName
50444
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50445
+ * @returns Promise resolving to price or null
50446
+ *
50447
+ * @example
50448
+ * ```typescript
50449
+ * const troughPrice = await Reflect.getPositionMaxDrawdownPrice(
50450
+ * "BTCUSDT",
50451
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50452
+ * );
50453
+ * console.log(`Worst price: ${troughPrice}`);
50454
+ * ```
50455
+ */
50456
+ this.getPositionMaxDrawdownPrice = async (symbol, context, backtest$1 = false) => {
50457
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE, { symbol, context });
50458
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE);
50459
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE);
50460
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE);
50461
+ {
50462
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50463
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE);
50464
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE));
50465
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE));
50466
+ }
50467
+ return await backtest.strategyCoreService.getPositionMaxDrawdownPrice(backtest$1, symbol, context);
50468
+ };
50469
+ /**
50470
+ * Returns the timestamp when the worst loss price was recorded during this position's life.
50471
+ *
50472
+ * Returns null if no pending signal exists.
50473
+ *
50474
+ * @param symbol - Trading pair symbol
50475
+ * @param context - Execution context with strategyName, exchangeName and frameName
50476
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50477
+ * @returns Promise resolving to timestamp in milliseconds or null
50478
+ *
50479
+ * @example
50480
+ * ```typescript
50481
+ * const ts = await Reflect.getPositionMaxDrawdownTimestamp(
50482
+ * "BTCUSDT",
50483
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50484
+ * );
50485
+ * console.log(`Worst drawdown at: ${new Date(ts).toISOString()}`);
50486
+ * ```
50487
+ */
50488
+ this.getPositionMaxDrawdownTimestamp = async (symbol, context, backtest$1 = false) => {
50489
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP, { symbol, context });
50490
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP);
50491
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP);
50492
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP);
50493
+ {
50494
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50495
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP);
50496
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP));
50497
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP));
50498
+ }
50499
+ return await backtest.strategyCoreService.getPositionMaxDrawdownTimestamp(backtest$1, symbol, context);
50500
+ };
50501
+ /**
50502
+ * Returns the PnL percentage at the moment the worst loss price was recorded during this position's life.
50503
+ *
50504
+ * Returns null if no pending signal exists.
50505
+ *
50506
+ * @param symbol - Trading pair symbol
50507
+ * @param context - Execution context with strategyName, exchangeName and frameName
50508
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50509
+ * @returns Promise resolving to PnL percentage or null
50510
+ *
50511
+ * @example
50512
+ * ```typescript
50513
+ * const worstPnl = await Reflect.getPositionMaxDrawdownPnlPercentage(
50514
+ * "BTCUSDT",
50515
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50516
+ * );
50517
+ * console.log(`Worst PNL: ${worstPnl}%`);
50518
+ * ```
50519
+ */
50520
+ this.getPositionMaxDrawdownPnlPercentage = async (symbol, context, backtest$1 = false) => {
50521
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE, { symbol, context });
50522
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE);
50523
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE);
50524
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE);
50525
+ {
50526
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50527
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE);
50528
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE));
50529
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE));
50530
+ }
50531
+ return await backtest.strategyCoreService.getPositionMaxDrawdownPnlPercentage(backtest$1, symbol, context);
50532
+ };
50533
+ /**
50534
+ * Returns the PnL cost (in quote currency) at the moment the worst loss price was recorded during this position's life.
50535
+ *
50536
+ * Returns null if no pending signal exists.
50537
+ *
50538
+ * @param symbol - Trading pair symbol
50539
+ * @param context - Execution context with strategyName, exchangeName and frameName
50540
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50541
+ * @returns Promise resolving to PnL cost in quote currency or null
50542
+ *
50543
+ * @example
50544
+ * ```typescript
50545
+ * const worstCost = await Reflect.getPositionMaxDrawdownPnlCost(
50546
+ * "BTCUSDT",
50547
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50548
+ * );
50549
+ * console.log(`Worst PNL: $${worstCost}`);
50550
+ * ```
50551
+ */
50552
+ this.getPositionMaxDrawdownPnlCost = async (symbol, context, backtest$1 = false) => {
50553
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST, { symbol, context });
50554
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST);
50555
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST);
50556
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST);
50557
+ {
50558
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50559
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST);
50560
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST));
50561
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST));
50562
+ }
50563
+ return await backtest.strategyCoreService.getPositionMaxDrawdownPnlCost(backtest$1, symbol, context);
50564
+ };
50565
+ /**
50566
+ * Returns the distance in PnL percentage between the current price and the highest profit peak.
50567
+ *
50568
+ * Result is ≥ 0. Returns null if no pending signal exists.
50569
+ *
50570
+ * @param symbol - Trading pair symbol
50571
+ * @param context - Execution context with strategyName, exchangeName and frameName
50572
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50573
+ * @returns Promise resolving to drawdown distance in PnL% (≥ 0) or null
50574
+ *
50575
+ * @example
50576
+ * ```typescript
50577
+ * const distance = await Reflect.getPositionHighestProfitDistancePnlPercentage(
50578
+ * "BTCUSDT",
50579
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50580
+ * );
50581
+ * console.log(`Dropped ${distance}% from peak`);
50582
+ * ```
50583
+ */
50584
+ this.getPositionHighestProfitDistancePnlPercentage = async (symbol, context, backtest$1 = false) => {
50585
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE, { symbol, context });
50586
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE);
50587
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE);
50588
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE);
50589
+ {
50590
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50591
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE);
50592
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE));
50593
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE));
50594
+ }
50595
+ return await backtest.strategyCoreService.getPositionHighestProfitDistancePnlPercentage(backtest$1, symbol, context);
50596
+ };
50597
+ /**
50598
+ * Returns the distance in PnL cost between the current price and the highest profit peak.
50599
+ *
50600
+ * Result is ≥ 0. Returns null if no pending signal exists.
50601
+ *
50602
+ * @param symbol - Trading pair symbol
50603
+ * @param context - Execution context with strategyName, exchangeName and frameName
50604
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50605
+ * @returns Promise resolving to drawdown distance in PnL cost (≥ 0) or null
50606
+ *
50607
+ * @example
50608
+ * ```typescript
50609
+ * const distance = await Reflect.getPositionHighestProfitDistancePnlCost(
50610
+ * "BTCUSDT",
50611
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50612
+ * );
50613
+ * console.log(`Dropped $${distance} from peak`);
50614
+ * ```
50615
+ */
50616
+ this.getPositionHighestProfitDistancePnlCost = async (symbol, context, backtest$1 = false) => {
50617
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST, { symbol, context });
50618
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST);
50619
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST);
50620
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST);
50621
+ {
50622
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50623
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST);
50624
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST));
50625
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST));
50626
+ }
50627
+ return await backtest.strategyCoreService.getPositionHighestProfitDistancePnlCost(backtest$1, symbol, context);
50628
+ };
50629
+ /**
50630
+ * Returns the distance in PnL percentage between the current price and the worst drawdown trough.
50631
+ *
50632
+ * Result is ≥ 0. Returns null if no pending signal exists.
50633
+ *
50634
+ * @param symbol - Trading pair symbol
50635
+ * @param context - Execution context with strategyName, exchangeName and frameName
50636
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50637
+ * @returns Promise resolving to recovery distance from worst drawdown trough in PnL% (≥ 0) or null
50638
+ *
50639
+ * @example
50640
+ * ```typescript
50641
+ * const distance = await Reflect.getPositionHighestMaxDrawdownPnlPercentage(
50642
+ * "BTCUSDT",
50643
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50644
+ * );
50645
+ * console.log(`${distance}% above worst trough`);
50646
+ * ```
50647
+ */
50648
+ this.getPositionHighestMaxDrawdownPnlPercentage = async (symbol, context, backtest$1 = false) => {
50649
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE, { symbol, context });
50650
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE);
50651
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE);
50652
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE);
50653
+ {
50654
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50655
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE);
50656
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE));
50657
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE));
50658
+ }
50659
+ return await backtest.strategyCoreService.getPositionHighestMaxDrawdownPnlPercentage(backtest$1, symbol, context);
50660
+ };
50661
+ /**
50662
+ * Returns the distance in PnL cost between the current price and the worst drawdown trough.
50663
+ *
50664
+ * Result is ≥ 0. Returns null if no pending signal exists.
50665
+ *
50666
+ * @param symbol - Trading pair symbol
50667
+ * @param context - Execution context with strategyName, exchangeName and frameName
50668
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50669
+ * @returns Promise resolving to recovery distance from worst drawdown trough in PnL cost (≥ 0) or null
50670
+ *
50671
+ * @example
50672
+ * ```typescript
50673
+ * const distance = await Reflect.getPositionHighestMaxDrawdownPnlCost(
50674
+ * "BTCUSDT",
50675
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50676
+ * );
50677
+ * console.log(`$${distance} above worst trough`);
50678
+ * ```
50679
+ */
50680
+ this.getPositionHighestMaxDrawdownPnlCost = async (symbol, context, backtest$1 = false) => {
50681
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST, { symbol, context });
50682
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST);
50683
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST);
50684
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST);
50685
+ {
50686
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50687
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST);
50688
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST));
50689
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST));
50690
+ }
50691
+ return await backtest.strategyCoreService.getPositionHighestMaxDrawdownPnlCost(backtest$1, symbol, context);
50692
+ };
50693
+ /**
50694
+ * Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
50695
+ *
50696
+ * Result is ≥ 0. Returns null if no pending signal exists.
50697
+ *
50698
+ * @param symbol - Trading pair symbol
50699
+ * @param context - Execution context with strategyName, exchangeName and frameName
50700
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50701
+ * @returns Promise resolving to peak-to-trough PnL percentage distance (≥ 0) or null
50702
+ *
50703
+ * @example
50704
+ * ```typescript
50705
+ * const distance = await Reflect.getMaxDrawdownDistancePnlPercentage(
50706
+ * "BTCUSDT",
50707
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50708
+ * );
50709
+ * console.log(`Peak-to-trough: ${distance}%`);
50710
+ * ```
50711
+ */
50712
+ this.getMaxDrawdownDistancePnlPercentage = async (symbol, context, backtest$1 = false) => {
50713
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE, { symbol, context });
50714
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
50715
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
50716
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
50717
+ {
50718
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50719
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
50720
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE));
50721
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE));
50722
+ }
50723
+ return await backtest.strategyCoreService.getMaxDrawdownDistancePnlPercentage(backtest$1, symbol, context);
50724
+ };
50725
+ /**
50726
+ * Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
50727
+ *
50728
+ * Result is ≥ 0. Returns null if no pending signal exists.
50729
+ *
50730
+ * @param symbol - Trading pair symbol
50731
+ * @param context - Execution context with strategyName, exchangeName and frameName
50732
+ * @param backtest - True if backtest mode, false if live mode (default: false)
50733
+ * @returns Promise resolving to peak-to-trough PnL cost distance (≥ 0) or null
50734
+ *
50735
+ * @example
50736
+ * ```typescript
50737
+ * const distance = await Reflect.getMaxDrawdownDistancePnlCost(
50738
+ * "BTCUSDT",
50739
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50740
+ * );
50741
+ * console.log(`Peak-to-trough: $${distance}`);
50742
+ * ```
50743
+ */
50744
+ this.getMaxDrawdownDistancePnlCost = async (symbol, context, backtest$1 = false) => {
50745
+ backtest.loggerService.info(REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST, { symbol, context });
50746
+ backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
50747
+ backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
50748
+ context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
50749
+ {
50750
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
50751
+ riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
50752
+ riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST));
50753
+ actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST));
50754
+ }
50755
+ return await backtest.strategyCoreService.getMaxDrawdownDistancePnlCost(backtest$1, symbol, context);
50756
+ };
50757
+ }
50758
+ }
50759
+ /**
50760
+ * Singleton instance of ReflectUtils for convenient position state queries.
50761
+ *
50762
+ * @example
50763
+ * ```typescript
50764
+ * import { Reflect } from "backtest-kit";
50765
+ *
50766
+ * // Real-time PNL
50767
+ * const pnl = await Reflect.getPositionPnlPercent(
50768
+ * "BTCUSDT",
50769
+ * 45000,
50770
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50771
+ * );
50772
+ * console.log(`PNL: ${pnl}%`);
50773
+ *
50774
+ * // Peak profit
50775
+ * const peakPnl = await Reflect.getPositionHighestPnlPercentage(
50776
+ * "BTCUSDT",
50777
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50778
+ * );
50779
+ * console.log(`Peak PNL: ${peakPnl}%`);
50780
+ *
50781
+ * // Drawdown from peak
50782
+ * const drawdown = await Reflect.getPositionHighestProfitDistancePnlPercentage(
50783
+ * "BTCUSDT",
50784
+ * { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
50785
+ * );
50786
+ * console.log(`Dropped ${drawdown}% from peak`);
50787
+ * ```
50788
+ */
50789
+ const Reflect$1 = new ReflectUtils();
50790
+
49738
50791
  /**
49739
50792
  * Utility class containing predefined trading constants for take-profit and stop-loss levels.
49740
50793
  *
@@ -55802,4 +56855,4 @@ const validateSignal = (signal, currentPrice) => {
55802
56855
  return !errors.length;
55803
56856
  };
55804
56857
 
55805
- export { ActionBase, Backtest, Breakeven, Broker, BrokerBase, Cache, Constant, Dump, Exchange, ExecutionContextService, Heat, HighestProfit, Interval, Live, Log, Markdown, MarkdownFileBase, MarkdownFolderBase, MarkdownWriter, MaxDrawdown, Memory, MethodContextService, Notification, NotificationBacktest, NotificationLive, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistCandleAdapter, PersistIntervalAdapter, PersistLogAdapter, PersistMeasureAdapter, PersistMemoryAdapter, PersistNotificationAdapter, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PersistStorageAdapter, Position, PositionSize, Report, ReportBase, ReportWriter, Risk, Schedule, Storage, StorageBacktest, StorageLive, Strategy, Sync, Walker, addActionSchema, addExchangeSchema, addFrameSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, alignToInterval, checkCandles, commitActivateScheduled, commitAverageBuy, commitBreakeven, commitCancelScheduled, commitClosePending, commitPartialLoss, commitPartialLossCost, commitPartialProfit, commitPartialProfitCost, commitTrailingStop, commitTrailingStopCost, commitTrailingTake, commitTrailingTakeCost, dumpAgentAnswer, dumpError, dumpJson, dumpRecord, dumpTable, dumpText, emitters, formatPrice, formatQuantity, get, getActionSchema, getAggregatedTrades, getAveragePrice, getBacktestTimeframe, getBreakeven, getCandles, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getEffectivePriceOpen, getExchangeSchema, getFrameSchema, getMode, getNextCandles, getOrderBook, getPendingSignal, getPositionCountdownMinutes, getPositionDrawdownMinutes, getPositionEffectivePrice, getPositionEntries, getPositionEntryOverlap, getPositionEstimateMinutes, getPositionHighestMaxDrawdownPnlCost, getPositionHighestMaxDrawdownPnlPercentage, getPositionHighestPnlCost, getPositionHighestPnlPercentage, getPositionHighestProfitBreakeven, getPositionHighestProfitDistancePnlCost, getPositionHighestProfitDistancePnlPercentage, getPositionHighestProfitMinutes, getPositionHighestProfitPrice, getPositionHighestProfitTimestamp, getPositionInvestedCost, getPositionInvestedCount, getPositionLevels, getPositionMaxDrawdownMinutes, getPositionMaxDrawdownPnlCost, getPositionMaxDrawdownPnlPercentage, getPositionMaxDrawdownPrice, getPositionMaxDrawdownTimestamp, getPositionPartialOverlap, getPositionPartials, getPositionPnlCost, getPositionPnlPercent, getRawCandles, getRiskSchema, getScheduledSignal, getSizingSchema, getStrategySchema, getSymbol, getTimestamp, getTotalClosed, getTotalCostClosed, getTotalPercentClosed, getWalkerSchema, hasNoPendingSignal, hasNoScheduledSignal, hasTradeContext, investedCostToPercent, backtest as lib, listExchangeSchema, listFrameSchema, listMemory, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenBacktestProgress, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenHighestProfit, listenHighestProfitOnce, listenMaxDrawdown, listenMaxDrawdownOnce, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenStrategyCommit, listenStrategyCommitOnce, listenSync, listenSyncOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideActionSchema, overrideExchangeSchema, overrideFrameSchema, overrideRiskSchema, overrideSizingSchema, overrideStrategySchema, overrideWalkerSchema, parseArgs, percentDiff, percentToCloseCost, percentValue, readMemory, removeMemory, roundTicks, runInMockContext, searchMemory, set, setColumns, setConfig, setLogger, shutdown, slPercentShiftToPrice, slPriceToPercentShift, stopStrategy, toProfitLossDto, tpPercentShiftToPrice, tpPriceToPercentShift, validate, validateCommonSignal, validatePendingSignal, validateScheduledSignal, validateSignal, waitForCandle, warmCandles, writeMemory };
56858
+ export { ActionBase, Backtest, Breakeven, Broker, BrokerBase, Cache, Constant, Dump, Exchange, ExecutionContextService, Heat, HighestProfit, Interval, Live, Log, Markdown, MarkdownFileBase, MarkdownFolderBase, MarkdownWriter, MaxDrawdown, Memory, MethodContextService, Notification, NotificationBacktest, NotificationLive, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistCandleAdapter, PersistIntervalAdapter, PersistLogAdapter, PersistMeasureAdapter, PersistMemoryAdapter, PersistNotificationAdapter, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PersistStorageAdapter, Position, PositionSize, Reflect$1 as Reflect, Report, ReportBase, ReportWriter, Risk, Schedule, Storage, StorageBacktest, StorageLive, Strategy, Sync, Walker, addActionSchema, addExchangeSchema, addFrameSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, alignToInterval, checkCandles, commitActivateScheduled, commitAverageBuy, commitBreakeven, commitCancelScheduled, commitClosePending, commitPartialLoss, commitPartialLossCost, commitPartialProfit, commitPartialProfitCost, commitTrailingStop, commitTrailingStopCost, commitTrailingTake, commitTrailingTakeCost, dumpAgentAnswer, dumpError, dumpJson, dumpRecord, dumpTable, dumpText, emitters, formatPrice, formatQuantity, get, getActionSchema, getAggregatedTrades, getAveragePrice, getBacktestTimeframe, getBreakeven, getCandles, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getEffectivePriceOpen, getExchangeSchema, getFrameSchema, getMaxDrawdownDistancePnlCost, getMaxDrawdownDistancePnlPercentage, getMode, getNextCandles, getOrderBook, getPendingSignal, getPositionCountdownMinutes, getPositionDrawdownMinutes, getPositionEffectivePrice, getPositionEntries, getPositionEntryOverlap, getPositionEstimateMinutes, getPositionHighestMaxDrawdownPnlCost, getPositionHighestMaxDrawdownPnlPercentage, getPositionHighestPnlCost, getPositionHighestPnlPercentage, getPositionHighestProfitBreakeven, getPositionHighestProfitDistancePnlCost, getPositionHighestProfitDistancePnlPercentage, getPositionHighestProfitMinutes, getPositionHighestProfitPrice, getPositionHighestProfitTimestamp, getPositionInvestedCost, getPositionInvestedCount, getPositionLevels, getPositionMaxDrawdownMinutes, getPositionMaxDrawdownPnlCost, getPositionMaxDrawdownPnlPercentage, getPositionMaxDrawdownPrice, getPositionMaxDrawdownTimestamp, getPositionPartialOverlap, getPositionPartials, getPositionPnlCost, getPositionPnlPercent, getRawCandles, getRiskSchema, getScheduledSignal, getSizingSchema, getStrategySchema, getSymbol, getTimestamp, getTotalClosed, getTotalCostClosed, getTotalPercentClosed, getWalkerSchema, hasNoPendingSignal, hasNoScheduledSignal, hasTradeContext, investedCostToPercent, backtest as lib, listExchangeSchema, listFrameSchema, listMemory, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenBacktestProgress, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenHighestProfit, listenHighestProfitOnce, listenMaxDrawdown, listenMaxDrawdownOnce, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenStrategyCommit, listenStrategyCommitOnce, listenSync, listenSyncOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideActionSchema, overrideExchangeSchema, overrideFrameSchema, overrideRiskSchema, overrideSizingSchema, overrideStrategySchema, overrideWalkerSchema, parseArgs, percentDiff, percentToCloseCost, percentValue, readMemory, removeMemory, roundTicks, runInMockContext, searchMemory, set, setColumns, setConfig, setLogger, shutdown, slPercentShiftToPrice, slPriceToPercentShift, stopStrategy, toProfitLossDto, tpPercentShiftToPrice, tpPriceToPercentShift, validate, validateCommonSignal, validatePendingSignal, validateScheduledSignal, validateSignal, waitForCandle, warmCandles, writeMemory };