@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.
@@ -40,7 +40,7 @@ __export(replayRunner_exports, {
40
40
  module.exports = __toCommonJS(replayRunner_exports);
41
41
  var import_chalk13 = __toESM(require("chalk"));
42
42
  var import_constants6 = require("@tradejs/core/constants");
43
- var import_data2 = require("@tradejs/core/data");
43
+ var import_data4 = require("@tradejs/core/data");
44
44
  var import_time7 = require("@tradejs/core/time");
45
45
  var import_redis6 = require("@tradejs/infra/redis");
46
46
 
@@ -5614,6 +5614,78 @@ var prepareHistoricalReplay = async (context, adapters) => {
5614
5614
  };
5615
5615
  };
5616
5616
 
5617
+ // src/lib/replay/historicalSignalsReplaySkipEvidence.ts
5618
+ var import_data = require("@tradejs/core/data");
5619
+ var replayScopeKey = (strategy, symbol) => `${strategy}::${symbol}`;
5620
+ var appendReplaySignalEvaluationRun = ({
5621
+ runs = [],
5622
+ reason,
5623
+ timestamp,
5624
+ interval
5625
+ }) => {
5626
+ const stepMs = (0, import_data.intervalToMs)(interval);
5627
+ const last = runs.at(-1);
5628
+ if (last?.reason === reason && last.stepMs === stepMs && timestamp === last.lastTimestamp + stepMs) {
5629
+ return [...runs.slice(0, -1), { ...last, lastTimestamp: timestamp }];
5630
+ }
5631
+ return [
5632
+ ...runs,
5633
+ {
5634
+ status: "skip",
5635
+ reason,
5636
+ firstTimestamp: timestamp,
5637
+ lastTimestamp: timestamp,
5638
+ stepMs
5639
+ }
5640
+ ];
5641
+ };
5642
+ var createHistoricalReplaySkipEvidence = (runtimeLineages) => {
5643
+ const lineageByStrategySymbol = new Map(
5644
+ runtimeLineages.map((record2) => [
5645
+ replayScopeKey(record2.strategy, record2.symbol),
5646
+ record2
5647
+ ])
5648
+ );
5649
+ const scopes = /* @__PURE__ */ new Map();
5650
+ const record = ({
5651
+ strategyName,
5652
+ symbol,
5653
+ strategyConfig,
5654
+ runtimeLineage,
5655
+ timestamp,
5656
+ result
5657
+ }) => {
5658
+ const scopeKey = replayScopeKey(strategyName, symbol);
5659
+ const lineageRecord = lineageByStrategySymbol.get(scopeKey);
5660
+ if (!lineageRecord) return;
5661
+ const existing = scopes.get(scopeKey);
5662
+ const skipReason = typeof result === "string" ? result.trim() || "NO_SIGNAL" : result ? null : "NO_SIGNAL";
5663
+ scopes.set(scopeKey, {
5664
+ strategy: strategyName,
5665
+ symbol,
5666
+ ...lineageRecord.deploymentId ? { deploymentId: lineageRecord.deploymentId } : {},
5667
+ ...lineageRecord.accountId ? { accountId: lineageRecord.accountId } : {},
5668
+ strategyRevision: runtimeLineage.strategyRevision,
5669
+ lineage: runtimeLineage,
5670
+ firstTimestamp: Math.min(
5671
+ existing?.firstTimestamp ?? timestamp,
5672
+ timestamp
5673
+ ),
5674
+ lastTimestamp: Math.max(existing?.lastTimestamp ?? timestamp, timestamp),
5675
+ evaluationRuns: skipReason ? appendReplaySignalEvaluationRun({
5676
+ runs: existing?.evaluationRuns,
5677
+ reason: skipReason,
5678
+ timestamp,
5679
+ interval: strategyConfig.INTERVAL ?? "15"
5680
+ }) : existing?.evaluationRuns
5681
+ });
5682
+ };
5683
+ const values = () => [...scopes.values()].sort(
5684
+ (left, right) => left.strategy.localeCompare(right.strategy) || left.symbol.localeCompare(right.symbol)
5685
+ );
5686
+ return { record, values };
5687
+ };
5688
+
5617
5689
  // src/lib/replay/historicalSignalsReplayExecution.ts
5618
5690
  var advanceCycleMarket = async (plan, connector, timestamp) => {
5619
5691
  const cycleSymbols = plan.cycleSymbolsByTimestamp.get(timestamp) ?? [];
@@ -5639,6 +5711,9 @@ var advanceAbortedCycle = (cycleSymbols, timestamp) => {
5639
5711
  var executeHistoricalReplay = async (context, adapters) => {
5640
5712
  const signals = [];
5641
5713
  let abortedCycles = 0;
5714
+ const skipEvidence = createHistoricalReplaySkipEvidence(
5715
+ context.plan.runtimeLineages
5716
+ );
5642
5717
  try {
5643
5718
  for (const timestamp of context.plan.orderedTimestamps) {
5644
5719
  const cycleStartedAt = adapters.clock.now();
@@ -5681,6 +5756,14 @@ var executeHistoricalReplay = async (context, adapters) => {
5681
5756
  btcCandle,
5682
5757
  ethCandle?.timestamp === timestamp ? ethCandle : void 0
5683
5758
  );
5759
+ skipEvidence.record({
5760
+ strategyName: strategyRuntime.strategyName,
5761
+ symbol: symbolRuntime.symbol,
5762
+ strategyConfig: strategyRuntime.strategyConfig,
5763
+ runtimeLineage: strategyRuntime.runtimeLineage,
5764
+ timestamp,
5765
+ result
5766
+ });
5684
5767
  if (result && typeof result !== "string") {
5685
5768
  result.runtimeLineage = strategyRuntime.runtimeLineage;
5686
5769
  await adapters.enrichSignal(result);
@@ -5708,7 +5791,11 @@ var executeHistoricalReplay = async (context, adapters) => {
5708
5791
  adapters.releaseReplayCache(keyPrefix);
5709
5792
  }
5710
5793
  }
5711
- return { signals, abortedCycles };
5794
+ return {
5795
+ signals,
5796
+ abortedCycles,
5797
+ replayLineageScopes: skipEvidence.values()
5798
+ };
5712
5799
  };
5713
5800
 
5714
5801
  // src/lib/replay/historicalSignalsReplayResults.ts
@@ -5737,7 +5824,8 @@ var collectHistoricalReplayResult = (context) => ({
5737
5824
  positionLog: context.artifacts.positionLog,
5738
5825
  cycleCount: context.cycleCount,
5739
5826
  abortedCycles: context.abortedCycles,
5740
- runtimeLineages: context.runtimeLineages
5827
+ runtimeLineages: context.runtimeLineages,
5828
+ replayLineageScopes: context.replayLineageScopes
5741
5829
  });
5742
5830
 
5743
5831
  // src/lib/replay/historicalSignalsReplay.ts
@@ -5956,7 +6044,8 @@ var runHistoricalSignalsReplay = async ({
5956
6044
  signals: execution.signals,
5957
6045
  cycleCount: plan.orderedTimestamps.length,
5958
6046
  abortedCycles: execution.abortedCycles,
5959
- runtimeLineages: plan.runtimeLineages
6047
+ runtimeLineages: plan.runtimeLineages,
6048
+ replayLineageScopes: execution.replayLineageScopes
5960
6049
  });
5961
6050
  };
5962
6051
 
@@ -6443,6 +6532,7 @@ var import_redis4 = require("@tradejs/infra/redis");
6443
6532
 
6444
6533
  // src/lib/runtimeSignalsStorage.ts
6445
6534
  var import_constants4 = require("@tradejs/core/constants");
6535
+ var import_data2 = require("@tradejs/core/data");
6446
6536
  var import_time4 = require("@tradejs/core/time");
6447
6537
  var isRuntimeSignalBucketRef = (value) => {
6448
6538
  if (!value || typeof value !== "object") {
@@ -6991,6 +7081,90 @@ var findNearestEvaluation = ({
6991
7081
  timestampDiffMs: bestDiff
6992
7082
  } : null;
6993
7083
  };
7084
+ var findNearestRuntimeEvaluationOutcome = ({
7085
+ entry,
7086
+ lineageScopes,
7087
+ toleranceMs
7088
+ }) => {
7089
+ const strategy = getEntryStrategy(entry);
7090
+ const timestamp = entry.signalTimestamp ?? entry.timestamp;
7091
+ if (!strategy) return null;
7092
+ let nearest = null;
7093
+ for (const scope of lineageScopes) {
7094
+ if (scope.strategy !== strategy || scope.symbol !== entry.symbol) {
7095
+ continue;
7096
+ }
7097
+ for (const run of scope.evaluationRuns ?? []) {
7098
+ if (run.stepMs <= 0 || timestamp < run.firstTimestamp - toleranceMs || timestamp > run.lastTimestamp + toleranceMs) {
7099
+ continue;
7100
+ }
7101
+ const step = Math.round((timestamp - run.firstTimestamp) / run.stepMs);
7102
+ const candidateTimestamp = run.firstTimestamp + step * run.stepMs;
7103
+ const timestampDiffMs = Math.abs(candidateTimestamp - timestamp);
7104
+ if (candidateTimestamp < run.firstTimestamp || candidateTimestamp > run.lastTimestamp || timestampDiffMs > toleranceMs || nearest && timestampDiffMs >= nearest.timestampDiffMs) {
7105
+ continue;
7106
+ }
7107
+ nearest = {
7108
+ status: run.status,
7109
+ reason: run.reason,
7110
+ timestamp: candidateTimestamp,
7111
+ timestampDiffMs,
7112
+ source: "lineage_scope_compact"
7113
+ };
7114
+ }
7115
+ }
7116
+ return nearest;
7117
+ };
7118
+ var findNearestReplayEvaluationOutcome = ({
7119
+ entry,
7120
+ lineageScopes,
7121
+ toleranceMs,
7122
+ timestampOffsetMs
7123
+ }) => {
7124
+ const strategy = getEntryStrategy(entry);
7125
+ if (!strategy) return null;
7126
+ let nearest = null;
7127
+ for (const scope of lineageScopes) {
7128
+ if (scope.strategy !== strategy || scope.symbol !== entry.symbol) {
7129
+ continue;
7130
+ }
7131
+ for (const run of scope.evaluationRuns ?? []) {
7132
+ if (run.stepMs <= 0) continue;
7133
+ const targetTimestamp = entry.timestamp - timestampOffsetMs;
7134
+ if (targetTimestamp < run.firstTimestamp - toleranceMs || targetTimestamp > run.lastTimestamp + toleranceMs) {
7135
+ continue;
7136
+ }
7137
+ const step = Math.round(
7138
+ (targetTimestamp - run.firstTimestamp) / run.stepMs
7139
+ );
7140
+ const candidateTimestamp = run.firstTimestamp + step * run.stepMs;
7141
+ const timestampDiffMs = Math.abs(
7142
+ candidateTimestamp + timestampOffsetMs - entry.timestamp
7143
+ );
7144
+ if (candidateTimestamp < run.firstTimestamp || candidateTimestamp > run.lastTimestamp || timestampDiffMs > toleranceMs || nearest && timestampDiffMs >= nearest.timestampDiffMs) {
7145
+ continue;
7146
+ }
7147
+ nearest = {
7148
+ status: run.status,
7149
+ reason: run.reason,
7150
+ timestamp: candidateTimestamp,
7151
+ timestampDiffMs,
7152
+ source: "replay_scope_compact"
7153
+ };
7154
+ }
7155
+ }
7156
+ return nearest;
7157
+ };
7158
+ var hasRuntimeLineageCoverage = ({
7159
+ entry,
7160
+ lineageScopes
7161
+ }) => {
7162
+ const strategy = getEntryStrategy(entry);
7163
+ const timestamp = entry.signalTimestamp ?? entry.timestamp;
7164
+ return lineageScopes.some(
7165
+ (scope) => scope.strategy === strategy && scope.symbol === entry.symbol && timestamp >= scope.firstTimestamp && timestamp <= scope.lastTimestamp
7166
+ );
7167
+ };
6994
7168
  var classifySignalDiagnostic = (signal) => {
6995
7169
  if (signal.orderStatus === "failed") {
6996
7170
  return {
@@ -7235,6 +7409,8 @@ var buildReplayMismatchDrilldown = ({
7235
7409
  runtimeSignalEvaluations = [],
7236
7410
  replaySignals = [],
7237
7411
  replaySignalEvaluations = [],
7412
+ replayLineageScopes = [],
7413
+ runtimeLineageScopes = [],
7238
7414
  toleranceMs,
7239
7415
  backtestTimestampOffsetMs,
7240
7416
  limit
@@ -7260,7 +7436,16 @@ var buildReplayMismatchDrilldown = ({
7260
7436
  toleranceMs,
7261
7437
  timestampOffsetMs: backtestTimestampOffsetMs
7262
7438
  });
7263
- const classification = nearestReplaySignal ? classifySignalDiagnostic(nearestReplaySignal.signal) : nearestReplayEvaluation ? classifyEvaluationDiagnostic(nearestReplayEvaluation.evaluation) : nearestCandidate?.nearest ? {
7439
+ const nearestReplayEvaluationOutcome = findNearestReplayEvaluationOutcome({
7440
+ entry,
7441
+ lineageScopes: replayLineageScopes,
7442
+ toleranceMs,
7443
+ timestampOffsetMs: backtestTimestampOffsetMs
7444
+ });
7445
+ const classification = nearestReplaySignal ? classifySignalDiagnostic(nearestReplaySignal.signal) : nearestReplayEvaluation ? classifyEvaluationDiagnostic(nearestReplayEvaluation.evaluation) : nearestReplayEvaluationOutcome ? {
7446
+ classification: "core_skipped",
7447
+ reason: nearestReplayEvaluationOutcome.reason
7448
+ } : nearestCandidate?.nearest ? {
7264
7449
  classification: "timing_or_price_drift",
7265
7450
  reason: nearestCandidate.reason
7266
7451
  } : {
@@ -7284,7 +7469,8 @@ var buildReplayMismatchDrilldown = ({
7284
7469
  timestampDiffMs: nearestReplayEvaluation.timestampDiffMs
7285
7470
  }),
7286
7471
  ...includeArtifacts ? { replayEvaluationArtifact: nearestReplayEvaluation.evaluation } : {}
7287
- } : {}
7472
+ } : {},
7473
+ ...nearestReplayEvaluationOutcome ? { replayEvaluationOutcome: nearestReplayEvaluationOutcome } : {}
7288
7474
  };
7289
7475
  });
7290
7476
  const backtestOnlyDiagnostics = backtestOnly.map((entry, index) => {
@@ -7302,9 +7488,26 @@ var buildReplayMismatchDrilldown = ({
7302
7488
  toleranceMs,
7303
7489
  timestampOffsetMs: 0
7304
7490
  });
7305
- const classification = nearestRuntimeSignal ? classifySignalDiagnostic(nearestRuntimeSignal.signal) : nearestRuntimeEvaluation ? classifyEvaluationDiagnostic(nearestRuntimeEvaluation.evaluation) : nearestCandidate?.nearest ? {
7491
+ const nearestRuntimeEvaluationOutcome = findNearestRuntimeEvaluationOutcome(
7492
+ {
7493
+ entry,
7494
+ lineageScopes: runtimeLineageScopes,
7495
+ toleranceMs
7496
+ }
7497
+ );
7498
+ const runtimeLineageCoversEntry = hasRuntimeLineageCoverage({
7499
+ entry,
7500
+ lineageScopes: runtimeLineageScopes
7501
+ });
7502
+ const classification = nearestRuntimeSignal ? classifySignalDiagnostic(nearestRuntimeSignal.signal) : nearestRuntimeEvaluation ? classifyEvaluationDiagnostic(nearestRuntimeEvaluation.evaluation) : nearestRuntimeEvaluationOutcome ? {
7503
+ classification: "core_skipped",
7504
+ reason: nearestRuntimeEvaluationOutcome.reason
7505
+ } : nearestCandidate?.nearest ? {
7306
7506
  classification: "timing_or_price_drift",
7307
7507
  reason: nearestCandidate.reason
7508
+ } : runtimeLineageCoversEntry ? {
7509
+ classification: "runtime_evaluation_detail_unavailable",
7510
+ reason: "runtime_scope_covers_timestamp_but_skip_detail_was_not_stored"
7308
7511
  } : {
7309
7512
  classification: "no_runtime_evaluation",
7310
7513
  reason: "no_runtime_signal_or_evaluation"
@@ -7328,7 +7531,8 @@ var buildReplayMismatchDrilldown = ({
7328
7531
  ...includeArtifacts ? {
7329
7532
  runtimeEvaluationArtifact: nearestRuntimeEvaluation.evaluation
7330
7533
  } : {}
7331
- } : {}
7534
+ } : {},
7535
+ ...nearestRuntimeEvaluationOutcome ? { runtimeEvaluationOutcome: nearestRuntimeEvaluationOutcome } : {}
7332
7536
  };
7333
7537
  });
7334
7538
  for (const diagnostic of runtimeOnlyDiagnostics) {
@@ -7375,7 +7579,9 @@ var buildReplayRuntimeComparisonDetails = ({
7375
7579
  runtimeSignals,
7376
7580
  runtimeSignalEvaluations,
7377
7581
  replaySignals,
7378
- replaySignalEvaluations
7582
+ replaySignalEvaluations,
7583
+ replayLineageScopes,
7584
+ runtimeLineageScopes
7379
7585
  }) => {
7380
7586
  const runtimeDetails = runtimeEntries.map(toRuntimeParityDetail);
7381
7587
  const backtestDetails = backtestEntries.map(
@@ -7434,6 +7640,8 @@ var buildReplayRuntimeComparisonDetails = ({
7434
7640
  runtimeSignalEvaluations,
7435
7641
  replaySignals,
7436
7642
  replaySignalEvaluations,
7643
+ replayLineageScopes,
7644
+ runtimeLineageScopes,
7437
7645
  toleranceMs,
7438
7646
  backtestTimestampOffsetMs,
7439
7647
  limit
@@ -7454,7 +7662,9 @@ var buildReplayExchangeComparisonDetails = ({
7454
7662
  runtimeSignals,
7455
7663
  runtimeSignalEvaluations,
7456
7664
  replaySignals,
7457
- replaySignalEvaluations
7665
+ replaySignalEvaluations,
7666
+ replayLineageScopes,
7667
+ runtimeLineageScopes
7458
7668
  }) => {
7459
7669
  const toExchangeDetail = (entry) => toExchangeParityDetail({
7460
7670
  entry,
@@ -7541,6 +7751,8 @@ var buildReplayExchangeComparisonDetails = ({
7541
7751
  runtimeSignalEvaluations,
7542
7752
  replaySignals,
7543
7753
  replaySignalEvaluations,
7754
+ replayLineageScopes,
7755
+ runtimeLineageScopes,
7544
7756
  toleranceMs,
7545
7757
  backtestTimestampOffsetMs,
7546
7758
  limit
@@ -7663,19 +7875,41 @@ var filterReplayComparisonByLineage = ({
7663
7875
  lineageScopeKey(record)
7664
7876
  ])
7665
7877
  );
7666
- const comparableBacktestEntries = backtestEntries.filter((entry) => {
7878
+ const comparableBacktestEntries = [];
7879
+ const excludedBacktestEntryDetails = [];
7880
+ for (const entry of backtestEntries) {
7667
7881
  const replayScope = replayScopeByStrategySymbol.get(
7668
7882
  `${entry.strategy}::${entry.symbol}`
7669
7883
  );
7670
7884
  const window = replayScope ? deploymentWindows.get(replayScope) : null;
7671
7885
  const signalTimestamp = entry.signalTimestamp ?? entry.timestamp;
7672
- return window != null && signalTimestamp >= window.firstTimestamp && signalTimestamp <= window.lastTimestamp;
7673
- });
7886
+ if (window != null && signalTimestamp >= window.firstTimestamp && signalTimestamp <= window.lastTimestamp) {
7887
+ comparableBacktestEntries.push(entry);
7888
+ continue;
7889
+ }
7890
+ const reason = !replayScope ? "replay_scope_missing" : !window ? "runtime_scope_missing" : signalTimestamp < window.firstTimestamp ? "before_runtime_window" : "after_runtime_window";
7891
+ excludedBacktestEntryDetails.push({
7892
+ id: entry.id,
7893
+ strategy: entry.strategy,
7894
+ symbol: entry.symbol,
7895
+ direction: entry.direction,
7896
+ ...entry.qty != null ? { qty: entry.qty } : {},
7897
+ timestamp: entry.timestamp,
7898
+ ...entry.signalTimestamp != null ? { signalTimestamp: entry.signalTimestamp } : {},
7899
+ price: entry.price,
7900
+ ...entry.orderId ? { orderId: entry.orderId } : {},
7901
+ ...entry.signalId ? { signalId: entry.signalId } : {},
7902
+ ...entry.expectedPnl != null ? { expectedPnl: entry.expectedPnl } : {},
7903
+ reason,
7904
+ runtimeWindow: window ?? null
7905
+ });
7906
+ }
7674
7907
  const comparableScopeKeys = new Set(deploymentWindows.keys());
7675
7908
  return {
7676
7909
  runtimeTrades: comparableRuntimeTrades,
7677
7910
  runtimeSignals: comparableRuntimeSignals,
7678
7911
  runtimeSignalEvaluations: comparableRuntimeEvaluations,
7912
+ runtimeLineageScopes: comparableRuntimeLineageScopes,
7679
7913
  backtestEntries: comparableBacktestEntries,
7680
7914
  lineage: {
7681
7915
  enforced: true,
@@ -7687,6 +7921,7 @@ var filterReplayComparisonByLineage = ({
7687
7921
  excludedRuntimeLineageScopes: runtimeLineageScopes.length - comparableRuntimeLineageScopes.length,
7688
7922
  excludedExchangeEntries: 0,
7689
7923
  excludedBacktestEntries: backtestEntries.length - comparableBacktestEntries.length,
7924
+ excludedBacktestEntryDetails,
7690
7925
  reason: comparableScopeKeys.size > 0 ? null : expectedByScope.size === 0 ? "replay_produced_no_lineage_scopes" : "no_runtime_artifacts_with_matching_lineage",
7691
7926
  replay: [...expectedByScope.entries()].map(([scope, lineage]) => {
7692
7927
  const [deploymentId, accountId, strategy, symbol] = scope.split("::");
@@ -8270,8 +8505,10 @@ var saveAndPrintReplayExchangeComparison = async ({
8270
8505
  backtestEntries,
8271
8506
  replaySignals,
8272
8507
  replaySignalEvaluations,
8508
+ replayLineageScopes,
8273
8509
  runtimeSignals,
8274
8510
  runtimeSignalEvaluations,
8511
+ runtimeLineageScopes,
8275
8512
  lineage
8276
8513
  }) => {
8277
8514
  const { connector, connectorName, window } = getRuntimeCompareContext();
@@ -8323,7 +8560,9 @@ var saveAndPrintReplayExchangeComparison = async ({
8323
8560
  runtimeSignals,
8324
8561
  runtimeSignalEvaluations,
8325
8562
  replaySignals,
8326
- replaySignalEvaluations
8563
+ replaySignalEvaluations,
8564
+ runtimeLineageScopes,
8565
+ replayLineageScopes
8327
8566
  });
8328
8567
  const emptyRowsByStrategy = new Map(
8329
8568
  buildExchangeComparisonRows({
@@ -8379,7 +8618,9 @@ var saveAndPrintReplayExchangeComparison = async ({
8379
8618
  runtimeSignals,
8380
8619
  runtimeSignalEvaluations,
8381
8620
  replaySignals,
8382
- replaySignalEvaluations
8621
+ replaySignalEvaluations,
8622
+ runtimeLineageScopes,
8623
+ replayLineageScopes
8383
8624
  });
8384
8625
  const rows = buildExchangeComparisonRows({
8385
8626
  liveStrategySummaries,
@@ -8417,6 +8658,7 @@ var saveAndPrintReplayRuntimeComparison = async ({
8417
8658
  backtestEntries,
8418
8659
  replaySignals,
8419
8660
  replayLineages,
8661
+ replayLineageScopes,
8420
8662
  runtimeEvidencePath
8421
8663
  }) => {
8422
8664
  const { connector, connectorName, window } = getRuntimeCompareContext();
@@ -8528,8 +8770,10 @@ var saveAndPrintReplayRuntimeComparison = async ({
8528
8770
  backtestEntries: comparableBacktestEntries,
8529
8771
  replaySignals,
8530
8772
  replaySignalEvaluations,
8773
+ replayLineageScopes,
8531
8774
  runtimeSignals: comparable.runtimeSignals,
8532
8775
  runtimeSignalEvaluations: comparable.runtimeSignalEvaluations,
8776
+ runtimeLineageScopes: comparable.runtimeLineageScopes,
8533
8777
  lineage: comparable.lineage
8534
8778
  });
8535
8779
  }
@@ -8553,7 +8797,9 @@ var saveAndPrintReplayRuntimeComparison = async ({
8553
8797
  runtimeSignals: comparable.runtimeSignals,
8554
8798
  runtimeSignalEvaluations: comparable.runtimeSignalEvaluations,
8555
8799
  replaySignals,
8556
- replaySignalEvaluations
8800
+ replaySignalEvaluations,
8801
+ runtimeLineageScopes: comparable.runtimeLineageScopes,
8802
+ replayLineageScopes
8557
8803
  });
8558
8804
  const parityRows = summarizeTradeParityByStrategy({
8559
8805
  runtimeEntries: runtimeDedupe.entries,
@@ -8596,7 +8842,7 @@ var saveAndPrintReplayRuntimeComparison = async ({
8596
8842
  // src/lib/replay/outputReport.ts
8597
8843
  var import_promises2 = __toESM(require("fs/promises"));
8598
8844
  var import_node_path2 = __toESM(require("path"));
8599
- var import_data = require("@tradejs/core/data");
8845
+ var import_data3 = require("@tradejs/core/data");
8600
8846
  var formatMsk = (timestamp) => typeof timestamp === "number" && Number.isFinite(timestamp) ? new Intl.DateTimeFormat("ru-RU", {
8601
8847
  timeZone: "Europe/Moscow",
8602
8848
  dateStyle: "short",
@@ -9003,7 +9249,7 @@ var writeReplayOutputReport = async ({
9003
9249
  ];
9004
9250
  await Promise.all([
9005
9251
  import_promises2.default.writeFile(markdownPath, lines.join("\n"), "utf8"),
9006
- import_promises2.default.writeFile(jsonPath, `${(0, import_data.toJson)(report, true)}
9252
+ import_promises2.default.writeFile(jsonPath, `${(0, import_data3.toJson)(report, true)}
9007
9253
  `, "utf8")
9008
9254
  ]);
9009
9255
  return {
@@ -9045,6 +9291,7 @@ var finishReplay = async ({
9045
9291
  backtestEntries: replayStrategySnapshot.backtestEntries,
9046
9292
  replaySignals: replayResult.signals,
9047
9293
  replayLineages: replayResult.runtimeLineages,
9294
+ replayLineageScopes: replayResult.replayLineageScopes,
9048
9295
  runtimeEvidencePath: replayRuntimeEvidencePath
9049
9296
  });
9050
9297
  const finishedAt = /* @__PURE__ */ new Date();
@@ -9211,7 +9458,7 @@ var replayBacktest = async () => {
9211
9458
  deploymentId: deployment.id,
9212
9459
  assetClasses: deployment.assetClasses,
9213
9460
  deployment,
9214
- closedIntervalMs: (0, import_data2.intervalToMs)(replayInterval)
9461
+ closedIntervalMs: (0, import_data4.intervalToMs)(replayInterval)
9215
9462
  });
9216
9463
  if (!preparedRun || isReplayUpdateOnlyRun) {
9217
9464
  return;
@@ -117,6 +117,7 @@ var import_redis2 = require("@tradejs/infra/redis");
117
117
 
118
118
  // src/lib/runtimeSignalsStorage.ts
119
119
  var import_constants = require("@tradejs/core/constants");
120
+ var import_data = require("@tradejs/core/data");
120
121
  var import_time2 = require("@tradejs/core/time");
121
122
  var isRuntimeSignalBucketRef = (value) => {
122
123
  if (!value || typeof value !== "object") {
@@ -540,9 +541,21 @@ var summarizeReplayComparison = (runtimeComparison, strategies) => {
540
541
  const replayLineage = asArray(comparisonLineage.replay).filter(
541
542
  (entry) => !useFilter || strategySet.has(String(asRecord(entry).strategy ?? ""))
542
543
  );
544
+ const hasExcludedBacktestEntryDetails = Array.isArray(
545
+ comparisonLineage.excludedBacktestEntryDetails
546
+ );
547
+ const excludedBacktestEntryDetails = asArray(
548
+ comparisonLineage.excludedBacktestEntryDetails
549
+ ).filter(
550
+ (entry) => !useFilter || strategySet.has(String(asRecord(entry).strategy ?? ""))
551
+ );
543
552
  return {
544
553
  mode: comparison.mode ?? null,
545
- lineage: comparisonLineage ? { ...comparisonLineage, replay: replayLineage } : null,
554
+ lineage: comparisonLineage ? {
555
+ ...comparisonLineage,
556
+ replay: replayLineage,
557
+ ...hasExcludedBacktestEntryDetails ? { excludedBacktestEntryDetails } : {}
558
+ } : null,
546
559
  rows: asArray(comparison.rows).filter(
547
560
  (row) => !useFilter || strategySet.has(String(row.strategyName ?? ""))
548
561
  ),
@@ -115,6 +115,7 @@ var import_redis2 = require("@tradejs/infra/redis");
115
115
 
116
116
  // src/lib/runtimeSignalsStorage.ts
117
117
  var import_constants = require("@tradejs/core/constants");
118
+ var import_data = require("@tradejs/core/data");
118
119
  var import_time2 = require("@tradejs/core/time");
119
120
  var isRuntimeSignalBucketRef = (value) => {
120
121
  if (!value || typeof value !== "object") {
@@ -394,6 +394,7 @@ var import_redis2 = require("@tradejs/infra/redis");
394
394
 
395
395
  // src/lib/runtimeSignalsStorage.ts
396
396
  var import_constants = require("@tradejs/core/constants");
397
+ var import_data = require("@tradejs/core/data");
397
398
  var import_time2 = require("@tradejs/core/time");
398
399
  var isRuntimeSignalBucketRef = (value) => {
399
400
  if (!value || typeof value !== "object") {