backtest-kit 1.10.1 → 1.10.2
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/LICENSE +21 -21
- package/README.md +259 -259
- package/build/index.cjs +465 -31
- package/build/index.mjs +465 -31
- package/package.json +81 -81
- package/types.d.ts +197 -19
package/build/index.cjs
CHANGED
|
@@ -2792,6 +2792,19 @@ const BREAKEVEN_FN = (self, signal, currentPrice) => {
|
|
|
2792
2792
|
const thresholdPrice = signal.priceOpen * (1 + breakevenThresholdPercent / 100);
|
|
2793
2793
|
const isThresholdReached = currentPrice >= thresholdPrice;
|
|
2794
2794
|
if (isThresholdReached && breakevenPrice > trailingStopLoss) {
|
|
2795
|
+
// Check for price intrusion before setting new SL
|
|
2796
|
+
if (currentPrice < breakevenPrice) {
|
|
2797
|
+
// Price already crossed the breakeven level - skip setting SL
|
|
2798
|
+
self.params.logger.debug("BREAKEVEN_FN: price intrusion detected, skipping SL update", {
|
|
2799
|
+
signalId: signal.id,
|
|
2800
|
+
position: signal.position,
|
|
2801
|
+
priceOpen: signal.priceOpen,
|
|
2802
|
+
breakevenPrice,
|
|
2803
|
+
currentPrice,
|
|
2804
|
+
reason: "currentPrice below breakevenPrice (LONG position)"
|
|
2805
|
+
});
|
|
2806
|
+
return false;
|
|
2807
|
+
}
|
|
2795
2808
|
// Breakeven is better than current trailing SL - upgrade to breakeven
|
|
2796
2809
|
signal._trailingPriceStopLoss = breakevenPrice;
|
|
2797
2810
|
self.params.logger.info("BREAKEVEN_FN: upgraded negative trailing stop to breakeven", {
|
|
@@ -2844,6 +2857,19 @@ const BREAKEVEN_FN = (self, signal, currentPrice) => {
|
|
|
2844
2857
|
const thresholdPrice = signal.priceOpen * (1 - breakevenThresholdPercent / 100);
|
|
2845
2858
|
const isThresholdReached = currentPrice <= thresholdPrice;
|
|
2846
2859
|
if (isThresholdReached && breakevenPrice < trailingStopLoss) {
|
|
2860
|
+
// Check for price intrusion before setting new SL
|
|
2861
|
+
if (currentPrice > breakevenPrice) {
|
|
2862
|
+
// Price already crossed the breakeven level - skip setting SL
|
|
2863
|
+
self.params.logger.debug("BREAKEVEN_FN: price intrusion detected, skipping SL update", {
|
|
2864
|
+
signalId: signal.id,
|
|
2865
|
+
position: signal.position,
|
|
2866
|
+
priceOpen: signal.priceOpen,
|
|
2867
|
+
breakevenPrice,
|
|
2868
|
+
currentPrice,
|
|
2869
|
+
reason: "currentPrice above breakevenPrice (SHORT position)"
|
|
2870
|
+
});
|
|
2871
|
+
return false;
|
|
2872
|
+
}
|
|
2847
2873
|
// Breakeven is better than current trailing SL - upgrade to breakeven
|
|
2848
2874
|
signal._trailingPriceStopLoss = breakevenPrice;
|
|
2849
2875
|
self.params.logger.info("BREAKEVEN_FN: upgraded negative trailing stop to breakeven", {
|
|
@@ -2914,6 +2940,31 @@ const BREAKEVEN_FN = (self, signal, currentPrice) => {
|
|
|
2914
2940
|
});
|
|
2915
2941
|
return false;
|
|
2916
2942
|
}
|
|
2943
|
+
// Check for price intrusion before setting new SL
|
|
2944
|
+
if (signal.position === "long" && currentPrice < breakevenPrice) {
|
|
2945
|
+
// LONG: Price already crossed the breakeven level - skip setting SL
|
|
2946
|
+
self.params.logger.debug("BREAKEVEN_FN: price intrusion detected, skipping SL update", {
|
|
2947
|
+
signalId: signal.id,
|
|
2948
|
+
position: signal.position,
|
|
2949
|
+
priceOpen: signal.priceOpen,
|
|
2950
|
+
breakevenPrice,
|
|
2951
|
+
currentPrice,
|
|
2952
|
+
reason: "currentPrice below breakevenPrice (LONG position)"
|
|
2953
|
+
});
|
|
2954
|
+
return false;
|
|
2955
|
+
}
|
|
2956
|
+
if (signal.position === "short" && currentPrice > breakevenPrice) {
|
|
2957
|
+
// SHORT: Price already crossed the breakeven level - skip setting SL
|
|
2958
|
+
self.params.logger.debug("BREAKEVEN_FN: price intrusion detected, skipping SL update", {
|
|
2959
|
+
signalId: signal.id,
|
|
2960
|
+
position: signal.position,
|
|
2961
|
+
priceOpen: signal.priceOpen,
|
|
2962
|
+
breakevenPrice,
|
|
2963
|
+
currentPrice,
|
|
2964
|
+
reason: "currentPrice above breakevenPrice (SHORT position)"
|
|
2965
|
+
});
|
|
2966
|
+
return false;
|
|
2967
|
+
}
|
|
2917
2968
|
// Move SL to breakeven (entry price)
|
|
2918
2969
|
signal._trailingPriceStopLoss = breakevenPrice;
|
|
2919
2970
|
self.params.logger.info("BREAKEVEN_FN executed", {
|
|
@@ -4075,6 +4126,112 @@ class ClientStrategy {
|
|
|
4075
4126
|
});
|
|
4076
4127
|
return this._scheduledSignal ? TO_PUBLIC_SIGNAL(this._scheduledSignal) : null;
|
|
4077
4128
|
}
|
|
4129
|
+
/**
|
|
4130
|
+
* Checks if breakeven threshold has been reached for the current pending signal.
|
|
4131
|
+
*
|
|
4132
|
+
* Uses the same formula as BREAKEVEN_FN to determine if price has moved far enough
|
|
4133
|
+
* to cover transaction costs (slippage + fees) and allow breakeven to be set.
|
|
4134
|
+
* Threshold: (CC_PERCENT_SLIPPAGE + CC_PERCENT_FEE) * 2 transactions
|
|
4135
|
+
*
|
|
4136
|
+
* For LONG position:
|
|
4137
|
+
* - Returns true when: currentPrice >= priceOpen * (1 + threshold%)
|
|
4138
|
+
* - Example: entry=100, threshold=0.4% → true when price >= 100.4
|
|
4139
|
+
*
|
|
4140
|
+
* For SHORT position:
|
|
4141
|
+
* - Returns true when: currentPrice <= priceOpen * (1 - threshold%)
|
|
4142
|
+
* - Example: entry=100, threshold=0.4% → true when price <= 99.6
|
|
4143
|
+
*
|
|
4144
|
+
* Special cases:
|
|
4145
|
+
* - Returns false if no pending signal exists
|
|
4146
|
+
* - Returns true if trailing stop is already in profit zone (breakeven already achieved)
|
|
4147
|
+
* - Returns false if threshold not reached yet
|
|
4148
|
+
*
|
|
4149
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
4150
|
+
* @param currentPrice - Current market price to check against threshold
|
|
4151
|
+
* @returns Promise<boolean> - true if breakeven threshold reached, false otherwise
|
|
4152
|
+
*
|
|
4153
|
+
* @example
|
|
4154
|
+
* ```typescript
|
|
4155
|
+
* // Check if breakeven is available for LONG position (entry=100, threshold=0.4%)
|
|
4156
|
+
* const canBreakeven = await strategy.getBreakeven("BTCUSDT", 100.5);
|
|
4157
|
+
* // Returns true (price >= 100.4)
|
|
4158
|
+
*
|
|
4159
|
+
* if (canBreakeven) {
|
|
4160
|
+
* await strategy.breakeven("BTCUSDT", 100.5, false);
|
|
4161
|
+
* }
|
|
4162
|
+
* ```
|
|
4163
|
+
*/
|
|
4164
|
+
async getBreakeven(symbol, currentPrice) {
|
|
4165
|
+
this.params.logger.debug("ClientStrategy getBreakeven", {
|
|
4166
|
+
symbol,
|
|
4167
|
+
currentPrice,
|
|
4168
|
+
});
|
|
4169
|
+
// No pending signal - breakeven not available
|
|
4170
|
+
if (!this._pendingSignal) {
|
|
4171
|
+
return false;
|
|
4172
|
+
}
|
|
4173
|
+
const signal = this._pendingSignal;
|
|
4174
|
+
// Calculate breakeven threshold based on slippage and fees
|
|
4175
|
+
// Need to cover: entry slippage + entry fee + exit slippage + exit fee
|
|
4176
|
+
// Total: (slippage + fee) * 2 transactions
|
|
4177
|
+
const breakevenThresholdPercent = (GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE + GLOBAL_CONFIG.CC_PERCENT_FEE) * 2 + GLOBAL_CONFIG.CC_BREAKEVEN_THRESHOLD;
|
|
4178
|
+
// Check if trailing stop is already set
|
|
4179
|
+
if (signal._trailingPriceStopLoss !== undefined) {
|
|
4180
|
+
const trailingStopLoss = signal._trailingPriceStopLoss;
|
|
4181
|
+
if (signal.position === "long") {
|
|
4182
|
+
// LONG: trailing SL is positive if it's above entry (in profit zone)
|
|
4183
|
+
const isPositiveTrailing = trailingStopLoss > signal.priceOpen;
|
|
4184
|
+
if (isPositiveTrailing) {
|
|
4185
|
+
// Trailing stop is already protecting profit - breakeven achieved
|
|
4186
|
+
return true;
|
|
4187
|
+
}
|
|
4188
|
+
// Trailing stop is negative (below entry)
|
|
4189
|
+
// Check if we can upgrade it to breakeven
|
|
4190
|
+
const thresholdPrice = signal.priceOpen * (1 + breakevenThresholdPercent / 100);
|
|
4191
|
+
const isThresholdReached = currentPrice >= thresholdPrice;
|
|
4192
|
+
const breakevenPrice = signal.priceOpen;
|
|
4193
|
+
// Can upgrade to breakeven if threshold reached and breakeven is better than current trailing SL
|
|
4194
|
+
return isThresholdReached && breakevenPrice > trailingStopLoss;
|
|
4195
|
+
}
|
|
4196
|
+
else {
|
|
4197
|
+
// SHORT: trailing SL is positive if it's below entry (in profit zone)
|
|
4198
|
+
const isPositiveTrailing = trailingStopLoss < signal.priceOpen;
|
|
4199
|
+
if (isPositiveTrailing) {
|
|
4200
|
+
// Trailing stop is already protecting profit - breakeven achieved
|
|
4201
|
+
return true;
|
|
4202
|
+
}
|
|
4203
|
+
// Trailing stop is negative (above entry)
|
|
4204
|
+
// Check if we can upgrade it to breakeven
|
|
4205
|
+
const thresholdPrice = signal.priceOpen * (1 - breakevenThresholdPercent / 100);
|
|
4206
|
+
const isThresholdReached = currentPrice <= thresholdPrice;
|
|
4207
|
+
const breakevenPrice = signal.priceOpen;
|
|
4208
|
+
// Can upgrade to breakeven if threshold reached and breakeven is better than current trailing SL
|
|
4209
|
+
return isThresholdReached && breakevenPrice < trailingStopLoss;
|
|
4210
|
+
}
|
|
4211
|
+
}
|
|
4212
|
+
// No trailing stop set - proceed with normal breakeven logic
|
|
4213
|
+
const currentStopLoss = signal.priceStopLoss;
|
|
4214
|
+
const breakevenPrice = signal.priceOpen;
|
|
4215
|
+
// Calculate threshold price
|
|
4216
|
+
let thresholdPrice;
|
|
4217
|
+
let isThresholdReached;
|
|
4218
|
+
let canMoveToBreakeven;
|
|
4219
|
+
if (signal.position === "long") {
|
|
4220
|
+
// LONG: threshold reached when price goes UP by breakevenThresholdPercent from entry
|
|
4221
|
+
thresholdPrice = signal.priceOpen * (1 + breakevenThresholdPercent / 100);
|
|
4222
|
+
isThresholdReached = currentPrice >= thresholdPrice;
|
|
4223
|
+
// Can move to breakeven only if threshold reached and SL is below entry
|
|
4224
|
+
canMoveToBreakeven = isThresholdReached && currentStopLoss < breakevenPrice;
|
|
4225
|
+
}
|
|
4226
|
+
else {
|
|
4227
|
+
// SHORT: threshold reached when price goes DOWN by breakevenThresholdPercent from entry
|
|
4228
|
+
thresholdPrice = signal.priceOpen * (1 - breakevenThresholdPercent / 100);
|
|
4229
|
+
isThresholdReached = currentPrice <= thresholdPrice;
|
|
4230
|
+
// Can move to breakeven only if threshold reached and SL is above entry
|
|
4231
|
+
canMoveToBreakeven = isThresholdReached && currentStopLoss > breakevenPrice;
|
|
4232
|
+
}
|
|
4233
|
+
return canMoveToBreakeven;
|
|
4234
|
+
}
|
|
4078
4235
|
/**
|
|
4079
4236
|
* Returns the stopped state of the strategy.
|
|
4080
4237
|
*
|
|
@@ -4738,30 +4895,38 @@ class ClientStrategy {
|
|
|
4738
4895
|
* - Throws if percentShift is not a finite number
|
|
4739
4896
|
* - Throws if percentShift < -100 or > 100
|
|
4740
4897
|
* - Throws if percentShift === 0
|
|
4898
|
+
* - Throws if currentPrice is not a positive finite number
|
|
4741
4899
|
* - Skips if new SL would cross entry price
|
|
4900
|
+
* - Skips if currentPrice already crossed new SL level (price intrusion protection)
|
|
4742
4901
|
*
|
|
4743
4902
|
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
4744
4903
|
* @param percentShift - Percentage shift of SL distance [-100, 100], excluding 0
|
|
4904
|
+
* @param currentPrice - Current market price to check for intrusion
|
|
4745
4905
|
* @param backtest - Whether running in backtest mode (controls persistence)
|
|
4746
4906
|
* @returns Promise that resolves when trailing SL is updated and persisted
|
|
4747
4907
|
*
|
|
4748
4908
|
* @example
|
|
4749
4909
|
* ```typescript
|
|
4750
|
-
* // LONG position: entry=100, originalSL=90, distance=10
|
|
4910
|
+
* // LONG position: entry=100, originalSL=90, distance=10%, currentPrice=102
|
|
4911
|
+
*
|
|
4912
|
+
* // Move SL 50% closer to entry (tighten): reduces distance by 50%
|
|
4913
|
+
* await strategy.trailingStop("BTCUSDT", -50, 102, false);
|
|
4914
|
+
* // newDistance = 10% - 50% = 5%, newSL = 100 * (1 - 0.05) = 95
|
|
4751
4915
|
*
|
|
4752
|
-
* // Move SL
|
|
4753
|
-
* await strategy.trailingStop("BTCUSDT",
|
|
4754
|
-
* // newSL = 100
|
|
4916
|
+
* // Move SL 30% away from entry (loosen): increases distance by 30%
|
|
4917
|
+
* await strategy.trailingStop("BTCUSDT", 30, 102, false);
|
|
4918
|
+
* // newDistance = 10% + 30% = 40%, newSL = 100 * (1 - 0.4) = 60
|
|
4755
4919
|
*
|
|
4756
|
-
* //
|
|
4757
|
-
* await strategy.trailingStop("BTCUSDT",
|
|
4758
|
-
* //
|
|
4920
|
+
* // Price intrusion example: currentPrice=92, trying to set SL=95
|
|
4921
|
+
* await strategy.trailingStop("BTCUSDT", -50, 92, false);
|
|
4922
|
+
* // SKIPPED: currentPrice (92) < newSL (95) - would trigger immediate stop
|
|
4759
4923
|
* ```
|
|
4760
4924
|
*/
|
|
4761
|
-
async trailingStop(symbol, percentShift, backtest) {
|
|
4925
|
+
async trailingStop(symbol, percentShift, currentPrice, backtest) {
|
|
4762
4926
|
this.params.logger.debug("ClientStrategy trailingStop", {
|
|
4763
4927
|
symbol,
|
|
4764
4928
|
percentShift,
|
|
4929
|
+
currentPrice,
|
|
4765
4930
|
hasPendingSignal: this._pendingSignal !== null,
|
|
4766
4931
|
});
|
|
4767
4932
|
// Validation: must have pending signal
|
|
@@ -4778,6 +4943,46 @@ class ClientStrategy {
|
|
|
4778
4943
|
if (percentShift === 0) {
|
|
4779
4944
|
throw new Error(`ClientStrategy trailingStop: percentShift cannot be 0`);
|
|
4780
4945
|
}
|
|
4946
|
+
// Validation: currentPrice must be valid
|
|
4947
|
+
if (typeof currentPrice !== "number" || !isFinite(currentPrice) || currentPrice <= 0) {
|
|
4948
|
+
throw new Error(`ClientStrategy trailingStop: currentPrice must be a positive finite number, got ${currentPrice}`);
|
|
4949
|
+
}
|
|
4950
|
+
// Calculate what the new stop loss would be
|
|
4951
|
+
const signal = this._pendingSignal;
|
|
4952
|
+
const slDistancePercent = Math.abs((signal.priceOpen - signal.priceStopLoss) / signal.priceOpen * 100);
|
|
4953
|
+
const newSlDistancePercent = slDistancePercent + percentShift;
|
|
4954
|
+
let newStopLoss;
|
|
4955
|
+
if (signal.position === "long") {
|
|
4956
|
+
newStopLoss = signal.priceOpen * (1 - newSlDistancePercent / 100);
|
|
4957
|
+
}
|
|
4958
|
+
else {
|
|
4959
|
+
newStopLoss = signal.priceOpen * (1 + newSlDistancePercent / 100);
|
|
4960
|
+
}
|
|
4961
|
+
// Check for price intrusion before executing trailing logic
|
|
4962
|
+
if (signal.position === "long" && currentPrice < newStopLoss) {
|
|
4963
|
+
// LONG: Price already crossed the new stop loss level - skip setting SL
|
|
4964
|
+
this.params.logger.debug("ClientStrategy trailingStop: price intrusion detected, skipping SL update", {
|
|
4965
|
+
signalId: signal.id,
|
|
4966
|
+
position: signal.position,
|
|
4967
|
+
priceOpen: signal.priceOpen,
|
|
4968
|
+
newStopLoss,
|
|
4969
|
+
currentPrice,
|
|
4970
|
+
reason: "currentPrice below newStopLoss (LONG position)"
|
|
4971
|
+
});
|
|
4972
|
+
return;
|
|
4973
|
+
}
|
|
4974
|
+
if (signal.position === "short" && currentPrice > newStopLoss) {
|
|
4975
|
+
// SHORT: Price already crossed the new stop loss level - skip setting SL
|
|
4976
|
+
this.params.logger.debug("ClientStrategy trailingStop: price intrusion detected, skipping SL update", {
|
|
4977
|
+
signalId: signal.id,
|
|
4978
|
+
position: signal.position,
|
|
4979
|
+
priceOpen: signal.priceOpen,
|
|
4980
|
+
newStopLoss,
|
|
4981
|
+
currentPrice,
|
|
4982
|
+
reason: "currentPrice above newStopLoss (SHORT position)"
|
|
4983
|
+
});
|
|
4984
|
+
return;
|
|
4985
|
+
}
|
|
4781
4986
|
// Execute trailing logic
|
|
4782
4987
|
TRAILING_STOP_FN(this, this._pendingSignal, percentShift);
|
|
4783
4988
|
// Persist updated signal state (inline setPendingSignal content)
|
|
@@ -4963,6 +5168,7 @@ class RiskUtils {
|
|
|
4963
5168
|
strategyName: context.strategyName,
|
|
4964
5169
|
});
|
|
4965
5170
|
bt.strategyValidationService.validate(context.strategyName, RISK_METHOD_NAME_GET_DATA);
|
|
5171
|
+
bt.exchangeValidationService.validate(context.exchangeName, RISK_METHOD_NAME_GET_DATA);
|
|
4966
5172
|
{
|
|
4967
5173
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
4968
5174
|
riskName &&
|
|
@@ -5019,6 +5225,7 @@ class RiskUtils {
|
|
|
5019
5225
|
strategyName: context.strategyName,
|
|
5020
5226
|
});
|
|
5021
5227
|
bt.strategyValidationService.validate(context.strategyName, RISK_METHOD_NAME_GET_REPORT);
|
|
5228
|
+
bt.exchangeValidationService.validate(context.exchangeName, RISK_METHOD_NAME_GET_REPORT);
|
|
5022
5229
|
{
|
|
5023
5230
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
5024
5231
|
riskName &&
|
|
@@ -5067,6 +5274,7 @@ class RiskUtils {
|
|
|
5067
5274
|
path,
|
|
5068
5275
|
});
|
|
5069
5276
|
bt.strategyValidationService.validate(context.strategyName, RISK_METHOD_NAME_DUMP);
|
|
5277
|
+
bt.exchangeValidationService.validate(context.exchangeName, RISK_METHOD_NAME_DUMP);
|
|
5070
5278
|
{
|
|
5071
5279
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
5072
5280
|
riskName &&
|
|
@@ -5277,6 +5485,46 @@ class StrategyConnectionService {
|
|
|
5277
5485
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
5278
5486
|
return await strategy.getScheduledSignal(symbol);
|
|
5279
5487
|
};
|
|
5488
|
+
/**
|
|
5489
|
+
* Checks if breakeven threshold has been reached for the current pending signal.
|
|
5490
|
+
*
|
|
5491
|
+
* Uses the same formula as BREAKEVEN_FN to determine if price has moved far enough
|
|
5492
|
+
* to cover transaction costs and allow breakeven to be set.
|
|
5493
|
+
*
|
|
5494
|
+
* Delegates to ClientStrategy.getBreakeven() with current execution context.
|
|
5495
|
+
*
|
|
5496
|
+
* @param backtest - Whether running in backtest mode
|
|
5497
|
+
* @param symbol - Trading pair symbol
|
|
5498
|
+
* @param currentPrice - Current market price to check against threshold
|
|
5499
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
5500
|
+
* @returns Promise<boolean> - true if breakeven threshold reached, false otherwise
|
|
5501
|
+
*
|
|
5502
|
+
* @example
|
|
5503
|
+
* ```typescript
|
|
5504
|
+
* // Check if breakeven is available for LONG position (entry=100, threshold=0.4%)
|
|
5505
|
+
* const canBreakeven = await strategyConnectionService.getBreakeven(
|
|
5506
|
+
* false,
|
|
5507
|
+
* "BTCUSDT",
|
|
5508
|
+
* 100.5,
|
|
5509
|
+
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "" }
|
|
5510
|
+
* );
|
|
5511
|
+
* // Returns true (price >= 100.4)
|
|
5512
|
+
*
|
|
5513
|
+
* if (canBreakeven) {
|
|
5514
|
+
* await strategyConnectionService.breakeven(false, "BTCUSDT", 100.5, context);
|
|
5515
|
+
* }
|
|
5516
|
+
* ```
|
|
5517
|
+
*/
|
|
5518
|
+
this.getBreakeven = async (backtest, symbol, currentPrice, context) => {
|
|
5519
|
+
this.loggerService.log("strategyConnectionService getBreakeven", {
|
|
5520
|
+
symbol,
|
|
5521
|
+
context,
|
|
5522
|
+
currentPrice,
|
|
5523
|
+
backtest,
|
|
5524
|
+
});
|
|
5525
|
+
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
5526
|
+
return await strategy.getBreakeven(symbol, currentPrice);
|
|
5527
|
+
};
|
|
5280
5528
|
/**
|
|
5281
5529
|
* Retrieves the stopped state of the strategy.
|
|
5282
5530
|
*
|
|
@@ -5511,30 +5759,33 @@ class StrategyConnectionService {
|
|
|
5511
5759
|
* @param backtest - Whether running in backtest mode
|
|
5512
5760
|
* @param symbol - Trading pair symbol
|
|
5513
5761
|
* @param percentShift - Percentage adjustment to SL distance (-100 to 100)
|
|
5762
|
+
* @param currentPrice - Current market price to check for intrusion
|
|
5514
5763
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
5515
5764
|
* @returns Promise that resolves when trailing SL is updated
|
|
5516
5765
|
*
|
|
5517
5766
|
* @example
|
|
5518
5767
|
* ```typescript
|
|
5519
|
-
* // LONG: entry=100, originalSL=90, distance=10
|
|
5520
|
-
* // Tighten stop by 50%: newSL = 100 -
|
|
5768
|
+
* // LONG: entry=100, originalSL=90, distance=10%, currentPrice=102
|
|
5769
|
+
* // Tighten stop by 50%: newSL = 100 - 5% = 95
|
|
5521
5770
|
* await strategyConnectionService.trailingStop(
|
|
5522
5771
|
* false,
|
|
5523
5772
|
* "BTCUSDT",
|
|
5524
5773
|
* -50,
|
|
5774
|
+
* 102,
|
|
5525
5775
|
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "" }
|
|
5526
5776
|
* );
|
|
5527
5777
|
* ```
|
|
5528
5778
|
*/
|
|
5529
|
-
this.trailingStop = async (backtest, symbol, percentShift, context) => {
|
|
5779
|
+
this.trailingStop = async (backtest, symbol, percentShift, currentPrice, context) => {
|
|
5530
5780
|
this.loggerService.log("strategyConnectionService trailingStop", {
|
|
5531
5781
|
symbol,
|
|
5532
5782
|
context,
|
|
5533
5783
|
percentShift,
|
|
5784
|
+
currentPrice,
|
|
5534
5785
|
backtest,
|
|
5535
5786
|
});
|
|
5536
5787
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
5537
|
-
await strategy.trailingStop(symbol, percentShift, backtest);
|
|
5788
|
+
await strategy.trailingStop(symbol, percentShift, currentPrice, backtest);
|
|
5538
5789
|
};
|
|
5539
5790
|
/**
|
|
5540
5791
|
* Delegates to ClientStrategy.breakeven() with current execution context.
|
|
@@ -6675,6 +6926,46 @@ class StrategyCoreService {
|
|
|
6675
6926
|
await this.validate(symbol, context);
|
|
6676
6927
|
return await this.strategyConnectionService.getScheduledSignal(backtest, symbol, context);
|
|
6677
6928
|
};
|
|
6929
|
+
/**
|
|
6930
|
+
* Checks if breakeven threshold has been reached for the current pending signal.
|
|
6931
|
+
*
|
|
6932
|
+
* Validates strategy existence and delegates to connection service
|
|
6933
|
+
* to check if price has moved far enough to cover transaction costs.
|
|
6934
|
+
*
|
|
6935
|
+
* Does not require execution context as this is a state query operation.
|
|
6936
|
+
*
|
|
6937
|
+
* @param backtest - Whether running in backtest mode
|
|
6938
|
+
* @param symbol - Trading pair symbol
|
|
6939
|
+
* @param currentPrice - Current market price to check against threshold
|
|
6940
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
6941
|
+
* @returns Promise<boolean> - true if breakeven threshold reached, false otherwise
|
|
6942
|
+
*
|
|
6943
|
+
* @example
|
|
6944
|
+
* ```typescript
|
|
6945
|
+
* // Check if breakeven is available for LONG position (entry=100, threshold=0.4%)
|
|
6946
|
+
* const canBreakeven = await strategyCoreService.getBreakeven(
|
|
6947
|
+
* false,
|
|
6948
|
+
* "BTCUSDT",
|
|
6949
|
+
* 100.5,
|
|
6950
|
+
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "" }
|
|
6951
|
+
* );
|
|
6952
|
+
* // Returns true (price >= 100.4)
|
|
6953
|
+
*
|
|
6954
|
+
* if (canBreakeven) {
|
|
6955
|
+
* await strategyCoreService.breakeven(false, "BTCUSDT", 100.5, context);
|
|
6956
|
+
* }
|
|
6957
|
+
* ```
|
|
6958
|
+
*/
|
|
6959
|
+
this.getBreakeven = async (backtest, symbol, currentPrice, context) => {
|
|
6960
|
+
this.loggerService.log("strategyCoreService getBreakeven", {
|
|
6961
|
+
symbol,
|
|
6962
|
+
currentPrice,
|
|
6963
|
+
context,
|
|
6964
|
+
backtest,
|
|
6965
|
+
});
|
|
6966
|
+
await this.validate(symbol, context);
|
|
6967
|
+
return await this.strategyConnectionService.getBreakeven(backtest, symbol, currentPrice, context);
|
|
6968
|
+
};
|
|
6678
6969
|
/**
|
|
6679
6970
|
* Checks if the strategy has been stopped.
|
|
6680
6971
|
*
|
|
@@ -6904,30 +7195,33 @@ class StrategyCoreService {
|
|
|
6904
7195
|
* @param backtest - Whether running in backtest mode
|
|
6905
7196
|
* @param symbol - Trading pair symbol
|
|
6906
7197
|
* @param percentShift - Percentage adjustment to SL distance (-100 to 100)
|
|
7198
|
+
* @param currentPrice - Current market price to check for intrusion
|
|
6907
7199
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
6908
7200
|
* @returns Promise that resolves when trailing SL is updated
|
|
6909
7201
|
*
|
|
6910
7202
|
* @example
|
|
6911
7203
|
* ```typescript
|
|
6912
|
-
* // LONG: entry=100, originalSL=90, distance=10
|
|
6913
|
-
* // Tighten stop by 50%: newSL = 100 -
|
|
7204
|
+
* // LONG: entry=100, originalSL=90, distance=10%, currentPrice=102
|
|
7205
|
+
* // Tighten stop by 50%: newSL = 100 - 5% = 95
|
|
6914
7206
|
* await strategyCoreService.trailingStop(
|
|
6915
7207
|
* false,
|
|
6916
7208
|
* "BTCUSDT",
|
|
6917
7209
|
* -50,
|
|
7210
|
+
* 102,
|
|
6918
7211
|
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "" }
|
|
6919
7212
|
* );
|
|
6920
7213
|
* ```
|
|
6921
7214
|
*/
|
|
6922
|
-
this.trailingStop = async (backtest, symbol, percentShift, context) => {
|
|
7215
|
+
this.trailingStop = async (backtest, symbol, percentShift, currentPrice, context) => {
|
|
6923
7216
|
this.loggerService.log("strategyCoreService trailingStop", {
|
|
6924
7217
|
symbol,
|
|
6925
7218
|
percentShift,
|
|
7219
|
+
currentPrice,
|
|
6926
7220
|
context,
|
|
6927
7221
|
backtest,
|
|
6928
7222
|
});
|
|
6929
7223
|
await this.validate(symbol, context);
|
|
6930
|
-
return await this.strategyConnectionService.trailingStop(backtest, symbol, percentShift, context);
|
|
7224
|
+
return await this.strategyConnectionService.trailingStop(backtest, symbol, percentShift, currentPrice, context);
|
|
6931
7225
|
};
|
|
6932
7226
|
/**
|
|
6933
7227
|
* Moves stop-loss to breakeven when price reaches threshold.
|
|
@@ -7036,6 +7330,7 @@ class RiskGlobalService {
|
|
|
7036
7330
|
this.loggerService = inject(TYPES.loggerService);
|
|
7037
7331
|
this.riskConnectionService = inject(TYPES.riskConnectionService);
|
|
7038
7332
|
this.riskValidationService = inject(TYPES.riskValidationService);
|
|
7333
|
+
this.exchangeValidationService = inject(TYPES.exchangeValidationService);
|
|
7039
7334
|
/**
|
|
7040
7335
|
* Validates risk configuration.
|
|
7041
7336
|
* Memoized to avoid redundant validations for the same risk-exchange-frame combination.
|
|
@@ -7048,6 +7343,7 @@ class RiskGlobalService {
|
|
|
7048
7343
|
payload,
|
|
7049
7344
|
});
|
|
7050
7345
|
this.riskValidationService.validate(payload.riskName, "riskGlobalService validate");
|
|
7346
|
+
this.exchangeValidationService.validate(payload.exchangeName, "riskGlobalService validate");
|
|
7051
7347
|
});
|
|
7052
7348
|
/**
|
|
7053
7349
|
* Checks if a signal should be allowed based on risk limits.
|
|
@@ -15320,6 +15616,10 @@ class PartialGlobalService {
|
|
|
15320
15616
|
* Risk validation service for validating risk existence.
|
|
15321
15617
|
*/
|
|
15322
15618
|
this.riskValidationService = inject(TYPES.riskValidationService);
|
|
15619
|
+
/**
|
|
15620
|
+
* Exchange validation service for validating exchange existence.
|
|
15621
|
+
*/
|
|
15622
|
+
this.exchangeValidationService = inject(TYPES.exchangeValidationService);
|
|
15323
15623
|
/**
|
|
15324
15624
|
* Validates strategy and associated risk configuration.
|
|
15325
15625
|
* Memoized to avoid redundant validations for the same strategy-exchange-frame combination.
|
|
@@ -15333,6 +15633,7 @@ class PartialGlobalService {
|
|
|
15333
15633
|
methodName,
|
|
15334
15634
|
});
|
|
15335
15635
|
this.strategyValidationService.validate(context.strategyName, methodName);
|
|
15636
|
+
this.exchangeValidationService.validate(context.exchangeName, methodName);
|
|
15336
15637
|
const { riskName, riskList } = this.strategySchemaService.get(context.strategyName);
|
|
15337
15638
|
riskName && this.riskValidationService.validate(riskName, methodName);
|
|
15338
15639
|
riskList && riskList.forEach((riskName) => this.riskValidationService.validate(riskName, methodName));
|
|
@@ -16294,6 +16595,10 @@ class BreakevenGlobalService {
|
|
|
16294
16595
|
* Risk validation service for validating risk existence.
|
|
16295
16596
|
*/
|
|
16296
16597
|
this.riskValidationService = inject(TYPES.riskValidationService);
|
|
16598
|
+
/**
|
|
16599
|
+
* Exchange validation service for validating exchange existence.
|
|
16600
|
+
*/
|
|
16601
|
+
this.exchangeValidationService = inject(TYPES.exchangeValidationService);
|
|
16297
16602
|
/**
|
|
16298
16603
|
* Validates strategy and associated risk configuration.
|
|
16299
16604
|
* Memoized to avoid redundant validations for the same strategy-exchange-frame combination.
|
|
@@ -16307,6 +16612,7 @@ class BreakevenGlobalService {
|
|
|
16307
16612
|
methodName,
|
|
16308
16613
|
});
|
|
16309
16614
|
this.strategyValidationService.validate(context.strategyName, methodName);
|
|
16615
|
+
this.exchangeValidationService.validate(context.exchangeName, methodName);
|
|
16310
16616
|
const { riskName, riskList } = this.strategySchemaService.get(context.strategyName);
|
|
16311
16617
|
riskName && this.riskValidationService.validate(riskName, methodName);
|
|
16312
16618
|
riskList && riskList.forEach((riskName) => this.riskValidationService.validate(riskName, methodName));
|
|
@@ -17787,21 +18093,23 @@ async function partialLoss(symbol, percentToClose) {
|
|
|
17787
18093
|
*
|
|
17788
18094
|
* @param symbol - Trading pair symbol
|
|
17789
18095
|
* @param percentShift - Percentage adjustment to SL distance (-100 to 100)
|
|
18096
|
+
* @param currentPrice - Current market price to check for intrusion
|
|
17790
18097
|
* @returns Promise that resolves when trailing SL is updated
|
|
17791
18098
|
*
|
|
17792
18099
|
* @example
|
|
17793
18100
|
* ```typescript
|
|
17794
18101
|
* import { trailingStop } from "backtest-kit";
|
|
17795
18102
|
*
|
|
17796
|
-
* // LONG: entry=100, originalSL=90, distance=10
|
|
17797
|
-
* // Tighten stop by 50%: newSL = 100 -
|
|
17798
|
-
* await trailingStop("BTCUSDT", -50);
|
|
18103
|
+
* // LONG: entry=100, originalSL=90, distance=10%, currentPrice=102
|
|
18104
|
+
* // Tighten stop by 50%: newSL = 100 - 5% = 95
|
|
18105
|
+
* await trailingStop("BTCUSDT", -50, 102);
|
|
17799
18106
|
* ```
|
|
17800
18107
|
*/
|
|
17801
|
-
async function trailingStop(symbol, percentShift) {
|
|
18108
|
+
async function trailingStop(symbol, percentShift, currentPrice) {
|
|
17802
18109
|
bt.loggerService.info(TRAILING_STOP_METHOD_NAME, {
|
|
17803
18110
|
symbol,
|
|
17804
18111
|
percentShift,
|
|
18112
|
+
currentPrice,
|
|
17805
18113
|
});
|
|
17806
18114
|
if (!ExecutionContextService.hasContext()) {
|
|
17807
18115
|
throw new Error("trailingStop requires an execution context");
|
|
@@ -17811,7 +18119,7 @@ async function trailingStop(symbol, percentShift) {
|
|
|
17811
18119
|
}
|
|
17812
18120
|
const { backtest: isBacktest } = bt.executionContextService.context;
|
|
17813
18121
|
const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
|
|
17814
|
-
await bt.strategyCoreService.trailingStop(isBacktest, symbol, percentShift, { exchangeName, frameName, strategyName });
|
|
18122
|
+
await bt.strategyCoreService.trailingStop(isBacktest, symbol, percentShift, currentPrice, { exchangeName, frameName, strategyName });
|
|
17815
18123
|
}
|
|
17816
18124
|
/**
|
|
17817
18125
|
* Moves stop-loss to breakeven when price reaches threshold.
|
|
@@ -19753,6 +20061,7 @@ const BACKTEST_METHOD_NAME_TASK = "BacktestUtils.task";
|
|
|
19753
20061
|
const BACKTEST_METHOD_NAME_GET_STATUS = "BacktestUtils.getStatus";
|
|
19754
20062
|
const BACKTEST_METHOD_NAME_GET_PENDING_SIGNAL = "BacktestUtils.getPendingSignal";
|
|
19755
20063
|
const BACKTEST_METHOD_NAME_GET_SCHEDULED_SIGNAL = "BacktestUtils.getScheduledSignal";
|
|
20064
|
+
const BACKTEST_METHOD_NAME_GET_BREAKEVEN = "BacktestUtils.getBreakeven";
|
|
19756
20065
|
const BACKTEST_METHOD_NAME_CANCEL = "BacktestUtils.cancel";
|
|
19757
20066
|
const BACKTEST_METHOD_NAME_PARTIAL_PROFIT = "BacktestUtils.partialProfit";
|
|
19758
20067
|
const BACKTEST_METHOD_NAME_PARTIAL_LOSS = "BacktestUtils.partialLoss";
|
|
@@ -20123,6 +20432,7 @@ class BacktestUtils {
|
|
|
20123
20432
|
context,
|
|
20124
20433
|
});
|
|
20125
20434
|
bt.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_GET_PENDING_SIGNAL);
|
|
20435
|
+
bt.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_GET_PENDING_SIGNAL);
|
|
20126
20436
|
{
|
|
20127
20437
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
20128
20438
|
riskName &&
|
|
@@ -20154,6 +20464,7 @@ class BacktestUtils {
|
|
|
20154
20464
|
context,
|
|
20155
20465
|
});
|
|
20156
20466
|
bt.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_GET_SCHEDULED_SIGNAL);
|
|
20467
|
+
bt.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_GET_SCHEDULED_SIGNAL);
|
|
20157
20468
|
{
|
|
20158
20469
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
20159
20470
|
riskName &&
|
|
@@ -20163,6 +20474,47 @@ class BacktestUtils {
|
|
|
20163
20474
|
}
|
|
20164
20475
|
return await bt.strategyCoreService.getScheduledSignal(true, symbol, context);
|
|
20165
20476
|
};
|
|
20477
|
+
/**
|
|
20478
|
+
* Checks if breakeven threshold has been reached for the current pending signal.
|
|
20479
|
+
*
|
|
20480
|
+
* Uses the same formula as BREAKEVEN_FN to determine if price has moved far enough
|
|
20481
|
+
* to cover transaction costs (slippage + fees) and allow breakeven to be set.
|
|
20482
|
+
*
|
|
20483
|
+
* @param symbol - Trading pair symbol
|
|
20484
|
+
* @param currentPrice - Current market price to check against threshold
|
|
20485
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
20486
|
+
* @returns Promise<boolean> - true if breakeven threshold reached, false otherwise
|
|
20487
|
+
*
|
|
20488
|
+
* @example
|
|
20489
|
+
* ```typescript
|
|
20490
|
+
* const canBreakeven = await Backtest.getBreakeven("BTCUSDT", 100.5, {
|
|
20491
|
+
* strategyName: "my-strategy",
|
|
20492
|
+
* exchangeName: "binance",
|
|
20493
|
+
* frameName: "backtest_frame"
|
|
20494
|
+
* });
|
|
20495
|
+
* if (canBreakeven) {
|
|
20496
|
+
* console.log("Breakeven threshold reached");
|
|
20497
|
+
* await Backtest.breakeven("BTCUSDT", 100.5, context);
|
|
20498
|
+
* }
|
|
20499
|
+
* ```
|
|
20500
|
+
*/
|
|
20501
|
+
this.getBreakeven = async (symbol, currentPrice, context) => {
|
|
20502
|
+
bt.loggerService.info(BACKTEST_METHOD_NAME_GET_BREAKEVEN, {
|
|
20503
|
+
symbol,
|
|
20504
|
+
currentPrice,
|
|
20505
|
+
context,
|
|
20506
|
+
});
|
|
20507
|
+
bt.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_GET_BREAKEVEN);
|
|
20508
|
+
bt.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_GET_BREAKEVEN);
|
|
20509
|
+
{
|
|
20510
|
+
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
20511
|
+
riskName &&
|
|
20512
|
+
bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_BREAKEVEN);
|
|
20513
|
+
riskList &&
|
|
20514
|
+
riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_BREAKEVEN));
|
|
20515
|
+
}
|
|
20516
|
+
return await bt.strategyCoreService.getBreakeven(true, symbol, currentPrice, context);
|
|
20517
|
+
};
|
|
20166
20518
|
/**
|
|
20167
20519
|
* Stops the strategy from generating new signals.
|
|
20168
20520
|
*
|
|
@@ -20191,6 +20543,7 @@ class BacktestUtils {
|
|
|
20191
20543
|
context,
|
|
20192
20544
|
});
|
|
20193
20545
|
bt.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_STOP);
|
|
20546
|
+
bt.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_STOP);
|
|
20194
20547
|
{
|
|
20195
20548
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
20196
20549
|
riskName &&
|
|
@@ -20230,6 +20583,7 @@ class BacktestUtils {
|
|
|
20230
20583
|
cancelId,
|
|
20231
20584
|
});
|
|
20232
20585
|
bt.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_CANCEL);
|
|
20586
|
+
bt.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_CANCEL);
|
|
20233
20587
|
{
|
|
20234
20588
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
20235
20589
|
riskName &&
|
|
@@ -20273,6 +20627,7 @@ class BacktestUtils {
|
|
|
20273
20627
|
context,
|
|
20274
20628
|
});
|
|
20275
20629
|
bt.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_PARTIAL_PROFIT);
|
|
20630
|
+
bt.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_PARTIAL_PROFIT);
|
|
20276
20631
|
{
|
|
20277
20632
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
20278
20633
|
riskName &&
|
|
@@ -20316,6 +20671,7 @@ class BacktestUtils {
|
|
|
20316
20671
|
context,
|
|
20317
20672
|
});
|
|
20318
20673
|
bt.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_PARTIAL_LOSS);
|
|
20674
|
+
bt.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_PARTIAL_LOSS);
|
|
20319
20675
|
{
|
|
20320
20676
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
20321
20677
|
riskName &&
|
|
@@ -20333,27 +20689,30 @@ class BacktestUtils {
|
|
|
20333
20689
|
*
|
|
20334
20690
|
* @param symbol - Trading pair symbol
|
|
20335
20691
|
* @param percentShift - Percentage adjustment to SL distance (-100 to 100)
|
|
20692
|
+
* @param currentPrice - Current market price to check for intrusion
|
|
20336
20693
|
* @param context - Execution context with strategyName, exchangeName, and frameName
|
|
20337
20694
|
* @returns Promise that resolves when trailing SL is updated
|
|
20338
20695
|
*
|
|
20339
20696
|
* @example
|
|
20340
20697
|
* ```typescript
|
|
20341
|
-
* // LONG: entry=100, originalSL=90, distance=10
|
|
20342
|
-
* // Tighten stop by 50%: newSL = 100 -
|
|
20343
|
-
* await Backtest.trailingStop("BTCUSDT", -50, {
|
|
20698
|
+
* // LONG: entry=100, originalSL=90, distance=10%, currentPrice=102
|
|
20699
|
+
* // Tighten stop by 50%: newSL = 100 - 5% = 95
|
|
20700
|
+
* await Backtest.trailingStop("BTCUSDT", -50, 102, {
|
|
20344
20701
|
* exchangeName: "binance",
|
|
20345
20702
|
* frameName: "frame1",
|
|
20346
20703
|
* strategyName: "my-strategy"
|
|
20347
20704
|
* });
|
|
20348
20705
|
* ```
|
|
20349
20706
|
*/
|
|
20350
|
-
this.trailingStop = async (symbol, percentShift, context) => {
|
|
20707
|
+
this.trailingStop = async (symbol, percentShift, currentPrice, context) => {
|
|
20351
20708
|
bt.loggerService.info(BACKTEST_METHOD_NAME_TRAILING_STOP, {
|
|
20352
20709
|
symbol,
|
|
20353
20710
|
percentShift,
|
|
20711
|
+
currentPrice,
|
|
20354
20712
|
context,
|
|
20355
20713
|
});
|
|
20356
20714
|
bt.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_TRAILING_STOP);
|
|
20715
|
+
bt.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_TRAILING_STOP);
|
|
20357
20716
|
{
|
|
20358
20717
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
20359
20718
|
riskName &&
|
|
@@ -20361,7 +20720,7 @@ class BacktestUtils {
|
|
|
20361
20720
|
riskList &&
|
|
20362
20721
|
riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_TRAILING_STOP));
|
|
20363
20722
|
}
|
|
20364
|
-
await bt.strategyCoreService.trailingStop(true, symbol, percentShift, context);
|
|
20723
|
+
await bt.strategyCoreService.trailingStop(true, symbol, percentShift, currentPrice, context);
|
|
20365
20724
|
};
|
|
20366
20725
|
/**
|
|
20367
20726
|
* Moves stop-loss to breakeven when price reaches threshold.
|
|
@@ -20391,6 +20750,7 @@ class BacktestUtils {
|
|
|
20391
20750
|
context,
|
|
20392
20751
|
});
|
|
20393
20752
|
bt.strategyValidationService.validate(context.strategyName, "Backtest.breakeven");
|
|
20753
|
+
bt.exchangeValidationService.validate(context.exchangeName, "Backtest.breakeven");
|
|
20394
20754
|
{
|
|
20395
20755
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
20396
20756
|
riskName &&
|
|
@@ -20424,6 +20784,7 @@ class BacktestUtils {
|
|
|
20424
20784
|
context,
|
|
20425
20785
|
});
|
|
20426
20786
|
bt.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_GET_DATA);
|
|
20787
|
+
bt.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_GET_DATA);
|
|
20427
20788
|
{
|
|
20428
20789
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
20429
20790
|
riskName &&
|
|
@@ -20458,6 +20819,7 @@ class BacktestUtils {
|
|
|
20458
20819
|
context,
|
|
20459
20820
|
});
|
|
20460
20821
|
bt.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_GET_REPORT);
|
|
20822
|
+
bt.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_GET_REPORT);
|
|
20461
20823
|
{
|
|
20462
20824
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
20463
20825
|
riskName &&
|
|
@@ -20500,6 +20862,7 @@ class BacktestUtils {
|
|
|
20500
20862
|
path,
|
|
20501
20863
|
});
|
|
20502
20864
|
bt.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_DUMP);
|
|
20865
|
+
bt.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_DUMP);
|
|
20503
20866
|
{
|
|
20504
20867
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
20505
20868
|
riskName &&
|
|
@@ -20558,6 +20921,7 @@ const LIVE_METHOD_NAME_TASK = "LiveUtils.task";
|
|
|
20558
20921
|
const LIVE_METHOD_NAME_GET_STATUS = "LiveUtils.getStatus";
|
|
20559
20922
|
const LIVE_METHOD_NAME_GET_PENDING_SIGNAL = "LiveUtils.getPendingSignal";
|
|
20560
20923
|
const LIVE_METHOD_NAME_GET_SCHEDULED_SIGNAL = "LiveUtils.getScheduledSignal";
|
|
20924
|
+
const LIVE_METHOD_NAME_GET_BREAKEVEN = "LiveUtils.getBreakeven";
|
|
20561
20925
|
const LIVE_METHOD_NAME_CANCEL = "LiveUtils.cancel";
|
|
20562
20926
|
const LIVE_METHOD_NAME_PARTIAL_PROFIT = "LiveUtils.partialProfit";
|
|
20563
20927
|
const LIVE_METHOD_NAME_PARTIAL_LOSS = "LiveUtils.partialLoss";
|
|
@@ -20899,6 +21263,7 @@ class LiveUtils {
|
|
|
20899
21263
|
context,
|
|
20900
21264
|
});
|
|
20901
21265
|
bt.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_GET_PENDING_SIGNAL);
|
|
21266
|
+
bt.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_GET_PENDING_SIGNAL);
|
|
20902
21267
|
{
|
|
20903
21268
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
20904
21269
|
riskName && bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_PENDING_SIGNAL);
|
|
@@ -20932,6 +21297,7 @@ class LiveUtils {
|
|
|
20932
21297
|
context,
|
|
20933
21298
|
});
|
|
20934
21299
|
bt.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_GET_SCHEDULED_SIGNAL);
|
|
21300
|
+
bt.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_GET_SCHEDULED_SIGNAL);
|
|
20935
21301
|
{
|
|
20936
21302
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
20937
21303
|
riskName && bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_SCHEDULED_SIGNAL);
|
|
@@ -20943,6 +21309,48 @@ class LiveUtils {
|
|
|
20943
21309
|
frameName: "",
|
|
20944
21310
|
});
|
|
20945
21311
|
};
|
|
21312
|
+
/**
|
|
21313
|
+
* Checks if breakeven threshold has been reached for the current pending signal.
|
|
21314
|
+
*
|
|
21315
|
+
* Uses the same formula as BREAKEVEN_FN to determine if price has moved far enough
|
|
21316
|
+
* to cover transaction costs (slippage + fees) and allow breakeven to be set.
|
|
21317
|
+
*
|
|
21318
|
+
* @param symbol - Trading pair symbol
|
|
21319
|
+
* @param currentPrice - Current market price to check against threshold
|
|
21320
|
+
* @param context - Execution context with strategyName and exchangeName
|
|
21321
|
+
* @returns Promise<boolean> - true if breakeven threshold reached, false otherwise
|
|
21322
|
+
*
|
|
21323
|
+
* @example
|
|
21324
|
+
* ```typescript
|
|
21325
|
+
* const canBreakeven = await Live.getBreakeven("BTCUSDT", 100.5, {
|
|
21326
|
+
* strategyName: "my-strategy",
|
|
21327
|
+
* exchangeName: "binance"
|
|
21328
|
+
* });
|
|
21329
|
+
* if (canBreakeven) {
|
|
21330
|
+
* console.log("Breakeven threshold reached");
|
|
21331
|
+
* await Live.breakeven("BTCUSDT", 100.5, context);
|
|
21332
|
+
* }
|
|
21333
|
+
* ```
|
|
21334
|
+
*/
|
|
21335
|
+
this.getBreakeven = async (symbol, currentPrice, context) => {
|
|
21336
|
+
bt.loggerService.info(LIVE_METHOD_NAME_GET_BREAKEVEN, {
|
|
21337
|
+
symbol,
|
|
21338
|
+
currentPrice,
|
|
21339
|
+
context,
|
|
21340
|
+
});
|
|
21341
|
+
bt.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_GET_BREAKEVEN);
|
|
21342
|
+
bt.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_GET_BREAKEVEN);
|
|
21343
|
+
{
|
|
21344
|
+
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
21345
|
+
riskName && bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_BREAKEVEN);
|
|
21346
|
+
riskList && riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_BREAKEVEN));
|
|
21347
|
+
}
|
|
21348
|
+
return await bt.strategyCoreService.getBreakeven(false, symbol, currentPrice, {
|
|
21349
|
+
strategyName: context.strategyName,
|
|
21350
|
+
exchangeName: context.exchangeName,
|
|
21351
|
+
frameName: "",
|
|
21352
|
+
});
|
|
21353
|
+
};
|
|
20946
21354
|
/**
|
|
20947
21355
|
* Stops the strategy from generating new signals.
|
|
20948
21356
|
*
|
|
@@ -20966,6 +21374,7 @@ class LiveUtils {
|
|
|
20966
21374
|
context,
|
|
20967
21375
|
});
|
|
20968
21376
|
bt.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_STOP);
|
|
21377
|
+
bt.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_STOP);
|
|
20969
21378
|
{
|
|
20970
21379
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
20971
21380
|
riskName && bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_STOP);
|
|
@@ -21007,6 +21416,7 @@ class LiveUtils {
|
|
|
21007
21416
|
cancelId,
|
|
21008
21417
|
});
|
|
21009
21418
|
bt.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_CANCEL);
|
|
21419
|
+
bt.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_CANCEL);
|
|
21010
21420
|
{
|
|
21011
21421
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
21012
21422
|
riskName && bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_CANCEL);
|
|
@@ -21051,6 +21461,7 @@ class LiveUtils {
|
|
|
21051
21461
|
context,
|
|
21052
21462
|
});
|
|
21053
21463
|
bt.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_PARTIAL_PROFIT);
|
|
21464
|
+
bt.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_PARTIAL_PROFIT);
|
|
21054
21465
|
{
|
|
21055
21466
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
21056
21467
|
riskName && bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_PARTIAL_PROFIT);
|
|
@@ -21095,6 +21506,7 @@ class LiveUtils {
|
|
|
21095
21506
|
context,
|
|
21096
21507
|
});
|
|
21097
21508
|
bt.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_PARTIAL_LOSS);
|
|
21509
|
+
bt.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_PARTIAL_LOSS);
|
|
21098
21510
|
{
|
|
21099
21511
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
21100
21512
|
riskName && bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_PARTIAL_LOSS);
|
|
@@ -21114,32 +21526,35 @@ class LiveUtils {
|
|
|
21114
21526
|
*
|
|
21115
21527
|
* @param symbol - Trading pair symbol
|
|
21116
21528
|
* @param percentShift - Percentage adjustment to SL distance (-100 to 100)
|
|
21529
|
+
* @param currentPrice - Current market price to check for intrusion
|
|
21117
21530
|
* @param context - Execution context with strategyName and exchangeName
|
|
21118
21531
|
* @returns Promise that resolves when trailing SL is updated
|
|
21119
21532
|
*
|
|
21120
21533
|
* @example
|
|
21121
21534
|
* ```typescript
|
|
21122
|
-
* // LONG: entry=100, originalSL=90, distance=10
|
|
21123
|
-
* // Tighten stop by 50%: newSL = 100 -
|
|
21124
|
-
* await Live.trailingStop("BTCUSDT", -50, {
|
|
21535
|
+
* // LONG: entry=100, originalSL=90, distance=10%, currentPrice=102
|
|
21536
|
+
* // Tighten stop by 50%: newSL = 100 - 5% = 95
|
|
21537
|
+
* await Live.trailingStop("BTCUSDT", -50, 102, {
|
|
21125
21538
|
* exchangeName: "binance",
|
|
21126
21539
|
* strategyName: "my-strategy"
|
|
21127
21540
|
* });
|
|
21128
21541
|
* ```
|
|
21129
21542
|
*/
|
|
21130
|
-
this.trailingStop = async (symbol, percentShift, context) => {
|
|
21543
|
+
this.trailingStop = async (symbol, percentShift, currentPrice, context) => {
|
|
21131
21544
|
bt.loggerService.info(LIVE_METHOD_NAME_TRAILING_STOP, {
|
|
21132
21545
|
symbol,
|
|
21133
21546
|
percentShift,
|
|
21547
|
+
currentPrice,
|
|
21134
21548
|
context,
|
|
21135
21549
|
});
|
|
21136
21550
|
bt.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_TRAILING_STOP);
|
|
21551
|
+
bt.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_TRAILING_STOP);
|
|
21137
21552
|
{
|
|
21138
21553
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
21139
21554
|
riskName && bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_TRAILING_STOP);
|
|
21140
21555
|
riskList && riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_TRAILING_STOP));
|
|
21141
21556
|
}
|
|
21142
|
-
await bt.strategyCoreService.trailingStop(false, symbol, percentShift, {
|
|
21557
|
+
await bt.strategyCoreService.trailingStop(false, symbol, percentShift, currentPrice, {
|
|
21143
21558
|
strategyName: context.strategyName,
|
|
21144
21559
|
exchangeName: context.exchangeName,
|
|
21145
21560
|
frameName: "",
|
|
@@ -21173,6 +21588,7 @@ class LiveUtils {
|
|
|
21173
21588
|
context,
|
|
21174
21589
|
});
|
|
21175
21590
|
bt.strategyValidationService.validate(context.strategyName, "Live.breakeven");
|
|
21591
|
+
bt.exchangeValidationService.validate(context.exchangeName, "Live.breakeven");
|
|
21176
21592
|
{
|
|
21177
21593
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
21178
21594
|
riskName && bt.riskValidationService.validate(riskName, "Live.breakeven");
|
|
@@ -21208,6 +21624,7 @@ class LiveUtils {
|
|
|
21208
21624
|
context,
|
|
21209
21625
|
});
|
|
21210
21626
|
bt.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_GET_DATA);
|
|
21627
|
+
bt.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_GET_DATA);
|
|
21211
21628
|
{
|
|
21212
21629
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
21213
21630
|
riskName && bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_DATA);
|
|
@@ -21240,6 +21657,7 @@ class LiveUtils {
|
|
|
21240
21657
|
context,
|
|
21241
21658
|
});
|
|
21242
21659
|
bt.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_GET_REPORT);
|
|
21660
|
+
bt.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_GET_REPORT);
|
|
21243
21661
|
{
|
|
21244
21662
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
21245
21663
|
riskName && bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_REPORT);
|
|
@@ -21280,6 +21698,7 @@ class LiveUtils {
|
|
|
21280
21698
|
path,
|
|
21281
21699
|
});
|
|
21282
21700
|
bt.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_DUMP);
|
|
21701
|
+
bt.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_DUMP);
|
|
21283
21702
|
{
|
|
21284
21703
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
21285
21704
|
riskName && bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_DUMP);
|
|
@@ -21373,6 +21792,7 @@ class ScheduleUtils {
|
|
|
21373
21792
|
backtest,
|
|
21374
21793
|
});
|
|
21375
21794
|
bt.strategyValidationService.validate(context.strategyName, SCHEDULE_METHOD_NAME_GET_DATA);
|
|
21795
|
+
bt.exchangeValidationService.validate(context.exchangeName, SCHEDULE_METHOD_NAME_GET_DATA);
|
|
21376
21796
|
{
|
|
21377
21797
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
21378
21798
|
riskName && bt.riskValidationService.validate(riskName, SCHEDULE_METHOD_NAME_GET_DATA);
|
|
@@ -21401,6 +21821,7 @@ class ScheduleUtils {
|
|
|
21401
21821
|
backtest,
|
|
21402
21822
|
});
|
|
21403
21823
|
bt.strategyValidationService.validate(context.strategyName, SCHEDULE_METHOD_NAME_GET_REPORT);
|
|
21824
|
+
bt.exchangeValidationService.validate(context.exchangeName, SCHEDULE_METHOD_NAME_GET_REPORT);
|
|
21404
21825
|
{
|
|
21405
21826
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
21406
21827
|
riskName && bt.riskValidationService.validate(riskName, SCHEDULE_METHOD_NAME_GET_REPORT);
|
|
@@ -21433,6 +21854,7 @@ class ScheduleUtils {
|
|
|
21433
21854
|
path,
|
|
21434
21855
|
});
|
|
21435
21856
|
bt.strategyValidationService.validate(context.strategyName, SCHEDULE_METHOD_NAME_DUMP);
|
|
21857
|
+
bt.exchangeValidationService.validate(context.exchangeName, SCHEDULE_METHOD_NAME_DUMP);
|
|
21436
21858
|
{
|
|
21437
21859
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
21438
21860
|
riskName && bt.riskValidationService.validate(riskName, SCHEDULE_METHOD_NAME_DUMP);
|
|
@@ -21521,6 +21943,7 @@ class Performance {
|
|
|
21521
21943
|
*/
|
|
21522
21944
|
static async getData(symbol, context, backtest = false) {
|
|
21523
21945
|
bt.strategyValidationService.validate(context.strategyName, PERFORMANCE_METHOD_NAME_GET_DATA);
|
|
21946
|
+
bt.exchangeValidationService.validate(context.exchangeName, PERFORMANCE_METHOD_NAME_GET_DATA);
|
|
21524
21947
|
{
|
|
21525
21948
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
21526
21949
|
riskName && bt.riskValidationService.validate(riskName, PERFORMANCE_METHOD_NAME_GET_DATA);
|
|
@@ -21553,6 +21976,7 @@ class Performance {
|
|
|
21553
21976
|
*/
|
|
21554
21977
|
static async getReport(symbol, context, backtest = false, columns) {
|
|
21555
21978
|
bt.strategyValidationService.validate(context.strategyName, PERFORMANCE_METHOD_NAME_GET_REPORT);
|
|
21979
|
+
bt.exchangeValidationService.validate(context.exchangeName, PERFORMANCE_METHOD_NAME_GET_REPORT);
|
|
21556
21980
|
{
|
|
21557
21981
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
21558
21982
|
riskName && bt.riskValidationService.validate(riskName, PERFORMANCE_METHOD_NAME_GET_REPORT);
|
|
@@ -21582,6 +22006,7 @@ class Performance {
|
|
|
21582
22006
|
*/
|
|
21583
22007
|
static async dump(symbol, context, backtest = false, path = "./dump/performance", columns) {
|
|
21584
22008
|
bt.strategyValidationService.validate(context.strategyName, PERFORMANCE_METHOD_NAME_DUMP);
|
|
22009
|
+
bt.exchangeValidationService.validate(context.exchangeName, PERFORMANCE_METHOD_NAME_DUMP);
|
|
21585
22010
|
{
|
|
21586
22011
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
21587
22012
|
riskName && bt.riskValidationService.validate(riskName, PERFORMANCE_METHOD_NAME_DUMP);
|
|
@@ -22172,6 +22597,7 @@ class HeatUtils {
|
|
|
22172
22597
|
this.getData = async (context, backtest = false) => {
|
|
22173
22598
|
bt.loggerService.info(HEAT_METHOD_NAME_GET_DATA, { strategyName: context.strategyName });
|
|
22174
22599
|
bt.strategyValidationService.validate(context.strategyName, HEAT_METHOD_NAME_GET_DATA);
|
|
22600
|
+
bt.exchangeValidationService.validate(context.exchangeName, HEAT_METHOD_NAME_GET_DATA);
|
|
22175
22601
|
{
|
|
22176
22602
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
22177
22603
|
riskName && bt.riskValidationService.validate(riskName, HEAT_METHOD_NAME_GET_DATA);
|
|
@@ -22213,6 +22639,7 @@ class HeatUtils {
|
|
|
22213
22639
|
this.getReport = async (context, backtest = false, columns) => {
|
|
22214
22640
|
bt.loggerService.info(HEAT_METHOD_NAME_GET_REPORT, { strategyName: context.strategyName });
|
|
22215
22641
|
bt.strategyValidationService.validate(context.strategyName, HEAT_METHOD_NAME_GET_REPORT);
|
|
22642
|
+
bt.exchangeValidationService.validate(context.exchangeName, HEAT_METHOD_NAME_GET_REPORT);
|
|
22216
22643
|
{
|
|
22217
22644
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
22218
22645
|
riskName && bt.riskValidationService.validate(riskName, HEAT_METHOD_NAME_GET_REPORT);
|
|
@@ -22251,6 +22678,7 @@ class HeatUtils {
|
|
|
22251
22678
|
this.dump = async (context, backtest = false, path, columns) => {
|
|
22252
22679
|
bt.loggerService.info(HEAT_METHOD_NAME_DUMP, { strategyName: context.strategyName, path });
|
|
22253
22680
|
bt.strategyValidationService.validate(context.strategyName, HEAT_METHOD_NAME_DUMP);
|
|
22681
|
+
bt.exchangeValidationService.validate(context.exchangeName, HEAT_METHOD_NAME_DUMP);
|
|
22254
22682
|
{
|
|
22255
22683
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
22256
22684
|
riskName && bt.riskValidationService.validate(riskName, HEAT_METHOD_NAME_DUMP);
|
|
@@ -22583,6 +23011,7 @@ class PartialUtils {
|
|
|
22583
23011
|
this.getData = async (symbol, context, backtest = false) => {
|
|
22584
23012
|
bt.loggerService.info(PARTIAL_METHOD_NAME_GET_DATA, { symbol, strategyName: context.strategyName });
|
|
22585
23013
|
bt.strategyValidationService.validate(context.strategyName, PARTIAL_METHOD_NAME_GET_DATA);
|
|
23014
|
+
bt.exchangeValidationService.validate(context.exchangeName, PARTIAL_METHOD_NAME_GET_DATA);
|
|
22586
23015
|
{
|
|
22587
23016
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
22588
23017
|
riskName && bt.riskValidationService.validate(riskName, PARTIAL_METHOD_NAME_GET_DATA);
|
|
@@ -22632,6 +23061,7 @@ class PartialUtils {
|
|
|
22632
23061
|
this.getReport = async (symbol, context, backtest = false, columns) => {
|
|
22633
23062
|
bt.loggerService.info(PARTIAL_METHOD_NAME_GET_REPORT, { symbol, strategyName: context.strategyName });
|
|
22634
23063
|
bt.strategyValidationService.validate(context.strategyName, PARTIAL_METHOD_NAME_GET_REPORT);
|
|
23064
|
+
bt.exchangeValidationService.validate(context.exchangeName, PARTIAL_METHOD_NAME_GET_REPORT);
|
|
22635
23065
|
{
|
|
22636
23066
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
22637
23067
|
riskName && bt.riskValidationService.validate(riskName, PARTIAL_METHOD_NAME_GET_REPORT);
|
|
@@ -22674,6 +23104,7 @@ class PartialUtils {
|
|
|
22674
23104
|
this.dump = async (symbol, context, backtest = false, path, columns) => {
|
|
22675
23105
|
bt.loggerService.info(PARTIAL_METHOD_NAME_DUMP, { symbol, strategyName: context.strategyName, path });
|
|
22676
23106
|
bt.strategyValidationService.validate(context.strategyName, PARTIAL_METHOD_NAME_DUMP);
|
|
23107
|
+
bt.exchangeValidationService.validate(context.exchangeName, PARTIAL_METHOD_NAME_DUMP);
|
|
22677
23108
|
{
|
|
22678
23109
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
22679
23110
|
riskName && bt.riskValidationService.validate(riskName, PARTIAL_METHOD_NAME_DUMP);
|
|
@@ -23863,6 +24294,7 @@ class BreakevenUtils {
|
|
|
23863
24294
|
this.getData = async (symbol, context, backtest = false) => {
|
|
23864
24295
|
bt.loggerService.info(BREAKEVEN_METHOD_NAME_GET_DATA, { symbol, strategyName: context.strategyName });
|
|
23865
24296
|
bt.strategyValidationService.validate(context.strategyName, BREAKEVEN_METHOD_NAME_GET_DATA);
|
|
24297
|
+
bt.exchangeValidationService.validate(context.exchangeName, BREAKEVEN_METHOD_NAME_GET_DATA);
|
|
23866
24298
|
{
|
|
23867
24299
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
23868
24300
|
riskName && bt.riskValidationService.validate(riskName, BREAKEVEN_METHOD_NAME_GET_DATA);
|
|
@@ -23908,6 +24340,7 @@ class BreakevenUtils {
|
|
|
23908
24340
|
this.getReport = async (symbol, context, backtest = false, columns) => {
|
|
23909
24341
|
bt.loggerService.info(BREAKEVEN_METHOD_NAME_GET_REPORT, { symbol, strategyName: context.strategyName });
|
|
23910
24342
|
bt.strategyValidationService.validate(context.strategyName, BREAKEVEN_METHOD_NAME_GET_REPORT);
|
|
24343
|
+
bt.exchangeValidationService.validate(context.exchangeName, BREAKEVEN_METHOD_NAME_GET_REPORT);
|
|
23911
24344
|
{
|
|
23912
24345
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
23913
24346
|
riskName && bt.riskValidationService.validate(riskName, BREAKEVEN_METHOD_NAME_GET_REPORT);
|
|
@@ -23950,6 +24383,7 @@ class BreakevenUtils {
|
|
|
23950
24383
|
this.dump = async (symbol, context, backtest = false, path, columns) => {
|
|
23951
24384
|
bt.loggerService.info(BREAKEVEN_METHOD_NAME_DUMP, { symbol, strategyName: context.strategyName, path });
|
|
23952
24385
|
bt.strategyValidationService.validate(context.strategyName, BREAKEVEN_METHOD_NAME_DUMP);
|
|
24386
|
+
bt.exchangeValidationService.validate(context.exchangeName, BREAKEVEN_METHOD_NAME_DUMP);
|
|
23953
24387
|
{
|
|
23954
24388
|
const { riskName, riskList } = bt.strategySchemaService.get(context.strategyName);
|
|
23955
24389
|
riskName && bt.riskValidationService.validate(riskName, BREAKEVEN_METHOD_NAME_DUMP);
|