@tradejs/cli 2.0.15 → 2.0.17

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.
@@ -2349,6 +2349,8 @@ var prepareRunEnvironment = async ({
2349
2349
 
2350
2350
  // src/lib/marketContextPrepare.ts
2351
2351
  var import_chalk7 = __toESM(require("chalk"));
2352
+ var import_timescale5 = require("@tradejs/infra/timescale");
2353
+ var import_registry = require("@tradejs/node/registry");
2352
2354
 
2353
2355
  // src/lib/binanceMarketContextBackfill.ts
2354
2356
  var import_progress = __toESM(require("progress"));
@@ -5184,6 +5186,7 @@ var backfillCoinMarketCapContextForReplay = backfillCoinMarketCapContext;
5184
5186
  var backfillCoinMarketCapContextForSignals = (params) => backfillCoinMarketCapContext({ ...params, refreshStaleFearGreed: true });
5185
5187
 
5186
5188
  // src/lib/hyperliquidWhaleBackfill.ts
5189
+ var import_progress3 = __toESM(require("progress"));
5187
5190
  var import_timescale4 = require("@tradejs/infra/timescale");
5188
5191
  var import_strategies2 = require("@tradejs/node/strategies");
5189
5192
 
@@ -5558,11 +5561,36 @@ var backfillHyperliquidWhaleContext = async (params) => {
5558
5561
  toMs,
5559
5562
  ...identity
5560
5563
  });
5564
+ const totalCoverageBuckets = Math.ceil(
5565
+ (toMs - fromMs) / HYPERLIQUID_WHALE_BUCKET_MS
5566
+ );
5567
+ const coverageBar = new import_progress3.default(
5568
+ "Hyperliquid coverage :current/:total [:bar][:percent] :eta(s) rows=:rows chunk=:chunk",
5569
+ {
5570
+ total: Math.max(1, totalCoverageBuckets),
5571
+ width: 24
5572
+ }
5573
+ );
5574
+ let displayedCoverageBuckets = 0;
5561
5575
  const coverageBuckets = await (0, import_timescale4.rebuildHyperliquidWhaleCoverageRows)({
5562
5576
  fromMs,
5563
5577
  toMs,
5564
5578
  expectedWhales: whales.addresses.length,
5565
- ...identity
5579
+ ...identity,
5580
+ onProgress: (progress) => {
5581
+ const next = Math.max(
5582
+ displayedCoverageBuckets,
5583
+ Math.min(progress.completedBuckets, coverageBar.total)
5584
+ );
5585
+ const delta = next - displayedCoverageBuckets;
5586
+ if (delta > 0) {
5587
+ coverageBar.tick(delta, {
5588
+ rows: progress.rows,
5589
+ chunk: `${progress.chunkIndex}/${progress.totalChunks}`
5590
+ });
5591
+ displayedCoverageBuckets = next;
5592
+ }
5593
+ }
5566
5594
  });
5567
5595
  const result = {
5568
5596
  cached: false,
@@ -5590,6 +5618,9 @@ var backfillHyperliquidWhaleContext = async (params) => {
5590
5618
  };
5591
5619
 
5592
5620
  // src/lib/marketContextPrepare.ts
5621
+ var uniqueReasons = (reasons) => [
5622
+ ...new Set(reasons)
5623
+ ];
5593
5624
  var shouldPrepareDerivativesContextForRun = (params) => {
5594
5625
  if (params.universe === "tradfi") return false;
5595
5626
  if (params.mode === "signals") {
@@ -5597,6 +5628,9 @@ var shouldPrepareDerivativesContextForRun = (params) => {
5597
5628
  cacheOnly: params.cacheOnly
5598
5629
  });
5599
5630
  }
5631
+ if (params.mode === "backtest" && !params.aiEnabled && !params.mlEnabled) {
5632
+ return false;
5633
+ }
5600
5634
  return shouldBackfillDerivativesContextForBacktest({
5601
5635
  aiEnabled: Boolean(params.aiEnabled),
5602
5636
  cacheOnly: params.cacheOnly,
@@ -5606,6 +5640,7 @@ var shouldPrepareDerivativesContextForRun = (params) => {
5606
5640
  var shouldPrepareBinanceMarketContextForRun = (params) => {
5607
5641
  if (params.universe === "tradfi") return false;
5608
5642
  if (params.mode === "backtest") {
5643
+ if (!params.aiEnabled && !params.mlEnabled) return false;
5609
5644
  return shouldBackfillBinanceMarketContextForBacktest({
5610
5645
  aiEnabled: Boolean(params.aiEnabled),
5611
5646
  cacheOnly: params.cacheOnly,
@@ -5624,6 +5659,7 @@ var shouldPrepareBinanceMarketContextForRun = (params) => {
5624
5659
  var shouldPrepareCoinMarketCapContextForRun = (params) => {
5625
5660
  if (params.universe === "tradfi") return false;
5626
5661
  if (params.mode === "backtest") {
5662
+ if (!params.aiEnabled && !params.mlEnabled) return false;
5627
5663
  return shouldBackfillCoinMarketCapContextForBacktest({
5628
5664
  aiEnabled: Boolean(params.aiEnabled),
5629
5665
  cacheOnly: params.cacheOnly,
@@ -5639,10 +5675,8 @@ var shouldPrepareCoinMarketCapContextForRun = (params) => {
5639
5675
  cacheOnly: params.cacheOnly
5640
5676
  });
5641
5677
  };
5642
- var shouldPrepareHyperliquidWhaleContextForRun = (params) => {
5643
- const strategyRequiresContext = params.strategyNames?.includes(
5644
- "HyperliquidConsensus"
5645
- );
5678
+ var shouldPrepareHyperliquidWhaleContextForRun = (params, coreContextSources = []) => {
5679
+ const strategyRequiresContext = coreContextSources.includes("hyperliquidWhales");
5646
5680
  if (params.universe === "tradfi" || params.mode === "signals" || params.cacheOnly || !params.aiEnabled && !params.mlEnabled && !strategyRequiresContext) {
5647
5681
  return false;
5648
5682
  }
@@ -5653,6 +5687,64 @@ var shouldPrepareHyperliquidWhaleContextForRun = (params) => {
5653
5687
  if (strategyRequiresContext) return true;
5654
5688
  return ["1", "true", "yes", "on"].includes(configured);
5655
5689
  };
5690
+ var resolveCoreContextSources = async (params) => {
5691
+ const strategyNames = [...new Set(params.strategyNames ?? [])];
5692
+ if (!strategyNames.length) return [];
5693
+ await (0, import_registry.ensureStrategyPluginsLoaded)(params.projectRoot);
5694
+ return [
5695
+ ...new Set(
5696
+ strategyNames.flatMap(
5697
+ (strategyName) => (0, import_registry.getStrategyManifest)(strategyName, params.projectRoot)?.contextRequirements?.core ?? []
5698
+ )
5699
+ )
5700
+ ];
5701
+ };
5702
+ var resolveMarketContextRunRequirements = async (params) => {
5703
+ const coreContextSources = await resolveCoreContextSources(params);
5704
+ const paramsWithCoreDemand = (source) => coreContextSources.includes(source) ? { ...params, aiEnabled: true } : params;
5705
+ const isCrypto = params.universe !== "tradfi";
5706
+ const capturesPayload = Boolean(params.aiEnabled || params.mlEnabled);
5707
+ const runtimeReadsContext = params.mode !== "backtest";
5708
+ const reasonsFor = (source) => uniqueReasons([
5709
+ ...coreContextSources.includes(source) ? ["core"] : [],
5710
+ ...params.aiEnabled ? ["ai"] : [],
5711
+ ...params.mlEnabled ? ["ml"] : [],
5712
+ ...runtimeReadsContext ? ["runtime"] : []
5713
+ ]);
5714
+ const readsStandardContext = isCrypto && (capturesPayload || runtimeReadsContext);
5715
+ const readsHyperliquid = isCrypto && (readsStandardContext || coreContextSources.includes("hyperliquidWhales"));
5716
+ return {
5717
+ binance: {
5718
+ read: readsStandardContext || coreContextSources.includes("binance"),
5719
+ backfill: shouldPrepareBinanceMarketContextForRun(
5720
+ paramsWithCoreDemand("binance")
5721
+ ),
5722
+ requiredBy: reasonsFor("binance")
5723
+ },
5724
+ coinmarketcap: {
5725
+ read: readsStandardContext || coreContextSources.includes("coinmarketcap"),
5726
+ backfill: shouldPrepareCoinMarketCapContextForRun(
5727
+ paramsWithCoreDemand("coinmarketcap")
5728
+ ),
5729
+ requiredBy: reasonsFor("coinmarketcap")
5730
+ },
5731
+ derivatives: {
5732
+ read: readsStandardContext || coreContextSources.includes("derivatives"),
5733
+ backfill: shouldPrepareDerivativesContextForRun(
5734
+ paramsWithCoreDemand("derivatives")
5735
+ ),
5736
+ requiredBy: reasonsFor("derivatives")
5737
+ },
5738
+ hyperliquidWhales: {
5739
+ read: readsHyperliquid,
5740
+ backfill: shouldPrepareHyperliquidWhaleContextForRun(
5741
+ params,
5742
+ coreContextSources
5743
+ ),
5744
+ requiredBy: reasonsFor("hyperliquidWhales")
5745
+ }
5746
+ };
5747
+ };
5656
5748
  var resolveCoinMarketCapBackfillForMode = (mode) => mode === "backtest" ? backfillCoinMarketCapContextForBacktest : mode === "signals" ? backfillCoinMarketCapContextForSignals : backfillCoinMarketCapContextForReplay;
5657
5749
  var buildCoinMarketCapBackfillParams = (params) => ({
5658
5750
  userName: params.userName,
@@ -5681,7 +5773,15 @@ var prepareMarketContextForRun = async (params) => {
5681
5773
  console.log(import_chalk7.default.gray(message));
5682
5774
  });
5683
5775
  const timeOperation3 = (label, operation) => timeOperation(label, operation, log);
5684
- if (shouldPrepareDerivativesContextForRun(params)) {
5776
+ const requirements = await resolveMarketContextRunRequirements(params);
5777
+ const readableSources = Object.entries(requirements).filter(([, requirement]) => requirement.read).map(([source]) => source);
5778
+ if (readableSources.length) {
5779
+ await timeOperation3(
5780
+ "market context schema ensure",
5781
+ () => (0, import_timescale5.ensureMarketContextSchemas)(readableSources)
5782
+ );
5783
+ }
5784
+ if (requirements.derivatives.backfill) {
5685
5785
  await timeOperation3(
5686
5786
  "derivatives context backfill",
5687
5787
  () => (params.mode === "signals" ? backfillDerivativesContextForSignals : backfillDerivativesContextForBacktest)(
@@ -5689,7 +5789,7 @@ var prepareMarketContextForRun = async (params) => {
5689
5789
  )
5690
5790
  );
5691
5791
  }
5692
- if (shouldPrepareHyperliquidWhaleContextForRun(params)) {
5792
+ if (requirements.hyperliquidWhales.backfill) {
5693
5793
  await timeOperation3(
5694
5794
  "hyperliquid whale context backfill",
5695
5795
  () => backfillHyperliquidWhaleContext({
@@ -5701,13 +5801,13 @@ var prepareMarketContextForRun = async (params) => {
5701
5801
  })
5702
5802
  );
5703
5803
  }
5704
- if (shouldPrepareBinanceMarketContextForRun(params)) {
5804
+ if (requirements.binance.backfill) {
5705
5805
  await timeOperation3("binance market context backfill", () => {
5706
5806
  const backfill = params.mode === "backtest" ? backfillBinanceMarketContextForBacktest : params.mode === "signals" ? backfillBinanceMarketContextForSignals : backfillBinanceMarketContextForReplay;
5707
5807
  return backfill(buildBinanceMarketBackfillParams(params));
5708
5808
  });
5709
5809
  }
5710
- if (shouldPrepareCoinMarketCapContextForRun(params)) {
5810
+ if (requirements.coinmarketcap.backfill) {
5711
5811
  await timeOperation3(
5712
5812
  "coinmarketcap historical context backfill",
5713
5813
  () => resolveCoinMarketCapBackfillForMode(params.mode)(
@@ -5715,6 +5815,7 @@ var prepareMarketContextForRun = async (params) => {
5715
5815
  )
5716
5816
  );
5717
5817
  }
5818
+ return requirements;
5718
5819
  };
5719
5820
 
5720
5821
  // src/lib/backtest/runState.ts
@@ -5987,7 +6088,7 @@ var compactReplaySignal = (signal) => {
5987
6088
 
5988
6089
  // src/lib/replay/historicalSignalsReplay.ts
5989
6090
  var import_chalk9 = __toESM(require("chalk"));
5990
- var import_progress3 = __toESM(require("progress"));
6091
+ var import_progress4 = __toESM(require("progress"));
5991
6092
  var import_backtest = require("@tradejs/core/backtest");
5992
6093
  var import_strategies4 = require("@tradejs/core/strategies");
5993
6094
  var import_time3 = require("@tradejs/core/time");
@@ -6871,7 +6972,7 @@ var runHistoricalSignalsReplay = async ({
6871
6972
  const sharedReplayKeyPrefixes = [];
6872
6973
  let preparedSymbols = 0;
6873
6974
  let skippedSymbols = 0;
6874
- const prepareBar = new import_progress3.default(
6975
+ const prepareBar = new import_progress4.default(
6875
6976
  "prepare :current/:total [:bar][:percent] skipped=:skipped :etas(s) :symbol",
6876
6977
  {
6877
6978
  total: preparedRun.tickers.length,
@@ -6993,7 +7094,7 @@ var runHistoricalSignalsReplay = async ({
6993
7094
  interval
6994
7095
  });
6995
7096
  let abortedCycles = 0;
6996
- const cycleBar = new import_progress3.default(
7097
+ const cycleBar = new import_progress4.default(
6997
7098
  "cycles :current/:total [:bar][:percent] sig=:signals abort=:aborted :etas(s) :ts",
6998
7099
  {
6999
7100
  total: orderedTimestamps.length,
@@ -7963,6 +8064,15 @@ var syncRuntimeTrades = async ({
7963
8064
  nextTrade,
7964
8065
  { expire: import_constants5.TTL_1M }
7965
8066
  ),
8067
+ (0, import_redis5.setHashJsonField)(
8068
+ import_redis5.redisKeys.runtimeClosedTradeBucket(
8069
+ userName,
8070
+ (0, import_time5.getRuntimeStorageDayKey)(nextTrade.exitTimestamp)
8071
+ ),
8072
+ trade.orderId,
8073
+ nextTrade,
8074
+ { expire: import_constants5.TTL_1M }
8075
+ ),
7966
8076
  ...isCurrentActiveTrade ? [(0, import_redis5.delKey)(activeTradeKey)] : []
7967
8077
  ]);
7968
8078
  syncedTrades.push(nextTrade);
@@ -135,6 +135,29 @@ var loadRuntimeTrades = async (userName, {
135
135
  const trades = await Promise.all(keys.map((key) => (0, import_redis.getData)(key, null)));
136
136
  return trades.filter(isRuntimeTradeRecord).filter(filterByWindow).sort((left, right) => left.entryTimestamp - right.entryTimestamp);
137
137
  };
138
+ var loadRuntimeClosedTrades = async (userName, {
139
+ startTime,
140
+ endTime
141
+ }) => {
142
+ const dayKeys = (0, import_time.getRuntimeStorageDayKeys)(startTime, endTime);
143
+ const trades = (await Promise.all(
144
+ dayKeys.map(
145
+ (dayKey) => (0, import_redis.getHashJsonValues)(
146
+ import_redis.redisKeys.runtimeClosedTradeBucket(userName, dayKey)
147
+ )
148
+ )
149
+ )).flat();
150
+ const deduped = /* @__PURE__ */ new Map();
151
+ for (const trade of trades) {
152
+ if (!isRuntimeTradeRecord(trade) || trade.status !== "closed" || !Number.isFinite(trade.exitTimestamp) || trade.exitTimestamp < startTime || trade.exitTimestamp >= endTime) {
153
+ continue;
154
+ }
155
+ deduped.set(trade.orderId, trade);
156
+ }
157
+ return [...deduped.values()].sort(
158
+ (left, right) => left.exitTimestamp - right.exitTimestamp
159
+ );
160
+ };
138
161
 
139
162
  // src/lib/runtimeSignalsLoader.ts
140
163
  var import_redis2 = require("@tradejs/infra/redis");
@@ -299,11 +322,6 @@ var formatRuntimeDebugDateTime = (timestamp) => new Intl.DateTimeFormat("ru-RU",
299
322
  minute: "2-digit",
300
323
  hour12: false
301
324
  }).format(new Date(timestamp));
302
- var buildRuntimeDebugEvaluationId = ({
303
- strategy,
304
- symbol,
305
- timestamp
306
- }) => `${strategy}:${symbol}:${timestamp}`;
307
325
  var filterByStrategies = (values, strategies) => {
308
326
  if (!strategies?.length) {
309
327
  return values;
@@ -319,13 +337,30 @@ var collectRuntimeDebugEvidence = async ({
319
337
  }) => {
320
338
  const dayKeys = (0, import_time2.getRuntimeStorageDayKeys)(startTime, endTime);
321
339
  const dayKeySet = new Set(dayKeys);
322
- const [trades, signals, evaluations, evaluationStatsBuckets, configs] = await Promise.all([
340
+ const [
341
+ entryTrades,
342
+ closedTrades,
343
+ signals,
344
+ evaluations,
345
+ evaluationStatsBuckets,
346
+ configs
347
+ ] = await Promise.all([
323
348
  loadRuntimeTrades(userName, { startTime, endTime }),
349
+ loadRuntimeClosedTrades(userName, { startTime, endTime }),
324
350
  loadRuntimeSignals(userName, { startTime, endTime }),
325
351
  loadRuntimeSignalEvaluations(userName, { startTime, endTime }),
326
352
  loadRuntimeSignalEvaluationStatsBuckets(userName),
327
353
  loadRuntimeStrategyConfigs(userName)
328
354
  ]);
355
+ const tradesByOrderId = new Map(
356
+ entryTrades.map((trade) => [trade.orderId, trade])
357
+ );
358
+ for (const trade of closedTrades) {
359
+ tradesByOrderId.set(trade.orderId, trade);
360
+ }
361
+ const trades = [...tradesByOrderId.values()].sort(
362
+ (left, right) => left.entryTimestamp - right.entryTimestamp
363
+ );
329
364
  const filteredStats = evaluationStatsBuckets.filter(
330
365
  (entry) => dayKeySet.has(entry.dayKey) && (!strategies?.length || strategies.includes(entry.strategy))
331
366
  );
@@ -340,7 +375,7 @@ var collectRuntimeDebugEvidence = async ({
340
375
  )
341
376
  };
342
377
  };
343
- var buildRuntimeDebugReportPayload = async ({
378
+ var buildRuntimeEvidenceReportPayload = ({
344
379
  userName,
345
380
  startTime,
346
381
  endTime,
@@ -349,159 +384,30 @@ var buildRuntimeDebugReportPayload = async ({
349
384
  trades,
350
385
  strategyConfigs,
351
386
  evaluationStatsBuckets
352
- }) => {
353
- const signalById = new Map(
354
- signals.map((signal) => [signal.signalId, signal])
355
- );
356
- const evaluationById = new Map(
357
- evaluations.map((evaluation) => [evaluation.evaluationId, evaluation])
358
- );
359
- const evaluationBySignalShape = new Map(
360
- evaluations.map((evaluation) => [
361
- buildRuntimeDebugEvaluationId(evaluation),
362
- evaluation
363
- ])
364
- );
365
- const dayKeys = (0, import_time2.getRuntimeStorageDayKeys)(startTime, endTime);
366
- const debugTrades = await Promise.all(
367
- trades.map(async (trade) => {
368
- const signal = trade.signalId != null ? signalById.get(trade.signalId) : void 0;
369
- const evaluationId = buildRuntimeDebugEvaluationId({
370
- strategy: trade.strategy,
371
- symbol: trade.symbol,
372
- timestamp: signal?.timestamp ?? trade.entryTimestamp
373
- });
374
- const knownEvaluation = evaluationById.get(evaluationId) ?? evaluationBySignalShape.get(evaluationId);
375
- const tradeDayKey = (0, import_time2.getRuntimeStorageDayKey)(trade.entryTimestamp);
376
- const signalDayKey = signal != null ? (0, import_time2.getRuntimeStorageDayKey)(signal.timestamp) : tradeDayKey;
377
- const evaluationDayKey = knownEvaluation != null ? (0, import_time2.getRuntimeStorageDayKey)(knownEvaluation.timestamp) : signalDayKey;
378
- const tradeKey = import_redis3.redisKeys.runtimeTrade(userName, trade.orderId);
379
- const tradeBucketKey = import_redis3.redisKeys.runtimeTradeBucket(
380
- userName,
381
- tradeDayKey
382
- );
383
- const activeTradeKey = import_redis3.redisKeys.runtimeActiveTrade(
384
- userName,
385
- trade.symbol,
386
- trade.deploymentId ?? trade.accountId
387
- );
388
- const signalKey = trade.signalId != null ? import_redis3.redisKeys.storeSignal(trade.symbol, trade.signalId) : null;
389
- const evaluationBucketKey = import_redis3.redisKeys.runtimeSignalEvaluationBucket(
390
- userName,
391
- evaluationDayKey,
392
- knownEvaluation?.strategy ?? trade.strategy
393
- );
394
- const evaluationField = knownEvaluation?.evaluationId ?? evaluationId;
395
- const evaluationKey = import_redis3.redisKeys.runtimeSignalEvaluation(
396
- userName,
397
- evaluationField
398
- );
399
- const [
400
- tradeValue,
401
- activeTradeValue,
402
- signalValue,
403
- directEvaluationValue,
404
- bucketEvaluationValue
405
- ] = await Promise.all([
406
- (0, import_redis3.getData)(tradeKey, null),
407
- (0, import_redis3.getData)(activeTradeKey, null),
408
- signalKey ? (0, import_redis3.getData)(signalKey, null) : Promise.resolve(null),
409
- (0, import_redis3.getData)(evaluationKey, null),
410
- knownEvaluation ? Promise.resolve(knownEvaluation) : (0, import_redis3.getHashJsonField)(
411
- evaluationBucketKey,
412
- evaluationField,
413
- null
414
- )
415
- ]);
416
- const evaluationValue = directEvaluationValue ?? bucketEvaluationValue ?? knownEvaluation ?? null;
417
- return {
418
- redisDebug: {
419
- trade: tradeKey,
420
- tradeBucket: {
421
- key: tradeBucketKey,
422
- field: trade.orderId
423
- },
424
- activeTrade: activeTradeKey,
425
- signal: signalKey,
426
- evaluation: {
427
- key: evaluationBucketKey,
428
- field: evaluationField,
429
- directKey: evaluationKey
430
- }
431
- },
432
- trade,
433
- redisValues: {
434
- trade: tradeValue,
435
- tradeBucket: trade,
436
- activeTrade: activeTradeValue,
437
- signal: signalValue ?? signal ?? null,
438
- evaluation: evaluationValue ?? knownEvaluation ?? null
439
- }
440
- };
441
- })
442
- );
443
- const linkedEvaluations = /* @__PURE__ */ new Map();
444
- for (const trade of debugTrades) {
445
- const evaluation = trade.redisValues.evaluation;
446
- if (evaluation && typeof evaluation === "object" && "evaluationId" in evaluation && typeof evaluation.evaluationId === "string") {
447
- linkedEvaluations.set(
448
- evaluation.evaluationId,
449
- evaluation
450
- );
451
- }
452
- }
453
- const reportEvaluations = evaluations.length > 0 ? evaluations : [...linkedEvaluations.values()];
454
- return {
455
- reportType: "runtime-daily-debug",
456
- generatedAt: Date.now(),
457
- generatedAtMsk: formatRuntimeDebugDateTime(Date.now()),
458
- userName,
459
- window: {
460
- startTime,
461
- endTime,
462
- startMsk: formatRuntimeDebugDateTime(startTime),
463
- endMsk: formatRuntimeDebugDateTime(endTime),
464
- dayKeys
465
- },
466
- redisPrefixes: {
467
- runtimeTrades: import_redis3.redisKeys.runtimeTrades(userName),
468
- runtimeTradeBuckets: import_redis3.redisKeys.runtimeTradeBuckets(userName),
469
- runtimeActiveTrades: import_redis3.redisKeys.runtimeActiveTrades(userName),
470
- runtimeSignals: import_redis3.redisKeys.runtimeSignalBuckets(userName),
471
- runtimeSignalEvaluations: import_redis3.redisKeys.runtimeSignalEvaluationBuckets(userName),
472
- runtimeSignalEvaluationStats: import_redis3.redisKeys.runtimeSignalEvaluationStatsBuckets(userName)
473
- },
474
- counts: {
475
- trades: trades.length,
476
- signals: signals.length,
477
- evaluations: reportEvaluations.length,
478
- evaluationStatsBuckets: evaluationStatsBuckets?.length ?? 0,
479
- strategyConfigs: strategyConfigs?.length ?? 0
480
- },
481
- trades: debugTrades,
482
- signals: signals.map((signal) => ({
483
- redisKey: import_redis3.redisKeys.storeSignal(signal.symbol, signal.signalId),
484
- signal
485
- })),
486
- evaluations: reportEvaluations.map((evaluation) => ({
487
- redisDebug: {
488
- key: import_redis3.redisKeys.runtimeSignalEvaluationBucket(
489
- userName,
490
- (0, import_time2.getRuntimeStorageDayKey)(evaluation.timestamp),
491
- evaluation.strategy
492
- ),
493
- field: evaluation.evaluationId,
494
- directKey: import_redis3.redisKeys.runtimeSignalEvaluation(
495
- userName,
496
- evaluation.evaluationId
497
- )
498
- },
499
- evaluation
500
- })),
501
- evaluationStatsBuckets: evaluationStatsBuckets ?? [],
502
- strategyConfigs: strategyConfigs ?? []
503
- };
504
- };
387
+ }) => ({
388
+ reportType: "runtime-daily-evidence",
389
+ generatedAt: Date.now(),
390
+ userName,
391
+ window: {
392
+ startTime,
393
+ endTime,
394
+ startMsk: formatRuntimeDebugDateTime(startTime),
395
+ endMsk: formatRuntimeDebugDateTime(endTime),
396
+ dayKeys: (0, import_time2.getRuntimeStorageDayKeys)(startTime, endTime)
397
+ },
398
+ counts: {
399
+ trades: trades.length,
400
+ signals: signals.length,
401
+ evaluations: evaluations.length,
402
+ evaluationStatsBuckets: evaluationStatsBuckets?.length ?? 0,
403
+ strategyConfigs: strategyConfigs?.length ?? 0
404
+ },
405
+ trades: trades.map((trade) => ({ trade })),
406
+ signals: signals.map((signal) => ({ signal })),
407
+ evaluations: evaluations.map((evaluation) => ({ evaluation })),
408
+ evaluationStatsBuckets: evaluationStatsBuckets ?? [],
409
+ strategyConfigs: strategyConfigs ?? []
410
+ });
505
411
 
506
412
  // src/lib/replay/support.ts
507
413
  var import_chalk = __toESM(require("chalk"));
@@ -659,6 +565,7 @@ var summarizeReplayComparison = (runtimeComparison, strategies) => {
659
565
  ].sort((left, right) => left.localeCompare(right));
660
566
  return {
661
567
  mode: comparison.mode ?? null,
568
+ lineage: comparison.lineage ?? null,
662
569
  rows: asArray(comparison.rows).filter(
663
570
  (row) => !useFilter || strategySet.has(String(row.strategyName ?? ""))
664
571
  ),
@@ -786,7 +693,7 @@ var loadRuntimePayload = async ({
786
693
  return {
787
694
  source: "local-redis",
788
695
  path: null,
789
- payload: await buildRuntimeDebugReportPayload({
696
+ payload: buildRuntimeEvidenceReportPayload({
790
697
  userName: flags.user,
791
698
  startTime,
792
699
  endTime,