@tradejs/cli 3.1.8 → 3.1.9-beta.214

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
@@ -12181,114 +12181,6 @@ var init_marketContextPrepare = __esm({
12181
12181
  }
12182
12182
  });
12183
12183
 
12184
- // src/lib/timeWindow.ts
12185
- var ONE_DAY_MS, toEpochMs, toPositiveNumber, resolveTimeWindow;
12186
- var init_timeWindow = __esm({
12187
- "src/lib/timeWindow.ts"() {
12188
- "use strict";
12189
- ONE_DAY_MS = 24 * 60 * 60 * 1e3;
12190
- toEpochMs = (value) => {
12191
- if (value == null || value === "") {
12192
- return null;
12193
- }
12194
- const parsed = Number(value);
12195
- if (!Number.isFinite(parsed) || parsed <= 0) {
12196
- return null;
12197
- }
12198
- return parsed < 1e12 ? Math.trunc(parsed * 1e3) : Math.trunc(parsed);
12199
- };
12200
- toPositiveNumber = (value) => {
12201
- if (value == null || value === "") {
12202
- return null;
12203
- }
12204
- const parsed = Number(value);
12205
- if (!Number.isFinite(parsed) || parsed <= 0) {
12206
- return null;
12207
- }
12208
- return parsed;
12209
- };
12210
- resolveTimeWindow = ({
12211
- days,
12212
- startTime,
12213
- endTime,
12214
- nowMs = Date.now(),
12215
- defaultStartMs,
12216
- defaultEndMs,
12217
- closedIntervalMs
12218
- }) => {
12219
- const explicitStart = toEpochMs(startTime);
12220
- const explicitEnd = toEpochMs(endTime);
12221
- const resolvedDays = toPositiveNumber(days);
12222
- const alignToLastClosedInterval = (window, preserveDuration) => {
12223
- if (typeof closedIntervalMs !== "number" || !Number.isFinite(closedIntervalMs) || closedIntervalMs <= 0) {
12224
- return window;
12225
- }
12226
- const alignedEnd = Math.floor(window.end / closedIntervalMs) * closedIntervalMs - 1;
12227
- const delta = window.end - alignedEnd;
12228
- const alignedStart = preserveDuration ? window.start - delta : window.start;
12229
- if (alignedStart >= alignedEnd) {
12230
- throw new Error(
12231
- `Invalid closed-candle window: start (${alignedStart}) must be less than end (${alignedEnd})`
12232
- );
12233
- }
12234
- return {
12235
- ...window,
12236
- start: Math.trunc(alignedStart),
12237
- end: Math.trunc(alignedEnd)
12238
- };
12239
- };
12240
- if (explicitStart != null || explicitEnd != null) {
12241
- const resolvedStart = explicitStart ?? (resolvedDays != null && explicitEnd != null ? explicitEnd - resolvedDays * ONE_DAY_MS : defaultStartMs);
12242
- const resolvedEnd2 = explicitEnd ?? (resolvedDays != null && explicitStart != null ? explicitStart + resolvedDays * ONE_DAY_MS : defaultEndMs ?? nowMs);
12243
- if (resolvedStart >= resolvedEnd2) {
12244
- throw new Error(
12245
- `Invalid time window: startTime (${resolvedStart}) must be less than endTime (${resolvedEnd2})`
12246
- );
12247
- }
12248
- return alignToLastClosedInterval(
12249
- {
12250
- start: resolvedStart,
12251
- end: resolvedEnd2,
12252
- source: "explicit"
12253
- },
12254
- explicitStart == null && resolvedDays != null
12255
- );
12256
- }
12257
- if (resolvedDays != null) {
12258
- const resolvedEnd2 = defaultEndMs ?? nowMs;
12259
- const resolvedStart = resolvedEnd2 - resolvedDays * ONE_DAY_MS;
12260
- if (resolvedStart >= resolvedEnd2) {
12261
- throw new Error(
12262
- `Invalid time window: computed start (${resolvedStart}) must be less than end (${resolvedEnd2})`
12263
- );
12264
- }
12265
- return alignToLastClosedInterval(
12266
- {
12267
- start: Math.trunc(resolvedStart),
12268
- end: Math.trunc(resolvedEnd2),
12269
- source: "days"
12270
- },
12271
- true
12272
- );
12273
- }
12274
- const resolvedEnd = defaultEndMs ?? nowMs;
12275
- if (defaultStartMs >= resolvedEnd) {
12276
- throw new Error(
12277
- `Invalid default time window: start (${defaultStartMs}) must be less than end (${resolvedEnd})`
12278
- );
12279
- }
12280
- return alignToLastClosedInterval(
12281
- {
12282
- start: Math.trunc(defaultStartMs),
12283
- end: Math.trunc(resolvedEnd),
12284
- source: "default"
12285
- },
12286
- true
12287
- );
12288
- };
12289
- }
12290
- });
12291
-
12292
12184
  // src/lib/runtimeRedis.ts
12293
12185
  var import_redis4, import_time, import_runtimeStrategyConfigs, isRuntimeStrategyEnabled, isRuntimeTradeRecord, loadRuntimeTrades, loadRuntimeClosedTrades, loadRuntimeActiveTradeOrderIds, loadStrategyResultSymbols;
12294
12186
  var init_runtimeRedis = __esm({
@@ -12433,6 +12325,114 @@ var init_runtimeStrategyBacktest = __esm({
12433
12325
  }
12434
12326
  });
12435
12327
 
12328
+ // src/lib/timeWindow.ts
12329
+ var ONE_DAY_MS, toEpochMs, toPositiveNumber, resolveTimeWindow;
12330
+ var init_timeWindow = __esm({
12331
+ "src/lib/timeWindow.ts"() {
12332
+ "use strict";
12333
+ ONE_DAY_MS = 24 * 60 * 60 * 1e3;
12334
+ toEpochMs = (value) => {
12335
+ if (value == null || value === "") {
12336
+ return null;
12337
+ }
12338
+ const parsed = Number(value);
12339
+ if (!Number.isFinite(parsed) || parsed <= 0) {
12340
+ return null;
12341
+ }
12342
+ return parsed < 1e12 ? Math.trunc(parsed * 1e3) : Math.trunc(parsed);
12343
+ };
12344
+ toPositiveNumber = (value) => {
12345
+ if (value == null || value === "") {
12346
+ return null;
12347
+ }
12348
+ const parsed = Number(value);
12349
+ if (!Number.isFinite(parsed) || parsed <= 0) {
12350
+ return null;
12351
+ }
12352
+ return parsed;
12353
+ };
12354
+ resolveTimeWindow = ({
12355
+ days,
12356
+ startTime,
12357
+ endTime,
12358
+ nowMs = Date.now(),
12359
+ defaultStartMs,
12360
+ defaultEndMs,
12361
+ closedIntervalMs
12362
+ }) => {
12363
+ const explicitStart = toEpochMs(startTime);
12364
+ const explicitEnd = toEpochMs(endTime);
12365
+ const resolvedDays = toPositiveNumber(days);
12366
+ const alignToLastClosedInterval = (window, preserveDuration) => {
12367
+ if (typeof closedIntervalMs !== "number" || !Number.isFinite(closedIntervalMs) || closedIntervalMs <= 0) {
12368
+ return window;
12369
+ }
12370
+ const alignedEnd = Math.floor(window.end / closedIntervalMs) * closedIntervalMs - 1;
12371
+ const delta = window.end - alignedEnd;
12372
+ const alignedStart = preserveDuration ? window.start - delta : window.start;
12373
+ if (alignedStart >= alignedEnd) {
12374
+ throw new Error(
12375
+ `Invalid closed-candle window: start (${alignedStart}) must be less than end (${alignedEnd})`
12376
+ );
12377
+ }
12378
+ return {
12379
+ ...window,
12380
+ start: Math.trunc(alignedStart),
12381
+ end: Math.trunc(alignedEnd)
12382
+ };
12383
+ };
12384
+ if (explicitStart != null || explicitEnd != null) {
12385
+ const resolvedStart = explicitStart ?? (resolvedDays != null && explicitEnd != null ? explicitEnd - resolvedDays * ONE_DAY_MS : defaultStartMs);
12386
+ const resolvedEnd2 = explicitEnd ?? (resolvedDays != null && explicitStart != null ? explicitStart + resolvedDays * ONE_DAY_MS : defaultEndMs ?? nowMs);
12387
+ if (resolvedStart >= resolvedEnd2) {
12388
+ throw new Error(
12389
+ `Invalid time window: startTime (${resolvedStart}) must be less than endTime (${resolvedEnd2})`
12390
+ );
12391
+ }
12392
+ return alignToLastClosedInterval(
12393
+ {
12394
+ start: resolvedStart,
12395
+ end: resolvedEnd2,
12396
+ source: "explicit"
12397
+ },
12398
+ explicitStart == null && resolvedDays != null
12399
+ );
12400
+ }
12401
+ if (resolvedDays != null) {
12402
+ const resolvedEnd2 = defaultEndMs ?? nowMs;
12403
+ const resolvedStart = resolvedEnd2 - resolvedDays * ONE_DAY_MS;
12404
+ if (resolvedStart >= resolvedEnd2) {
12405
+ throw new Error(
12406
+ `Invalid time window: computed start (${resolvedStart}) must be less than end (${resolvedEnd2})`
12407
+ );
12408
+ }
12409
+ return alignToLastClosedInterval(
12410
+ {
12411
+ start: Math.trunc(resolvedStart),
12412
+ end: Math.trunc(resolvedEnd2),
12413
+ source: "days"
12414
+ },
12415
+ true
12416
+ );
12417
+ }
12418
+ const resolvedEnd = defaultEndMs ?? nowMs;
12419
+ if (defaultStartMs >= resolvedEnd) {
12420
+ throw new Error(
12421
+ `Invalid default time window: start (${defaultStartMs}) must be less than end (${resolvedEnd})`
12422
+ );
12423
+ }
12424
+ return alignToLastClosedInterval(
12425
+ {
12426
+ start: Math.trunc(defaultStartMs),
12427
+ end: Math.trunc(resolvedEnd),
12428
+ source: "default"
12429
+ },
12430
+ true
12431
+ );
12432
+ };
12433
+ }
12434
+ });
12435
+
12436
12436
  // src/lib/marketData/historyPrepare.ts
12437
12437
  var import_chalk11, import_connectors3, import_cli2, import_connectors4, withPrimaryMarketReferenceSymbols, loadBtcReferenceConnectors, updateMarketHistoryWithBtcReferences, updatePrimaryMarketHistory, updateBtcReferenceHistory;
12438
12438
  var init_historyPrepare = __esm({
@@ -12689,7 +12689,7 @@ var init_tickerUniverseCache = __esm({
12689
12689
  });
12690
12690
 
12691
12691
  // src/lib/runEnvironment.ts
12692
- var import_chalk12, import_connectors5, import_cli4, import_constants6, import_time2, timeOperation2, resolveBacktestExecutionPreloadInterval, resolveRunConnectorName, loadReplayStrategies, prepareRunEnvironment;
12692
+ var import_chalk12, import_connectors5, import_cli4, import_constants6, import_time2, import_runtimeStrategies, timeOperation2, resolveBacktestExecutionPreloadInterval, resolveRunConnectorName, loadDeploymentReplayStrategies, prepareRunEnvironment;
12693
12693
  var init_runEnvironment = __esm({
12694
12694
  "src/lib/runEnvironment.ts"() {
12695
12695
  "use strict";
@@ -12698,8 +12698,9 @@ var init_runEnvironment = __esm({
12698
12698
  import_cli4 = require("@tradejs/node/cli");
12699
12699
  import_constants6 = require("@tradejs/core/constants");
12700
12700
  import_time2 = require("@tradejs/core/time");
12701
- init_timeWindow();
12701
+ import_runtimeStrategies = require("@tradejs/node/runtimeStrategies");
12702
12702
  init_runtimeStrategyBacktest();
12703
+ init_timeWindow();
12703
12704
  init_runFormatting();
12704
12705
  init_historyPrepare();
12705
12706
  init_tickerUniverseCache();
@@ -12733,17 +12734,43 @@ var init_runEnvironment = __esm({
12733
12734
  );
12734
12735
  return import_connectors5.DEFAULT_CONNECTOR_NAME;
12735
12736
  };
12736
- loadReplayStrategies = async (userName3) => {
12737
- const runtimeStrategies = await loadRuntimeStrategyBacktestConfigs(userName3);
12738
- if (!runtimeStrategies.length) {
12737
+ loadDeploymentReplayStrategies = async ({
12738
+ userName: userName3,
12739
+ projectRoot: projectRoot15,
12740
+ deploymentId
12741
+ }) => {
12742
+ const [deployment, runtimeStrategies] = await Promise.all([
12743
+ (0, import_runtimeStrategies.getRuntimeDeployment)({ userName: userName3, projectRoot: projectRoot15, deploymentId }),
12744
+ (0, import_runtimeStrategies.loadResolvedRuntimeStrategies)({ userName: userName3, projectRoot: projectRoot15, deploymentId })
12745
+ ]);
12746
+ if (!deployment) {
12747
+ throw new Error(`Runtime deployment not found: ${deploymentId}`);
12748
+ }
12749
+ const strategies = deployment.enabled ? runtimeStrategies.filter((strategy) => strategy.enabled).map(
12750
+ ({
12751
+ strategyName,
12752
+ version,
12753
+ strategyPackage,
12754
+ strategyPackageVersion,
12755
+ runtimePackageVersion,
12756
+ sourceStrategyConfig
12757
+ }) => ({
12758
+ strategyName,
12759
+ version,
12760
+ strategyPackage,
12761
+ strategyPackageVersion,
12762
+ runtimePackageVersion,
12763
+ strategyConfig: sourceStrategyConfig
12764
+ })
12765
+ ) : [];
12766
+ if (!strategies.length) {
12739
12767
  console.log(
12740
12768
  import_chalk12.default.yellow(
12741
- `No active runtime strategy configs found by users:${userName3}:strategies:*:config`
12769
+ `No enabled strategies found in runtime deployment ${deploymentId}`
12742
12770
  )
12743
12771
  );
12744
- return [];
12745
12772
  }
12746
- return runtimeStrategies;
12773
+ return { deployment, strategies };
12747
12774
  };
12748
12775
  prepareRunEnvironment = async ({
12749
12776
  connector,
@@ -13460,7 +13487,7 @@ var init_workerPool = __esm({
13460
13487
  });
13461
13488
 
13462
13489
  // src/lib/backtest/runnerCore.ts
13463
- var import_fs, import_path5, import_chalk14, import_backtest, import_constants8, import_time3, import_backtestArtifacts, import_redis6, import_runtimeStrategies, import_types4, formatWindowDays, getResultAmount, getResultNetProfit2, recordError, insertTopResult, updateTopResults, updateBestTickerResult, chunkTestSuiteBySymbol, mergePersistedTestSummaries, getLogsById, resolveResultArtifacts, setTestData, persistTestSummariesIndex, validateBacktestRuntimeDeployment, prepareRunEnvironment2, buildPreparedTestSuite, trackTopResult, buildRunIntroLines, resolveTesterWorker, executeTestSuite, printRunOutro;
13490
+ var import_fs, import_path5, import_chalk14, import_backtest, import_constants8, import_time3, import_backtestArtifacts, import_redis6, import_runtimeStrategies2, import_types4, formatWindowDays, getResultAmount, getResultNetProfit2, recordError, insertTopResult, updateTopResults, updateBestTickerResult, chunkTestSuiteBySymbol, mergePersistedTestSummaries, getLogsById, resolveResultArtifacts, setTestData, persistTestSummariesIndex, validateBacktestRuntimeDeployment, prepareRunEnvironment2, buildPreparedTestSuite, trackTopResult, buildRunIntroLines, resolveTesterWorker, executeTestSuite, printRunOutro;
13464
13491
  var init_runnerCore = __esm({
13465
13492
  "src/lib/backtest/runnerCore.ts"() {
13466
13493
  "use strict";
@@ -13472,7 +13499,7 @@ var init_runnerCore = __esm({
13472
13499
  import_time3 = require("@tradejs/core/time");
13473
13500
  import_backtestArtifacts = require("@tradejs/infra/backtestArtifacts");
13474
13501
  import_redis6 = require("@tradejs/infra/redis");
13475
- import_runtimeStrategies = require("@tradejs/node/runtimeStrategies");
13502
+ import_runtimeStrategies2 = require("@tradejs/node/runtimeStrategies");
13476
13503
  import_types4 = require("@tradejs/types");
13477
13504
  init_runFormatting();
13478
13505
  init_marketContextPrepare();
@@ -13680,7 +13707,7 @@ var init_runnerCore = __esm({
13680
13707
  };
13681
13708
  prepareRunEnvironment2 = async () => {
13682
13709
  const deployment = validateBacktestRuntimeDeployment({
13683
- deployment: flags5.deployment ? await (0, import_runtimeStrategies.getRuntimeDeployment)({
13710
+ deployment: flags5.deployment ? await (0, import_runtimeStrategies2.getRuntimeDeployment)({
13684
13711
  userName,
13685
13712
  projectRoot: projectRoot3,
13686
13713
  deploymentId: String(flags5.deployment)
@@ -24177,13 +24204,17 @@ var init_runtimeSignalsLoader = __esm({
24177
24204
  return null;
24178
24205
  }
24179
24206
  const suffix = key.slice(prefix.length);
24180
- const firstColon = suffix.indexOf(":");
24181
- if (firstColon <= 0 || firstColon >= suffix.length - 1) {
24207
+ const parts = suffix.split(":");
24208
+ if (parts.length < 2 || parts.some((part) => !part)) {
24182
24209
  return null;
24183
24210
  }
24211
+ const [dayKey, ...scopeParts] = parts;
24212
+ const strategy = scopeParts.at(-1);
24213
+ if (!dayKey || !strategy) return null;
24184
24214
  return {
24185
- dayKey: suffix.slice(0, firstColon),
24186
- strategy: suffix.slice(firstColon + 1)
24215
+ dayKey,
24216
+ ...scopeParts.length > 1 ? { deploymentId: scopeParts.slice(0, -1).join(":") } : {},
24217
+ strategy
24187
24218
  };
24188
24219
  };
24189
24220
  loadRuntimeSignalEvaluationStatsBuckets = async (userName3) => {
@@ -24255,7 +24286,8 @@ var init_runtimeDebugEvidence = __esm({
24255
24286
  userName: userName3,
24256
24287
  startTime,
24257
24288
  endTime,
24258
- strategies
24289
+ strategies,
24290
+ deploymentId
24259
24291
  }) => {
24260
24292
  const dayKeys = (0, import_time6.getRuntimeStorageDayKeys)(startTime, endTime);
24261
24293
  const dayKeySet = new Set(dayKeys);
@@ -24265,6 +24297,7 @@ var init_runtimeDebugEvidence = __esm({
24265
24297
  signals2,
24266
24298
  evaluations,
24267
24299
  evaluationStatsBuckets,
24300
+ lineageScopes,
24268
24301
  configs
24269
24302
  ] = await Promise.all([
24270
24303
  loadRuntimeTrades(userName3, { startTime, endTime }),
@@ -24272,6 +24305,7 @@ var init_runtimeDebugEvidence = __esm({
24272
24305
  loadRuntimeSignals(userName3, { startTime, endTime }),
24273
24306
  loadRuntimeSignalEvaluations(userName3, { startTime, endTime }),
24274
24307
  loadRuntimeSignalEvaluationStatsBuckets(userName3),
24308
+ loadRuntimeLineageScopes(userName3, { startTime, endTime }),
24275
24309
  (0, import_runtimeStrategyConfigs.loadRuntimeStrategyConfigs)(userName3)
24276
24310
  ]);
24277
24311
  const tradesByOrderId = new Map(
@@ -24284,15 +24318,21 @@ var init_runtimeDebugEvidence = __esm({
24284
24318
  (left, right) => left.entryTimestamp - right.entryTimestamp
24285
24319
  );
24286
24320
  const filteredStats = evaluationStatsBuckets.filter(
24287
- (entry) => dayKeySet.has(entry.dayKey) && (!strategies?.length || strategies.includes(entry.strategy))
24321
+ (entry) => dayKeySet.has(entry.dayKey) && (!deploymentId || entry.deploymentId === deploymentId) && (!strategies?.length || strategies.includes(entry.strategy))
24288
24322
  );
24323
+ const matchesDeployment = (value) => !deploymentId || value.deploymentId === deploymentId;
24289
24324
  return {
24290
24325
  dayKeys,
24291
- trades: filterByStrategies(trades, strategies),
24292
- signals: filterByStrategies(signals2, strategies),
24293
- evaluations: filterByStrategies(evaluations, strategies),
24326
+ trades: filterByStrategies(trades, strategies).filter(matchesDeployment),
24327
+ signals: filterByStrategies(signals2, strategies).filter(matchesDeployment),
24328
+ evaluations: filterByStrategies(evaluations, strategies).filter(
24329
+ matchesDeployment
24330
+ ),
24294
24331
  evaluationStatsBuckets: filteredStats,
24295
- strategyConfigs: configs.filter(
24332
+ lineageScopes: lineageScopes.filter(matchesDeployment).filter(
24333
+ (scope) => !strategies?.length || strategies.includes(scope.strategy)
24334
+ ),
24335
+ strategyConfigs: deploymentId ? [] : configs.filter(
24296
24336
  (config2) => !strategies?.length || strategies.includes(config2.strategyName)
24297
24337
  )
24298
24338
  };
@@ -24305,7 +24345,8 @@ var init_runtimeDebugEvidence = __esm({
24305
24345
  evaluations,
24306
24346
  trades,
24307
24347
  strategyConfigs,
24308
- evaluationStatsBuckets
24348
+ evaluationStatsBuckets,
24349
+ lineageScopes
24309
24350
  }) => {
24310
24351
  const signalById = new Map(
24311
24352
  signals2.map((signal) => [signal.signalId, signal])
@@ -24433,6 +24474,7 @@ var init_runtimeDebugEvidence = __esm({
24433
24474
  signals: signals2.length,
24434
24475
  evaluations: reportEvaluations.length,
24435
24476
  evaluationStatsBuckets: evaluationStatsBuckets?.length ?? 0,
24477
+ lineageScopes: lineageScopes?.length ?? 0,
24436
24478
  strategyConfigs: strategyConfigs?.length ?? 0
24437
24479
  },
24438
24480
  trades: debugTrades,
@@ -24456,6 +24498,7 @@ var init_runtimeDebugEvidence = __esm({
24456
24498
  evaluation
24457
24499
  })),
24458
24500
  evaluationStatsBuckets: evaluationStatsBuckets ?? [],
24501
+ lineageScopes: lineageScopes ?? [],
24459
24502
  strategyConfigs: strategyConfigs ?? []
24460
24503
  };
24461
24504
  };
@@ -24467,7 +24510,8 @@ var init_runtimeDebugEvidence = __esm({
24467
24510
  evaluations,
24468
24511
  trades,
24469
24512
  strategyConfigs,
24470
- evaluationStatsBuckets
24513
+ evaluationStatsBuckets,
24514
+ lineageScopes
24471
24515
  }) => ({
24472
24516
  reportType: "runtime-daily-evidence",
24473
24517
  generatedAt: Date.now(),
@@ -24484,12 +24528,14 @@ var init_runtimeDebugEvidence = __esm({
24484
24528
  signals: signals2.length,
24485
24529
  evaluations: evaluations.length,
24486
24530
  evaluationStatsBuckets: evaluationStatsBuckets?.length ?? 0,
24531
+ lineageScopes: lineageScopes?.length ?? 0,
24487
24532
  strategyConfigs: strategyConfigs?.length ?? 0
24488
24533
  },
24489
24534
  trades: trades.map((trade) => ({ trade })),
24490
24535
  signals: signals2.map((signal) => ({ signal })),
24491
24536
  evaluations: evaluations.map((evaluation) => ({ evaluation })),
24492
24537
  evaluationStatsBuckets: evaluationStatsBuckets ?? [],
24538
+ lineageScopes: lineageScopes ?? [],
24493
24539
  strategyConfigs: strategyConfigs ?? []
24494
24540
  });
24495
24541
  buildRuntimeDebugReportAttachment = async (params) => {
@@ -24899,7 +24945,8 @@ var init_replayRuntimeEvidence = __esm({
24899
24945
  return {
24900
24946
  source: "file",
24901
24947
  path: runtimeEvidencePath,
24902
- payload: artifact.runtime ?? artifact
24948
+ payload: artifact.runtime ?? artifact,
24949
+ deployment: artifact.deployment ?? null
24903
24950
  };
24904
24951
  }
24905
24952
  const runtimeEvidence2 = await collectRuntimeDebugEvidence({
@@ -24911,6 +24958,7 @@ var init_replayRuntimeEvidence = __esm({
24911
24958
  return {
24912
24959
  source: "local-redis",
24913
24960
  path: null,
24961
+ deployment: null,
24914
24962
  payload: buildRuntimeEvidenceReportPayload({
24915
24963
  userName: userName3,
24916
24964
  startTime,
@@ -24919,7 +24967,8 @@ var init_replayRuntimeEvidence = __esm({
24919
24967
  evaluations: runtimeEvidence2.evaluations,
24920
24968
  trades: runtimeEvidence2.trades,
24921
24969
  strategyConfigs: runtimeEvidence2.strategyConfigs,
24922
- evaluationStatsBuckets: runtimeEvidence2.evaluationStatsBuckets
24970
+ evaluationStatsBuckets: runtimeEvidence2.evaluationStatsBuckets,
24971
+ lineageScopes: runtimeEvidence2.lineageScopes
24923
24972
  })
24924
24973
  };
24925
24974
  };
@@ -24959,6 +25008,7 @@ var init_replayRuntimeEvidence = __esm({
24959
25008
  type: runtime.source,
24960
25009
  path: runtime.path
24961
25010
  },
25011
+ deployment: runtime.deployment,
24962
25012
  replay: {
24963
25013
  key: replay.key,
24964
25014
  availableKeys: replay.availableKeys,
@@ -25283,7 +25333,7 @@ async function buildRuntimeLineage({
25283
25333
  maxLossValue: resolveRuntimeMaxLossValue(config2)
25284
25334
  };
25285
25335
  }
25286
- var import_node_child_process6, import_node_crypto11, import_promises23, import_node_path17, import_types8, import_strategies10, import_strategyReleaseEvidence2, RUNTIME_CONTEXT_ENV_KEYS, RUNTIME_CONTEXT_DATA_MODEL, normalizeForStableJson2, resolveRuntimeMaxLossValue, fingerprintRuntimeValue, gitLineageCache, gateFingerprintCache, binanceBreadthFingerprintCache, snapshotFingerprintCache, resolveGitLineage, readOptionalFile, resolveBinanceBreadthFingerprint, resolveSnapshotFingerprint, resolveRuntimeGateFingerprint, runtimeLineageKey, runtimeLineagesMatch;
25336
+ var import_node_child_process6, import_node_crypto11, import_promises23, import_node_path17, import_types8, import_strategies10, import_strategyReleaseEvidence2, RUNTIME_CONTEXT_ENV_KEYS, RUNTIME_CONTEXT_DATA_MODEL, normalizeForStableJson2, resolveRuntimeMaxLossValue, fingerprintRuntimeValue, gitLineageCache, gateFingerprintCache, binanceBreadthFingerprintCache, snapshotFingerprintCache, resolveGitLineage, readOptionalFile, resolveBinanceBreadthFingerprint, resolveSnapshotFingerprint, resolveRuntimeGateFingerprint, runtimeLineageKey, runtimeLineagesMatch, runtimeLineagesComparable;
25287
25337
  var init_runtimeLineage = __esm({
25288
25338
  "src/lib/runtimeLineage.ts"() {
25289
25339
  "use strict";
@@ -25469,7 +25519,12 @@ var init_runtimeLineage = __esm({
25469
25519
  gateFingerprintCache.set(cacheKey, result);
25470
25520
  return result;
25471
25521
  };
25472
- runtimeLineageKey = (lineage) => lineage.schemaVersion === 2 ? ["v2", lineage.version].join(":") : [
25522
+ runtimeLineageKey = (lineage) => lineage.schemaVersion === 2 ? [
25523
+ "v2",
25524
+ lineage.version,
25525
+ lineage.strategyPackageVersion ?? "strategy:unknown",
25526
+ lineage.runtimePackageVersion ?? "runtime:unknown"
25527
+ ].join(":") : [
25473
25528
  lineage.schemaVersion,
25474
25529
  lineage.compositionId ?? "unbound-composition",
25475
25530
  lineage.gitSha ?? "unknown",
@@ -25479,26 +25534,163 @@ var init_runtimeLineage = __esm({
25479
25534
  lineage.contextFingerprint
25480
25535
  ].join(":");
25481
25536
  runtimeLineagesMatch = (left, right) => left != null && right != null && runtimeLineageKey(left) === runtimeLineageKey(right);
25537
+ runtimeLineagesComparable = (left, right) => runtimeLineagesMatch(left, right) && typeof left?.maxLossValue === "number" && Number.isFinite(left.maxLossValue) && typeof right?.maxLossValue === "number" && Number.isFinite(right.maxLossValue) && left.maxLossValue === right.maxLossValue;
25482
25538
  }
25483
25539
  });
25484
25540
 
25485
- // src/scripts/runtimeEvidence.ts
25486
- var runtimeEvidence_exports = {};
25487
- __export(runtimeEvidence_exports, {
25488
- main: () => main34,
25489
- runtimeEvidence: () => runtimeEvidence
25490
- });
25491
- var import_promises24, import_node_path18, import_args26, import_chalk39, flags25, projectRoot9, toEpochMs3, resolveWindow2, parseStrategyFilter2, runtimeEvidence, main34;
25492
- var init_runtimeEvidence = __esm({
25493
- "src/scripts/runtimeEvidence.ts"() {
25541
+ // src/lib/runtimeEvidenceDeployment.ts
25542
+ var import_runtimeStrategies3, isRecord, isNonEmptyString, INTERVALS, UNIVERSES, parseRuntimeEvidenceDeploymentSnapshot, activeRuntimeEvidenceStrategies, runtimeDeploymentFromEvidence, resolveRuntimeEvidenceDeploymentSnapshot;
25543
+ var init_runtimeEvidenceDeployment = __esm({
25544
+ "src/lib/runtimeEvidenceDeployment.ts"() {
25494
25545
  "use strict";
25495
- import_promises24 = __toESM(require("fs/promises"));
25546
+ import_runtimeStrategies3 = require("@tradejs/node/runtimeStrategies");
25547
+ isRecord = (value) => value != null && typeof value === "object" && !Array.isArray(value);
25548
+ isNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0;
25549
+ INTERVALS = /* @__PURE__ */ new Set([
25550
+ "1",
25551
+ "3",
25552
+ "5",
25553
+ "15",
25554
+ "30",
25555
+ "60",
25556
+ "120",
25557
+ "240",
25558
+ "360",
25559
+ "720",
25560
+ "D",
25561
+ "W",
25562
+ "M"
25563
+ ]);
25564
+ UNIVERSES = /* @__PURE__ */ new Set(["crypto", "tradfi"]);
25565
+ parseRuntimeEvidenceDeploymentSnapshot = (value) => {
25566
+ if (!isRecord(value) || value.schemaVersion !== 1 || !isNonEmptyString(value.id) || !isNonEmptyString(value.label) || !isNonEmptyString(value.connectorName) || !isNonEmptyString(value.provider) || !isNonEmptyString(value.accountId) || typeof value.enabled !== "boolean" || !Array.isArray(value.strategies)) {
25567
+ throw new Error(
25568
+ "Runtime evidence deployment snapshot is missing or invalid"
25569
+ );
25570
+ }
25571
+ const strategies = value.strategies.map((candidate) => {
25572
+ if (!isRecord(candidate) || !isNonEmptyString(candidate.strategyName) || !Number.isSafeInteger(candidate.version) || Number(candidate.version) <= 0 || typeof candidate.enabled !== "boolean" || !["active", "entries_paused"].includes(String(candidate.controlState)) || !INTERVALS.has(String(candidate.interval)) || !UNIVERSES.has(String(candidate.universe)) || !isNonEmptyString(candidate.strategyPackage) || !isNonEmptyString(candidate.strategyPackageVersion) || !isNonEmptyString(candidate.runtimePackageVersion) || !isRecord(candidate.strategyConfig)) {
25573
+ throw new Error(
25574
+ "Runtime evidence deployment strategy snapshot is invalid"
25575
+ );
25576
+ }
25577
+ if (String(candidate.strategyConfig.INTERVAL) !== String(candidate.interval) || String(candidate.strategyConfig.UNIVERSE) !== String(candidate.universe)) {
25578
+ throw new Error(
25579
+ `Runtime evidence strategy config does not match its scope: ${candidate.strategyName}`
25580
+ );
25581
+ }
25582
+ if (candidate.accountId != null && (!isNonEmptyString(candidate.accountId) || candidate.accountId !== value.accountId)) {
25583
+ throw new Error(
25584
+ `Runtime evidence strategy account does not match deployment: ${candidate.strategyName}`
25585
+ );
25586
+ }
25587
+ return candidate;
25588
+ });
25589
+ const identities = /* @__PURE__ */ new Set();
25590
+ for (const strategy of strategies) {
25591
+ if (identities.has(strategy.strategyName)) {
25592
+ throw new Error(
25593
+ `Duplicate runtime evidence strategy: ${strategy.strategyName}`
25594
+ );
25595
+ }
25596
+ identities.add(strategy.strategyName);
25597
+ }
25598
+ return {
25599
+ ...value,
25600
+ strategies
25601
+ };
25602
+ };
25603
+ activeRuntimeEvidenceStrategies = (deployment) => deployment.enabled ? deployment.strategies.filter((strategy) => strategy.enabled) : [];
25604
+ runtimeDeploymentFromEvidence = (deployment) => ({
25605
+ id: deployment.id,
25606
+ label: deployment.label,
25607
+ connectorName: deployment.connectorName,
25608
+ provider: deployment.provider,
25609
+ accountId: deployment.accountId,
25610
+ enabled: deployment.enabled,
25611
+ strategies: deployment.strategies.map(
25612
+ ({ strategyName, version, enabled, controlState }) => ({
25613
+ strategyName,
25614
+ version,
25615
+ enabled,
25616
+ controlState
25617
+ })
25618
+ ),
25619
+ ...deployment.assetClasses ? { assetClasses: [...deployment.assetClasses] } : {},
25620
+ ...deployment.tickers ? { tickers: [...deployment.tickers] } : {}
25621
+ });
25622
+ resolveRuntimeEvidenceDeploymentSnapshot = async ({
25623
+ userName: userName3,
25624
+ projectRoot: projectRoot15,
25625
+ deploymentId
25626
+ }) => {
25627
+ const [deployment, strategies] = await Promise.all([
25628
+ (0, import_runtimeStrategies3.getRuntimeDeployment)({ userName: userName3, projectRoot: projectRoot15, deploymentId }),
25629
+ (0, import_runtimeStrategies3.loadResolvedRuntimeStrategies)({ userName: userName3, projectRoot: projectRoot15, deploymentId })
25630
+ ]);
25631
+ if (!deployment) {
25632
+ throw new Error(`Runtime deployment not found: ${deploymentId}`);
25633
+ }
25634
+ return {
25635
+ schemaVersion: 1,
25636
+ id: deployment.id,
25637
+ label: deployment.label,
25638
+ connectorName: deployment.connectorName,
25639
+ provider: deployment.provider,
25640
+ accountId: deployment.accountId,
25641
+ enabled: deployment.enabled,
25642
+ strategies: strategies.map(
25643
+ ({
25644
+ strategyName,
25645
+ version,
25646
+ enabled,
25647
+ controlState,
25648
+ interval: interval4,
25649
+ universe,
25650
+ accountId,
25651
+ strategyPackage,
25652
+ strategyPackageVersion,
25653
+ runtimePackageVersion,
25654
+ sourceStrategyConfig
25655
+ }) => ({
25656
+ strategyName,
25657
+ version,
25658
+ enabled,
25659
+ controlState,
25660
+ interval: interval4,
25661
+ universe,
25662
+ ...accountId ? { accountId } : {},
25663
+ strategyPackage,
25664
+ strategyPackageVersion,
25665
+ runtimePackageVersion,
25666
+ strategyConfig: sourceStrategyConfig
25667
+ })
25668
+ ),
25669
+ ...deployment.assetClasses ? { assetClasses: [...deployment.assetClasses] } : {},
25670
+ ...deployment.tickers ? { tickers: [...deployment.tickers] } : {}
25671
+ };
25672
+ };
25673
+ }
25674
+ });
25675
+
25676
+ // src/scripts/runtimeEvidence.ts
25677
+ var runtimeEvidence_exports = {};
25678
+ __export(runtimeEvidence_exports, {
25679
+ main: () => main34,
25680
+ runtimeEvidence: () => runtimeEvidence
25681
+ });
25682
+ var import_promises24, import_node_path18, import_args26, import_chalk39, flags25, projectRoot9, toEpochMs3, resolveWindow2, parseStrategyFilter2, runtimeEvidence, main34;
25683
+ var init_runtimeEvidence = __esm({
25684
+ "src/scripts/runtimeEvidence.ts"() {
25685
+ "use strict";
25686
+ import_promises24 = __toESM(require("fs/promises"));
25496
25687
  import_node_path18 = __toESM(require("path"));
25497
25688
  import_args26 = __toESM(require("args"));
25498
25689
  import_chalk39 = __toESM(require("chalk"));
25499
25690
  init_runtimeDebugEvidence();
25500
25691
  init_runtimeEvidenceArtifacts();
25501
25692
  init_runtimeLineage();
25693
+ init_runtimeEvidenceDeployment();
25502
25694
  import_args26.default.option(["u", "user"], "Use user config", "root");
25503
25695
  import_args26.default.option(
25504
25696
  ["S", "startTime"],
@@ -25577,12 +25769,34 @@ var init_runtimeEvidence = __esm({
25577
25769
  parseStrategyFilter2 = (value) => String(value ?? "").split(",").map((item) => item.trim()).filter(Boolean);
25578
25770
  runtimeEvidence = async () => {
25579
25771
  const { startTime, endTime, source } = resolveWindow2();
25580
- const strategies = parseStrategyFilter2(flags25.strategy);
25772
+ const deploymentId = String(flags25.deployment);
25773
+ const deployment = await resolveRuntimeEvidenceDeploymentSnapshot({
25774
+ userName: String(flags25.user),
25775
+ projectRoot: projectRoot9,
25776
+ deploymentId
25777
+ });
25778
+ const activeStrategies = activeRuntimeEvidenceStrategies(deployment).map(
25779
+ ({ strategyName }) => strategyName
25780
+ );
25781
+ const requestedStrategies = parseStrategyFilter2(flags25.strategy);
25782
+ const strategies = requestedStrategies.length ? requestedStrategies : activeStrategies;
25783
+ const declaredStrategies = new Set(
25784
+ deployment.strategies.map(({ strategyName }) => strategyName)
25785
+ );
25786
+ const unknownStrategies = strategies.filter(
25787
+ (strategyName) => !declaredStrategies.has(strategyName)
25788
+ );
25789
+ if (unknownStrategies.length) {
25790
+ throw new Error(
25791
+ `Strategies are not declared in ${deploymentId}: ${unknownStrategies.join(", ")}`
25792
+ );
25793
+ }
25581
25794
  const evidence = await collectRuntimeDebugEvidence({
25582
25795
  userName: flags25.user,
25583
25796
  startTime,
25584
25797
  endTime,
25585
- strategies: strategies.length ? strategies : void 0
25798
+ strategies,
25799
+ deploymentId
25586
25800
  });
25587
25801
  const runtime = buildRuntimeEvidenceReportPayload({
25588
25802
  userName: flags25.user,
@@ -25592,7 +25806,8 @@ var init_runtimeEvidence = __esm({
25592
25806
  evaluations: evidence.evaluations,
25593
25807
  trades: evidence.trades,
25594
25808
  strategyConfigs: evidence.strategyConfigs,
25595
- evaluationStatsBuckets: evidence.evaluationStatsBuckets
25809
+ evaluationStatsBuckets: evidence.evaluationStatsBuckets,
25810
+ lineageScopes: evidence.lineageScopes
25596
25811
  });
25597
25812
  const artifact = {
25598
25813
  reportType: "runtime-evidence",
@@ -25606,6 +25821,7 @@ var init_runtimeEvidence = __esm({
25606
25821
  source
25607
25822
  },
25608
25823
  strategies,
25824
+ deployment,
25609
25825
  runtime
25610
25826
  };
25611
25827
  const outPath = import_node_path18.default.resolve(projectRoot9, String(flags25.out));
@@ -25615,7 +25831,7 @@ var init_runtimeEvidence = __esm({
25615
25831
  const publishDir = String(flags25.publishDir ?? "").trim();
25616
25832
  const published = publishDir ? await publishRuntimeEvidenceBundle({
25617
25833
  publishRoot: import_node_path18.default.resolve(projectRoot9, publishDir),
25618
- deploymentId: String(flags25.deployment),
25834
+ deploymentId,
25619
25835
  userName: String(flags25.user),
25620
25836
  startTime,
25621
25837
  endTime,
@@ -25626,8 +25842,9 @@ var init_runtimeEvidence = __esm({
25626
25842
  ...evidence.evaluations.map(
25627
25843
  (evaluation) => evaluation.runtimeLineage
25628
25844
  ),
25629
- ...evidence.trades.map((trade) => trade.runtimeLineage)
25630
- ].filter((lineage) => lineage != null).map(runtimeLineageKey)
25845
+ ...evidence.trades.map((trade) => trade.runtimeLineage),
25846
+ ...evidence.lineageScopes.map((scope) => scope.lineage)
25847
+ ].filter((lineage) => lineage != null).map(runtimeLineageKey).filter((key, index, values) => values.indexOf(key) === index)
25631
25848
  }) : null;
25632
25849
  console.log(import_chalk39.default.green(`Wrote ${outPath}`));
25633
25850
  if (published) {
@@ -25886,10 +26103,11 @@ var init_runtimeEvidenceSync2 = __esm({
25886
26103
  });
25887
26104
 
25888
26105
  // src/lib/runtimeScorecard.ts
25889
- var asRecord6, asArray3, finiteNumber5, finiteString2, round, unwrapRows, runtimePayload, runtimeWindow, extractRuntimeRows, buildRuntimeLineageSummary, belongsToStrategy, filterArtifactByStrategy, countBy2, buildDistributions, buildDistributionDelta, sum2, statsCount, tradeTimestamp, dedupeTrades, maxDrawdown, buildRollingPerformance, getReplayComparison, replayCountsForStrategy, getCalibrationSummary, getProspectiveSummary, buildRuntimeScorecard, formatRuntimeScorecardMarkdown;
26106
+ var asRecord6, asArray3, finiteNumber5, finiteString2, round, unwrapRows, runtimePayload, runtimeWindow, extractRuntimeRows, buildRuntimeLineageSummary, belongsToRuntimeLineage, runtimeDeploymentBinding, belongsToRuntimeDeployment, belongsToStrategy, filterArtifactByStrategy, countBy2, buildDistributions, buildDistributionDelta, sum2, statsCount, tradeTimestamp, dedupeTrades, maxDrawdown, buildRollingPerformance, getReplayComparison, replayCountsForStrategy, getCalibrationSummary, getProspectiveSummary, buildRuntimeScorecard, formatRuntimeScorecardMarkdown;
25890
26107
  var init_runtimeScorecard = __esm({
25891
26108
  "src/lib/runtimeScorecard.ts"() {
25892
26109
  "use strict";
26110
+ init_runtimeLineage();
25893
26111
  asRecord6 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
25894
26112
  asArray3 = (value) => Array.isArray(value) ? value : [];
25895
26113
  finiteNumber5 = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
@@ -25910,15 +26128,32 @@ var init_runtimeScorecard = __esm({
25910
26128
  evaluations: unwrapRows(runtime.evaluations, "evaluation"),
25911
26129
  signals: unwrapRows(runtime.signals, "signal"),
25912
26130
  trades: unwrapRows(runtime.trades, "trade"),
26131
+ lineageScopes: unwrapRows(runtime.lineageScopes, "lineageScope"),
25913
26132
  statsBuckets: asArray3(runtime.evaluationStatsBuckets).map(asRecord6).filter((item) => item != null)
25914
26133
  };
25915
26134
  };
25916
26135
  buildRuntimeLineageSummary = (rows) => {
25917
- const lineageRows = [...rows.evaluations, ...rows.signals, ...rows.trades];
26136
+ const lineageRows = [
26137
+ ...rows.evaluations,
26138
+ ...rows.signals,
26139
+ ...rows.trades,
26140
+ ...rows.lineageScopes.map((scope) => ({
26141
+ runtimeLineage: scope.lineage
26142
+ }))
26143
+ ];
25918
26144
  const lineages = lineageRows.map((row) => asRecord6(row.runtimeLineage)).filter((lineage) => lineage != null);
25919
26145
  const identities = /* @__PURE__ */ new Map();
25920
26146
  for (const lineage of lineages) {
25921
- const identity2 = {
26147
+ const identity2 = lineage.schemaVersion === 2 ? {
26148
+ schemaVersion: 2,
26149
+ version: finiteNumber5(lineage.version),
26150
+ strategyPackageVersion: finiteString2(
26151
+ lineage.strategyPackageVersion
26152
+ ),
26153
+ runtimePackageVersion: finiteString2(lineage.runtimePackageVersion),
26154
+ maxLossValue: finiteNumber5(lineage.maxLossValue)
26155
+ } : {
26156
+ schemaVersion: 1,
25922
26157
  compositionId: finiteString2(lineage.compositionId),
25923
26158
  gitSha: finiteString2(lineage.gitSha),
25924
26159
  gitDirty: typeof lineage.gitDirty === "boolean" ? lineage.gitDirty : null,
@@ -25930,12 +26165,23 @@ var init_runtimeScorecard = __esm({
25930
26165
  identities.set(JSON.stringify(identity2), identity2);
25931
26166
  }
25932
26167
  const identity = identities.size === 1 ? [...identities.values()][0] : null;
25933
- const complete = lineageRows.length > 0 && lineages.length === lineageRows.length && identity != null && identity.compositionId != null && identity.gitSha != null && identity.gitDirty === false && identity.gateFingerprint != null && identity.configFingerprint != null && identity.contextFingerprint != null && identity.maxLossValue != null;
26168
+ const schemaVersion = finiteNumber5(identity?.schemaVersion);
26169
+ const identityComplete = lineages.length > 0 && identity != null && (schemaVersion === 2 && finiteNumber5(identity.version) != null && finiteString2(identity.strategyPackageVersion) != null && finiteString2(identity.runtimePackageVersion) != null || schemaVersion === 1 && identity.compositionId != null && identity.gitSha != null && identity.gitDirty === false && identity.gateFingerprint != null && identity.configFingerprint != null && identity.contextFingerprint != null) && identity.maxLossValue != null;
26170
+ const coverageComplete = lineageRows.length > 0 && lineages.length === lineageRows.length;
26171
+ const complete = identityComplete && coverageComplete;
26172
+ const lineageKey = identityComplete ? runtimeLineageKey(identity) : null;
25934
26173
  return {
25935
26174
  complete,
26175
+ identityComplete,
26176
+ coverageComplete,
25936
26177
  conflicts: identities.size > 1,
25937
26178
  rows: lineageRows.length,
25938
26179
  rowsWithLineage: lineages.length,
26180
+ schemaVersion,
26181
+ lineageKey,
26182
+ version: finiteNumber5(identity?.version),
26183
+ strategyPackageVersion: finiteString2(identity?.strategyPackageVersion),
26184
+ runtimePackageVersion: finiteString2(identity?.runtimePackageVersion),
25939
26185
  compositionId: finiteString2(identity?.compositionId),
25940
26186
  gitSha: finiteString2(identity?.gitSha),
25941
26187
  gitDirty: typeof identity?.gitDirty === "boolean" ? identity.gitDirty : null,
@@ -25945,6 +26191,20 @@ var init_runtimeScorecard = __esm({
25945
26191
  maxLossValue: finiteNumber5(identity?.maxLossValue)
25946
26192
  };
25947
26193
  };
26194
+ belongsToRuntimeLineage = (row, summary) => {
26195
+ if (!summary.identityComplete || !summary.lineageKey) return false;
26196
+ const lineage = asRecord6(row.runtimeLineage);
26197
+ if (!lineage) return false;
26198
+ return runtimeLineageKey(lineage) === summary.lineageKey && finiteNumber5(lineage.maxLossValue) === summary.maxLossValue;
26199
+ };
26200
+ runtimeDeploymentBinding = (artifact) => {
26201
+ const deployment = asRecord6(asRecord6(artifact)?.deployment);
26202
+ return {
26203
+ deploymentId: finiteString2(deployment?.id),
26204
+ accountId: finiteString2(deployment?.accountId)
26205
+ };
26206
+ };
26207
+ belongsToRuntimeDeployment = (row, binding) => binding.deploymentId == null && binding.accountId == null ? true : finiteString2(row.deploymentId) === binding.deploymentId && finiteString2(row.accountId) === binding.accountId;
25948
26208
  belongsToStrategy = (row, strategy) => strategy == null || finiteString2(row.strategy) === strategy;
25949
26209
  filterArtifactByStrategy = (artifact, strategy) => {
25950
26210
  if (!strategy) return artifact;
@@ -25972,6 +26232,9 @@ var init_runtimeScorecard = __esm({
25972
26232
  strategy
25973
26233
  )
25974
26234
  ),
26235
+ lineageScopes: asArray3(runtime.lineageScopes).filter(
26236
+ (item) => belongsToStrategy(asRecord6(item) ?? {}, strategy)
26237
+ ),
25975
26238
  evaluationStatsBuckets: asArray3(runtime.evaluationStatsBuckets).filter(
25976
26239
  (item) => belongsToStrategy(asRecord6(item) ?? {}, strategy)
25977
26240
  )
@@ -26151,6 +26414,7 @@ var init_runtimeScorecard = __esm({
26151
26414
  const scopedHistoryArtifacts = historyRuntimeArtifacts.map(
26152
26415
  (artifact) => filterArtifactByStrategy(artifact, strategy)
26153
26416
  );
26417
+ const deploymentBinding = runtimeDeploymentBinding(runtimeArtifact);
26154
26418
  const rows = extractRuntimeRows(scopedRuntimeArtifact);
26155
26419
  const runtimeLineage = buildRuntimeLineageSummary(rows);
26156
26420
  const window = runtimeWindow(runtimeArtifact);
@@ -26213,6 +26477,12 @@ var init_runtimeScorecard = __esm({
26213
26477
  ...scopedHistoryArtifacts,
26214
26478
  scopedRuntimeArtifact
26215
26479
  ]);
26480
+ const comparableHistoryTrades = historyTrades.filter(
26481
+ (trade) => belongsToRuntimeDeployment(trade, deploymentBinding) && belongsToRuntimeLineage(trade, runtimeLineage)
26482
+ );
26483
+ const comparableCurrentClosedTrades = currentClosedTrades.filter(
26484
+ (trade) => belongsToRuntimeDeployment(trade, deploymentBinding) && belongsToRuntimeLineage(trade, runtimeLineage)
26485
+ );
26216
26486
  const currentDistributions = buildDistributions(rows);
26217
26487
  const previousArtifact = scopedHistoryArtifacts.map((artifact) => ({ artifact, window: runtimeWindow(artifact) })).filter(({ window: candidateWindow }) => {
26218
26488
  const candidateEndTime = finiteNumber5(candidateWindow.endTime);
@@ -26223,7 +26493,7 @@ var init_runtimeScorecard = __esm({
26223
26493
  const previousDistributions = previousArtifact ? buildDistributions(extractRuntimeRows(previousArtifact.artifact)) : null;
26224
26494
  const rolling = [7, 30, 90].map(
26225
26495
  (days) => buildRollingPerformance({
26226
- trades: [...historyTrades],
26496
+ trades: [...comparableHistoryTrades],
26227
26497
  anchorTime: endTime,
26228
26498
  days
26229
26499
  })
@@ -26237,6 +26507,13 @@ var init_runtimeScorecard = __esm({
26237
26507
  message: lineageReason
26238
26508
  });
26239
26509
  }
26510
+ if (!runtimeLineage.complete) {
26511
+ reactions.push({
26512
+ code: "RUNTIME_LINEAGE_INCOMPLETE",
26513
+ severity: "blocking",
26514
+ message: "Runtime rows do not share one complete composition lineage."
26515
+ });
26516
+ }
26240
26517
  if (parityRatio != null && parityRatio < thresholds.minimumParityRatio) {
26241
26518
  reactions.push({
26242
26519
  code: "PARITY_REGRESSION",
@@ -26274,6 +26551,7 @@ var init_runtimeScorecard = __esm({
26274
26551
  generatedAt,
26275
26552
  window: { startTime, endTime },
26276
26553
  strategy,
26554
+ deployment: deploymentBinding,
26277
26555
  lineage: runtimeLineage,
26278
26556
  promotionStatus,
26279
26557
  thresholds,
@@ -26318,17 +26596,25 @@ var init_runtimeScorecard = __esm({
26318
26596
  )
26319
26597
  ).length,
26320
26598
  fills: rows.trades.length,
26321
- closedTrades: currentClosedTrades.length
26599
+ closedTrades: currentClosedTrades.length,
26600
+ comparableClosedTrades: comparableCurrentClosedTrades.length,
26601
+ nonComparableClosedTrades: currentClosedTrades.length - comparableCurrentClosedTrades.length
26322
26602
  },
26323
26603
  realized: {
26324
26604
  pnl: sum2(
26325
- currentClosedTrades.map((trade) => finiteNumber5(trade.closedPnl))
26605
+ comparableCurrentClosedTrades.map(
26606
+ (trade) => finiteNumber5(trade.closedPnl)
26607
+ )
26326
26608
  ),
26327
26609
  fees: sum2(
26328
- currentClosedTrades.map((trade) => finiteNumber5(trade.totalFee))
26610
+ comparableCurrentClosedTrades.map(
26611
+ (trade) => finiteNumber5(trade.totalFee)
26612
+ )
26329
26613
  ),
26330
26614
  funding: sum2(
26331
- currentClosedTrades.map((trade) => finiteNumber5(trade.fundingFee))
26615
+ comparableCurrentClosedTrades.map(
26616
+ (trade) => finiteNumber5(trade.fundingFee)
26617
+ )
26332
26618
  )
26333
26619
  },
26334
26620
  gateComparison: {
@@ -28094,13 +28380,13 @@ __export(runtimeControl_exports, {
28094
28380
  main: () => main37,
28095
28381
  runtimeControl: () => runtimeControl
28096
28382
  });
28097
- var import_config9, import_runtimeControls, import_runtimeStrategies2, argv, RUNTIME_CONTROL_ACTIONS, action, option, required2, projectRoot12, userName2, verify, inspect, updateControlState, runtimeControl, main37;
28383
+ var import_config9, import_runtimeControls, import_runtimeStrategies4, argv, RUNTIME_CONTROL_ACTIONS, action, option, required2, projectRoot12, userName2, verify, inspect, updateControlState, runtimeControl, main37;
28098
28384
  var init_runtimeControl = __esm({
28099
28385
  "src/scripts/runtimeControl.ts"() {
28100
28386
  "use strict";
28101
28387
  import_config9 = require("dotenv/config");
28102
28388
  import_runtimeControls = require("@tradejs/infra/runtimeControls");
28103
- import_runtimeStrategies2 = require("@tradejs/node/runtimeStrategies");
28389
+ import_runtimeStrategies4 = require("@tradejs/node/runtimeStrategies");
28104
28390
  argv = process.argv.slice(2);
28105
28391
  RUNTIME_CONTROL_ACTIONS = [
28106
28392
  "inspect",
@@ -28121,7 +28407,7 @@ var init_runtimeControl = __esm({
28121
28407
  projectRoot12 = String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
28122
28408
  userName2 = String(option("user") ?? process.env.USER_NAME ?? "root");
28123
28409
  verify = async () => {
28124
- const deployments = await (0, import_runtimeStrategies2.listRuntimeDeployments)({ userName: userName2, projectRoot: projectRoot12 });
28410
+ const deployments = await (0, import_runtimeStrategies4.listRuntimeDeployments)({ userName: userName2, projectRoot: projectRoot12 });
28125
28411
  const requestedDeployment = option("deployment");
28126
28412
  const selected = requestedDeployment ? deployments.filter((deployment) => deployment.id === requestedDeployment) : deployments;
28127
28413
  if (requestedDeployment && !selected.length) {
@@ -28130,7 +28416,7 @@ var init_runtimeControl = __esm({
28130
28416
  const results2 = [];
28131
28417
  for (const deployment of selected) {
28132
28418
  try {
28133
- const strategies = await (0, import_runtimeStrategies2.loadResolvedRuntimeStrategies)({
28419
+ const strategies = await (0, import_runtimeStrategies4.loadResolvedRuntimeStrategies)({
28134
28420
  userName: userName2,
28135
28421
  projectRoot: projectRoot12,
28136
28422
  deploymentId: deployment.id
@@ -28164,7 +28450,7 @@ var init_runtimeControl = __esm({
28164
28450
  inspect = async () => {
28165
28451
  const requestedDeployment = option("deployment");
28166
28452
  const requestedStrategy = option("strategy");
28167
- const deployments = (await (0, import_runtimeStrategies2.listRuntimeDeployments)({ userName: userName2, projectRoot: projectRoot12 })).filter(
28453
+ const deployments = (await (0, import_runtimeStrategies4.listRuntimeDeployments)({ userName: userName2, projectRoot: projectRoot12 })).filter(
28168
28454
  (deployment) => !requestedDeployment || deployment.id === requestedDeployment
28169
28455
  ).map((deployment) => ({
28170
28456
  ...deployment,
@@ -28177,7 +28463,7 @@ var init_runtimeControl = __esm({
28177
28463
  updateControlState = async (nextState) => {
28178
28464
  const deploymentId = required2("deployment");
28179
28465
  const strategyName = required2("strategy");
28180
- const deployment = await (0, import_runtimeStrategies2.getRuntimeDeployment)({
28466
+ const deployment = await (0, import_runtimeStrategies4.getRuntimeDeployment)({
28181
28467
  userName: userName2,
28182
28468
  projectRoot: projectRoot12,
28183
28469
  deploymentId
@@ -28207,7 +28493,7 @@ var init_runtimeControl = __esm({
28207
28493
  } else {
28208
28494
  await (0, import_runtimeControls.resumeRuntimeStrategy)({ userName: userName2, deploymentId, strategyName });
28209
28495
  }
28210
- const updated = await (0, import_runtimeStrategies2.getRuntimeDeployment)({
28496
+ const updated = await (0, import_runtimeStrategies4.getRuntimeDeployment)({
28211
28497
  userName: userName2,
28212
28498
  projectRoot: projectRoot12,
28213
28499
  deploymentId
@@ -31155,11 +31441,11 @@ var init_hooks = __esm({
31155
31441
  });
31156
31442
 
31157
31443
  // src/lib/signals/runtimeStrategies.ts
31158
- var import_runtimeStrategies3;
31444
+ var import_runtimeStrategies5;
31159
31445
  var init_runtimeStrategies = __esm({
31160
31446
  "src/lib/signals/runtimeStrategies.ts"() {
31161
31447
  "use strict";
31162
- import_runtimeStrategies3 = require("@tradejs/node/runtimeStrategies");
31448
+ import_runtimeStrategies5 = require("@tradejs/node/runtimeStrategies");
31163
31449
  }
31164
31450
  });
31165
31451
 
@@ -31234,9 +31520,28 @@ var init_evaluations = __esm({
31234
31520
  symbol,
31235
31521
  timestamp,
31236
31522
  runtimeConfigId,
31237
- runtimeVersion
31238
- }) => runtimeVersion ? [strategyName, `v${runtimeVersion}`, symbol, timestamp].join(":") : runtimeConfigId && runtimeConfigId !== "config" ? [strategyName, runtimeConfigId, symbol, timestamp].join(":") : [strategyName, symbol, timestamp].join(":");
31523
+ runtimeVersion,
31524
+ deploymentId,
31525
+ accountId
31526
+ }) => {
31527
+ const runtimeScope = deploymentId ? [deploymentId, accountId ?? "default-account"] : [];
31528
+ return runtimeVersion ? [
31529
+ ...runtimeScope,
31530
+ strategyName,
31531
+ `v${runtimeVersion}`,
31532
+ symbol,
31533
+ timestamp
31534
+ ].join(":") : runtimeConfigId && runtimeConfigId !== "config" ? [
31535
+ ...runtimeScope,
31536
+ strategyName,
31537
+ runtimeConfigId,
31538
+ symbol,
31539
+ timestamp
31540
+ ].join(":") : [...runtimeScope, strategyName, symbol, timestamp].join(":");
31541
+ };
31239
31542
  buildLineageField = (scope) => [
31543
+ scope.deploymentId ?? "default-deployment",
31544
+ scope.accountId ?? "default-account",
31240
31545
  scope.strategy,
31241
31546
  scope.runtimeConfigId ?? "config",
31242
31547
  scope.runtimeVersion ? `v${scope.runtimeVersion}` : "legacy",
@@ -31278,6 +31583,8 @@ var init_evaluations = __esm({
31278
31583
  for (const evaluation of lineageEvaluations) {
31279
31584
  const lineageField = buildLineageField({
31280
31585
  strategy: evaluation.strategy,
31586
+ deploymentId: evaluation.deploymentId,
31587
+ accountId: evaluation.accountId,
31281
31588
  runtimeConfigId: evaluation.runtimeConfigId,
31282
31589
  runtimeVersion: evaluation.runtimeVersion,
31283
31590
  symbol: evaluation.symbol,
@@ -31288,6 +31595,8 @@ var init_evaluations = __esm({
31288
31595
  const scope = {
31289
31596
  strategy: evaluation.strategy,
31290
31597
  symbol: evaluation.symbol,
31598
+ ...evaluation.deploymentId ? { deploymentId: evaluation.deploymentId } : {},
31599
+ ...evaluation.accountId ? { accountId: evaluation.accountId } : {},
31291
31600
  runtimeConfigId: evaluation.runtimeConfigId,
31292
31601
  runtimeVersion: evaluation.runtimeVersion,
31293
31602
  lineage: evaluation.runtimeLineage,
@@ -31331,7 +31640,8 @@ var init_evaluations = __esm({
31331
31640
  const statsBucket = import_redis18.redisKeys.runtimeSignalEvaluationStatsBucket(
31332
31641
  evaluation.userName,
31333
31642
  dayKey,
31334
- evaluation.strategy
31643
+ evaluation.strategy,
31644
+ evaluation.deploymentId
31335
31645
  );
31336
31646
  const increments = statsIncrements.get(statsBucket) ?? {};
31337
31647
  for (const [field, increment] of Object.entries(
@@ -31954,7 +32264,9 @@ var init_tickerEvaluator = __esm({
31954
32264
  symbol,
31955
32265
  timestamp: lastCandle.timestamp,
31956
32266
  runtimeConfigId: runtimeIdentity,
31957
- runtimeVersion: version
32267
+ runtimeVersion: version,
32268
+ deploymentId,
32269
+ accountId
31958
32270
  }),
31959
32271
  userName: userName3,
31960
32272
  strategy: strategyName,
@@ -31979,8 +32291,14 @@ var init_tickerEvaluator = __esm({
31979
32291
  signal.runtimeConfigId = runtimeIdentity;
31980
32292
  signal.runtimeVersion = version;
31981
32293
  signal.runtimeLineage = runtimeLineage;
31982
- if (version && !signal.signalId.endsWith(`:v${version}`)) {
31983
- signal.signalId = `${signal.signalId}:v${version}`;
32294
+ signal.deploymentId = deploymentId;
32295
+ signal.accountId = accountId;
32296
+ const signalRuntimeIdentity = [
32297
+ ...deploymentId ? [deploymentId, accountId ?? "default-account"] : [],
32298
+ ...version ? [`v${version}`] : []
32299
+ ].join(":");
32300
+ if (signalRuntimeIdentity && !signal.signalId.endsWith(`:${signalRuntimeIdentity}`)) {
32301
+ signal.signalId = `${signal.signalId}:${signalRuntimeIdentity}`;
31984
32302
  }
31985
32303
  await (0, import_strategies11.enrichSignalWithBinanceMarketContext)({ signal, env: "CRON" });
31986
32304
  if (signal.orderStatus === "skipped" && typeof signal.orderSkipReason === "string" && signal.orderSkipReason.trim()) {
@@ -31999,7 +32317,9 @@ var init_tickerEvaluator = __esm({
31999
32317
  symbol,
32000
32318
  timestamp: lastCandle.timestamp,
32001
32319
  runtimeConfigId: runtimeIdentity,
32002
- runtimeVersion: version
32320
+ runtimeVersion: version,
32321
+ deploymentId,
32322
+ accountId
32003
32323
  }),
32004
32324
  userName: userName3,
32005
32325
  strategy: strategyName,
@@ -32083,7 +32403,7 @@ var init_configuredScopes = __esm({
32083
32403
  });
32084
32404
 
32085
32405
  // src/lib/signals/runner.ts
32086
- var import_progress11, import_chalk45, import_connectors16, import_cli12, import_async5, import_strategies12, import_constants16, import_time8, import_logger10, import_runtimeHeartbeats, import_runtimeStrategies4, SLOW_SIGNALS_WARNING_MS, formatDuration3, resolvePositiveInteger2, resolveNonNegativeInteger, createSignalsRunner;
32406
+ var import_progress11, import_chalk45, import_connectors16, import_cli12, import_async5, import_strategies12, import_constants16, import_time8, import_logger10, import_runtimeHeartbeats, import_runtimeStrategies6, SLOW_SIGNALS_WARNING_MS, formatDuration3, resolvePositiveInteger2, resolveNonNegativeInteger, createSignalsRunner;
32087
32407
  var init_runner = __esm({
32088
32408
  "src/lib/signals/runner.ts"() {
32089
32409
  "use strict";
@@ -32097,7 +32417,7 @@ var init_runner = __esm({
32097
32417
  import_time8 = require("@tradejs/core/time");
32098
32418
  import_logger10 = require("@tradejs/infra/logger");
32099
32419
  import_runtimeHeartbeats = require("@tradejs/infra/runtimeHeartbeats");
32100
- import_runtimeStrategies4 = require("@tradejs/node/runtimeStrategies");
32420
+ import_runtimeStrategies6 = require("@tradejs/node/runtimeStrategies");
32101
32421
  init_windows();
32102
32422
  init_runFormatting();
32103
32423
  init_hooks();
@@ -32173,7 +32493,7 @@ var init_runner = __esm({
32173
32493
  return connectorName;
32174
32494
  };
32175
32495
  const createSignalsSession2 = async (lifecycle, scope = {}) => {
32176
- const deployment = config2.deploymentId ? await (0, import_runtimeStrategies4.getRuntimeDeployment)({
32496
+ const deployment = config2.deploymentId ? await (0, import_runtimeStrategies6.getRuntimeDeployment)({
32177
32497
  userName: config2.userName,
32178
32498
  projectRoot: projectRoot15,
32179
32499
  deploymentId: config2.deploymentId
@@ -32398,7 +32718,7 @@ var init_runner = __esm({
32398
32718
  const primaryEthByTimestamp = new Map(
32399
32719
  primaryEthClosedData.map((candle) => [candle.timestamp, candle])
32400
32720
  );
32401
- const runtimeStrategies = await (0, import_runtimeStrategies3.loadResolvedRuntimeStrategies)({
32721
+ const runtimeStrategies = await (0, import_runtimeStrategies5.loadResolvedRuntimeStrategies)({
32402
32722
  userName: config2.userName,
32403
32723
  projectRoot: projectRoot15,
32404
32724
  deploymentId: deployment?.id ?? "",
@@ -32612,7 +32932,7 @@ var init_runner = __esm({
32612
32932
  }
32613
32933
  };
32614
32934
  const loadConfiguredSignalsScopes = async (deployment) => {
32615
- const configuredStrategies = await (0, import_runtimeStrategies3.loadResolvedRuntimeStrategies)({
32935
+ const configuredStrategies = await (0, import_runtimeStrategies5.loadResolvedRuntimeStrategies)({
32616
32936
  userName: config2.userName,
32617
32937
  projectRoot: projectRoot15,
32618
32938
  deploymentId: deployment.id
@@ -32635,7 +32955,7 @@ var init_runner = __esm({
32635
32955
  if (!config2.deploymentId) {
32636
32956
  throw new Error("Runtime deployment id is required");
32637
32957
  }
32638
- const configuredDeployment = await (0, import_runtimeStrategies4.getRuntimeDeployment)({
32958
+ const configuredDeployment = await (0, import_runtimeStrategies6.getRuntimeDeployment)({
32639
32959
  userName: config2.userName,
32640
32960
  projectRoot: projectRoot15,
32641
32961
  deploymentId: config2.deploymentId
@@ -32669,7 +32989,7 @@ var init_runner = __esm({
32669
32989
  throw new Error("Runtime deployment id is required");
32670
32990
  }
32671
32991
  const deploymentId = config2.deploymentId;
32672
- const initialDeployment = await (0, import_runtimeStrategies4.getRuntimeDeployment)({
32992
+ const initialDeployment = await (0, import_runtimeStrategies6.getRuntimeDeployment)({
32673
32993
  userName: config2.userName,
32674
32994
  projectRoot: projectRoot15,
32675
32995
  deploymentId
@@ -32694,7 +33014,7 @@ var init_runner = __esm({
32694
33014
  settleDelayMs: resolveNonNegativeInteger(config2.settleDelayMs, 5e3),
32695
33015
  signal: abortController.signal,
32696
33016
  runCycle: async () => {
32697
- const currentDeployment = await (0, import_runtimeStrategies4.getRuntimeDeployment)({
33017
+ const currentDeployment = await (0, import_runtimeStrategies6.getRuntimeDeployment)({
32698
33018
  userName: config2.userName,
32699
33019
  projectRoot: projectRoot15,
32700
33020
  deploymentId
@@ -32899,7 +33219,7 @@ var init_signalsDaemon = __esm({
32899
33219
  });
32900
33220
 
32901
33221
  // src/lib/replay/cliConfig.ts
32902
- var import_args32, REPLAY_UNSUPPORTED_FLAGS, hasCliFlag2, assertNoUnsupportedReplayFlags, replayNormalizedArgv, replayFlags, replayInterval, replayUserName, replayProjectRoot, replayRuntimeEvidencePath, isReplayUpdateOnlyRun;
33222
+ var import_args32, REPLAY_UNSUPPORTED_FLAGS, hasCliFlag2, assertNoUnsupportedReplayFlags, replayNormalizedArgv, replayFlags, replayInterval, replayUserName, replayProjectRoot, replayRuntimeEvidencePath, replayDeploymentId, isReplayUpdateOnlyRun;
32903
33223
  var init_cliConfig2 = __esm({
32904
33224
  "src/lib/replay/cliConfig.ts"() {
32905
33225
  "use strict";
@@ -32952,6 +33272,11 @@ var init_cliConfig2 = __esm({
32952
33272
  "runtimeEvidence",
32953
33273
  "Immutable runtime evidence JSON used for offline runtime comparison"
32954
33274
  );
33275
+ import_args32.default.option(
33276
+ "deployment",
33277
+ "Git-owned runtime deployment used when runtime evidence is omitted",
33278
+ "production"
33279
+ );
32955
33280
  import_args32.default.option(["u", "updateOnly"], "Only update tickers history", false);
32956
33281
  import_args32.default.option(["C", "cacheOnly"], "Do not update tickers history", false);
32957
33282
  import_args32.default.option(
@@ -32978,10 +33303,203 @@ var init_cliConfig2 = __esm({
32978
33303
  replayUserName = String(replayFlags.user ?? "root");
32979
33304
  replayProjectRoot = String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
32980
33305
  replayRuntimeEvidencePath = typeof replayFlags.runtimeEvidence === "string" && replayFlags.runtimeEvidence.trim() ? replayFlags.runtimeEvidence.trim() : null;
33306
+ replayDeploymentId = String(
33307
+ replayFlags.deployment ?? "production"
33308
+ ).trim();
32981
33309
  isReplayUpdateOnlyRun = Boolean(replayFlags.updateOnly);
32982
33310
  }
32983
33311
  });
32984
33312
 
33313
+ // src/lib/replay/runtimeEvidenceSource.ts
33314
+ var import_promises29, import_node_path24, asRecord7, asFiniteNumber, unwrapRows2, assertRuntimeRowsMatchDeployment, readRuntimeEvidenceArtifact, loadReplayRuntimeEvidenceMetadata, buildRuntimeEvidenceLineageScopes, loadReplayRuntimeEvidenceSource;
33315
+ var init_runtimeEvidenceSource = __esm({
33316
+ "src/lib/replay/runtimeEvidenceSource.ts"() {
33317
+ "use strict";
33318
+ import_promises29 = __toESM(require("fs/promises"));
33319
+ import_node_path24 = __toESM(require("path"));
33320
+ init_runtimeLineage();
33321
+ init_runtimeEvidenceDeployment();
33322
+ asRecord7 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
33323
+ asFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
33324
+ unwrapRows2 = (value, field) => (Array.isArray(value) ? value : []).map((item) => {
33325
+ const row = asRecord7(item);
33326
+ return asRecord7(row?.[field]) ?? row;
33327
+ }).filter((item) => item != null);
33328
+ assertRuntimeRowsMatchDeployment = ({
33329
+ label,
33330
+ rows,
33331
+ deployment
33332
+ }) => {
33333
+ const activeStrategies = new Set(
33334
+ activeRuntimeEvidenceStrategies(deployment).map(
33335
+ ({ strategyName }) => strategyName
33336
+ )
33337
+ );
33338
+ for (const row of rows) {
33339
+ if (row.deploymentId !== deployment.id || row.accountId !== deployment.accountId || !row.strategy || !activeStrategies.has(row.strategy)) {
33340
+ throw new Error(
33341
+ `Runtime evidence ${label} row is outside embedded deployment composition`
33342
+ );
33343
+ }
33344
+ }
33345
+ };
33346
+ readRuntimeEvidenceArtifact = async ({
33347
+ filePath,
33348
+ projectRoot: projectRoot15
33349
+ }) => {
33350
+ const resolvedPath = import_node_path24.default.resolve(projectRoot15, filePath);
33351
+ const root = asRecord7(
33352
+ JSON.parse(await import_promises29.default.readFile(resolvedPath, "utf8"))
33353
+ );
33354
+ const runtime = asRecord7(root?.runtime) ?? root;
33355
+ if (!root || !runtime) {
33356
+ throw new Error(`Invalid runtime evidence JSON: ${resolvedPath}`);
33357
+ }
33358
+ const rootWindow = asRecord7(root.window);
33359
+ const runtimeWindow2 = asRecord7(runtime.window);
33360
+ return {
33361
+ resolvedPath,
33362
+ root,
33363
+ runtime,
33364
+ userName: String(root.userName ?? runtime.userName ?? "").trim(),
33365
+ startTime: asFiniteNumber(rootWindow?.startTime) ?? asFiniteNumber(runtimeWindow2?.startTime),
33366
+ endTime: asFiniteNumber(rootWindow?.endTime) ?? asFiniteNumber(runtimeWindow2?.endTime),
33367
+ deployment: parseRuntimeEvidenceDeploymentSnapshot(root.deployment)
33368
+ };
33369
+ };
33370
+ loadReplayRuntimeEvidenceMetadata = async (params) => {
33371
+ const artifact = await readRuntimeEvidenceArtifact(params);
33372
+ return {
33373
+ path: artifact.resolvedPath,
33374
+ userName: artifact.userName,
33375
+ startTime: artifact.startTime,
33376
+ endTime: artifact.endTime,
33377
+ deployment: artifact.deployment
33378
+ };
33379
+ };
33380
+ buildRuntimeEvidenceLineageScopes = ({
33381
+ signals: signals2,
33382
+ evaluations
33383
+ }) => {
33384
+ const scopes = /* @__PURE__ */ new Map();
33385
+ const add = ({
33386
+ strategy,
33387
+ symbol,
33388
+ deploymentId,
33389
+ accountId,
33390
+ runtimeConfigId,
33391
+ runtimeVersion,
33392
+ timestamp,
33393
+ lineage
33394
+ }) => {
33395
+ if (!lineage || !Number.isFinite(timestamp)) {
33396
+ return;
33397
+ }
33398
+ const key = `${deploymentId ?? "default"}::${accountId ?? "default"}::${strategy}::${symbol}::${runtimeLineageKey(lineage)}`;
33399
+ const existing = scopes.get(key);
33400
+ scopes.set(key, {
33401
+ strategy,
33402
+ symbol,
33403
+ ...deploymentId ? { deploymentId } : {},
33404
+ ...accountId ? { accountId } : {},
33405
+ ...runtimeConfigId ? { runtimeConfigId } : {},
33406
+ ...runtimeVersion ? { runtimeVersion } : {},
33407
+ lineage,
33408
+ firstTimestamp: existing ? Math.min(existing.firstTimestamp, timestamp) : timestamp,
33409
+ lastTimestamp: existing ? Math.max(existing.lastTimestamp, timestamp) : timestamp
33410
+ });
33411
+ };
33412
+ for (const signal of signals2) {
33413
+ add({
33414
+ strategy: signal.strategy,
33415
+ symbol: signal.symbol,
33416
+ deploymentId: signal.deploymentId,
33417
+ accountId: signal.accountId,
33418
+ runtimeConfigId: signal.runtimeConfigId,
33419
+ runtimeVersion: signal.runtimeVersion,
33420
+ timestamp: signal.timestamp,
33421
+ lineage: signal.runtimeLineage
33422
+ });
33423
+ }
33424
+ for (const evaluation of evaluations) {
33425
+ add({
33426
+ strategy: evaluation.strategy,
33427
+ symbol: evaluation.symbol,
33428
+ deploymentId: evaluation.deploymentId,
33429
+ accountId: evaluation.accountId,
33430
+ runtimeConfigId: evaluation.runtimeConfigId,
33431
+ runtimeVersion: evaluation.runtimeVersion,
33432
+ timestamp: evaluation.timestamp,
33433
+ lineage: evaluation.runtimeLineage
33434
+ });
33435
+ }
33436
+ return [...scopes.values()].sort(
33437
+ (left, right) => left.strategy.localeCompare(right.strategy) || left.symbol.localeCompare(right.symbol) || left.firstTimestamp - right.firstTimestamp
33438
+ );
33439
+ };
33440
+ loadReplayRuntimeEvidenceSource = async ({
33441
+ filePath,
33442
+ projectRoot: projectRoot15,
33443
+ expectedUserName,
33444
+ expectedWindow
33445
+ }) => {
33446
+ const { resolvedPath, runtime, userName: userName3, startTime, endTime, deployment } = await readRuntimeEvidenceArtifact({ filePath, projectRoot: projectRoot15 });
33447
+ if (!userName3 || userName3 !== expectedUserName) {
33448
+ throw new Error(
33449
+ `Runtime evidence user mismatch: expected=${expectedUserName}, actual=${userName3 || "missing"}`
33450
+ );
33451
+ }
33452
+ const endTimeMatches = endTime === expectedWindow.end || endTime === expectedWindow.end + 1;
33453
+ if (startTime !== expectedWindow.start || !endTimeMatches) {
33454
+ throw new Error(
33455
+ `Runtime evidence window mismatch: expected=${expectedWindow.start}..${expectedWindow.end}, actual=${startTime ?? "missing"}..${endTime ?? "missing"}`
33456
+ );
33457
+ }
33458
+ const trades = unwrapRows2(runtime.trades, "trade");
33459
+ const signals2 = unwrapRows2(runtime.signals, "signal");
33460
+ const evaluations = unwrapRows2(
33461
+ runtime.evaluations,
33462
+ "evaluation"
33463
+ );
33464
+ const storedLineageScopes = unwrapRows2(
33465
+ runtime.lineageScopes,
33466
+ "lineageScope"
33467
+ );
33468
+ assertRuntimeRowsMatchDeployment({
33469
+ label: "trade",
33470
+ rows: trades,
33471
+ deployment
33472
+ });
33473
+ assertRuntimeRowsMatchDeployment({
33474
+ label: "signal",
33475
+ rows: signals2,
33476
+ deployment
33477
+ });
33478
+ assertRuntimeRowsMatchDeployment({
33479
+ label: "evaluation",
33480
+ rows: evaluations,
33481
+ deployment
33482
+ });
33483
+ assertRuntimeRowsMatchDeployment({
33484
+ label: "lineage scope",
33485
+ rows: storedLineageScopes,
33486
+ deployment
33487
+ });
33488
+ return {
33489
+ path: resolvedPath,
33490
+ userName: userName3,
33491
+ startTime,
33492
+ endTime,
33493
+ trades,
33494
+ signals: signals2,
33495
+ evaluations,
33496
+ lineageScopes: storedLineageScopes.length ? storedLineageScopes : buildRuntimeEvidenceLineageScopes({ signals: signals2, evaluations }),
33497
+ deployment
33498
+ };
33499
+ };
33500
+ }
33501
+ });
33502
+
32985
33503
  // src/lib/replay/portfolioReplayConnector.ts
32986
33504
  var import_constants17, import_math, import_trade2, UNKNOWN_STRATEGY, getStrategyName, getPositionPnlSnapshot, createPortfolioReplayConnector;
32987
33505
  var init_portfolioReplayConnector = __esm({
@@ -33393,7 +33911,7 @@ var init_portfolioReplayConnector = __esm({
33393
33911
  });
33394
33912
 
33395
33913
  // src/lib/replay/historicalSignalsReplay.ts
33396
- var import_chalk46, import_progress12, import_backtest4, import_strategies13, import_time9, import_logger11, import_redis19, import_connectors17, import_cli13, import_strategies14, loadRuntimeStrategies, loadReferenceConnector, buildPreparedData, buildAfterSignalsContext, buildSymbolSharedReplayKey, runHistoricalSignalsReplay;
33914
+ var import_chalk46, import_progress12, import_backtest4, import_strategies13, import_time9, import_logger11, import_redis19, import_connectors17, import_cli13, import_strategies14, import_runtimeStrategies8, loadRuntimeStrategies, loadReferenceConnector, buildPreparedData, buildAfterSignalsContext, buildSymbolSharedReplayKey, runHistoricalSignalsReplay;
33397
33915
  var init_historicalSignalsReplay = __esm({
33398
33916
  "src/lib/replay/historicalSignalsReplay.ts"() {
33399
33917
  "use strict";
@@ -33407,6 +33925,7 @@ var init_historicalSignalsReplay = __esm({
33407
33925
  import_connectors17 = require("@tradejs/node/connectors");
33408
33926
  import_cli13 = require("@tradejs/node/cli");
33409
33927
  import_strategies14 = require("@tradejs/node/strategies");
33928
+ import_runtimeStrategies8 = require("@tradejs/node/runtimeStrategies");
33410
33929
  init_cliConfig2();
33411
33930
  init_support();
33412
33931
  init_portfolioReplayConnector();
@@ -33415,7 +33934,15 @@ var init_historicalSignalsReplay = __esm({
33415
33934
  init_runtimeLineage();
33416
33935
  loadRuntimeStrategies = async (runtimeStrategies) => {
33417
33936
  const strategies = await Promise.all(
33418
- runtimeStrategies.map(async ({ strategyName, strategyConfig }) => {
33937
+ runtimeStrategies.map(async (runtimeStrategy) => {
33938
+ const {
33939
+ strategyName,
33940
+ version,
33941
+ strategyPackage,
33942
+ strategyPackageVersion,
33943
+ runtimePackageVersion,
33944
+ strategyConfig
33945
+ } = runtimeStrategy;
33419
33946
  const strategyCreator = await (0, import_strategies14.getStrategyCreator)(
33420
33947
  strategyName,
33421
33948
  replayProjectRoot
@@ -33423,12 +33950,25 @@ var init_historicalSignalsReplay = __esm({
33423
33950
  if (!strategyCreator) {
33424
33951
  throw new Error(`Unknown strategy: ${strategyName}`);
33425
33952
  }
33953
+ const installed = await (0, import_runtimeStrategies8.getRuntimeStrategyPackageMetadata)({
33954
+ strategyName,
33955
+ projectRoot: replayProjectRoot
33956
+ });
33957
+ if (installed.strategyPackage !== strategyPackage || installed.strategyPackageVersion !== strategyPackageVersion || installed.runtimePackageVersion !== runtimePackageVersion) {
33958
+ throw new Error(
33959
+ `Runtime evidence package mismatch for ${strategyName}: expected=${strategyPackage}@${strategyPackageVersion}/@tradejs/node@${runtimePackageVersion}, installed=${installed.strategyPackage ?? "missing"}@${installed.strategyPackageVersion ?? "missing"}/@tradejs/node@${installed.runtimePackageVersion ?? "missing"}`
33960
+ );
33961
+ }
33426
33962
  const strategyResults = await (0, import_redis19.getData)(
33427
33963
  import_redis19.redisKeys.strategyResults(replayUserName, strategyName),
33428
33964
  {}
33429
33965
  );
33430
33966
  return {
33431
33967
  strategyName,
33968
+ version,
33969
+ strategyPackage,
33970
+ strategyPackageVersion,
33971
+ runtimePackageVersion,
33432
33972
  strategyCreator,
33433
33973
  strategyConfig,
33434
33974
  strategyResults
@@ -33622,6 +34162,9 @@ var init_historicalSignalsReplay = __esm({
33622
34162
  loadedStrategies.map(
33623
34163
  async ({
33624
34164
  strategyName,
34165
+ version,
34166
+ strategyPackageVersion,
34167
+ runtimePackageVersion,
33625
34168
  strategyCreator,
33626
34169
  strategyConfig,
33627
34170
  strategyResults
@@ -33629,8 +34172,11 @@ var init_historicalSignalsReplay = __esm({
33629
34172
  const runtimeLineage = await buildRuntimeLineage({
33630
34173
  projectRoot: replayProjectRoot,
33631
34174
  strategyName,
34175
+ version,
34176
+ strategyPackageVersion,
34177
+ runtimePackageVersion,
33632
34178
  config: {
33633
- configId: "config",
34179
+ configId: `v${version}`,
33634
34180
  strategyConfig,
33635
34181
  symbolResultConfig: strategyResults?.[symbol]?.config ?? null
33636
34182
  },
@@ -33643,6 +34189,8 @@ var init_historicalSignalsReplay = __esm({
33643
34189
  runtimeLineages.push({
33644
34190
  strategy: strategyName,
33645
34191
  symbol,
34192
+ ...preparedRun.deploymentId ? { deploymentId: preparedRun.deploymentId } : {},
34193
+ ...preparedRun.accountId ? { accountId: preparedRun.accountId } : {},
33646
34194
  lineage: runtimeLineage
33647
34195
  });
33648
34196
  return {
@@ -33652,8 +34200,11 @@ var init_historicalSignalsReplay = __esm({
33652
34200
  run: await strategyCreator({
33653
34201
  userName: replayUserName,
33654
34202
  connectorName,
33655
- runtimeConfigId: "config",
34203
+ runtimeConfigId: `v${version}`,
33656
34204
  runtimeLineage,
34205
+ universe: preparedRun.universe,
34206
+ accountId: preparedRun.accountId,
34207
+ deploymentId: preparedRun.deploymentId,
33657
34208
  config: buildReplayStrategyConfig({
33658
34209
  strategyConfig,
33659
34210
  interval: interval4
@@ -35006,125 +35557,6 @@ var init_runtimeParityDetails = __esm({
35006
35557
  }
35007
35558
  });
35008
35559
 
35009
- // src/lib/replay/runtimeEvidenceSource.ts
35010
- var import_promises29, import_node_path24, asRecord7, asFiniteNumber, unwrapRows2, buildRuntimeEvidenceLineageScopes, loadReplayRuntimeEvidenceSource;
35011
- var init_runtimeEvidenceSource = __esm({
35012
- "src/lib/replay/runtimeEvidenceSource.ts"() {
35013
- "use strict";
35014
- import_promises29 = __toESM(require("fs/promises"));
35015
- import_node_path24 = __toESM(require("path"));
35016
- init_runtimeLineage();
35017
- asRecord7 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
35018
- asFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
35019
- unwrapRows2 = (value, field) => (Array.isArray(value) ? value : []).map((item) => {
35020
- const row = asRecord7(item);
35021
- return asRecord7(row?.[field]) ?? row;
35022
- }).filter((item) => item != null);
35023
- buildRuntimeEvidenceLineageScopes = ({
35024
- signals: signals2,
35025
- evaluations
35026
- }) => {
35027
- const scopes = /* @__PURE__ */ new Map();
35028
- const add = ({
35029
- strategy,
35030
- symbol,
35031
- runtimeConfigId,
35032
- runtimeVersion,
35033
- timestamp,
35034
- lineage
35035
- }) => {
35036
- if (!lineage || !Number.isFinite(timestamp)) {
35037
- return;
35038
- }
35039
- const key = `${strategy}::${symbol}::${runtimeLineageKey(lineage)}`;
35040
- const existing = scopes.get(key);
35041
- scopes.set(key, {
35042
- strategy,
35043
- symbol,
35044
- ...runtimeConfigId ? { runtimeConfigId } : {},
35045
- ...runtimeVersion ? { runtimeVersion } : {},
35046
- lineage,
35047
- firstTimestamp: existing ? Math.min(existing.firstTimestamp, timestamp) : timestamp,
35048
- lastTimestamp: existing ? Math.max(existing.lastTimestamp, timestamp) : timestamp
35049
- });
35050
- };
35051
- for (const signal of signals2) {
35052
- add({
35053
- strategy: signal.strategy,
35054
- symbol: signal.symbol,
35055
- runtimeConfigId: signal.runtimeConfigId,
35056
- runtimeVersion: signal.runtimeVersion,
35057
- timestamp: signal.timestamp,
35058
- lineage: signal.runtimeLineage
35059
- });
35060
- }
35061
- for (const evaluation of evaluations) {
35062
- add({
35063
- strategy: evaluation.strategy,
35064
- symbol: evaluation.symbol,
35065
- runtimeConfigId: evaluation.runtimeConfigId,
35066
- runtimeVersion: evaluation.runtimeVersion,
35067
- timestamp: evaluation.timestamp,
35068
- lineage: evaluation.runtimeLineage
35069
- });
35070
- }
35071
- return [...scopes.values()].sort(
35072
- (left, right) => left.strategy.localeCompare(right.strategy) || left.symbol.localeCompare(right.symbol) || left.firstTimestamp - right.firstTimestamp
35073
- );
35074
- };
35075
- loadReplayRuntimeEvidenceSource = async ({
35076
- filePath,
35077
- projectRoot: projectRoot15,
35078
- expectedUserName,
35079
- expectedWindow
35080
- }) => {
35081
- const resolvedPath = import_node_path24.default.resolve(projectRoot15, filePath);
35082
- const root = asRecord7(
35083
- JSON.parse(await import_promises29.default.readFile(resolvedPath, "utf8"))
35084
- );
35085
- const runtime = asRecord7(root?.runtime) ?? root;
35086
- if (!root || !runtime) {
35087
- throw new Error(`Invalid runtime evidence JSON: ${resolvedPath}`);
35088
- }
35089
- const rootWindow = asRecord7(root.window);
35090
- const runtimeWindow2 = asRecord7(runtime.window);
35091
- const startTime = asFiniteNumber(rootWindow?.startTime) ?? asFiniteNumber(runtimeWindow2?.startTime);
35092
- const endTime = asFiniteNumber(rootWindow?.endTime) ?? asFiniteNumber(runtimeWindow2?.endTime);
35093
- const userName3 = String(root.userName ?? runtime.userName ?? "").trim();
35094
- if (!userName3 || userName3 !== expectedUserName) {
35095
- throw new Error(
35096
- `Runtime evidence user mismatch: expected=${expectedUserName}, actual=${userName3 || "missing"}`
35097
- );
35098
- }
35099
- const endTimeMatches = endTime === expectedWindow.end || endTime === expectedWindow.end + 1;
35100
- if (startTime !== expectedWindow.start || !endTimeMatches) {
35101
- throw new Error(
35102
- `Runtime evidence window mismatch: expected=${expectedWindow.start}..${expectedWindow.end}, actual=${startTime ?? "missing"}..${endTime ?? "missing"}`
35103
- );
35104
- }
35105
- const trades = unwrapRows2(runtime.trades, "trade");
35106
- const signals2 = unwrapRows2(runtime.signals, "signal");
35107
- const evaluations = unwrapRows2(
35108
- runtime.evaluations,
35109
- "evaluation"
35110
- );
35111
- return {
35112
- path: resolvedPath,
35113
- userName: userName3,
35114
- startTime,
35115
- endTime,
35116
- trades,
35117
- signals: signals2,
35118
- evaluations,
35119
- lineageScopes: buildRuntimeEvidenceLineageScopes({
35120
- signals: signals2,
35121
- evaluations
35122
- })
35123
- };
35124
- };
35125
- }
35126
- });
35127
-
35128
35560
  // src/lib/replay/runtimeComparison.ts
35129
35561
  var import_chalk48, import_time11, getReplayEntryTimestampCompareOffsetMs, buildReplaySignalEvaluations, lineageScopeKey, buildExpectedLineageByScope, hasExpectedLineage, filterReplayComparisonByLineage, formatDrilldownSummary, toFiniteNumberOrNull4, sumOptionalNumbers, firstFiniteNumber, firstString, summarizeBacktestPnlByStrategy, getExchangeExecutionMergeKey, mergeExchangeExecutionGroup, mergeExchangeEntryExecutions, loadExchangeEntryRows, buildPriceDeltaPct3, loadExchangeEntriesForComparison, compareExchangeEntriesToBacktest, findExchangeRuntimeOutcome, splitExchangeMatchesByRuntimeOrderStatus, saveAndPrintReplayExchangeComparison, saveAndPrintReplayRuntimeComparison;
35130
35562
  var init_runtimeComparison = __esm({
@@ -35160,27 +35592,40 @@ var init_runtimeComparison = __esm({
35160
35592
  reason: signal.orderSkipReason || signal.orderStatus || "SIGNAL",
35161
35593
  signalId: signal.signalId,
35162
35594
  direction: signal.direction,
35595
+ accountId: signal.accountId,
35596
+ deploymentId: signal.deploymentId,
35163
35597
  runtimeLineage: signal.runtimeLineage,
35164
35598
  orderStatus: signal.orderStatus,
35165
35599
  orderSkipReason: signal.orderSkipReason,
35166
35600
  ...signal.aiAnalysis ? { aiAnalysis: signal.aiAnalysis } : {},
35167
35601
  ...signal.ml ? { ml: signal.ml } : {}
35168
35602
  }));
35169
- lineageScopeKey = (strategy, symbol) => `${strategy}::${symbol}`;
35603
+ lineageScopeKey = ({
35604
+ strategy,
35605
+ symbol,
35606
+ deploymentId,
35607
+ accountId
35608
+ }) => `${deploymentId ?? "default-deployment"}::${accountId ?? "default-account"}::${strategy}::${symbol}`;
35170
35609
  buildExpectedLineageByScope = (replayLineages) => new Map(
35171
- replayLineages.map((record) => [
35172
- lineageScopeKey(record.strategy, record.symbol),
35173
- record.lineage
35174
- ])
35610
+ replayLineages.map((record) => [lineageScopeKey(record), record.lineage])
35175
35611
  );
35176
35612
  hasExpectedLineage = ({
35177
35613
  strategy,
35178
35614
  symbol,
35615
+ deploymentId,
35616
+ accountId,
35179
35617
  lineage,
35180
35618
  expectedByScope
35181
- }) => runtimeLineagesMatch(
35619
+ }) => runtimeLineagesComparable(
35182
35620
  lineage,
35183
- expectedByScope.get(lineageScopeKey(strategy, symbol))
35621
+ expectedByScope.get(
35622
+ lineageScopeKey({
35623
+ strategy,
35624
+ symbol,
35625
+ deploymentId,
35626
+ accountId
35627
+ })
35628
+ )
35184
35629
  );
35185
35630
  filterReplayComparisonByLineage = ({
35186
35631
  replayLineages,
@@ -35195,6 +35640,8 @@ var init_runtimeComparison = __esm({
35195
35640
  (signal) => hasExpectedLineage({
35196
35641
  strategy: signal.strategy,
35197
35642
  symbol: signal.symbol,
35643
+ deploymentId: signal.deploymentId,
35644
+ accountId: signal.accountId,
35198
35645
  lineage: signal.runtimeLineage,
35199
35646
  expectedByScope
35200
35647
  })
@@ -35203,6 +35650,8 @@ var init_runtimeComparison = __esm({
35203
35650
  (evaluation) => hasExpectedLineage({
35204
35651
  strategy: evaluation.strategy,
35205
35652
  symbol: evaluation.symbol,
35653
+ deploymentId: evaluation.deploymentId,
35654
+ accountId: evaluation.accountId,
35206
35655
  lineage: evaluation.runtimeLineage,
35207
35656
  expectedByScope
35208
35657
  })
@@ -35211,13 +35660,15 @@ var init_runtimeComparison = __esm({
35211
35660
  (scope) => hasExpectedLineage({
35212
35661
  strategy: scope.strategy,
35213
35662
  symbol: scope.symbol,
35663
+ deploymentId: scope.deploymentId,
35664
+ accountId: scope.accountId,
35214
35665
  lineage: scope.lineage,
35215
35666
  expectedByScope
35216
35667
  })
35217
35668
  );
35218
35669
  const deploymentWindows = /* @__PURE__ */ new Map();
35219
35670
  for (const scope of comparableRuntimeLineageScopes) {
35220
- const key = lineageScopeKey(scope.strategy, scope.symbol);
35671
+ const key = lineageScopeKey(scope);
35221
35672
  const existing = deploymentWindows.get(key);
35222
35673
  deploymentWindows.set(key, {
35223
35674
  firstTimestamp: existing == null ? scope.firstTimestamp : Math.min(existing.firstTimestamp, scope.firstTimestamp),
@@ -35241,13 +35692,13 @@ var init_runtimeComparison = __esm({
35241
35692
  return hasExpectedLineage({
35242
35693
  strategy: trade.strategy,
35243
35694
  symbol: trade.symbol,
35695
+ deploymentId: trade.deploymentId,
35696
+ accountId: trade.accountId,
35244
35697
  lineage,
35245
35698
  expectedByScope
35246
35699
  });
35247
35700
  }
35248
- const window = deploymentWindows.get(
35249
- lineageScopeKey(trade.strategy, trade.symbol)
35250
- );
35701
+ const window = deploymentWindows.get(lineageScopeKey(trade));
35251
35702
  const signalTimestamp = trade.signalTimestamp ?? trade.entryTimestamp;
35252
35703
  return window != null && signalTimestamp >= window.firstTimestamp && signalTimestamp <= window.lastTimestamp;
35253
35704
  });
@@ -35255,17 +35706,24 @@ var init_runtimeComparison = __esm({
35255
35706
  ...comparableRuntimeSignals,
35256
35707
  ...comparableRuntimeEvaluations
35257
35708
  ]) {
35258
- const key = lineageScopeKey(artifact.strategy, artifact.symbol);
35709
+ const key = lineageScopeKey(artifact);
35259
35710
  const existing = deploymentWindows.get(key);
35260
35711
  deploymentWindows.set(key, {
35261
35712
  firstTimestamp: existing == null ? artifact.timestamp : Math.min(existing.firstTimestamp, artifact.timestamp),
35262
35713
  lastTimestamp: existing == null ? artifact.timestamp : Math.max(existing.lastTimestamp, artifact.timestamp)
35263
35714
  });
35264
35715
  }
35716
+ const replayScopeByStrategySymbol = new Map(
35717
+ replayLineages.map((record) => [
35718
+ `${record.strategy}::${record.symbol}`,
35719
+ lineageScopeKey(record)
35720
+ ])
35721
+ );
35265
35722
  const comparableBacktestEntries = backtestEntries.filter((entry) => {
35266
- const window = deploymentWindows.get(
35267
- lineageScopeKey(entry.strategy, entry.symbol)
35723
+ const replayScope = replayScopeByStrategySymbol.get(
35724
+ `${entry.strategy}::${entry.symbol}`
35268
35725
  );
35726
+ const window = replayScope ? deploymentWindows.get(replayScope) : null;
35269
35727
  const signalTimestamp = entry.signalTimestamp ?? entry.timestamp;
35270
35728
  return window != null && signalTimestamp >= window.firstTimestamp && signalTimestamp <= window.lastTimestamp;
35271
35729
  });
@@ -35287,10 +35745,12 @@ var init_runtimeComparison = __esm({
35287
35745
  excludedBacktestEntries: backtestEntries.length - comparableBacktestEntries.length,
35288
35746
  reason: comparableScopeKeys.size > 0 ? null : "no_runtime_artifacts_with_matching_lineage",
35289
35747
  replay: [...expectedByScope.entries()].map(([scope, lineage]) => {
35290
- const separator = scope.indexOf("::");
35748
+ const [deploymentId, accountId, strategy, symbol] = scope.split("::");
35291
35749
  return {
35292
- strategy: scope.slice(0, separator),
35293
- symbol: scope.slice(separator + 2),
35750
+ deploymentId,
35751
+ accountId,
35752
+ strategy,
35753
+ symbol,
35294
35754
  lineage
35295
35755
  };
35296
35756
  }).sort(
@@ -36602,6 +37062,8 @@ var init_replayRunner = __esm({
36602
37062
  init_marketContextPrepare();
36603
37063
  init_runState();
36604
37064
  init_cliConfig2();
37065
+ init_runtimeEvidenceSource();
37066
+ init_runtimeEvidenceDeployment();
36605
37067
  init_support();
36606
37068
  init_historicalSignalsReplay();
36607
37069
  init_chartSnapshot();
@@ -36725,10 +37187,69 @@ var init_replayRunner = __esm({
36725
37187
  };
36726
37188
  replayBacktest = async () => {
36727
37189
  resetRunState();
37190
+ const evidenceMetadata = replayRuntimeEvidencePath ? await loadReplayRuntimeEvidenceMetadata({
37191
+ filePath: replayRuntimeEvidencePath,
37192
+ projectRoot: replayProjectRoot
37193
+ }) : null;
37194
+ if (evidenceMetadata && evidenceMetadata.userName !== replayUserName) {
37195
+ throw new Error(
37196
+ `Runtime evidence user mismatch: expected=${replayUserName}, actual=${evidenceMetadata.userName || "missing"}`
37197
+ );
37198
+ }
37199
+ const replayComposition = evidenceMetadata ? {
37200
+ deployment: runtimeDeploymentFromEvidence(evidenceMetadata.deployment),
37201
+ strategies: activeRuntimeEvidenceStrategies(
37202
+ evidenceMetadata.deployment
37203
+ ).map(
37204
+ ({
37205
+ strategyName,
37206
+ version,
37207
+ strategyPackage,
37208
+ strategyPackageVersion,
37209
+ runtimePackageVersion,
37210
+ strategyConfig
37211
+ }) => ({
37212
+ strategyName,
37213
+ version,
37214
+ strategyPackage,
37215
+ strategyPackageVersion,
37216
+ runtimePackageVersion,
37217
+ strategyConfig
37218
+ })
37219
+ )
37220
+ } : await loadDeploymentReplayStrategies({
37221
+ userName: replayUserName,
37222
+ projectRoot: replayProjectRoot,
37223
+ deploymentId: replayDeploymentId
37224
+ });
37225
+ const { deployment, strategies: replayStrategies } = replayComposition;
37226
+ if (!replayStrategies.length) {
37227
+ throw new Error(`No enabled strategies in deployment ${deployment.id}`);
37228
+ }
37229
+ const strategyIntervals = new Set(
37230
+ replayStrategies.map(
37231
+ ({ strategyConfig }) => String(strategyConfig.INTERVAL)
37232
+ )
37233
+ );
37234
+ if (strategyIntervals.size !== 1 || !strategyIntervals.has(replayInterval)) {
37235
+ throw new Error(
37236
+ `Replay interval does not match deployment ${deployment.id}: replay=${replayInterval}, strategies=${[...strategyIntervals].join(",")}`
37237
+ );
37238
+ }
37239
+ const strategyUniverses = new Set(
37240
+ replayStrategies.map(
37241
+ ({ strategyConfig }) => String(strategyConfig.UNIVERSE)
37242
+ )
37243
+ );
37244
+ if (strategyUniverses.size !== 1) {
37245
+ throw new Error(
37246
+ `Replay requires one deployment universe: ${[...strategyUniverses].join(",")}`
37247
+ );
37248
+ }
36728
37249
  const preparedRun = await prepareRunEnvironment({
36729
- connector: replayFlags.connector,
37250
+ connector: deployment.connectorName,
36730
37251
  userName: replayUserName,
36731
- tickers: replayFlags.tickers,
37252
+ tickers: replayFlags.tickers ?? deployment.tickers,
36732
37253
  exclude: replayFlags.exclude,
36733
37254
  tickersLimit: replayFlags.tickersLimit,
36734
37255
  showTickersList: replayFlags.showTickersList,
@@ -36738,6 +37259,11 @@ var init_replayRunner = __esm({
36738
37259
  cacheOnly: replayFlags.cacheOnly,
36739
37260
  interval: replayInterval,
36740
37261
  projectRoot: replayProjectRoot,
37262
+ universe: [...strategyUniverses][0],
37263
+ accountId: deployment.accountId,
37264
+ deploymentId: deployment.id,
37265
+ assetClasses: deployment.assetClasses,
37266
+ deployment,
36741
37267
  closedIntervalMs: (0, import_data5.intervalToMs)(replayInterval)
36742
37268
  });
36743
37269
  if (!preparedRun || isReplayUpdateOnlyRun) {
@@ -36751,10 +37277,6 @@ var init_replayRunner = __esm({
36751
37277
  end: preparedRun.window.end
36752
37278
  }
36753
37279
  });
36754
- const replayStrategies = await loadReplayStrategies(replayUserName);
36755
- if (!replayStrategies.length) {
36756
- return;
36757
- }
36758
37280
  await prepareReplayBinanceMarketContext({
36759
37281
  ...preparedRun,
36760
37282
  aiEnabled: replayStrategies.some(
@@ -36812,7 +37334,7 @@ __export(signalsSummary_exports, {
36812
37334
  main: () => main44,
36813
37335
  signalsSummary: () => signalsSummary
36814
37336
  });
36815
- var import_config13, import_args33, import_logger12, import_connectors18, import_runtimeStrategies6, flags30, projectRoot14, SUMMARY_TIMEZONE2, SUMMARY_TIMEZONE_LABEL2, escapeHtml6, formatMskDateTime3, formatSigned3, formatPnlMoneyText, formatWinRateText, resolveSummaryTitle, normalizeStatus, shouldAttachDebugReport, getSummaryDateParts, formatSummaryIsoDayKey, unescapeHtml, stripTelegramHtml, buildSignalsSummaryAttachment, appendReportAttachmentSummary, resolveSummaryConnectorName, groupRuntimeTradesByConnectorScope, syncRuntimeTradeScopes, createRuntimeSignalStats, mergeRuntimeSignalStats, buildSummaryPrelude, resolveTradeStatusEmoji, isTimestampInWindow, isRuntimeTradeClosedInWindow, buildSummaryMessages, signalsSummary, main44;
37337
+ var import_config13, import_args33, import_logger12, import_connectors18, import_runtimeStrategies9, flags30, projectRoot14, SUMMARY_TIMEZONE2, SUMMARY_TIMEZONE_LABEL2, escapeHtml6, formatMskDateTime3, formatSigned3, formatPnlMoneyText, formatWinRateText, resolveSummaryTitle, normalizeStatus, shouldAttachDebugReport, getSummaryDateParts, formatSummaryIsoDayKey, unescapeHtml, stripTelegramHtml, buildSignalsSummaryAttachment, appendReportAttachmentSummary, resolveSummaryConnectorName, groupRuntimeTradesByConnectorScope, syncRuntimeTradeScopes, createRuntimeSignalStats, mergeRuntimeSignalStats, buildSummaryPrelude, resolveTradeStatusEmoji, isTimestampInWindow, isRuntimeTradeClosedInWindow, buildSummaryMessages, signalsSummary, main44;
36816
37338
  var init_signalsSummary = __esm({
36817
37339
  "src/scripts/signalsSummary.ts"() {
36818
37340
  "use strict";
@@ -36826,7 +37348,7 @@ var init_signalsSummary = __esm({
36826
37348
  init_runtimeSignalsLoader();
36827
37349
  init_runtimeSignalsStorage();
36828
37350
  init_runtimeDebugEvidence();
36829
- import_runtimeStrategies6 = require("@tradejs/node/runtimeStrategies");
37351
+ import_runtimeStrategies9 = require("@tradejs/node/runtimeStrategies");
36830
37352
  import_args33.default.option(["u", "user"], "Use user config", "root");
36831
37353
  import_args33.default.option(
36832
37354
  "connector",
@@ -37364,7 +37886,7 @@ none`
37364
37886
  loadRuntimeSignals(flags30.user, { startTime, endTime }),
37365
37887
  loadRuntimeSignalEvaluationStatsBuckets(flags30.user),
37366
37888
  loadRuntimeTrades(flags30.user),
37367
- (0, import_runtimeStrategies6.listRuntimeDeployments)({ userName: flags30.user, projectRoot: projectRoot14 })
37889
+ (0, import_runtimeStrategies9.listRuntimeDeployments)({ userName: flags30.user, projectRoot: projectRoot14 })
37368
37890
  ]);
37369
37891
  const {
37370
37892
  trades: syncedTrades,