@tradejs/cli 3.1.26-beta.244 → 3.1.27-beta.245

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -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;
@@ -26960,7 +26991,7 @@ var init_runtimeEvidenceSync2 = __esm({
26960
26991
  });
26961
26992
 
26962
26993
  // 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;
26994
+ 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
26995
  var init_runtimeScorecard = __esm({
26965
26996
  "src/lib/runtimeScorecard.ts"() {
26966
26997
  "use strict";
@@ -27180,6 +27211,20 @@ var init_runtimeScorecard = __esm({
27180
27211
  Object.entries(counts).sort(([left], [right]) => left.localeCompare(right))
27181
27212
  );
27182
27213
  };
27214
+ resolveGateDecision = (evaluation) => {
27215
+ const analysis = asRecord6(evaluation.aiAnalysis);
27216
+ const explicit = finiteString2(analysis?.gateDecision);
27217
+ if (explicit === "approved" || explicit === "rejected") {
27218
+ return { decision: explicit, source: "gate_decision" };
27219
+ }
27220
+ if (typeof analysis?.approved === "boolean") {
27221
+ return {
27222
+ decision: analysis.approved ? "approved" : "rejected",
27223
+ source: "legacy_approved_fallback"
27224
+ };
27225
+ }
27226
+ return null;
27227
+ };
27183
27228
  buildDistributions = ({
27184
27229
  evaluations,
27185
27230
  signals: signals2
@@ -27360,18 +27405,15 @@ var init_runtimeScorecard = __esm({
27360
27405
  const signalEvaluations = rows.evaluations.filter(
27361
27406
  (evaluation) => evaluation.status === "signal"
27362
27407
  );
27363
- const gateDecisions = signalEvaluations.map((evaluation) => ({
27364
- evaluation,
27365
- decision: finiteString2(asRecord6(evaluation.aiAnalysis)?.gateDecision)
27366
- })).filter(
27367
- ({ decision }) => decision === "approved" || decision === "rejected"
27408
+ const gateDecisions = signalEvaluations.map(resolveGateDecision).filter(
27409
+ (decision) => Boolean(decision)
27368
27410
  );
27369
27411
  const llmEligibleEvaluations = llmComparatorPolicy === "disabled" ? [] : llmComparatorPolicy === "ai_approved_only" ? signalEvaluations.filter(
27370
- (evaluation) => finiteString2(asRecord6(evaluation.aiAnalysis)?.gateDecision) === "approved"
27412
+ (evaluation) => resolveGateDecision(evaluation)?.decision === "approved"
27371
27413
  ) : signalEvaluations;
27372
27414
  const gateComparisons = llmEligibleEvaluations.map((evaluation) => {
27373
27415
  const analysis = asRecord6(evaluation.aiAnalysis);
27374
- const gateDecision = finiteString2(analysis?.gateDecision);
27416
+ const gateDecision = resolveGateDecision(evaluation)?.decision ?? null;
27375
27417
  const llmDecision = finiteString2(analysis?.llmDecision);
27376
27418
  if (gateDecision !== "approved" && gateDecision !== "rejected" || llmDecision !== "approved" && llmDecision !== "rejected") {
27377
27419
  return null;
@@ -27406,6 +27448,27 @@ var init_runtimeScorecard = __esm({
27406
27448
  };
27407
27449
  const actualSlippageBps = averageSampleMetric("signalToFillAdverseBps");
27408
27450
  const residualVsModelBps = averageSampleMetric("residualVsCurrentModelBps");
27451
+ const signalToArrivalAdverseBps = averageSampleMetric(
27452
+ "signalToArrivalAdverseBps"
27453
+ );
27454
+ const arrivalToFillAdverseBps = averageSampleMetric(
27455
+ "arrivalToFillAdverseBps"
27456
+ );
27457
+ const averageSignalCloseToSubmitMs = averageSampleMetric(
27458
+ "signalCloseToSubmitMs"
27459
+ );
27460
+ const averageSubmitToFillMs = averageSampleMetric("submitToFillMs");
27461
+ const fullTelemetryTrades = calibrationSamples.filter(
27462
+ (sample) => finiteString2(sample.telemetryQuality) === "full"
27463
+ ).length;
27464
+ const arrivalQuoteTrades = calibrationSamples.filter(
27465
+ (sample) => finiteNumber5(sample.arrivalMid) != null
27466
+ ).length;
27467
+ const arrivalQuoteCoverage = calibrationSamples.length ? round(arrivalQuoteTrades / calibrationSamples.length) : null;
27468
+ const hasTelemetryQuality = calibrationSamples.some(
27469
+ (sample) => finiteString2(sample.telemetryQuality) != null
27470
+ );
27471
+ const maximumSignalCloseToSubmitMs = thresholds.maximumSignalCloseToSubmitMs ?? 3e4;
27409
27472
  const prospective = getProspectiveSummary(prospectiveEvidenceArtifact);
27410
27473
  const historyTrades = dedupeTrades([
27411
27474
  ...scopedHistoryArtifacts,
@@ -27462,7 +27525,28 @@ var init_runtimeScorecard = __esm({
27462
27525
  reactions.push({
27463
27526
  code: "SLIPPAGE_DRIFT",
27464
27527
  severity: "warning",
27465
- message: `Execution residual ${round(residualVsModelBps, 2)} bps exceeds ${thresholds.maximumSlippageResidualBps} bps.`
27528
+ message: `Signal-close-to-fill residual ${round(residualVsModelBps, 2)} bps exceeds ${thresholds.maximumSlippageResidualBps} bps.`
27529
+ });
27530
+ }
27531
+ if (averageSignalCloseToSubmitMs != null && averageSignalCloseToSubmitMs > maximumSignalCloseToSubmitMs) {
27532
+ reactions.push({
27533
+ code: "PRE_SUBMIT_LATENCY",
27534
+ severity: "warning",
27535
+ message: `Average signal-close-to-submit latency ${round(averageSignalCloseToSubmitMs, 3)} ms exceeds ${maximumSignalCloseToSubmitMs} ms.`
27536
+ });
27537
+ }
27538
+ if (hasTelemetryQuality && calibrationSamples.length > 0 && fullTelemetryTrades < calibrationSamples.length) {
27539
+ reactions.push({
27540
+ code: "EXECUTION_TELEMETRY_INCOMPLETE",
27541
+ severity: "info",
27542
+ message: `Full arrival and fill telemetry is available for ${fullTelemetryTrades}/${calibrationSamples.length} trades.`
27543
+ });
27544
+ }
27545
+ if (riskDecisions.some((decision) => decision.status === "rejected")) {
27546
+ reactions.push({
27547
+ code: "RISK_PLAN_EXCEEDS_DECLARED_LIMIT",
27548
+ severity: "warning",
27549
+ message: "At least one observed order plan exceeds its declared MAX_LOSS_VALUE; telemetry is not yet enforced."
27466
27550
  });
27467
27551
  }
27468
27552
  if (sevenDay.closedTrades >= thresholds.minimumClosedTrades && sevenDay.expectancy != null && sevenDay.expectancy < thresholds.minimumExpectancy) {
@@ -27491,7 +27575,10 @@ var init_runtimeScorecard = __esm({
27491
27575
  deployment: deploymentBinding,
27492
27576
  lineage: runtimeLineage,
27493
27577
  promotionStatus,
27494
- thresholds,
27578
+ thresholds: {
27579
+ ...thresholds,
27580
+ maximumSignalCloseToSubmitMs
27581
+ },
27495
27582
  funnel: {
27496
27583
  evaluations: rows.evaluations.length,
27497
27584
  coreCandidates: signalEvaluations.length,
@@ -27503,25 +27590,38 @@ var init_runtimeScorecard = __esm({
27503
27590
  rejected: gateDecisions.filter(
27504
27591
  ({ decision }) => decision === "rejected"
27505
27592
  ).length,
27506
- coverage: signalEvaluations.length ? round(gateDecisions.length / signalEvaluations.length) : null
27593
+ coverage: signalEvaluations.length ? round(gateDecisions.length / signalEvaluations.length) : null,
27594
+ legacyApprovedFallback: gateDecisions.filter(
27595
+ ({ source }) => source === "legacy_approved_fallback"
27596
+ ).length
27507
27597
  },
27508
27598
  allocator: {
27509
27599
  available: allocatorDecisions.length > 0,
27510
27600
  approved: allocatorDecisions.filter(
27511
- (decision) => decision.decision === "approved"
27601
+ (decision) => decision.status === "approved"
27512
27602
  ).length,
27513
27603
  rejected: allocatorDecisions.filter(
27514
- (decision) => decision.decision === "rejected"
27604
+ (decision) => decision.status === "rejected"
27605
+ ).length,
27606
+ notApplicable: allocatorDecisions.filter(
27607
+ (decision) => decision.status === "not_applicable"
27608
+ ).length,
27609
+ unavailable: allocatorDecisions.filter(
27610
+ (decision) => decision.status === "unavailable"
27515
27611
  ).length
27516
27612
  },
27517
27613
  risk: {
27518
27614
  available: riskDecisions.length > 0,
27519
27615
  approved: riskDecisions.filter(
27520
- (decision) => decision.decision === "approved"
27616
+ (decision) => decision.status === "approved"
27521
27617
  ).length,
27522
27618
  rejected: riskDecisions.filter(
27523
- (decision) => decision.decision === "rejected"
27524
- ).length
27619
+ (decision) => decision.status === "rejected"
27620
+ ).length,
27621
+ unavailable: riskDecisions.filter(
27622
+ (decision) => decision.status === "unavailable"
27623
+ ).length,
27624
+ enforced: riskDecisions.some((decision) => decision.enforced === true)
27525
27625
  },
27526
27626
  orderAttempts: orderAttempts.length,
27527
27627
  orderFailures: orderAttempts.filter(
@@ -27583,8 +27683,16 @@ var init_runtimeScorecard = __esm({
27583
27683
  },
27584
27684
  execution: {
27585
27685
  available: actualSlippageBps != null || residualVsModelBps != null,
27686
+ metricScope: arrivalToFillAdverseBps == null ? "signal_close_to_fill" : "signal_and_exchange_components",
27687
+ samples: calibrationSamples.length,
27688
+ fullTelemetryTrades,
27689
+ arrivalQuoteCoverage,
27586
27690
  actualSignalToFillSlippageBps: actualSlippageBps,
27587
- residualVsCurrentModelBps: residualVsModelBps
27691
+ residualVsCurrentModelBps: residualVsModelBps,
27692
+ signalToArrivalAdverseBps,
27693
+ arrivalToFillAdverseBps,
27694
+ averageSignalCloseToSubmitMs,
27695
+ averageSubmitToFillMs
27588
27696
  },
27589
27697
  prospective,
27590
27698
  rolling,
@@ -27638,10 +27746,10 @@ Window: ${new Date(scorecard.window.startTime).toISOString()} \u2014 ${new Date(
27638
27746
 
27639
27747
  - Evaluations: ${scorecard.funnel.evaluations}
27640
27748
  - Core candidates: ${scorecard.funnel.coreCandidates}
27641
- - Gate approvals/rejects: ${scorecard.funnel.gate.approved}/${scorecard.funnel.gate.rejected}
27749
+ - Gate approvals/rejects: ${scorecard.funnel.gate.approved}/${scorecard.funnel.gate.rejected} (legacy fallback: ${scorecard.funnel.gate.legacyApprovedFallback})
27642
27750
  - AI / LLM disagreement: ${scorecard.gateComparison.disagreements}/${scorecard.gateComparison.compared}
27643
- - Allocator approvals/rejects: ${scorecard.funnel.allocator.available ? `${scorecard.funnel.allocator.approved}/${scorecard.funnel.allocator.rejected}` : "unavailable"}
27644
- - Risk approvals/rejects: ${scorecard.funnel.risk.available ? `${scorecard.funnel.risk.approved}/${scorecard.funnel.risk.rejected}` : "unavailable"}
27751
+ - Runtime admission approved/rejected/not-applicable: ${scorecard.funnel.allocator.available ? `${scorecard.funnel.allocator.approved}/${scorecard.funnel.allocator.rejected}/${scorecard.funnel.allocator.notApplicable}` : "unavailable"}
27752
+ - 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
27753
  - Order attempts/failures: ${scorecard.funnel.orderAttempts}/${scorecard.funnel.orderFailures}
27646
27754
  - Fills: ${scorecard.funnel.fills}
27647
27755
  - Closed trades: ${scorecard.funnel.closedTrades}
@@ -27650,8 +27758,14 @@ Window: ${new Date(scorecard.window.startTime).toISOString()} \u2014 ${new Date(
27650
27758
 
27651
27759
  - Parity: ${scorecard.parity.ratio == null ? "n/a" : `${round(scorecard.parity.ratio * 100, 2)}%`}
27652
27760
  - Runtime/replay mismatches: ${scorecard.parity.backtestOnly + scorecard.parity.runtimeOnly}
27653
- - Actual signal-to-fill slippage: ${scorecard.execution.actualSignalToFillSlippageBps ?? "n/a"} bps
27761
+ - Execution metric scope: ${scorecard.execution.metricScope}
27762
+ - Telemetry samples/full/arrival coverage: ${scorecard.execution.samples}/${scorecard.execution.fullTelemetryTrades}/${scorecard.execution.arrivalQuoteCoverage == null ? "n/a" : `${round(scorecard.execution.arrivalQuoteCoverage * 100, 2)}%`}
27763
+ - Signal-close-to-fill adverse move: ${scorecard.execution.actualSignalToFillSlippageBps ?? "n/a"} bps
27764
+ - Signal-to-arrival adverse move: ${scorecard.execution.signalToArrivalAdverseBps ?? "n/a"} bps
27765
+ - Arrival-to-fill exchange slippage: ${scorecard.execution.arrivalToFillAdverseBps ?? "n/a"} bps
27654
27766
  - Residual vs current model: ${scorecard.execution.residualVsCurrentModelBps ?? "n/a"} bps
27767
+ - Average signal-close-to-submit: ${scorecard.execution.averageSignalCloseToSubmitMs ?? "n/a"} ms
27768
+ - Average submit-to-fill: ${scorecard.execution.averageSubmitToFillMs ?? "n/a"} ms
27655
27769
 
27656
27770
  ## Rolling performance
27657
27771
 
@@ -29179,6 +29293,11 @@ var init_runtimeScorecard2 = __esm({
29179
29293
  "Maximum acceptable execution residual in bps",
29180
29294
  3
29181
29295
  );
29296
+ import_args30.default.option(
29297
+ ["L", "maximumSignalCloseToSubmitMs"],
29298
+ "Maximum acceptable average signal-close-to-submit latency in milliseconds",
29299
+ 3e4
29300
+ );
29182
29301
  import_args30.default.option(
29183
29302
  ["T", "minimumClosedTrades"],
29184
29303
  "Minimum 7d closed trades for expectancy reactions",
@@ -29271,6 +29390,7 @@ var init_runtimeScorecard2 = __esm({
29271
29390
  thresholds: {
29272
29391
  minimumParityRatio: Number(flags29.minimumParityRatio),
29273
29392
  maximumSlippageResidualBps: Number(flags29.maximumSlippageResidualBps),
29393
+ maximumSignalCloseToSubmitMs: Number(flags29.maximumSignalCloseToSubmitMs),
29274
29394
  minimumClosedTrades: Number(flags29.minimumClosedTrades),
29275
29395
  minimumExpectancy: Number(flags29.minimumExpectancy)
29276
29396
  },
@@ -32667,6 +32787,10 @@ var init_evaluations = __esm({
32667
32787
  lastTimestamp: Math.max(
32668
32788
  existing?.lastTimestamp ?? evaluation.timestamp,
32669
32789
  evaluation.timestamp
32790
+ ),
32791
+ evaluationRuns: appendRuntimeSignalEvaluationRun(
32792
+ existing?.evaluationRuns,
32793
+ evaluation
32670
32794
  )
32671
32795
  };
32672
32796
  runtimeLineageScopeCache.set(cacheKey, scope);
@@ -33398,6 +33522,8 @@ var init_tickerEvaluator = __esm({
33398
33522
  orderStatus: signal.orderStatus,
33399
33523
  orderSkipReason: signal.orderSkipReason,
33400
33524
  ...signal.aiAnalysis ? { aiAnalysis: signal.aiAnalysis } : {},
33525
+ ...signal.allocatorDecision ? { allocatorDecision: signal.allocatorDecision } : {},
33526
+ ...signal.riskDecision ? { riskDecision: signal.riskDecision } : {},
33401
33527
  ...signal.ml ? { ml: signal.ml } : {}
33402
33528
  });
33403
33529
  import_logger9.logger.info("Signal found %s by strategy %s", symbol, strategyName);
@@ -36077,7 +36203,7 @@ var init_runtimeTradeSync = __esm({
36077
36203
  });
36078
36204
 
36079
36205
  // 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;
36206
+ 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, hasRuntimeLineageCoverage, classifySignalDiagnostic, classifyEvaluationDiagnostic, summarizeMismatchDiagnostics, buildPnlDelta, buildPriceDeltaPct2, toFiniteNumberOrNull3, toAiDiagnostic, toSignalDiagnostic, toEvaluationDiagnostic, buildSlippageCost, buildMatchedReplayDetail, buildNearestCandidates, buildReplayMismatchDrilldown, buildReplayRuntimeComparisonDetails, buildReplayExchangeComparisonDetails;
36081
36207
  var init_runtimeParityDetails = __esm({
36082
36208
  "src/lib/runtimeParityDetails.ts"() {
36083
36209
  "use strict";
@@ -36253,6 +36379,50 @@ var init_runtimeParityDetails = __esm({
36253
36379
  timestampDiffMs: bestDiff
36254
36380
  } : null;
36255
36381
  };
36382
+ findNearestRuntimeEvaluationOutcome = ({
36383
+ entry,
36384
+ lineageScopes,
36385
+ toleranceMs
36386
+ }) => {
36387
+ const strategy = getEntryStrategy(entry);
36388
+ const timestamp = entry.signalTimestamp ?? entry.timestamp;
36389
+ if (!strategy) return null;
36390
+ let nearest = null;
36391
+ for (const scope of lineageScopes) {
36392
+ if (scope.strategy !== strategy || scope.symbol !== entry.symbol) {
36393
+ continue;
36394
+ }
36395
+ for (const run of scope.evaluationRuns ?? []) {
36396
+ if (run.stepMs <= 0 || timestamp < run.firstTimestamp - toleranceMs || timestamp > run.lastTimestamp + toleranceMs) {
36397
+ continue;
36398
+ }
36399
+ const step = Math.round((timestamp - run.firstTimestamp) / run.stepMs);
36400
+ const candidateTimestamp = run.firstTimestamp + step * run.stepMs;
36401
+ const timestampDiffMs = Math.abs(candidateTimestamp - timestamp);
36402
+ if (candidateTimestamp < run.firstTimestamp || candidateTimestamp > run.lastTimestamp || timestampDiffMs > toleranceMs || nearest && timestampDiffMs >= nearest.timestampDiffMs) {
36403
+ continue;
36404
+ }
36405
+ nearest = {
36406
+ status: run.status,
36407
+ reason: run.reason,
36408
+ timestamp: candidateTimestamp,
36409
+ timestampDiffMs,
36410
+ source: "lineage_scope_compact"
36411
+ };
36412
+ }
36413
+ }
36414
+ return nearest;
36415
+ };
36416
+ hasRuntimeLineageCoverage = ({
36417
+ entry,
36418
+ lineageScopes
36419
+ }) => {
36420
+ const strategy = getEntryStrategy(entry);
36421
+ const timestamp = entry.signalTimestamp ?? entry.timestamp;
36422
+ return lineageScopes.some(
36423
+ (scope) => scope.strategy === strategy && scope.symbol === entry.symbol && timestamp >= scope.firstTimestamp && timestamp <= scope.lastTimestamp
36424
+ );
36425
+ };
36256
36426
  classifySignalDiagnostic = (signal) => {
36257
36427
  if (signal.orderStatus === "failed") {
36258
36428
  return {
@@ -36497,6 +36667,7 @@ var init_runtimeParityDetails = __esm({
36497
36667
  runtimeSignalEvaluations = [],
36498
36668
  replaySignals = [],
36499
36669
  replaySignalEvaluations = [],
36670
+ runtimeLineageScopes = [],
36500
36671
  toleranceMs,
36501
36672
  backtestTimestampOffsetMs,
36502
36673
  limit
@@ -36564,9 +36735,26 @@ var init_runtimeParityDetails = __esm({
36564
36735
  toleranceMs,
36565
36736
  timestampOffsetMs: 0
36566
36737
  });
36567
- const classification = nearestRuntimeSignal ? classifySignalDiagnostic(nearestRuntimeSignal.signal) : nearestRuntimeEvaluation ? classifyEvaluationDiagnostic(nearestRuntimeEvaluation.evaluation) : nearestCandidate?.nearest ? {
36738
+ const nearestRuntimeEvaluationOutcome = findNearestRuntimeEvaluationOutcome(
36739
+ {
36740
+ entry,
36741
+ lineageScopes: runtimeLineageScopes,
36742
+ toleranceMs
36743
+ }
36744
+ );
36745
+ const runtimeLineageCoversEntry = hasRuntimeLineageCoverage({
36746
+ entry,
36747
+ lineageScopes: runtimeLineageScopes
36748
+ });
36749
+ const classification = nearestRuntimeSignal ? classifySignalDiagnostic(nearestRuntimeSignal.signal) : nearestRuntimeEvaluation ? classifyEvaluationDiagnostic(nearestRuntimeEvaluation.evaluation) : nearestRuntimeEvaluationOutcome ? {
36750
+ classification: "core_skipped",
36751
+ reason: nearestRuntimeEvaluationOutcome.reason
36752
+ } : nearestCandidate?.nearest ? {
36568
36753
  classification: "timing_or_price_drift",
36569
36754
  reason: nearestCandidate.reason
36755
+ } : runtimeLineageCoversEntry ? {
36756
+ classification: "runtime_evaluation_detail_unavailable",
36757
+ reason: "runtime_scope_covers_timestamp_but_skip_detail_was_not_stored"
36570
36758
  } : {
36571
36759
  classification: "no_runtime_evaluation",
36572
36760
  reason: "no_runtime_signal_or_evaluation"
@@ -36590,7 +36778,8 @@ var init_runtimeParityDetails = __esm({
36590
36778
  ...includeArtifacts ? {
36591
36779
  runtimeEvaluationArtifact: nearestRuntimeEvaluation.evaluation
36592
36780
  } : {}
36593
- } : {}
36781
+ } : {},
36782
+ ...nearestRuntimeEvaluationOutcome ? { runtimeEvaluationOutcome: nearestRuntimeEvaluationOutcome } : {}
36594
36783
  };
36595
36784
  });
36596
36785
  for (const diagnostic of runtimeOnlyDiagnostics) {
@@ -36637,7 +36826,8 @@ var init_runtimeParityDetails = __esm({
36637
36826
  runtimeSignals,
36638
36827
  runtimeSignalEvaluations,
36639
36828
  replaySignals,
36640
- replaySignalEvaluations
36829
+ replaySignalEvaluations,
36830
+ runtimeLineageScopes
36641
36831
  }) => {
36642
36832
  const runtimeDetails = runtimeEntries.map(toRuntimeParityDetail);
36643
36833
  const backtestDetails = backtestEntries.map(
@@ -36696,6 +36886,7 @@ var init_runtimeParityDetails = __esm({
36696
36886
  runtimeSignalEvaluations,
36697
36887
  replaySignals,
36698
36888
  replaySignalEvaluations,
36889
+ runtimeLineageScopes,
36699
36890
  toleranceMs,
36700
36891
  backtestTimestampOffsetMs,
36701
36892
  limit
@@ -36716,7 +36907,8 @@ var init_runtimeParityDetails = __esm({
36716
36907
  runtimeSignals,
36717
36908
  runtimeSignalEvaluations,
36718
36909
  replaySignals,
36719
- replaySignalEvaluations
36910
+ replaySignalEvaluations,
36911
+ runtimeLineageScopes
36720
36912
  }) => {
36721
36913
  const toExchangeDetail = (entry) => toExchangeParityDetail({
36722
36914
  entry,
@@ -36803,6 +36995,7 @@ var init_runtimeParityDetails = __esm({
36803
36995
  runtimeSignalEvaluations,
36804
36996
  replaySignals,
36805
36997
  replaySignalEvaluations,
36998
+ runtimeLineageScopes,
36806
36999
  toleranceMs,
36807
37000
  backtestTimestampOffsetMs,
36808
37001
  limit
@@ -36947,6 +37140,7 @@ var init_runtimeComparisonCalculations = __esm({
36947
37140
  runtimeTrades: comparableRuntimeTrades,
36948
37141
  runtimeSignals: comparableRuntimeSignals,
36949
37142
  runtimeSignalEvaluations: comparableRuntimeEvaluations,
37143
+ runtimeLineageScopes: comparableRuntimeLineageScopes,
36950
37144
  backtestEntries: comparableBacktestEntries,
36951
37145
  lineage: {
36952
37146
  enforced: true,
@@ -37571,6 +37765,7 @@ var init_runtimeComparison = __esm({
37571
37765
  replaySignalEvaluations,
37572
37766
  runtimeSignals,
37573
37767
  runtimeSignalEvaluations,
37768
+ runtimeLineageScopes,
37574
37769
  lineage
37575
37770
  }) => {
37576
37771
  const { connector, connectorName, window } = getRuntimeCompareContext();
@@ -37622,7 +37817,8 @@ var init_runtimeComparison = __esm({
37622
37817
  runtimeSignals,
37623
37818
  runtimeSignalEvaluations,
37624
37819
  replaySignals,
37625
- replaySignalEvaluations
37820
+ replaySignalEvaluations,
37821
+ runtimeLineageScopes
37626
37822
  });
37627
37823
  const emptyRowsByStrategy = new Map(
37628
37824
  buildExchangeComparisonRows({
@@ -37678,7 +37874,8 @@ var init_runtimeComparison = __esm({
37678
37874
  runtimeSignals,
37679
37875
  runtimeSignalEvaluations,
37680
37876
  replaySignals,
37681
- replaySignalEvaluations
37877
+ replaySignalEvaluations,
37878
+ runtimeLineageScopes
37682
37879
  });
37683
37880
  const rows = buildExchangeComparisonRows({
37684
37881
  liveStrategySummaries,
@@ -37829,6 +38026,7 @@ var init_runtimeComparison = __esm({
37829
38026
  replaySignalEvaluations,
37830
38027
  runtimeSignals: comparable.runtimeSignals,
37831
38028
  runtimeSignalEvaluations: comparable.runtimeSignalEvaluations,
38029
+ runtimeLineageScopes: comparable.runtimeLineageScopes,
37832
38030
  lineage: comparable.lineage
37833
38031
  });
37834
38032
  }
@@ -37852,7 +38050,8 @@ var init_runtimeComparison = __esm({
37852
38050
  runtimeSignals: comparable.runtimeSignals,
37853
38051
  runtimeSignalEvaluations: comparable.runtimeSignalEvaluations,
37854
38052
  replaySignals,
37855
- replaySignalEvaluations
38053
+ replaySignalEvaluations,
38054
+ runtimeLineageScopes: comparable.runtimeLineageScopes
37856
38055
  });
37857
38056
  const parityRows = summarizeTradeParityByStrategy({
37858
38057
  runtimeEntries: runtimeDedupe.entries,
@@ -37895,13 +38094,13 @@ var init_runtimeComparison = __esm({
37895
38094
  });
37896
38095
 
37897
38096
  // src/lib/replay/outputReport.ts
37898
- var import_promises33, import_node_path30, import_data4, formatMsk, formatNumber5, formatMoney2, formatPct, tableCell, createMarkdownTable2, entryStrategy, entryKey, buildDrilldownMap, buildPerTradeAnalysis, renderMatchedRows, renderRuntimeOnlyRows, renderOrderFailedRows, renderBacktestOnlyRows, renderStrategyRows, renderLineageRows, writeReplayOutputReport;
38097
+ var import_promises33, import_node_path30, import_data5, formatMsk, formatNumber5, formatMoney2, formatPct, tableCell, createMarkdownTable2, entryStrategy, entryKey, buildDrilldownMap, buildPerTradeAnalysis, renderMatchedRows, renderRuntimeOnlyRows, renderOrderFailedRows, renderBacktestOnlyRows, renderStrategyRows, renderLineageRows, writeReplayOutputReport;
37899
38098
  var init_outputReport = __esm({
37900
38099
  "src/lib/replay/outputReport.ts"() {
37901
38100
  "use strict";
37902
38101
  import_promises33 = __toESM(require("fs/promises"));
37903
38102
  import_node_path30 = __toESM(require("path"));
37904
- import_data4 = require("@tradejs/core/data");
38103
+ import_data5 = require("@tradejs/core/data");
37905
38104
  init_support();
37906
38105
  formatMsk = (timestamp) => typeof timestamp === "number" && Number.isFinite(timestamp) ? new Intl.DateTimeFormat("ru-RU", {
37907
38106
  timeZone: "Europe/Moscow",
@@ -38309,7 +38508,7 @@ var init_outputReport = __esm({
38309
38508
  ];
38310
38509
  await Promise.all([
38311
38510
  import_promises33.default.writeFile(markdownPath, lines.join("\n"), "utf8"),
38312
- import_promises33.default.writeFile(jsonPath, `${(0, import_data4.toJson)(report, true)}
38511
+ import_promises33.default.writeFile(jsonPath, `${(0, import_data5.toJson)(report, true)}
38313
38512
  `, "utf8")
38314
38513
  ]);
38315
38514
  return {
@@ -38321,13 +38520,13 @@ var init_outputReport = __esm({
38321
38520
  });
38322
38521
 
38323
38522
  // src/scripts/replayRunner.ts
38324
- var import_chalk53, import_constants19, import_data5, import_time14, import_redis21, prepareReplayBinanceMarketContext, finishReplay, replayBacktest;
38523
+ var import_chalk53, import_constants19, import_data6, import_time14, import_redis21, prepareReplayBinanceMarketContext, finishReplay, replayBacktest;
38325
38524
  var init_replayRunner = __esm({
38326
38525
  "src/scripts/replayRunner.ts"() {
38327
38526
  "use strict";
38328
38527
  import_chalk53 = __toESM(require("chalk"));
38329
38528
  import_constants19 = require("@tradejs/core/constants");
38330
- import_data5 = require("@tradejs/core/data");
38529
+ import_data6 = require("@tradejs/core/data");
38331
38530
  import_time14 = require("@tradejs/core/time");
38332
38531
  import_redis21 = require("@tradejs/infra/redis");
38333
38532
  init_runFormatting();
@@ -38543,7 +38742,7 @@ var init_replayRunner = __esm({
38543
38742
  deploymentId: deployment.id,
38544
38743
  assetClasses: deployment.assetClasses,
38545
38744
  deployment,
38546
- closedIntervalMs: (0, import_data5.intervalToMs)(replayInterval)
38745
+ closedIntervalMs: (0, import_data6.intervalToMs)(replayInterval)
38547
38746
  });
38548
38747
  if (!preparedRun || isReplayUpdateOnlyRun) {
38549
38748
  return;
@@ -104,6 +104,7 @@ var import_redis2 = require("@tradejs/infra/redis");
104
104
 
105
105
  // src/lib/runtimeSignalsStorage.ts
106
106
  var import_constants = require("@tradejs/core/constants");
107
+ var import_data = require("@tradejs/core/data");
107
108
  var import_time2 = require("@tradejs/core/time");
108
109
  var isRuntimeSignalBucketRef = (value) => {
109
110
  if (!value || typeof value !== "object") {