@tradejs/cli 3.1.29-beta.265 → 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 +125 -58
- package/dist/lib/coreResearch.js +29 -8
- package/dist/lib/runtimeFeedbackReplay.js +49 -40
- package/dist/lib/runtimeParity.js +31 -2
- package/dist/lib/runtimeParityDetails.js +23 -7
- package/dist/scripts/coreResearch.js +29 -8
- package/dist/scripts/replay.js +45 -9
- package/dist/scripts/replayRunner.js +45 -9
- package/dist/scripts/runtimeFeedbackReplay.js +49 -40
- package/dist/scripts/runtimeParity.js +10 -2
- package/package.json +7 -7
|
@@ -20,23 +20,50 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/lib/runtimeParity.ts
|
|
21
21
|
var runtimeParity_exports = {};
|
|
22
22
|
__export(runtimeParity_exports, {
|
|
23
|
+
buildTradeParityTimestampOffset: () => buildTradeParityTimestampOffset,
|
|
23
24
|
compareTradeParityEntries: () => compareTradeParityEntries,
|
|
24
25
|
dedupeRuntimeParityEntries: () => dedupeRuntimeParityEntries,
|
|
25
26
|
extractBacktestEntryParityEntries: () => extractBacktestEntryParityEntries,
|
|
26
27
|
extractRuntimeParityEntries: () => extractRuntimeParityEntries,
|
|
27
28
|
findRuntimeDuplicateGroups: () => findRuntimeDuplicateGroups,
|
|
28
29
|
getBacktestParityComparisonTimestamp: () => getBacktestParityComparisonTimestamp,
|
|
30
|
+
resolveTradeParityTimestampOffset: () => resolveTradeParityTimestampOffset,
|
|
29
31
|
summarizeMatchedParity: () => summarizeMatchedParity
|
|
30
32
|
});
|
|
31
33
|
module.exports = __toCommonJS(runtimeParity_exports);
|
|
34
|
+
var resolveTradeParityTimestampOffset = (strategy, offset) => typeof offset === "function" ? offset(strategy) : offset;
|
|
35
|
+
var buildTradeParityTimestampOffset = (strategies) => {
|
|
36
|
+
const intervals = /* @__PURE__ */ new Map();
|
|
37
|
+
for (const { strategyName, interval } of strategies) {
|
|
38
|
+
const minutes = Number(interval);
|
|
39
|
+
if (!Number.isFinite(minutes) || minutes <= 0) {
|
|
40
|
+
throw new Error(`Invalid embedded replay interval for ${strategyName}`);
|
|
41
|
+
}
|
|
42
|
+
intervals.set(strategyName, minutes * 60 * 1e3);
|
|
43
|
+
}
|
|
44
|
+
return (strategy) => {
|
|
45
|
+
const offsetMs = intervals.get(strategy);
|
|
46
|
+
if (offsetMs == null) {
|
|
47
|
+
throw new Error(`Missing embedded replay interval for ${strategy}`);
|
|
48
|
+
}
|
|
49
|
+
return offsetMs;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
32
52
|
var toFiniteNumberOrNull = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
33
53
|
var toGroupKey = (entry) => `${entry.strategy}::${entry.symbol}::${entry.direction}`;
|
|
34
54
|
var toRuntimeDuplicateKey = (entry) => `${toGroupKey(entry)}::${entry.timestamp}`;
|
|
35
55
|
var getBacktestParityComparisonTimestamp = (entry, timestampOffsetMs = 0) => {
|
|
36
|
-
|
|
56
|
+
const offsetMs = resolveTradeParityTimestampOffset(
|
|
57
|
+
entry.strategy,
|
|
58
|
+
timestampOffsetMs
|
|
59
|
+
);
|
|
60
|
+
if (!Number.isFinite(offsetMs) || offsetMs < 0) {
|
|
61
|
+
throw new Error(`Invalid backtest timestamp offset for ${entry.strategy}`);
|
|
62
|
+
}
|
|
63
|
+
if (offsetMs > 0 && entry.signalTimestamp != null && entry.timestamp >= entry.signalTimestamp + offsetMs) {
|
|
37
64
|
return entry.timestamp;
|
|
38
65
|
}
|
|
39
|
-
return (entry.signalTimestamp ?? entry.timestamp) +
|
|
66
|
+
return (entry.signalTimestamp ?? entry.timestamp) + offsetMs;
|
|
40
67
|
};
|
|
41
68
|
var sortTradeParityEntries = (left, right) => left.timestamp - right.timestamp || left.strategy.localeCompare(right.strategy) || left.symbol.localeCompare(right.symbol) || left.direction.localeCompare(right.direction) || left.id.localeCompare(right.id);
|
|
42
69
|
var buildPriceDeltaPct = (leftPrice, rightPrice) => {
|
|
@@ -301,11 +328,13 @@ var summarizeMatchedParity = (matched) => {
|
|
|
301
328
|
};
|
|
302
329
|
// Annotate the CommonJS export names for ESM import in node:
|
|
303
330
|
0 && (module.exports = {
|
|
331
|
+
buildTradeParityTimestampOffset,
|
|
304
332
|
compareTradeParityEntries,
|
|
305
333
|
dedupeRuntimeParityEntries,
|
|
306
334
|
extractBacktestEntryParityEntries,
|
|
307
335
|
extractRuntimeParityEntries,
|
|
308
336
|
findRuntimeDuplicateGroups,
|
|
309
337
|
getBacktestParityComparisonTimestamp,
|
|
338
|
+
resolveTradeParityTimestampOffset,
|
|
310
339
|
summarizeMatchedParity
|
|
311
340
|
});
|
|
@@ -30,11 +30,19 @@ module.exports = __toCommonJS(runtimeParityDetails_exports);
|
|
|
30
30
|
var import_trade = require("@tradejs/core/trade");
|
|
31
31
|
|
|
32
32
|
// src/lib/runtimeParity.ts
|
|
33
|
+
var resolveTradeParityTimestampOffset = (strategy, offset) => typeof offset === "function" ? offset(strategy) : offset;
|
|
33
34
|
var getBacktestParityComparisonTimestamp = (entry, timestampOffsetMs = 0) => {
|
|
34
|
-
|
|
35
|
+
const offsetMs = resolveTradeParityTimestampOffset(
|
|
36
|
+
entry.strategy,
|
|
37
|
+
timestampOffsetMs
|
|
38
|
+
);
|
|
39
|
+
if (!Number.isFinite(offsetMs) || offsetMs < 0) {
|
|
40
|
+
throw new Error(`Invalid backtest timestamp offset for ${entry.strategy}`);
|
|
41
|
+
}
|
|
42
|
+
if (offsetMs > 0 && entry.signalTimestamp != null && entry.timestamp >= entry.signalTimestamp + offsetMs) {
|
|
35
43
|
return entry.timestamp;
|
|
36
44
|
}
|
|
37
|
-
return (entry.signalTimestamp ?? entry.timestamp) +
|
|
45
|
+
return (entry.signalTimestamp ?? entry.timestamp) + offsetMs;
|
|
38
46
|
};
|
|
39
47
|
|
|
40
48
|
// src/lib/runtimeParityDetails.ts
|
|
@@ -78,9 +86,13 @@ var buildCostDetail = (entry) => ({
|
|
|
78
86
|
totalFee: entry.totalFee ?? null
|
|
79
87
|
});
|
|
80
88
|
var toBacktestParityDetail = (entry, timestampOffsetMs = 0) => {
|
|
89
|
+
const offsetMs = resolveTradeParityTimestampOffset(
|
|
90
|
+
entry.strategy,
|
|
91
|
+
timestampOffsetMs
|
|
92
|
+
);
|
|
81
93
|
const comparisonTimestamp = getBacktestParityComparisonTimestamp(
|
|
82
94
|
entry,
|
|
83
|
-
|
|
95
|
+
offsetMs
|
|
84
96
|
);
|
|
85
97
|
return {
|
|
86
98
|
source: "backtest",
|
|
@@ -90,7 +102,7 @@ var toBacktestParityDetail = (entry, timestampOffsetMs = 0) => {
|
|
|
90
102
|
qty: entry.qty ?? null,
|
|
91
103
|
timestamp: entry.timestamp,
|
|
92
104
|
signalTimestamp: entry.signalTimestamp ?? null,
|
|
93
|
-
comparisonTimestamp:
|
|
105
|
+
comparisonTimestamp: offsetMs === 0 ? null : comparisonTimestamp,
|
|
94
106
|
price: entry.price,
|
|
95
107
|
exitType: entry.exitType ?? null,
|
|
96
108
|
exitTimestamp: entry.exitTimestamp ?? null,
|
|
@@ -549,25 +561,29 @@ var buildReplayMismatchDrilldown = ({
|
|
|
549
561
|
let runtimeOnlyArtifactsIncluded = 0;
|
|
550
562
|
let backtestOnlyArtifactsIncluded = 0;
|
|
551
563
|
const runtimeOnlyDiagnostics = runtimeOnly.map((entry, index) => {
|
|
564
|
+
const entryOffsetMs = resolveTradeParityTimestampOffset(
|
|
565
|
+
getEntryStrategy(entry) ?? "",
|
|
566
|
+
backtestTimestampOffsetMs
|
|
567
|
+
);
|
|
552
568
|
const includeArtifacts = index < artifactLimit;
|
|
553
569
|
const nearestCandidate = nearestByEntry.get(entry);
|
|
554
570
|
const nearestReplaySignal = findNearestSignal({
|
|
555
571
|
entry,
|
|
556
572
|
signals: replaySignals,
|
|
557
573
|
toleranceMs,
|
|
558
|
-
timestampOffsetMs:
|
|
574
|
+
timestampOffsetMs: entryOffsetMs
|
|
559
575
|
});
|
|
560
576
|
const nearestReplayEvaluation = findNearestEvaluation({
|
|
561
577
|
entry,
|
|
562
578
|
evaluations: replaySignalEvaluations,
|
|
563
579
|
toleranceMs,
|
|
564
|
-
timestampOffsetMs:
|
|
580
|
+
timestampOffsetMs: entryOffsetMs
|
|
565
581
|
});
|
|
566
582
|
const nearestReplayEvaluationOutcome = findNearestReplayEvaluationOutcome({
|
|
567
583
|
entry,
|
|
568
584
|
lineageScopes: replayLineageScopes,
|
|
569
585
|
toleranceMs,
|
|
570
|
-
timestampOffsetMs:
|
|
586
|
+
timestampOffsetMs: entryOffsetMs
|
|
571
587
|
});
|
|
572
588
|
const classification = nearestReplaySignal ? classifySignalDiagnostic(nearestReplaySignal.signal) : nearestReplayEvaluation ? classifyEvaluationDiagnostic(nearestReplayEvaluation.evaluation) : nearestReplayEvaluationOutcome ? {
|
|
573
589
|
classification: "core_skipped",
|
|
@@ -682,7 +682,12 @@ var writeCoreResearchMatches = async (params) => {
|
|
|
682
682
|
"controlPnl",
|
|
683
683
|
"candidatePnl",
|
|
684
684
|
"pnlDelta",
|
|
685
|
-
"
|
|
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.
|
|
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>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>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
|
|
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/dist/scripts/replay.js
CHANGED
|
@@ -6056,14 +6056,39 @@ var buildReplayChartSnapshot = (params) => {
|
|
|
6056
6056
|
var import_chalk10 = __toESM(require("chalk"));
|
|
6057
6057
|
|
|
6058
6058
|
// src/lib/runtimeParity.ts
|
|
6059
|
+
var resolveTradeParityTimestampOffset = (strategy, offset) => typeof offset === "function" ? offset(strategy) : offset;
|
|
6060
|
+
var buildTradeParityTimestampOffset = (strategies) => {
|
|
6061
|
+
const intervals = /* @__PURE__ */ new Map();
|
|
6062
|
+
for (const { strategyName, interval } of strategies) {
|
|
6063
|
+
const minutes = Number(interval);
|
|
6064
|
+
if (!Number.isFinite(minutes) || minutes <= 0) {
|
|
6065
|
+
throw new Error(`Invalid embedded replay interval for ${strategyName}`);
|
|
6066
|
+
}
|
|
6067
|
+
intervals.set(strategyName, minutes * 60 * 1e3);
|
|
6068
|
+
}
|
|
6069
|
+
return (strategy) => {
|
|
6070
|
+
const offsetMs = intervals.get(strategy);
|
|
6071
|
+
if (offsetMs == null) {
|
|
6072
|
+
throw new Error(`Missing embedded replay interval for ${strategy}`);
|
|
6073
|
+
}
|
|
6074
|
+
return offsetMs;
|
|
6075
|
+
};
|
|
6076
|
+
};
|
|
6059
6077
|
var toFiniteNumberOrNull2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
6060
6078
|
var toGroupKey = (entry) => `${entry.strategy}::${entry.symbol}::${entry.direction}`;
|
|
6061
6079
|
var toRuntimeDuplicateKey = (entry) => `${toGroupKey(entry)}::${entry.timestamp}`;
|
|
6062
6080
|
var getBacktestParityComparisonTimestamp = (entry, timestampOffsetMs = 0) => {
|
|
6063
|
-
|
|
6081
|
+
const offsetMs = resolveTradeParityTimestampOffset(
|
|
6082
|
+
entry.strategy,
|
|
6083
|
+
timestampOffsetMs
|
|
6084
|
+
);
|
|
6085
|
+
if (!Number.isFinite(offsetMs) || offsetMs < 0) {
|
|
6086
|
+
throw new Error(`Invalid backtest timestamp offset for ${entry.strategy}`);
|
|
6087
|
+
}
|
|
6088
|
+
if (offsetMs > 0 && entry.signalTimestamp != null && entry.timestamp >= entry.signalTimestamp + offsetMs) {
|
|
6064
6089
|
return entry.timestamp;
|
|
6065
6090
|
}
|
|
6066
|
-
return (entry.signalTimestamp ?? entry.timestamp) +
|
|
6091
|
+
return (entry.signalTimestamp ?? entry.timestamp) + offsetMs;
|
|
6067
6092
|
};
|
|
6068
6093
|
var sortTradeParityEntries = (left, right) => left.timestamp - right.timestamp || left.strategy.localeCompare(right.strategy) || left.symbol.localeCompare(right.symbol) || left.direction.localeCompare(right.direction) || left.id.localeCompare(right.id);
|
|
6069
6094
|
var buildPriceDeltaPct = (leftPrice, rightPrice) => {
|
|
@@ -6869,9 +6894,13 @@ var buildCostDetail = (entry) => ({
|
|
|
6869
6894
|
totalFee: entry.totalFee ?? null
|
|
6870
6895
|
});
|
|
6871
6896
|
var toBacktestParityDetail = (entry, timestampOffsetMs = 0) => {
|
|
6897
|
+
const offsetMs = resolveTradeParityTimestampOffset(
|
|
6898
|
+
entry.strategy,
|
|
6899
|
+
timestampOffsetMs
|
|
6900
|
+
);
|
|
6872
6901
|
const comparisonTimestamp = getBacktestParityComparisonTimestamp(
|
|
6873
6902
|
entry,
|
|
6874
|
-
|
|
6903
|
+
offsetMs
|
|
6875
6904
|
);
|
|
6876
6905
|
return {
|
|
6877
6906
|
source: "backtest",
|
|
@@ -6881,7 +6910,7 @@ var toBacktestParityDetail = (entry, timestampOffsetMs = 0) => {
|
|
|
6881
6910
|
qty: entry.qty ?? null,
|
|
6882
6911
|
timestamp: entry.timestamp,
|
|
6883
6912
|
signalTimestamp: entry.signalTimestamp ?? null,
|
|
6884
|
-
comparisonTimestamp:
|
|
6913
|
+
comparisonTimestamp: offsetMs === 0 ? null : comparisonTimestamp,
|
|
6885
6914
|
price: entry.price,
|
|
6886
6915
|
exitType: entry.exitType ?? null,
|
|
6887
6916
|
exitTimestamp: entry.exitTimestamp ?? null,
|
|
@@ -7340,25 +7369,29 @@ var buildReplayMismatchDrilldown = ({
|
|
|
7340
7369
|
let runtimeOnlyArtifactsIncluded = 0;
|
|
7341
7370
|
let backtestOnlyArtifactsIncluded = 0;
|
|
7342
7371
|
const runtimeOnlyDiagnostics = runtimeOnly.map((entry, index) => {
|
|
7372
|
+
const entryOffsetMs = resolveTradeParityTimestampOffset(
|
|
7373
|
+
getEntryStrategy(entry) ?? "",
|
|
7374
|
+
backtestTimestampOffsetMs
|
|
7375
|
+
);
|
|
7343
7376
|
const includeArtifacts = index < artifactLimit;
|
|
7344
7377
|
const nearestCandidate = nearestByEntry.get(entry);
|
|
7345
7378
|
const nearestReplaySignal = findNearestSignal({
|
|
7346
7379
|
entry,
|
|
7347
7380
|
signals: replaySignals,
|
|
7348
7381
|
toleranceMs,
|
|
7349
|
-
timestampOffsetMs:
|
|
7382
|
+
timestampOffsetMs: entryOffsetMs
|
|
7350
7383
|
});
|
|
7351
7384
|
const nearestReplayEvaluation = findNearestEvaluation({
|
|
7352
7385
|
entry,
|
|
7353
7386
|
evaluations: replaySignalEvaluations,
|
|
7354
7387
|
toleranceMs,
|
|
7355
|
-
timestampOffsetMs:
|
|
7388
|
+
timestampOffsetMs: entryOffsetMs
|
|
7356
7389
|
});
|
|
7357
7390
|
const nearestReplayEvaluationOutcome = findNearestReplayEvaluationOutcome({
|
|
7358
7391
|
entry,
|
|
7359
7392
|
lineageScopes: replayLineageScopes,
|
|
7360
7393
|
toleranceMs,
|
|
7361
|
-
timestampOffsetMs:
|
|
7394
|
+
timestampOffsetMs: entryOffsetMs
|
|
7362
7395
|
});
|
|
7363
7396
|
const classification = nearestReplaySignal ? classifySignalDiagnostic(nearestReplaySignal.signal) : nearestReplayEvaluation ? classifyEvaluationDiagnostic(nearestReplayEvaluation.evaluation) : nearestReplayEvaluationOutcome ? {
|
|
7364
7397
|
classification: "core_skipped",
|
|
@@ -8593,6 +8626,9 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
8593
8626
|
expectedUserName: replayUserName,
|
|
8594
8627
|
expectedWindow: window
|
|
8595
8628
|
}) : null;
|
|
8629
|
+
const replayTimestampOffset = runtimeEvidence ? buildTradeParityTimestampOffset(
|
|
8630
|
+
(0, runtimeEvidenceDeployment_exports.activeRuntimeEvidenceStrategies)(runtimeEvidence.deployment)
|
|
8631
|
+
) : getReplayEntryTimestampCompareOffsetMs();
|
|
8596
8632
|
const [
|
|
8597
8633
|
rawRuntimeTrades,
|
|
8598
8634
|
runtimeSignals,
|
|
@@ -8702,7 +8738,7 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
8702
8738
|
runtimeEntries: runtimeDedupe.entries,
|
|
8703
8739
|
backtestEntries: comparableBacktestEntries,
|
|
8704
8740
|
toleranceMs: REPLAY_RUNTIME_COMPARE_TOLERANCE_MS,
|
|
8705
|
-
backtestTimestampOffsetMs:
|
|
8741
|
+
backtestTimestampOffsetMs: replayTimestampOffset
|
|
8706
8742
|
});
|
|
8707
8743
|
const details = buildReplayRuntimeComparisonDetails({
|
|
8708
8744
|
matched: comparison.matched,
|
|
@@ -8711,7 +8747,7 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
8711
8747
|
runtimeEntries: runtimeDedupe.entries,
|
|
8712
8748
|
backtestEntries: comparableBacktestEntries,
|
|
8713
8749
|
toleranceMs: REPLAY_RUNTIME_COMPARE_TOLERANCE_MS,
|
|
8714
|
-
backtestTimestampOffsetMs:
|
|
8750
|
+
backtestTimestampOffsetMs: replayTimestampOffset,
|
|
8715
8751
|
runtimeSignals: comparable.runtimeSignals,
|
|
8716
8752
|
runtimeSignalEvaluations: comparable.runtimeSignalEvaluations,
|
|
8717
8753
|
replaySignals,
|
|
@@ -6059,14 +6059,39 @@ var buildReplayChartSnapshot = (params) => {
|
|
|
6059
6059
|
var import_chalk10 = __toESM(require("chalk"));
|
|
6060
6060
|
|
|
6061
6061
|
// src/lib/runtimeParity.ts
|
|
6062
|
+
var resolveTradeParityTimestampOffset = (strategy, offset) => typeof offset === "function" ? offset(strategy) : offset;
|
|
6063
|
+
var buildTradeParityTimestampOffset = (strategies) => {
|
|
6064
|
+
const intervals = /* @__PURE__ */ new Map();
|
|
6065
|
+
for (const { strategyName, interval } of strategies) {
|
|
6066
|
+
const minutes = Number(interval);
|
|
6067
|
+
if (!Number.isFinite(minutes) || minutes <= 0) {
|
|
6068
|
+
throw new Error(`Invalid embedded replay interval for ${strategyName}`);
|
|
6069
|
+
}
|
|
6070
|
+
intervals.set(strategyName, minutes * 60 * 1e3);
|
|
6071
|
+
}
|
|
6072
|
+
return (strategy) => {
|
|
6073
|
+
const offsetMs = intervals.get(strategy);
|
|
6074
|
+
if (offsetMs == null) {
|
|
6075
|
+
throw new Error(`Missing embedded replay interval for ${strategy}`);
|
|
6076
|
+
}
|
|
6077
|
+
return offsetMs;
|
|
6078
|
+
};
|
|
6079
|
+
};
|
|
6062
6080
|
var toFiniteNumberOrNull2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
6063
6081
|
var toGroupKey = (entry) => `${entry.strategy}::${entry.symbol}::${entry.direction}`;
|
|
6064
6082
|
var toRuntimeDuplicateKey = (entry) => `${toGroupKey(entry)}::${entry.timestamp}`;
|
|
6065
6083
|
var getBacktestParityComparisonTimestamp = (entry, timestampOffsetMs = 0) => {
|
|
6066
|
-
|
|
6084
|
+
const offsetMs = resolveTradeParityTimestampOffset(
|
|
6085
|
+
entry.strategy,
|
|
6086
|
+
timestampOffsetMs
|
|
6087
|
+
);
|
|
6088
|
+
if (!Number.isFinite(offsetMs) || offsetMs < 0) {
|
|
6089
|
+
throw new Error(`Invalid backtest timestamp offset for ${entry.strategy}`);
|
|
6090
|
+
}
|
|
6091
|
+
if (offsetMs > 0 && entry.signalTimestamp != null && entry.timestamp >= entry.signalTimestamp + offsetMs) {
|
|
6067
6092
|
return entry.timestamp;
|
|
6068
6093
|
}
|
|
6069
|
-
return (entry.signalTimestamp ?? entry.timestamp) +
|
|
6094
|
+
return (entry.signalTimestamp ?? entry.timestamp) + offsetMs;
|
|
6070
6095
|
};
|
|
6071
6096
|
var sortTradeParityEntries = (left, right) => left.timestamp - right.timestamp || left.strategy.localeCompare(right.strategy) || left.symbol.localeCompare(right.symbol) || left.direction.localeCompare(right.direction) || left.id.localeCompare(right.id);
|
|
6072
6097
|
var buildPriceDeltaPct = (leftPrice, rightPrice) => {
|
|
@@ -6872,9 +6897,13 @@ var buildCostDetail = (entry) => ({
|
|
|
6872
6897
|
totalFee: entry.totalFee ?? null
|
|
6873
6898
|
});
|
|
6874
6899
|
var toBacktestParityDetail = (entry, timestampOffsetMs = 0) => {
|
|
6900
|
+
const offsetMs = resolveTradeParityTimestampOffset(
|
|
6901
|
+
entry.strategy,
|
|
6902
|
+
timestampOffsetMs
|
|
6903
|
+
);
|
|
6875
6904
|
const comparisonTimestamp = getBacktestParityComparisonTimestamp(
|
|
6876
6905
|
entry,
|
|
6877
|
-
|
|
6906
|
+
offsetMs
|
|
6878
6907
|
);
|
|
6879
6908
|
return {
|
|
6880
6909
|
source: "backtest",
|
|
@@ -6884,7 +6913,7 @@ var toBacktestParityDetail = (entry, timestampOffsetMs = 0) => {
|
|
|
6884
6913
|
qty: entry.qty ?? null,
|
|
6885
6914
|
timestamp: entry.timestamp,
|
|
6886
6915
|
signalTimestamp: entry.signalTimestamp ?? null,
|
|
6887
|
-
comparisonTimestamp:
|
|
6916
|
+
comparisonTimestamp: offsetMs === 0 ? null : comparisonTimestamp,
|
|
6888
6917
|
price: entry.price,
|
|
6889
6918
|
exitType: entry.exitType ?? null,
|
|
6890
6919
|
exitTimestamp: entry.exitTimestamp ?? null,
|
|
@@ -7343,25 +7372,29 @@ var buildReplayMismatchDrilldown = ({
|
|
|
7343
7372
|
let runtimeOnlyArtifactsIncluded = 0;
|
|
7344
7373
|
let backtestOnlyArtifactsIncluded = 0;
|
|
7345
7374
|
const runtimeOnlyDiagnostics = runtimeOnly.map((entry, index) => {
|
|
7375
|
+
const entryOffsetMs = resolveTradeParityTimestampOffset(
|
|
7376
|
+
getEntryStrategy(entry) ?? "",
|
|
7377
|
+
backtestTimestampOffsetMs
|
|
7378
|
+
);
|
|
7346
7379
|
const includeArtifacts = index < artifactLimit;
|
|
7347
7380
|
const nearestCandidate = nearestByEntry.get(entry);
|
|
7348
7381
|
const nearestReplaySignal = findNearestSignal({
|
|
7349
7382
|
entry,
|
|
7350
7383
|
signals: replaySignals,
|
|
7351
7384
|
toleranceMs,
|
|
7352
|
-
timestampOffsetMs:
|
|
7385
|
+
timestampOffsetMs: entryOffsetMs
|
|
7353
7386
|
});
|
|
7354
7387
|
const nearestReplayEvaluation = findNearestEvaluation({
|
|
7355
7388
|
entry,
|
|
7356
7389
|
evaluations: replaySignalEvaluations,
|
|
7357
7390
|
toleranceMs,
|
|
7358
|
-
timestampOffsetMs:
|
|
7391
|
+
timestampOffsetMs: entryOffsetMs
|
|
7359
7392
|
});
|
|
7360
7393
|
const nearestReplayEvaluationOutcome = findNearestReplayEvaluationOutcome({
|
|
7361
7394
|
entry,
|
|
7362
7395
|
lineageScopes: replayLineageScopes,
|
|
7363
7396
|
toleranceMs,
|
|
7364
|
-
timestampOffsetMs:
|
|
7397
|
+
timestampOffsetMs: entryOffsetMs
|
|
7365
7398
|
});
|
|
7366
7399
|
const classification = nearestReplaySignal ? classifySignalDiagnostic(nearestReplaySignal.signal) : nearestReplayEvaluation ? classifyEvaluationDiagnostic(nearestReplayEvaluation.evaluation) : nearestReplayEvaluationOutcome ? {
|
|
7367
7400
|
classification: "core_skipped",
|
|
@@ -8596,6 +8629,9 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
8596
8629
|
expectedUserName: replayUserName,
|
|
8597
8630
|
expectedWindow: window
|
|
8598
8631
|
}) : null;
|
|
8632
|
+
const replayTimestampOffset = runtimeEvidence ? buildTradeParityTimestampOffset(
|
|
8633
|
+
(0, runtimeEvidenceDeployment_exports.activeRuntimeEvidenceStrategies)(runtimeEvidence.deployment)
|
|
8634
|
+
) : getReplayEntryTimestampCompareOffsetMs();
|
|
8599
8635
|
const [
|
|
8600
8636
|
rawRuntimeTrades,
|
|
8601
8637
|
runtimeSignals,
|
|
@@ -8705,7 +8741,7 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
8705
8741
|
runtimeEntries: runtimeDedupe.entries,
|
|
8706
8742
|
backtestEntries: comparableBacktestEntries,
|
|
8707
8743
|
toleranceMs: REPLAY_RUNTIME_COMPARE_TOLERANCE_MS,
|
|
8708
|
-
backtestTimestampOffsetMs:
|
|
8744
|
+
backtestTimestampOffsetMs: replayTimestampOffset
|
|
8709
8745
|
});
|
|
8710
8746
|
const details = buildReplayRuntimeComparisonDetails({
|
|
8711
8747
|
matched: comparison.matched,
|
|
@@ -8714,7 +8750,7 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
8714
8750
|
runtimeEntries: runtimeDedupe.entries,
|
|
8715
8751
|
backtestEntries: comparableBacktestEntries,
|
|
8716
8752
|
toleranceMs: REPLAY_RUNTIME_COMPARE_TOLERANCE_MS,
|
|
8717
|
-
backtestTimestampOffsetMs:
|
|
8753
|
+
backtestTimestampOffsetMs: replayTimestampOffset,
|
|
8718
8754
|
runtimeSignals: comparable.runtimeSignals,
|
|
8719
8755
|
runtimeSignalEvaluations: comparable.runtimeSignalEvaluations,
|
|
8720
8756
|
replaySignals,
|
|
@@ -137,41 +137,52 @@ var buildRuntimeFeedbackReplayCommands = ({
|
|
|
137
137
|
interval,
|
|
138
138
|
startTime,
|
|
139
139
|
endTime
|
|
140
|
-
}) =>
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
140
|
+
}) => {
|
|
141
|
+
const intervals = interval.split(",").map((value) => value.trim());
|
|
142
|
+
if (!intervals.length || intervals.some(
|
|
143
|
+
(value) => !value || !Number.isFinite(Number(value)) || Number(value) <= 0
|
|
144
|
+
)) {
|
|
145
|
+
throw new Error(`Invalid runtime feedback replay intervals: ${interval}`);
|
|
146
|
+
}
|
|
147
|
+
const replayTimeframe = intervals.sort(
|
|
148
|
+
(left, right) => Number(left) - Number(right)
|
|
149
|
+
)[0];
|
|
150
|
+
return [
|
|
151
|
+
[
|
|
152
|
+
cliPath,
|
|
153
|
+
"replay",
|
|
154
|
+
"--user",
|
|
155
|
+
userName,
|
|
156
|
+
"--connector",
|
|
157
|
+
connectorName,
|
|
158
|
+
"--deployment",
|
|
159
|
+
deploymentId,
|
|
160
|
+
"--timeframe",
|
|
161
|
+
replayTimeframe,
|
|
162
|
+
"--startTime",
|
|
163
|
+
String(startTime),
|
|
164
|
+
"--endTime",
|
|
165
|
+
String(endTime),
|
|
166
|
+
"--runtimeEvidence",
|
|
167
|
+
runtimeEvidencePath,
|
|
168
|
+
"--cacheOnly"
|
|
169
|
+
],
|
|
170
|
+
[
|
|
171
|
+
cliPath,
|
|
172
|
+
"replay-runtime-evidence",
|
|
173
|
+
"--user",
|
|
174
|
+
userName,
|
|
175
|
+
"--runtimeEvidence",
|
|
176
|
+
runtimeEvidencePath,
|
|
177
|
+
"--startTime",
|
|
178
|
+
String(startTime),
|
|
179
|
+
"--endTime",
|
|
180
|
+
String(endTime),
|
|
181
|
+
"--out",
|
|
182
|
+
replayEvidencePath
|
|
183
|
+
]
|
|
184
|
+
];
|
|
185
|
+
};
|
|
175
186
|
var runLoggedCommand = async ({
|
|
176
187
|
executable,
|
|
177
188
|
command,
|
|
@@ -258,10 +269,8 @@ var runRuntimeFeedbackReplay = async ({
|
|
|
258
269
|
const intervals = new Set(
|
|
259
270
|
activeStrategies.map(({ interval }) => String(interval))
|
|
260
271
|
);
|
|
261
|
-
if (intervals.size
|
|
262
|
-
throw new Error(
|
|
263
|
-
`Runtime feedback replay requires one interval, received ${[...intervals].join(",")}`
|
|
264
|
-
);
|
|
272
|
+
if (!intervals.size) {
|
|
273
|
+
throw new Error("Runtime feedback replay requires an active interval");
|
|
265
274
|
}
|
|
266
275
|
await import_promises.default.mkdir(outDir, { recursive: true });
|
|
267
276
|
const existing = await import_promises.default.readdir(outDir);
|
|
@@ -282,7 +291,7 @@ var runRuntimeFeedbackReplay = async ({
|
|
|
282
291
|
userName: runtimeEvidenceBundle.manifest.userName,
|
|
283
292
|
connectorName: deployment.connectorName,
|
|
284
293
|
deploymentId: deployment.id,
|
|
285
|
-
interval: [...intervals]
|
|
294
|
+
interval: [...intervals].join(","),
|
|
286
295
|
startTime: runtimeEvidenceBundle.manifest.window.startTime,
|
|
287
296
|
endTime: runtimeEvidenceBundle.manifest.window.endTime
|
|
288
297
|
});
|