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.cjs CHANGED
@@ -470,9 +470,17 @@ const GLOBAL_CONFIG = {
470
470
  * Allows to commitAverageBuy if currentPrice is not the lowest price since entry, but still lower than priceOpen.
471
471
  * 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.
472
472
  *
473
- * Default: true (DCA logic enabled everywhere, not just when antirecord is broken)
473
+ * Default: false (DCA logic enabled only when antirecord is broken)
474
474
  */
475
475
  CC_ENABLE_DCA_EVERYWHERE: false,
476
+ /**
477
+ * Enables PPPL (Partial Profit, Partial Loss) logic even if this breaks a direction of exits
478
+ * Allows to take partial profit or loss on a position even if it results in a mix of profit and loss exits
479
+ * This can help lock in profits or cut losses on part of the position without waiting for a perfect exit scenario.
480
+ *
481
+ * Default: false (PPPL logic is only applied when it does not break the direction of exits, ensuring clearer profit/loss outcomes)
482
+ */
483
+ CC_ENABLE_PPPL_EVERYWHERE: false,
476
484
  /**
477
485
  * Cost of entering a position (in USD).
478
486
  * This is used as a default value for calculating position size and risk management when cost data is not provided by the strategy
@@ -6630,6 +6638,18 @@ class ClientStrategy {
6630
6638
  }
6631
6639
  return entries.map((e) => e.price);
6632
6640
  }
6641
+ /**
6642
+ * Returns the list of partial closes for the current pending signal.
6643
+ *
6644
+ * Each entry records a partial profit or loss close event with its type,
6645
+ * percent closed, price at close, cost basis snapshot, and entry count at close.
6646
+ *
6647
+ * Returns null if no pending signal exists.
6648
+ * Returns an empty array if no partial closes have been executed.
6649
+ *
6650
+ * @param symbol - Trading pair symbol
6651
+ * @returns Promise resolving to array of partial close records or null
6652
+ */
6633
6653
  async getPositionPartials(symbol) {
6634
6654
  this.params.logger.debug("ClientStrategy getPositionPartials", { symbol });
6635
6655
  if (!this._pendingSignal) {
@@ -6637,6 +6657,34 @@ class ClientStrategy {
6637
6657
  }
6638
6658
  return this._pendingSignal._partial ?? [];
6639
6659
  }
6660
+ /**
6661
+ * Returns the list of DCA entry prices and costs for the current pending signal.
6662
+ *
6663
+ * Each entry records the price and cost of a single position entry.
6664
+ * The first element is always the original priceOpen (initial entry).
6665
+ * Each subsequent element is an entry added by averageBuy().
6666
+ *
6667
+ * Returns null if no pending signal exists.
6668
+ * Returns a single-element array [{ price: priceOpen, cost }] if no DCA entries were made.
6669
+ *
6670
+ * @param symbol - Trading pair symbol
6671
+ * @returns Promise resolving to array of entry records or null
6672
+ *
6673
+ * @example
6674
+ * // No DCA: [{ price: 43000, cost: 100 }]
6675
+ * // One DCA: [{ price: 43000, cost: 100 }, { price: 42000, cost: 100 }]
6676
+ */
6677
+ async getPositionEntries(symbol) {
6678
+ this.params.logger.debug("ClientStrategy getPositionEntries", { symbol });
6679
+ if (!this._pendingSignal) {
6680
+ return null;
6681
+ }
6682
+ const entries = this._pendingSignal._entry;
6683
+ if (!entries || entries.length === 0) {
6684
+ return [{ price: this._pendingSignal.priceOpen, cost: GLOBAL_CONFIG.CC_POSITION_ENTRY_COST }];
6685
+ }
6686
+ return entries.map(({ price, cost }) => ({ price, cost }));
6687
+ }
6640
6688
  /**
6641
6689
  * Performs a single tick of strategy execution.
6642
6690
  *
@@ -7408,10 +7456,12 @@ class ClientStrategy {
7408
7456
  if (typeof currentPrice !== "number" || !isFinite(currentPrice) || currentPrice <= 0)
7409
7457
  return false;
7410
7458
  const effectivePriceOpen = getEffectivePriceOpen(this._pendingSignal);
7411
- if (this._pendingSignal.position === "long" && currentPrice <= effectivePriceOpen)
7412
- return false;
7413
- if (this._pendingSignal.position === "short" && currentPrice >= effectivePriceOpen)
7414
- return false;
7459
+ if (!GLOBAL_CONFIG.CC_ENABLE_PPPL_EVERYWHERE) {
7460
+ if (this._pendingSignal.position === "long" && currentPrice <= effectivePriceOpen)
7461
+ return false;
7462
+ if (this._pendingSignal.position === "short" && currentPrice >= effectivePriceOpen)
7463
+ return false;
7464
+ }
7415
7465
  const effectiveTakeProfit = this._pendingSignal._trailingPriceTakeProfit ?? this._pendingSignal.priceTakeProfit;
7416
7466
  if (this._pendingSignal.position === "long" && currentPrice >= effectiveTakeProfit)
7417
7467
  return false;
@@ -7495,7 +7545,7 @@ class ClientStrategy {
7495
7545
  throw new Error(`ClientStrategy partialProfit: currentPrice must be a positive finite number, got ${currentPrice}`);
7496
7546
  }
7497
7547
  // Validation: currentPrice must be moving toward TP (profit direction)
7498
- {
7548
+ if (!GLOBAL_CONFIG.CC_ENABLE_PPPL_EVERYWHERE) {
7499
7549
  const effectivePriceOpen = getEffectivePriceOpen(this._pendingSignal);
7500
7550
  if (this._pendingSignal.position === "long") {
7501
7551
  // For LONG: currentPrice must be higher than effectivePriceOpen (moving toward TP)
@@ -7589,10 +7639,12 @@ class ClientStrategy {
7589
7639
  if (typeof currentPrice !== "number" || !isFinite(currentPrice) || currentPrice <= 0)
7590
7640
  return false;
7591
7641
  const effectivePriceOpen = getEffectivePriceOpen(this._pendingSignal);
7592
- if (this._pendingSignal.position === "long" && currentPrice >= effectivePriceOpen)
7593
- return false;
7594
- if (this._pendingSignal.position === "short" && currentPrice <= effectivePriceOpen)
7595
- return false;
7642
+ if (!GLOBAL_CONFIG.CC_ENABLE_PPPL_EVERYWHERE) {
7643
+ if (this._pendingSignal.position === "long" && currentPrice >= effectivePriceOpen)
7644
+ return false;
7645
+ if (this._pendingSignal.position === "short" && currentPrice <= effectivePriceOpen)
7646
+ return false;
7647
+ }
7596
7648
  const effectiveStopLoss = this._pendingSignal._trailingPriceStopLoss ?? this._pendingSignal.priceStopLoss;
7597
7649
  if (this._pendingSignal.position === "long" && currentPrice <= effectiveStopLoss)
7598
7650
  return false;
@@ -7676,7 +7728,7 @@ class ClientStrategy {
7676
7728
  throw new Error(`ClientStrategy partialLoss: currentPrice must be a positive finite number, got ${currentPrice}`);
7677
7729
  }
7678
7730
  // Validation: currentPrice must be moving toward SL (loss direction)
7679
- {
7731
+ if (!GLOBAL_CONFIG.CC_ENABLE_PPPL_EVERYWHERE) {
7680
7732
  const effectivePriceOpen = getEffectivePriceOpen(this._pendingSignal);
7681
7733
  if (this._pendingSignal.position === "long") {
7682
7734
  // For LONG: currentPrice must be lower than effectivePriceOpen (moving toward SL)
@@ -9291,6 +9343,15 @@ class StrategyConnectionService {
9291
9343
  const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
9292
9344
  return await strategy.getPositionPartials(symbol);
9293
9345
  };
9346
+ this.getPositionEntries = async (backtest, symbol, context) => {
9347
+ this.loggerService.log("strategyConnectionService getPositionEntries", {
9348
+ symbol,
9349
+ context,
9350
+ backtest,
9351
+ });
9352
+ const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
9353
+ return await strategy.getPositionEntries(symbol);
9354
+ };
9294
9355
  /**
9295
9356
  * Retrieves the currently active scheduled signal for the strategy.
9296
9357
  * If no scheduled signal exists, returns null.
@@ -10815,7 +10876,6 @@ const METHOD_NAME_PARTIAL_LOSS_AVAILABLE = "ActionBase.partialLossAvailable";
10815
10876
  const METHOD_NAME_PING_SCHEDULED = "ActionBase.pingScheduled";
10816
10877
  const METHOD_NAME_PING_ACTIVE = "ActionBase.pingActive";
10817
10878
  const METHOD_NAME_RISK_REJECTION = "ActionBase.riskRejection";
10818
- const METHOD_NAME_SIGNAL_SYNC = "ActionBase.signalSync";
10819
10879
  const METHOD_NAME_DISPOSE = "ActionBase.dispose";
10820
10880
  const DEFAULT_SOURCE = "default";
10821
10881
  /**
@@ -11227,6 +11287,11 @@ class ActionProxy {
11227
11287
  */
11228
11288
  async signalSync(event) {
11229
11289
  if (this._target.signalSync) {
11290
+ console.error("Action::signalSync is unwanted cause exchange integration should be implemented in Broker.useBrokerAdapter as an infrastructure domain layer");
11291
+ console.error("If you need to implement custom logic on signal open/close, please use signal(), signalBacktest(), signalLive()");
11292
+ console.error("If Action::signalSync throws the exchange will not execute the order!");
11293
+ console.error("");
11294
+ console.error("You have been warned!");
11230
11295
  await this._target.signalSync(event);
11231
11296
  }
11232
11297
  }
@@ -11689,19 +11754,6 @@ class ActionBase {
11689
11754
  source,
11690
11755
  });
11691
11756
  }
11692
- /**
11693
- * Gate for position open/close via limit order. Default allows all.
11694
- * Throw to reject — framework retries next tick.
11695
- *
11696
- * NOTE: Exceptions are NOT swallowed — they propagate to CREATE_SYNC_FN.
11697
- *
11698
- * @param event - Sync event with action "signal-open" or "signal-close"
11699
- */
11700
- signalSync(_event, source = DEFAULT_SOURCE) {
11701
- bt.loggerService.info(METHOD_NAME_SIGNAL_SYNC, {
11702
- source,
11703
- });
11704
- }
11705
11757
  /**
11706
11758
  * Cleans up resources and subscriptions when action handler is disposed.
11707
11759
  *
@@ -12980,6 +13032,14 @@ class StrategyCoreService {
12980
13032
  await this.validate(context);
12981
13033
  return await this.strategyConnectionService.getPositionPartials(backtest, symbol, context);
12982
13034
  };
13035
+ this.getPositionEntries = async (backtest, symbol, context) => {
13036
+ this.loggerService.log("strategyCoreService getPositionEntries", {
13037
+ symbol,
13038
+ context,
13039
+ });
13040
+ await this.validate(context);
13041
+ return await this.strategyConnectionService.getPositionEntries(backtest, symbol, context);
13042
+ };
12983
13043
  /**
12984
13044
  * Retrieves the currently active scheduled signal for the symbol.
12985
13045
  * If no scheduled signal exists, returns null.
@@ -31724,6 +31784,11 @@ BrokerBase = functoolsKit.makeExtendable(BrokerBase);
31724
31784
  */
31725
31785
  const Broker = new BrokerAdapter();
31726
31786
 
31787
+ const POSITION_OVERLAP_LADDER_DEFAULT = {
31788
+ upperPercent: 1.5,
31789
+ lowerPercent: 1.5,
31790
+ };
31791
+
31727
31792
  const CANCEL_SCHEDULED_METHOD_NAME = "strategy.commitCancelScheduled";
31728
31793
  const CLOSE_PENDING_METHOD_NAME = "strategy.commitClosePending";
31729
31794
  const PARTIAL_PROFIT_METHOD_NAME = "strategy.commitPartialProfit";
@@ -31749,6 +31814,8 @@ const GET_POSITION_PNL_PERCENT_METHOD_NAME = "strategy.getPositionPnlPercent";
31749
31814
  const GET_POSITION_PNL_COST_METHOD_NAME = "strategy.getPositionPnlCost";
31750
31815
  const GET_POSITION_LEVELS_METHOD_NAME = "strategy.getPositionLevels";
31751
31816
  const GET_POSITION_PARTIALS_METHOD_NAME = "strategy.getPositionPartials";
31817
+ const GET_POSITION_ENTRY_OVERLAP_METHOD_NAME = "strategy.getPositionEntryOverlap";
31818
+ const GET_POSITION_PARTIAL_OVERLAP_METHOD_NAME = "strategy.getPositionPartialOverlap";
31752
31819
  /**
31753
31820
  * Cancels the scheduled signal without stopping the strategy.
31754
31821
  *
@@ -32025,6 +32092,7 @@ async function commitTrailingStop(symbol, percentShift, currentPrice) {
32025
32092
  percentShift,
32026
32093
  currentPrice,
32027
32094
  newStopLossPrice: slPercentShiftToPrice(percentShift, signal.priceStopLoss, effectivePriceOpen, signal.position),
32095
+ takeProfitPrice: signal.priceTakeProfit,
32028
32096
  position: signal.position,
32029
32097
  context: { exchangeName, frameName, strategyName },
32030
32098
  backtest: isBacktest,
@@ -32104,6 +32172,7 @@ async function commitTrailingTake(symbol, percentShift, currentPrice) {
32104
32172
  percentShift,
32105
32173
  currentPrice,
32106
32174
  newTakeProfitPrice: tpPercentShiftToPrice(percentShift, signal.priceTakeProfit, effectivePriceOpen, signal.position),
32175
+ takeProfitPrice: signal.priceTakeProfit,
32107
32176
  position: signal.position,
32108
32177
  context: { exchangeName, frameName, strategyName },
32109
32178
  backtest: isBacktest,
@@ -32155,6 +32224,7 @@ async function commitTrailingStopCost(symbol, newStopLossPrice) {
32155
32224
  currentPrice,
32156
32225
  newStopLossPrice,
32157
32226
  position: signal.position,
32227
+ takeProfitPrice: signal.priceTakeProfit,
32158
32228
  context: { exchangeName, frameName, strategyName },
32159
32229
  backtest: isBacktest,
32160
32230
  });
@@ -32204,6 +32274,7 @@ async function commitTrailingTakeCost(symbol, newTakeProfitPrice) {
32204
32274
  percentShift,
32205
32275
  currentPrice,
32206
32276
  newTakeProfitPrice,
32277
+ takeProfitPrice: signal.priceTakeProfit,
32207
32278
  position: signal.position,
32208
32279
  context: { exchangeName, frameName, strategyName },
32209
32280
  backtest: isBacktest,
@@ -32530,6 +32601,31 @@ async function getBreakeven(symbol, currentPrice) {
32530
32601
  const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
32531
32602
  return await bt.strategyCoreService.getBreakeven(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName });
32532
32603
  }
32604
+ /**
32605
+ * Returns the effective (DCA-weighted) entry price for the current pending signal.
32606
+ *
32607
+ * Uses cost-weighted harmonic mean: Σcost / Σ(cost/price).
32608
+ * When partial closes exist, the price is computed iteratively using
32609
+ * costBasisAtClose snapshots from each partial, then blended with any
32610
+ * DCA entries added after the last partial.
32611
+ * With no DCA entries, equals the original priceOpen.
32612
+ *
32613
+ * Returns null if no pending signal exists.
32614
+ *
32615
+ * Automatically detects backtest/live mode from execution context.
32616
+ *
32617
+ * @param symbol - Trading pair symbol
32618
+ * @returns Promise resolving to effective entry price or null
32619
+ *
32620
+ * @example
32621
+ * ```typescript
32622
+ * import { getPositionAveragePrice } from "backtest-kit";
32623
+ *
32624
+ * const avgPrice = await getPositionAveragePrice("BTCUSDT");
32625
+ * // No DCA: avgPrice === priceOpen
32626
+ * // After DCA at lower price: avgPrice < priceOpen
32627
+ * ```
32628
+ */
32533
32629
  async function getPositionAveragePrice(symbol) {
32534
32630
  bt.loggerService.info(GET_POSITION_AVERAGE_PRICE_METHOD_NAME, {
32535
32631
  symbol,
@@ -32544,6 +32640,28 @@ async function getPositionAveragePrice(symbol) {
32544
32640
  const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
32545
32641
  return await bt.strategyCoreService.getPositionAveragePrice(isBacktest, symbol, { exchangeName, frameName, strategyName });
32546
32642
  }
32643
+ /**
32644
+ * Returns the number of DCA entries made for the current pending signal.
32645
+ *
32646
+ * 1 = original entry only (no DCA).
32647
+ * Increases by 1 with each successful commitAverageBuy().
32648
+ *
32649
+ * Returns null if no pending signal exists.
32650
+ *
32651
+ * Automatically detects backtest/live mode from execution context.
32652
+ *
32653
+ * @param symbol - Trading pair symbol
32654
+ * @returns Promise resolving to entry count or null
32655
+ *
32656
+ * @example
32657
+ * ```typescript
32658
+ * import { getPositionInvestedCount } from "backtest-kit";
32659
+ *
32660
+ * const count = await getPositionInvestedCount("BTCUSDT");
32661
+ * // No DCA: count === 1
32662
+ * // After one DCA: count === 2
32663
+ * ```
32664
+ */
32547
32665
  async function getPositionInvestedCount(symbol) {
32548
32666
  bt.loggerService.info(GET_POSITION_INVESTED_COUNT_METHOD_NAME, {
32549
32667
  symbol,
@@ -32558,6 +32676,28 @@ async function getPositionInvestedCount(symbol) {
32558
32676
  const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
32559
32677
  return await bt.strategyCoreService.getPositionInvestedCount(isBacktest, symbol, { exchangeName, frameName, strategyName });
32560
32678
  }
32679
+ /**
32680
+ * Returns the total invested cost basis in dollars for the current pending signal.
32681
+ *
32682
+ * Equal to the sum of all _entry costs (Σ entry.cost).
32683
+ * Each entry cost is set at the time of commitAverageBuy (defaults to CC_POSITION_ENTRY_COST).
32684
+ *
32685
+ * Returns null if no pending signal exists.
32686
+ *
32687
+ * Automatically detects backtest/live mode from execution context.
32688
+ *
32689
+ * @param symbol - Trading pair symbol
32690
+ * @returns Promise resolving to total invested cost in dollars or null
32691
+ *
32692
+ * @example
32693
+ * ```typescript
32694
+ * import { getPositionInvestedCost } from "backtest-kit";
32695
+ *
32696
+ * const cost = await getPositionInvestedCost("BTCUSDT");
32697
+ * // No DCA, default cost: cost === 100
32698
+ * // After one DCA with default cost: cost === 200
32699
+ * ```
32700
+ */
32561
32701
  async function getPositionInvestedCost(symbol) {
32562
32702
  bt.loggerService.info(GET_POSITION_INVESTED_COST_METHOD_NAME, {
32563
32703
  symbol,
@@ -32572,6 +32712,29 @@ async function getPositionInvestedCost(symbol) {
32572
32712
  const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
32573
32713
  return await bt.strategyCoreService.getPositionInvestedCost(isBacktest, symbol, { exchangeName, frameName, strategyName });
32574
32714
  }
32715
+ /**
32716
+ * Returns the unrealized PNL percentage for the current pending signal at current market price.
32717
+ *
32718
+ * Accounts for partial closes, DCA entries, slippage and fees
32719
+ * (delegates to toProfitLossDto).
32720
+ *
32721
+ * Returns null if no pending signal exists.
32722
+ *
32723
+ * Automatically detects backtest/live mode from execution context.
32724
+ * Automatically fetches current price via getAveragePrice.
32725
+ *
32726
+ * @param symbol - Trading pair symbol
32727
+ * @returns Promise resolving to PNL percentage or null
32728
+ *
32729
+ * @example
32730
+ * ```typescript
32731
+ * import { getPositionPnlPercent } from "backtest-kit";
32732
+ *
32733
+ * const pnlPct = await getPositionPnlPercent("BTCUSDT");
32734
+ * // LONG at 100, current=105: pnlPct ≈ 5
32735
+ * // LONG at 100, current=95: pnlPct ≈ -5
32736
+ * ```
32737
+ */
32575
32738
  async function getPositionPnlPercent(symbol) {
32576
32739
  bt.loggerService.info(GET_POSITION_PNL_PERCENT_METHOD_NAME, { symbol });
32577
32740
  if (!ExecutionContextService.hasContext()) {
@@ -32721,6 +32884,29 @@ async function commitPartialLossCost(symbol, dollarAmount) {
32721
32884
  });
32722
32885
  return await bt.strategyCoreService.partialLoss(isBacktest, symbol, percentToClose, currentPrice, { exchangeName, frameName, strategyName });
32723
32886
  }
32887
+ /**
32888
+ * Returns the unrealized PNL in dollars for the current pending signal at current market price.
32889
+ *
32890
+ * Calculated as: pnlPercentage / 100 × totalInvestedCost.
32891
+ * Accounts for partial closes, DCA entries, slippage and fees.
32892
+ *
32893
+ * Returns null if no pending signal exists.
32894
+ *
32895
+ * Automatically detects backtest/live mode from execution context.
32896
+ * Automatically fetches current price via getAveragePrice.
32897
+ *
32898
+ * @param symbol - Trading pair symbol
32899
+ * @returns Promise resolving to PNL in dollars or null
32900
+ *
32901
+ * @example
32902
+ * ```typescript
32903
+ * import { getPositionPnlCost } from "backtest-kit";
32904
+ *
32905
+ * const pnlCost = await getPositionPnlCost("BTCUSDT");
32906
+ * // LONG at 100, invested $100, current=105: pnlCost ≈ 5
32907
+ * // LONG at 100, invested $200 (DCA), current=95: pnlCost ≈ -10
32908
+ * ```
32909
+ */
32724
32910
  async function getPositionPnlCost(symbol) {
32725
32911
  bt.loggerService.info(GET_POSITION_PNL_COST_METHOD_NAME, { symbol });
32726
32912
  if (!ExecutionContextService.hasContext()) {
@@ -32807,6 +32993,104 @@ async function getPositionPartials(symbol) {
32807
32993
  const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
32808
32994
  return await bt.strategyCoreService.getPositionPartials(isBacktest, symbol, { exchangeName, frameName, strategyName });
32809
32995
  }
32996
+ /**
32997
+ * Checks whether the current price falls within the tolerance zone of any existing DCA entry level.
32998
+ * Use this to prevent duplicate DCA entries at the same price area.
32999
+ *
33000
+ * Returns true if currentPrice is within [level - lowerStep, level + upperStep] for any level,
33001
+ * where step = level * percent / 100.
33002
+ * Returns false if no pending signal exists.
33003
+ *
33004
+ * @param symbol - Trading pair symbol
33005
+ * @param currentPrice - Price to check against existing DCA levels
33006
+ * @param ladder - Tolerance zone config; percentages in 0–100 format (default: 1.5% up and down)
33007
+ * @returns Promise<boolean> - true if price overlaps an existing entry level (DCA not recommended)
33008
+ *
33009
+ * @example
33010
+ * ```typescript
33011
+ * import { getPositionEntryOverlap } from "backtest-kit";
33012
+ *
33013
+ * // LONG with levels [43000, 42000], check if 42100 is too close to 42000
33014
+ * const overlap = await getPositionEntryOverlap("BTCUSDT", 42100, { upperPercent: 5, lowerPercent: 5 });
33015
+ * // overlap = true (42100 is within 5% of 42000 = [39900, 44100])
33016
+ * if (!overlap) {
33017
+ * await commitAverageBuy("BTCUSDT");
33018
+ * }
33019
+ * ```
33020
+ */
33021
+ async function getPositionEntryOverlap(symbol, currentPrice, ladder = POSITION_OVERLAP_LADDER_DEFAULT) {
33022
+ bt.loggerService.info(GET_POSITION_ENTRY_OVERLAP_METHOD_NAME, {
33023
+ symbol,
33024
+ currentPrice,
33025
+ ladder,
33026
+ });
33027
+ if (!ExecutionContextService.hasContext()) {
33028
+ throw new Error("getPositionEntryOverlap requires an execution context");
33029
+ }
33030
+ if (!MethodContextService.hasContext()) {
33031
+ throw new Error("getPositionEntryOverlap requires a method context");
33032
+ }
33033
+ const { backtest: isBacktest } = bt.executionContextService.context;
33034
+ const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
33035
+ const levels = await bt.strategyCoreService.getPositionLevels(isBacktest, symbol, { exchangeName, frameName, strategyName });
33036
+ if (!levels) {
33037
+ return false;
33038
+ }
33039
+ return levels.some((level) => {
33040
+ const upperStep = (level * ladder.upperPercent) / 100;
33041
+ const lowerStep = (level * ladder.lowerPercent) / 100;
33042
+ return currentPrice >= level - lowerStep && currentPrice <= level + upperStep;
33043
+ });
33044
+ }
33045
+ /**
33046
+ * Checks whether the current price falls within the tolerance zone of any existing partial close price.
33047
+ * Use this to prevent duplicate partial closes at the same price area.
33048
+ *
33049
+ * Returns true if currentPrice is within [partial.currentPrice - lowerStep, partial.currentPrice + upperStep]
33050
+ * for any partial, where step = partial.currentPrice * percent / 100.
33051
+ * Returns false if no pending signal exists or no partials have been executed yet.
33052
+ *
33053
+ * @param symbol - Trading pair symbol
33054
+ * @param currentPrice - Price to check against existing partial close prices
33055
+ * @param ladder - Tolerance zone config; percentages in 0–100 format (default: 1.5% up and down)
33056
+ * @returns Promise<boolean> - true if price overlaps an existing partial price (partial not recommended)
33057
+ *
33058
+ * @example
33059
+ * ```typescript
33060
+ * import { getPositionPartialOverlap } from "backtest-kit";
33061
+ *
33062
+ * // Partials at [45000], check if 45100 is too close
33063
+ * const overlap = await getPositionPartialOverlap("BTCUSDT", 45100, { upperPercent: 1.5, lowerPercent: 1.5 });
33064
+ * // overlap = true (45100 is within 1.5% of 45000)
33065
+ * if (!overlap) {
33066
+ * await commitPartialProfit("BTCUSDT", 50);
33067
+ * }
33068
+ * ```
33069
+ */
33070
+ async function getPositionPartialOverlap(symbol, currentPrice, ladder = POSITION_OVERLAP_LADDER_DEFAULT) {
33071
+ bt.loggerService.info(GET_POSITION_PARTIAL_OVERLAP_METHOD_NAME, {
33072
+ symbol,
33073
+ currentPrice,
33074
+ ladder,
33075
+ });
33076
+ if (!ExecutionContextService.hasContext()) {
33077
+ throw new Error("getPositionPartialOverlap requires an execution context");
33078
+ }
33079
+ if (!MethodContextService.hasContext()) {
33080
+ throw new Error("getPositionPartialOverlap requires a method context");
33081
+ }
33082
+ const { backtest: isBacktest } = bt.executionContextService.context;
33083
+ const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
33084
+ const partials = await bt.strategyCoreService.getPositionPartials(isBacktest, symbol, { exchangeName, frameName, strategyName });
33085
+ if (!partials) {
33086
+ return false;
33087
+ }
33088
+ return partials.some((partial) => {
33089
+ const upperStep = (partial.currentPrice * ladder.upperPercent) / 100;
33090
+ const lowerStep = (partial.currentPrice * ladder.lowerPercent) / 100;
33091
+ return currentPrice >= partial.currentPrice - lowerStep && currentPrice <= partial.currentPrice + upperStep;
33092
+ });
33093
+ }
32810
33094
 
32811
33095
  const STOP_STRATEGY_METHOD_NAME = "control.stopStrategy";
32812
33096
  /**
@@ -34032,6 +34316,9 @@ const BACKTEST_METHOD_NAME_GET_POSITION_PNL_PERCENT = "BacktestUtils.getPosition
34032
34316
  const BACKTEST_METHOD_NAME_GET_POSITION_PNL_COST = "BacktestUtils.getPositionPnlCost";
34033
34317
  const BACKTEST_METHOD_NAME_GET_POSITION_LEVELS = "BacktestUtils.getPositionLevels";
34034
34318
  const BACKTEST_METHOD_NAME_GET_POSITION_PARTIALS = "BacktestUtils.getPositionPartials";
34319
+ const BACKTEST_METHOD_NAME_GET_POSITION_ENTRIES = "BacktestUtils.getPositionEntries";
34320
+ const BACKTEST_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP = "BacktestUtils.getPositionEntryOverlap";
34321
+ const BACKTEST_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP = "BacktestUtils.getPositionPartialOverlap";
34035
34322
  const BACKTEST_METHOD_NAME_BREAKEVEN = "Backtest.commitBreakeven";
34036
34323
  const BACKTEST_METHOD_NAME_CANCEL_SCHEDULED = "Backtest.commitCancelScheduled";
34037
34324
  const BACKTEST_METHOD_NAME_CLOSE_PENDING = "Backtest.commitClosePending";
@@ -34787,6 +35074,125 @@ class BacktestUtils {
34787
35074
  }
34788
35075
  return await bt.strategyCoreService.getPositionPartials(true, symbol, context);
34789
35076
  };
35077
+ /**
35078
+ * Returns the list of DCA entry prices and costs for the current pending signal.
35079
+ *
35080
+ * Each element represents a single position entry — the initial open or a subsequent
35081
+ * DCA entry added via commitAverageBuy.
35082
+ *
35083
+ * Returns null if no pending signal exists.
35084
+ * Returns a single-element array if no DCA entries were made.
35085
+ *
35086
+ * Each entry contains:
35087
+ * - `price` — execution price of this entry
35088
+ * - `cost` — dollar cost allocated to this entry (e.g. 100 for $100)
35089
+ *
35090
+ * @param symbol - Trading pair symbol
35091
+ * @param context - Execution context with strategyName, exchangeName, and frameName
35092
+ * @returns Array of entry records, or null if no active position
35093
+ */
35094
+ this.getPositionEntries = async (symbol, context) => {
35095
+ bt.loggerService.info(BACKTEST_METHOD_NAME_GET_POSITION_ENTRIES, {
35096
+ symbol,
35097
+ context,
35098
+ });
35099
+ bt.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_GET_POSITION_ENTRIES);
35100
+ bt.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_GET_POSITION_ENTRIES);
35101
+ {
35102
+ const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
35103
+ riskName &&
35104
+ bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_POSITION_ENTRIES);
35105
+ riskList &&
35106
+ riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_POSITION_ENTRIES));
35107
+ actions &&
35108
+ actions.forEach((actionName) => bt.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_GET_POSITION_ENTRIES));
35109
+ }
35110
+ return await bt.strategyCoreService.getPositionEntries(true, symbol, context);
35111
+ };
35112
+ /**
35113
+ * Checks whether the current price falls within the tolerance zone of any existing DCA entry level.
35114
+ * Use this to prevent duplicate DCA entries at the same price area.
35115
+ *
35116
+ * Returns true if currentPrice is within [level - lowerStep, level + upperStep] for any level,
35117
+ * where step = level * percent / 100.
35118
+ * Returns false if no pending signal exists.
35119
+ *
35120
+ * @param symbol - Trading pair symbol
35121
+ * @param currentPrice - Price to check against existing DCA levels
35122
+ * @param context - Execution context with strategyName, exchangeName, and frameName
35123
+ * @param ladder - Tolerance zone config; percentages in 0–100 format (default: 1.5% up and down)
35124
+ * @returns true if price overlaps an existing entry level (DCA not recommended)
35125
+ */
35126
+ this.getPositionEntryOverlap = async (symbol, currentPrice, context, ladder = POSITION_OVERLAP_LADDER_DEFAULT) => {
35127
+ bt.loggerService.info(BACKTEST_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP, {
35128
+ symbol,
35129
+ currentPrice,
35130
+ context,
35131
+ ladder,
35132
+ });
35133
+ bt.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP);
35134
+ bt.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP);
35135
+ {
35136
+ const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
35137
+ riskName &&
35138
+ bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP);
35139
+ riskList &&
35140
+ riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP));
35141
+ actions &&
35142
+ actions.forEach((actionName) => bt.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP));
35143
+ }
35144
+ const levels = await bt.strategyCoreService.getPositionLevels(true, symbol, context);
35145
+ if (!levels) {
35146
+ return false;
35147
+ }
35148
+ return levels.some((level) => {
35149
+ const upperStep = (level * ladder.upperPercent) / 100;
35150
+ const lowerStep = (level * ladder.lowerPercent) / 100;
35151
+ return currentPrice >= level - lowerStep && currentPrice <= level + upperStep;
35152
+ });
35153
+ };
35154
+ /**
35155
+ * Checks whether the current price falls within the tolerance zone of any existing partial close price.
35156
+ * Use this to prevent duplicate partial closes at the same price area.
35157
+ *
35158
+ * Returns true if currentPrice is within [partial.currentPrice - lowerStep, partial.currentPrice + upperStep]
35159
+ * for any partial, where step = partial.currentPrice * percent / 100.
35160
+ * Returns false if no pending signal exists or no partials have been executed yet.
35161
+ *
35162
+ * @param symbol - Trading pair symbol
35163
+ * @param currentPrice - Price to check against existing partial close prices
35164
+ * @param context - Execution context with strategyName, exchangeName, and frameName
35165
+ * @param ladder - Tolerance zone config; percentages in 0–100 format (default: 1.5% up and down)
35166
+ * @returns true if price overlaps an existing partial price (partial not recommended)
35167
+ */
35168
+ this.getPositionPartialOverlap = async (symbol, currentPrice, context, ladder = POSITION_OVERLAP_LADDER_DEFAULT) => {
35169
+ bt.loggerService.info(BACKTEST_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP, {
35170
+ symbol,
35171
+ currentPrice,
35172
+ context,
35173
+ ladder,
35174
+ });
35175
+ bt.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP);
35176
+ bt.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP);
35177
+ {
35178
+ const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
35179
+ riskName &&
35180
+ bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP);
35181
+ riskList &&
35182
+ riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP));
35183
+ actions &&
35184
+ actions.forEach((actionName) => bt.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP));
35185
+ }
35186
+ const partials = await bt.strategyCoreService.getPositionPartials(true, symbol, context);
35187
+ if (!partials) {
35188
+ return false;
35189
+ }
35190
+ return partials.some((partial) => {
35191
+ const upperStep = (partial.currentPrice * ladder.upperPercent) / 100;
35192
+ const lowerStep = (partial.currentPrice * ladder.lowerPercent) / 100;
35193
+ return currentPrice >= partial.currentPrice - lowerStep && currentPrice <= partial.currentPrice + upperStep;
35194
+ });
35195
+ };
34790
35196
  /**
34791
35197
  * Stops the strategy from generating new signals.
34792
35198
  *
@@ -35276,6 +35682,7 @@ class BacktestUtils {
35276
35682
  percentShift,
35277
35683
  currentPrice,
35278
35684
  newStopLossPrice: slPercentShiftToPrice(percentShift, signal.priceStopLoss, effectivePriceOpen, signal.position),
35685
+ takeProfitPrice: signal.priceTakeProfit,
35279
35686
  position: signal.position,
35280
35687
  context,
35281
35688
  backtest: true,
@@ -35360,6 +35767,7 @@ class BacktestUtils {
35360
35767
  percentShift,
35361
35768
  currentPrice,
35362
35769
  newTakeProfitPrice: tpPercentShiftToPrice(percentShift, signal.priceTakeProfit, effectivePriceOpen, signal.position),
35770
+ takeProfitPrice: signal.priceTakeProfit,
35363
35771
  position: signal.position,
35364
35772
  context,
35365
35773
  backtest: true,
@@ -35414,6 +35822,7 @@ class BacktestUtils {
35414
35822
  currentPrice,
35415
35823
  newStopLossPrice,
35416
35824
  position: signal.position,
35825
+ takeProfitPrice: signal.priceTakeProfit,
35417
35826
  context,
35418
35827
  backtest: true,
35419
35828
  });
@@ -35467,6 +35876,7 @@ class BacktestUtils {
35467
35876
  currentPrice,
35468
35877
  newTakeProfitPrice,
35469
35878
  position: signal.position,
35879
+ takeProfitPrice: signal.priceTakeProfit,
35470
35880
  context,
35471
35881
  backtest: true,
35472
35882
  });
@@ -35809,6 +36219,9 @@ const LIVE_METHOD_NAME_GET_POSITION_PNL_PERCENT = "LiveUtils.getPositionPnlPerce
35809
36219
  const LIVE_METHOD_NAME_GET_POSITION_PNL_COST = "LiveUtils.getPositionPnlCost";
35810
36220
  const LIVE_METHOD_NAME_GET_POSITION_LEVELS = "LiveUtils.getPositionLevels";
35811
36221
  const LIVE_METHOD_NAME_GET_POSITION_PARTIALS = "LiveUtils.getPositionPartials";
36222
+ const LIVE_METHOD_NAME_GET_POSITION_ENTRIES = "LiveUtils.getPositionEntries";
36223
+ const LIVE_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP = "LiveUtils.getPositionEntryOverlap";
36224
+ const LIVE_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP = "LiveUtils.getPositionPartialOverlap";
35812
36225
  const LIVE_METHOD_NAME_BREAKEVEN = "Live.commitBreakeven";
35813
36226
  const LIVE_METHOD_NAME_CANCEL_SCHEDULED = "Live.cancelScheduled";
35814
36227
  const LIVE_METHOD_NAME_CLOSE_PENDING = "Live.closePending";
@@ -36623,6 +37036,137 @@ class LiveUtils {
36623
37036
  frameName: "",
36624
37037
  });
36625
37038
  };
37039
+ /**
37040
+ * Returns the list of DCA entry prices and costs for the current pending signal.
37041
+ *
37042
+ * Each element represents a single position entry — the initial open or a subsequent
37043
+ * DCA entry added via commitAverageBuy.
37044
+ *
37045
+ * Returns null if no pending signal exists.
37046
+ * Returns a single-element array if no DCA entries were made.
37047
+ *
37048
+ * Each entry contains:
37049
+ * - `price` — execution price of this entry
37050
+ * - `cost` — dollar cost allocated to this entry (e.g. 100 for $100)
37051
+ *
37052
+ * @param symbol - Trading pair symbol
37053
+ * @param context - Execution context with strategyName and exchangeName
37054
+ * @returns Array of entry records, or null if no active position
37055
+ */
37056
+ this.getPositionEntries = async (symbol, context) => {
37057
+ bt.loggerService.info(LIVE_METHOD_NAME_GET_POSITION_ENTRIES, {
37058
+ symbol,
37059
+ context,
37060
+ });
37061
+ bt.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_GET_POSITION_ENTRIES);
37062
+ bt.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_GET_POSITION_ENTRIES);
37063
+ {
37064
+ const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
37065
+ riskName &&
37066
+ bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_POSITION_ENTRIES);
37067
+ riskList &&
37068
+ riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_POSITION_ENTRIES));
37069
+ actions &&
37070
+ actions.forEach((actionName) => bt.actionValidationService.validate(actionName, LIVE_METHOD_NAME_GET_POSITION_ENTRIES));
37071
+ }
37072
+ return await bt.strategyCoreService.getPositionEntries(false, symbol, {
37073
+ strategyName: context.strategyName,
37074
+ exchangeName: context.exchangeName,
37075
+ frameName: "",
37076
+ });
37077
+ };
37078
+ /**
37079
+ * Checks whether the current price falls within the tolerance zone of any existing DCA entry level.
37080
+ * Use this to prevent duplicate DCA entries at the same price area.
37081
+ *
37082
+ * Returns true if currentPrice is within [level - lowerStep, level + upperStep] for any level,
37083
+ * where step = level * percent / 100.
37084
+ * Returns false if no pending signal exists.
37085
+ *
37086
+ * @param symbol - Trading pair symbol
37087
+ * @param currentPrice - Price to check against existing DCA levels
37088
+ * @param context - Execution context with strategyName and exchangeName
37089
+ * @param ladder - Tolerance zone config; percentages in 0–100 format (default: 1.5% up and down)
37090
+ * @returns true if price overlaps an existing entry level (DCA not recommended)
37091
+ */
37092
+ this.getPositionEntryOverlap = async (symbol, currentPrice, context, ladder = POSITION_OVERLAP_LADDER_DEFAULT) => {
37093
+ bt.loggerService.info(LIVE_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP, {
37094
+ symbol,
37095
+ currentPrice,
37096
+ context,
37097
+ ladder,
37098
+ });
37099
+ bt.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP);
37100
+ bt.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP);
37101
+ {
37102
+ const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
37103
+ riskName &&
37104
+ bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP);
37105
+ riskList &&
37106
+ riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP));
37107
+ actions &&
37108
+ actions.forEach((actionName) => bt.actionValidationService.validate(actionName, LIVE_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP));
37109
+ }
37110
+ const levels = await bt.strategyCoreService.getPositionLevels(false, symbol, {
37111
+ strategyName: context.strategyName,
37112
+ exchangeName: context.exchangeName,
37113
+ frameName: "",
37114
+ });
37115
+ if (!levels) {
37116
+ return false;
37117
+ }
37118
+ return levels.some((level) => {
37119
+ const upperStep = (level * ladder.upperPercent) / 100;
37120
+ const lowerStep = (level * ladder.lowerPercent) / 100;
37121
+ return currentPrice >= level - lowerStep && currentPrice <= level + upperStep;
37122
+ });
37123
+ };
37124
+ /**
37125
+ * Checks whether the current price falls within the tolerance zone of any existing partial close price.
37126
+ * Use this to prevent duplicate partial closes at the same price area.
37127
+ *
37128
+ * Returns true if currentPrice is within [partial.currentPrice - lowerStep, partial.currentPrice + upperStep]
37129
+ * for any partial, where step = partial.currentPrice * percent / 100.
37130
+ * Returns false if no pending signal exists or no partials have been executed yet.
37131
+ *
37132
+ * @param symbol - Trading pair symbol
37133
+ * @param currentPrice - Price to check against existing partial close prices
37134
+ * @param context - Execution context with strategyName and exchangeName
37135
+ * @param ladder - Tolerance zone config; percentages in 0–100 format (default: 1.5% up and down)
37136
+ * @returns true if price overlaps an existing partial price (partial not recommended)
37137
+ */
37138
+ this.getPositionPartialOverlap = async (symbol, currentPrice, context, ladder = POSITION_OVERLAP_LADDER_DEFAULT) => {
37139
+ bt.loggerService.info(LIVE_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP, {
37140
+ symbol,
37141
+ currentPrice,
37142
+ context,
37143
+ ladder,
37144
+ });
37145
+ bt.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP);
37146
+ bt.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP);
37147
+ {
37148
+ const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
37149
+ riskName &&
37150
+ bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP);
37151
+ riskList &&
37152
+ riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP));
37153
+ actions &&
37154
+ actions.forEach((actionName) => bt.actionValidationService.validate(actionName, LIVE_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP));
37155
+ }
37156
+ const partials = await bt.strategyCoreService.getPositionPartials(false, symbol, {
37157
+ strategyName: context.strategyName,
37158
+ exchangeName: context.exchangeName,
37159
+ frameName: "",
37160
+ });
37161
+ if (!partials) {
37162
+ return false;
37163
+ }
37164
+ return partials.some((partial) => {
37165
+ const upperStep = (partial.currentPrice * ladder.upperPercent) / 100;
37166
+ const lowerStep = (partial.currentPrice * ladder.lowerPercent) / 100;
37167
+ return currentPrice >= partial.currentPrice - lowerStep && currentPrice <= partial.currentPrice + upperStep;
37168
+ });
37169
+ };
36626
37170
  /**
36627
37171
  * Stops the strategy from generating new signals.
36628
37172
  *
@@ -37205,6 +37749,7 @@ class LiveUtils {
37205
37749
  percentShift,
37206
37750
  currentPrice,
37207
37751
  newStopLossPrice: slPercentShiftToPrice(percentShift, signal.priceStopLoss, effectivePriceOpen, signal.position),
37752
+ takeProfitPrice: signal.priceTakeProfit,
37208
37753
  position: signal.position,
37209
37754
  context,
37210
37755
  backtest: false,
@@ -37304,6 +37849,7 @@ class LiveUtils {
37304
37849
  percentShift,
37305
37850
  currentPrice,
37306
37851
  newTakeProfitPrice: tpPercentShiftToPrice(percentShift, signal.priceTakeProfit, effectivePriceOpen, signal.position),
37852
+ takeProfitPrice: signal.priceTakeProfit,
37307
37853
  position: signal.position,
37308
37854
  context,
37309
37855
  backtest: false,
@@ -37373,6 +37919,7 @@ class LiveUtils {
37373
37919
  percentShift,
37374
37920
  currentPrice,
37375
37921
  newStopLossPrice,
37922
+ takeProfitPrice: signal.priceTakeProfit,
37376
37923
  position: signal.position,
37377
37924
  context,
37378
37925
  backtest: false,
@@ -37442,6 +37989,7 @@ class LiveUtils {
37442
37989
  percentShift,
37443
37990
  currentPrice,
37444
37991
  newTakeProfitPrice,
37992
+ takeProfitPrice: signal.priceTakeProfit,
37445
37993
  position: signal.position,
37446
37994
  context,
37447
37995
  backtest: false,
@@ -45442,9 +45990,11 @@ exports.getNextCandles = getNextCandles;
45442
45990
  exports.getOrderBook = getOrderBook;
45443
45991
  exports.getPendingSignal = getPendingSignal;
45444
45992
  exports.getPositionAveragePrice = getPositionAveragePrice;
45993
+ exports.getPositionEntryOverlap = getPositionEntryOverlap;
45445
45994
  exports.getPositionInvestedCost = getPositionInvestedCost;
45446
45995
  exports.getPositionInvestedCount = getPositionInvestedCount;
45447
45996
  exports.getPositionLevels = getPositionLevels;
45997
+ exports.getPositionPartialOverlap = getPositionPartialOverlap;
45448
45998
  exports.getPositionPartials = getPositionPartials;
45449
45999
  exports.getPositionPnlCost = getPositionPnlCost;
45450
46000
  exports.getPositionPnlPercent = getPositionPnlPercent;