backtest-kit 3.2.0 → 3.3.1
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/README.md +1 -1
- package/build/index.cjs +786 -89
- package/build/index.mjs +786 -90
- package/package.json +1 -1
- package/types.d.ts +391 -17
package/build/index.mjs
CHANGED
|
@@ -2880,6 +2880,27 @@ class ExchangeConnectionService {
|
|
|
2880
2880
|
}
|
|
2881
2881
|
}
|
|
2882
2882
|
|
|
2883
|
+
/**
|
|
2884
|
+
* Returns the effective entry price for price calculations.
|
|
2885
|
+
*
|
|
2886
|
+
* When the _entry array exists and has at least one element, returns
|
|
2887
|
+
* the simple arithmetic mean of all entry prices (DCA average).
|
|
2888
|
+
* Otherwise returns the original signal.priceOpen.
|
|
2889
|
+
*
|
|
2890
|
+
* This mirrors the _trailingPriceStopLoss pattern: original price is preserved
|
|
2891
|
+
* in signal.priceOpen (for identity/tracking), while calculations use the
|
|
2892
|
+
* effective averaged price returned by this function.
|
|
2893
|
+
*
|
|
2894
|
+
* @param signal - Signal row (ISignalRow or IScheduledSignalRow)
|
|
2895
|
+
* @returns Effective entry price for distance and PNL calculations
|
|
2896
|
+
*/
|
|
2897
|
+
const getEffectivePriceOpen = (signal) => {
|
|
2898
|
+
if (signal._entry && signal._entry.length > 0) {
|
|
2899
|
+
return signal._entry.reduce((sum, e) => sum + e.price, 0) / signal._entry.length;
|
|
2900
|
+
}
|
|
2901
|
+
return signal.priceOpen;
|
|
2902
|
+
};
|
|
2903
|
+
|
|
2883
2904
|
/**
|
|
2884
2905
|
* Calculates profit/loss for a closed signal with slippage and fees.
|
|
2885
2906
|
*
|
|
@@ -2930,7 +2951,7 @@ class ExchangeConnectionService {
|
|
|
2930
2951
|
* ```
|
|
2931
2952
|
*/
|
|
2932
2953
|
const toProfitLossDto = (signal, priceClose) => {
|
|
2933
|
-
const priceOpen = signal
|
|
2954
|
+
const priceOpen = getEffectivePriceOpen(signal);
|
|
2934
2955
|
// Calculate weighted PNL with partial closes
|
|
2935
2956
|
if (signal._partial && signal._partial.length > 0) {
|
|
2936
2957
|
let totalWeightedPnl = 0;
|
|
@@ -3184,6 +3205,7 @@ const PROCESS_COMMIT_QUEUE_FN = async (self, timestamp) => {
|
|
|
3184
3205
|
percentToClose: commit.percentToClose,
|
|
3185
3206
|
currentPrice: commit.currentPrice,
|
|
3186
3207
|
timestamp,
|
|
3208
|
+
totalEntries: publicSignal.totalEntries,
|
|
3187
3209
|
position: publicSignal.position,
|
|
3188
3210
|
priceOpen: publicSignal.priceOpen,
|
|
3189
3211
|
signalId: publicSignal.id,
|
|
@@ -3191,6 +3213,7 @@ const PROCESS_COMMIT_QUEUE_FN = async (self, timestamp) => {
|
|
|
3191
3213
|
priceStopLoss: publicSignal.priceStopLoss,
|
|
3192
3214
|
originalPriceTakeProfit: publicSignal.originalPriceTakeProfit,
|
|
3193
3215
|
originalPriceStopLoss: publicSignal.originalPriceStopLoss,
|
|
3216
|
+
originalPriceOpen: publicSignal.originalPriceOpen,
|
|
3194
3217
|
scheduledAt: publicSignal.scheduledAt,
|
|
3195
3218
|
pendingAt: publicSignal.pendingAt,
|
|
3196
3219
|
});
|
|
@@ -3207,6 +3230,7 @@ const PROCESS_COMMIT_QUEUE_FN = async (self, timestamp) => {
|
|
|
3207
3230
|
percentToClose: commit.percentToClose,
|
|
3208
3231
|
currentPrice: commit.currentPrice,
|
|
3209
3232
|
timestamp,
|
|
3233
|
+
totalEntries: publicSignal.totalEntries,
|
|
3210
3234
|
position: publicSignal.position,
|
|
3211
3235
|
priceOpen: publicSignal.priceOpen,
|
|
3212
3236
|
signalId: publicSignal.id,
|
|
@@ -3214,6 +3238,7 @@ const PROCESS_COMMIT_QUEUE_FN = async (self, timestamp) => {
|
|
|
3214
3238
|
priceStopLoss: publicSignal.priceStopLoss,
|
|
3215
3239
|
originalPriceTakeProfit: publicSignal.originalPriceTakeProfit,
|
|
3216
3240
|
originalPriceStopLoss: publicSignal.originalPriceStopLoss,
|
|
3241
|
+
originalPriceOpen: publicSignal.originalPriceOpen,
|
|
3217
3242
|
scheduledAt: publicSignal.scheduledAt,
|
|
3218
3243
|
pendingAt: publicSignal.pendingAt,
|
|
3219
3244
|
});
|
|
@@ -3229,6 +3254,7 @@ const PROCESS_COMMIT_QUEUE_FN = async (self, timestamp) => {
|
|
|
3229
3254
|
backtest: commit.backtest,
|
|
3230
3255
|
currentPrice: commit.currentPrice,
|
|
3231
3256
|
timestamp,
|
|
3257
|
+
totalEntries: publicSignal.totalEntries,
|
|
3232
3258
|
signalId: publicSignal.id,
|
|
3233
3259
|
position: publicSignal.position,
|
|
3234
3260
|
priceOpen: publicSignal.priceOpen,
|
|
@@ -3236,6 +3262,7 @@ const PROCESS_COMMIT_QUEUE_FN = async (self, timestamp) => {
|
|
|
3236
3262
|
priceStopLoss: publicSignal.priceStopLoss,
|
|
3237
3263
|
originalPriceTakeProfit: publicSignal.originalPriceTakeProfit,
|
|
3238
3264
|
originalPriceStopLoss: publicSignal.originalPriceStopLoss,
|
|
3265
|
+
originalPriceOpen: publicSignal.originalPriceOpen,
|
|
3239
3266
|
scheduledAt: publicSignal.scheduledAt,
|
|
3240
3267
|
pendingAt: publicSignal.pendingAt,
|
|
3241
3268
|
});
|
|
@@ -3252,6 +3279,7 @@ const PROCESS_COMMIT_QUEUE_FN = async (self, timestamp) => {
|
|
|
3252
3279
|
percentShift: commit.percentShift,
|
|
3253
3280
|
currentPrice: commit.currentPrice,
|
|
3254
3281
|
timestamp,
|
|
3282
|
+
totalEntries: publicSignal.totalEntries,
|
|
3255
3283
|
signalId: publicSignal.id,
|
|
3256
3284
|
position: publicSignal.position,
|
|
3257
3285
|
priceOpen: publicSignal.priceOpen,
|
|
@@ -3259,6 +3287,7 @@ const PROCESS_COMMIT_QUEUE_FN = async (self, timestamp) => {
|
|
|
3259
3287
|
priceStopLoss: publicSignal.priceStopLoss,
|
|
3260
3288
|
originalPriceTakeProfit: publicSignal.originalPriceTakeProfit,
|
|
3261
3289
|
originalPriceStopLoss: publicSignal.originalPriceStopLoss,
|
|
3290
|
+
originalPriceOpen: publicSignal.originalPriceOpen,
|
|
3262
3291
|
scheduledAt: publicSignal.scheduledAt,
|
|
3263
3292
|
pendingAt: publicSignal.pendingAt,
|
|
3264
3293
|
});
|
|
@@ -3275,6 +3304,7 @@ const PROCESS_COMMIT_QUEUE_FN = async (self, timestamp) => {
|
|
|
3275
3304
|
percentShift: commit.percentShift,
|
|
3276
3305
|
currentPrice: commit.currentPrice,
|
|
3277
3306
|
timestamp,
|
|
3307
|
+
totalEntries: publicSignal.totalEntries,
|
|
3278
3308
|
signalId: publicSignal.id,
|
|
3279
3309
|
position: publicSignal.position,
|
|
3280
3310
|
priceOpen: publicSignal.priceOpen,
|
|
@@ -3282,6 +3312,33 @@ const PROCESS_COMMIT_QUEUE_FN = async (self, timestamp) => {
|
|
|
3282
3312
|
priceStopLoss: publicSignal.priceStopLoss,
|
|
3283
3313
|
originalPriceTakeProfit: publicSignal.originalPriceTakeProfit,
|
|
3284
3314
|
originalPriceStopLoss: publicSignal.originalPriceStopLoss,
|
|
3315
|
+
originalPriceOpen: publicSignal.originalPriceOpen,
|
|
3316
|
+
scheduledAt: publicSignal.scheduledAt,
|
|
3317
|
+
pendingAt: publicSignal.pendingAt,
|
|
3318
|
+
});
|
|
3319
|
+
continue;
|
|
3320
|
+
}
|
|
3321
|
+
if (commit.action === "average-buy") {
|
|
3322
|
+
const effectivePriceOpen = getEffectivePriceOpen(self._pendingSignal);
|
|
3323
|
+
await CALL_COMMIT_FN(self, {
|
|
3324
|
+
action: "average-buy",
|
|
3325
|
+
symbol: commit.symbol,
|
|
3326
|
+
strategyName: self.params.strategyName,
|
|
3327
|
+
exchangeName: self.params.exchangeName,
|
|
3328
|
+
frameName: self.params.frameName,
|
|
3329
|
+
backtest: commit.backtest,
|
|
3330
|
+
currentPrice: commit.currentPrice,
|
|
3331
|
+
effectivePriceOpen,
|
|
3332
|
+
timestamp,
|
|
3333
|
+
totalEntries: publicSignal.totalEntries,
|
|
3334
|
+
signalId: publicSignal.id,
|
|
3335
|
+
position: publicSignal.position,
|
|
3336
|
+
priceOpen: publicSignal.originalPriceOpen,
|
|
3337
|
+
priceTakeProfit: publicSignal.priceTakeProfit,
|
|
3338
|
+
priceStopLoss: publicSignal.priceStopLoss,
|
|
3339
|
+
originalPriceTakeProfit: publicSignal.originalPriceTakeProfit,
|
|
3340
|
+
originalPriceStopLoss: publicSignal.originalPriceStopLoss,
|
|
3341
|
+
originalPriceOpen: publicSignal.originalPriceOpen,
|
|
3285
3342
|
scheduledAt: publicSignal.scheduledAt,
|
|
3286
3343
|
pendingAt: publicSignal.pendingAt,
|
|
3287
3344
|
});
|
|
@@ -3342,13 +3399,20 @@ const TO_PUBLIC_SIGNAL = (signal) => {
|
|
|
3342
3399
|
const partialExecuted = ("_partial" in signal && Array.isArray(signal._partial))
|
|
3343
3400
|
? signal._partial.reduce((sum, partial) => sum + partial.percent, 0)
|
|
3344
3401
|
: 0;
|
|
3402
|
+
const totalEntries = ("_entry" in signal && Array.isArray(signal._entry))
|
|
3403
|
+
? signal._entry.length
|
|
3404
|
+
: 1;
|
|
3405
|
+
const effectivePriceOpen = "_entry" in signal ? getEffectivePriceOpen(signal) : signal.priceOpen;
|
|
3345
3406
|
return {
|
|
3346
3407
|
...structuredClone(signal),
|
|
3408
|
+
priceOpen: effectivePriceOpen,
|
|
3347
3409
|
priceStopLoss: hasTrailingSL ? signal._trailingPriceStopLoss : signal.priceStopLoss,
|
|
3348
3410
|
priceTakeProfit: hasTrailingTP ? signal._trailingPriceTakeProfit : signal.priceTakeProfit,
|
|
3411
|
+
originalPriceOpen: signal.priceOpen,
|
|
3349
3412
|
originalPriceStopLoss: signal.priceStopLoss,
|
|
3350
3413
|
originalPriceTakeProfit: signal.priceTakeProfit,
|
|
3351
3414
|
partialExecuted,
|
|
3415
|
+
totalEntries,
|
|
3352
3416
|
};
|
|
3353
3417
|
};
|
|
3354
3418
|
const VALIDATE_SIGNAL_FN = (signal, currentPrice, isScheduled) => {
|
|
@@ -3678,6 +3742,7 @@ const GET_SIGNAL_FN = trycatch(async (self) => {
|
|
|
3678
3742
|
scheduledAt: currentTime,
|
|
3679
3743
|
pendingAt: currentTime, // Для immediate signal оба времени одинаковые
|
|
3680
3744
|
_isScheduled: false,
|
|
3745
|
+
_entry: [{ price: signal.priceOpen }],
|
|
3681
3746
|
};
|
|
3682
3747
|
// Валидируем сигнал перед возвратом
|
|
3683
3748
|
VALIDATE_SIGNAL_FN(signalRow, currentPrice, false);
|
|
@@ -3699,6 +3764,7 @@ const GET_SIGNAL_FN = trycatch(async (self) => {
|
|
|
3699
3764
|
scheduledAt: currentTime,
|
|
3700
3765
|
pendingAt: SCHEDULED_SIGNAL_PENDING_MOCK, // Временно, обновится при активации
|
|
3701
3766
|
_isScheduled: true,
|
|
3767
|
+
_entry: [{ price: signal.priceOpen }],
|
|
3702
3768
|
};
|
|
3703
3769
|
// Валидируем сигнал перед возвратом
|
|
3704
3770
|
VALIDATE_SIGNAL_FN(scheduledSignalRow, currentPrice, true);
|
|
@@ -3716,6 +3782,7 @@ const GET_SIGNAL_FN = trycatch(async (self) => {
|
|
|
3716
3782
|
scheduledAt: currentTime,
|
|
3717
3783
|
pendingAt: currentTime, // Для immediate signal оба времени одинаковые
|
|
3718
3784
|
_isScheduled: false,
|
|
3785
|
+
_entry: [{ price: currentPrice }],
|
|
3719
3786
|
};
|
|
3720
3787
|
// Валидируем сигнал перед возвратом
|
|
3721
3788
|
VALIDATE_SIGNAL_FN(signalRow, currentPrice, false);
|
|
@@ -3862,9 +3929,10 @@ const PARTIAL_LOSS_FN = (self, signal, percentToClose, currentPrice) => {
|
|
|
3862
3929
|
return true;
|
|
3863
3930
|
};
|
|
3864
3931
|
const TRAILING_STOP_LOSS_FN = (self, signal, percentShift) => {
|
|
3932
|
+
const effectivePriceOpen = getEffectivePriceOpen(signal);
|
|
3865
3933
|
// CRITICAL: Always calculate from ORIGINAL SL, not from current trailing SL
|
|
3866
3934
|
// This prevents error accumulation on repeated calls
|
|
3867
|
-
const originalSlDistancePercent = Math.abs((
|
|
3935
|
+
const originalSlDistancePercent = Math.abs((effectivePriceOpen - signal.priceStopLoss) / effectivePriceOpen * 100);
|
|
3868
3936
|
// Calculate new stop-loss distance percentage by adding shift to ORIGINAL distance
|
|
3869
3937
|
// Negative percentShift: reduces distance % (tightens stop, moves SL toward entry or beyond)
|
|
3870
3938
|
// Positive percentShift: increases distance % (loosens stop, moves SL away from entry)
|
|
@@ -3876,13 +3944,13 @@ const TRAILING_STOP_LOSS_FN = (self, signal, percentShift) => {
|
|
|
3876
3944
|
// LONG: SL is below entry (or above entry if in profit zone)
|
|
3877
3945
|
// Formula: entry * (1 - newDistance%)
|
|
3878
3946
|
// Example: entry=100, originalSL=90 (10%), shift=-5% → newDistance=5% → 100 * 0.95 = 95 (tighter)
|
|
3879
|
-
newStopLoss =
|
|
3947
|
+
newStopLoss = effectivePriceOpen * (1 - newSlDistancePercent / 100);
|
|
3880
3948
|
}
|
|
3881
3949
|
else {
|
|
3882
3950
|
// SHORT: SL is above entry (or below entry if in profit zone)
|
|
3883
3951
|
// Formula: entry * (1 + newDistance%)
|
|
3884
3952
|
// Example: entry=100, originalSL=110 (10%), shift=-5% → newDistance=5% → 100 * 1.05 = 105 (tighter)
|
|
3885
|
-
newStopLoss =
|
|
3953
|
+
newStopLoss = effectivePriceOpen * (1 + newSlDistancePercent / 100);
|
|
3886
3954
|
}
|
|
3887
3955
|
const currentTrailingSL = signal._trailingPriceStopLoss;
|
|
3888
3956
|
const isFirstCall = currentTrailingSL === undefined;
|
|
@@ -3945,9 +4013,10 @@ const TRAILING_STOP_LOSS_FN = (self, signal, percentShift) => {
|
|
|
3945
4013
|
}
|
|
3946
4014
|
};
|
|
3947
4015
|
const TRAILING_TAKE_PROFIT_FN = (self, signal, percentShift) => {
|
|
4016
|
+
const effectivePriceOpen = getEffectivePriceOpen(signal);
|
|
3948
4017
|
// CRITICAL: Always calculate from ORIGINAL TP, not from current trailing TP
|
|
3949
4018
|
// This prevents error accumulation on repeated calls
|
|
3950
|
-
const originalTpDistancePercent = Math.abs((signal.priceTakeProfit -
|
|
4019
|
+
const originalTpDistancePercent = Math.abs((signal.priceTakeProfit - effectivePriceOpen) / effectivePriceOpen * 100);
|
|
3951
4020
|
// Calculate new take-profit distance percentage by adding shift to ORIGINAL distance
|
|
3952
4021
|
// Negative percentShift: reduces distance % (brings TP closer to entry)
|
|
3953
4022
|
// Positive percentShift: increases distance % (moves TP further from entry)
|
|
@@ -3958,13 +4027,13 @@ const TRAILING_TAKE_PROFIT_FN = (self, signal, percentShift) => {
|
|
|
3958
4027
|
// LONG: TP is above entry
|
|
3959
4028
|
// Formula: entry * (1 + newDistance%)
|
|
3960
4029
|
// Example: entry=100, originalTP=110 (10%), shift=-3% → newDistance=7% → 100 * 1.07 = 107 (closer)
|
|
3961
|
-
newTakeProfit =
|
|
4030
|
+
newTakeProfit = effectivePriceOpen * (1 + newTpDistancePercent / 100);
|
|
3962
4031
|
}
|
|
3963
4032
|
else {
|
|
3964
4033
|
// SHORT: TP is below entry
|
|
3965
4034
|
// Formula: entry * (1 - newDistance%)
|
|
3966
4035
|
// Example: entry=100, originalTP=90 (10%), shift=-3% → newDistance=7% → 100 * 0.93 = 93 (closer)
|
|
3967
|
-
newTakeProfit =
|
|
4036
|
+
newTakeProfit = effectivePriceOpen * (1 - newTpDistancePercent / 100);
|
|
3968
4037
|
}
|
|
3969
4038
|
const currentTrailingTP = signal._trailingPriceTakeProfit;
|
|
3970
4039
|
const isFirstCall = currentTrailingTP === undefined;
|
|
@@ -4025,6 +4094,7 @@ const TRAILING_TAKE_PROFIT_FN = (self, signal, percentShift) => {
|
|
|
4025
4094
|
}
|
|
4026
4095
|
};
|
|
4027
4096
|
const BREAKEVEN_FN = (self, signal, currentPrice) => {
|
|
4097
|
+
const effectivePriceOpen = getEffectivePriceOpen(signal);
|
|
4028
4098
|
// Calculate breakeven threshold based on slippage and fees
|
|
4029
4099
|
// Need to cover: entry slippage + entry fee + exit slippage + exit fee
|
|
4030
4100
|
// Total: (slippage + fee) * 2 transactions
|
|
@@ -4032,10 +4102,10 @@ const BREAKEVEN_FN = (self, signal, currentPrice) => {
|
|
|
4032
4102
|
// Check if trailing stop is already set
|
|
4033
4103
|
if (signal._trailingPriceStopLoss !== undefined) {
|
|
4034
4104
|
const trailingStopLoss = signal._trailingPriceStopLoss;
|
|
4035
|
-
const breakevenPrice =
|
|
4105
|
+
const breakevenPrice = effectivePriceOpen;
|
|
4036
4106
|
if (signal.position === "long") {
|
|
4037
4107
|
// LONG: trailing SL is positive if it's above entry (in profit zone)
|
|
4038
|
-
const isPositiveTrailing = trailingStopLoss >
|
|
4108
|
+
const isPositiveTrailing = trailingStopLoss > effectivePriceOpen;
|
|
4039
4109
|
if (isPositiveTrailing) {
|
|
4040
4110
|
// Trailing stop is already protecting profit - consider breakeven achieved
|
|
4041
4111
|
self.params.logger.debug("BREAKEVEN_FN: positive trailing stop already set, returning true", {
|
|
@@ -4051,7 +4121,7 @@ const BREAKEVEN_FN = (self, signal, currentPrice) => {
|
|
|
4051
4121
|
else {
|
|
4052
4122
|
// Trailing stop is negative (below entry)
|
|
4053
4123
|
// Check if we can upgrade it to breakeven
|
|
4054
|
-
const thresholdPrice =
|
|
4124
|
+
const thresholdPrice = effectivePriceOpen * (1 + breakevenThresholdPercent / 100);
|
|
4055
4125
|
const isThresholdReached = currentPrice >= thresholdPrice;
|
|
4056
4126
|
if (isThresholdReached && breakevenPrice > trailingStopLoss) {
|
|
4057
4127
|
// Check for price intrusion before setting new SL
|
|
@@ -4100,7 +4170,7 @@ const BREAKEVEN_FN = (self, signal, currentPrice) => {
|
|
|
4100
4170
|
}
|
|
4101
4171
|
else {
|
|
4102
4172
|
// SHORT: trailing SL is positive if it's below entry (in profit zone)
|
|
4103
|
-
const isPositiveTrailing = trailingStopLoss <
|
|
4173
|
+
const isPositiveTrailing = trailingStopLoss < effectivePriceOpen;
|
|
4104
4174
|
if (isPositiveTrailing) {
|
|
4105
4175
|
// Trailing stop is already protecting profit - consider breakeven achieved
|
|
4106
4176
|
self.params.logger.debug("BREAKEVEN_FN: positive trailing stop already set, returning true", {
|
|
@@ -4116,7 +4186,7 @@ const BREAKEVEN_FN = (self, signal, currentPrice) => {
|
|
|
4116
4186
|
else {
|
|
4117
4187
|
// Trailing stop is negative (above entry)
|
|
4118
4188
|
// Check if we can upgrade it to breakeven
|
|
4119
|
-
const thresholdPrice =
|
|
4189
|
+
const thresholdPrice = effectivePriceOpen * (1 - breakevenThresholdPercent / 100);
|
|
4120
4190
|
const isThresholdReached = currentPrice <= thresholdPrice;
|
|
4121
4191
|
if (isThresholdReached && breakevenPrice < trailingStopLoss) {
|
|
4122
4192
|
// Check for price intrusion before setting new SL
|
|
@@ -4166,21 +4236,21 @@ const BREAKEVEN_FN = (self, signal, currentPrice) => {
|
|
|
4166
4236
|
}
|
|
4167
4237
|
// No trailing stop set - proceed with normal breakeven logic
|
|
4168
4238
|
const currentStopLoss = signal.priceStopLoss;
|
|
4169
|
-
const breakevenPrice =
|
|
4239
|
+
const breakevenPrice = effectivePriceOpen;
|
|
4170
4240
|
// Calculate threshold price
|
|
4171
4241
|
let thresholdPrice;
|
|
4172
4242
|
let isThresholdReached;
|
|
4173
4243
|
let canMoveToBreakeven;
|
|
4174
4244
|
if (signal.position === "long") {
|
|
4175
4245
|
// LONG: threshold reached when price goes UP by breakevenThresholdPercent from entry
|
|
4176
|
-
thresholdPrice =
|
|
4246
|
+
thresholdPrice = effectivePriceOpen * (1 + breakevenThresholdPercent / 100);
|
|
4177
4247
|
isThresholdReached = currentPrice >= thresholdPrice;
|
|
4178
4248
|
// Can move to breakeven only if threshold reached and SL is below entry
|
|
4179
4249
|
canMoveToBreakeven = isThresholdReached && currentStopLoss < breakevenPrice;
|
|
4180
4250
|
}
|
|
4181
4251
|
else {
|
|
4182
4252
|
// SHORT: threshold reached when price goes DOWN by breakevenThresholdPercent from entry
|
|
4183
|
-
thresholdPrice =
|
|
4253
|
+
thresholdPrice = effectivePriceOpen * (1 - breakevenThresholdPercent / 100);
|
|
4184
4254
|
isThresholdReached = currentPrice <= thresholdPrice;
|
|
4185
4255
|
// Can move to breakeven only if threshold reached and SL is above entry
|
|
4186
4256
|
canMoveToBreakeven = isThresholdReached && currentStopLoss > breakevenPrice;
|
|
@@ -4240,8 +4310,51 @@ const BREAKEVEN_FN = (self, signal, currentPrice) => {
|
|
|
4240
4310
|
thresholdPrice,
|
|
4241
4311
|
breakevenThresholdPercent,
|
|
4242
4312
|
profitDistancePercent: signal.position === "long"
|
|
4243
|
-
? ((currentPrice -
|
|
4244
|
-
: ((
|
|
4313
|
+
? ((currentPrice - effectivePriceOpen) / effectivePriceOpen * 100)
|
|
4314
|
+
: ((effectivePriceOpen - currentPrice) / effectivePriceOpen * 100),
|
|
4315
|
+
});
|
|
4316
|
+
return true;
|
|
4317
|
+
};
|
|
4318
|
+
const AVERAGE_BUY_FN = (self, signal, currentPrice) => {
|
|
4319
|
+
// Ensure _entry is initialized (handles signals loaded from disk without _entry)
|
|
4320
|
+
if (!signal._entry || signal._entry.length === 0) {
|
|
4321
|
+
signal._entry = [{ price: signal.priceOpen }];
|
|
4322
|
+
}
|
|
4323
|
+
const lastEntry = signal._entry[signal._entry.length - 1];
|
|
4324
|
+
if (signal.position === "long") {
|
|
4325
|
+
// LONG: averaging down = currentPrice must be strictly lower than last entry
|
|
4326
|
+
if (currentPrice >= lastEntry.price) {
|
|
4327
|
+
self.params.logger.debug("AVERAGE_BUY_FN: rejected — currentPrice >= last entry (LONG)", {
|
|
4328
|
+
signalId: signal.id,
|
|
4329
|
+
position: signal.position,
|
|
4330
|
+
currentPrice,
|
|
4331
|
+
lastEntryPrice: lastEntry.price,
|
|
4332
|
+
reason: "must average down for LONG",
|
|
4333
|
+
});
|
|
4334
|
+
return false;
|
|
4335
|
+
}
|
|
4336
|
+
}
|
|
4337
|
+
else {
|
|
4338
|
+
// SHORT: averaging down = currentPrice must be strictly higher than last entry
|
|
4339
|
+
if (currentPrice <= lastEntry.price) {
|
|
4340
|
+
self.params.logger.debug("AVERAGE_BUY_FN: rejected — currentPrice <= last entry (SHORT)", {
|
|
4341
|
+
signalId: signal.id,
|
|
4342
|
+
position: signal.position,
|
|
4343
|
+
currentPrice,
|
|
4344
|
+
lastEntryPrice: lastEntry.price,
|
|
4345
|
+
reason: "must average down for SHORT",
|
|
4346
|
+
});
|
|
4347
|
+
return false;
|
|
4348
|
+
}
|
|
4349
|
+
}
|
|
4350
|
+
signal._entry.push({ price: currentPrice });
|
|
4351
|
+
self.params.logger.info("AVERAGE_BUY_FN executed", {
|
|
4352
|
+
signalId: signal.id,
|
|
4353
|
+
position: signal.position,
|
|
4354
|
+
originalPriceOpen: signal.priceOpen,
|
|
4355
|
+
newEntryPrice: currentPrice,
|
|
4356
|
+
newEffectivePrice: getEffectivePriceOpen(signal),
|
|
4357
|
+
totalEntries: signal._entry.length,
|
|
4245
4358
|
});
|
|
4246
4359
|
return true;
|
|
4247
4360
|
};
|
|
@@ -4970,9 +5083,10 @@ const RETURN_PENDING_SIGNAL_ACTIVE_FN = async (self, signal, currentPrice) => {
|
|
|
4970
5083
|
await CALL_ACTIVE_PING_CALLBACKS_FN(self, self.params.execution.context.symbol, signal, currentTime, self.params.execution.context.backtest);
|
|
4971
5084
|
// Calculate percentage of path to TP/SL for partial fill/loss callbacks
|
|
4972
5085
|
{
|
|
5086
|
+
const effectivePriceOpen = getEffectivePriceOpen(signal);
|
|
4973
5087
|
if (signal.position === "long") {
|
|
4974
5088
|
// For long: calculate progress towards TP or SL
|
|
4975
|
-
const currentDistance = currentPrice -
|
|
5089
|
+
const currentDistance = currentPrice - effectivePriceOpen;
|
|
4976
5090
|
if (currentDistance > 0) {
|
|
4977
5091
|
// Check if breakeven should be triggered
|
|
4978
5092
|
await CALL_BREAKEVEN_CHECK_FN(self, self.params.execution.context.symbol, signal, currentPrice, currentTime, self.params.execution.context.backtest);
|
|
@@ -4980,7 +5094,7 @@ const RETURN_PENDING_SIGNAL_ACTIVE_FN = async (self, signal, currentPrice) => {
|
|
|
4980
5094
|
if (currentDistance > 0) {
|
|
4981
5095
|
// Moving towards TP (use trailing TP if set)
|
|
4982
5096
|
const effectiveTakeProfit = signal._trailingPriceTakeProfit ?? signal.priceTakeProfit;
|
|
4983
|
-
const tpDistance = effectiveTakeProfit -
|
|
5097
|
+
const tpDistance = effectiveTakeProfit - effectivePriceOpen;
|
|
4984
5098
|
const progressPercent = (currentDistance / tpDistance) * 100;
|
|
4985
5099
|
percentTp = Math.min(progressPercent, 100);
|
|
4986
5100
|
await CALL_PARTIAL_PROFIT_CALLBACKS_FN(self, self.params.execution.context.symbol, signal, currentPrice, percentTp, currentTime, self.params.execution.context.backtest);
|
|
@@ -4988,7 +5102,7 @@ const RETURN_PENDING_SIGNAL_ACTIVE_FN = async (self, signal, currentPrice) => {
|
|
|
4988
5102
|
else if (currentDistance < 0) {
|
|
4989
5103
|
// Moving towards SL (use trailing SL if set)
|
|
4990
5104
|
const effectiveStopLoss = signal._trailingPriceStopLoss ?? signal.priceStopLoss;
|
|
4991
|
-
const slDistance =
|
|
5105
|
+
const slDistance = effectivePriceOpen - effectiveStopLoss;
|
|
4992
5106
|
const progressPercent = (Math.abs(currentDistance) / slDistance) * 100;
|
|
4993
5107
|
percentSl = Math.min(progressPercent, 100);
|
|
4994
5108
|
await CALL_PARTIAL_LOSS_CALLBACKS_FN(self, self.params.execution.context.symbol, signal, currentPrice, percentSl, currentTime, self.params.execution.context.backtest);
|
|
@@ -4996,7 +5110,7 @@ const RETURN_PENDING_SIGNAL_ACTIVE_FN = async (self, signal, currentPrice) => {
|
|
|
4996
5110
|
}
|
|
4997
5111
|
else if (signal.position === "short") {
|
|
4998
5112
|
// For short: calculate progress towards TP or SL
|
|
4999
|
-
const currentDistance =
|
|
5113
|
+
const currentDistance = effectivePriceOpen - currentPrice;
|
|
5000
5114
|
if (currentDistance > 0) {
|
|
5001
5115
|
// Check if breakeven should be triggered
|
|
5002
5116
|
await CALL_BREAKEVEN_CHECK_FN(self, self.params.execution.context.symbol, signal, currentPrice, currentTime, self.params.execution.context.backtest);
|
|
@@ -5004,7 +5118,7 @@ const RETURN_PENDING_SIGNAL_ACTIVE_FN = async (self, signal, currentPrice) => {
|
|
|
5004
5118
|
if (currentDistance > 0) {
|
|
5005
5119
|
// Moving towards TP (use trailing TP if set)
|
|
5006
5120
|
const effectiveTakeProfit = signal._trailingPriceTakeProfit ?? signal.priceTakeProfit;
|
|
5007
|
-
const tpDistance =
|
|
5121
|
+
const tpDistance = effectivePriceOpen - effectiveTakeProfit;
|
|
5008
5122
|
const progressPercent = (currentDistance / tpDistance) * 100;
|
|
5009
5123
|
percentTp = Math.min(progressPercent, 100);
|
|
5010
5124
|
await CALL_PARTIAL_PROFIT_CALLBACKS_FN(self, self.params.execution.context.symbol, signal, currentPrice, percentTp, currentTime, self.params.execution.context.backtest);
|
|
@@ -5012,7 +5126,7 @@ const RETURN_PENDING_SIGNAL_ACTIVE_FN = async (self, signal, currentPrice) => {
|
|
|
5012
5126
|
if (currentDistance < 0) {
|
|
5013
5127
|
// Moving towards SL (use trailing SL if set)
|
|
5014
5128
|
const effectiveStopLoss = signal._trailingPriceStopLoss ?? signal.priceStopLoss;
|
|
5015
|
-
const slDistance = effectiveStopLoss -
|
|
5129
|
+
const slDistance = effectiveStopLoss - effectivePriceOpen;
|
|
5016
5130
|
const progressPercent = (Math.abs(currentDistance) / slDistance) * 100;
|
|
5017
5131
|
percentSl = Math.min(progressPercent, 100);
|
|
5018
5132
|
await CALL_PARTIAL_LOSS_CALLBACKS_FN(self, self.params.execution.context.symbol, signal, currentPrice, percentSl, currentTime, self.params.execution.context.backtest);
|
|
@@ -5232,8 +5346,10 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
|
|
|
5232
5346
|
priceStopLoss: publicSignalForCommit.priceStopLoss,
|
|
5233
5347
|
originalPriceTakeProfit: publicSignalForCommit.originalPriceTakeProfit,
|
|
5234
5348
|
originalPriceStopLoss: publicSignalForCommit.originalPriceStopLoss,
|
|
5349
|
+
originalPriceOpen: publicSignalForCommit.originalPriceOpen,
|
|
5235
5350
|
scheduledAt: publicSignalForCommit.scheduledAt,
|
|
5236
5351
|
pendingAt: publicSignalForCommit.pendingAt,
|
|
5352
|
+
totalEntries: publicSignalForCommit.totalEntries,
|
|
5237
5353
|
});
|
|
5238
5354
|
await CALL_OPEN_CALLBACKS_FN(self, self.params.execution.context.symbol, pendingSignal, pendingSignal.priceOpen, candle.timestamp, self.params.execution.context.backtest);
|
|
5239
5355
|
await CALL_BACKTEST_SCHEDULE_OPEN_FN(self, self.params.execution.context.symbol, pendingSignal, candle.timestamp, self.params.execution.context.backtest);
|
|
@@ -5380,9 +5496,10 @@ const PROCESS_PENDING_SIGNAL_CANDLES_FN = async (self, signal, candles) => {
|
|
|
5380
5496
|
// Call onPartialProfit/onPartialLoss callbacks during backtest candle processing
|
|
5381
5497
|
// Calculate percentage of path to TP/SL
|
|
5382
5498
|
{
|
|
5499
|
+
const effectivePriceOpen = getEffectivePriceOpen(signal);
|
|
5383
5500
|
if (signal.position === "long") {
|
|
5384
5501
|
// For long: calculate progress towards TP or SL
|
|
5385
|
-
const currentDistance = averagePrice -
|
|
5502
|
+
const currentDistance = averagePrice - effectivePriceOpen;
|
|
5386
5503
|
if (currentDistance > 0) {
|
|
5387
5504
|
// Check if breakeven should be triggered
|
|
5388
5505
|
await CALL_BREAKEVEN_CHECK_FN(self, self.params.execution.context.symbol, signal, averagePrice, currentCandleTimestamp, self.params.execution.context.backtest);
|
|
@@ -5390,21 +5507,21 @@ const PROCESS_PENDING_SIGNAL_CANDLES_FN = async (self, signal, candles) => {
|
|
|
5390
5507
|
if (currentDistance > 0) {
|
|
5391
5508
|
// Moving towards TP (use trailing TP if set)
|
|
5392
5509
|
const effectiveTakeProfit = signal._trailingPriceTakeProfit ?? signal.priceTakeProfit;
|
|
5393
|
-
const tpDistance = effectiveTakeProfit -
|
|
5510
|
+
const tpDistance = effectiveTakeProfit - effectivePriceOpen;
|
|
5394
5511
|
const progressPercent = (currentDistance / tpDistance) * 100;
|
|
5395
5512
|
await CALL_PARTIAL_PROFIT_CALLBACKS_FN(self, self.params.execution.context.symbol, signal, averagePrice, Math.min(progressPercent, 100), currentCandleTimestamp, self.params.execution.context.backtest);
|
|
5396
5513
|
}
|
|
5397
5514
|
else if (currentDistance < 0) {
|
|
5398
5515
|
// Moving towards SL (use trailing SL if set)
|
|
5399
5516
|
const effectiveStopLoss = signal._trailingPriceStopLoss ?? signal.priceStopLoss;
|
|
5400
|
-
const slDistance =
|
|
5517
|
+
const slDistance = effectivePriceOpen - effectiveStopLoss;
|
|
5401
5518
|
const progressPercent = (Math.abs(currentDistance) / slDistance) * 100;
|
|
5402
5519
|
await CALL_PARTIAL_LOSS_CALLBACKS_FN(self, self.params.execution.context.symbol, signal, averagePrice, Math.min(progressPercent, 100), currentCandleTimestamp, self.params.execution.context.backtest);
|
|
5403
5520
|
}
|
|
5404
5521
|
}
|
|
5405
5522
|
else if (signal.position === "short") {
|
|
5406
5523
|
// For short: calculate progress towards TP or SL
|
|
5407
|
-
const currentDistance =
|
|
5524
|
+
const currentDistance = effectivePriceOpen - averagePrice;
|
|
5408
5525
|
if (currentDistance > 0) {
|
|
5409
5526
|
// Check if breakeven should be triggered
|
|
5410
5527
|
await CALL_BREAKEVEN_CHECK_FN(self, self.params.execution.context.symbol, signal, averagePrice, currentCandleTimestamp, self.params.execution.context.backtest);
|
|
@@ -5412,14 +5529,14 @@ const PROCESS_PENDING_SIGNAL_CANDLES_FN = async (self, signal, candles) => {
|
|
|
5412
5529
|
if (currentDistance > 0) {
|
|
5413
5530
|
// Moving towards TP (use trailing TP if set)
|
|
5414
5531
|
const effectiveTakeProfit = signal._trailingPriceTakeProfit ?? signal.priceTakeProfit;
|
|
5415
|
-
const tpDistance =
|
|
5532
|
+
const tpDistance = effectivePriceOpen - effectiveTakeProfit;
|
|
5416
5533
|
const progressPercent = (currentDistance / tpDistance) * 100;
|
|
5417
5534
|
await CALL_PARTIAL_PROFIT_CALLBACKS_FN(self, self.params.execution.context.symbol, signal, averagePrice, Math.min(progressPercent, 100), currentCandleTimestamp, self.params.execution.context.backtest);
|
|
5418
5535
|
}
|
|
5419
5536
|
if (currentDistance < 0) {
|
|
5420
5537
|
// Moving towards SL (use trailing SL if set)
|
|
5421
5538
|
const effectiveStopLoss = signal._trailingPriceStopLoss ?? signal.priceStopLoss;
|
|
5422
|
-
const slDistance = effectiveStopLoss -
|
|
5539
|
+
const slDistance = effectiveStopLoss - effectivePriceOpen;
|
|
5423
5540
|
const progressPercent = (Math.abs(currentDistance) / slDistance) * 100;
|
|
5424
5541
|
await CALL_PARTIAL_LOSS_CALLBACKS_FN(self, self.params.execution.context.symbol, signal, averagePrice, Math.min(progressPercent, 100), currentCandleTimestamp, self.params.execution.context.backtest);
|
|
5425
5542
|
}
|
|
@@ -5617,6 +5734,7 @@ class ClientStrategy {
|
|
|
5617
5734
|
return false;
|
|
5618
5735
|
}
|
|
5619
5736
|
const signal = this._pendingSignal;
|
|
5737
|
+
const effectivePriceOpen = getEffectivePriceOpen(signal);
|
|
5620
5738
|
// Calculate breakeven threshold based on slippage and fees
|
|
5621
5739
|
// Need to cover: entry slippage + entry fee + exit slippage + exit fee
|
|
5622
5740
|
// Total: (slippage + fee) * 2 transactions
|
|
@@ -5626,52 +5744,52 @@ class ClientStrategy {
|
|
|
5626
5744
|
const trailingStopLoss = signal._trailingPriceStopLoss;
|
|
5627
5745
|
if (signal.position === "long") {
|
|
5628
5746
|
// LONG: trailing SL is positive if it's above entry (in profit zone)
|
|
5629
|
-
const isPositiveTrailing = trailingStopLoss >
|
|
5747
|
+
const isPositiveTrailing = trailingStopLoss > effectivePriceOpen;
|
|
5630
5748
|
if (isPositiveTrailing) {
|
|
5631
5749
|
// Trailing stop is already protecting profit - breakeven achieved
|
|
5632
5750
|
return true;
|
|
5633
5751
|
}
|
|
5634
5752
|
// Trailing stop is negative (below entry)
|
|
5635
5753
|
// Check if we can upgrade it to breakeven
|
|
5636
|
-
const thresholdPrice =
|
|
5754
|
+
const thresholdPrice = effectivePriceOpen * (1 + breakevenThresholdPercent / 100);
|
|
5637
5755
|
const isThresholdReached = currentPrice >= thresholdPrice;
|
|
5638
|
-
const breakevenPrice =
|
|
5756
|
+
const breakevenPrice = effectivePriceOpen;
|
|
5639
5757
|
// Can upgrade to breakeven if threshold reached and breakeven is better than current trailing SL
|
|
5640
5758
|
return isThresholdReached && breakevenPrice > trailingStopLoss;
|
|
5641
5759
|
}
|
|
5642
5760
|
else {
|
|
5643
5761
|
// SHORT: trailing SL is positive if it's below entry (in profit zone)
|
|
5644
|
-
const isPositiveTrailing = trailingStopLoss <
|
|
5762
|
+
const isPositiveTrailing = trailingStopLoss < effectivePriceOpen;
|
|
5645
5763
|
if (isPositiveTrailing) {
|
|
5646
5764
|
// Trailing stop is already protecting profit - breakeven achieved
|
|
5647
5765
|
return true;
|
|
5648
5766
|
}
|
|
5649
5767
|
// Trailing stop is negative (above entry)
|
|
5650
5768
|
// Check if we can upgrade it to breakeven
|
|
5651
|
-
const thresholdPrice =
|
|
5769
|
+
const thresholdPrice = effectivePriceOpen * (1 - breakevenThresholdPercent / 100);
|
|
5652
5770
|
const isThresholdReached = currentPrice <= thresholdPrice;
|
|
5653
|
-
const breakevenPrice =
|
|
5771
|
+
const breakevenPrice = effectivePriceOpen;
|
|
5654
5772
|
// Can upgrade to breakeven if threshold reached and breakeven is better than current trailing SL
|
|
5655
5773
|
return isThresholdReached && breakevenPrice < trailingStopLoss;
|
|
5656
5774
|
}
|
|
5657
5775
|
}
|
|
5658
5776
|
// No trailing stop set - proceed with normal breakeven logic
|
|
5659
5777
|
const currentStopLoss = signal.priceStopLoss;
|
|
5660
|
-
const breakevenPrice =
|
|
5778
|
+
const breakevenPrice = effectivePriceOpen;
|
|
5661
5779
|
// Calculate threshold price
|
|
5662
5780
|
let thresholdPrice;
|
|
5663
5781
|
let isThresholdReached;
|
|
5664
5782
|
let canMoveToBreakeven;
|
|
5665
5783
|
if (signal.position === "long") {
|
|
5666
5784
|
// LONG: threshold reached when price goes UP by breakevenThresholdPercent from entry
|
|
5667
|
-
thresholdPrice =
|
|
5785
|
+
thresholdPrice = effectivePriceOpen * (1 + breakevenThresholdPercent / 100);
|
|
5668
5786
|
isThresholdReached = currentPrice >= thresholdPrice;
|
|
5669
5787
|
// Can move to breakeven only if threshold reached and SL is below entry
|
|
5670
5788
|
canMoveToBreakeven = isThresholdReached && currentStopLoss < breakevenPrice;
|
|
5671
5789
|
}
|
|
5672
5790
|
else {
|
|
5673
5791
|
// SHORT: threshold reached when price goes DOWN by breakevenThresholdPercent from entry
|
|
5674
|
-
thresholdPrice =
|
|
5792
|
+
thresholdPrice = effectivePriceOpen * (1 - breakevenThresholdPercent / 100);
|
|
5675
5793
|
isThresholdReached = currentPrice <= thresholdPrice;
|
|
5676
5794
|
// Can move to breakeven only if threshold reached and SL is above entry
|
|
5677
5795
|
canMoveToBreakeven = isThresholdReached && currentStopLoss > breakevenPrice;
|
|
@@ -5754,6 +5872,8 @@ class ClientStrategy {
|
|
|
5754
5872
|
backtest: this.params.execution.context.backtest,
|
|
5755
5873
|
cancelId: cancelledSignal.cancelId,
|
|
5756
5874
|
timestamp: currentTime,
|
|
5875
|
+
totalEntries: cancelledSignal._entry?.length ?? 1,
|
|
5876
|
+
originalPriceOpen: cancelledSignal.priceOpen,
|
|
5757
5877
|
});
|
|
5758
5878
|
// Call onCancel callback
|
|
5759
5879
|
await CALL_CANCEL_CALLBACKS_FN(this, this.params.execution.context.symbol, cancelledSignal, currentPrice, currentTime, this.params.execution.context.backtest);
|
|
@@ -5794,6 +5914,8 @@ class ClientStrategy {
|
|
|
5794
5914
|
backtest: this.params.execution.context.backtest,
|
|
5795
5915
|
closeId: closedSignal.closeId,
|
|
5796
5916
|
timestamp: currentTime,
|
|
5917
|
+
totalEntries: closedSignal._entry?.length ?? 1,
|
|
5918
|
+
originalPriceOpen: closedSignal.priceOpen,
|
|
5797
5919
|
});
|
|
5798
5920
|
// Call onClose callback
|
|
5799
5921
|
await CALL_CLOSE_CALLBACKS_FN(this, this.params.execution.context.symbol, closedSignal, currentPrice, currentTime, this.params.execution.context.backtest);
|
|
@@ -5874,8 +5996,10 @@ class ClientStrategy {
|
|
|
5874
5996
|
priceStopLoss: publicSignalForCommit.priceStopLoss,
|
|
5875
5997
|
originalPriceTakeProfit: publicSignalForCommit.originalPriceTakeProfit,
|
|
5876
5998
|
originalPriceStopLoss: publicSignalForCommit.originalPriceStopLoss,
|
|
5999
|
+
originalPriceOpen: publicSignalForCommit.originalPriceOpen,
|
|
5877
6000
|
scheduledAt: publicSignalForCommit.scheduledAt,
|
|
5878
6001
|
pendingAt: publicSignalForCommit.pendingAt,
|
|
6002
|
+
totalEntries: publicSignalForCommit.totalEntries,
|
|
5879
6003
|
});
|
|
5880
6004
|
// Call onOpen callback
|
|
5881
6005
|
await CALL_OPEN_CALLBACKS_FN(this, this.params.execution.context.symbol, pendingSignal, currentPrice, currentTime, this.params.execution.context.backtest);
|
|
@@ -6006,6 +6130,8 @@ class ClientStrategy {
|
|
|
6006
6130
|
backtest: true,
|
|
6007
6131
|
cancelId: cancelledSignal.cancelId,
|
|
6008
6132
|
timestamp: closeTimestamp,
|
|
6133
|
+
totalEntries: cancelledSignal._entry?.length ?? 1,
|
|
6134
|
+
originalPriceOpen: cancelledSignal.priceOpen,
|
|
6009
6135
|
});
|
|
6010
6136
|
await CALL_CANCEL_CALLBACKS_FN(this, this.params.execution.context.symbol, cancelledSignal, currentPrice, closeTimestamp, this.params.execution.context.backtest);
|
|
6011
6137
|
const cancelledResult = {
|
|
@@ -6043,6 +6169,8 @@ class ClientStrategy {
|
|
|
6043
6169
|
backtest: true,
|
|
6044
6170
|
closeId: closedSignal.closeId,
|
|
6045
6171
|
timestamp: closeTimestamp,
|
|
6172
|
+
totalEntries: closedSignal._entry?.length ?? 1,
|
|
6173
|
+
originalPriceOpen: closedSignal.priceOpen,
|
|
6046
6174
|
});
|
|
6047
6175
|
await CALL_CLOSE_CALLBACKS_FN(this, this.params.execution.context.symbol, closedSignal, currentPrice, closeTimestamp, this.params.execution.context.backtest);
|
|
6048
6176
|
// КРИТИЧНО: Очищаем состояние ClientPartial при закрытии позиции
|
|
@@ -6430,16 +6558,19 @@ class ClientStrategy {
|
|
|
6430
6558
|
throw new Error(`ClientStrategy partialProfit: currentPrice must be a positive finite number, got ${currentPrice}`);
|
|
6431
6559
|
}
|
|
6432
6560
|
// Validation: currentPrice must be moving toward TP (profit direction)
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
if (
|
|
6436
|
-
|
|
6561
|
+
{
|
|
6562
|
+
const effectivePriceOpen = getEffectivePriceOpen(this._pendingSignal);
|
|
6563
|
+
if (this._pendingSignal.position === "long") {
|
|
6564
|
+
// For LONG: currentPrice must be higher than effectivePriceOpen (moving toward TP)
|
|
6565
|
+
if (currentPrice <= effectivePriceOpen) {
|
|
6566
|
+
throw new Error(`ClientStrategy partialProfit: For LONG position, currentPrice (${currentPrice}) must be > effectivePriceOpen (${effectivePriceOpen})`);
|
|
6567
|
+
}
|
|
6437
6568
|
}
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
|
|
6569
|
+
else {
|
|
6570
|
+
// For SHORT: currentPrice must be lower than effectivePriceOpen (moving toward TP)
|
|
6571
|
+
if (currentPrice >= effectivePriceOpen) {
|
|
6572
|
+
throw new Error(`ClientStrategy partialProfit: For SHORT position, currentPrice (${currentPrice}) must be < effectivePriceOpen (${effectivePriceOpen})`);
|
|
6573
|
+
}
|
|
6443
6574
|
}
|
|
6444
6575
|
}
|
|
6445
6576
|
// Check if currentPrice already crossed take profit level
|
|
@@ -6562,16 +6693,19 @@ class ClientStrategy {
|
|
|
6562
6693
|
throw new Error(`ClientStrategy partialLoss: currentPrice must be a positive finite number, got ${currentPrice}`);
|
|
6563
6694
|
}
|
|
6564
6695
|
// Validation: currentPrice must be moving toward SL (loss direction)
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
if (
|
|
6568
|
-
|
|
6696
|
+
{
|
|
6697
|
+
const effectivePriceOpen = getEffectivePriceOpen(this._pendingSignal);
|
|
6698
|
+
if (this._pendingSignal.position === "long") {
|
|
6699
|
+
// For LONG: currentPrice must be lower than effectivePriceOpen (moving toward SL)
|
|
6700
|
+
if (currentPrice >= effectivePriceOpen) {
|
|
6701
|
+
throw new Error(`ClientStrategy partialLoss: For LONG position, currentPrice (${currentPrice}) must be < effectivePriceOpen (${effectivePriceOpen})`);
|
|
6702
|
+
}
|
|
6569
6703
|
}
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6704
|
+
else {
|
|
6705
|
+
// For SHORT: currentPrice must be higher than effectivePriceOpen (moving toward SL)
|
|
6706
|
+
if (currentPrice <= effectivePriceOpen) {
|
|
6707
|
+
throw new Error(`ClientStrategy partialLoss: For SHORT position, currentPrice (${currentPrice}) must be > effectivePriceOpen (${effectivePriceOpen})`);
|
|
6708
|
+
}
|
|
6575
6709
|
}
|
|
6576
6710
|
}
|
|
6577
6711
|
// Check if currentPrice already crossed stop loss level
|
|
@@ -6692,7 +6826,7 @@ class ClientStrategy {
|
|
|
6692
6826
|
}
|
|
6693
6827
|
// Check for conflict with existing trailing take profit
|
|
6694
6828
|
const signal = this._pendingSignal;
|
|
6695
|
-
const breakevenPrice = signal
|
|
6829
|
+
const breakevenPrice = getEffectivePriceOpen(signal);
|
|
6696
6830
|
const effectiveTakeProfit = signal._trailingPriceTakeProfit ?? signal.priceTakeProfit;
|
|
6697
6831
|
if (signal.position === "long" && breakevenPrice >= effectiveTakeProfit) {
|
|
6698
6832
|
// LONG: Breakeven SL would be at or above current TP - invalid configuration
|
|
@@ -6844,14 +6978,15 @@ class ClientStrategy {
|
|
|
6844
6978
|
}
|
|
6845
6979
|
// Calculate what the new stop loss would be
|
|
6846
6980
|
const signal = this._pendingSignal;
|
|
6847
|
-
const
|
|
6981
|
+
const effectivePriceOpen = getEffectivePriceOpen(signal);
|
|
6982
|
+
const slDistancePercent = Math.abs((effectivePriceOpen - signal.priceStopLoss) / effectivePriceOpen * 100);
|
|
6848
6983
|
const newSlDistancePercent = slDistancePercent + percentShift;
|
|
6849
6984
|
let newStopLoss;
|
|
6850
6985
|
if (signal.position === "long") {
|
|
6851
|
-
newStopLoss =
|
|
6986
|
+
newStopLoss = effectivePriceOpen * (1 - newSlDistancePercent / 100);
|
|
6852
6987
|
}
|
|
6853
6988
|
else {
|
|
6854
|
-
newStopLoss =
|
|
6989
|
+
newStopLoss = effectivePriceOpen * (1 + newSlDistancePercent / 100);
|
|
6855
6990
|
}
|
|
6856
6991
|
// Check for price intrusion before executing trailing logic
|
|
6857
6992
|
if (signal.position === "long" && currentPrice < newStopLoss) {
|
|
@@ -7017,14 +7152,15 @@ class ClientStrategy {
|
|
|
7017
7152
|
}
|
|
7018
7153
|
// Calculate what the new take profit would be
|
|
7019
7154
|
const signal = this._pendingSignal;
|
|
7020
|
-
const
|
|
7155
|
+
const effectivePriceOpen = getEffectivePriceOpen(signal);
|
|
7156
|
+
const tpDistancePercent = Math.abs((signal.priceTakeProfit - effectivePriceOpen) / effectivePriceOpen * 100);
|
|
7021
7157
|
const newTpDistancePercent = tpDistancePercent + percentShift;
|
|
7022
7158
|
let newTakeProfit;
|
|
7023
7159
|
if (signal.position === "long") {
|
|
7024
|
-
newTakeProfit =
|
|
7160
|
+
newTakeProfit = effectivePriceOpen * (1 + newTpDistancePercent / 100);
|
|
7025
7161
|
}
|
|
7026
7162
|
else {
|
|
7027
|
-
newTakeProfit =
|
|
7163
|
+
newTakeProfit = effectivePriceOpen * (1 - newTpDistancePercent / 100);
|
|
7028
7164
|
}
|
|
7029
7165
|
// Check for price intrusion before executing trailing logic
|
|
7030
7166
|
if (signal.position === "long" && currentPrice > newTakeProfit) {
|
|
@@ -7106,6 +7242,70 @@ class ClientStrategy {
|
|
|
7106
7242
|
});
|
|
7107
7243
|
return true;
|
|
7108
7244
|
}
|
|
7245
|
+
/**
|
|
7246
|
+
* Adds a new averaging entry to an open position (DCA — Dollar Cost Averaging).
|
|
7247
|
+
*
|
|
7248
|
+
* Appends currentPrice to the _entry array. The effective entry price used in all
|
|
7249
|
+
* distance and PNL calculations becomes the simple arithmetic mean of all _entry prices.
|
|
7250
|
+
* Original priceOpen is preserved unchanged for identity/audit purposes.
|
|
7251
|
+
*
|
|
7252
|
+
* Rejection rules (returns false without throwing):
|
|
7253
|
+
* - LONG: currentPrice >= last entry price (must average down, not up or equal)
|
|
7254
|
+
* - SHORT: currentPrice <= last entry price (must average down, not up or equal)
|
|
7255
|
+
*
|
|
7256
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
7257
|
+
* @param currentPrice - New entry price to add to the averaging history
|
|
7258
|
+
* @param backtest - Whether running in backtest mode
|
|
7259
|
+
* @returns Promise<boolean> - true if entry added, false if rejected by direction check
|
|
7260
|
+
*/
|
|
7261
|
+
async averageBuy(symbol, currentPrice, backtest) {
|
|
7262
|
+
this.params.logger.debug("ClientStrategy averageBuy", {
|
|
7263
|
+
symbol,
|
|
7264
|
+
currentPrice,
|
|
7265
|
+
hasPendingSignal: this._pendingSignal !== null,
|
|
7266
|
+
});
|
|
7267
|
+
// Validation: must have pending signal
|
|
7268
|
+
if (!this._pendingSignal) {
|
|
7269
|
+
throw new Error(`ClientStrategy averageBuy: No pending signal exists for symbol=${symbol}`);
|
|
7270
|
+
}
|
|
7271
|
+
// Validation: currentPrice must be valid
|
|
7272
|
+
if (typeof currentPrice !== "number" || !isFinite(currentPrice) || currentPrice <= 0) {
|
|
7273
|
+
throw new Error(`ClientStrategy averageBuy: currentPrice must be a positive finite number, got ${currentPrice}`);
|
|
7274
|
+
}
|
|
7275
|
+
// Reject if any partial closes have already been executed
|
|
7276
|
+
if (this._pendingSignal._partial && this._pendingSignal._partial.length > 0) {
|
|
7277
|
+
this.params.logger.debug("ClientStrategy averageBuy: rejected — partial closes already executed", {
|
|
7278
|
+
symbol,
|
|
7279
|
+
partialCount: this._pendingSignal._partial.length,
|
|
7280
|
+
});
|
|
7281
|
+
return false;
|
|
7282
|
+
}
|
|
7283
|
+
// Execute averaging logic
|
|
7284
|
+
const result = AVERAGE_BUY_FN(this, this._pendingSignal, currentPrice);
|
|
7285
|
+
if (!result) {
|
|
7286
|
+
return false;
|
|
7287
|
+
}
|
|
7288
|
+
// Persist updated signal state
|
|
7289
|
+
this.params.logger.debug("ClientStrategy setPendingSignal (inline)", {
|
|
7290
|
+
pendingSignal: this._pendingSignal,
|
|
7291
|
+
});
|
|
7292
|
+
// Call onWrite callback for testing persist storage
|
|
7293
|
+
if (this.params.callbacks?.onWrite) {
|
|
7294
|
+
this.params.callbacks.onWrite(this.params.execution.context.symbol, TO_PUBLIC_SIGNAL(this._pendingSignal), backtest);
|
|
7295
|
+
}
|
|
7296
|
+
if (!backtest) {
|
|
7297
|
+
await PersistSignalAdapter.writeSignalData(this._pendingSignal, this.params.execution.context.symbol, this.params.strategyName, this.params.exchangeName);
|
|
7298
|
+
}
|
|
7299
|
+
// Queue commit event for processing in tick()/backtest() with proper timestamp
|
|
7300
|
+
this._commitQueue.push({
|
|
7301
|
+
action: "average-buy",
|
|
7302
|
+
symbol,
|
|
7303
|
+
backtest,
|
|
7304
|
+
currentPrice,
|
|
7305
|
+
totalEntries: this._pendingSignal._entry?.length ?? 1,
|
|
7306
|
+
});
|
|
7307
|
+
return true;
|
|
7308
|
+
}
|
|
7109
7309
|
}
|
|
7110
7310
|
|
|
7111
7311
|
const RISK_METHOD_NAME_GET_DATA = "RiskUtils.getData";
|
|
@@ -8230,6 +8430,27 @@ class StrategyConnectionService {
|
|
|
8230
8430
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
8231
8431
|
return await strategy.activateScheduled(symbol, backtest, activateId);
|
|
8232
8432
|
};
|
|
8433
|
+
/**
|
|
8434
|
+
* Adds a new DCA entry to the active pending signal.
|
|
8435
|
+
*
|
|
8436
|
+
* Delegates to ClientStrategy.averageBuy() with current execution context.
|
|
8437
|
+
*
|
|
8438
|
+
* @param backtest - Whether running in backtest mode
|
|
8439
|
+
* @param symbol - Trading pair symbol
|
|
8440
|
+
* @param currentPrice - New entry price to add to the averaging history
|
|
8441
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
8442
|
+
* @returns Promise<boolean> - true if entry added, false if rejected
|
|
8443
|
+
*/
|
|
8444
|
+
this.averageBuy = async (backtest, symbol, currentPrice, context) => {
|
|
8445
|
+
this.loggerService.log("strategyConnectionService averageBuy", {
|
|
8446
|
+
symbol,
|
|
8447
|
+
context,
|
|
8448
|
+
currentPrice,
|
|
8449
|
+
backtest,
|
|
8450
|
+
});
|
|
8451
|
+
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
8452
|
+
return await strategy.averageBuy(symbol, currentPrice, backtest);
|
|
8453
|
+
};
|
|
8233
8454
|
}
|
|
8234
8455
|
}
|
|
8235
8456
|
|
|
@@ -8703,11 +8924,13 @@ const TO_RISK_SIGNAL = (signal, currentPrice) => {
|
|
|
8703
8924
|
: 0;
|
|
8704
8925
|
return {
|
|
8705
8926
|
...structuredClone(signal),
|
|
8927
|
+
totalEntries: 1,
|
|
8706
8928
|
priceOpen: signal.priceOpen ?? currentPrice,
|
|
8707
8929
|
priceStopLoss: hasTrailingSL ? signal._trailingPriceStopLoss : signal.priceStopLoss,
|
|
8708
8930
|
priceTakeProfit: hasTrailingTP ? signal._trailingPriceTakeProfit : signal.priceTakeProfit,
|
|
8709
8931
|
originalPriceStopLoss: signal.priceStopLoss,
|
|
8710
8932
|
originalPriceTakeProfit: signal.priceTakeProfit,
|
|
8933
|
+
originalPriceOpen: signal.priceOpen ?? currentPrice,
|
|
8711
8934
|
partialExecuted,
|
|
8712
8935
|
};
|
|
8713
8936
|
};
|
|
@@ -11575,6 +11798,28 @@ class StrategyCoreService {
|
|
|
11575
11798
|
await this.validate(context);
|
|
11576
11799
|
return await this.strategyConnectionService.activateScheduled(backtest, symbol, context, activateId);
|
|
11577
11800
|
};
|
|
11801
|
+
/**
|
|
11802
|
+
* Adds a new DCA entry to the active pending signal.
|
|
11803
|
+
*
|
|
11804
|
+
* Validates strategy existence and delegates to connection service
|
|
11805
|
+
* to add a new averaging entry to the position.
|
|
11806
|
+
*
|
|
11807
|
+
* @param backtest - Whether running in backtest mode
|
|
11808
|
+
* @param symbol - Trading pair symbol
|
|
11809
|
+
* @param currentPrice - New entry price to add to the averaging history
|
|
11810
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
11811
|
+
* @returns Promise<boolean> - true if entry added, false if rejected
|
|
11812
|
+
*/
|
|
11813
|
+
this.averageBuy = async (backtest, symbol, currentPrice, context) => {
|
|
11814
|
+
this.loggerService.log("strategyCoreService averageBuy", {
|
|
11815
|
+
symbol,
|
|
11816
|
+
currentPrice,
|
|
11817
|
+
context,
|
|
11818
|
+
backtest,
|
|
11819
|
+
});
|
|
11820
|
+
await this.validate(context);
|
|
11821
|
+
return await this.strategyConnectionService.averageBuy(backtest, symbol, currentPrice, context);
|
|
11822
|
+
};
|
|
11578
11823
|
}
|
|
11579
11824
|
}
|
|
11580
11825
|
|
|
@@ -14122,6 +14367,18 @@ const backtest_columns = [
|
|
|
14122
14367
|
format: (data) => `${data.signal.originalPriceStopLoss.toFixed(8)} USD`,
|
|
14123
14368
|
isVisible: () => true,
|
|
14124
14369
|
},
|
|
14370
|
+
{
|
|
14371
|
+
key: "originalPriceOpen",
|
|
14372
|
+
label: "Original Entry",
|
|
14373
|
+
format: (data) => `${data.signal.originalPriceOpen.toFixed(8)} USD`,
|
|
14374
|
+
isVisible: () => true,
|
|
14375
|
+
},
|
|
14376
|
+
{
|
|
14377
|
+
key: "totalEntries",
|
|
14378
|
+
label: "DCA Entries",
|
|
14379
|
+
format: (data) => String(data.signal.totalEntries),
|
|
14380
|
+
isVisible: () => true,
|
|
14381
|
+
},
|
|
14125
14382
|
{
|
|
14126
14383
|
key: "pnl",
|
|
14127
14384
|
label: "PNL (net)",
|
|
@@ -14409,6 +14666,20 @@ const live_columns = [
|
|
|
14409
14666
|
: "N/A",
|
|
14410
14667
|
isVisible: () => true,
|
|
14411
14668
|
},
|
|
14669
|
+
{
|
|
14670
|
+
key: "originalPriceOpen",
|
|
14671
|
+
label: "Original Entry",
|
|
14672
|
+
format: (data) => data.originalPriceOpen !== undefined
|
|
14673
|
+
? `${data.originalPriceOpen.toFixed(8)} USD`
|
|
14674
|
+
: "N/A",
|
|
14675
|
+
isVisible: () => true,
|
|
14676
|
+
},
|
|
14677
|
+
{
|
|
14678
|
+
key: "totalEntries",
|
|
14679
|
+
label: "DCA Entries",
|
|
14680
|
+
format: (data) => data.totalEntries !== undefined ? String(data.totalEntries) : "N/A",
|
|
14681
|
+
isVisible: () => true,
|
|
14682
|
+
},
|
|
14412
14683
|
{
|
|
14413
14684
|
key: "partialExecuted",
|
|
14414
14685
|
label: "Partial Executed %",
|
|
@@ -14573,6 +14844,18 @@ const partial_columns = [
|
|
|
14573
14844
|
format: (data) => (data.originalPriceStopLoss ? `${data.originalPriceStopLoss.toFixed(8)} USD` : "N/A"),
|
|
14574
14845
|
isVisible: () => true,
|
|
14575
14846
|
},
|
|
14847
|
+
{
|
|
14848
|
+
key: "originalPriceOpen",
|
|
14849
|
+
label: "Original Entry",
|
|
14850
|
+
format: (data) => (data.originalPriceOpen ? `${data.originalPriceOpen.toFixed(8)} USD` : "N/A"),
|
|
14851
|
+
isVisible: () => true,
|
|
14852
|
+
},
|
|
14853
|
+
{
|
|
14854
|
+
key: "totalEntries",
|
|
14855
|
+
label: "DCA Entries",
|
|
14856
|
+
format: (data) => (data.totalEntries !== undefined ? String(data.totalEntries) : "N/A"),
|
|
14857
|
+
isVisible: () => true,
|
|
14858
|
+
},
|
|
14576
14859
|
{
|
|
14577
14860
|
key: "partialExecuted",
|
|
14578
14861
|
label: "Partial Executed %",
|
|
@@ -14707,6 +14990,18 @@ const breakeven_columns = [
|
|
|
14707
14990
|
format: (data) => (data.originalPriceStopLoss ? `${data.originalPriceStopLoss.toFixed(8)} USD` : "N/A"),
|
|
14708
14991
|
isVisible: () => true,
|
|
14709
14992
|
},
|
|
14993
|
+
{
|
|
14994
|
+
key: "originalPriceOpen",
|
|
14995
|
+
label: "Original Entry",
|
|
14996
|
+
format: (data) => (data.originalPriceOpen ? `${data.originalPriceOpen.toFixed(8)} USD` : "N/A"),
|
|
14997
|
+
isVisible: () => true,
|
|
14998
|
+
},
|
|
14999
|
+
{
|
|
15000
|
+
key: "totalEntries",
|
|
15001
|
+
label: "DCA Entries",
|
|
15002
|
+
format: (data) => (data.totalEntries !== undefined ? String(data.totalEntries) : "N/A"),
|
|
15003
|
+
isVisible: () => true,
|
|
15004
|
+
},
|
|
14710
15005
|
{
|
|
14711
15006
|
key: "partialExecuted",
|
|
14712
15007
|
label: "Partial Executed %",
|
|
@@ -14977,6 +15272,22 @@ const risk_columns = [
|
|
|
14977
15272
|
: "N/A",
|
|
14978
15273
|
isVisible: () => true,
|
|
14979
15274
|
},
|
|
15275
|
+
{
|
|
15276
|
+
key: "originalPriceOpen",
|
|
15277
|
+
label: "Original Entry",
|
|
15278
|
+
format: (data) => data.currentSignal.originalPriceOpen !== undefined
|
|
15279
|
+
? `${data.currentSignal.originalPriceOpen.toFixed(8)} USD`
|
|
15280
|
+
: "N/A",
|
|
15281
|
+
isVisible: () => true,
|
|
15282
|
+
},
|
|
15283
|
+
{
|
|
15284
|
+
key: "totalEntries",
|
|
15285
|
+
label: "DCA Entries",
|
|
15286
|
+
format: (data) => data.currentSignal.totalEntries !== undefined
|
|
15287
|
+
? String(data.currentSignal.totalEntries)
|
|
15288
|
+
: "N/A",
|
|
15289
|
+
isVisible: () => true,
|
|
15290
|
+
},
|
|
14980
15291
|
{
|
|
14981
15292
|
key: "partialExecuted",
|
|
14982
15293
|
label: "Partial Executed %",
|
|
@@ -15151,6 +15462,20 @@ const schedule_columns = [
|
|
|
15151
15462
|
: "N/A",
|
|
15152
15463
|
isVisible: () => true,
|
|
15153
15464
|
},
|
|
15465
|
+
{
|
|
15466
|
+
key: "originalPriceOpen",
|
|
15467
|
+
label: "Original Entry",
|
|
15468
|
+
format: (data) => data.originalPriceOpen !== undefined
|
|
15469
|
+
? `${data.originalPriceOpen.toFixed(8)} USD`
|
|
15470
|
+
: "N/A",
|
|
15471
|
+
isVisible: () => true,
|
|
15472
|
+
},
|
|
15473
|
+
{
|
|
15474
|
+
key: "totalEntries",
|
|
15475
|
+
label: "DCA Entries",
|
|
15476
|
+
format: (data) => data.totalEntries !== undefined ? String(data.totalEntries) : "N/A",
|
|
15477
|
+
isVisible: () => true,
|
|
15478
|
+
},
|
|
15154
15479
|
{
|
|
15155
15480
|
key: "partialExecuted",
|
|
15156
15481
|
label: "Partial Executed %",
|
|
@@ -17240,6 +17565,8 @@ let ReportStorage$5 = class ReportStorage {
|
|
|
17240
17565
|
priceStopLoss: data.signal.priceStopLoss,
|
|
17241
17566
|
originalPriceTakeProfit: data.signal.originalPriceTakeProfit,
|
|
17242
17567
|
originalPriceStopLoss: data.signal.originalPriceStopLoss,
|
|
17568
|
+
totalEntries: data.signal.totalEntries,
|
|
17569
|
+
originalPriceOpen: data.signal.originalPriceOpen,
|
|
17243
17570
|
partialExecuted: data.signal.partialExecuted,
|
|
17244
17571
|
scheduledAt: data.signal.scheduledAt,
|
|
17245
17572
|
});
|
|
@@ -17269,6 +17596,8 @@ let ReportStorage$5 = class ReportStorage {
|
|
|
17269
17596
|
priceStopLoss: data.signal.priceStopLoss,
|
|
17270
17597
|
originalPriceTakeProfit: data.signal.originalPriceTakeProfit,
|
|
17271
17598
|
originalPriceStopLoss: data.signal.originalPriceStopLoss,
|
|
17599
|
+
totalEntries: data.signal.totalEntries,
|
|
17600
|
+
originalPriceOpen: data.signal.originalPriceOpen,
|
|
17272
17601
|
partialExecuted: data.signal.partialExecuted,
|
|
17273
17602
|
duration: durationMin,
|
|
17274
17603
|
pendingAt: data.signal.pendingAt,
|
|
@@ -17301,6 +17630,8 @@ let ReportStorage$5 = class ReportStorage {
|
|
|
17301
17630
|
priceStopLoss: data.signal.priceStopLoss,
|
|
17302
17631
|
originalPriceTakeProfit: data.signal.originalPriceTakeProfit,
|
|
17303
17632
|
originalPriceStopLoss: data.signal.originalPriceStopLoss,
|
|
17633
|
+
totalEntries: data.signal.totalEntries,
|
|
17634
|
+
originalPriceOpen: data.signal.originalPriceOpen,
|
|
17304
17635
|
partialExecuted: data.signal.partialExecuted,
|
|
17305
17636
|
closeTimestamp: data.closeTimestamp,
|
|
17306
17637
|
duration: durationMin,
|
|
@@ -20446,6 +20777,8 @@ let ReportStorage$3 = class ReportStorage {
|
|
|
20446
20777
|
priceStopLoss: data.priceStopLoss,
|
|
20447
20778
|
originalPriceTakeProfit: data.originalPriceTakeProfit,
|
|
20448
20779
|
originalPriceStopLoss: data.originalPriceStopLoss,
|
|
20780
|
+
totalEntries: data.totalEntries,
|
|
20781
|
+
originalPriceOpen: data.originalPriceOpen,
|
|
20449
20782
|
partialExecuted: data.partialExecuted,
|
|
20450
20783
|
note: data.note,
|
|
20451
20784
|
pendingAt: data.pendingAt,
|
|
@@ -20480,6 +20813,8 @@ let ReportStorage$3 = class ReportStorage {
|
|
|
20480
20813
|
priceStopLoss: data.priceStopLoss,
|
|
20481
20814
|
originalPriceTakeProfit: data.originalPriceTakeProfit,
|
|
20482
20815
|
originalPriceStopLoss: data.originalPriceStopLoss,
|
|
20816
|
+
totalEntries: data.totalEntries,
|
|
20817
|
+
originalPriceOpen: data.originalPriceOpen,
|
|
20483
20818
|
partialExecuted: data.partialExecuted,
|
|
20484
20819
|
note: data.note,
|
|
20485
20820
|
pendingAt: data.pendingAt,
|
|
@@ -21579,6 +21914,8 @@ let ReportStorage$2 = class ReportStorage {
|
|
|
21579
21914
|
priceStopLoss: data.priceStopLoss,
|
|
21580
21915
|
originalPriceTakeProfit: data.originalPriceTakeProfit,
|
|
21581
21916
|
originalPriceStopLoss: data.originalPriceStopLoss,
|
|
21917
|
+
totalEntries: data.totalEntries,
|
|
21918
|
+
originalPriceOpen: data.originalPriceOpen,
|
|
21582
21919
|
partialExecuted: data.partialExecuted,
|
|
21583
21920
|
note: data.note,
|
|
21584
21921
|
pendingAt: data.pendingAt,
|
|
@@ -23582,6 +23919,8 @@ class ScheduleReportService {
|
|
|
23582
23919
|
priceStopLoss: data.signal?.priceStopLoss,
|
|
23583
23920
|
originalPriceTakeProfit: data.signal?.originalPriceTakeProfit,
|
|
23584
23921
|
originalPriceStopLoss: data.signal?.originalPriceStopLoss,
|
|
23922
|
+
totalEntries: data.signal?.totalEntries,
|
|
23923
|
+
originalPriceOpen: data.signal?.originalPriceOpen,
|
|
23585
23924
|
partialExecuted: data.signal?.partialExecuted,
|
|
23586
23925
|
pendingAt: data.signal?.pendingAt,
|
|
23587
23926
|
minuteEstimatedTime: data.signal?.minuteEstimatedTime,
|
|
@@ -23626,6 +23965,8 @@ class ScheduleReportService {
|
|
|
23626
23965
|
priceStopLoss: data.signal?.priceStopLoss,
|
|
23627
23966
|
originalPriceTakeProfit: data.signal?.originalPriceTakeProfit,
|
|
23628
23967
|
originalPriceStopLoss: data.signal?.originalPriceStopLoss,
|
|
23968
|
+
totalEntries: data.signal?.totalEntries,
|
|
23969
|
+
originalPriceOpen: data.signal?.originalPriceOpen,
|
|
23629
23970
|
partialExecuted: data.signal?.partialExecuted,
|
|
23630
23971
|
scheduledAt: data.signal?.scheduledAt,
|
|
23631
23972
|
pendingAt: data.signal?.pendingAt,
|
|
@@ -24103,6 +24444,8 @@ class PartialReportService {
|
|
|
24103
24444
|
priceStopLoss: data.data.priceStopLoss,
|
|
24104
24445
|
originalPriceTakeProfit: data.data.originalPriceTakeProfit,
|
|
24105
24446
|
originalPriceStopLoss: data.data.originalPriceStopLoss,
|
|
24447
|
+
totalEntries: data.data.totalEntries,
|
|
24448
|
+
originalPriceOpen: data.data.originalPriceOpen,
|
|
24106
24449
|
partialExecuted: data.data.partialExecuted,
|
|
24107
24450
|
_partial: data.data._partial,
|
|
24108
24451
|
note: data.data.note,
|
|
@@ -24144,6 +24487,8 @@ class PartialReportService {
|
|
|
24144
24487
|
priceStopLoss: data.data.priceStopLoss,
|
|
24145
24488
|
originalPriceTakeProfit: data.data.originalPriceTakeProfit,
|
|
24146
24489
|
originalPriceStopLoss: data.data.originalPriceStopLoss,
|
|
24490
|
+
totalEntries: data.data.totalEntries,
|
|
24491
|
+
originalPriceOpen: data.data.originalPriceOpen,
|
|
24147
24492
|
partialExecuted: data.data.partialExecuted,
|
|
24148
24493
|
_partial: data.data._partial,
|
|
24149
24494
|
note: data.data.note,
|
|
@@ -24266,6 +24611,8 @@ class BreakevenReportService {
|
|
|
24266
24611
|
priceStopLoss: data.data.priceStopLoss,
|
|
24267
24612
|
originalPriceTakeProfit: data.data.originalPriceTakeProfit,
|
|
24268
24613
|
originalPriceStopLoss: data.data.originalPriceStopLoss,
|
|
24614
|
+
totalEntries: data.data.totalEntries,
|
|
24615
|
+
originalPriceOpen: data.data.originalPriceOpen,
|
|
24269
24616
|
partialExecuted: data.data.partialExecuted,
|
|
24270
24617
|
_partial: data.data._partial,
|
|
24271
24618
|
note: data.data.note,
|
|
@@ -24574,7 +24921,7 @@ class StrategyReportService {
|
|
|
24574
24921
|
* @param scheduledAt - Signal creation timestamp in milliseconds
|
|
24575
24922
|
* @param pendingAt - Pending timestamp in milliseconds
|
|
24576
24923
|
*/
|
|
24577
|
-
this.partialProfit = async (symbol, percentToClose, currentPrice, isBacktest, context, timestamp, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt) => {
|
|
24924
|
+
this.partialProfit = async (symbol, percentToClose, currentPrice, isBacktest, context, timestamp, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen) => {
|
|
24578
24925
|
this.loggerService.log("strategyReportService partialProfit", {
|
|
24579
24926
|
symbol,
|
|
24580
24927
|
percentToClose,
|
|
@@ -24606,6 +24953,8 @@ class StrategyReportService {
|
|
|
24606
24953
|
priceStopLoss,
|
|
24607
24954
|
originalPriceTakeProfit,
|
|
24608
24955
|
originalPriceStopLoss,
|
|
24956
|
+
originalPriceOpen,
|
|
24957
|
+
totalEntries,
|
|
24609
24958
|
scheduledAt,
|
|
24610
24959
|
pendingAt,
|
|
24611
24960
|
}, {
|
|
@@ -24635,7 +24984,7 @@ class StrategyReportService {
|
|
|
24635
24984
|
* @param scheduledAt - Signal creation timestamp in milliseconds
|
|
24636
24985
|
* @param pendingAt - Pending timestamp in milliseconds
|
|
24637
24986
|
*/
|
|
24638
|
-
this.partialLoss = async (symbol, percentToClose, currentPrice, isBacktest, context, timestamp, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt) => {
|
|
24987
|
+
this.partialLoss = async (symbol, percentToClose, currentPrice, isBacktest, context, timestamp, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen) => {
|
|
24639
24988
|
this.loggerService.log("strategyReportService partialLoss", {
|
|
24640
24989
|
symbol,
|
|
24641
24990
|
percentToClose,
|
|
@@ -24667,6 +25016,8 @@ class StrategyReportService {
|
|
|
24667
25016
|
priceStopLoss,
|
|
24668
25017
|
originalPriceTakeProfit,
|
|
24669
25018
|
originalPriceStopLoss,
|
|
25019
|
+
originalPriceOpen,
|
|
25020
|
+
totalEntries,
|
|
24670
25021
|
scheduledAt,
|
|
24671
25022
|
pendingAt,
|
|
24672
25023
|
}, {
|
|
@@ -24696,7 +25047,7 @@ class StrategyReportService {
|
|
|
24696
25047
|
* @param scheduledAt - Signal creation timestamp in milliseconds
|
|
24697
25048
|
* @param pendingAt - Pending timestamp in milliseconds
|
|
24698
25049
|
*/
|
|
24699
|
-
this.trailingStop = async (symbol, percentShift, currentPrice, isBacktest, context, timestamp, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt) => {
|
|
25050
|
+
this.trailingStop = async (symbol, percentShift, currentPrice, isBacktest, context, timestamp, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen) => {
|
|
24700
25051
|
this.loggerService.log("strategyReportService trailingStop", {
|
|
24701
25052
|
symbol,
|
|
24702
25053
|
percentShift,
|
|
@@ -24728,6 +25079,8 @@ class StrategyReportService {
|
|
|
24728
25079
|
priceStopLoss,
|
|
24729
25080
|
originalPriceTakeProfit,
|
|
24730
25081
|
originalPriceStopLoss,
|
|
25082
|
+
originalPriceOpen,
|
|
25083
|
+
totalEntries,
|
|
24731
25084
|
scheduledAt,
|
|
24732
25085
|
pendingAt,
|
|
24733
25086
|
}, {
|
|
@@ -24757,7 +25110,7 @@ class StrategyReportService {
|
|
|
24757
25110
|
* @param scheduledAt - Signal creation timestamp in milliseconds
|
|
24758
25111
|
* @param pendingAt - Pending timestamp in milliseconds
|
|
24759
25112
|
*/
|
|
24760
|
-
this.trailingTake = async (symbol, percentShift, currentPrice, isBacktest, context, timestamp, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt) => {
|
|
25113
|
+
this.trailingTake = async (symbol, percentShift, currentPrice, isBacktest, context, timestamp, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen) => {
|
|
24761
25114
|
this.loggerService.log("strategyReportService trailingTake", {
|
|
24762
25115
|
symbol,
|
|
24763
25116
|
percentShift,
|
|
@@ -24789,6 +25142,8 @@ class StrategyReportService {
|
|
|
24789
25142
|
priceStopLoss,
|
|
24790
25143
|
originalPriceTakeProfit,
|
|
24791
25144
|
originalPriceStopLoss,
|
|
25145
|
+
originalPriceOpen,
|
|
25146
|
+
totalEntries,
|
|
24792
25147
|
scheduledAt,
|
|
24793
25148
|
pendingAt,
|
|
24794
25149
|
}, {
|
|
@@ -24817,7 +25172,7 @@ class StrategyReportService {
|
|
|
24817
25172
|
* @param scheduledAt - Signal creation timestamp in milliseconds
|
|
24818
25173
|
* @param pendingAt - Pending timestamp in milliseconds
|
|
24819
25174
|
*/
|
|
24820
|
-
this.breakeven = async (symbol, currentPrice, isBacktest, context, timestamp, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt) => {
|
|
25175
|
+
this.breakeven = async (symbol, currentPrice, isBacktest, context, timestamp, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen) => {
|
|
24821
25176
|
this.loggerService.log("strategyReportService breakeven", {
|
|
24822
25177
|
symbol,
|
|
24823
25178
|
currentPrice,
|
|
@@ -24847,6 +25202,8 @@ class StrategyReportService {
|
|
|
24847
25202
|
priceStopLoss,
|
|
24848
25203
|
originalPriceTakeProfit,
|
|
24849
25204
|
originalPriceStopLoss,
|
|
25205
|
+
originalPriceOpen,
|
|
25206
|
+
totalEntries,
|
|
24850
25207
|
scheduledAt,
|
|
24851
25208
|
pendingAt,
|
|
24852
25209
|
}, {
|
|
@@ -24876,7 +25233,7 @@ class StrategyReportService {
|
|
|
24876
25233
|
* @param pendingAt - Pending timestamp in milliseconds
|
|
24877
25234
|
* @param activateId - Optional identifier for the activation reason
|
|
24878
25235
|
*/
|
|
24879
|
-
this.activateScheduled = async (symbol, currentPrice, isBacktest, context, timestamp, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, activateId) => {
|
|
25236
|
+
this.activateScheduled = async (symbol, currentPrice, isBacktest, context, timestamp, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen, activateId) => {
|
|
24880
25237
|
this.loggerService.log("strategyReportService activateScheduled", {
|
|
24881
25238
|
symbol,
|
|
24882
25239
|
currentPrice,
|
|
@@ -24908,6 +25265,8 @@ class StrategyReportService {
|
|
|
24908
25265
|
priceStopLoss,
|
|
24909
25266
|
originalPriceTakeProfit,
|
|
24910
25267
|
originalPriceStopLoss,
|
|
25268
|
+
originalPriceOpen,
|
|
25269
|
+
totalEntries,
|
|
24911
25270
|
scheduledAt,
|
|
24912
25271
|
pendingAt,
|
|
24913
25272
|
}, {
|
|
@@ -24919,6 +25278,71 @@ class StrategyReportService {
|
|
|
24919
25278
|
walkerName: "",
|
|
24920
25279
|
});
|
|
24921
25280
|
};
|
|
25281
|
+
/**
|
|
25282
|
+
* Logs an average-buy (DCA) event when a new averaging entry is added to an open position.
|
|
25283
|
+
*
|
|
25284
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
25285
|
+
* @param currentPrice - Price at which the new averaging entry was executed
|
|
25286
|
+
* @param effectivePriceOpen - Averaged entry price after this addition
|
|
25287
|
+
* @param totalEntries - Total number of DCA entries after this addition
|
|
25288
|
+
* @param isBacktest - Whether this is a backtest or live trading event
|
|
25289
|
+
* @param context - Strategy context with strategyName, exchangeName, frameName
|
|
25290
|
+
* @param timestamp - Timestamp from StrategyCommitContract (execution context time)
|
|
25291
|
+
* @param position - Trade direction: "long" or "short"
|
|
25292
|
+
* @param priceOpen - Original entry price (unchanged by averaging)
|
|
25293
|
+
* @param priceTakeProfit - Effective take profit price
|
|
25294
|
+
* @param priceStopLoss - Effective stop loss price
|
|
25295
|
+
* @param originalPriceTakeProfit - Original take profit before trailing
|
|
25296
|
+
* @param originalPriceStopLoss - Original stop loss before trailing
|
|
25297
|
+
* @param scheduledAt - Signal creation timestamp in milliseconds
|
|
25298
|
+
* @param pendingAt - Pending timestamp in milliseconds
|
|
25299
|
+
*/
|
|
25300
|
+
this.averageBuy = async (symbol, currentPrice, effectivePriceOpen, totalEntries, isBacktest, context, timestamp, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, originalPriceOpen) => {
|
|
25301
|
+
this.loggerService.log("strategyReportService averageBuy", {
|
|
25302
|
+
symbol,
|
|
25303
|
+
currentPrice,
|
|
25304
|
+
effectivePriceOpen,
|
|
25305
|
+
totalEntries,
|
|
25306
|
+
isBacktest,
|
|
25307
|
+
});
|
|
25308
|
+
if (!this.subscribe.hasValue()) {
|
|
25309
|
+
return;
|
|
25310
|
+
}
|
|
25311
|
+
const pendingRow = await this.strategyCoreService.getPendingSignal(isBacktest, symbol, {
|
|
25312
|
+
exchangeName: context.exchangeName,
|
|
25313
|
+
strategyName: context.strategyName,
|
|
25314
|
+
frameName: context.frameName,
|
|
25315
|
+
});
|
|
25316
|
+
if (!pendingRow) {
|
|
25317
|
+
return;
|
|
25318
|
+
}
|
|
25319
|
+
const createdAt = new Date(timestamp).toISOString();
|
|
25320
|
+
await Report.writeData("strategy", {
|
|
25321
|
+
action: "average-buy",
|
|
25322
|
+
currentPrice,
|
|
25323
|
+
effectivePriceOpen,
|
|
25324
|
+
totalEntries,
|
|
25325
|
+
symbol,
|
|
25326
|
+
timestamp,
|
|
25327
|
+
createdAt,
|
|
25328
|
+
position,
|
|
25329
|
+
priceOpen,
|
|
25330
|
+
priceTakeProfit,
|
|
25331
|
+
priceStopLoss,
|
|
25332
|
+
originalPriceTakeProfit,
|
|
25333
|
+
originalPriceStopLoss,
|
|
25334
|
+
originalPriceOpen,
|
|
25335
|
+
scheduledAt,
|
|
25336
|
+
pendingAt,
|
|
25337
|
+
}, {
|
|
25338
|
+
signalId: pendingRow.id,
|
|
25339
|
+
exchangeName: context.exchangeName,
|
|
25340
|
+
frameName: context.frameName,
|
|
25341
|
+
strategyName: context.strategyName,
|
|
25342
|
+
symbol,
|
|
25343
|
+
walkerName: "",
|
|
25344
|
+
});
|
|
25345
|
+
};
|
|
24922
25346
|
/**
|
|
24923
25347
|
* Initializes the service for event logging.
|
|
24924
25348
|
*
|
|
@@ -24949,43 +25373,50 @@ class StrategyReportService {
|
|
|
24949
25373
|
exchangeName: event.exchangeName,
|
|
24950
25374
|
frameName: event.frameName,
|
|
24951
25375
|
strategyName: event.strategyName,
|
|
24952
|
-
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt));
|
|
25376
|
+
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.totalEntries, event.originalPriceOpen));
|
|
24953
25377
|
const unPartialLoss = strategyCommitSubject
|
|
24954
25378
|
.filter(({ action }) => action === "partial-loss")
|
|
24955
25379
|
.connect(async (event) => await this.partialLoss(event.symbol, event.percentToClose, event.currentPrice, event.backtest, {
|
|
24956
25380
|
exchangeName: event.exchangeName,
|
|
24957
25381
|
frameName: event.frameName,
|
|
24958
25382
|
strategyName: event.strategyName,
|
|
24959
|
-
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt));
|
|
25383
|
+
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.totalEntries, event.originalPriceOpen));
|
|
24960
25384
|
const unTrailingStop = strategyCommitSubject
|
|
24961
25385
|
.filter(({ action }) => action === "trailing-stop")
|
|
24962
25386
|
.connect(async (event) => await this.trailingStop(event.symbol, event.percentShift, event.currentPrice, event.backtest, {
|
|
24963
25387
|
exchangeName: event.exchangeName,
|
|
24964
25388
|
frameName: event.frameName,
|
|
24965
25389
|
strategyName: event.strategyName,
|
|
24966
|
-
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt));
|
|
25390
|
+
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.totalEntries, event.originalPriceOpen));
|
|
24967
25391
|
const unTrailingTake = strategyCommitSubject
|
|
24968
25392
|
.filter(({ action }) => action === "trailing-take")
|
|
24969
25393
|
.connect(async (event) => await this.trailingTake(event.symbol, event.percentShift, event.currentPrice, event.backtest, {
|
|
24970
25394
|
exchangeName: event.exchangeName,
|
|
24971
25395
|
frameName: event.frameName,
|
|
24972
25396
|
strategyName: event.strategyName,
|
|
24973
|
-
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt));
|
|
25397
|
+
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.totalEntries, event.originalPriceOpen));
|
|
24974
25398
|
const unBreakeven = strategyCommitSubject
|
|
24975
25399
|
.filter(({ action }) => action === "breakeven")
|
|
24976
25400
|
.connect(async (event) => await this.breakeven(event.symbol, event.currentPrice, event.backtest, {
|
|
24977
25401
|
exchangeName: event.exchangeName,
|
|
24978
25402
|
frameName: event.frameName,
|
|
24979
25403
|
strategyName: event.strategyName,
|
|
24980
|
-
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt));
|
|
25404
|
+
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.totalEntries, event.originalPriceOpen));
|
|
24981
25405
|
const unActivateScheduled = strategyCommitSubject
|
|
24982
25406
|
.filter(({ action }) => action === "activate-scheduled")
|
|
24983
25407
|
.connect(async (event) => await this.activateScheduled(event.symbol, event.currentPrice, event.backtest, {
|
|
24984
25408
|
exchangeName: event.exchangeName,
|
|
24985
25409
|
frameName: event.frameName,
|
|
24986
25410
|
strategyName: event.strategyName,
|
|
24987
|
-
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.activateId));
|
|
24988
|
-
const
|
|
25411
|
+
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.totalEntries, event.originalPriceOpen, event.activateId));
|
|
25412
|
+
const unAverageBuy = strategyCommitSubject
|
|
25413
|
+
.filter(({ action }) => action === "average-buy")
|
|
25414
|
+
.connect(async (event) => await this.averageBuy(event.symbol, event.currentPrice, event.effectivePriceOpen, event.totalEntries, event.backtest, {
|
|
25415
|
+
exchangeName: event.exchangeName,
|
|
25416
|
+
frameName: event.frameName,
|
|
25417
|
+
strategyName: event.strategyName,
|
|
25418
|
+
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.originalPriceOpen));
|
|
25419
|
+
const disposeFn = compose(() => unCancelSchedule(), () => unClosePending(), () => unPartialProfit(), () => unPartialLoss(), () => unTrailingStop(), () => unTrailingTake(), () => unBreakeven(), () => unActivateScheduled(), () => unAverageBuy());
|
|
24989
25420
|
return () => {
|
|
24990
25421
|
disposeFn();
|
|
24991
25422
|
this.subscribe.clear();
|
|
@@ -25117,6 +25548,7 @@ class ReportStorage {
|
|
|
25117
25548
|
trailingTakeCount: 0,
|
|
25118
25549
|
breakevenCount: 0,
|
|
25119
25550
|
activateScheduledCount: 0,
|
|
25551
|
+
averageBuyCount: 0,
|
|
25120
25552
|
};
|
|
25121
25553
|
}
|
|
25122
25554
|
return {
|
|
@@ -25130,6 +25562,7 @@ class ReportStorage {
|
|
|
25130
25562
|
trailingTakeCount: this._eventList.filter(e => e.action === "trailing-take").length,
|
|
25131
25563
|
breakevenCount: this._eventList.filter(e => e.action === "breakeven").length,
|
|
25132
25564
|
activateScheduledCount: this._eventList.filter(e => e.action === "activate-scheduled").length,
|
|
25565
|
+
averageBuyCount: this._eventList.filter(e => e.action === "average-buy").length,
|
|
25133
25566
|
};
|
|
25134
25567
|
}
|
|
25135
25568
|
/**
|
|
@@ -25179,6 +25612,7 @@ class ReportStorage {
|
|
|
25179
25612
|
`- Trailing take: ${stats.trailingTakeCount}`,
|
|
25180
25613
|
`- Breakeven: ${stats.breakevenCount}`,
|
|
25181
25614
|
`- Activate scheduled: ${stats.activateScheduledCount}`,
|
|
25615
|
+
`- Average buy: ${stats.averageBuyCount}`,
|
|
25182
25616
|
].join("\n");
|
|
25183
25617
|
}
|
|
25184
25618
|
/**
|
|
@@ -25366,7 +25800,7 @@ class StrategyMarkdownService {
|
|
|
25366
25800
|
* @param scheduledAt - Signal creation timestamp in milliseconds
|
|
25367
25801
|
* @param pendingAt - Pending timestamp in milliseconds
|
|
25368
25802
|
*/
|
|
25369
|
-
this.partialProfit = async (symbol, percentToClose, currentPrice, isBacktest, context, timestamp, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt) => {
|
|
25803
|
+
this.partialProfit = async (symbol, percentToClose, currentPrice, isBacktest, context, timestamp, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen) => {
|
|
25370
25804
|
this.loggerService.log("strategyMarkdownService partialProfit", {
|
|
25371
25805
|
symbol,
|
|
25372
25806
|
percentToClose,
|
|
@@ -25404,6 +25838,8 @@ class StrategyMarkdownService {
|
|
|
25404
25838
|
priceStopLoss,
|
|
25405
25839
|
originalPriceTakeProfit,
|
|
25406
25840
|
originalPriceStopLoss,
|
|
25841
|
+
originalPriceOpen,
|
|
25842
|
+
totalEntries,
|
|
25407
25843
|
scheduledAt,
|
|
25408
25844
|
pendingAt,
|
|
25409
25845
|
});
|
|
@@ -25426,7 +25862,7 @@ class StrategyMarkdownService {
|
|
|
25426
25862
|
* @param scheduledAt - Signal creation timestamp in milliseconds
|
|
25427
25863
|
* @param pendingAt - Pending timestamp in milliseconds
|
|
25428
25864
|
*/
|
|
25429
|
-
this.partialLoss = async (symbol, percentToClose, currentPrice, isBacktest, context, timestamp, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt) => {
|
|
25865
|
+
this.partialLoss = async (symbol, percentToClose, currentPrice, isBacktest, context, timestamp, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen) => {
|
|
25430
25866
|
this.loggerService.log("strategyMarkdownService partialLoss", {
|
|
25431
25867
|
symbol,
|
|
25432
25868
|
percentToClose,
|
|
@@ -25464,6 +25900,8 @@ class StrategyMarkdownService {
|
|
|
25464
25900
|
priceStopLoss,
|
|
25465
25901
|
originalPriceTakeProfit,
|
|
25466
25902
|
originalPriceStopLoss,
|
|
25903
|
+
originalPriceOpen,
|
|
25904
|
+
totalEntries,
|
|
25467
25905
|
scheduledAt,
|
|
25468
25906
|
pendingAt,
|
|
25469
25907
|
});
|
|
@@ -25486,7 +25924,7 @@ class StrategyMarkdownService {
|
|
|
25486
25924
|
* @param scheduledAt - Signal creation timestamp in milliseconds
|
|
25487
25925
|
* @param pendingAt - Pending timestamp in milliseconds
|
|
25488
25926
|
*/
|
|
25489
|
-
this.trailingStop = async (symbol, percentShift, currentPrice, isBacktest, context, timestamp, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt) => {
|
|
25927
|
+
this.trailingStop = async (symbol, percentShift, currentPrice, isBacktest, context, timestamp, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen) => {
|
|
25490
25928
|
this.loggerService.log("strategyMarkdownService trailingStop", {
|
|
25491
25929
|
symbol,
|
|
25492
25930
|
percentShift,
|
|
@@ -25524,6 +25962,8 @@ class StrategyMarkdownService {
|
|
|
25524
25962
|
priceStopLoss,
|
|
25525
25963
|
originalPriceTakeProfit,
|
|
25526
25964
|
originalPriceStopLoss,
|
|
25965
|
+
originalPriceOpen,
|
|
25966
|
+
totalEntries,
|
|
25527
25967
|
scheduledAt,
|
|
25528
25968
|
pendingAt,
|
|
25529
25969
|
});
|
|
@@ -25546,7 +25986,7 @@ class StrategyMarkdownService {
|
|
|
25546
25986
|
* @param scheduledAt - Signal creation timestamp in milliseconds
|
|
25547
25987
|
* @param pendingAt - Pending timestamp in milliseconds
|
|
25548
25988
|
*/
|
|
25549
|
-
this.trailingTake = async (symbol, percentShift, currentPrice, isBacktest, context, timestamp, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt) => {
|
|
25989
|
+
this.trailingTake = async (symbol, percentShift, currentPrice, isBacktest, context, timestamp, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen) => {
|
|
25550
25990
|
this.loggerService.log("strategyMarkdownService trailingTake", {
|
|
25551
25991
|
symbol,
|
|
25552
25992
|
percentShift,
|
|
@@ -25584,6 +26024,8 @@ class StrategyMarkdownService {
|
|
|
25584
26024
|
priceStopLoss,
|
|
25585
26025
|
originalPriceTakeProfit,
|
|
25586
26026
|
originalPriceStopLoss,
|
|
26027
|
+
originalPriceOpen,
|
|
26028
|
+
totalEntries,
|
|
25587
26029
|
scheduledAt,
|
|
25588
26030
|
pendingAt,
|
|
25589
26031
|
});
|
|
@@ -25605,7 +26047,7 @@ class StrategyMarkdownService {
|
|
|
25605
26047
|
* @param scheduledAt - Signal creation timestamp in milliseconds
|
|
25606
26048
|
* @param pendingAt - Pending timestamp in milliseconds
|
|
25607
26049
|
*/
|
|
25608
|
-
this.breakeven = async (symbol, currentPrice, isBacktest, context, timestamp, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt) => {
|
|
26050
|
+
this.breakeven = async (symbol, currentPrice, isBacktest, context, timestamp, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen) => {
|
|
25609
26051
|
this.loggerService.log("strategyMarkdownService breakeven", {
|
|
25610
26052
|
symbol,
|
|
25611
26053
|
currentPrice,
|
|
@@ -25641,6 +26083,8 @@ class StrategyMarkdownService {
|
|
|
25641
26083
|
priceStopLoss,
|
|
25642
26084
|
originalPriceTakeProfit,
|
|
25643
26085
|
originalPriceStopLoss,
|
|
26086
|
+
originalPriceOpen,
|
|
26087
|
+
totalEntries,
|
|
25644
26088
|
scheduledAt,
|
|
25645
26089
|
pendingAt,
|
|
25646
26090
|
});
|
|
@@ -25663,7 +26107,7 @@ class StrategyMarkdownService {
|
|
|
25663
26107
|
* @param pendingAt - Pending timestamp in milliseconds
|
|
25664
26108
|
* @param activateId - Optional identifier for the activation reason
|
|
25665
26109
|
*/
|
|
25666
|
-
this.activateScheduled = async (symbol, currentPrice, isBacktest, context, timestamp, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, activateId) => {
|
|
26110
|
+
this.activateScheduled = async (symbol, currentPrice, isBacktest, context, timestamp, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen, activateId) => {
|
|
25667
26111
|
this.loggerService.log("strategyMarkdownService activateScheduled", {
|
|
25668
26112
|
symbol,
|
|
25669
26113
|
currentPrice,
|
|
@@ -25701,6 +26145,72 @@ class StrategyMarkdownService {
|
|
|
25701
26145
|
priceStopLoss,
|
|
25702
26146
|
originalPriceTakeProfit,
|
|
25703
26147
|
originalPriceStopLoss,
|
|
26148
|
+
originalPriceOpen,
|
|
26149
|
+
totalEntries,
|
|
26150
|
+
scheduledAt,
|
|
26151
|
+
pendingAt,
|
|
26152
|
+
});
|
|
26153
|
+
};
|
|
26154
|
+
/**
|
|
26155
|
+
* Records an average-buy (DCA) event when a new averaging entry is added to an open position.
|
|
26156
|
+
*
|
|
26157
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
26158
|
+
* @param currentPrice - Price at which the new averaging entry was executed
|
|
26159
|
+
* @param effectivePriceOpen - Averaged entry price after this addition
|
|
26160
|
+
* @param totalEntries - Total number of DCA entries after this addition
|
|
26161
|
+
* @param isBacktest - Whether this is a backtest or live trading event
|
|
26162
|
+
* @param context - Strategy context with strategyName, exchangeName, frameName
|
|
26163
|
+
* @param timestamp - Timestamp from StrategyCommitContract (execution context time)
|
|
26164
|
+
* @param position - Trade direction: "long" or "short"
|
|
26165
|
+
* @param priceOpen - Original entry price (unchanged by averaging)
|
|
26166
|
+
* @param priceTakeProfit - Effective take profit price
|
|
26167
|
+
* @param priceStopLoss - Effective stop loss price
|
|
26168
|
+
* @param originalPriceTakeProfit - Original take profit before trailing
|
|
26169
|
+
* @param originalPriceStopLoss - Original stop loss before trailing
|
|
26170
|
+
* @param scheduledAt - Signal creation timestamp in milliseconds
|
|
26171
|
+
* @param pendingAt - Pending timestamp in milliseconds
|
|
26172
|
+
*/
|
|
26173
|
+
this.averageBuy = async (symbol, currentPrice, effectivePriceOpen, totalEntries, isBacktest, context, timestamp, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, originalPriceOpen) => {
|
|
26174
|
+
this.loggerService.log("strategyMarkdownService averageBuy", {
|
|
26175
|
+
symbol,
|
|
26176
|
+
currentPrice,
|
|
26177
|
+
effectivePriceOpen,
|
|
26178
|
+
totalEntries,
|
|
26179
|
+
isBacktest,
|
|
26180
|
+
});
|
|
26181
|
+
if (!this.subscribe.hasValue()) {
|
|
26182
|
+
return;
|
|
26183
|
+
}
|
|
26184
|
+
const pendingRow = await this.strategyCoreService.getPendingSignal(isBacktest, symbol, {
|
|
26185
|
+
exchangeName: context.exchangeName,
|
|
26186
|
+
strategyName: context.strategyName,
|
|
26187
|
+
frameName: context.frameName,
|
|
26188
|
+
});
|
|
26189
|
+
if (!pendingRow) {
|
|
26190
|
+
return;
|
|
26191
|
+
}
|
|
26192
|
+
const createdAt = new Date(timestamp).toISOString();
|
|
26193
|
+
const storage = this.getStorage(symbol, context.strategyName, context.exchangeName, context.frameName, isBacktest);
|
|
26194
|
+
storage.addEvent({
|
|
26195
|
+
timestamp,
|
|
26196
|
+
symbol,
|
|
26197
|
+
strategyName: context.strategyName,
|
|
26198
|
+
exchangeName: context.exchangeName,
|
|
26199
|
+
frameName: context.frameName,
|
|
26200
|
+
signalId: pendingRow.id,
|
|
26201
|
+
action: "average-buy",
|
|
26202
|
+
currentPrice,
|
|
26203
|
+
effectivePriceOpen,
|
|
26204
|
+
totalEntries,
|
|
26205
|
+
createdAt,
|
|
26206
|
+
backtest: isBacktest,
|
|
26207
|
+
position,
|
|
26208
|
+
priceOpen,
|
|
26209
|
+
priceTakeProfit,
|
|
26210
|
+
priceStopLoss,
|
|
26211
|
+
originalPriceTakeProfit,
|
|
26212
|
+
originalPriceStopLoss,
|
|
26213
|
+
originalPriceOpen,
|
|
25704
26214
|
scheduledAt,
|
|
25705
26215
|
pendingAt,
|
|
25706
26216
|
});
|
|
@@ -25849,43 +26359,50 @@ class StrategyMarkdownService {
|
|
|
25849
26359
|
exchangeName: event.exchangeName,
|
|
25850
26360
|
frameName: event.frameName,
|
|
25851
26361
|
strategyName: event.strategyName,
|
|
25852
|
-
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt));
|
|
26362
|
+
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.totalEntries, event.originalPriceOpen));
|
|
25853
26363
|
const unPartialLoss = strategyCommitSubject
|
|
25854
26364
|
.filter(({ action }) => action === "partial-loss")
|
|
25855
26365
|
.connect(async (event) => await this.partialLoss(event.symbol, event.percentToClose, event.currentPrice, event.backtest, {
|
|
25856
26366
|
exchangeName: event.exchangeName,
|
|
25857
26367
|
frameName: event.frameName,
|
|
25858
26368
|
strategyName: event.strategyName,
|
|
25859
|
-
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt));
|
|
26369
|
+
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.totalEntries, event.originalPriceOpen));
|
|
25860
26370
|
const unTrailingStop = strategyCommitSubject
|
|
25861
26371
|
.filter(({ action }) => action === "trailing-stop")
|
|
25862
26372
|
.connect(async (event) => await this.trailingStop(event.symbol, event.percentShift, event.currentPrice, event.backtest, {
|
|
25863
26373
|
exchangeName: event.exchangeName,
|
|
25864
26374
|
frameName: event.frameName,
|
|
25865
26375
|
strategyName: event.strategyName,
|
|
25866
|
-
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt));
|
|
26376
|
+
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.totalEntries, event.originalPriceOpen));
|
|
25867
26377
|
const unTrailingTake = strategyCommitSubject
|
|
25868
26378
|
.filter(({ action }) => action === "trailing-take")
|
|
25869
26379
|
.connect(async (event) => await this.trailingTake(event.symbol, event.percentShift, event.currentPrice, event.backtest, {
|
|
25870
26380
|
exchangeName: event.exchangeName,
|
|
25871
26381
|
frameName: event.frameName,
|
|
25872
26382
|
strategyName: event.strategyName,
|
|
25873
|
-
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt));
|
|
26383
|
+
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.totalEntries, event.originalPriceOpen));
|
|
25874
26384
|
const unBreakeven = strategyCommitSubject
|
|
25875
26385
|
.filter(({ action }) => action === "breakeven")
|
|
25876
26386
|
.connect(async (event) => await this.breakeven(event.symbol, event.currentPrice, event.backtest, {
|
|
25877
26387
|
exchangeName: event.exchangeName,
|
|
25878
26388
|
frameName: event.frameName,
|
|
25879
26389
|
strategyName: event.strategyName,
|
|
25880
|
-
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt));
|
|
26390
|
+
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.totalEntries, event.originalPriceOpen));
|
|
25881
26391
|
const unActivateScheduled = strategyCommitSubject
|
|
25882
26392
|
.filter(({ action }) => action === "activate-scheduled")
|
|
25883
26393
|
.connect(async (event) => await this.activateScheduled(event.symbol, event.currentPrice, event.backtest, {
|
|
25884
26394
|
exchangeName: event.exchangeName,
|
|
25885
26395
|
frameName: event.frameName,
|
|
25886
26396
|
strategyName: event.strategyName,
|
|
25887
|
-
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.activateId));
|
|
25888
|
-
const
|
|
26397
|
+
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.totalEntries, event.originalPriceOpen, event.activateId));
|
|
26398
|
+
const unAverageBuy = strategyCommitSubject
|
|
26399
|
+
.filter(({ action }) => action === "average-buy")
|
|
26400
|
+
.connect(async (event) => await this.averageBuy(event.symbol, event.currentPrice, event.effectivePriceOpen, event.totalEntries, event.backtest, {
|
|
26401
|
+
exchangeName: event.exchangeName,
|
|
26402
|
+
frameName: event.frameName,
|
|
26403
|
+
strategyName: event.strategyName,
|
|
26404
|
+
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.originalPriceOpen));
|
|
26405
|
+
const disposeFn = compose(() => unCancelSchedule(), () => unClosePending(), () => unPartialProfit(), () => unPartialLoss(), () => unTrailingStop(), () => unTrailingTake(), () => unBreakeven(), () => unActivateScheduled(), () => unAverageBuy());
|
|
25889
26406
|
return () => {
|
|
25890
26407
|
disposeFn();
|
|
25891
26408
|
this.subscribe.clear();
|
|
@@ -27691,6 +28208,7 @@ const TRAILING_STOP_METHOD_NAME = "strategy.commitTrailingStop";
|
|
|
27691
28208
|
const TRAILING_PROFIT_METHOD_NAME = "strategy.commitTrailingTake";
|
|
27692
28209
|
const BREAKEVEN_METHOD_NAME = "strategy.commitBreakeven";
|
|
27693
28210
|
const ACTIVATE_SCHEDULED_METHOD_NAME = "strategy.commitActivateScheduled";
|
|
28211
|
+
const AVERAGE_BUY_METHOD_NAME = "strategy.commitAverageBuy";
|
|
27694
28212
|
/**
|
|
27695
28213
|
* Cancels the scheduled signal without stopping the strategy.
|
|
27696
28214
|
*
|
|
@@ -28043,6 +28561,45 @@ async function commitActivateScheduled(symbol, activateId) {
|
|
|
28043
28561
|
const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
|
|
28044
28562
|
await bt.strategyCoreService.activateScheduled(isBacktest, symbol, { exchangeName, frameName, strategyName }, activateId);
|
|
28045
28563
|
}
|
|
28564
|
+
/**
|
|
28565
|
+
* Adds a new DCA entry to the active pending signal.
|
|
28566
|
+
*
|
|
28567
|
+
* Adds a new averaging entry at the current market price to the position's
|
|
28568
|
+
* entry history. Updates effectivePriceOpen (mean of all entries) and emits
|
|
28569
|
+
* an average-buy commit event.
|
|
28570
|
+
*
|
|
28571
|
+
* Automatically detects backtest/live mode from execution context.
|
|
28572
|
+
* Automatically fetches current price via getAveragePrice.
|
|
28573
|
+
*
|
|
28574
|
+
* @param symbol - Trading pair symbol
|
|
28575
|
+
* @returns Promise<boolean> - true if entry added, false if rejected
|
|
28576
|
+
*
|
|
28577
|
+
* @example
|
|
28578
|
+
* ```typescript
|
|
28579
|
+
* import { commitAverageBuy } from "backtest-kit";
|
|
28580
|
+
*
|
|
28581
|
+
* // Add DCA entry at current market price
|
|
28582
|
+
* const success = await commitAverageBuy("BTCUSDT");
|
|
28583
|
+
* if (success) {
|
|
28584
|
+
* console.log("DCA entry added");
|
|
28585
|
+
* }
|
|
28586
|
+
* ```
|
|
28587
|
+
*/
|
|
28588
|
+
async function commitAverageBuy(symbol) {
|
|
28589
|
+
bt.loggerService.info(AVERAGE_BUY_METHOD_NAME, {
|
|
28590
|
+
symbol,
|
|
28591
|
+
});
|
|
28592
|
+
if (!ExecutionContextService.hasContext()) {
|
|
28593
|
+
throw new Error("commitAverageBuy requires an execution context");
|
|
28594
|
+
}
|
|
28595
|
+
if (!MethodContextService.hasContext()) {
|
|
28596
|
+
throw new Error("commitAverageBuy requires a method context");
|
|
28597
|
+
}
|
|
28598
|
+
const currentPrice = await getAveragePrice(symbol);
|
|
28599
|
+
const { backtest: isBacktest } = bt.executionContextService.context;
|
|
28600
|
+
const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
|
|
28601
|
+
return await bt.strategyCoreService.averageBuy(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName });
|
|
28602
|
+
}
|
|
28046
28603
|
|
|
28047
28604
|
const STOP_STRATEGY_METHOD_NAME = "control.stopStrategy";
|
|
28048
28605
|
/**
|
|
@@ -30323,6 +30880,7 @@ const BACKTEST_METHOD_NAME_PARTIAL_LOSS = "BacktestUtils.commitPartialLoss";
|
|
|
30323
30880
|
const BACKTEST_METHOD_NAME_TRAILING_STOP = "BacktestUtils.commitTrailingStop";
|
|
30324
30881
|
const BACKTEST_METHOD_NAME_TRAILING_PROFIT = "BacktestUtils.commitTrailingTake";
|
|
30325
30882
|
const BACKTEST_METHOD_NAME_ACTIVATE_SCHEDULED = "Backtest.commitActivateScheduled";
|
|
30883
|
+
const BACKTEST_METHOD_NAME_AVERAGE_BUY = "Backtest.commitAverageBuy";
|
|
30326
30884
|
const BACKTEST_METHOD_NAME_GET_DATA = "BacktestUtils.getData";
|
|
30327
30885
|
/**
|
|
30328
30886
|
* Internal task function that runs backtest and handles completion.
|
|
@@ -31219,6 +31777,49 @@ class BacktestUtils {
|
|
|
31219
31777
|
}
|
|
31220
31778
|
await bt.strategyCoreService.activateScheduled(true, symbol, context, activateId);
|
|
31221
31779
|
};
|
|
31780
|
+
/**
|
|
31781
|
+
* Adds a new DCA entry to the active pending signal.
|
|
31782
|
+
*
|
|
31783
|
+
* Adds a new averaging entry at currentPrice to the position's entry history.
|
|
31784
|
+
* Updates effectivePriceOpen (mean of all entries) and emits average-buy commit event.
|
|
31785
|
+
*
|
|
31786
|
+
* @param symbol - Trading pair symbol
|
|
31787
|
+
* @param currentPrice - New entry price to add to the averaging history
|
|
31788
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
31789
|
+
* @returns Promise<boolean> - true if entry added, false if rejected
|
|
31790
|
+
*
|
|
31791
|
+
* @example
|
|
31792
|
+
* ```typescript
|
|
31793
|
+
* // Add DCA entry at current price
|
|
31794
|
+
* const success = await Backtest.commitAverageBuy("BTCUSDT", 42000, {
|
|
31795
|
+
* strategyName: "my-strategy",
|
|
31796
|
+
* exchangeName: "binance",
|
|
31797
|
+
* frameName: "1h"
|
|
31798
|
+
* });
|
|
31799
|
+
* if (success) {
|
|
31800
|
+
* console.log('DCA entry added');
|
|
31801
|
+
* }
|
|
31802
|
+
* ```
|
|
31803
|
+
*/
|
|
31804
|
+
this.commitAverageBuy = async (symbol, currentPrice, context) => {
|
|
31805
|
+
bt.loggerService.info(BACKTEST_METHOD_NAME_AVERAGE_BUY, {
|
|
31806
|
+
symbol,
|
|
31807
|
+
currentPrice,
|
|
31808
|
+
context,
|
|
31809
|
+
});
|
|
31810
|
+
bt.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_AVERAGE_BUY);
|
|
31811
|
+
bt.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_AVERAGE_BUY);
|
|
31812
|
+
{
|
|
31813
|
+
const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
|
|
31814
|
+
riskName &&
|
|
31815
|
+
bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_AVERAGE_BUY);
|
|
31816
|
+
riskList &&
|
|
31817
|
+
riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_AVERAGE_BUY));
|
|
31818
|
+
actions &&
|
|
31819
|
+
actions.forEach((actionName) => bt.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_AVERAGE_BUY));
|
|
31820
|
+
}
|
|
31821
|
+
return await bt.strategyCoreService.averageBuy(true, symbol, currentPrice, context);
|
|
31822
|
+
};
|
|
31222
31823
|
/**
|
|
31223
31824
|
* Gets statistical data from all closed signals for a symbol-strategy pair.
|
|
31224
31825
|
*
|
|
@@ -31395,6 +31996,7 @@ const LIVE_METHOD_NAME_PARTIAL_LOSS = "LiveUtils.commitPartialLoss";
|
|
|
31395
31996
|
const LIVE_METHOD_NAME_TRAILING_STOP = "LiveUtils.commitTrailingStop";
|
|
31396
31997
|
const LIVE_METHOD_NAME_TRAILING_PROFIT = "LiveUtils.commitTrailingTake";
|
|
31397
31998
|
const LIVE_METHOD_NAME_ACTIVATE_SCHEDULED = "Live.commitActivateScheduled";
|
|
31999
|
+
const LIVE_METHOD_NAME_AVERAGE_BUY = "Live.commitAverageBuy";
|
|
31398
32000
|
/**
|
|
31399
32001
|
* Internal task function that runs live trading and handles completion.
|
|
31400
32002
|
* Consumes live trading results and updates instance state flags.
|
|
@@ -32259,6 +32861,49 @@ class LiveUtils {
|
|
|
32259
32861
|
frameName: "",
|
|
32260
32862
|
}, activateId);
|
|
32261
32863
|
};
|
|
32864
|
+
/**
|
|
32865
|
+
* Adds a new DCA entry to the active pending signal.
|
|
32866
|
+
*
|
|
32867
|
+
* Adds a new averaging entry at currentPrice to the position's entry history.
|
|
32868
|
+
* Updates effectivePriceOpen (mean of all entries) and emits average-buy commit event.
|
|
32869
|
+
*
|
|
32870
|
+
* @param symbol - Trading pair symbol
|
|
32871
|
+
* @param currentPrice - New entry price to add to the averaging history
|
|
32872
|
+
* @param context - Execution context with strategyName and exchangeName
|
|
32873
|
+
* @returns Promise<boolean> - true if entry added, false if rejected
|
|
32874
|
+
*
|
|
32875
|
+
* @example
|
|
32876
|
+
* ```typescript
|
|
32877
|
+
* // Add DCA entry at current price
|
|
32878
|
+
* const success = await Live.commitAverageBuy("BTCUSDT", 42000, {
|
|
32879
|
+
* strategyName: "my-strategy",
|
|
32880
|
+
* exchangeName: "binance"
|
|
32881
|
+
* });
|
|
32882
|
+
* if (success) {
|
|
32883
|
+
* console.log('DCA entry added');
|
|
32884
|
+
* }
|
|
32885
|
+
* ```
|
|
32886
|
+
*/
|
|
32887
|
+
this.commitAverageBuy = async (symbol, currentPrice, context) => {
|
|
32888
|
+
bt.loggerService.info(LIVE_METHOD_NAME_AVERAGE_BUY, {
|
|
32889
|
+
symbol,
|
|
32890
|
+
currentPrice,
|
|
32891
|
+
context,
|
|
32892
|
+
});
|
|
32893
|
+
bt.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_AVERAGE_BUY);
|
|
32894
|
+
bt.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_AVERAGE_BUY);
|
|
32895
|
+
{
|
|
32896
|
+
const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
|
|
32897
|
+
riskName && bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_AVERAGE_BUY);
|
|
32898
|
+
riskList && riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_AVERAGE_BUY));
|
|
32899
|
+
actions && actions.forEach((actionName) => bt.actionValidationService.validate(actionName, LIVE_METHOD_NAME_AVERAGE_BUY));
|
|
32900
|
+
}
|
|
32901
|
+
return await bt.strategyCoreService.averageBuy(false, symbol, currentPrice, {
|
|
32902
|
+
strategyName: context.strategyName,
|
|
32903
|
+
exchangeName: context.exchangeName,
|
|
32904
|
+
frameName: "",
|
|
32905
|
+
});
|
|
32906
|
+
};
|
|
32262
32907
|
/**
|
|
32263
32908
|
* Gets statistical data from all live trading events for a symbol-strategy pair.
|
|
32264
32909
|
*
|
|
@@ -34922,6 +35567,8 @@ const CREATE_SIGNAL_NOTIFICATION_FN = (data) => {
|
|
|
34922
35567
|
priceStopLoss: data.signal.priceStopLoss,
|
|
34923
35568
|
originalPriceTakeProfit: data.signal.originalPriceTakeProfit,
|
|
34924
35569
|
originalPriceStopLoss: data.signal.originalPriceStopLoss,
|
|
35570
|
+
originalPriceOpen: data.signal.originalPriceOpen,
|
|
35571
|
+
totalEntries: data.signal.totalEntries,
|
|
34925
35572
|
note: data.signal.note,
|
|
34926
35573
|
scheduledAt: data.signal.scheduledAt,
|
|
34927
35574
|
pendingAt: data.signal.pendingAt,
|
|
@@ -34947,6 +35594,8 @@ const CREATE_SIGNAL_NOTIFICATION_FN = (data) => {
|
|
|
34947
35594
|
priceStopLoss: data.signal.priceStopLoss,
|
|
34948
35595
|
originalPriceTakeProfit: data.signal.originalPriceTakeProfit,
|
|
34949
35596
|
originalPriceStopLoss: data.signal.originalPriceStopLoss,
|
|
35597
|
+
originalPriceOpen: data.signal.originalPriceOpen,
|
|
35598
|
+
totalEntries: data.signal.totalEntries,
|
|
34950
35599
|
pnlPercentage: data.pnl.pnlPercentage,
|
|
34951
35600
|
closeReason: data.closeReason,
|
|
34952
35601
|
duration: durationMin,
|
|
@@ -34972,6 +35621,8 @@ const CREATE_SIGNAL_NOTIFICATION_FN = (data) => {
|
|
|
34972
35621
|
priceStopLoss: data.signal.priceStopLoss,
|
|
34973
35622
|
originalPriceTakeProfit: data.signal.originalPriceTakeProfit,
|
|
34974
35623
|
originalPriceStopLoss: data.signal.originalPriceStopLoss,
|
|
35624
|
+
originalPriceOpen: data.signal.originalPriceOpen,
|
|
35625
|
+
totalEntries: data.signal.totalEntries,
|
|
34975
35626
|
scheduledAt: data.signal.scheduledAt,
|
|
34976
35627
|
currentPrice: data.currentPrice,
|
|
34977
35628
|
createdAt: data.createdAt,
|
|
@@ -34995,6 +35646,8 @@ const CREATE_SIGNAL_NOTIFICATION_FN = (data) => {
|
|
|
34995
35646
|
priceStopLoss: data.signal.priceStopLoss,
|
|
34996
35647
|
originalPriceTakeProfit: data.signal.originalPriceTakeProfit,
|
|
34997
35648
|
originalPriceStopLoss: data.signal.originalPriceStopLoss,
|
|
35649
|
+
originalPriceOpen: data.signal.originalPriceOpen,
|
|
35650
|
+
totalEntries: data.signal.totalEntries,
|
|
34998
35651
|
cancelReason: data.reason,
|
|
34999
35652
|
cancelId: data.cancelId,
|
|
35000
35653
|
duration: durationMin,
|
|
@@ -35027,6 +35680,8 @@ const CREATE_PARTIAL_PROFIT_NOTIFICATION_FN = (data) => ({
|
|
|
35027
35680
|
priceStopLoss: data.data.priceStopLoss,
|
|
35028
35681
|
originalPriceTakeProfit: data.data.originalPriceTakeProfit,
|
|
35029
35682
|
originalPriceStopLoss: data.data.originalPriceStopLoss,
|
|
35683
|
+
originalPriceOpen: data.data.originalPriceOpen,
|
|
35684
|
+
totalEntries: data.data.totalEntries,
|
|
35030
35685
|
scheduledAt: data.data.scheduledAt,
|
|
35031
35686
|
pendingAt: data.data.pendingAt,
|
|
35032
35687
|
createdAt: data.timestamp,
|
|
@@ -35053,6 +35708,8 @@ const CREATE_PARTIAL_LOSS_NOTIFICATION_FN = (data) => ({
|
|
|
35053
35708
|
priceStopLoss: data.data.priceStopLoss,
|
|
35054
35709
|
originalPriceTakeProfit: data.data.originalPriceTakeProfit,
|
|
35055
35710
|
originalPriceStopLoss: data.data.originalPriceStopLoss,
|
|
35711
|
+
originalPriceOpen: data.data.originalPriceOpen,
|
|
35712
|
+
totalEntries: data.data.totalEntries,
|
|
35056
35713
|
scheduledAt: data.data.scheduledAt,
|
|
35057
35714
|
pendingAt: data.data.pendingAt,
|
|
35058
35715
|
createdAt: data.timestamp,
|
|
@@ -35078,6 +35735,8 @@ const CREATE_BREAKEVEN_NOTIFICATION_FN = (data) => ({
|
|
|
35078
35735
|
priceStopLoss: data.data.priceStopLoss,
|
|
35079
35736
|
originalPriceTakeProfit: data.data.originalPriceTakeProfit,
|
|
35080
35737
|
originalPriceStopLoss: data.data.originalPriceStopLoss,
|
|
35738
|
+
originalPriceOpen: data.data.originalPriceOpen,
|
|
35739
|
+
totalEntries: data.data.totalEntries,
|
|
35081
35740
|
scheduledAt: data.data.scheduledAt,
|
|
35082
35741
|
pendingAt: data.data.pendingAt,
|
|
35083
35742
|
createdAt: data.timestamp,
|
|
@@ -35108,6 +35767,8 @@ const CREATE_STRATEGY_COMMIT_NOTIFICATION_FN = (data) => {
|
|
|
35108
35767
|
priceStopLoss: data.priceStopLoss,
|
|
35109
35768
|
originalPriceTakeProfit: data.originalPriceTakeProfit,
|
|
35110
35769
|
originalPriceStopLoss: data.originalPriceStopLoss,
|
|
35770
|
+
originalPriceOpen: data.originalPriceOpen,
|
|
35771
|
+
totalEntries: data.totalEntries,
|
|
35111
35772
|
scheduledAt: data.scheduledAt,
|
|
35112
35773
|
pendingAt: data.pendingAt,
|
|
35113
35774
|
createdAt: data.timestamp,
|
|
@@ -35131,6 +35792,8 @@ const CREATE_STRATEGY_COMMIT_NOTIFICATION_FN = (data) => {
|
|
|
35131
35792
|
priceStopLoss: data.priceStopLoss,
|
|
35132
35793
|
originalPriceTakeProfit: data.originalPriceTakeProfit,
|
|
35133
35794
|
originalPriceStopLoss: data.originalPriceStopLoss,
|
|
35795
|
+
originalPriceOpen: data.originalPriceOpen,
|
|
35796
|
+
totalEntries: data.totalEntries,
|
|
35134
35797
|
scheduledAt: data.scheduledAt,
|
|
35135
35798
|
pendingAt: data.pendingAt,
|
|
35136
35799
|
createdAt: data.timestamp,
|
|
@@ -35153,6 +35816,8 @@ const CREATE_STRATEGY_COMMIT_NOTIFICATION_FN = (data) => {
|
|
|
35153
35816
|
priceStopLoss: data.priceStopLoss,
|
|
35154
35817
|
originalPriceTakeProfit: data.originalPriceTakeProfit,
|
|
35155
35818
|
originalPriceStopLoss: data.originalPriceStopLoss,
|
|
35819
|
+
originalPriceOpen: data.originalPriceOpen,
|
|
35820
|
+
totalEntries: data.totalEntries,
|
|
35156
35821
|
scheduledAt: data.scheduledAt,
|
|
35157
35822
|
pendingAt: data.pendingAt,
|
|
35158
35823
|
createdAt: data.timestamp,
|
|
@@ -35176,6 +35841,8 @@ const CREATE_STRATEGY_COMMIT_NOTIFICATION_FN = (data) => {
|
|
|
35176
35841
|
priceStopLoss: data.priceStopLoss,
|
|
35177
35842
|
originalPriceTakeProfit: data.originalPriceTakeProfit,
|
|
35178
35843
|
originalPriceStopLoss: data.originalPriceStopLoss,
|
|
35844
|
+
originalPriceOpen: data.originalPriceOpen,
|
|
35845
|
+
totalEntries: data.totalEntries,
|
|
35179
35846
|
scheduledAt: data.scheduledAt,
|
|
35180
35847
|
pendingAt: data.pendingAt,
|
|
35181
35848
|
createdAt: data.timestamp,
|
|
@@ -35199,6 +35866,8 @@ const CREATE_STRATEGY_COMMIT_NOTIFICATION_FN = (data) => {
|
|
|
35199
35866
|
priceStopLoss: data.priceStopLoss,
|
|
35200
35867
|
originalPriceTakeProfit: data.originalPriceTakeProfit,
|
|
35201
35868
|
originalPriceStopLoss: data.originalPriceStopLoss,
|
|
35869
|
+
originalPriceOpen: data.originalPriceOpen,
|
|
35870
|
+
totalEntries: data.totalEntries,
|
|
35202
35871
|
scheduledAt: data.scheduledAt,
|
|
35203
35872
|
pendingAt: data.pendingAt,
|
|
35204
35873
|
createdAt: data.timestamp,
|
|
@@ -35222,6 +35891,33 @@ const CREATE_STRATEGY_COMMIT_NOTIFICATION_FN = (data) => {
|
|
|
35222
35891
|
priceStopLoss: data.priceStopLoss,
|
|
35223
35892
|
originalPriceTakeProfit: data.originalPriceTakeProfit,
|
|
35224
35893
|
originalPriceStopLoss: data.originalPriceStopLoss,
|
|
35894
|
+
originalPriceOpen: data.originalPriceOpen,
|
|
35895
|
+
totalEntries: data.totalEntries,
|
|
35896
|
+
scheduledAt: data.scheduledAt,
|
|
35897
|
+
pendingAt: data.pendingAt,
|
|
35898
|
+
createdAt: data.timestamp,
|
|
35899
|
+
};
|
|
35900
|
+
}
|
|
35901
|
+
if (data.action === "average-buy") {
|
|
35902
|
+
return {
|
|
35903
|
+
type: "average_buy.commit",
|
|
35904
|
+
id: CREATE_KEY_FN$1(),
|
|
35905
|
+
timestamp: data.timestamp,
|
|
35906
|
+
backtest: data.backtest,
|
|
35907
|
+
symbol: data.symbol,
|
|
35908
|
+
strategyName: data.strategyName,
|
|
35909
|
+
exchangeName: data.exchangeName,
|
|
35910
|
+
signalId: data.signalId,
|
|
35911
|
+
currentPrice: data.currentPrice,
|
|
35912
|
+
effectivePriceOpen: data.effectivePriceOpen,
|
|
35913
|
+
totalEntries: data.totalEntries,
|
|
35914
|
+
position: data.position,
|
|
35915
|
+
priceOpen: data.priceOpen,
|
|
35916
|
+
priceTakeProfit: data.priceTakeProfit,
|
|
35917
|
+
priceStopLoss: data.priceStopLoss,
|
|
35918
|
+
originalPriceTakeProfit: data.originalPriceTakeProfit,
|
|
35919
|
+
originalPriceStopLoss: data.originalPriceStopLoss,
|
|
35920
|
+
originalPriceOpen: data.originalPriceOpen,
|
|
35225
35921
|
scheduledAt: data.scheduledAt,
|
|
35226
35922
|
pendingAt: data.pendingAt,
|
|
35227
35923
|
createdAt: data.timestamp,
|
|
@@ -37564,4 +38260,4 @@ const set = (object, path, value) => {
|
|
|
37564
38260
|
}
|
|
37565
38261
|
};
|
|
37566
38262
|
|
|
37567
|
-
export { ActionBase, Backtest, Breakeven, Cache, Constant, Exchange, ExecutionContextService, Heat, Live, Markdown, MarkdownFileBase, MarkdownFolderBase, MethodContextService, Notification, NotificationBacktest, NotificationLive, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistCandleAdapter, PersistNotificationAdapter, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PersistStorageAdapter, PositionSize, Report, ReportBase, Risk, Schedule, Storage, StorageBacktest, StorageLive, Strategy, Walker, addActionSchema, addExchangeSchema, addFrameSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, alignToInterval, checkCandles, commitActivateScheduled, commitBreakeven, commitCancelScheduled, commitClosePending, commitPartialLoss, commitPartialProfit, commitTrailingStop, commitTrailingTake, dumpMessages, emitters, formatPrice, formatQuantity, get, getActionSchema, getAveragePrice, getBacktestTimeframe, getCandles, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getExchangeSchema, getFrameSchema, getMode, getNextCandles, getOrderBook, getRawCandles, getRiskSchema, getSizingSchema, getStrategySchema, getSymbol, getWalkerSchema, hasTradeContext, backtest as lib, listExchangeSchema, listFrameSchema, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenBacktestProgress, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenStrategyCommit, listenStrategyCommitOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideActionSchema, overrideExchangeSchema, overrideFrameSchema, overrideRiskSchema, overrideSizingSchema, overrideStrategySchema, overrideWalkerSchema, parseArgs, roundTicks, set, setColumns, setConfig, setLogger, stopStrategy, validate, waitForCandle, warmCandles };
|
|
38263
|
+
export { ActionBase, Backtest, Breakeven, Cache, Constant, Exchange, ExecutionContextService, Heat, Live, Markdown, MarkdownFileBase, MarkdownFolderBase, MethodContextService, Notification, NotificationBacktest, NotificationLive, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistCandleAdapter, PersistNotificationAdapter, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PersistStorageAdapter, PositionSize, Report, ReportBase, Risk, Schedule, Storage, StorageBacktest, StorageLive, Strategy, Walker, addActionSchema, addExchangeSchema, addFrameSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, alignToInterval, checkCandles, commitActivateScheduled, commitAverageBuy, commitBreakeven, commitCancelScheduled, commitClosePending, commitPartialLoss, commitPartialProfit, commitTrailingStop, commitTrailingTake, dumpMessages, emitters, formatPrice, formatQuantity, get, getActionSchema, getAveragePrice, getBacktestTimeframe, getCandles, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getExchangeSchema, getFrameSchema, getMode, getNextCandles, getOrderBook, getRawCandles, getRiskSchema, getSizingSchema, getStrategySchema, getSymbol, getWalkerSchema, hasTradeContext, backtest as lib, listExchangeSchema, listFrameSchema, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenBacktestProgress, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenStrategyCommit, listenStrategyCommitOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideActionSchema, overrideExchangeSchema, overrideFrameSchema, overrideRiskSchema, overrideSizingSchema, overrideStrategySchema, overrideWalkerSchema, parseArgs, roundTicks, set, setColumns, setConfig, setLogger, stopStrategy, validate, waitForCandle, warmCandles };
|