backtest-kit 4.0.1 → 5.0.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
@@ -450,9 +450,17 @@ const GLOBAL_CONFIG = {
450
450
  * Allows to commitAverageBuy if currentPrice is not the lowest price since entry, but still lower than priceOpen.
451
451
  * This can help improve average entry price in cases where price has rebounded after entry but is still below priceOpen, without waiting for a new lower price.
452
452
  *
453
- * Default: true (DCA logic enabled everywhere, not just when antirecord is broken)
453
+ * Default: false (DCA logic enabled only when antirecord is broken)
454
454
  */
455
455
  CC_ENABLE_DCA_EVERYWHERE: false,
456
+ /**
457
+ * Enables PPPL (Partial Profit, Partial Loss) logic even if this breaks a direction of exits
458
+ * Allows to take partial profit or loss on a position even if it results in a mix of profit and loss exits
459
+ * This can help lock in profits or cut losses on part of the position without waiting for a perfect exit scenario.
460
+ *
461
+ * Default: false (PPPL logic is only applied when it does not break the direction of exits, ensuring clearer profit/loss outcomes)
462
+ */
463
+ CC_ENABLE_PPPL_EVERYWHERE: false,
456
464
  /**
457
465
  * Cost of entering a position (in USD).
458
466
  * This is used as a default value for calculating position size and risk management when cost data is not provided by the strategy
@@ -6610,6 +6618,18 @@ class ClientStrategy {
6610
6618
  }
6611
6619
  return entries.map((e) => e.price);
6612
6620
  }
6621
+ /**
6622
+ * Returns the list of partial closes for the current pending signal.
6623
+ *
6624
+ * Each entry records a partial profit or loss close event with its type,
6625
+ * percent closed, price at close, cost basis snapshot, and entry count at close.
6626
+ *
6627
+ * Returns null if no pending signal exists.
6628
+ * Returns an empty array if no partial closes have been executed.
6629
+ *
6630
+ * @param symbol - Trading pair symbol
6631
+ * @returns Promise resolving to array of partial close records or null
6632
+ */
6613
6633
  async getPositionPartials(symbol) {
6614
6634
  this.params.logger.debug("ClientStrategy getPositionPartials", { symbol });
6615
6635
  if (!this._pendingSignal) {
@@ -6617,6 +6637,34 @@ class ClientStrategy {
6617
6637
  }
6618
6638
  return this._pendingSignal._partial ?? [];
6619
6639
  }
6640
+ /**
6641
+ * Returns the list of DCA entry prices and costs for the current pending signal.
6642
+ *
6643
+ * Each entry records the price and cost of a single position entry.
6644
+ * The first element is always the original priceOpen (initial entry).
6645
+ * Each subsequent element is an entry added by averageBuy().
6646
+ *
6647
+ * Returns null if no pending signal exists.
6648
+ * Returns a single-element array [{ price: priceOpen, cost }] if no DCA entries were made.
6649
+ *
6650
+ * @param symbol - Trading pair symbol
6651
+ * @returns Promise resolving to array of entry records or null
6652
+ *
6653
+ * @example
6654
+ * // No DCA: [{ price: 43000, cost: 100 }]
6655
+ * // One DCA: [{ price: 43000, cost: 100 }, { price: 42000, cost: 100 }]
6656
+ */
6657
+ async getPositionEntries(symbol) {
6658
+ this.params.logger.debug("ClientStrategy getPositionEntries", { symbol });
6659
+ if (!this._pendingSignal) {
6660
+ return null;
6661
+ }
6662
+ const entries = this._pendingSignal._entry;
6663
+ if (!entries || entries.length === 0) {
6664
+ return [{ price: this._pendingSignal.priceOpen, cost: GLOBAL_CONFIG.CC_POSITION_ENTRY_COST }];
6665
+ }
6666
+ return entries.map(({ price, cost }) => ({ price, cost }));
6667
+ }
6620
6668
  /**
6621
6669
  * Performs a single tick of strategy execution.
6622
6670
  *
@@ -7388,10 +7436,12 @@ class ClientStrategy {
7388
7436
  if (typeof currentPrice !== "number" || !isFinite(currentPrice) || currentPrice <= 0)
7389
7437
  return false;
7390
7438
  const effectivePriceOpen = getEffectivePriceOpen(this._pendingSignal);
7391
- if (this._pendingSignal.position === "long" && currentPrice <= effectivePriceOpen)
7392
- return false;
7393
- if (this._pendingSignal.position === "short" && currentPrice >= effectivePriceOpen)
7394
- return false;
7439
+ if (!GLOBAL_CONFIG.CC_ENABLE_PPPL_EVERYWHERE) {
7440
+ if (this._pendingSignal.position === "long" && currentPrice <= effectivePriceOpen)
7441
+ return false;
7442
+ if (this._pendingSignal.position === "short" && currentPrice >= effectivePriceOpen)
7443
+ return false;
7444
+ }
7395
7445
  const effectiveTakeProfit = this._pendingSignal._trailingPriceTakeProfit ?? this._pendingSignal.priceTakeProfit;
7396
7446
  if (this._pendingSignal.position === "long" && currentPrice >= effectiveTakeProfit)
7397
7447
  return false;
@@ -7475,7 +7525,7 @@ class ClientStrategy {
7475
7525
  throw new Error(`ClientStrategy partialProfit: currentPrice must be a positive finite number, got ${currentPrice}`);
7476
7526
  }
7477
7527
  // Validation: currentPrice must be moving toward TP (profit direction)
7478
- {
7528
+ if (!GLOBAL_CONFIG.CC_ENABLE_PPPL_EVERYWHERE) {
7479
7529
  const effectivePriceOpen = getEffectivePriceOpen(this._pendingSignal);
7480
7530
  if (this._pendingSignal.position === "long") {
7481
7531
  // For LONG: currentPrice must be higher than effectivePriceOpen (moving toward TP)
@@ -7569,10 +7619,12 @@ class ClientStrategy {
7569
7619
  if (typeof currentPrice !== "number" || !isFinite(currentPrice) || currentPrice <= 0)
7570
7620
  return false;
7571
7621
  const effectivePriceOpen = getEffectivePriceOpen(this._pendingSignal);
7572
- if (this._pendingSignal.position === "long" && currentPrice >= effectivePriceOpen)
7573
- return false;
7574
- if (this._pendingSignal.position === "short" && currentPrice <= effectivePriceOpen)
7575
- return false;
7622
+ if (!GLOBAL_CONFIG.CC_ENABLE_PPPL_EVERYWHERE) {
7623
+ if (this._pendingSignal.position === "long" && currentPrice >= effectivePriceOpen)
7624
+ return false;
7625
+ if (this._pendingSignal.position === "short" && currentPrice <= effectivePriceOpen)
7626
+ return false;
7627
+ }
7576
7628
  const effectiveStopLoss = this._pendingSignal._trailingPriceStopLoss ?? this._pendingSignal.priceStopLoss;
7577
7629
  if (this._pendingSignal.position === "long" && currentPrice <= effectiveStopLoss)
7578
7630
  return false;
@@ -7656,7 +7708,7 @@ class ClientStrategy {
7656
7708
  throw new Error(`ClientStrategy partialLoss: currentPrice must be a positive finite number, got ${currentPrice}`);
7657
7709
  }
7658
7710
  // Validation: currentPrice must be moving toward SL (loss direction)
7659
- {
7711
+ if (!GLOBAL_CONFIG.CC_ENABLE_PPPL_EVERYWHERE) {
7660
7712
  const effectivePriceOpen = getEffectivePriceOpen(this._pendingSignal);
7661
7713
  if (this._pendingSignal.position === "long") {
7662
7714
  // For LONG: currentPrice must be lower than effectivePriceOpen (moving toward SL)
@@ -9271,6 +9323,15 @@ class StrategyConnectionService {
9271
9323
  const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
9272
9324
  return await strategy.getPositionPartials(symbol);
9273
9325
  };
9326
+ this.getPositionEntries = async (backtest, symbol, context) => {
9327
+ this.loggerService.log("strategyConnectionService getPositionEntries", {
9328
+ symbol,
9329
+ context,
9330
+ backtest,
9331
+ });
9332
+ const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
9333
+ return await strategy.getPositionEntries(symbol);
9334
+ };
9274
9335
  /**
9275
9336
  * Retrieves the currently active scheduled signal for the strategy.
9276
9337
  * If no scheduled signal exists, returns null.
@@ -10795,7 +10856,6 @@ const METHOD_NAME_PARTIAL_LOSS_AVAILABLE = "ActionBase.partialLossAvailable";
10795
10856
  const METHOD_NAME_PING_SCHEDULED = "ActionBase.pingScheduled";
10796
10857
  const METHOD_NAME_PING_ACTIVE = "ActionBase.pingActive";
10797
10858
  const METHOD_NAME_RISK_REJECTION = "ActionBase.riskRejection";
10798
- const METHOD_NAME_SIGNAL_SYNC = "ActionBase.signalSync";
10799
10859
  const METHOD_NAME_DISPOSE = "ActionBase.dispose";
10800
10860
  const DEFAULT_SOURCE = "default";
10801
10861
  /**
@@ -11207,6 +11267,11 @@ class ActionProxy {
11207
11267
  */
11208
11268
  async signalSync(event) {
11209
11269
  if (this._target.signalSync) {
11270
+ console.error("Action::signalSync is unwanted cause exchange integration should be implemented in Broker.useBrokerAdapter as an infrastructure domain layer");
11271
+ console.error("If you need to implement custom logic on signal open/close, please use signal(), signalBacktest(), signalLive()");
11272
+ console.error("If Action::signalSync throws the exchange will not execute the order!");
11273
+ console.error("");
11274
+ console.error("You have been warned!");
11210
11275
  await this._target.signalSync(event);
11211
11276
  }
11212
11277
  }
@@ -11669,19 +11734,6 @@ class ActionBase {
11669
11734
  source,
11670
11735
  });
11671
11736
  }
11672
- /**
11673
- * Gate for position open/close via limit order. Default allows all.
11674
- * Throw to reject — framework retries next tick.
11675
- *
11676
- * NOTE: Exceptions are NOT swallowed — they propagate to CREATE_SYNC_FN.
11677
- *
11678
- * @param event - Sync event with action "signal-open" or "signal-close"
11679
- */
11680
- signalSync(_event, source = DEFAULT_SOURCE) {
11681
- bt.loggerService.info(METHOD_NAME_SIGNAL_SYNC, {
11682
- source,
11683
- });
11684
- }
11685
11737
  /**
11686
11738
  * Cleans up resources and subscriptions when action handler is disposed.
11687
11739
  *
@@ -12960,6 +13012,14 @@ class StrategyCoreService {
12960
13012
  await this.validate(context);
12961
13013
  return await this.strategyConnectionService.getPositionPartials(backtest, symbol, context);
12962
13014
  };
13015
+ this.getPositionEntries = async (backtest, symbol, context) => {
13016
+ this.loggerService.log("strategyCoreService getPositionEntries", {
13017
+ symbol,
13018
+ context,
13019
+ });
13020
+ await this.validate(context);
13021
+ return await this.strategyConnectionService.getPositionEntries(backtest, symbol, context);
13022
+ };
12963
13023
  /**
12964
13024
  * Retrieves the currently active scheduled signal for the symbol.
12965
13025
  * If no scheduled signal exists, returns null.
@@ -31704,6 +31764,11 @@ BrokerBase = makeExtendable(BrokerBase);
31704
31764
  */
31705
31765
  const Broker = new BrokerAdapter();
31706
31766
 
31767
+ const POSITION_OVERLAP_LADDER_DEFAULT = {
31768
+ upperPercent: 1.5,
31769
+ lowerPercent: 1.5,
31770
+ };
31771
+
31707
31772
  const CANCEL_SCHEDULED_METHOD_NAME = "strategy.commitCancelScheduled";
31708
31773
  const CLOSE_PENDING_METHOD_NAME = "strategy.commitClosePending";
31709
31774
  const PARTIAL_PROFIT_METHOD_NAME = "strategy.commitPartialProfit";
@@ -31729,6 +31794,8 @@ const GET_POSITION_PNL_PERCENT_METHOD_NAME = "strategy.getPositionPnlPercent";
31729
31794
  const GET_POSITION_PNL_COST_METHOD_NAME = "strategy.getPositionPnlCost";
31730
31795
  const GET_POSITION_LEVELS_METHOD_NAME = "strategy.getPositionLevels";
31731
31796
  const GET_POSITION_PARTIALS_METHOD_NAME = "strategy.getPositionPartials";
31797
+ const GET_POSITION_ENTRY_OVERLAP_METHOD_NAME = "strategy.getPositionEntryOverlap";
31798
+ const GET_POSITION_PARTIAL_OVERLAP_METHOD_NAME = "strategy.getPositionPartialOverlap";
31732
31799
  /**
31733
31800
  * Cancels the scheduled signal without stopping the strategy.
31734
31801
  *
@@ -32005,6 +32072,7 @@ async function commitTrailingStop(symbol, percentShift, currentPrice) {
32005
32072
  percentShift,
32006
32073
  currentPrice,
32007
32074
  newStopLossPrice: slPercentShiftToPrice(percentShift, signal.priceStopLoss, effectivePriceOpen, signal.position),
32075
+ takeProfitPrice: signal.priceTakeProfit,
32008
32076
  position: signal.position,
32009
32077
  context: { exchangeName, frameName, strategyName },
32010
32078
  backtest: isBacktest,
@@ -32084,6 +32152,7 @@ async function commitTrailingTake(symbol, percentShift, currentPrice) {
32084
32152
  percentShift,
32085
32153
  currentPrice,
32086
32154
  newTakeProfitPrice: tpPercentShiftToPrice(percentShift, signal.priceTakeProfit, effectivePriceOpen, signal.position),
32155
+ takeProfitPrice: signal.priceTakeProfit,
32087
32156
  position: signal.position,
32088
32157
  context: { exchangeName, frameName, strategyName },
32089
32158
  backtest: isBacktest,
@@ -32135,6 +32204,7 @@ async function commitTrailingStopCost(symbol, newStopLossPrice) {
32135
32204
  currentPrice,
32136
32205
  newStopLossPrice,
32137
32206
  position: signal.position,
32207
+ takeProfitPrice: signal.priceTakeProfit,
32138
32208
  context: { exchangeName, frameName, strategyName },
32139
32209
  backtest: isBacktest,
32140
32210
  });
@@ -32184,6 +32254,7 @@ async function commitTrailingTakeCost(symbol, newTakeProfitPrice) {
32184
32254
  percentShift,
32185
32255
  currentPrice,
32186
32256
  newTakeProfitPrice,
32257
+ takeProfitPrice: signal.priceTakeProfit,
32187
32258
  position: signal.position,
32188
32259
  context: { exchangeName, frameName, strategyName },
32189
32260
  backtest: isBacktest,
@@ -32510,6 +32581,31 @@ async function getBreakeven(symbol, currentPrice) {
32510
32581
  const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
32511
32582
  return await bt.strategyCoreService.getBreakeven(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName });
32512
32583
  }
32584
+ /**
32585
+ * Returns the effective (DCA-weighted) entry price for the current pending signal.
32586
+ *
32587
+ * Uses cost-weighted harmonic mean: Σcost / Σ(cost/price).
32588
+ * When partial closes exist, the price is computed iteratively using
32589
+ * costBasisAtClose snapshots from each partial, then blended with any
32590
+ * DCA entries added after the last partial.
32591
+ * With no DCA entries, equals the original priceOpen.
32592
+ *
32593
+ * Returns null if no pending signal exists.
32594
+ *
32595
+ * Automatically detects backtest/live mode from execution context.
32596
+ *
32597
+ * @param symbol - Trading pair symbol
32598
+ * @returns Promise resolving to effective entry price or null
32599
+ *
32600
+ * @example
32601
+ * ```typescript
32602
+ * import { getPositionAveragePrice } from "backtest-kit";
32603
+ *
32604
+ * const avgPrice = await getPositionAveragePrice("BTCUSDT");
32605
+ * // No DCA: avgPrice === priceOpen
32606
+ * // After DCA at lower price: avgPrice < priceOpen
32607
+ * ```
32608
+ */
32513
32609
  async function getPositionAveragePrice(symbol) {
32514
32610
  bt.loggerService.info(GET_POSITION_AVERAGE_PRICE_METHOD_NAME, {
32515
32611
  symbol,
@@ -32524,6 +32620,28 @@ async function getPositionAveragePrice(symbol) {
32524
32620
  const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
32525
32621
  return await bt.strategyCoreService.getPositionAveragePrice(isBacktest, symbol, { exchangeName, frameName, strategyName });
32526
32622
  }
32623
+ /**
32624
+ * Returns the number of DCA entries made for the current pending signal.
32625
+ *
32626
+ * 1 = original entry only (no DCA).
32627
+ * Increases by 1 with each successful commitAverageBuy().
32628
+ *
32629
+ * Returns null if no pending signal exists.
32630
+ *
32631
+ * Automatically detects backtest/live mode from execution context.
32632
+ *
32633
+ * @param symbol - Trading pair symbol
32634
+ * @returns Promise resolving to entry count or null
32635
+ *
32636
+ * @example
32637
+ * ```typescript
32638
+ * import { getPositionInvestedCount } from "backtest-kit";
32639
+ *
32640
+ * const count = await getPositionInvestedCount("BTCUSDT");
32641
+ * // No DCA: count === 1
32642
+ * // After one DCA: count === 2
32643
+ * ```
32644
+ */
32527
32645
  async function getPositionInvestedCount(symbol) {
32528
32646
  bt.loggerService.info(GET_POSITION_INVESTED_COUNT_METHOD_NAME, {
32529
32647
  symbol,
@@ -32538,6 +32656,28 @@ async function getPositionInvestedCount(symbol) {
32538
32656
  const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
32539
32657
  return await bt.strategyCoreService.getPositionInvestedCount(isBacktest, symbol, { exchangeName, frameName, strategyName });
32540
32658
  }
32659
+ /**
32660
+ * Returns the total invested cost basis in dollars for the current pending signal.
32661
+ *
32662
+ * Equal to the sum of all _entry costs (Σ entry.cost).
32663
+ * Each entry cost is set at the time of commitAverageBuy (defaults to CC_POSITION_ENTRY_COST).
32664
+ *
32665
+ * Returns null if no pending signal exists.
32666
+ *
32667
+ * Automatically detects backtest/live mode from execution context.
32668
+ *
32669
+ * @param symbol - Trading pair symbol
32670
+ * @returns Promise resolving to total invested cost in dollars or null
32671
+ *
32672
+ * @example
32673
+ * ```typescript
32674
+ * import { getPositionInvestedCost } from "backtest-kit";
32675
+ *
32676
+ * const cost = await getPositionInvestedCost("BTCUSDT");
32677
+ * // No DCA, default cost: cost === 100
32678
+ * // After one DCA with default cost: cost === 200
32679
+ * ```
32680
+ */
32541
32681
  async function getPositionInvestedCost(symbol) {
32542
32682
  bt.loggerService.info(GET_POSITION_INVESTED_COST_METHOD_NAME, {
32543
32683
  symbol,
@@ -32552,6 +32692,29 @@ async function getPositionInvestedCost(symbol) {
32552
32692
  const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
32553
32693
  return await bt.strategyCoreService.getPositionInvestedCost(isBacktest, symbol, { exchangeName, frameName, strategyName });
32554
32694
  }
32695
+ /**
32696
+ * Returns the unrealized PNL percentage for the current pending signal at current market price.
32697
+ *
32698
+ * Accounts for partial closes, DCA entries, slippage and fees
32699
+ * (delegates to toProfitLossDto).
32700
+ *
32701
+ * Returns null if no pending signal exists.
32702
+ *
32703
+ * Automatically detects backtest/live mode from execution context.
32704
+ * Automatically fetches current price via getAveragePrice.
32705
+ *
32706
+ * @param symbol - Trading pair symbol
32707
+ * @returns Promise resolving to PNL percentage or null
32708
+ *
32709
+ * @example
32710
+ * ```typescript
32711
+ * import { getPositionPnlPercent } from "backtest-kit";
32712
+ *
32713
+ * const pnlPct = await getPositionPnlPercent("BTCUSDT");
32714
+ * // LONG at 100, current=105: pnlPct ≈ 5
32715
+ * // LONG at 100, current=95: pnlPct ≈ -5
32716
+ * ```
32717
+ */
32555
32718
  async function getPositionPnlPercent(symbol) {
32556
32719
  bt.loggerService.info(GET_POSITION_PNL_PERCENT_METHOD_NAME, { symbol });
32557
32720
  if (!ExecutionContextService.hasContext()) {
@@ -32701,6 +32864,29 @@ async function commitPartialLossCost(symbol, dollarAmount) {
32701
32864
  });
32702
32865
  return await bt.strategyCoreService.partialLoss(isBacktest, symbol, percentToClose, currentPrice, { exchangeName, frameName, strategyName });
32703
32866
  }
32867
+ /**
32868
+ * Returns the unrealized PNL in dollars for the current pending signal at current market price.
32869
+ *
32870
+ * Calculated as: pnlPercentage / 100 × totalInvestedCost.
32871
+ * Accounts for partial closes, DCA entries, slippage and fees.
32872
+ *
32873
+ * Returns null if no pending signal exists.
32874
+ *
32875
+ * Automatically detects backtest/live mode from execution context.
32876
+ * Automatically fetches current price via getAveragePrice.
32877
+ *
32878
+ * @param symbol - Trading pair symbol
32879
+ * @returns Promise resolving to PNL in dollars or null
32880
+ *
32881
+ * @example
32882
+ * ```typescript
32883
+ * import { getPositionPnlCost } from "backtest-kit";
32884
+ *
32885
+ * const pnlCost = await getPositionPnlCost("BTCUSDT");
32886
+ * // LONG at 100, invested $100, current=105: pnlCost ≈ 5
32887
+ * // LONG at 100, invested $200 (DCA), current=95: pnlCost ≈ -10
32888
+ * ```
32889
+ */
32704
32890
  async function getPositionPnlCost(symbol) {
32705
32891
  bt.loggerService.info(GET_POSITION_PNL_COST_METHOD_NAME, { symbol });
32706
32892
  if (!ExecutionContextService.hasContext()) {
@@ -32787,6 +32973,104 @@ async function getPositionPartials(symbol) {
32787
32973
  const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
32788
32974
  return await bt.strategyCoreService.getPositionPartials(isBacktest, symbol, { exchangeName, frameName, strategyName });
32789
32975
  }
32976
+ /**
32977
+ * Checks whether the current price falls within the tolerance zone of any existing DCA entry level.
32978
+ * Use this to prevent duplicate DCA entries at the same price area.
32979
+ *
32980
+ * Returns true if currentPrice is within [level - lowerStep, level + upperStep] for any level,
32981
+ * where step = level * percent / 100.
32982
+ * Returns false if no pending signal exists.
32983
+ *
32984
+ * @param symbol - Trading pair symbol
32985
+ * @param currentPrice - Price to check against existing DCA levels
32986
+ * @param ladder - Tolerance zone config; percentages in 0–100 format (default: 1.5% up and down)
32987
+ * @returns Promise<boolean> - true if price overlaps an existing entry level (DCA not recommended)
32988
+ *
32989
+ * @example
32990
+ * ```typescript
32991
+ * import { getPositionEntryOverlap } from "backtest-kit";
32992
+ *
32993
+ * // LONG with levels [43000, 42000], check if 42100 is too close to 42000
32994
+ * const overlap = await getPositionEntryOverlap("BTCUSDT", 42100, { upperPercent: 5, lowerPercent: 5 });
32995
+ * // overlap = true (42100 is within 5% of 42000 = [39900, 44100])
32996
+ * if (!overlap) {
32997
+ * await commitAverageBuy("BTCUSDT");
32998
+ * }
32999
+ * ```
33000
+ */
33001
+ async function getPositionEntryOverlap(symbol, currentPrice, ladder = POSITION_OVERLAP_LADDER_DEFAULT) {
33002
+ bt.loggerService.info(GET_POSITION_ENTRY_OVERLAP_METHOD_NAME, {
33003
+ symbol,
33004
+ currentPrice,
33005
+ ladder,
33006
+ });
33007
+ if (!ExecutionContextService.hasContext()) {
33008
+ throw new Error("getPositionEntryOverlap requires an execution context");
33009
+ }
33010
+ if (!MethodContextService.hasContext()) {
33011
+ throw new Error("getPositionEntryOverlap requires a method context");
33012
+ }
33013
+ const { backtest: isBacktest } = bt.executionContextService.context;
33014
+ const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
33015
+ const levels = await bt.strategyCoreService.getPositionLevels(isBacktest, symbol, { exchangeName, frameName, strategyName });
33016
+ if (!levels) {
33017
+ return false;
33018
+ }
33019
+ return levels.some((level) => {
33020
+ const upperStep = (level * ladder.upperPercent) / 100;
33021
+ const lowerStep = (level * ladder.lowerPercent) / 100;
33022
+ return currentPrice >= level - lowerStep && currentPrice <= level + upperStep;
33023
+ });
33024
+ }
33025
+ /**
33026
+ * Checks whether the current price falls within the tolerance zone of any existing partial close price.
33027
+ * Use this to prevent duplicate partial closes at the same price area.
33028
+ *
33029
+ * Returns true if currentPrice is within [partial.currentPrice - lowerStep, partial.currentPrice + upperStep]
33030
+ * for any partial, where step = partial.currentPrice * percent / 100.
33031
+ * Returns false if no pending signal exists or no partials have been executed yet.
33032
+ *
33033
+ * @param symbol - Trading pair symbol
33034
+ * @param currentPrice - Price to check against existing partial close prices
33035
+ * @param ladder - Tolerance zone config; percentages in 0–100 format (default: 1.5% up and down)
33036
+ * @returns Promise<boolean> - true if price overlaps an existing partial price (partial not recommended)
33037
+ *
33038
+ * @example
33039
+ * ```typescript
33040
+ * import { getPositionPartialOverlap } from "backtest-kit";
33041
+ *
33042
+ * // Partials at [45000], check if 45100 is too close
33043
+ * const overlap = await getPositionPartialOverlap("BTCUSDT", 45100, { upperPercent: 1.5, lowerPercent: 1.5 });
33044
+ * // overlap = true (45100 is within 1.5% of 45000)
33045
+ * if (!overlap) {
33046
+ * await commitPartialProfit("BTCUSDT", 50);
33047
+ * }
33048
+ * ```
33049
+ */
33050
+ async function getPositionPartialOverlap(symbol, currentPrice, ladder = POSITION_OVERLAP_LADDER_DEFAULT) {
33051
+ bt.loggerService.info(GET_POSITION_PARTIAL_OVERLAP_METHOD_NAME, {
33052
+ symbol,
33053
+ currentPrice,
33054
+ ladder,
33055
+ });
33056
+ if (!ExecutionContextService.hasContext()) {
33057
+ throw new Error("getPositionPartialOverlap requires an execution context");
33058
+ }
33059
+ if (!MethodContextService.hasContext()) {
33060
+ throw new Error("getPositionPartialOverlap requires a method context");
33061
+ }
33062
+ const { backtest: isBacktest } = bt.executionContextService.context;
33063
+ const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
33064
+ const partials = await bt.strategyCoreService.getPositionPartials(isBacktest, symbol, { exchangeName, frameName, strategyName });
33065
+ if (!partials) {
33066
+ return false;
33067
+ }
33068
+ return partials.some((partial) => {
33069
+ const upperStep = (partial.currentPrice * ladder.upperPercent) / 100;
33070
+ const lowerStep = (partial.currentPrice * ladder.lowerPercent) / 100;
33071
+ return currentPrice >= partial.currentPrice - lowerStep && currentPrice <= partial.currentPrice + upperStep;
33072
+ });
33073
+ }
32790
33074
 
32791
33075
  const STOP_STRATEGY_METHOD_NAME = "control.stopStrategy";
32792
33076
  /**
@@ -34012,6 +34296,9 @@ const BACKTEST_METHOD_NAME_GET_POSITION_PNL_PERCENT = "BacktestUtils.getPosition
34012
34296
  const BACKTEST_METHOD_NAME_GET_POSITION_PNL_COST = "BacktestUtils.getPositionPnlCost";
34013
34297
  const BACKTEST_METHOD_NAME_GET_POSITION_LEVELS = "BacktestUtils.getPositionLevels";
34014
34298
  const BACKTEST_METHOD_NAME_GET_POSITION_PARTIALS = "BacktestUtils.getPositionPartials";
34299
+ const BACKTEST_METHOD_NAME_GET_POSITION_ENTRIES = "BacktestUtils.getPositionEntries";
34300
+ const BACKTEST_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP = "BacktestUtils.getPositionEntryOverlap";
34301
+ const BACKTEST_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP = "BacktestUtils.getPositionPartialOverlap";
34015
34302
  const BACKTEST_METHOD_NAME_BREAKEVEN = "Backtest.commitBreakeven";
34016
34303
  const BACKTEST_METHOD_NAME_CANCEL_SCHEDULED = "Backtest.commitCancelScheduled";
34017
34304
  const BACKTEST_METHOD_NAME_CLOSE_PENDING = "Backtest.commitClosePending";
@@ -34767,6 +35054,125 @@ class BacktestUtils {
34767
35054
  }
34768
35055
  return await bt.strategyCoreService.getPositionPartials(true, symbol, context);
34769
35056
  };
35057
+ /**
35058
+ * Returns the list of DCA entry prices and costs for the current pending signal.
35059
+ *
35060
+ * Each element represents a single position entry — the initial open or a subsequent
35061
+ * DCA entry added via commitAverageBuy.
35062
+ *
35063
+ * Returns null if no pending signal exists.
35064
+ * Returns a single-element array if no DCA entries were made.
35065
+ *
35066
+ * Each entry contains:
35067
+ * - `price` — execution price of this entry
35068
+ * - `cost` — dollar cost allocated to this entry (e.g. 100 for $100)
35069
+ *
35070
+ * @param symbol - Trading pair symbol
35071
+ * @param context - Execution context with strategyName, exchangeName, and frameName
35072
+ * @returns Array of entry records, or null if no active position
35073
+ */
35074
+ this.getPositionEntries = async (symbol, context) => {
35075
+ bt.loggerService.info(BACKTEST_METHOD_NAME_GET_POSITION_ENTRIES, {
35076
+ symbol,
35077
+ context,
35078
+ });
35079
+ bt.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_GET_POSITION_ENTRIES);
35080
+ bt.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_GET_POSITION_ENTRIES);
35081
+ {
35082
+ const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
35083
+ riskName &&
35084
+ bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_POSITION_ENTRIES);
35085
+ riskList &&
35086
+ riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_POSITION_ENTRIES));
35087
+ actions &&
35088
+ actions.forEach((actionName) => bt.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_GET_POSITION_ENTRIES));
35089
+ }
35090
+ return await bt.strategyCoreService.getPositionEntries(true, symbol, context);
35091
+ };
35092
+ /**
35093
+ * Checks whether the current price falls within the tolerance zone of any existing DCA entry level.
35094
+ * Use this to prevent duplicate DCA entries at the same price area.
35095
+ *
35096
+ * Returns true if currentPrice is within [level - lowerStep, level + upperStep] for any level,
35097
+ * where step = level * percent / 100.
35098
+ * Returns false if no pending signal exists.
35099
+ *
35100
+ * @param symbol - Trading pair symbol
35101
+ * @param currentPrice - Price to check against existing DCA levels
35102
+ * @param context - Execution context with strategyName, exchangeName, and frameName
35103
+ * @param ladder - Tolerance zone config; percentages in 0–100 format (default: 1.5% up and down)
35104
+ * @returns true if price overlaps an existing entry level (DCA not recommended)
35105
+ */
35106
+ this.getPositionEntryOverlap = async (symbol, currentPrice, context, ladder = POSITION_OVERLAP_LADDER_DEFAULT) => {
35107
+ bt.loggerService.info(BACKTEST_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP, {
35108
+ symbol,
35109
+ currentPrice,
35110
+ context,
35111
+ ladder,
35112
+ });
35113
+ bt.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP);
35114
+ bt.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP);
35115
+ {
35116
+ const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
35117
+ riskName &&
35118
+ bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP);
35119
+ riskList &&
35120
+ riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP));
35121
+ actions &&
35122
+ actions.forEach((actionName) => bt.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP));
35123
+ }
35124
+ const levels = await bt.strategyCoreService.getPositionLevels(true, symbol, context);
35125
+ if (!levels) {
35126
+ return false;
35127
+ }
35128
+ return levels.some((level) => {
35129
+ const upperStep = (level * ladder.upperPercent) / 100;
35130
+ const lowerStep = (level * ladder.lowerPercent) / 100;
35131
+ return currentPrice >= level - lowerStep && currentPrice <= level + upperStep;
35132
+ });
35133
+ };
35134
+ /**
35135
+ * Checks whether the current price falls within the tolerance zone of any existing partial close price.
35136
+ * Use this to prevent duplicate partial closes at the same price area.
35137
+ *
35138
+ * Returns true if currentPrice is within [partial.currentPrice - lowerStep, partial.currentPrice + upperStep]
35139
+ * for any partial, where step = partial.currentPrice * percent / 100.
35140
+ * Returns false if no pending signal exists or no partials have been executed yet.
35141
+ *
35142
+ * @param symbol - Trading pair symbol
35143
+ * @param currentPrice - Price to check against existing partial close prices
35144
+ * @param context - Execution context with strategyName, exchangeName, and frameName
35145
+ * @param ladder - Tolerance zone config; percentages in 0–100 format (default: 1.5% up and down)
35146
+ * @returns true if price overlaps an existing partial price (partial not recommended)
35147
+ */
35148
+ this.getPositionPartialOverlap = async (symbol, currentPrice, context, ladder = POSITION_OVERLAP_LADDER_DEFAULT) => {
35149
+ bt.loggerService.info(BACKTEST_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP, {
35150
+ symbol,
35151
+ currentPrice,
35152
+ context,
35153
+ ladder,
35154
+ });
35155
+ bt.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP);
35156
+ bt.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP);
35157
+ {
35158
+ const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
35159
+ riskName &&
35160
+ bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP);
35161
+ riskList &&
35162
+ riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP));
35163
+ actions &&
35164
+ actions.forEach((actionName) => bt.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP));
35165
+ }
35166
+ const partials = await bt.strategyCoreService.getPositionPartials(true, symbol, context);
35167
+ if (!partials) {
35168
+ return false;
35169
+ }
35170
+ return partials.some((partial) => {
35171
+ const upperStep = (partial.currentPrice * ladder.upperPercent) / 100;
35172
+ const lowerStep = (partial.currentPrice * ladder.lowerPercent) / 100;
35173
+ return currentPrice >= partial.currentPrice - lowerStep && currentPrice <= partial.currentPrice + upperStep;
35174
+ });
35175
+ };
34770
35176
  /**
34771
35177
  * Stops the strategy from generating new signals.
34772
35178
  *
@@ -35256,6 +35662,7 @@ class BacktestUtils {
35256
35662
  percentShift,
35257
35663
  currentPrice,
35258
35664
  newStopLossPrice: slPercentShiftToPrice(percentShift, signal.priceStopLoss, effectivePriceOpen, signal.position),
35665
+ takeProfitPrice: signal.priceTakeProfit,
35259
35666
  position: signal.position,
35260
35667
  context,
35261
35668
  backtest: true,
@@ -35340,6 +35747,7 @@ class BacktestUtils {
35340
35747
  percentShift,
35341
35748
  currentPrice,
35342
35749
  newTakeProfitPrice: tpPercentShiftToPrice(percentShift, signal.priceTakeProfit, effectivePriceOpen, signal.position),
35750
+ takeProfitPrice: signal.priceTakeProfit,
35343
35751
  position: signal.position,
35344
35752
  context,
35345
35753
  backtest: true,
@@ -35394,6 +35802,7 @@ class BacktestUtils {
35394
35802
  currentPrice,
35395
35803
  newStopLossPrice,
35396
35804
  position: signal.position,
35805
+ takeProfitPrice: signal.priceTakeProfit,
35397
35806
  context,
35398
35807
  backtest: true,
35399
35808
  });
@@ -35447,6 +35856,7 @@ class BacktestUtils {
35447
35856
  currentPrice,
35448
35857
  newTakeProfitPrice,
35449
35858
  position: signal.position,
35859
+ takeProfitPrice: signal.priceTakeProfit,
35450
35860
  context,
35451
35861
  backtest: true,
35452
35862
  });
@@ -35789,6 +36199,9 @@ const LIVE_METHOD_NAME_GET_POSITION_PNL_PERCENT = "LiveUtils.getPositionPnlPerce
35789
36199
  const LIVE_METHOD_NAME_GET_POSITION_PNL_COST = "LiveUtils.getPositionPnlCost";
35790
36200
  const LIVE_METHOD_NAME_GET_POSITION_LEVELS = "LiveUtils.getPositionLevels";
35791
36201
  const LIVE_METHOD_NAME_GET_POSITION_PARTIALS = "LiveUtils.getPositionPartials";
36202
+ const LIVE_METHOD_NAME_GET_POSITION_ENTRIES = "LiveUtils.getPositionEntries";
36203
+ const LIVE_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP = "LiveUtils.getPositionEntryOverlap";
36204
+ const LIVE_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP = "LiveUtils.getPositionPartialOverlap";
35792
36205
  const LIVE_METHOD_NAME_BREAKEVEN = "Live.commitBreakeven";
35793
36206
  const LIVE_METHOD_NAME_CANCEL_SCHEDULED = "Live.cancelScheduled";
35794
36207
  const LIVE_METHOD_NAME_CLOSE_PENDING = "Live.closePending";
@@ -36603,6 +37016,137 @@ class LiveUtils {
36603
37016
  frameName: "",
36604
37017
  });
36605
37018
  };
37019
+ /**
37020
+ * Returns the list of DCA entry prices and costs for the current pending signal.
37021
+ *
37022
+ * Each element represents a single position entry — the initial open or a subsequent
37023
+ * DCA entry added via commitAverageBuy.
37024
+ *
37025
+ * Returns null if no pending signal exists.
37026
+ * Returns a single-element array if no DCA entries were made.
37027
+ *
37028
+ * Each entry contains:
37029
+ * - `price` — execution price of this entry
37030
+ * - `cost` — dollar cost allocated to this entry (e.g. 100 for $100)
37031
+ *
37032
+ * @param symbol - Trading pair symbol
37033
+ * @param context - Execution context with strategyName and exchangeName
37034
+ * @returns Array of entry records, or null if no active position
37035
+ */
37036
+ this.getPositionEntries = async (symbol, context) => {
37037
+ bt.loggerService.info(LIVE_METHOD_NAME_GET_POSITION_ENTRIES, {
37038
+ symbol,
37039
+ context,
37040
+ });
37041
+ bt.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_GET_POSITION_ENTRIES);
37042
+ bt.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_GET_POSITION_ENTRIES);
37043
+ {
37044
+ const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
37045
+ riskName &&
37046
+ bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_POSITION_ENTRIES);
37047
+ riskList &&
37048
+ riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_POSITION_ENTRIES));
37049
+ actions &&
37050
+ actions.forEach((actionName) => bt.actionValidationService.validate(actionName, LIVE_METHOD_NAME_GET_POSITION_ENTRIES));
37051
+ }
37052
+ return await bt.strategyCoreService.getPositionEntries(false, symbol, {
37053
+ strategyName: context.strategyName,
37054
+ exchangeName: context.exchangeName,
37055
+ frameName: "",
37056
+ });
37057
+ };
37058
+ /**
37059
+ * Checks whether the current price falls within the tolerance zone of any existing DCA entry level.
37060
+ * Use this to prevent duplicate DCA entries at the same price area.
37061
+ *
37062
+ * Returns true if currentPrice is within [level - lowerStep, level + upperStep] for any level,
37063
+ * where step = level * percent / 100.
37064
+ * Returns false if no pending signal exists.
37065
+ *
37066
+ * @param symbol - Trading pair symbol
37067
+ * @param currentPrice - Price to check against existing DCA levels
37068
+ * @param context - Execution context with strategyName and exchangeName
37069
+ * @param ladder - Tolerance zone config; percentages in 0–100 format (default: 1.5% up and down)
37070
+ * @returns true if price overlaps an existing entry level (DCA not recommended)
37071
+ */
37072
+ this.getPositionEntryOverlap = async (symbol, currentPrice, context, ladder = POSITION_OVERLAP_LADDER_DEFAULT) => {
37073
+ bt.loggerService.info(LIVE_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP, {
37074
+ symbol,
37075
+ currentPrice,
37076
+ context,
37077
+ ladder,
37078
+ });
37079
+ bt.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP);
37080
+ bt.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP);
37081
+ {
37082
+ const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
37083
+ riskName &&
37084
+ bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP);
37085
+ riskList &&
37086
+ riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP));
37087
+ actions &&
37088
+ actions.forEach((actionName) => bt.actionValidationService.validate(actionName, LIVE_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP));
37089
+ }
37090
+ const levels = await bt.strategyCoreService.getPositionLevels(false, symbol, {
37091
+ strategyName: context.strategyName,
37092
+ exchangeName: context.exchangeName,
37093
+ frameName: "",
37094
+ });
37095
+ if (!levels) {
37096
+ return false;
37097
+ }
37098
+ return levels.some((level) => {
37099
+ const upperStep = (level * ladder.upperPercent) / 100;
37100
+ const lowerStep = (level * ladder.lowerPercent) / 100;
37101
+ return currentPrice >= level - lowerStep && currentPrice <= level + upperStep;
37102
+ });
37103
+ };
37104
+ /**
37105
+ * Checks whether the current price falls within the tolerance zone of any existing partial close price.
37106
+ * Use this to prevent duplicate partial closes at the same price area.
37107
+ *
37108
+ * Returns true if currentPrice is within [partial.currentPrice - lowerStep, partial.currentPrice + upperStep]
37109
+ * for any partial, where step = partial.currentPrice * percent / 100.
37110
+ * Returns false if no pending signal exists or no partials have been executed yet.
37111
+ *
37112
+ * @param symbol - Trading pair symbol
37113
+ * @param currentPrice - Price to check against existing partial close prices
37114
+ * @param context - Execution context with strategyName and exchangeName
37115
+ * @param ladder - Tolerance zone config; percentages in 0–100 format (default: 1.5% up and down)
37116
+ * @returns true if price overlaps an existing partial price (partial not recommended)
37117
+ */
37118
+ this.getPositionPartialOverlap = async (symbol, currentPrice, context, ladder = POSITION_OVERLAP_LADDER_DEFAULT) => {
37119
+ bt.loggerService.info(LIVE_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP, {
37120
+ symbol,
37121
+ currentPrice,
37122
+ context,
37123
+ ladder,
37124
+ });
37125
+ bt.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP);
37126
+ bt.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP);
37127
+ {
37128
+ const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
37129
+ riskName &&
37130
+ bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP);
37131
+ riskList &&
37132
+ riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP));
37133
+ actions &&
37134
+ actions.forEach((actionName) => bt.actionValidationService.validate(actionName, LIVE_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP));
37135
+ }
37136
+ const partials = await bt.strategyCoreService.getPositionPartials(false, symbol, {
37137
+ strategyName: context.strategyName,
37138
+ exchangeName: context.exchangeName,
37139
+ frameName: "",
37140
+ });
37141
+ if (!partials) {
37142
+ return false;
37143
+ }
37144
+ return partials.some((partial) => {
37145
+ const upperStep = (partial.currentPrice * ladder.upperPercent) / 100;
37146
+ const lowerStep = (partial.currentPrice * ladder.lowerPercent) / 100;
37147
+ return currentPrice >= partial.currentPrice - lowerStep && currentPrice <= partial.currentPrice + upperStep;
37148
+ });
37149
+ };
36606
37150
  /**
36607
37151
  * Stops the strategy from generating new signals.
36608
37152
  *
@@ -37185,6 +37729,7 @@ class LiveUtils {
37185
37729
  percentShift,
37186
37730
  currentPrice,
37187
37731
  newStopLossPrice: slPercentShiftToPrice(percentShift, signal.priceStopLoss, effectivePriceOpen, signal.position),
37732
+ takeProfitPrice: signal.priceTakeProfit,
37188
37733
  position: signal.position,
37189
37734
  context,
37190
37735
  backtest: false,
@@ -37284,6 +37829,7 @@ class LiveUtils {
37284
37829
  percentShift,
37285
37830
  currentPrice,
37286
37831
  newTakeProfitPrice: tpPercentShiftToPrice(percentShift, signal.priceTakeProfit, effectivePriceOpen, signal.position),
37832
+ takeProfitPrice: signal.priceTakeProfit,
37287
37833
  position: signal.position,
37288
37834
  context,
37289
37835
  backtest: false,
@@ -37353,6 +37899,7 @@ class LiveUtils {
37353
37899
  percentShift,
37354
37900
  currentPrice,
37355
37901
  newStopLossPrice,
37902
+ takeProfitPrice: signal.priceTakeProfit,
37356
37903
  position: signal.position,
37357
37904
  context,
37358
37905
  backtest: false,
@@ -37422,6 +37969,7 @@ class LiveUtils {
37422
37969
  percentShift,
37423
37970
  currentPrice,
37424
37971
  newTakeProfitPrice,
37972
+ takeProfitPrice: signal.priceTakeProfit,
37425
37973
  position: signal.position,
37426
37974
  context,
37427
37975
  backtest: false,
@@ -45332,4 +45880,4 @@ const percentValue = (yesterdayValue, todayValue) => {
45332
45880
  return yesterdayValue / todayValue - 1;
45333
45881
  };
45334
45882
 
45335
- export { ActionBase, Backtest, Breakeven, Broker, BrokerBase, Cache, Constant, Exchange, ExecutionContextService, Heat, Live, Log, Markdown, MarkdownFileBase, MarkdownFolderBase, MethodContextService, Notification, NotificationBacktest, NotificationLive, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistCandleAdapter, PersistLogAdapter, PersistMeasureAdapter, PersistNotificationAdapter, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PersistStorageAdapter, PositionSize, Report, ReportBase, 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, dumpMessages, emitters, formatPrice, formatQuantity, get, getActionSchema, getAggregatedTrades, getAveragePrice, getBacktestTimeframe, getBreakeven, getCandles, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getEffectivePriceOpen, getExchangeSchema, getFrameSchema, getMode, getNextCandles, getOrderBook, getPendingSignal, getPositionAveragePrice, getPositionInvestedCost, getPositionInvestedCount, getPositionLevels, getPositionPartials, getPositionPnlCost, getPositionPnlPercent, getRawCandles, getRiskSchema, getScheduledSignal, getSizingSchema, getStrategySchema, getSymbol, getTimestamp, getTotalClosed, getTotalCostClosed, getTotalPercentClosed, getWalkerSchema, hasTradeContext, investedCostToPercent, backtest as lib, listExchangeSchema, listFrameSchema, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenBacktestProgress, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, 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, roundTicks, set, setColumns, setConfig, setLogger, shutdown, slPercentShiftToPrice, slPriceToPercentShift, stopStrategy, toProfitLossDto, tpPercentShiftToPrice, tpPriceToPercentShift, validate, waitForCandle, warmCandles };
45883
+ export { ActionBase, Backtest, Breakeven, Broker, BrokerBase, Cache, Constant, Exchange, ExecutionContextService, Heat, Live, Log, Markdown, MarkdownFileBase, MarkdownFolderBase, MethodContextService, Notification, NotificationBacktest, NotificationLive, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistCandleAdapter, PersistLogAdapter, PersistMeasureAdapter, PersistNotificationAdapter, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PersistStorageAdapter, PositionSize, Report, ReportBase, 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, dumpMessages, emitters, formatPrice, formatQuantity, get, getActionSchema, getAggregatedTrades, getAveragePrice, getBacktestTimeframe, getBreakeven, getCandles, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getEffectivePriceOpen, getExchangeSchema, getFrameSchema, getMode, getNextCandles, getOrderBook, getPendingSignal, getPositionAveragePrice, getPositionEntryOverlap, getPositionInvestedCost, getPositionInvestedCount, getPositionLevels, getPositionPartialOverlap, getPositionPartials, getPositionPnlCost, getPositionPnlPercent, getRawCandles, getRiskSchema, getScheduledSignal, getSizingSchema, getStrategySchema, getSymbol, getTimestamp, getTotalClosed, getTotalCostClosed, getTotalPercentClosed, getWalkerSchema, hasTradeContext, investedCostToPercent, backtest as lib, listExchangeSchema, listFrameSchema, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenBacktestProgress, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, 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, roundTicks, set, setColumns, setConfig, setLogger, shutdown, slPercentShiftToPrice, slPriceToPercentShift, stopStrategy, toProfitLossDto, tpPercentShiftToPrice, tpPriceToPercentShift, validate, waitForCandle, warmCandles };