backtest-kit 3.1.1 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/build/index.cjs +816 -139
- package/build/index.mjs +816 -140
- 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
|
*
|
|
@@ -2887,7 +2908,7 @@ class ExchangeConnectionService {
|
|
|
2887
2908
|
* - Calculates weighted PNL: Σ(percent_i × pnl_i) for each partial + (remaining% × final_pnl)
|
|
2888
2909
|
* - Each partial close has its own slippage
|
|
2889
2910
|
* - Open fee is charged once; close fees are proportional to each partial's size
|
|
2890
|
-
* - Total fees = CC_PERCENT_FEE (open) + CC_PERCENT_FEE ×
|
|
2911
|
+
* - Total fees = CC_PERCENT_FEE (open) + Σ CC_PERCENT_FEE × (partial% / 100) × (closeWithSlip / openWithSlip)
|
|
2891
2912
|
*
|
|
2892
2913
|
* Formula breakdown:
|
|
2893
2914
|
* 1. Apply slippage to open/close prices (worse execution)
|
|
@@ -2896,7 +2917,7 @@ class ExchangeConnectionService {
|
|
|
2896
2917
|
* 2. Calculate raw PNL percentage
|
|
2897
2918
|
* - LONG: ((closePrice - openPrice) / openPrice) * 100
|
|
2898
2919
|
* - SHORT: ((openPrice - closePrice) / openPrice) * 100
|
|
2899
|
-
* 3. Subtract total fees
|
|
2920
|
+
* 3. Subtract total fees: open fee + close fee adjusted for slippage-affected execution price
|
|
2900
2921
|
*
|
|
2901
2922
|
* @param signal - Closed signal with position details and optional partial history
|
|
2902
2923
|
* @param priceClose - Actual close price at final exit
|
|
@@ -2930,73 +2951,53 @@ 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;
|
|
2937
2958
|
// Open fee is paid once for the whole position
|
|
2938
2959
|
let totalFees = GLOBAL_CONFIG.CC_PERCENT_FEE;
|
|
2960
|
+
// priceOpenWithSlippage is the same for all partials — compute once
|
|
2961
|
+
const priceOpenWithSlippage = signal.position === "long"
|
|
2962
|
+
? priceOpen * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100)
|
|
2963
|
+
: priceOpen * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
|
|
2939
2964
|
// Calculate PNL for each partial close
|
|
2940
2965
|
for (const partial of signal._partial) {
|
|
2941
2966
|
const partialPercent = partial.percent;
|
|
2942
|
-
const
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
let priceCloseWithSlippage;
|
|
2946
|
-
if (signal.position === "long") {
|
|
2947
|
-
priceOpenWithSlippage = priceOpen * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
|
|
2948
|
-
priceCloseWithSlippage = partialPrice * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
|
|
2949
|
-
}
|
|
2950
|
-
else {
|
|
2951
|
-
priceOpenWithSlippage = priceOpen * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
|
|
2952
|
-
priceCloseWithSlippage = partialPrice * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
|
|
2953
|
-
}
|
|
2967
|
+
const priceCloseWithSlippage = signal.position === "long"
|
|
2968
|
+
? partial.price * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100)
|
|
2969
|
+
: partial.price * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
|
|
2954
2970
|
// Calculate PNL for this partial
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
}
|
|
2959
|
-
else {
|
|
2960
|
-
partialPnl = ((priceOpenWithSlippage - priceCloseWithSlippage) / priceOpenWithSlippage) * 100;
|
|
2961
|
-
}
|
|
2971
|
+
const partialPnl = signal.position === "long"
|
|
2972
|
+
? ((priceCloseWithSlippage - priceOpenWithSlippage) / priceOpenWithSlippage) * 100
|
|
2973
|
+
: ((priceOpenWithSlippage - priceCloseWithSlippage) / priceOpenWithSlippage) * 100;
|
|
2962
2974
|
// Weight by percentage of position closed
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
totalFees += GLOBAL_CONFIG.CC_PERCENT_FEE * (partialPercent / 100);
|
|
2975
|
+
totalWeightedPnl += (partialPercent / 100) * partialPnl;
|
|
2976
|
+
// Close fee is proportional to the size of this partial and adjusted for slippage
|
|
2977
|
+
totalFees += GLOBAL_CONFIG.CC_PERCENT_FEE * (partialPercent / 100) * (priceCloseWithSlippage / priceOpenWithSlippage);
|
|
2967
2978
|
}
|
|
2968
2979
|
// Calculate PNL for remaining position (if any)
|
|
2969
2980
|
// Compute totalClosed from _partial array
|
|
2970
2981
|
const totalClosed = signal._partial.reduce((sum, p) => sum + p.percent, 0);
|
|
2982
|
+
if (totalClosed > 100) {
|
|
2983
|
+
throw new Error(`Partial closes exceed 100%: ${totalClosed}% (signal id: ${signal.id})`);
|
|
2984
|
+
}
|
|
2971
2985
|
const remainingPercent = 100 - totalClosed;
|
|
2972
2986
|
if (remainingPercent > 0) {
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
if (signal.position === "long") {
|
|
2977
|
-
priceOpenWithSlippage = priceOpen * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
|
|
2978
|
-
priceCloseWithSlippage = priceClose * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
|
|
2979
|
-
}
|
|
2980
|
-
else {
|
|
2981
|
-
priceOpenWithSlippage = priceOpen * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
|
|
2982
|
-
priceCloseWithSlippage = priceClose * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
|
|
2983
|
-
}
|
|
2987
|
+
const priceCloseWithSlippage = signal.position === "long"
|
|
2988
|
+
? priceClose * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100)
|
|
2989
|
+
: priceClose * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
|
|
2984
2990
|
// Calculate PNL for remaining
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
}
|
|
2989
|
-
else {
|
|
2990
|
-
remainingPnl = ((priceOpenWithSlippage - priceCloseWithSlippage) / priceOpenWithSlippage) * 100;
|
|
2991
|
-
}
|
|
2991
|
+
const remainingPnl = signal.position === "long"
|
|
2992
|
+
? ((priceCloseWithSlippage - priceOpenWithSlippage) / priceOpenWithSlippage) * 100
|
|
2993
|
+
: ((priceOpenWithSlippage - priceCloseWithSlippage) / priceOpenWithSlippage) * 100;
|
|
2992
2994
|
// Weight by remaining percentage
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
totalFees += GLOBAL_CONFIG.CC_PERCENT_FEE * (remainingPercent / 100);
|
|
2995
|
+
totalWeightedPnl += (remainingPercent / 100) * remainingPnl;
|
|
2996
|
+
// Close fee is proportional to the remaining size and adjusted for slippage
|
|
2997
|
+
totalFees += GLOBAL_CONFIG.CC_PERCENT_FEE * (remainingPercent / 100) * (priceCloseWithSlippage / priceOpenWithSlippage);
|
|
2997
2998
|
}
|
|
2998
2999
|
// Subtract total fees from weighted PNL
|
|
2999
|
-
// totalFees = CC_PERCENT_FEE (open) + CC_PERCENT_FEE ×
|
|
3000
|
+
// totalFees = CC_PERCENT_FEE (open) + Σ CC_PERCENT_FEE × (partialPercent/100) × (closeWithSlip/openWithSlip)
|
|
3000
3001
|
const pnlPercentage = totalWeightedPnl - totalFees;
|
|
3001
3002
|
return {
|
|
3002
3003
|
pnlPercentage,
|
|
@@ -3017,8 +3018,8 @@ const toProfitLossDto = (signal, priceClose) => {
|
|
|
3017
3018
|
priceOpenWithSlippage = priceOpen * (1 - GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
|
|
3018
3019
|
priceCloseWithSlippage = priceClose * (1 + GLOBAL_CONFIG.CC_PERCENT_SLIPPAGE / 100);
|
|
3019
3020
|
}
|
|
3020
|
-
//
|
|
3021
|
-
const totalFee = GLOBAL_CONFIG.CC_PERCENT_FEE *
|
|
3021
|
+
// Открытие: комиссия от цены входа; закрытие: комиссия от фактической цены выхода (с учётом slippage)
|
|
3022
|
+
const totalFee = GLOBAL_CONFIG.CC_PERCENT_FEE * (1 + priceCloseWithSlippage / priceOpenWithSlippage);
|
|
3022
3023
|
let pnlPercentage;
|
|
3023
3024
|
if (signal.position === "long") {
|
|
3024
3025
|
// LONG: прибыль при росте цены
|
|
@@ -3204,6 +3205,7 @@ const PROCESS_COMMIT_QUEUE_FN = async (self, timestamp) => {
|
|
|
3204
3205
|
percentToClose: commit.percentToClose,
|
|
3205
3206
|
currentPrice: commit.currentPrice,
|
|
3206
3207
|
timestamp,
|
|
3208
|
+
totalEntries: publicSignal.totalEntries,
|
|
3207
3209
|
position: publicSignal.position,
|
|
3208
3210
|
priceOpen: publicSignal.priceOpen,
|
|
3209
3211
|
signalId: publicSignal.id,
|
|
@@ -3211,6 +3213,7 @@ const PROCESS_COMMIT_QUEUE_FN = async (self, timestamp) => {
|
|
|
3211
3213
|
priceStopLoss: publicSignal.priceStopLoss,
|
|
3212
3214
|
originalPriceTakeProfit: publicSignal.originalPriceTakeProfit,
|
|
3213
3215
|
originalPriceStopLoss: publicSignal.originalPriceStopLoss,
|
|
3216
|
+
originalPriceOpen: publicSignal.originalPriceOpen,
|
|
3214
3217
|
scheduledAt: publicSignal.scheduledAt,
|
|
3215
3218
|
pendingAt: publicSignal.pendingAt,
|
|
3216
3219
|
});
|
|
@@ -3227,6 +3230,7 @@ const PROCESS_COMMIT_QUEUE_FN = async (self, timestamp) => {
|
|
|
3227
3230
|
percentToClose: commit.percentToClose,
|
|
3228
3231
|
currentPrice: commit.currentPrice,
|
|
3229
3232
|
timestamp,
|
|
3233
|
+
totalEntries: publicSignal.totalEntries,
|
|
3230
3234
|
position: publicSignal.position,
|
|
3231
3235
|
priceOpen: publicSignal.priceOpen,
|
|
3232
3236
|
signalId: publicSignal.id,
|
|
@@ -3234,6 +3238,7 @@ const PROCESS_COMMIT_QUEUE_FN = async (self, timestamp) => {
|
|
|
3234
3238
|
priceStopLoss: publicSignal.priceStopLoss,
|
|
3235
3239
|
originalPriceTakeProfit: publicSignal.originalPriceTakeProfit,
|
|
3236
3240
|
originalPriceStopLoss: publicSignal.originalPriceStopLoss,
|
|
3241
|
+
originalPriceOpen: publicSignal.originalPriceOpen,
|
|
3237
3242
|
scheduledAt: publicSignal.scheduledAt,
|
|
3238
3243
|
pendingAt: publicSignal.pendingAt,
|
|
3239
3244
|
});
|
|
@@ -3249,6 +3254,7 @@ const PROCESS_COMMIT_QUEUE_FN = async (self, timestamp) => {
|
|
|
3249
3254
|
backtest: commit.backtest,
|
|
3250
3255
|
currentPrice: commit.currentPrice,
|
|
3251
3256
|
timestamp,
|
|
3257
|
+
totalEntries: publicSignal.totalEntries,
|
|
3252
3258
|
signalId: publicSignal.id,
|
|
3253
3259
|
position: publicSignal.position,
|
|
3254
3260
|
priceOpen: publicSignal.priceOpen,
|
|
@@ -3256,6 +3262,7 @@ const PROCESS_COMMIT_QUEUE_FN = async (self, timestamp) => {
|
|
|
3256
3262
|
priceStopLoss: publicSignal.priceStopLoss,
|
|
3257
3263
|
originalPriceTakeProfit: publicSignal.originalPriceTakeProfit,
|
|
3258
3264
|
originalPriceStopLoss: publicSignal.originalPriceStopLoss,
|
|
3265
|
+
originalPriceOpen: publicSignal.originalPriceOpen,
|
|
3259
3266
|
scheduledAt: publicSignal.scheduledAt,
|
|
3260
3267
|
pendingAt: publicSignal.pendingAt,
|
|
3261
3268
|
});
|
|
@@ -3272,6 +3279,7 @@ const PROCESS_COMMIT_QUEUE_FN = async (self, timestamp) => {
|
|
|
3272
3279
|
percentShift: commit.percentShift,
|
|
3273
3280
|
currentPrice: commit.currentPrice,
|
|
3274
3281
|
timestamp,
|
|
3282
|
+
totalEntries: publicSignal.totalEntries,
|
|
3275
3283
|
signalId: publicSignal.id,
|
|
3276
3284
|
position: publicSignal.position,
|
|
3277
3285
|
priceOpen: publicSignal.priceOpen,
|
|
@@ -3279,6 +3287,7 @@ const PROCESS_COMMIT_QUEUE_FN = async (self, timestamp) => {
|
|
|
3279
3287
|
priceStopLoss: publicSignal.priceStopLoss,
|
|
3280
3288
|
originalPriceTakeProfit: publicSignal.originalPriceTakeProfit,
|
|
3281
3289
|
originalPriceStopLoss: publicSignal.originalPriceStopLoss,
|
|
3290
|
+
originalPriceOpen: publicSignal.originalPriceOpen,
|
|
3282
3291
|
scheduledAt: publicSignal.scheduledAt,
|
|
3283
3292
|
pendingAt: publicSignal.pendingAt,
|
|
3284
3293
|
});
|
|
@@ -3295,6 +3304,7 @@ const PROCESS_COMMIT_QUEUE_FN = async (self, timestamp) => {
|
|
|
3295
3304
|
percentShift: commit.percentShift,
|
|
3296
3305
|
currentPrice: commit.currentPrice,
|
|
3297
3306
|
timestamp,
|
|
3307
|
+
totalEntries: publicSignal.totalEntries,
|
|
3298
3308
|
signalId: publicSignal.id,
|
|
3299
3309
|
position: publicSignal.position,
|
|
3300
3310
|
priceOpen: publicSignal.priceOpen,
|
|
@@ -3302,6 +3312,33 @@ const PROCESS_COMMIT_QUEUE_FN = async (self, timestamp) => {
|
|
|
3302
3312
|
priceStopLoss: publicSignal.priceStopLoss,
|
|
3303
3313
|
originalPriceTakeProfit: publicSignal.originalPriceTakeProfit,
|
|
3304
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,
|
|
3305
3342
|
scheduledAt: publicSignal.scheduledAt,
|
|
3306
3343
|
pendingAt: publicSignal.pendingAt,
|
|
3307
3344
|
});
|
|
@@ -3362,13 +3399,20 @@ const TO_PUBLIC_SIGNAL = (signal) => {
|
|
|
3362
3399
|
const partialExecuted = ("_partial" in signal && Array.isArray(signal._partial))
|
|
3363
3400
|
? signal._partial.reduce((sum, partial) => sum + partial.percent, 0)
|
|
3364
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;
|
|
3365
3406
|
return {
|
|
3366
3407
|
...structuredClone(signal),
|
|
3408
|
+
priceOpen: effectivePriceOpen,
|
|
3367
3409
|
priceStopLoss: hasTrailingSL ? signal._trailingPriceStopLoss : signal.priceStopLoss,
|
|
3368
3410
|
priceTakeProfit: hasTrailingTP ? signal._trailingPriceTakeProfit : signal.priceTakeProfit,
|
|
3411
|
+
originalPriceOpen: signal.priceOpen,
|
|
3369
3412
|
originalPriceStopLoss: signal.priceStopLoss,
|
|
3370
3413
|
originalPriceTakeProfit: signal.priceTakeProfit,
|
|
3371
3414
|
partialExecuted,
|
|
3415
|
+
totalEntries,
|
|
3372
3416
|
};
|
|
3373
3417
|
};
|
|
3374
3418
|
const VALIDATE_SIGNAL_FN = (signal, currentPrice, isScheduled) => {
|
|
@@ -3698,6 +3742,7 @@ const GET_SIGNAL_FN = trycatch(async (self) => {
|
|
|
3698
3742
|
scheduledAt: currentTime,
|
|
3699
3743
|
pendingAt: currentTime, // Для immediate signal оба времени одинаковые
|
|
3700
3744
|
_isScheduled: false,
|
|
3745
|
+
_entry: [{ price: signal.priceOpen }],
|
|
3701
3746
|
};
|
|
3702
3747
|
// Валидируем сигнал перед возвратом
|
|
3703
3748
|
VALIDATE_SIGNAL_FN(signalRow, currentPrice, false);
|
|
@@ -3719,6 +3764,7 @@ const GET_SIGNAL_FN = trycatch(async (self) => {
|
|
|
3719
3764
|
scheduledAt: currentTime,
|
|
3720
3765
|
pendingAt: SCHEDULED_SIGNAL_PENDING_MOCK, // Временно, обновится при активации
|
|
3721
3766
|
_isScheduled: true,
|
|
3767
|
+
_entry: [{ price: signal.priceOpen }],
|
|
3722
3768
|
};
|
|
3723
3769
|
// Валидируем сигнал перед возвратом
|
|
3724
3770
|
VALIDATE_SIGNAL_FN(scheduledSignalRow, currentPrice, true);
|
|
@@ -3736,6 +3782,7 @@ const GET_SIGNAL_FN = trycatch(async (self) => {
|
|
|
3736
3782
|
scheduledAt: currentTime,
|
|
3737
3783
|
pendingAt: currentTime, // Для immediate signal оба времени одинаковые
|
|
3738
3784
|
_isScheduled: false,
|
|
3785
|
+
_entry: [{ price: currentPrice }],
|
|
3739
3786
|
};
|
|
3740
3787
|
// Валидируем сигнал перед возвратом
|
|
3741
3788
|
VALIDATE_SIGNAL_FN(signalRow, currentPrice, false);
|
|
@@ -3882,9 +3929,10 @@ const PARTIAL_LOSS_FN = (self, signal, percentToClose, currentPrice) => {
|
|
|
3882
3929
|
return true;
|
|
3883
3930
|
};
|
|
3884
3931
|
const TRAILING_STOP_LOSS_FN = (self, signal, percentShift) => {
|
|
3932
|
+
const effectivePriceOpen = getEffectivePriceOpen(signal);
|
|
3885
3933
|
// CRITICAL: Always calculate from ORIGINAL SL, not from current trailing SL
|
|
3886
3934
|
// This prevents error accumulation on repeated calls
|
|
3887
|
-
const originalSlDistancePercent = Math.abs((
|
|
3935
|
+
const originalSlDistancePercent = Math.abs((effectivePriceOpen - signal.priceStopLoss) / effectivePriceOpen * 100);
|
|
3888
3936
|
// Calculate new stop-loss distance percentage by adding shift to ORIGINAL distance
|
|
3889
3937
|
// Negative percentShift: reduces distance % (tightens stop, moves SL toward entry or beyond)
|
|
3890
3938
|
// Positive percentShift: increases distance % (loosens stop, moves SL away from entry)
|
|
@@ -3896,13 +3944,13 @@ const TRAILING_STOP_LOSS_FN = (self, signal, percentShift) => {
|
|
|
3896
3944
|
// LONG: SL is below entry (or above entry if in profit zone)
|
|
3897
3945
|
// Formula: entry * (1 - newDistance%)
|
|
3898
3946
|
// Example: entry=100, originalSL=90 (10%), shift=-5% → newDistance=5% → 100 * 0.95 = 95 (tighter)
|
|
3899
|
-
newStopLoss =
|
|
3947
|
+
newStopLoss = effectivePriceOpen * (1 - newSlDistancePercent / 100);
|
|
3900
3948
|
}
|
|
3901
3949
|
else {
|
|
3902
3950
|
// SHORT: SL is above entry (or below entry if in profit zone)
|
|
3903
3951
|
// Formula: entry * (1 + newDistance%)
|
|
3904
3952
|
// Example: entry=100, originalSL=110 (10%), shift=-5% → newDistance=5% → 100 * 1.05 = 105 (tighter)
|
|
3905
|
-
newStopLoss =
|
|
3953
|
+
newStopLoss = effectivePriceOpen * (1 + newSlDistancePercent / 100);
|
|
3906
3954
|
}
|
|
3907
3955
|
const currentTrailingSL = signal._trailingPriceStopLoss;
|
|
3908
3956
|
const isFirstCall = currentTrailingSL === undefined;
|
|
@@ -3965,9 +4013,10 @@ const TRAILING_STOP_LOSS_FN = (self, signal, percentShift) => {
|
|
|
3965
4013
|
}
|
|
3966
4014
|
};
|
|
3967
4015
|
const TRAILING_TAKE_PROFIT_FN = (self, signal, percentShift) => {
|
|
4016
|
+
const effectivePriceOpen = getEffectivePriceOpen(signal);
|
|
3968
4017
|
// CRITICAL: Always calculate from ORIGINAL TP, not from current trailing TP
|
|
3969
4018
|
// This prevents error accumulation on repeated calls
|
|
3970
|
-
const originalTpDistancePercent = Math.abs((signal.priceTakeProfit -
|
|
4019
|
+
const originalTpDistancePercent = Math.abs((signal.priceTakeProfit - effectivePriceOpen) / effectivePriceOpen * 100);
|
|
3971
4020
|
// Calculate new take-profit distance percentage by adding shift to ORIGINAL distance
|
|
3972
4021
|
// Negative percentShift: reduces distance % (brings TP closer to entry)
|
|
3973
4022
|
// Positive percentShift: increases distance % (moves TP further from entry)
|
|
@@ -3978,13 +4027,13 @@ const TRAILING_TAKE_PROFIT_FN = (self, signal, percentShift) => {
|
|
|
3978
4027
|
// LONG: TP is above entry
|
|
3979
4028
|
// Formula: entry * (1 + newDistance%)
|
|
3980
4029
|
// Example: entry=100, originalTP=110 (10%), shift=-3% → newDistance=7% → 100 * 1.07 = 107 (closer)
|
|
3981
|
-
newTakeProfit =
|
|
4030
|
+
newTakeProfit = effectivePriceOpen * (1 + newTpDistancePercent / 100);
|
|
3982
4031
|
}
|
|
3983
4032
|
else {
|
|
3984
4033
|
// SHORT: TP is below entry
|
|
3985
4034
|
// Formula: entry * (1 - newDistance%)
|
|
3986
4035
|
// Example: entry=100, originalTP=90 (10%), shift=-3% → newDistance=7% → 100 * 0.93 = 93 (closer)
|
|
3987
|
-
newTakeProfit =
|
|
4036
|
+
newTakeProfit = effectivePriceOpen * (1 - newTpDistancePercent / 100);
|
|
3988
4037
|
}
|
|
3989
4038
|
const currentTrailingTP = signal._trailingPriceTakeProfit;
|
|
3990
4039
|
const isFirstCall = currentTrailingTP === undefined;
|
|
@@ -4045,6 +4094,7 @@ const TRAILING_TAKE_PROFIT_FN = (self, signal, percentShift) => {
|
|
|
4045
4094
|
}
|
|
4046
4095
|
};
|
|
4047
4096
|
const BREAKEVEN_FN = (self, signal, currentPrice) => {
|
|
4097
|
+
const effectivePriceOpen = getEffectivePriceOpen(signal);
|
|
4048
4098
|
// Calculate breakeven threshold based on slippage and fees
|
|
4049
4099
|
// Need to cover: entry slippage + entry fee + exit slippage + exit fee
|
|
4050
4100
|
// Total: (slippage + fee) * 2 transactions
|
|
@@ -4052,10 +4102,10 @@ const BREAKEVEN_FN = (self, signal, currentPrice) => {
|
|
|
4052
4102
|
// Check if trailing stop is already set
|
|
4053
4103
|
if (signal._trailingPriceStopLoss !== undefined) {
|
|
4054
4104
|
const trailingStopLoss = signal._trailingPriceStopLoss;
|
|
4055
|
-
const breakevenPrice =
|
|
4105
|
+
const breakevenPrice = effectivePriceOpen;
|
|
4056
4106
|
if (signal.position === "long") {
|
|
4057
4107
|
// LONG: trailing SL is positive if it's above entry (in profit zone)
|
|
4058
|
-
const isPositiveTrailing = trailingStopLoss >
|
|
4108
|
+
const isPositiveTrailing = trailingStopLoss > effectivePriceOpen;
|
|
4059
4109
|
if (isPositiveTrailing) {
|
|
4060
4110
|
// Trailing stop is already protecting profit - consider breakeven achieved
|
|
4061
4111
|
self.params.logger.debug("BREAKEVEN_FN: positive trailing stop already set, returning true", {
|
|
@@ -4071,7 +4121,7 @@ const BREAKEVEN_FN = (self, signal, currentPrice) => {
|
|
|
4071
4121
|
else {
|
|
4072
4122
|
// Trailing stop is negative (below entry)
|
|
4073
4123
|
// Check if we can upgrade it to breakeven
|
|
4074
|
-
const thresholdPrice =
|
|
4124
|
+
const thresholdPrice = effectivePriceOpen * (1 + breakevenThresholdPercent / 100);
|
|
4075
4125
|
const isThresholdReached = currentPrice >= thresholdPrice;
|
|
4076
4126
|
if (isThresholdReached && breakevenPrice > trailingStopLoss) {
|
|
4077
4127
|
// Check for price intrusion before setting new SL
|
|
@@ -4120,7 +4170,7 @@ const BREAKEVEN_FN = (self, signal, currentPrice) => {
|
|
|
4120
4170
|
}
|
|
4121
4171
|
else {
|
|
4122
4172
|
// SHORT: trailing SL is positive if it's below entry (in profit zone)
|
|
4123
|
-
const isPositiveTrailing = trailingStopLoss <
|
|
4173
|
+
const isPositiveTrailing = trailingStopLoss < effectivePriceOpen;
|
|
4124
4174
|
if (isPositiveTrailing) {
|
|
4125
4175
|
// Trailing stop is already protecting profit - consider breakeven achieved
|
|
4126
4176
|
self.params.logger.debug("BREAKEVEN_FN: positive trailing stop already set, returning true", {
|
|
@@ -4136,7 +4186,7 @@ const BREAKEVEN_FN = (self, signal, currentPrice) => {
|
|
|
4136
4186
|
else {
|
|
4137
4187
|
// Trailing stop is negative (above entry)
|
|
4138
4188
|
// Check if we can upgrade it to breakeven
|
|
4139
|
-
const thresholdPrice =
|
|
4189
|
+
const thresholdPrice = effectivePriceOpen * (1 - breakevenThresholdPercent / 100);
|
|
4140
4190
|
const isThresholdReached = currentPrice <= thresholdPrice;
|
|
4141
4191
|
if (isThresholdReached && breakevenPrice < trailingStopLoss) {
|
|
4142
4192
|
// Check for price intrusion before setting new SL
|
|
@@ -4186,21 +4236,21 @@ const BREAKEVEN_FN = (self, signal, currentPrice) => {
|
|
|
4186
4236
|
}
|
|
4187
4237
|
// No trailing stop set - proceed with normal breakeven logic
|
|
4188
4238
|
const currentStopLoss = signal.priceStopLoss;
|
|
4189
|
-
const breakevenPrice =
|
|
4239
|
+
const breakevenPrice = effectivePriceOpen;
|
|
4190
4240
|
// Calculate threshold price
|
|
4191
4241
|
let thresholdPrice;
|
|
4192
4242
|
let isThresholdReached;
|
|
4193
4243
|
let canMoveToBreakeven;
|
|
4194
4244
|
if (signal.position === "long") {
|
|
4195
4245
|
// LONG: threshold reached when price goes UP by breakevenThresholdPercent from entry
|
|
4196
|
-
thresholdPrice =
|
|
4246
|
+
thresholdPrice = effectivePriceOpen * (1 + breakevenThresholdPercent / 100);
|
|
4197
4247
|
isThresholdReached = currentPrice >= thresholdPrice;
|
|
4198
4248
|
// Can move to breakeven only if threshold reached and SL is below entry
|
|
4199
4249
|
canMoveToBreakeven = isThresholdReached && currentStopLoss < breakevenPrice;
|
|
4200
4250
|
}
|
|
4201
4251
|
else {
|
|
4202
4252
|
// SHORT: threshold reached when price goes DOWN by breakevenThresholdPercent from entry
|
|
4203
|
-
thresholdPrice =
|
|
4253
|
+
thresholdPrice = effectivePriceOpen * (1 - breakevenThresholdPercent / 100);
|
|
4204
4254
|
isThresholdReached = currentPrice <= thresholdPrice;
|
|
4205
4255
|
// Can move to breakeven only if threshold reached and SL is above entry
|
|
4206
4256
|
canMoveToBreakeven = isThresholdReached && currentStopLoss > breakevenPrice;
|
|
@@ -4260,8 +4310,51 @@ const BREAKEVEN_FN = (self, signal, currentPrice) => {
|
|
|
4260
4310
|
thresholdPrice,
|
|
4261
4311
|
breakevenThresholdPercent,
|
|
4262
4312
|
profitDistancePercent: signal.position === "long"
|
|
4263
|
-
? ((currentPrice -
|
|
4264
|
-
: ((
|
|
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,
|
|
4265
4358
|
});
|
|
4266
4359
|
return true;
|
|
4267
4360
|
};
|
|
@@ -4990,9 +5083,10 @@ const RETURN_PENDING_SIGNAL_ACTIVE_FN = async (self, signal, currentPrice) => {
|
|
|
4990
5083
|
await CALL_ACTIVE_PING_CALLBACKS_FN(self, self.params.execution.context.symbol, signal, currentTime, self.params.execution.context.backtest);
|
|
4991
5084
|
// Calculate percentage of path to TP/SL for partial fill/loss callbacks
|
|
4992
5085
|
{
|
|
5086
|
+
const effectivePriceOpen = getEffectivePriceOpen(signal);
|
|
4993
5087
|
if (signal.position === "long") {
|
|
4994
5088
|
// For long: calculate progress towards TP or SL
|
|
4995
|
-
const currentDistance = currentPrice -
|
|
5089
|
+
const currentDistance = currentPrice - effectivePriceOpen;
|
|
4996
5090
|
if (currentDistance > 0) {
|
|
4997
5091
|
// Check if breakeven should be triggered
|
|
4998
5092
|
await CALL_BREAKEVEN_CHECK_FN(self, self.params.execution.context.symbol, signal, currentPrice, currentTime, self.params.execution.context.backtest);
|
|
@@ -5000,7 +5094,7 @@ const RETURN_PENDING_SIGNAL_ACTIVE_FN = async (self, signal, currentPrice) => {
|
|
|
5000
5094
|
if (currentDistance > 0) {
|
|
5001
5095
|
// Moving towards TP (use trailing TP if set)
|
|
5002
5096
|
const effectiveTakeProfit = signal._trailingPriceTakeProfit ?? signal.priceTakeProfit;
|
|
5003
|
-
const tpDistance = effectiveTakeProfit -
|
|
5097
|
+
const tpDistance = effectiveTakeProfit - effectivePriceOpen;
|
|
5004
5098
|
const progressPercent = (currentDistance / tpDistance) * 100;
|
|
5005
5099
|
percentTp = Math.min(progressPercent, 100);
|
|
5006
5100
|
await CALL_PARTIAL_PROFIT_CALLBACKS_FN(self, self.params.execution.context.symbol, signal, currentPrice, percentTp, currentTime, self.params.execution.context.backtest);
|
|
@@ -5008,7 +5102,7 @@ const RETURN_PENDING_SIGNAL_ACTIVE_FN = async (self, signal, currentPrice) => {
|
|
|
5008
5102
|
else if (currentDistance < 0) {
|
|
5009
5103
|
// Moving towards SL (use trailing SL if set)
|
|
5010
5104
|
const effectiveStopLoss = signal._trailingPriceStopLoss ?? signal.priceStopLoss;
|
|
5011
|
-
const slDistance =
|
|
5105
|
+
const slDistance = effectivePriceOpen - effectiveStopLoss;
|
|
5012
5106
|
const progressPercent = (Math.abs(currentDistance) / slDistance) * 100;
|
|
5013
5107
|
percentSl = Math.min(progressPercent, 100);
|
|
5014
5108
|
await CALL_PARTIAL_LOSS_CALLBACKS_FN(self, self.params.execution.context.symbol, signal, currentPrice, percentSl, currentTime, self.params.execution.context.backtest);
|
|
@@ -5016,7 +5110,7 @@ const RETURN_PENDING_SIGNAL_ACTIVE_FN = async (self, signal, currentPrice) => {
|
|
|
5016
5110
|
}
|
|
5017
5111
|
else if (signal.position === "short") {
|
|
5018
5112
|
// For short: calculate progress towards TP or SL
|
|
5019
|
-
const currentDistance =
|
|
5113
|
+
const currentDistance = effectivePriceOpen - currentPrice;
|
|
5020
5114
|
if (currentDistance > 0) {
|
|
5021
5115
|
// Check if breakeven should be triggered
|
|
5022
5116
|
await CALL_BREAKEVEN_CHECK_FN(self, self.params.execution.context.symbol, signal, currentPrice, currentTime, self.params.execution.context.backtest);
|
|
@@ -5024,7 +5118,7 @@ const RETURN_PENDING_SIGNAL_ACTIVE_FN = async (self, signal, currentPrice) => {
|
|
|
5024
5118
|
if (currentDistance > 0) {
|
|
5025
5119
|
// Moving towards TP (use trailing TP if set)
|
|
5026
5120
|
const effectiveTakeProfit = signal._trailingPriceTakeProfit ?? signal.priceTakeProfit;
|
|
5027
|
-
const tpDistance =
|
|
5121
|
+
const tpDistance = effectivePriceOpen - effectiveTakeProfit;
|
|
5028
5122
|
const progressPercent = (currentDistance / tpDistance) * 100;
|
|
5029
5123
|
percentTp = Math.min(progressPercent, 100);
|
|
5030
5124
|
await CALL_PARTIAL_PROFIT_CALLBACKS_FN(self, self.params.execution.context.symbol, signal, currentPrice, percentTp, currentTime, self.params.execution.context.backtest);
|
|
@@ -5032,7 +5126,7 @@ const RETURN_PENDING_SIGNAL_ACTIVE_FN = async (self, signal, currentPrice) => {
|
|
|
5032
5126
|
if (currentDistance < 0) {
|
|
5033
5127
|
// Moving towards SL (use trailing SL if set)
|
|
5034
5128
|
const effectiveStopLoss = signal._trailingPriceStopLoss ?? signal.priceStopLoss;
|
|
5035
|
-
const slDistance = effectiveStopLoss -
|
|
5129
|
+
const slDistance = effectiveStopLoss - effectivePriceOpen;
|
|
5036
5130
|
const progressPercent = (Math.abs(currentDistance) / slDistance) * 100;
|
|
5037
5131
|
percentSl = Math.min(progressPercent, 100);
|
|
5038
5132
|
await CALL_PARTIAL_LOSS_CALLBACKS_FN(self, self.params.execution.context.symbol, signal, currentPrice, percentSl, currentTime, self.params.execution.context.backtest);
|
|
@@ -5252,8 +5346,10 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
|
|
|
5252
5346
|
priceStopLoss: publicSignalForCommit.priceStopLoss,
|
|
5253
5347
|
originalPriceTakeProfit: publicSignalForCommit.originalPriceTakeProfit,
|
|
5254
5348
|
originalPriceStopLoss: publicSignalForCommit.originalPriceStopLoss,
|
|
5349
|
+
originalPriceOpen: publicSignalForCommit.originalPriceOpen,
|
|
5255
5350
|
scheduledAt: publicSignalForCommit.scheduledAt,
|
|
5256
5351
|
pendingAt: publicSignalForCommit.pendingAt,
|
|
5352
|
+
totalEntries: publicSignalForCommit.totalEntries,
|
|
5257
5353
|
});
|
|
5258
5354
|
await CALL_OPEN_CALLBACKS_FN(self, self.params.execution.context.symbol, pendingSignal, pendingSignal.priceOpen, candle.timestamp, self.params.execution.context.backtest);
|
|
5259
5355
|
await CALL_BACKTEST_SCHEDULE_OPEN_FN(self, self.params.execution.context.symbol, pendingSignal, candle.timestamp, self.params.execution.context.backtest);
|
|
@@ -5400,9 +5496,10 @@ const PROCESS_PENDING_SIGNAL_CANDLES_FN = async (self, signal, candles) => {
|
|
|
5400
5496
|
// Call onPartialProfit/onPartialLoss callbacks during backtest candle processing
|
|
5401
5497
|
// Calculate percentage of path to TP/SL
|
|
5402
5498
|
{
|
|
5499
|
+
const effectivePriceOpen = getEffectivePriceOpen(signal);
|
|
5403
5500
|
if (signal.position === "long") {
|
|
5404
5501
|
// For long: calculate progress towards TP or SL
|
|
5405
|
-
const currentDistance = averagePrice -
|
|
5502
|
+
const currentDistance = averagePrice - effectivePriceOpen;
|
|
5406
5503
|
if (currentDistance > 0) {
|
|
5407
5504
|
// Check if breakeven should be triggered
|
|
5408
5505
|
await CALL_BREAKEVEN_CHECK_FN(self, self.params.execution.context.symbol, signal, averagePrice, currentCandleTimestamp, self.params.execution.context.backtest);
|
|
@@ -5410,21 +5507,21 @@ const PROCESS_PENDING_SIGNAL_CANDLES_FN = async (self, signal, candles) => {
|
|
|
5410
5507
|
if (currentDistance > 0) {
|
|
5411
5508
|
// Moving towards TP (use trailing TP if set)
|
|
5412
5509
|
const effectiveTakeProfit = signal._trailingPriceTakeProfit ?? signal.priceTakeProfit;
|
|
5413
|
-
const tpDistance = effectiveTakeProfit -
|
|
5510
|
+
const tpDistance = effectiveTakeProfit - effectivePriceOpen;
|
|
5414
5511
|
const progressPercent = (currentDistance / tpDistance) * 100;
|
|
5415
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);
|
|
5416
5513
|
}
|
|
5417
5514
|
else if (currentDistance < 0) {
|
|
5418
5515
|
// Moving towards SL (use trailing SL if set)
|
|
5419
5516
|
const effectiveStopLoss = signal._trailingPriceStopLoss ?? signal.priceStopLoss;
|
|
5420
|
-
const slDistance =
|
|
5517
|
+
const slDistance = effectivePriceOpen - effectiveStopLoss;
|
|
5421
5518
|
const progressPercent = (Math.abs(currentDistance) / slDistance) * 100;
|
|
5422
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);
|
|
5423
5520
|
}
|
|
5424
5521
|
}
|
|
5425
5522
|
else if (signal.position === "short") {
|
|
5426
5523
|
// For short: calculate progress towards TP or SL
|
|
5427
|
-
const currentDistance =
|
|
5524
|
+
const currentDistance = effectivePriceOpen - averagePrice;
|
|
5428
5525
|
if (currentDistance > 0) {
|
|
5429
5526
|
// Check if breakeven should be triggered
|
|
5430
5527
|
await CALL_BREAKEVEN_CHECK_FN(self, self.params.execution.context.symbol, signal, averagePrice, currentCandleTimestamp, self.params.execution.context.backtest);
|
|
@@ -5432,14 +5529,14 @@ const PROCESS_PENDING_SIGNAL_CANDLES_FN = async (self, signal, candles) => {
|
|
|
5432
5529
|
if (currentDistance > 0) {
|
|
5433
5530
|
// Moving towards TP (use trailing TP if set)
|
|
5434
5531
|
const effectiveTakeProfit = signal._trailingPriceTakeProfit ?? signal.priceTakeProfit;
|
|
5435
|
-
const tpDistance =
|
|
5532
|
+
const tpDistance = effectivePriceOpen - effectiveTakeProfit;
|
|
5436
5533
|
const progressPercent = (currentDistance / tpDistance) * 100;
|
|
5437
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);
|
|
5438
5535
|
}
|
|
5439
5536
|
if (currentDistance < 0) {
|
|
5440
5537
|
// Moving towards SL (use trailing SL if set)
|
|
5441
5538
|
const effectiveStopLoss = signal._trailingPriceStopLoss ?? signal.priceStopLoss;
|
|
5442
|
-
const slDistance = effectiveStopLoss -
|
|
5539
|
+
const slDistance = effectiveStopLoss - effectivePriceOpen;
|
|
5443
5540
|
const progressPercent = (Math.abs(currentDistance) / slDistance) * 100;
|
|
5444
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);
|
|
5445
5542
|
}
|
|
@@ -5637,6 +5734,7 @@ class ClientStrategy {
|
|
|
5637
5734
|
return false;
|
|
5638
5735
|
}
|
|
5639
5736
|
const signal = this._pendingSignal;
|
|
5737
|
+
const effectivePriceOpen = getEffectivePriceOpen(signal);
|
|
5640
5738
|
// Calculate breakeven threshold based on slippage and fees
|
|
5641
5739
|
// Need to cover: entry slippage + entry fee + exit slippage + exit fee
|
|
5642
5740
|
// Total: (slippage + fee) * 2 transactions
|
|
@@ -5646,52 +5744,52 @@ class ClientStrategy {
|
|
|
5646
5744
|
const trailingStopLoss = signal._trailingPriceStopLoss;
|
|
5647
5745
|
if (signal.position === "long") {
|
|
5648
5746
|
// LONG: trailing SL is positive if it's above entry (in profit zone)
|
|
5649
|
-
const isPositiveTrailing = trailingStopLoss >
|
|
5747
|
+
const isPositiveTrailing = trailingStopLoss > effectivePriceOpen;
|
|
5650
5748
|
if (isPositiveTrailing) {
|
|
5651
5749
|
// Trailing stop is already protecting profit - breakeven achieved
|
|
5652
5750
|
return true;
|
|
5653
5751
|
}
|
|
5654
5752
|
// Trailing stop is negative (below entry)
|
|
5655
5753
|
// Check if we can upgrade it to breakeven
|
|
5656
|
-
const thresholdPrice =
|
|
5754
|
+
const thresholdPrice = effectivePriceOpen * (1 + breakevenThresholdPercent / 100);
|
|
5657
5755
|
const isThresholdReached = currentPrice >= thresholdPrice;
|
|
5658
|
-
const breakevenPrice =
|
|
5756
|
+
const breakevenPrice = effectivePriceOpen;
|
|
5659
5757
|
// Can upgrade to breakeven if threshold reached and breakeven is better than current trailing SL
|
|
5660
5758
|
return isThresholdReached && breakevenPrice > trailingStopLoss;
|
|
5661
5759
|
}
|
|
5662
5760
|
else {
|
|
5663
5761
|
// SHORT: trailing SL is positive if it's below entry (in profit zone)
|
|
5664
|
-
const isPositiveTrailing = trailingStopLoss <
|
|
5762
|
+
const isPositiveTrailing = trailingStopLoss < effectivePriceOpen;
|
|
5665
5763
|
if (isPositiveTrailing) {
|
|
5666
5764
|
// Trailing stop is already protecting profit - breakeven achieved
|
|
5667
5765
|
return true;
|
|
5668
5766
|
}
|
|
5669
5767
|
// Trailing stop is negative (above entry)
|
|
5670
5768
|
// Check if we can upgrade it to breakeven
|
|
5671
|
-
const thresholdPrice =
|
|
5769
|
+
const thresholdPrice = effectivePriceOpen * (1 - breakevenThresholdPercent / 100);
|
|
5672
5770
|
const isThresholdReached = currentPrice <= thresholdPrice;
|
|
5673
|
-
const breakevenPrice =
|
|
5771
|
+
const breakevenPrice = effectivePriceOpen;
|
|
5674
5772
|
// Can upgrade to breakeven if threshold reached and breakeven is better than current trailing SL
|
|
5675
5773
|
return isThresholdReached && breakevenPrice < trailingStopLoss;
|
|
5676
5774
|
}
|
|
5677
5775
|
}
|
|
5678
5776
|
// No trailing stop set - proceed with normal breakeven logic
|
|
5679
5777
|
const currentStopLoss = signal.priceStopLoss;
|
|
5680
|
-
const breakevenPrice =
|
|
5778
|
+
const breakevenPrice = effectivePriceOpen;
|
|
5681
5779
|
// Calculate threshold price
|
|
5682
5780
|
let thresholdPrice;
|
|
5683
5781
|
let isThresholdReached;
|
|
5684
5782
|
let canMoveToBreakeven;
|
|
5685
5783
|
if (signal.position === "long") {
|
|
5686
5784
|
// LONG: threshold reached when price goes UP by breakevenThresholdPercent from entry
|
|
5687
|
-
thresholdPrice =
|
|
5785
|
+
thresholdPrice = effectivePriceOpen * (1 + breakevenThresholdPercent / 100);
|
|
5688
5786
|
isThresholdReached = currentPrice >= thresholdPrice;
|
|
5689
5787
|
// Can move to breakeven only if threshold reached and SL is below entry
|
|
5690
5788
|
canMoveToBreakeven = isThresholdReached && currentStopLoss < breakevenPrice;
|
|
5691
5789
|
}
|
|
5692
5790
|
else {
|
|
5693
5791
|
// SHORT: threshold reached when price goes DOWN by breakevenThresholdPercent from entry
|
|
5694
|
-
thresholdPrice =
|
|
5792
|
+
thresholdPrice = effectivePriceOpen * (1 - breakevenThresholdPercent / 100);
|
|
5695
5793
|
isThresholdReached = currentPrice <= thresholdPrice;
|
|
5696
5794
|
// Can move to breakeven only if threshold reached and SL is above entry
|
|
5697
5795
|
canMoveToBreakeven = isThresholdReached && currentStopLoss > breakevenPrice;
|
|
@@ -5774,6 +5872,8 @@ class ClientStrategy {
|
|
|
5774
5872
|
backtest: this.params.execution.context.backtest,
|
|
5775
5873
|
cancelId: cancelledSignal.cancelId,
|
|
5776
5874
|
timestamp: currentTime,
|
|
5875
|
+
totalEntries: cancelledSignal._entry?.length ?? 1,
|
|
5876
|
+
originalPriceOpen: cancelledSignal.priceOpen,
|
|
5777
5877
|
});
|
|
5778
5878
|
// Call onCancel callback
|
|
5779
5879
|
await CALL_CANCEL_CALLBACKS_FN(this, this.params.execution.context.symbol, cancelledSignal, currentPrice, currentTime, this.params.execution.context.backtest);
|
|
@@ -5814,6 +5914,8 @@ class ClientStrategy {
|
|
|
5814
5914
|
backtest: this.params.execution.context.backtest,
|
|
5815
5915
|
closeId: closedSignal.closeId,
|
|
5816
5916
|
timestamp: currentTime,
|
|
5917
|
+
totalEntries: closedSignal._entry?.length ?? 1,
|
|
5918
|
+
originalPriceOpen: closedSignal.priceOpen,
|
|
5817
5919
|
});
|
|
5818
5920
|
// Call onClose callback
|
|
5819
5921
|
await CALL_CLOSE_CALLBACKS_FN(this, this.params.execution.context.symbol, closedSignal, currentPrice, currentTime, this.params.execution.context.backtest);
|
|
@@ -5894,8 +5996,10 @@ class ClientStrategy {
|
|
|
5894
5996
|
priceStopLoss: publicSignalForCommit.priceStopLoss,
|
|
5895
5997
|
originalPriceTakeProfit: publicSignalForCommit.originalPriceTakeProfit,
|
|
5896
5998
|
originalPriceStopLoss: publicSignalForCommit.originalPriceStopLoss,
|
|
5999
|
+
originalPriceOpen: publicSignalForCommit.originalPriceOpen,
|
|
5897
6000
|
scheduledAt: publicSignalForCommit.scheduledAt,
|
|
5898
6001
|
pendingAt: publicSignalForCommit.pendingAt,
|
|
6002
|
+
totalEntries: publicSignalForCommit.totalEntries,
|
|
5899
6003
|
});
|
|
5900
6004
|
// Call onOpen callback
|
|
5901
6005
|
await CALL_OPEN_CALLBACKS_FN(this, this.params.execution.context.symbol, pendingSignal, currentPrice, currentTime, this.params.execution.context.backtest);
|
|
@@ -6026,6 +6130,8 @@ class ClientStrategy {
|
|
|
6026
6130
|
backtest: true,
|
|
6027
6131
|
cancelId: cancelledSignal.cancelId,
|
|
6028
6132
|
timestamp: closeTimestamp,
|
|
6133
|
+
totalEntries: cancelledSignal._entry?.length ?? 1,
|
|
6134
|
+
originalPriceOpen: cancelledSignal.priceOpen,
|
|
6029
6135
|
});
|
|
6030
6136
|
await CALL_CANCEL_CALLBACKS_FN(this, this.params.execution.context.symbol, cancelledSignal, currentPrice, closeTimestamp, this.params.execution.context.backtest);
|
|
6031
6137
|
const cancelledResult = {
|
|
@@ -6063,6 +6169,8 @@ class ClientStrategy {
|
|
|
6063
6169
|
backtest: true,
|
|
6064
6170
|
closeId: closedSignal.closeId,
|
|
6065
6171
|
timestamp: closeTimestamp,
|
|
6172
|
+
totalEntries: closedSignal._entry?.length ?? 1,
|
|
6173
|
+
originalPriceOpen: closedSignal.priceOpen,
|
|
6066
6174
|
});
|
|
6067
6175
|
await CALL_CLOSE_CALLBACKS_FN(this, this.params.execution.context.symbol, closedSignal, currentPrice, closeTimestamp, this.params.execution.context.backtest);
|
|
6068
6176
|
// КРИТИЧНО: Очищаем состояние ClientPartial при закрытии позиции
|
|
@@ -6450,16 +6558,19 @@ class ClientStrategy {
|
|
|
6450
6558
|
throw new Error(`ClientStrategy partialProfit: currentPrice must be a positive finite number, got ${currentPrice}`);
|
|
6451
6559
|
}
|
|
6452
6560
|
// Validation: currentPrice must be moving toward TP (profit direction)
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
if (
|
|
6456
|
-
|
|
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
|
+
}
|
|
6457
6568
|
}
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
|
|
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
|
+
}
|
|
6463
6574
|
}
|
|
6464
6575
|
}
|
|
6465
6576
|
// Check if currentPrice already crossed take profit level
|
|
@@ -6582,16 +6693,19 @@ class ClientStrategy {
|
|
|
6582
6693
|
throw new Error(`ClientStrategy partialLoss: currentPrice must be a positive finite number, got ${currentPrice}`);
|
|
6583
6694
|
}
|
|
6584
6695
|
// Validation: currentPrice must be moving toward SL (loss direction)
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
if (
|
|
6588
|
-
|
|
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
|
+
}
|
|
6589
6703
|
}
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
|
|
6594
|
-
|
|
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
|
+
}
|
|
6595
6709
|
}
|
|
6596
6710
|
}
|
|
6597
6711
|
// Check if currentPrice already crossed stop loss level
|
|
@@ -6712,7 +6826,7 @@ class ClientStrategy {
|
|
|
6712
6826
|
}
|
|
6713
6827
|
// Check for conflict with existing trailing take profit
|
|
6714
6828
|
const signal = this._pendingSignal;
|
|
6715
|
-
const breakevenPrice = signal
|
|
6829
|
+
const breakevenPrice = getEffectivePriceOpen(signal);
|
|
6716
6830
|
const effectiveTakeProfit = signal._trailingPriceTakeProfit ?? signal.priceTakeProfit;
|
|
6717
6831
|
if (signal.position === "long" && breakevenPrice >= effectiveTakeProfit) {
|
|
6718
6832
|
// LONG: Breakeven SL would be at or above current TP - invalid configuration
|
|
@@ -6864,14 +6978,15 @@ class ClientStrategy {
|
|
|
6864
6978
|
}
|
|
6865
6979
|
// Calculate what the new stop loss would be
|
|
6866
6980
|
const signal = this._pendingSignal;
|
|
6867
|
-
const
|
|
6981
|
+
const effectivePriceOpen = getEffectivePriceOpen(signal);
|
|
6982
|
+
const slDistancePercent = Math.abs((effectivePriceOpen - signal.priceStopLoss) / effectivePriceOpen * 100);
|
|
6868
6983
|
const newSlDistancePercent = slDistancePercent + percentShift;
|
|
6869
6984
|
let newStopLoss;
|
|
6870
6985
|
if (signal.position === "long") {
|
|
6871
|
-
newStopLoss =
|
|
6986
|
+
newStopLoss = effectivePriceOpen * (1 - newSlDistancePercent / 100);
|
|
6872
6987
|
}
|
|
6873
6988
|
else {
|
|
6874
|
-
newStopLoss =
|
|
6989
|
+
newStopLoss = effectivePriceOpen * (1 + newSlDistancePercent / 100);
|
|
6875
6990
|
}
|
|
6876
6991
|
// Check for price intrusion before executing trailing logic
|
|
6877
6992
|
if (signal.position === "long" && currentPrice < newStopLoss) {
|
|
@@ -7037,14 +7152,15 @@ class ClientStrategy {
|
|
|
7037
7152
|
}
|
|
7038
7153
|
// Calculate what the new take profit would be
|
|
7039
7154
|
const signal = this._pendingSignal;
|
|
7040
|
-
const
|
|
7155
|
+
const effectivePriceOpen = getEffectivePriceOpen(signal);
|
|
7156
|
+
const tpDistancePercent = Math.abs((signal.priceTakeProfit - effectivePriceOpen) / effectivePriceOpen * 100);
|
|
7041
7157
|
const newTpDistancePercent = tpDistancePercent + percentShift;
|
|
7042
7158
|
let newTakeProfit;
|
|
7043
7159
|
if (signal.position === "long") {
|
|
7044
|
-
newTakeProfit =
|
|
7160
|
+
newTakeProfit = effectivePriceOpen * (1 + newTpDistancePercent / 100);
|
|
7045
7161
|
}
|
|
7046
7162
|
else {
|
|
7047
|
-
newTakeProfit =
|
|
7163
|
+
newTakeProfit = effectivePriceOpen * (1 - newTpDistancePercent / 100);
|
|
7048
7164
|
}
|
|
7049
7165
|
// Check for price intrusion before executing trailing logic
|
|
7050
7166
|
if (signal.position === "long" && currentPrice > newTakeProfit) {
|
|
@@ -7126,6 +7242,70 @@ class ClientStrategy {
|
|
|
7126
7242
|
});
|
|
7127
7243
|
return true;
|
|
7128
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
|
+
}
|
|
7129
7309
|
}
|
|
7130
7310
|
|
|
7131
7311
|
const RISK_METHOD_NAME_GET_DATA = "RiskUtils.getData";
|
|
@@ -8250,6 +8430,27 @@ class StrategyConnectionService {
|
|
|
8250
8430
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
8251
8431
|
return await strategy.activateScheduled(symbol, backtest, activateId);
|
|
8252
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
|
+
};
|
|
8253
8454
|
}
|
|
8254
8455
|
}
|
|
8255
8456
|
|
|
@@ -8723,11 +8924,13 @@ const TO_RISK_SIGNAL = (signal, currentPrice) => {
|
|
|
8723
8924
|
: 0;
|
|
8724
8925
|
return {
|
|
8725
8926
|
...structuredClone(signal),
|
|
8927
|
+
totalEntries: 1,
|
|
8726
8928
|
priceOpen: signal.priceOpen ?? currentPrice,
|
|
8727
8929
|
priceStopLoss: hasTrailingSL ? signal._trailingPriceStopLoss : signal.priceStopLoss,
|
|
8728
8930
|
priceTakeProfit: hasTrailingTP ? signal._trailingPriceTakeProfit : signal.priceTakeProfit,
|
|
8729
8931
|
originalPriceStopLoss: signal.priceStopLoss,
|
|
8730
8932
|
originalPriceTakeProfit: signal.priceTakeProfit,
|
|
8933
|
+
originalPriceOpen: signal.priceOpen ?? currentPrice,
|
|
8731
8934
|
partialExecuted,
|
|
8732
8935
|
};
|
|
8733
8936
|
};
|
|
@@ -11595,6 +11798,28 @@ class StrategyCoreService {
|
|
|
11595
11798
|
await this.validate(context);
|
|
11596
11799
|
return await this.strategyConnectionService.activateScheduled(backtest, symbol, context, activateId);
|
|
11597
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
|
+
};
|
|
11598
11823
|
}
|
|
11599
11824
|
}
|
|
11600
11825
|
|
|
@@ -14142,6 +14367,18 @@ const backtest_columns = [
|
|
|
14142
14367
|
format: (data) => `${data.signal.originalPriceStopLoss.toFixed(8)} USD`,
|
|
14143
14368
|
isVisible: () => true,
|
|
14144
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
|
+
},
|
|
14145
14382
|
{
|
|
14146
14383
|
key: "pnl",
|
|
14147
14384
|
label: "PNL (net)",
|
|
@@ -14429,6 +14666,20 @@ const live_columns = [
|
|
|
14429
14666
|
: "N/A",
|
|
14430
14667
|
isVisible: () => true,
|
|
14431
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
|
+
},
|
|
14432
14683
|
{
|
|
14433
14684
|
key: "partialExecuted",
|
|
14434
14685
|
label: "Partial Executed %",
|
|
@@ -14593,6 +14844,18 @@ const partial_columns = [
|
|
|
14593
14844
|
format: (data) => (data.originalPriceStopLoss ? `${data.originalPriceStopLoss.toFixed(8)} USD` : "N/A"),
|
|
14594
14845
|
isVisible: () => true,
|
|
14595
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
|
+
},
|
|
14596
14859
|
{
|
|
14597
14860
|
key: "partialExecuted",
|
|
14598
14861
|
label: "Partial Executed %",
|
|
@@ -14727,6 +14990,18 @@ const breakeven_columns = [
|
|
|
14727
14990
|
format: (data) => (data.originalPriceStopLoss ? `${data.originalPriceStopLoss.toFixed(8)} USD` : "N/A"),
|
|
14728
14991
|
isVisible: () => true,
|
|
14729
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
|
+
},
|
|
14730
15005
|
{
|
|
14731
15006
|
key: "partialExecuted",
|
|
14732
15007
|
label: "Partial Executed %",
|
|
@@ -14997,6 +15272,22 @@ const risk_columns = [
|
|
|
14997
15272
|
: "N/A",
|
|
14998
15273
|
isVisible: () => true,
|
|
14999
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
|
+
},
|
|
15000
15291
|
{
|
|
15001
15292
|
key: "partialExecuted",
|
|
15002
15293
|
label: "Partial Executed %",
|
|
@@ -15171,6 +15462,20 @@ const schedule_columns = [
|
|
|
15171
15462
|
: "N/A",
|
|
15172
15463
|
isVisible: () => true,
|
|
15173
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
|
+
},
|
|
15174
15479
|
{
|
|
15175
15480
|
key: "partialExecuted",
|
|
15176
15481
|
label: "Partial Executed %",
|
|
@@ -17260,6 +17565,8 @@ let ReportStorage$5 = class ReportStorage {
|
|
|
17260
17565
|
priceStopLoss: data.signal.priceStopLoss,
|
|
17261
17566
|
originalPriceTakeProfit: data.signal.originalPriceTakeProfit,
|
|
17262
17567
|
originalPriceStopLoss: data.signal.originalPriceStopLoss,
|
|
17568
|
+
totalEntries: data.signal.totalEntries,
|
|
17569
|
+
originalPriceOpen: data.signal.originalPriceOpen,
|
|
17263
17570
|
partialExecuted: data.signal.partialExecuted,
|
|
17264
17571
|
scheduledAt: data.signal.scheduledAt,
|
|
17265
17572
|
});
|
|
@@ -17289,6 +17596,8 @@ let ReportStorage$5 = class ReportStorage {
|
|
|
17289
17596
|
priceStopLoss: data.signal.priceStopLoss,
|
|
17290
17597
|
originalPriceTakeProfit: data.signal.originalPriceTakeProfit,
|
|
17291
17598
|
originalPriceStopLoss: data.signal.originalPriceStopLoss,
|
|
17599
|
+
totalEntries: data.signal.totalEntries,
|
|
17600
|
+
originalPriceOpen: data.signal.originalPriceOpen,
|
|
17292
17601
|
partialExecuted: data.signal.partialExecuted,
|
|
17293
17602
|
duration: durationMin,
|
|
17294
17603
|
pendingAt: data.signal.pendingAt,
|
|
@@ -17321,6 +17630,8 @@ let ReportStorage$5 = class ReportStorage {
|
|
|
17321
17630
|
priceStopLoss: data.signal.priceStopLoss,
|
|
17322
17631
|
originalPriceTakeProfit: data.signal.originalPriceTakeProfit,
|
|
17323
17632
|
originalPriceStopLoss: data.signal.originalPriceStopLoss,
|
|
17633
|
+
totalEntries: data.signal.totalEntries,
|
|
17634
|
+
originalPriceOpen: data.signal.originalPriceOpen,
|
|
17324
17635
|
partialExecuted: data.signal.partialExecuted,
|
|
17325
17636
|
closeTimestamp: data.closeTimestamp,
|
|
17326
17637
|
duration: durationMin,
|
|
@@ -20466,6 +20777,8 @@ let ReportStorage$3 = class ReportStorage {
|
|
|
20466
20777
|
priceStopLoss: data.priceStopLoss,
|
|
20467
20778
|
originalPriceTakeProfit: data.originalPriceTakeProfit,
|
|
20468
20779
|
originalPriceStopLoss: data.originalPriceStopLoss,
|
|
20780
|
+
totalEntries: data.totalEntries,
|
|
20781
|
+
originalPriceOpen: data.originalPriceOpen,
|
|
20469
20782
|
partialExecuted: data.partialExecuted,
|
|
20470
20783
|
note: data.note,
|
|
20471
20784
|
pendingAt: data.pendingAt,
|
|
@@ -20500,6 +20813,8 @@ let ReportStorage$3 = class ReportStorage {
|
|
|
20500
20813
|
priceStopLoss: data.priceStopLoss,
|
|
20501
20814
|
originalPriceTakeProfit: data.originalPriceTakeProfit,
|
|
20502
20815
|
originalPriceStopLoss: data.originalPriceStopLoss,
|
|
20816
|
+
totalEntries: data.totalEntries,
|
|
20817
|
+
originalPriceOpen: data.originalPriceOpen,
|
|
20503
20818
|
partialExecuted: data.partialExecuted,
|
|
20504
20819
|
note: data.note,
|
|
20505
20820
|
pendingAt: data.pendingAt,
|
|
@@ -21599,6 +21914,8 @@ let ReportStorage$2 = class ReportStorage {
|
|
|
21599
21914
|
priceStopLoss: data.priceStopLoss,
|
|
21600
21915
|
originalPriceTakeProfit: data.originalPriceTakeProfit,
|
|
21601
21916
|
originalPriceStopLoss: data.originalPriceStopLoss,
|
|
21917
|
+
totalEntries: data.totalEntries,
|
|
21918
|
+
originalPriceOpen: data.originalPriceOpen,
|
|
21602
21919
|
partialExecuted: data.partialExecuted,
|
|
21603
21920
|
note: data.note,
|
|
21604
21921
|
pendingAt: data.pendingAt,
|
|
@@ -23602,6 +23919,8 @@ class ScheduleReportService {
|
|
|
23602
23919
|
priceStopLoss: data.signal?.priceStopLoss,
|
|
23603
23920
|
originalPriceTakeProfit: data.signal?.originalPriceTakeProfit,
|
|
23604
23921
|
originalPriceStopLoss: data.signal?.originalPriceStopLoss,
|
|
23922
|
+
totalEntries: data.signal?.totalEntries,
|
|
23923
|
+
originalPriceOpen: data.signal?.originalPriceOpen,
|
|
23605
23924
|
partialExecuted: data.signal?.partialExecuted,
|
|
23606
23925
|
pendingAt: data.signal?.pendingAt,
|
|
23607
23926
|
minuteEstimatedTime: data.signal?.minuteEstimatedTime,
|
|
@@ -23646,6 +23965,8 @@ class ScheduleReportService {
|
|
|
23646
23965
|
priceStopLoss: data.signal?.priceStopLoss,
|
|
23647
23966
|
originalPriceTakeProfit: data.signal?.originalPriceTakeProfit,
|
|
23648
23967
|
originalPriceStopLoss: data.signal?.originalPriceStopLoss,
|
|
23968
|
+
totalEntries: data.signal?.totalEntries,
|
|
23969
|
+
originalPriceOpen: data.signal?.originalPriceOpen,
|
|
23649
23970
|
partialExecuted: data.signal?.partialExecuted,
|
|
23650
23971
|
scheduledAt: data.signal?.scheduledAt,
|
|
23651
23972
|
pendingAt: data.signal?.pendingAt,
|
|
@@ -24123,6 +24444,8 @@ class PartialReportService {
|
|
|
24123
24444
|
priceStopLoss: data.data.priceStopLoss,
|
|
24124
24445
|
originalPriceTakeProfit: data.data.originalPriceTakeProfit,
|
|
24125
24446
|
originalPriceStopLoss: data.data.originalPriceStopLoss,
|
|
24447
|
+
totalEntries: data.data.totalEntries,
|
|
24448
|
+
originalPriceOpen: data.data.originalPriceOpen,
|
|
24126
24449
|
partialExecuted: data.data.partialExecuted,
|
|
24127
24450
|
_partial: data.data._partial,
|
|
24128
24451
|
note: data.data.note,
|
|
@@ -24164,6 +24487,8 @@ class PartialReportService {
|
|
|
24164
24487
|
priceStopLoss: data.data.priceStopLoss,
|
|
24165
24488
|
originalPriceTakeProfit: data.data.originalPriceTakeProfit,
|
|
24166
24489
|
originalPriceStopLoss: data.data.originalPriceStopLoss,
|
|
24490
|
+
totalEntries: data.data.totalEntries,
|
|
24491
|
+
originalPriceOpen: data.data.originalPriceOpen,
|
|
24167
24492
|
partialExecuted: data.data.partialExecuted,
|
|
24168
24493
|
_partial: data.data._partial,
|
|
24169
24494
|
note: data.data.note,
|
|
@@ -24286,6 +24611,8 @@ class BreakevenReportService {
|
|
|
24286
24611
|
priceStopLoss: data.data.priceStopLoss,
|
|
24287
24612
|
originalPriceTakeProfit: data.data.originalPriceTakeProfit,
|
|
24288
24613
|
originalPriceStopLoss: data.data.originalPriceStopLoss,
|
|
24614
|
+
totalEntries: data.data.totalEntries,
|
|
24615
|
+
originalPriceOpen: data.data.originalPriceOpen,
|
|
24289
24616
|
partialExecuted: data.data.partialExecuted,
|
|
24290
24617
|
_partial: data.data._partial,
|
|
24291
24618
|
note: data.data.note,
|
|
@@ -24594,7 +24921,7 @@ class StrategyReportService {
|
|
|
24594
24921
|
* @param scheduledAt - Signal creation timestamp in milliseconds
|
|
24595
24922
|
* @param pendingAt - Pending timestamp in milliseconds
|
|
24596
24923
|
*/
|
|
24597
|
-
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) => {
|
|
24598
24925
|
this.loggerService.log("strategyReportService partialProfit", {
|
|
24599
24926
|
symbol,
|
|
24600
24927
|
percentToClose,
|
|
@@ -24626,6 +24953,8 @@ class StrategyReportService {
|
|
|
24626
24953
|
priceStopLoss,
|
|
24627
24954
|
originalPriceTakeProfit,
|
|
24628
24955
|
originalPriceStopLoss,
|
|
24956
|
+
originalPriceOpen,
|
|
24957
|
+
totalEntries,
|
|
24629
24958
|
scheduledAt,
|
|
24630
24959
|
pendingAt,
|
|
24631
24960
|
}, {
|
|
@@ -24655,7 +24984,7 @@ class StrategyReportService {
|
|
|
24655
24984
|
* @param scheduledAt - Signal creation timestamp in milliseconds
|
|
24656
24985
|
* @param pendingAt - Pending timestamp in milliseconds
|
|
24657
24986
|
*/
|
|
24658
|
-
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) => {
|
|
24659
24988
|
this.loggerService.log("strategyReportService partialLoss", {
|
|
24660
24989
|
symbol,
|
|
24661
24990
|
percentToClose,
|
|
@@ -24687,6 +25016,8 @@ class StrategyReportService {
|
|
|
24687
25016
|
priceStopLoss,
|
|
24688
25017
|
originalPriceTakeProfit,
|
|
24689
25018
|
originalPriceStopLoss,
|
|
25019
|
+
originalPriceOpen,
|
|
25020
|
+
totalEntries,
|
|
24690
25021
|
scheduledAt,
|
|
24691
25022
|
pendingAt,
|
|
24692
25023
|
}, {
|
|
@@ -24716,7 +25047,7 @@ class StrategyReportService {
|
|
|
24716
25047
|
* @param scheduledAt - Signal creation timestamp in milliseconds
|
|
24717
25048
|
* @param pendingAt - Pending timestamp in milliseconds
|
|
24718
25049
|
*/
|
|
24719
|
-
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) => {
|
|
24720
25051
|
this.loggerService.log("strategyReportService trailingStop", {
|
|
24721
25052
|
symbol,
|
|
24722
25053
|
percentShift,
|
|
@@ -24748,6 +25079,8 @@ class StrategyReportService {
|
|
|
24748
25079
|
priceStopLoss,
|
|
24749
25080
|
originalPriceTakeProfit,
|
|
24750
25081
|
originalPriceStopLoss,
|
|
25082
|
+
originalPriceOpen,
|
|
25083
|
+
totalEntries,
|
|
24751
25084
|
scheduledAt,
|
|
24752
25085
|
pendingAt,
|
|
24753
25086
|
}, {
|
|
@@ -24777,7 +25110,7 @@ class StrategyReportService {
|
|
|
24777
25110
|
* @param scheduledAt - Signal creation timestamp in milliseconds
|
|
24778
25111
|
* @param pendingAt - Pending timestamp in milliseconds
|
|
24779
25112
|
*/
|
|
24780
|
-
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) => {
|
|
24781
25114
|
this.loggerService.log("strategyReportService trailingTake", {
|
|
24782
25115
|
symbol,
|
|
24783
25116
|
percentShift,
|
|
@@ -24809,6 +25142,8 @@ class StrategyReportService {
|
|
|
24809
25142
|
priceStopLoss,
|
|
24810
25143
|
originalPriceTakeProfit,
|
|
24811
25144
|
originalPriceStopLoss,
|
|
25145
|
+
originalPriceOpen,
|
|
25146
|
+
totalEntries,
|
|
24812
25147
|
scheduledAt,
|
|
24813
25148
|
pendingAt,
|
|
24814
25149
|
}, {
|
|
@@ -24837,7 +25172,7 @@ class StrategyReportService {
|
|
|
24837
25172
|
* @param scheduledAt - Signal creation timestamp in milliseconds
|
|
24838
25173
|
* @param pendingAt - Pending timestamp in milliseconds
|
|
24839
25174
|
*/
|
|
24840
|
-
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) => {
|
|
24841
25176
|
this.loggerService.log("strategyReportService breakeven", {
|
|
24842
25177
|
symbol,
|
|
24843
25178
|
currentPrice,
|
|
@@ -24867,6 +25202,8 @@ class StrategyReportService {
|
|
|
24867
25202
|
priceStopLoss,
|
|
24868
25203
|
originalPriceTakeProfit,
|
|
24869
25204
|
originalPriceStopLoss,
|
|
25205
|
+
originalPriceOpen,
|
|
25206
|
+
totalEntries,
|
|
24870
25207
|
scheduledAt,
|
|
24871
25208
|
pendingAt,
|
|
24872
25209
|
}, {
|
|
@@ -24896,7 +25233,7 @@ class StrategyReportService {
|
|
|
24896
25233
|
* @param pendingAt - Pending timestamp in milliseconds
|
|
24897
25234
|
* @param activateId - Optional identifier for the activation reason
|
|
24898
25235
|
*/
|
|
24899
|
-
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) => {
|
|
24900
25237
|
this.loggerService.log("strategyReportService activateScheduled", {
|
|
24901
25238
|
symbol,
|
|
24902
25239
|
currentPrice,
|
|
@@ -24928,6 +25265,8 @@ class StrategyReportService {
|
|
|
24928
25265
|
priceStopLoss,
|
|
24929
25266
|
originalPriceTakeProfit,
|
|
24930
25267
|
originalPriceStopLoss,
|
|
25268
|
+
originalPriceOpen,
|
|
25269
|
+
totalEntries,
|
|
24931
25270
|
scheduledAt,
|
|
24932
25271
|
pendingAt,
|
|
24933
25272
|
}, {
|
|
@@ -24939,6 +25278,71 @@ class StrategyReportService {
|
|
|
24939
25278
|
walkerName: "",
|
|
24940
25279
|
});
|
|
24941
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
|
+
};
|
|
24942
25346
|
/**
|
|
24943
25347
|
* Initializes the service for event logging.
|
|
24944
25348
|
*
|
|
@@ -24969,43 +25373,50 @@ class StrategyReportService {
|
|
|
24969
25373
|
exchangeName: event.exchangeName,
|
|
24970
25374
|
frameName: event.frameName,
|
|
24971
25375
|
strategyName: event.strategyName,
|
|
24972
|
-
}, 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));
|
|
24973
25377
|
const unPartialLoss = strategyCommitSubject
|
|
24974
25378
|
.filter(({ action }) => action === "partial-loss")
|
|
24975
25379
|
.connect(async (event) => await this.partialLoss(event.symbol, event.percentToClose, event.currentPrice, event.backtest, {
|
|
24976
25380
|
exchangeName: event.exchangeName,
|
|
24977
25381
|
frameName: event.frameName,
|
|
24978
25382
|
strategyName: event.strategyName,
|
|
24979
|
-
}, 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));
|
|
24980
25384
|
const unTrailingStop = strategyCommitSubject
|
|
24981
25385
|
.filter(({ action }) => action === "trailing-stop")
|
|
24982
25386
|
.connect(async (event) => await this.trailingStop(event.symbol, event.percentShift, event.currentPrice, event.backtest, {
|
|
24983
25387
|
exchangeName: event.exchangeName,
|
|
24984
25388
|
frameName: event.frameName,
|
|
24985
25389
|
strategyName: event.strategyName,
|
|
24986
|
-
}, 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));
|
|
24987
25391
|
const unTrailingTake = strategyCommitSubject
|
|
24988
25392
|
.filter(({ action }) => action === "trailing-take")
|
|
24989
25393
|
.connect(async (event) => await this.trailingTake(event.symbol, event.percentShift, event.currentPrice, event.backtest, {
|
|
24990
25394
|
exchangeName: event.exchangeName,
|
|
24991
25395
|
frameName: event.frameName,
|
|
24992
25396
|
strategyName: event.strategyName,
|
|
24993
|
-
}, 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));
|
|
24994
25398
|
const unBreakeven = strategyCommitSubject
|
|
24995
25399
|
.filter(({ action }) => action === "breakeven")
|
|
24996
25400
|
.connect(async (event) => await this.breakeven(event.symbol, event.currentPrice, event.backtest, {
|
|
24997
25401
|
exchangeName: event.exchangeName,
|
|
24998
25402
|
frameName: event.frameName,
|
|
24999
25403
|
strategyName: event.strategyName,
|
|
25000
|
-
}, 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));
|
|
25001
25405
|
const unActivateScheduled = strategyCommitSubject
|
|
25002
25406
|
.filter(({ action }) => action === "activate-scheduled")
|
|
25003
25407
|
.connect(async (event) => await this.activateScheduled(event.symbol, event.currentPrice, event.backtest, {
|
|
25004
25408
|
exchangeName: event.exchangeName,
|
|
25005
25409
|
frameName: event.frameName,
|
|
25006
25410
|
strategyName: event.strategyName,
|
|
25007
|
-
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.activateId));
|
|
25008
|
-
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());
|
|
25009
25420
|
return () => {
|
|
25010
25421
|
disposeFn();
|
|
25011
25422
|
this.subscribe.clear();
|
|
@@ -25137,6 +25548,7 @@ class ReportStorage {
|
|
|
25137
25548
|
trailingTakeCount: 0,
|
|
25138
25549
|
breakevenCount: 0,
|
|
25139
25550
|
activateScheduledCount: 0,
|
|
25551
|
+
averageBuyCount: 0,
|
|
25140
25552
|
};
|
|
25141
25553
|
}
|
|
25142
25554
|
return {
|
|
@@ -25150,6 +25562,7 @@ class ReportStorage {
|
|
|
25150
25562
|
trailingTakeCount: this._eventList.filter(e => e.action === "trailing-take").length,
|
|
25151
25563
|
breakevenCount: this._eventList.filter(e => e.action === "breakeven").length,
|
|
25152
25564
|
activateScheduledCount: this._eventList.filter(e => e.action === "activate-scheduled").length,
|
|
25565
|
+
averageBuyCount: this._eventList.filter(e => e.action === "average-buy").length,
|
|
25153
25566
|
};
|
|
25154
25567
|
}
|
|
25155
25568
|
/**
|
|
@@ -25199,6 +25612,7 @@ class ReportStorage {
|
|
|
25199
25612
|
`- Trailing take: ${stats.trailingTakeCount}`,
|
|
25200
25613
|
`- Breakeven: ${stats.breakevenCount}`,
|
|
25201
25614
|
`- Activate scheduled: ${stats.activateScheduledCount}`,
|
|
25615
|
+
`- Average buy: ${stats.averageBuyCount}`,
|
|
25202
25616
|
].join("\n");
|
|
25203
25617
|
}
|
|
25204
25618
|
/**
|
|
@@ -25386,7 +25800,7 @@ class StrategyMarkdownService {
|
|
|
25386
25800
|
* @param scheduledAt - Signal creation timestamp in milliseconds
|
|
25387
25801
|
* @param pendingAt - Pending timestamp in milliseconds
|
|
25388
25802
|
*/
|
|
25389
|
-
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) => {
|
|
25390
25804
|
this.loggerService.log("strategyMarkdownService partialProfit", {
|
|
25391
25805
|
symbol,
|
|
25392
25806
|
percentToClose,
|
|
@@ -25424,6 +25838,8 @@ class StrategyMarkdownService {
|
|
|
25424
25838
|
priceStopLoss,
|
|
25425
25839
|
originalPriceTakeProfit,
|
|
25426
25840
|
originalPriceStopLoss,
|
|
25841
|
+
originalPriceOpen,
|
|
25842
|
+
totalEntries,
|
|
25427
25843
|
scheduledAt,
|
|
25428
25844
|
pendingAt,
|
|
25429
25845
|
});
|
|
@@ -25446,7 +25862,7 @@ class StrategyMarkdownService {
|
|
|
25446
25862
|
* @param scheduledAt - Signal creation timestamp in milliseconds
|
|
25447
25863
|
* @param pendingAt - Pending timestamp in milliseconds
|
|
25448
25864
|
*/
|
|
25449
|
-
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) => {
|
|
25450
25866
|
this.loggerService.log("strategyMarkdownService partialLoss", {
|
|
25451
25867
|
symbol,
|
|
25452
25868
|
percentToClose,
|
|
@@ -25484,6 +25900,8 @@ class StrategyMarkdownService {
|
|
|
25484
25900
|
priceStopLoss,
|
|
25485
25901
|
originalPriceTakeProfit,
|
|
25486
25902
|
originalPriceStopLoss,
|
|
25903
|
+
originalPriceOpen,
|
|
25904
|
+
totalEntries,
|
|
25487
25905
|
scheduledAt,
|
|
25488
25906
|
pendingAt,
|
|
25489
25907
|
});
|
|
@@ -25506,7 +25924,7 @@ class StrategyMarkdownService {
|
|
|
25506
25924
|
* @param scheduledAt - Signal creation timestamp in milliseconds
|
|
25507
25925
|
* @param pendingAt - Pending timestamp in milliseconds
|
|
25508
25926
|
*/
|
|
25509
|
-
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) => {
|
|
25510
25928
|
this.loggerService.log("strategyMarkdownService trailingStop", {
|
|
25511
25929
|
symbol,
|
|
25512
25930
|
percentShift,
|
|
@@ -25544,6 +25962,8 @@ class StrategyMarkdownService {
|
|
|
25544
25962
|
priceStopLoss,
|
|
25545
25963
|
originalPriceTakeProfit,
|
|
25546
25964
|
originalPriceStopLoss,
|
|
25965
|
+
originalPriceOpen,
|
|
25966
|
+
totalEntries,
|
|
25547
25967
|
scheduledAt,
|
|
25548
25968
|
pendingAt,
|
|
25549
25969
|
});
|
|
@@ -25566,7 +25986,7 @@ class StrategyMarkdownService {
|
|
|
25566
25986
|
* @param scheduledAt - Signal creation timestamp in milliseconds
|
|
25567
25987
|
* @param pendingAt - Pending timestamp in milliseconds
|
|
25568
25988
|
*/
|
|
25569
|
-
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) => {
|
|
25570
25990
|
this.loggerService.log("strategyMarkdownService trailingTake", {
|
|
25571
25991
|
symbol,
|
|
25572
25992
|
percentShift,
|
|
@@ -25604,6 +26024,8 @@ class StrategyMarkdownService {
|
|
|
25604
26024
|
priceStopLoss,
|
|
25605
26025
|
originalPriceTakeProfit,
|
|
25606
26026
|
originalPriceStopLoss,
|
|
26027
|
+
originalPriceOpen,
|
|
26028
|
+
totalEntries,
|
|
25607
26029
|
scheduledAt,
|
|
25608
26030
|
pendingAt,
|
|
25609
26031
|
});
|
|
@@ -25625,7 +26047,7 @@ class StrategyMarkdownService {
|
|
|
25625
26047
|
* @param scheduledAt - Signal creation timestamp in milliseconds
|
|
25626
26048
|
* @param pendingAt - Pending timestamp in milliseconds
|
|
25627
26049
|
*/
|
|
25628
|
-
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) => {
|
|
25629
26051
|
this.loggerService.log("strategyMarkdownService breakeven", {
|
|
25630
26052
|
symbol,
|
|
25631
26053
|
currentPrice,
|
|
@@ -25661,6 +26083,8 @@ class StrategyMarkdownService {
|
|
|
25661
26083
|
priceStopLoss,
|
|
25662
26084
|
originalPriceTakeProfit,
|
|
25663
26085
|
originalPriceStopLoss,
|
|
26086
|
+
originalPriceOpen,
|
|
26087
|
+
totalEntries,
|
|
25664
26088
|
scheduledAt,
|
|
25665
26089
|
pendingAt,
|
|
25666
26090
|
});
|
|
@@ -25683,7 +26107,7 @@ class StrategyMarkdownService {
|
|
|
25683
26107
|
* @param pendingAt - Pending timestamp in milliseconds
|
|
25684
26108
|
* @param activateId - Optional identifier for the activation reason
|
|
25685
26109
|
*/
|
|
25686
|
-
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) => {
|
|
25687
26111
|
this.loggerService.log("strategyMarkdownService activateScheduled", {
|
|
25688
26112
|
symbol,
|
|
25689
26113
|
currentPrice,
|
|
@@ -25721,6 +26145,72 @@ class StrategyMarkdownService {
|
|
|
25721
26145
|
priceStopLoss,
|
|
25722
26146
|
originalPriceTakeProfit,
|
|
25723
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,
|
|
25724
26214
|
scheduledAt,
|
|
25725
26215
|
pendingAt,
|
|
25726
26216
|
});
|
|
@@ -25869,43 +26359,50 @@ class StrategyMarkdownService {
|
|
|
25869
26359
|
exchangeName: event.exchangeName,
|
|
25870
26360
|
frameName: event.frameName,
|
|
25871
26361
|
strategyName: event.strategyName,
|
|
25872
|
-
}, 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));
|
|
25873
26363
|
const unPartialLoss = strategyCommitSubject
|
|
25874
26364
|
.filter(({ action }) => action === "partial-loss")
|
|
25875
26365
|
.connect(async (event) => await this.partialLoss(event.symbol, event.percentToClose, event.currentPrice, event.backtest, {
|
|
25876
26366
|
exchangeName: event.exchangeName,
|
|
25877
26367
|
frameName: event.frameName,
|
|
25878
26368
|
strategyName: event.strategyName,
|
|
25879
|
-
}, 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));
|
|
25880
26370
|
const unTrailingStop = strategyCommitSubject
|
|
25881
26371
|
.filter(({ action }) => action === "trailing-stop")
|
|
25882
26372
|
.connect(async (event) => await this.trailingStop(event.symbol, event.percentShift, event.currentPrice, event.backtest, {
|
|
25883
26373
|
exchangeName: event.exchangeName,
|
|
25884
26374
|
frameName: event.frameName,
|
|
25885
26375
|
strategyName: event.strategyName,
|
|
25886
|
-
}, 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));
|
|
25887
26377
|
const unTrailingTake = strategyCommitSubject
|
|
25888
26378
|
.filter(({ action }) => action === "trailing-take")
|
|
25889
26379
|
.connect(async (event) => await this.trailingTake(event.symbol, event.percentShift, event.currentPrice, event.backtest, {
|
|
25890
26380
|
exchangeName: event.exchangeName,
|
|
25891
26381
|
frameName: event.frameName,
|
|
25892
26382
|
strategyName: event.strategyName,
|
|
25893
|
-
}, 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));
|
|
25894
26384
|
const unBreakeven = strategyCommitSubject
|
|
25895
26385
|
.filter(({ action }) => action === "breakeven")
|
|
25896
26386
|
.connect(async (event) => await this.breakeven(event.symbol, event.currentPrice, event.backtest, {
|
|
25897
26387
|
exchangeName: event.exchangeName,
|
|
25898
26388
|
frameName: event.frameName,
|
|
25899
26389
|
strategyName: event.strategyName,
|
|
25900
|
-
}, 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));
|
|
25901
26391
|
const unActivateScheduled = strategyCommitSubject
|
|
25902
26392
|
.filter(({ action }) => action === "activate-scheduled")
|
|
25903
26393
|
.connect(async (event) => await this.activateScheduled(event.symbol, event.currentPrice, event.backtest, {
|
|
25904
26394
|
exchangeName: event.exchangeName,
|
|
25905
26395
|
frameName: event.frameName,
|
|
25906
26396
|
strategyName: event.strategyName,
|
|
25907
|
-
}, event.timestamp, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.activateId));
|
|
25908
|
-
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());
|
|
25909
26406
|
return () => {
|
|
25910
26407
|
disposeFn();
|
|
25911
26408
|
this.subscribe.clear();
|
|
@@ -27711,6 +28208,7 @@ const TRAILING_STOP_METHOD_NAME = "strategy.commitTrailingStop";
|
|
|
27711
28208
|
const TRAILING_PROFIT_METHOD_NAME = "strategy.commitTrailingTake";
|
|
27712
28209
|
const BREAKEVEN_METHOD_NAME = "strategy.commitBreakeven";
|
|
27713
28210
|
const ACTIVATE_SCHEDULED_METHOD_NAME = "strategy.commitActivateScheduled";
|
|
28211
|
+
const AVERAGE_BUY_METHOD_NAME = "strategy.commitAverageBuy";
|
|
27714
28212
|
/**
|
|
27715
28213
|
* Cancels the scheduled signal without stopping the strategy.
|
|
27716
28214
|
*
|
|
@@ -28063,6 +28561,45 @@ async function commitActivateScheduled(symbol, activateId) {
|
|
|
28063
28561
|
const { exchangeName, frameName, strategyName } = bt.methodContextService.context;
|
|
28064
28562
|
await bt.strategyCoreService.activateScheduled(isBacktest, symbol, { exchangeName, frameName, strategyName }, activateId);
|
|
28065
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
|
+
}
|
|
28066
28603
|
|
|
28067
28604
|
const STOP_STRATEGY_METHOD_NAME = "control.stopStrategy";
|
|
28068
28605
|
/**
|
|
@@ -30343,6 +30880,7 @@ const BACKTEST_METHOD_NAME_PARTIAL_LOSS = "BacktestUtils.commitPartialLoss";
|
|
|
30343
30880
|
const BACKTEST_METHOD_NAME_TRAILING_STOP = "BacktestUtils.commitTrailingStop";
|
|
30344
30881
|
const BACKTEST_METHOD_NAME_TRAILING_PROFIT = "BacktestUtils.commitTrailingTake";
|
|
30345
30882
|
const BACKTEST_METHOD_NAME_ACTIVATE_SCHEDULED = "Backtest.commitActivateScheduled";
|
|
30883
|
+
const BACKTEST_METHOD_NAME_AVERAGE_BUY = "Backtest.commitAverageBuy";
|
|
30346
30884
|
const BACKTEST_METHOD_NAME_GET_DATA = "BacktestUtils.getData";
|
|
30347
30885
|
/**
|
|
30348
30886
|
* Internal task function that runs backtest and handles completion.
|
|
@@ -31239,6 +31777,49 @@ class BacktestUtils {
|
|
|
31239
31777
|
}
|
|
31240
31778
|
await bt.strategyCoreService.activateScheduled(true, symbol, context, activateId);
|
|
31241
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
|
+
};
|
|
31242
31823
|
/**
|
|
31243
31824
|
* Gets statistical data from all closed signals for a symbol-strategy pair.
|
|
31244
31825
|
*
|
|
@@ -31415,6 +31996,7 @@ const LIVE_METHOD_NAME_PARTIAL_LOSS = "LiveUtils.commitPartialLoss";
|
|
|
31415
31996
|
const LIVE_METHOD_NAME_TRAILING_STOP = "LiveUtils.commitTrailingStop";
|
|
31416
31997
|
const LIVE_METHOD_NAME_TRAILING_PROFIT = "LiveUtils.commitTrailingTake";
|
|
31417
31998
|
const LIVE_METHOD_NAME_ACTIVATE_SCHEDULED = "Live.commitActivateScheduled";
|
|
31999
|
+
const LIVE_METHOD_NAME_AVERAGE_BUY = "Live.commitAverageBuy";
|
|
31418
32000
|
/**
|
|
31419
32001
|
* Internal task function that runs live trading and handles completion.
|
|
31420
32002
|
* Consumes live trading results and updates instance state flags.
|
|
@@ -32279,6 +32861,49 @@ class LiveUtils {
|
|
|
32279
32861
|
frameName: "",
|
|
32280
32862
|
}, activateId);
|
|
32281
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
|
+
};
|
|
32282
32907
|
/**
|
|
32283
32908
|
* Gets statistical data from all live trading events for a symbol-strategy pair.
|
|
32284
32909
|
*
|
|
@@ -34942,6 +35567,8 @@ const CREATE_SIGNAL_NOTIFICATION_FN = (data) => {
|
|
|
34942
35567
|
priceStopLoss: data.signal.priceStopLoss,
|
|
34943
35568
|
originalPriceTakeProfit: data.signal.originalPriceTakeProfit,
|
|
34944
35569
|
originalPriceStopLoss: data.signal.originalPriceStopLoss,
|
|
35570
|
+
originalPriceOpen: data.signal.originalPriceOpen,
|
|
35571
|
+
totalEntries: data.signal.totalEntries,
|
|
34945
35572
|
note: data.signal.note,
|
|
34946
35573
|
scheduledAt: data.signal.scheduledAt,
|
|
34947
35574
|
pendingAt: data.signal.pendingAt,
|
|
@@ -34967,6 +35594,8 @@ const CREATE_SIGNAL_NOTIFICATION_FN = (data) => {
|
|
|
34967
35594
|
priceStopLoss: data.signal.priceStopLoss,
|
|
34968
35595
|
originalPriceTakeProfit: data.signal.originalPriceTakeProfit,
|
|
34969
35596
|
originalPriceStopLoss: data.signal.originalPriceStopLoss,
|
|
35597
|
+
originalPriceOpen: data.signal.originalPriceOpen,
|
|
35598
|
+
totalEntries: data.signal.totalEntries,
|
|
34970
35599
|
pnlPercentage: data.pnl.pnlPercentage,
|
|
34971
35600
|
closeReason: data.closeReason,
|
|
34972
35601
|
duration: durationMin,
|
|
@@ -34992,6 +35621,8 @@ const CREATE_SIGNAL_NOTIFICATION_FN = (data) => {
|
|
|
34992
35621
|
priceStopLoss: data.signal.priceStopLoss,
|
|
34993
35622
|
originalPriceTakeProfit: data.signal.originalPriceTakeProfit,
|
|
34994
35623
|
originalPriceStopLoss: data.signal.originalPriceStopLoss,
|
|
35624
|
+
originalPriceOpen: data.signal.originalPriceOpen,
|
|
35625
|
+
totalEntries: data.signal.totalEntries,
|
|
34995
35626
|
scheduledAt: data.signal.scheduledAt,
|
|
34996
35627
|
currentPrice: data.currentPrice,
|
|
34997
35628
|
createdAt: data.createdAt,
|
|
@@ -35015,6 +35646,8 @@ const CREATE_SIGNAL_NOTIFICATION_FN = (data) => {
|
|
|
35015
35646
|
priceStopLoss: data.signal.priceStopLoss,
|
|
35016
35647
|
originalPriceTakeProfit: data.signal.originalPriceTakeProfit,
|
|
35017
35648
|
originalPriceStopLoss: data.signal.originalPriceStopLoss,
|
|
35649
|
+
originalPriceOpen: data.signal.originalPriceOpen,
|
|
35650
|
+
totalEntries: data.signal.totalEntries,
|
|
35018
35651
|
cancelReason: data.reason,
|
|
35019
35652
|
cancelId: data.cancelId,
|
|
35020
35653
|
duration: durationMin,
|
|
@@ -35047,6 +35680,8 @@ const CREATE_PARTIAL_PROFIT_NOTIFICATION_FN = (data) => ({
|
|
|
35047
35680
|
priceStopLoss: data.data.priceStopLoss,
|
|
35048
35681
|
originalPriceTakeProfit: data.data.originalPriceTakeProfit,
|
|
35049
35682
|
originalPriceStopLoss: data.data.originalPriceStopLoss,
|
|
35683
|
+
originalPriceOpen: data.data.originalPriceOpen,
|
|
35684
|
+
totalEntries: data.data.totalEntries,
|
|
35050
35685
|
scheduledAt: data.data.scheduledAt,
|
|
35051
35686
|
pendingAt: data.data.pendingAt,
|
|
35052
35687
|
createdAt: data.timestamp,
|
|
@@ -35073,6 +35708,8 @@ const CREATE_PARTIAL_LOSS_NOTIFICATION_FN = (data) => ({
|
|
|
35073
35708
|
priceStopLoss: data.data.priceStopLoss,
|
|
35074
35709
|
originalPriceTakeProfit: data.data.originalPriceTakeProfit,
|
|
35075
35710
|
originalPriceStopLoss: data.data.originalPriceStopLoss,
|
|
35711
|
+
originalPriceOpen: data.data.originalPriceOpen,
|
|
35712
|
+
totalEntries: data.data.totalEntries,
|
|
35076
35713
|
scheduledAt: data.data.scheduledAt,
|
|
35077
35714
|
pendingAt: data.data.pendingAt,
|
|
35078
35715
|
createdAt: data.timestamp,
|
|
@@ -35098,6 +35735,8 @@ const CREATE_BREAKEVEN_NOTIFICATION_FN = (data) => ({
|
|
|
35098
35735
|
priceStopLoss: data.data.priceStopLoss,
|
|
35099
35736
|
originalPriceTakeProfit: data.data.originalPriceTakeProfit,
|
|
35100
35737
|
originalPriceStopLoss: data.data.originalPriceStopLoss,
|
|
35738
|
+
originalPriceOpen: data.data.originalPriceOpen,
|
|
35739
|
+
totalEntries: data.data.totalEntries,
|
|
35101
35740
|
scheduledAt: data.data.scheduledAt,
|
|
35102
35741
|
pendingAt: data.data.pendingAt,
|
|
35103
35742
|
createdAt: data.timestamp,
|
|
@@ -35128,6 +35767,8 @@ const CREATE_STRATEGY_COMMIT_NOTIFICATION_FN = (data) => {
|
|
|
35128
35767
|
priceStopLoss: data.priceStopLoss,
|
|
35129
35768
|
originalPriceTakeProfit: data.originalPriceTakeProfit,
|
|
35130
35769
|
originalPriceStopLoss: data.originalPriceStopLoss,
|
|
35770
|
+
originalPriceOpen: data.originalPriceOpen,
|
|
35771
|
+
totalEntries: data.totalEntries,
|
|
35131
35772
|
scheduledAt: data.scheduledAt,
|
|
35132
35773
|
pendingAt: data.pendingAt,
|
|
35133
35774
|
createdAt: data.timestamp,
|
|
@@ -35151,6 +35792,8 @@ const CREATE_STRATEGY_COMMIT_NOTIFICATION_FN = (data) => {
|
|
|
35151
35792
|
priceStopLoss: data.priceStopLoss,
|
|
35152
35793
|
originalPriceTakeProfit: data.originalPriceTakeProfit,
|
|
35153
35794
|
originalPriceStopLoss: data.originalPriceStopLoss,
|
|
35795
|
+
originalPriceOpen: data.originalPriceOpen,
|
|
35796
|
+
totalEntries: data.totalEntries,
|
|
35154
35797
|
scheduledAt: data.scheduledAt,
|
|
35155
35798
|
pendingAt: data.pendingAt,
|
|
35156
35799
|
createdAt: data.timestamp,
|
|
@@ -35173,6 +35816,8 @@ const CREATE_STRATEGY_COMMIT_NOTIFICATION_FN = (data) => {
|
|
|
35173
35816
|
priceStopLoss: data.priceStopLoss,
|
|
35174
35817
|
originalPriceTakeProfit: data.originalPriceTakeProfit,
|
|
35175
35818
|
originalPriceStopLoss: data.originalPriceStopLoss,
|
|
35819
|
+
originalPriceOpen: data.originalPriceOpen,
|
|
35820
|
+
totalEntries: data.totalEntries,
|
|
35176
35821
|
scheduledAt: data.scheduledAt,
|
|
35177
35822
|
pendingAt: data.pendingAt,
|
|
35178
35823
|
createdAt: data.timestamp,
|
|
@@ -35196,6 +35841,8 @@ const CREATE_STRATEGY_COMMIT_NOTIFICATION_FN = (data) => {
|
|
|
35196
35841
|
priceStopLoss: data.priceStopLoss,
|
|
35197
35842
|
originalPriceTakeProfit: data.originalPriceTakeProfit,
|
|
35198
35843
|
originalPriceStopLoss: data.originalPriceStopLoss,
|
|
35844
|
+
originalPriceOpen: data.originalPriceOpen,
|
|
35845
|
+
totalEntries: data.totalEntries,
|
|
35199
35846
|
scheduledAt: data.scheduledAt,
|
|
35200
35847
|
pendingAt: data.pendingAt,
|
|
35201
35848
|
createdAt: data.timestamp,
|
|
@@ -35219,6 +35866,8 @@ const CREATE_STRATEGY_COMMIT_NOTIFICATION_FN = (data) => {
|
|
|
35219
35866
|
priceStopLoss: data.priceStopLoss,
|
|
35220
35867
|
originalPriceTakeProfit: data.originalPriceTakeProfit,
|
|
35221
35868
|
originalPriceStopLoss: data.originalPriceStopLoss,
|
|
35869
|
+
originalPriceOpen: data.originalPriceOpen,
|
|
35870
|
+
totalEntries: data.totalEntries,
|
|
35222
35871
|
scheduledAt: data.scheduledAt,
|
|
35223
35872
|
pendingAt: data.pendingAt,
|
|
35224
35873
|
createdAt: data.timestamp,
|
|
@@ -35242,6 +35891,33 @@ const CREATE_STRATEGY_COMMIT_NOTIFICATION_FN = (data) => {
|
|
|
35242
35891
|
priceStopLoss: data.priceStopLoss,
|
|
35243
35892
|
originalPriceTakeProfit: data.originalPriceTakeProfit,
|
|
35244
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,
|
|
35245
35921
|
scheduledAt: data.scheduledAt,
|
|
35246
35922
|
pendingAt: data.pendingAt,
|
|
35247
35923
|
createdAt: data.timestamp,
|
|
@@ -37584,4 +38260,4 @@ const set = (object, path, value) => {
|
|
|
37584
38260
|
}
|
|
37585
38261
|
};
|
|
37586
38262
|
|
|
37587
|
-
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 };
|