backtest-kit 1.5.47 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.cjs +648 -12
- package/build/index.mjs +647 -13
- package/package.json +1 -1
- package/types.d.ts +322 -2
package/build/index.cjs
CHANGED
|
@@ -1746,6 +1746,12 @@ const partialLossSubject = new functoolsKit.Subject();
|
|
|
1746
1746
|
* Does not emit for allowed signals (prevents spam).
|
|
1747
1747
|
*/
|
|
1748
1748
|
const riskSubject = new functoolsKit.Subject();
|
|
1749
|
+
/**
|
|
1750
|
+
* Ping emitter for scheduled signal monitoring events.
|
|
1751
|
+
* Emits every minute when a scheduled signal is being monitored (waiting for activation).
|
|
1752
|
+
* Allows users to track scheduled signal lifecycle and implement custom cancellation logic.
|
|
1753
|
+
*/
|
|
1754
|
+
const pingSubject = new functoolsKit.Subject();
|
|
1749
1755
|
|
|
1750
1756
|
var emitters = /*#__PURE__*/Object.freeze({
|
|
1751
1757
|
__proto__: null,
|
|
@@ -1757,6 +1763,7 @@ var emitters = /*#__PURE__*/Object.freeze({
|
|
|
1757
1763
|
partialLossSubject: partialLossSubject,
|
|
1758
1764
|
partialProfitSubject: partialProfitSubject,
|
|
1759
1765
|
performanceEmitter: performanceEmitter,
|
|
1766
|
+
pingSubject: pingSubject,
|
|
1760
1767
|
progressBacktestEmitter: progressBacktestEmitter,
|
|
1761
1768
|
progressOptimizerEmitter: progressOptimizerEmitter,
|
|
1762
1769
|
progressWalkerEmitter: progressWalkerEmitter,
|
|
@@ -2245,6 +2252,7 @@ const CHECK_SCHEDULED_SIGNAL_TIMEOUT_FN = async (self, scheduled, currentPrice)
|
|
|
2245
2252
|
exchangeName: self.params.method.context.exchangeName,
|
|
2246
2253
|
symbol: self.params.execution.context.symbol,
|
|
2247
2254
|
backtest: self.params.execution.context.backtest,
|
|
2255
|
+
reason: "timeout",
|
|
2248
2256
|
};
|
|
2249
2257
|
if (self.params.callbacks?.onTick) {
|
|
2250
2258
|
self.params.callbacks.onTick(self.params.execution.context.symbol, result, self.params.execution.context.backtest);
|
|
@@ -2281,7 +2289,7 @@ const CHECK_SCHEDULED_SIGNAL_PRICE_ACTIVATION_FN = (scheduled, currentPrice) =>
|
|
|
2281
2289
|
return { shouldActivate, shouldCancel };
|
|
2282
2290
|
};
|
|
2283
2291
|
const CANCEL_SCHEDULED_SIGNAL_BY_STOPLOSS_FN = async (self, scheduled, currentPrice) => {
|
|
2284
|
-
self.params.logger.info("ClientStrategy scheduled signal cancelled", {
|
|
2292
|
+
self.params.logger.info("ClientStrategy scheduled signal cancelled by StopLoss", {
|
|
2285
2293
|
symbol: self.params.execution.context.symbol,
|
|
2286
2294
|
signalId: scheduled.id,
|
|
2287
2295
|
position: scheduled.position,
|
|
@@ -2289,14 +2297,19 @@ const CANCEL_SCHEDULED_SIGNAL_BY_STOPLOSS_FN = async (self, scheduled, currentPr
|
|
|
2289
2297
|
priceStopLoss: scheduled.priceStopLoss,
|
|
2290
2298
|
});
|
|
2291
2299
|
await self.setScheduledSignal(null);
|
|
2300
|
+
if (self.params.callbacks?.onCancel) {
|
|
2301
|
+
self.params.callbacks.onCancel(self.params.execution.context.symbol, scheduled, currentPrice, self.params.execution.context.backtest);
|
|
2302
|
+
}
|
|
2292
2303
|
const result = {
|
|
2293
|
-
action: "
|
|
2294
|
-
signal:
|
|
2304
|
+
action: "cancelled",
|
|
2305
|
+
signal: scheduled,
|
|
2306
|
+
currentPrice: currentPrice,
|
|
2307
|
+
closeTimestamp: self.params.execution.context.when.getTime(),
|
|
2295
2308
|
strategyName: self.params.method.context.strategyName,
|
|
2296
2309
|
exchangeName: self.params.method.context.exchangeName,
|
|
2297
2310
|
symbol: self.params.execution.context.symbol,
|
|
2298
|
-
currentPrice: currentPrice,
|
|
2299
2311
|
backtest: self.params.execution.context.backtest,
|
|
2312
|
+
reason: "price_reject",
|
|
2300
2313
|
};
|
|
2301
2314
|
if (self.params.callbacks?.onTick) {
|
|
2302
2315
|
self.params.callbacks.onTick(self.params.execution.context.symbol, result, self.params.execution.context.backtest);
|
|
@@ -2371,7 +2384,16 @@ const ACTIVATE_SCHEDULED_SIGNAL_FN = async (self, scheduled, activationTimestamp
|
|
|
2371
2384
|
}
|
|
2372
2385
|
return result;
|
|
2373
2386
|
};
|
|
2387
|
+
const CALL_PING_CALLBACKS_FN = async (self, scheduled, timestamp) => {
|
|
2388
|
+
// Call system onPing callback first (emits to pingSubject)
|
|
2389
|
+
await self.params.onPing(self.params.execution.context.symbol, self.params.method.context.strategyName, self.params.method.context.exchangeName, scheduled, self.params.execution.context.backtest, timestamp);
|
|
2390
|
+
// Call user onPing callback only if signal is still active (not cancelled, not activated)
|
|
2391
|
+
if (self.params.callbacks?.onPing) {
|
|
2392
|
+
await self.params.callbacks.onPing(self.params.execution.context.symbol, scheduled, new Date(timestamp), self.params.execution.context.backtest);
|
|
2393
|
+
}
|
|
2394
|
+
};
|
|
2374
2395
|
const RETURN_SCHEDULED_SIGNAL_ACTIVE_FN = async (self, scheduled, currentPrice) => {
|
|
2396
|
+
await CALL_PING_CALLBACKS_FN(self, scheduled, self.params.execution.context.when.getTime());
|
|
2375
2397
|
const result = {
|
|
2376
2398
|
action: "active",
|
|
2377
2399
|
signal: scheduled,
|
|
@@ -2599,13 +2621,14 @@ const RETURN_IDLE_FN = async (self, currentPrice) => {
|
|
|
2599
2621
|
}
|
|
2600
2622
|
return result;
|
|
2601
2623
|
};
|
|
2602
|
-
const CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN = async (self, scheduled, averagePrice, closeTimestamp) => {
|
|
2624
|
+
const CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN = async (self, scheduled, averagePrice, closeTimestamp, reason) => {
|
|
2603
2625
|
self.params.logger.info("ClientStrategy backtest scheduled signal cancelled", {
|
|
2604
2626
|
symbol: self.params.execution.context.symbol,
|
|
2605
2627
|
signalId: scheduled.id,
|
|
2606
2628
|
closeTimestamp,
|
|
2607
2629
|
averagePrice,
|
|
2608
2630
|
priceStopLoss: scheduled.priceStopLoss,
|
|
2631
|
+
reason,
|
|
2609
2632
|
});
|
|
2610
2633
|
await self.setScheduledSignal(null);
|
|
2611
2634
|
if (self.params.callbacks?.onCancel) {
|
|
@@ -2620,6 +2643,7 @@ const CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN = async (self, scheduled, averagePr
|
|
|
2620
2643
|
exchangeName: self.params.method.context.exchangeName,
|
|
2621
2644
|
symbol: self.params.execution.context.symbol,
|
|
2622
2645
|
backtest: self.params.execution.context.backtest,
|
|
2646
|
+
reason,
|
|
2623
2647
|
};
|
|
2624
2648
|
if (self.params.callbacks?.onTick) {
|
|
2625
2649
|
self.params.callbacks.onTick(self.params.execution.context.symbol, result, self.params.execution.context.backtest);
|
|
@@ -2735,10 +2759,16 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
|
|
|
2735
2759
|
}
|
|
2736
2760
|
const recentCandles = candles.slice(Math.max(0, i - (candlesCount - 1)), i + 1);
|
|
2737
2761
|
const averagePrice = GET_AVG_PRICE_FN(recentCandles);
|
|
2762
|
+
// КРИТИЧНО: Проверяем был ли сигнал отменен пользователем через cancel()
|
|
2763
|
+
if (self._cancelledSignal) {
|
|
2764
|
+
// Сигнал был отменен через cancel() в onPing
|
|
2765
|
+
const result = await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, averagePrice, candle.timestamp, "user");
|
|
2766
|
+
return { activated: false, cancelled: true, activationIndex: i, result };
|
|
2767
|
+
}
|
|
2738
2768
|
// КРИТИЧНО: Проверяем timeout ПЕРЕД проверкой цены
|
|
2739
2769
|
const elapsedTime = candle.timestamp - scheduled.scheduledAt;
|
|
2740
2770
|
if (elapsedTime >= maxTimeToWait) {
|
|
2741
|
-
const result = await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, averagePrice, candle.timestamp);
|
|
2771
|
+
const result = await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, averagePrice, candle.timestamp, "timeout");
|
|
2742
2772
|
return { activated: false, cancelled: true, activationIndex: i, result };
|
|
2743
2773
|
}
|
|
2744
2774
|
let shouldActivate = false;
|
|
@@ -2776,7 +2806,7 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
|
|
|
2776
2806
|
}
|
|
2777
2807
|
}
|
|
2778
2808
|
if (shouldCancel) {
|
|
2779
|
-
const result = await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, averagePrice, candle.timestamp);
|
|
2809
|
+
const result = await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, averagePrice, candle.timestamp, "price_reject");
|
|
2780
2810
|
return { activated: false, cancelled: true, activationIndex: i, result };
|
|
2781
2811
|
}
|
|
2782
2812
|
if (shouldActivate) {
|
|
@@ -2788,6 +2818,7 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
|
|
|
2788
2818
|
result: null,
|
|
2789
2819
|
};
|
|
2790
2820
|
}
|
|
2821
|
+
await CALL_PING_CALLBACKS_FN(self, scheduled, candle.timestamp);
|
|
2791
2822
|
}
|
|
2792
2823
|
return {
|
|
2793
2824
|
activated: false,
|
|
@@ -2941,8 +2972,9 @@ class ClientStrategy {
|
|
|
2941
2972
|
this.params = params;
|
|
2942
2973
|
this._isStopped = false;
|
|
2943
2974
|
this._pendingSignal = null;
|
|
2944
|
-
this._scheduledSignal = null;
|
|
2945
2975
|
this._lastSignalTimestamp = null;
|
|
2976
|
+
this._scheduledSignal = null;
|
|
2977
|
+
this._cancelledSignal = null;
|
|
2946
2978
|
/**
|
|
2947
2979
|
* Initializes strategy state by loading persisted signal from disk.
|
|
2948
2980
|
*
|
|
@@ -3009,6 +3041,18 @@ class ClientStrategy {
|
|
|
3009
3041
|
});
|
|
3010
3042
|
return this._pendingSignal;
|
|
3011
3043
|
}
|
|
3044
|
+
/**
|
|
3045
|
+
* Retrieves the current scheduled signal.
|
|
3046
|
+
* If no scheduled signal exists, returns null.
|
|
3047
|
+
* @returns Promise resolving to the scheduled signal or null.
|
|
3048
|
+
*/
|
|
3049
|
+
async getScheduledSignal(symbol, strategyName) {
|
|
3050
|
+
this.params.logger.debug("ClientStrategy getScheduledSignal", {
|
|
3051
|
+
symbol,
|
|
3052
|
+
strategyName,
|
|
3053
|
+
});
|
|
3054
|
+
return this._scheduledSignal;
|
|
3055
|
+
}
|
|
3012
3056
|
/**
|
|
3013
3057
|
* Returns the stopped state of the strategy.
|
|
3014
3058
|
*
|
|
@@ -3068,6 +3112,33 @@ class ClientStrategy {
|
|
|
3068
3112
|
const currentPrice = await this.params.exchange.getAveragePrice(this.params.execution.context.symbol);
|
|
3069
3113
|
return await RETURN_IDLE_FN(this, currentPrice);
|
|
3070
3114
|
}
|
|
3115
|
+
// Check if scheduled signal was cancelled - emit cancelled event once
|
|
3116
|
+
if (this._cancelledSignal) {
|
|
3117
|
+
const currentPrice = await this.params.exchange.getAveragePrice(this.params.execution.context.symbol);
|
|
3118
|
+
const cancelledSignal = this._cancelledSignal;
|
|
3119
|
+
this._cancelledSignal = null; // Clear after emitting
|
|
3120
|
+
this.params.logger.info("ClientStrategy tick: scheduled signal was cancelled", {
|
|
3121
|
+
symbol: this.params.execution.context.symbol,
|
|
3122
|
+
signalId: cancelledSignal.id,
|
|
3123
|
+
});
|
|
3124
|
+
// Call onCancel callback
|
|
3125
|
+
if (this.params.callbacks?.onCancel) {
|
|
3126
|
+
this.params.callbacks.onCancel(this.params.execution.context.symbol, cancelledSignal, currentPrice, this.params.execution.context.backtest);
|
|
3127
|
+
}
|
|
3128
|
+
const result = {
|
|
3129
|
+
action: "cancelled",
|
|
3130
|
+
signal: cancelledSignal,
|
|
3131
|
+
currentPrice,
|
|
3132
|
+
closeTimestamp: currentTime,
|
|
3133
|
+
strategyName: this.params.method.context.strategyName,
|
|
3134
|
+
exchangeName: this.params.method.context.exchangeName,
|
|
3135
|
+
symbol: this.params.execution.context.symbol,
|
|
3136
|
+
backtest: this.params.execution.context.backtest,
|
|
3137
|
+
reason: "user",
|
|
3138
|
+
cancelId: cancelledSignal.cancelId,
|
|
3139
|
+
};
|
|
3140
|
+
return result;
|
|
3141
|
+
}
|
|
3071
3142
|
// Monitor scheduled signal
|
|
3072
3143
|
if (this._scheduledSignal && !this._pendingSignal) {
|
|
3073
3144
|
const currentPrice = await this.params.exchange.getAveragePrice(this.params.execution.context.symbol);
|
|
@@ -3158,6 +3229,29 @@ class ClientStrategy {
|
|
|
3158
3229
|
if (!this.params.execution.context.backtest) {
|
|
3159
3230
|
throw new Error("ClientStrategy backtest: running in live context");
|
|
3160
3231
|
}
|
|
3232
|
+
// If signal was cancelled - return cancelled
|
|
3233
|
+
if (this._cancelledSignal) {
|
|
3234
|
+
this.params.logger.debug("ClientStrategy backtest: no signal (cancelled or not created)");
|
|
3235
|
+
const currentPrice = await this.params.exchange.getAveragePrice(symbol);
|
|
3236
|
+
const cancelledSignal = this._cancelledSignal;
|
|
3237
|
+
this._cancelledSignal = null; // Clear after using
|
|
3238
|
+
if (this.params.callbacks?.onCancel) {
|
|
3239
|
+
this.params.callbacks.onCancel(this.params.execution.context.symbol, cancelledSignal, currentPrice, this.params.execution.context.backtest);
|
|
3240
|
+
}
|
|
3241
|
+
const cancelledResult = {
|
|
3242
|
+
action: "cancelled",
|
|
3243
|
+
signal: cancelledSignal,
|
|
3244
|
+
currentPrice,
|
|
3245
|
+
closeTimestamp: this.params.execution.context.when.getTime(),
|
|
3246
|
+
strategyName: this.params.method.context.strategyName,
|
|
3247
|
+
exchangeName: this.params.method.context.exchangeName,
|
|
3248
|
+
symbol: this.params.execution.context.symbol,
|
|
3249
|
+
backtest: true,
|
|
3250
|
+
reason: "user",
|
|
3251
|
+
cancelId: cancelledSignal.cancelId,
|
|
3252
|
+
};
|
|
3253
|
+
return cancelledResult;
|
|
3254
|
+
}
|
|
3161
3255
|
if (!this._pendingSignal && !this._scheduledSignal) {
|
|
3162
3256
|
throw new Error("ClientStrategy backtest: no pending or scheduled signal");
|
|
3163
3257
|
}
|
|
@@ -3230,7 +3324,7 @@ class ClientStrategy {
|
|
|
3230
3324
|
maxMinutes: GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES,
|
|
3231
3325
|
reason: "timeout - price never reached priceOpen",
|
|
3232
3326
|
});
|
|
3233
|
-
return await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(this, scheduled, lastPrice, lastCandleTimestamp);
|
|
3327
|
+
return await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(this, scheduled, lastPrice, lastCandleTimestamp, "timeout");
|
|
3234
3328
|
}
|
|
3235
3329
|
}
|
|
3236
3330
|
// Process pending signal
|
|
@@ -3288,6 +3382,47 @@ class ClientStrategy {
|
|
|
3288
3382
|
}
|
|
3289
3383
|
await PersistScheduleAdapter.writeScheduleData(this._scheduledSignal, symbol, strategyName);
|
|
3290
3384
|
}
|
|
3385
|
+
/**
|
|
3386
|
+
* Cancels the scheduled signal without stopping the strategy.
|
|
3387
|
+
*
|
|
3388
|
+
* Clears the scheduled signal (waiting for priceOpen activation).
|
|
3389
|
+
* Does NOT affect active pending signals or strategy operation.
|
|
3390
|
+
* Does NOT set stop flag - strategy can continue generating new signals.
|
|
3391
|
+
*
|
|
3392
|
+
* Use case: Cancel a scheduled entry that is no longer desired without stopping the entire strategy.
|
|
3393
|
+
*
|
|
3394
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
3395
|
+
* @param strategyName - Name of the strategy
|
|
3396
|
+
* @param backtest - Whether running in backtest mode
|
|
3397
|
+
* @returns Promise that resolves when scheduled signal is cleared
|
|
3398
|
+
*
|
|
3399
|
+
* @example
|
|
3400
|
+
* ```typescript
|
|
3401
|
+
* // Cancel scheduled signal without stopping strategy
|
|
3402
|
+
* await strategy.cancel("BTCUSDT", "my-strategy", false);
|
|
3403
|
+
* // Strategy continues, can generate new signals
|
|
3404
|
+
* ```
|
|
3405
|
+
*/
|
|
3406
|
+
async cancel(symbol, strategyName, backtest, cancelId) {
|
|
3407
|
+
this.params.logger.debug("ClientStrategy cancel", {
|
|
3408
|
+
symbol,
|
|
3409
|
+
strategyName,
|
|
3410
|
+
hasScheduledSignal: this._scheduledSignal !== null,
|
|
3411
|
+
backtest,
|
|
3412
|
+
cancelId,
|
|
3413
|
+
});
|
|
3414
|
+
// Save cancelled signal for next tick to emit cancelled event
|
|
3415
|
+
if (this._scheduledSignal) {
|
|
3416
|
+
this._cancelledSignal = Object.assign({}, this._scheduledSignal, {
|
|
3417
|
+
cancelId,
|
|
3418
|
+
});
|
|
3419
|
+
this._scheduledSignal = null;
|
|
3420
|
+
}
|
|
3421
|
+
if (backtest) {
|
|
3422
|
+
return;
|
|
3423
|
+
}
|
|
3424
|
+
await PersistScheduleAdapter.writeScheduleData(this._scheduledSignal, symbol, strategyName);
|
|
3425
|
+
}
|
|
3291
3426
|
}
|
|
3292
3427
|
|
|
3293
3428
|
const RISK_METHOD_NAME_GET_DATA = "RiskUtils.getData";
|
|
@@ -3614,6 +3749,27 @@ const GET_RISK_FN = (dto, backtest, self) => {
|
|
|
3614
3749
|
...dto.riskList.map((riskName) => self.riskConnectionService.getRisk(riskName, backtest)),
|
|
3615
3750
|
]);
|
|
3616
3751
|
};
|
|
3752
|
+
/**
|
|
3753
|
+
* Callback function for emitting ping events to pingSubject.
|
|
3754
|
+
*
|
|
3755
|
+
* Called by ClientStrategy when a scheduled signal is being monitored every minute.
|
|
3756
|
+
* Emits PingContract event to all subscribers.
|
|
3757
|
+
*
|
|
3758
|
+
* @param symbol - Trading pair symbol
|
|
3759
|
+
* @param strategyName - Strategy name that is monitoring this scheduled signal
|
|
3760
|
+
* @param exchangeName - Exchange name where this scheduled signal is being executed
|
|
3761
|
+
* @param data - Scheduled signal row data
|
|
3762
|
+
* @param backtest - True if backtest mode
|
|
3763
|
+
* @param timestamp - Event timestamp in milliseconds
|
|
3764
|
+
*/
|
|
3765
|
+
const COMMIT_PING_FN = async (symbol, strategyName, exchangeName, data, backtest, timestamp) => await pingSubject.next({
|
|
3766
|
+
symbol,
|
|
3767
|
+
strategyName,
|
|
3768
|
+
exchangeName,
|
|
3769
|
+
data,
|
|
3770
|
+
backtest,
|
|
3771
|
+
timestamp,
|
|
3772
|
+
});
|
|
3617
3773
|
/**
|
|
3618
3774
|
* Connection service routing strategy operations to correct ClientStrategy instance.
|
|
3619
3775
|
*
|
|
@@ -3671,6 +3827,7 @@ class StrategyConnectionService {
|
|
|
3671
3827
|
strategyName,
|
|
3672
3828
|
getSignal,
|
|
3673
3829
|
callbacks,
|
|
3830
|
+
onPing: COMMIT_PING_FN,
|
|
3674
3831
|
});
|
|
3675
3832
|
});
|
|
3676
3833
|
/**
|
|
@@ -3692,6 +3849,25 @@ class StrategyConnectionService {
|
|
|
3692
3849
|
const strategy = this.getStrategy(symbol, strategyName, backtest);
|
|
3693
3850
|
return await strategy.getPendingSignal(symbol, strategyName);
|
|
3694
3851
|
};
|
|
3852
|
+
/**
|
|
3853
|
+
* Retrieves the currently active scheduled signal for the strategy.
|
|
3854
|
+
* If no scheduled signal exists, returns null.
|
|
3855
|
+
* Used internally for monitoring scheduled signal activation.
|
|
3856
|
+
*
|
|
3857
|
+
* @param symbol - Trading pair symbol
|
|
3858
|
+
* @param strategyName - Name of strategy to get scheduled signal for
|
|
3859
|
+
*
|
|
3860
|
+
* @returns Promise resolving to scheduled signal or null
|
|
3861
|
+
*/
|
|
3862
|
+
this.getScheduledSignal = async (backtest, symbol, strategyName) => {
|
|
3863
|
+
this.loggerService.log("strategyConnectionService getScheduledSignal", {
|
|
3864
|
+
symbol,
|
|
3865
|
+
strategyName,
|
|
3866
|
+
backtest,
|
|
3867
|
+
});
|
|
3868
|
+
const strategy = this.getStrategy(symbol, strategyName, backtest);
|
|
3869
|
+
return await strategy.getScheduledSignal(symbol, strategyName);
|
|
3870
|
+
};
|
|
3695
3871
|
/**
|
|
3696
3872
|
* Retrieves the stopped state of the strategy.
|
|
3697
3873
|
*
|
|
@@ -3807,6 +3983,28 @@ class StrategyConnectionService {
|
|
|
3807
3983
|
this.getStrategy.clear();
|
|
3808
3984
|
}
|
|
3809
3985
|
};
|
|
3986
|
+
/**
|
|
3987
|
+
* Cancels the scheduled signal for the specified strategy.
|
|
3988
|
+
*
|
|
3989
|
+
* Delegates to ClientStrategy.cancel() which clears the scheduled signal
|
|
3990
|
+
* without stopping the strategy or affecting pending signals.
|
|
3991
|
+
*
|
|
3992
|
+
* Note: Cancelled event will be emitted on next tick() call when strategy
|
|
3993
|
+
* detects the scheduled signal was cancelled.
|
|
3994
|
+
*
|
|
3995
|
+
* @param backtest - Whether running in backtest mode
|
|
3996
|
+
* @param ctx - Context with symbol and strategyName
|
|
3997
|
+
* @param cancelId - Optional cancellation ID for user-initiated cancellations
|
|
3998
|
+
* @returns Promise that resolves when scheduled signal is cancelled
|
|
3999
|
+
*/
|
|
4000
|
+
this.cancel = async (backtest, ctx, cancelId) => {
|
|
4001
|
+
this.loggerService.log("strategyConnectionService cancel", {
|
|
4002
|
+
ctx,
|
|
4003
|
+
cancelId,
|
|
4004
|
+
});
|
|
4005
|
+
const strategy = this.getStrategy(ctx.symbol, ctx.strategyName, backtest);
|
|
4006
|
+
await strategy.cancel(ctx.symbol, ctx.strategyName, backtest, cancelId);
|
|
4007
|
+
};
|
|
3810
4008
|
}
|
|
3811
4009
|
}
|
|
3812
4010
|
|
|
@@ -4752,6 +4950,26 @@ class StrategyCoreService {
|
|
|
4752
4950
|
await this.validate(symbol, strategyName);
|
|
4753
4951
|
return await this.strategyConnectionService.getPendingSignal(backtest, symbol, strategyName);
|
|
4754
4952
|
};
|
|
4953
|
+
/**
|
|
4954
|
+
* Retrieves the currently active scheduled signal for the symbol.
|
|
4955
|
+
* If no scheduled signal exists, returns null.
|
|
4956
|
+
* Used internally for monitoring scheduled signal activation.
|
|
4957
|
+
*
|
|
4958
|
+
* @param symbol - Trading pair symbol
|
|
4959
|
+
* @param strategyName - Name of the strategy
|
|
4960
|
+
* @returns Promise resolving to scheduled signal or null
|
|
4961
|
+
*/
|
|
4962
|
+
this.getScheduledSignal = async (backtest, symbol, strategyName) => {
|
|
4963
|
+
this.loggerService.log("strategyCoreService getScheduledSignal", {
|
|
4964
|
+
symbol,
|
|
4965
|
+
strategyName,
|
|
4966
|
+
});
|
|
4967
|
+
if (!MethodContextService.hasContext()) {
|
|
4968
|
+
throw new Error("strategyCoreService getScheduledSignal requires a method context");
|
|
4969
|
+
}
|
|
4970
|
+
await this.validate(symbol, strategyName);
|
|
4971
|
+
return await this.strategyConnectionService.getScheduledSignal(backtest, symbol, strategyName);
|
|
4972
|
+
};
|
|
4755
4973
|
/**
|
|
4756
4974
|
* Checks if the strategy has been stopped.
|
|
4757
4975
|
*
|
|
@@ -4854,6 +5072,27 @@ class StrategyCoreService {
|
|
|
4854
5072
|
await this.validate(ctx.symbol, ctx.strategyName);
|
|
4855
5073
|
return await this.strategyConnectionService.stop(backtest, ctx);
|
|
4856
5074
|
};
|
|
5075
|
+
/**
|
|
5076
|
+
* Cancels the scheduled signal without stopping the strategy.
|
|
5077
|
+
*
|
|
5078
|
+
* Delegates to StrategyConnectionService.cancel() to clear scheduled signal
|
|
5079
|
+
* and emit cancelled event through emitters.
|
|
5080
|
+
* Does not require execution context.
|
|
5081
|
+
*
|
|
5082
|
+
* @param backtest - Whether running in backtest mode
|
|
5083
|
+
* @param ctx - Context with symbol and strategyName
|
|
5084
|
+
* @param cancelId - Optional cancellation ID for user-initiated cancellations
|
|
5085
|
+
* @returns Promise that resolves when scheduled signal is cancelled
|
|
5086
|
+
*/
|
|
5087
|
+
this.cancel = async (backtest, ctx, cancelId) => {
|
|
5088
|
+
this.loggerService.log("strategyCoreService cancel", {
|
|
5089
|
+
ctx,
|
|
5090
|
+
backtest,
|
|
5091
|
+
cancelId,
|
|
5092
|
+
});
|
|
5093
|
+
await this.validate(ctx.symbol, ctx.strategyName);
|
|
5094
|
+
return await this.strategyConnectionService.cancel(backtest, ctx, cancelId);
|
|
5095
|
+
};
|
|
4857
5096
|
/**
|
|
4858
5097
|
* Clears the memoized ClientStrategy instance from cache.
|
|
4859
5098
|
*
|
|
@@ -7177,12 +7416,17 @@ const risk_columns = [
|
|
|
7177
7416
|
* - Signal identification (symbol, signal ID, position)
|
|
7178
7417
|
* - Price data (current price, entry price, take profit, stop loss)
|
|
7179
7418
|
* - Timing information (wait time in minutes before activation or cancellation)
|
|
7419
|
+
* - Cancellation details (reason: timeout/stoploss/user, optional user cancel ID)
|
|
7180
7420
|
*
|
|
7181
7421
|
* @remarks
|
|
7182
7422
|
* This configuration tracks the lifecycle of scheduled signals from creation to activation or cancellation.
|
|
7183
7423
|
* The "note" column visibility is controlled by {@link GLOBAL_CONFIG.CC_REPORT_SHOW_SIGNAL_NOTE}.
|
|
7184
7424
|
* Helps analyze signal scheduling effectiveness and cancellation patterns.
|
|
7185
7425
|
*
|
|
7426
|
+
* Cancellation tracking includes:
|
|
7427
|
+
* - cancelReason: "timeout" (expired wait time), "price_reject" (price hit SL), "user" (manual cancellation)
|
|
7428
|
+
* - cancelId: Optional ID provided when calling Backtest.cancel() or Live.cancel()
|
|
7429
|
+
*
|
|
7186
7430
|
* @example
|
|
7187
7431
|
* ```typescript
|
|
7188
7432
|
* import { schedule_columns } from "./assets/schedule.columns";
|
|
@@ -7191,9 +7435,9 @@ const risk_columns = [
|
|
|
7191
7435
|
* const service = new ScheduleMarkdownService();
|
|
7192
7436
|
* await service.getReport("BTCUSDT", "my-strategy", schedule_columns);
|
|
7193
7437
|
*
|
|
7194
|
-
* // Or customize for
|
|
7438
|
+
* // Or customize for cancellation analysis
|
|
7195
7439
|
* const customColumns = schedule_columns.filter(col =>
|
|
7196
|
-
* ["timestamp", "action", "
|
|
7440
|
+
* ["timestamp", "action", "cancelReason", "cancelId"].includes(col.key)
|
|
7197
7441
|
* );
|
|
7198
7442
|
* await service.getReport("BTCUSDT", "my-strategy", customColumns);
|
|
7199
7443
|
* ```
|
|
@@ -7242,7 +7486,7 @@ const schedule_columns = [
|
|
|
7242
7486
|
{
|
|
7243
7487
|
key: "currentPrice",
|
|
7244
7488
|
label: "Current Price",
|
|
7245
|
-
format: (data) => `${data.currentPrice.toFixed(8)} USD
|
|
7489
|
+
format: (data) => data.currentPrice ? `${data.currentPrice.toFixed(8)} USD` : "N/A",
|
|
7246
7490
|
isVisible: () => true,
|
|
7247
7491
|
},
|
|
7248
7492
|
{
|
|
@@ -7269,6 +7513,18 @@ const schedule_columns = [
|
|
|
7269
7513
|
format: (data) => data.duration !== undefined ? `${data.duration}` : "N/A",
|
|
7270
7514
|
isVisible: () => true,
|
|
7271
7515
|
},
|
|
7516
|
+
{
|
|
7517
|
+
key: "cancelReason",
|
|
7518
|
+
label: "Cancel Reason",
|
|
7519
|
+
format: (data) => data.cancelReason ? data.cancelReason.toUpperCase() : "N/A",
|
|
7520
|
+
isVisible: () => true,
|
|
7521
|
+
},
|
|
7522
|
+
{
|
|
7523
|
+
key: "cancelId",
|
|
7524
|
+
label: "Cancel ID",
|
|
7525
|
+
format: (data) => data.cancelId ?? "N/A",
|
|
7526
|
+
isVisible: () => true,
|
|
7527
|
+
},
|
|
7272
7528
|
];
|
|
7273
7529
|
|
|
7274
7530
|
/**
|
|
@@ -8449,6 +8705,8 @@ let ReportStorage$3 = class ReportStorage {
|
|
|
8449
8705
|
stopLoss: data.signal.priceStopLoss,
|
|
8450
8706
|
closeTimestamp: data.closeTimestamp,
|
|
8451
8707
|
duration: durationMin,
|
|
8708
|
+
cancelReason: data.reason,
|
|
8709
|
+
cancelId: data.cancelId,
|
|
8452
8710
|
};
|
|
8453
8711
|
this._eventList.unshift(newEvent);
|
|
8454
8712
|
// Trim queue if exceeded MAX_EVENTS
|
|
@@ -14729,6 +14987,8 @@ const LISTEN_PARTIAL_LOSS_METHOD_NAME = "event.listenPartialLoss";
|
|
|
14729
14987
|
const LISTEN_PARTIAL_LOSS_ONCE_METHOD_NAME = "event.listenPartialLossOnce";
|
|
14730
14988
|
const LISTEN_RISK_METHOD_NAME = "event.listenRisk";
|
|
14731
14989
|
const LISTEN_RISK_ONCE_METHOD_NAME = "event.listenRiskOnce";
|
|
14990
|
+
const LISTEN_PING_METHOD_NAME = "event.listenPing";
|
|
14991
|
+
const LISTEN_PING_ONCE_METHOD_NAME = "event.listenPingOnce";
|
|
14732
14992
|
/**
|
|
14733
14993
|
* Subscribes to all signal events with queued async processing.
|
|
14734
14994
|
*
|
|
@@ -15596,6 +15856,67 @@ function listenRiskOnce(filterFn, fn) {
|
|
|
15596
15856
|
backtest$1.loggerService.log(LISTEN_RISK_ONCE_METHOD_NAME);
|
|
15597
15857
|
return riskSubject.filter(filterFn).once(fn);
|
|
15598
15858
|
}
|
|
15859
|
+
/**
|
|
15860
|
+
* Subscribes to ping events during scheduled signal monitoring with queued async processing.
|
|
15861
|
+
*
|
|
15862
|
+
* Events are emitted every minute when a scheduled signal is being monitored (waiting for activation).
|
|
15863
|
+
* Allows tracking of scheduled signal lifecycle and custom monitoring logic.
|
|
15864
|
+
*
|
|
15865
|
+
* @param fn - Callback function to handle ping events
|
|
15866
|
+
* @returns Unsubscribe function to stop listening
|
|
15867
|
+
*
|
|
15868
|
+
* @example
|
|
15869
|
+
* ```typescript
|
|
15870
|
+
* import { listenPing } from "./function/event";
|
|
15871
|
+
*
|
|
15872
|
+
* const unsubscribe = listenPing((event) => {
|
|
15873
|
+
* console.log(`Ping for ${event.symbol} at ${new Date(event.timestamp).toISOString()}`);
|
|
15874
|
+
* console.log(`Strategy: ${event.strategyName}, Exchange: ${event.exchangeName}`);
|
|
15875
|
+
* console.log(`Mode: ${event.backtest ? "Backtest" : "Live"}`);
|
|
15876
|
+
* });
|
|
15877
|
+
*
|
|
15878
|
+
* // Later: stop listening
|
|
15879
|
+
* unsubscribe();
|
|
15880
|
+
* ```
|
|
15881
|
+
*/
|
|
15882
|
+
function listenPing(fn) {
|
|
15883
|
+
backtest$1.loggerService.log(LISTEN_PING_METHOD_NAME);
|
|
15884
|
+
return pingSubject.subscribe(functoolsKit.queued(async (event) => fn(event)));
|
|
15885
|
+
}
|
|
15886
|
+
/**
|
|
15887
|
+
* Subscribes to filtered ping events with one-time execution.
|
|
15888
|
+
*
|
|
15889
|
+
* Listens for events matching the filter predicate, then executes callback once
|
|
15890
|
+
* and automatically unsubscribes. Useful for waiting for specific ping conditions.
|
|
15891
|
+
*
|
|
15892
|
+
* @param filterFn - Predicate to filter which events trigger the callback
|
|
15893
|
+
* @param fn - Callback function to handle the filtered event (called only once)
|
|
15894
|
+
* @returns Unsubscribe function to cancel the listener before it fires
|
|
15895
|
+
*
|
|
15896
|
+
* @example
|
|
15897
|
+
* ```typescript
|
|
15898
|
+
* import { listenPingOnce } from "./function/event";
|
|
15899
|
+
*
|
|
15900
|
+
* // Wait for first ping on BTCUSDT
|
|
15901
|
+
* listenPingOnce(
|
|
15902
|
+
* (event) => event.symbol === "BTCUSDT",
|
|
15903
|
+
* (event) => console.log("First BTCUSDT ping received")
|
|
15904
|
+
* );
|
|
15905
|
+
*
|
|
15906
|
+
* // Wait for ping in backtest mode
|
|
15907
|
+
* const cancel = listenPingOnce(
|
|
15908
|
+
* (event) => event.backtest === true,
|
|
15909
|
+
* (event) => console.log("Backtest ping received at", new Date(event.timestamp))
|
|
15910
|
+
* );
|
|
15911
|
+
*
|
|
15912
|
+
* // Cancel if needed before event fires
|
|
15913
|
+
* cancel();
|
|
15914
|
+
* ```
|
|
15915
|
+
*/
|
|
15916
|
+
function listenPingOnce(filterFn, fn) {
|
|
15917
|
+
backtest$1.loggerService.log(LISTEN_PING_ONCE_METHOD_NAME);
|
|
15918
|
+
return pingSubject.filter(filterFn).once(fn);
|
|
15919
|
+
}
|
|
15599
15920
|
|
|
15600
15921
|
const GET_CANDLES_METHOD_NAME = "exchange.getCandles";
|
|
15601
15922
|
const GET_AVERAGE_PRICE_METHOD_NAME = "exchange.getAveragePrice";
|
|
@@ -15869,6 +16190,8 @@ const BACKTEST_METHOD_NAME_GET_REPORT = "BacktestUtils.getReport";
|
|
|
15869
16190
|
const BACKTEST_METHOD_NAME_DUMP = "BacktestUtils.dump";
|
|
15870
16191
|
const BACKTEST_METHOD_NAME_TASK = "BacktestUtils.task";
|
|
15871
16192
|
const BACKTEST_METHOD_NAME_GET_STATUS = "BacktestUtils.getStatus";
|
|
16193
|
+
const BACKTEST_METHOD_NAME_GET_PENDING_SIGNAL = "BacktestUtils.getPendingSignal";
|
|
16194
|
+
const BACKTEST_METHOD_NAME_GET_SCHEDULED_SIGNAL = "BacktestUtils.getScheduledSignal";
|
|
15872
16195
|
/**
|
|
15873
16196
|
* Internal task function that runs backtest and handles completion.
|
|
15874
16197
|
* Consumes backtest results and updates instance state flags.
|
|
@@ -16059,6 +16382,54 @@ class BacktestInstance {
|
|
|
16059
16382
|
this._isStopped = true;
|
|
16060
16383
|
};
|
|
16061
16384
|
};
|
|
16385
|
+
/**
|
|
16386
|
+
* Retrieves the currently active pending signal for the strategy.
|
|
16387
|
+
* If no active signal exists, returns null.
|
|
16388
|
+
*
|
|
16389
|
+
* @param symbol - Trading pair symbol
|
|
16390
|
+
* @param strategyName - Name of strategy to get pending signal for
|
|
16391
|
+
* @returns Promise resolving to pending signal or null
|
|
16392
|
+
*
|
|
16393
|
+
* @example
|
|
16394
|
+
* ```typescript
|
|
16395
|
+
* const instance = new BacktestInstance();
|
|
16396
|
+
* const pending = await instance.getPendingSignal("BTCUSDT", "my-strategy");
|
|
16397
|
+
* if (pending) {
|
|
16398
|
+
* console.log("Active signal:", pending.id);
|
|
16399
|
+
* }
|
|
16400
|
+
* ```
|
|
16401
|
+
*/
|
|
16402
|
+
this.getPendingSignal = async (symbol, strategyName) => {
|
|
16403
|
+
backtest$1.loggerService.info(BACKTEST_METHOD_NAME_GET_PENDING_SIGNAL, {
|
|
16404
|
+
symbol,
|
|
16405
|
+
strategyName,
|
|
16406
|
+
});
|
|
16407
|
+
return await backtest$1.strategyCoreService.getPendingSignal(true, symbol, strategyName);
|
|
16408
|
+
};
|
|
16409
|
+
/**
|
|
16410
|
+
* Retrieves the currently active scheduled signal for the strategy.
|
|
16411
|
+
* If no scheduled signal exists, returns null.
|
|
16412
|
+
*
|
|
16413
|
+
* @param symbol - Trading pair symbol
|
|
16414
|
+
* @param strategyName - Name of strategy to get scheduled signal for
|
|
16415
|
+
* @returns Promise resolving to scheduled signal or null
|
|
16416
|
+
*
|
|
16417
|
+
* @example
|
|
16418
|
+
* ```typescript
|
|
16419
|
+
* const instance = new BacktestInstance();
|
|
16420
|
+
* const scheduled = await instance.getScheduledSignal("BTCUSDT", "my-strategy");
|
|
16421
|
+
* if (scheduled) {
|
|
16422
|
+
* console.log("Scheduled signal:", scheduled.id);
|
|
16423
|
+
* }
|
|
16424
|
+
* ```
|
|
16425
|
+
*/
|
|
16426
|
+
this.getScheduledSignal = async (symbol, strategyName) => {
|
|
16427
|
+
backtest$1.loggerService.info(BACKTEST_METHOD_NAME_GET_SCHEDULED_SIGNAL, {
|
|
16428
|
+
symbol,
|
|
16429
|
+
strategyName,
|
|
16430
|
+
});
|
|
16431
|
+
return await backtest$1.strategyCoreService.getScheduledSignal(true, symbol, strategyName);
|
|
16432
|
+
};
|
|
16062
16433
|
/**
|
|
16063
16434
|
* Stops the strategy from generating new signals.
|
|
16064
16435
|
*
|
|
@@ -16083,6 +16454,32 @@ class BacktestInstance {
|
|
|
16083
16454
|
});
|
|
16084
16455
|
await backtest$1.strategyCoreService.stop(true, { symbol, strategyName });
|
|
16085
16456
|
};
|
|
16457
|
+
/**
|
|
16458
|
+
* Cancels the scheduled signal without stopping the strategy.
|
|
16459
|
+
*
|
|
16460
|
+
* Clears the scheduled signal (waiting for priceOpen activation).
|
|
16461
|
+
* Does NOT affect active pending signals or strategy operation.
|
|
16462
|
+
* Does NOT set stop flag - strategy can continue generating new signals.
|
|
16463
|
+
*
|
|
16464
|
+
* @param symbol - Trading pair symbol
|
|
16465
|
+
* @param strategyName - Strategy name
|
|
16466
|
+
* @param cancelId - Optional cancellation ID for tracking user-initiated cancellations
|
|
16467
|
+
* @returns Promise that resolves when scheduled signal is cancelled
|
|
16468
|
+
*
|
|
16469
|
+
* @example
|
|
16470
|
+
* ```typescript
|
|
16471
|
+
* const instance = new BacktestInstance();
|
|
16472
|
+
* await instance.cancel("BTCUSDT", "my-strategy", "manual-cancel-001");
|
|
16473
|
+
* ```
|
|
16474
|
+
*/
|
|
16475
|
+
this.cancel = async (symbol, strategyName, cancelId) => {
|
|
16476
|
+
backtest$1.loggerService.info("BacktestInstance.cancel", {
|
|
16477
|
+
symbol,
|
|
16478
|
+
strategyName,
|
|
16479
|
+
cancelId,
|
|
16480
|
+
});
|
|
16481
|
+
await backtest$1.strategyCoreService.cancel(true, { symbol, strategyName }, cancelId);
|
|
16482
|
+
};
|
|
16086
16483
|
/**
|
|
16087
16484
|
* Gets statistical data from all closed signals for a symbol-strategy pair.
|
|
16088
16485
|
*
|
|
@@ -16234,6 +16631,58 @@ class BacktestUtils {
|
|
|
16234
16631
|
const instance = this._getInstance(symbol, context.strategyName);
|
|
16235
16632
|
return instance.background(symbol, context);
|
|
16236
16633
|
};
|
|
16634
|
+
/**
|
|
16635
|
+
* Retrieves the currently active pending signal for the strategy.
|
|
16636
|
+
* If no active signal exists, returns null.
|
|
16637
|
+
*
|
|
16638
|
+
* @param symbol - Trading pair symbol
|
|
16639
|
+
* @param strategyName - Name of strategy to get pending signal for
|
|
16640
|
+
* @returns Promise resolving to pending signal or null
|
|
16641
|
+
*
|
|
16642
|
+
* @example
|
|
16643
|
+
* ```typescript
|
|
16644
|
+
* const pending = await Backtest.getPendingSignal("BTCUSDT", "my-strategy");
|
|
16645
|
+
* if (pending) {
|
|
16646
|
+
* console.log("Active signal:", pending.id);
|
|
16647
|
+
* }
|
|
16648
|
+
* ```
|
|
16649
|
+
*/
|
|
16650
|
+
this.getPendingSignal = async (symbol, strategyName) => {
|
|
16651
|
+
backtest$1.strategyValidationService.validate(strategyName, BACKTEST_METHOD_NAME_GET_PENDING_SIGNAL);
|
|
16652
|
+
{
|
|
16653
|
+
const { riskName, riskList } = backtest$1.strategySchemaService.get(strategyName);
|
|
16654
|
+
riskName && backtest$1.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_PENDING_SIGNAL);
|
|
16655
|
+
riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_PENDING_SIGNAL));
|
|
16656
|
+
}
|
|
16657
|
+
const instance = this._getInstance(symbol, strategyName);
|
|
16658
|
+
return await instance.getPendingSignal(symbol, strategyName);
|
|
16659
|
+
};
|
|
16660
|
+
/**
|
|
16661
|
+
* Retrieves the currently active scheduled signal for the strategy.
|
|
16662
|
+
* If no scheduled signal exists, returns null.
|
|
16663
|
+
*
|
|
16664
|
+
* @param symbol - Trading pair symbol
|
|
16665
|
+
* @param strategyName - Name of strategy to get scheduled signal for
|
|
16666
|
+
* @returns Promise resolving to scheduled signal or null
|
|
16667
|
+
*
|
|
16668
|
+
* @example
|
|
16669
|
+
* ```typescript
|
|
16670
|
+
* const scheduled = await Backtest.getScheduledSignal("BTCUSDT", "my-strategy");
|
|
16671
|
+
* if (scheduled) {
|
|
16672
|
+
* console.log("Scheduled signal:", scheduled.id);
|
|
16673
|
+
* }
|
|
16674
|
+
* ```
|
|
16675
|
+
*/
|
|
16676
|
+
this.getScheduledSignal = async (symbol, strategyName) => {
|
|
16677
|
+
backtest$1.strategyValidationService.validate(strategyName, BACKTEST_METHOD_NAME_GET_SCHEDULED_SIGNAL);
|
|
16678
|
+
{
|
|
16679
|
+
const { riskName, riskList } = backtest$1.strategySchemaService.get(strategyName);
|
|
16680
|
+
riskName && backtest$1.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_SCHEDULED_SIGNAL);
|
|
16681
|
+
riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_SCHEDULED_SIGNAL));
|
|
16682
|
+
}
|
|
16683
|
+
const instance = this._getInstance(symbol, strategyName);
|
|
16684
|
+
return await instance.getScheduledSignal(symbol, strategyName);
|
|
16685
|
+
};
|
|
16237
16686
|
/**
|
|
16238
16687
|
* Stops the strategy from generating new signals.
|
|
16239
16688
|
*
|
|
@@ -16261,6 +16710,34 @@ class BacktestUtils {
|
|
|
16261
16710
|
const instance = this._getInstance(symbol, strategyName);
|
|
16262
16711
|
return await instance.stop(symbol, strategyName);
|
|
16263
16712
|
};
|
|
16713
|
+
/**
|
|
16714
|
+
* Cancels the scheduled signal without stopping the strategy.
|
|
16715
|
+
*
|
|
16716
|
+
* Clears the scheduled signal (waiting for priceOpen activation).
|
|
16717
|
+
* Does NOT affect active pending signals or strategy operation.
|
|
16718
|
+
* Does NOT set stop flag - strategy can continue generating new signals.
|
|
16719
|
+
*
|
|
16720
|
+
* @param symbol - Trading pair symbol
|
|
16721
|
+
* @param strategyName - Strategy name
|
|
16722
|
+
* @param cancelId - Optional cancellation ID for tracking user-initiated cancellations
|
|
16723
|
+
* @returns Promise that resolves when scheduled signal is cancelled
|
|
16724
|
+
*
|
|
16725
|
+
* @example
|
|
16726
|
+
* ```typescript
|
|
16727
|
+
* // Cancel scheduled signal with custom ID
|
|
16728
|
+
* await Backtest.cancel("BTCUSDT", "my-strategy", "manual-cancel-001");
|
|
16729
|
+
* ```
|
|
16730
|
+
*/
|
|
16731
|
+
this.cancel = async (symbol, strategyName, cancelId) => {
|
|
16732
|
+
backtest$1.strategyValidationService.validate(strategyName, "BacktestUtils.cancel");
|
|
16733
|
+
{
|
|
16734
|
+
const { riskName, riskList } = backtest$1.strategySchemaService.get(strategyName);
|
|
16735
|
+
riskName && backtest$1.riskValidationService.validate(riskName, "BacktestUtils.cancel");
|
|
16736
|
+
riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, "BacktestUtils.cancel"));
|
|
16737
|
+
}
|
|
16738
|
+
const instance = this._getInstance(symbol, strategyName);
|
|
16739
|
+
return await instance.cancel(symbol, strategyName, cancelId);
|
|
16740
|
+
};
|
|
16264
16741
|
/**
|
|
16265
16742
|
* Gets statistical data from all closed signals for a symbol-strategy pair.
|
|
16266
16743
|
*
|
|
@@ -16382,6 +16859,9 @@ const LIVE_METHOD_NAME_GET_DATA = "LiveUtils.getData";
|
|
|
16382
16859
|
const LIVE_METHOD_NAME_DUMP = "LiveUtils.dump";
|
|
16383
16860
|
const LIVE_METHOD_NAME_TASK = "LiveUtils.task";
|
|
16384
16861
|
const LIVE_METHOD_NAME_GET_STATUS = "LiveUtils.getStatus";
|
|
16862
|
+
const LIVE_METHOD_NAME_GET_PENDING_SIGNAL = "LiveUtils.getPendingSignal";
|
|
16863
|
+
const LIVE_METHOD_NAME_GET_SCHEDULED_SIGNAL = "LiveUtils.getScheduledSignal";
|
|
16864
|
+
const LIVE_METHOD_NAME_CANCEL = "LiveUtils.cancel";
|
|
16385
16865
|
/**
|
|
16386
16866
|
* Internal task function that runs live trading and handles completion.
|
|
16387
16867
|
* Consumes live trading results and updates instance state flags.
|
|
@@ -16576,6 +17056,54 @@ class LiveInstance {
|
|
|
16576
17056
|
this._isStopped = true;
|
|
16577
17057
|
};
|
|
16578
17058
|
};
|
|
17059
|
+
/**
|
|
17060
|
+
* Retrieves the currently active pending signal for the strategy.
|
|
17061
|
+
* If no active signal exists, returns null.
|
|
17062
|
+
*
|
|
17063
|
+
* @param symbol - Trading pair symbol
|
|
17064
|
+
* @param strategyName - Name of strategy to get pending signal for
|
|
17065
|
+
* @returns Promise resolving to pending signal or null
|
|
17066
|
+
*
|
|
17067
|
+
* @example
|
|
17068
|
+
* ```typescript
|
|
17069
|
+
* const instance = new LiveInstance();
|
|
17070
|
+
* const pending = await instance.getPendingSignal("BTCUSDT", "my-strategy");
|
|
17071
|
+
* if (pending) {
|
|
17072
|
+
* console.log("Active signal:", pending.id);
|
|
17073
|
+
* }
|
|
17074
|
+
* ```
|
|
17075
|
+
*/
|
|
17076
|
+
this.getPendingSignal = async (symbol, strategyName) => {
|
|
17077
|
+
backtest$1.loggerService.info(LIVE_METHOD_NAME_GET_PENDING_SIGNAL, {
|
|
17078
|
+
symbol,
|
|
17079
|
+
strategyName,
|
|
17080
|
+
});
|
|
17081
|
+
return await backtest$1.strategyCoreService.getPendingSignal(false, symbol, strategyName);
|
|
17082
|
+
};
|
|
17083
|
+
/**
|
|
17084
|
+
* Retrieves the currently active scheduled signal for the strategy.
|
|
17085
|
+
* If no scheduled signal exists, returns null.
|
|
17086
|
+
*
|
|
17087
|
+
* @param symbol - Trading pair symbol
|
|
17088
|
+
* @param strategyName - Name of strategy to get scheduled signal for
|
|
17089
|
+
* @returns Promise resolving to scheduled signal or null
|
|
17090
|
+
*
|
|
17091
|
+
* @example
|
|
17092
|
+
* ```typescript
|
|
17093
|
+
* const instance = new LiveInstance();
|
|
17094
|
+
* const scheduled = await instance.getScheduledSignal("BTCUSDT", "my-strategy");
|
|
17095
|
+
* if (scheduled) {
|
|
17096
|
+
* console.log("Scheduled signal:", scheduled.id);
|
|
17097
|
+
* }
|
|
17098
|
+
* ```
|
|
17099
|
+
*/
|
|
17100
|
+
this.getScheduledSignal = async (symbol, strategyName) => {
|
|
17101
|
+
backtest$1.loggerService.info(LIVE_METHOD_NAME_GET_SCHEDULED_SIGNAL, {
|
|
17102
|
+
symbol,
|
|
17103
|
+
strategyName,
|
|
17104
|
+
});
|
|
17105
|
+
return await backtest$1.strategyCoreService.getScheduledSignal(false, symbol, strategyName);
|
|
17106
|
+
};
|
|
16579
17107
|
/**
|
|
16580
17108
|
* Stops the strategy from generating new signals.
|
|
16581
17109
|
*
|
|
@@ -16600,6 +17128,32 @@ class LiveInstance {
|
|
|
16600
17128
|
});
|
|
16601
17129
|
await backtest$1.strategyCoreService.stop(false, { symbol, strategyName });
|
|
16602
17130
|
};
|
|
17131
|
+
/**
|
|
17132
|
+
* Cancels the scheduled signal without stopping the strategy.
|
|
17133
|
+
*
|
|
17134
|
+
* Clears the scheduled signal (waiting for priceOpen activation).
|
|
17135
|
+
* Does NOT affect active pending signals or strategy operation.
|
|
17136
|
+
* Does NOT set stop flag - strategy can continue generating new signals.
|
|
17137
|
+
*
|
|
17138
|
+
* @param symbol - Trading pair symbol
|
|
17139
|
+
* @param strategyName - Strategy name
|
|
17140
|
+
* @param cancelId - Optional cancellation ID for tracking user-initiated cancellations
|
|
17141
|
+
* @returns Promise that resolves when scheduled signal is cancelled
|
|
17142
|
+
*
|
|
17143
|
+
* @example
|
|
17144
|
+
* ```typescript
|
|
17145
|
+
* const instance = new LiveInstance();
|
|
17146
|
+
* await instance.cancel("BTCUSDT", "my-strategy", "manual-cancel-001");
|
|
17147
|
+
* ```
|
|
17148
|
+
*/
|
|
17149
|
+
this.cancel = async (symbol, strategyName, cancelId) => {
|
|
17150
|
+
backtest$1.loggerService.info(LIVE_METHOD_NAME_CANCEL, {
|
|
17151
|
+
symbol,
|
|
17152
|
+
strategyName,
|
|
17153
|
+
cancelId,
|
|
17154
|
+
});
|
|
17155
|
+
await backtest$1.strategyCoreService.cancel(false, { symbol, strategyName }, cancelId);
|
|
17156
|
+
};
|
|
16603
17157
|
/**
|
|
16604
17158
|
* Gets statistical data from all live trading events for a symbol-strategy pair.
|
|
16605
17159
|
*
|
|
@@ -16762,6 +17316,58 @@ class LiveUtils {
|
|
|
16762
17316
|
const instance = this._getInstance(symbol, context.strategyName);
|
|
16763
17317
|
return instance.background(symbol, context);
|
|
16764
17318
|
};
|
|
17319
|
+
/**
|
|
17320
|
+
* Retrieves the currently active pending signal for the strategy.
|
|
17321
|
+
* If no active signal exists, returns null.
|
|
17322
|
+
*
|
|
17323
|
+
* @param symbol - Trading pair symbol
|
|
17324
|
+
* @param strategyName - Name of strategy to get pending signal for
|
|
17325
|
+
* @returns Promise resolving to pending signal or null
|
|
17326
|
+
*
|
|
17327
|
+
* @example
|
|
17328
|
+
* ```typescript
|
|
17329
|
+
* const pending = await Live.getPendingSignal("BTCUSDT", "my-strategy");
|
|
17330
|
+
* if (pending) {
|
|
17331
|
+
* console.log("Active signal:", pending.id);
|
|
17332
|
+
* }
|
|
17333
|
+
* ```
|
|
17334
|
+
*/
|
|
17335
|
+
this.getPendingSignal = async (symbol, strategyName) => {
|
|
17336
|
+
backtest$1.strategyValidationService.validate(strategyName, LIVE_METHOD_NAME_GET_PENDING_SIGNAL);
|
|
17337
|
+
{
|
|
17338
|
+
const { riskName, riskList } = backtest$1.strategySchemaService.get(strategyName);
|
|
17339
|
+
riskName && backtest$1.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_PENDING_SIGNAL);
|
|
17340
|
+
riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_PENDING_SIGNAL));
|
|
17341
|
+
}
|
|
17342
|
+
const instance = this._getInstance(symbol, strategyName);
|
|
17343
|
+
return await instance.getPendingSignal(symbol, strategyName);
|
|
17344
|
+
};
|
|
17345
|
+
/**
|
|
17346
|
+
* Retrieves the currently active scheduled signal for the strategy.
|
|
17347
|
+
* If no scheduled signal exists, returns null.
|
|
17348
|
+
*
|
|
17349
|
+
* @param symbol - Trading pair symbol
|
|
17350
|
+
* @param strategyName - Name of strategy to get scheduled signal for
|
|
17351
|
+
* @returns Promise resolving to scheduled signal or null
|
|
17352
|
+
*
|
|
17353
|
+
* @example
|
|
17354
|
+
* ```typescript
|
|
17355
|
+
* const scheduled = await Live.getScheduledSignal("BTCUSDT", "my-strategy");
|
|
17356
|
+
* if (scheduled) {
|
|
17357
|
+
* console.log("Scheduled signal:", scheduled.id);
|
|
17358
|
+
* }
|
|
17359
|
+
* ```
|
|
17360
|
+
*/
|
|
17361
|
+
this.getScheduledSignal = async (symbol, strategyName) => {
|
|
17362
|
+
backtest$1.strategyValidationService.validate(strategyName, LIVE_METHOD_NAME_GET_SCHEDULED_SIGNAL);
|
|
17363
|
+
{
|
|
17364
|
+
const { riskName, riskList } = backtest$1.strategySchemaService.get(strategyName);
|
|
17365
|
+
riskName && backtest$1.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_SCHEDULED_SIGNAL);
|
|
17366
|
+
riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_SCHEDULED_SIGNAL));
|
|
17367
|
+
}
|
|
17368
|
+
const instance = this._getInstance(symbol, strategyName);
|
|
17369
|
+
return await instance.getScheduledSignal(symbol, strategyName);
|
|
17370
|
+
};
|
|
16765
17371
|
/**
|
|
16766
17372
|
* Stops the strategy from generating new signals.
|
|
16767
17373
|
*
|
|
@@ -16789,6 +17395,34 @@ class LiveUtils {
|
|
|
16789
17395
|
const instance = this._getInstance(symbol, strategyName);
|
|
16790
17396
|
return await instance.stop(symbol, strategyName);
|
|
16791
17397
|
};
|
|
17398
|
+
/**
|
|
17399
|
+
* Cancels the scheduled signal without stopping the strategy.
|
|
17400
|
+
*
|
|
17401
|
+
* Clears the scheduled signal (waiting for priceOpen activation).
|
|
17402
|
+
* Does NOT affect active pending signals or strategy operation.
|
|
17403
|
+
* Does NOT set stop flag - strategy can continue generating new signals.
|
|
17404
|
+
*
|
|
17405
|
+
* @param symbol - Trading pair symbol
|
|
17406
|
+
* @param strategyName - Strategy name
|
|
17407
|
+
* @param cancelId - Optional cancellation ID for tracking user-initiated cancellations
|
|
17408
|
+
* @returns Promise that resolves when scheduled signal is cancelled
|
|
17409
|
+
*
|
|
17410
|
+
* @example
|
|
17411
|
+
* ```typescript
|
|
17412
|
+
* // Cancel scheduled signal in live trading with custom ID
|
|
17413
|
+
* await Live.cancel("BTCUSDT", "my-strategy", "manual-cancel-001");
|
|
17414
|
+
* ```
|
|
17415
|
+
*/
|
|
17416
|
+
this.cancel = async (symbol, strategyName, cancelId) => {
|
|
17417
|
+
backtest$1.strategyValidationService.validate(strategyName, LIVE_METHOD_NAME_CANCEL);
|
|
17418
|
+
{
|
|
17419
|
+
const { riskName, riskList } = backtest$1.strategySchemaService.get(strategyName);
|
|
17420
|
+
riskName && backtest$1.riskValidationService.validate(riskName, LIVE_METHOD_NAME_CANCEL);
|
|
17421
|
+
riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, LIVE_METHOD_NAME_CANCEL));
|
|
17422
|
+
}
|
|
17423
|
+
const instance = this._getInstance(symbol, strategyName);
|
|
17424
|
+
return await instance.cancel(symbol, strategyName, cancelId);
|
|
17425
|
+
};
|
|
16792
17426
|
/**
|
|
16793
17427
|
* Gets statistical data from all live trading events for a symbol-strategy pair.
|
|
16794
17428
|
*
|
|
@@ -19012,6 +19646,8 @@ exports.listenPartialLossOnce = listenPartialLossOnce;
|
|
|
19012
19646
|
exports.listenPartialProfit = listenPartialProfit;
|
|
19013
19647
|
exports.listenPartialProfitOnce = listenPartialProfitOnce;
|
|
19014
19648
|
exports.listenPerformance = listenPerformance;
|
|
19649
|
+
exports.listenPing = listenPing;
|
|
19650
|
+
exports.listenPingOnce = listenPingOnce;
|
|
19015
19651
|
exports.listenRisk = listenRisk;
|
|
19016
19652
|
exports.listenRiskOnce = listenRiskOnce;
|
|
19017
19653
|
exports.listenSignal = listenSignal;
|