@tradejs/cli 3.1.29-beta.266 → 3.1.29-beta.268

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/dist/cli.js CHANGED
@@ -28332,7 +28332,12 @@ var init_artifacts = __esm({
28332
28332
  "controlPnl",
28333
28333
  "candidatePnl",
28334
28334
  "pnlDelta",
28335
- "exitReasonChanged"
28335
+ "controlExitReason",
28336
+ "candidateExitReason",
28337
+ "exitReasonChanged",
28338
+ "controlExitCode",
28339
+ "candidateExitCode",
28340
+ "exitCodeChanged"
28336
28341
  ].map(csv).join(",") + "\n"
28337
28342
  );
28338
28343
  for (const comparison of params.comparisons) {
@@ -28350,7 +28355,12 @@ var init_artifacts = __esm({
28350
28355
  pair.control.netProfit,
28351
28356
  pair.candidate.netProfit,
28352
28357
  pair.pnlDelta,
28353
- pair.exitReasonChanged
28358
+ pair.control.exitReason,
28359
+ pair.candidate.exitReason,
28360
+ pair.exitReasonChanged,
28361
+ pair.control.exitCode,
28362
+ pair.candidate.exitCode,
28363
+ pair.exitCodeChanged
28354
28364
  ].map(csv).join(",") + "\n"
28355
28365
  );
28356
28366
  }
@@ -28995,6 +29005,7 @@ var init_comparison = __esm({
28995
29005
  candidate: candidateTrade,
28996
29006
  pnlDelta: candidateTrade.netProfit - controlTrade.netProfit,
28997
29007
  exitReasonChanged: candidateTrade.exitReason !== controlTrade.exitReason,
29008
+ exitCodeChanged: candidateTrade.exitCode !== controlTrade.exitCode,
28998
29009
  entryTimestampDeltaMs: candidateTrade.entryTimestamp - controlTrade.entryTimestamp,
28999
29010
  exitTimestampDeltaMs: candidateTrade.exitTimestamp - controlTrade.exitTimestamp
29000
29011
  });
@@ -29212,6 +29223,7 @@ var init_dataset2 = __esm({
29212
29223
  "totalSlippageCost"
29213
29224
  ),
29214
29225
  exitReason: result.exitReason,
29226
+ ...result.exitCode ? { exitCode: result.exitCode } : {},
29215
29227
  regime: resolveCoreResearchRegime(row)
29216
29228
  };
29217
29229
  };
@@ -29255,7 +29267,7 @@ var init_dataset2 = __esm({
29255
29267
  }
29256
29268
  const primary = primaryRows[0];
29257
29269
  if (cycle.some(
29258
- (trade) => trade.exitTimestamp !== primary.exitTimestamp || trade.exitReason !== primary.exitReason
29270
+ (trade) => trade.exitTimestamp !== primary.exitTimestamp || trade.exitReason !== primary.exitReason || trade.exitCode !== primary.exitCode
29259
29271
  )) {
29260
29272
  throw new Error(
29261
29273
  `Position cycle ${identity} contains inconsistent final exits`
@@ -29444,7 +29456,7 @@ var init_report = __esm({
29444
29456
  )}</svg><div class="legend">${series.map((entry) => `<span><i style="background:${entry.color}"></i>${escapeHtml4(entry.id)}</span>`).join("")}</div>`;
29445
29457
  };
29446
29458
  comparisonCards = (comparisons) => comparisons.map(
29447
- (comparison) => `<article class="card"><h3>${escapeHtml4(comparison.candidateId)} vs ${escapeHtml4(comparison.controlId)}</h3><p class="verdict ${comparison.selection.passed ? "positive" : "negative"}">${comparison.selection.passed ? "PASS selection" : "REJECT selection"}</p><dl><dt>Matched</dt><dd>${comparison.matched}</dd><dt>Control only</dt><dd>${comparison.controlOnly}</dd><dt>Candidate only</dt><dd>${comparison.candidateOnly}</dd><dt>Identity retained</dt><dd>${format2(comparison.matchedIdentityPctOfControl)}%</dd><dt>Matched \u0394PnL</dt><dd>${format2(comparison.cohorts.ALL.matchedPnlDelta)}</dd><dt>Removed control PnL</dt><dd>${format2(comparison.cohorts.ALL.controlOnlyPnl)}</dd><dt>Added candidate PnL</dt><dd>${format2(comparison.cohorts.ALL.candidateOnlyPnl)}</dd><dt>Aggregate \u0394PnL</dt><dd>${format2(comparison.cohorts.ALL.delta.pnl)}</dd><dt>Aggregate \u0394DD</dt><dd>${format2(comparison.cohorts.ALL.delta.realizedMaxDrawdown)}</dd><dt>Bootstrap P(\u0394&gt;0)</dt><dd>${format2(comparison.bootstrap.probabilityPositive, 4)}</dd><dt>Holm p</dt><dd>${format2(comparison.bootstrap.holmAdjustedPValue, 4)}</dd></dl><p>${comparison.selection.failedRules.length ? escapeHtml4(comparison.selection.failedRules.map((rule) => `${rule.scope}.${rule.metric} ${rule.expected}`).join("; ")) : "All preregistered rules passed."}</p></article>`
29459
+ (comparison) => `<article class="card"><h3>${escapeHtml4(comparison.candidateId)} vs ${escapeHtml4(comparison.controlId)}</h3><p class="verdict ${comparison.selection.passed ? "positive" : "negative"}">${comparison.selection.passed ? "PASS selection" : "REJECT selection"}</p><dl><dt>Matched</dt><dd>${comparison.matched}</dd><dt>Control only</dt><dd>${comparison.controlOnly}</dd><dt>Candidate only</dt><dd>${comparison.candidateOnly}</dd><dt>Identity retained</dt><dd>${format2(comparison.matchedIdentityPctOfControl)}%</dd><dt>Exit reason changes</dt><dd>${comparison.matchedPairs.filter((pair) => pair.exitReasonChanged).length}</dd><dt>Exit code changes</dt><dd>${comparison.matchedPairs.filter((pair) => pair.exitCodeChanged).length}</dd><dt>Matched \u0394PnL</dt><dd>${format2(comparison.cohorts.ALL.matchedPnlDelta)}</dd><dt>Removed control PnL</dt><dd>${format2(comparison.cohorts.ALL.controlOnlyPnl)}</dd><dt>Added candidate PnL</dt><dd>${format2(comparison.cohorts.ALL.candidateOnlyPnl)}</dd><dt>Aggregate \u0394PnL</dt><dd>${format2(comparison.cohorts.ALL.delta.pnl)}</dd><dt>Aggregate \u0394DD</dt><dd>${format2(comparison.cohorts.ALL.delta.realizedMaxDrawdown)}</dd><dt>Bootstrap P(\u0394&gt;0)</dt><dd>${format2(comparison.bootstrap.probabilityPositive, 4)}</dd><dt>Holm p</dt><dd>${format2(comparison.bootstrap.holmAdjustedPValue, 4)}</dd></dl><p>${comparison.selection.failedRules.length ? escapeHtml4(comparison.selection.failedRules.map((rule) => `${rule.scope}.${rule.metric} ${rule.expected}`).join("; ")) : "All preregistered rules passed."}</p></article>`
29448
29460
  ).join("");
29449
29461
  regimeRows = (variants) => {
29450
29462
  const keys = [
@@ -29458,7 +29470,7 @@ var init_report = __esm({
29458
29470
  ).join("");
29459
29471
  };
29460
29472
  traceRows = (variants) => variants.map(
29461
- (variant) => `<tr><td>${escapeHtml4(variant.variant.label)}</td><td>${escapeHtml4(variant.reconciliation.status)}</td><td>${escapeHtml4(JSON.stringify(variant.setupIdentitySources))}</td><td>${escapeHtml4(JSON.stringify(variant.traceFunnel.events))}</td><td>${escapeHtml4(JSON.stringify(variant.traceFunnel.skipCounts))}</td><td>${escapeHtml4(variant.latestSignalTimeRegime?.key ?? "n/a")}</td></tr>`
29473
+ (variant) => `<tr><td>${escapeHtml4(variant.variant.label)}</td><td>${escapeHtml4(variant.reconciliation.status)}</td><td>${escapeHtml4(JSON.stringify(variant.setupIdentitySources))}</td><td>${escapeHtml4(JSON.stringify(variant.traceFunnel.events))}</td><td>${escapeHtml4(JSON.stringify(variant.traceFunnel.skipCounts))}</td><td>${escapeHtml4(JSON.stringify(variant.traceFunnel.exitCodes))}</td><td>${escapeHtml4(variant.latestSignalTimeRegime?.key ?? "n/a")}</td></tr>`
29462
29474
  ).join("");
29463
29475
  buildCoreResearchHtml = (params) => {
29464
29476
  const { spec, result, tradesByVariant } = params;
@@ -29484,7 +29496,7 @@ var init_report = __esm({
29484
29496
  ([key, value]) => `<span class="${value === "present" ? "positive" : "negative"}">${escapeHtml4(key)}: ${value}</span>`
29485
29497
  ).join(
29486
29498
  ""
29487
- )}</section><h2>ALL equity</h2>${buildCurveSvg(tradesByVariant, "ALL", "equity")}<h2>ALL realized drawdown</h2>${buildCurveSvg(tradesByVariant, "ALL", "drawdown")}<h2>LONG equity</h2>${buildCurveSvg(tradesByVariant, "LONG", "equity")}<h2>LONG realized drawdown</h2>${buildCurveSvg(tradesByVariant, "LONG", "drawdown")}<h2>SHORT equity</h2>${buildCurveSvg(tradesByVariant, "SHORT", "equity")}<h2>SHORT realized drawdown</h2>${buildCurveSvg(tradesByVariant, "SHORT", "drawdown")}<h2>Fixed cohort metrics</h2><p class="muted">Avg PnL/trade = cohort PnL / cohort N. ALL drawdown is aggregate portfolio; LONG/SHORT drawdowns are side-only.</p><div class="scroll"><table><thead><tr><th>Variant</th><th>Window</th><th>Cohort</th><th>N</th><th>PnL</th><th>Avg PnL/trade</th><th>PF</th><th>WR</th><th>Realized DD</th><th>Cad/day</th><th>Avg win</th><th>Avg loss</th><th>Payoff</th><th>Median</th><th>P05</th><th>P95</th><th>Median hold h</th><th>Max loss streak</th></tr></thead><tbody>${allRows}</tbody></table></div><h2>Fold and monthly robustness</h2><div class="scroll"><table><thead><tr><th>Variant</th><th>Window</th><th>Cohort</th><th>N</th><th>PnL</th><th>Avg PnL/trade</th><th>PF</th><th>WR</th><th>Realized DD</th><th>Cad/day</th><th>Avg win</th><th>Avg loss</th><th>Payoff</th><th>Median</th><th>P05</th><th>P95</th><th>Median hold h</th><th>Max loss streak</th></tr></thead><tbody>${robustnessRows}</tbody></table></div><h2>Cost stress</h2><div class="scroll"><table><thead><tr><th>Variant</th><th>Extra round-trip bps</th><th>N</th><th>PnL</th><th>Avg PnL/trade</th><th>PF</th><th>Realized DD</th></tr></thead><tbody>${costRows}</tbody></table></div><h2>Signal-time market regimes</h2><div class="scroll"><table><thead><tr><th>Trend | Volatility | Breadth | Derivatives</th><th>Variant</th><th>N</th><th>PnL</th><th>PF</th><th>WR</th></tr></thead><tbody>${regimeRows(result.variants)}</tbody></table></div><h2>Trace funnel and reconciliation</h2><div class="scroll"><table><thead><tr><th>Variant</th><th>Reconcile</th><th>Setup identity source</th><th>Events</th><th>Top skips</th><th>Latest signal-time regime</th></tr></thead><tbody>${traceRows(result.variants)}</tbody></table></div><h2>Multiple testing / overfitting</h2><pre>${escapeHtml4(JSON.stringify({ multipleTesting: result.multipleTesting, overfittingDiagnostics: result.overfittingDiagnostics }, null, 2))}</pre><h2>Lineage</h2><pre>${escapeHtml4(JSON.stringify({ stage: spec.stage, universeSha256: spec.universe.sha256, lineage: spec.lineage ?? null, artifactHashes: result.artifactHashes }, null, 2))}</pre></main></body></html>`;
29499
+ )}</section><h2>ALL equity</h2>${buildCurveSvg(tradesByVariant, "ALL", "equity")}<h2>ALL realized drawdown</h2>${buildCurveSvg(tradesByVariant, "ALL", "drawdown")}<h2>LONG equity</h2>${buildCurveSvg(tradesByVariant, "LONG", "equity")}<h2>LONG realized drawdown</h2>${buildCurveSvg(tradesByVariant, "LONG", "drawdown")}<h2>SHORT equity</h2>${buildCurveSvg(tradesByVariant, "SHORT", "equity")}<h2>SHORT realized drawdown</h2>${buildCurveSvg(tradesByVariant, "SHORT", "drawdown")}<h2>Fixed cohort metrics</h2><p class="muted">Avg PnL/trade = cohort PnL / cohort completed positions. ALL drawdown is aggregate portfolio; LONG/SHORT drawdowns are side-only.</p><div class="scroll"><table><thead><tr><th>Variant</th><th>Window</th><th>Cohort</th><th>N</th><th>PnL</th><th>Avg PnL/trade</th><th>PF</th><th>WR</th><th>Realized DD</th><th>Cad/day</th><th>Avg win</th><th>Avg loss</th><th>Payoff</th><th>Median</th><th>P05</th><th>P95</th><th>Median hold h</th><th>Max loss streak</th></tr></thead><tbody>${allRows}</tbody></table></div><h2>Fold and monthly robustness</h2><div class="scroll"><table><thead><tr><th>Variant</th><th>Window</th><th>Cohort</th><th>N</th><th>PnL</th><th>Avg PnL/trade</th><th>PF</th><th>WR</th><th>Realized DD</th><th>Cad/day</th><th>Avg win</th><th>Avg loss</th><th>Payoff</th><th>Median</th><th>P05</th><th>P95</th><th>Median hold h</th><th>Max loss streak</th></tr></thead><tbody>${robustnessRows}</tbody></table></div><h2>Cost stress</h2><div class="scroll"><table><thead><tr><th>Variant</th><th>Extra round-trip bps</th><th>N</th><th>PnL</th><th>Avg PnL/trade</th><th>PF</th><th>Realized DD</th></tr></thead><tbody>${costRows}</tbody></table></div><h2>Signal-time market regimes</h2><div class="scroll"><table><thead><tr><th>Trend | Volatility | Breadth | Derivatives</th><th>Variant</th><th>N</th><th>PnL</th><th>PF</th><th>WR</th></tr></thead><tbody>${regimeRows(result.variants)}</tbody></table></div><h2>Trace funnel and reconciliation</h2><div class="scroll"><table><thead><tr><th>Variant</th><th>Reconcile</th><th>Setup identity source</th><th>Events</th><th>Top skips</th><th>Exit codes</th><th>Latest signal-time regime</th></tr></thead><tbody>${traceRows(result.variants)}</tbody></table></div><h2>Multiple testing / overfitting</h2><pre>${escapeHtml4(JSON.stringify({ multipleTesting: result.multipleTesting, overfittingDiagnostics: result.overfittingDiagnostics }, null, 2))}</pre><h2>Lineage</h2><pre>${escapeHtml4(JSON.stringify({ stage: spec.stage, universeSha256: spec.universe.sha256, lineage: spec.lineage ?? null, artifactHashes: result.artifactHashes }, null, 2))}</pre></main></body></html>`;
29488
29500
  };
29489
29501
  }
29490
29502
  });
@@ -29786,8 +29798,9 @@ var init_trace = __esm({
29786
29798
  import_promises24 = __toESM(require("fs/promises"));
29787
29799
  import_node_path18 = __toESM(require("path"));
29788
29800
  summarizeCoreResearchTrace = async (filePaths = []) => {
29789
- const events = {};
29790
- const skipCounts = {};
29801
+ const events = /* @__PURE__ */ Object.create(null);
29802
+ const skipCounts = /* @__PURE__ */ Object.create(null);
29803
+ const exitCodes = /* @__PURE__ */ Object.create(null);
29791
29804
  for (const inputPath of filePaths) {
29792
29805
  const filePath = import_node_path18.default.resolve(inputPath);
29793
29806
  const text = await import_promises24.default.readFile(filePath, "utf8");
@@ -29807,6 +29820,9 @@ var init_trace = __esm({
29807
29820
  skipCounts[reason] = (skipCounts[reason] ?? 0) + count;
29808
29821
  }
29809
29822
  }
29823
+ if (event.event === "position_exited" && event.exitCode) {
29824
+ exitCodes[event.exitCode] = (exitCodes[event.exitCode] ?? 0) + 1;
29825
+ }
29810
29826
  }
29811
29827
  }
29812
29828
  return {
@@ -29815,6 +29831,11 @@ var init_trace = __esm({
29815
29831
  Object.entries(skipCounts).sort(
29816
29832
  ([leftKey, leftValue], [rightKey, rightValue]) => rightValue - leftValue || (leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0)
29817
29833
  )
29834
+ ),
29835
+ exitCodes: Object.fromEntries(
29836
+ Object.entries(exitCodes).sort(
29837
+ ([leftKey, leftValue], [rightKey, rightValue]) => rightValue - leftValue || (leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0)
29838
+ )
29818
29839
  )
29819
29840
  };
29820
29841
  };
@@ -691,7 +691,12 @@ var writeCoreResearchMatches = async (params) => {
691
691
  "controlPnl",
692
692
  "candidatePnl",
693
693
  "pnlDelta",
694
- "exitReasonChanged"
694
+ "controlExitReason",
695
+ "candidateExitReason",
696
+ "exitReasonChanged",
697
+ "controlExitCode",
698
+ "candidateExitCode",
699
+ "exitCodeChanged"
695
700
  ].map(csv).join(",") + "\n"
696
701
  );
697
702
  for (const comparison of params.comparisons) {
@@ -709,7 +714,12 @@ var writeCoreResearchMatches = async (params) => {
709
714
  pair.control.netProfit,
710
715
  pair.candidate.netProfit,
711
716
  pair.pnlDelta,
712
- pair.exitReasonChanged
717
+ pair.control.exitReason,
718
+ pair.candidate.exitReason,
719
+ pair.exitReasonChanged,
720
+ pair.control.exitCode,
721
+ pair.candidate.exitCode,
722
+ pair.exitCodeChanged
713
723
  ].map(csv).join(",") + "\n"
714
724
  );
715
725
  }
@@ -1326,6 +1336,7 @@ var compareCoreResearchVariants = (params) => {
1326
1336
  candidate: candidateTrade,
1327
1337
  pnlDelta: candidateTrade.netProfit - controlTrade.netProfit,
1328
1338
  exitReasonChanged: candidateTrade.exitReason !== controlTrade.exitReason,
1339
+ exitCodeChanged: candidateTrade.exitCode !== controlTrade.exitCode,
1329
1340
  entryTimestampDeltaMs: candidateTrade.entryTimestamp - controlTrade.entryTimestamp,
1330
1341
  exitTimestampDeltaMs: candidateTrade.exitTimestamp - controlTrade.exitTimestamp
1331
1342
  });
@@ -1537,6 +1548,7 @@ var normalizeCompletedTrade = (params) => {
1537
1548
  "totalSlippageCost"
1538
1549
  ),
1539
1550
  exitReason: result.exitReason,
1551
+ ...result.exitCode ? { exitCode: result.exitCode } : {},
1540
1552
  regime: resolveCoreResearchRegime(row)
1541
1553
  };
1542
1554
  };
@@ -1580,7 +1592,7 @@ var aggregatePositionCycles = (trades) => {
1580
1592
  }
1581
1593
  const primary = primaryRows[0];
1582
1594
  if (cycle.some(
1583
- (trade) => trade.exitTimestamp !== primary.exitTimestamp || trade.exitReason !== primary.exitReason
1595
+ (trade) => trade.exitTimestamp !== primary.exitTimestamp || trade.exitReason !== primary.exitReason || trade.exitCode !== primary.exitCode
1584
1596
  )) {
1585
1597
  throw new Error(
1586
1598
  `Position cycle ${identity} contains inconsistent final exits`
@@ -1762,7 +1774,7 @@ var buildCurveSvg = (tradesByVariant, cohort, curve) => {
1762
1774
  )}</svg><div class="legend">${series.map((entry) => `<span><i style="background:${entry.color}"></i>${escapeHtml(entry.id)}</span>`).join("")}</div>`;
1763
1775
  };
1764
1776
  var comparisonCards = (comparisons) => comparisons.map(
1765
- (comparison) => `<article class="card"><h3>${escapeHtml(comparison.candidateId)} vs ${escapeHtml(comparison.controlId)}</h3><p class="verdict ${comparison.selection.passed ? "positive" : "negative"}">${comparison.selection.passed ? "PASS selection" : "REJECT selection"}</p><dl><dt>Matched</dt><dd>${comparison.matched}</dd><dt>Control only</dt><dd>${comparison.controlOnly}</dd><dt>Candidate only</dt><dd>${comparison.candidateOnly}</dd><dt>Identity retained</dt><dd>${format(comparison.matchedIdentityPctOfControl)}%</dd><dt>Matched \u0394PnL</dt><dd>${format(comparison.cohorts.ALL.matchedPnlDelta)}</dd><dt>Removed control PnL</dt><dd>${format(comparison.cohorts.ALL.controlOnlyPnl)}</dd><dt>Added candidate PnL</dt><dd>${format(comparison.cohorts.ALL.candidateOnlyPnl)}</dd><dt>Aggregate \u0394PnL</dt><dd>${format(comparison.cohorts.ALL.delta.pnl)}</dd><dt>Aggregate \u0394DD</dt><dd>${format(comparison.cohorts.ALL.delta.realizedMaxDrawdown)}</dd><dt>Bootstrap P(\u0394&gt;0)</dt><dd>${format(comparison.bootstrap.probabilityPositive, 4)}</dd><dt>Holm p</dt><dd>${format(comparison.bootstrap.holmAdjustedPValue, 4)}</dd></dl><p>${comparison.selection.failedRules.length ? escapeHtml(comparison.selection.failedRules.map((rule) => `${rule.scope}.${rule.metric} ${rule.expected}`).join("; ")) : "All preregistered rules passed."}</p></article>`
1777
+ (comparison) => `<article class="card"><h3>${escapeHtml(comparison.candidateId)} vs ${escapeHtml(comparison.controlId)}</h3><p class="verdict ${comparison.selection.passed ? "positive" : "negative"}">${comparison.selection.passed ? "PASS selection" : "REJECT selection"}</p><dl><dt>Matched</dt><dd>${comparison.matched}</dd><dt>Control only</dt><dd>${comparison.controlOnly}</dd><dt>Candidate only</dt><dd>${comparison.candidateOnly}</dd><dt>Identity retained</dt><dd>${format(comparison.matchedIdentityPctOfControl)}%</dd><dt>Exit reason changes</dt><dd>${comparison.matchedPairs.filter((pair) => pair.exitReasonChanged).length}</dd><dt>Exit code changes</dt><dd>${comparison.matchedPairs.filter((pair) => pair.exitCodeChanged).length}</dd><dt>Matched \u0394PnL</dt><dd>${format(comparison.cohorts.ALL.matchedPnlDelta)}</dd><dt>Removed control PnL</dt><dd>${format(comparison.cohorts.ALL.controlOnlyPnl)}</dd><dt>Added candidate PnL</dt><dd>${format(comparison.cohorts.ALL.candidateOnlyPnl)}</dd><dt>Aggregate \u0394PnL</dt><dd>${format(comparison.cohorts.ALL.delta.pnl)}</dd><dt>Aggregate \u0394DD</dt><dd>${format(comparison.cohorts.ALL.delta.realizedMaxDrawdown)}</dd><dt>Bootstrap P(\u0394&gt;0)</dt><dd>${format(comparison.bootstrap.probabilityPositive, 4)}</dd><dt>Holm p</dt><dd>${format(comparison.bootstrap.holmAdjustedPValue, 4)}</dd></dl><p>${comparison.selection.failedRules.length ? escapeHtml(comparison.selection.failedRules.map((rule) => `${rule.scope}.${rule.metric} ${rule.expected}`).join("; ")) : "All preregistered rules passed."}</p></article>`
1766
1778
  ).join("");
1767
1779
  var regimeRows = (variants) => {
1768
1780
  const keys = [
@@ -1776,7 +1788,7 @@ var regimeRows = (variants) => {
1776
1788
  ).join("");
1777
1789
  };
1778
1790
  var traceRows = (variants) => variants.map(
1779
- (variant) => `<tr><td>${escapeHtml(variant.variant.label)}</td><td>${escapeHtml(variant.reconciliation.status)}</td><td>${escapeHtml(JSON.stringify(variant.setupIdentitySources))}</td><td>${escapeHtml(JSON.stringify(variant.traceFunnel.events))}</td><td>${escapeHtml(JSON.stringify(variant.traceFunnel.skipCounts))}</td><td>${escapeHtml(variant.latestSignalTimeRegime?.key ?? "n/a")}</td></tr>`
1791
+ (variant) => `<tr><td>${escapeHtml(variant.variant.label)}</td><td>${escapeHtml(variant.reconciliation.status)}</td><td>${escapeHtml(JSON.stringify(variant.setupIdentitySources))}</td><td>${escapeHtml(JSON.stringify(variant.traceFunnel.events))}</td><td>${escapeHtml(JSON.stringify(variant.traceFunnel.skipCounts))}</td><td>${escapeHtml(JSON.stringify(variant.traceFunnel.exitCodes))}</td><td>${escapeHtml(variant.latestSignalTimeRegime?.key ?? "n/a")}</td></tr>`
1780
1792
  ).join("");
1781
1793
  var buildCoreResearchHtml = (params) => {
1782
1794
  const { spec, result, tradesByVariant } = params;
@@ -1802,7 +1814,7 @@ var buildCoreResearchHtml = (params) => {
1802
1814
  ([key, value]) => `<span class="${value === "present" ? "positive" : "negative"}">${escapeHtml(key)}: ${value}</span>`
1803
1815
  ).join(
1804
1816
  ""
1805
- )}</section><h2>ALL equity</h2>${buildCurveSvg(tradesByVariant, "ALL", "equity")}<h2>ALL realized drawdown</h2>${buildCurveSvg(tradesByVariant, "ALL", "drawdown")}<h2>LONG equity</h2>${buildCurveSvg(tradesByVariant, "LONG", "equity")}<h2>LONG realized drawdown</h2>${buildCurveSvg(tradesByVariant, "LONG", "drawdown")}<h2>SHORT equity</h2>${buildCurveSvg(tradesByVariant, "SHORT", "equity")}<h2>SHORT realized drawdown</h2>${buildCurveSvg(tradesByVariant, "SHORT", "drawdown")}<h2>Fixed cohort metrics</h2><p class="muted">Avg PnL/trade = cohort PnL / cohort N. ALL drawdown is aggregate portfolio; LONG/SHORT drawdowns are side-only.</p><div class="scroll"><table><thead><tr><th>Variant</th><th>Window</th><th>Cohort</th><th>N</th><th>PnL</th><th>Avg PnL/trade</th><th>PF</th><th>WR</th><th>Realized DD</th><th>Cad/day</th><th>Avg win</th><th>Avg loss</th><th>Payoff</th><th>Median</th><th>P05</th><th>P95</th><th>Median hold h</th><th>Max loss streak</th></tr></thead><tbody>${allRows}</tbody></table></div><h2>Fold and monthly robustness</h2><div class="scroll"><table><thead><tr><th>Variant</th><th>Window</th><th>Cohort</th><th>N</th><th>PnL</th><th>Avg PnL/trade</th><th>PF</th><th>WR</th><th>Realized DD</th><th>Cad/day</th><th>Avg win</th><th>Avg loss</th><th>Payoff</th><th>Median</th><th>P05</th><th>P95</th><th>Median hold h</th><th>Max loss streak</th></tr></thead><tbody>${robustnessRows}</tbody></table></div><h2>Cost stress</h2><div class="scroll"><table><thead><tr><th>Variant</th><th>Extra round-trip bps</th><th>N</th><th>PnL</th><th>Avg PnL/trade</th><th>PF</th><th>Realized DD</th></tr></thead><tbody>${costRows}</tbody></table></div><h2>Signal-time market regimes</h2><div class="scroll"><table><thead><tr><th>Trend | Volatility | Breadth | Derivatives</th><th>Variant</th><th>N</th><th>PnL</th><th>PF</th><th>WR</th></tr></thead><tbody>${regimeRows(result.variants)}</tbody></table></div><h2>Trace funnel and reconciliation</h2><div class="scroll"><table><thead><tr><th>Variant</th><th>Reconcile</th><th>Setup identity source</th><th>Events</th><th>Top skips</th><th>Latest signal-time regime</th></tr></thead><tbody>${traceRows(result.variants)}</tbody></table></div><h2>Multiple testing / overfitting</h2><pre>${escapeHtml(JSON.stringify({ multipleTesting: result.multipleTesting, overfittingDiagnostics: result.overfittingDiagnostics }, null, 2))}</pre><h2>Lineage</h2><pre>${escapeHtml(JSON.stringify({ stage: spec.stage, universeSha256: spec.universe.sha256, lineage: spec.lineage ?? null, artifactHashes: result.artifactHashes }, null, 2))}</pre></main></body></html>`;
1817
+ )}</section><h2>ALL equity</h2>${buildCurveSvg(tradesByVariant, "ALL", "equity")}<h2>ALL realized drawdown</h2>${buildCurveSvg(tradesByVariant, "ALL", "drawdown")}<h2>LONG equity</h2>${buildCurveSvg(tradesByVariant, "LONG", "equity")}<h2>LONG realized drawdown</h2>${buildCurveSvg(tradesByVariant, "LONG", "drawdown")}<h2>SHORT equity</h2>${buildCurveSvg(tradesByVariant, "SHORT", "equity")}<h2>SHORT realized drawdown</h2>${buildCurveSvg(tradesByVariant, "SHORT", "drawdown")}<h2>Fixed cohort metrics</h2><p class="muted">Avg PnL/trade = cohort PnL / cohort completed positions. ALL drawdown is aggregate portfolio; LONG/SHORT drawdowns are side-only.</p><div class="scroll"><table><thead><tr><th>Variant</th><th>Window</th><th>Cohort</th><th>N</th><th>PnL</th><th>Avg PnL/trade</th><th>PF</th><th>WR</th><th>Realized DD</th><th>Cad/day</th><th>Avg win</th><th>Avg loss</th><th>Payoff</th><th>Median</th><th>P05</th><th>P95</th><th>Median hold h</th><th>Max loss streak</th></tr></thead><tbody>${allRows}</tbody></table></div><h2>Fold and monthly robustness</h2><div class="scroll"><table><thead><tr><th>Variant</th><th>Window</th><th>Cohort</th><th>N</th><th>PnL</th><th>Avg PnL/trade</th><th>PF</th><th>WR</th><th>Realized DD</th><th>Cad/day</th><th>Avg win</th><th>Avg loss</th><th>Payoff</th><th>Median</th><th>P05</th><th>P95</th><th>Median hold h</th><th>Max loss streak</th></tr></thead><tbody>${robustnessRows}</tbody></table></div><h2>Cost stress</h2><div class="scroll"><table><thead><tr><th>Variant</th><th>Extra round-trip bps</th><th>N</th><th>PnL</th><th>Avg PnL/trade</th><th>PF</th><th>Realized DD</th></tr></thead><tbody>${costRows}</tbody></table></div><h2>Signal-time market regimes</h2><div class="scroll"><table><thead><tr><th>Trend | Volatility | Breadth | Derivatives</th><th>Variant</th><th>N</th><th>PnL</th><th>PF</th><th>WR</th></tr></thead><tbody>${regimeRows(result.variants)}</tbody></table></div><h2>Trace funnel and reconciliation</h2><div class="scroll"><table><thead><tr><th>Variant</th><th>Reconcile</th><th>Setup identity source</th><th>Events</th><th>Top skips</th><th>Exit codes</th><th>Latest signal-time regime</th></tr></thead><tbody>${traceRows(result.variants)}</tbody></table></div><h2>Multiple testing / overfitting</h2><pre>${escapeHtml(JSON.stringify({ multipleTesting: result.multipleTesting, overfittingDiagnostics: result.overfittingDiagnostics }, null, 2))}</pre><h2>Lineage</h2><pre>${escapeHtml(JSON.stringify({ stage: spec.stage, universeSha256: spec.universe.sha256, lineage: spec.lineage ?? null, artifactHashes: result.artifactHashes }, null, 2))}</pre></main></body></html>`;
1806
1818
  };
1807
1819
 
1808
1820
  // src/lib/backtest/checkpoint.ts
@@ -2108,8 +2120,9 @@ var applyCoreResearchRobustnessGuardrails = (params) => {
2108
2120
  var import_promises3 = __toESM(require("fs/promises"));
2109
2121
  var import_node_path4 = __toESM(require("path"));
2110
2122
  var summarizeCoreResearchTrace = async (filePaths = []) => {
2111
- const events = {};
2112
- const skipCounts = {};
2123
+ const events = /* @__PURE__ */ Object.create(null);
2124
+ const skipCounts = /* @__PURE__ */ Object.create(null);
2125
+ const exitCodes = /* @__PURE__ */ Object.create(null);
2113
2126
  for (const inputPath of filePaths) {
2114
2127
  const filePath = import_node_path4.default.resolve(inputPath);
2115
2128
  const text = await import_promises3.default.readFile(filePath, "utf8");
@@ -2129,6 +2142,9 @@ var summarizeCoreResearchTrace = async (filePaths = []) => {
2129
2142
  skipCounts[reason] = (skipCounts[reason] ?? 0) + count;
2130
2143
  }
2131
2144
  }
2145
+ if (event.event === "position_exited" && event.exitCode) {
2146
+ exitCodes[event.exitCode] = (exitCodes[event.exitCode] ?? 0) + 1;
2147
+ }
2132
2148
  }
2133
2149
  }
2134
2150
  return {
@@ -2137,6 +2153,11 @@ var summarizeCoreResearchTrace = async (filePaths = []) => {
2137
2153
  Object.entries(skipCounts).sort(
2138
2154
  ([leftKey, leftValue], [rightKey, rightValue]) => rightValue - leftValue || (leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0)
2139
2155
  )
2156
+ ),
2157
+ exitCodes: Object.fromEntries(
2158
+ Object.entries(exitCodes).sort(
2159
+ ([leftKey, leftValue], [rightKey, rightValue]) => rightValue - leftValue || (leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0)
2160
+ )
2140
2161
  )
2141
2162
  };
2142
2163
  };
@@ -682,7 +682,12 @@ var writeCoreResearchMatches = async (params) => {
682
682
  "controlPnl",
683
683
  "candidatePnl",
684
684
  "pnlDelta",
685
- "exitReasonChanged"
685
+ "controlExitReason",
686
+ "candidateExitReason",
687
+ "exitReasonChanged",
688
+ "controlExitCode",
689
+ "candidateExitCode",
690
+ "exitCodeChanged"
686
691
  ].map(csv).join(",") + "\n"
687
692
  );
688
693
  for (const comparison of params.comparisons) {
@@ -700,7 +705,12 @@ var writeCoreResearchMatches = async (params) => {
700
705
  pair.control.netProfit,
701
706
  pair.candidate.netProfit,
702
707
  pair.pnlDelta,
703
- pair.exitReasonChanged
708
+ pair.control.exitReason,
709
+ pair.candidate.exitReason,
710
+ pair.exitReasonChanged,
711
+ pair.control.exitCode,
712
+ pair.candidate.exitCode,
713
+ pair.exitCodeChanged
704
714
  ].map(csv).join(",") + "\n"
705
715
  );
706
716
  }
@@ -1317,6 +1327,7 @@ var compareCoreResearchVariants = (params) => {
1317
1327
  candidate: candidateTrade,
1318
1328
  pnlDelta: candidateTrade.netProfit - controlTrade.netProfit,
1319
1329
  exitReasonChanged: candidateTrade.exitReason !== controlTrade.exitReason,
1330
+ exitCodeChanged: candidateTrade.exitCode !== controlTrade.exitCode,
1320
1331
  entryTimestampDeltaMs: candidateTrade.entryTimestamp - controlTrade.entryTimestamp,
1321
1332
  exitTimestampDeltaMs: candidateTrade.exitTimestamp - controlTrade.exitTimestamp
1322
1333
  });
@@ -1528,6 +1539,7 @@ var normalizeCompletedTrade = (params) => {
1528
1539
  "totalSlippageCost"
1529
1540
  ),
1530
1541
  exitReason: result.exitReason,
1542
+ ...result.exitCode ? { exitCode: result.exitCode } : {},
1531
1543
  regime: resolveCoreResearchRegime(row)
1532
1544
  };
1533
1545
  };
@@ -1571,7 +1583,7 @@ var aggregatePositionCycles = (trades) => {
1571
1583
  }
1572
1584
  const primary = primaryRows[0];
1573
1585
  if (cycle.some(
1574
- (trade) => trade.exitTimestamp !== primary.exitTimestamp || trade.exitReason !== primary.exitReason
1586
+ (trade) => trade.exitTimestamp !== primary.exitTimestamp || trade.exitReason !== primary.exitReason || trade.exitCode !== primary.exitCode
1575
1587
  )) {
1576
1588
  throw new Error(
1577
1589
  `Position cycle ${identity} contains inconsistent final exits`
@@ -1753,7 +1765,7 @@ var buildCurveSvg = (tradesByVariant, cohort, curve) => {
1753
1765
  )}</svg><div class="legend">${series.map((entry) => `<span><i style="background:${entry.color}"></i>${escapeHtml(entry.id)}</span>`).join("")}</div>`;
1754
1766
  };
1755
1767
  var comparisonCards = (comparisons) => comparisons.map(
1756
- (comparison) => `<article class="card"><h3>${escapeHtml(comparison.candidateId)} vs ${escapeHtml(comparison.controlId)}</h3><p class="verdict ${comparison.selection.passed ? "positive" : "negative"}">${comparison.selection.passed ? "PASS selection" : "REJECT selection"}</p><dl><dt>Matched</dt><dd>${comparison.matched}</dd><dt>Control only</dt><dd>${comparison.controlOnly}</dd><dt>Candidate only</dt><dd>${comparison.candidateOnly}</dd><dt>Identity retained</dt><dd>${format(comparison.matchedIdentityPctOfControl)}%</dd><dt>Matched \u0394PnL</dt><dd>${format(comparison.cohorts.ALL.matchedPnlDelta)}</dd><dt>Removed control PnL</dt><dd>${format(comparison.cohorts.ALL.controlOnlyPnl)}</dd><dt>Added candidate PnL</dt><dd>${format(comparison.cohorts.ALL.candidateOnlyPnl)}</dd><dt>Aggregate \u0394PnL</dt><dd>${format(comparison.cohorts.ALL.delta.pnl)}</dd><dt>Aggregate \u0394DD</dt><dd>${format(comparison.cohorts.ALL.delta.realizedMaxDrawdown)}</dd><dt>Bootstrap P(\u0394&gt;0)</dt><dd>${format(comparison.bootstrap.probabilityPositive, 4)}</dd><dt>Holm p</dt><dd>${format(comparison.bootstrap.holmAdjustedPValue, 4)}</dd></dl><p>${comparison.selection.failedRules.length ? escapeHtml(comparison.selection.failedRules.map((rule) => `${rule.scope}.${rule.metric} ${rule.expected}`).join("; ")) : "All preregistered rules passed."}</p></article>`
1768
+ (comparison) => `<article class="card"><h3>${escapeHtml(comparison.candidateId)} vs ${escapeHtml(comparison.controlId)}</h3><p class="verdict ${comparison.selection.passed ? "positive" : "negative"}">${comparison.selection.passed ? "PASS selection" : "REJECT selection"}</p><dl><dt>Matched</dt><dd>${comparison.matched}</dd><dt>Control only</dt><dd>${comparison.controlOnly}</dd><dt>Candidate only</dt><dd>${comparison.candidateOnly}</dd><dt>Identity retained</dt><dd>${format(comparison.matchedIdentityPctOfControl)}%</dd><dt>Exit reason changes</dt><dd>${comparison.matchedPairs.filter((pair) => pair.exitReasonChanged).length}</dd><dt>Exit code changes</dt><dd>${comparison.matchedPairs.filter((pair) => pair.exitCodeChanged).length}</dd><dt>Matched \u0394PnL</dt><dd>${format(comparison.cohorts.ALL.matchedPnlDelta)}</dd><dt>Removed control PnL</dt><dd>${format(comparison.cohorts.ALL.controlOnlyPnl)}</dd><dt>Added candidate PnL</dt><dd>${format(comparison.cohorts.ALL.candidateOnlyPnl)}</dd><dt>Aggregate \u0394PnL</dt><dd>${format(comparison.cohorts.ALL.delta.pnl)}</dd><dt>Aggregate \u0394DD</dt><dd>${format(comparison.cohorts.ALL.delta.realizedMaxDrawdown)}</dd><dt>Bootstrap P(\u0394&gt;0)</dt><dd>${format(comparison.bootstrap.probabilityPositive, 4)}</dd><dt>Holm p</dt><dd>${format(comparison.bootstrap.holmAdjustedPValue, 4)}</dd></dl><p>${comparison.selection.failedRules.length ? escapeHtml(comparison.selection.failedRules.map((rule) => `${rule.scope}.${rule.metric} ${rule.expected}`).join("; ")) : "All preregistered rules passed."}</p></article>`
1757
1769
  ).join("");
1758
1770
  var regimeRows = (variants) => {
1759
1771
  const keys = [
@@ -1767,7 +1779,7 @@ var regimeRows = (variants) => {
1767
1779
  ).join("");
1768
1780
  };
1769
1781
  var traceRows = (variants) => variants.map(
1770
- (variant) => `<tr><td>${escapeHtml(variant.variant.label)}</td><td>${escapeHtml(variant.reconciliation.status)}</td><td>${escapeHtml(JSON.stringify(variant.setupIdentitySources))}</td><td>${escapeHtml(JSON.stringify(variant.traceFunnel.events))}</td><td>${escapeHtml(JSON.stringify(variant.traceFunnel.skipCounts))}</td><td>${escapeHtml(variant.latestSignalTimeRegime?.key ?? "n/a")}</td></tr>`
1782
+ (variant) => `<tr><td>${escapeHtml(variant.variant.label)}</td><td>${escapeHtml(variant.reconciliation.status)}</td><td>${escapeHtml(JSON.stringify(variant.setupIdentitySources))}</td><td>${escapeHtml(JSON.stringify(variant.traceFunnel.events))}</td><td>${escapeHtml(JSON.stringify(variant.traceFunnel.skipCounts))}</td><td>${escapeHtml(JSON.stringify(variant.traceFunnel.exitCodes))}</td><td>${escapeHtml(variant.latestSignalTimeRegime?.key ?? "n/a")}</td></tr>`
1771
1783
  ).join("");
1772
1784
  var buildCoreResearchHtml = (params) => {
1773
1785
  const { spec, result, tradesByVariant } = params;
@@ -1793,7 +1805,7 @@ var buildCoreResearchHtml = (params) => {
1793
1805
  ([key, value]) => `<span class="${value === "present" ? "positive" : "negative"}">${escapeHtml(key)}: ${value}</span>`
1794
1806
  ).join(
1795
1807
  ""
1796
- )}</section><h2>ALL equity</h2>${buildCurveSvg(tradesByVariant, "ALL", "equity")}<h2>ALL realized drawdown</h2>${buildCurveSvg(tradesByVariant, "ALL", "drawdown")}<h2>LONG equity</h2>${buildCurveSvg(tradesByVariant, "LONG", "equity")}<h2>LONG realized drawdown</h2>${buildCurveSvg(tradesByVariant, "LONG", "drawdown")}<h2>SHORT equity</h2>${buildCurveSvg(tradesByVariant, "SHORT", "equity")}<h2>SHORT realized drawdown</h2>${buildCurveSvg(tradesByVariant, "SHORT", "drawdown")}<h2>Fixed cohort metrics</h2><p class="muted">Avg PnL/trade = cohort PnL / cohort N. ALL drawdown is aggregate portfolio; LONG/SHORT drawdowns are side-only.</p><div class="scroll"><table><thead><tr><th>Variant</th><th>Window</th><th>Cohort</th><th>N</th><th>PnL</th><th>Avg PnL/trade</th><th>PF</th><th>WR</th><th>Realized DD</th><th>Cad/day</th><th>Avg win</th><th>Avg loss</th><th>Payoff</th><th>Median</th><th>P05</th><th>P95</th><th>Median hold h</th><th>Max loss streak</th></tr></thead><tbody>${allRows}</tbody></table></div><h2>Fold and monthly robustness</h2><div class="scroll"><table><thead><tr><th>Variant</th><th>Window</th><th>Cohort</th><th>N</th><th>PnL</th><th>Avg PnL/trade</th><th>PF</th><th>WR</th><th>Realized DD</th><th>Cad/day</th><th>Avg win</th><th>Avg loss</th><th>Payoff</th><th>Median</th><th>P05</th><th>P95</th><th>Median hold h</th><th>Max loss streak</th></tr></thead><tbody>${robustnessRows}</tbody></table></div><h2>Cost stress</h2><div class="scroll"><table><thead><tr><th>Variant</th><th>Extra round-trip bps</th><th>N</th><th>PnL</th><th>Avg PnL/trade</th><th>PF</th><th>Realized DD</th></tr></thead><tbody>${costRows}</tbody></table></div><h2>Signal-time market regimes</h2><div class="scroll"><table><thead><tr><th>Trend | Volatility | Breadth | Derivatives</th><th>Variant</th><th>N</th><th>PnL</th><th>PF</th><th>WR</th></tr></thead><tbody>${regimeRows(result.variants)}</tbody></table></div><h2>Trace funnel and reconciliation</h2><div class="scroll"><table><thead><tr><th>Variant</th><th>Reconcile</th><th>Setup identity source</th><th>Events</th><th>Top skips</th><th>Latest signal-time regime</th></tr></thead><tbody>${traceRows(result.variants)}</tbody></table></div><h2>Multiple testing / overfitting</h2><pre>${escapeHtml(JSON.stringify({ multipleTesting: result.multipleTesting, overfittingDiagnostics: result.overfittingDiagnostics }, null, 2))}</pre><h2>Lineage</h2><pre>${escapeHtml(JSON.stringify({ stage: spec.stage, universeSha256: spec.universe.sha256, lineage: spec.lineage ?? null, artifactHashes: result.artifactHashes }, null, 2))}</pre></main></body></html>`;
1808
+ )}</section><h2>ALL equity</h2>${buildCurveSvg(tradesByVariant, "ALL", "equity")}<h2>ALL realized drawdown</h2>${buildCurveSvg(tradesByVariant, "ALL", "drawdown")}<h2>LONG equity</h2>${buildCurveSvg(tradesByVariant, "LONG", "equity")}<h2>LONG realized drawdown</h2>${buildCurveSvg(tradesByVariant, "LONG", "drawdown")}<h2>SHORT equity</h2>${buildCurveSvg(tradesByVariant, "SHORT", "equity")}<h2>SHORT realized drawdown</h2>${buildCurveSvg(tradesByVariant, "SHORT", "drawdown")}<h2>Fixed cohort metrics</h2><p class="muted">Avg PnL/trade = cohort PnL / cohort completed positions. ALL drawdown is aggregate portfolio; LONG/SHORT drawdowns are side-only.</p><div class="scroll"><table><thead><tr><th>Variant</th><th>Window</th><th>Cohort</th><th>N</th><th>PnL</th><th>Avg PnL/trade</th><th>PF</th><th>WR</th><th>Realized DD</th><th>Cad/day</th><th>Avg win</th><th>Avg loss</th><th>Payoff</th><th>Median</th><th>P05</th><th>P95</th><th>Median hold h</th><th>Max loss streak</th></tr></thead><tbody>${allRows}</tbody></table></div><h2>Fold and monthly robustness</h2><div class="scroll"><table><thead><tr><th>Variant</th><th>Window</th><th>Cohort</th><th>N</th><th>PnL</th><th>Avg PnL/trade</th><th>PF</th><th>WR</th><th>Realized DD</th><th>Cad/day</th><th>Avg win</th><th>Avg loss</th><th>Payoff</th><th>Median</th><th>P05</th><th>P95</th><th>Median hold h</th><th>Max loss streak</th></tr></thead><tbody>${robustnessRows}</tbody></table></div><h2>Cost stress</h2><div class="scroll"><table><thead><tr><th>Variant</th><th>Extra round-trip bps</th><th>N</th><th>PnL</th><th>Avg PnL/trade</th><th>PF</th><th>Realized DD</th></tr></thead><tbody>${costRows}</tbody></table></div><h2>Signal-time market regimes</h2><div class="scroll"><table><thead><tr><th>Trend | Volatility | Breadth | Derivatives</th><th>Variant</th><th>N</th><th>PnL</th><th>PF</th><th>WR</th></tr></thead><tbody>${regimeRows(result.variants)}</tbody></table></div><h2>Trace funnel and reconciliation</h2><div class="scroll"><table><thead><tr><th>Variant</th><th>Reconcile</th><th>Setup identity source</th><th>Events</th><th>Top skips</th><th>Exit codes</th><th>Latest signal-time regime</th></tr></thead><tbody>${traceRows(result.variants)}</tbody></table></div><h2>Multiple testing / overfitting</h2><pre>${escapeHtml(JSON.stringify({ multipleTesting: result.multipleTesting, overfittingDiagnostics: result.overfittingDiagnostics }, null, 2))}</pre><h2>Lineage</h2><pre>${escapeHtml(JSON.stringify({ stage: spec.stage, universeSha256: spec.universe.sha256, lineage: spec.lineage ?? null, artifactHashes: result.artifactHashes }, null, 2))}</pre></main></body></html>`;
1797
1809
  };
1798
1810
 
1799
1811
  // src/lib/backtest/checkpoint.ts
@@ -2099,8 +2111,9 @@ var applyCoreResearchRobustnessGuardrails = (params) => {
2099
2111
  var import_promises3 = __toESM(require("fs/promises"));
2100
2112
  var import_node_path4 = __toESM(require("path"));
2101
2113
  var summarizeCoreResearchTrace = async (filePaths = []) => {
2102
- const events = {};
2103
- const skipCounts = {};
2114
+ const events = /* @__PURE__ */ Object.create(null);
2115
+ const skipCounts = /* @__PURE__ */ Object.create(null);
2116
+ const exitCodes = /* @__PURE__ */ Object.create(null);
2104
2117
  for (const inputPath of filePaths) {
2105
2118
  const filePath = import_node_path4.default.resolve(inputPath);
2106
2119
  const text = await import_promises3.default.readFile(filePath, "utf8");
@@ -2120,6 +2133,9 @@ var summarizeCoreResearchTrace = async (filePaths = []) => {
2120
2133
  skipCounts[reason] = (skipCounts[reason] ?? 0) + count;
2121
2134
  }
2122
2135
  }
2136
+ if (event.event === "position_exited" && event.exitCode) {
2137
+ exitCodes[event.exitCode] = (exitCodes[event.exitCode] ?? 0) + 1;
2138
+ }
2123
2139
  }
2124
2140
  }
2125
2141
  return {
@@ -2128,6 +2144,11 @@ var summarizeCoreResearchTrace = async (filePaths = []) => {
2128
2144
  Object.entries(skipCounts).sort(
2129
2145
  ([leftKey, leftValue], [rightKey, rightValue]) => rightValue - leftValue || (leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0)
2130
2146
  )
2147
+ ),
2148
+ exitCodes: Object.fromEntries(
2149
+ Object.entries(exitCodes).sort(
2150
+ ([leftKey, leftValue], [rightKey, rightValue]) => rightValue - leftValue || (leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0)
2151
+ )
2131
2152
  )
2132
2153
  };
2133
2154
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tradejs/cli",
3
- "version": "3.1.29-beta.266",
3
+ "version": "3.1.29-beta.268",
4
4
  "description": "Official CLI for the TradeJS TypeScript framework: infra setup, backtests, signals, bots, and ML workflows.",
5
5
  "keywords": [
6
6
  "tradejs",
@@ -37,13 +37,13 @@
37
37
  "tradejs": "dist/cli.js"
38
38
  },
39
39
  "dependencies": {
40
- "@tradejs/connectors": "^3.1.29-beta.266",
41
- "@tradejs/core": "^3.1.29-beta.266",
42
- "@tradejs/indicators": "^3.1.29-beta.266",
43
- "@tradejs/infra": "^3.1.29-beta.266",
44
- "@tradejs/node": "^3.1.29-beta.266",
40
+ "@tradejs/connectors": "^3.1.29-beta.268",
41
+ "@tradejs/core": "^3.1.29-beta.268",
42
+ "@tradejs/indicators": "^3.1.29-beta.268",
43
+ "@tradejs/infra": "^3.1.29-beta.268",
44
+ "@tradejs/node": "^3.1.29-beta.268",
45
45
  "@tradejs/strategy-kit": "3.0.6",
46
- "@tradejs/types": "^3.1.29-beta.266",
46
+ "@tradejs/types": "^3.1.29-beta.268",
47
47
  "args": "^5.0.3",
48
48
  "bcryptjs": "^2.4.3",
49
49
  "chalk": "4.1.2",