backtest-kit 1.4.10 → 1.4.11
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 +193 -1
- package/build/index.mjs +193 -1
- package/package.json +1 -1
- package/types.d.ts +17 -1
package/build/index.cjs
CHANGED
|
@@ -7262,6 +7262,118 @@ function formatMetric(value) {
|
|
|
7262
7262
|
}
|
|
7263
7263
|
return value.toFixed(2);
|
|
7264
7264
|
}
|
|
7265
|
+
/**
|
|
7266
|
+
* Creates strategy comparison columns based on metric name.
|
|
7267
|
+
* Dynamically builds column configuration with metric-specific header.
|
|
7268
|
+
*
|
|
7269
|
+
* @param metric - Metric being optimized
|
|
7270
|
+
* @returns Array of column configurations for strategy comparison table
|
|
7271
|
+
*/
|
|
7272
|
+
function createStrategyColumns(metric) {
|
|
7273
|
+
return [
|
|
7274
|
+
{
|
|
7275
|
+
key: "rank",
|
|
7276
|
+
label: "Rank",
|
|
7277
|
+
format: (data, index) => `${index + 1}`,
|
|
7278
|
+
},
|
|
7279
|
+
{
|
|
7280
|
+
key: "strategy",
|
|
7281
|
+
label: "Strategy",
|
|
7282
|
+
format: (data) => data.strategyName,
|
|
7283
|
+
},
|
|
7284
|
+
{
|
|
7285
|
+
key: "metric",
|
|
7286
|
+
label: metric,
|
|
7287
|
+
format: (data) => formatMetric(data.metricValue),
|
|
7288
|
+
},
|
|
7289
|
+
{
|
|
7290
|
+
key: "totalSignals",
|
|
7291
|
+
label: "Total Signals",
|
|
7292
|
+
format: (data) => `${data.stats.totalSignals}`,
|
|
7293
|
+
},
|
|
7294
|
+
{
|
|
7295
|
+
key: "winRate",
|
|
7296
|
+
label: "Win Rate",
|
|
7297
|
+
format: (data) => data.stats.winRate !== null
|
|
7298
|
+
? `${data.stats.winRate.toFixed(2)}%`
|
|
7299
|
+
: "N/A",
|
|
7300
|
+
},
|
|
7301
|
+
{
|
|
7302
|
+
key: "avgPnl",
|
|
7303
|
+
label: "Avg PNL",
|
|
7304
|
+
format: (data) => data.stats.avgPnl !== null
|
|
7305
|
+
? `${data.stats.avgPnl > 0 ? "+" : ""}${data.stats.avgPnl.toFixed(2)}%`
|
|
7306
|
+
: "N/A",
|
|
7307
|
+
},
|
|
7308
|
+
{
|
|
7309
|
+
key: "totalPnl",
|
|
7310
|
+
label: "Total PNL",
|
|
7311
|
+
format: (data) => data.stats.totalPnl !== null
|
|
7312
|
+
? `${data.stats.totalPnl > 0 ? "+" : ""}${data.stats.totalPnl.toFixed(2)}%`
|
|
7313
|
+
: "N/A",
|
|
7314
|
+
},
|
|
7315
|
+
{
|
|
7316
|
+
key: "sharpeRatio",
|
|
7317
|
+
label: "Sharpe Ratio",
|
|
7318
|
+
format: (data) => data.stats.sharpeRatio !== null
|
|
7319
|
+
? `${data.stats.sharpeRatio.toFixed(3)}`
|
|
7320
|
+
: "N/A",
|
|
7321
|
+
},
|
|
7322
|
+
{
|
|
7323
|
+
key: "stdDev",
|
|
7324
|
+
label: "Std Dev",
|
|
7325
|
+
format: (data) => data.stats.stdDev !== null
|
|
7326
|
+
? `${data.stats.stdDev.toFixed(3)}%`
|
|
7327
|
+
: "N/A",
|
|
7328
|
+
},
|
|
7329
|
+
];
|
|
7330
|
+
}
|
|
7331
|
+
/**
|
|
7332
|
+
* Column configuration for PNL table.
|
|
7333
|
+
* Defines all columns for displaying closed signals across strategies.
|
|
7334
|
+
*/
|
|
7335
|
+
const pnlColumns = [
|
|
7336
|
+
{
|
|
7337
|
+
key: "strategy",
|
|
7338
|
+
label: "Strategy",
|
|
7339
|
+
format: (data) => data.strategyName,
|
|
7340
|
+
},
|
|
7341
|
+
{
|
|
7342
|
+
key: "signalId",
|
|
7343
|
+
label: "Signal ID",
|
|
7344
|
+
format: (data) => data.signalId,
|
|
7345
|
+
},
|
|
7346
|
+
{
|
|
7347
|
+
key: "symbol",
|
|
7348
|
+
label: "Symbol",
|
|
7349
|
+
format: (data) => data.symbol,
|
|
7350
|
+
},
|
|
7351
|
+
{
|
|
7352
|
+
key: "position",
|
|
7353
|
+
label: "Position",
|
|
7354
|
+
format: (data) => data.position.toUpperCase(),
|
|
7355
|
+
},
|
|
7356
|
+
{
|
|
7357
|
+
key: "pnl",
|
|
7358
|
+
label: "PNL (net)",
|
|
7359
|
+
format: (data) => `${data.pnl > 0 ? "+" : ""}${data.pnl.toFixed(2)}%`,
|
|
7360
|
+
},
|
|
7361
|
+
{
|
|
7362
|
+
key: "closeReason",
|
|
7363
|
+
label: "Close Reason",
|
|
7364
|
+
format: (data) => data.closeReason,
|
|
7365
|
+
},
|
|
7366
|
+
{
|
|
7367
|
+
key: "openTime",
|
|
7368
|
+
label: "Open Time",
|
|
7369
|
+
format: (data) => new Date(data.openTime).toISOString(),
|
|
7370
|
+
},
|
|
7371
|
+
{
|
|
7372
|
+
key: "closeTime",
|
|
7373
|
+
label: "Close Time",
|
|
7374
|
+
format: (data) => new Date(data.closeTime).toISOString(),
|
|
7375
|
+
},
|
|
7376
|
+
];
|
|
7265
7377
|
/**
|
|
7266
7378
|
* Storage class for accumulating walker results.
|
|
7267
7379
|
* Maintains a list of all strategy results and provides methods to generate reports.
|
|
@@ -7274,9 +7386,12 @@ let ReportStorage$1 = class ReportStorage {
|
|
|
7274
7386
|
this._bestStats = null;
|
|
7275
7387
|
this._bestMetric = null;
|
|
7276
7388
|
this._bestStrategy = null;
|
|
7389
|
+
/** All strategy results for comparison table */
|
|
7390
|
+
this._strategyResults = [];
|
|
7277
7391
|
}
|
|
7278
7392
|
/**
|
|
7279
7393
|
* Adds a strategy result to the storage.
|
|
7394
|
+
* Updates best strategy tracking and accumulates result for comparison table.
|
|
7280
7395
|
*
|
|
7281
7396
|
* @param data - Walker contract with strategy result
|
|
7282
7397
|
*/
|
|
@@ -7290,6 +7405,12 @@ let ReportStorage$1 = class ReportStorage {
|
|
|
7290
7405
|
if (data.strategyName === data.bestStrategy) {
|
|
7291
7406
|
this._bestStats = data.stats;
|
|
7292
7407
|
}
|
|
7408
|
+
// Add strategy result to comparison list
|
|
7409
|
+
this._strategyResults.push({
|
|
7410
|
+
strategyName: data.strategyName,
|
|
7411
|
+
stats: data.stats,
|
|
7412
|
+
metricValue: data.metricValue,
|
|
7413
|
+
});
|
|
7293
7414
|
}
|
|
7294
7415
|
/**
|
|
7295
7416
|
* Calculates walker results from strategy results.
|
|
@@ -7314,10 +7435,79 @@ let ReportStorage$1 = class ReportStorage {
|
|
|
7314
7435
|
bestStrategy: this._bestStrategy,
|
|
7315
7436
|
bestMetric: this._bestMetric,
|
|
7316
7437
|
bestStats: this._bestStats,
|
|
7438
|
+
strategyResults: this._strategyResults,
|
|
7317
7439
|
};
|
|
7318
7440
|
}
|
|
7441
|
+
/**
|
|
7442
|
+
* Generates comparison table for top N strategies (View).
|
|
7443
|
+
* Sorts strategies by metric value and formats as markdown table.
|
|
7444
|
+
*
|
|
7445
|
+
* @param metric - Metric being optimized
|
|
7446
|
+
* @param topN - Number of top strategies to include (default: 10)
|
|
7447
|
+
* @returns Markdown formatted comparison table
|
|
7448
|
+
*/
|
|
7449
|
+
getComparisonTable(metric, topN = 10) {
|
|
7450
|
+
if (this._strategyResults.length === 0) {
|
|
7451
|
+
return "No strategy results available.";
|
|
7452
|
+
}
|
|
7453
|
+
// Sort strategies by metric value (descending)
|
|
7454
|
+
const sortedResults = [...this._strategyResults].sort((a, b) => {
|
|
7455
|
+
const aValue = a.metricValue ?? -Infinity;
|
|
7456
|
+
const bValue = b.metricValue ?? -Infinity;
|
|
7457
|
+
return bValue - aValue;
|
|
7458
|
+
});
|
|
7459
|
+
// Take top N strategies
|
|
7460
|
+
const topStrategies = sortedResults.slice(0, topN);
|
|
7461
|
+
// Get columns configuration
|
|
7462
|
+
const columns = createStrategyColumns(metric);
|
|
7463
|
+
// Build table header
|
|
7464
|
+
const header = columns.map((col) => col.label);
|
|
7465
|
+
const separator = columns.map(() => "---");
|
|
7466
|
+
// Build table rows
|
|
7467
|
+
const rows = topStrategies.map((result, index) => columns.map((col) => col.format(result, index)));
|
|
7468
|
+
const tableData = [header, separator, ...rows];
|
|
7469
|
+
return functoolsKit.str.newline(tableData.map((row) => `| ${row.join(" | ")} |`));
|
|
7470
|
+
}
|
|
7471
|
+
/**
|
|
7472
|
+
* Generates PNL table showing all closed signals across all strategies (View).
|
|
7473
|
+
* Collects all signals from all strategies and formats as markdown table.
|
|
7474
|
+
*
|
|
7475
|
+
* @returns Markdown formatted PNL table
|
|
7476
|
+
*/
|
|
7477
|
+
getPnlTable() {
|
|
7478
|
+
if (this._strategyResults.length === 0) {
|
|
7479
|
+
return "No strategy results available.";
|
|
7480
|
+
}
|
|
7481
|
+
// Collect all closed signals from all strategies
|
|
7482
|
+
const allSignals = [];
|
|
7483
|
+
for (const result of this._strategyResults) {
|
|
7484
|
+
for (const signal of result.stats.signalList) {
|
|
7485
|
+
allSignals.push({
|
|
7486
|
+
strategyName: result.strategyName,
|
|
7487
|
+
signalId: signal.signal.id,
|
|
7488
|
+
symbol: signal.signal.symbol,
|
|
7489
|
+
position: signal.signal.position,
|
|
7490
|
+
pnl: signal.pnl.pnlPercentage,
|
|
7491
|
+
closeReason: signal.closeReason,
|
|
7492
|
+
openTime: signal.signal.pendingAt,
|
|
7493
|
+
closeTime: signal.closeTimestamp,
|
|
7494
|
+
});
|
|
7495
|
+
}
|
|
7496
|
+
}
|
|
7497
|
+
if (allSignals.length === 0) {
|
|
7498
|
+
return "No closed signals available.";
|
|
7499
|
+
}
|
|
7500
|
+
// Build table header
|
|
7501
|
+
const header = pnlColumns.map((col) => col.label);
|
|
7502
|
+
const separator = pnlColumns.map(() => "---");
|
|
7503
|
+
// Build table rows
|
|
7504
|
+
const rows = allSignals.map((signal) => pnlColumns.map((col) => col.format(signal)));
|
|
7505
|
+
const tableData = [header, separator, ...rows];
|
|
7506
|
+
return functoolsKit.str.newline(tableData.map((row) => `| ${row.join(" | ")} |`));
|
|
7507
|
+
}
|
|
7319
7508
|
/**
|
|
7320
7509
|
* Generates markdown report with all strategy results (View).
|
|
7510
|
+
* Includes best strategy summary, comparison table, and PNL table.
|
|
7321
7511
|
*
|
|
7322
7512
|
* @param symbol - Trading symbol
|
|
7323
7513
|
* @param metric - Metric being optimized
|
|
@@ -7326,7 +7516,9 @@ let ReportStorage$1 = class ReportStorage {
|
|
|
7326
7516
|
*/
|
|
7327
7517
|
async getReport(symbol, metric, context) {
|
|
7328
7518
|
const results = await this.getData(symbol, metric, context);
|
|
7329
|
-
|
|
7519
|
+
// Get total signals for best strategy
|
|
7520
|
+
const bestStrategySignals = results.bestStats?.totalSignals ?? 0;
|
|
7521
|
+
return functoolsKit.str.newline(`# Walker Comparison Report: ${results.walkerName}`, "", `**Symbol:** ${results.symbol}`, `**Exchange:** ${results.exchangeName}`, `**Frame:** ${results.frameName}`, `**Optimization Metric:** ${results.metric}`, `**Strategies Tested:** ${results.totalStrategies}`, "", `## Best Strategy: ${results.bestStrategy}`, "", `**Best ${results.metric}:** ${formatMetric(results.bestMetric)}`, `**Total Signals:** ${bestStrategySignals}`, "", "## Top Strategies Comparison", "", this.getComparisonTable(metric, 10), "", "## All Signals (PNL Table)", "", this.getPnlTable(), "", "**Note:** Higher values are better for all metrics except Standard Deviation (lower is better).");
|
|
7330
7522
|
}
|
|
7331
7523
|
/**
|
|
7332
7524
|
* Saves walker report to disk.
|
package/build/index.mjs
CHANGED
|
@@ -7260,6 +7260,118 @@ function formatMetric(value) {
|
|
|
7260
7260
|
}
|
|
7261
7261
|
return value.toFixed(2);
|
|
7262
7262
|
}
|
|
7263
|
+
/**
|
|
7264
|
+
* Creates strategy comparison columns based on metric name.
|
|
7265
|
+
* Dynamically builds column configuration with metric-specific header.
|
|
7266
|
+
*
|
|
7267
|
+
* @param metric - Metric being optimized
|
|
7268
|
+
* @returns Array of column configurations for strategy comparison table
|
|
7269
|
+
*/
|
|
7270
|
+
function createStrategyColumns(metric) {
|
|
7271
|
+
return [
|
|
7272
|
+
{
|
|
7273
|
+
key: "rank",
|
|
7274
|
+
label: "Rank",
|
|
7275
|
+
format: (data, index) => `${index + 1}`,
|
|
7276
|
+
},
|
|
7277
|
+
{
|
|
7278
|
+
key: "strategy",
|
|
7279
|
+
label: "Strategy",
|
|
7280
|
+
format: (data) => data.strategyName,
|
|
7281
|
+
},
|
|
7282
|
+
{
|
|
7283
|
+
key: "metric",
|
|
7284
|
+
label: metric,
|
|
7285
|
+
format: (data) => formatMetric(data.metricValue),
|
|
7286
|
+
},
|
|
7287
|
+
{
|
|
7288
|
+
key: "totalSignals",
|
|
7289
|
+
label: "Total Signals",
|
|
7290
|
+
format: (data) => `${data.stats.totalSignals}`,
|
|
7291
|
+
},
|
|
7292
|
+
{
|
|
7293
|
+
key: "winRate",
|
|
7294
|
+
label: "Win Rate",
|
|
7295
|
+
format: (data) => data.stats.winRate !== null
|
|
7296
|
+
? `${data.stats.winRate.toFixed(2)}%`
|
|
7297
|
+
: "N/A",
|
|
7298
|
+
},
|
|
7299
|
+
{
|
|
7300
|
+
key: "avgPnl",
|
|
7301
|
+
label: "Avg PNL",
|
|
7302
|
+
format: (data) => data.stats.avgPnl !== null
|
|
7303
|
+
? `${data.stats.avgPnl > 0 ? "+" : ""}${data.stats.avgPnl.toFixed(2)}%`
|
|
7304
|
+
: "N/A",
|
|
7305
|
+
},
|
|
7306
|
+
{
|
|
7307
|
+
key: "totalPnl",
|
|
7308
|
+
label: "Total PNL",
|
|
7309
|
+
format: (data) => data.stats.totalPnl !== null
|
|
7310
|
+
? `${data.stats.totalPnl > 0 ? "+" : ""}${data.stats.totalPnl.toFixed(2)}%`
|
|
7311
|
+
: "N/A",
|
|
7312
|
+
},
|
|
7313
|
+
{
|
|
7314
|
+
key: "sharpeRatio",
|
|
7315
|
+
label: "Sharpe Ratio",
|
|
7316
|
+
format: (data) => data.stats.sharpeRatio !== null
|
|
7317
|
+
? `${data.stats.sharpeRatio.toFixed(3)}`
|
|
7318
|
+
: "N/A",
|
|
7319
|
+
},
|
|
7320
|
+
{
|
|
7321
|
+
key: "stdDev",
|
|
7322
|
+
label: "Std Dev",
|
|
7323
|
+
format: (data) => data.stats.stdDev !== null
|
|
7324
|
+
? `${data.stats.stdDev.toFixed(3)}%`
|
|
7325
|
+
: "N/A",
|
|
7326
|
+
},
|
|
7327
|
+
];
|
|
7328
|
+
}
|
|
7329
|
+
/**
|
|
7330
|
+
* Column configuration for PNL table.
|
|
7331
|
+
* Defines all columns for displaying closed signals across strategies.
|
|
7332
|
+
*/
|
|
7333
|
+
const pnlColumns = [
|
|
7334
|
+
{
|
|
7335
|
+
key: "strategy",
|
|
7336
|
+
label: "Strategy",
|
|
7337
|
+
format: (data) => data.strategyName,
|
|
7338
|
+
},
|
|
7339
|
+
{
|
|
7340
|
+
key: "signalId",
|
|
7341
|
+
label: "Signal ID",
|
|
7342
|
+
format: (data) => data.signalId,
|
|
7343
|
+
},
|
|
7344
|
+
{
|
|
7345
|
+
key: "symbol",
|
|
7346
|
+
label: "Symbol",
|
|
7347
|
+
format: (data) => data.symbol,
|
|
7348
|
+
},
|
|
7349
|
+
{
|
|
7350
|
+
key: "position",
|
|
7351
|
+
label: "Position",
|
|
7352
|
+
format: (data) => data.position.toUpperCase(),
|
|
7353
|
+
},
|
|
7354
|
+
{
|
|
7355
|
+
key: "pnl",
|
|
7356
|
+
label: "PNL (net)",
|
|
7357
|
+
format: (data) => `${data.pnl > 0 ? "+" : ""}${data.pnl.toFixed(2)}%`,
|
|
7358
|
+
},
|
|
7359
|
+
{
|
|
7360
|
+
key: "closeReason",
|
|
7361
|
+
label: "Close Reason",
|
|
7362
|
+
format: (data) => data.closeReason,
|
|
7363
|
+
},
|
|
7364
|
+
{
|
|
7365
|
+
key: "openTime",
|
|
7366
|
+
label: "Open Time",
|
|
7367
|
+
format: (data) => new Date(data.openTime).toISOString(),
|
|
7368
|
+
},
|
|
7369
|
+
{
|
|
7370
|
+
key: "closeTime",
|
|
7371
|
+
label: "Close Time",
|
|
7372
|
+
format: (data) => new Date(data.closeTime).toISOString(),
|
|
7373
|
+
},
|
|
7374
|
+
];
|
|
7263
7375
|
/**
|
|
7264
7376
|
* Storage class for accumulating walker results.
|
|
7265
7377
|
* Maintains a list of all strategy results and provides methods to generate reports.
|
|
@@ -7272,9 +7384,12 @@ let ReportStorage$1 = class ReportStorage {
|
|
|
7272
7384
|
this._bestStats = null;
|
|
7273
7385
|
this._bestMetric = null;
|
|
7274
7386
|
this._bestStrategy = null;
|
|
7387
|
+
/** All strategy results for comparison table */
|
|
7388
|
+
this._strategyResults = [];
|
|
7275
7389
|
}
|
|
7276
7390
|
/**
|
|
7277
7391
|
* Adds a strategy result to the storage.
|
|
7392
|
+
* Updates best strategy tracking and accumulates result for comparison table.
|
|
7278
7393
|
*
|
|
7279
7394
|
* @param data - Walker contract with strategy result
|
|
7280
7395
|
*/
|
|
@@ -7288,6 +7403,12 @@ let ReportStorage$1 = class ReportStorage {
|
|
|
7288
7403
|
if (data.strategyName === data.bestStrategy) {
|
|
7289
7404
|
this._bestStats = data.stats;
|
|
7290
7405
|
}
|
|
7406
|
+
// Add strategy result to comparison list
|
|
7407
|
+
this._strategyResults.push({
|
|
7408
|
+
strategyName: data.strategyName,
|
|
7409
|
+
stats: data.stats,
|
|
7410
|
+
metricValue: data.metricValue,
|
|
7411
|
+
});
|
|
7291
7412
|
}
|
|
7292
7413
|
/**
|
|
7293
7414
|
* Calculates walker results from strategy results.
|
|
@@ -7312,10 +7433,79 @@ let ReportStorage$1 = class ReportStorage {
|
|
|
7312
7433
|
bestStrategy: this._bestStrategy,
|
|
7313
7434
|
bestMetric: this._bestMetric,
|
|
7314
7435
|
bestStats: this._bestStats,
|
|
7436
|
+
strategyResults: this._strategyResults,
|
|
7315
7437
|
};
|
|
7316
7438
|
}
|
|
7439
|
+
/**
|
|
7440
|
+
* Generates comparison table for top N strategies (View).
|
|
7441
|
+
* Sorts strategies by metric value and formats as markdown table.
|
|
7442
|
+
*
|
|
7443
|
+
* @param metric - Metric being optimized
|
|
7444
|
+
* @param topN - Number of top strategies to include (default: 10)
|
|
7445
|
+
* @returns Markdown formatted comparison table
|
|
7446
|
+
*/
|
|
7447
|
+
getComparisonTable(metric, topN = 10) {
|
|
7448
|
+
if (this._strategyResults.length === 0) {
|
|
7449
|
+
return "No strategy results available.";
|
|
7450
|
+
}
|
|
7451
|
+
// Sort strategies by metric value (descending)
|
|
7452
|
+
const sortedResults = [...this._strategyResults].sort((a, b) => {
|
|
7453
|
+
const aValue = a.metricValue ?? -Infinity;
|
|
7454
|
+
const bValue = b.metricValue ?? -Infinity;
|
|
7455
|
+
return bValue - aValue;
|
|
7456
|
+
});
|
|
7457
|
+
// Take top N strategies
|
|
7458
|
+
const topStrategies = sortedResults.slice(0, topN);
|
|
7459
|
+
// Get columns configuration
|
|
7460
|
+
const columns = createStrategyColumns(metric);
|
|
7461
|
+
// Build table header
|
|
7462
|
+
const header = columns.map((col) => col.label);
|
|
7463
|
+
const separator = columns.map(() => "---");
|
|
7464
|
+
// Build table rows
|
|
7465
|
+
const rows = topStrategies.map((result, index) => columns.map((col) => col.format(result, index)));
|
|
7466
|
+
const tableData = [header, separator, ...rows];
|
|
7467
|
+
return str.newline(tableData.map((row) => `| ${row.join(" | ")} |`));
|
|
7468
|
+
}
|
|
7469
|
+
/**
|
|
7470
|
+
* Generates PNL table showing all closed signals across all strategies (View).
|
|
7471
|
+
* Collects all signals from all strategies and formats as markdown table.
|
|
7472
|
+
*
|
|
7473
|
+
* @returns Markdown formatted PNL table
|
|
7474
|
+
*/
|
|
7475
|
+
getPnlTable() {
|
|
7476
|
+
if (this._strategyResults.length === 0) {
|
|
7477
|
+
return "No strategy results available.";
|
|
7478
|
+
}
|
|
7479
|
+
// Collect all closed signals from all strategies
|
|
7480
|
+
const allSignals = [];
|
|
7481
|
+
for (const result of this._strategyResults) {
|
|
7482
|
+
for (const signal of result.stats.signalList) {
|
|
7483
|
+
allSignals.push({
|
|
7484
|
+
strategyName: result.strategyName,
|
|
7485
|
+
signalId: signal.signal.id,
|
|
7486
|
+
symbol: signal.signal.symbol,
|
|
7487
|
+
position: signal.signal.position,
|
|
7488
|
+
pnl: signal.pnl.pnlPercentage,
|
|
7489
|
+
closeReason: signal.closeReason,
|
|
7490
|
+
openTime: signal.signal.pendingAt,
|
|
7491
|
+
closeTime: signal.closeTimestamp,
|
|
7492
|
+
});
|
|
7493
|
+
}
|
|
7494
|
+
}
|
|
7495
|
+
if (allSignals.length === 0) {
|
|
7496
|
+
return "No closed signals available.";
|
|
7497
|
+
}
|
|
7498
|
+
// Build table header
|
|
7499
|
+
const header = pnlColumns.map((col) => col.label);
|
|
7500
|
+
const separator = pnlColumns.map(() => "---");
|
|
7501
|
+
// Build table rows
|
|
7502
|
+
const rows = allSignals.map((signal) => pnlColumns.map((col) => col.format(signal)));
|
|
7503
|
+
const tableData = [header, separator, ...rows];
|
|
7504
|
+
return str.newline(tableData.map((row) => `| ${row.join(" | ")} |`));
|
|
7505
|
+
}
|
|
7317
7506
|
/**
|
|
7318
7507
|
* Generates markdown report with all strategy results (View).
|
|
7508
|
+
* Includes best strategy summary, comparison table, and PNL table.
|
|
7319
7509
|
*
|
|
7320
7510
|
* @param symbol - Trading symbol
|
|
7321
7511
|
* @param metric - Metric being optimized
|
|
@@ -7324,7 +7514,9 @@ let ReportStorage$1 = class ReportStorage {
|
|
|
7324
7514
|
*/
|
|
7325
7515
|
async getReport(symbol, metric, context) {
|
|
7326
7516
|
const results = await this.getData(symbol, metric, context);
|
|
7327
|
-
|
|
7517
|
+
// Get total signals for best strategy
|
|
7518
|
+
const bestStrategySignals = results.bestStats?.totalSignals ?? 0;
|
|
7519
|
+
return str.newline(`# Walker Comparison Report: ${results.walkerName}`, "", `**Symbol:** ${results.symbol}`, `**Exchange:** ${results.exchangeName}`, `**Frame:** ${results.frameName}`, `**Optimization Metric:** ${results.metric}`, `**Strategies Tested:** ${results.totalStrategies}`, "", `## Best Strategy: ${results.bestStrategy}`, "", `**Best ${results.metric}:** ${formatMetric(results.bestMetric)}`, `**Total Signals:** ${bestStrategySignals}`, "", "## Top Strategies Comparison", "", this.getComparisonTable(metric, 10), "", "## All Signals (PNL Table)", "", this.getPnlTable(), "", "**Note:** Higher values are better for all metrics except Standard Deviation (lower is better).");
|
|
7328
7520
|
}
|
|
7329
7521
|
/**
|
|
7330
7522
|
* Saves walker report to disk.
|
package/package.json
CHANGED
package/types.d.ts
CHANGED
|
@@ -4258,8 +4258,24 @@ declare class PerformanceMarkdownService {
|
|
|
4258
4258
|
* Alias for walker statistics result interface.
|
|
4259
4259
|
* Used for clarity in markdown service context.
|
|
4260
4260
|
*
|
|
4261
|
+
* Extends IWalkerResults with additional strategy comparison data.
|
|
4261
4262
|
*/
|
|
4262
|
-
|
|
4263
|
+
interface WalkerStatistics extends IWalkerResults {
|
|
4264
|
+
/** Array of all strategy results for comparison and analysis */
|
|
4265
|
+
strategyResults: IStrategyResult[];
|
|
4266
|
+
}
|
|
4267
|
+
/**
|
|
4268
|
+
* Strategy result entry for comparison table.
|
|
4269
|
+
* Contains strategy name, full statistics, and metric value for ranking.
|
|
4270
|
+
*/
|
|
4271
|
+
interface IStrategyResult {
|
|
4272
|
+
/** Strategy name */
|
|
4273
|
+
strategyName: StrategyName;
|
|
4274
|
+
/** Complete backtest statistics for this strategy */
|
|
4275
|
+
stats: BacktestStatistics;
|
|
4276
|
+
/** Value of the optimization metric (null if invalid) */
|
|
4277
|
+
metricValue: number | null;
|
|
4278
|
+
}
|
|
4263
4279
|
/**
|
|
4264
4280
|
* Service for generating and saving walker markdown reports.
|
|
4265
4281
|
*
|