backtest-kit 5.6.2 → 5.6.4

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
@@ -6475,6 +6475,18 @@ class ClientStrategy {
6475
6475
  });
6476
6476
  return this._pendingSignal !== null;
6477
6477
  }
6478
+ /**
6479
+ * Checks if there is a scheduled signal.
6480
+ *
6481
+ * @param symbol - Trading symbol to check for scheduled signal
6482
+ * @returns Promise resolving to true if a scheduled signal exists, false otherwise
6483
+ */
6484
+ async hasScheduledSignal(symbol) {
6485
+ this.params.logger.debug("ClientStrategy hasScheduledSignal", {
6486
+ symbol,
6487
+ });
6488
+ return this._scheduledSignal !== null;
6489
+ }
6478
6490
  /**
6479
6491
  * Updates pending signal and persists to disk in live mode.
6480
6492
  *
@@ -10034,6 +10046,22 @@ class StrategyConnectionService {
10034
10046
  const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
10035
10047
  return await strategy.hasPendingSignal(symbol);
10036
10048
  };
10049
+ /**
10050
+ * Checks if there is an active scheduled signal for the strategy.
10051
+ * Delegates to ClientStrategy.hasScheduledSignal() which checks if there is a waiting position signal
10052
+ * @param backtest - Whether running in backtest mode
10053
+ * @param symbol - Trading pair symbol
10054
+ * @param context - Execution context with strategyName, exchangeName, frameName
10055
+ * @returns Promise resolving to true if there is a waiting scheduled signal, false otherwise
10056
+ */
10057
+ this.hasScheduledSignal = async (backtest, symbol, context) => {
10058
+ this.loggerService.log("strategyConnectionService hasScheduledSignal", {
10059
+ symbol,
10060
+ context,
10061
+ });
10062
+ const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
10063
+ return await strategy.hasScheduledSignal(symbol);
10064
+ };
10037
10065
  /**
10038
10066
  * Returns the original estimated duration for the current pending signal.
10039
10067
  *
@@ -14385,6 +14413,24 @@ class StrategyCoreService {
14385
14413
  await this.validate(context);
14386
14414
  return await this.strategyConnectionService.hasPendingSignal(backtest, symbol, context);
14387
14415
  };
14416
+ /**
14417
+ * Checks if there is a waiting scheduled signal for the symbol.
14418
+ * Validates strategy existence and delegates to connection service
14419
+ * to check if a scheduled signal exists for the symbol.
14420
+ * Does not require execution context as this is a state query operation.
14421
+ * @param backtest - Whether running in backtest mode
14422
+ * @param symbol - Trading pair symbol
14423
+ * @param context - Execution context with strategyName, exchangeName, frameName
14424
+ * @returns Promise<boolean> - true if scheduled signal exists, false otherwise
14425
+ */
14426
+ this.hasScheduledSignal = async (backtest, symbol, context) => {
14427
+ this.loggerService.log("strategyCoreService hasScheduledSignal", {
14428
+ symbol,
14429
+ context,
14430
+ });
14431
+ await this.validate(context);
14432
+ return await this.strategyConnectionService.hasScheduledSignal(backtest, symbol, context);
14433
+ };
14388
14434
  /**
14389
14435
  * Returns the original estimated duration for the current pending signal.
14390
14436
  *
@@ -36018,6 +36064,13 @@ function listenStrategyCommitOnce(filterFn, fn) {
36018
36064
  */
36019
36065
  function listenSync(fn) {
36020
36066
  bt.loggerService.log(LISTEN_SYNC_METHOD_NAME);
36067
+ {
36068
+ console.error("listenSync is unwanted cause exchange integration should be implemented in Broker.useBrokerAdapter as an infrastructure domain layer");
36069
+ console.error("If you need to implement custom logic on signal open/close, please use signal(), signalBacktest(), signalLive() in addActionSchema handler");
36070
+ console.error("If listenSync throws the exchange will not execute the order!");
36071
+ console.error("");
36072
+ console.error("You have been warned!");
36073
+ }
36021
36074
  return syncSubject.subscribe(functoolsKit.queued(async (event) => fn(event)));
36022
36075
  }
36023
36076
  /**
@@ -36030,6 +36083,13 @@ function listenSync(fn) {
36030
36083
  */
36031
36084
  function listenSyncOnce(filterFn, fn) {
36032
36085
  bt.loggerService.log(LISTEN_SYNC_ONCE_METHOD_NAME);
36086
+ {
36087
+ console.error("listenSyncOnce is unwanted cause exchange integration should be implemented in Broker.useBrokerAdapter as an infrastructure domain layer");
36088
+ console.error("If you need to implement custom logic on signal open/close, please use signal(), signalBacktest(), signalLive() in addActionSchema handler");
36089
+ console.error("If listenSyncOnce throws the exchange WILL EXECUTE the order!");
36090
+ console.error("");
36091
+ console.error("You have been warned!");
36092
+ }
36033
36093
  return syncSubject.filter(filterFn).once(fn);
36034
36094
  }
36035
36095
  /**
@@ -36104,6 +36164,8 @@ const BACKTEST_METHOD_NAME_TRAILING_PROFIT_COST = "BacktestUtils.commitTrailingT
36104
36164
  const BACKTEST_METHOD_NAME_ACTIVATE_SCHEDULED = "Backtest.commitActivateScheduled";
36105
36165
  const BACKTEST_METHOD_NAME_AVERAGE_BUY = "Backtest.commitAverageBuy";
36106
36166
  const BACKTEST_METHOD_NAME_GET_DATA = "BacktestUtils.getData";
36167
+ const BACKTEST_METHOD_NAME_HAS_NO_PENDING_SIGNAL = "BacktestUtils.hasNoPendingSignal";
36168
+ const BACKTEST_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL = "BacktestUtils.hasNoScheduledSignal";
36107
36169
  /**
36108
36170
  * Internal task function that runs backtest and handles completion.
36109
36171
  * Consumes backtest results and updates instance state flags.
@@ -36607,6 +36669,74 @@ class BacktestUtils {
36607
36669
  }
36608
36670
  return await bt.strategyCoreService.getScheduledSignal(true, symbol, currentPrice, context);
36609
36671
  };
36672
+ /**
36673
+ * Returns true if there is NO active pending signal for the given symbol.
36674
+ *
36675
+ * Inverse of strategyCoreService.hasPendingSignal. Use to guard signal generation logic.
36676
+ *
36677
+ * @param symbol - Trading pair symbol
36678
+ * @param context - Execution context with strategyName, exchangeName, frameName
36679
+ * @returns Promise<boolean> - true if no pending signal exists, false if one does
36680
+ *
36681
+ * @example
36682
+ * ```typescript
36683
+ * if (await Backtest.hasNoPendingSignal("BTCUSDT", { strategyName, exchangeName, frameName })) {
36684
+ * // safe to open a new position
36685
+ * }
36686
+ * ```
36687
+ */
36688
+ this.hasNoPendingSignal = async (symbol, context) => {
36689
+ bt.loggerService.info(BACKTEST_METHOD_NAME_HAS_NO_PENDING_SIGNAL, {
36690
+ symbol,
36691
+ context,
36692
+ });
36693
+ bt.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_HAS_NO_PENDING_SIGNAL);
36694
+ bt.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_HAS_NO_PENDING_SIGNAL);
36695
+ {
36696
+ const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
36697
+ riskName &&
36698
+ bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_HAS_NO_PENDING_SIGNAL);
36699
+ riskList &&
36700
+ riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_HAS_NO_PENDING_SIGNAL));
36701
+ actions &&
36702
+ actions.forEach((actionName) => bt.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_HAS_NO_PENDING_SIGNAL));
36703
+ }
36704
+ return await functoolsKit.not(bt.strategyCoreService.hasPendingSignal(true, symbol, context));
36705
+ };
36706
+ /**
36707
+ * Returns true if there is NO active scheduled signal for the given symbol.
36708
+ *
36709
+ * Inverse of strategyCoreService.hasScheduledSignal. Use to guard signal generation logic.
36710
+ *
36711
+ * @param symbol - Trading pair symbol
36712
+ * @param context - Execution context with strategyName, exchangeName, frameName
36713
+ * @returns Promise<boolean> - true if no scheduled signal exists, false if one does
36714
+ *
36715
+ * @example
36716
+ * ```typescript
36717
+ * if (await Backtest.hasNoScheduledSignal("BTCUSDT", { strategyName, exchangeName, frameName })) {
36718
+ * // safe to schedule a new signal
36719
+ * }
36720
+ * ```
36721
+ */
36722
+ this.hasNoScheduledSignal = async (symbol, context) => {
36723
+ bt.loggerService.info(BACKTEST_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL, {
36724
+ symbol,
36725
+ context,
36726
+ });
36727
+ bt.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL);
36728
+ bt.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL);
36729
+ {
36730
+ const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
36731
+ riskName &&
36732
+ bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL);
36733
+ riskList &&
36734
+ riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL));
36735
+ actions &&
36736
+ actions.forEach((actionName) => bt.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL));
36737
+ }
36738
+ return await functoolsKit.not(bt.strategyCoreService.hasScheduledSignal(true, symbol, context));
36739
+ };
36610
36740
  /**
36611
36741
  * Checks if breakeven threshold has been reached for the current pending signal.
36612
36742
  *
@@ -38252,6 +38382,8 @@ const LIVE_METHOD_NAME_TRAILING_STOP_COST = "LiveUtils.commitTrailingStopCost";
38252
38382
  const LIVE_METHOD_NAME_TRAILING_PROFIT_COST = "LiveUtils.commitTrailingTakeCost";
38253
38383
  const LIVE_METHOD_NAME_ACTIVATE_SCHEDULED = "Live.commitActivateScheduled";
38254
38384
  const LIVE_METHOD_NAME_AVERAGE_BUY = "Live.commitAverageBuy";
38385
+ const LIVE_METHOD_NAME_HAS_NO_PENDING_SIGNAL = "LiveUtils.hasNoPendingSignal";
38386
+ const LIVE_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL = "LiveUtils.hasNoScheduledSignal";
38255
38387
  /**
38256
38388
  * Internal task function that runs live trading and handles completion.
38257
38389
  * Consumes live trading results and updates instance state flags.
@@ -38784,6 +38916,82 @@ class LiveUtils {
38784
38916
  frameName: "",
38785
38917
  });
38786
38918
  };
38919
+ /**
38920
+ * Returns true if there is NO active pending signal for the given symbol.
38921
+ *
38922
+ * Inverse of strategyCoreService.hasPendingSignal. Use to guard signal generation logic.
38923
+ *
38924
+ * @param symbol - Trading pair symbol
38925
+ * @param context - Execution context with strategyName and exchangeName
38926
+ * @returns Promise<boolean> - true if no pending signal exists, false if one does
38927
+ *
38928
+ * @example
38929
+ * ```typescript
38930
+ * if (await Live.hasNoPendingSignal("BTCUSDT", { strategyName, exchangeName })) {
38931
+ * // safe to open a new position
38932
+ * }
38933
+ * ```
38934
+ */
38935
+ this.hasNoPendingSignal = async (symbol, context) => {
38936
+ bt.loggerService.info(LIVE_METHOD_NAME_HAS_NO_PENDING_SIGNAL, {
38937
+ symbol,
38938
+ context,
38939
+ });
38940
+ bt.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_HAS_NO_PENDING_SIGNAL);
38941
+ bt.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_HAS_NO_PENDING_SIGNAL);
38942
+ {
38943
+ const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
38944
+ riskName &&
38945
+ bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_HAS_NO_PENDING_SIGNAL);
38946
+ riskList &&
38947
+ riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_HAS_NO_PENDING_SIGNAL));
38948
+ actions &&
38949
+ actions.forEach((actionName) => bt.actionValidationService.validate(actionName, LIVE_METHOD_NAME_HAS_NO_PENDING_SIGNAL));
38950
+ }
38951
+ return await functoolsKit.not(bt.strategyCoreService.hasPendingSignal(false, symbol, {
38952
+ strategyName: context.strategyName,
38953
+ exchangeName: context.exchangeName,
38954
+ frameName: "",
38955
+ }));
38956
+ };
38957
+ /**
38958
+ * Returns true if there is NO active scheduled signal for the given symbol.
38959
+ *
38960
+ * Inverse of strategyCoreService.hasScheduledSignal. Use to guard signal generation logic.
38961
+ *
38962
+ * @param symbol - Trading pair symbol
38963
+ * @param context - Execution context with strategyName and exchangeName
38964
+ * @returns Promise<boolean> - true if no scheduled signal exists, false if one does
38965
+ *
38966
+ * @example
38967
+ * ```typescript
38968
+ * if (await Live.hasNoScheduledSignal("BTCUSDT", { strategyName, exchangeName })) {
38969
+ * // safe to schedule a new signal
38970
+ * }
38971
+ * ```
38972
+ */
38973
+ this.hasNoScheduledSignal = async (symbol, context) => {
38974
+ bt.loggerService.info(LIVE_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL, {
38975
+ symbol,
38976
+ context,
38977
+ });
38978
+ bt.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL);
38979
+ bt.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL);
38980
+ {
38981
+ const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
38982
+ riskName &&
38983
+ bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL);
38984
+ riskList &&
38985
+ riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL));
38986
+ actions &&
38987
+ actions.forEach((actionName) => bt.actionValidationService.validate(actionName, LIVE_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL));
38988
+ }
38989
+ return await functoolsKit.not(bt.strategyCoreService.hasScheduledSignal(false, symbol, {
38990
+ strategyName: context.strategyName,
38991
+ exchangeName: context.exchangeName,
38992
+ frameName: "",
38993
+ }));
38994
+ };
38787
38995
  /**
38788
38996
  * Checks if breakeven threshold has been reached for the current pending signal.
38789
38997
  *
package/build/index.mjs CHANGED
@@ -6455,6 +6455,18 @@ class ClientStrategy {
6455
6455
  });
6456
6456
  return this._pendingSignal !== null;
6457
6457
  }
6458
+ /**
6459
+ * Checks if there is a scheduled signal.
6460
+ *
6461
+ * @param symbol - Trading symbol to check for scheduled signal
6462
+ * @returns Promise resolving to true if a scheduled signal exists, false otherwise
6463
+ */
6464
+ async hasScheduledSignal(symbol) {
6465
+ this.params.logger.debug("ClientStrategy hasScheduledSignal", {
6466
+ symbol,
6467
+ });
6468
+ return this._scheduledSignal !== null;
6469
+ }
6458
6470
  /**
6459
6471
  * Updates pending signal and persists to disk in live mode.
6460
6472
  *
@@ -10014,6 +10026,22 @@ class StrategyConnectionService {
10014
10026
  const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
10015
10027
  return await strategy.hasPendingSignal(symbol);
10016
10028
  };
10029
+ /**
10030
+ * Checks if there is an active scheduled signal for the strategy.
10031
+ * Delegates to ClientStrategy.hasScheduledSignal() which checks if there is a waiting position signal
10032
+ * @param backtest - Whether running in backtest mode
10033
+ * @param symbol - Trading pair symbol
10034
+ * @param context - Execution context with strategyName, exchangeName, frameName
10035
+ * @returns Promise resolving to true if there is a waiting scheduled signal, false otherwise
10036
+ */
10037
+ this.hasScheduledSignal = async (backtest, symbol, context) => {
10038
+ this.loggerService.log("strategyConnectionService hasScheduledSignal", {
10039
+ symbol,
10040
+ context,
10041
+ });
10042
+ const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
10043
+ return await strategy.hasScheduledSignal(symbol);
10044
+ };
10017
10045
  /**
10018
10046
  * Returns the original estimated duration for the current pending signal.
10019
10047
  *
@@ -14365,6 +14393,24 @@ class StrategyCoreService {
14365
14393
  await this.validate(context);
14366
14394
  return await this.strategyConnectionService.hasPendingSignal(backtest, symbol, context);
14367
14395
  };
14396
+ /**
14397
+ * Checks if there is a waiting scheduled signal for the symbol.
14398
+ * Validates strategy existence and delegates to connection service
14399
+ * to check if a scheduled signal exists for the symbol.
14400
+ * Does not require execution context as this is a state query operation.
14401
+ * @param backtest - Whether running in backtest mode
14402
+ * @param symbol - Trading pair symbol
14403
+ * @param context - Execution context with strategyName, exchangeName, frameName
14404
+ * @returns Promise<boolean> - true if scheduled signal exists, false otherwise
14405
+ */
14406
+ this.hasScheduledSignal = async (backtest, symbol, context) => {
14407
+ this.loggerService.log("strategyCoreService hasScheduledSignal", {
14408
+ symbol,
14409
+ context,
14410
+ });
14411
+ await this.validate(context);
14412
+ return await this.strategyConnectionService.hasScheduledSignal(backtest, symbol, context);
14413
+ };
14368
14414
  /**
14369
14415
  * Returns the original estimated duration for the current pending signal.
14370
14416
  *
@@ -35998,6 +36044,13 @@ function listenStrategyCommitOnce(filterFn, fn) {
35998
36044
  */
35999
36045
  function listenSync(fn) {
36000
36046
  bt.loggerService.log(LISTEN_SYNC_METHOD_NAME);
36047
+ {
36048
+ console.error("listenSync is unwanted cause exchange integration should be implemented in Broker.useBrokerAdapter as an infrastructure domain layer");
36049
+ console.error("If you need to implement custom logic on signal open/close, please use signal(), signalBacktest(), signalLive() in addActionSchema handler");
36050
+ console.error("If listenSync throws the exchange will not execute the order!");
36051
+ console.error("");
36052
+ console.error("You have been warned!");
36053
+ }
36001
36054
  return syncSubject.subscribe(queued(async (event) => fn(event)));
36002
36055
  }
36003
36056
  /**
@@ -36010,6 +36063,13 @@ function listenSync(fn) {
36010
36063
  */
36011
36064
  function listenSyncOnce(filterFn, fn) {
36012
36065
  bt.loggerService.log(LISTEN_SYNC_ONCE_METHOD_NAME);
36066
+ {
36067
+ console.error("listenSyncOnce is unwanted cause exchange integration should be implemented in Broker.useBrokerAdapter as an infrastructure domain layer");
36068
+ console.error("If you need to implement custom logic on signal open/close, please use signal(), signalBacktest(), signalLive() in addActionSchema handler");
36069
+ console.error("If listenSyncOnce throws the exchange WILL EXECUTE the order!");
36070
+ console.error("");
36071
+ console.error("You have been warned!");
36072
+ }
36013
36073
  return syncSubject.filter(filterFn).once(fn);
36014
36074
  }
36015
36075
  /**
@@ -36084,6 +36144,8 @@ const BACKTEST_METHOD_NAME_TRAILING_PROFIT_COST = "BacktestUtils.commitTrailingT
36084
36144
  const BACKTEST_METHOD_NAME_ACTIVATE_SCHEDULED = "Backtest.commitActivateScheduled";
36085
36145
  const BACKTEST_METHOD_NAME_AVERAGE_BUY = "Backtest.commitAverageBuy";
36086
36146
  const BACKTEST_METHOD_NAME_GET_DATA = "BacktestUtils.getData";
36147
+ const BACKTEST_METHOD_NAME_HAS_NO_PENDING_SIGNAL = "BacktestUtils.hasNoPendingSignal";
36148
+ const BACKTEST_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL = "BacktestUtils.hasNoScheduledSignal";
36087
36149
  /**
36088
36150
  * Internal task function that runs backtest and handles completion.
36089
36151
  * Consumes backtest results and updates instance state flags.
@@ -36587,6 +36649,74 @@ class BacktestUtils {
36587
36649
  }
36588
36650
  return await bt.strategyCoreService.getScheduledSignal(true, symbol, currentPrice, context);
36589
36651
  };
36652
+ /**
36653
+ * Returns true if there is NO active pending signal for the given symbol.
36654
+ *
36655
+ * Inverse of strategyCoreService.hasPendingSignal. Use to guard signal generation logic.
36656
+ *
36657
+ * @param symbol - Trading pair symbol
36658
+ * @param context - Execution context with strategyName, exchangeName, frameName
36659
+ * @returns Promise<boolean> - true if no pending signal exists, false if one does
36660
+ *
36661
+ * @example
36662
+ * ```typescript
36663
+ * if (await Backtest.hasNoPendingSignal("BTCUSDT", { strategyName, exchangeName, frameName })) {
36664
+ * // safe to open a new position
36665
+ * }
36666
+ * ```
36667
+ */
36668
+ this.hasNoPendingSignal = async (symbol, context) => {
36669
+ bt.loggerService.info(BACKTEST_METHOD_NAME_HAS_NO_PENDING_SIGNAL, {
36670
+ symbol,
36671
+ context,
36672
+ });
36673
+ bt.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_HAS_NO_PENDING_SIGNAL);
36674
+ bt.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_HAS_NO_PENDING_SIGNAL);
36675
+ {
36676
+ const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
36677
+ riskName &&
36678
+ bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_HAS_NO_PENDING_SIGNAL);
36679
+ riskList &&
36680
+ riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_HAS_NO_PENDING_SIGNAL));
36681
+ actions &&
36682
+ actions.forEach((actionName) => bt.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_HAS_NO_PENDING_SIGNAL));
36683
+ }
36684
+ return await not(bt.strategyCoreService.hasPendingSignal(true, symbol, context));
36685
+ };
36686
+ /**
36687
+ * Returns true if there is NO active scheduled signal for the given symbol.
36688
+ *
36689
+ * Inverse of strategyCoreService.hasScheduledSignal. Use to guard signal generation logic.
36690
+ *
36691
+ * @param symbol - Trading pair symbol
36692
+ * @param context - Execution context with strategyName, exchangeName, frameName
36693
+ * @returns Promise<boolean> - true if no scheduled signal exists, false if one does
36694
+ *
36695
+ * @example
36696
+ * ```typescript
36697
+ * if (await Backtest.hasNoScheduledSignal("BTCUSDT", { strategyName, exchangeName, frameName })) {
36698
+ * // safe to schedule a new signal
36699
+ * }
36700
+ * ```
36701
+ */
36702
+ this.hasNoScheduledSignal = async (symbol, context) => {
36703
+ bt.loggerService.info(BACKTEST_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL, {
36704
+ symbol,
36705
+ context,
36706
+ });
36707
+ bt.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL);
36708
+ bt.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL);
36709
+ {
36710
+ const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
36711
+ riskName &&
36712
+ bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL);
36713
+ riskList &&
36714
+ riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL));
36715
+ actions &&
36716
+ actions.forEach((actionName) => bt.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL));
36717
+ }
36718
+ return await not(bt.strategyCoreService.hasScheduledSignal(true, symbol, context));
36719
+ };
36590
36720
  /**
36591
36721
  * Checks if breakeven threshold has been reached for the current pending signal.
36592
36722
  *
@@ -38232,6 +38362,8 @@ const LIVE_METHOD_NAME_TRAILING_STOP_COST = "LiveUtils.commitTrailingStopCost";
38232
38362
  const LIVE_METHOD_NAME_TRAILING_PROFIT_COST = "LiveUtils.commitTrailingTakeCost";
38233
38363
  const LIVE_METHOD_NAME_ACTIVATE_SCHEDULED = "Live.commitActivateScheduled";
38234
38364
  const LIVE_METHOD_NAME_AVERAGE_BUY = "Live.commitAverageBuy";
38365
+ const LIVE_METHOD_NAME_HAS_NO_PENDING_SIGNAL = "LiveUtils.hasNoPendingSignal";
38366
+ const LIVE_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL = "LiveUtils.hasNoScheduledSignal";
38235
38367
  /**
38236
38368
  * Internal task function that runs live trading and handles completion.
38237
38369
  * Consumes live trading results and updates instance state flags.
@@ -38764,6 +38896,82 @@ class LiveUtils {
38764
38896
  frameName: "",
38765
38897
  });
38766
38898
  };
38899
+ /**
38900
+ * Returns true if there is NO active pending signal for the given symbol.
38901
+ *
38902
+ * Inverse of strategyCoreService.hasPendingSignal. Use to guard signal generation logic.
38903
+ *
38904
+ * @param symbol - Trading pair symbol
38905
+ * @param context - Execution context with strategyName and exchangeName
38906
+ * @returns Promise<boolean> - true if no pending signal exists, false if one does
38907
+ *
38908
+ * @example
38909
+ * ```typescript
38910
+ * if (await Live.hasNoPendingSignal("BTCUSDT", { strategyName, exchangeName })) {
38911
+ * // safe to open a new position
38912
+ * }
38913
+ * ```
38914
+ */
38915
+ this.hasNoPendingSignal = async (symbol, context) => {
38916
+ bt.loggerService.info(LIVE_METHOD_NAME_HAS_NO_PENDING_SIGNAL, {
38917
+ symbol,
38918
+ context,
38919
+ });
38920
+ bt.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_HAS_NO_PENDING_SIGNAL);
38921
+ bt.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_HAS_NO_PENDING_SIGNAL);
38922
+ {
38923
+ const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
38924
+ riskName &&
38925
+ bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_HAS_NO_PENDING_SIGNAL);
38926
+ riskList &&
38927
+ riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_HAS_NO_PENDING_SIGNAL));
38928
+ actions &&
38929
+ actions.forEach((actionName) => bt.actionValidationService.validate(actionName, LIVE_METHOD_NAME_HAS_NO_PENDING_SIGNAL));
38930
+ }
38931
+ return await not(bt.strategyCoreService.hasPendingSignal(false, symbol, {
38932
+ strategyName: context.strategyName,
38933
+ exchangeName: context.exchangeName,
38934
+ frameName: "",
38935
+ }));
38936
+ };
38937
+ /**
38938
+ * Returns true if there is NO active scheduled signal for the given symbol.
38939
+ *
38940
+ * Inverse of strategyCoreService.hasScheduledSignal. Use to guard signal generation logic.
38941
+ *
38942
+ * @param symbol - Trading pair symbol
38943
+ * @param context - Execution context with strategyName and exchangeName
38944
+ * @returns Promise<boolean> - true if no scheduled signal exists, false if one does
38945
+ *
38946
+ * @example
38947
+ * ```typescript
38948
+ * if (await Live.hasNoScheduledSignal("BTCUSDT", { strategyName, exchangeName })) {
38949
+ * // safe to schedule a new signal
38950
+ * }
38951
+ * ```
38952
+ */
38953
+ this.hasNoScheduledSignal = async (symbol, context) => {
38954
+ bt.loggerService.info(LIVE_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL, {
38955
+ symbol,
38956
+ context,
38957
+ });
38958
+ bt.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL);
38959
+ bt.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL);
38960
+ {
38961
+ const { riskName, riskList, actions } = bt.strategySchemaService.get(context.strategyName);
38962
+ riskName &&
38963
+ bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL);
38964
+ riskList &&
38965
+ riskList.forEach((riskName) => bt.riskValidationService.validate(riskName, LIVE_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL));
38966
+ actions &&
38967
+ actions.forEach((actionName) => bt.actionValidationService.validate(actionName, LIVE_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL));
38968
+ }
38969
+ return await not(bt.strategyCoreService.hasScheduledSignal(false, symbol, {
38970
+ strategyName: context.strategyName,
38971
+ exchangeName: context.exchangeName,
38972
+ frameName: "",
38973
+ }));
38974
+ };
38767
38975
  /**
38768
38976
  * Checks if breakeven threshold has been reached for the current pending signal.
38769
38977
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backtest-kit",
3
- "version": "5.6.2",
3
+ "version": "5.6.4",
4
4
  "description": "A TypeScript library for trading system backtest",
5
5
  "author": {
6
6
  "name": "Petr Tripolsky",
package/types.d.ts CHANGED
@@ -1021,7 +1021,7 @@ interface ActivePingContract {
1021
1021
  * Complete pending signal row data.
1022
1022
  * Contains all signal information: id, position, priceOpen, priceTakeProfit, priceStopLoss, etc.
1023
1023
  */
1024
- data: ISignalRow;
1024
+ data: IPublicSignalRow;
1025
1025
  /**
1026
1026
  * Current market price of the symbol at the time of the ping.
1027
1027
  * Useful for users to implement custom management logic based on price conditions.
@@ -3399,6 +3399,15 @@ interface IStrategy {
3399
3399
  * @returns Promise resolving to true if pending signal exists, false otherwise
3400
3400
  */
3401
3401
  hasPendingSignal: (symbol: string) => Promise<boolean>;
3402
+ /**
3403
+ * Checks if there is an active scheduled signal for the symbol.
3404
+ *
3405
+ * Used internally to determine if TP/SL monitoring should occur on tick.
3406
+ *
3407
+ * @param symbol - Trading pair symbol
3408
+ * @returns Promise resolving to true if scheduled signal exists, false otherwise
3409
+ */
3410
+ hasScheduledSignal: (symbol: string) => Promise<boolean>;
3402
3411
  /**
3403
3412
  * Returns the original estimated duration for the current pending signal.
3404
3413
  *
@@ -12295,6 +12304,48 @@ declare class BacktestUtils {
12295
12304
  exchangeName: ExchangeName;
12296
12305
  frameName: FrameName;
12297
12306
  }) => Promise<IScheduledSignalRow>;
12307
+ /**
12308
+ * Returns true if there is NO active pending signal for the given symbol.
12309
+ *
12310
+ * Inverse of strategyCoreService.hasPendingSignal. Use to guard signal generation logic.
12311
+ *
12312
+ * @param symbol - Trading pair symbol
12313
+ * @param context - Execution context with strategyName, exchangeName, frameName
12314
+ * @returns Promise<boolean> - true if no pending signal exists, false if one does
12315
+ *
12316
+ * @example
12317
+ * ```typescript
12318
+ * if (await Backtest.hasNoPendingSignal("BTCUSDT", { strategyName, exchangeName, frameName })) {
12319
+ * // safe to open a new position
12320
+ * }
12321
+ * ```
12322
+ */
12323
+ hasNoPendingSignal: (symbol: string, context: {
12324
+ strategyName: StrategyName;
12325
+ exchangeName: ExchangeName;
12326
+ frameName: FrameName;
12327
+ }) => Promise<boolean>;
12328
+ /**
12329
+ * Returns true if there is NO active scheduled signal for the given symbol.
12330
+ *
12331
+ * Inverse of strategyCoreService.hasScheduledSignal. Use to guard signal generation logic.
12332
+ *
12333
+ * @param symbol - Trading pair symbol
12334
+ * @param context - Execution context with strategyName, exchangeName, frameName
12335
+ * @returns Promise<boolean> - true if no scheduled signal exists, false if one does
12336
+ *
12337
+ * @example
12338
+ * ```typescript
12339
+ * if (await Backtest.hasNoScheduledSignal("BTCUSDT", { strategyName, exchangeName, frameName })) {
12340
+ * // safe to schedule a new signal
12341
+ * }
12342
+ * ```
12343
+ */
12344
+ hasNoScheduledSignal: (symbol: string, context: {
12345
+ strategyName: StrategyName;
12346
+ exchangeName: ExchangeName;
12347
+ frameName: FrameName;
12348
+ }) => Promise<boolean>;
12298
12349
  /**
12299
12350
  * Checks if breakeven threshold has been reached for the current pending signal.
12300
12351
  *
@@ -13546,6 +13597,46 @@ declare class LiveUtils {
13546
13597
  strategyName: StrategyName;
13547
13598
  exchangeName: ExchangeName;
13548
13599
  }) => Promise<IScheduledSignalRow>;
13600
+ /**
13601
+ * Returns true if there is NO active pending signal for the given symbol.
13602
+ *
13603
+ * Inverse of strategyCoreService.hasPendingSignal. Use to guard signal generation logic.
13604
+ *
13605
+ * @param symbol - Trading pair symbol
13606
+ * @param context - Execution context with strategyName and exchangeName
13607
+ * @returns Promise<boolean> - true if no pending signal exists, false if one does
13608
+ *
13609
+ * @example
13610
+ * ```typescript
13611
+ * if (await Live.hasNoPendingSignal("BTCUSDT", { strategyName, exchangeName })) {
13612
+ * // safe to open a new position
13613
+ * }
13614
+ * ```
13615
+ */
13616
+ hasNoPendingSignal: (symbol: string, context: {
13617
+ strategyName: StrategyName;
13618
+ exchangeName: ExchangeName;
13619
+ }) => Promise<boolean>;
13620
+ /**
13621
+ * Returns true if there is NO active scheduled signal for the given symbol.
13622
+ *
13623
+ * Inverse of strategyCoreService.hasScheduledSignal. Use to guard signal generation logic.
13624
+ *
13625
+ * @param symbol - Trading pair symbol
13626
+ * @param context - Execution context with strategyName and exchangeName
13627
+ * @returns Promise<boolean> - true if no scheduled signal exists, false if one does
13628
+ *
13629
+ * @example
13630
+ * ```typescript
13631
+ * if (await Live.hasNoScheduledSignal("BTCUSDT", { strategyName, exchangeName })) {
13632
+ * // safe to schedule a new signal
13633
+ * }
13634
+ * ```
13635
+ */
13636
+ hasNoScheduledSignal: (symbol: string, context: {
13637
+ strategyName: StrategyName;
13638
+ exchangeName: ExchangeName;
13639
+ }) => Promise<boolean>;
13549
13640
  /**
13550
13641
  * Checks if breakeven threshold has been reached for the current pending signal.
13551
13642
  *
@@ -22432,6 +22523,19 @@ declare class StrategyConnectionService implements TStrategy$1 {
22432
22523
  exchangeName: ExchangeName;
22433
22524
  frameName: FrameName;
22434
22525
  }) => Promise<boolean>;
22526
+ /**
22527
+ * Checks if there is an active scheduled signal for the strategy.
22528
+ * Delegates to ClientStrategy.hasScheduledSignal() which checks if there is a waiting position signal
22529
+ * @param backtest - Whether running in backtest mode
22530
+ * @param symbol - Trading pair symbol
22531
+ * @param context - Execution context with strategyName, exchangeName, frameName
22532
+ * @returns Promise resolving to true if there is a waiting scheduled signal, false otherwise
22533
+ */
22534
+ hasScheduledSignal: (backtest: boolean, symbol: string, context: {
22535
+ strategyName: StrategyName;
22536
+ exchangeName: ExchangeName;
22537
+ frameName: FrameName;
22538
+ }) => Promise<boolean>;
22435
22539
  /**
22436
22540
  * Returns the original estimated duration for the current pending signal.
22437
22541
  *
@@ -24186,6 +24290,21 @@ declare class StrategyCoreService implements TStrategy {
24186
24290
  exchangeName: ExchangeName;
24187
24291
  frameName: FrameName;
24188
24292
  }) => Promise<boolean>;
24293
+ /**
24294
+ * Checks if there is a waiting scheduled signal for the symbol.
24295
+ * Validates strategy existence and delegates to connection service
24296
+ * to check if a scheduled signal exists for the symbol.
24297
+ * Does not require execution context as this is a state query operation.
24298
+ * @param backtest - Whether running in backtest mode
24299
+ * @param symbol - Trading pair symbol
24300
+ * @param context - Execution context with strategyName, exchangeName, frameName
24301
+ * @returns Promise<boolean> - true if scheduled signal exists, false otherwise
24302
+ */
24303
+ hasScheduledSignal: (backtest: boolean, symbol: string, context: {
24304
+ strategyName: StrategyName;
24305
+ exchangeName: ExchangeName;
24306
+ frameName: FrameName;
24307
+ }) => Promise<boolean>;
24189
24308
  /**
24190
24309
  * Returns the original estimated duration for the current pending signal.
24191
24310
  *