@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.
- package/dist/cli.js +2606 -2171
- package/dist/lib/aiPocketSearch.js +10 -0
- package/dist/lib/aiTrainResearch.js +24 -15
- package/dist/lib/researchAgent.js +16 -2
- package/dist/lib/runtimeLineage.js +24 -15
- package/dist/lib/strategyGateFingerprint.js +26 -15
- package/dist/lib/strategyRelease.js +266 -253
- package/dist/scripts/agentRun.js +50 -45
- package/dist/scripts/aiPocketSearch.js +2269 -1972
- package/dist/scripts/aiTrain.js +24 -15
- package/dist/scripts/backtest.js +2 -2
- package/dist/scripts/replay.js +24 -15
- package/dist/scripts/replayRunner.js +24 -15
- package/dist/scripts/researchAuto.js +2 -2
- package/dist/scripts/runtimeEvidence.js +1 -0
- package/dist/scripts/runtimeScorecard.js +108 -103
- package/dist/scripts/signals.js +35 -17
- package/dist/scripts/signalsDaemon.js +35 -17
- package/dist/scripts/strategyRelease.js +222 -209
- package/dist/scripts/test-ml.js +8 -2
- package/package.json +8 -9
package/dist/scripts/aiTrain.js
CHANGED
|
@@ -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
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
"
|
|
1012
|
-
"
|
|
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
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
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.
|
|
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.
|
|
1050
|
+
gateFingerprintFiles: sourceEntries.map((entry) => entry.specifier)
|
|
1042
1051
|
};
|
|
1043
1052
|
};
|
|
1044
1053
|
|
package/dist/scripts/backtest.js
CHANGED
|
@@ -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 (
|
|
5714
|
+
if (deploymentStrategy?.enabled === false) {
|
|
5715
5715
|
throw new Error(
|
|
5716
|
-
`Strategy ${config.strategyName} is
|
|
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;
|
package/dist/scripts/replay.js
CHANGED
|
@@ -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
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
"
|
|
4921
|
-
"
|
|
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
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
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.
|
|
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.
|
|
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
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
"
|
|
4924
|
-
"
|
|
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
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
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.
|
|
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.
|
|
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
|
|
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,
|
|
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
|
|
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:
|
|
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 ===
|
|
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,
|
package/dist/scripts/signals.js
CHANGED
|
@@ -3762,7 +3762,10 @@ var loadRuntimeStrategies = async ({
|
|
|
3762
3762
|
interval
|
|
3763
3763
|
}) => {
|
|
3764
3764
|
const deploymentStrategies = new Map(
|
|
3765
|
-
(deployment?.strategies ?? []).
|
|
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 (
|
|
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
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
"
|
|
4018
|
-
"
|
|
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
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
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.
|
|
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.
|
|
4065
|
+
gateFingerprintFiles: sourceEntries.map((entry) => entry.specifier)
|
|
4048
4066
|
};
|
|
4049
4067
|
};
|
|
4050
4068
|
|