backtest-kit 1.3.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.cjs CHANGED
@@ -1625,6 +1625,9 @@ const GET_SIGNAL_FN = functoolsKit.trycatch(async (self) => {
1625
1625
  if (!signal) {
1626
1626
  return null;
1627
1627
  }
1628
+ if (self._isStopped) {
1629
+ return null;
1630
+ }
1628
1631
  // Если priceOpen указан - проверяем нужно ли ждать активации или открыть сразу
1629
1632
  if (signal.priceOpen !== undefined) {
1630
1633
  // КРИТИЧЕСКАЯ ПРОВЕРКА: достигнут ли priceOpen?
@@ -2351,6 +2354,14 @@ class ClientStrategy {
2351
2354
  }
2352
2355
  await PersistSignalAdapter.writeSignalData(this._pendingSignal, this.params.strategyName, this.params.execution.context.symbol);
2353
2356
  }
2357
+ /**
2358
+ * Retrieves the current pending signal.
2359
+ * If no signal is pending, returns null.
2360
+ * @returns Promise resolving to the pending signal or null.
2361
+ */
2362
+ async getPendingSignal() {
2363
+ return this._pendingSignal;
2364
+ }
2354
2365
  /**
2355
2366
  * Performs a single tick of strategy execution.
2356
2367
  *
@@ -2656,6 +2667,17 @@ class StrategyConnectionService {
2656
2667
  callbacks,
2657
2668
  });
2658
2669
  });
2670
+ /**
2671
+ * Retrieves the currently active pending signal for the strategy.
2672
+ * If no active signal exists, returns null.
2673
+ * Used internally for monitoring TP/SL and time expiration.
2674
+ * @returns Promise resolving to pending signal or null
2675
+ */
2676
+ this.getPendingSignal = async () => {
2677
+ this.loggerService.log("strategyConnectionService getPendingSignal");
2678
+ const strategy = await this.getStrategy(this.methodContextService.context.strategyName);
2679
+ return await strategy.getPendingSignal();
2680
+ };
2659
2681
  /**
2660
2682
  * Executes live trading tick for current strategy.
2661
2683
  *
@@ -3558,6 +3580,31 @@ class StrategyGlobalService {
3558
3580
  riskName &&
3559
3581
  this.riskValidationService.validate(riskName, METHOD_NAME_VALIDATE);
3560
3582
  });
3583
+ /**
3584
+ * Retrieves the currently active pending signal for the symbol.
3585
+ * If no active signal exists, returns null.
3586
+ * Used internally for monitoring TP/SL and time expiration.
3587
+ *
3588
+ * @param symbol - Trading pair symbol
3589
+ * @param when - Timestamp for tick evaluation
3590
+ * @param backtest - Whether running in backtest mode
3591
+ * @returns Promise resolving to pending signal or null
3592
+ */
3593
+ this.getPendingSignal = async (symbol, when, backtest) => {
3594
+ this.loggerService.log("strategyGlobalService getPendingSignal", {
3595
+ symbol,
3596
+ when,
3597
+ backtest,
3598
+ });
3599
+ await this.validate(this.methodContextService.context.strategyName);
3600
+ return await ExecutionContextService.runInContext(async () => {
3601
+ return await this.strategyConnectionService.getPendingSignal();
3602
+ }, {
3603
+ symbol,
3604
+ when,
3605
+ backtest,
3606
+ });
3607
+ };
3561
3608
  /**
3562
3609
  * Checks signal status at a specific timestamp.
3563
3610
  *
@@ -9485,22 +9532,39 @@ class LiveUtils {
9485
9532
  context,
9486
9533
  });
9487
9534
  let isStopped = false;
9535
+ let isDone = false;
9488
9536
  const task = async () => {
9489
9537
  for await (const signal of this.run(symbol, context)) {
9490
9538
  if (signal?.action === "closed" && isStopped) {
9491
9539
  break;
9492
9540
  }
9493
9541
  }
9494
- await doneLiveSubject.next({
9495
- exchangeName: context.exchangeName,
9496
- strategyName: context.strategyName,
9497
- backtest: false,
9498
- symbol,
9499
- });
9542
+ if (!isDone) {
9543
+ await doneLiveSubject.next({
9544
+ exchangeName: context.exchangeName,
9545
+ strategyName: context.strategyName,
9546
+ backtest: false,
9547
+ symbol,
9548
+ });
9549
+ }
9550
+ isDone = true;
9500
9551
  };
9501
9552
  task().catch((error) => errorEmitter.next(new Error(functoolsKit.getErrorMessage(error))));
9502
9553
  return () => {
9503
9554
  backtest$1.strategyGlobalService.stop(context.strategyName);
9555
+ backtest$1.strategyGlobalService
9556
+ .getPendingSignal(symbol, new Date(), false)
9557
+ .then(async () => {
9558
+ if (!isDone) {
9559
+ await doneLiveSubject.next({
9560
+ exchangeName: context.exchangeName,
9561
+ strategyName: context.strategyName,
9562
+ backtest: false,
9563
+ symbol,
9564
+ });
9565
+ }
9566
+ isDone = true;
9567
+ });
9504
9568
  isStopped = true;
9505
9569
  };
9506
9570
  };
package/build/index.mjs CHANGED
@@ -1623,6 +1623,9 @@ const GET_SIGNAL_FN = trycatch(async (self) => {
1623
1623
  if (!signal) {
1624
1624
  return null;
1625
1625
  }
1626
+ if (self._isStopped) {
1627
+ return null;
1628
+ }
1626
1629
  // Если priceOpen указан - проверяем нужно ли ждать активации или открыть сразу
1627
1630
  if (signal.priceOpen !== undefined) {
1628
1631
  // КРИТИЧЕСКАЯ ПРОВЕРКА: достигнут ли priceOpen?
@@ -2349,6 +2352,14 @@ class ClientStrategy {
2349
2352
  }
2350
2353
  await PersistSignalAdapter.writeSignalData(this._pendingSignal, this.params.strategyName, this.params.execution.context.symbol);
2351
2354
  }
2355
+ /**
2356
+ * Retrieves the current pending signal.
2357
+ * If no signal is pending, returns null.
2358
+ * @returns Promise resolving to the pending signal or null.
2359
+ */
2360
+ async getPendingSignal() {
2361
+ return this._pendingSignal;
2362
+ }
2352
2363
  /**
2353
2364
  * Performs a single tick of strategy execution.
2354
2365
  *
@@ -2654,6 +2665,17 @@ class StrategyConnectionService {
2654
2665
  callbacks,
2655
2666
  });
2656
2667
  });
2668
+ /**
2669
+ * Retrieves the currently active pending signal for the strategy.
2670
+ * If no active signal exists, returns null.
2671
+ * Used internally for monitoring TP/SL and time expiration.
2672
+ * @returns Promise resolving to pending signal or null
2673
+ */
2674
+ this.getPendingSignal = async () => {
2675
+ this.loggerService.log("strategyConnectionService getPendingSignal");
2676
+ const strategy = await this.getStrategy(this.methodContextService.context.strategyName);
2677
+ return await strategy.getPendingSignal();
2678
+ };
2657
2679
  /**
2658
2680
  * Executes live trading tick for current strategy.
2659
2681
  *
@@ -3556,6 +3578,31 @@ class StrategyGlobalService {
3556
3578
  riskName &&
3557
3579
  this.riskValidationService.validate(riskName, METHOD_NAME_VALIDATE);
3558
3580
  });
3581
+ /**
3582
+ * Retrieves the currently active pending signal for the symbol.
3583
+ * If no active signal exists, returns null.
3584
+ * Used internally for monitoring TP/SL and time expiration.
3585
+ *
3586
+ * @param symbol - Trading pair symbol
3587
+ * @param when - Timestamp for tick evaluation
3588
+ * @param backtest - Whether running in backtest mode
3589
+ * @returns Promise resolving to pending signal or null
3590
+ */
3591
+ this.getPendingSignal = async (symbol, when, backtest) => {
3592
+ this.loggerService.log("strategyGlobalService getPendingSignal", {
3593
+ symbol,
3594
+ when,
3595
+ backtest,
3596
+ });
3597
+ await this.validate(this.methodContextService.context.strategyName);
3598
+ return await ExecutionContextService.runInContext(async () => {
3599
+ return await this.strategyConnectionService.getPendingSignal();
3600
+ }, {
3601
+ symbol,
3602
+ when,
3603
+ backtest,
3604
+ });
3605
+ };
3559
3606
  /**
3560
3607
  * Checks signal status at a specific timestamp.
3561
3608
  *
@@ -9483,22 +9530,39 @@ class LiveUtils {
9483
9530
  context,
9484
9531
  });
9485
9532
  let isStopped = false;
9533
+ let isDone = false;
9486
9534
  const task = async () => {
9487
9535
  for await (const signal of this.run(symbol, context)) {
9488
9536
  if (signal?.action === "closed" && isStopped) {
9489
9537
  break;
9490
9538
  }
9491
9539
  }
9492
- await doneLiveSubject.next({
9493
- exchangeName: context.exchangeName,
9494
- strategyName: context.strategyName,
9495
- backtest: false,
9496
- symbol,
9497
- });
9540
+ if (!isDone) {
9541
+ await doneLiveSubject.next({
9542
+ exchangeName: context.exchangeName,
9543
+ strategyName: context.strategyName,
9544
+ backtest: false,
9545
+ symbol,
9546
+ });
9547
+ }
9548
+ isDone = true;
9498
9549
  };
9499
9550
  task().catch((error) => errorEmitter.next(new Error(getErrorMessage(error))));
9500
9551
  return () => {
9501
9552
  backtest$1.strategyGlobalService.stop(context.strategyName);
9553
+ backtest$1.strategyGlobalService
9554
+ .getPendingSignal(symbol, new Date(), false)
9555
+ .then(async () => {
9556
+ if (!isDone) {
9557
+ await doneLiveSubject.next({
9558
+ exchangeName: context.exchangeName,
9559
+ strategyName: context.strategyName,
9560
+ backtest: false,
9561
+ symbol,
9562
+ });
9563
+ }
9564
+ isDone = true;
9565
+ });
9502
9566
  isStopped = true;
9503
9567
  };
9504
9568
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backtest-kit",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "A TypeScript library for trading system backtest",
5
5
  "author": {
6
6
  "name": "Petr Tripolsky",
package/types.d.ts CHANGED
@@ -825,6 +825,15 @@ interface IStrategy {
825
825
  * @returns Promise resolving to tick result (idle | opened | active | closed)
826
826
  */
827
827
  tick: (symbol: string) => Promise<IStrategyTickResult>;
828
+ /**
829
+ * Retrieves the currently active pending signal for the symbol.
830
+ * If no active signal exists, returns null.
831
+ * Used internally for monitoring TP/SL and time expiration.
832
+ *
833
+ * @param symbol
834
+ * @returns
835
+ */
836
+ getPendingSignal: (symbol: string) => Promise<ISignalRow | null>;
828
837
  /**
829
838
  * Fast backtest using historical candles.
830
839
  * Iterates through candles, calculates VWAP, checks TP/SL on each candle.
@@ -4718,6 +4727,13 @@ declare class StrategyConnectionService implements IStrategy {
4718
4727
  * @returns Configured ClientStrategy instance
4719
4728
  */
4720
4729
  private getStrategy;
4730
+ /**
4731
+ * Retrieves the currently active pending signal for the strategy.
4732
+ * If no active signal exists, returns null.
4733
+ * Used internally for monitoring TP/SL and time expiration.
4734
+ * @returns Promise resolving to pending signal or null
4735
+ */
4736
+ getPendingSignal: () => Promise<ISignalRow | null>;
4721
4737
  /**
4722
4738
  * Executes live trading tick for current strategy.
4723
4739
  *
@@ -5165,6 +5181,17 @@ declare class StrategyGlobalService {
5165
5181
  * @returns Promise that resolves when validation is complete
5166
5182
  */
5167
5183
  private validate;
5184
+ /**
5185
+ * Retrieves the currently active pending signal for the symbol.
5186
+ * If no active signal exists, returns null.
5187
+ * Used internally for monitoring TP/SL and time expiration.
5188
+ *
5189
+ * @param symbol - Trading pair symbol
5190
+ * @param when - Timestamp for tick evaluation
5191
+ * @param backtest - Whether running in backtest mode
5192
+ * @returns Promise resolving to pending signal or null
5193
+ */
5194
+ getPendingSignal: (symbol: string, when: Date, backtest: boolean) => Promise<ISignalRow | null>;
5168
5195
  /**
5169
5196
  * Checks signal status at a specific timestamp.
5170
5197
  *