backtest-kit 1.5.3 → 1.5.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
@@ -13402,17 +13402,50 @@ const BACKTEST_METHOD_NAME_BACKGROUND = "BacktestUtils.background";
13402
13402
  const BACKTEST_METHOD_NAME_STOP = "BacktestUtils.stop";
13403
13403
  const BACKTEST_METHOD_NAME_GET_REPORT = "BacktestUtils.getReport";
13404
13404
  const BACKTEST_METHOD_NAME_DUMP = "BacktestUtils.dump";
13405
+ const BACKTEST_METHOD_NAME_TASK = "BacktestUtils.task";
13406
+ const BACKTEST_METHOD_NAME_GET_STATUS = "BacktestUtils.getStatus";
13405
13407
  /**
13406
- * Utility class for backtest operations.
13408
+ * Internal task function that runs backtest and handles completion.
13409
+ * Consumes backtest results and updates instance state flags.
13407
13410
  *
13408
- * Provides simplified access to backtestCommandService.run() with logging.
13409
- * Exported as singleton instance for convenient usage.
13411
+ * @param symbol - Trading pair symbol
13412
+ * @param context - Execution context with strategy, exchange, and frame names
13413
+ * @param self - BacktestInstance reference for state management
13414
+ * @returns Promise that resolves when backtest completes
13415
+ *
13416
+ * @internal
13417
+ */
13418
+ const INSTANCE_TASK_FN$2 = async (symbol, context, self) => {
13419
+ {
13420
+ self._isStopped = false;
13421
+ self._isDone = false;
13422
+ }
13423
+ for await (const _ of self.run(symbol, context)) {
13424
+ if (self._isStopped) {
13425
+ break;
13426
+ }
13427
+ }
13428
+ if (!self._isDone) {
13429
+ await doneBacktestSubject.next({
13430
+ exchangeName: context.exchangeName,
13431
+ strategyName: context.strategyName,
13432
+ backtest: true,
13433
+ symbol,
13434
+ });
13435
+ }
13436
+ self._isDone = true;
13437
+ };
13438
+ /**
13439
+ * Instance class for backtest operations on a specific symbol-strategy pair.
13440
+ *
13441
+ * Provides isolated backtest execution and reporting for a single symbol-strategy combination.
13442
+ * Each instance maintains its own state and context.
13410
13443
  *
13411
13444
  * @example
13412
13445
  * ```typescript
13413
- * import { Backtest } from "./classes/Backtest";
13446
+ * const instance = new BacktestInstance("BTCUSDT", "my-strategy");
13414
13447
  *
13415
- * for await (const result of Backtest.run("BTCUSDT", {
13448
+ * for await (const result of instance.run("BTCUSDT", {
13416
13449
  * strategyName: "my-strategy",
13417
13450
  * exchangeName: "my-exchange",
13418
13451
  * frameName: "1d-backtest"
@@ -13421,8 +13454,57 @@ const BACKTEST_METHOD_NAME_DUMP = "BacktestUtils.dump";
13421
13454
  * }
13422
13455
  * ```
13423
13456
  */
13424
- class BacktestUtils {
13425
- constructor() {
13457
+ class BacktestInstance {
13458
+ /**
13459
+ * Creates a new BacktestInstance for a specific symbol-strategy pair.
13460
+ *
13461
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
13462
+ * @param strategyName - Strategy name for this backtest instance
13463
+ */
13464
+ constructor(symbol, strategyName) {
13465
+ this.symbol = symbol;
13466
+ this.strategyName = strategyName;
13467
+ /** Internal flag indicating if backtest was stopped manually */
13468
+ this._isStopped = false;
13469
+ /** Internal flag indicating if backtest task completed */
13470
+ this._isDone = false;
13471
+ /**
13472
+ * Internal singlerun task that executes the backtest.
13473
+ * Ensures only one backtest run per instance using singlerun wrapper.
13474
+ *
13475
+ * @param symbol - Trading pair symbol
13476
+ * @param context - Execution context with strategy, exchange, and frame names
13477
+ * @returns Promise that resolves when backtest completes
13478
+ *
13479
+ * @internal
13480
+ */
13481
+ this.task = functoolsKit.singlerun(async (symbol, context) => {
13482
+ backtest$1.loggerService.info(BACKTEST_METHOD_NAME_TASK, {
13483
+ symbol,
13484
+ context,
13485
+ });
13486
+ return await INSTANCE_TASK_FN$2(symbol, context, this);
13487
+ });
13488
+ /**
13489
+ * Gets the current status of this backtest instance.
13490
+ *
13491
+ * @returns Promise resolving to status object with symbol, strategyName, and task status
13492
+ *
13493
+ * @example
13494
+ * ```typescript
13495
+ * const instance = new BacktestInstance("BTCUSDT", "my-strategy");
13496
+ * const status = await instance.getStatus();
13497
+ * console.log(status.status); // "idle", "running", or "done"
13498
+ * ```
13499
+ */
13500
+ this.getStatus = async () => {
13501
+ backtest$1.loggerService.info(BACKTEST_METHOD_NAME_GET_STATUS);
13502
+ return {
13503
+ symbol: this.symbol,
13504
+ strategyName: this.strategyName,
13505
+ status: this.task.getStatus(),
13506
+ };
13507
+ };
13426
13508
  /**
13427
13509
  * Runs backtest for a symbol with context propagation.
13428
13510
  *
@@ -13460,13 +13542,12 @@ class BacktestUtils {
13460
13542
  *
13461
13543
  * @example
13462
13544
  * ```typescript
13463
- * // Run backtest silently, only callbacks will fire
13464
- * await Backtest.background("BTCUSDT", {
13545
+ * const instance = new BacktestInstance();
13546
+ * const cancel = instance.background("BTCUSDT", {
13465
13547
  * strategyName: "my-strategy",
13466
13548
  * exchangeName: "my-exchange",
13467
13549
  * frameName: "1d-backtest"
13468
13550
  * });
13469
- * console.log("Backtest completed");
13470
13551
  * ```
13471
13552
  */
13472
13553
  this.background = (symbol, context) => {
@@ -13474,25 +13555,7 @@ class BacktestUtils {
13474
13555
  symbol,
13475
13556
  context,
13476
13557
  });
13477
- let isStopped = false;
13478
- let isDone = false;
13479
- const task = async () => {
13480
- for await (const _ of this.run(symbol, context)) {
13481
- if (isStopped) {
13482
- break;
13483
- }
13484
- }
13485
- if (!isDone) {
13486
- await doneBacktestSubject.next({
13487
- exchangeName: context.exchangeName,
13488
- strategyName: context.strategyName,
13489
- backtest: true,
13490
- symbol,
13491
- });
13492
- }
13493
- isDone = true;
13494
- };
13495
- task().catch((error) => exitEmitter.next(new Error(functoolsKit.getErrorMessage(error))));
13558
+ this.task(symbol, context).catch((error) => exitEmitter.next(new Error(functoolsKit.getErrorMessage(error))));
13496
13559
  return () => {
13497
13560
  backtest$1.strategyGlobalService.stop({ symbol, strategyName: context.strategyName }, true);
13498
13561
  backtest$1.strategyGlobalService
@@ -13501,7 +13564,7 @@ class BacktestUtils {
13501
13564
  if (pendingSignal) {
13502
13565
  return;
13503
13566
  }
13504
- if (!isDone) {
13567
+ if (!this._isDone) {
13505
13568
  await doneBacktestSubject.next({
13506
13569
  exchangeName: context.exchangeName,
13507
13570
  strategyName: context.strategyName,
@@ -13509,9 +13572,9 @@ class BacktestUtils {
13509
13572
  symbol,
13510
13573
  });
13511
13574
  }
13512
- isDone = true;
13575
+ this._isDone = true;
13513
13576
  });
13514
- isStopped = true;
13577
+ this._isStopped = true;
13515
13578
  };
13516
13579
  };
13517
13580
  /**
@@ -13527,8 +13590,8 @@ class BacktestUtils {
13527
13590
  *
13528
13591
  * @example
13529
13592
  * ```typescript
13530
- * // Stop strategy after some condition
13531
- * await Backtest.stop("BTCUSDT", "my-strategy");
13593
+ * const instance = new BacktestInstance();
13594
+ * await instance.stop("BTCUSDT", "my-strategy");
13532
13595
  * ```
13533
13596
  */
13534
13597
  this.stop = async (symbol, strategyName) => {
@@ -13547,7 +13610,8 @@ class BacktestUtils {
13547
13610
  *
13548
13611
  * @example
13549
13612
  * ```typescript
13550
- * const stats = await Backtest.getData("BTCUSDT", "my-strategy");
13613
+ * const instance = new BacktestInstance();
13614
+ * const stats = await instance.getData("BTCUSDT", "my-strategy");
13551
13615
  * console.log(stats.sharpeRatio, stats.winRate);
13552
13616
  * ```
13553
13617
  */
@@ -13567,7 +13631,8 @@ class BacktestUtils {
13567
13631
  *
13568
13632
  * @example
13569
13633
  * ```typescript
13570
- * const markdown = await Backtest.getReport("BTCUSDT", "my-strategy");
13634
+ * const instance = new BacktestInstance();
13635
+ * const markdown = await instance.getReport("BTCUSDT", "my-strategy");
13571
13636
  * console.log(markdown);
13572
13637
  * ```
13573
13638
  */
@@ -13587,11 +13652,12 @@ class BacktestUtils {
13587
13652
  *
13588
13653
  * @example
13589
13654
  * ```typescript
13655
+ * const instance = new BacktestInstance();
13590
13656
  * // Save to default path: ./dump/backtest/my-strategy.md
13591
- * await Backtest.dump("BTCUSDT", "my-strategy");
13657
+ * await instance.dump("BTCUSDT", "my-strategy");
13592
13658
  *
13593
13659
  * // Save to custom path: ./custom/path/my-strategy.md
13594
- * await Backtest.dump("BTCUSDT", "my-strategy", "./custom/path");
13660
+ * await instance.dump("BTCUSDT", "my-strategy", "./custom/path");
13595
13661
  * ```
13596
13662
  */
13597
13663
  this.dump = async (symbol, strategyName, path) => {
@@ -13604,6 +13670,162 @@ class BacktestUtils {
13604
13670
  };
13605
13671
  }
13606
13672
  }
13673
+ /**
13674
+ * Utility class for backtest operations.
13675
+ *
13676
+ * Provides simplified access to backtestCommandService.run() with logging.
13677
+ * Exported as singleton instance for convenient usage.
13678
+ *
13679
+ * @example
13680
+ * ```typescript
13681
+ * import { Backtest } from "./classes/Backtest";
13682
+ *
13683
+ * for await (const result of Backtest.run("BTCUSDT", {
13684
+ * strategyName: "my-strategy",
13685
+ * exchangeName: "my-exchange",
13686
+ * frameName: "1d-backtest"
13687
+ * })) {
13688
+ * console.log("Closed signal PNL:", result.pnl.pnlPercentage);
13689
+ * }
13690
+ * ```
13691
+ */
13692
+ class BacktestUtils {
13693
+ constructor() {
13694
+ /**
13695
+ * Memoized function to get or create BacktestInstance for a symbol-strategy pair.
13696
+ * Each symbol-strategy combination gets its own isolated instance.
13697
+ */
13698
+ this._getInstance = functoolsKit.memoize(([symbol, strategyName]) => `${symbol}:${strategyName}`, (symbol, strategyName) => new BacktestInstance(symbol, strategyName));
13699
+ /**
13700
+ * Runs backtest for a symbol with context propagation.
13701
+ *
13702
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
13703
+ * @param context - Execution context with strategy, exchange, and frame names
13704
+ * @returns Async generator yielding closed signals with PNL
13705
+ */
13706
+ this.run = (symbol, context) => {
13707
+ const instance = this._getInstance(symbol, context.strategyName);
13708
+ return instance.run(symbol, context);
13709
+ };
13710
+ /**
13711
+ * Runs backtest in background without yielding results.
13712
+ *
13713
+ * Consumes all backtest results internally without exposing them.
13714
+ * Useful for running backtests for side effects only (callbacks, logging).
13715
+ *
13716
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
13717
+ * @param context - Execution context with strategy, exchange, and frame names
13718
+ * @returns Cancellation closure
13719
+ *
13720
+ * @example
13721
+ * ```typescript
13722
+ * // Run backtest silently, only callbacks will fire
13723
+ * await Backtest.background("BTCUSDT", {
13724
+ * strategyName: "my-strategy",
13725
+ * exchangeName: "my-exchange",
13726
+ * frameName: "1d-backtest"
13727
+ * });
13728
+ * console.log("Backtest completed");
13729
+ * ```
13730
+ */
13731
+ this.background = (symbol, context) => {
13732
+ const instance = this._getInstance(symbol, context.strategyName);
13733
+ return instance.background(symbol, context);
13734
+ };
13735
+ /**
13736
+ * Stops the strategy from generating new signals.
13737
+ *
13738
+ * Sets internal flag to prevent strategy from opening new signals.
13739
+ * Current active signal (if any) will complete normally.
13740
+ * Backtest will stop at the next safe point (idle state or after signal closes).
13741
+ *
13742
+ * @param symbol - Trading pair symbol
13743
+ * @param strategyName - Strategy name to stop
13744
+ * @returns Promise that resolves when stop flag is set
13745
+ *
13746
+ * @example
13747
+ * ```typescript
13748
+ * // Stop strategy after some condition
13749
+ * await Backtest.stop("BTCUSDT", "my-strategy");
13750
+ * ```
13751
+ */
13752
+ this.stop = async (symbol, strategyName) => {
13753
+ const instance = this._getInstance(symbol, strategyName);
13754
+ return await instance.stop(symbol, strategyName);
13755
+ };
13756
+ /**
13757
+ * Gets statistical data from all closed signals for a symbol-strategy pair.
13758
+ *
13759
+ * @param symbol - Trading pair symbol
13760
+ * @param strategyName - Strategy name to get data for
13761
+ * @returns Promise resolving to statistical data object
13762
+ *
13763
+ * @example
13764
+ * ```typescript
13765
+ * const stats = await Backtest.getData("BTCUSDT", "my-strategy");
13766
+ * console.log(stats.sharpeRatio, stats.winRate);
13767
+ * ```
13768
+ */
13769
+ this.getData = async (symbol, strategyName) => {
13770
+ const instance = this._getInstance(symbol, strategyName);
13771
+ return await instance.getData(symbol, strategyName);
13772
+ };
13773
+ /**
13774
+ * Generates markdown report with all closed signals for a symbol-strategy pair.
13775
+ *
13776
+ * @param symbol - Trading pair symbol
13777
+ * @param strategyName - Strategy name to generate report for
13778
+ * @returns Promise resolving to markdown formatted report string
13779
+ *
13780
+ * @example
13781
+ * ```typescript
13782
+ * const markdown = await Backtest.getReport("BTCUSDT", "my-strategy");
13783
+ * console.log(markdown);
13784
+ * ```
13785
+ */
13786
+ this.getReport = async (symbol, strategyName) => {
13787
+ const instance = this._getInstance(symbol, strategyName);
13788
+ return await instance.getReport(symbol, strategyName);
13789
+ };
13790
+ /**
13791
+ * Saves strategy report to disk.
13792
+ *
13793
+ * @param symbol - Trading pair symbol
13794
+ * @param strategyName - Strategy name to save report for
13795
+ * @param path - Optional directory path to save report (default: "./dump/backtest")
13796
+ *
13797
+ * @example
13798
+ * ```typescript
13799
+ * // Save to default path: ./dump/backtest/my-strategy.md
13800
+ * await Backtest.dump("BTCUSDT", "my-strategy");
13801
+ *
13802
+ * // Save to custom path: ./custom/path/my-strategy.md
13803
+ * await Backtest.dump("BTCUSDT", "my-strategy", "./custom/path");
13804
+ * ```
13805
+ */
13806
+ this.dump = async (symbol, strategyName, path) => {
13807
+ const instance = this._getInstance(symbol, strategyName);
13808
+ return await instance.dump(symbol, strategyName, path);
13809
+ };
13810
+ /**
13811
+ * Lists all active backtest instances with their current status.
13812
+ *
13813
+ * @returns Promise resolving to array of status objects for all instances
13814
+ *
13815
+ * @example
13816
+ * ```typescript
13817
+ * const statusList = await Backtest.list();
13818
+ * statusList.forEach(status => {
13819
+ * console.log(`${status.symbol} - ${status.strategyName}: ${status.status}`);
13820
+ * });
13821
+ * ```
13822
+ */
13823
+ this.list = async () => {
13824
+ const instanceList = this._getInstance.values();
13825
+ return await Promise.all(instanceList.map((instance) => instance.getStatus()));
13826
+ };
13827
+ }
13828
+ }
13607
13829
  /**
13608
13830
  * Singleton instance of BacktestUtils for convenient backtest operations.
13609
13831
  *
@@ -13629,37 +13851,110 @@ const LIVE_METHOD_NAME_BACKGROUND = "LiveUtils.background";
13629
13851
  const LIVE_METHOD_NAME_STOP = "LiveUtils.stop";
13630
13852
  const LIVE_METHOD_NAME_GET_REPORT = "LiveUtils.getReport";
13631
13853
  const LIVE_METHOD_NAME_DUMP = "LiveUtils.dump";
13854
+ const LIVE_METHOD_NAME_TASK = "LiveUtils.task";
13855
+ const LIVE_METHOD_NAME_GET_STATUS = "LiveUtils.getStatus";
13632
13856
  /**
13633
- * Utility class for live trading operations.
13857
+ * Internal task function that runs live trading and handles completion.
13858
+ * Consumes live trading results and updates instance state flags.
13634
13859
  *
13635
- * Provides simplified access to liveCommandService.run() with logging.
13636
- * Exported as singleton instance for convenient usage.
13860
+ * @param symbol - Trading pair symbol
13861
+ * @param context - Execution context with strategy and exchange names
13862
+ * @param self - LiveInstance reference for state management
13863
+ * @returns Promise that resolves when live trading completes
13637
13864
  *
13638
- * Features:
13639
- * - Infinite async generator (never completes)
13640
- * - Crash recovery via persisted state
13641
- * - Real-time progression with Date.now()
13865
+ * @internal
13866
+ */
13867
+ const INSTANCE_TASK_FN$1 = async (symbol, context, self) => {
13868
+ {
13869
+ self._isStopped = false;
13870
+ self._isDone = false;
13871
+ }
13872
+ for await (const signal of self.run(symbol, context)) {
13873
+ if (signal?.action === "closed" && self._isStopped) {
13874
+ break;
13875
+ }
13876
+ }
13877
+ if (!self._isDone) {
13878
+ await doneLiveSubject.next({
13879
+ exchangeName: context.exchangeName,
13880
+ strategyName: context.strategyName,
13881
+ backtest: false,
13882
+ symbol,
13883
+ });
13884
+ }
13885
+ self._isDone = true;
13886
+ };
13887
+ /**
13888
+ * Instance class for live trading operations on a specific symbol-strategy pair.
13889
+ *
13890
+ * Provides isolated live trading execution and reporting for a single symbol-strategy combination.
13891
+ * Each instance maintains its own state and context.
13642
13892
  *
13643
13893
  * @example
13644
13894
  * ```typescript
13645
- * import { Live } from "./classes/Live";
13895
+ * const instance = new LiveInstance("BTCUSDT", "my-strategy");
13646
13896
  *
13647
- * // Infinite loop - use Ctrl+C to stop
13648
- * for await (const result of Live.run("BTCUSDT", {
13897
+ * for await (const result of instance.run("BTCUSDT", {
13649
13898
  * strategyName: "my-strategy",
13650
- * exchangeName: "my-exchange",
13651
- * frameName: ""
13899
+ * exchangeName: "my-exchange"
13652
13900
  * })) {
13653
- * if (result.action === "opened") {
13654
- * console.log("Signal opened:", result.signal);
13655
- * } else if (result.action === "closed") {
13656
- * console.log("PNL:", result.pnl.pnlPercentage);
13901
+ * if (result.action === "closed") {
13902
+ * console.log("Signal closed, PNL:", result.pnl.pnlPercentage);
13657
13903
  * }
13658
13904
  * }
13659
13905
  * ```
13660
13906
  */
13661
- class LiveUtils {
13662
- constructor() {
13907
+ class LiveInstance {
13908
+ /**
13909
+ * Creates a new LiveInstance for a specific symbol-strategy pair.
13910
+ *
13911
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
13912
+ * @param strategyName - Strategy name for this live trading instance
13913
+ */
13914
+ constructor(symbol, strategyName) {
13915
+ this.symbol = symbol;
13916
+ this.strategyName = strategyName;
13917
+ /** Internal flag indicating if live trading was stopped manually */
13918
+ this._isStopped = false;
13919
+ /** Internal flag indicating if live trading task completed */
13920
+ this._isDone = false;
13921
+ /**
13922
+ * Internal singlerun task that executes the live trading.
13923
+ * Ensures only one live trading run per instance using singlerun wrapper.
13924
+ *
13925
+ * @param symbol - Trading pair symbol
13926
+ * @param context - Execution context with strategy and exchange names
13927
+ * @returns Promise that resolves when live trading completes
13928
+ *
13929
+ * @internal
13930
+ */
13931
+ this.task = functoolsKit.singlerun(async (symbol, context) => {
13932
+ backtest$1.loggerService.info(LIVE_METHOD_NAME_TASK, {
13933
+ symbol,
13934
+ context,
13935
+ });
13936
+ return await INSTANCE_TASK_FN$1(symbol, context, this);
13937
+ });
13938
+ /**
13939
+ * Gets the current status of this live trading instance.
13940
+ *
13941
+ * @returns Promise resolving to status object with symbol, strategyName, and task status
13942
+ *
13943
+ * @example
13944
+ * ```typescript
13945
+ * const instance = new LiveInstance("BTCUSDT", "my-strategy");
13946
+ * const status = await instance.getStatus();
13947
+ * console.log(status.status); // "idle", "running", or "done"
13948
+ * ```
13949
+ */
13950
+ this.getStatus = async () => {
13951
+ backtest$1.loggerService.info(LIVE_METHOD_NAME_GET_STATUS);
13952
+ return {
13953
+ symbol: this.symbol,
13954
+ strategyName: this.strategyName,
13955
+ status: this.task.getStatus(),
13956
+ };
13957
+ };
13663
13958
  /**
13664
13959
  * Runs live trading for a symbol with context propagation.
13665
13960
  *
@@ -13701,9 +13996,8 @@ class LiveUtils {
13701
13996
  *
13702
13997
  * @example
13703
13998
  * ```typescript
13704
- * // Run live trading silently in background, only callbacks will fire
13705
- * // This will run forever until Ctrl+C
13706
- * await Live.background("BTCUSDT", {
13999
+ * const instance = new LiveInstance();
14000
+ * const cancel = instance.background("BTCUSDT", {
13707
14001
  * strategyName: "my-strategy",
13708
14002
  * exchangeName: "my-exchange"
13709
14003
  * });
@@ -13714,25 +14008,7 @@ class LiveUtils {
13714
14008
  symbol,
13715
14009
  context,
13716
14010
  });
13717
- let isStopped = false;
13718
- let isDone = false;
13719
- const task = async () => {
13720
- for await (const signal of this.run(symbol, context)) {
13721
- if (signal?.action === "closed" && isStopped) {
13722
- break;
13723
- }
13724
- }
13725
- if (!isDone) {
13726
- await doneLiveSubject.next({
13727
- exchangeName: context.exchangeName,
13728
- strategyName: context.strategyName,
13729
- backtest: false,
13730
- symbol,
13731
- });
13732
- }
13733
- isDone = true;
13734
- };
13735
- task().catch((error) => exitEmitter.next(new Error(functoolsKit.getErrorMessage(error))));
14011
+ this.task(symbol, context).catch((error) => exitEmitter.next(new Error(functoolsKit.getErrorMessage(error))));
13736
14012
  return () => {
13737
14013
  backtest$1.strategyGlobalService.stop({ symbol, strategyName: context.strategyName }, false);
13738
14014
  backtest$1.strategyGlobalService
@@ -13741,7 +14017,7 @@ class LiveUtils {
13741
14017
  if (pendingSignal) {
13742
14018
  return;
13743
14019
  }
13744
- if (!isDone) {
14020
+ if (!this._isDone) {
13745
14021
  await doneLiveSubject.next({
13746
14022
  exchangeName: context.exchangeName,
13747
14023
  strategyName: context.strategyName,
@@ -13749,11 +14025,179 @@ class LiveUtils {
13749
14025
  symbol,
13750
14026
  });
13751
14027
  }
13752
- isDone = true;
14028
+ this._isDone = true;
13753
14029
  });
13754
- isStopped = true;
14030
+ this._isStopped = true;
13755
14031
  };
13756
14032
  };
14033
+ /**
14034
+ * Stops the strategy from generating new signals.
14035
+ *
14036
+ * Sets internal flag to prevent strategy from opening new signals.
14037
+ * Current active signal (if any) will complete normally.
14038
+ * Live trading will stop at the next safe point (idle/closed state).
14039
+ *
14040
+ * @param symbol - Trading pair symbol
14041
+ * @param strategyName - Strategy name to stop
14042
+ * @returns Promise that resolves when stop flag is set
14043
+ *
14044
+ * @example
14045
+ * ```typescript
14046
+ * const instance = new LiveInstance();
14047
+ * await instance.stop("BTCUSDT", "my-strategy");
14048
+ * ```
14049
+ */
14050
+ this.stop = async (symbol, strategyName) => {
14051
+ backtest$1.loggerService.info(LIVE_METHOD_NAME_STOP, {
14052
+ symbol,
14053
+ strategyName,
14054
+ });
14055
+ await backtest$1.strategyGlobalService.stop({ symbol, strategyName }, false);
14056
+ };
14057
+ /**
14058
+ * Gets statistical data from all live trading events for a symbol-strategy pair.
14059
+ *
14060
+ * @param symbol - Trading pair symbol
14061
+ * @param strategyName - Strategy name to get data for
14062
+ * @returns Promise resolving to statistical data object
14063
+ *
14064
+ * @example
14065
+ * ```typescript
14066
+ * const instance = new LiveInstance();
14067
+ * const stats = await instance.getData("BTCUSDT", "my-strategy");
14068
+ * console.log(stats.sharpeRatio, stats.winRate);
14069
+ * ```
14070
+ */
14071
+ this.getData = async (symbol, strategyName) => {
14072
+ backtest$1.loggerService.info("LiveUtils.getData", {
14073
+ symbol,
14074
+ strategyName,
14075
+ });
14076
+ return await backtest$1.liveMarkdownService.getData(symbol, strategyName);
14077
+ };
14078
+ /**
14079
+ * Generates markdown report with all events for a symbol-strategy pair.
14080
+ *
14081
+ * @param symbol - Trading pair symbol
14082
+ * @param strategyName - Strategy name to generate report for
14083
+ * @returns Promise resolving to markdown formatted report string
14084
+ *
14085
+ * @example
14086
+ * ```typescript
14087
+ * const instance = new LiveInstance();
14088
+ * const markdown = await instance.getReport("BTCUSDT", "my-strategy");
14089
+ * console.log(markdown);
14090
+ * ```
14091
+ */
14092
+ this.getReport = async (symbol, strategyName) => {
14093
+ backtest$1.loggerService.info(LIVE_METHOD_NAME_GET_REPORT, {
14094
+ symbol,
14095
+ strategyName,
14096
+ });
14097
+ return await backtest$1.liveMarkdownService.getReport(symbol, strategyName);
14098
+ };
14099
+ /**
14100
+ * Saves strategy report to disk.
14101
+ *
14102
+ * @param symbol - Trading pair symbol
14103
+ * @param strategyName - Strategy name to save report for
14104
+ * @param path - Optional directory path to save report (default: "./dump/live")
14105
+ *
14106
+ * @example
14107
+ * ```typescript
14108
+ * const instance = new LiveInstance();
14109
+ * // Save to default path: ./dump/live/my-strategy.md
14110
+ * await instance.dump("BTCUSDT", "my-strategy");
14111
+ *
14112
+ * // Save to custom path: ./custom/path/my-strategy.md
14113
+ * await instance.dump("BTCUSDT", "my-strategy", "./custom/path");
14114
+ * ```
14115
+ */
14116
+ this.dump = async (symbol, strategyName, path) => {
14117
+ backtest$1.loggerService.info(LIVE_METHOD_NAME_DUMP, {
14118
+ symbol,
14119
+ strategyName,
14120
+ path,
14121
+ });
14122
+ await backtest$1.liveMarkdownService.dump(symbol, strategyName, path);
14123
+ };
14124
+ }
14125
+ }
14126
+ /**
14127
+ * Utility class for live trading operations.
14128
+ *
14129
+ * Provides simplified access to liveCommandService.run() with logging.
14130
+ * Exported as singleton instance for convenient usage.
14131
+ *
14132
+ * Features:
14133
+ * - Infinite async generator (never completes)
14134
+ * - Crash recovery via persisted state
14135
+ * - Real-time progression with Date.now()
14136
+ *
14137
+ * @example
14138
+ * ```typescript
14139
+ * import { Live } from "./classes/Live";
14140
+ *
14141
+ * // Infinite loop - use Ctrl+C to stop
14142
+ * for await (const result of Live.run("BTCUSDT", {
14143
+ * strategyName: "my-strategy",
14144
+ * exchangeName: "my-exchange",
14145
+ * frameName: ""
14146
+ * })) {
14147
+ * if (result.action === "opened") {
14148
+ * console.log("Signal opened:", result.signal);
14149
+ * } else if (result.action === "closed") {
14150
+ * console.log("PNL:", result.pnl.pnlPercentage);
14151
+ * }
14152
+ * }
14153
+ * ```
14154
+ */
14155
+ class LiveUtils {
14156
+ constructor() {
14157
+ /**
14158
+ * Memoized function to get or create LiveInstance for a symbol-strategy pair.
14159
+ * Each symbol-strategy combination gets its own isolated instance.
14160
+ */
14161
+ this._getInstance = functoolsKit.memoize(([symbol, strategyName]) => `${symbol}:${strategyName}`, (symbol, strategyName) => new LiveInstance(symbol, strategyName));
14162
+ /**
14163
+ * Runs live trading for a symbol with context propagation.
14164
+ *
14165
+ * Infinite async generator with crash recovery support.
14166
+ * Process can crash and restart - state will be recovered from disk.
14167
+ *
14168
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
14169
+ * @param context - Execution context with strategy and exchange names
14170
+ * @returns Infinite async generator yielding opened and closed signals
14171
+ */
14172
+ this.run = (symbol, context) => {
14173
+ const instance = this._getInstance(symbol, context.strategyName);
14174
+ return instance.run(symbol, context);
14175
+ };
14176
+ /**
14177
+ * Runs live trading in background without yielding results.
14178
+ *
14179
+ * Consumes all live trading results internally without exposing them.
14180
+ * Infinite loop - will run until process is stopped or crashes.
14181
+ * Useful for running live trading for side effects only (callbacks, persistence).
14182
+ *
14183
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
14184
+ * @param context - Execution context with strategy and exchange names
14185
+ * @returns Cancellation closure
14186
+ *
14187
+ * @example
14188
+ * ```typescript
14189
+ * // Run live trading silently in background, only callbacks will fire
14190
+ * // This will run forever until Ctrl+C
14191
+ * await Live.background("BTCUSDT", {
14192
+ * strategyName: "my-strategy",
14193
+ * exchangeName: "my-exchange"
14194
+ * });
14195
+ * ```
14196
+ */
14197
+ this.background = (symbol, context) => {
14198
+ const instance = this._getInstance(symbol, context.strategyName);
14199
+ return instance.background(symbol, context);
14200
+ };
13757
14201
  /**
13758
14202
  * Stops the strategy from generating new signals.
13759
14203
  *
@@ -13772,11 +14216,8 @@ class LiveUtils {
13772
14216
  * ```
13773
14217
  */
13774
14218
  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);
14219
+ const instance = this._getInstance(symbol, strategyName);
14220
+ return await instance.stop(symbol, strategyName);
13780
14221
  };
13781
14222
  /**
13782
14223
  * Gets statistical data from all live trading events for a symbol-strategy pair.
@@ -13792,11 +14233,8 @@ class LiveUtils {
13792
14233
  * ```
13793
14234
  */
13794
14235
  this.getData = async (symbol, strategyName) => {
13795
- backtest$1.loggerService.info("LiveUtils.getData", {
13796
- symbol,
13797
- strategyName,
13798
- });
13799
- return await backtest$1.liveMarkdownService.getData(symbol, strategyName);
14236
+ const instance = this._getInstance(symbol, strategyName);
14237
+ return await instance.getData(symbol, strategyName);
13800
14238
  };
13801
14239
  /**
13802
14240
  * Generates markdown report with all events for a symbol-strategy pair.
@@ -13812,11 +14250,8 @@ class LiveUtils {
13812
14250
  * ```
13813
14251
  */
13814
14252
  this.getReport = async (symbol, strategyName) => {
13815
- backtest$1.loggerService.info(LIVE_METHOD_NAME_GET_REPORT, {
13816
- symbol,
13817
- strategyName,
13818
- });
13819
- return await backtest$1.liveMarkdownService.getReport(symbol, strategyName);
14253
+ const instance = this._getInstance(symbol, strategyName);
14254
+ return await instance.getReport(symbol, strategyName);
13820
14255
  };
13821
14256
  /**
13822
14257
  * Saves strategy report to disk.
@@ -13835,12 +14270,25 @@ class LiveUtils {
13835
14270
  * ```
13836
14271
  */
13837
14272
  this.dump = async (symbol, strategyName, path) => {
13838
- backtest$1.loggerService.info(LIVE_METHOD_NAME_DUMP, {
13839
- symbol,
13840
- strategyName,
13841
- path,
13842
- });
13843
- await backtest$1.liveMarkdownService.dump(symbol, strategyName, path);
14273
+ const instance = this._getInstance(symbol, strategyName);
14274
+ return await instance.dump(symbol, strategyName, path);
14275
+ };
14276
+ /**
14277
+ * Lists all active live trading instances with their current status.
14278
+ *
14279
+ * @returns Promise resolving to array of status objects for all instances
14280
+ *
14281
+ * @example
14282
+ * ```typescript
14283
+ * const statusList = await Live.list();
14284
+ * statusList.forEach(status => {
14285
+ * console.log(`${status.symbol} - ${status.strategyName}: ${status.status}`);
14286
+ * });
14287
+ * ```
14288
+ */
14289
+ this.list = async () => {
14290
+ const instanceList = this._getInstance.values();
14291
+ return await Promise.all(instanceList.map((instance) => instance.getStatus()));
13844
14292
  };
13845
14293
  }
13846
14294
  }
@@ -14089,27 +14537,108 @@ const WALKER_METHOD_NAME_STOP = "WalkerUtils.stop";
14089
14537
  const WALKER_METHOD_NAME_GET_DATA = "WalkerUtils.getData";
14090
14538
  const WALKER_METHOD_NAME_GET_REPORT = "WalkerUtils.getReport";
14091
14539
  const WALKER_METHOD_NAME_DUMP = "WalkerUtils.dump";
14540
+ const WALKER_METHOD_NAME_TASK = "WalkerUtils.task";
14541
+ const WALKER_METHOD_NAME_GET_STATUS = "WalkerUtils.getStatus";
14092
14542
  /**
14093
- * Utility class for walker operations.
14543
+ * Internal task function that runs walker and handles completion.
14544
+ * Consumes walker results and updates instance state flags.
14094
14545
  *
14095
- * Provides simplified access to walkerCommandService.run() with logging.
14096
- * Automatically pulls exchangeName and frameName from walker schema.
14097
- * Exported as singleton instance for convenient usage.
14546
+ * @param symbol - Trading pair symbol
14547
+ * @param context - Execution context with walker name
14548
+ * @param self - WalkerInstance reference for state management
14549
+ * @returns Promise that resolves when walker completes
14550
+ *
14551
+ * @internal
14552
+ */
14553
+ const INSTANCE_TASK_FN = async (symbol, context, self) => {
14554
+ {
14555
+ self._isStopped = false;
14556
+ self._isDone = false;
14557
+ }
14558
+ for await (const _ of self.run(symbol, context)) {
14559
+ if (self._isStopped) {
14560
+ break;
14561
+ }
14562
+ }
14563
+ if (!self._isDone) {
14564
+ const walkerSchema = backtest$1.walkerSchemaService.get(context.walkerName);
14565
+ await doneWalkerSubject.next({
14566
+ exchangeName: walkerSchema.exchangeName,
14567
+ strategyName: context.walkerName,
14568
+ backtest: true,
14569
+ symbol,
14570
+ });
14571
+ }
14572
+ self._isDone = true;
14573
+ };
14574
+ /**
14575
+ * Instance class for walker operations on a specific symbol-walker pair.
14576
+ *
14577
+ * Provides isolated walker execution and reporting for a single symbol-walker combination.
14578
+ * Each instance maintains its own state and context.
14098
14579
  *
14099
14580
  * @example
14100
14581
  * ```typescript
14101
- * import { Walker } from "./classes/Walker";
14582
+ * const instance = new WalkerInstance("BTCUSDT", "my-walker");
14102
14583
  *
14103
- * for await (const result of Walker.run("BTCUSDT", {
14584
+ * for await (const result of instance.run("BTCUSDT", {
14104
14585
  * walkerName: "my-walker"
14105
14586
  * })) {
14106
14587
  * console.log("Progress:", result.strategiesTested, "/", result.totalStrategies);
14107
- * console.log("Best strategy:", result.bestStrategy, result.bestMetric);
14108
14588
  * }
14109
14589
  * ```
14110
14590
  */
14111
- class WalkerUtils {
14112
- constructor() {
14591
+ class WalkerInstance {
14592
+ /**
14593
+ * Creates a new WalkerInstance for a specific symbol-walker pair.
14594
+ *
14595
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
14596
+ * @param walkerName - Walker name for this walker instance
14597
+ */
14598
+ constructor(symbol, walkerName) {
14599
+ this.symbol = symbol;
14600
+ this.walkerName = walkerName;
14601
+ /** Internal flag indicating if walker was stopped manually */
14602
+ this._isStopped = false;
14603
+ /** Internal flag indicating if walker task completed */
14604
+ this._isDone = false;
14605
+ /**
14606
+ * Internal singlerun task that executes the walker.
14607
+ * Ensures only one walker run per instance using singlerun wrapper.
14608
+ *
14609
+ * @param symbol - Trading pair symbol
14610
+ * @param context - Execution context with walker name
14611
+ * @returns Promise that resolves when walker completes
14612
+ *
14613
+ * @internal
14614
+ */
14615
+ this.task = functoolsKit.singlerun(async (symbol, context) => {
14616
+ backtest$1.loggerService.info(WALKER_METHOD_NAME_TASK, {
14617
+ symbol,
14618
+ context,
14619
+ });
14620
+ return await INSTANCE_TASK_FN(symbol, context, this);
14621
+ });
14622
+ /**
14623
+ * Gets the current status of this walker instance.
14624
+ *
14625
+ * @returns Promise resolving to status object with symbol, walkerName, and task status
14626
+ *
14627
+ * @example
14628
+ * ```typescript
14629
+ * const instance = new WalkerInstance("BTCUSDT", "my-walker");
14630
+ * const status = await instance.getStatus();
14631
+ * console.log(status.status); // "idle", "running", or "done"
14632
+ * ```
14633
+ */
14634
+ this.getStatus = async () => {
14635
+ backtest$1.loggerService.info(WALKER_METHOD_NAME_GET_STATUS);
14636
+ return {
14637
+ symbol: this.symbol,
14638
+ walkerName: this.walkerName,
14639
+ status: this.task.getStatus(),
14640
+ };
14641
+ };
14113
14642
  /**
14114
14643
  * Runs walker comparison for a symbol with context propagation.
14115
14644
  *
@@ -14162,11 +14691,10 @@ class WalkerUtils {
14162
14691
  *
14163
14692
  * @example
14164
14693
  * ```typescript
14165
- * // Run walker silently, only callbacks will fire
14166
- * await Walker.background("BTCUSDT", {
14694
+ * const instance = new WalkerInstance();
14695
+ * const cancel = instance.background("BTCUSDT", {
14167
14696
  * walkerName: "my-walker"
14168
14697
  * });
14169
- * console.log("Walker comparison completed");
14170
14698
  * ```
14171
14699
  */
14172
14700
  this.background = (symbol, context) => {
@@ -14175,31 +14703,13 @@ class WalkerUtils {
14175
14703
  context,
14176
14704
  });
14177
14705
  const walkerSchema = backtest$1.walkerSchemaService.get(context.walkerName);
14178
- let isStopped = false;
14179
- let isDone = false;
14180
- const task = async () => {
14181
- for await (const _ of this.run(symbol, context)) {
14182
- if (isStopped) {
14183
- break;
14184
- }
14185
- }
14186
- if (!isDone) {
14187
- await doneWalkerSubject.next({
14188
- exchangeName: walkerSchema.exchangeName,
14189
- strategyName: context.walkerName,
14190
- backtest: true,
14191
- symbol,
14192
- });
14193
- }
14194
- isDone = true;
14195
- };
14196
- task().catch((error) => exitEmitter.next(new Error(functoolsKit.getErrorMessage(error))));
14706
+ this.task(symbol, context).catch((error) => exitEmitter.next(new Error(functoolsKit.getErrorMessage(error))));
14197
14707
  return () => {
14198
14708
  for (const strategyName of walkerSchema.strategies) {
14199
14709
  backtest$1.strategyGlobalService.stop({ symbol, strategyName }, true);
14200
14710
  walkerStopSubject.next({ symbol, strategyName, walkerName: context.walkerName });
14201
14711
  }
14202
- if (!isDone) {
14712
+ if (!this._isDone) {
14203
14713
  doneWalkerSubject.next({
14204
14714
  exchangeName: walkerSchema.exchangeName,
14205
14715
  strategyName: context.walkerName,
@@ -14207,8 +14717,8 @@ class WalkerUtils {
14207
14717
  symbol,
14208
14718
  });
14209
14719
  }
14210
- isDone = true;
14211
- isStopped = true;
14720
+ this._isDone = true;
14721
+ this._isStopped = true;
14212
14722
  };
14213
14723
  };
14214
14724
  /**
@@ -14230,8 +14740,8 @@ class WalkerUtils {
14230
14740
  *
14231
14741
  * @example
14232
14742
  * ```typescript
14233
- * // Stop walker and all its strategies
14234
- * await Walker.stop("BTCUSDT", "my-walker");
14743
+ * const instance = new WalkerInstance();
14744
+ * await instance.stop("BTCUSDT", "my-walker");
14235
14745
  * ```
14236
14746
  */
14237
14747
  this.stop = async (symbol, walkerName) => {
@@ -14254,7 +14764,8 @@ class WalkerUtils {
14254
14764
  *
14255
14765
  * @example
14256
14766
  * ```typescript
14257
- * const results = await Walker.getData("BTCUSDT", "my-walker");
14767
+ * const instance = new WalkerInstance();
14768
+ * const results = await instance.getData("BTCUSDT", "my-walker");
14258
14769
  * console.log(results.bestStrategy, results.bestMetric);
14259
14770
  * ```
14260
14771
  */
@@ -14278,7 +14789,8 @@ class WalkerUtils {
14278
14789
  *
14279
14790
  * @example
14280
14791
  * ```typescript
14281
- * const markdown = await Walker.getReport("BTCUSDT", "my-walker");
14792
+ * const instance = new WalkerInstance();
14793
+ * const markdown = await instance.getReport("BTCUSDT", "my-walker");
14282
14794
  * console.log(markdown);
14283
14795
  * ```
14284
14796
  */
@@ -14302,11 +14814,12 @@ class WalkerUtils {
14302
14814
  *
14303
14815
  * @example
14304
14816
  * ```typescript
14817
+ * const instance = new WalkerInstance();
14305
14818
  * // Save to default path: ./dump/walker/my-walker.md
14306
- * await Walker.dump("BTCUSDT", "my-walker");
14819
+ * await instance.dump("BTCUSDT", "my-walker");
14307
14820
  *
14308
14821
  * // Save to custom path: ./custom/path/my-walker.md
14309
- * await Walker.dump("BTCUSDT", "my-walker", "./custom/path");
14822
+ * await instance.dump("BTCUSDT", "my-walker", "./custom/path");
14310
14823
  * ```
14311
14824
  */
14312
14825
  this.dump = async (symbol, walkerName, path) => {
@@ -14323,6 +14836,166 @@ class WalkerUtils {
14323
14836
  };
14324
14837
  }
14325
14838
  }
14839
+ /**
14840
+ * Utility class for walker operations.
14841
+ *
14842
+ * Provides simplified access to walkerCommandService.run() with logging.
14843
+ * Automatically pulls exchangeName and frameName from walker schema.
14844
+ * Exported as singleton instance for convenient usage.
14845
+ *
14846
+ * @example
14847
+ * ```typescript
14848
+ * import { Walker } from "./classes/Walker";
14849
+ *
14850
+ * for await (const result of Walker.run("BTCUSDT", {
14851
+ * walkerName: "my-walker"
14852
+ * })) {
14853
+ * console.log("Progress:", result.strategiesTested, "/", result.totalStrategies);
14854
+ * console.log("Best strategy:", result.bestStrategy, result.bestMetric);
14855
+ * }
14856
+ * ```
14857
+ */
14858
+ class WalkerUtils {
14859
+ constructor() {
14860
+ /**
14861
+ * Memoized function to get or create WalkerInstance for a symbol-walker pair.
14862
+ * Each symbol-walker combination gets its own isolated instance.
14863
+ */
14864
+ this._getInstance = functoolsKit.memoize(([symbol, walkerName]) => `${symbol}:${walkerName}`, (symbol, walkerName) => new WalkerInstance(symbol, walkerName));
14865
+ /**
14866
+ * Runs walker comparison for a symbol with context propagation.
14867
+ *
14868
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
14869
+ * @param context - Execution context with walker name
14870
+ * @returns Async generator yielding progress updates after each strategy
14871
+ */
14872
+ this.run = (symbol, context) => {
14873
+ const instance = this._getInstance(symbol, context.walkerName);
14874
+ return instance.run(symbol, context);
14875
+ };
14876
+ /**
14877
+ * Runs walker comparison in background without yielding results.
14878
+ *
14879
+ * Consumes all walker progress updates internally without exposing them.
14880
+ * Useful for running walker comparison for side effects only (callbacks, logging).
14881
+ *
14882
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
14883
+ * @param context - Execution context with walker name
14884
+ * @returns Cancellation closure
14885
+ *
14886
+ * @example
14887
+ * ```typescript
14888
+ * // Run walker silently, only callbacks will fire
14889
+ * await Walker.background("BTCUSDT", {
14890
+ * walkerName: "my-walker"
14891
+ * });
14892
+ * console.log("Walker comparison completed");
14893
+ * ```
14894
+ */
14895
+ this.background = (symbol, context) => {
14896
+ const instance = this._getInstance(symbol, context.walkerName);
14897
+ return instance.background(symbol, context);
14898
+ };
14899
+ /**
14900
+ * Stops all strategies in the walker from generating new signals.
14901
+ *
14902
+ * Iterates through all strategies defined in walker schema and:
14903
+ * 1. Sends stop signal via walkerStopSubject (interrupts current running strategy)
14904
+ * 2. Sets internal stop flag for each strategy (prevents new signals)
14905
+ *
14906
+ * Current active signals (if any) will complete normally.
14907
+ * Walker will stop at the next safe point.
14908
+ *
14909
+ * Supports multiple walkers running on the same symbol simultaneously.
14910
+ * Stop signal is filtered by walkerName to prevent interference.
14911
+ *
14912
+ * @param symbol - Trading pair symbol
14913
+ * @param walkerName - Walker name to stop
14914
+ * @returns Promise that resolves when all stop flags are set
14915
+ *
14916
+ * @example
14917
+ * ```typescript
14918
+ * // Stop walker and all its strategies
14919
+ * await Walker.stop("BTCUSDT", "my-walker");
14920
+ * ```
14921
+ */
14922
+ this.stop = async (symbol, walkerName) => {
14923
+ const instance = this._getInstance(symbol, walkerName);
14924
+ return await instance.stop(symbol, walkerName);
14925
+ };
14926
+ /**
14927
+ * Gets walker results data from all strategy comparisons.
14928
+ *
14929
+ * @param symbol - Trading symbol
14930
+ * @param walkerName - Walker name to get data for
14931
+ * @returns Promise resolving to walker results data object
14932
+ *
14933
+ * @example
14934
+ * ```typescript
14935
+ * const results = await Walker.getData("BTCUSDT", "my-walker");
14936
+ * console.log(results.bestStrategy, results.bestMetric);
14937
+ * ```
14938
+ */
14939
+ this.getData = async (symbol, walkerName) => {
14940
+ const instance = this._getInstance(symbol, walkerName);
14941
+ return await instance.getData(symbol, walkerName);
14942
+ };
14943
+ /**
14944
+ * Generates markdown report with all strategy comparisons for a walker.
14945
+ *
14946
+ * @param symbol - Trading symbol
14947
+ * @param walkerName - Walker name to generate report for
14948
+ * @returns Promise resolving to markdown formatted report string
14949
+ *
14950
+ * @example
14951
+ * ```typescript
14952
+ * const markdown = await Walker.getReport("BTCUSDT", "my-walker");
14953
+ * console.log(markdown);
14954
+ * ```
14955
+ */
14956
+ this.getReport = async (symbol, walkerName) => {
14957
+ const instance = this._getInstance(symbol, walkerName);
14958
+ return await instance.getReport(symbol, walkerName);
14959
+ };
14960
+ /**
14961
+ * Saves walker report to disk.
14962
+ *
14963
+ * @param symbol - Trading symbol
14964
+ * @param walkerName - Walker name to save report for
14965
+ * @param path - Optional directory path to save report (default: "./dump/walker")
14966
+ *
14967
+ * @example
14968
+ * ```typescript
14969
+ * // Save to default path: ./dump/walker/my-walker.md
14970
+ * await Walker.dump("BTCUSDT", "my-walker");
14971
+ *
14972
+ * // Save to custom path: ./custom/path/my-walker.md
14973
+ * await Walker.dump("BTCUSDT", "my-walker", "./custom/path");
14974
+ * ```
14975
+ */
14976
+ this.dump = async (symbol, walkerName, path) => {
14977
+ const instance = this._getInstance(symbol, walkerName);
14978
+ return await instance.dump(symbol, walkerName, path);
14979
+ };
14980
+ /**
14981
+ * Lists all active walker instances with their current status.
14982
+ *
14983
+ * @returns Promise resolving to array of status objects for all instances
14984
+ *
14985
+ * @example
14986
+ * ```typescript
14987
+ * const statusList = await Walker.list();
14988
+ * statusList.forEach(status => {
14989
+ * console.log(`${status.symbol} - ${status.walkerName}: ${status.status}`);
14990
+ * });
14991
+ * ```
14992
+ */
14993
+ this.list = async () => {
14994
+ const instanceList = this._getInstance.values();
14995
+ return await Promise.all(instanceList.map((instance) => instance.getStatus()));
14996
+ };
14997
+ }
14998
+ }
14326
14999
  /**
14327
15000
  * Singleton instance of WalkerUtils for convenient walker operations.
14328
15001
  *