backtest-kit 1.5.17 → 1.5.18
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 +225 -61
- package/build/index.mjs +225 -61
- package/package.json +1 -1
- package/types.d.ts +10 -0
package/build/index.mjs
CHANGED
|
@@ -109,6 +109,14 @@ const GLOBAL_CONFIG = {
|
|
|
109
109
|
* Example: 3 candles = 12 points (use average), 5 candles = 20 points (use median)
|
|
110
110
|
*/
|
|
111
111
|
CC_GET_CANDLES_MIN_CANDLES_FOR_MEDIAN: 5,
|
|
112
|
+
/**
|
|
113
|
+
* Controls visibility of signal notes in markdown report tables.
|
|
114
|
+
* When enabled, the "Note" column will be displayed in all markdown reports
|
|
115
|
+
* (backtest, live, schedule, risk, etc.)
|
|
116
|
+
*
|
|
117
|
+
* Default: false (notes are hidden to reduce table width and improve readability)
|
|
118
|
+
*/
|
|
119
|
+
CC_REPORT_SHOW_SIGNAL_NOTE: false,
|
|
112
120
|
};
|
|
113
121
|
const DEFAULT_CONFIG = Object.freeze({ ...GLOBAL_CONFIG });
|
|
114
122
|
|
|
@@ -6030,46 +6038,54 @@ function isUnsafe$3(value) {
|
|
|
6030
6038
|
}
|
|
6031
6039
|
return false;
|
|
6032
6040
|
}
|
|
6033
|
-
const columns$
|
|
6041
|
+
const columns$6 = [
|
|
6034
6042
|
{
|
|
6035
6043
|
key: "signalId",
|
|
6036
6044
|
label: "Signal ID",
|
|
6037
6045
|
format: (data) => data.signal.id,
|
|
6046
|
+
isVisible: () => true,
|
|
6038
6047
|
},
|
|
6039
6048
|
{
|
|
6040
6049
|
key: "symbol",
|
|
6041
6050
|
label: "Symbol",
|
|
6042
6051
|
format: (data) => data.signal.symbol,
|
|
6052
|
+
isVisible: () => true,
|
|
6043
6053
|
},
|
|
6044
6054
|
{
|
|
6045
6055
|
key: "position",
|
|
6046
6056
|
label: "Position",
|
|
6047
6057
|
format: (data) => data.signal.position.toUpperCase(),
|
|
6058
|
+
isVisible: () => true,
|
|
6048
6059
|
},
|
|
6049
6060
|
{
|
|
6050
6061
|
key: "note",
|
|
6051
6062
|
label: "Note",
|
|
6052
6063
|
format: (data) => toPlainString(data.signal.note ?? "N/A"),
|
|
6064
|
+
isVisible: () => GLOBAL_CONFIG.CC_REPORT_SHOW_SIGNAL_NOTE,
|
|
6053
6065
|
},
|
|
6054
6066
|
{
|
|
6055
6067
|
key: "openPrice",
|
|
6056
6068
|
label: "Open Price",
|
|
6057
6069
|
format: (data) => `${data.signal.priceOpen.toFixed(8)} USD`,
|
|
6070
|
+
isVisible: () => true,
|
|
6058
6071
|
},
|
|
6059
6072
|
{
|
|
6060
6073
|
key: "closePrice",
|
|
6061
6074
|
label: "Close Price",
|
|
6062
6075
|
format: (data) => `${data.currentPrice.toFixed(8)} USD`,
|
|
6076
|
+
isVisible: () => true,
|
|
6063
6077
|
},
|
|
6064
6078
|
{
|
|
6065
6079
|
key: "takeProfit",
|
|
6066
6080
|
label: "Take Profit",
|
|
6067
6081
|
format: (data) => `${data.signal.priceTakeProfit.toFixed(8)} USD`,
|
|
6082
|
+
isVisible: () => true,
|
|
6068
6083
|
},
|
|
6069
6084
|
{
|
|
6070
6085
|
key: "stopLoss",
|
|
6071
6086
|
label: "Stop Loss",
|
|
6072
6087
|
format: (data) => `${data.signal.priceStopLoss.toFixed(8)} USD`,
|
|
6088
|
+
isVisible: () => true,
|
|
6073
6089
|
},
|
|
6074
6090
|
{
|
|
6075
6091
|
key: "pnl",
|
|
@@ -6078,11 +6094,13 @@ const columns$5 = [
|
|
|
6078
6094
|
const pnlPercentage = data.pnl.pnlPercentage;
|
|
6079
6095
|
return `${pnlPercentage > 0 ? "+" : ""}${pnlPercentage.toFixed(2)}%`;
|
|
6080
6096
|
},
|
|
6097
|
+
isVisible: () => true,
|
|
6081
6098
|
},
|
|
6082
6099
|
{
|
|
6083
6100
|
key: "closeReason",
|
|
6084
6101
|
label: "Close Reason",
|
|
6085
6102
|
format: (data) => data.closeReason,
|
|
6103
|
+
isVisible: () => true,
|
|
6086
6104
|
},
|
|
6087
6105
|
{
|
|
6088
6106
|
key: "duration",
|
|
@@ -6092,16 +6110,19 @@ const columns$5 = [
|
|
|
6092
6110
|
const durationMin = Math.round(durationMs / 60000);
|
|
6093
6111
|
return `${durationMin}`;
|
|
6094
6112
|
},
|
|
6113
|
+
isVisible: () => true,
|
|
6095
6114
|
},
|
|
6096
6115
|
{
|
|
6097
6116
|
key: "openTimestamp",
|
|
6098
6117
|
label: "Open Time",
|
|
6099
6118
|
format: (data) => new Date(data.signal.pendingAt).toISOString(),
|
|
6119
|
+
isVisible: () => true,
|
|
6100
6120
|
},
|
|
6101
6121
|
{
|
|
6102
6122
|
key: "closeTimestamp",
|
|
6103
6123
|
label: "Close Time",
|
|
6104
6124
|
format: (data) => new Date(data.closeTimestamp).toISOString(),
|
|
6125
|
+
isVisible: () => true,
|
|
6105
6126
|
},
|
|
6106
6127
|
];
|
|
6107
6128
|
/** Maximum number of signals to store in backtest reports */
|
|
@@ -6208,9 +6229,10 @@ let ReportStorage$5 = class ReportStorage {
|
|
|
6208
6229
|
"No signals closed yet."
|
|
6209
6230
|
].join("\n");
|
|
6210
6231
|
}
|
|
6211
|
-
const
|
|
6212
|
-
const
|
|
6213
|
-
const
|
|
6232
|
+
const visibleColumns = columns$6.filter((col) => col.isVisible());
|
|
6233
|
+
const header = visibleColumns.map((col) => col.label);
|
|
6234
|
+
const separator = visibleColumns.map(() => "---");
|
|
6235
|
+
const rows = this._signalList.map((closedSignal) => visibleColumns.map((col) => col.format(closedSignal)));
|
|
6214
6236
|
const tableData = [header, separator, ...rows];
|
|
6215
6237
|
const table = tableData.map(row => `| ${row.join(" | ")} |`).join("\n");
|
|
6216
6238
|
return [
|
|
@@ -6457,46 +6479,54 @@ function isUnsafe$2(value) {
|
|
|
6457
6479
|
}
|
|
6458
6480
|
return false;
|
|
6459
6481
|
}
|
|
6460
|
-
const columns$
|
|
6482
|
+
const columns$5 = [
|
|
6461
6483
|
{
|
|
6462
6484
|
key: "timestamp",
|
|
6463
6485
|
label: "Timestamp",
|
|
6464
6486
|
format: (data) => new Date(data.timestamp).toISOString(),
|
|
6487
|
+
isVisible: () => true,
|
|
6465
6488
|
},
|
|
6466
6489
|
{
|
|
6467
6490
|
key: "action",
|
|
6468
6491
|
label: "Action",
|
|
6469
6492
|
format: (data) => data.action.toUpperCase(),
|
|
6493
|
+
isVisible: () => true,
|
|
6470
6494
|
},
|
|
6471
6495
|
{
|
|
6472
6496
|
key: "symbol",
|
|
6473
6497
|
label: "Symbol",
|
|
6474
6498
|
format: (data) => data.symbol ?? "N/A",
|
|
6499
|
+
isVisible: () => true,
|
|
6475
6500
|
},
|
|
6476
6501
|
{
|
|
6477
6502
|
key: "signalId",
|
|
6478
6503
|
label: "Signal ID",
|
|
6479
6504
|
format: (data) => data.signalId ?? "N/A",
|
|
6505
|
+
isVisible: () => true,
|
|
6480
6506
|
},
|
|
6481
6507
|
{
|
|
6482
6508
|
key: "position",
|
|
6483
6509
|
label: "Position",
|
|
6484
6510
|
format: (data) => data.position?.toUpperCase() ?? "N/A",
|
|
6511
|
+
isVisible: () => true,
|
|
6485
6512
|
},
|
|
6486
6513
|
{
|
|
6487
6514
|
key: "note",
|
|
6488
6515
|
label: "Note",
|
|
6489
6516
|
format: (data) => toPlainString(data.note ?? "N/A"),
|
|
6517
|
+
isVisible: () => GLOBAL_CONFIG.CC_REPORT_SHOW_SIGNAL_NOTE,
|
|
6490
6518
|
},
|
|
6491
6519
|
{
|
|
6492
6520
|
key: "currentPrice",
|
|
6493
6521
|
label: "Current Price",
|
|
6494
6522
|
format: (data) => `${data.currentPrice.toFixed(8)} USD`,
|
|
6523
|
+
isVisible: () => true,
|
|
6495
6524
|
},
|
|
6496
6525
|
{
|
|
6497
6526
|
key: "openPrice",
|
|
6498
6527
|
label: "Open Price",
|
|
6499
6528
|
format: (data) => data.openPrice !== undefined ? `${data.openPrice.toFixed(8)} USD` : "N/A",
|
|
6529
|
+
isVisible: () => true,
|
|
6500
6530
|
},
|
|
6501
6531
|
{
|
|
6502
6532
|
key: "takeProfit",
|
|
@@ -6504,21 +6534,25 @@ const columns$4 = [
|
|
|
6504
6534
|
format: (data) => data.takeProfit !== undefined
|
|
6505
6535
|
? `${data.takeProfit.toFixed(8)} USD`
|
|
6506
6536
|
: "N/A",
|
|
6537
|
+
isVisible: () => true,
|
|
6507
6538
|
},
|
|
6508
6539
|
{
|
|
6509
6540
|
key: "stopLoss",
|
|
6510
6541
|
label: "Stop Loss",
|
|
6511
6542
|
format: (data) => data.stopLoss !== undefined ? `${data.stopLoss.toFixed(8)} USD` : "N/A",
|
|
6543
|
+
isVisible: () => true,
|
|
6512
6544
|
},
|
|
6513
6545
|
{
|
|
6514
6546
|
key: "percentTp",
|
|
6515
6547
|
label: "% to TP",
|
|
6516
6548
|
format: (data) => data.percentTp !== undefined ? `${data.percentTp.toFixed(2)}%` : "N/A",
|
|
6549
|
+
isVisible: () => true,
|
|
6517
6550
|
},
|
|
6518
6551
|
{
|
|
6519
6552
|
key: "percentSl",
|
|
6520
6553
|
label: "% to SL",
|
|
6521
6554
|
format: (data) => data.percentSl !== undefined ? `${data.percentSl.toFixed(2)}%` : "N/A",
|
|
6555
|
+
isVisible: () => true,
|
|
6522
6556
|
},
|
|
6523
6557
|
{
|
|
6524
6558
|
key: "pnl",
|
|
@@ -6528,16 +6562,19 @@ const columns$4 = [
|
|
|
6528
6562
|
return "N/A";
|
|
6529
6563
|
return `${data.pnl > 0 ? "+" : ""}${data.pnl.toFixed(2)}%`;
|
|
6530
6564
|
},
|
|
6565
|
+
isVisible: () => true,
|
|
6531
6566
|
},
|
|
6532
6567
|
{
|
|
6533
6568
|
key: "closeReason",
|
|
6534
6569
|
label: "Close Reason",
|
|
6535
6570
|
format: (data) => data.closeReason ?? "N/A",
|
|
6571
|
+
isVisible: () => true,
|
|
6536
6572
|
},
|
|
6537
6573
|
{
|
|
6538
6574
|
key: "duration",
|
|
6539
6575
|
label: "Duration (min)",
|
|
6540
6576
|
format: (data) => data.duration !== undefined ? `${data.duration}` : "N/A",
|
|
6577
|
+
isVisible: () => true,
|
|
6541
6578
|
},
|
|
6542
6579
|
];
|
|
6543
6580
|
/** Maximum number of events to store in live trading reports */
|
|
@@ -6762,9 +6799,10 @@ let ReportStorage$4 = class ReportStorage {
|
|
|
6762
6799
|
"No events recorded yet."
|
|
6763
6800
|
].join("\n");
|
|
6764
6801
|
}
|
|
6765
|
-
const
|
|
6766
|
-
const
|
|
6767
|
-
const
|
|
6802
|
+
const visibleColumns = columns$5.filter((col) => col.isVisible());
|
|
6803
|
+
const header = visibleColumns.map((col) => col.label);
|
|
6804
|
+
const separator = visibleColumns.map(() => "---");
|
|
6805
|
+
const rows = this._eventList.map((event) => visibleColumns.map((col) => col.format(event)));
|
|
6768
6806
|
const tableData = [header, separator, ...rows];
|
|
6769
6807
|
const table = tableData.map(row => `| ${row.join(" | ")} |`).join("\n");
|
|
6770
6808
|
return [
|
|
@@ -7006,61 +7044,72 @@ class LiveMarkdownService {
|
|
|
7006
7044
|
}
|
|
7007
7045
|
}
|
|
7008
7046
|
|
|
7009
|
-
const columns$
|
|
7047
|
+
const columns$4 = [
|
|
7010
7048
|
{
|
|
7011
7049
|
key: "timestamp",
|
|
7012
7050
|
label: "Timestamp",
|
|
7013
7051
|
format: (data) => new Date(data.timestamp).toISOString(),
|
|
7052
|
+
isVisible: () => true,
|
|
7014
7053
|
},
|
|
7015
7054
|
{
|
|
7016
7055
|
key: "action",
|
|
7017
7056
|
label: "Action",
|
|
7018
7057
|
format: (data) => data.action.toUpperCase(),
|
|
7058
|
+
isVisible: () => true,
|
|
7019
7059
|
},
|
|
7020
7060
|
{
|
|
7021
7061
|
key: "symbol",
|
|
7022
7062
|
label: "Symbol",
|
|
7023
7063
|
format: (data) => data.symbol,
|
|
7064
|
+
isVisible: () => true,
|
|
7024
7065
|
},
|
|
7025
7066
|
{
|
|
7026
7067
|
key: "signalId",
|
|
7027
7068
|
label: "Signal ID",
|
|
7028
7069
|
format: (data) => data.signalId,
|
|
7070
|
+
isVisible: () => true,
|
|
7029
7071
|
},
|
|
7030
7072
|
{
|
|
7031
7073
|
key: "position",
|
|
7032
7074
|
label: "Position",
|
|
7033
7075
|
format: (data) => data.position.toUpperCase(),
|
|
7076
|
+
isVisible: () => true,
|
|
7034
7077
|
},
|
|
7035
7078
|
{
|
|
7036
7079
|
key: "note",
|
|
7037
7080
|
label: "Note",
|
|
7038
7081
|
format: (data) => toPlainString(data.note ?? "N/A"),
|
|
7082
|
+
isVisible: () => GLOBAL_CONFIG.CC_REPORT_SHOW_SIGNAL_NOTE,
|
|
7039
7083
|
},
|
|
7040
7084
|
{
|
|
7041
7085
|
key: "currentPrice",
|
|
7042
7086
|
label: "Current Price",
|
|
7043
7087
|
format: (data) => `${data.currentPrice.toFixed(8)} USD`,
|
|
7088
|
+
isVisible: () => true,
|
|
7044
7089
|
},
|
|
7045
7090
|
{
|
|
7046
7091
|
key: "priceOpen",
|
|
7047
7092
|
label: "Entry Price",
|
|
7048
7093
|
format: (data) => `${data.priceOpen.toFixed(8)} USD`,
|
|
7094
|
+
isVisible: () => true,
|
|
7049
7095
|
},
|
|
7050
7096
|
{
|
|
7051
7097
|
key: "takeProfit",
|
|
7052
7098
|
label: "Take Profit",
|
|
7053
7099
|
format: (data) => `${data.takeProfit.toFixed(8)} USD`,
|
|
7100
|
+
isVisible: () => true,
|
|
7054
7101
|
},
|
|
7055
7102
|
{
|
|
7056
7103
|
key: "stopLoss",
|
|
7057
7104
|
label: "Stop Loss",
|
|
7058
7105
|
format: (data) => `${data.stopLoss.toFixed(8)} USD`,
|
|
7106
|
+
isVisible: () => true,
|
|
7059
7107
|
},
|
|
7060
7108
|
{
|
|
7061
7109
|
key: "duration",
|
|
7062
7110
|
label: "Wait Time (min)",
|
|
7063
7111
|
format: (data) => data.duration !== undefined ? `${data.duration}` : "N/A",
|
|
7112
|
+
isVisible: () => true,
|
|
7064
7113
|
},
|
|
7065
7114
|
];
|
|
7066
7115
|
/** Maximum number of events to store in schedule reports */
|
|
@@ -7218,9 +7267,10 @@ let ReportStorage$3 = class ReportStorage {
|
|
|
7218
7267
|
"No scheduled signals recorded yet."
|
|
7219
7268
|
].join("\n");
|
|
7220
7269
|
}
|
|
7221
|
-
const
|
|
7222
|
-
const
|
|
7223
|
-
const
|
|
7270
|
+
const visibleColumns = columns$4.filter((col) => col.isVisible());
|
|
7271
|
+
const header = visibleColumns.map((col) => col.label);
|
|
7272
|
+
const separator = visibleColumns.map(() => "---");
|
|
7273
|
+
const rows = this._eventList.map((event) => visibleColumns.map((col) => col.format(event)));
|
|
7224
7274
|
const tableData = [header, separator, ...rows];
|
|
7225
7275
|
const table = tableData.map((row) => `| ${row.join(" | ")} |`).join("\n");
|
|
7226
7276
|
return [
|
|
@@ -7454,6 +7504,86 @@ function percentile(sortedArray, p) {
|
|
|
7454
7504
|
const index = Math.ceil((sortedArray.length * p) / 100) - 1;
|
|
7455
7505
|
return sortedArray[Math.max(0, index)];
|
|
7456
7506
|
}
|
|
7507
|
+
const columns$3 = [
|
|
7508
|
+
{
|
|
7509
|
+
key: "metricType",
|
|
7510
|
+
label: "Metric Type",
|
|
7511
|
+
format: (data) => data.metricType,
|
|
7512
|
+
isVisible: () => true,
|
|
7513
|
+
},
|
|
7514
|
+
{
|
|
7515
|
+
key: "count",
|
|
7516
|
+
label: "Count",
|
|
7517
|
+
format: (data) => data.count.toString(),
|
|
7518
|
+
isVisible: () => true,
|
|
7519
|
+
},
|
|
7520
|
+
{
|
|
7521
|
+
key: "totalDuration",
|
|
7522
|
+
label: "Total (ms)",
|
|
7523
|
+
format: (data) => data.totalDuration.toFixed(2),
|
|
7524
|
+
isVisible: () => true,
|
|
7525
|
+
},
|
|
7526
|
+
{
|
|
7527
|
+
key: "avgDuration",
|
|
7528
|
+
label: "Avg (ms)",
|
|
7529
|
+
format: (data) => data.avgDuration.toFixed(2),
|
|
7530
|
+
isVisible: () => true,
|
|
7531
|
+
},
|
|
7532
|
+
{
|
|
7533
|
+
key: "minDuration",
|
|
7534
|
+
label: "Min (ms)",
|
|
7535
|
+
format: (data) => data.minDuration.toFixed(2),
|
|
7536
|
+
isVisible: () => true,
|
|
7537
|
+
},
|
|
7538
|
+
{
|
|
7539
|
+
key: "maxDuration",
|
|
7540
|
+
label: "Max (ms)",
|
|
7541
|
+
format: (data) => data.maxDuration.toFixed(2),
|
|
7542
|
+
isVisible: () => true,
|
|
7543
|
+
},
|
|
7544
|
+
{
|
|
7545
|
+
key: "stdDev",
|
|
7546
|
+
label: "Std Dev (ms)",
|
|
7547
|
+
format: (data) => data.stdDev.toFixed(2),
|
|
7548
|
+
isVisible: () => true,
|
|
7549
|
+
},
|
|
7550
|
+
{
|
|
7551
|
+
key: "median",
|
|
7552
|
+
label: "Median (ms)",
|
|
7553
|
+
format: (data) => data.median.toFixed(2),
|
|
7554
|
+
isVisible: () => true,
|
|
7555
|
+
},
|
|
7556
|
+
{
|
|
7557
|
+
key: "p95",
|
|
7558
|
+
label: "P95 (ms)",
|
|
7559
|
+
format: (data) => data.p95.toFixed(2),
|
|
7560
|
+
isVisible: () => true,
|
|
7561
|
+
},
|
|
7562
|
+
{
|
|
7563
|
+
key: "p99",
|
|
7564
|
+
label: "P99 (ms)",
|
|
7565
|
+
format: (data) => data.p99.toFixed(2),
|
|
7566
|
+
isVisible: () => true,
|
|
7567
|
+
},
|
|
7568
|
+
{
|
|
7569
|
+
key: "avgWaitTime",
|
|
7570
|
+
label: "Avg Wait (ms)",
|
|
7571
|
+
format: (data) => data.avgWaitTime.toFixed(2),
|
|
7572
|
+
isVisible: () => true,
|
|
7573
|
+
},
|
|
7574
|
+
{
|
|
7575
|
+
key: "minWaitTime",
|
|
7576
|
+
label: "Min Wait (ms)",
|
|
7577
|
+
format: (data) => data.minWaitTime.toFixed(2),
|
|
7578
|
+
isVisible: () => true,
|
|
7579
|
+
},
|
|
7580
|
+
{
|
|
7581
|
+
key: "maxWaitTime",
|
|
7582
|
+
label: "Max Wait (ms)",
|
|
7583
|
+
format: (data) => data.maxWaitTime.toFixed(2),
|
|
7584
|
+
isVisible: () => true,
|
|
7585
|
+
},
|
|
7586
|
+
];
|
|
7457
7587
|
/** Maximum number of performance events to store per strategy */
|
|
7458
7588
|
const MAX_EVENTS$3 = 10000;
|
|
7459
7589
|
/**
|
|
@@ -7569,40 +7699,13 @@ class PerformanceStorage {
|
|
|
7569
7699
|
}
|
|
7570
7700
|
// Sort metrics by total duration (descending) to show bottlenecks first
|
|
7571
7701
|
const sortedMetrics = Object.values(stats.metricStats).sort((a, b) => b.totalDuration - a.totalDuration);
|
|
7572
|
-
// Generate summary table
|
|
7573
|
-
const
|
|
7574
|
-
|
|
7575
|
-
|
|
7576
|
-
|
|
7577
|
-
|
|
7578
|
-
|
|
7579
|
-
"Max (ms)",
|
|
7580
|
-
"Std Dev (ms)",
|
|
7581
|
-
"Median (ms)",
|
|
7582
|
-
"P95 (ms)",
|
|
7583
|
-
"P99 (ms)",
|
|
7584
|
-
"Avg Wait (ms)",
|
|
7585
|
-
"Min Wait (ms)",
|
|
7586
|
-
"Max Wait (ms)",
|
|
7587
|
-
];
|
|
7588
|
-
const summarySeparator = summaryHeader.map(() => "---");
|
|
7589
|
-
const summaryRows = sortedMetrics.map((metric) => [
|
|
7590
|
-
metric.metricType,
|
|
7591
|
-
metric.count.toString(),
|
|
7592
|
-
metric.totalDuration.toFixed(2),
|
|
7593
|
-
metric.avgDuration.toFixed(2),
|
|
7594
|
-
metric.minDuration.toFixed(2),
|
|
7595
|
-
metric.maxDuration.toFixed(2),
|
|
7596
|
-
metric.stdDev.toFixed(2),
|
|
7597
|
-
metric.median.toFixed(2),
|
|
7598
|
-
metric.p95.toFixed(2),
|
|
7599
|
-
metric.p99.toFixed(2),
|
|
7600
|
-
metric.avgWaitTime.toFixed(2),
|
|
7601
|
-
metric.minWaitTime.toFixed(2),
|
|
7602
|
-
metric.maxWaitTime.toFixed(2),
|
|
7603
|
-
]);
|
|
7604
|
-
const summaryTableData = [summaryHeader, summarySeparator, ...summaryRows];
|
|
7605
|
-
const summaryTable = summaryTableData.map((row) => `| ${row.join(" | ")} |`).join("\n");
|
|
7702
|
+
// Generate summary table using Column interface
|
|
7703
|
+
const visibleColumns = columns$3.filter((col) => col.isVisible());
|
|
7704
|
+
const header = visibleColumns.map((col) => col.label);
|
|
7705
|
+
const separator = visibleColumns.map(() => "---");
|
|
7706
|
+
const rows = sortedMetrics.map((metric) => visibleColumns.map((col) => col.format(metric)));
|
|
7707
|
+
const tableData = [header, separator, ...rows];
|
|
7708
|
+
const summaryTable = tableData.map((row) => `| ${row.join(" | ")} |`).join("\n");
|
|
7606
7709
|
// Calculate percentage of total time for each metric
|
|
7607
7710
|
const percentages = sortedMetrics.map((metric) => {
|
|
7608
7711
|
const pct = (metric.totalDuration / stats.totalDuration) * 100;
|
|
@@ -7837,21 +7940,25 @@ function createStrategyColumns(metric) {
|
|
|
7837
7940
|
key: "rank",
|
|
7838
7941
|
label: "Rank",
|
|
7839
7942
|
format: (data, index) => `${index + 1}`,
|
|
7943
|
+
isVisible: () => true,
|
|
7840
7944
|
},
|
|
7841
7945
|
{
|
|
7842
7946
|
key: "strategy",
|
|
7843
7947
|
label: "Strategy",
|
|
7844
7948
|
format: (data) => data.strategyName,
|
|
7949
|
+
isVisible: () => true,
|
|
7845
7950
|
},
|
|
7846
7951
|
{
|
|
7847
7952
|
key: "metric",
|
|
7848
7953
|
label: metric,
|
|
7849
7954
|
format: (data) => formatMetric(data.metricValue),
|
|
7955
|
+
isVisible: () => true,
|
|
7850
7956
|
},
|
|
7851
7957
|
{
|
|
7852
7958
|
key: "totalSignals",
|
|
7853
7959
|
label: "Total Signals",
|
|
7854
7960
|
format: (data) => `${data.stats.totalSignals}`,
|
|
7961
|
+
isVisible: () => true,
|
|
7855
7962
|
},
|
|
7856
7963
|
{
|
|
7857
7964
|
key: "winRate",
|
|
@@ -7859,6 +7966,7 @@ function createStrategyColumns(metric) {
|
|
|
7859
7966
|
format: (data) => data.stats.winRate !== null
|
|
7860
7967
|
? `${data.stats.winRate.toFixed(2)}%`
|
|
7861
7968
|
: "N/A",
|
|
7969
|
+
isVisible: () => true,
|
|
7862
7970
|
},
|
|
7863
7971
|
{
|
|
7864
7972
|
key: "avgPnl",
|
|
@@ -7866,6 +7974,7 @@ function createStrategyColumns(metric) {
|
|
|
7866
7974
|
format: (data) => data.stats.avgPnl !== null
|
|
7867
7975
|
? `${data.stats.avgPnl > 0 ? "+" : ""}${data.stats.avgPnl.toFixed(2)}%`
|
|
7868
7976
|
: "N/A",
|
|
7977
|
+
isVisible: () => true,
|
|
7869
7978
|
},
|
|
7870
7979
|
{
|
|
7871
7980
|
key: "totalPnl",
|
|
@@ -7873,6 +7982,7 @@ function createStrategyColumns(metric) {
|
|
|
7873
7982
|
format: (data) => data.stats.totalPnl !== null
|
|
7874
7983
|
? `${data.stats.totalPnl > 0 ? "+" : ""}${data.stats.totalPnl.toFixed(2)}%`
|
|
7875
7984
|
: "N/A",
|
|
7985
|
+
isVisible: () => true,
|
|
7876
7986
|
},
|
|
7877
7987
|
{
|
|
7878
7988
|
key: "sharpeRatio",
|
|
@@ -7880,6 +7990,7 @@ function createStrategyColumns(metric) {
|
|
|
7880
7990
|
format: (data) => data.stats.sharpeRatio !== null
|
|
7881
7991
|
? `${data.stats.sharpeRatio.toFixed(3)}`
|
|
7882
7992
|
: "N/A",
|
|
7993
|
+
isVisible: () => true,
|
|
7883
7994
|
},
|
|
7884
7995
|
{
|
|
7885
7996
|
key: "stdDev",
|
|
@@ -7887,6 +7998,7 @@ function createStrategyColumns(metric) {
|
|
|
7887
7998
|
format: (data) => data.stats.stdDev !== null
|
|
7888
7999
|
? `${data.stats.stdDev.toFixed(3)}%`
|
|
7889
8000
|
: "N/A",
|
|
8001
|
+
isVisible: () => true,
|
|
7890
8002
|
},
|
|
7891
8003
|
];
|
|
7892
8004
|
}
|
|
@@ -7899,41 +8011,49 @@ const pnlColumns = [
|
|
|
7899
8011
|
key: "strategy",
|
|
7900
8012
|
label: "Strategy",
|
|
7901
8013
|
format: (data) => data.strategyName,
|
|
8014
|
+
isVisible: () => true,
|
|
7902
8015
|
},
|
|
7903
8016
|
{
|
|
7904
8017
|
key: "signalId",
|
|
7905
8018
|
label: "Signal ID",
|
|
7906
8019
|
format: (data) => data.signalId,
|
|
8020
|
+
isVisible: () => true,
|
|
7907
8021
|
},
|
|
7908
8022
|
{
|
|
7909
8023
|
key: "symbol",
|
|
7910
8024
|
label: "Symbol",
|
|
7911
8025
|
format: (data) => data.symbol,
|
|
8026
|
+
isVisible: () => true,
|
|
7912
8027
|
},
|
|
7913
8028
|
{
|
|
7914
8029
|
key: "position",
|
|
7915
8030
|
label: "Position",
|
|
7916
8031
|
format: (data) => data.position.toUpperCase(),
|
|
8032
|
+
isVisible: () => true,
|
|
7917
8033
|
},
|
|
7918
8034
|
{
|
|
7919
8035
|
key: "pnl",
|
|
7920
8036
|
label: "PNL (net)",
|
|
7921
8037
|
format: (data) => `${data.pnl > 0 ? "+" : ""}${data.pnl.toFixed(2)}%`,
|
|
8038
|
+
isVisible: () => true,
|
|
7922
8039
|
},
|
|
7923
8040
|
{
|
|
7924
8041
|
key: "closeReason",
|
|
7925
8042
|
label: "Close Reason",
|
|
7926
8043
|
format: (data) => data.closeReason,
|
|
8044
|
+
isVisible: () => true,
|
|
7927
8045
|
},
|
|
7928
8046
|
{
|
|
7929
8047
|
key: "openTime",
|
|
7930
8048
|
label: "Open Time",
|
|
7931
8049
|
format: (data) => new Date(data.openTime).toISOString(),
|
|
8050
|
+
isVisible: () => true,
|
|
7932
8051
|
},
|
|
7933
8052
|
{
|
|
7934
8053
|
key: "closeTime",
|
|
7935
8054
|
label: "Close Time",
|
|
7936
8055
|
format: (data) => new Date(data.closeTime).toISOString(),
|
|
8056
|
+
isVisible: () => true,
|
|
7937
8057
|
},
|
|
7938
8058
|
];
|
|
7939
8059
|
/**
|
|
@@ -8018,11 +8138,12 @@ let ReportStorage$2 = class ReportStorage {
|
|
|
8018
8138
|
const topStrategies = sortedResults.slice(0, topN);
|
|
8019
8139
|
// Get columns configuration
|
|
8020
8140
|
const columns = createStrategyColumns(metric);
|
|
8141
|
+
const visibleColumns = columns.filter((col) => col.isVisible());
|
|
8021
8142
|
// Build table header
|
|
8022
|
-
const header =
|
|
8023
|
-
const separator =
|
|
8143
|
+
const header = visibleColumns.map((col) => col.label);
|
|
8144
|
+
const separator = visibleColumns.map(() => "---");
|
|
8024
8145
|
// Build table rows
|
|
8025
|
-
const rows = topStrategies.map((result, index) =>
|
|
8146
|
+
const rows = topStrategies.map((result, index) => visibleColumns.map((col) => col.format(result, index)));
|
|
8026
8147
|
const tableData = [header, separator, ...rows];
|
|
8027
8148
|
return tableData.map((row) => `| ${row.join(" | ")} |`).join("\n");
|
|
8028
8149
|
}
|
|
@@ -8056,10 +8177,11 @@ let ReportStorage$2 = class ReportStorage {
|
|
|
8056
8177
|
return "No closed signals available.";
|
|
8057
8178
|
}
|
|
8058
8179
|
// Build table header
|
|
8059
|
-
const
|
|
8060
|
-
const
|
|
8180
|
+
const visibleColumns = pnlColumns.filter((col) => col.isVisible());
|
|
8181
|
+
const header = visibleColumns.map((col) => col.label);
|
|
8182
|
+
const separator = visibleColumns.map(() => "---");
|
|
8061
8183
|
// Build table rows
|
|
8062
|
-
const rows = allSignals.map((signal) =>
|
|
8184
|
+
const rows = allSignals.map((signal) => visibleColumns.map((col) => col.format(signal)));
|
|
8063
8185
|
const tableData = [header, separator, ...rows];
|
|
8064
8186
|
return tableData.map((row) => `| ${row.join(" | ")} |`).join("\n");
|
|
8065
8187
|
}
|
|
@@ -8324,61 +8446,73 @@ const columns$2 = [
|
|
|
8324
8446
|
key: "symbol",
|
|
8325
8447
|
label: "Symbol",
|
|
8326
8448
|
format: (data) => data.symbol,
|
|
8449
|
+
isVisible: () => true,
|
|
8327
8450
|
},
|
|
8328
8451
|
{
|
|
8329
8452
|
key: "totalPnl",
|
|
8330
8453
|
label: "Total PNL",
|
|
8331
8454
|
format: (data) => data.totalPnl !== null ? str(data.totalPnl, "%+.2f%%") : "N/A",
|
|
8455
|
+
isVisible: () => true,
|
|
8332
8456
|
},
|
|
8333
8457
|
{
|
|
8334
8458
|
key: "sharpeRatio",
|
|
8335
8459
|
label: "Sharpe",
|
|
8336
8460
|
format: (data) => data.sharpeRatio !== null ? str(data.sharpeRatio, "%.2f") : "N/A",
|
|
8461
|
+
isVisible: () => true,
|
|
8337
8462
|
},
|
|
8338
8463
|
{
|
|
8339
8464
|
key: "profitFactor",
|
|
8340
8465
|
label: "PF",
|
|
8341
8466
|
format: (data) => data.profitFactor !== null ? str(data.profitFactor, "%.2f") : "N/A",
|
|
8467
|
+
isVisible: () => true,
|
|
8342
8468
|
},
|
|
8343
8469
|
{
|
|
8344
8470
|
key: "expectancy",
|
|
8345
8471
|
label: "Expect",
|
|
8346
8472
|
format: (data) => data.expectancy !== null ? str(data.expectancy, "%+.2f%%") : "N/A",
|
|
8473
|
+
isVisible: () => true,
|
|
8347
8474
|
},
|
|
8348
8475
|
{
|
|
8349
8476
|
key: "winRate",
|
|
8350
8477
|
label: "WR",
|
|
8351
8478
|
format: (data) => data.winRate !== null ? str(data.winRate, "%.1f%%") : "N/A",
|
|
8479
|
+
isVisible: () => true,
|
|
8352
8480
|
},
|
|
8353
8481
|
{
|
|
8354
8482
|
key: "avgWin",
|
|
8355
8483
|
label: "Avg Win",
|
|
8356
8484
|
format: (data) => data.avgWin !== null ? str(data.avgWin, "%+.2f%%") : "N/A",
|
|
8485
|
+
isVisible: () => true,
|
|
8357
8486
|
},
|
|
8358
8487
|
{
|
|
8359
8488
|
key: "avgLoss",
|
|
8360
8489
|
label: "Avg Loss",
|
|
8361
8490
|
format: (data) => data.avgLoss !== null ? str(data.avgLoss, "%+.2f%%") : "N/A",
|
|
8491
|
+
isVisible: () => true,
|
|
8362
8492
|
},
|
|
8363
8493
|
{
|
|
8364
8494
|
key: "maxDrawdown",
|
|
8365
8495
|
label: "Max DD",
|
|
8366
8496
|
format: (data) => data.maxDrawdown !== null ? str(-data.maxDrawdown, "%.2f%%") : "N/A",
|
|
8497
|
+
isVisible: () => true,
|
|
8367
8498
|
},
|
|
8368
8499
|
{
|
|
8369
8500
|
key: "maxWinStreak",
|
|
8370
8501
|
label: "W Streak",
|
|
8371
8502
|
format: (data) => data.maxWinStreak.toString(),
|
|
8503
|
+
isVisible: () => true,
|
|
8372
8504
|
},
|
|
8373
8505
|
{
|
|
8374
8506
|
key: "maxLossStreak",
|
|
8375
8507
|
label: "L Streak",
|
|
8376
8508
|
format: (data) => data.maxLossStreak.toString(),
|
|
8509
|
+
isVisible: () => true,
|
|
8377
8510
|
},
|
|
8378
8511
|
{
|
|
8379
8512
|
key: "totalTrades",
|
|
8380
8513
|
label: "Trades",
|
|
8381
8514
|
format: (data) => data.totalTrades.toString(),
|
|
8515
|
+
isVisible: () => true,
|
|
8382
8516
|
},
|
|
8383
8517
|
];
|
|
8384
8518
|
/** Maximum number of signals to store per symbol in heatmap reports */
|
|
@@ -8626,9 +8760,10 @@ class HeatmapStorage {
|
|
|
8626
8760
|
"*No data available*"
|
|
8627
8761
|
].join("\n");
|
|
8628
8762
|
}
|
|
8629
|
-
const
|
|
8630
|
-
const
|
|
8631
|
-
const
|
|
8763
|
+
const visibleColumns = columns$2.filter((col) => col.isVisible());
|
|
8764
|
+
const header = visibleColumns.map((col) => col.label);
|
|
8765
|
+
const separator = visibleColumns.map(() => "---");
|
|
8766
|
+
const rows = data.symbols.map((row) => visibleColumns.map((col) => col.format(row)));
|
|
8632
8767
|
const tableData = [header, separator, ...rows];
|
|
8633
8768
|
const table = tableData.map((row) => `| ${row.join(" | ")} |`).join("\n");
|
|
8634
8769
|
return [
|
|
@@ -11188,46 +11323,55 @@ const columns$1 = [
|
|
|
11188
11323
|
key: "action",
|
|
11189
11324
|
label: "Action",
|
|
11190
11325
|
format: (data) => data.action.toUpperCase(),
|
|
11326
|
+
isVisible: () => true,
|
|
11191
11327
|
},
|
|
11192
11328
|
{
|
|
11193
11329
|
key: "symbol",
|
|
11194
11330
|
label: "Symbol",
|
|
11195
11331
|
format: (data) => data.symbol,
|
|
11332
|
+
isVisible: () => true,
|
|
11196
11333
|
},
|
|
11197
11334
|
{
|
|
11198
11335
|
key: "strategyName",
|
|
11199
11336
|
label: "Strategy",
|
|
11200
11337
|
format: (data) => data.strategyName,
|
|
11338
|
+
isVisible: () => true,
|
|
11201
11339
|
},
|
|
11202
11340
|
{
|
|
11203
11341
|
key: "signalId",
|
|
11204
11342
|
label: "Signal ID",
|
|
11205
11343
|
format: (data) => data.signalId,
|
|
11344
|
+
isVisible: () => true,
|
|
11206
11345
|
},
|
|
11207
11346
|
{
|
|
11208
11347
|
key: "position",
|
|
11209
11348
|
label: "Position",
|
|
11210
11349
|
format: (data) => data.position.toUpperCase(),
|
|
11350
|
+
isVisible: () => true,
|
|
11211
11351
|
},
|
|
11212
11352
|
{
|
|
11213
11353
|
key: "level",
|
|
11214
11354
|
label: "Level %",
|
|
11215
11355
|
format: (data) => data.action === "profit" ? `+${data.level}%` : `-${data.level}%`,
|
|
11356
|
+
isVisible: () => true,
|
|
11216
11357
|
},
|
|
11217
11358
|
{
|
|
11218
11359
|
key: "currentPrice",
|
|
11219
11360
|
label: "Current Price",
|
|
11220
11361
|
format: (data) => `${data.currentPrice.toFixed(8)} USD`,
|
|
11362
|
+
isVisible: () => true,
|
|
11221
11363
|
},
|
|
11222
11364
|
{
|
|
11223
11365
|
key: "timestamp",
|
|
11224
11366
|
label: "Timestamp",
|
|
11225
11367
|
format: (data) => new Date(data.timestamp).toISOString(),
|
|
11368
|
+
isVisible: () => true,
|
|
11226
11369
|
},
|
|
11227
11370
|
{
|
|
11228
11371
|
key: "mode",
|
|
11229
11372
|
label: "Mode",
|
|
11230
11373
|
format: (data) => (data.backtest ? "Backtest" : "Live"),
|
|
11374
|
+
isVisible: () => true,
|
|
11231
11375
|
},
|
|
11232
11376
|
];
|
|
11233
11377
|
/** Maximum number of events to store in partial reports */
|
|
@@ -11330,9 +11474,10 @@ let ReportStorage$1 = class ReportStorage {
|
|
|
11330
11474
|
"No partial profit/loss events recorded yet."
|
|
11331
11475
|
].join("\n");
|
|
11332
11476
|
}
|
|
11333
|
-
const
|
|
11334
|
-
const
|
|
11335
|
-
const
|
|
11477
|
+
const visibleColumns = columns$1.filter((col) => col.isVisible());
|
|
11478
|
+
const header = visibleColumns.map((col) => col.label);
|
|
11479
|
+
const separator = visibleColumns.map(() => "---");
|
|
11480
|
+
const rows = this._eventList.map((event) => visibleColumns.map((col) => col.format(event)));
|
|
11336
11481
|
const tableData = [header, separator, ...rows];
|
|
11337
11482
|
const table = tableData.map((row) => `| ${row.join(" | ")} |`).join("\n");
|
|
11338
11483
|
return [
|
|
@@ -11973,26 +12118,37 @@ const columns = [
|
|
|
11973
12118
|
key: "symbol",
|
|
11974
12119
|
label: "Symbol",
|
|
11975
12120
|
format: (data) => data.symbol,
|
|
12121
|
+
isVisible: () => true,
|
|
11976
12122
|
},
|
|
11977
12123
|
{
|
|
11978
12124
|
key: "strategyName",
|
|
11979
12125
|
label: "Strategy",
|
|
11980
12126
|
format: (data) => data.strategyName,
|
|
12127
|
+
isVisible: () => true,
|
|
11981
12128
|
},
|
|
11982
12129
|
{
|
|
11983
12130
|
key: "signalId",
|
|
11984
12131
|
label: "Signal ID",
|
|
11985
12132
|
format: (data) => data.pendingSignal.id || "N/A",
|
|
12133
|
+
isVisible: () => true,
|
|
11986
12134
|
},
|
|
11987
12135
|
{
|
|
11988
12136
|
key: "position",
|
|
11989
12137
|
label: "Position",
|
|
11990
12138
|
format: (data) => data.pendingSignal.position.toUpperCase(),
|
|
12139
|
+
isVisible: () => true,
|
|
12140
|
+
},
|
|
12141
|
+
{
|
|
12142
|
+
key: "note",
|
|
12143
|
+
label: "Note",
|
|
12144
|
+
format: (data) => toPlainString(data.pendingSignal.note ?? "N/A"),
|
|
12145
|
+
isVisible: () => GLOBAL_CONFIG.CC_REPORT_SHOW_SIGNAL_NOTE,
|
|
11991
12146
|
},
|
|
11992
12147
|
{
|
|
11993
12148
|
key: "exchangeName",
|
|
11994
12149
|
label: "Exchange",
|
|
11995
12150
|
format: (data) => data.exchangeName,
|
|
12151
|
+
isVisible: () => true,
|
|
11996
12152
|
},
|
|
11997
12153
|
{
|
|
11998
12154
|
key: "openPrice",
|
|
@@ -12000,6 +12156,7 @@ const columns = [
|
|
|
12000
12156
|
format: (data) => data.pendingSignal.priceOpen !== undefined
|
|
12001
12157
|
? `${data.pendingSignal.priceOpen.toFixed(8)} USD`
|
|
12002
12158
|
: "N/A",
|
|
12159
|
+
isVisible: () => true,
|
|
12003
12160
|
},
|
|
12004
12161
|
{
|
|
12005
12162
|
key: "takeProfit",
|
|
@@ -12007,6 +12164,7 @@ const columns = [
|
|
|
12007
12164
|
format: (data) => data.pendingSignal.priceTakeProfit !== undefined
|
|
12008
12165
|
? `${data.pendingSignal.priceTakeProfit.toFixed(8)} USD`
|
|
12009
12166
|
: "N/A",
|
|
12167
|
+
isVisible: () => true,
|
|
12010
12168
|
},
|
|
12011
12169
|
{
|
|
12012
12170
|
key: "stopLoss",
|
|
@@ -12014,26 +12172,31 @@ const columns = [
|
|
|
12014
12172
|
format: (data) => data.pendingSignal.priceStopLoss !== undefined
|
|
12015
12173
|
? `${data.pendingSignal.priceStopLoss.toFixed(8)} USD`
|
|
12016
12174
|
: "N/A",
|
|
12175
|
+
isVisible: () => true,
|
|
12017
12176
|
},
|
|
12018
12177
|
{
|
|
12019
12178
|
key: "currentPrice",
|
|
12020
12179
|
label: "Current Price",
|
|
12021
12180
|
format: (data) => `${data.currentPrice.toFixed(8)} USD`,
|
|
12181
|
+
isVisible: () => true,
|
|
12022
12182
|
},
|
|
12023
12183
|
{
|
|
12024
12184
|
key: "activePositionCount",
|
|
12025
12185
|
label: "Active Positions",
|
|
12026
12186
|
format: (data) => data.activePositionCount.toString(),
|
|
12187
|
+
isVisible: () => true,
|
|
12027
12188
|
},
|
|
12028
12189
|
{
|
|
12029
12190
|
key: "comment",
|
|
12030
12191
|
label: "Reason",
|
|
12031
12192
|
format: (data) => data.comment,
|
|
12193
|
+
isVisible: () => true,
|
|
12032
12194
|
},
|
|
12033
12195
|
{
|
|
12034
12196
|
key: "timestamp",
|
|
12035
12197
|
label: "Timestamp",
|
|
12036
12198
|
format: (data) => new Date(data.timestamp).toISOString(),
|
|
12199
|
+
isVisible: () => true,
|
|
12037
12200
|
},
|
|
12038
12201
|
];
|
|
12039
12202
|
/** Maximum number of events to store in risk reports */
|
|
@@ -12102,9 +12265,10 @@ class ReportStorage {
|
|
|
12102
12265
|
"No risk rejections recorded yet.",
|
|
12103
12266
|
].join("\n");
|
|
12104
12267
|
}
|
|
12105
|
-
const
|
|
12106
|
-
const
|
|
12107
|
-
const
|
|
12268
|
+
const visibleColumns = columns.filter((col) => col.isVisible());
|
|
12269
|
+
const header = visibleColumns.map((col) => col.label);
|
|
12270
|
+
const separator = visibleColumns.map(() => "---");
|
|
12271
|
+
const rows = this._eventList.map((event) => visibleColumns.map((col) => col.format(event)));
|
|
12108
12272
|
const tableData = [header, separator, ...rows];
|
|
12109
12273
|
const table = tableData.map((row) => `| ${row.join(" | ")} |`).join("\n");
|
|
12110
12274
|
return [
|