backtest-kit 1.5.47 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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,
@@ -2160,7 +2167,7 @@ const GET_SIGNAL_FN = functoolsKit.trycatch(async (self) => {
2160
2167
  }, {
2161
2168
  defaultValue: null,
2162
2169
  fallback: (error) => {
2163
- const message = "ClientStrategy exception thrown";
2170
+ const message = "ClientStrategy GET_SIGNAL_FN thrown";
2164
2171
  const payload = {
2165
2172
  error: functoolsKit.errorData(error),
2166
2173
  message: functoolsKit.getErrorMessage(error),
@@ -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: "idle",
2294
- signal: null,
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,27 @@ const ACTIVATE_SCHEDULED_SIGNAL_FN = async (self, scheduled, activationTimestamp
2371
2384
  }
2372
2385
  return result;
2373
2386
  };
2387
+ const CALL_PING_CALLBACKS_FN = functoolsKit.trycatch(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
+ }, {
2395
+ fallback: (error) => {
2396
+ const message = "ClientStrategy CALL_PING_CALLBACKS_FN thrown";
2397
+ const payload = {
2398
+ error: functoolsKit.errorData(error),
2399
+ message: functoolsKit.getErrorMessage(error),
2400
+ };
2401
+ backtest$1.loggerService.warn(message, payload);
2402
+ console.warn(message, payload);
2403
+ errorEmitter.next(error);
2404
+ }
2405
+ });
2374
2406
  const RETURN_SCHEDULED_SIGNAL_ACTIVE_FN = async (self, scheduled, currentPrice) => {
2407
+ await CALL_PING_CALLBACKS_FN(self, scheduled, self.params.execution.context.when.getTime());
2375
2408
  const result = {
2376
2409
  action: "active",
2377
2410
  signal: scheduled,
@@ -2599,13 +2632,14 @@ const RETURN_IDLE_FN = async (self, currentPrice) => {
2599
2632
  }
2600
2633
  return result;
2601
2634
  };
2602
- const CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN = async (self, scheduled, averagePrice, closeTimestamp) => {
2635
+ const CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN = async (self, scheduled, averagePrice, closeTimestamp, reason) => {
2603
2636
  self.params.logger.info("ClientStrategy backtest scheduled signal cancelled", {
2604
2637
  symbol: self.params.execution.context.symbol,
2605
2638
  signalId: scheduled.id,
2606
2639
  closeTimestamp,
2607
2640
  averagePrice,
2608
2641
  priceStopLoss: scheduled.priceStopLoss,
2642
+ reason,
2609
2643
  });
2610
2644
  await self.setScheduledSignal(null);
2611
2645
  if (self.params.callbacks?.onCancel) {
@@ -2620,6 +2654,7 @@ const CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN = async (self, scheduled, averagePr
2620
2654
  exchangeName: self.params.method.context.exchangeName,
2621
2655
  symbol: self.params.execution.context.symbol,
2622
2656
  backtest: self.params.execution.context.backtest,
2657
+ reason,
2623
2658
  };
2624
2659
  if (self.params.callbacks?.onTick) {
2625
2660
  self.params.callbacks.onTick(self.params.execution.context.symbol, result, self.params.execution.context.backtest);
@@ -2735,10 +2770,16 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
2735
2770
  }
2736
2771
  const recentCandles = candles.slice(Math.max(0, i - (candlesCount - 1)), i + 1);
2737
2772
  const averagePrice = GET_AVG_PRICE_FN(recentCandles);
2773
+ // КРИТИЧНО: Проверяем был ли сигнал отменен пользователем через cancel()
2774
+ if (self._cancelledSignal) {
2775
+ // Сигнал был отменен через cancel() в onPing
2776
+ const result = await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, averagePrice, candle.timestamp, "user");
2777
+ return { activated: false, cancelled: true, activationIndex: i, result };
2778
+ }
2738
2779
  // КРИТИЧНО: Проверяем timeout ПЕРЕД проверкой цены
2739
2780
  const elapsedTime = candle.timestamp - scheduled.scheduledAt;
2740
2781
  if (elapsedTime >= maxTimeToWait) {
2741
- const result = await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, averagePrice, candle.timestamp);
2782
+ const result = await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, averagePrice, candle.timestamp, "timeout");
2742
2783
  return { activated: false, cancelled: true, activationIndex: i, result };
2743
2784
  }
2744
2785
  let shouldActivate = false;
@@ -2776,7 +2817,7 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
2776
2817
  }
2777
2818
  }
2778
2819
  if (shouldCancel) {
2779
- const result = await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, averagePrice, candle.timestamp);
2820
+ const result = await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, averagePrice, candle.timestamp, "price_reject");
2780
2821
  return { activated: false, cancelled: true, activationIndex: i, result };
2781
2822
  }
2782
2823
  if (shouldActivate) {
@@ -2788,6 +2829,7 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
2788
2829
  result: null,
2789
2830
  };
2790
2831
  }
2832
+ await CALL_PING_CALLBACKS_FN(self, scheduled, candle.timestamp);
2791
2833
  }
2792
2834
  return {
2793
2835
  activated: false,
@@ -2941,8 +2983,9 @@ class ClientStrategy {
2941
2983
  this.params = params;
2942
2984
  this._isStopped = false;
2943
2985
  this._pendingSignal = null;
2944
- this._scheduledSignal = null;
2945
2986
  this._lastSignalTimestamp = null;
2987
+ this._scheduledSignal = null;
2988
+ this._cancelledSignal = null;
2946
2989
  /**
2947
2990
  * Initializes strategy state by loading persisted signal from disk.
2948
2991
  *
@@ -3009,6 +3052,18 @@ class ClientStrategy {
3009
3052
  });
3010
3053
  return this._pendingSignal;
3011
3054
  }
3055
+ /**
3056
+ * Retrieves the current scheduled signal.
3057
+ * If no scheduled signal exists, returns null.
3058
+ * @returns Promise resolving to the scheduled signal or null.
3059
+ */
3060
+ async getScheduledSignal(symbol, strategyName) {
3061
+ this.params.logger.debug("ClientStrategy getScheduledSignal", {
3062
+ symbol,
3063
+ strategyName,
3064
+ });
3065
+ return this._scheduledSignal;
3066
+ }
3012
3067
  /**
3013
3068
  * Returns the stopped state of the strategy.
3014
3069
  *
@@ -3068,6 +3123,33 @@ class ClientStrategy {
3068
3123
  const currentPrice = await this.params.exchange.getAveragePrice(this.params.execution.context.symbol);
3069
3124
  return await RETURN_IDLE_FN(this, currentPrice);
3070
3125
  }
3126
+ // Check if scheduled signal was cancelled - emit cancelled event once
3127
+ if (this._cancelledSignal) {
3128
+ const currentPrice = await this.params.exchange.getAveragePrice(this.params.execution.context.symbol);
3129
+ const cancelledSignal = this._cancelledSignal;
3130
+ this._cancelledSignal = null; // Clear after emitting
3131
+ this.params.logger.info("ClientStrategy tick: scheduled signal was cancelled", {
3132
+ symbol: this.params.execution.context.symbol,
3133
+ signalId: cancelledSignal.id,
3134
+ });
3135
+ // Call onCancel callback
3136
+ if (this.params.callbacks?.onCancel) {
3137
+ this.params.callbacks.onCancel(this.params.execution.context.symbol, cancelledSignal, currentPrice, this.params.execution.context.backtest);
3138
+ }
3139
+ const result = {
3140
+ action: "cancelled",
3141
+ signal: cancelledSignal,
3142
+ currentPrice,
3143
+ closeTimestamp: currentTime,
3144
+ strategyName: this.params.method.context.strategyName,
3145
+ exchangeName: this.params.method.context.exchangeName,
3146
+ symbol: this.params.execution.context.symbol,
3147
+ backtest: this.params.execution.context.backtest,
3148
+ reason: "user",
3149
+ cancelId: cancelledSignal.cancelId,
3150
+ };
3151
+ return result;
3152
+ }
3071
3153
  // Monitor scheduled signal
3072
3154
  if (this._scheduledSignal && !this._pendingSignal) {
3073
3155
  const currentPrice = await this.params.exchange.getAveragePrice(this.params.execution.context.symbol);
@@ -3158,6 +3240,29 @@ class ClientStrategy {
3158
3240
  if (!this.params.execution.context.backtest) {
3159
3241
  throw new Error("ClientStrategy backtest: running in live context");
3160
3242
  }
3243
+ // If signal was cancelled - return cancelled
3244
+ if (this._cancelledSignal) {
3245
+ this.params.logger.debug("ClientStrategy backtest: no signal (cancelled or not created)");
3246
+ const currentPrice = await this.params.exchange.getAveragePrice(symbol);
3247
+ const cancelledSignal = this._cancelledSignal;
3248
+ this._cancelledSignal = null; // Clear after using
3249
+ if (this.params.callbacks?.onCancel) {
3250
+ this.params.callbacks.onCancel(this.params.execution.context.symbol, cancelledSignal, currentPrice, this.params.execution.context.backtest);
3251
+ }
3252
+ const cancelledResult = {
3253
+ action: "cancelled",
3254
+ signal: cancelledSignal,
3255
+ currentPrice,
3256
+ closeTimestamp: this.params.execution.context.when.getTime(),
3257
+ strategyName: this.params.method.context.strategyName,
3258
+ exchangeName: this.params.method.context.exchangeName,
3259
+ symbol: this.params.execution.context.symbol,
3260
+ backtest: true,
3261
+ reason: "user",
3262
+ cancelId: cancelledSignal.cancelId,
3263
+ };
3264
+ return cancelledResult;
3265
+ }
3161
3266
  if (!this._pendingSignal && !this._scheduledSignal) {
3162
3267
  throw new Error("ClientStrategy backtest: no pending or scheduled signal");
3163
3268
  }
@@ -3230,7 +3335,7 @@ class ClientStrategy {
3230
3335
  maxMinutes: GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES,
3231
3336
  reason: "timeout - price never reached priceOpen",
3232
3337
  });
3233
- return await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(this, scheduled, lastPrice, lastCandleTimestamp);
3338
+ return await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(this, scheduled, lastPrice, lastCandleTimestamp, "timeout");
3234
3339
  }
3235
3340
  }
3236
3341
  // Process pending signal
@@ -3288,6 +3393,47 @@ class ClientStrategy {
3288
3393
  }
3289
3394
  await PersistScheduleAdapter.writeScheduleData(this._scheduledSignal, symbol, strategyName);
3290
3395
  }
3396
+ /**
3397
+ * Cancels the scheduled signal without stopping the strategy.
3398
+ *
3399
+ * Clears the scheduled signal (waiting for priceOpen activation).
3400
+ * Does NOT affect active pending signals or strategy operation.
3401
+ * Does NOT set stop flag - strategy can continue generating new signals.
3402
+ *
3403
+ * Use case: Cancel a scheduled entry that is no longer desired without stopping the entire strategy.
3404
+ *
3405
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
3406
+ * @param strategyName - Name of the strategy
3407
+ * @param backtest - Whether running in backtest mode
3408
+ * @returns Promise that resolves when scheduled signal is cleared
3409
+ *
3410
+ * @example
3411
+ * ```typescript
3412
+ * // Cancel scheduled signal without stopping strategy
3413
+ * await strategy.cancel("BTCUSDT", "my-strategy", false);
3414
+ * // Strategy continues, can generate new signals
3415
+ * ```
3416
+ */
3417
+ async cancel(symbol, strategyName, backtest, cancelId) {
3418
+ this.params.logger.debug("ClientStrategy cancel", {
3419
+ symbol,
3420
+ strategyName,
3421
+ hasScheduledSignal: this._scheduledSignal !== null,
3422
+ backtest,
3423
+ cancelId,
3424
+ });
3425
+ // Save cancelled signal for next tick to emit cancelled event
3426
+ if (this._scheduledSignal) {
3427
+ this._cancelledSignal = Object.assign({}, this._scheduledSignal, {
3428
+ cancelId,
3429
+ });
3430
+ this._scheduledSignal = null;
3431
+ }
3432
+ if (backtest) {
3433
+ return;
3434
+ }
3435
+ await PersistScheduleAdapter.writeScheduleData(this._scheduledSignal, symbol, strategyName);
3436
+ }
3291
3437
  }
3292
3438
 
3293
3439
  const RISK_METHOD_NAME_GET_DATA = "RiskUtils.getData";
@@ -3614,6 +3760,27 @@ const GET_RISK_FN = (dto, backtest, self) => {
3614
3760
  ...dto.riskList.map((riskName) => self.riskConnectionService.getRisk(riskName, backtest)),
3615
3761
  ]);
3616
3762
  };
3763
+ /**
3764
+ * Callback function for emitting ping events to pingSubject.
3765
+ *
3766
+ * Called by ClientStrategy when a scheduled signal is being monitored every minute.
3767
+ * Emits PingContract event to all subscribers.
3768
+ *
3769
+ * @param symbol - Trading pair symbol
3770
+ * @param strategyName - Strategy name that is monitoring this scheduled signal
3771
+ * @param exchangeName - Exchange name where this scheduled signal is being executed
3772
+ * @param data - Scheduled signal row data
3773
+ * @param backtest - True if backtest mode
3774
+ * @param timestamp - Event timestamp in milliseconds
3775
+ */
3776
+ const COMMIT_PING_FN = async (symbol, strategyName, exchangeName, data, backtest, timestamp) => await pingSubject.next({
3777
+ symbol,
3778
+ strategyName,
3779
+ exchangeName,
3780
+ data,
3781
+ backtest,
3782
+ timestamp,
3783
+ });
3617
3784
  /**
3618
3785
  * Connection service routing strategy operations to correct ClientStrategy instance.
3619
3786
  *
@@ -3671,6 +3838,7 @@ class StrategyConnectionService {
3671
3838
  strategyName,
3672
3839
  getSignal,
3673
3840
  callbacks,
3841
+ onPing: COMMIT_PING_FN,
3674
3842
  });
3675
3843
  });
3676
3844
  /**
@@ -3692,6 +3860,25 @@ class StrategyConnectionService {
3692
3860
  const strategy = this.getStrategy(symbol, strategyName, backtest);
3693
3861
  return await strategy.getPendingSignal(symbol, strategyName);
3694
3862
  };
3863
+ /**
3864
+ * Retrieves the currently active scheduled signal for the strategy.
3865
+ * If no scheduled signal exists, returns null.
3866
+ * Used internally for monitoring scheduled signal activation.
3867
+ *
3868
+ * @param symbol - Trading pair symbol
3869
+ * @param strategyName - Name of strategy to get scheduled signal for
3870
+ *
3871
+ * @returns Promise resolving to scheduled signal or null
3872
+ */
3873
+ this.getScheduledSignal = async (backtest, symbol, strategyName) => {
3874
+ this.loggerService.log("strategyConnectionService getScheduledSignal", {
3875
+ symbol,
3876
+ strategyName,
3877
+ backtest,
3878
+ });
3879
+ const strategy = this.getStrategy(symbol, strategyName, backtest);
3880
+ return await strategy.getScheduledSignal(symbol, strategyName);
3881
+ };
3695
3882
  /**
3696
3883
  * Retrieves the stopped state of the strategy.
3697
3884
  *
@@ -3807,6 +3994,28 @@ class StrategyConnectionService {
3807
3994
  this.getStrategy.clear();
3808
3995
  }
3809
3996
  };
3997
+ /**
3998
+ * Cancels the scheduled signal for the specified strategy.
3999
+ *
4000
+ * Delegates to ClientStrategy.cancel() which clears the scheduled signal
4001
+ * without stopping the strategy or affecting pending signals.
4002
+ *
4003
+ * Note: Cancelled event will be emitted on next tick() call when strategy
4004
+ * detects the scheduled signal was cancelled.
4005
+ *
4006
+ * @param backtest - Whether running in backtest mode
4007
+ * @param ctx - Context with symbol and strategyName
4008
+ * @param cancelId - Optional cancellation ID for user-initiated cancellations
4009
+ * @returns Promise that resolves when scheduled signal is cancelled
4010
+ */
4011
+ this.cancel = async (backtest, ctx, cancelId) => {
4012
+ this.loggerService.log("strategyConnectionService cancel", {
4013
+ ctx,
4014
+ cancelId,
4015
+ });
4016
+ const strategy = this.getStrategy(ctx.symbol, ctx.strategyName, backtest);
4017
+ await strategy.cancel(ctx.symbol, ctx.strategyName, backtest, cancelId);
4018
+ };
3810
4019
  }
3811
4020
  }
3812
4021
 
@@ -4752,6 +4961,26 @@ class StrategyCoreService {
4752
4961
  await this.validate(symbol, strategyName);
4753
4962
  return await this.strategyConnectionService.getPendingSignal(backtest, symbol, strategyName);
4754
4963
  };
4964
+ /**
4965
+ * Retrieves the currently active scheduled signal for the symbol.
4966
+ * If no scheduled signal exists, returns null.
4967
+ * Used internally for monitoring scheduled signal activation.
4968
+ *
4969
+ * @param symbol - Trading pair symbol
4970
+ * @param strategyName - Name of the strategy
4971
+ * @returns Promise resolving to scheduled signal or null
4972
+ */
4973
+ this.getScheduledSignal = async (backtest, symbol, strategyName) => {
4974
+ this.loggerService.log("strategyCoreService getScheduledSignal", {
4975
+ symbol,
4976
+ strategyName,
4977
+ });
4978
+ if (!MethodContextService.hasContext()) {
4979
+ throw new Error("strategyCoreService getScheduledSignal requires a method context");
4980
+ }
4981
+ await this.validate(symbol, strategyName);
4982
+ return await this.strategyConnectionService.getScheduledSignal(backtest, symbol, strategyName);
4983
+ };
4755
4984
  /**
4756
4985
  * Checks if the strategy has been stopped.
4757
4986
  *
@@ -4854,6 +5083,27 @@ class StrategyCoreService {
4854
5083
  await this.validate(ctx.symbol, ctx.strategyName);
4855
5084
  return await this.strategyConnectionService.stop(backtest, ctx);
4856
5085
  };
5086
+ /**
5087
+ * Cancels the scheduled signal without stopping the strategy.
5088
+ *
5089
+ * Delegates to StrategyConnectionService.cancel() to clear scheduled signal
5090
+ * and emit cancelled event through emitters.
5091
+ * Does not require execution context.
5092
+ *
5093
+ * @param backtest - Whether running in backtest mode
5094
+ * @param ctx - Context with symbol and strategyName
5095
+ * @param cancelId - Optional cancellation ID for user-initiated cancellations
5096
+ * @returns Promise that resolves when scheduled signal is cancelled
5097
+ */
5098
+ this.cancel = async (backtest, ctx, cancelId) => {
5099
+ this.loggerService.log("strategyCoreService cancel", {
5100
+ ctx,
5101
+ backtest,
5102
+ cancelId,
5103
+ });
5104
+ await this.validate(ctx.symbol, ctx.strategyName);
5105
+ return await this.strategyConnectionService.cancel(backtest, ctx, cancelId);
5106
+ };
4857
5107
  /**
4858
5108
  * Clears the memoized ClientStrategy instance from cache.
4859
5109
  *
@@ -7177,12 +7427,17 @@ const risk_columns = [
7177
7427
  * - Signal identification (symbol, signal ID, position)
7178
7428
  * - Price data (current price, entry price, take profit, stop loss)
7179
7429
  * - Timing information (wait time in minutes before activation or cancellation)
7430
+ * - Cancellation details (reason: timeout/stoploss/user, optional user cancel ID)
7180
7431
  *
7181
7432
  * @remarks
7182
7433
  * This configuration tracks the lifecycle of scheduled signals from creation to activation or cancellation.
7183
7434
  * The "note" column visibility is controlled by {@link GLOBAL_CONFIG.CC_REPORT_SHOW_SIGNAL_NOTE}.
7184
7435
  * Helps analyze signal scheduling effectiveness and cancellation patterns.
7185
7436
  *
7437
+ * Cancellation tracking includes:
7438
+ * - cancelReason: "timeout" (expired wait time), "price_reject" (price hit SL), "user" (manual cancellation)
7439
+ * - cancelId: Optional ID provided when calling Backtest.cancel() or Live.cancel()
7440
+ *
7186
7441
  * @example
7187
7442
  * ```typescript
7188
7443
  * import { schedule_columns } from "./assets/schedule.columns";
@@ -7191,9 +7446,9 @@ const risk_columns = [
7191
7446
  * const service = new ScheduleMarkdownService();
7192
7447
  * await service.getReport("BTCUSDT", "my-strategy", schedule_columns);
7193
7448
  *
7194
- * // Or customize for timing analysis
7449
+ * // Or customize for cancellation analysis
7195
7450
  * const customColumns = schedule_columns.filter(col =>
7196
- * ["timestamp", "action", "symbol", "duration"].includes(col.key)
7451
+ * ["timestamp", "action", "cancelReason", "cancelId"].includes(col.key)
7197
7452
  * );
7198
7453
  * await service.getReport("BTCUSDT", "my-strategy", customColumns);
7199
7454
  * ```
@@ -7242,7 +7497,7 @@ const schedule_columns = [
7242
7497
  {
7243
7498
  key: "currentPrice",
7244
7499
  label: "Current Price",
7245
- format: (data) => `${data.currentPrice.toFixed(8)} USD`,
7500
+ format: (data) => data.currentPrice ? `${data.currentPrice.toFixed(8)} USD` : "N/A",
7246
7501
  isVisible: () => true,
7247
7502
  },
7248
7503
  {
@@ -7269,6 +7524,18 @@ const schedule_columns = [
7269
7524
  format: (data) => data.duration !== undefined ? `${data.duration}` : "N/A",
7270
7525
  isVisible: () => true,
7271
7526
  },
7527
+ {
7528
+ key: "cancelReason",
7529
+ label: "Cancel Reason",
7530
+ format: (data) => data.cancelReason ? data.cancelReason.toUpperCase() : "N/A",
7531
+ isVisible: () => true,
7532
+ },
7533
+ {
7534
+ key: "cancelId",
7535
+ label: "Cancel ID",
7536
+ format: (data) => data.cancelId ?? "N/A",
7537
+ isVisible: () => true,
7538
+ },
7272
7539
  ];
7273
7540
 
7274
7541
  /**
@@ -8449,6 +8716,8 @@ let ReportStorage$3 = class ReportStorage {
8449
8716
  stopLoss: data.signal.priceStopLoss,
8450
8717
  closeTimestamp: data.closeTimestamp,
8451
8718
  duration: durationMin,
8719
+ cancelReason: data.reason,
8720
+ cancelId: data.cancelId,
8452
8721
  };
8453
8722
  this._eventList.unshift(newEvent);
8454
8723
  // Trim queue if exceeded MAX_EVENTS
@@ -14729,6 +14998,8 @@ const LISTEN_PARTIAL_LOSS_METHOD_NAME = "event.listenPartialLoss";
14729
14998
  const LISTEN_PARTIAL_LOSS_ONCE_METHOD_NAME = "event.listenPartialLossOnce";
14730
14999
  const LISTEN_RISK_METHOD_NAME = "event.listenRisk";
14731
15000
  const LISTEN_RISK_ONCE_METHOD_NAME = "event.listenRiskOnce";
15001
+ const LISTEN_PING_METHOD_NAME = "event.listenPing";
15002
+ const LISTEN_PING_ONCE_METHOD_NAME = "event.listenPingOnce";
14732
15003
  /**
14733
15004
  * Subscribes to all signal events with queued async processing.
14734
15005
  *
@@ -15596,6 +15867,67 @@ function listenRiskOnce(filterFn, fn) {
15596
15867
  backtest$1.loggerService.log(LISTEN_RISK_ONCE_METHOD_NAME);
15597
15868
  return riskSubject.filter(filterFn).once(fn);
15598
15869
  }
15870
+ /**
15871
+ * Subscribes to ping events during scheduled signal monitoring with queued async processing.
15872
+ *
15873
+ * Events are emitted every minute when a scheduled signal is being monitored (waiting for activation).
15874
+ * Allows tracking of scheduled signal lifecycle and custom monitoring logic.
15875
+ *
15876
+ * @param fn - Callback function to handle ping events
15877
+ * @returns Unsubscribe function to stop listening
15878
+ *
15879
+ * @example
15880
+ * ```typescript
15881
+ * import { listenPing } from "./function/event";
15882
+ *
15883
+ * const unsubscribe = listenPing((event) => {
15884
+ * console.log(`Ping for ${event.symbol} at ${new Date(event.timestamp).toISOString()}`);
15885
+ * console.log(`Strategy: ${event.strategyName}, Exchange: ${event.exchangeName}`);
15886
+ * console.log(`Mode: ${event.backtest ? "Backtest" : "Live"}`);
15887
+ * });
15888
+ *
15889
+ * // Later: stop listening
15890
+ * unsubscribe();
15891
+ * ```
15892
+ */
15893
+ function listenPing(fn) {
15894
+ backtest$1.loggerService.log(LISTEN_PING_METHOD_NAME);
15895
+ return pingSubject.subscribe(functoolsKit.queued(async (event) => fn(event)));
15896
+ }
15897
+ /**
15898
+ * Subscribes to filtered ping events with one-time execution.
15899
+ *
15900
+ * Listens for events matching the filter predicate, then executes callback once
15901
+ * and automatically unsubscribes. Useful for waiting for specific ping conditions.
15902
+ *
15903
+ * @param filterFn - Predicate to filter which events trigger the callback
15904
+ * @param fn - Callback function to handle the filtered event (called only once)
15905
+ * @returns Unsubscribe function to cancel the listener before it fires
15906
+ *
15907
+ * @example
15908
+ * ```typescript
15909
+ * import { listenPingOnce } from "./function/event";
15910
+ *
15911
+ * // Wait for first ping on BTCUSDT
15912
+ * listenPingOnce(
15913
+ * (event) => event.symbol === "BTCUSDT",
15914
+ * (event) => console.log("First BTCUSDT ping received")
15915
+ * );
15916
+ *
15917
+ * // Wait for ping in backtest mode
15918
+ * const cancel = listenPingOnce(
15919
+ * (event) => event.backtest === true,
15920
+ * (event) => console.log("Backtest ping received at", new Date(event.timestamp))
15921
+ * );
15922
+ *
15923
+ * // Cancel if needed before event fires
15924
+ * cancel();
15925
+ * ```
15926
+ */
15927
+ function listenPingOnce(filterFn, fn) {
15928
+ backtest$1.loggerService.log(LISTEN_PING_ONCE_METHOD_NAME);
15929
+ return pingSubject.filter(filterFn).once(fn);
15930
+ }
15599
15931
 
15600
15932
  const GET_CANDLES_METHOD_NAME = "exchange.getCandles";
15601
15933
  const GET_AVERAGE_PRICE_METHOD_NAME = "exchange.getAveragePrice";
@@ -15869,6 +16201,8 @@ const BACKTEST_METHOD_NAME_GET_REPORT = "BacktestUtils.getReport";
15869
16201
  const BACKTEST_METHOD_NAME_DUMP = "BacktestUtils.dump";
15870
16202
  const BACKTEST_METHOD_NAME_TASK = "BacktestUtils.task";
15871
16203
  const BACKTEST_METHOD_NAME_GET_STATUS = "BacktestUtils.getStatus";
16204
+ const BACKTEST_METHOD_NAME_GET_PENDING_SIGNAL = "BacktestUtils.getPendingSignal";
16205
+ const BACKTEST_METHOD_NAME_GET_SCHEDULED_SIGNAL = "BacktestUtils.getScheduledSignal";
15872
16206
  /**
15873
16207
  * Internal task function that runs backtest and handles completion.
15874
16208
  * Consumes backtest results and updates instance state flags.
@@ -16059,6 +16393,54 @@ class BacktestInstance {
16059
16393
  this._isStopped = true;
16060
16394
  };
16061
16395
  };
16396
+ /**
16397
+ * Retrieves the currently active pending signal for the strategy.
16398
+ * If no active signal exists, returns null.
16399
+ *
16400
+ * @param symbol - Trading pair symbol
16401
+ * @param strategyName - Name of strategy to get pending signal for
16402
+ * @returns Promise resolving to pending signal or null
16403
+ *
16404
+ * @example
16405
+ * ```typescript
16406
+ * const instance = new BacktestInstance();
16407
+ * const pending = await instance.getPendingSignal("BTCUSDT", "my-strategy");
16408
+ * if (pending) {
16409
+ * console.log("Active signal:", pending.id);
16410
+ * }
16411
+ * ```
16412
+ */
16413
+ this.getPendingSignal = async (symbol, strategyName) => {
16414
+ backtest$1.loggerService.info(BACKTEST_METHOD_NAME_GET_PENDING_SIGNAL, {
16415
+ symbol,
16416
+ strategyName,
16417
+ });
16418
+ return await backtest$1.strategyCoreService.getPendingSignal(true, symbol, strategyName);
16419
+ };
16420
+ /**
16421
+ * Retrieves the currently active scheduled signal for the strategy.
16422
+ * If no scheduled signal exists, returns null.
16423
+ *
16424
+ * @param symbol - Trading pair symbol
16425
+ * @param strategyName - Name of strategy to get scheduled signal for
16426
+ * @returns Promise resolving to scheduled signal or null
16427
+ *
16428
+ * @example
16429
+ * ```typescript
16430
+ * const instance = new BacktestInstance();
16431
+ * const scheduled = await instance.getScheduledSignal("BTCUSDT", "my-strategy");
16432
+ * if (scheduled) {
16433
+ * console.log("Scheduled signal:", scheduled.id);
16434
+ * }
16435
+ * ```
16436
+ */
16437
+ this.getScheduledSignal = async (symbol, strategyName) => {
16438
+ backtest$1.loggerService.info(BACKTEST_METHOD_NAME_GET_SCHEDULED_SIGNAL, {
16439
+ symbol,
16440
+ strategyName,
16441
+ });
16442
+ return await backtest$1.strategyCoreService.getScheduledSignal(true, symbol, strategyName);
16443
+ };
16062
16444
  /**
16063
16445
  * Stops the strategy from generating new signals.
16064
16446
  *
@@ -16083,6 +16465,32 @@ class BacktestInstance {
16083
16465
  });
16084
16466
  await backtest$1.strategyCoreService.stop(true, { symbol, strategyName });
16085
16467
  };
16468
+ /**
16469
+ * Cancels the scheduled signal without stopping the strategy.
16470
+ *
16471
+ * Clears the scheduled signal (waiting for priceOpen activation).
16472
+ * Does NOT affect active pending signals or strategy operation.
16473
+ * Does NOT set stop flag - strategy can continue generating new signals.
16474
+ *
16475
+ * @param symbol - Trading pair symbol
16476
+ * @param strategyName - Strategy name
16477
+ * @param cancelId - Optional cancellation ID for tracking user-initiated cancellations
16478
+ * @returns Promise that resolves when scheduled signal is cancelled
16479
+ *
16480
+ * @example
16481
+ * ```typescript
16482
+ * const instance = new BacktestInstance();
16483
+ * await instance.cancel("BTCUSDT", "my-strategy", "manual-cancel-001");
16484
+ * ```
16485
+ */
16486
+ this.cancel = async (symbol, strategyName, cancelId) => {
16487
+ backtest$1.loggerService.info("BacktestInstance.cancel", {
16488
+ symbol,
16489
+ strategyName,
16490
+ cancelId,
16491
+ });
16492
+ await backtest$1.strategyCoreService.cancel(true, { symbol, strategyName }, cancelId);
16493
+ };
16086
16494
  /**
16087
16495
  * Gets statistical data from all closed signals for a symbol-strategy pair.
16088
16496
  *
@@ -16234,6 +16642,58 @@ class BacktestUtils {
16234
16642
  const instance = this._getInstance(symbol, context.strategyName);
16235
16643
  return instance.background(symbol, context);
16236
16644
  };
16645
+ /**
16646
+ * Retrieves the currently active pending signal for the strategy.
16647
+ * If no active signal exists, returns null.
16648
+ *
16649
+ * @param symbol - Trading pair symbol
16650
+ * @param strategyName - Name of strategy to get pending signal for
16651
+ * @returns Promise resolving to pending signal or null
16652
+ *
16653
+ * @example
16654
+ * ```typescript
16655
+ * const pending = await Backtest.getPendingSignal("BTCUSDT", "my-strategy");
16656
+ * if (pending) {
16657
+ * console.log("Active signal:", pending.id);
16658
+ * }
16659
+ * ```
16660
+ */
16661
+ this.getPendingSignal = async (symbol, strategyName) => {
16662
+ backtest$1.strategyValidationService.validate(strategyName, BACKTEST_METHOD_NAME_GET_PENDING_SIGNAL);
16663
+ {
16664
+ const { riskName, riskList } = backtest$1.strategySchemaService.get(strategyName);
16665
+ riskName && backtest$1.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_PENDING_SIGNAL);
16666
+ riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_PENDING_SIGNAL));
16667
+ }
16668
+ const instance = this._getInstance(symbol, strategyName);
16669
+ return await instance.getPendingSignal(symbol, strategyName);
16670
+ };
16671
+ /**
16672
+ * Retrieves the currently active scheduled signal for the strategy.
16673
+ * If no scheduled signal exists, returns null.
16674
+ *
16675
+ * @param symbol - Trading pair symbol
16676
+ * @param strategyName - Name of strategy to get scheduled signal for
16677
+ * @returns Promise resolving to scheduled signal or null
16678
+ *
16679
+ * @example
16680
+ * ```typescript
16681
+ * const scheduled = await Backtest.getScheduledSignal("BTCUSDT", "my-strategy");
16682
+ * if (scheduled) {
16683
+ * console.log("Scheduled signal:", scheduled.id);
16684
+ * }
16685
+ * ```
16686
+ */
16687
+ this.getScheduledSignal = async (symbol, strategyName) => {
16688
+ backtest$1.strategyValidationService.validate(strategyName, BACKTEST_METHOD_NAME_GET_SCHEDULED_SIGNAL);
16689
+ {
16690
+ const { riskName, riskList } = backtest$1.strategySchemaService.get(strategyName);
16691
+ riskName && backtest$1.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_SCHEDULED_SIGNAL);
16692
+ riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_SCHEDULED_SIGNAL));
16693
+ }
16694
+ const instance = this._getInstance(symbol, strategyName);
16695
+ return await instance.getScheduledSignal(symbol, strategyName);
16696
+ };
16237
16697
  /**
16238
16698
  * Stops the strategy from generating new signals.
16239
16699
  *
@@ -16261,6 +16721,34 @@ class BacktestUtils {
16261
16721
  const instance = this._getInstance(symbol, strategyName);
16262
16722
  return await instance.stop(symbol, strategyName);
16263
16723
  };
16724
+ /**
16725
+ * Cancels the scheduled signal without stopping the strategy.
16726
+ *
16727
+ * Clears the scheduled signal (waiting for priceOpen activation).
16728
+ * Does NOT affect active pending signals or strategy operation.
16729
+ * Does NOT set stop flag - strategy can continue generating new signals.
16730
+ *
16731
+ * @param symbol - Trading pair symbol
16732
+ * @param strategyName - Strategy name
16733
+ * @param cancelId - Optional cancellation ID for tracking user-initiated cancellations
16734
+ * @returns Promise that resolves when scheduled signal is cancelled
16735
+ *
16736
+ * @example
16737
+ * ```typescript
16738
+ * // Cancel scheduled signal with custom ID
16739
+ * await Backtest.cancel("BTCUSDT", "my-strategy", "manual-cancel-001");
16740
+ * ```
16741
+ */
16742
+ this.cancel = async (symbol, strategyName, cancelId) => {
16743
+ backtest$1.strategyValidationService.validate(strategyName, "BacktestUtils.cancel");
16744
+ {
16745
+ const { riskName, riskList } = backtest$1.strategySchemaService.get(strategyName);
16746
+ riskName && backtest$1.riskValidationService.validate(riskName, "BacktestUtils.cancel");
16747
+ riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, "BacktestUtils.cancel"));
16748
+ }
16749
+ const instance = this._getInstance(symbol, strategyName);
16750
+ return await instance.cancel(symbol, strategyName, cancelId);
16751
+ };
16264
16752
  /**
16265
16753
  * Gets statistical data from all closed signals for a symbol-strategy pair.
16266
16754
  *
@@ -16382,6 +16870,9 @@ const LIVE_METHOD_NAME_GET_DATA = "LiveUtils.getData";
16382
16870
  const LIVE_METHOD_NAME_DUMP = "LiveUtils.dump";
16383
16871
  const LIVE_METHOD_NAME_TASK = "LiveUtils.task";
16384
16872
  const LIVE_METHOD_NAME_GET_STATUS = "LiveUtils.getStatus";
16873
+ const LIVE_METHOD_NAME_GET_PENDING_SIGNAL = "LiveUtils.getPendingSignal";
16874
+ const LIVE_METHOD_NAME_GET_SCHEDULED_SIGNAL = "LiveUtils.getScheduledSignal";
16875
+ const LIVE_METHOD_NAME_CANCEL = "LiveUtils.cancel";
16385
16876
  /**
16386
16877
  * Internal task function that runs live trading and handles completion.
16387
16878
  * Consumes live trading results and updates instance state flags.
@@ -16576,6 +17067,54 @@ class LiveInstance {
16576
17067
  this._isStopped = true;
16577
17068
  };
16578
17069
  };
17070
+ /**
17071
+ * Retrieves the currently active pending signal for the strategy.
17072
+ * If no active signal exists, returns null.
17073
+ *
17074
+ * @param symbol - Trading pair symbol
17075
+ * @param strategyName - Name of strategy to get pending signal for
17076
+ * @returns Promise resolving to pending signal or null
17077
+ *
17078
+ * @example
17079
+ * ```typescript
17080
+ * const instance = new LiveInstance();
17081
+ * const pending = await instance.getPendingSignal("BTCUSDT", "my-strategy");
17082
+ * if (pending) {
17083
+ * console.log("Active signal:", pending.id);
17084
+ * }
17085
+ * ```
17086
+ */
17087
+ this.getPendingSignal = async (symbol, strategyName) => {
17088
+ backtest$1.loggerService.info(LIVE_METHOD_NAME_GET_PENDING_SIGNAL, {
17089
+ symbol,
17090
+ strategyName,
17091
+ });
17092
+ return await backtest$1.strategyCoreService.getPendingSignal(false, symbol, strategyName);
17093
+ };
17094
+ /**
17095
+ * Retrieves the currently active scheduled signal for the strategy.
17096
+ * If no scheduled signal exists, returns null.
17097
+ *
17098
+ * @param symbol - Trading pair symbol
17099
+ * @param strategyName - Name of strategy to get scheduled signal for
17100
+ * @returns Promise resolving to scheduled signal or null
17101
+ *
17102
+ * @example
17103
+ * ```typescript
17104
+ * const instance = new LiveInstance();
17105
+ * const scheduled = await instance.getScheduledSignal("BTCUSDT", "my-strategy");
17106
+ * if (scheduled) {
17107
+ * console.log("Scheduled signal:", scheduled.id);
17108
+ * }
17109
+ * ```
17110
+ */
17111
+ this.getScheduledSignal = async (symbol, strategyName) => {
17112
+ backtest$1.loggerService.info(LIVE_METHOD_NAME_GET_SCHEDULED_SIGNAL, {
17113
+ symbol,
17114
+ strategyName,
17115
+ });
17116
+ return await backtest$1.strategyCoreService.getScheduledSignal(false, symbol, strategyName);
17117
+ };
16579
17118
  /**
16580
17119
  * Stops the strategy from generating new signals.
16581
17120
  *
@@ -16600,6 +17139,32 @@ class LiveInstance {
16600
17139
  });
16601
17140
  await backtest$1.strategyCoreService.stop(false, { symbol, strategyName });
16602
17141
  };
17142
+ /**
17143
+ * Cancels the scheduled signal without stopping the strategy.
17144
+ *
17145
+ * Clears the scheduled signal (waiting for priceOpen activation).
17146
+ * Does NOT affect active pending signals or strategy operation.
17147
+ * Does NOT set stop flag - strategy can continue generating new signals.
17148
+ *
17149
+ * @param symbol - Trading pair symbol
17150
+ * @param strategyName - Strategy name
17151
+ * @param cancelId - Optional cancellation ID for tracking user-initiated cancellations
17152
+ * @returns Promise that resolves when scheduled signal is cancelled
17153
+ *
17154
+ * @example
17155
+ * ```typescript
17156
+ * const instance = new LiveInstance();
17157
+ * await instance.cancel("BTCUSDT", "my-strategy", "manual-cancel-001");
17158
+ * ```
17159
+ */
17160
+ this.cancel = async (symbol, strategyName, cancelId) => {
17161
+ backtest$1.loggerService.info(LIVE_METHOD_NAME_CANCEL, {
17162
+ symbol,
17163
+ strategyName,
17164
+ cancelId,
17165
+ });
17166
+ await backtest$1.strategyCoreService.cancel(false, { symbol, strategyName }, cancelId);
17167
+ };
16603
17168
  /**
16604
17169
  * Gets statistical data from all live trading events for a symbol-strategy pair.
16605
17170
  *
@@ -16762,6 +17327,58 @@ class LiveUtils {
16762
17327
  const instance = this._getInstance(symbol, context.strategyName);
16763
17328
  return instance.background(symbol, context);
16764
17329
  };
17330
+ /**
17331
+ * Retrieves the currently active pending signal for the strategy.
17332
+ * If no active signal exists, returns null.
17333
+ *
17334
+ * @param symbol - Trading pair symbol
17335
+ * @param strategyName - Name of strategy to get pending signal for
17336
+ * @returns Promise resolving to pending signal or null
17337
+ *
17338
+ * @example
17339
+ * ```typescript
17340
+ * const pending = await Live.getPendingSignal("BTCUSDT", "my-strategy");
17341
+ * if (pending) {
17342
+ * console.log("Active signal:", pending.id);
17343
+ * }
17344
+ * ```
17345
+ */
17346
+ this.getPendingSignal = async (symbol, strategyName) => {
17347
+ backtest$1.strategyValidationService.validate(strategyName, LIVE_METHOD_NAME_GET_PENDING_SIGNAL);
17348
+ {
17349
+ const { riskName, riskList } = backtest$1.strategySchemaService.get(strategyName);
17350
+ riskName && backtest$1.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_PENDING_SIGNAL);
17351
+ riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_PENDING_SIGNAL));
17352
+ }
17353
+ const instance = this._getInstance(symbol, strategyName);
17354
+ return await instance.getPendingSignal(symbol, strategyName);
17355
+ };
17356
+ /**
17357
+ * Retrieves the currently active scheduled signal for the strategy.
17358
+ * If no scheduled signal exists, returns null.
17359
+ *
17360
+ * @param symbol - Trading pair symbol
17361
+ * @param strategyName - Name of strategy to get scheduled signal for
17362
+ * @returns Promise resolving to scheduled signal or null
17363
+ *
17364
+ * @example
17365
+ * ```typescript
17366
+ * const scheduled = await Live.getScheduledSignal("BTCUSDT", "my-strategy");
17367
+ * if (scheduled) {
17368
+ * console.log("Scheduled signal:", scheduled.id);
17369
+ * }
17370
+ * ```
17371
+ */
17372
+ this.getScheduledSignal = async (symbol, strategyName) => {
17373
+ backtest$1.strategyValidationService.validate(strategyName, LIVE_METHOD_NAME_GET_SCHEDULED_SIGNAL);
17374
+ {
17375
+ const { riskName, riskList } = backtest$1.strategySchemaService.get(strategyName);
17376
+ riskName && backtest$1.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_SCHEDULED_SIGNAL);
17377
+ riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_SCHEDULED_SIGNAL));
17378
+ }
17379
+ const instance = this._getInstance(symbol, strategyName);
17380
+ return await instance.getScheduledSignal(symbol, strategyName);
17381
+ };
16765
17382
  /**
16766
17383
  * Stops the strategy from generating new signals.
16767
17384
  *
@@ -16789,6 +17406,34 @@ class LiveUtils {
16789
17406
  const instance = this._getInstance(symbol, strategyName);
16790
17407
  return await instance.stop(symbol, strategyName);
16791
17408
  };
17409
+ /**
17410
+ * Cancels the scheduled signal without stopping the strategy.
17411
+ *
17412
+ * Clears the scheduled signal (waiting for priceOpen activation).
17413
+ * Does NOT affect active pending signals or strategy operation.
17414
+ * Does NOT set stop flag - strategy can continue generating new signals.
17415
+ *
17416
+ * @param symbol - Trading pair symbol
17417
+ * @param strategyName - Strategy name
17418
+ * @param cancelId - Optional cancellation ID for tracking user-initiated cancellations
17419
+ * @returns Promise that resolves when scheduled signal is cancelled
17420
+ *
17421
+ * @example
17422
+ * ```typescript
17423
+ * // Cancel scheduled signal in live trading with custom ID
17424
+ * await Live.cancel("BTCUSDT", "my-strategy", "manual-cancel-001");
17425
+ * ```
17426
+ */
17427
+ this.cancel = async (symbol, strategyName, cancelId) => {
17428
+ backtest$1.strategyValidationService.validate(strategyName, LIVE_METHOD_NAME_CANCEL);
17429
+ {
17430
+ const { riskName, riskList } = backtest$1.strategySchemaService.get(strategyName);
17431
+ riskName && backtest$1.riskValidationService.validate(riskName, LIVE_METHOD_NAME_CANCEL);
17432
+ riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, LIVE_METHOD_NAME_CANCEL));
17433
+ }
17434
+ const instance = this._getInstance(symbol, strategyName);
17435
+ return await instance.cancel(symbol, strategyName, cancelId);
17436
+ };
16792
17437
  /**
16793
17438
  * Gets statistical data from all live trading events for a symbol-strategy pair.
16794
17439
  *
@@ -19012,6 +19657,8 @@ exports.listenPartialLossOnce = listenPartialLossOnce;
19012
19657
  exports.listenPartialProfit = listenPartialProfit;
19013
19658
  exports.listenPartialProfitOnce = listenPartialProfitOnce;
19014
19659
  exports.listenPerformance = listenPerformance;
19660
+ exports.listenPing = listenPing;
19661
+ exports.listenPingOnce = listenPingOnce;
19015
19662
  exports.listenRisk = listenRisk;
19016
19663
  exports.listenRiskOnce = listenRiskOnce;
19017
19664
  exports.listenSignal = listenSignal;