backtest-kit 1.1.5 → 1.1.6
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/README.md +3 -3
- package/build/index.cjs +273 -31
- package/build/index.mjs +273 -31
- package/package.json +1 -1
- package/types.d.ts +441 -256
package/build/index.mjs
CHANGED
|
@@ -2740,6 +2740,24 @@ class BacktestGlobalService {
|
|
|
2740
2740
|
}
|
|
2741
2741
|
}
|
|
2742
2742
|
|
|
2743
|
+
/**
|
|
2744
|
+
* Checks if a value is unsafe for display (not a number, NaN, or Infinity).
|
|
2745
|
+
*
|
|
2746
|
+
* @param value - Value to check
|
|
2747
|
+
* @returns true if value is unsafe, false otherwise
|
|
2748
|
+
*/
|
|
2749
|
+
function isUnsafe$1(value) {
|
|
2750
|
+
if (typeof value !== "number") {
|
|
2751
|
+
return true;
|
|
2752
|
+
}
|
|
2753
|
+
if (isNaN(value)) {
|
|
2754
|
+
return true;
|
|
2755
|
+
}
|
|
2756
|
+
if (!isFinite(value)) {
|
|
2757
|
+
return true;
|
|
2758
|
+
}
|
|
2759
|
+
return false;
|
|
2760
|
+
}
|
|
2743
2761
|
const columns$1 = [
|
|
2744
2762
|
{
|
|
2745
2763
|
key: "signalId",
|
|
@@ -2832,13 +2850,80 @@ let ReportStorage$1 = class ReportStorage {
|
|
|
2832
2850
|
this._signalList.push(data);
|
|
2833
2851
|
}
|
|
2834
2852
|
/**
|
|
2835
|
-
*
|
|
2853
|
+
* Calculates statistical data from closed signals (Controller).
|
|
2854
|
+
* Returns null for any unsafe numeric values (NaN, Infinity, etc).
|
|
2855
|
+
*
|
|
2856
|
+
* @returns Statistical data (empty object if no signals)
|
|
2857
|
+
*/
|
|
2858
|
+
async getData() {
|
|
2859
|
+
if (this._signalList.length === 0) {
|
|
2860
|
+
return {
|
|
2861
|
+
signalList: [],
|
|
2862
|
+
totalSignals: 0,
|
|
2863
|
+
winCount: 0,
|
|
2864
|
+
lossCount: 0,
|
|
2865
|
+
winRate: null,
|
|
2866
|
+
avgPnl: null,
|
|
2867
|
+
totalPnl: null,
|
|
2868
|
+
stdDev: null,
|
|
2869
|
+
sharpeRatio: null,
|
|
2870
|
+
annualizedSharpeRatio: null,
|
|
2871
|
+
certaintyRatio: null,
|
|
2872
|
+
expectedYearlyReturns: null,
|
|
2873
|
+
};
|
|
2874
|
+
}
|
|
2875
|
+
const totalSignals = this._signalList.length;
|
|
2876
|
+
const winCount = this._signalList.filter((s) => s.pnl.pnlPercentage > 0).length;
|
|
2877
|
+
const lossCount = this._signalList.filter((s) => s.pnl.pnlPercentage < 0).length;
|
|
2878
|
+
// Calculate basic statistics
|
|
2879
|
+
const avgPnl = this._signalList.reduce((sum, s) => sum + s.pnl.pnlPercentage, 0) / totalSignals;
|
|
2880
|
+
const totalPnl = this._signalList.reduce((sum, s) => sum + s.pnl.pnlPercentage, 0);
|
|
2881
|
+
const winRate = (winCount / totalSignals) * 100;
|
|
2882
|
+
// Calculate Sharpe Ratio (risk-free rate = 0)
|
|
2883
|
+
const returns = this._signalList.map((s) => s.pnl.pnlPercentage);
|
|
2884
|
+
const variance = returns.reduce((sum, r) => sum + Math.pow(r - avgPnl, 2), 0) / totalSignals;
|
|
2885
|
+
const stdDev = Math.sqrt(variance);
|
|
2886
|
+
const sharpeRatio = stdDev > 0 ? avgPnl / stdDev : 0;
|
|
2887
|
+
const annualizedSharpeRatio = sharpeRatio * Math.sqrt(365);
|
|
2888
|
+
// Calculate Certainty Ratio
|
|
2889
|
+
const wins = this._signalList.filter((s) => s.pnl.pnlPercentage > 0);
|
|
2890
|
+
const losses = this._signalList.filter((s) => s.pnl.pnlPercentage < 0);
|
|
2891
|
+
const avgWin = wins.length > 0
|
|
2892
|
+
? wins.reduce((sum, s) => sum + s.pnl.pnlPercentage, 0) / wins.length
|
|
2893
|
+
: 0;
|
|
2894
|
+
const avgLoss = losses.length > 0
|
|
2895
|
+
? losses.reduce((sum, s) => sum + s.pnl.pnlPercentage, 0) / losses.length
|
|
2896
|
+
: 0;
|
|
2897
|
+
const certaintyRatio = avgLoss < 0 ? avgWin / Math.abs(avgLoss) : 0;
|
|
2898
|
+
// Calculate Expected Yearly Returns
|
|
2899
|
+
const avgDurationMs = this._signalList.reduce((sum, s) => sum + (s.closeTimestamp - s.signal.timestamp), 0) / totalSignals;
|
|
2900
|
+
const avgDurationDays = avgDurationMs / (1000 * 60 * 60 * 24);
|
|
2901
|
+
const tradesPerYear = avgDurationDays > 0 ? 365 / avgDurationDays : 0;
|
|
2902
|
+
const expectedYearlyReturns = avgPnl * tradesPerYear;
|
|
2903
|
+
return {
|
|
2904
|
+
signalList: this._signalList,
|
|
2905
|
+
totalSignals,
|
|
2906
|
+
winCount,
|
|
2907
|
+
lossCount,
|
|
2908
|
+
winRate: isUnsafe$1(winRate) ? null : winRate,
|
|
2909
|
+
avgPnl: isUnsafe$1(avgPnl) ? null : avgPnl,
|
|
2910
|
+
totalPnl: isUnsafe$1(totalPnl) ? null : totalPnl,
|
|
2911
|
+
stdDev: isUnsafe$1(stdDev) ? null : stdDev,
|
|
2912
|
+
sharpeRatio: isUnsafe$1(sharpeRatio) ? null : sharpeRatio,
|
|
2913
|
+
annualizedSharpeRatio: isUnsafe$1(annualizedSharpeRatio) ? null : annualizedSharpeRatio,
|
|
2914
|
+
certaintyRatio: isUnsafe$1(certaintyRatio) ? null : certaintyRatio,
|
|
2915
|
+
expectedYearlyReturns: isUnsafe$1(expectedYearlyReturns) ? null : expectedYearlyReturns,
|
|
2916
|
+
};
|
|
2917
|
+
}
|
|
2918
|
+
/**
|
|
2919
|
+
* Generates markdown report with all closed signals for a strategy (View).
|
|
2836
2920
|
*
|
|
2837
2921
|
* @param strategyName - Strategy name
|
|
2838
2922
|
* @returns Markdown formatted report with all signals
|
|
2839
2923
|
*/
|
|
2840
|
-
getReport(strategyName) {
|
|
2841
|
-
|
|
2924
|
+
async getReport(strategyName) {
|
|
2925
|
+
const stats = await this.getData();
|
|
2926
|
+
if (stats.totalSignals === 0) {
|
|
2842
2927
|
return str.newline(`# Backtest Report: ${strategyName}`, "", "No signals closed yet.");
|
|
2843
2928
|
}
|
|
2844
2929
|
const header = columns$1.map((col) => col.label);
|
|
@@ -2846,13 +2931,7 @@ let ReportStorage$1 = class ReportStorage {
|
|
|
2846
2931
|
const rows = this._signalList.map((closedSignal) => columns$1.map((col) => col.format(closedSignal)));
|
|
2847
2932
|
const tableData = [header, separator, ...rows];
|
|
2848
2933
|
const table = str.newline(tableData.map(row => `| ${row.join(" | ")} |`));
|
|
2849
|
-
|
|
2850
|
-
const totalSignals = this._signalList.length;
|
|
2851
|
-
const winCount = this._signalList.filter((s) => s.pnl.pnlPercentage > 0).length;
|
|
2852
|
-
const lossCount = this._signalList.filter((s) => s.pnl.pnlPercentage < 0).length;
|
|
2853
|
-
const avgPnl = this._signalList.reduce((sum, s) => sum + s.pnl.pnlPercentage, 0) / totalSignals;
|
|
2854
|
-
const totalPnl = this._signalList.reduce((sum, s) => sum + s.pnl.pnlPercentage, 0);
|
|
2855
|
-
return 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)}%`);
|
|
2934
|
+
return 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)`}`);
|
|
2856
2935
|
}
|
|
2857
2936
|
/**
|
|
2858
2937
|
* Saves strategy report to disk.
|
|
@@ -2861,7 +2940,7 @@ let ReportStorage$1 = class ReportStorage {
|
|
|
2861
2940
|
* @param path - Directory path to save report (default: "./logs/backtest")
|
|
2862
2941
|
*/
|
|
2863
2942
|
async dump(strategyName, path = "./logs/backtest") {
|
|
2864
|
-
const markdown = this.getReport(strategyName);
|
|
2943
|
+
const markdown = await this.getReport(strategyName);
|
|
2865
2944
|
try {
|
|
2866
2945
|
const dir = join(process.cwd(), path);
|
|
2867
2946
|
await mkdir(dir, { recursive: true });
|
|
@@ -2940,6 +3019,27 @@ class BacktestMarkdownService {
|
|
|
2940
3019
|
const storage = this.getStorage(data.strategyName);
|
|
2941
3020
|
storage.addSignal(data);
|
|
2942
3021
|
};
|
|
3022
|
+
/**
|
|
3023
|
+
* Gets statistical data from all closed signals for a strategy.
|
|
3024
|
+
* Delegates to ReportStorage.getData().
|
|
3025
|
+
*
|
|
3026
|
+
* @param strategyName - Strategy name to get data for
|
|
3027
|
+
* @returns Statistical data object with all metrics
|
|
3028
|
+
*
|
|
3029
|
+
* @example
|
|
3030
|
+
* ```typescript
|
|
3031
|
+
* const service = new BacktestMarkdownService();
|
|
3032
|
+
* const stats = await service.getData("my-strategy");
|
|
3033
|
+
* console.log(stats.sharpeRatio, stats.winRate);
|
|
3034
|
+
* ```
|
|
3035
|
+
*/
|
|
3036
|
+
this.getData = async (strategyName) => {
|
|
3037
|
+
this.loggerService.log("backtestMarkdownService getData", {
|
|
3038
|
+
strategyName,
|
|
3039
|
+
});
|
|
3040
|
+
const storage = this.getStorage(strategyName);
|
|
3041
|
+
return storage.getData();
|
|
3042
|
+
};
|
|
2943
3043
|
/**
|
|
2944
3044
|
* Generates markdown report with all closed signals for a strategy.
|
|
2945
3045
|
* Delegates to ReportStorage.generateReport().
|
|
@@ -2950,7 +3050,7 @@ class BacktestMarkdownService {
|
|
|
2950
3050
|
* @example
|
|
2951
3051
|
* ```typescript
|
|
2952
3052
|
* const service = new BacktestMarkdownService();
|
|
2953
|
-
* const markdown = service.
|
|
3053
|
+
* const markdown = await service.getReport("my-strategy");
|
|
2954
3054
|
* console.log(markdown);
|
|
2955
3055
|
* ```
|
|
2956
3056
|
*/
|
|
@@ -3030,6 +3130,24 @@ class BacktestMarkdownService {
|
|
|
3030
3130
|
}
|
|
3031
3131
|
}
|
|
3032
3132
|
|
|
3133
|
+
/**
|
|
3134
|
+
* Checks if a value is unsafe for display (not a number, NaN, or Infinity).
|
|
3135
|
+
*
|
|
3136
|
+
* @param value - Value to check
|
|
3137
|
+
* @returns true if value is unsafe, false otherwise
|
|
3138
|
+
*/
|
|
3139
|
+
function isUnsafe(value) {
|
|
3140
|
+
if (typeof value !== "number") {
|
|
3141
|
+
return true;
|
|
3142
|
+
}
|
|
3143
|
+
if (isNaN(value)) {
|
|
3144
|
+
return true;
|
|
3145
|
+
}
|
|
3146
|
+
if (!isFinite(value)) {
|
|
3147
|
+
return true;
|
|
3148
|
+
}
|
|
3149
|
+
return false;
|
|
3150
|
+
}
|
|
3033
3151
|
const columns = [
|
|
3034
3152
|
{
|
|
3035
3153
|
key: "timestamp",
|
|
@@ -3237,36 +3355,103 @@ class ReportStorage {
|
|
|
3237
3355
|
}
|
|
3238
3356
|
}
|
|
3239
3357
|
/**
|
|
3240
|
-
*
|
|
3358
|
+
* Calculates statistical data from live trading events (Controller).
|
|
3359
|
+
* Returns null for any unsafe numeric values (NaN, Infinity, etc).
|
|
3241
3360
|
*
|
|
3242
|
-
* @
|
|
3243
|
-
* @returns Markdown formatted report with all events
|
|
3361
|
+
* @returns Statistical data (empty object if no events)
|
|
3244
3362
|
*/
|
|
3245
|
-
|
|
3363
|
+
async getData() {
|
|
3246
3364
|
if (this._eventList.length === 0) {
|
|
3247
|
-
return
|
|
3365
|
+
return {
|
|
3366
|
+
eventList: [],
|
|
3367
|
+
totalEvents: 0,
|
|
3368
|
+
totalClosed: 0,
|
|
3369
|
+
winCount: 0,
|
|
3370
|
+
lossCount: 0,
|
|
3371
|
+
winRate: null,
|
|
3372
|
+
avgPnl: null,
|
|
3373
|
+
totalPnl: null,
|
|
3374
|
+
stdDev: null,
|
|
3375
|
+
sharpeRatio: null,
|
|
3376
|
+
annualizedSharpeRatio: null,
|
|
3377
|
+
certaintyRatio: null,
|
|
3378
|
+
expectedYearlyReturns: null,
|
|
3379
|
+
};
|
|
3248
3380
|
}
|
|
3249
|
-
const header = columns.map((col) => col.label);
|
|
3250
|
-
const separator = columns.map(() => "---");
|
|
3251
|
-
const rows = this._eventList.map((event) => columns.map((col) => col.format(event)));
|
|
3252
|
-
const tableData = [header, separator, ...rows];
|
|
3253
|
-
const table = str.newline(tableData.map(row => `| ${row.join(" | ")} |`));
|
|
3254
|
-
// Calculate statistics
|
|
3255
3381
|
const closedEvents = this._eventList.filter((e) => e.action === "closed");
|
|
3256
3382
|
const totalClosed = closedEvents.length;
|
|
3257
3383
|
const winCount = closedEvents.filter((e) => e.pnl && e.pnl > 0).length;
|
|
3258
3384
|
const lossCount = closedEvents.filter((e) => e.pnl && e.pnl < 0).length;
|
|
3385
|
+
// Calculate basic statistics
|
|
3259
3386
|
const avgPnl = totalClosed > 0
|
|
3260
3387
|
? closedEvents.reduce((sum, e) => sum + (e.pnl || 0), 0) / totalClosed
|
|
3261
3388
|
: 0;
|
|
3262
3389
|
const totalPnl = closedEvents.reduce((sum, e) => sum + (e.pnl || 0), 0);
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3390
|
+
const winRate = (winCount / totalClosed) * 100;
|
|
3391
|
+
// Calculate Sharpe Ratio (risk-free rate = 0)
|
|
3392
|
+
let sharpeRatio = 0;
|
|
3393
|
+
let stdDev = 0;
|
|
3394
|
+
if (totalClosed > 0) {
|
|
3395
|
+
const returns = closedEvents.map((e) => e.pnl || 0);
|
|
3396
|
+
const variance = returns.reduce((sum, r) => sum + Math.pow(r - avgPnl, 2), 0) / totalClosed;
|
|
3397
|
+
stdDev = Math.sqrt(variance);
|
|
3398
|
+
sharpeRatio = stdDev > 0 ? avgPnl / stdDev : 0;
|
|
3399
|
+
}
|
|
3400
|
+
const annualizedSharpeRatio = sharpeRatio * Math.sqrt(365);
|
|
3401
|
+
// Calculate Certainty Ratio
|
|
3402
|
+
let certaintyRatio = 0;
|
|
3403
|
+
if (totalClosed > 0) {
|
|
3404
|
+
const wins = closedEvents.filter((e) => e.pnl && e.pnl > 0);
|
|
3405
|
+
const losses = closedEvents.filter((e) => e.pnl && e.pnl < 0);
|
|
3406
|
+
const avgWin = wins.length > 0
|
|
3407
|
+
? wins.reduce((sum, e) => sum + (e.pnl || 0), 0) / wins.length
|
|
3408
|
+
: 0;
|
|
3409
|
+
const avgLoss = losses.length > 0
|
|
3410
|
+
? losses.reduce((sum, e) => sum + (e.pnl || 0), 0) / losses.length
|
|
3411
|
+
: 0;
|
|
3412
|
+
certaintyRatio = avgLoss < 0 ? avgWin / Math.abs(avgLoss) : 0;
|
|
3413
|
+
}
|
|
3414
|
+
// Calculate Expected Yearly Returns
|
|
3415
|
+
let expectedYearlyReturns = 0;
|
|
3416
|
+
if (totalClosed > 0) {
|
|
3417
|
+
const avgDurationMin = closedEvents.reduce((sum, e) => sum + (e.duration || 0), 0) / totalClosed;
|
|
3418
|
+
const avgDurationDays = avgDurationMin / (60 * 24);
|
|
3419
|
+
const tradesPerYear = avgDurationDays > 0 ? 365 / avgDurationDays : 0;
|
|
3420
|
+
expectedYearlyReturns = avgPnl * tradesPerYear;
|
|
3421
|
+
}
|
|
3422
|
+
return {
|
|
3423
|
+
eventList: this._eventList,
|
|
3424
|
+
totalEvents: this._eventList.length,
|
|
3425
|
+
totalClosed,
|
|
3426
|
+
winCount,
|
|
3427
|
+
lossCount,
|
|
3428
|
+
winRate: isUnsafe(winRate) ? null : winRate,
|
|
3429
|
+
avgPnl: isUnsafe(avgPnl) ? null : avgPnl,
|
|
3430
|
+
totalPnl: isUnsafe(totalPnl) ? null : totalPnl,
|
|
3431
|
+
stdDev: isUnsafe(stdDev) ? null : stdDev,
|
|
3432
|
+
sharpeRatio: isUnsafe(sharpeRatio) ? null : sharpeRatio,
|
|
3433
|
+
annualizedSharpeRatio: isUnsafe(annualizedSharpeRatio) ? null : annualizedSharpeRatio,
|
|
3434
|
+
certaintyRatio: isUnsafe(certaintyRatio) ? null : certaintyRatio,
|
|
3435
|
+
expectedYearlyReturns: isUnsafe(expectedYearlyReturns) ? null : expectedYearlyReturns,
|
|
3436
|
+
};
|
|
3437
|
+
}
|
|
3438
|
+
/**
|
|
3439
|
+
* Generates markdown report with all tick events for a strategy (View).
|
|
3440
|
+
*
|
|
3441
|
+
* @param strategyName - Strategy name
|
|
3442
|
+
* @returns Markdown formatted report with all events
|
|
3443
|
+
*/
|
|
3444
|
+
async getReport(strategyName) {
|
|
3445
|
+
const stats = await this.getData();
|
|
3446
|
+
if (stats.totalEvents === 0) {
|
|
3447
|
+
return str.newline(`# Live Trading Report: ${strategyName}`, "", "No events recorded yet.");
|
|
3448
|
+
}
|
|
3449
|
+
const header = columns.map((col) => col.label);
|
|
3450
|
+
const separator = columns.map(() => "---");
|
|
3451
|
+
const rows = this._eventList.map((event) => columns.map((col) => col.format(event)));
|
|
3452
|
+
const tableData = [header, separator, ...rows];
|
|
3453
|
+
const table = str.newline(tableData.map(row => `| ${row.join(" | ")} |`));
|
|
3454
|
+
return 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)`}`);
|
|
3270
3455
|
}
|
|
3271
3456
|
/**
|
|
3272
3457
|
* Saves strategy report to disk.
|
|
@@ -3275,7 +3460,7 @@ class ReportStorage {
|
|
|
3275
3460
|
* @param path - Directory path to save report (default: "./logs/live")
|
|
3276
3461
|
*/
|
|
3277
3462
|
async dump(strategyName, path = "./logs/live") {
|
|
3278
|
-
const markdown = this.getReport(strategyName);
|
|
3463
|
+
const markdown = await this.getReport(strategyName);
|
|
3279
3464
|
try {
|
|
3280
3465
|
const dir = join(process.cwd(), path);
|
|
3281
3466
|
await mkdir(dir, { recursive: true });
|
|
@@ -3367,6 +3552,27 @@ class LiveMarkdownService {
|
|
|
3367
3552
|
storage.addClosedEvent(data);
|
|
3368
3553
|
}
|
|
3369
3554
|
};
|
|
3555
|
+
/**
|
|
3556
|
+
* Gets statistical data from all live trading events for a strategy.
|
|
3557
|
+
* Delegates to ReportStorage.getData().
|
|
3558
|
+
*
|
|
3559
|
+
* @param strategyName - Strategy name to get data for
|
|
3560
|
+
* @returns Statistical data object with all metrics
|
|
3561
|
+
*
|
|
3562
|
+
* @example
|
|
3563
|
+
* ```typescript
|
|
3564
|
+
* const service = new LiveMarkdownService();
|
|
3565
|
+
* const stats = await service.getData("my-strategy");
|
|
3566
|
+
* console.log(stats.sharpeRatio, stats.winRate);
|
|
3567
|
+
* ```
|
|
3568
|
+
*/
|
|
3569
|
+
this.getData = async (strategyName) => {
|
|
3570
|
+
this.loggerService.log("liveMarkdownService getData", {
|
|
3571
|
+
strategyName,
|
|
3572
|
+
});
|
|
3573
|
+
const storage = this.getStorage(strategyName);
|
|
3574
|
+
return storage.getData();
|
|
3575
|
+
};
|
|
3370
3576
|
/**
|
|
3371
3577
|
* Generates markdown report with all events for a strategy.
|
|
3372
3578
|
* Delegates to ReportStorage.getReport().
|
|
@@ -4504,6 +4710,24 @@ class BacktestUtils {
|
|
|
4504
4710
|
isStopped = true;
|
|
4505
4711
|
};
|
|
4506
4712
|
};
|
|
4713
|
+
/**
|
|
4714
|
+
* Gets statistical data from all closed signals for a strategy.
|
|
4715
|
+
*
|
|
4716
|
+
* @param strategyName - Strategy name to get data for
|
|
4717
|
+
* @returns Promise resolving to statistical data object
|
|
4718
|
+
*
|
|
4719
|
+
* @example
|
|
4720
|
+
* ```typescript
|
|
4721
|
+
* const stats = await Backtest.getData("my-strategy");
|
|
4722
|
+
* console.log(stats.sharpeRatio, stats.winRate);
|
|
4723
|
+
* ```
|
|
4724
|
+
*/
|
|
4725
|
+
this.getData = async (strategyName) => {
|
|
4726
|
+
backtest$1.loggerService.info("BacktestUtils.getData", {
|
|
4727
|
+
strategyName,
|
|
4728
|
+
});
|
|
4729
|
+
return await backtest$1.backtestMarkdownService.getData(strategyName);
|
|
4730
|
+
};
|
|
4507
4731
|
/**
|
|
4508
4732
|
* Generates markdown report with all closed signals for a strategy.
|
|
4509
4733
|
*
|
|
@@ -4666,6 +4890,24 @@ class LiveUtils {
|
|
|
4666
4890
|
isStopped = true;
|
|
4667
4891
|
};
|
|
4668
4892
|
};
|
|
4893
|
+
/**
|
|
4894
|
+
* Gets statistical data from all live trading events for a strategy.
|
|
4895
|
+
*
|
|
4896
|
+
* @param strategyName - Strategy name to get data for
|
|
4897
|
+
* @returns Promise resolving to statistical data object
|
|
4898
|
+
*
|
|
4899
|
+
* @example
|
|
4900
|
+
* ```typescript
|
|
4901
|
+
* const stats = await Live.getData("my-strategy");
|
|
4902
|
+
* console.log(stats.sharpeRatio, stats.winRate);
|
|
4903
|
+
* ```
|
|
4904
|
+
*/
|
|
4905
|
+
this.getData = async (strategyName) => {
|
|
4906
|
+
backtest$1.loggerService.info("LiveUtils.getData", {
|
|
4907
|
+
strategyName,
|
|
4908
|
+
});
|
|
4909
|
+
return await backtest$1.liveMarkdownService.getData(strategyName);
|
|
4910
|
+
};
|
|
4669
4911
|
/**
|
|
4670
4912
|
* Generates markdown report with all events for a strategy.
|
|
4671
4913
|
*
|