backtest-kit 1.5.17 → 1.5.19

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.
Files changed (5) hide show
  1. package/README.md +107 -2477
  2. package/build/index.cjs +225 -61
  3. package/build/index.mjs +225 -61
  4. package/package.json +2 -3
  5. package/types.d.ts +9884 -9874
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$5 = [
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 header = columns$5.map((col) => col.label);
6212
- const separator = columns$5.map(() => "---");
6213
- const rows = this._signalList.map((closedSignal) => columns$5.map((col) => col.format(closedSignal)));
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$4 = [
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 header = columns$4.map((col) => col.label);
6766
- const separator = columns$4.map(() => "---");
6767
- const rows = this._eventList.map((event) => columns$4.map((col) => col.format(event)));
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$3 = [
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 header = columns$3.map((col) => col.label);
7222
- const separator = columns$3.map(() => "---");
7223
- const rows = this._eventList.map((event) => columns$3.map((col) => col.format(event)));
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 summaryHeader = [
7574
- "Metric Type",
7575
- "Count",
7576
- "Total (ms)",
7577
- "Avg (ms)",
7578
- "Min (ms)",
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 = columns.map((col) => col.label);
8023
- const separator = columns.map(() => "---");
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) => columns.map((col) => col.format(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 header = pnlColumns.map((col) => col.label);
8060
- const separator = pnlColumns.map(() => "---");
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) => pnlColumns.map((col) => col.format(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 header = columns$2.map((col) => col.label);
8630
- const separator = columns$2.map(() => "---");
8631
- const rows = data.symbols.map((row) => columns$2.map((col) => col.format(row)));
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 header = columns$1.map((col) => col.label);
11334
- const separator = columns$1.map(() => "---");
11335
- const rows = this._eventList.map((event) => columns$1.map((col) => col.format(event)));
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 header = columns.map((col) => col.label);
12106
- const separator = columns.map(() => "---");
12107
- const rows = this._eventList.map((event) => columns.map((col) => col.format(event)));
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 [