@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.
@@ -1001,28 +1001,37 @@ var import_types = require("@tradejs/types");
1001
1001
 
1002
1002
  // src/lib/strategyGateFingerprint.ts
1003
1003
  var import_node_crypto = require("crypto");
1004
+ var import_node_module = require("module");
1004
1005
  var import_promises = __toESM(require("fs/promises"));
1005
1006
  var import_node_path = __toESM(require("path"));
1006
- var gateFingerprintRelativePaths = (strategyName) => [
1007
- `packages/strategies/src/${strategyName}/adapters/ai.ts`,
1008
- `packages/strategies/src/${strategyName}/guardrails.ts`,
1009
- `packages/strategies/src/${strategyName}/pockets.ts`,
1010
- `packages/strategies/src/${strategyName}/config.ts`,
1011
- "packages/strategies/src/shared/localAiGate.ts",
1012
- "packages/node/src/ai.ts",
1013
- "packages/core/src/utils/strategyHelpers/signalBuilders.ts"
1007
+ var toPackageToken = (strategyName) => strategyName.replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").replace(/([a-z\d])([A-Z])/g, "$1-$2").toLowerCase();
1008
+ var getStrategyPackageName = (strategyName) => strategyName === "TrendLine" || strategyName === "ReverseTrendLine" ? "@tradejs/strategy-trend-line" : `@tradejs/strategy-${toPackageToken(strategyName)}`;
1009
+ var gateFingerprintSpecifiers = (strategyName) => [
1010
+ getStrategyPackageName(strategyName),
1011
+ "@tradejs/strategy-kit/ai-gate",
1012
+ "@tradejs/node/ai",
1013
+ "@tradejs/core/strategies"
1014
1014
  ];
1015
1015
  var resolveStrategyGateFingerprint = async ({
1016
1016
  projectRoot,
1017
1017
  strategyName,
1018
1018
  gitSha
1019
1019
  }) => {
1020
- const candidates = await Promise.all(
1021
- gateFingerprintRelativePaths(strategyName).map(async (relativePath) => {
1022
- const content = await import_promises.default.readFile(import_node_path.default.join(projectRoot, relativePath)).catch(() => null);
1023
- return content == null ? null : { relativePath, content };
1020
+ const projectManifestPath = import_node_path.default.join(projectRoot, "package.json");
1021
+ const projectExists = await import_promises.default.access(projectManifestPath).then(() => true).catch(() => false);
1022
+ const requireFromProject = projectExists ? (0, import_node_module.createRequire)(projectManifestPath) : null;
1023
+ const candidates = requireFromProject ? await Promise.all(
1024
+ gateFingerprintSpecifiers(strategyName).map(async (specifier) => {
1025
+ let resolvedPath;
1026
+ try {
1027
+ resolvedPath = requireFromProject.resolve(specifier);
1028
+ } catch {
1029
+ return null;
1030
+ }
1031
+ const content = await import_promises.default.readFile(resolvedPath).catch(() => null);
1032
+ return content == null ? null : { specifier, content };
1024
1033
  })
1025
- );
1034
+ ) : [];
1026
1035
  const sourceEntries = candidates.filter(
1027
1036
  (entry) => entry != null
1028
1037
  );
@@ -1030,7 +1039,7 @@ var resolveStrategyGateFingerprint = async ({
1030
1039
  hash.update(strategyName);
1031
1040
  if (sourceEntries.length) {
1032
1041
  for (const entry of sourceEntries) {
1033
- hash.update(entry.relativePath);
1042
+ hash.update(entry.specifier);
1034
1043
  hash.update(entry.content);
1035
1044
  }
1036
1045
  } else {
@@ -1038,7 +1047,7 @@ var resolveStrategyGateFingerprint = async ({
1038
1047
  }
1039
1048
  return {
1040
1049
  gateFingerprint: hash.digest("hex").slice(0, 16),
1041
- gateFingerprintFiles: sourceEntries.map((entry) => entry.relativePath)
1050
+ gateFingerprintFiles: sourceEntries.map((entry) => entry.specifier)
1042
1051
  };
1043
1052
  };
1044
1053
 
@@ -5711,9 +5711,9 @@ var backtest = async () => {
5711
5711
  const deploymentStrategy = preparedRun.deployment?.strategies.find(
5712
5712
  ({ strategyName }) => strategyName === config.strategyName
5713
5713
  );
5714
- if (preparedRun.deployment && !deploymentStrategy) {
5714
+ if (deploymentStrategy?.enabled === false) {
5715
5715
  throw new Error(
5716
- `Strategy ${config.strategyName} is not enabled in deployment ${preparedRun.deployment.id}`
5716
+ `Strategy ${config.strategyName} is disabled in deployment ${preparedRun.deployment?.id}`
5717
5717
  );
5718
5718
  }
5719
5719
  const policyProfileId = (typeof flags.policyProfile === "string" && flags.policyProfile.trim() ? flags.policyProfile.trim() : deploymentStrategy?.policyProfileId) || void 0;
@@ -4910,28 +4910,37 @@ var import_strategyReleaseEvidence = require("@tradejs/infra/strategyReleaseEvid
4910
4910
 
4911
4911
  // src/lib/strategyGateFingerprint.ts
4912
4912
  var import_node_crypto = require("crypto");
4913
+ var import_node_module = require("module");
4913
4914
  var import_promises = __toESM(require("fs/promises"));
4914
4915
  var import_node_path = __toESM(require("path"));
4915
- var gateFingerprintRelativePaths = (strategyName) => [
4916
- `packages/strategies/src/${strategyName}/adapters/ai.ts`,
4917
- `packages/strategies/src/${strategyName}/guardrails.ts`,
4918
- `packages/strategies/src/${strategyName}/pockets.ts`,
4919
- `packages/strategies/src/${strategyName}/config.ts`,
4920
- "packages/strategies/src/shared/localAiGate.ts",
4921
- "packages/node/src/ai.ts",
4922
- "packages/core/src/utils/strategyHelpers/signalBuilders.ts"
4916
+ var toPackageToken = (strategyName) => strategyName.replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").replace(/([a-z\d])([A-Z])/g, "$1-$2").toLowerCase();
4917
+ var getStrategyPackageName = (strategyName) => strategyName === "TrendLine" || strategyName === "ReverseTrendLine" ? "@tradejs/strategy-trend-line" : `@tradejs/strategy-${toPackageToken(strategyName)}`;
4918
+ var gateFingerprintSpecifiers = (strategyName) => [
4919
+ getStrategyPackageName(strategyName),
4920
+ "@tradejs/strategy-kit/ai-gate",
4921
+ "@tradejs/node/ai",
4922
+ "@tradejs/core/strategies"
4923
4923
  ];
4924
4924
  var resolveStrategyGateFingerprint = async ({
4925
4925
  projectRoot,
4926
4926
  strategyName,
4927
4927
  gitSha
4928
4928
  }) => {
4929
- const candidates = await Promise.all(
4930
- gateFingerprintRelativePaths(strategyName).map(async (relativePath) => {
4931
- const content = await import_promises.default.readFile(import_node_path.default.join(projectRoot, relativePath)).catch(() => null);
4932
- return content == null ? null : { relativePath, content };
4929
+ const projectManifestPath = import_node_path.default.join(projectRoot, "package.json");
4930
+ const projectExists = await import_promises.default.access(projectManifestPath).then(() => true).catch(() => false);
4931
+ const requireFromProject = projectExists ? (0, import_node_module.createRequire)(projectManifestPath) : null;
4932
+ const candidates = requireFromProject ? await Promise.all(
4933
+ gateFingerprintSpecifiers(strategyName).map(async (specifier) => {
4934
+ let resolvedPath;
4935
+ try {
4936
+ resolvedPath = requireFromProject.resolve(specifier);
4937
+ } catch {
4938
+ return null;
4939
+ }
4940
+ const content = await import_promises.default.readFile(resolvedPath).catch(() => null);
4941
+ return content == null ? null : { specifier, content };
4933
4942
  })
4934
- );
4943
+ ) : [];
4935
4944
  const sourceEntries = candidates.filter(
4936
4945
  (entry) => entry != null
4937
4946
  );
@@ -4939,7 +4948,7 @@ var resolveStrategyGateFingerprint = async ({
4939
4948
  hash.update(strategyName);
4940
4949
  if (sourceEntries.length) {
4941
4950
  for (const entry of sourceEntries) {
4942
- hash.update(entry.relativePath);
4951
+ hash.update(entry.specifier);
4943
4952
  hash.update(entry.content);
4944
4953
  }
4945
4954
  } else {
@@ -4947,7 +4956,7 @@ var resolveStrategyGateFingerprint = async ({
4947
4956
  }
4948
4957
  return {
4949
4958
  gateFingerprint: hash.digest("hex").slice(0, 16),
4950
- gateFingerprintFiles: sourceEntries.map((entry) => entry.relativePath)
4959
+ gateFingerprintFiles: sourceEntries.map((entry) => entry.specifier)
4951
4960
  };
4952
4961
  };
4953
4962
 
@@ -4913,28 +4913,37 @@ var import_strategyReleaseEvidence = require("@tradejs/infra/strategyReleaseEvid
4913
4913
 
4914
4914
  // src/lib/strategyGateFingerprint.ts
4915
4915
  var import_node_crypto = require("crypto");
4916
+ var import_node_module = require("module");
4916
4917
  var import_promises = __toESM(require("fs/promises"));
4917
4918
  var import_node_path = __toESM(require("path"));
4918
- var gateFingerprintRelativePaths = (strategyName) => [
4919
- `packages/strategies/src/${strategyName}/adapters/ai.ts`,
4920
- `packages/strategies/src/${strategyName}/guardrails.ts`,
4921
- `packages/strategies/src/${strategyName}/pockets.ts`,
4922
- `packages/strategies/src/${strategyName}/config.ts`,
4923
- "packages/strategies/src/shared/localAiGate.ts",
4924
- "packages/node/src/ai.ts",
4925
- "packages/core/src/utils/strategyHelpers/signalBuilders.ts"
4919
+ var toPackageToken = (strategyName) => strategyName.replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").replace(/([a-z\d])([A-Z])/g, "$1-$2").toLowerCase();
4920
+ var getStrategyPackageName = (strategyName) => strategyName === "TrendLine" || strategyName === "ReverseTrendLine" ? "@tradejs/strategy-trend-line" : `@tradejs/strategy-${toPackageToken(strategyName)}`;
4921
+ var gateFingerprintSpecifiers = (strategyName) => [
4922
+ getStrategyPackageName(strategyName),
4923
+ "@tradejs/strategy-kit/ai-gate",
4924
+ "@tradejs/node/ai",
4925
+ "@tradejs/core/strategies"
4926
4926
  ];
4927
4927
  var resolveStrategyGateFingerprint = async ({
4928
4928
  projectRoot,
4929
4929
  strategyName,
4930
4930
  gitSha
4931
4931
  }) => {
4932
- const candidates = await Promise.all(
4933
- gateFingerprintRelativePaths(strategyName).map(async (relativePath) => {
4934
- const content = await import_promises.default.readFile(import_node_path.default.join(projectRoot, relativePath)).catch(() => null);
4935
- return content == null ? null : { relativePath, content };
4932
+ const projectManifestPath = import_node_path.default.join(projectRoot, "package.json");
4933
+ const projectExists = await import_promises.default.access(projectManifestPath).then(() => true).catch(() => false);
4934
+ const requireFromProject = projectExists ? (0, import_node_module.createRequire)(projectManifestPath) : null;
4935
+ const candidates = requireFromProject ? await Promise.all(
4936
+ gateFingerprintSpecifiers(strategyName).map(async (specifier) => {
4937
+ let resolvedPath;
4938
+ try {
4939
+ resolvedPath = requireFromProject.resolve(specifier);
4940
+ } catch {
4941
+ return null;
4942
+ }
4943
+ const content = await import_promises.default.readFile(resolvedPath).catch(() => null);
4944
+ return content == null ? null : { specifier, content };
4936
4945
  })
4937
- );
4946
+ ) : [];
4938
4947
  const sourceEntries = candidates.filter(
4939
4948
  (entry) => entry != null
4940
4949
  );
@@ -4942,7 +4951,7 @@ var resolveStrategyGateFingerprint = async ({
4942
4951
  hash.update(strategyName);
4943
4952
  if (sourceEntries.length) {
4944
4953
  for (const entry of sourceEntries) {
4945
- hash.update(entry.relativePath);
4954
+ hash.update(entry.specifier);
4946
4955
  hash.update(entry.content);
4947
4956
  }
4948
4957
  } else {
@@ -4950,7 +4959,7 @@ var resolveStrategyGateFingerprint = async ({
4950
4959
  }
4951
4960
  return {
4952
4961
  gateFingerprint: hash.digest("hex").slice(0, 16),
4953
- gateFingerprintFiles: sourceEntries.map((entry) => entry.relativePath)
4962
+ gateFingerprintFiles: sourceEntries.map((entry) => entry.specifier)
4954
4963
  };
4955
4964
  };
4956
4965
 
@@ -62,7 +62,7 @@ var import_constants = require("@tradejs/core/constants");
62
62
  var import_ai = require("@tradejs/infra/ai");
63
63
  var import_logger = require("@tradejs/infra/logger");
64
64
  var import_redis2 = require("@tradejs/infra/redis");
65
- var import_strategies = require("@tradejs/strategies");
65
+ var import_registry = require("@tradejs/node/registry");
66
66
 
67
67
  // src/lib/runtimeRedis.ts
68
68
  var import_redis = require("@tradejs/infra/redis");
@@ -762,7 +762,7 @@ var main = async () => {
762
762
  target.strategy
763
763
  );
764
764
  const backtestConfigKey = import_redis2.redisKeys.backtestConfig(userName, target.config);
765
- const defaults = (0, import_strategies.getBuiltInStrategyDefaultConfig)(target.strategy) || {};
765
+ const defaults = await (0, import_registry.getStrategyDefaults)(target.strategy, projectRoot) || {};
766
766
  const userStrategyConfig = await (0, import_redis2.getData)(
767
767
  strategyConfigKey,
768
768
  {}
@@ -568,6 +568,7 @@ var import_strategyReleaseEvidence = require("@tradejs/infra/strategyReleaseEvid
568
568
 
569
569
  // src/lib/strategyGateFingerprint.ts
570
570
  var import_node_crypto2 = require("crypto");
571
+ var import_node_module = require("module");
571
572
  var import_promises2 = __toESM(require("fs/promises"));
572
573
  var import_node_path2 = __toESM(require("path"));
573
574
 
@@ -752,8 +752,113 @@ ${reactions}
752
752
  var import_node_crypto2 = require("crypto");
753
753
  var import_promises3 = __toESM(require("fs/promises"));
754
754
  var import_node_path3 = __toESM(require("path"));
755
- var import_types = require("@tradejs/types");
755
+ var import_types2 = require("@tradejs/types");
756
756
  var import_strategyReleaseEvidence = require("@tradejs/infra/strategyReleaseEvidence");
757
+
758
+ // src/lib/strategyRelease/liveDiagnosis.ts
759
+ var import_types = require("@tradejs/types");
760
+ var buildStrategyLiveDiagnosis = (input) => {
761
+ const minimumClosedTrades = input.minimumClosedTrades ?? 20;
762
+ const minimumParityRatio = input.minimumParityRatio ?? 0.95;
763
+ const maximumOrderFailureRate = input.maximumOrderFailureRate ?? 0.05;
764
+ const minimumRegimeCoverage = input.minimumRegimeCoverage ?? 0.5;
765
+ const hasExplicitRiskScale = input.riskScaleComparable != null;
766
+ const comparisonObservedDrawdown = hasExplicitRiskScale ? input.riskScaleComparable ? input.normalizedObservedDrawdown ?? null : null : input.observedDrawdown;
767
+ const runtimeDivergence = !input.lineageComparable || input.parityRatio != null && input.parityRatio < minimumParityRatio || input.orderFailureRate != null && input.orderFailureRate > maximumOrderFailureRate;
768
+ const insufficient = input.closedTrades < minimumClosedTrades || input.parityRatio == null || comparisonObservedDrawdown == null || input.historicalDrawdownP95 == null;
769
+ const expected = !runtimeDivergence && !insufficient && comparisonObservedDrawdown <= input.historicalDrawdownP95;
770
+ const verdict = runtimeDivergence ? "RUNTIME_DIVERGENCE" : insufficient ? "INSUFFICIENT_EVIDENCE" : expected ? "EXPECTED_DRAWDOWN" : "GENERALIZATION_FAILURE";
771
+ const attributionIncomplete = input.rawCoreExpectancyDelta == null || input.aiGateAddedValue == null || input.regimeCoverage == null;
772
+ const attributedVerdict = verdict === "GENERALIZATION_FAILURE" && attributionIncomplete ? "INSUFFICIENT_EVIDENCE" : verdict;
773
+ 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";
774
+ 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.";
775
+ const recommendations = attributedVerdict === "RUNTIME_DIVERGENCE" ? [
776
+ "Resolve lineage and replay parity mismatches before evaluating strategy economics.",
777
+ "Inspect order failures and execution residuals against the frozen execution model."
778
+ ] : attributedVerdict === "EXPECTED_DRAWDOWN" ? [
779
+ "Keep the frozen composition unchanged and continue prospective evidence collection."
780
+ ] : attributedVerdict === "GENERALIZATION_FAILURE" ? [
781
+ "Do not retune the live cohort; open a new immutable research lineage.",
782
+ "Compare raw-core decay, AI-gate added value, and causal regime coverage."
783
+ ] : [
784
+ 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."
785
+ ];
786
+ return {
787
+ schema: import_types.STRATEGY_LIVE_DIAGNOSIS_SCHEMA,
788
+ strategy: input.strategy,
789
+ compositionId: input.compositionId,
790
+ createdAt: input.createdAt,
791
+ verdict: attributedVerdict,
792
+ subtype,
793
+ confidence: runtimeDivergence || input.closedTrades >= 50 ? "high" : input.closedTrades >= 20 ? "medium" : "low",
794
+ evidence: {
795
+ lineageComparable: input.lineageComparable,
796
+ riskScaleComparable: input.riskScaleComparable ?? false,
797
+ releaseMaxLossValue: input.releaseMaxLossValue ?? null,
798
+ runtimeMaxLossValue: input.runtimeMaxLossValue ?? null,
799
+ riskScaleRatio: input.riskScaleRatio ?? null,
800
+ parityRatio: input.parityRatio,
801
+ orderFailureRate: input.orderFailureRate,
802
+ observedDrawdown: input.observedDrawdown,
803
+ normalizedObservedDrawdown: input.normalizedObservedDrawdown ?? null,
804
+ historicalDrawdownP95: input.historicalDrawdownP95,
805
+ historicalDrawdownMaximum: input.historicalDrawdownMaximum,
806
+ closedTrades: input.closedTrades,
807
+ rawCoreExpectancyDelta: input.rawCoreExpectancyDelta,
808
+ aiGateAddedValue: input.aiGateAddedValue,
809
+ regimeCoverage: input.regimeCoverage,
810
+ overfitProbability: input.overfitProbability
811
+ },
812
+ explanation,
813
+ recommendations
814
+ };
815
+ };
816
+ var buildStrategyLiveDiagnosisFromScorecard = ({
817
+ manifest,
818
+ scorecard,
819
+ days
820
+ }) => {
821
+ const rolling = scorecard.rolling.find((entry) => entry.days === days);
822
+ const envelope = manifest.monitoring.drawdownEnvelopes.find(
823
+ (entry) => entry.days === days
824
+ );
825
+ const orderFailureRate = scorecard.funnel.orderAttempts ? scorecard.funnel.orderFailures / scorecard.funnel.orderAttempts : null;
826
+ const lineage = scorecard.lineage;
827
+ const lineageComparable = scorecard.parity.lineageReason == null && lineage?.complete === true && lineage.conflicts === false && lineage.compositionId === manifest.composition.compositionId && lineage.gitDirty === false && lineage.gitSha === manifest.composition.gitSha && lineage.configFingerprint === manifest.composition.runtimeConfigFingerprint && lineage.gateFingerprint === manifest.composition.gateFingerprint && lineage.contextFingerprint === manifest.composition.runtimeContextFingerprint;
828
+ const releaseMaxLossValue = manifest.composition.maxLossValue;
829
+ const runtimeMaxLossValue = lineage?.maxLossValue ?? null;
830
+ const riskScaleComparable = Number.isFinite(releaseMaxLossValue) && releaseMaxLossValue > 0 && runtimeMaxLossValue != null && Number.isFinite(runtimeMaxLossValue) && runtimeMaxLossValue > 0;
831
+ const riskScaleRatio = riskScaleComparable ? runtimeMaxLossValue / releaseMaxLossValue : null;
832
+ const normalizedObservedDrawdown = rolling != null && riskScaleRatio != null ? rolling.maxDrawdown / riskScaleRatio : null;
833
+ const normalizeRuntimeEconomics = (value) => value != null && riskScaleRatio != null ? value / riskScaleRatio : null;
834
+ return buildStrategyLiveDiagnosis({
835
+ strategy: manifest.strategy,
836
+ compositionId: manifest.composition.compositionId,
837
+ createdAt: scorecard.generatedAt,
838
+ lineageComparable,
839
+ riskScaleComparable,
840
+ releaseMaxLossValue,
841
+ runtimeMaxLossValue,
842
+ riskScaleRatio,
843
+ parityRatio: scorecard.parity.ratio,
844
+ orderFailureRate,
845
+ observedDrawdown: rolling?.maxDrawdown ?? null,
846
+ normalizedObservedDrawdown,
847
+ historicalDrawdownP95: envelope?.p95 ?? null,
848
+ historicalDrawdownMaximum: envelope?.maximum ?? null,
849
+ closedTrades: rolling?.closedTrades ?? 0,
850
+ rawCoreExpectancyDelta: scorecard.prospective?.rawCoreExpectancy != null && manifest.monitoring.rawCoreExpectancy != null ? normalizeRuntimeEconomics(scorecard.prospective.rawCoreExpectancy) - manifest.monitoring.rawCoreExpectancy : null,
851
+ aiGateAddedValue: scorecard.prospective?.rawCoreExpectancy != null && scorecard.prospective.aiGateExpectancy != null ? normalizeRuntimeEconomics(scorecard.prospective.aiGateExpectancy) - normalizeRuntimeEconomics(scorecard.prospective.rawCoreExpectancy) : null,
852
+ regimeCoverage: scorecard.prospective?.regimeCoverage ?? null,
853
+ overfitProbability: manifest.monitoring.overfitProbability,
854
+ minimumClosedTrades: manifest.monitoring.minimumProspectiveClosedTrades,
855
+ minimumParityRatio: manifest.monitoring.minimumParityRatio,
856
+ maximumOrderFailureRate: manifest.monitoring.maximumOrderFailureRate,
857
+ minimumRegimeCoverage: manifest.monitoring.minimumRegimeCoverage
858
+ });
859
+ };
860
+
861
+ // src/lib/strategyRelease.ts
757
862
  var SHA256_RE = /^[a-f0-9]{64}$/;
758
863
  var LINEAGE_FINGERPRINT_RE = /^[a-f0-9]{16}$/;
759
864
  var safeSegment2 = import_strategyReleaseEvidence.safeStrategyEvidenceSegment;
@@ -1128,7 +1233,7 @@ var createStrategyReleaseManifest = (input) => {
1128
1233
  verdict
1129
1234
  };
1130
1235
  return {
1131
- schema: import_types.STRATEGY_RELEASE_SCHEMA,
1236
+ schema: import_types2.STRATEGY_RELEASE_SCHEMA,
1132
1237
  releaseId: `${safeSegment2(input.strategy, "strategy")}_${compactTimestamp(input.createdAt)}_${strategyReleaseSha256(releaseIdentity).slice(0, 16)}`,
1133
1238
  strategy: input.strategy,
1134
1239
  createdAt: input.createdAt,
@@ -1163,7 +1268,7 @@ var verifyStrategyReleaseEnvelope = async (valueOrPath) => {
1163
1268
  );
1164
1269
  const manifest = envelope.manifest;
1165
1270
  assert(
1166
- manifest.schema === import_types.STRATEGY_RELEASE_SCHEMA,
1271
+ manifest.schema === import_types2.STRATEGY_RELEASE_SCHEMA,
1167
1272
  "manifest schema mismatch"
1168
1273
  );
1169
1274
  const rebuilt = createStrategyReleaseManifest({
@@ -1212,106 +1317,6 @@ var writeJsonAtomic = async (filePath, value) => {
1212
1317
  );
1213
1318
  await import_promises3.default.rename(temporaryPath, filePath);
1214
1319
  };
1215
- var buildStrategyLiveDiagnosis = (input) => {
1216
- const minimumClosedTrades = input.minimumClosedTrades ?? 20;
1217
- const minimumParityRatio = input.minimumParityRatio ?? 0.95;
1218
- const maximumOrderFailureRate = input.maximumOrderFailureRate ?? 0.05;
1219
- const minimumRegimeCoverage = input.minimumRegimeCoverage ?? 0.5;
1220
- const hasExplicitRiskScale = input.riskScaleComparable != null;
1221
- const comparisonObservedDrawdown = hasExplicitRiskScale ? input.riskScaleComparable ? input.normalizedObservedDrawdown ?? null : null : input.observedDrawdown;
1222
- const runtimeDivergence = !input.lineageComparable || input.parityRatio != null && input.parityRatio < minimumParityRatio || input.orderFailureRate != null && input.orderFailureRate > maximumOrderFailureRate;
1223
- const insufficient = input.closedTrades < minimumClosedTrades || input.parityRatio == null || comparisonObservedDrawdown == null || input.historicalDrawdownP95 == null;
1224
- const expected = !runtimeDivergence && !insufficient && comparisonObservedDrawdown <= input.historicalDrawdownP95;
1225
- const verdict = runtimeDivergence ? "RUNTIME_DIVERGENCE" : insufficient ? "INSUFFICIENT_EVIDENCE" : expected ? "EXPECTED_DRAWDOWN" : "GENERALIZATION_FAILURE";
1226
- const attributionIncomplete = input.rawCoreExpectancyDelta == null || input.aiGateAddedValue == null || input.regimeCoverage == null;
1227
- const attributedVerdict = verdict === "GENERALIZATION_FAILURE" && attributionIncomplete ? "INSUFFICIENT_EVIDENCE" : verdict;
1228
- 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";
1229
- 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.";
1230
- const recommendations = attributedVerdict === "RUNTIME_DIVERGENCE" ? [
1231
- "Resolve lineage and replay parity mismatches before evaluating strategy economics.",
1232
- "Inspect order failures and execution residuals against the frozen execution model."
1233
- ] : attributedVerdict === "EXPECTED_DRAWDOWN" ? [
1234
- "Keep the frozen composition unchanged and continue prospective evidence collection."
1235
- ] : attributedVerdict === "GENERALIZATION_FAILURE" ? [
1236
- "Do not retune the live cohort; open a new immutable research lineage.",
1237
- "Compare raw-core decay, AI-gate added value, and causal regime coverage."
1238
- ] : [
1239
- 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."
1240
- ];
1241
- return {
1242
- schema: import_types.STRATEGY_LIVE_DIAGNOSIS_SCHEMA,
1243
- strategy: input.strategy,
1244
- compositionId: input.compositionId,
1245
- createdAt: input.createdAt,
1246
- verdict: attributedVerdict,
1247
- subtype,
1248
- confidence: runtimeDivergence || input.closedTrades >= 50 ? "high" : input.closedTrades >= 20 ? "medium" : "low",
1249
- evidence: {
1250
- lineageComparable: input.lineageComparable,
1251
- riskScaleComparable: input.riskScaleComparable ?? false,
1252
- releaseMaxLossValue: input.releaseMaxLossValue ?? null,
1253
- runtimeMaxLossValue: input.runtimeMaxLossValue ?? null,
1254
- riskScaleRatio: input.riskScaleRatio ?? null,
1255
- parityRatio: input.parityRatio,
1256
- orderFailureRate: input.orderFailureRate,
1257
- observedDrawdown: input.observedDrawdown,
1258
- normalizedObservedDrawdown: input.normalizedObservedDrawdown ?? null,
1259
- historicalDrawdownP95: input.historicalDrawdownP95,
1260
- historicalDrawdownMaximum: input.historicalDrawdownMaximum,
1261
- closedTrades: input.closedTrades,
1262
- rawCoreExpectancyDelta: input.rawCoreExpectancyDelta,
1263
- aiGateAddedValue: input.aiGateAddedValue,
1264
- regimeCoverage: input.regimeCoverage,
1265
- overfitProbability: input.overfitProbability
1266
- },
1267
- explanation,
1268
- recommendations
1269
- };
1270
- };
1271
- var buildStrategyLiveDiagnosisFromScorecard = ({
1272
- manifest,
1273
- scorecard,
1274
- days
1275
- }) => {
1276
- const rolling = scorecard.rolling.find((entry) => entry.days === days);
1277
- const envelope = manifest.monitoring.drawdownEnvelopes.find(
1278
- (entry) => entry.days === days
1279
- );
1280
- const orderFailureRate = scorecard.funnel.orderAttempts ? scorecard.funnel.orderFailures / scorecard.funnel.orderAttempts : null;
1281
- const lineage = scorecard.lineage;
1282
- const lineageComparable = scorecard.parity.lineageReason == null && lineage?.complete === true && lineage.conflicts === false && lineage.compositionId === manifest.composition.compositionId && lineage.gitDirty === false && lineage.gitSha === manifest.composition.gitSha && lineage.configFingerprint === manifest.composition.runtimeConfigFingerprint && lineage.gateFingerprint === manifest.composition.gateFingerprint && lineage.contextFingerprint === manifest.composition.runtimeContextFingerprint;
1283
- const releaseMaxLossValue = manifest.composition.maxLossValue;
1284
- const runtimeMaxLossValue = lineage?.maxLossValue ?? null;
1285
- const riskScaleComparable = Number.isFinite(releaseMaxLossValue) && releaseMaxLossValue > 0 && runtimeMaxLossValue != null && Number.isFinite(runtimeMaxLossValue) && runtimeMaxLossValue > 0;
1286
- const riskScaleRatio = riskScaleComparable ? runtimeMaxLossValue / releaseMaxLossValue : null;
1287
- const normalizedObservedDrawdown = rolling != null && riskScaleRatio != null ? rolling.maxDrawdown / riskScaleRatio : null;
1288
- const normalizeRuntimeEconomics = (value) => value != null && riskScaleRatio != null ? value / riskScaleRatio : null;
1289
- return buildStrategyLiveDiagnosis({
1290
- strategy: manifest.strategy,
1291
- compositionId: manifest.composition.compositionId,
1292
- createdAt: scorecard.generatedAt,
1293
- lineageComparable,
1294
- riskScaleComparable,
1295
- releaseMaxLossValue,
1296
- runtimeMaxLossValue,
1297
- riskScaleRatio,
1298
- parityRatio: scorecard.parity.ratio,
1299
- orderFailureRate,
1300
- observedDrawdown: rolling?.maxDrawdown ?? null,
1301
- normalizedObservedDrawdown,
1302
- historicalDrawdownP95: envelope?.p95 ?? null,
1303
- historicalDrawdownMaximum: envelope?.maximum ?? null,
1304
- closedTrades: rolling?.closedTrades ?? 0,
1305
- rawCoreExpectancyDelta: scorecard.prospective?.rawCoreExpectancy != null && manifest.monitoring.rawCoreExpectancy != null ? normalizeRuntimeEconomics(scorecard.prospective.rawCoreExpectancy) - manifest.monitoring.rawCoreExpectancy : null,
1306
- aiGateAddedValue: scorecard.prospective?.rawCoreExpectancy != null && scorecard.prospective.aiGateExpectancy != null ? normalizeRuntimeEconomics(scorecard.prospective.aiGateExpectancy) - normalizeRuntimeEconomics(scorecard.prospective.rawCoreExpectancy) : null,
1307
- regimeCoverage: scorecard.prospective?.regimeCoverage ?? null,
1308
- overfitProbability: manifest.monitoring.overfitProbability,
1309
- minimumClosedTrades: manifest.monitoring.minimumProspectiveClosedTrades,
1310
- minimumParityRatio: manifest.monitoring.minimumParityRatio,
1311
- maximumOrderFailureRate: manifest.monitoring.maximumOrderFailureRate,
1312
- minimumRegimeCoverage: manifest.monitoring.minimumRegimeCoverage
1313
- });
1314
- };
1315
1320
  var publishStrategyLiveDiagnosis = async ({
1316
1321
  rootDir,
1317
1322
  diagnosis,
@@ -3762,7 +3762,10 @@ var loadRuntimeStrategies = async ({
3762
3762
  interval
3763
3763
  }) => {
3764
3764
  const deploymentStrategies = new Map(
3765
- (deployment?.strategies ?? []).filter(({ enabled }) => enabled !== false).map((strategy) => [strategy.strategyName, strategy])
3765
+ (deployment?.strategies ?? []).map((strategy) => [
3766
+ strategy.strategyName,
3767
+ strategy
3768
+ ])
3766
3769
  );
3767
3770
  const strategyConfigs = await Promise.all(
3768
3771
  (await (0, import_runtimeStrategyConfigs.loadRuntimeStrategyConfigs)(userName)).map(
@@ -3773,7 +3776,13 @@ var loadRuntimeStrategies = async ({
3773
3776
  strategyConfig
3774
3777
  }) => {
3775
3778
  const deploymentStrategy = deployment ? deploymentStrategies.get(strategyName) : void 0;
3776
- if (deployment && !deploymentStrategy) return null;
3779
+ if (deploymentStrategy?.enabled === false) {
3780
+ import_logger.logger.info(
3781
+ "Skip deployment-disabled strategy config: %s",
3782
+ strategyName
3783
+ );
3784
+ return null;
3785
+ }
3777
3786
  if (!isRuntimeStrategyEnabled(strategyConfig)) {
3778
3787
  import_logger.logger.info(
3779
3788
  "Skip inactive strategy config by ENABLE=false: %s",
@@ -4007,28 +4016,37 @@ var import_strategyReleaseEvidence = require("@tradejs/infra/strategyReleaseEvid
4007
4016
 
4008
4017
  // src/lib/strategyGateFingerprint.ts
4009
4018
  var import_node_crypto = require("crypto");
4019
+ var import_node_module = require("module");
4010
4020
  var import_promises = __toESM(require("fs/promises"));
4011
4021
  var import_node_path = __toESM(require("path"));
4012
- var gateFingerprintRelativePaths = (strategyName) => [
4013
- `packages/strategies/src/${strategyName}/adapters/ai.ts`,
4014
- `packages/strategies/src/${strategyName}/guardrails.ts`,
4015
- `packages/strategies/src/${strategyName}/pockets.ts`,
4016
- `packages/strategies/src/${strategyName}/config.ts`,
4017
- "packages/strategies/src/shared/localAiGate.ts",
4018
- "packages/node/src/ai.ts",
4019
- "packages/core/src/utils/strategyHelpers/signalBuilders.ts"
4022
+ var toPackageToken = (strategyName) => strategyName.replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").replace(/([a-z\d])([A-Z])/g, "$1-$2").toLowerCase();
4023
+ var getStrategyPackageName = (strategyName) => strategyName === "TrendLine" || strategyName === "ReverseTrendLine" ? "@tradejs/strategy-trend-line" : `@tradejs/strategy-${toPackageToken(strategyName)}`;
4024
+ var gateFingerprintSpecifiers = (strategyName) => [
4025
+ getStrategyPackageName(strategyName),
4026
+ "@tradejs/strategy-kit/ai-gate",
4027
+ "@tradejs/node/ai",
4028
+ "@tradejs/core/strategies"
4020
4029
  ];
4021
4030
  var resolveStrategyGateFingerprint = async ({
4022
4031
  projectRoot,
4023
4032
  strategyName,
4024
4033
  gitSha
4025
4034
  }) => {
4026
- const candidates = await Promise.all(
4027
- gateFingerprintRelativePaths(strategyName).map(async (relativePath) => {
4028
- const content = await import_promises.default.readFile(import_node_path.default.join(projectRoot, relativePath)).catch(() => null);
4029
- return content == null ? null : { relativePath, content };
4035
+ const projectManifestPath = import_node_path.default.join(projectRoot, "package.json");
4036
+ const projectExists = await import_promises.default.access(projectManifestPath).then(() => true).catch(() => false);
4037
+ const requireFromProject = projectExists ? (0, import_node_module.createRequire)(projectManifestPath) : null;
4038
+ const candidates = requireFromProject ? await Promise.all(
4039
+ gateFingerprintSpecifiers(strategyName).map(async (specifier) => {
4040
+ let resolvedPath;
4041
+ try {
4042
+ resolvedPath = requireFromProject.resolve(specifier);
4043
+ } catch {
4044
+ return null;
4045
+ }
4046
+ const content = await import_promises.default.readFile(resolvedPath).catch(() => null);
4047
+ return content == null ? null : { specifier, content };
4030
4048
  })
4031
- );
4049
+ ) : [];
4032
4050
  const sourceEntries = candidates.filter(
4033
4051
  (entry) => entry != null
4034
4052
  );
@@ -4036,7 +4054,7 @@ var resolveStrategyGateFingerprint = async ({
4036
4054
  hash.update(strategyName);
4037
4055
  if (sourceEntries.length) {
4038
4056
  for (const entry of sourceEntries) {
4039
- hash.update(entry.relativePath);
4057
+ hash.update(entry.specifier);
4040
4058
  hash.update(entry.content);
4041
4059
  }
4042
4060
  } else {
@@ -4044,7 +4062,7 @@ var resolveStrategyGateFingerprint = async ({
4044
4062
  }
4045
4063
  return {
4046
4064
  gateFingerprint: hash.digest("hex").slice(0, 16),
4047
- gateFingerprintFiles: sourceEntries.map((entry) => entry.relativePath)
4065
+ gateFingerprintFiles: sourceEntries.map((entry) => entry.specifier)
4048
4066
  };
4049
4067
  };
4050
4068