@tradejs/cli 3.1.15-beta.228 → 3.1.15

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.
@@ -639,7 +639,74 @@ var runtimeLineageKey = (lineage) => lineage.schemaVersion === 3 ? [
639
639
 
640
640
  // src/lib/runtimeEvidenceDeployment.ts
641
641
  var import_runtimeStrategies = require("@tradejs/node/runtimeStrategies");
642
+ var isNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0;
643
+ var samePackageVersions = (left, right) => {
644
+ if (!left || !right) return false;
645
+ const leftEntries = Object.entries(left).sort(
646
+ ([a], [b]) => a.localeCompare(b)
647
+ );
648
+ const rightEntries = Object.entries(right).sort(
649
+ ([a], [b]) => a.localeCompare(b)
650
+ );
651
+ return JSON.stringify(leftEntries) === JSON.stringify(rightEntries);
652
+ };
653
+ var lineageMatchesStrategySnapshot = ({
654
+ lineage,
655
+ deployment,
656
+ strategy
657
+ }) => lineage?.schemaVersion === 3 && lineage.strategyRevision === strategy.strategyRevision && lineage.deploymentCompositionId === deployment.deploymentCompositionId && lineage.strategyPackageVersion === strategy.strategyPackageVersion && lineage.runtimePackageVersion === strategy.runtimePackageVersion && samePackageVersions(
658
+ lineage.strategyDependencyVersions,
659
+ strategy.strategyDependencyVersions
660
+ );
642
661
  var activeRuntimeEvidenceStrategies = (deployment) => deployment.enabled ? deployment.strategies.filter((strategy) => strategy.enabled) : [];
662
+ var resolveRuntimeEvidenceTickerUniverse = ({
663
+ deployment,
664
+ lineageScopes
665
+ }) => {
666
+ const declaredTickers = deployment.tickers?.filter(isNonEmptyString) ?? [];
667
+ if (declaredTickers.length > 0) {
668
+ return {
669
+ ...deployment,
670
+ tickers: [
671
+ ...new Set(declaredTickers.map((ticker) => ticker.trim()))
672
+ ].sort()
673
+ };
674
+ }
675
+ const activeStrategies = activeRuntimeEvidenceStrategies(deployment);
676
+ if (activeStrategies.length > 0 && activeStrategies.every((strategy) => strategy.selection?.tickers?.length)) {
677
+ return {
678
+ ...deployment,
679
+ tickers: [
680
+ ...new Set(
681
+ activeStrategies.flatMap(
682
+ (strategy) => strategy.selection?.tickers ?? []
683
+ )
684
+ )
685
+ ].sort()
686
+ };
687
+ }
688
+ const strategiesByName = new Map(
689
+ activeStrategies.map((strategy) => [strategy.strategyName, strategy])
690
+ );
691
+ const tickers = /* @__PURE__ */ new Set();
692
+ for (const scope of lineageScopes) {
693
+ const strategy = strategiesByName.get(scope.strategy);
694
+ if (!strategy || scope.deploymentId !== deployment.id || scope.accountId !== deployment.accountId || !isNonEmptyString(scope.symbol) || !lineageMatchesStrategySnapshot({
695
+ lineage: scope.lineage,
696
+ deployment,
697
+ strategy
698
+ })) {
699
+ continue;
700
+ }
701
+ tickers.add(scope.symbol.trim());
702
+ }
703
+ if (tickers.size === 0) {
704
+ throw new Error(
705
+ "Runtime evidence has no immutable ticker universe for the embedded deployment composition"
706
+ );
707
+ }
708
+ return { ...deployment, tickers: [...tickers].sort() };
709
+ };
643
710
  var resolveRuntimeEvidenceDeploymentSnapshot = async ({
644
711
  userName,
645
712
  projectRoot: projectRoot2,
@@ -777,18 +844,18 @@ var parseStrategyFilter = (value) => String(value ?? "").split(",").map((item) =
777
844
  var runtimeEvidence = async () => {
778
845
  const { startTime, endTime, source } = resolveWindow();
779
846
  const deploymentId = String(flags.deployment);
780
- const deployment = await resolveRuntimeEvidenceDeploymentSnapshot({
847
+ const deploymentSnapshot = await resolveRuntimeEvidenceDeploymentSnapshot({
781
848
  userName: String(flags.user),
782
849
  projectRoot,
783
850
  deploymentId
784
851
  });
785
- const activeStrategies = activeRuntimeEvidenceStrategies(deployment).map(
786
- ({ strategyName }) => strategyName
787
- );
852
+ const activeStrategies = activeRuntimeEvidenceStrategies(
853
+ deploymentSnapshot
854
+ ).map(({ strategyName }) => strategyName);
788
855
  const requestedStrategies = parseStrategyFilter(flags.strategy);
789
856
  const strategies = requestedStrategies.length ? requestedStrategies : activeStrategies;
790
857
  const declaredStrategies = new Set(
791
- deployment.strategies.map(({ strategyName }) => strategyName)
858
+ deploymentSnapshot.strategies.map(({ strategyName }) => strategyName)
792
859
  );
793
860
  const unknownStrategies = strategies.filter(
794
861
  (strategyName) => !declaredStrategies.has(strategyName)
@@ -816,6 +883,10 @@ var runtimeEvidence = async () => {
816
883
  evaluationStatsBuckets: evidence.evaluationStatsBuckets,
817
884
  lineageScopes: evidence.lineageScopes
818
885
  });
886
+ const deployment = resolveRuntimeEvidenceTickerUniverse({
887
+ deployment: deploymentSnapshot,
888
+ lineageScopes: evidence.lineageScopes
889
+ });
819
890
  const artifact = {
820
891
  reportType: "runtime-evidence",
821
892
  generatedAt: Date.now(),
@@ -151,6 +151,70 @@ var discoverRuntimeEvidenceBundles = async (rootDir) => {
151
151
  return bundles.sort();
152
152
  };
153
153
 
154
+ // src/lib/runtimeEvidenceDeployment.ts
155
+ var import_runtimeStrategies = require("@tradejs/node/runtimeStrategies");
156
+ var isRecord = (value) => value != null && typeof value === "object" && !Array.isArray(value);
157
+ var isNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0;
158
+ var isPackageVersionMap = (value) => isRecord(value) && Object.keys(value).length > 0 && Object.entries(value).every(
159
+ ([name, version]) => name.startsWith("@tradejs/") && isNonEmptyString(version)
160
+ );
161
+ var INTERVALS = /* @__PURE__ */ new Set([
162
+ "1",
163
+ "3",
164
+ "5",
165
+ "15",
166
+ "30",
167
+ "60",
168
+ "120",
169
+ "240",
170
+ "360",
171
+ "720",
172
+ "D",
173
+ "W",
174
+ "M"
175
+ ]);
176
+ var UNIVERSES = /* @__PURE__ */ new Set(["crypto", "tradfi"]);
177
+ var isRuntimeStrategySelection = (value) => value === void 0 || isRecord(value) && Object.keys(value).length === 1 && Object.keys(value).every((key) => key === "tickers") && Array.isArray(value.tickers) && value.tickers.length > 0 && value.tickers.every((ticker) => isNonEmptyString(ticker));
178
+ var parseRuntimeEvidenceDeploymentSnapshot = (value) => {
179
+ if (!isRecord(value) || value.schemaVersion !== 2 || !isNonEmptyString(value.id) || !isNonEmptyString(value.deploymentCompositionId) || !/^dc1:[a-f0-9]{16}$/.test(value.deploymentCompositionId) || !isNonEmptyString(value.label) || !isNonEmptyString(value.connectorName) || !isNonEmptyString(value.provider) || !isNonEmptyString(value.accountId) || typeof value.enabled !== "boolean" || !Array.isArray(value.strategies)) {
180
+ throw new Error(
181
+ "Runtime evidence deployment snapshot is missing or invalid"
182
+ );
183
+ }
184
+ const strategies = value.strategies.map((candidate) => {
185
+ if (!isRecord(candidate) || !isNonEmptyString(candidate.strategyName) || !isNonEmptyString(candidate.strategyRevision) || !/^sr1:[a-f0-9]{16}$/.test(candidate.strategyRevision) || 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) || !isPackageVersionMap(candidate.strategyDependencyVersions) || !isNonEmptyString(candidate.runtimePackageVersion) || !isRecord(candidate.strategyConfig) || !isRuntimeStrategySelection(candidate.selection)) {
186
+ throw new Error(
187
+ "Runtime evidence deployment strategy snapshot is invalid"
188
+ );
189
+ }
190
+ if (String(candidate.strategyConfig.INTERVAL) !== String(candidate.interval) || String(candidate.strategyConfig.UNIVERSE) !== String(candidate.universe)) {
191
+ throw new Error(
192
+ `Runtime evidence strategy config does not match its scope: ${candidate.strategyName}`
193
+ );
194
+ }
195
+ if (candidate.accountId != null && (!isNonEmptyString(candidate.accountId) || candidate.accountId !== value.accountId)) {
196
+ throw new Error(
197
+ `Runtime evidence strategy account does not match deployment: ${candidate.strategyName}`
198
+ );
199
+ }
200
+ return candidate;
201
+ });
202
+ const identities = /* @__PURE__ */ new Set();
203
+ for (const strategy of strategies) {
204
+ if (identities.has(strategy.strategyName)) {
205
+ throw new Error(
206
+ `Duplicate runtime evidence strategy: ${strategy.strategyName}`
207
+ );
208
+ }
209
+ identities.add(strategy.strategyName);
210
+ }
211
+ return {
212
+ ...value,
213
+ strategies
214
+ };
215
+ };
216
+ var activeRuntimeEvidenceStrategies = (deployment) => deployment.enabled ? deployment.strategies.filter((strategy) => strategy.enabled) : [];
217
+
154
218
  // src/lib/runtimeEvidenceSync.ts
155
219
  var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
156
220
  var isExistingDestinationError = (error) => {
@@ -183,7 +247,26 @@ var buildRuntimeEvidenceRsyncArgs = ({
183
247
  withTrailingSlash(destination)
184
248
  ];
185
249
  var receiptPath = (receiptsRoot, artifactId) => import_node_path2.default.join(receiptsRoot, `${safeSegment(artifactId, "artifact")}.json`);
186
- var listPendingRuntimeEvidenceBundles = async ({
250
+ var compareBundles = (left, right) => left.manifest.window.endTime - right.manifest.window.endTime || left.manifest.createdAt - right.manifest.createdAt;
251
+ var inspectRuntimeEvidenceBundleSupport = (bundle) => {
252
+ try {
253
+ const deployment = parseRuntimeEvidenceDeploymentSnapshot(
254
+ bundle.artifact.deployment
255
+ );
256
+ if (activeRuntimeEvidenceStrategies(deployment).length === 0) {
257
+ throw new Error(
258
+ "Runtime evidence deployment has no enabled active strategies"
259
+ );
260
+ }
261
+ return { supported: true };
262
+ } catch (error) {
263
+ return {
264
+ supported: false,
265
+ reason: error instanceof Error ? error.message : String(error)
266
+ };
267
+ }
268
+ };
269
+ var listRuntimeEvidenceBundleStatuses = async ({
187
270
  evidenceRoot,
188
271
  deploymentId
189
272
  }) => {
@@ -192,19 +275,29 @@ var listPendingRuntimeEvidenceBundles = async ({
192
275
  const receiptsRoot = import_node_path2.default.join(evidenceRoot, "receipts", safeDeploymentId);
193
276
  const bundles = await discoverRuntimeEvidenceBundles(artifactsRoot);
194
277
  const pending = [];
278
+ const unsupported = [];
195
279
  for (const bundleDir of bundles) {
196
280
  const verified = await verifyRuntimeEvidenceBundle(bundleDir);
197
281
  if (verified.manifest.deploymentId !== safeDeploymentId) continue;
198
282
  try {
199
283
  await import_promises2.default.access(receiptPath(receiptsRoot, verified.manifest.artifactId));
284
+ continue;
200
285
  } catch (error) {
201
286
  if (error.code !== "ENOENT") throw error;
287
+ }
288
+ const support = inspectRuntimeEvidenceBundleSupport(verified);
289
+ if (support.supported) {
202
290
  pending.push(verified);
291
+ } else {
292
+ unsupported.push({ bundle: verified, reason: support.reason });
203
293
  }
204
294
  }
205
- return pending.sort(
206
- (left, right) => left.manifest.window.startTime - right.manifest.window.startTime || left.manifest.createdAt - right.manifest.createdAt
207
- );
295
+ return {
296
+ pending: pending.sort(compareBundles),
297
+ unsupported: unsupported.sort(
298
+ (left, right) => compareBundles(left.bundle, right.bundle)
299
+ )
300
+ };
208
301
  };
209
302
  var syncRuntimeEvidenceBundles = async ({
210
303
  source,
@@ -258,15 +351,16 @@ var syncRuntimeEvidenceBundles = async ({
258
351
  }
259
352
  received.push(await verifyRuntimeEvidenceBundle(destinationDir));
260
353
  }
354
+ const statuses = await listRuntimeEvidenceBundleStatuses({
355
+ evidenceRoot,
356
+ deploymentId: safeDeploymentId
357
+ });
261
358
  return {
262
359
  rsyncArgs,
263
360
  stdout: String(transfer.stdout ?? ""),
264
361
  stderr: String(transfer.stderr ?? ""),
265
362
  received,
266
- pending: await listPendingRuntimeEvidenceBundles({
267
- evidenceRoot,
268
- deploymentId: safeDeploymentId
269
- })
363
+ ...statuses
270
364
  };
271
365
  };
272
366
 
@@ -303,7 +397,7 @@ var runtimeEvidenceSync = async () => {
303
397
  });
304
398
  console.log(
305
399
  import_chalk.default.green(
306
- `Runtime evidence sync completed: received=${result.received.length} pending=${result.pending.length}`
400
+ `Runtime evidence sync completed: received=${result.received.length} pending=${result.pending.length} unsupported=${result.unsupported.length}`
307
401
  )
308
402
  );
309
403
  console.log(
@@ -320,6 +414,13 @@ var runtimeEvidenceSync = async () => {
320
414
  bundleDir: bundle.bundleDir,
321
415
  payloadPath: bundle.payloadPath,
322
416
  window: bundle.manifest.window
417
+ })),
418
+ unsupported: result.unsupported.map(({ bundle, reason }) => ({
419
+ artifactId: bundle.manifest.artifactId,
420
+ bundleDir: bundle.bundleDir,
421
+ payloadPath: bundle.payloadPath,
422
+ window: bundle.manifest.window,
423
+ reason
323
424
  }))
324
425
  },
325
426
  null,
@@ -152,6 +152,11 @@ var import_node_child_process = require("child_process");
152
152
  var import_promises2 = __toESM(require("fs/promises"));
153
153
  var import_node_path2 = __toESM(require("path"));
154
154
  var import_node_util = require("util");
155
+
156
+ // src/lib/runtimeEvidenceDeployment.ts
157
+ var import_runtimeStrategies = require("@tradejs/node/runtimeStrategies");
158
+
159
+ // src/lib/runtimeEvidenceSync.ts
155
160
  var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
156
161
  var safeSegment = (value, fallback) => {
157
162
  const normalized = value.trim().replace(/[^a-zA-Z0-9._-]+/g, "-");
@@ -346,6 +351,138 @@ var buildRuntimeLineageSummary = (rows) => {
346
351
  maxLossValue: finiteNumber(identity?.maxLossValue)
347
352
  };
348
353
  };
354
+ var buildEmbeddedLineageScope = ({
355
+ artifact,
356
+ rows,
357
+ strategy
358
+ }) => {
359
+ const deployment = asRecord2(asRecord2(artifact)?.deployment);
360
+ if (finiteNumber(deployment?.schemaVersion) !== 2) return null;
361
+ const deploymentId = finiteString(deployment?.id);
362
+ const accountId = finiteString(deployment?.accountId);
363
+ const deploymentCompositionId = finiteString(
364
+ deployment?.deploymentCompositionId
365
+ );
366
+ if (!deploymentId || !accountId || !deploymentCompositionId) return null;
367
+ const snapshots = asArray(deployment?.strategies).map(asRecord2).filter((item) => item != null).filter((item) => item.enabled === true).filter(
368
+ (item) => !strategy || finiteString(item.strategyName) === strategy
369
+ );
370
+ const targetSeeds = snapshots.map((snapshot) => {
371
+ const strategyName = finiteString(snapshot.strategyName);
372
+ const strategyRevision = finiteString(snapshot.strategyRevision);
373
+ const strategyPackageVersion = finiteString(
374
+ snapshot.strategyPackageVersion
375
+ );
376
+ const strategyDependencyVersions = packageVersionMap(
377
+ snapshot.strategyDependencyVersions
378
+ );
379
+ const runtimePackageVersion = finiteString(
380
+ snapshot.runtimePackageVersion
381
+ );
382
+ return strategyName && strategyRevision && strategyPackageVersion && strategyDependencyVersions && runtimePackageVersion ? {
383
+ strategyName,
384
+ strategyRevision,
385
+ deploymentCompositionId,
386
+ strategyPackageVersion,
387
+ strategyDependencyVersions,
388
+ runtimePackageVersion
389
+ } : null;
390
+ }).filter((item) => item != null);
391
+ if (targetSeeds.length !== snapshots.length || targetSeeds.length === 0) {
392
+ return null;
393
+ }
394
+ const lineageRows = [
395
+ ...rows.evaluations,
396
+ ...rows.signals,
397
+ ...rows.trades,
398
+ ...rows.lineageScopes.map((scope) => ({
399
+ strategy: scope.strategy,
400
+ deploymentId: scope.deploymentId,
401
+ accountId: scope.accountId,
402
+ runtimeLineage: scope.lineage
403
+ }))
404
+ ];
405
+ const identityMatches = (lineage, target) => finiteNumber(lineage?.schemaVersion) === 3 && finiteString(lineage?.strategyRevision) === target.strategyRevision && finiteString(lineage?.deploymentCompositionId) === target.deploymentCompositionId && finiteString(lineage?.strategyPackageVersion) === target.strategyPackageVersion && JSON.stringify(packageVersionMap(lineage?.strategyDependencyVersions)) === JSON.stringify(target.strategyDependencyVersions) && finiteString(lineage?.runtimePackageVersion) === target.runtimePackageVersion;
406
+ const targets = /* @__PURE__ */ new Map();
407
+ for (const seed of targetSeeds) {
408
+ const matchingRows = lineageRows.filter(
409
+ (row) => finiteString(row.strategy) === seed.strategyName && finiteString(row.deploymentId) === deploymentId && finiteString(row.accountId) === accountId && identityMatches(asRecord2(row.runtimeLineage), seed)
410
+ );
411
+ const riskValues = new Set(
412
+ matchingRows.map((row) => finiteNumber(asRecord2(row.runtimeLineage)?.maxLossValue)).filter((value) => value != null)
413
+ );
414
+ targets.set(seed.strategyName, {
415
+ ...seed,
416
+ maxLossValue: riskValues.size === 1 ? [...riskValues][0] : null,
417
+ rowsWithLineage: matchingRows.length,
418
+ riskConflict: riskValues.size > 1
419
+ });
420
+ }
421
+ const belongs = (row) => {
422
+ const strategyName = finiteString(row.strategy);
423
+ const target = strategyName ? targets.get(strategyName) : null;
424
+ const lineage = asRecord2(row.runtimeLineage);
425
+ return Boolean(
426
+ target && target.maxLossValue != null && identityMatches(lineage, target) && finiteNumber(lineage?.maxLossValue) === target.maxLossValue
427
+ );
428
+ };
429
+ const targetValues = [...targets.values()];
430
+ const coverageComplete = targetValues.every(
431
+ (target) => target.rowsWithLineage > 0 && target.maxLossValue != null
432
+ );
433
+ const commonRiskValues = new Set(
434
+ targetValues.map((target) => target.maxLossValue).filter((value) => value != null)
435
+ );
436
+ const singleTarget = targetValues.length === 1 ? targetValues[0] : null;
437
+ const singleLineage = singleTarget?.maxLossValue != null ? {
438
+ schemaVersion: 3,
439
+ strategyRevision: singleTarget.strategyRevision,
440
+ deploymentCompositionId: singleTarget.deploymentCompositionId,
441
+ strategyPackageVersion: singleTarget.strategyPackageVersion,
442
+ strategyDependencyVersions: singleTarget.strategyDependencyVersions,
443
+ runtimePackageVersion: singleTarget.runtimePackageVersion,
444
+ maxLossValue: singleTarget.maxLossValue
445
+ } : null;
446
+ return {
447
+ belongs,
448
+ summary: {
449
+ complete: coverageComplete,
450
+ identityComplete: true,
451
+ coverageComplete,
452
+ conflicts: targetValues.some((target) => target.riskConflict),
453
+ rows: lineageRows.length,
454
+ rowsWithLineage: targetValues.reduce(
455
+ (total, target) => total + target.rowsWithLineage,
456
+ 0
457
+ ),
458
+ schemaVersion: 3,
459
+ lineageKey: singleLineage ? runtimeLineageKey(singleLineage) : null,
460
+ strategyRevision: singleTarget?.strategyRevision ?? null,
461
+ deploymentCompositionId,
462
+ strategyPackageVersion: singleTarget?.strategyPackageVersion ?? null,
463
+ strategyDependencyVersions: singleTarget?.strategyDependencyVersions ?? null,
464
+ runtimePackageVersion: singleTarget?.runtimePackageVersion ?? null,
465
+ compositionId: null,
466
+ gitSha: null,
467
+ gitDirty: null,
468
+ gateFingerprint: null,
469
+ configFingerprint: null,
470
+ contextFingerprint: null,
471
+ maxLossValue: commonRiskValues.size === 1 ? [...commonRiskValues][0] : null,
472
+ strategyScopes: Object.fromEntries(
473
+ targetValues.map((target) => [
474
+ target.strategyName,
475
+ {
476
+ strategyRevision: target.strategyRevision,
477
+ rowsWithLineage: target.rowsWithLineage,
478
+ maxLossValue: target.maxLossValue,
479
+ complete: target.rowsWithLineage > 0 && target.maxLossValue != null
480
+ }
481
+ ])
482
+ )
483
+ }
484
+ };
485
+ };
349
486
  var belongsToRuntimeLineage = (row, summary) => {
350
487
  if (!summary.identityComplete || !summary.lineageKey) return false;
351
488
  const lineage = asRecord2(row.runtimeLineage);
@@ -571,7 +708,12 @@ var buildRuntimeScorecard = ({
571
708
  );
572
709
  const deploymentBinding = runtimeDeploymentBinding(runtimeArtifact);
573
710
  const rows = extractRuntimeRows(scopedRuntimeArtifact);
574
- const runtimeLineage = buildRuntimeLineageSummary(rows);
711
+ const embeddedLineageScope = buildEmbeddedLineageScope({
712
+ artifact: runtimeArtifact,
713
+ rows,
714
+ strategy
715
+ });
716
+ const runtimeLineage = embeddedLineageScope?.summary ?? buildRuntimeLineageSummary(rows);
575
717
  const window = runtimeWindow(runtimeArtifact);
576
718
  const startTime = finiteNumber(window.startTime) ?? generatedAt - 864e5;
577
719
  const endTime = finiteNumber(window.endTime) ?? generatedAt;
@@ -620,12 +762,17 @@ var buildRuntimeScorecard = ({
620
762
  const lineageReason = finiteString(lineage?.reason);
621
763
  const calibrationRoot = asRecord2(calibrationArtifact) ?? {};
622
764
  const calibrationSummary = asRecord2(calibrationRoot.summary);
623
- const calibration = strategy ? asRecord2(asRecord2(calibrationSummary?.byStrategy)?.[strategy]) : getCalibrationSummary(calibrationArtifact);
624
- const actualSlippageBps = finiteNumber(
625
- asRecord2(calibration?.signalToFillAdverseBps)?.avg
765
+ const calibrationSamples = asArray(calibrationRoot.samples).map(asRecord2).filter((item) => item != null).filter((item) => !strategy || finiteString(item.strategy) === strategy).filter(
766
+ (item) => !embeddedLineageScope || embeddedLineageScope.belongs(item)
626
767
  );
627
- const residualVsModelBps = finiteNumber(
628
- asRecord2(calibration?.residualVsCurrentModelBps)?.avg
768
+ const averageSampleMetric = (field) => {
769
+ const values = calibrationSamples.map((sample) => finiteNumber(sample[field])).filter((value) => value != null);
770
+ return values.length ? round(sum(values) / values.length) : null;
771
+ };
772
+ const fallbackCalibration = strategy ? asRecord2(asRecord2(calibrationSummary?.byStrategy)?.[strategy]) : getCalibrationSummary(calibrationArtifact);
773
+ const actualSlippageBps = embeddedLineageScope ? averageSampleMetric("signalToFillAdverseBps") : finiteNumber(asRecord2(fallbackCalibration?.signalToFillAdverseBps)?.avg);
774
+ const residualVsModelBps = embeddedLineageScope ? averageSampleMetric("residualVsCurrentModelBps") : finiteNumber(
775
+ asRecord2(fallbackCalibration?.residualVsCurrentModelBps)?.avg
629
776
  );
630
777
  const prospective = getProspectiveSummary(prospectiveEvidenceArtifact);
631
778
  const historyTrades = dedupeTrades([
@@ -633,10 +780,13 @@ var buildRuntimeScorecard = ({
633
780
  scopedRuntimeArtifact
634
781
  ]);
635
782
  const comparableHistoryTrades = historyTrades.filter(
636
- (trade) => belongsToRuntimeDeployment(trade, deploymentBinding) && belongsToRuntimeLineage(trade, runtimeLineage)
783
+ (trade) => belongsToRuntimeDeployment(trade, deploymentBinding) && (embeddedLineageScope ? embeddedLineageScope.belongs(trade) : belongsToRuntimeLineage(trade, runtimeLineage))
637
784
  );
638
785
  const comparableCurrentClosedTrades = currentClosedTrades.filter(
639
- (trade) => belongsToRuntimeDeployment(trade, deploymentBinding) && belongsToRuntimeLineage(trade, runtimeLineage)
786
+ (trade) => belongsToRuntimeDeployment(trade, deploymentBinding) && (embeddedLineageScope ? embeddedLineageScope.belongs(trade) : belongsToRuntimeLineage(trade, runtimeLineage))
787
+ );
788
+ const comparableFills = rows.trades.filter(
789
+ (trade) => belongsToRuntimeDeployment(trade, deploymentBinding) && (embeddedLineageScope ? embeddedLineageScope.belongs(trade) : belongsToRuntimeLineage(trade, runtimeLineage))
640
790
  );
641
791
  const currentDistributions = buildDistributions(rows);
642
792
  const previousArtifact = scopedHistoryArtifacts.map((artifact) => ({ artifact, window: runtimeWindow(artifact) })).filter(({ window: candidateWindow }) => {
@@ -750,7 +900,8 @@ var buildRuntimeScorecard = ({
750
900
  `${finiteString(signal.orderSkipReason) ?? ""} ${finiteString(signal.orderFailureReason) ?? ""}`
751
901
  )
752
902
  ).length,
753
- fills: rows.trades.length,
903
+ fills: comparableFills.length,
904
+ nonComparableFills: rows.trades.length - comparableFills.length,
754
905
  closedTrades: currentClosedTrades.length,
755
906
  comparableClosedTrades: comparableCurrentClosedTrades.length,
756
907
  nonComparableClosedTrades: currentClosedTrades.length - comparableCurrentClosedTrades.length
@@ -799,7 +950,7 @@ var buildRuntimeScorecard = ({
799
950
  lineageReason
800
951
  },
801
952
  execution: {
802
- available: calibration != null,
953
+ available: actualSlippageBps != null || residualVsModelBps != null,
803
954
  actualSignalToFillSlippageBps: actualSlippageBps,
804
955
  residualVsCurrentModelBps: residualVsModelBps
805
956
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tradejs/cli",
3
- "version": "3.1.15-beta.228",
3
+ "version": "3.1.15",
4
4
  "description": "Official CLI for the TradeJS TypeScript framework: infra setup, backtests, signals, bots, and ML workflows.",
5
5
  "keywords": [
6
6
  "tradejs",
@@ -36,13 +36,13 @@
36
36
  "tradejs": "dist/cli.js"
37
37
  },
38
38
  "dependencies": {
39
- "@tradejs/connectors": "^3.1.15-beta.228",
40
- "@tradejs/core": "^3.1.15-beta.228",
41
- "@tradejs/indicators": "^3.1.15-beta.228",
42
- "@tradejs/infra": "^3.1.15-beta.228",
43
- "@tradejs/node": "^3.1.15-beta.228",
39
+ "@tradejs/connectors": "^3.1.15",
40
+ "@tradejs/core": "^3.1.15",
41
+ "@tradejs/indicators": "^3.1.15",
42
+ "@tradejs/infra": "^3.1.15",
43
+ "@tradejs/node": "^3.1.15",
44
44
  "@tradejs/strategy-kit": "3.0.3",
45
- "@tradejs/types": "^3.1.15-beta.228",
45
+ "@tradejs/types": "^3.1.15",
46
46
  "args": "^5.0.3",
47
47
  "bcryptjs": "^2.4.3",
48
48
  "chalk": "4.1.2",