backtest-kit 6.9.0 → 6.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.cjs CHANGED
@@ -7990,6 +7990,90 @@ class ClientStrategy {
7990
7990
  }
7991
7991
  return this._pendingSignal._fall.pnlCost;
7992
7992
  }
7993
+ /**
7994
+ * Returns the distance in PnL percentage between the current price and the highest profit peak.
7995
+ *
7996
+ * Measures how much PnL% the position has given back from its best point.
7997
+ * Computed as: max(0, peakPnlPercentage - currentPnlPercentage).
7998
+ * Zero when called at the exact moment the peak was set, or when current PnL >= peak PnL.
7999
+ *
8000
+ * Returns null if no pending signal exists.
8001
+ *
8002
+ * @param symbol - Trading pair symbol
8003
+ * @param currentPrice - Current market price
8004
+ * @returns Promise resolving to drawdown distance in PnL% (≥ 0) or null
8005
+ */
8006
+ async getPositionHighestProfitDistancePnlPercentage(symbol, currentPrice) {
8007
+ this.params.logger.debug("ClientStrategy getPositionHighestProfitDistancePnlPercentage", { symbol, currentPrice });
8008
+ if (!this._pendingSignal) {
8009
+ return null;
8010
+ }
8011
+ const currentPnl = toProfitLossDto(this._pendingSignal, currentPrice);
8012
+ return Math.max(0, this._pendingSignal._peak.pnlPercentage - currentPnl.pnlPercentage);
8013
+ }
8014
+ /**
8015
+ * Returns the distance in PnL cost between the current price and the highest profit peak.
8016
+ *
8017
+ * Measures how much PnL cost the position has given back from its best point.
8018
+ * Computed as: max(0, peakPnlCost - currentPnlCost).
8019
+ * Zero when called at the exact moment the peak was set, or when current PnL >= peak PnL.
8020
+ *
8021
+ * Returns null if no pending signal exists.
8022
+ *
8023
+ * @param symbol - Trading pair symbol
8024
+ * @param currentPrice - Current market price
8025
+ * @returns Promise resolving to drawdown distance in PnL cost (≥ 0) or null
8026
+ */
8027
+ async getPositionHighestProfitDistancePnlCost(symbol, currentPrice) {
8028
+ this.params.logger.debug("ClientStrategy getPositionHighestProfitDistancePnlCost", { symbol, currentPrice });
8029
+ if (!this._pendingSignal) {
8030
+ return null;
8031
+ }
8032
+ const currentPnl = toProfitLossDto(this._pendingSignal, currentPrice);
8033
+ return Math.max(0, this._pendingSignal._peak.pnlCost - currentPnl.pnlCost);
8034
+ }
8035
+ /**
8036
+ * Returns the distance in PnL percentage between the current price and the worst drawdown trough.
8037
+ *
8038
+ * Measures how much the position has recovered from its deepest loss point.
8039
+ * Computed as: max(0, currentPnlPercentage - fallPnlPercentage).
8040
+ * Zero when called at the exact moment the trough was set, or when current PnL <= trough PnL.
8041
+ *
8042
+ * Returns null if no pending signal exists.
8043
+ *
8044
+ * @param symbol - Trading pair symbol
8045
+ * @param currentPrice - Current market price
8046
+ * @returns Promise resolving to recovery distance in PnL% (≥ 0) or null
8047
+ */
8048
+ async getPositionHighestMaxDrawdownPnlPercentage(symbol, currentPrice) {
8049
+ this.params.logger.debug("ClientStrategy getPositionHighestMaxDrawdownPnlPercentage", { symbol, currentPrice });
8050
+ if (!this._pendingSignal) {
8051
+ return null;
8052
+ }
8053
+ const currentPnl = toProfitLossDto(this._pendingSignal, currentPrice);
8054
+ return Math.max(0, currentPnl.pnlPercentage - this._pendingSignal._fall.pnlPercentage);
8055
+ }
8056
+ /**
8057
+ * Returns the distance in PnL cost between the current price and the worst drawdown trough.
8058
+ *
8059
+ * Measures how much the position has recovered from its deepest loss point.
8060
+ * Computed as: max(0, currentPnlCost - fallPnlCost).
8061
+ * Zero when called at the exact moment the trough was set, or when current PnL <= trough PnL.
8062
+ *
8063
+ * Returns null if no pending signal exists.
8064
+ *
8065
+ * @param symbol - Trading pair symbol
8066
+ * @param currentPrice - Current market price
8067
+ * @returns Promise resolving to recovery distance in PnL cost (≥ 0) or null
8068
+ */
8069
+ async getPositionHighestMaxDrawdownPnlCost(symbol, currentPrice) {
8070
+ this.params.logger.debug("ClientStrategy getPositionHighestMaxDrawdownPnlCost", { symbol, currentPrice });
8071
+ if (!this._pendingSignal) {
8072
+ return null;
8073
+ }
8074
+ const currentPnl = toProfitLossDto(this._pendingSignal, currentPrice);
8075
+ return Math.max(0, currentPnl.pnlCost - this._pendingSignal._fall.pnlCost);
8076
+ }
7993
8077
  /**
7994
8078
  * Performs a single tick of strategy execution.
7995
8079
  *
@@ -11097,6 +11181,90 @@ class StrategyConnectionService {
11097
11181
  const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
11098
11182
  return await strategy.getPositionMaxDrawdownPnlCost(symbol);
11099
11183
  };
11184
+ /**
11185
+ * Returns the distance in PnL percentage between the current price and the highest profit peak.
11186
+ *
11187
+ * Resolves current price via priceMetaService and delegates to
11188
+ * ClientStrategy.getPositionHighestProfitDistancePnlPercentage().
11189
+ * Returns null if no pending signal exists.
11190
+ *
11191
+ * @param backtest - Whether running in backtest mode
11192
+ * @param symbol - Trading pair symbol
11193
+ * @param context - Execution context with strategyName, exchangeName, frameName
11194
+ * @returns Promise resolving to drawdown distance in PnL% (≥ 0) or null
11195
+ */
11196
+ this.getPositionHighestProfitDistancePnlPercentage = async (backtest, symbol, context) => {
11197
+ this.loggerService.log("strategyConnectionService getPositionHighestProfitDistancePnlPercentage", {
11198
+ symbol,
11199
+ context,
11200
+ });
11201
+ const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
11202
+ const currentPrice = await this.priceMetaService.getCurrentPrice(symbol, context, backtest);
11203
+ return await strategy.getPositionHighestProfitDistancePnlPercentage(symbol, currentPrice);
11204
+ };
11205
+ /**
11206
+ * Returns the distance in PnL cost between the current price and the highest profit peak.
11207
+ *
11208
+ * Resolves current price via priceMetaService and delegates to
11209
+ * ClientStrategy.getPositionHighestProfitDistancePnlCost().
11210
+ * Returns null if no pending signal exists.
11211
+ *
11212
+ * @param backtest - Whether running in backtest mode
11213
+ * @param symbol - Trading pair symbol
11214
+ * @param context - Execution context with strategyName, exchangeName, frameName
11215
+ * @returns Promise resolving to drawdown distance in PnL cost (≥ 0) or null
11216
+ */
11217
+ this.getPositionHighestProfitDistancePnlCost = async (backtest, symbol, context) => {
11218
+ this.loggerService.log("strategyConnectionService getPositionHighestProfitDistancePnlCost", {
11219
+ symbol,
11220
+ context,
11221
+ });
11222
+ const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
11223
+ const currentPrice = await this.priceMetaService.getCurrentPrice(symbol, context, backtest);
11224
+ return await strategy.getPositionHighestProfitDistancePnlCost(symbol, currentPrice);
11225
+ };
11226
+ /**
11227
+ * Returns the distance in PnL percentage between the current price and the worst drawdown trough.
11228
+ *
11229
+ * Resolves current price via priceMetaService and delegates to
11230
+ * ClientStrategy.getPositionHighestMaxDrawdownPnlPercentage().
11231
+ * Returns null if no pending signal exists.
11232
+ *
11233
+ * @param backtest - Whether running in backtest mode
11234
+ * @param symbol - Trading pair symbol
11235
+ * @param context - Execution context with strategyName, exchangeName, frameName
11236
+ * @returns Promise resolving to recovery distance in PnL% (≥ 0) or null
11237
+ */
11238
+ this.getPositionHighestMaxDrawdownPnlPercentage = async (backtest, symbol, context) => {
11239
+ this.loggerService.log("strategyConnectionService getPositionHighestMaxDrawdownPnlPercentage", {
11240
+ symbol,
11241
+ context,
11242
+ });
11243
+ const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
11244
+ const currentPrice = await this.priceMetaService.getCurrentPrice(symbol, context, backtest);
11245
+ return await strategy.getPositionHighestMaxDrawdownPnlPercentage(symbol, currentPrice);
11246
+ };
11247
+ /**
11248
+ * Returns the distance in PnL cost between the current price and the worst drawdown trough.
11249
+ *
11250
+ * Resolves current price via priceMetaService and delegates to
11251
+ * ClientStrategy.getPositionHighestMaxDrawdownPnlCost().
11252
+ * Returns null if no pending signal exists.
11253
+ *
11254
+ * @param backtest - Whether running in backtest mode
11255
+ * @param symbol - Trading pair symbol
11256
+ * @param context - Execution context with strategyName, exchangeName, frameName
11257
+ * @returns Promise resolving to recovery distance in PnL cost (≥ 0) or null
11258
+ */
11259
+ this.getPositionHighestMaxDrawdownPnlCost = async (backtest, symbol, context) => {
11260
+ this.loggerService.log("strategyConnectionService getPositionHighestMaxDrawdownPnlCost", {
11261
+ symbol,
11262
+ context,
11263
+ });
11264
+ const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
11265
+ const currentPrice = await this.priceMetaService.getCurrentPrice(symbol, context, backtest);
11266
+ return await strategy.getPositionHighestMaxDrawdownPnlCost(symbol, currentPrice);
11267
+ };
11100
11268
  /**
11101
11269
  * Disposes the ClientStrategy instance for the given context.
11102
11270
  *
@@ -15258,6 +15426,82 @@ class StrategyCoreService {
15258
15426
  await this.validate(context);
15259
15427
  return await this.strategyConnectionService.getPositionMaxDrawdownPnlCost(backtest, symbol, context);
15260
15428
  };
15429
+ /**
15430
+ * Returns the distance in PnL percentage between the current price and the highest profit peak.
15431
+ *
15432
+ * Delegates to StrategyConnectionService.getPositionHighestProfitDistancePnlPercentage().
15433
+ * Returns null if no pending signal exists.
15434
+ *
15435
+ * @param backtest - Whether running in backtest mode
15436
+ * @param symbol - Trading pair symbol
15437
+ * @param context - Execution context with strategyName, exchangeName, frameName
15438
+ * @returns Promise resolving to drawdown distance in PnL% (≥ 0) or null
15439
+ */
15440
+ this.getPositionHighestProfitDistancePnlPercentage = async (backtest, symbol, context) => {
15441
+ this.loggerService.log("strategyCoreService getPositionHighestProfitDistancePnlPercentage", {
15442
+ symbol,
15443
+ context,
15444
+ });
15445
+ await this.validate(context);
15446
+ return await this.strategyConnectionService.getPositionHighestProfitDistancePnlPercentage(backtest, symbol, context);
15447
+ };
15448
+ /**
15449
+ * Returns the distance in PnL cost between the current price and the highest profit peak.
15450
+ *
15451
+ * Delegates to StrategyConnectionService.getPositionHighestProfitDistancePnlCost().
15452
+ * Returns null if no pending signal exists.
15453
+ *
15454
+ * @param backtest - Whether running in backtest mode
15455
+ * @param symbol - Trading pair symbol
15456
+ * @param context - Execution context with strategyName, exchangeName, frameName
15457
+ * @returns Promise resolving to drawdown distance in PnL cost (≥ 0) or null
15458
+ */
15459
+ this.getPositionHighestProfitDistancePnlCost = async (backtest, symbol, context) => {
15460
+ this.loggerService.log("strategyCoreService getPositionHighestProfitDistancePnlCost", {
15461
+ symbol,
15462
+ context,
15463
+ });
15464
+ await this.validate(context);
15465
+ return await this.strategyConnectionService.getPositionHighestProfitDistancePnlCost(backtest, symbol, context);
15466
+ };
15467
+ /**
15468
+ * Returns the distance in PnL percentage between the current price and the worst drawdown trough.
15469
+ *
15470
+ * Delegates to StrategyConnectionService.getPositionHighestMaxDrawdownPnlPercentage().
15471
+ * Returns null if no pending signal exists.
15472
+ *
15473
+ * @param backtest - Whether running in backtest mode
15474
+ * @param symbol - Trading pair symbol
15475
+ * @param context - Execution context with strategyName, exchangeName, frameName
15476
+ * @returns Promise resolving to recovery distance in PnL% (≥ 0) or null
15477
+ */
15478
+ this.getPositionHighestMaxDrawdownPnlPercentage = async (backtest, symbol, context) => {
15479
+ this.loggerService.log("strategyCoreService getPositionHighestMaxDrawdownPnlPercentage", {
15480
+ symbol,
15481
+ context,
15482
+ });
15483
+ await this.validate(context);
15484
+ return await this.strategyConnectionService.getPositionHighestMaxDrawdownPnlPercentage(backtest, symbol, context);
15485
+ };
15486
+ /**
15487
+ * Returns the distance in PnL cost between the current price and the worst drawdown trough.
15488
+ *
15489
+ * Delegates to StrategyConnectionService.getPositionHighestMaxDrawdownPnlCost().
15490
+ * Returns null if no pending signal exists.
15491
+ *
15492
+ * @param backtest - Whether running in backtest mode
15493
+ * @param symbol - Trading pair symbol
15494
+ * @param context - Execution context with strategyName, exchangeName, frameName
15495
+ * @returns Promise resolving to recovery distance in PnL cost (≥ 0) or null
15496
+ */
15497
+ this.getPositionHighestMaxDrawdownPnlCost = async (backtest, symbol, context) => {
15498
+ this.loggerService.log("strategyCoreService getPositionHighestMaxDrawdownPnlCost", {
15499
+ symbol,
15500
+ context,
15501
+ });
15502
+ await this.validate(context);
15503
+ return await this.strategyConnectionService.getPositionHighestMaxDrawdownPnlCost(backtest, symbol, context);
15504
+ };
15261
15505
  }
15262
15506
  }
15263
15507
 
@@ -34913,6 +35157,10 @@ const GET_POSITION_MAX_DRAWDOWN_PRICE_METHOD_NAME = "strategy.getPositionMaxDraw
34913
35157
  const GET_POSITION_MAX_DRAWDOWN_TIMESTAMP_METHOD_NAME = "strategy.getPositionMaxDrawdownTimestamp";
34914
35158
  const GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE_METHOD_NAME = "strategy.getPositionMaxDrawdownPnlPercentage";
34915
35159
  const GET_POSITION_MAX_DRAWDOWN_PNL_COST_METHOD_NAME = "strategy.getPositionMaxDrawdownPnlCost";
35160
+ const GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE_METHOD_NAME = "strategy.getPositionHighestProfitDistancePnlPercentage";
35161
+ const GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST_METHOD_NAME = "strategy.getPositionHighestProfitDistancePnlCost";
35162
+ const GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE_METHOD_NAME = "strategy.getPositionHighestMaxDrawdownPnlPercentage";
35163
+ const GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST_METHOD_NAME = "strategy.getPositionHighestMaxDrawdownPnlCost";
34916
35164
  const GET_POSITION_ENTRY_OVERLAP_METHOD_NAME = "strategy.getPositionEntryOverlap";
34917
35165
  const GET_POSITION_PARTIAL_OVERLAP_METHOD_NAME = "strategy.getPositionPartialOverlap";
34918
35166
  const HAS_NO_PENDING_SIGNAL_METHOD_NAME = "strategy.hasNoPendingSignal";
@@ -36547,6 +36795,122 @@ async function getPositionMaxDrawdownPnlCost(symbol) {
36547
36795
  const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
36548
36796
  return await backtest.strategyCoreService.getPositionMaxDrawdownPnlCost(isBacktest, symbol, { exchangeName, frameName, strategyName });
36549
36797
  }
36798
+ /**
36799
+ * Returns the distance in PnL percentage between the current price and the highest profit peak.
36800
+ *
36801
+ * Computed as: max(0, peakPnlPercentage - currentPnlPercentage).
36802
+ * Returns null if no pending signal exists.
36803
+ *
36804
+ * @param symbol - Trading pair symbol
36805
+ * @returns Promise resolving to drawdown distance in PnL% (≥ 0) or null
36806
+ *
36807
+ * @example
36808
+ * ```typescript
36809
+ * import { getPositionHighestProfitDistancePnlPercentage } from "backtest-kit";
36810
+ *
36811
+ * const dist = await getPositionHighestProfitDistancePnlPercentage("BTCUSDT");
36812
+ * // e.g. 1.5 (gave back 1.5% from peak)
36813
+ * ```
36814
+ */
36815
+ async function getPositionHighestProfitDistancePnlPercentage(symbol) {
36816
+ backtest.loggerService.info(GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE_METHOD_NAME, { symbol });
36817
+ if (!ExecutionContextService.hasContext()) {
36818
+ throw new Error("getPositionHighestProfitDistancePnlPercentage requires an execution context");
36819
+ }
36820
+ if (!MethodContextService.hasContext()) {
36821
+ throw new Error("getPositionHighestProfitDistancePnlPercentage requires a method context");
36822
+ }
36823
+ const { backtest: isBacktest } = backtest.executionContextService.context;
36824
+ const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
36825
+ return await backtest.strategyCoreService.getPositionHighestProfitDistancePnlPercentage(isBacktest, symbol, { exchangeName, frameName, strategyName });
36826
+ }
36827
+ /**
36828
+ * Returns the distance in PnL cost between the current price and the highest profit peak.
36829
+ *
36830
+ * Computed as: max(0, peakPnlCost - currentPnlCost).
36831
+ * Returns null if no pending signal exists.
36832
+ *
36833
+ * @param symbol - Trading pair symbol
36834
+ * @returns Promise resolving to drawdown distance in PnL cost (≥ 0) or null
36835
+ *
36836
+ * @example
36837
+ * ```typescript
36838
+ * import { getPositionHighestProfitDistancePnlCost } from "backtest-kit";
36839
+ *
36840
+ * const dist = await getPositionHighestProfitDistancePnlCost("BTCUSDT");
36841
+ * // e.g. 3.2 (gave back $3.2 from peak)
36842
+ * ```
36843
+ */
36844
+ async function getPositionHighestProfitDistancePnlCost(symbol) {
36845
+ backtest.loggerService.info(GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST_METHOD_NAME, { symbol });
36846
+ if (!ExecutionContextService.hasContext()) {
36847
+ throw new Error("getPositionHighestProfitDistancePnlCost requires an execution context");
36848
+ }
36849
+ if (!MethodContextService.hasContext()) {
36850
+ throw new Error("getPositionHighestProfitDistancePnlCost requires a method context");
36851
+ }
36852
+ const { backtest: isBacktest } = backtest.executionContextService.context;
36853
+ const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
36854
+ return await backtest.strategyCoreService.getPositionHighestProfitDistancePnlCost(isBacktest, symbol, { exchangeName, frameName, strategyName });
36855
+ }
36856
+ /**
36857
+ * Returns the distance in PnL percentage between the current price and the worst drawdown trough.
36858
+ *
36859
+ * Computed as: max(0, currentPnlPercentage - fallPnlPercentage).
36860
+ * Returns null if no pending signal exists.
36861
+ *
36862
+ * @param symbol - Trading pair symbol
36863
+ * @returns Promise resolving to recovery distance in PnL% (≥ 0) or null
36864
+ *
36865
+ * @example
36866
+ * ```typescript
36867
+ * import { getPositionHighestMaxDrawdownPnlPercentage } from "backtest-kit";
36868
+ *
36869
+ * const dist = await getPositionHighestMaxDrawdownPnlPercentage("BTCUSDT");
36870
+ * // e.g. 2.1 (recovered 2.1% from trough)
36871
+ * ```
36872
+ */
36873
+ async function getPositionHighestMaxDrawdownPnlPercentage(symbol) {
36874
+ backtest.loggerService.info(GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE_METHOD_NAME, { symbol });
36875
+ if (!ExecutionContextService.hasContext()) {
36876
+ throw new Error("getPositionHighestMaxDrawdownPnlPercentage requires an execution context");
36877
+ }
36878
+ if (!MethodContextService.hasContext()) {
36879
+ throw new Error("getPositionHighestMaxDrawdownPnlPercentage requires a method context");
36880
+ }
36881
+ const { backtest: isBacktest } = backtest.executionContextService.context;
36882
+ const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
36883
+ return await backtest.strategyCoreService.getPositionHighestMaxDrawdownPnlPercentage(isBacktest, symbol, { exchangeName, frameName, strategyName });
36884
+ }
36885
+ /**
36886
+ * Returns the distance in PnL cost between the current price and the worst drawdown trough.
36887
+ *
36888
+ * Computed as: max(0, currentPnlCost - fallPnlCost).
36889
+ * Returns null if no pending signal exists.
36890
+ *
36891
+ * @param symbol - Trading pair symbol
36892
+ * @returns Promise resolving to recovery distance in PnL cost (≥ 0) or null
36893
+ *
36894
+ * @example
36895
+ * ```typescript
36896
+ * import { getPositionHighestMaxDrawdownPnlCost } from "backtest-kit";
36897
+ *
36898
+ * const dist = await getPositionHighestMaxDrawdownPnlCost("BTCUSDT");
36899
+ * // e.g. 4.8 (recovered $4.8 from trough)
36900
+ * ```
36901
+ */
36902
+ async function getPositionHighestMaxDrawdownPnlCost(symbol) {
36903
+ backtest.loggerService.info(GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST_METHOD_NAME, { symbol });
36904
+ if (!ExecutionContextService.hasContext()) {
36905
+ throw new Error("getPositionHighestMaxDrawdownPnlCost requires an execution context");
36906
+ }
36907
+ if (!MethodContextService.hasContext()) {
36908
+ throw new Error("getPositionHighestMaxDrawdownPnlCost requires a method context");
36909
+ }
36910
+ const { backtest: isBacktest } = backtest.executionContextService.context;
36911
+ const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
36912
+ return await backtest.strategyCoreService.getPositionHighestMaxDrawdownPnlCost(isBacktest, symbol, { exchangeName, frameName, strategyName });
36913
+ }
36550
36914
  /**
36551
36915
  * Checks whether the current price falls within the tolerance zone of any existing DCA entry level.
36552
36916
  * Use this to prevent duplicate DCA entries at the same price area.
@@ -38216,6 +38580,10 @@ const BACKTEST_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE = "BacktestUtils.getP
38216
38580
  const BACKTEST_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP = "BacktestUtils.getPositionMaxDrawdownTimestamp";
38217
38581
  const BACKTEST_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE = "BacktestUtils.getPositionMaxDrawdownPnlPercentage";
38218
38582
  const BACKTEST_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST = "BacktestUtils.getPositionMaxDrawdownPnlCost";
38583
+ const BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE = "BacktestUtils.getPositionHighestProfitDistancePnlPercentage";
38584
+ const BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST = "BacktestUtils.getPositionHighestProfitDistancePnlCost";
38585
+ const BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE = "BacktestUtils.getPositionHighestMaxDrawdownPnlPercentage";
38586
+ const BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST = "BacktestUtils.getPositionHighestMaxDrawdownPnlCost";
38219
38587
  const BACKTEST_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP = "BacktestUtils.getPositionEntryOverlap";
38220
38588
  const BACKTEST_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP = "BacktestUtils.getPositionPartialOverlap";
38221
38589
  const BACKTEST_METHOD_NAME_BREAKEVEN = "Backtest.commitBreakeven";
@@ -39485,6 +39853,118 @@ class BacktestUtils {
39485
39853
  }
39486
39854
  return await backtest.strategyCoreService.getPositionMaxDrawdownPnlCost(true, symbol, context);
39487
39855
  };
39856
+ /**
39857
+ * Returns the distance in PnL percentage between the current price and the highest profit peak.
39858
+ *
39859
+ * Computed as: max(0, peakPnlPercentage - currentPnlPercentage).
39860
+ * Returns null if no pending signal exists.
39861
+ *
39862
+ * @param symbol - Trading pair symbol
39863
+ * @param context - Execution context with strategyName, exchangeName, and frameName
39864
+ * @returns drawdown distance in PnL% (≥ 0) or null if no active position
39865
+ */
39866
+ this.getPositionHighestProfitDistancePnlPercentage = async (symbol, context) => {
39867
+ backtest.loggerService.info(BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE, {
39868
+ symbol,
39869
+ context,
39870
+ });
39871
+ backtest.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE);
39872
+ backtest.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE);
39873
+ {
39874
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
39875
+ riskName &&
39876
+ backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE);
39877
+ riskList &&
39878
+ riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE));
39879
+ actions &&
39880
+ actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE));
39881
+ }
39882
+ return await backtest.strategyCoreService.getPositionHighestProfitDistancePnlPercentage(true, symbol, context);
39883
+ };
39884
+ /**
39885
+ * Returns the distance in PnL cost between the current price and the highest profit peak.
39886
+ *
39887
+ * Computed as: max(0, peakPnlCost - currentPnlCost).
39888
+ * Returns null if no pending signal exists.
39889
+ *
39890
+ * @param symbol - Trading pair symbol
39891
+ * @param context - Execution context with strategyName, exchangeName, and frameName
39892
+ * @returns drawdown distance in PnL cost (≥ 0) or null if no active position
39893
+ */
39894
+ this.getPositionHighestProfitDistancePnlCost = async (symbol, context) => {
39895
+ backtest.loggerService.info(BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST, {
39896
+ symbol,
39897
+ context,
39898
+ });
39899
+ backtest.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST);
39900
+ backtest.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST);
39901
+ {
39902
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
39903
+ riskName &&
39904
+ backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST);
39905
+ riskList &&
39906
+ riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST));
39907
+ actions &&
39908
+ actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST));
39909
+ }
39910
+ return await backtest.strategyCoreService.getPositionHighestProfitDistancePnlCost(true, symbol, context);
39911
+ };
39912
+ /**
39913
+ * Returns the distance in PnL percentage between the current price and the worst drawdown trough.
39914
+ *
39915
+ * Computed as: max(0, currentPnlPercentage - fallPnlPercentage).
39916
+ * Returns null if no pending signal exists.
39917
+ *
39918
+ * @param symbol - Trading pair symbol
39919
+ * @param context - Execution context with strategyName, exchangeName, and frameName
39920
+ * @returns recovery distance in PnL% (≥ 0) or null if no active position
39921
+ */
39922
+ this.getPositionHighestMaxDrawdownPnlPercentage = async (symbol, context) => {
39923
+ backtest.loggerService.info(BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE, {
39924
+ symbol,
39925
+ context,
39926
+ });
39927
+ backtest.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE);
39928
+ backtest.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE);
39929
+ {
39930
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
39931
+ riskName &&
39932
+ backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE);
39933
+ riskList &&
39934
+ riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE));
39935
+ actions &&
39936
+ actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE));
39937
+ }
39938
+ return await backtest.strategyCoreService.getPositionHighestMaxDrawdownPnlPercentage(true, symbol, context);
39939
+ };
39940
+ /**
39941
+ * Returns the distance in PnL cost between the current price and the worst drawdown trough.
39942
+ *
39943
+ * Computed as: max(0, currentPnlCost - fallPnlCost).
39944
+ * Returns null if no pending signal exists.
39945
+ *
39946
+ * @param symbol - Trading pair symbol
39947
+ * @param context - Execution context with strategyName, exchangeName, and frameName
39948
+ * @returns recovery distance in PnL cost (≥ 0) or null if no active position
39949
+ */
39950
+ this.getPositionHighestMaxDrawdownPnlCost = async (symbol, context) => {
39951
+ backtest.loggerService.info(BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST, {
39952
+ symbol,
39953
+ context,
39954
+ });
39955
+ backtest.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST);
39956
+ backtest.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST);
39957
+ {
39958
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
39959
+ riskName &&
39960
+ backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST);
39961
+ riskList &&
39962
+ riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST));
39963
+ actions &&
39964
+ actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST));
39965
+ }
39966
+ return await backtest.strategyCoreService.getPositionHighestMaxDrawdownPnlCost(true, symbol, context);
39967
+ };
39488
39968
  /**
39489
39969
  * Checks whether the current price falls within the tolerance zone of any existing DCA entry level.
39490
39970
  * Use this to prevent duplicate DCA entries at the same price area.
@@ -40610,6 +41090,10 @@ const LIVE_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE = "LiveUtils.getPositionM
40610
41090
  const LIVE_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP = "LiveUtils.getPositionMaxDrawdownTimestamp";
40611
41091
  const LIVE_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE = "LiveUtils.getPositionMaxDrawdownPnlPercentage";
40612
41092
  const LIVE_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST = "LiveUtils.getPositionMaxDrawdownPnlCost";
41093
+ const LIVE_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE = "LiveUtils.getPositionHighestProfitDistancePnlPercentage";
41094
+ const LIVE_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST = "LiveUtils.getPositionHighestProfitDistancePnlCost";
41095
+ const LIVE_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE = "LiveUtils.getPositionHighestMaxDrawdownPnlPercentage";
41096
+ const LIVE_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST = "LiveUtils.getPositionHighestMaxDrawdownPnlCost";
40613
41097
  const LIVE_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP = "LiveUtils.getPositionEntryOverlap";
40614
41098
  const LIVE_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP = "LiveUtils.getPositionPartialOverlap";
40615
41099
  const LIVE_METHOD_NAME_BREAKEVEN = "Live.commitBreakeven";
@@ -42006,6 +42490,134 @@ class LiveUtils {
42006
42490
  frameName: "",
42007
42491
  });
42008
42492
  };
42493
+ /**
42494
+ * Returns the distance in PnL percentage between the current price and the highest profit peak.
42495
+ *
42496
+ * Computed as: max(0, peakPnlPercentage - currentPnlPercentage).
42497
+ * Returns null if no pending signal exists.
42498
+ *
42499
+ * @param symbol - Trading pair symbol
42500
+ * @param context - Execution context with strategyName and exchangeName
42501
+ * @returns drawdown distance in PnL% (≥ 0) or null if no active position
42502
+ */
42503
+ this.getPositionHighestProfitDistancePnlPercentage = async (symbol, context) => {
42504
+ backtest.loggerService.info(LIVE_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE, {
42505
+ symbol,
42506
+ context,
42507
+ });
42508
+ backtest.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE);
42509
+ backtest.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE);
42510
+ {
42511
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
42512
+ riskName &&
42513
+ backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE);
42514
+ riskList &&
42515
+ riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE));
42516
+ actions &&
42517
+ actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, LIVE_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE));
42518
+ }
42519
+ return await backtest.strategyCoreService.getPositionHighestProfitDistancePnlPercentage(false, symbol, {
42520
+ strategyName: context.strategyName,
42521
+ exchangeName: context.exchangeName,
42522
+ frameName: "",
42523
+ });
42524
+ };
42525
+ /**
42526
+ * Returns the distance in PnL cost between the current price and the highest profit peak.
42527
+ *
42528
+ * Computed as: max(0, peakPnlCost - currentPnlCost).
42529
+ * Returns null if no pending signal exists.
42530
+ *
42531
+ * @param symbol - Trading pair symbol
42532
+ * @param context - Execution context with strategyName and exchangeName
42533
+ * @returns drawdown distance in PnL cost (≥ 0) or null if no active position
42534
+ */
42535
+ this.getPositionHighestProfitDistancePnlCost = async (symbol, context) => {
42536
+ backtest.loggerService.info(LIVE_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST, {
42537
+ symbol,
42538
+ context,
42539
+ });
42540
+ backtest.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST);
42541
+ backtest.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST);
42542
+ {
42543
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
42544
+ riskName &&
42545
+ backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST);
42546
+ riskList &&
42547
+ riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST));
42548
+ actions &&
42549
+ actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, LIVE_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST));
42550
+ }
42551
+ return await backtest.strategyCoreService.getPositionHighestProfitDistancePnlCost(false, symbol, {
42552
+ strategyName: context.strategyName,
42553
+ exchangeName: context.exchangeName,
42554
+ frameName: "",
42555
+ });
42556
+ };
42557
+ /**
42558
+ * Returns the distance in PnL percentage between the current price and the worst drawdown trough.
42559
+ *
42560
+ * Computed as: max(0, currentPnlPercentage - fallPnlPercentage).
42561
+ * Returns null if no pending signal exists.
42562
+ *
42563
+ * @param symbol - Trading pair symbol
42564
+ * @param context - Execution context with strategyName and exchangeName
42565
+ * @returns recovery distance in PnL% (≥ 0) or null if no active position
42566
+ */
42567
+ this.getPositionHighestMaxDrawdownPnlPercentage = async (symbol, context) => {
42568
+ backtest.loggerService.info(LIVE_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE, {
42569
+ symbol,
42570
+ context,
42571
+ });
42572
+ backtest.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE);
42573
+ backtest.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE);
42574
+ {
42575
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
42576
+ riskName &&
42577
+ backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE);
42578
+ riskList &&
42579
+ riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE));
42580
+ actions &&
42581
+ actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, LIVE_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE));
42582
+ }
42583
+ return await backtest.strategyCoreService.getPositionHighestMaxDrawdownPnlPercentage(false, symbol, {
42584
+ strategyName: context.strategyName,
42585
+ exchangeName: context.exchangeName,
42586
+ frameName: "",
42587
+ });
42588
+ };
42589
+ /**
42590
+ * Returns the distance in PnL cost between the current price and the worst drawdown trough.
42591
+ *
42592
+ * Computed as: max(0, currentPnlCost - fallPnlCost).
42593
+ * Returns null if no pending signal exists.
42594
+ *
42595
+ * @param symbol - Trading pair symbol
42596
+ * @param context - Execution context with strategyName and exchangeName
42597
+ * @returns recovery distance in PnL cost (≥ 0) or null if no active position
42598
+ */
42599
+ this.getPositionHighestMaxDrawdownPnlCost = async (symbol, context) => {
42600
+ backtest.loggerService.info(LIVE_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST, {
42601
+ symbol,
42602
+ context,
42603
+ });
42604
+ backtest.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST);
42605
+ backtest.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST);
42606
+ {
42607
+ const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
42608
+ riskName &&
42609
+ backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST);
42610
+ riskList &&
42611
+ riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST));
42612
+ actions &&
42613
+ actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, LIVE_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST));
42614
+ }
42615
+ return await backtest.strategyCoreService.getPositionHighestMaxDrawdownPnlCost(false, symbol, {
42616
+ strategyName: context.strategyName,
42617
+ exchangeName: context.exchangeName,
42618
+ frameName: "",
42619
+ });
42620
+ };
42009
42621
  /**
42010
42622
  * Checks whether the current price falls within the tolerance zone of any existing DCA entry level.
42011
42623
  * Use this to prevent duplicate DCA entries at the same price area.
@@ -53645,17 +54257,17 @@ const CREATE_KEY_FN = (strategyName, exchangeName, frameName, isBacktest) => {
53645
54257
  * @example
53646
54258
  * ```typescript
53647
54259
  * const instance = new IntervalFnInstance(mySignalFn, "1h");
53648
- * await instance.run("BTCUSDT"); // → ISignalIntervalDto | null (fn called)
53649
- * await instance.run("BTCUSDT"); // → null (skipped, same interval)
54260
+ * await instance.run("BTCUSDT"); // → T | null (fn called)
54261
+ * await instance.run("BTCUSDT"); // → null (skipped, same interval)
53650
54262
  * // After 1 hour passes:
53651
- * await instance.run("BTCUSDT"); // → ISignalIntervalDto | null (fn called again)
54263
+ * await instance.run("BTCUSDT"); // → T | null (fn called again)
53652
54264
  * ```
53653
54265
  */
53654
54266
  class IntervalFnInstance {
53655
54267
  /**
53656
54268
  * Creates a new IntervalFnInstance.
53657
54269
  *
53658
- * @param fn - Signal function to fire once per interval
54270
+ * @param fn - Function to fire once per interval
53659
54271
  * @param interval - Candle interval that controls the firing boundary
53660
54272
  */
53661
54273
  constructor(fn, interval) {
@@ -53675,7 +54287,7 @@ class IntervalFnInstance {
53675
54287
  * Requires active method context and execution context.
53676
54288
  *
53677
54289
  * @param symbol - Trading pair symbol (e.g. "BTCUSDT")
53678
- * @returns The signal returned by `fn` on the first non-null fire, `null` on all subsequent calls
54290
+ * @returns The value returned by `fn` on the first non-null fire, `null` on all subsequent calls
53679
54291
  * within the same interval or when `fn` itself returned `null`
53680
54292
  * @throws Error if method context, execution context, or interval is missing
53681
54293
  */
@@ -53736,13 +54348,13 @@ class IntervalFnInstance {
53736
54348
  *
53737
54349
  * Fired state survives process restarts — unlike `IntervalFnInstance` which is in-memory only.
53738
54350
  *
53739
- * @template T - Async function type: `(symbol: string, ...args) => Promise<ISignalIntervalDto | null>`
54351
+ * @template T - Async function type: `(symbol: string, ...args) => Promise<R | null>`
53740
54352
  *
53741
54353
  * @example
53742
54354
  * ```typescript
53743
54355
  * const instance = new IntervalFileInstance(fetchSignal, "1h", "mySignal");
53744
- * await instance.run("BTCUSDT"); // → ISignalIntervalDto | null (fn called, result written to disk)
53745
- * await instance.run("BTCUSDT"); // → null (record exists, already fired)
54356
+ * await instance.run("BTCUSDT"); // → R | null (fn called, result written to disk)
54357
+ * await instance.run("BTCUSDT"); // → null (record exists, already fired)
53746
54358
  * ```
53747
54359
  */
53748
54360
  class IntervalFileInstance {
@@ -53772,7 +54384,7 @@ class IntervalFileInstance {
53772
54384
  this.interval = interval;
53773
54385
  this.name = name;
53774
54386
  /**
53775
- * Execute the async signal function with persistent once-per-interval enforcement.
54387
+ * Execute the async function with persistent once-per-interval enforcement.
53776
54388
  *
53777
54389
  * Algorithm:
53778
54390
  * 1. Build bucket = `${name}_${interval}_${index}` — fixed per instance, used as directory name.
@@ -53784,13 +54396,13 @@ class IntervalFileInstance {
53784
54396
  *
53785
54397
  * Requires active method context and execution context.
53786
54398
  *
53787
- * @param args - Arguments forwarded to the wrapped function (first must be `symbol: string`)
53788
- * @returns The signal on the first non-null fire, `null` if already fired this interval
54399
+ * @param symbol - Trading pair symbol (e.g. "BTCUSDT")
54400
+ * @returns The value on the first non-null fire, `null` if already fired this interval
53789
54401
  * or if `fn` itself returned `null`
53790
54402
  * @throws Error if method context, execution context, or interval is missing
53791
54403
  */
53792
- this.run = async (...args) => {
53793
- backtest.loggerService.debug(INTERVAL_FILE_INSTANCE_METHOD_NAME_RUN, { args });
54404
+ this.run = async (symbol) => {
54405
+ backtest.loggerService.debug(INTERVAL_FILE_INSTANCE_METHOD_NAME_RUN, { symbol });
53794
54406
  const step = INTERVAL_MINUTES[this.interval];
53795
54407
  {
53796
54408
  if (!MethodContextService.hasContext()) {
@@ -53803,7 +54415,6 @@ class IntervalFileInstance {
53803
54415
  throw new Error(`IntervalFileInstance unknown interval=${this.interval}`);
53804
54416
  }
53805
54417
  }
53806
- const [symbol] = args;
53807
54418
  const { when } = backtest.executionContextService.context;
53808
54419
  const alignedTs = align(when.getTime(), this.interval);
53809
54420
  const bucket = `${this.name}_${this.interval}_${this.index}`;
@@ -53812,7 +54423,7 @@ class IntervalFileInstance {
53812
54423
  if (cached !== null) {
53813
54424
  return null;
53814
54425
  }
53815
- const result = await this.fn.call(null, ...args);
54426
+ const result = await this.fn(symbol, when);
53816
54427
  if (result !== null) {
53817
54428
  await PersistIntervalAdapter.writeIntervalData({ id: entityKey, data: result, removed: false }, bucket, entityKey);
53818
54429
  }
@@ -53843,8 +54454,8 @@ IntervalFileInstance._indexCounter = 0;
53843
54454
  * import { Interval } from "./classes/Interval";
53844
54455
  *
53845
54456
  * const fireOncePerHour = Interval.fn(mySignalFn, { interval: "1h" });
53846
- * await fireOncePerHour("BTCUSDT", when); // fn called — returns its result
53847
- * await fireOncePerHour("BTCUSDT", when); // returns null (same interval)
54457
+ * await fireOncePerHour("BTCUSDT"); // fn called — returns its result
54458
+ * await fireOncePerHour("BTCUSDT"); // returns null (same interval)
53848
54459
  * ```
53849
54460
  */
53850
54461
  class IntervalUtils {
@@ -53870,19 +54481,19 @@ class IntervalUtils {
53870
54481
  *
53871
54482
  * @param run - Signal function to wrap
53872
54483
  * @param context.interval - Candle interval that controls the firing boundary
53873
- * @returns Wrapped function with the same signature as `TIntervalFn`, plus a `clear()` method
54484
+ * @returns Wrapped function with the same signature as `TIntervalFn<T>`, plus a `clear()` method
53874
54485
  *
53875
54486
  * @example
53876
54487
  * ```typescript
53877
54488
  * const fireOnce = Interval.fn(mySignalFn, { interval: "15m" });
53878
54489
  *
53879
- * await fireOnce("BTCUSDT", when); // → signal or null (fn called)
53880
- * await fireOnce("BTCUSDT", when); // → null (same interval, skipped)
54490
+ * await fireOnce("BTCUSDT"); // → T or null (fn called)
54491
+ * await fireOnce("BTCUSDT"); // → null (same interval, skipped)
53881
54492
  * ```
53882
54493
  */
53883
54494
  this.fn = (run, context) => {
53884
54495
  backtest.loggerService.info(INTERVAL_METHOD_NAME_FN, { context });
53885
- const wrappedFn = (symbol, _when) => {
54496
+ const wrappedFn = (symbol) => {
53886
54497
  const instance = this._getInstance(run, context.interval);
53887
54498
  return instance.run(symbol);
53888
54499
  };
@@ -53919,7 +54530,7 @@ class IntervalUtils {
53919
54530
  *
53920
54531
  * @example
53921
54532
  * ```typescript
53922
- * const fetchSignal = async (symbol: string, period: number) => { ... };
54533
+ * const fetchSignal = async (symbol: string, when: Date) => { ... };
53923
54534
  * const fireOnce = Interval.file(fetchSignal, { interval: "1h", name: "fetchSignal" });
53924
54535
  * await fireOnce.clear(); // delete disk records so the function fires again next call
53925
54536
  * ```
@@ -53929,9 +54540,9 @@ class IntervalUtils {
53929
54540
  {
53930
54541
  this._getFileInstance(run, context.interval, context.name);
53931
54542
  }
53932
- const wrappedFn = (...args) => {
54543
+ const wrappedFn = (symbol) => {
53933
54544
  const instance = this._getFileInstance(run, context.interval, context.name);
53934
- return instance.run(...args);
54545
+ return instance.run(symbol);
53935
54546
  };
53936
54547
  wrappedFn.clear = async () => {
53937
54548
  backtest.loggerService.info(INTERVAL_METHOD_NAME_FILE_CLEAR);
@@ -55212,9 +55823,13 @@ exports.getPositionEffectivePrice = getPositionEffectivePrice;
55212
55823
  exports.getPositionEntries = getPositionEntries;
55213
55824
  exports.getPositionEntryOverlap = getPositionEntryOverlap;
55214
55825
  exports.getPositionEstimateMinutes = getPositionEstimateMinutes;
55826
+ exports.getPositionHighestMaxDrawdownPnlCost = getPositionHighestMaxDrawdownPnlCost;
55827
+ exports.getPositionHighestMaxDrawdownPnlPercentage = getPositionHighestMaxDrawdownPnlPercentage;
55215
55828
  exports.getPositionHighestPnlCost = getPositionHighestPnlCost;
55216
55829
  exports.getPositionHighestPnlPercentage = getPositionHighestPnlPercentage;
55217
55830
  exports.getPositionHighestProfitBreakeven = getPositionHighestProfitBreakeven;
55831
+ exports.getPositionHighestProfitDistancePnlCost = getPositionHighestProfitDistancePnlCost;
55832
+ exports.getPositionHighestProfitDistancePnlPercentage = getPositionHighestProfitDistancePnlPercentage;
55218
55833
  exports.getPositionHighestProfitMinutes = getPositionHighestProfitMinutes;
55219
55834
  exports.getPositionHighestProfitPrice = getPositionHighestProfitPrice;
55220
55835
  exports.getPositionHighestProfitTimestamp = getPositionHighestProfitTimestamp;