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