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 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$5 = [
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 header = columns$5.map((col) => col.label);
6214
- const separator = columns$5.map(() => "---");
6215
- const rows = this._signalList.map((closedSignal) => columns$5.map((col) => col.format(closedSignal)));
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$4 = [
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 header = columns$4.map((col) => col.label);
6768
- const separator = columns$4.map(() => "---");
6769
- const rows = this._eventList.map((event) => columns$4.map((col) => col.format(event)));
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$3 = [
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 header = columns$3.map((col) => col.label);
7224
- const separator = columns$3.map(() => "---");
7225
- const rows = this._eventList.map((event) => columns$3.map((col) => col.format(event)));
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 summaryHeader = [
7576
- "Metric Type",
7577
- "Count",
7578
- "Total (ms)",
7579
- "Avg (ms)",
7580
- "Min (ms)",
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 = columns.map((col) => col.label);
8025
- const separator = columns.map(() => "---");
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) => columns.map((col) => col.format(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 header = pnlColumns.map((col) => col.label);
8062
- const separator = pnlColumns.map(() => "---");
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) => pnlColumns.map((col) => col.format(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 header = columns$2.map((col) => col.label);
8632
- const separator = columns$2.map(() => "---");
8633
- const rows = data.symbols.map((row) => columns$2.map((col) => col.format(row)));
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 header = columns$1.map((col) => col.label);
11336
- const separator = columns$1.map(() => "---");
11337
- const rows = this._eventList.map((event) => columns$1.map((col) => col.format(event)));
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 header = columns.map((col) => col.label);
12108
- const separator = columns.map(() => "---");
12109
- const rows = this._eventList.map((event) => columns.map((col) => col.format(event)));
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 [