@tradejs/cli 3.0.0 → 3.1.0

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.
@@ -44,11 +44,191 @@ var import_chalk = __toESM(require("chalk"));
44
44
  var import_node_crypto = require("crypto");
45
45
  var import_promises = __toESM(require("fs/promises"));
46
46
  var import_node_path = __toESM(require("path"));
47
- var import_types = require("@tradejs/types");
47
+ var import_types2 = require("@tradejs/types");
48
48
  var import_strategyReleaseEvidence = require("@tradejs/infra/strategyReleaseEvidence");
49
+
50
+ // src/lib/strategyRelease/liveDiagnosis.ts
51
+ var import_types = require("@tradejs/types");
52
+ var buildStrategyLiveDiagnosis = (input) => {
53
+ const minimumClosedTrades = input.minimumClosedTrades ?? 20;
54
+ const minimumParityRatio = input.minimumParityRatio ?? 0.95;
55
+ const maximumOrderFailureRate = input.maximumOrderFailureRate ?? 0.05;
56
+ const minimumRegimeCoverage = input.minimumRegimeCoverage ?? 0.5;
57
+ const hasExplicitRiskScale = input.riskScaleComparable != null;
58
+ const comparisonObservedDrawdown = hasExplicitRiskScale ? input.riskScaleComparable ? input.normalizedObservedDrawdown ?? null : null : input.observedDrawdown;
59
+ const runtimeDivergence = !input.lineageComparable || input.parityRatio != null && input.parityRatio < minimumParityRatio || input.orderFailureRate != null && input.orderFailureRate > maximumOrderFailureRate;
60
+ const insufficient = input.closedTrades < minimumClosedTrades || input.parityRatio == null || comparisonObservedDrawdown == null || input.historicalDrawdownP95 == null;
61
+ const expected = !runtimeDivergence && !insufficient && comparisonObservedDrawdown <= input.historicalDrawdownP95;
62
+ const verdict = runtimeDivergence ? "RUNTIME_DIVERGENCE" : insufficient ? "INSUFFICIENT_EVIDENCE" : expected ? "EXPECTED_DRAWDOWN" : "GENERALIZATION_FAILURE";
63
+ const attributionIncomplete = input.rawCoreExpectancyDelta == null || input.aiGateAddedValue == null || input.regimeCoverage == null;
64
+ const attributedVerdict = verdict === "GENERALIZATION_FAILURE" && attributionIncomplete ? "INSUFFICIENT_EVIDENCE" : verdict;
65
+ const subtype = attributedVerdict !== "GENERALIZATION_FAILURE" ? null : input.rawCoreExpectancyDelta < 0 && input.aiGateAddedValue >= 0 ? "RAW_CORE_DECAY" : input.aiGateAddedValue < 0 ? "AI_GATE_FAILURE" : input.regimeCoverage < minimumRegimeCoverage ? "REGIME_SHIFT" : (input.overfitProbability ?? 0) >= 0.5 ? "SUSPECTED_HISTORICAL_OVERFIT" : "RAW_CORE_DECAY";
66
+ const explanation = attributedVerdict === "RUNTIME_DIVERGENCE" ? "Runtime cannot be compared economically until lineage, replay parity, and order execution agree with the frozen composition." : attributedVerdict === "EXPECTED_DRAWDOWN" ? "The observed drawdown is within the preregistered equal-length historical drawdown envelope." : attributedVerdict === "GENERALIZATION_FAILURE" ? `Runtime is comparable, but the observed drawdown exceeds the historical envelope (${subtype}).` : attributionIncomplete && verdict === "GENERALIZATION_FAILURE" ? "The drawdown breaches its historical envelope, but matched shadow raw-core, deterministic-gate, or causal regime evidence is missing for attribution." : "There are not enough comparable closed trades and historical drawdown observations for attribution.";
67
+ const recommendations = attributedVerdict === "RUNTIME_DIVERGENCE" ? [
68
+ "Resolve lineage and replay parity mismatches before evaluating strategy economics.",
69
+ "Inspect order failures and execution residuals against the frozen execution model."
70
+ ] : attributedVerdict === "EXPECTED_DRAWDOWN" ? [
71
+ "Keep the frozen composition unchanged and continue prospective evidence collection."
72
+ ] : attributedVerdict === "GENERALIZATION_FAILURE" ? [
73
+ "Do not retune the live cohort; open a new immutable research lineage.",
74
+ "Compare raw-core decay, AI-gate added value, and causal regime coverage."
75
+ ] : [
76
+ attributionIncomplete && verdict === "GENERALIZATION_FAILURE" ? "Collect matched shadow raw-core, deterministic-gate, and causal regime evidence without changing the composition." : "Collect more verified prospective outcomes without changing the composition."
77
+ ];
78
+ return {
79
+ schema: import_types.STRATEGY_LIVE_DIAGNOSIS_SCHEMA,
80
+ strategy: input.strategy,
81
+ compositionId: input.compositionId,
82
+ createdAt: input.createdAt,
83
+ verdict: attributedVerdict,
84
+ subtype,
85
+ confidence: runtimeDivergence || input.closedTrades >= 50 ? "high" : input.closedTrades >= 20 ? "medium" : "low",
86
+ evidence: {
87
+ lineageComparable: input.lineageComparable,
88
+ riskScaleComparable: input.riskScaleComparable ?? false,
89
+ releaseMaxLossValue: input.releaseMaxLossValue ?? null,
90
+ runtimeMaxLossValue: input.runtimeMaxLossValue ?? null,
91
+ riskScaleRatio: input.riskScaleRatio ?? null,
92
+ parityRatio: input.parityRatio,
93
+ orderFailureRate: input.orderFailureRate,
94
+ observedDrawdown: input.observedDrawdown,
95
+ normalizedObservedDrawdown: input.normalizedObservedDrawdown ?? null,
96
+ historicalDrawdownP95: input.historicalDrawdownP95,
97
+ historicalDrawdownMaximum: input.historicalDrawdownMaximum,
98
+ closedTrades: input.closedTrades,
99
+ rawCoreExpectancyDelta: input.rawCoreExpectancyDelta,
100
+ aiGateAddedValue: input.aiGateAddedValue,
101
+ regimeCoverage: input.regimeCoverage,
102
+ overfitProbability: input.overfitProbability
103
+ },
104
+ explanation,
105
+ recommendations
106
+ };
107
+ };
108
+
109
+ // src/lib/strategyRelease/monitoringProfile.ts
110
+ var DAY_MS = 864e5;
111
+ function assert(condition, message) {
112
+ if (!condition) throw new Error(`Invalid strategy release: ${message}`);
113
+ }
114
+ var realizedDrawdown = (trades, startIndex = 0, endIndex = trades.length) => {
115
+ let equity = 0;
116
+ let peak = 0;
117
+ let maximum = 0;
118
+ for (let index = startIndex; index < endIndex; index += 1) {
119
+ const trade = trades[index];
120
+ equity += trade.netProfit;
121
+ peak = Math.max(peak, equity);
122
+ maximum = Math.max(maximum, peak - equity);
123
+ }
124
+ return maximum;
125
+ };
126
+ var lowerBoundExitTimestamp = (trades, timestamp) => {
127
+ let left = 0;
128
+ let right = trades.length;
129
+ while (left < right) {
130
+ const middle = Math.floor((left + right) / 2);
131
+ if (trades[middle].exitTimestamp < timestamp) left = middle + 1;
132
+ else right = middle;
133
+ }
134
+ return left;
135
+ };
136
+ var percentile = (values, probability) => {
137
+ const sorted = [...values].sort((left, right) => left - right);
138
+ const position = (sorted.length - 1) * probability;
139
+ const lower = Math.floor(position);
140
+ const upper = Math.ceil(position);
141
+ if (lower === upper) return sorted[lower];
142
+ return sorted[lower] * (upper - position) + sorted[upper] * (position - lower);
143
+ };
144
+ var buildStrategyMonitoringProfile = ({
145
+ trades,
146
+ startTime,
147
+ endTime,
148
+ days,
149
+ minimumProspectiveClosedTrades = 20,
150
+ minimumParityRatio = 0.95,
151
+ maximumOrderFailureRate = 0.05,
152
+ minimumRegimeCoverage = 0.5,
153
+ aiGateExpectancy = null,
154
+ overfitProbability = null
155
+ }) => {
156
+ assert(endTime > startTime, "monitoring profile window is invalid");
157
+ assert(days.length > 0, "monitoring profile days are required");
158
+ const ordered = trades.filter(
159
+ (trade) => Number.isFinite(trade.exitTimestamp) && Number.isFinite(trade.netProfit) && trade.exitTimestamp >= startTime && trade.exitTimestamp < endTime
160
+ ).sort((left, right) => left.exitTimestamp - right.exitTimestamp);
161
+ const drawdownEnvelopes = [...new Set(days)].sort((left, right) => left - right).map((windowDays) => {
162
+ assert(
163
+ Number.isInteger(windowDays) && windowDays > 0,
164
+ "monitoring profile days must be positive integers"
165
+ );
166
+ const duration = windowDays * DAY_MS;
167
+ assert(
168
+ endTime - startTime >= duration,
169
+ `${windowDays}d monitoring window exceeds historical coverage`
170
+ );
171
+ const drawdowns = [];
172
+ for (let windowStart = startTime; windowStart + duration <= endTime; windowStart += DAY_MS) {
173
+ const windowEnd = windowStart + duration;
174
+ drawdowns.push(
175
+ realizedDrawdown(
176
+ ordered,
177
+ lowerBoundExitTimestamp(ordered, windowStart),
178
+ lowerBoundExitTimestamp(ordered, windowEnd)
179
+ )
180
+ );
181
+ }
182
+ return {
183
+ days: windowDays,
184
+ p95: percentile(drawdowns, 0.95),
185
+ maximum: Math.max(...drawdowns)
186
+ };
187
+ });
188
+ return {
189
+ minimumProspectiveClosedTrades,
190
+ minimumParityRatio,
191
+ maximumOrderFailureRate,
192
+ minimumRegimeCoverage,
193
+ drawdownEnvelopes,
194
+ rawCoreExpectancy: ordered.length ? ordered.reduce((sum, trade) => sum + trade.netProfit, 0) / ordered.length : null,
195
+ aiGateExpectancy,
196
+ overfitProbability
197
+ };
198
+ };
199
+
200
+ // src/lib/strategyRelease/evidenceRetention.ts
201
+ var DAY_MS2 = 864e5;
202
+ var planStrategyEvidenceRetention = ({
203
+ now,
204
+ entries,
205
+ retentionDays = {
206
+ operational_redis: 3,
207
+ verbose_payload: 14,
208
+ verified_runtime_bundle: 90,
209
+ compact_ledger: null
210
+ }
211
+ }) => {
212
+ const keep = [];
213
+ const remove = [];
214
+ for (const entry of entries) {
215
+ const days = retentionDays[entry.kind];
216
+ if (days == null || !entry.verified || !entry.aggregated || now - entry.createdAt <= days * DAY_MS2) {
217
+ keep.push(entry);
218
+ } else {
219
+ remove.push(entry);
220
+ }
221
+ }
222
+ return {
223
+ keep,
224
+ delete: remove,
225
+ bytesReclaimable: remove.reduce((total, entry) => total + entry.bytes, 0)
226
+ };
227
+ };
228
+
229
+ // src/lib/strategyRelease.ts
49
230
  var SHA256_RE = /^[a-f0-9]{64}$/;
50
231
  var LINEAGE_FINGERPRINT_RE = /^[a-f0-9]{16}$/;
51
- var DAY_MS = 864e5;
52
232
  var REQUIRED_LONG_WINDOWS = [1095, 1460, 1825];
53
233
  var REQUIRED_STABLE_TERMINAL_WINDOWS = [365, 180, 90];
54
234
  var MIN_RECENT_DIRECTION_REPAIR_TRADES = 20;
@@ -187,11 +367,11 @@ var deriveStrategyReleaseResearchDecision = async (input) => {
187
367
  };
188
368
  var canonicalStrategyReleaseJson = (value) => (0, import_strategyReleaseEvidence.canonicalStrategyEvidenceJson)(value);
189
369
  var strategyReleaseSha256 = (value) => (0, import_strategyReleaseEvidence.strategyEvidenceSha256)(value);
190
- function assert(condition, message) {
370
+ function assert2(condition, message) {
191
371
  if (!condition) throw new Error(`Invalid strategy release: ${message}`);
192
372
  }
193
- var assertFingerprint = (value, label) => assert(SHA256_RE.test(value), `${label} must be a lowercase SHA-256`);
194
- var assertLineageFingerprint = (value, label) => assert(
373
+ var assertFingerprint = (value, label) => assert2(SHA256_RE.test(value), `${label} must be a lowercase SHA-256`);
374
+ var assertLineageFingerprint = (value, label) => assert2(
195
375
  LINEAGE_FINGERPRINT_RE.test(value),
196
376
  `${label} must be a 16-character lowercase lineage fingerprint`
197
377
  );
@@ -239,7 +419,7 @@ var deriveReleaseGates = (evidence) => {
239
419
  };
240
420
  var verifyEvidenceComposition = (strategy, composition, evidence) => {
241
421
  const directionPolicy = composition.directionPolicy ?? "both";
242
- const requireEqual = (actual, expected, label, artifactId) => assert(
422
+ const requireEqual = (actual, expected, label, artifactId) => assert2(
243
423
  actual === expected,
244
424
  `evidence ${artifactId} ${label} does not match the frozen composition`
245
425
  );
@@ -271,7 +451,7 @@ var verifyEvidenceComposition = (strategy, composition, evidence) => {
271
451
  "MAX_LOSS_VALUE",
272
452
  reference.artifactId
273
453
  );
274
- assert(
454
+ assert2(
275
455
  lineage?.sourceSha256s.includes(composition.coreExportSha256),
276
456
  `evidence ${reference.artifactId} does not include the frozen core export`
277
457
  );
@@ -317,7 +497,7 @@ var verifyEvidenceComposition = (strategy, composition, evidence) => {
317
497
  reference.artifactId
318
498
  );
319
499
  }
320
- assert(
500
+ assert2(
321
501
  lineage?.sourceSha256s.includes(composition.coreExportSha256),
322
502
  `evidence ${reference.artifactId} does not use the frozen core export`
323
503
  );
@@ -421,60 +601,60 @@ var verifyEvidenceComposition = (strategy, composition, evidence) => {
421
601
  }
422
602
  };
423
603
  var createStrategyReleaseManifest = (input) => {
424
- assert(input.strategy.trim(), "strategy is required");
425
- assert(
604
+ assert2(input.strategy.trim(), "strategy is required");
605
+ assert2(
426
606
  Number.isFinite(input.createdAt),
427
607
  "createdAt must be a finite timestamp"
428
608
  );
429
- assert(
609
+ assert2(
430
610
  input.researchBudget.hypothesisFamilies <= 3,
431
611
  "at most 3 hypothesis families are allowed"
432
612
  );
433
- assert(
613
+ assert2(
434
614
  input.researchBudget.maximumVariantsPerFamily <= 5,
435
615
  "at most 5 variants per family are allowed"
436
616
  );
437
- assert(
617
+ assert2(
438
618
  input.researchBudget.isolatedLongFinalists <= 1,
439
619
  "at most one isolated-long finalist is allowed"
440
620
  );
441
- assert(
621
+ assert2(
442
622
  input.researchBudget.aiGateTuningRounds <= 1,
443
623
  "at most one AI-gate tuning round is allowed"
444
624
  );
445
- assert(input.marketWindow.cacheOnly, "market window must be cache-only");
446
- assert(
625
+ assert2(input.marketWindow.cacheOnly, "market window must be cache-only");
626
+ assert2(
447
627
  input.marketWindow.endTime > input.marketWindow.startTime,
448
628
  "market window is invalid"
449
629
  );
450
- assert(input.composition.maxLossValue > 0, "MAX_LOSS_VALUE must be positive");
451
- assert(
630
+ assert2(input.composition.maxLossValue > 0, "MAX_LOSS_VALUE must be positive");
631
+ assert2(
452
632
  input.composition.longEnabled && input.composition.shortEnabled,
453
633
  "LONG and SHORT must both remain enabled"
454
634
  );
455
- assert(
635
+ assert2(
456
636
  input.composition.directionPolicy == null || ["both", "long_only", "short_only", "direction_aware"].includes(
457
637
  input.composition.directionPolicy
458
638
  ),
459
639
  "composition.directionPolicy is invalid"
460
640
  );
461
- assert(
641
+ assert2(
462
642
  Number.isInteger(input.monitoring.minimumProspectiveClosedTrades) && input.monitoring.minimumProspectiveClosedTrades > 0,
463
643
  "monitoring.minimumProspectiveClosedTrades must be positive"
464
644
  );
465
- assert(
645
+ assert2(
466
646
  Number.isFinite(input.monitoring.minimumParityRatio) && input.monitoring.minimumParityRatio >= 0 && input.monitoring.minimumParityRatio <= 1,
467
647
  "monitoring.minimumParityRatio must be between 0 and 1"
468
648
  );
469
- assert(
649
+ assert2(
470
650
  Number.isFinite(input.monitoring.maximumOrderFailureRate) && input.monitoring.maximumOrderFailureRate >= 0 && input.monitoring.maximumOrderFailureRate <= 1,
471
651
  "monitoring.maximumOrderFailureRate must be between 0 and 1"
472
652
  );
473
- assert(
653
+ assert2(
474
654
  Number.isFinite(input.monitoring.minimumRegimeCoverage) && input.monitoring.minimumRegimeCoverage >= 0 && input.monitoring.minimumRegimeCoverage <= 1,
475
655
  "monitoring.minimumRegimeCoverage must be between 0 and 1"
476
656
  );
477
- assert(
657
+ assert2(
478
658
  input.monitoring.drawdownEnvelopes.length > 0 && input.monitoring.drawdownEnvelopes.every(
479
659
  (entry) => Number.isInteger(entry.days) && entry.days > 0 && Number.isFinite(entry.p95) && Number.isFinite(entry.maximum) && entry.p95 >= 0 && entry.maximum >= entry.p95
480
660
  ),
@@ -508,7 +688,7 @@ var createStrategyReleaseManifest = (input) => {
508
688
  }
509
689
  verifyEvidenceComposition(input.strategy, input.composition, input.evidence);
510
690
  const evidenceGates = deriveReleaseGates(input.evidence);
511
- assert(
691
+ assert2(
512
692
  canonicalStrategyReleaseJson(input.gates) === canonicalStrategyReleaseJson(evidenceGates),
513
693
  "release gates must be derived from verified evidence assertions"
514
694
  );
@@ -555,7 +735,7 @@ var createStrategyReleaseManifest = (input) => {
555
735
  verdict
556
736
  };
557
737
  return {
558
- schema: import_types.STRATEGY_RELEASE_SCHEMA,
738
+ schema: import_types2.STRATEGY_RELEASE_SCHEMA,
559
739
  releaseId: `${safeSegment(input.strategy, "strategy")}_${compactTimestamp(input.createdAt)}_${strategyReleaseSha256(releaseIdentity).slice(0, 16)}`,
560
740
  strategy: input.strategy,
561
741
  createdAt: input.createdAt,
@@ -582,21 +762,21 @@ var createStrategyReleaseEnvelope = (manifest) => ({
582
762
  var verifyStrategyReleaseEnvelope = async (valueOrPath) => {
583
763
  const value = typeof valueOrPath === "string" ? JSON.parse(await import_promises.default.readFile(valueOrPath, "utf8")) : valueOrPath;
584
764
  const envelope = value;
585
- assert(
765
+ assert2(
586
766
  envelope?.schema === "tradejs-strategy-release-envelope/v1",
587
767
  "release envelope schema mismatch"
588
768
  );
589
- assert(
769
+ assert2(
590
770
  envelope.releaseId === envelope.manifest?.releaseId,
591
771
  "releaseId mismatch"
592
772
  );
593
- assert(
773
+ assert2(
594
774
  strategyReleaseSha256(envelope.manifest) === envelope.manifestSha256,
595
775
  "release manifest checksum mismatch"
596
776
  );
597
777
  const manifest = envelope.manifest;
598
- assert(
599
- manifest.schema === import_types.STRATEGY_RELEASE_SCHEMA,
778
+ assert2(
779
+ manifest.schema === import_types2.STRATEGY_RELEASE_SCHEMA,
600
780
  "manifest schema mismatch"
601
781
  );
602
782
  const rebuilt = createStrategyReleaseManifest({
@@ -624,7 +804,7 @@ var verifyStrategyReleaseEnvelope = async (valueOrPath) => {
624
804
  summary: manifest.verdict.summary,
625
805
  prospective: manifest.prospective
626
806
  });
627
- assert(
807
+ assert2(
628
808
  canonicalStrategyReleaseJson(rebuilt) === canonicalStrategyReleaseJson(manifest),
629
809
  "release manifest contains self-declared or inconsistent derived fields"
630
810
  );
@@ -739,147 +919,6 @@ var publishStrategyRelease = async ({
739
919
  ]);
740
920
  return { releasePath, markerPath, envelope, markerEnvelope };
741
921
  };
742
- var buildStrategyLiveDiagnosis = (input) => {
743
- const minimumClosedTrades = input.minimumClosedTrades ?? 20;
744
- const minimumParityRatio = input.minimumParityRatio ?? 0.95;
745
- const maximumOrderFailureRate = input.maximumOrderFailureRate ?? 0.05;
746
- const minimumRegimeCoverage = input.minimumRegimeCoverage ?? 0.5;
747
- const hasExplicitRiskScale = input.riskScaleComparable != null;
748
- const comparisonObservedDrawdown = hasExplicitRiskScale ? input.riskScaleComparable ? input.normalizedObservedDrawdown ?? null : null : input.observedDrawdown;
749
- const runtimeDivergence = !input.lineageComparable || input.parityRatio != null && input.parityRatio < minimumParityRatio || input.orderFailureRate != null && input.orderFailureRate > maximumOrderFailureRate;
750
- const insufficient = input.closedTrades < minimumClosedTrades || input.parityRatio == null || comparisonObservedDrawdown == null || input.historicalDrawdownP95 == null;
751
- const expected = !runtimeDivergence && !insufficient && comparisonObservedDrawdown <= input.historicalDrawdownP95;
752
- const verdict = runtimeDivergence ? "RUNTIME_DIVERGENCE" : insufficient ? "INSUFFICIENT_EVIDENCE" : expected ? "EXPECTED_DRAWDOWN" : "GENERALIZATION_FAILURE";
753
- const attributionIncomplete = input.rawCoreExpectancyDelta == null || input.aiGateAddedValue == null || input.regimeCoverage == null;
754
- const attributedVerdict = verdict === "GENERALIZATION_FAILURE" && attributionIncomplete ? "INSUFFICIENT_EVIDENCE" : verdict;
755
- const subtype = attributedVerdict !== "GENERALIZATION_FAILURE" ? null : input.rawCoreExpectancyDelta < 0 && input.aiGateAddedValue >= 0 ? "RAW_CORE_DECAY" : input.aiGateAddedValue < 0 ? "AI_GATE_FAILURE" : input.regimeCoverage < minimumRegimeCoverage ? "REGIME_SHIFT" : (input.overfitProbability ?? 0) >= 0.5 ? "SUSPECTED_HISTORICAL_OVERFIT" : "RAW_CORE_DECAY";
756
- const explanation = attributedVerdict === "RUNTIME_DIVERGENCE" ? "Runtime cannot be compared economically until lineage, replay parity, and order execution agree with the frozen composition." : attributedVerdict === "EXPECTED_DRAWDOWN" ? "The observed drawdown is within the preregistered equal-length historical drawdown envelope." : attributedVerdict === "GENERALIZATION_FAILURE" ? `Runtime is comparable, but the observed drawdown exceeds the historical envelope (${subtype}).` : attributionIncomplete && verdict === "GENERALIZATION_FAILURE" ? "The drawdown breaches its historical envelope, but matched shadow raw-core, deterministic-gate, or causal regime evidence is missing for attribution." : "There are not enough comparable closed trades and historical drawdown observations for attribution.";
757
- const recommendations = attributedVerdict === "RUNTIME_DIVERGENCE" ? [
758
- "Resolve lineage and replay parity mismatches before evaluating strategy economics.",
759
- "Inspect order failures and execution residuals against the frozen execution model."
760
- ] : attributedVerdict === "EXPECTED_DRAWDOWN" ? [
761
- "Keep the frozen composition unchanged and continue prospective evidence collection."
762
- ] : attributedVerdict === "GENERALIZATION_FAILURE" ? [
763
- "Do not retune the live cohort; open a new immutable research lineage.",
764
- "Compare raw-core decay, AI-gate added value, and causal regime coverage."
765
- ] : [
766
- attributionIncomplete && verdict === "GENERALIZATION_FAILURE" ? "Collect matched shadow raw-core, deterministic-gate, and causal regime evidence without changing the composition." : "Collect more verified prospective outcomes without changing the composition."
767
- ];
768
- return {
769
- schema: import_types.STRATEGY_LIVE_DIAGNOSIS_SCHEMA,
770
- strategy: input.strategy,
771
- compositionId: input.compositionId,
772
- createdAt: input.createdAt,
773
- verdict: attributedVerdict,
774
- subtype,
775
- confidence: runtimeDivergence || input.closedTrades >= 50 ? "high" : input.closedTrades >= 20 ? "medium" : "low",
776
- evidence: {
777
- lineageComparable: input.lineageComparable,
778
- riskScaleComparable: input.riskScaleComparable ?? false,
779
- releaseMaxLossValue: input.releaseMaxLossValue ?? null,
780
- runtimeMaxLossValue: input.runtimeMaxLossValue ?? null,
781
- riskScaleRatio: input.riskScaleRatio ?? null,
782
- parityRatio: input.parityRatio,
783
- orderFailureRate: input.orderFailureRate,
784
- observedDrawdown: input.observedDrawdown,
785
- normalizedObservedDrawdown: input.normalizedObservedDrawdown ?? null,
786
- historicalDrawdownP95: input.historicalDrawdownP95,
787
- historicalDrawdownMaximum: input.historicalDrawdownMaximum,
788
- closedTrades: input.closedTrades,
789
- rawCoreExpectancyDelta: input.rawCoreExpectancyDelta,
790
- aiGateAddedValue: input.aiGateAddedValue,
791
- regimeCoverage: input.regimeCoverage,
792
- overfitProbability: input.overfitProbability
793
- },
794
- explanation,
795
- recommendations
796
- };
797
- };
798
- var realizedDrawdown = (trades, startIndex = 0, endIndex = trades.length) => {
799
- let equity = 0;
800
- let peak = 0;
801
- let maximum = 0;
802
- for (let index = startIndex; index < endIndex; index += 1) {
803
- const trade = trades[index];
804
- equity += trade.netProfit;
805
- peak = Math.max(peak, equity);
806
- maximum = Math.max(maximum, peak - equity);
807
- }
808
- return maximum;
809
- };
810
- var lowerBoundExitTimestamp = (trades, timestamp) => {
811
- let left = 0;
812
- let right = trades.length;
813
- while (left < right) {
814
- const middle = Math.floor((left + right) / 2);
815
- if (trades[middle].exitTimestamp < timestamp) left = middle + 1;
816
- else right = middle;
817
- }
818
- return left;
819
- };
820
- var percentile = (values, probability) => {
821
- const sorted = [...values].sort((left, right) => left - right);
822
- const position = (sorted.length - 1) * probability;
823
- const lower = Math.floor(position);
824
- const upper = Math.ceil(position);
825
- if (lower === upper) return sorted[lower];
826
- return sorted[lower] * (upper - position) + sorted[upper] * (position - lower);
827
- };
828
- var buildStrategyMonitoringProfile = ({
829
- trades,
830
- startTime,
831
- endTime,
832
- days,
833
- minimumProspectiveClosedTrades = 20,
834
- minimumParityRatio = 0.95,
835
- maximumOrderFailureRate = 0.05,
836
- minimumRegimeCoverage = 0.5,
837
- aiGateExpectancy = null,
838
- overfitProbability = null
839
- }) => {
840
- assert(endTime > startTime, "monitoring profile window is invalid");
841
- assert(days.length > 0, "monitoring profile days are required");
842
- const ordered = trades.filter(
843
- (trade) => Number.isFinite(trade.exitTimestamp) && Number.isFinite(trade.netProfit) && trade.exitTimestamp >= startTime && trade.exitTimestamp < endTime
844
- ).sort((left, right) => left.exitTimestamp - right.exitTimestamp);
845
- const drawdownEnvelopes = [...new Set(days)].sort((left, right) => left - right).map((windowDays) => {
846
- assert(
847
- Number.isInteger(windowDays) && windowDays > 0,
848
- "monitoring profile days must be positive integers"
849
- );
850
- const duration = windowDays * DAY_MS;
851
- assert(
852
- endTime - startTime >= duration,
853
- `${windowDays}d monitoring window exceeds historical coverage`
854
- );
855
- const drawdowns = [];
856
- for (let windowStart = startTime; windowStart + duration <= endTime; windowStart += DAY_MS) {
857
- const windowEnd = windowStart + duration;
858
- drawdowns.push(
859
- realizedDrawdown(
860
- ordered,
861
- lowerBoundExitTimestamp(ordered, windowStart),
862
- lowerBoundExitTimestamp(ordered, windowEnd)
863
- )
864
- );
865
- }
866
- return {
867
- days: windowDays,
868
- p95: percentile(drawdowns, 0.95),
869
- maximum: Math.max(...drawdowns)
870
- };
871
- });
872
- return {
873
- minimumProspectiveClosedTrades,
874
- minimumParityRatio,
875
- maximumOrderFailureRate,
876
- minimumRegimeCoverage,
877
- drawdownEnvelopes,
878
- rawCoreExpectancy: ordered.length ? ordered.reduce((sum, trade) => sum + trade.netProfit, 0) / ordered.length : null,
879
- aiGateExpectancy,
880
- overfitProbability
881
- };
882
- };
883
922
  var publishStrategyLiveDiagnosis = async ({
884
923
  rootDir,
885
924
  diagnosis,
@@ -963,32 +1002,6 @@ var publishStrategyLiveDiagnosis = async ({
963
1002
  await verifyStrategyEvidenceMarkerEnvelope(markerPath);
964
1003
  return { diagnosisPath, markerPath, envelope, markerEnvelope };
965
1004
  };
966
- var planStrategyEvidenceRetention = ({
967
- now,
968
- entries,
969
- retentionDays = {
970
- operational_redis: 3,
971
- verbose_payload: 14,
972
- verified_runtime_bundle: 90,
973
- compact_ledger: null
974
- }
975
- }) => {
976
- const keep = [];
977
- const remove = [];
978
- for (const entry of entries) {
979
- const days = retentionDays[entry.kind];
980
- if (days == null || !entry.verified || !entry.aggregated || now - entry.createdAt <= days * DAY_MS) {
981
- keep.push(entry);
982
- } else {
983
- remove.push(entry);
984
- }
985
- }
986
- return {
987
- keep,
988
- delete: remove,
989
- bytesReclaimable: remove.reduce((total, entry) => total + entry.bytes, 0)
990
- };
991
- };
992
1005
  var finiteString = (value) => typeof value === "string" && value.trim() ? value.trim() : null;
993
1006
  var finiteNumber = (value) => {
994
1007
  const number = Number(value);
@@ -1164,39 +1177,39 @@ var verifyCompletedCoreResearchBundle = async ({
1164
1177
  }) => {
1165
1178
  const researchId = finiteString(artifact.researchId);
1166
1179
  const specSha256 = finiteString(artifact.specSha256);
1167
- assert(researchId != null, "core result researchId is required");
1168
- assert(specSha256 != null, "core result specSha256 is required");
1180
+ assert2(researchId != null, "core result researchId is required");
1181
+ assert2(specSha256 != null, "core result specSha256 is required");
1169
1182
  const researchDir = import_node_path.default.dirname(resultPath);
1170
1183
  const manifestPath = import_node_path.default.join(researchDir, "manifest.json");
1171
1184
  const specPath = import_node_path.default.join(researchDir, "spec.json");
1172
1185
  const manifestBytes = await import_promises.default.readFile(manifestPath).catch(() => null);
1173
1186
  const specBytes = await import_promises.default.readFile(specPath).catch(() => null);
1174
- assert(
1187
+ assert2(
1175
1188
  manifestBytes != null && specBytes != null,
1176
1189
  "core result must belong to a completed core research bundle"
1177
1190
  );
1178
1191
  const manifest = JSON.parse(manifestBytes.toString("utf8"));
1179
1192
  const artifactHashes = manifest.artifactHashes != null && typeof manifest.artifactHashes === "object" && !Array.isArray(manifest.artifactHashes) ? manifest.artifactHashes : null;
1180
1193
  const parsedSpec = JSON.parse(specBytes.toString("utf8"));
1181
- assert(
1194
+ assert2(
1182
1195
  manifest.schema === "tradejs-core-research-manifest/v1" && manifest.status === "completed" && manifest.researchId === researchId && manifest.specSha256 === specSha256 && artifactHashes?.["result.json"] === resultSha256 && (0, import_node_crypto.createHash)("sha256").update(canonicalStrategyReleaseJson(parsedSpec)).digest("hex") === specSha256,
1183
1196
  "core result is not bound by its completed core research manifest"
1184
1197
  );
1185
1198
  for (const [relativePath, expectedSha256] of Object.entries(
1186
1199
  artifactHashes ?? {}
1187
1200
  )) {
1188
- assert(
1201
+ assert2(
1189
1202
  !import_node_path.default.isAbsolute(relativePath) && !relativePath.split(/[\\/]/).includes(".."),
1190
1203
  `core bundle artifact ${relativePath} escapes its research directory`
1191
1204
  );
1192
- assert(
1205
+ assert2(
1193
1206
  typeof expectedSha256 === "string" && SHA256_RE.test(expectedSha256),
1194
1207
  `core bundle artifact ${relativePath} has an invalid checksum`
1195
1208
  );
1196
1209
  const actualSha256 = await (0, import_strategyReleaseEvidence.strategyEvidenceFileSha256)(
1197
1210
  import_node_path.default.join(researchDir, relativePath)
1198
1211
  );
1199
- assert(
1212
+ assert2(
1200
1213
  actualSha256 === expectedSha256,
1201
1214
  `core bundle artifact ${relativePath} checksum mismatch`
1202
1215
  );
@@ -1206,7 +1219,7 @@ async function collectReleaseEvidenceReferences(references) {
1206
1219
  return Promise.all(
1207
1220
  references.map(async (reference) => {
1208
1221
  const actualSha256 = await (0, import_strategyReleaseEvidence.strategyEvidenceFileSha256)(reference.path);
1209
- assert(
1222
+ assert2(
1210
1223
  actualSha256 === reference.sha256,
1211
1224
  `evidence ${reference.artifactId} checksum mismatch`
1212
1225
  );
@@ -1215,7 +1228,7 @@ async function collectReleaseEvidenceReferences(references) {
1215
1228
  const parsed = JSON.parse(
1216
1229
  await import_promises.default.readFile(reference.path, "utf8")
1217
1230
  );
1218
- assert(
1231
+ assert2(
1219
1232
  parsed != null && typeof parsed === "object" && !Array.isArray(parsed),
1220
1233
  `evidence ${reference.artifactId} must be a JSON object`
1221
1234
  );
@@ -1228,7 +1241,7 @@ async function collectReleaseEvidenceReferences(references) {
1228
1241
  const nested = (value) => value != null && typeof value === "object" && !Array.isArray(value) ? value : null;
1229
1242
  const array = (value) => Array.isArray(value) ? value : [];
1230
1243
  const semanticallyValid = reference.kind === "core_research" ? artifact.schema === "tradejs-core-research-result/v1" || artifact.schema === "tradejs-core-research-manifest/v1" && artifact.status === "completed" : reference.kind === "ai_gate" ? nested(artifact.run)?.mode === "local-deterministic" && nested(artifact.research)?.lineage != null && nested(artifact.outcome) != null : reference.kind === "runtime_parity" ? artifact.reportType === "replay-runtime-evidence" && nested(nested(artifact.replay)?.runtimeComparison) != null : reference.kind === "execution_calibration" ? artifact.reportType === "execution-calibration" && nested(artifact.summary) != null : reference.kind === "runtime_evidence" ? artifact.reportType === "runtime-evidence" : artifact.reportType === "runtime-scorecard";
1231
- assert(
1244
+ assert2(
1232
1245
  semanticallyValid,
1233
1246
  `evidence ${reference.artifactId} does not match ${reference.kind}`
1234
1247
  );
@@ -26,7 +26,7 @@ module.exports = __toCommonJS(test_ml_exports);
26
26
  var import_config = require("dotenv/config");
27
27
  var import_constants = require("@tradejs/core/constants");
28
28
  var import_ml = require("@tradejs/infra/ml");
29
- var import_strategies = require("@tradejs/strategies");
29
+ var import_registry = require("@tradejs/node/registry");
30
30
  var now = Date.now();
31
31
  var INTERVAL_MIN = 15;
32
32
  var CANDLES = import_constants.ML_BASE_CANDLES_WINDOW;
@@ -70,6 +70,12 @@ var buildTrendline = (entryTs, price) => ({
70
70
  ]
71
71
  });
72
72
  var main = async () => {
73
+ const defaultConfig = await (0, import_registry.getStrategyDefaults)("TrendLine", process.cwd());
74
+ if (!defaultConfig) {
75
+ throw new Error(
76
+ "TrendLine is not registered by the current tradejs.config.ts"
77
+ );
78
+ }
73
79
  const startTs = now - CANDLES * INTERVAL_MIN * 6e4;
74
80
  const candles = makeCandles(120, startTs);
75
81
  const btcCandles = makeCandles(42e3, startTs);
@@ -120,7 +126,7 @@ var main = async () => {
120
126
  volume24h: makeSeries(10, 8e3, 200)
121
127
  }
122
128
  };
123
- const { TRENDLINE, HIGHS, LOWS, ML_THRESHOLD } = import_strategies.trendLineDefaultConfig;
129
+ const { TRENDLINE, HIGHS, LOWS, ML_THRESHOLD } = defaultConfig;
124
130
  const fullRow = (0, import_ml.buildMlTrainingRow)(
125
131
  {
126
132
  signal,