@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
package/dist/cli.js
CHANGED
|
@@ -16947,18 +16947,43 @@ var init_chartSnapshot = __esm({
|
|
|
16947
16947
|
});
|
|
16948
16948
|
|
|
16949
16949
|
// src/lib/runtimeParity.ts
|
|
16950
|
-
var toFiniteNumberOrNull2, toGroupKey, toRuntimeDuplicateKey, getBacktestParityComparisonTimestamp, sortTradeParityEntries, buildPriceDeltaPct, toExitType, resolveBacktestExitType, extractBacktestEntryParityEntries, extractRuntimeParityEntries, findRuntimeDuplicateGroups, dedupeRuntimeParityEntries, compareTradeParityEntries, summarizeMatchedParity;
|
|
16950
|
+
var resolveTradeParityTimestampOffset, buildTradeParityTimestampOffset, toFiniteNumberOrNull2, toGroupKey, toRuntimeDuplicateKey, getBacktestParityComparisonTimestamp, sortTradeParityEntries, buildPriceDeltaPct, toExitType, resolveBacktestExitType, extractBacktestEntryParityEntries, extractRuntimeParityEntries, findRuntimeDuplicateGroups, dedupeRuntimeParityEntries, compareTradeParityEntries, summarizeMatchedParity;
|
|
16951
16951
|
var init_runtimeParity = __esm({
|
|
16952
16952
|
"src/lib/runtimeParity.ts"() {
|
|
16953
16953
|
"use strict";
|
|
16954
|
+
resolveTradeParityTimestampOffset = (strategy, offset) => typeof offset === "function" ? offset(strategy) : offset;
|
|
16955
|
+
buildTradeParityTimestampOffset = (strategies) => {
|
|
16956
|
+
const intervals = /* @__PURE__ */ new Map();
|
|
16957
|
+
for (const { strategyName, interval: interval4 } of strategies) {
|
|
16958
|
+
const minutes = Number(interval4);
|
|
16959
|
+
if (!Number.isFinite(minutes) || minutes <= 0) {
|
|
16960
|
+
throw new Error(`Invalid embedded replay interval for ${strategyName}`);
|
|
16961
|
+
}
|
|
16962
|
+
intervals.set(strategyName, minutes * 60 * 1e3);
|
|
16963
|
+
}
|
|
16964
|
+
return (strategy) => {
|
|
16965
|
+
const offsetMs = intervals.get(strategy);
|
|
16966
|
+
if (offsetMs == null) {
|
|
16967
|
+
throw new Error(`Missing embedded replay interval for ${strategy}`);
|
|
16968
|
+
}
|
|
16969
|
+
return offsetMs;
|
|
16970
|
+
};
|
|
16971
|
+
};
|
|
16954
16972
|
toFiniteNumberOrNull2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
16955
16973
|
toGroupKey = (entry) => `${entry.strategy}::${entry.symbol}::${entry.direction}`;
|
|
16956
16974
|
toRuntimeDuplicateKey = (entry) => `${toGroupKey(entry)}::${entry.timestamp}`;
|
|
16957
16975
|
getBacktestParityComparisonTimestamp = (entry, timestampOffsetMs = 0) => {
|
|
16958
|
-
|
|
16976
|
+
const offsetMs = resolveTradeParityTimestampOffset(
|
|
16977
|
+
entry.strategy,
|
|
16978
|
+
timestampOffsetMs
|
|
16979
|
+
);
|
|
16980
|
+
if (!Number.isFinite(offsetMs) || offsetMs < 0) {
|
|
16981
|
+
throw new Error(`Invalid backtest timestamp offset for ${entry.strategy}`);
|
|
16982
|
+
}
|
|
16983
|
+
if (offsetMs > 0 && entry.signalTimestamp != null && entry.timestamp >= entry.signalTimestamp + offsetMs) {
|
|
16959
16984
|
return entry.timestamp;
|
|
16960
16985
|
}
|
|
16961
|
-
return (entry.signalTimestamp ?? entry.timestamp) +
|
|
16986
|
+
return (entry.signalTimestamp ?? entry.timestamp) + offsetMs;
|
|
16962
16987
|
};
|
|
16963
16988
|
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);
|
|
16964
16989
|
buildPriceDeltaPct = (leftPrice, rightPrice) => {
|
|
@@ -18014,9 +18039,13 @@ var init_runtimeParityDetails = __esm({
|
|
|
18014
18039
|
totalFee: entry.totalFee ?? null
|
|
18015
18040
|
});
|
|
18016
18041
|
toBacktestParityDetail = (entry, timestampOffsetMs = 0) => {
|
|
18042
|
+
const offsetMs = resolveTradeParityTimestampOffset(
|
|
18043
|
+
entry.strategy,
|
|
18044
|
+
timestampOffsetMs
|
|
18045
|
+
);
|
|
18017
18046
|
const comparisonTimestamp = getBacktestParityComparisonTimestamp(
|
|
18018
18047
|
entry,
|
|
18019
|
-
|
|
18048
|
+
offsetMs
|
|
18020
18049
|
);
|
|
18021
18050
|
return {
|
|
18022
18051
|
source: "backtest",
|
|
@@ -18026,7 +18055,7 @@ var init_runtimeParityDetails = __esm({
|
|
|
18026
18055
|
qty: entry.qty ?? null,
|
|
18027
18056
|
timestamp: entry.timestamp,
|
|
18028
18057
|
signalTimestamp: entry.signalTimestamp ?? null,
|
|
18029
|
-
comparisonTimestamp:
|
|
18058
|
+
comparisonTimestamp: offsetMs === 0 ? null : comparisonTimestamp,
|
|
18030
18059
|
price: entry.price,
|
|
18031
18060
|
exitType: entry.exitType ?? null,
|
|
18032
18061
|
exitTimestamp: entry.exitTimestamp ?? null,
|
|
@@ -18485,25 +18514,29 @@ var init_runtimeParityDetails = __esm({
|
|
|
18485
18514
|
let runtimeOnlyArtifactsIncluded = 0;
|
|
18486
18515
|
let backtestOnlyArtifactsIncluded = 0;
|
|
18487
18516
|
const runtimeOnlyDiagnostics = runtimeOnly.map((entry, index) => {
|
|
18517
|
+
const entryOffsetMs = resolveTradeParityTimestampOffset(
|
|
18518
|
+
getEntryStrategy(entry) ?? "",
|
|
18519
|
+
backtestTimestampOffsetMs
|
|
18520
|
+
);
|
|
18488
18521
|
const includeArtifacts = index < artifactLimit;
|
|
18489
18522
|
const nearestCandidate = nearestByEntry.get(entry);
|
|
18490
18523
|
const nearestReplaySignal = findNearestSignal({
|
|
18491
18524
|
entry,
|
|
18492
18525
|
signals: replaySignals,
|
|
18493
18526
|
toleranceMs,
|
|
18494
|
-
timestampOffsetMs:
|
|
18527
|
+
timestampOffsetMs: entryOffsetMs
|
|
18495
18528
|
});
|
|
18496
18529
|
const nearestReplayEvaluation = findNearestEvaluation({
|
|
18497
18530
|
entry,
|
|
18498
18531
|
evaluations: replaySignalEvaluations,
|
|
18499
18532
|
toleranceMs,
|
|
18500
|
-
timestampOffsetMs:
|
|
18533
|
+
timestampOffsetMs: entryOffsetMs
|
|
18501
18534
|
});
|
|
18502
18535
|
const nearestReplayEvaluationOutcome = findNearestReplayEvaluationOutcome({
|
|
18503
18536
|
entry,
|
|
18504
18537
|
lineageScopes: replayLineageScopes,
|
|
18505
18538
|
toleranceMs,
|
|
18506
|
-
timestampOffsetMs:
|
|
18539
|
+
timestampOffsetMs: entryOffsetMs
|
|
18507
18540
|
});
|
|
18508
18541
|
const classification = nearestReplaySignal ? classifySignalDiagnostic(nearestReplaySignal.signal) : nearestReplayEvaluation ? classifyEvaluationDiagnostic(nearestReplayEvaluation.evaluation) : nearestReplayEvaluationOutcome ? {
|
|
18509
18542
|
classification: "core_skipped",
|
|
@@ -19386,6 +19419,7 @@ var init_runtimeComparison = __esm({
|
|
|
19386
19419
|
init_cliConfig2();
|
|
19387
19420
|
init_runtimeEvidenceSource();
|
|
19388
19421
|
init_support();
|
|
19422
|
+
init_runtimeEvidenceDeployment();
|
|
19389
19423
|
init_runtimeComparisonCalculations();
|
|
19390
19424
|
init_runtimeComparisonReporting();
|
|
19391
19425
|
init_runtimeComparisonCalculations();
|
|
@@ -19775,6 +19809,9 @@ var init_runtimeComparison = __esm({
|
|
|
19775
19809
|
expectedUserName: replayUserName,
|
|
19776
19810
|
expectedWindow: window
|
|
19777
19811
|
}) : null;
|
|
19812
|
+
const replayTimestampOffset = runtimeEvidence2 ? buildTradeParityTimestampOffset(
|
|
19813
|
+
(0, runtimeEvidenceDeployment_exports.activeRuntimeEvidenceStrategies)(runtimeEvidence2.deployment)
|
|
19814
|
+
) : getReplayEntryTimestampCompareOffsetMs();
|
|
19778
19815
|
const [
|
|
19779
19816
|
rawRuntimeTrades,
|
|
19780
19817
|
runtimeSignals,
|
|
@@ -19884,7 +19921,7 @@ var init_runtimeComparison = __esm({
|
|
|
19884
19921
|
runtimeEntries: runtimeDedupe.entries,
|
|
19885
19922
|
backtestEntries: comparableBacktestEntries,
|
|
19886
19923
|
toleranceMs: REPLAY_RUNTIME_COMPARE_TOLERANCE_MS,
|
|
19887
|
-
backtestTimestampOffsetMs:
|
|
19924
|
+
backtestTimestampOffsetMs: replayTimestampOffset
|
|
19888
19925
|
});
|
|
19889
19926
|
const details = buildReplayRuntimeComparisonDetails({
|
|
19890
19927
|
matched: comparison.matched,
|
|
@@ -19893,7 +19930,7 @@ var init_runtimeComparison = __esm({
|
|
|
19893
19930
|
runtimeEntries: runtimeDedupe.entries,
|
|
19894
19931
|
backtestEntries: comparableBacktestEntries,
|
|
19895
19932
|
toleranceMs: REPLAY_RUNTIME_COMPARE_TOLERANCE_MS,
|
|
19896
|
-
backtestTimestampOffsetMs:
|
|
19933
|
+
backtestTimestampOffsetMs: replayTimestampOffset,
|
|
19897
19934
|
runtimeSignals: comparable.runtimeSignals,
|
|
19898
19935
|
runtimeSignalEvaluations: comparable.runtimeSignalEvaluations,
|
|
19899
19936
|
replaySignals,
|
|
@@ -28295,7 +28332,12 @@ var init_artifacts = __esm({
|
|
|
28295
28332
|
"controlPnl",
|
|
28296
28333
|
"candidatePnl",
|
|
28297
28334
|
"pnlDelta",
|
|
28298
|
-
"
|
|
28335
|
+
"controlExitReason",
|
|
28336
|
+
"candidateExitReason",
|
|
28337
|
+
"exitReasonChanged",
|
|
28338
|
+
"controlExitCode",
|
|
28339
|
+
"candidateExitCode",
|
|
28340
|
+
"exitCodeChanged"
|
|
28299
28341
|
].map(csv).join(",") + "\n"
|
|
28300
28342
|
);
|
|
28301
28343
|
for (const comparison of params.comparisons) {
|
|
@@ -28313,7 +28355,12 @@ var init_artifacts = __esm({
|
|
|
28313
28355
|
pair.control.netProfit,
|
|
28314
28356
|
pair.candidate.netProfit,
|
|
28315
28357
|
pair.pnlDelta,
|
|
28316
|
-
pair.
|
|
28358
|
+
pair.control.exitReason,
|
|
28359
|
+
pair.candidate.exitReason,
|
|
28360
|
+
pair.exitReasonChanged,
|
|
28361
|
+
pair.control.exitCode,
|
|
28362
|
+
pair.candidate.exitCode,
|
|
28363
|
+
pair.exitCodeChanged
|
|
28317
28364
|
].map(csv).join(",") + "\n"
|
|
28318
28365
|
);
|
|
28319
28366
|
}
|
|
@@ -28958,6 +29005,7 @@ var init_comparison = __esm({
|
|
|
28958
29005
|
candidate: candidateTrade,
|
|
28959
29006
|
pnlDelta: candidateTrade.netProfit - controlTrade.netProfit,
|
|
28960
29007
|
exitReasonChanged: candidateTrade.exitReason !== controlTrade.exitReason,
|
|
29008
|
+
exitCodeChanged: candidateTrade.exitCode !== controlTrade.exitCode,
|
|
28961
29009
|
entryTimestampDeltaMs: candidateTrade.entryTimestamp - controlTrade.entryTimestamp,
|
|
28962
29010
|
exitTimestampDeltaMs: candidateTrade.exitTimestamp - controlTrade.exitTimestamp
|
|
28963
29011
|
});
|
|
@@ -29175,6 +29223,7 @@ var init_dataset2 = __esm({
|
|
|
29175
29223
|
"totalSlippageCost"
|
|
29176
29224
|
),
|
|
29177
29225
|
exitReason: result.exitReason,
|
|
29226
|
+
...result.exitCode ? { exitCode: result.exitCode } : {},
|
|
29178
29227
|
regime: resolveCoreResearchRegime(row)
|
|
29179
29228
|
};
|
|
29180
29229
|
};
|
|
@@ -29218,7 +29267,7 @@ var init_dataset2 = __esm({
|
|
|
29218
29267
|
}
|
|
29219
29268
|
const primary = primaryRows[0];
|
|
29220
29269
|
if (cycle.some(
|
|
29221
|
-
(trade) => trade.exitTimestamp !== primary.exitTimestamp || trade.exitReason !== primary.exitReason
|
|
29270
|
+
(trade) => trade.exitTimestamp !== primary.exitTimestamp || trade.exitReason !== primary.exitReason || trade.exitCode !== primary.exitCode
|
|
29222
29271
|
)) {
|
|
29223
29272
|
throw new Error(
|
|
29224
29273
|
`Position cycle ${identity} contains inconsistent final exits`
|
|
@@ -29407,7 +29456,7 @@ var init_report = __esm({
|
|
|
29407
29456
|
)}</svg><div class="legend">${series.map((entry) => `<span><i style="background:${entry.color}"></i>${escapeHtml4(entry.id)}</span>`).join("")}</div>`;
|
|
29408
29457
|
};
|
|
29409
29458
|
comparisonCards = (comparisons) => comparisons.map(
|
|
29410
|
-
(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>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>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>`
|
|
29411
29460
|
).join("");
|
|
29412
29461
|
regimeRows = (variants) => {
|
|
29413
29462
|
const keys = [
|
|
@@ -29421,7 +29470,7 @@ var init_report = __esm({
|
|
|
29421
29470
|
).join("");
|
|
29422
29471
|
};
|
|
29423
29472
|
traceRows = (variants) => variants.map(
|
|
29424
|
-
(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>`
|
|
29425
29474
|
).join("");
|
|
29426
29475
|
buildCoreResearchHtml = (params) => {
|
|
29427
29476
|
const { spec, result, tradesByVariant } = params;
|
|
@@ -29447,7 +29496,7 @@ var init_report = __esm({
|
|
|
29447
29496
|
([key, value]) => `<span class="${value === "present" ? "positive" : "negative"}">${escapeHtml4(key)}: ${value}</span>`
|
|
29448
29497
|
).join(
|
|
29449
29498
|
""
|
|
29450
|
-
)}</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
|
|
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>`;
|
|
29451
29500
|
};
|
|
29452
29501
|
}
|
|
29453
29502
|
});
|
|
@@ -29749,8 +29798,9 @@ var init_trace = __esm({
|
|
|
29749
29798
|
import_promises24 = __toESM(require("fs/promises"));
|
|
29750
29799
|
import_node_path18 = __toESM(require("path"));
|
|
29751
29800
|
summarizeCoreResearchTrace = async (filePaths = []) => {
|
|
29752
|
-
const events =
|
|
29753
|
-
const skipCounts =
|
|
29801
|
+
const events = /* @__PURE__ */ Object.create(null);
|
|
29802
|
+
const skipCounts = /* @__PURE__ */ Object.create(null);
|
|
29803
|
+
const exitCodes = /* @__PURE__ */ Object.create(null);
|
|
29754
29804
|
for (const inputPath of filePaths) {
|
|
29755
29805
|
const filePath = import_node_path18.default.resolve(inputPath);
|
|
29756
29806
|
const text = await import_promises24.default.readFile(filePath, "utf8");
|
|
@@ -29770,6 +29820,9 @@ var init_trace = __esm({
|
|
|
29770
29820
|
skipCounts[reason] = (skipCounts[reason] ?? 0) + count;
|
|
29771
29821
|
}
|
|
29772
29822
|
}
|
|
29823
|
+
if (event.event === "position_exited" && event.exitCode) {
|
|
29824
|
+
exitCodes[event.exitCode] = (exitCodes[event.exitCode] ?? 0) + 1;
|
|
29825
|
+
}
|
|
29773
29826
|
}
|
|
29774
29827
|
}
|
|
29775
29828
|
return {
|
|
@@ -29778,6 +29831,11 @@ var init_trace = __esm({
|
|
|
29778
29831
|
Object.entries(skipCounts).sort(
|
|
29779
29832
|
([leftKey, leftValue], [rightKey, rightValue]) => rightValue - leftValue || (leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0)
|
|
29780
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
|
+
)
|
|
29781
29839
|
)
|
|
29782
29840
|
};
|
|
29783
29841
|
};
|
|
@@ -31772,41 +31830,52 @@ var init_runtimeFeedbackReplay = __esm({
|
|
|
31772
31830
|
interval: interval4,
|
|
31773
31831
|
startTime,
|
|
31774
31832
|
endTime
|
|
31775
|
-
}) =>
|
|
31776
|
-
|
|
31777
|
-
|
|
31778
|
-
|
|
31779
|
-
|
|
31780
|
-
|
|
31781
|
-
|
|
31782
|
-
|
|
31783
|
-
|
|
31784
|
-
|
|
31785
|
-
|
|
31786
|
-
|
|
31787
|
-
|
|
31788
|
-
|
|
31789
|
-
|
|
31790
|
-
|
|
31791
|
-
|
|
31792
|
-
|
|
31793
|
-
|
|
31794
|
-
|
|
31795
|
-
|
|
31796
|
-
|
|
31797
|
-
|
|
31798
|
-
|
|
31799
|
-
|
|
31800
|
-
|
|
31801
|
-
|
|
31802
|
-
|
|
31803
|
-
|
|
31804
|
-
|
|
31805
|
-
|
|
31806
|
-
|
|
31807
|
-
|
|
31808
|
-
|
|
31809
|
-
|
|
31833
|
+
}) => {
|
|
31834
|
+
const intervals = interval4.split(",").map((value) => value.trim());
|
|
31835
|
+
if (!intervals.length || intervals.some(
|
|
31836
|
+
(value) => !value || !Number.isFinite(Number(value)) || Number(value) <= 0
|
|
31837
|
+
)) {
|
|
31838
|
+
throw new Error(`Invalid runtime feedback replay intervals: ${interval4}`);
|
|
31839
|
+
}
|
|
31840
|
+
const replayTimeframe = intervals.sort(
|
|
31841
|
+
(left, right) => Number(left) - Number(right)
|
|
31842
|
+
)[0];
|
|
31843
|
+
return [
|
|
31844
|
+
[
|
|
31845
|
+
cliPath,
|
|
31846
|
+
"replay",
|
|
31847
|
+
"--user",
|
|
31848
|
+
userName3,
|
|
31849
|
+
"--connector",
|
|
31850
|
+
connectorName,
|
|
31851
|
+
"--deployment",
|
|
31852
|
+
deploymentId,
|
|
31853
|
+
"--timeframe",
|
|
31854
|
+
replayTimeframe,
|
|
31855
|
+
"--startTime",
|
|
31856
|
+
String(startTime),
|
|
31857
|
+
"--endTime",
|
|
31858
|
+
String(endTime),
|
|
31859
|
+
"--runtimeEvidence",
|
|
31860
|
+
runtimeEvidencePath,
|
|
31861
|
+
"--cacheOnly"
|
|
31862
|
+
],
|
|
31863
|
+
[
|
|
31864
|
+
cliPath,
|
|
31865
|
+
"replay-runtime-evidence",
|
|
31866
|
+
"--user",
|
|
31867
|
+
userName3,
|
|
31868
|
+
"--runtimeEvidence",
|
|
31869
|
+
runtimeEvidencePath,
|
|
31870
|
+
"--startTime",
|
|
31871
|
+
String(startTime),
|
|
31872
|
+
"--endTime",
|
|
31873
|
+
String(endTime),
|
|
31874
|
+
"--out",
|
|
31875
|
+
replayEvidencePath
|
|
31876
|
+
]
|
|
31877
|
+
];
|
|
31878
|
+
};
|
|
31810
31879
|
runLoggedCommand = async ({
|
|
31811
31880
|
executable,
|
|
31812
31881
|
command,
|
|
@@ -31893,10 +31962,8 @@ $ ${[executable, ...command].join(" ")}
|
|
|
31893
31962
|
const intervals = new Set(
|
|
31894
31963
|
activeStrategies.map(({ interval: interval4 }) => String(interval4))
|
|
31895
31964
|
);
|
|
31896
|
-
if (intervals.size
|
|
31897
|
-
throw new Error(
|
|
31898
|
-
`Runtime feedback replay requires one interval, received ${[...intervals].join(",")}`
|
|
31899
|
-
);
|
|
31965
|
+
if (!intervals.size) {
|
|
31966
|
+
throw new Error("Runtime feedback replay requires an active interval");
|
|
31900
31967
|
}
|
|
31901
31968
|
await import_promises28.default.mkdir(outDir, { recursive: true });
|
|
31902
31969
|
const existing = await import_promises28.default.readdir(outDir);
|
|
@@ -31917,7 +31984,7 @@ $ ${[executable, ...command].join(" ")}
|
|
|
31917
31984
|
userName: runtimeEvidenceBundle.manifest.userName,
|
|
31918
31985
|
connectorName: deployment.connectorName,
|
|
31919
31986
|
deploymentId: deployment.id,
|
|
31920
|
-
interval: [...intervals]
|
|
31987
|
+
interval: [...intervals].join(","),
|
|
31921
31988
|
startTime: runtimeEvidenceBundle.manifest.window.startTime,
|
|
31922
31989
|
endTime: runtimeEvidenceBundle.manifest.window.endTime
|
|
31923
31990
|
});
|
package/dist/lib/coreResearch.js
CHANGED
|
@@ -691,7 +691,12 @@ var writeCoreResearchMatches = async (params) => {
|
|
|
691
691
|
"controlPnl",
|
|
692
692
|
"candidatePnl",
|
|
693
693
|
"pnlDelta",
|
|
694
|
-
"
|
|
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.
|
|
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>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>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
|
|
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
|
};
|
|
@@ -133,41 +133,52 @@ var buildRuntimeFeedbackReplayCommands = ({
|
|
|
133
133
|
interval,
|
|
134
134
|
startTime,
|
|
135
135
|
endTime
|
|
136
|
-
}) =>
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
136
|
+
}) => {
|
|
137
|
+
const intervals = interval.split(",").map((value) => value.trim());
|
|
138
|
+
if (!intervals.length || intervals.some(
|
|
139
|
+
(value) => !value || !Number.isFinite(Number(value)) || Number(value) <= 0
|
|
140
|
+
)) {
|
|
141
|
+
throw new Error(`Invalid runtime feedback replay intervals: ${interval}`);
|
|
142
|
+
}
|
|
143
|
+
const replayTimeframe = intervals.sort(
|
|
144
|
+
(left, right) => Number(left) - Number(right)
|
|
145
|
+
)[0];
|
|
146
|
+
return [
|
|
147
|
+
[
|
|
148
|
+
cliPath,
|
|
149
|
+
"replay",
|
|
150
|
+
"--user",
|
|
151
|
+
userName,
|
|
152
|
+
"--connector",
|
|
153
|
+
connectorName,
|
|
154
|
+
"--deployment",
|
|
155
|
+
deploymentId,
|
|
156
|
+
"--timeframe",
|
|
157
|
+
replayTimeframe,
|
|
158
|
+
"--startTime",
|
|
159
|
+
String(startTime),
|
|
160
|
+
"--endTime",
|
|
161
|
+
String(endTime),
|
|
162
|
+
"--runtimeEvidence",
|
|
163
|
+
runtimeEvidencePath,
|
|
164
|
+
"--cacheOnly"
|
|
165
|
+
],
|
|
166
|
+
[
|
|
167
|
+
cliPath,
|
|
168
|
+
"replay-runtime-evidence",
|
|
169
|
+
"--user",
|
|
170
|
+
userName,
|
|
171
|
+
"--runtimeEvidence",
|
|
172
|
+
runtimeEvidencePath,
|
|
173
|
+
"--startTime",
|
|
174
|
+
String(startTime),
|
|
175
|
+
"--endTime",
|
|
176
|
+
String(endTime),
|
|
177
|
+
"--out",
|
|
178
|
+
replayEvidencePath
|
|
179
|
+
]
|
|
180
|
+
];
|
|
181
|
+
};
|
|
171
182
|
var runLoggedCommand = async ({
|
|
172
183
|
executable,
|
|
173
184
|
command,
|
|
@@ -254,10 +265,8 @@ var runRuntimeFeedbackReplay = async ({
|
|
|
254
265
|
const intervals = new Set(
|
|
255
266
|
activeStrategies.map(({ interval }) => String(interval))
|
|
256
267
|
);
|
|
257
|
-
if (intervals.size
|
|
258
|
-
throw new Error(
|
|
259
|
-
`Runtime feedback replay requires one interval, received ${[...intervals].join(",")}`
|
|
260
|
-
);
|
|
268
|
+
if (!intervals.size) {
|
|
269
|
+
throw new Error("Runtime feedback replay requires an active interval");
|
|
261
270
|
}
|
|
262
271
|
await import_promises.default.mkdir(outDir, { recursive: true });
|
|
263
272
|
const existing = await import_promises.default.readdir(outDir);
|
|
@@ -278,7 +287,7 @@ var runRuntimeFeedbackReplay = async ({
|
|
|
278
287
|
userName: runtimeEvidenceBundle.manifest.userName,
|
|
279
288
|
connectorName: deployment.connectorName,
|
|
280
289
|
deploymentId: deployment.id,
|
|
281
|
-
interval: [...intervals]
|
|
290
|
+
interval: [...intervals].join(","),
|
|
282
291
|
startTime: runtimeEvidenceBundle.manifest.window.startTime,
|
|
283
292
|
endTime: runtimeEvidenceBundle.manifest.window.endTime
|
|
284
293
|
});
|