backtest-kit 1.1.5 → 1.1.7

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
@@ -1043,8 +1043,7 @@ class PersistSignalUtils {
1043
1043
  const stateStorage = this.getSignalStorage(strategyName);
1044
1044
  await stateStorage.waitForInit(isInitial);
1045
1045
  if (await stateStorage.hasValue(symbol)) {
1046
- const { signalRow } = await stateStorage.readValue(symbol);
1047
- return signalRow;
1046
+ return await stateStorage.readValue(symbol);
1048
1047
  }
1049
1048
  return null;
1050
1049
  };
@@ -1064,7 +1063,7 @@ class PersistSignalUtils {
1064
1063
  const isInitial = !this.getSignalStorage.has(strategyName);
1065
1064
  const stateStorage = this.getSignalStorage(strategyName);
1066
1065
  await stateStorage.waitForInit(isInitial);
1067
- await stateStorage.writeValue(symbol, { signalRow });
1066
+ await stateStorage.writeValue(symbol, signalRow);
1068
1067
  };
1069
1068
  }
1070
1069
  /**
@@ -1206,6 +1205,7 @@ const GET_SIGNAL_FN = functoolsKit.trycatch(async (self) => {
1206
1205
  }
1207
1206
  const signalRow = {
1208
1207
  id: functoolsKit.randomString(),
1208
+ priceOpen: await self.params.exchange.getAveragePrice(self.params.execution.context.symbol),
1209
1209
  ...signal,
1210
1210
  symbol: self.params.execution.context.symbol,
1211
1211
  exchangeName: self.params.method.context.exchangeName,
@@ -2742,6 +2742,24 @@ class BacktestGlobalService {
2742
2742
  }
2743
2743
  }
2744
2744
 
2745
+ /**
2746
+ * Checks if a value is unsafe for display (not a number, NaN, or Infinity).
2747
+ *
2748
+ * @param value - Value to check
2749
+ * @returns true if value is unsafe, false otherwise
2750
+ */
2751
+ function isUnsafe$1(value) {
2752
+ if (typeof value !== "number") {
2753
+ return true;
2754
+ }
2755
+ if (isNaN(value)) {
2756
+ return true;
2757
+ }
2758
+ if (!isFinite(value)) {
2759
+ return true;
2760
+ }
2761
+ return false;
2762
+ }
2745
2763
  const columns$1 = [
2746
2764
  {
2747
2765
  key: "signalId",
@@ -2834,13 +2852,80 @@ let ReportStorage$1 = class ReportStorage {
2834
2852
  this._signalList.push(data);
2835
2853
  }
2836
2854
  /**
2837
- * Generates markdown report with all closed signals for a strategy.
2855
+ * Calculates statistical data from closed signals (Controller).
2856
+ * Returns null for any unsafe numeric values (NaN, Infinity, etc).
2857
+ *
2858
+ * @returns Statistical data (empty object if no signals)
2859
+ */
2860
+ async getData() {
2861
+ if (this._signalList.length === 0) {
2862
+ return {
2863
+ signalList: [],
2864
+ totalSignals: 0,
2865
+ winCount: 0,
2866
+ lossCount: 0,
2867
+ winRate: null,
2868
+ avgPnl: null,
2869
+ totalPnl: null,
2870
+ stdDev: null,
2871
+ sharpeRatio: null,
2872
+ annualizedSharpeRatio: null,
2873
+ certaintyRatio: null,
2874
+ expectedYearlyReturns: null,
2875
+ };
2876
+ }
2877
+ const totalSignals = this._signalList.length;
2878
+ const winCount = this._signalList.filter((s) => s.pnl.pnlPercentage > 0).length;
2879
+ const lossCount = this._signalList.filter((s) => s.pnl.pnlPercentage < 0).length;
2880
+ // Calculate basic statistics
2881
+ const avgPnl = this._signalList.reduce((sum, s) => sum + s.pnl.pnlPercentage, 0) / totalSignals;
2882
+ const totalPnl = this._signalList.reduce((sum, s) => sum + s.pnl.pnlPercentage, 0);
2883
+ const winRate = (winCount / totalSignals) * 100;
2884
+ // Calculate Sharpe Ratio (risk-free rate = 0)
2885
+ const returns = this._signalList.map((s) => s.pnl.pnlPercentage);
2886
+ const variance = returns.reduce((sum, r) => sum + Math.pow(r - avgPnl, 2), 0) / totalSignals;
2887
+ const stdDev = Math.sqrt(variance);
2888
+ const sharpeRatio = stdDev > 0 ? avgPnl / stdDev : 0;
2889
+ const annualizedSharpeRatio = sharpeRatio * Math.sqrt(365);
2890
+ // Calculate Certainty Ratio
2891
+ const wins = this._signalList.filter((s) => s.pnl.pnlPercentage > 0);
2892
+ const losses = this._signalList.filter((s) => s.pnl.pnlPercentage < 0);
2893
+ const avgWin = wins.length > 0
2894
+ ? wins.reduce((sum, s) => sum + s.pnl.pnlPercentage, 0) / wins.length
2895
+ : 0;
2896
+ const avgLoss = losses.length > 0
2897
+ ? losses.reduce((sum, s) => sum + s.pnl.pnlPercentage, 0) / losses.length
2898
+ : 0;
2899
+ const certaintyRatio = avgLoss < 0 ? avgWin / Math.abs(avgLoss) : 0;
2900
+ // Calculate Expected Yearly Returns
2901
+ const avgDurationMs = this._signalList.reduce((sum, s) => sum + (s.closeTimestamp - s.signal.timestamp), 0) / totalSignals;
2902
+ const avgDurationDays = avgDurationMs / (1000 * 60 * 60 * 24);
2903
+ const tradesPerYear = avgDurationDays > 0 ? 365 / avgDurationDays : 0;
2904
+ const expectedYearlyReturns = avgPnl * tradesPerYear;
2905
+ return {
2906
+ signalList: this._signalList,
2907
+ totalSignals,
2908
+ winCount,
2909
+ lossCount,
2910
+ winRate: isUnsafe$1(winRate) ? null : winRate,
2911
+ avgPnl: isUnsafe$1(avgPnl) ? null : avgPnl,
2912
+ totalPnl: isUnsafe$1(totalPnl) ? null : totalPnl,
2913
+ stdDev: isUnsafe$1(stdDev) ? null : stdDev,
2914
+ sharpeRatio: isUnsafe$1(sharpeRatio) ? null : sharpeRatio,
2915
+ annualizedSharpeRatio: isUnsafe$1(annualizedSharpeRatio) ? null : annualizedSharpeRatio,
2916
+ certaintyRatio: isUnsafe$1(certaintyRatio) ? null : certaintyRatio,
2917
+ expectedYearlyReturns: isUnsafe$1(expectedYearlyReturns) ? null : expectedYearlyReturns,
2918
+ };
2919
+ }
2920
+ /**
2921
+ * Generates markdown report with all closed signals for a strategy (View).
2838
2922
  *
2839
2923
  * @param strategyName - Strategy name
2840
2924
  * @returns Markdown formatted report with all signals
2841
2925
  */
2842
- getReport(strategyName) {
2843
- if (this._signalList.length === 0) {
2926
+ async getReport(strategyName) {
2927
+ const stats = await this.getData();
2928
+ if (stats.totalSignals === 0) {
2844
2929
  return functoolsKit.str.newline(`# Backtest Report: ${strategyName}`, "", "No signals closed yet.");
2845
2930
  }
2846
2931
  const header = columns$1.map((col) => col.label);
@@ -2848,13 +2933,7 @@ let ReportStorage$1 = class ReportStorage {
2848
2933
  const rows = this._signalList.map((closedSignal) => columns$1.map((col) => col.format(closedSignal)));
2849
2934
  const tableData = [header, separator, ...rows];
2850
2935
  const table = functoolsKit.str.newline(tableData.map(row => `| ${row.join(" | ")} |`));
2851
- // Calculate statistics
2852
- const totalSignals = this._signalList.length;
2853
- const winCount = this._signalList.filter((s) => s.pnl.pnlPercentage > 0).length;
2854
- const lossCount = this._signalList.filter((s) => s.pnl.pnlPercentage < 0).length;
2855
- const avgPnl = this._signalList.reduce((sum, s) => sum + s.pnl.pnlPercentage, 0) / totalSignals;
2856
- const totalPnl = this._signalList.reduce((sum, s) => sum + s.pnl.pnlPercentage, 0);
2857
- return functoolsKit.str.newline(`# Backtest Report: ${strategyName}`, "", table, "", `**Total signals:** ${totalSignals}`, `**Closed signals:** ${totalSignals}`, `**Win rate:** ${((winCount / totalSignals) * 100).toFixed(2)}% (${winCount}W / ${lossCount}L)`, `**Average PNL:** ${avgPnl > 0 ? "+" : ""}${avgPnl.toFixed(2)}%`, `**Total PNL:** ${totalPnl > 0 ? "+" : ""}${totalPnl.toFixed(2)}%`);
2936
+ return functoolsKit.str.newline(`# Backtest Report: ${strategyName}`, "", table, "", `**Total signals:** ${stats.totalSignals}`, `**Closed signals:** ${stats.totalSignals}`, `**Win rate:** ${stats.winRate === null ? "N/A" : `${stats.winRate.toFixed(2)}% (${stats.winCount}W / ${stats.lossCount}L) (higher is better)`}`, `**Average PNL:** ${stats.avgPnl === null ? "N/A" : `${stats.avgPnl > 0 ? "+" : ""}${stats.avgPnl.toFixed(2)}% (higher is better)`}`, `**Total PNL:** ${stats.totalPnl === null ? "N/A" : `${stats.totalPnl > 0 ? "+" : ""}${stats.totalPnl.toFixed(2)}% (higher is better)`}`, `**Standard Deviation:** ${stats.stdDev === null ? "N/A" : `${stats.stdDev.toFixed(3)}% (lower is better)`}`, `**Sharpe Ratio:** ${stats.sharpeRatio === null ? "N/A" : `${stats.sharpeRatio.toFixed(3)} (higher is better)`}`, `**Annualized Sharpe Ratio:** ${stats.annualizedSharpeRatio === null ? "N/A" : `${stats.annualizedSharpeRatio.toFixed(3)} (higher is better)`}`, `**Certainty Ratio:** ${stats.certaintyRatio === null ? "N/A" : `${stats.certaintyRatio.toFixed(3)} (higher is better)`}`, `**Expected Yearly Returns:** ${stats.expectedYearlyReturns === null ? "N/A" : `${stats.expectedYearlyReturns > 0 ? "+" : ""}${stats.expectedYearlyReturns.toFixed(2)}% (higher is better)`}`);
2858
2937
  }
2859
2938
  /**
2860
2939
  * Saves strategy report to disk.
@@ -2863,7 +2942,7 @@ let ReportStorage$1 = class ReportStorage {
2863
2942
  * @param path - Directory path to save report (default: "./logs/backtest")
2864
2943
  */
2865
2944
  async dump(strategyName, path$1 = "./logs/backtest") {
2866
- const markdown = this.getReport(strategyName);
2945
+ const markdown = await this.getReport(strategyName);
2867
2946
  try {
2868
2947
  const dir = path.join(process.cwd(), path$1);
2869
2948
  await fs.mkdir(dir, { recursive: true });
@@ -2942,6 +3021,27 @@ class BacktestMarkdownService {
2942
3021
  const storage = this.getStorage(data.strategyName);
2943
3022
  storage.addSignal(data);
2944
3023
  };
3024
+ /**
3025
+ * Gets statistical data from all closed signals for a strategy.
3026
+ * Delegates to ReportStorage.getData().
3027
+ *
3028
+ * @param strategyName - Strategy name to get data for
3029
+ * @returns Statistical data object with all metrics
3030
+ *
3031
+ * @example
3032
+ * ```typescript
3033
+ * const service = new BacktestMarkdownService();
3034
+ * const stats = await service.getData("my-strategy");
3035
+ * console.log(stats.sharpeRatio, stats.winRate);
3036
+ * ```
3037
+ */
3038
+ this.getData = async (strategyName) => {
3039
+ this.loggerService.log("backtestMarkdownService getData", {
3040
+ strategyName,
3041
+ });
3042
+ const storage = this.getStorage(strategyName);
3043
+ return storage.getData();
3044
+ };
2945
3045
  /**
2946
3046
  * Generates markdown report with all closed signals for a strategy.
2947
3047
  * Delegates to ReportStorage.generateReport().
@@ -2952,7 +3052,7 @@ class BacktestMarkdownService {
2952
3052
  * @example
2953
3053
  * ```typescript
2954
3054
  * const service = new BacktestMarkdownService();
2955
- * const markdown = service.generateReport("my-strategy");
3055
+ * const markdown = await service.getReport("my-strategy");
2956
3056
  * console.log(markdown);
2957
3057
  * ```
2958
3058
  */
@@ -3032,6 +3132,24 @@ class BacktestMarkdownService {
3032
3132
  }
3033
3133
  }
3034
3134
 
3135
+ /**
3136
+ * Checks if a value is unsafe for display (not a number, NaN, or Infinity).
3137
+ *
3138
+ * @param value - Value to check
3139
+ * @returns true if value is unsafe, false otherwise
3140
+ */
3141
+ function isUnsafe(value) {
3142
+ if (typeof value !== "number") {
3143
+ return true;
3144
+ }
3145
+ if (isNaN(value)) {
3146
+ return true;
3147
+ }
3148
+ if (!isFinite(value)) {
3149
+ return true;
3150
+ }
3151
+ return false;
3152
+ }
3035
3153
  const columns = [
3036
3154
  {
3037
3155
  key: "timestamp",
@@ -3239,36 +3357,103 @@ class ReportStorage {
3239
3357
  }
3240
3358
  }
3241
3359
  /**
3242
- * Generates markdown report with all tick events for a strategy.
3360
+ * Calculates statistical data from live trading events (Controller).
3361
+ * Returns null for any unsafe numeric values (NaN, Infinity, etc).
3243
3362
  *
3244
- * @param strategyName - Strategy name
3245
- * @returns Markdown formatted report with all events
3363
+ * @returns Statistical data (empty object if no events)
3246
3364
  */
3247
- getReport(strategyName) {
3365
+ async getData() {
3248
3366
  if (this._eventList.length === 0) {
3249
- return functoolsKit.str.newline(`# Live Trading Report: ${strategyName}`, "", "No events recorded yet.");
3367
+ return {
3368
+ eventList: [],
3369
+ totalEvents: 0,
3370
+ totalClosed: 0,
3371
+ winCount: 0,
3372
+ lossCount: 0,
3373
+ winRate: null,
3374
+ avgPnl: null,
3375
+ totalPnl: null,
3376
+ stdDev: null,
3377
+ sharpeRatio: null,
3378
+ annualizedSharpeRatio: null,
3379
+ certaintyRatio: null,
3380
+ expectedYearlyReturns: null,
3381
+ };
3250
3382
  }
3251
- const header = columns.map((col) => col.label);
3252
- const separator = columns.map(() => "---");
3253
- const rows = this._eventList.map((event) => columns.map((col) => col.format(event)));
3254
- const tableData = [header, separator, ...rows];
3255
- const table = functoolsKit.str.newline(tableData.map(row => `| ${row.join(" | ")} |`));
3256
- // Calculate statistics
3257
3383
  const closedEvents = this._eventList.filter((e) => e.action === "closed");
3258
3384
  const totalClosed = closedEvents.length;
3259
3385
  const winCount = closedEvents.filter((e) => e.pnl && e.pnl > 0).length;
3260
3386
  const lossCount = closedEvents.filter((e) => e.pnl && e.pnl < 0).length;
3387
+ // Calculate basic statistics
3261
3388
  const avgPnl = totalClosed > 0
3262
3389
  ? closedEvents.reduce((sum, e) => sum + (e.pnl || 0), 0) / totalClosed
3263
3390
  : 0;
3264
3391
  const totalPnl = closedEvents.reduce((sum, e) => sum + (e.pnl || 0), 0);
3265
- return functoolsKit.str.newline(`# Live Trading Report: ${strategyName}`, "", table, "", `**Total events:** ${this._eventList.length}`, `**Closed signals:** ${totalClosed}`, totalClosed > 0
3266
- ? `**Win rate:** ${((winCount / totalClosed) * 100).toFixed(2)}% (${winCount}W / ${lossCount}L)`
3267
- : "", totalClosed > 0
3268
- ? `**Average PNL:** ${avgPnl > 0 ? "+" : ""}${avgPnl.toFixed(2)}%`
3269
- : "", totalClosed > 0
3270
- ? `**Total PNL:** ${totalPnl > 0 ? "+" : ""}${totalPnl.toFixed(2)}%`
3271
- : "");
3392
+ const winRate = (winCount / totalClosed) * 100;
3393
+ // Calculate Sharpe Ratio (risk-free rate = 0)
3394
+ let sharpeRatio = 0;
3395
+ let stdDev = 0;
3396
+ if (totalClosed > 0) {
3397
+ const returns = closedEvents.map((e) => e.pnl || 0);
3398
+ const variance = returns.reduce((sum, r) => sum + Math.pow(r - avgPnl, 2), 0) / totalClosed;
3399
+ stdDev = Math.sqrt(variance);
3400
+ sharpeRatio = stdDev > 0 ? avgPnl / stdDev : 0;
3401
+ }
3402
+ const annualizedSharpeRatio = sharpeRatio * Math.sqrt(365);
3403
+ // Calculate Certainty Ratio
3404
+ let certaintyRatio = 0;
3405
+ if (totalClosed > 0) {
3406
+ const wins = closedEvents.filter((e) => e.pnl && e.pnl > 0);
3407
+ const losses = closedEvents.filter((e) => e.pnl && e.pnl < 0);
3408
+ const avgWin = wins.length > 0
3409
+ ? wins.reduce((sum, e) => sum + (e.pnl || 0), 0) / wins.length
3410
+ : 0;
3411
+ const avgLoss = losses.length > 0
3412
+ ? losses.reduce((sum, e) => sum + (e.pnl || 0), 0) / losses.length
3413
+ : 0;
3414
+ certaintyRatio = avgLoss < 0 ? avgWin / Math.abs(avgLoss) : 0;
3415
+ }
3416
+ // Calculate Expected Yearly Returns
3417
+ let expectedYearlyReturns = 0;
3418
+ if (totalClosed > 0) {
3419
+ const avgDurationMin = closedEvents.reduce((sum, e) => sum + (e.duration || 0), 0) / totalClosed;
3420
+ const avgDurationDays = avgDurationMin / (60 * 24);
3421
+ const tradesPerYear = avgDurationDays > 0 ? 365 / avgDurationDays : 0;
3422
+ expectedYearlyReturns = avgPnl * tradesPerYear;
3423
+ }
3424
+ return {
3425
+ eventList: this._eventList,
3426
+ totalEvents: this._eventList.length,
3427
+ totalClosed,
3428
+ winCount,
3429
+ lossCount,
3430
+ winRate: isUnsafe(winRate) ? null : winRate,
3431
+ avgPnl: isUnsafe(avgPnl) ? null : avgPnl,
3432
+ totalPnl: isUnsafe(totalPnl) ? null : totalPnl,
3433
+ stdDev: isUnsafe(stdDev) ? null : stdDev,
3434
+ sharpeRatio: isUnsafe(sharpeRatio) ? null : sharpeRatio,
3435
+ annualizedSharpeRatio: isUnsafe(annualizedSharpeRatio) ? null : annualizedSharpeRatio,
3436
+ certaintyRatio: isUnsafe(certaintyRatio) ? null : certaintyRatio,
3437
+ expectedYearlyReturns: isUnsafe(expectedYearlyReturns) ? null : expectedYearlyReturns,
3438
+ };
3439
+ }
3440
+ /**
3441
+ * Generates markdown report with all tick events for a strategy (View).
3442
+ *
3443
+ * @param strategyName - Strategy name
3444
+ * @returns Markdown formatted report with all events
3445
+ */
3446
+ async getReport(strategyName) {
3447
+ const stats = await this.getData();
3448
+ if (stats.totalEvents === 0) {
3449
+ return functoolsKit.str.newline(`# Live Trading Report: ${strategyName}`, "", "No events recorded yet.");
3450
+ }
3451
+ const header = columns.map((col) => col.label);
3452
+ const separator = columns.map(() => "---");
3453
+ const rows = this._eventList.map((event) => columns.map((col) => col.format(event)));
3454
+ const tableData = [header, separator, ...rows];
3455
+ const table = functoolsKit.str.newline(tableData.map(row => `| ${row.join(" | ")} |`));
3456
+ return functoolsKit.str.newline(`# Live Trading Report: ${strategyName}`, "", table, "", `**Total events:** ${stats.totalEvents}`, `**Closed signals:** ${stats.totalClosed}`, `**Win rate:** ${stats.winRate === null ? "N/A" : `${stats.winRate.toFixed(2)}% (${stats.winCount}W / ${stats.lossCount}L) (higher is better)`}`, `**Average PNL:** ${stats.avgPnl === null ? "N/A" : `${stats.avgPnl > 0 ? "+" : ""}${stats.avgPnl.toFixed(2)}% (higher is better)`}`, `**Total PNL:** ${stats.totalPnl === null ? "N/A" : `${stats.totalPnl > 0 ? "+" : ""}${stats.totalPnl.toFixed(2)}% (higher is better)`}`, `**Standard Deviation:** ${stats.stdDev === null ? "N/A" : `${stats.stdDev.toFixed(3)}% (lower is better)`}`, `**Sharpe Ratio:** ${stats.sharpeRatio === null ? "N/A" : `${stats.sharpeRatio.toFixed(3)} (higher is better)`}`, `**Annualized Sharpe Ratio:** ${stats.annualizedSharpeRatio === null ? "N/A" : `${stats.annualizedSharpeRatio.toFixed(3)} (higher is better)`}`, `**Certainty Ratio:** ${stats.certaintyRatio === null ? "N/A" : `${stats.certaintyRatio.toFixed(3)} (higher is better)`}`, `**Expected Yearly Returns:** ${stats.expectedYearlyReturns === null ? "N/A" : `${stats.expectedYearlyReturns > 0 ? "+" : ""}${stats.expectedYearlyReturns.toFixed(2)}% (higher is better)`}`);
3272
3457
  }
3273
3458
  /**
3274
3459
  * Saves strategy report to disk.
@@ -3277,7 +3462,7 @@ class ReportStorage {
3277
3462
  * @param path - Directory path to save report (default: "./logs/live")
3278
3463
  */
3279
3464
  async dump(strategyName, path$1 = "./logs/live") {
3280
- const markdown = this.getReport(strategyName);
3465
+ const markdown = await this.getReport(strategyName);
3281
3466
  try {
3282
3467
  const dir = path.join(process.cwd(), path$1);
3283
3468
  await fs.mkdir(dir, { recursive: true });
@@ -3369,6 +3554,27 @@ class LiveMarkdownService {
3369
3554
  storage.addClosedEvent(data);
3370
3555
  }
3371
3556
  };
3557
+ /**
3558
+ * Gets statistical data from all live trading events for a strategy.
3559
+ * Delegates to ReportStorage.getData().
3560
+ *
3561
+ * @param strategyName - Strategy name to get data for
3562
+ * @returns Statistical data object with all metrics
3563
+ *
3564
+ * @example
3565
+ * ```typescript
3566
+ * const service = new LiveMarkdownService();
3567
+ * const stats = await service.getData("my-strategy");
3568
+ * console.log(stats.sharpeRatio, stats.winRate);
3569
+ * ```
3570
+ */
3571
+ this.getData = async (strategyName) => {
3572
+ this.loggerService.log("liveMarkdownService getData", {
3573
+ strategyName,
3574
+ });
3575
+ const storage = this.getStorage(strategyName);
3576
+ return storage.getData();
3577
+ };
3372
3578
  /**
3373
3579
  * Generates markdown report with all events for a strategy.
3374
3580
  * Delegates to ReportStorage.getReport().
@@ -3791,8 +3997,8 @@ const ADD_FRAME_METHOD_NAME = "add.addFrame";
3791
3997
  * timestamp: Date.now(),
3792
3998
  * }),
3793
3999
  * callbacks: {
3794
- * onOpen: (backtest, symbol, signal) => console.log("Signal opened"),
3795
- * onClose: (backtest, symbol, priceClose, signal) => console.log("Signal closed"),
4000
+ * onOpen: (symbol, signal, currentPrice, backtest) => console.log("Signal opened"),
4001
+ * onClose: (symbol, signal, priceClose, backtest) => console.log("Signal closed"),
3796
4002
  * },
3797
4003
  * });
3798
4004
  * ```
@@ -4506,6 +4712,24 @@ class BacktestUtils {
4506
4712
  isStopped = true;
4507
4713
  };
4508
4714
  };
4715
+ /**
4716
+ * Gets statistical data from all closed signals for a strategy.
4717
+ *
4718
+ * @param strategyName - Strategy name to get data for
4719
+ * @returns Promise resolving to statistical data object
4720
+ *
4721
+ * @example
4722
+ * ```typescript
4723
+ * const stats = await Backtest.getData("my-strategy");
4724
+ * console.log(stats.sharpeRatio, stats.winRate);
4725
+ * ```
4726
+ */
4727
+ this.getData = async (strategyName) => {
4728
+ backtest$1.loggerService.info("BacktestUtils.getData", {
4729
+ strategyName,
4730
+ });
4731
+ return await backtest$1.backtestMarkdownService.getData(strategyName);
4732
+ };
4509
4733
  /**
4510
4734
  * Generates markdown report with all closed signals for a strategy.
4511
4735
  *
@@ -4668,6 +4892,24 @@ class LiveUtils {
4668
4892
  isStopped = true;
4669
4893
  };
4670
4894
  };
4895
+ /**
4896
+ * Gets statistical data from all live trading events for a strategy.
4897
+ *
4898
+ * @param strategyName - Strategy name to get data for
4899
+ * @returns Promise resolving to statistical data object
4900
+ *
4901
+ * @example
4902
+ * ```typescript
4903
+ * const stats = await Live.getData("my-strategy");
4904
+ * console.log(stats.sharpeRatio, stats.winRate);
4905
+ * ```
4906
+ */
4907
+ this.getData = async (strategyName) => {
4908
+ backtest$1.loggerService.info("LiveUtils.getData", {
4909
+ strategyName,
4910
+ });
4911
+ return await backtest$1.liveMarkdownService.getData(strategyName);
4912
+ };
4671
4913
  /**
4672
4914
  * Generates markdown report with all events for a strategy.
4673
4915
  *