backtest-kit 1.5.2 → 1.5.3

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
@@ -1675,6 +1675,8 @@ const walkerCompleteSubject = new functoolsKit.Subject();
1675
1675
  /**
1676
1676
  * Walker stop emitter for walker cancellation events.
1677
1677
  * Emits when a walker comparison is stopped/cancelled.
1678
+ *
1679
+ * Includes walkerName to support multiple walkers running on the same symbol.
1678
1680
  */
1679
1681
  const walkerStopSubject = new functoolsKit.Subject();
1680
1682
  /**
@@ -2909,6 +2911,23 @@ class ClientStrategy {
2909
2911
  });
2910
2912
  return this._pendingSignal;
2911
2913
  }
2914
+ /**
2915
+ * Returns the stopped state of the strategy.
2916
+ *
2917
+ * Indicates whether the strategy has been explicitly stopped and should
2918
+ * not continue processing new ticks or signals.
2919
+ *
2920
+ * @param symbol - Trading pair symbol
2921
+ * @param strategyName - Name of the strategy
2922
+ * @returns Promise resolving to true if strategy is stopped, false otherwise
2923
+ */
2924
+ async getStopped(symbol, strategyName) {
2925
+ this.params.logger.debug("ClientStrategy getStopped", {
2926
+ symbol,
2927
+ strategyName,
2928
+ });
2929
+ return this._isStopped;
2930
+ }
2912
2931
  /**
2913
2932
  * Performs a single tick of strategy execution.
2914
2933
  *
@@ -3151,18 +3170,24 @@ class ClientStrategy {
3151
3170
  * // Existing signal will continue until natural close
3152
3171
  * ```
3153
3172
  */
3154
- async stop(symbol, strategyName) {
3173
+ async stop(symbol, strategyName, backtest) {
3155
3174
  this.params.logger.debug("ClientStrategy stop", {
3156
3175
  symbol,
3157
3176
  strategyName,
3158
3177
  hasPendingSignal: this._pendingSignal !== null,
3159
3178
  hasScheduledSignal: this._scheduledSignal !== null,
3179
+ backtest,
3160
3180
  });
3161
3181
  this._isStopped = true;
3162
3182
  // Clear scheduled signal if exists
3163
- if (this._scheduledSignal) {
3164
- await this.setScheduledSignal(null);
3183
+ if (!this._scheduledSignal) {
3184
+ return;
3165
3185
  }
3186
+ this._scheduledSignal = null;
3187
+ if (backtest) {
3188
+ return;
3189
+ }
3190
+ await PersistScheduleAdapter.writeScheduleData(this._scheduledSignal, this.params.execution.context.symbol, this.params.strategyName);
3166
3191
  }
3167
3192
  }
3168
3193
 
@@ -3245,6 +3270,24 @@ class StrategyConnectionService {
3245
3270
  const strategy = this.getStrategy(symbol, strategyName);
3246
3271
  return await strategy.getPendingSignal(symbol, strategyName);
3247
3272
  };
3273
+ /**
3274
+ * Retrieves the stopped state of the strategy.
3275
+ *
3276
+ * Delegates to the underlying strategy instance to check if it has been
3277
+ * marked as stopped and should cease operation.
3278
+ *
3279
+ * @param symbol - Trading pair symbol
3280
+ * @param strategyName - Name of the strategy
3281
+ * @returns Promise resolving to true if strategy is stopped, false otherwise
3282
+ */
3283
+ this.getStopped = async (symbol, strategyName) => {
3284
+ this.loggerService.log("strategyConnectionService getStopped", {
3285
+ symbol,
3286
+ strategyName,
3287
+ });
3288
+ const strategy = this.getStrategy(symbol, strategyName);
3289
+ return await strategy.getStopped(symbol, strategyName);
3290
+ };
3248
3291
  /**
3249
3292
  * Executes live trading tick for current strategy.
3250
3293
  *
@@ -3312,12 +3355,12 @@ class StrategyConnectionService {
3312
3355
  * @param strategyName - Name of strategy to stop
3313
3356
  * @returns Promise that resolves when stop flag is set
3314
3357
  */
3315
- this.stop = async (ctx) => {
3358
+ this.stop = async (ctx, backtest) => {
3316
3359
  this.loggerService.log("strategyConnectionService stop", {
3317
3360
  ctx
3318
3361
  });
3319
3362
  const strategy = this.getStrategy(ctx.symbol, ctx.strategyName);
3320
- await strategy.stop(ctx.symbol, ctx.strategyName);
3363
+ await strategy.stop(ctx.symbol, ctx.strategyName, backtest);
3321
3364
  };
3322
3365
  /**
3323
3366
  * Clears the memoized ClientStrategy instance from cache.
@@ -4182,6 +4225,24 @@ class StrategyGlobalService {
4182
4225
  await this.validate(symbol, strategyName);
4183
4226
  return await this.strategyConnectionService.getPendingSignal(symbol, strategyName);
4184
4227
  };
4228
+ /**
4229
+ * Checks if the strategy has been stopped.
4230
+ *
4231
+ * Validates strategy existence and delegates to connection service
4232
+ * to retrieve the stopped state from the strategy instance.
4233
+ *
4234
+ * @param symbol - Trading pair symbol
4235
+ * @param strategyName - Name of the strategy
4236
+ * @returns Promise resolving to true if strategy is stopped, false otherwise
4237
+ */
4238
+ this.getStopped = async (symbol, strategyName) => {
4239
+ this.loggerService.log("strategyGlobalService getStopped", {
4240
+ symbol,
4241
+ strategyName,
4242
+ });
4243
+ await this.validate(symbol, strategyName);
4244
+ return await this.strategyConnectionService.getStopped(symbol, strategyName);
4245
+ };
4185
4246
  /**
4186
4247
  * Checks signal status at a specific timestamp.
4187
4248
  *
@@ -4248,12 +4309,13 @@ class StrategyGlobalService {
4248
4309
  * @param strategyName - Name of strategy to stop
4249
4310
  * @returns Promise that resolves when stop flag is set
4250
4311
  */
4251
- this.stop = async (ctx) => {
4312
+ this.stop = async (ctx, backtest) => {
4252
4313
  this.loggerService.log("strategyGlobalService stop", {
4253
4314
  ctx,
4315
+ backtest,
4254
4316
  });
4255
4317
  await this.validate(ctx.symbol, ctx.strategyName);
4256
- return await this.strategyConnectionService.stop(ctx);
4318
+ return await this.strategyConnectionService.stop(ctx, backtest);
4257
4319
  };
4258
4320
  /**
4259
4321
  * Clears the memoized ClientStrategy instance from cache.
@@ -4945,6 +5007,16 @@ class BacktestLogicPrivateService {
4945
5007
  progress: totalFrames > 0 ? i / totalFrames : 0,
4946
5008
  });
4947
5009
  }
5010
+ // Check if strategy should stop before processing next frame
5011
+ if (await this.strategyGlobalService.getStopped(symbol, this.methodContextService.context.strategyName)) {
5012
+ this.loggerService.info("backtestLogicPrivateService stopped by user request (before tick)", {
5013
+ symbol,
5014
+ when: when.toISOString(),
5015
+ processedFrames: i,
5016
+ totalFrames,
5017
+ });
5018
+ break;
5019
+ }
4948
5020
  let result;
4949
5021
  try {
4950
5022
  result = await this.strategyGlobalService.tick(symbol, when, true);
@@ -4960,6 +5032,16 @@ class BacktestLogicPrivateService {
4960
5032
  i++;
4961
5033
  continue;
4962
5034
  }
5035
+ // Check if strategy should stop when idle (no active signal)
5036
+ if (await functoolsKit.and(Promise.resolve(result.action === "idle"), this.strategyGlobalService.getStopped(symbol, this.methodContextService.context.strategyName))) {
5037
+ this.loggerService.info("backtestLogicPrivateService stopped by user request (idle state)", {
5038
+ symbol,
5039
+ when: when.toISOString(),
5040
+ processedFrames: i,
5041
+ totalFrames,
5042
+ });
5043
+ break;
5044
+ }
4963
5045
  // Если scheduled signal создан - обрабатываем через backtest()
4964
5046
  if (result.action === "scheduled") {
4965
5047
  const signalStartTime = performance.now();
@@ -5052,6 +5134,16 @@ class BacktestLogicPrivateService {
5052
5134
  i++;
5053
5135
  }
5054
5136
  yield backtestResult;
5137
+ // Check if strategy should stop after signal is closed
5138
+ if (await this.strategyGlobalService.getStopped(symbol, this.methodContextService.context.strategyName)) {
5139
+ this.loggerService.info("backtestLogicPrivateService stopped by user request (after scheduled signal closed)", {
5140
+ symbol,
5141
+ signalId: backtestResult.signal.id,
5142
+ processedFrames: i,
5143
+ totalFrames,
5144
+ });
5145
+ break;
5146
+ }
5055
5147
  }
5056
5148
  // Если обычный сигнал открыт, вызываем backtest
5057
5149
  if (result.action === "opened") {
@@ -5135,6 +5227,16 @@ class BacktestLogicPrivateService {
5135
5227
  i++;
5136
5228
  }
5137
5229
  yield backtestResult;
5230
+ // Check if strategy should stop after signal is closed
5231
+ if (await this.strategyGlobalService.getStopped(symbol, this.methodContextService.context.strategyName)) {
5232
+ this.loggerService.info("backtestLogicPrivateService stopped by user request (after signal closed)", {
5233
+ symbol,
5234
+ signalId: backtestResult.signal.id,
5235
+ processedFrames: i,
5236
+ totalFrames,
5237
+ });
5238
+ break;
5239
+ }
5138
5240
  }
5139
5241
  // Track timeframe processing duration
5140
5242
  const timeframeEndTime = performance.now();
@@ -5242,7 +5344,8 @@ class LiveLogicPrivateService {
5242
5344
  this.loggerService.warn("liveLogicPrivateService tick failed, retrying after sleep", {
5243
5345
  symbol,
5244
5346
  when: when.toISOString(),
5245
- error: functoolsKit.errorData(error), message: functoolsKit.getErrorMessage(error),
5347
+ error: functoolsKit.errorData(error),
5348
+ message: functoolsKit.getErrorMessage(error),
5246
5349
  });
5247
5350
  await errorEmitter.next(error);
5248
5351
  await functoolsKit.sleep(TICK_TTL);
@@ -5266,11 +5369,19 @@ class LiveLogicPrivateService {
5266
5369
  backtest: false,
5267
5370
  });
5268
5371
  previousEventTimestamp = currentTimestamp;
5269
- if (result.action === "active") {
5372
+ // Check if strategy should stop when idle (no active signal)
5373
+ if (result.action === "idle") {
5374
+ if (await functoolsKit.and(Promise.resolve(true), this.strategyGlobalService.getStopped(symbol, this.methodContextService.context.strategyName))) {
5375
+ this.loggerService.info("liveLogicPrivateService stopped by user request (idle state)", {
5376
+ symbol,
5377
+ when: when.toISOString(),
5378
+ });
5379
+ break;
5380
+ }
5270
5381
  await functoolsKit.sleep(TICK_TTL);
5271
5382
  continue;
5272
5383
  }
5273
- if (result.action === "idle") {
5384
+ if (result.action === "active") {
5274
5385
  await functoolsKit.sleep(TICK_TTL);
5275
5386
  continue;
5276
5387
  }
@@ -5280,12 +5391,21 @@ class LiveLogicPrivateService {
5280
5391
  }
5281
5392
  // Yield opened, closed results
5282
5393
  yield result;
5394
+ // Check if strategy should stop after signal is closed
5395
+ if (result.action === "closed") {
5396
+ if (await this.strategyGlobalService.getStopped(symbol, this.methodContextService.context.strategyName)) {
5397
+ this.loggerService.info("liveLogicPrivateService stopped by user request (after signal closed)", {
5398
+ symbol,
5399
+ signalId: result.signal.id,
5400
+ });
5401
+ break;
5402
+ }
5403
+ }
5283
5404
  await functoolsKit.sleep(TICK_TTL);
5284
5405
  }
5285
5406
  }
5286
5407
  }
5287
5408
 
5288
- const CANCEL_SYMBOL = Symbol("CANCEL_SYMBOL");
5289
5409
  /**
5290
5410
  * Private service for walker orchestration (strategy comparison).
5291
5411
  *
@@ -5343,112 +5463,121 @@ class WalkerLogicPrivateService {
5343
5463
  let strategiesTested = 0;
5344
5464
  let bestMetric = null;
5345
5465
  let bestStrategy = null;
5346
- let pendingStrategy;
5347
- const listenStop = walkerStopSubject
5348
- .filter((data) => {
5349
- let isOk = true;
5350
- isOk = isOk && data.symbol === symbol;
5351
- isOk = isOk && data.strategyName === pendingStrategy;
5352
- return isOk;
5353
- })
5354
- .map(() => CANCEL_SYMBOL)
5355
- .toPromise();
5356
- // Run backtest for each strategy
5357
- for (const strategyName of strategies) {
5358
- // Call onStrategyStart callback if provided
5359
- if (walkerSchema.callbacks?.onStrategyStart) {
5360
- walkerSchema.callbacks.onStrategyStart(strategyName, symbol);
5361
- }
5362
- this.loggerService.info("walkerLogicPrivateService testing strategy", {
5363
- strategyName,
5466
+ // Track stopped strategies in Set for efficient lookup
5467
+ const stoppedStrategies = new Set();
5468
+ // Subscribe to stop signals and collect them in Set
5469
+ // Filter by both symbol AND walkerName to support multiple walkers on same symbol
5470
+ // connect() returns destructor function
5471
+ const unsubscribe = walkerStopSubject
5472
+ .filter((data) => data.symbol === symbol && data.walkerName === context.walkerName)
5473
+ .connect((data) => {
5474
+ stoppedStrategies.add(data.strategyName);
5475
+ this.loggerService.info("walkerLogicPrivateService received stop signal for strategy", {
5364
5476
  symbol,
5477
+ walkerName: context.walkerName,
5478
+ strategyName: data.strategyName,
5479
+ stoppedCount: stoppedStrategies.size,
5365
5480
  });
5366
- const iterator = this.backtestLogicPublicService.run(symbol, {
5367
- strategyName,
5368
- exchangeName: context.exchangeName,
5369
- frameName: context.frameName,
5370
- });
5371
- pendingStrategy = strategyName;
5372
- let result;
5373
- try {
5374
- result = await Promise.race([
5375
- await functoolsKit.resolveDocuments(iterator),
5376
- listenStop,
5377
- ]);
5378
- }
5379
- catch (error) {
5380
- console.warn(`walkerLogicPrivateService backtest failed symbol=${symbol} strategyName=${strategyName} exchangeName=${context.exchangeName}`);
5381
- this.loggerService.warn("walkerLogicPrivateService backtest failed for strategy, skipping", {
5481
+ });
5482
+ try {
5483
+ // Run backtest for each strategy
5484
+ for (const strategyName of strategies) {
5485
+ // Check if this strategy should be stopped before starting
5486
+ if (stoppedStrategies.has(strategyName)) {
5487
+ this.loggerService.info("walkerLogicPrivateService skipping stopped strategy", {
5488
+ symbol,
5489
+ strategyName,
5490
+ });
5491
+ break;
5492
+ }
5493
+ // Call onStrategyStart callback if provided
5494
+ if (walkerSchema.callbacks?.onStrategyStart) {
5495
+ walkerSchema.callbacks.onStrategyStart(strategyName, symbol);
5496
+ }
5497
+ this.loggerService.info("walkerLogicPrivateService testing strategy", {
5382
5498
  strategyName,
5383
5499
  symbol,
5384
- error: functoolsKit.errorData(error), message: functoolsKit.getErrorMessage(error),
5385
5500
  });
5386
- await errorEmitter.next(error);
5387
- // Call onStrategyError callback if provided
5388
- if (walkerSchema.callbacks?.onStrategyError) {
5389
- walkerSchema.callbacks.onStrategyError(strategyName, symbol, error);
5501
+ const iterator = this.backtestLogicPublicService.run(symbol, {
5502
+ strategyName,
5503
+ exchangeName: context.exchangeName,
5504
+ frameName: context.frameName,
5505
+ });
5506
+ try {
5507
+ await functoolsKit.resolveDocuments(iterator);
5390
5508
  }
5391
- continue;
5392
- }
5393
- if (result === CANCEL_SYMBOL) {
5394
- this.loggerService.info("walkerLogicPrivateService received stop signal, cancelling walker", {
5395
- context,
5509
+ catch (error) {
5510
+ console.warn(`walkerLogicPrivateService backtest failed symbol=${symbol} strategyName=${strategyName} exchangeName=${context.exchangeName}`);
5511
+ this.loggerService.warn("walkerLogicPrivateService backtest failed for strategy, skipping", {
5512
+ strategyName,
5513
+ symbol,
5514
+ error: functoolsKit.errorData(error), message: functoolsKit.getErrorMessage(error),
5515
+ });
5516
+ await errorEmitter.next(error);
5517
+ // Call onStrategyError callback if provided
5518
+ if (walkerSchema.callbacks?.onStrategyError) {
5519
+ walkerSchema.callbacks.onStrategyError(strategyName, symbol, error);
5520
+ }
5521
+ continue;
5522
+ }
5523
+ this.loggerService.info("walkerLogicPrivateService backtest complete", {
5524
+ strategyName,
5525
+ symbol,
5396
5526
  });
5397
- break;
5398
- }
5399
- this.loggerService.info("walkerLogicPrivateService backtest complete", {
5400
- strategyName,
5401
- symbol,
5402
- });
5403
- // Get statistics from BacktestMarkdownService
5404
- const stats = await this.backtestMarkdownService.getData(symbol, strategyName);
5405
- // Extract metric value
5406
- const value = stats[metric];
5407
- const metricValue = value !== null &&
5408
- value !== undefined &&
5409
- typeof value === "number" &&
5410
- !isNaN(value) &&
5411
- isFinite(value)
5412
- ? value
5413
- : null;
5414
- // Update best strategy if needed
5415
- const isBetter = bestMetric === null ||
5416
- (metricValue !== null && metricValue > bestMetric);
5417
- if (isBetter && metricValue !== null) {
5418
- bestMetric = metricValue;
5419
- bestStrategy = strategyName;
5420
- }
5421
- strategiesTested++;
5422
- const walkerContract = {
5423
- walkerName: context.walkerName,
5424
- exchangeName: context.exchangeName,
5425
- frameName: context.frameName,
5426
- symbol,
5427
- strategyName,
5428
- stats,
5429
- metricValue,
5430
- metric,
5431
- bestMetric,
5432
- bestStrategy,
5433
- strategiesTested,
5434
- totalStrategies: strategies.length,
5435
- };
5436
- // Emit progress event
5437
- await progressWalkerEmitter.next({
5438
- walkerName: context.walkerName,
5439
- exchangeName: context.exchangeName,
5440
- frameName: context.frameName,
5441
- symbol,
5442
- totalStrategies: strategies.length,
5443
- processedStrategies: strategiesTested,
5444
- progress: strategies.length > 0 ? strategiesTested / strategies.length : 0,
5445
- });
5446
- // Call onStrategyComplete callback if provided
5447
- if (walkerSchema.callbacks?.onStrategyComplete) {
5448
- walkerSchema.callbacks.onStrategyComplete(strategyName, symbol, stats, metricValue);
5527
+ // Get statistics from BacktestMarkdownService
5528
+ const stats = await this.backtestMarkdownService.getData(symbol, strategyName);
5529
+ // Extract metric value
5530
+ const value = stats[metric];
5531
+ const metricValue = value !== null &&
5532
+ value !== undefined &&
5533
+ typeof value === "number" &&
5534
+ !isNaN(value) &&
5535
+ isFinite(value)
5536
+ ? value
5537
+ : null;
5538
+ // Update best strategy if needed
5539
+ const isBetter = bestMetric === null ||
5540
+ (metricValue !== null && metricValue > bestMetric);
5541
+ if (isBetter && metricValue !== null) {
5542
+ bestMetric = metricValue;
5543
+ bestStrategy = strategyName;
5544
+ }
5545
+ strategiesTested++;
5546
+ const walkerContract = {
5547
+ walkerName: context.walkerName,
5548
+ exchangeName: context.exchangeName,
5549
+ frameName: context.frameName,
5550
+ symbol,
5551
+ strategyName,
5552
+ stats,
5553
+ metricValue,
5554
+ metric,
5555
+ bestMetric,
5556
+ bestStrategy,
5557
+ strategiesTested,
5558
+ totalStrategies: strategies.length,
5559
+ };
5560
+ // Emit progress event
5561
+ await progressWalkerEmitter.next({
5562
+ walkerName: context.walkerName,
5563
+ exchangeName: context.exchangeName,
5564
+ frameName: context.frameName,
5565
+ symbol,
5566
+ totalStrategies: strategies.length,
5567
+ processedStrategies: strategiesTested,
5568
+ progress: strategies.length > 0 ? strategiesTested / strategies.length : 0,
5569
+ });
5570
+ // Call onStrategyComplete callback if provided
5571
+ if (walkerSchema.callbacks?.onStrategyComplete) {
5572
+ await walkerSchema.callbacks.onStrategyComplete(strategyName, symbol, stats, metricValue);
5573
+ }
5574
+ await walkerEmitter.next(walkerContract);
5575
+ yield walkerContract;
5449
5576
  }
5450
- await walkerEmitter.next(walkerContract);
5451
- yield walkerContract;
5577
+ }
5578
+ finally {
5579
+ // Unsubscribe from stop signals by calling destructor
5580
+ unsubscribe();
5452
5581
  }
5453
5582
  const finalResults = {
5454
5583
  walkerName: context.walkerName,
@@ -13270,6 +13399,7 @@ async function dumpSignal(signalId, history, signal, outputDir = "./dump/strateg
13270
13399
 
13271
13400
  const BACKTEST_METHOD_NAME_RUN = "BacktestUtils.run";
13272
13401
  const BACKTEST_METHOD_NAME_BACKGROUND = "BacktestUtils.background";
13402
+ const BACKTEST_METHOD_NAME_STOP = "BacktestUtils.stop";
13273
13403
  const BACKTEST_METHOD_NAME_GET_REPORT = "BacktestUtils.getReport";
13274
13404
  const BACKTEST_METHOD_NAME_DUMP = "BacktestUtils.dump";
13275
13405
  /**
@@ -13364,7 +13494,7 @@ class BacktestUtils {
13364
13494
  };
13365
13495
  task().catch((error) => exitEmitter.next(new Error(functoolsKit.getErrorMessage(error))));
13366
13496
  return () => {
13367
- backtest$1.strategyGlobalService.stop({ symbol, strategyName: context.strategyName });
13497
+ backtest$1.strategyGlobalService.stop({ symbol, strategyName: context.strategyName }, true);
13368
13498
  backtest$1.strategyGlobalService
13369
13499
  .getPendingSignal(symbol, context.strategyName)
13370
13500
  .then(async (pendingSignal) => {
@@ -13384,6 +13514,30 @@ class BacktestUtils {
13384
13514
  isStopped = true;
13385
13515
  };
13386
13516
  };
13517
+ /**
13518
+ * Stops the strategy from generating new signals.
13519
+ *
13520
+ * Sets internal flag to prevent strategy from opening new signals.
13521
+ * Current active signal (if any) will complete normally.
13522
+ * Backtest will stop at the next safe point (idle state or after signal closes).
13523
+ *
13524
+ * @param symbol - Trading pair symbol
13525
+ * @param strategyName - Strategy name to stop
13526
+ * @returns Promise that resolves when stop flag is set
13527
+ *
13528
+ * @example
13529
+ * ```typescript
13530
+ * // Stop strategy after some condition
13531
+ * await Backtest.stop("BTCUSDT", "my-strategy");
13532
+ * ```
13533
+ */
13534
+ this.stop = async (symbol, strategyName) => {
13535
+ backtest$1.loggerService.info(BACKTEST_METHOD_NAME_STOP, {
13536
+ symbol,
13537
+ strategyName,
13538
+ });
13539
+ await backtest$1.strategyGlobalService.stop({ symbol, strategyName }, true);
13540
+ };
13387
13541
  /**
13388
13542
  * Gets statistical data from all closed signals for a symbol-strategy pair.
13389
13543
  *
@@ -13472,6 +13626,7 @@ const Backtest = new BacktestUtils();
13472
13626
 
13473
13627
  const LIVE_METHOD_NAME_RUN = "LiveUtils.run";
13474
13628
  const LIVE_METHOD_NAME_BACKGROUND = "LiveUtils.background";
13629
+ const LIVE_METHOD_NAME_STOP = "LiveUtils.stop";
13475
13630
  const LIVE_METHOD_NAME_GET_REPORT = "LiveUtils.getReport";
13476
13631
  const LIVE_METHOD_NAME_DUMP = "LiveUtils.dump";
13477
13632
  /**
@@ -13579,7 +13734,7 @@ class LiveUtils {
13579
13734
  };
13580
13735
  task().catch((error) => exitEmitter.next(new Error(functoolsKit.getErrorMessage(error))));
13581
13736
  return () => {
13582
- backtest$1.strategyGlobalService.stop({ symbol, strategyName: context.strategyName });
13737
+ backtest$1.strategyGlobalService.stop({ symbol, strategyName: context.strategyName }, false);
13583
13738
  backtest$1.strategyGlobalService
13584
13739
  .getPendingSignal(symbol, context.strategyName)
13585
13740
  .then(async (pendingSignal) => {
@@ -13599,6 +13754,30 @@ class LiveUtils {
13599
13754
  isStopped = true;
13600
13755
  };
13601
13756
  };
13757
+ /**
13758
+ * Stops the strategy from generating new signals.
13759
+ *
13760
+ * Sets internal flag to prevent strategy from opening new signals.
13761
+ * Current active signal (if any) will complete normally.
13762
+ * Live trading will stop at the next safe point (idle/closed state).
13763
+ *
13764
+ * @param symbol - Trading pair symbol
13765
+ * @param strategyName - Strategy name to stop
13766
+ * @returns Promise that resolves when stop flag is set
13767
+ *
13768
+ * @example
13769
+ * ```typescript
13770
+ * // Stop live trading gracefully
13771
+ * await Live.stop("BTCUSDT", "my-strategy");
13772
+ * ```
13773
+ */
13774
+ this.stop = async (symbol, strategyName) => {
13775
+ backtest$1.loggerService.info(LIVE_METHOD_NAME_STOP, {
13776
+ symbol,
13777
+ strategyName,
13778
+ });
13779
+ await backtest$1.strategyGlobalService.stop({ symbol, strategyName }, false);
13780
+ };
13602
13781
  /**
13603
13782
  * Gets statistical data from all live trading events for a symbol-strategy pair.
13604
13783
  *
@@ -13906,6 +14085,7 @@ class Performance {
13906
14085
 
13907
14086
  const WALKER_METHOD_NAME_RUN = "WalkerUtils.run";
13908
14087
  const WALKER_METHOD_NAME_BACKGROUND = "WalkerUtils.background";
14088
+ const WALKER_METHOD_NAME_STOP = "WalkerUtils.stop";
13909
14089
  const WALKER_METHOD_NAME_GET_DATA = "WalkerUtils.getData";
13910
14090
  const WALKER_METHOD_NAME_GET_REPORT = "WalkerUtils.getReport";
13911
14091
  const WALKER_METHOD_NAME_DUMP = "WalkerUtils.dump";
@@ -14016,8 +14196,8 @@ class WalkerUtils {
14016
14196
  task().catch((error) => exitEmitter.next(new Error(functoolsKit.getErrorMessage(error))));
14017
14197
  return () => {
14018
14198
  for (const strategyName of walkerSchema.strategies) {
14019
- backtest$1.strategyGlobalService.stop({ symbol, strategyName });
14020
- walkerStopSubject.next({ symbol, strategyName });
14199
+ backtest$1.strategyGlobalService.stop({ symbol, strategyName }, true);
14200
+ walkerStopSubject.next({ symbol, strategyName, walkerName: context.walkerName });
14021
14201
  }
14022
14202
  if (!isDone) {
14023
14203
  doneWalkerSubject.next({
@@ -14031,6 +14211,40 @@ class WalkerUtils {
14031
14211
  isStopped = true;
14032
14212
  };
14033
14213
  };
14214
+ /**
14215
+ * Stops all strategies in the walker from generating new signals.
14216
+ *
14217
+ * Iterates through all strategies defined in walker schema and:
14218
+ * 1. Sends stop signal via walkerStopSubject (interrupts current running strategy)
14219
+ * 2. Sets internal stop flag for each strategy (prevents new signals)
14220
+ *
14221
+ * Current active signals (if any) will complete normally.
14222
+ * Walker will stop at the next safe point.
14223
+ *
14224
+ * Supports multiple walkers running on the same symbol simultaneously.
14225
+ * Stop signal is filtered by walkerName to prevent interference.
14226
+ *
14227
+ * @param symbol - Trading pair symbol
14228
+ * @param walkerName - Walker name to stop
14229
+ * @returns Promise that resolves when all stop flags are set
14230
+ *
14231
+ * @example
14232
+ * ```typescript
14233
+ * // Stop walker and all its strategies
14234
+ * await Walker.stop("BTCUSDT", "my-walker");
14235
+ * ```
14236
+ */
14237
+ this.stop = async (symbol, walkerName) => {
14238
+ backtest$1.loggerService.info(WALKER_METHOD_NAME_STOP, {
14239
+ symbol,
14240
+ walkerName,
14241
+ });
14242
+ const walkerSchema = backtest$1.walkerSchemaService.get(walkerName);
14243
+ for (const strategyName of walkerSchema.strategies) {
14244
+ await walkerStopSubject.next({ symbol, strategyName, walkerName });
14245
+ await backtest$1.strategyGlobalService.stop({ symbol, strategyName }, true);
14246
+ }
14247
+ };
14034
14248
  /**
14035
14249
  * Gets walker results data from all strategy comparisons.
14036
14250
  *