backtest-kit 4.0.2 → 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 +442 -25
- package/build/index.mjs +441 -26
- package/package.json +1 -1
- package/types.d.ts +271 -11
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:
|
|
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
|
|
@@ -7448,10 +7456,12 @@ class ClientStrategy {
|
|
|
7448
7456
|
if (typeof currentPrice !== "number" || !isFinite(currentPrice) || currentPrice <= 0)
|
|
7449
7457
|
return false;
|
|
7450
7458
|
const effectivePriceOpen = getEffectivePriceOpen(this._pendingSignal);
|
|
7451
|
-
if (
|
|
7452
|
-
|
|
7453
|
-
|
|
7454
|
-
|
|
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
|
+
}
|
|
7455
7465
|
const effectiveTakeProfit = this._pendingSignal._trailingPriceTakeProfit ?? this._pendingSignal.priceTakeProfit;
|
|
7456
7466
|
if (this._pendingSignal.position === "long" && currentPrice >= effectiveTakeProfit)
|
|
7457
7467
|
return false;
|
|
@@ -7535,7 +7545,7 @@ class ClientStrategy {
|
|
|
7535
7545
|
throw new Error(`ClientStrategy partialProfit: currentPrice must be a positive finite number, got ${currentPrice}`);
|
|
7536
7546
|
}
|
|
7537
7547
|
// Validation: currentPrice must be moving toward TP (profit direction)
|
|
7538
|
-
{
|
|
7548
|
+
if (!GLOBAL_CONFIG.CC_ENABLE_PPPL_EVERYWHERE) {
|
|
7539
7549
|
const effectivePriceOpen = getEffectivePriceOpen(this._pendingSignal);
|
|
7540
7550
|
if (this._pendingSignal.position === "long") {
|
|
7541
7551
|
// For LONG: currentPrice must be higher than effectivePriceOpen (moving toward TP)
|
|
@@ -7629,10 +7639,12 @@ class ClientStrategy {
|
|
|
7629
7639
|
if (typeof currentPrice !== "number" || !isFinite(currentPrice) || currentPrice <= 0)
|
|
7630
7640
|
return false;
|
|
7631
7641
|
const effectivePriceOpen = getEffectivePriceOpen(this._pendingSignal);
|
|
7632
|
-
if (
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
|
|
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
|
+
}
|
|
7636
7648
|
const effectiveStopLoss = this._pendingSignal._trailingPriceStopLoss ?? this._pendingSignal.priceStopLoss;
|
|
7637
7649
|
if (this._pendingSignal.position === "long" && currentPrice <= effectiveStopLoss)
|
|
7638
7650
|
return false;
|
|
@@ -7716,7 +7728,7 @@ class ClientStrategy {
|
|
|
7716
7728
|
throw new Error(`ClientStrategy partialLoss: currentPrice must be a positive finite number, got ${currentPrice}`);
|
|
7717
7729
|
}
|
|
7718
7730
|
// Validation: currentPrice must be moving toward SL (loss direction)
|
|
7719
|
-
{
|
|
7731
|
+
if (!GLOBAL_CONFIG.CC_ENABLE_PPPL_EVERYWHERE) {
|
|
7720
7732
|
const effectivePriceOpen = getEffectivePriceOpen(this._pendingSignal);
|
|
7721
7733
|
if (this._pendingSignal.position === "long") {
|
|
7722
7734
|
// For LONG: currentPrice must be lower than effectivePriceOpen (moving toward SL)
|
|
@@ -10864,7 +10876,6 @@ const METHOD_NAME_PARTIAL_LOSS_AVAILABLE = "ActionBase.partialLossAvailable";
|
|
|
10864
10876
|
const METHOD_NAME_PING_SCHEDULED = "ActionBase.pingScheduled";
|
|
10865
10877
|
const METHOD_NAME_PING_ACTIVE = "ActionBase.pingActive";
|
|
10866
10878
|
const METHOD_NAME_RISK_REJECTION = "ActionBase.riskRejection";
|
|
10867
|
-
const METHOD_NAME_SIGNAL_SYNC = "ActionBase.signalSync";
|
|
10868
10879
|
const METHOD_NAME_DISPOSE = "ActionBase.dispose";
|
|
10869
10880
|
const DEFAULT_SOURCE = "default";
|
|
10870
10881
|
/**
|
|
@@ -11276,6 +11287,11 @@ class ActionProxy {
|
|
|
11276
11287
|
*/
|
|
11277
11288
|
async signalSync(event) {
|
|
11278
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!");
|
|
11279
11295
|
await this._target.signalSync(event);
|
|
11280
11296
|
}
|
|
11281
11297
|
}
|
|
@@ -11738,19 +11754,6 @@ class ActionBase {
|
|
|
11738
11754
|
source,
|
|
11739
11755
|
});
|
|
11740
11756
|
}
|
|
11741
|
-
/**
|
|
11742
|
-
* Gate for position open/close via limit order. Default allows all.
|
|
11743
|
-
* Throw to reject — framework retries next tick.
|
|
11744
|
-
*
|
|
11745
|
-
* NOTE: Exceptions are NOT swallowed — they propagate to CREATE_SYNC_FN.
|
|
11746
|
-
*
|
|
11747
|
-
* @param event - Sync event with action "signal-open" or "signal-close"
|
|
11748
|
-
*/
|
|
11749
|
-
signalSync(_event, source = DEFAULT_SOURCE) {
|
|
11750
|
-
bt.loggerService.info(METHOD_NAME_SIGNAL_SYNC, {
|
|
11751
|
-
source,
|
|
11752
|
-
});
|
|
11753
|
-
}
|
|
11754
11757
|
/**
|
|
11755
11758
|
* Cleans up resources and subscriptions when action handler is disposed.
|
|
11756
11759
|
*
|
|
@@ -31781,6 +31784,11 @@ BrokerBase = functoolsKit.makeExtendable(BrokerBase);
|
|
|
31781
31784
|
*/
|
|
31782
31785
|
const Broker = new BrokerAdapter();
|
|
31783
31786
|
|
|
31787
|
+
const POSITION_OVERLAP_LADDER_DEFAULT = {
|
|
31788
|
+
upperPercent: 1.5,
|
|
31789
|
+
lowerPercent: 1.5,
|
|
31790
|
+
};
|
|
31791
|
+
|
|
31784
31792
|
const CANCEL_SCHEDULED_METHOD_NAME = "strategy.commitCancelScheduled";
|
|
31785
31793
|
const CLOSE_PENDING_METHOD_NAME = "strategy.commitClosePending";
|
|
31786
31794
|
const PARTIAL_PROFIT_METHOD_NAME = "strategy.commitPartialProfit";
|
|
@@ -31806,6 +31814,8 @@ const GET_POSITION_PNL_PERCENT_METHOD_NAME = "strategy.getPositionPnlPercent";
|
|
|
31806
31814
|
const GET_POSITION_PNL_COST_METHOD_NAME = "strategy.getPositionPnlCost";
|
|
31807
31815
|
const GET_POSITION_LEVELS_METHOD_NAME = "strategy.getPositionLevels";
|
|
31808
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";
|
|
31809
31819
|
/**
|
|
31810
31820
|
* Cancels the scheduled signal without stopping the strategy.
|
|
31811
31821
|
*
|
|
@@ -32082,6 +32092,7 @@ async function commitTrailingStop(symbol, percentShift, currentPrice) {
|
|
|
32082
32092
|
percentShift,
|
|
32083
32093
|
currentPrice,
|
|
32084
32094
|
newStopLossPrice: slPercentShiftToPrice(percentShift, signal.priceStopLoss, effectivePriceOpen, signal.position),
|
|
32095
|
+
takeProfitPrice: signal.priceTakeProfit,
|
|
32085
32096
|
position: signal.position,
|
|
32086
32097
|
context: { exchangeName, frameName, strategyName },
|
|
32087
32098
|
backtest: isBacktest,
|
|
@@ -32161,6 +32172,7 @@ async function commitTrailingTake(symbol, percentShift, currentPrice) {
|
|
|
32161
32172
|
percentShift,
|
|
32162
32173
|
currentPrice,
|
|
32163
32174
|
newTakeProfitPrice: tpPercentShiftToPrice(percentShift, signal.priceTakeProfit, effectivePriceOpen, signal.position),
|
|
32175
|
+
takeProfitPrice: signal.priceTakeProfit,
|
|
32164
32176
|
position: signal.position,
|
|
32165
32177
|
context: { exchangeName, frameName, strategyName },
|
|
32166
32178
|
backtest: isBacktest,
|
|
@@ -32212,6 +32224,7 @@ async function commitTrailingStopCost(symbol, newStopLossPrice) {
|
|
|
32212
32224
|
currentPrice,
|
|
32213
32225
|
newStopLossPrice,
|
|
32214
32226
|
position: signal.position,
|
|
32227
|
+
takeProfitPrice: signal.priceTakeProfit,
|
|
32215
32228
|
context: { exchangeName, frameName, strategyName },
|
|
32216
32229
|
backtest: isBacktest,
|
|
32217
32230
|
});
|
|
@@ -32261,6 +32274,7 @@ async function commitTrailingTakeCost(symbol, newTakeProfitPrice) {
|
|
|
32261
32274
|
percentShift,
|
|
32262
32275
|
currentPrice,
|
|
32263
32276
|
newTakeProfitPrice,
|
|
32277
|
+
takeProfitPrice: signal.priceTakeProfit,
|
|
32264
32278
|
position: signal.position,
|
|
32265
32279
|
context: { exchangeName, frameName, strategyName },
|
|
32266
32280
|
backtest: isBacktest,
|
|
@@ -32587,6 +32601,31 @@ async function getBreakeven(symbol, currentPrice) {
|
|
|
32587
32601
|
const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
|
|
32588
32602
|
return await bt.strategyCoreService.getBreakeven(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName });
|
|
32589
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
|
+
*/
|
|
32590
32629
|
async function getPositionAveragePrice(symbol) {
|
|
32591
32630
|
bt.loggerService.info(GET_POSITION_AVERAGE_PRICE_METHOD_NAME, {
|
|
32592
32631
|
symbol,
|
|
@@ -32601,6 +32640,28 @@ async function getPositionAveragePrice(symbol) {
|
|
|
32601
32640
|
const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
|
|
32602
32641
|
return await bt.strategyCoreService.getPositionAveragePrice(isBacktest, symbol, { exchangeName, frameName, strategyName });
|
|
32603
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
|
+
*/
|
|
32604
32665
|
async function getPositionInvestedCount(symbol) {
|
|
32605
32666
|
bt.loggerService.info(GET_POSITION_INVESTED_COUNT_METHOD_NAME, {
|
|
32606
32667
|
symbol,
|
|
@@ -32615,6 +32676,28 @@ async function getPositionInvestedCount(symbol) {
|
|
|
32615
32676
|
const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
|
|
32616
32677
|
return await bt.strategyCoreService.getPositionInvestedCount(isBacktest, symbol, { exchangeName, frameName, strategyName });
|
|
32617
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
|
+
*/
|
|
32618
32701
|
async function getPositionInvestedCost(symbol) {
|
|
32619
32702
|
bt.loggerService.info(GET_POSITION_INVESTED_COST_METHOD_NAME, {
|
|
32620
32703
|
symbol,
|
|
@@ -32629,6 +32712,29 @@ async function getPositionInvestedCost(symbol) {
|
|
|
32629
32712
|
const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
|
|
32630
32713
|
return await bt.strategyCoreService.getPositionInvestedCost(isBacktest, symbol, { exchangeName, frameName, strategyName });
|
|
32631
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
|
+
*/
|
|
32632
32738
|
async function getPositionPnlPercent(symbol) {
|
|
32633
32739
|
bt.loggerService.info(GET_POSITION_PNL_PERCENT_METHOD_NAME, { symbol });
|
|
32634
32740
|
if (!ExecutionContextService.hasContext()) {
|
|
@@ -32778,6 +32884,29 @@ async function commitPartialLossCost(symbol, dollarAmount) {
|
|
|
32778
32884
|
});
|
|
32779
32885
|
return await bt.strategyCoreService.partialLoss(isBacktest, symbol, percentToClose, currentPrice, { exchangeName, frameName, strategyName });
|
|
32780
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
|
+
*/
|
|
32781
32910
|
async function getPositionPnlCost(symbol) {
|
|
32782
32911
|
bt.loggerService.info(GET_POSITION_PNL_COST_METHOD_NAME, { symbol });
|
|
32783
32912
|
if (!ExecutionContextService.hasContext()) {
|
|
@@ -32864,6 +32993,104 @@ async function getPositionPartials(symbol) {
|
|
|
32864
32993
|
const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
|
|
32865
32994
|
return await bt.strategyCoreService.getPositionPartials(isBacktest, symbol, { exchangeName, frameName, strategyName });
|
|
32866
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
|
+
}
|
|
32867
33094
|
|
|
32868
33095
|
const STOP_STRATEGY_METHOD_NAME = "control.stopStrategy";
|
|
32869
33096
|
/**
|
|
@@ -34090,6 +34317,8 @@ const BACKTEST_METHOD_NAME_GET_POSITION_PNL_COST = "BacktestUtils.getPositionPnl
|
|
|
34090
34317
|
const BACKTEST_METHOD_NAME_GET_POSITION_LEVELS = "BacktestUtils.getPositionLevels";
|
|
34091
34318
|
const BACKTEST_METHOD_NAME_GET_POSITION_PARTIALS = "BacktestUtils.getPositionPartials";
|
|
34092
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";
|
|
34093
34322
|
const BACKTEST_METHOD_NAME_BREAKEVEN = "Backtest.commitBreakeven";
|
|
34094
34323
|
const BACKTEST_METHOD_NAME_CANCEL_SCHEDULED = "Backtest.commitCancelScheduled";
|
|
34095
34324
|
const BACKTEST_METHOD_NAME_CLOSE_PENDING = "Backtest.commitClosePending";
|
|
@@ -34880,6 +35109,90 @@ class BacktestUtils {
|
|
|
34880
35109
|
}
|
|
34881
35110
|
return await bt.strategyCoreService.getPositionEntries(true, symbol, context);
|
|
34882
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
|
+
};
|
|
34883
35196
|
/**
|
|
34884
35197
|
* Stops the strategy from generating new signals.
|
|
34885
35198
|
*
|
|
@@ -35369,6 +35682,7 @@ class BacktestUtils {
|
|
|
35369
35682
|
percentShift,
|
|
35370
35683
|
currentPrice,
|
|
35371
35684
|
newStopLossPrice: slPercentShiftToPrice(percentShift, signal.priceStopLoss, effectivePriceOpen, signal.position),
|
|
35685
|
+
takeProfitPrice: signal.priceTakeProfit,
|
|
35372
35686
|
position: signal.position,
|
|
35373
35687
|
context,
|
|
35374
35688
|
backtest: true,
|
|
@@ -35453,6 +35767,7 @@ class BacktestUtils {
|
|
|
35453
35767
|
percentShift,
|
|
35454
35768
|
currentPrice,
|
|
35455
35769
|
newTakeProfitPrice: tpPercentShiftToPrice(percentShift, signal.priceTakeProfit, effectivePriceOpen, signal.position),
|
|
35770
|
+
takeProfitPrice: signal.priceTakeProfit,
|
|
35456
35771
|
position: signal.position,
|
|
35457
35772
|
context,
|
|
35458
35773
|
backtest: true,
|
|
@@ -35507,6 +35822,7 @@ class BacktestUtils {
|
|
|
35507
35822
|
currentPrice,
|
|
35508
35823
|
newStopLossPrice,
|
|
35509
35824
|
position: signal.position,
|
|
35825
|
+
takeProfitPrice: signal.priceTakeProfit,
|
|
35510
35826
|
context,
|
|
35511
35827
|
backtest: true,
|
|
35512
35828
|
});
|
|
@@ -35560,6 +35876,7 @@ class BacktestUtils {
|
|
|
35560
35876
|
currentPrice,
|
|
35561
35877
|
newTakeProfitPrice,
|
|
35562
35878
|
position: signal.position,
|
|
35879
|
+
takeProfitPrice: signal.priceTakeProfit,
|
|
35563
35880
|
context,
|
|
35564
35881
|
backtest: true,
|
|
35565
35882
|
});
|
|
@@ -35903,6 +36220,8 @@ const LIVE_METHOD_NAME_GET_POSITION_PNL_COST = "LiveUtils.getPositionPnlCost";
|
|
|
35903
36220
|
const LIVE_METHOD_NAME_GET_POSITION_LEVELS = "LiveUtils.getPositionLevels";
|
|
35904
36221
|
const LIVE_METHOD_NAME_GET_POSITION_PARTIALS = "LiveUtils.getPositionPartials";
|
|
35905
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";
|
|
35906
36225
|
const LIVE_METHOD_NAME_BREAKEVEN = "Live.commitBreakeven";
|
|
35907
36226
|
const LIVE_METHOD_NAME_CANCEL_SCHEDULED = "Live.cancelScheduled";
|
|
35908
36227
|
const LIVE_METHOD_NAME_CLOSE_PENDING = "Live.closePending";
|
|
@@ -36756,6 +37075,98 @@ class LiveUtils {
|
|
|
36756
37075
|
frameName: "",
|
|
36757
37076
|
});
|
|
36758
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
|
+
};
|
|
36759
37170
|
/**
|
|
36760
37171
|
* Stops the strategy from generating new signals.
|
|
36761
37172
|
*
|
|
@@ -37338,6 +37749,7 @@ class LiveUtils {
|
|
|
37338
37749
|
percentShift,
|
|
37339
37750
|
currentPrice,
|
|
37340
37751
|
newStopLossPrice: slPercentShiftToPrice(percentShift, signal.priceStopLoss, effectivePriceOpen, signal.position),
|
|
37752
|
+
takeProfitPrice: signal.priceTakeProfit,
|
|
37341
37753
|
position: signal.position,
|
|
37342
37754
|
context,
|
|
37343
37755
|
backtest: false,
|
|
@@ -37437,6 +37849,7 @@ class LiveUtils {
|
|
|
37437
37849
|
percentShift,
|
|
37438
37850
|
currentPrice,
|
|
37439
37851
|
newTakeProfitPrice: tpPercentShiftToPrice(percentShift, signal.priceTakeProfit, effectivePriceOpen, signal.position),
|
|
37852
|
+
takeProfitPrice: signal.priceTakeProfit,
|
|
37440
37853
|
position: signal.position,
|
|
37441
37854
|
context,
|
|
37442
37855
|
backtest: false,
|
|
@@ -37506,6 +37919,7 @@ class LiveUtils {
|
|
|
37506
37919
|
percentShift,
|
|
37507
37920
|
currentPrice,
|
|
37508
37921
|
newStopLossPrice,
|
|
37922
|
+
takeProfitPrice: signal.priceTakeProfit,
|
|
37509
37923
|
position: signal.position,
|
|
37510
37924
|
context,
|
|
37511
37925
|
backtest: false,
|
|
@@ -37575,6 +37989,7 @@ class LiveUtils {
|
|
|
37575
37989
|
percentShift,
|
|
37576
37990
|
currentPrice,
|
|
37577
37991
|
newTakeProfitPrice,
|
|
37992
|
+
takeProfitPrice: signal.priceTakeProfit,
|
|
37578
37993
|
position: signal.position,
|
|
37579
37994
|
context,
|
|
37580
37995
|
backtest: false,
|
|
@@ -45575,9 +45990,11 @@ exports.getNextCandles = getNextCandles;
|
|
|
45575
45990
|
exports.getOrderBook = getOrderBook;
|
|
45576
45991
|
exports.getPendingSignal = getPendingSignal;
|
|
45577
45992
|
exports.getPositionAveragePrice = getPositionAveragePrice;
|
|
45993
|
+
exports.getPositionEntryOverlap = getPositionEntryOverlap;
|
|
45578
45994
|
exports.getPositionInvestedCost = getPositionInvestedCost;
|
|
45579
45995
|
exports.getPositionInvestedCount = getPositionInvestedCount;
|
|
45580
45996
|
exports.getPositionLevels = getPositionLevels;
|
|
45997
|
+
exports.getPositionPartialOverlap = getPositionPartialOverlap;
|
|
45581
45998
|
exports.getPositionPartials = getPositionPartials;
|
|
45582
45999
|
exports.getPositionPnlCost = getPositionPnlCost;
|
|
45583
46000
|
exports.getPositionPnlPercent = getPositionPnlPercent;
|