@tradejs/cli 3.1.26 → 3.1.27-beta.247
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 +436 -45
- package/dist/lib/runtimeDebugEvidence.js +1 -0
- package/dist/lib/runtimeParityDetails.js +128 -6
- package/dist/lib/runtimeScorecard.js +103 -20
- package/dist/lib/runtimeSignalsLoader.js +1 -0
- package/dist/lib/runtimeSignalsStorage.js +33 -0
- package/dist/scripts/replay.js +266 -19
- package/dist/scripts/replayRunner.js +266 -19
- package/dist/scripts/replayRuntimeEvidence.js +14 -1
- package/dist/scripts/runtimeEvidence.js +1 -0
- package/dist/scripts/runtimeParity.js +1 -0
- package/dist/scripts/runtimeScorecard.js +109 -20
- package/dist/scripts/signals.js +37 -0
- package/dist/scripts/signalsDaemon.js +37 -0
- package/dist/scripts/signalsSummary.js +1 -0
- package/package.json +7 -7
package/dist/cli.js
CHANGED
|
@@ -24004,12 +24004,43 @@ var init_results = __esm({
|
|
|
24004
24004
|
});
|
|
24005
24005
|
|
|
24006
24006
|
// src/lib/runtimeSignalsStorage.ts
|
|
24007
|
-
var import_constants14, import_time6, SECONDS_PER_DAY, RUNTIME_SIGNAL_RETENTION_DAYS_ENV, DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS, RUNTIME_LINEAGE_SCOPE_RETENTION_TTL_SECONDS, parsePositiveNumber, getRuntimeSignalRetentionTtlSeconds, toRuntimeSignalBucketRef, shouldStoreRuntimeSignalDiagnostics, toStoredRuntimeSignal, isRuntimeSignalBucketRef, normalizeRuntimeSignalSkipReason, buildStatsReasonField, buildRuntimeSignalStatsIncrements, shouldStoreDetailedRuntimeSignalEvaluation, toCount, parseRuntimeSignalStatsBucket;
|
|
24007
|
+
var import_constants14, import_data4, import_time6, appendRuntimeSignalEvaluationRun, SECONDS_PER_DAY, RUNTIME_SIGNAL_RETENTION_DAYS_ENV, DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS, RUNTIME_LINEAGE_SCOPE_RETENTION_TTL_SECONDS, parsePositiveNumber, getRuntimeSignalRetentionTtlSeconds, toRuntimeSignalBucketRef, shouldStoreRuntimeSignalDiagnostics, toStoredRuntimeSignal, isRuntimeSignalBucketRef, normalizeRuntimeSignalSkipReason, buildStatsReasonField, buildRuntimeSignalStatsIncrements, shouldStoreDetailedRuntimeSignalEvaluation, toCount, parseRuntimeSignalStatsBucket;
|
|
24008
24008
|
var init_runtimeSignalsStorage = __esm({
|
|
24009
24009
|
"src/lib/runtimeSignalsStorage.ts"() {
|
|
24010
24010
|
"use strict";
|
|
24011
24011
|
import_constants14 = require("@tradejs/core/constants");
|
|
24012
|
+
import_data4 = require("@tradejs/core/data");
|
|
24012
24013
|
import_time6 = require("@tradejs/core/time");
|
|
24014
|
+
appendRuntimeSignalEvaluationRun = (runs = [], evaluation) => {
|
|
24015
|
+
if (evaluation.status !== "skip") {
|
|
24016
|
+
return [...runs];
|
|
24017
|
+
}
|
|
24018
|
+
const stepMs = (0, import_data4.intervalToMs)(evaluation.interval);
|
|
24019
|
+
const reason = evaluation.reason?.trim() || "NO_SIGNAL";
|
|
24020
|
+
const existing = runs.find(
|
|
24021
|
+
(run) => run.status === "skip" && run.reason === reason && run.stepMs === stepMs && evaluation.timestamp >= run.firstTimestamp && evaluation.timestamp <= run.lastTimestamp && (evaluation.timestamp - run.firstTimestamp) % stepMs === 0
|
|
24022
|
+
);
|
|
24023
|
+
if (existing) {
|
|
24024
|
+
return [...runs];
|
|
24025
|
+
}
|
|
24026
|
+
const last = runs.at(-1);
|
|
24027
|
+
if (last?.status === "skip" && last.reason === reason && last.stepMs === stepMs && evaluation.timestamp === last.lastTimestamp + stepMs) {
|
|
24028
|
+
return [
|
|
24029
|
+
...runs.slice(0, -1),
|
|
24030
|
+
{ ...last, lastTimestamp: evaluation.timestamp }
|
|
24031
|
+
];
|
|
24032
|
+
}
|
|
24033
|
+
return [
|
|
24034
|
+
...runs,
|
|
24035
|
+
{
|
|
24036
|
+
status: "skip",
|
|
24037
|
+
reason,
|
|
24038
|
+
firstTimestamp: evaluation.timestamp,
|
|
24039
|
+
lastTimestamp: evaluation.timestamp,
|
|
24040
|
+
stepMs
|
|
24041
|
+
}
|
|
24042
|
+
];
|
|
24043
|
+
};
|
|
24013
24044
|
SECONDS_PER_DAY = 86400;
|
|
24014
24045
|
RUNTIME_SIGNAL_RETENTION_DAYS_ENV = "RUNTIME_SIGNAL_RETENTION_DAYS";
|
|
24015
24046
|
DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS = import_constants14.TTL_3D;
|
|
@@ -24862,9 +24893,21 @@ var init_replayRuntimeEvidence = __esm({
|
|
|
24862
24893
|
const replayLineage = asArray2(comparisonLineage.replay).filter(
|
|
24863
24894
|
(entry) => !useFilter || strategySet.has(String(asRecord4(entry).strategy ?? ""))
|
|
24864
24895
|
);
|
|
24896
|
+
const hasExcludedBacktestEntryDetails = Array.isArray(
|
|
24897
|
+
comparisonLineage.excludedBacktestEntryDetails
|
|
24898
|
+
);
|
|
24899
|
+
const excludedBacktestEntryDetails = asArray2(
|
|
24900
|
+
comparisonLineage.excludedBacktestEntryDetails
|
|
24901
|
+
).filter(
|
|
24902
|
+
(entry) => !useFilter || strategySet.has(String(asRecord4(entry).strategy ?? ""))
|
|
24903
|
+
);
|
|
24865
24904
|
return {
|
|
24866
24905
|
mode: comparison.mode ?? null,
|
|
24867
|
-
lineage: comparisonLineage ? {
|
|
24906
|
+
lineage: comparisonLineage ? {
|
|
24907
|
+
...comparisonLineage,
|
|
24908
|
+
replay: replayLineage,
|
|
24909
|
+
...hasExcludedBacktestEntryDetails ? { excludedBacktestEntryDetails } : {}
|
|
24910
|
+
} : null,
|
|
24868
24911
|
rows: asArray2(comparison.rows).filter(
|
|
24869
24912
|
(row) => !useFilter || strategySet.has(String(row.strategyName ?? ""))
|
|
24870
24913
|
),
|
|
@@ -26960,7 +27003,7 @@ var init_runtimeEvidenceSync2 = __esm({
|
|
|
26960
27003
|
});
|
|
26961
27004
|
|
|
26962
27005
|
// src/lib/runtimeScorecard.ts
|
|
26963
|
-
var asRecord6, asArray3, finiteNumber5, finiteString2, packageVersionMap, round, unwrapRows, runtimePayload, runtimeWindow, extractRuntimeRows, buildEmbeddedLineageScope, runtimeDeploymentBinding, belongsToRuntimeDeployment, belongsToStrategy, filterArtifactByStrategy, countBy2, buildDistributions, buildDistributionDelta, sum2, tradeTimestamp, dedupeTrades, maxDrawdown, buildRollingPerformance, getReplayComparison, replayCountsForStrategy, getProspectiveSummary, buildRuntimeScorecard, formatRuntimeScorecardMarkdown;
|
|
27006
|
+
var asRecord6, asArray3, finiteNumber5, finiteString2, packageVersionMap, round, unwrapRows, runtimePayload, runtimeWindow, extractRuntimeRows, buildEmbeddedLineageScope, runtimeDeploymentBinding, belongsToRuntimeDeployment, belongsToStrategy, filterArtifactByStrategy, countBy2, resolveGateDecision, buildDistributions, buildDistributionDelta, sum2, tradeTimestamp, dedupeTrades, maxDrawdown, buildRollingPerformance, getReplayComparison, replayCountsForStrategy, getProspectiveSummary, buildRuntimeScorecard, formatRuntimeScorecardMarkdown;
|
|
26964
27007
|
var init_runtimeScorecard = __esm({
|
|
26965
27008
|
"src/lib/runtimeScorecard.ts"() {
|
|
26966
27009
|
"use strict";
|
|
@@ -27180,6 +27223,20 @@ var init_runtimeScorecard = __esm({
|
|
|
27180
27223
|
Object.entries(counts).sort(([left], [right]) => left.localeCompare(right))
|
|
27181
27224
|
);
|
|
27182
27225
|
};
|
|
27226
|
+
resolveGateDecision = (evaluation) => {
|
|
27227
|
+
const analysis = asRecord6(evaluation.aiAnalysis);
|
|
27228
|
+
const explicit = finiteString2(analysis?.gateDecision);
|
|
27229
|
+
if (explicit === "approved" || explicit === "rejected") {
|
|
27230
|
+
return { decision: explicit, source: "gate_decision" };
|
|
27231
|
+
}
|
|
27232
|
+
if (typeof analysis?.approved === "boolean") {
|
|
27233
|
+
return {
|
|
27234
|
+
decision: analysis.approved ? "approved" : "rejected",
|
|
27235
|
+
source: "legacy_approved_fallback"
|
|
27236
|
+
};
|
|
27237
|
+
}
|
|
27238
|
+
return null;
|
|
27239
|
+
};
|
|
27183
27240
|
buildDistributions = ({
|
|
27184
27241
|
evaluations,
|
|
27185
27242
|
signals: signals2
|
|
@@ -27360,18 +27417,15 @@ var init_runtimeScorecard = __esm({
|
|
|
27360
27417
|
const signalEvaluations = rows.evaluations.filter(
|
|
27361
27418
|
(evaluation) => evaluation.status === "signal"
|
|
27362
27419
|
);
|
|
27363
|
-
const gateDecisions = signalEvaluations.map(
|
|
27364
|
-
|
|
27365
|
-
decision: finiteString2(asRecord6(evaluation.aiAnalysis)?.gateDecision)
|
|
27366
|
-
})).filter(
|
|
27367
|
-
({ decision }) => decision === "approved" || decision === "rejected"
|
|
27420
|
+
const gateDecisions = signalEvaluations.map(resolveGateDecision).filter(
|
|
27421
|
+
(decision) => Boolean(decision)
|
|
27368
27422
|
);
|
|
27369
27423
|
const llmEligibleEvaluations = llmComparatorPolicy === "disabled" ? [] : llmComparatorPolicy === "ai_approved_only" ? signalEvaluations.filter(
|
|
27370
|
-
(evaluation) =>
|
|
27424
|
+
(evaluation) => resolveGateDecision(evaluation)?.decision === "approved"
|
|
27371
27425
|
) : signalEvaluations;
|
|
27372
27426
|
const gateComparisons = llmEligibleEvaluations.map((evaluation) => {
|
|
27373
27427
|
const analysis = asRecord6(evaluation.aiAnalysis);
|
|
27374
|
-
const gateDecision =
|
|
27428
|
+
const gateDecision = resolveGateDecision(evaluation)?.decision ?? null;
|
|
27375
27429
|
const llmDecision = finiteString2(analysis?.llmDecision);
|
|
27376
27430
|
if (gateDecision !== "approved" && gateDecision !== "rejected" || llmDecision !== "approved" && llmDecision !== "rejected") {
|
|
27377
27431
|
return null;
|
|
@@ -27406,6 +27460,27 @@ var init_runtimeScorecard = __esm({
|
|
|
27406
27460
|
};
|
|
27407
27461
|
const actualSlippageBps = averageSampleMetric("signalToFillAdverseBps");
|
|
27408
27462
|
const residualVsModelBps = averageSampleMetric("residualVsCurrentModelBps");
|
|
27463
|
+
const signalToArrivalAdverseBps = averageSampleMetric(
|
|
27464
|
+
"signalToArrivalAdverseBps"
|
|
27465
|
+
);
|
|
27466
|
+
const arrivalToFillAdverseBps = averageSampleMetric(
|
|
27467
|
+
"arrivalToFillAdverseBps"
|
|
27468
|
+
);
|
|
27469
|
+
const averageSignalCloseToSubmitMs = averageSampleMetric(
|
|
27470
|
+
"signalCloseToSubmitMs"
|
|
27471
|
+
);
|
|
27472
|
+
const averageSubmitToFillMs = averageSampleMetric("submitToFillMs");
|
|
27473
|
+
const fullTelemetryTrades = calibrationSamples.filter(
|
|
27474
|
+
(sample) => finiteString2(sample.telemetryQuality) === "full"
|
|
27475
|
+
).length;
|
|
27476
|
+
const arrivalQuoteTrades = calibrationSamples.filter(
|
|
27477
|
+
(sample) => finiteNumber5(sample.arrivalMid) != null
|
|
27478
|
+
).length;
|
|
27479
|
+
const arrivalQuoteCoverage = calibrationSamples.length ? round(arrivalQuoteTrades / calibrationSamples.length) : null;
|
|
27480
|
+
const hasTelemetryQuality = calibrationSamples.some(
|
|
27481
|
+
(sample) => finiteString2(sample.telemetryQuality) != null
|
|
27482
|
+
);
|
|
27483
|
+
const maximumSignalCloseToSubmitMs = thresholds.maximumSignalCloseToSubmitMs ?? 3e4;
|
|
27409
27484
|
const prospective = getProspectiveSummary(prospectiveEvidenceArtifact);
|
|
27410
27485
|
const historyTrades = dedupeTrades([
|
|
27411
27486
|
...scopedHistoryArtifacts,
|
|
@@ -27462,7 +27537,28 @@ var init_runtimeScorecard = __esm({
|
|
|
27462
27537
|
reactions.push({
|
|
27463
27538
|
code: "SLIPPAGE_DRIFT",
|
|
27464
27539
|
severity: "warning",
|
|
27465
|
-
message: `
|
|
27540
|
+
message: `Signal-close-to-fill residual ${round(residualVsModelBps, 2)} bps exceeds ${thresholds.maximumSlippageResidualBps} bps.`
|
|
27541
|
+
});
|
|
27542
|
+
}
|
|
27543
|
+
if (averageSignalCloseToSubmitMs != null && averageSignalCloseToSubmitMs > maximumSignalCloseToSubmitMs) {
|
|
27544
|
+
reactions.push({
|
|
27545
|
+
code: "PRE_SUBMIT_LATENCY",
|
|
27546
|
+
severity: "warning",
|
|
27547
|
+
message: `Average signal-close-to-submit latency ${round(averageSignalCloseToSubmitMs, 3)} ms exceeds ${maximumSignalCloseToSubmitMs} ms.`
|
|
27548
|
+
});
|
|
27549
|
+
}
|
|
27550
|
+
if (hasTelemetryQuality && calibrationSamples.length > 0 && fullTelemetryTrades < calibrationSamples.length) {
|
|
27551
|
+
reactions.push({
|
|
27552
|
+
code: "EXECUTION_TELEMETRY_INCOMPLETE",
|
|
27553
|
+
severity: "info",
|
|
27554
|
+
message: `Full arrival and fill telemetry is available for ${fullTelemetryTrades}/${calibrationSamples.length} trades.`
|
|
27555
|
+
});
|
|
27556
|
+
}
|
|
27557
|
+
if (riskDecisions.some((decision) => decision.status === "rejected")) {
|
|
27558
|
+
reactions.push({
|
|
27559
|
+
code: "RISK_PLAN_EXCEEDS_DECLARED_LIMIT",
|
|
27560
|
+
severity: "warning",
|
|
27561
|
+
message: "At least one observed order plan exceeds its declared MAX_LOSS_VALUE; telemetry is not yet enforced."
|
|
27466
27562
|
});
|
|
27467
27563
|
}
|
|
27468
27564
|
if (sevenDay.closedTrades >= thresholds.minimumClosedTrades && sevenDay.expectancy != null && sevenDay.expectancy < thresholds.minimumExpectancy) {
|
|
@@ -27491,7 +27587,10 @@ var init_runtimeScorecard = __esm({
|
|
|
27491
27587
|
deployment: deploymentBinding,
|
|
27492
27588
|
lineage: runtimeLineage,
|
|
27493
27589
|
promotionStatus,
|
|
27494
|
-
thresholds
|
|
27590
|
+
thresholds: {
|
|
27591
|
+
...thresholds,
|
|
27592
|
+
maximumSignalCloseToSubmitMs
|
|
27593
|
+
},
|
|
27495
27594
|
funnel: {
|
|
27496
27595
|
evaluations: rows.evaluations.length,
|
|
27497
27596
|
coreCandidates: signalEvaluations.length,
|
|
@@ -27503,25 +27602,38 @@ var init_runtimeScorecard = __esm({
|
|
|
27503
27602
|
rejected: gateDecisions.filter(
|
|
27504
27603
|
({ decision }) => decision === "rejected"
|
|
27505
27604
|
).length,
|
|
27506
|
-
coverage: signalEvaluations.length ? round(gateDecisions.length / signalEvaluations.length) : null
|
|
27605
|
+
coverage: signalEvaluations.length ? round(gateDecisions.length / signalEvaluations.length) : null,
|
|
27606
|
+
legacyApprovedFallback: gateDecisions.filter(
|
|
27607
|
+
({ source }) => source === "legacy_approved_fallback"
|
|
27608
|
+
).length
|
|
27507
27609
|
},
|
|
27508
27610
|
allocator: {
|
|
27509
27611
|
available: allocatorDecisions.length > 0,
|
|
27510
27612
|
approved: allocatorDecisions.filter(
|
|
27511
|
-
(decision) => decision.
|
|
27613
|
+
(decision) => decision.status === "approved"
|
|
27512
27614
|
).length,
|
|
27513
27615
|
rejected: allocatorDecisions.filter(
|
|
27514
|
-
(decision) => decision.
|
|
27616
|
+
(decision) => decision.status === "rejected"
|
|
27617
|
+
).length,
|
|
27618
|
+
notApplicable: allocatorDecisions.filter(
|
|
27619
|
+
(decision) => decision.status === "not_applicable"
|
|
27620
|
+
).length,
|
|
27621
|
+
unavailable: allocatorDecisions.filter(
|
|
27622
|
+
(decision) => decision.status === "unavailable"
|
|
27515
27623
|
).length
|
|
27516
27624
|
},
|
|
27517
27625
|
risk: {
|
|
27518
27626
|
available: riskDecisions.length > 0,
|
|
27519
27627
|
approved: riskDecisions.filter(
|
|
27520
|
-
(decision) => decision.
|
|
27628
|
+
(decision) => decision.status === "approved"
|
|
27521
27629
|
).length,
|
|
27522
27630
|
rejected: riskDecisions.filter(
|
|
27523
|
-
(decision) => decision.
|
|
27524
|
-
).length
|
|
27631
|
+
(decision) => decision.status === "rejected"
|
|
27632
|
+
).length,
|
|
27633
|
+
unavailable: riskDecisions.filter(
|
|
27634
|
+
(decision) => decision.status === "unavailable"
|
|
27635
|
+
).length,
|
|
27636
|
+
enforced: riskDecisions.some((decision) => decision.enforced === true)
|
|
27525
27637
|
},
|
|
27526
27638
|
orderAttempts: orderAttempts.length,
|
|
27527
27639
|
orderFailures: orderAttempts.filter(
|
|
@@ -27583,8 +27695,16 @@ var init_runtimeScorecard = __esm({
|
|
|
27583
27695
|
},
|
|
27584
27696
|
execution: {
|
|
27585
27697
|
available: actualSlippageBps != null || residualVsModelBps != null,
|
|
27698
|
+
metricScope: arrivalToFillAdverseBps == null ? "signal_close_to_fill" : "signal_and_exchange_components",
|
|
27699
|
+
samples: calibrationSamples.length,
|
|
27700
|
+
fullTelemetryTrades,
|
|
27701
|
+
arrivalQuoteCoverage,
|
|
27586
27702
|
actualSignalToFillSlippageBps: actualSlippageBps,
|
|
27587
|
-
residualVsCurrentModelBps: residualVsModelBps
|
|
27703
|
+
residualVsCurrentModelBps: residualVsModelBps,
|
|
27704
|
+
signalToArrivalAdverseBps,
|
|
27705
|
+
arrivalToFillAdverseBps,
|
|
27706
|
+
averageSignalCloseToSubmitMs,
|
|
27707
|
+
averageSubmitToFillMs
|
|
27588
27708
|
},
|
|
27589
27709
|
prospective,
|
|
27590
27710
|
rolling,
|
|
@@ -27638,10 +27758,10 @@ Window: ${new Date(scorecard.window.startTime).toISOString()} \u2014 ${new Date(
|
|
|
27638
27758
|
|
|
27639
27759
|
- Evaluations: ${scorecard.funnel.evaluations}
|
|
27640
27760
|
- Core candidates: ${scorecard.funnel.coreCandidates}
|
|
27641
|
-
- Gate approvals/rejects: ${scorecard.funnel.gate.approved}/${scorecard.funnel.gate.rejected}
|
|
27761
|
+
- Gate approvals/rejects: ${scorecard.funnel.gate.approved}/${scorecard.funnel.gate.rejected} (legacy fallback: ${scorecard.funnel.gate.legacyApprovedFallback})
|
|
27642
27762
|
- AI / LLM disagreement: ${scorecard.gateComparison.disagreements}/${scorecard.gateComparison.compared}
|
|
27643
|
-
-
|
|
27644
|
-
- Risk
|
|
27763
|
+
- Runtime admission approved/rejected/not-applicable: ${scorecard.funnel.allocator.available ? `${scorecard.funnel.allocator.approved}/${scorecard.funnel.allocator.rejected}/${scorecard.funnel.allocator.notApplicable}` : "unavailable"}
|
|
27764
|
+
- Risk assessment within/exceeds/unavailable: ${scorecard.funnel.risk.available ? `${scorecard.funnel.risk.approved}/${scorecard.funnel.risk.rejected}/${scorecard.funnel.risk.unavailable}` : "unavailable"} (enforced: ${scorecard.funnel.risk.enforced})
|
|
27645
27765
|
- Order attempts/failures: ${scorecard.funnel.orderAttempts}/${scorecard.funnel.orderFailures}
|
|
27646
27766
|
- Fills: ${scorecard.funnel.fills}
|
|
27647
27767
|
- Closed trades: ${scorecard.funnel.closedTrades}
|
|
@@ -27650,8 +27770,14 @@ Window: ${new Date(scorecard.window.startTime).toISOString()} \u2014 ${new Date(
|
|
|
27650
27770
|
|
|
27651
27771
|
- Parity: ${scorecard.parity.ratio == null ? "n/a" : `${round(scorecard.parity.ratio * 100, 2)}%`}
|
|
27652
27772
|
- Runtime/replay mismatches: ${scorecard.parity.backtestOnly + scorecard.parity.runtimeOnly}
|
|
27653
|
-
-
|
|
27773
|
+
- Execution metric scope: ${scorecard.execution.metricScope}
|
|
27774
|
+
- Telemetry samples/full/arrival coverage: ${scorecard.execution.samples}/${scorecard.execution.fullTelemetryTrades}/${scorecard.execution.arrivalQuoteCoverage == null ? "n/a" : `${round(scorecard.execution.arrivalQuoteCoverage * 100, 2)}%`}
|
|
27775
|
+
- Signal-close-to-fill adverse move: ${scorecard.execution.actualSignalToFillSlippageBps ?? "n/a"} bps
|
|
27776
|
+
- Signal-to-arrival adverse move: ${scorecard.execution.signalToArrivalAdverseBps ?? "n/a"} bps
|
|
27777
|
+
- Arrival-to-fill exchange slippage: ${scorecard.execution.arrivalToFillAdverseBps ?? "n/a"} bps
|
|
27654
27778
|
- Residual vs current model: ${scorecard.execution.residualVsCurrentModelBps ?? "n/a"} bps
|
|
27779
|
+
- Average signal-close-to-submit: ${scorecard.execution.averageSignalCloseToSubmitMs ?? "n/a"} ms
|
|
27780
|
+
- Average submit-to-fill: ${scorecard.execution.averageSubmitToFillMs ?? "n/a"} ms
|
|
27655
27781
|
|
|
27656
27782
|
## Rolling performance
|
|
27657
27783
|
|
|
@@ -29179,6 +29305,11 @@ var init_runtimeScorecard2 = __esm({
|
|
|
29179
29305
|
"Maximum acceptable execution residual in bps",
|
|
29180
29306
|
3
|
|
29181
29307
|
);
|
|
29308
|
+
import_args30.default.option(
|
|
29309
|
+
["L", "maximumSignalCloseToSubmitMs"],
|
|
29310
|
+
"Maximum acceptable average signal-close-to-submit latency in milliseconds",
|
|
29311
|
+
3e4
|
|
29312
|
+
);
|
|
29182
29313
|
import_args30.default.option(
|
|
29183
29314
|
["T", "minimumClosedTrades"],
|
|
29184
29315
|
"Minimum 7d closed trades for expectancy reactions",
|
|
@@ -29271,6 +29402,7 @@ var init_runtimeScorecard2 = __esm({
|
|
|
29271
29402
|
thresholds: {
|
|
29272
29403
|
minimumParityRatio: Number(flags29.minimumParityRatio),
|
|
29273
29404
|
maximumSlippageResidualBps: Number(flags29.maximumSlippageResidualBps),
|
|
29405
|
+
maximumSignalCloseToSubmitMs: Number(flags29.maximumSignalCloseToSubmitMs),
|
|
29274
29406
|
minimumClosedTrades: Number(flags29.minimumClosedTrades),
|
|
29275
29407
|
minimumExpectancy: Number(flags29.minimumExpectancy)
|
|
29276
29408
|
},
|
|
@@ -32667,6 +32799,10 @@ var init_evaluations = __esm({
|
|
|
32667
32799
|
lastTimestamp: Math.max(
|
|
32668
32800
|
existing?.lastTimestamp ?? evaluation.timestamp,
|
|
32669
32801
|
evaluation.timestamp
|
|
32802
|
+
),
|
|
32803
|
+
evaluationRuns: appendRuntimeSignalEvaluationRun(
|
|
32804
|
+
existing?.evaluationRuns,
|
|
32805
|
+
evaluation
|
|
32670
32806
|
)
|
|
32671
32807
|
};
|
|
32672
32808
|
runtimeLineageScopeCache.set(cacheKey, scope);
|
|
@@ -33398,6 +33534,8 @@ var init_tickerEvaluator = __esm({
|
|
|
33398
33534
|
orderStatus: signal.orderStatus,
|
|
33399
33535
|
orderSkipReason: signal.orderSkipReason,
|
|
33400
33536
|
...signal.aiAnalysis ? { aiAnalysis: signal.aiAnalysis } : {},
|
|
33537
|
+
...signal.allocatorDecision ? { allocatorDecision: signal.allocatorDecision } : {},
|
|
33538
|
+
...signal.riskDecision ? { riskDecision: signal.riskDecision } : {},
|
|
33401
33539
|
...signal.ml ? { ml: signal.ml } : {}
|
|
33402
33540
|
});
|
|
33403
33541
|
import_logger9.logger.info("Signal found %s by strategy %s", symbol, strategyName);
|
|
@@ -35254,11 +35392,90 @@ var init_historicalSignalsReplayPreparation = __esm({
|
|
|
35254
35392
|
}
|
|
35255
35393
|
});
|
|
35256
35394
|
|
|
35395
|
+
// src/lib/replay/historicalSignalsReplaySkipEvidence.ts
|
|
35396
|
+
var import_data5, replayScopeKey, appendReplaySignalEvaluationRun, createHistoricalReplaySkipEvidence;
|
|
35397
|
+
var init_historicalSignalsReplaySkipEvidence = __esm({
|
|
35398
|
+
"src/lib/replay/historicalSignalsReplaySkipEvidence.ts"() {
|
|
35399
|
+
"use strict";
|
|
35400
|
+
import_data5 = require("@tradejs/core/data");
|
|
35401
|
+
replayScopeKey = (strategy, symbol) => `${strategy}::${symbol}`;
|
|
35402
|
+
appendReplaySignalEvaluationRun = ({
|
|
35403
|
+
runs = [],
|
|
35404
|
+
reason,
|
|
35405
|
+
timestamp,
|
|
35406
|
+
interval: interval4
|
|
35407
|
+
}) => {
|
|
35408
|
+
const stepMs = (0, import_data5.intervalToMs)(interval4);
|
|
35409
|
+
const last = runs.at(-1);
|
|
35410
|
+
if (last?.reason === reason && last.stepMs === stepMs && timestamp === last.lastTimestamp + stepMs) {
|
|
35411
|
+
return [...runs.slice(0, -1), { ...last, lastTimestamp: timestamp }];
|
|
35412
|
+
}
|
|
35413
|
+
return [
|
|
35414
|
+
...runs,
|
|
35415
|
+
{
|
|
35416
|
+
status: "skip",
|
|
35417
|
+
reason,
|
|
35418
|
+
firstTimestamp: timestamp,
|
|
35419
|
+
lastTimestamp: timestamp,
|
|
35420
|
+
stepMs
|
|
35421
|
+
}
|
|
35422
|
+
];
|
|
35423
|
+
};
|
|
35424
|
+
createHistoricalReplaySkipEvidence = (runtimeLineages) => {
|
|
35425
|
+
const lineageByStrategySymbol = new Map(
|
|
35426
|
+
runtimeLineages.map((record2) => [
|
|
35427
|
+
replayScopeKey(record2.strategy, record2.symbol),
|
|
35428
|
+
record2
|
|
35429
|
+
])
|
|
35430
|
+
);
|
|
35431
|
+
const scopes = /* @__PURE__ */ new Map();
|
|
35432
|
+
const record = ({
|
|
35433
|
+
strategyName,
|
|
35434
|
+
symbol,
|
|
35435
|
+
strategyConfig,
|
|
35436
|
+
runtimeLineage,
|
|
35437
|
+
timestamp,
|
|
35438
|
+
result
|
|
35439
|
+
}) => {
|
|
35440
|
+
const scopeKey = replayScopeKey(strategyName, symbol);
|
|
35441
|
+
const lineageRecord = lineageByStrategySymbol.get(scopeKey);
|
|
35442
|
+
if (!lineageRecord) return;
|
|
35443
|
+
const existing = scopes.get(scopeKey);
|
|
35444
|
+
const skipReason = typeof result === "string" ? result.trim() || "NO_SIGNAL" : result ? null : "NO_SIGNAL";
|
|
35445
|
+
scopes.set(scopeKey, {
|
|
35446
|
+
strategy: strategyName,
|
|
35447
|
+
symbol,
|
|
35448
|
+
...lineageRecord.deploymentId ? { deploymentId: lineageRecord.deploymentId } : {},
|
|
35449
|
+
...lineageRecord.accountId ? { accountId: lineageRecord.accountId } : {},
|
|
35450
|
+
strategyRevision: runtimeLineage.strategyRevision,
|
|
35451
|
+
lineage: runtimeLineage,
|
|
35452
|
+
firstTimestamp: Math.min(
|
|
35453
|
+
existing?.firstTimestamp ?? timestamp,
|
|
35454
|
+
timestamp
|
|
35455
|
+
),
|
|
35456
|
+
lastTimestamp: Math.max(existing?.lastTimestamp ?? timestamp, timestamp),
|
|
35457
|
+
evaluationRuns: skipReason ? appendReplaySignalEvaluationRun({
|
|
35458
|
+
runs: existing?.evaluationRuns,
|
|
35459
|
+
reason: skipReason,
|
|
35460
|
+
timestamp,
|
|
35461
|
+
interval: strategyConfig.INTERVAL ?? "15"
|
|
35462
|
+
}) : existing?.evaluationRuns
|
|
35463
|
+
});
|
|
35464
|
+
};
|
|
35465
|
+
const values = () => [...scopes.values()].sort(
|
|
35466
|
+
(left, right) => left.strategy.localeCompare(right.strategy) || left.symbol.localeCompare(right.symbol)
|
|
35467
|
+
);
|
|
35468
|
+
return { record, values };
|
|
35469
|
+
};
|
|
35470
|
+
}
|
|
35471
|
+
});
|
|
35472
|
+
|
|
35257
35473
|
// src/lib/replay/historicalSignalsReplayExecution.ts
|
|
35258
35474
|
var advanceCycleMarket, advanceAbortedCycle, executeHistoricalReplay;
|
|
35259
35475
|
var init_historicalSignalsReplayExecution = __esm({
|
|
35260
35476
|
"src/lib/replay/historicalSignalsReplayExecution.ts"() {
|
|
35261
35477
|
"use strict";
|
|
35478
|
+
init_historicalSignalsReplaySkipEvidence();
|
|
35262
35479
|
advanceCycleMarket = async (plan, connector, timestamp) => {
|
|
35263
35480
|
const cycleSymbols = plan.cycleSymbolsByTimestamp.get(timestamp) ?? [];
|
|
35264
35481
|
for (const symbolRuntime of cycleSymbols) {
|
|
@@ -35283,6 +35500,9 @@ var init_historicalSignalsReplayExecution = __esm({
|
|
|
35283
35500
|
executeHistoricalReplay = async (context, adapters) => {
|
|
35284
35501
|
const signals2 = [];
|
|
35285
35502
|
let abortedCycles = 0;
|
|
35503
|
+
const skipEvidence = createHistoricalReplaySkipEvidence(
|
|
35504
|
+
context.plan.runtimeLineages
|
|
35505
|
+
);
|
|
35286
35506
|
try {
|
|
35287
35507
|
for (const timestamp of context.plan.orderedTimestamps) {
|
|
35288
35508
|
const cycleStartedAt = adapters.clock.now();
|
|
@@ -35325,6 +35545,14 @@ var init_historicalSignalsReplayExecution = __esm({
|
|
|
35325
35545
|
btcCandle,
|
|
35326
35546
|
ethCandle?.timestamp === timestamp ? ethCandle : void 0
|
|
35327
35547
|
);
|
|
35548
|
+
skipEvidence.record({
|
|
35549
|
+
strategyName: strategyRuntime.strategyName,
|
|
35550
|
+
symbol: symbolRuntime.symbol,
|
|
35551
|
+
strategyConfig: strategyRuntime.strategyConfig,
|
|
35552
|
+
runtimeLineage: strategyRuntime.runtimeLineage,
|
|
35553
|
+
timestamp,
|
|
35554
|
+
result
|
|
35555
|
+
});
|
|
35328
35556
|
if (result && typeof result !== "string") {
|
|
35329
35557
|
result.runtimeLineage = strategyRuntime.runtimeLineage;
|
|
35330
35558
|
await adapters.enrichSignal(result);
|
|
@@ -35352,7 +35580,11 @@ var init_historicalSignalsReplayExecution = __esm({
|
|
|
35352
35580
|
adapters.releaseReplayCache(keyPrefix);
|
|
35353
35581
|
}
|
|
35354
35582
|
}
|
|
35355
|
-
return {
|
|
35583
|
+
return {
|
|
35584
|
+
signals: signals2,
|
|
35585
|
+
abortedCycles,
|
|
35586
|
+
replayLineageScopes: skipEvidence.values()
|
|
35587
|
+
};
|
|
35356
35588
|
};
|
|
35357
35589
|
}
|
|
35358
35590
|
});
|
|
@@ -35387,7 +35619,8 @@ var init_historicalSignalsReplayResults = __esm({
|
|
|
35387
35619
|
positionLog: context.artifacts.positionLog,
|
|
35388
35620
|
cycleCount: context.cycleCount,
|
|
35389
35621
|
abortedCycles: context.abortedCycles,
|
|
35390
|
-
runtimeLineages: context.runtimeLineages
|
|
35622
|
+
runtimeLineages: context.runtimeLineages,
|
|
35623
|
+
replayLineageScopes: context.replayLineageScopes
|
|
35391
35624
|
});
|
|
35392
35625
|
}
|
|
35393
35626
|
});
|
|
@@ -35629,7 +35862,8 @@ var init_historicalSignalsReplay = __esm({
|
|
|
35629
35862
|
signals: execution.signals,
|
|
35630
35863
|
cycleCount: plan.orderedTimestamps.length,
|
|
35631
35864
|
abortedCycles: execution.abortedCycles,
|
|
35632
|
-
runtimeLineages: plan.runtimeLineages
|
|
35865
|
+
runtimeLineages: plan.runtimeLineages,
|
|
35866
|
+
replayLineageScopes: execution.replayLineageScopes
|
|
35633
35867
|
});
|
|
35634
35868
|
};
|
|
35635
35869
|
}
|
|
@@ -36077,7 +36311,7 @@ var init_runtimeTradeSync = __esm({
|
|
|
36077
36311
|
});
|
|
36078
36312
|
|
|
36079
36313
|
// src/lib/runtimeParityDetails.ts
|
|
36080
|
-
var import_trade3, DEFAULT_REPLAY_PARITY_DETAILS_LIMIT, DEFAULT_REPLAY_PARITY_ARTIFACT_LIMIT, buildStrategyNameByOrderLinkKey, resolveReplayStrategyNameFromExchangeEntry, resolveReplayParityDetailsLimit, resolveReplayParityArtifactLimit, capReplayDetails, buildCostDetail, toBacktestParityDetail, toRuntimeParityDetail, toExchangeParityDetail, buildTimestampDelta, getComparisonTimestamp, getEntryStrategy, findNearestSignal, findNearestEvaluation, classifySignalDiagnostic, classifyEvaluationDiagnostic, summarizeMismatchDiagnostics, buildPnlDelta, buildPriceDeltaPct2, toFiniteNumberOrNull3, toAiDiagnostic, toSignalDiagnostic, toEvaluationDiagnostic, buildSlippageCost, buildMatchedReplayDetail, buildNearestCandidates, buildReplayMismatchDrilldown, buildReplayRuntimeComparisonDetails, buildReplayExchangeComparisonDetails;
|
|
36314
|
+
var import_trade3, DEFAULT_REPLAY_PARITY_DETAILS_LIMIT, DEFAULT_REPLAY_PARITY_ARTIFACT_LIMIT, buildStrategyNameByOrderLinkKey, resolveReplayStrategyNameFromExchangeEntry, resolveReplayParityDetailsLimit, resolveReplayParityArtifactLimit, capReplayDetails, buildCostDetail, toBacktestParityDetail, toRuntimeParityDetail, toExchangeParityDetail, buildTimestampDelta, getComparisonTimestamp, getEntryStrategy, findNearestSignal, findNearestEvaluation, findNearestRuntimeEvaluationOutcome, findNearestReplayEvaluationOutcome, hasRuntimeLineageCoverage, classifySignalDiagnostic, classifyEvaluationDiagnostic, summarizeMismatchDiagnostics, buildPnlDelta, buildPriceDeltaPct2, toFiniteNumberOrNull3, toAiDiagnostic, toSignalDiagnostic, toEvaluationDiagnostic, buildSlippageCost, buildMatchedReplayDetail, buildNearestCandidates, buildReplayMismatchDrilldown, buildReplayRuntimeComparisonDetails, buildReplayExchangeComparisonDetails;
|
|
36081
36315
|
var init_runtimeParityDetails = __esm({
|
|
36082
36316
|
"src/lib/runtimeParityDetails.ts"() {
|
|
36083
36317
|
"use strict";
|
|
@@ -36253,6 +36487,90 @@ var init_runtimeParityDetails = __esm({
|
|
|
36253
36487
|
timestampDiffMs: bestDiff
|
|
36254
36488
|
} : null;
|
|
36255
36489
|
};
|
|
36490
|
+
findNearestRuntimeEvaluationOutcome = ({
|
|
36491
|
+
entry,
|
|
36492
|
+
lineageScopes,
|
|
36493
|
+
toleranceMs
|
|
36494
|
+
}) => {
|
|
36495
|
+
const strategy = getEntryStrategy(entry);
|
|
36496
|
+
const timestamp = entry.signalTimestamp ?? entry.timestamp;
|
|
36497
|
+
if (!strategy) return null;
|
|
36498
|
+
let nearest = null;
|
|
36499
|
+
for (const scope of lineageScopes) {
|
|
36500
|
+
if (scope.strategy !== strategy || scope.symbol !== entry.symbol) {
|
|
36501
|
+
continue;
|
|
36502
|
+
}
|
|
36503
|
+
for (const run of scope.evaluationRuns ?? []) {
|
|
36504
|
+
if (run.stepMs <= 0 || timestamp < run.firstTimestamp - toleranceMs || timestamp > run.lastTimestamp + toleranceMs) {
|
|
36505
|
+
continue;
|
|
36506
|
+
}
|
|
36507
|
+
const step = Math.round((timestamp - run.firstTimestamp) / run.stepMs);
|
|
36508
|
+
const candidateTimestamp = run.firstTimestamp + step * run.stepMs;
|
|
36509
|
+
const timestampDiffMs = Math.abs(candidateTimestamp - timestamp);
|
|
36510
|
+
if (candidateTimestamp < run.firstTimestamp || candidateTimestamp > run.lastTimestamp || timestampDiffMs > toleranceMs || nearest && timestampDiffMs >= nearest.timestampDiffMs) {
|
|
36511
|
+
continue;
|
|
36512
|
+
}
|
|
36513
|
+
nearest = {
|
|
36514
|
+
status: run.status,
|
|
36515
|
+
reason: run.reason,
|
|
36516
|
+
timestamp: candidateTimestamp,
|
|
36517
|
+
timestampDiffMs,
|
|
36518
|
+
source: "lineage_scope_compact"
|
|
36519
|
+
};
|
|
36520
|
+
}
|
|
36521
|
+
}
|
|
36522
|
+
return nearest;
|
|
36523
|
+
};
|
|
36524
|
+
findNearestReplayEvaluationOutcome = ({
|
|
36525
|
+
entry,
|
|
36526
|
+
lineageScopes,
|
|
36527
|
+
toleranceMs,
|
|
36528
|
+
timestampOffsetMs
|
|
36529
|
+
}) => {
|
|
36530
|
+
const strategy = getEntryStrategy(entry);
|
|
36531
|
+
if (!strategy) return null;
|
|
36532
|
+
let nearest = null;
|
|
36533
|
+
for (const scope of lineageScopes) {
|
|
36534
|
+
if (scope.strategy !== strategy || scope.symbol !== entry.symbol) {
|
|
36535
|
+
continue;
|
|
36536
|
+
}
|
|
36537
|
+
for (const run of scope.evaluationRuns ?? []) {
|
|
36538
|
+
if (run.stepMs <= 0) continue;
|
|
36539
|
+
const targetTimestamp = entry.timestamp - timestampOffsetMs;
|
|
36540
|
+
if (targetTimestamp < run.firstTimestamp - toleranceMs || targetTimestamp > run.lastTimestamp + toleranceMs) {
|
|
36541
|
+
continue;
|
|
36542
|
+
}
|
|
36543
|
+
const step = Math.round(
|
|
36544
|
+
(targetTimestamp - run.firstTimestamp) / run.stepMs
|
|
36545
|
+
);
|
|
36546
|
+
const candidateTimestamp = run.firstTimestamp + step * run.stepMs;
|
|
36547
|
+
const timestampDiffMs = Math.abs(
|
|
36548
|
+
candidateTimestamp + timestampOffsetMs - entry.timestamp
|
|
36549
|
+
);
|
|
36550
|
+
if (candidateTimestamp < run.firstTimestamp || candidateTimestamp > run.lastTimestamp || timestampDiffMs > toleranceMs || nearest && timestampDiffMs >= nearest.timestampDiffMs) {
|
|
36551
|
+
continue;
|
|
36552
|
+
}
|
|
36553
|
+
nearest = {
|
|
36554
|
+
status: run.status,
|
|
36555
|
+
reason: run.reason,
|
|
36556
|
+
timestamp: candidateTimestamp,
|
|
36557
|
+
timestampDiffMs,
|
|
36558
|
+
source: "replay_scope_compact"
|
|
36559
|
+
};
|
|
36560
|
+
}
|
|
36561
|
+
}
|
|
36562
|
+
return nearest;
|
|
36563
|
+
};
|
|
36564
|
+
hasRuntimeLineageCoverage = ({
|
|
36565
|
+
entry,
|
|
36566
|
+
lineageScopes
|
|
36567
|
+
}) => {
|
|
36568
|
+
const strategy = getEntryStrategy(entry);
|
|
36569
|
+
const timestamp = entry.signalTimestamp ?? entry.timestamp;
|
|
36570
|
+
return lineageScopes.some(
|
|
36571
|
+
(scope) => scope.strategy === strategy && scope.symbol === entry.symbol && timestamp >= scope.firstTimestamp && timestamp <= scope.lastTimestamp
|
|
36572
|
+
);
|
|
36573
|
+
};
|
|
36256
36574
|
classifySignalDiagnostic = (signal) => {
|
|
36257
36575
|
if (signal.orderStatus === "failed") {
|
|
36258
36576
|
return {
|
|
@@ -36497,6 +36815,8 @@ var init_runtimeParityDetails = __esm({
|
|
|
36497
36815
|
runtimeSignalEvaluations = [],
|
|
36498
36816
|
replaySignals = [],
|
|
36499
36817
|
replaySignalEvaluations = [],
|
|
36818
|
+
replayLineageScopes = [],
|
|
36819
|
+
runtimeLineageScopes = [],
|
|
36500
36820
|
toleranceMs,
|
|
36501
36821
|
backtestTimestampOffsetMs,
|
|
36502
36822
|
limit
|
|
@@ -36522,7 +36842,16 @@ var init_runtimeParityDetails = __esm({
|
|
|
36522
36842
|
toleranceMs,
|
|
36523
36843
|
timestampOffsetMs: backtestTimestampOffsetMs
|
|
36524
36844
|
});
|
|
36525
|
-
const
|
|
36845
|
+
const nearestReplayEvaluationOutcome = findNearestReplayEvaluationOutcome({
|
|
36846
|
+
entry,
|
|
36847
|
+
lineageScopes: replayLineageScopes,
|
|
36848
|
+
toleranceMs,
|
|
36849
|
+
timestampOffsetMs: backtestTimestampOffsetMs
|
|
36850
|
+
});
|
|
36851
|
+
const classification = nearestReplaySignal ? classifySignalDiagnostic(nearestReplaySignal.signal) : nearestReplayEvaluation ? classifyEvaluationDiagnostic(nearestReplayEvaluation.evaluation) : nearestReplayEvaluationOutcome ? {
|
|
36852
|
+
classification: "core_skipped",
|
|
36853
|
+
reason: nearestReplayEvaluationOutcome.reason
|
|
36854
|
+
} : nearestCandidate?.nearest ? {
|
|
36526
36855
|
classification: "timing_or_price_drift",
|
|
36527
36856
|
reason: nearestCandidate.reason
|
|
36528
36857
|
} : {
|
|
@@ -36546,7 +36875,8 @@ var init_runtimeParityDetails = __esm({
|
|
|
36546
36875
|
timestampDiffMs: nearestReplayEvaluation.timestampDiffMs
|
|
36547
36876
|
}),
|
|
36548
36877
|
...includeArtifacts ? { replayEvaluationArtifact: nearestReplayEvaluation.evaluation } : {}
|
|
36549
|
-
} : {}
|
|
36878
|
+
} : {},
|
|
36879
|
+
...nearestReplayEvaluationOutcome ? { replayEvaluationOutcome: nearestReplayEvaluationOutcome } : {}
|
|
36550
36880
|
};
|
|
36551
36881
|
});
|
|
36552
36882
|
const backtestOnlyDiagnostics = backtestOnly.map((entry, index) => {
|
|
@@ -36564,9 +36894,26 @@ var init_runtimeParityDetails = __esm({
|
|
|
36564
36894
|
toleranceMs,
|
|
36565
36895
|
timestampOffsetMs: 0
|
|
36566
36896
|
});
|
|
36567
|
-
const
|
|
36897
|
+
const nearestRuntimeEvaluationOutcome = findNearestRuntimeEvaluationOutcome(
|
|
36898
|
+
{
|
|
36899
|
+
entry,
|
|
36900
|
+
lineageScopes: runtimeLineageScopes,
|
|
36901
|
+
toleranceMs
|
|
36902
|
+
}
|
|
36903
|
+
);
|
|
36904
|
+
const runtimeLineageCoversEntry = hasRuntimeLineageCoverage({
|
|
36905
|
+
entry,
|
|
36906
|
+
lineageScopes: runtimeLineageScopes
|
|
36907
|
+
});
|
|
36908
|
+
const classification = nearestRuntimeSignal ? classifySignalDiagnostic(nearestRuntimeSignal.signal) : nearestRuntimeEvaluation ? classifyEvaluationDiagnostic(nearestRuntimeEvaluation.evaluation) : nearestRuntimeEvaluationOutcome ? {
|
|
36909
|
+
classification: "core_skipped",
|
|
36910
|
+
reason: nearestRuntimeEvaluationOutcome.reason
|
|
36911
|
+
} : nearestCandidate?.nearest ? {
|
|
36568
36912
|
classification: "timing_or_price_drift",
|
|
36569
36913
|
reason: nearestCandidate.reason
|
|
36914
|
+
} : runtimeLineageCoversEntry ? {
|
|
36915
|
+
classification: "runtime_evaluation_detail_unavailable",
|
|
36916
|
+
reason: "runtime_scope_covers_timestamp_but_skip_detail_was_not_stored"
|
|
36570
36917
|
} : {
|
|
36571
36918
|
classification: "no_runtime_evaluation",
|
|
36572
36919
|
reason: "no_runtime_signal_or_evaluation"
|
|
@@ -36590,7 +36937,8 @@ var init_runtimeParityDetails = __esm({
|
|
|
36590
36937
|
...includeArtifacts ? {
|
|
36591
36938
|
runtimeEvaluationArtifact: nearestRuntimeEvaluation.evaluation
|
|
36592
36939
|
} : {}
|
|
36593
|
-
} : {}
|
|
36940
|
+
} : {},
|
|
36941
|
+
...nearestRuntimeEvaluationOutcome ? { runtimeEvaluationOutcome: nearestRuntimeEvaluationOutcome } : {}
|
|
36594
36942
|
};
|
|
36595
36943
|
});
|
|
36596
36944
|
for (const diagnostic of runtimeOnlyDiagnostics) {
|
|
@@ -36637,7 +36985,9 @@ var init_runtimeParityDetails = __esm({
|
|
|
36637
36985
|
runtimeSignals,
|
|
36638
36986
|
runtimeSignalEvaluations,
|
|
36639
36987
|
replaySignals,
|
|
36640
|
-
replaySignalEvaluations
|
|
36988
|
+
replaySignalEvaluations,
|
|
36989
|
+
replayLineageScopes,
|
|
36990
|
+
runtimeLineageScopes
|
|
36641
36991
|
}) => {
|
|
36642
36992
|
const runtimeDetails = runtimeEntries.map(toRuntimeParityDetail);
|
|
36643
36993
|
const backtestDetails = backtestEntries.map(
|
|
@@ -36696,6 +37046,8 @@ var init_runtimeParityDetails = __esm({
|
|
|
36696
37046
|
runtimeSignalEvaluations,
|
|
36697
37047
|
replaySignals,
|
|
36698
37048
|
replaySignalEvaluations,
|
|
37049
|
+
replayLineageScopes,
|
|
37050
|
+
runtimeLineageScopes,
|
|
36699
37051
|
toleranceMs,
|
|
36700
37052
|
backtestTimestampOffsetMs,
|
|
36701
37053
|
limit
|
|
@@ -36716,7 +37068,9 @@ var init_runtimeParityDetails = __esm({
|
|
|
36716
37068
|
runtimeSignals,
|
|
36717
37069
|
runtimeSignalEvaluations,
|
|
36718
37070
|
replaySignals,
|
|
36719
|
-
replaySignalEvaluations
|
|
37071
|
+
replaySignalEvaluations,
|
|
37072
|
+
replayLineageScopes,
|
|
37073
|
+
runtimeLineageScopes
|
|
36720
37074
|
}) => {
|
|
36721
37075
|
const toExchangeDetail = (entry) => toExchangeParityDetail({
|
|
36722
37076
|
entry,
|
|
@@ -36803,6 +37157,8 @@ var init_runtimeParityDetails = __esm({
|
|
|
36803
37157
|
runtimeSignalEvaluations,
|
|
36804
37158
|
replaySignals,
|
|
36805
37159
|
replaySignalEvaluations,
|
|
37160
|
+
replayLineageScopes,
|
|
37161
|
+
runtimeLineageScopes,
|
|
36806
37162
|
toleranceMs,
|
|
36807
37163
|
backtestTimestampOffsetMs,
|
|
36808
37164
|
limit
|
|
@@ -36934,19 +37290,41 @@ var init_runtimeComparisonCalculations = __esm({
|
|
|
36934
37290
|
lineageScopeKey(record)
|
|
36935
37291
|
])
|
|
36936
37292
|
);
|
|
36937
|
-
const comparableBacktestEntries =
|
|
37293
|
+
const comparableBacktestEntries = [];
|
|
37294
|
+
const excludedBacktestEntryDetails = [];
|
|
37295
|
+
for (const entry of backtestEntries) {
|
|
36938
37296
|
const replayScope = replayScopeByStrategySymbol.get(
|
|
36939
37297
|
`${entry.strategy}::${entry.symbol}`
|
|
36940
37298
|
);
|
|
36941
37299
|
const window = replayScope ? deploymentWindows.get(replayScope) : null;
|
|
36942
37300
|
const signalTimestamp = entry.signalTimestamp ?? entry.timestamp;
|
|
36943
|
-
|
|
36944
|
-
|
|
37301
|
+
if (window != null && signalTimestamp >= window.firstTimestamp && signalTimestamp <= window.lastTimestamp) {
|
|
37302
|
+
comparableBacktestEntries.push(entry);
|
|
37303
|
+
continue;
|
|
37304
|
+
}
|
|
37305
|
+
const reason = !replayScope ? "replay_scope_missing" : !window ? "runtime_scope_missing" : signalTimestamp < window.firstTimestamp ? "before_runtime_window" : "after_runtime_window";
|
|
37306
|
+
excludedBacktestEntryDetails.push({
|
|
37307
|
+
id: entry.id,
|
|
37308
|
+
strategy: entry.strategy,
|
|
37309
|
+
symbol: entry.symbol,
|
|
37310
|
+
direction: entry.direction,
|
|
37311
|
+
...entry.qty != null ? { qty: entry.qty } : {},
|
|
37312
|
+
timestamp: entry.timestamp,
|
|
37313
|
+
...entry.signalTimestamp != null ? { signalTimestamp: entry.signalTimestamp } : {},
|
|
37314
|
+
price: entry.price,
|
|
37315
|
+
...entry.orderId ? { orderId: entry.orderId } : {},
|
|
37316
|
+
...entry.signalId ? { signalId: entry.signalId } : {},
|
|
37317
|
+
...entry.expectedPnl != null ? { expectedPnl: entry.expectedPnl } : {},
|
|
37318
|
+
reason,
|
|
37319
|
+
runtimeWindow: window ?? null
|
|
37320
|
+
});
|
|
37321
|
+
}
|
|
36945
37322
|
const comparableScopeKeys = new Set(deploymentWindows.keys());
|
|
36946
37323
|
return {
|
|
36947
37324
|
runtimeTrades: comparableRuntimeTrades,
|
|
36948
37325
|
runtimeSignals: comparableRuntimeSignals,
|
|
36949
37326
|
runtimeSignalEvaluations: comparableRuntimeEvaluations,
|
|
37327
|
+
runtimeLineageScopes: comparableRuntimeLineageScopes,
|
|
36950
37328
|
backtestEntries: comparableBacktestEntries,
|
|
36951
37329
|
lineage: {
|
|
36952
37330
|
enforced: true,
|
|
@@ -36958,6 +37336,7 @@ var init_runtimeComparisonCalculations = __esm({
|
|
|
36958
37336
|
excludedRuntimeLineageScopes: runtimeLineageScopes.length - comparableRuntimeLineageScopes.length,
|
|
36959
37337
|
excludedExchangeEntries: 0,
|
|
36960
37338
|
excludedBacktestEntries: backtestEntries.length - comparableBacktestEntries.length,
|
|
37339
|
+
excludedBacktestEntryDetails,
|
|
36961
37340
|
reason: comparableScopeKeys.size > 0 ? null : expectedByScope.size === 0 ? "replay_produced_no_lineage_scopes" : "no_runtime_artifacts_with_matching_lineage",
|
|
36962
37341
|
replay: [...expectedByScope.entries()].map(([scope, lineage]) => {
|
|
36963
37342
|
const [deploymentId, accountId, strategy, symbol] = scope.split("::");
|
|
@@ -37569,8 +37948,10 @@ var init_runtimeComparison = __esm({
|
|
|
37569
37948
|
backtestEntries,
|
|
37570
37949
|
replaySignals,
|
|
37571
37950
|
replaySignalEvaluations,
|
|
37951
|
+
replayLineageScopes,
|
|
37572
37952
|
runtimeSignals,
|
|
37573
37953
|
runtimeSignalEvaluations,
|
|
37954
|
+
runtimeLineageScopes,
|
|
37574
37955
|
lineage
|
|
37575
37956
|
}) => {
|
|
37576
37957
|
const { connector, connectorName, window } = getRuntimeCompareContext();
|
|
@@ -37622,7 +38003,9 @@ var init_runtimeComparison = __esm({
|
|
|
37622
38003
|
runtimeSignals,
|
|
37623
38004
|
runtimeSignalEvaluations,
|
|
37624
38005
|
replaySignals,
|
|
37625
|
-
replaySignalEvaluations
|
|
38006
|
+
replaySignalEvaluations,
|
|
38007
|
+
runtimeLineageScopes,
|
|
38008
|
+
replayLineageScopes
|
|
37626
38009
|
});
|
|
37627
38010
|
const emptyRowsByStrategy = new Map(
|
|
37628
38011
|
buildExchangeComparisonRows({
|
|
@@ -37678,7 +38061,9 @@ var init_runtimeComparison = __esm({
|
|
|
37678
38061
|
runtimeSignals,
|
|
37679
38062
|
runtimeSignalEvaluations,
|
|
37680
38063
|
replaySignals,
|
|
37681
|
-
replaySignalEvaluations
|
|
38064
|
+
replaySignalEvaluations,
|
|
38065
|
+
runtimeLineageScopes,
|
|
38066
|
+
replayLineageScopes
|
|
37682
38067
|
});
|
|
37683
38068
|
const rows = buildExchangeComparisonRows({
|
|
37684
38069
|
liveStrategySummaries,
|
|
@@ -37716,6 +38101,7 @@ var init_runtimeComparison = __esm({
|
|
|
37716
38101
|
backtestEntries,
|
|
37717
38102
|
replaySignals,
|
|
37718
38103
|
replayLineages,
|
|
38104
|
+
replayLineageScopes,
|
|
37719
38105
|
runtimeEvidencePath
|
|
37720
38106
|
}) => {
|
|
37721
38107
|
const { connector, connectorName, window } = getRuntimeCompareContext();
|
|
@@ -37827,8 +38213,10 @@ var init_runtimeComparison = __esm({
|
|
|
37827
38213
|
backtestEntries: comparableBacktestEntries,
|
|
37828
38214
|
replaySignals,
|
|
37829
38215
|
replaySignalEvaluations,
|
|
38216
|
+
replayLineageScopes,
|
|
37830
38217
|
runtimeSignals: comparable.runtimeSignals,
|
|
37831
38218
|
runtimeSignalEvaluations: comparable.runtimeSignalEvaluations,
|
|
38219
|
+
runtimeLineageScopes: comparable.runtimeLineageScopes,
|
|
37832
38220
|
lineage: comparable.lineage
|
|
37833
38221
|
});
|
|
37834
38222
|
}
|
|
@@ -37852,7 +38240,9 @@ var init_runtimeComparison = __esm({
|
|
|
37852
38240
|
runtimeSignals: comparable.runtimeSignals,
|
|
37853
38241
|
runtimeSignalEvaluations: comparable.runtimeSignalEvaluations,
|
|
37854
38242
|
replaySignals,
|
|
37855
|
-
replaySignalEvaluations
|
|
38243
|
+
replaySignalEvaluations,
|
|
38244
|
+
runtimeLineageScopes: comparable.runtimeLineageScopes,
|
|
38245
|
+
replayLineageScopes
|
|
37856
38246
|
});
|
|
37857
38247
|
const parityRows = summarizeTradeParityByStrategy({
|
|
37858
38248
|
runtimeEntries: runtimeDedupe.entries,
|
|
@@ -37895,13 +38285,13 @@ var init_runtimeComparison = __esm({
|
|
|
37895
38285
|
});
|
|
37896
38286
|
|
|
37897
38287
|
// src/lib/replay/outputReport.ts
|
|
37898
|
-
var import_promises33, import_node_path30,
|
|
38288
|
+
var import_promises33, import_node_path30, import_data6, formatMsk, formatNumber5, formatMoney2, formatPct, tableCell, createMarkdownTable2, entryStrategy, entryKey, buildDrilldownMap, buildPerTradeAnalysis, renderMatchedRows, renderRuntimeOnlyRows, renderOrderFailedRows, renderBacktestOnlyRows, renderStrategyRows, renderLineageRows, writeReplayOutputReport;
|
|
37899
38289
|
var init_outputReport = __esm({
|
|
37900
38290
|
"src/lib/replay/outputReport.ts"() {
|
|
37901
38291
|
"use strict";
|
|
37902
38292
|
import_promises33 = __toESM(require("fs/promises"));
|
|
37903
38293
|
import_node_path30 = __toESM(require("path"));
|
|
37904
|
-
|
|
38294
|
+
import_data6 = require("@tradejs/core/data");
|
|
37905
38295
|
init_support();
|
|
37906
38296
|
formatMsk = (timestamp) => typeof timestamp === "number" && Number.isFinite(timestamp) ? new Intl.DateTimeFormat("ru-RU", {
|
|
37907
38297
|
timeZone: "Europe/Moscow",
|
|
@@ -38309,7 +38699,7 @@ var init_outputReport = __esm({
|
|
|
38309
38699
|
];
|
|
38310
38700
|
await Promise.all([
|
|
38311
38701
|
import_promises33.default.writeFile(markdownPath, lines.join("\n"), "utf8"),
|
|
38312
|
-
import_promises33.default.writeFile(jsonPath, `${(0,
|
|
38702
|
+
import_promises33.default.writeFile(jsonPath, `${(0, import_data6.toJson)(report, true)}
|
|
38313
38703
|
`, "utf8")
|
|
38314
38704
|
]);
|
|
38315
38705
|
return {
|
|
@@ -38321,13 +38711,13 @@ var init_outputReport = __esm({
|
|
|
38321
38711
|
});
|
|
38322
38712
|
|
|
38323
38713
|
// src/scripts/replayRunner.ts
|
|
38324
|
-
var import_chalk53, import_constants19,
|
|
38714
|
+
var import_chalk53, import_constants19, import_data7, import_time14, import_redis21, prepareReplayBinanceMarketContext, finishReplay, replayBacktest;
|
|
38325
38715
|
var init_replayRunner = __esm({
|
|
38326
38716
|
"src/scripts/replayRunner.ts"() {
|
|
38327
38717
|
"use strict";
|
|
38328
38718
|
import_chalk53 = __toESM(require("chalk"));
|
|
38329
38719
|
import_constants19 = require("@tradejs/core/constants");
|
|
38330
|
-
|
|
38720
|
+
import_data7 = require("@tradejs/core/data");
|
|
38331
38721
|
import_time14 = require("@tradejs/core/time");
|
|
38332
38722
|
import_redis21 = require("@tradejs/infra/redis");
|
|
38333
38723
|
init_runFormatting();
|
|
@@ -38377,6 +38767,7 @@ var init_replayRunner = __esm({
|
|
|
38377
38767
|
backtestEntries: replayStrategySnapshot.backtestEntries,
|
|
38378
38768
|
replaySignals: replayResult.signals,
|
|
38379
38769
|
replayLineages: replayResult.runtimeLineages,
|
|
38770
|
+
replayLineageScopes: replayResult.replayLineageScopes,
|
|
38380
38771
|
runtimeEvidencePath: replayRuntimeEvidencePath
|
|
38381
38772
|
});
|
|
38382
38773
|
const finishedAt = /* @__PURE__ */ new Date();
|
|
@@ -38543,7 +38934,7 @@ var init_replayRunner = __esm({
|
|
|
38543
38934
|
deploymentId: deployment.id,
|
|
38544
38935
|
assetClasses: deployment.assetClasses,
|
|
38545
38936
|
deployment,
|
|
38546
|
-
closedIntervalMs: (0,
|
|
38937
|
+
closedIntervalMs: (0, import_data7.intervalToMs)(replayInterval)
|
|
38547
38938
|
});
|
|
38548
38939
|
if (!preparedRun || isReplayUpdateOnlyRun) {
|
|
38549
38940
|
return;
|