@tradejs/cli 2.0.20 → 3.0.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 +267 -98
- package/dist/lib/aiTrainOptions.js +36 -0
- package/dist/lib/coreResearch.js +73 -8
- package/dist/lib/runtimeLineage.js +2 -14
- package/dist/lib/strategyRelease.js +82 -17
- package/dist/scripts/agentRun.js +22 -23
- package/dist/scripts/aiPocketSearch.js +2 -1
- package/dist/scripts/aiTrain.js +64 -13
- package/dist/scripts/coreResearch.js +73 -8
- package/dist/scripts/replay.js +2 -14
- package/dist/scripts/replayRunner.js +2 -14
- package/dist/scripts/runtimeEvidence.js +1 -0
- package/dist/scripts/runtimeScorecard.js +35 -2
- package/dist/scripts/signals.js +2 -14
- package/dist/scripts/signalsDaemon.js +2 -14
- package/dist/scripts/strategyRelease.js +82 -17
- package/package.json +9 -9
|
@@ -75,6 +75,12 @@ var hasExactRuntimeTarget = (target) => Boolean(
|
|
|
75
75
|
);
|
|
76
76
|
var deriveStrategyReleaseResearchDecision = async (input) => {
|
|
77
77
|
const blockers = [];
|
|
78
|
+
const directionPolicy = input.directionPolicy ?? "both";
|
|
79
|
+
const activeDirections = directionPolicy === "long_only" ? ["long"] : directionPolicy === "short_only" ? ["short"] : ["long", "short"];
|
|
80
|
+
const suppressedDirection = directionPolicy === "long_only" ? "short" : directionPolicy === "short_only" ? "long" : null;
|
|
81
|
+
const hasHistoricalEdge = (entry) => entry.pnl > 0 && entry.profitFactor >= 1 && activeDirections.every(
|
|
82
|
+
(direction) => entry[direction].pnl > 0 && entry[direction].profitFactor >= 1
|
|
83
|
+
) && (suppressedDirection == null || entry[suppressedDirection].trades === 0 && entry[suppressedDirection].pnl === 0);
|
|
78
84
|
const byDays = new Map(
|
|
79
85
|
input.historicalWindows.map((entry) => [entry.days, entry])
|
|
80
86
|
);
|
|
@@ -87,17 +93,13 @@ var deriveStrategyReleaseResearchDecision = async (input) => {
|
|
|
87
93
|
const longWindows = REQUIRED_LONG_WINDOWS.map(
|
|
88
94
|
(days) => byDays.get(days)
|
|
89
95
|
).filter((entry) => Boolean(entry));
|
|
90
|
-
if (longWindows.some(
|
|
91
|
-
(entry) => entry.pnl <= 0 || entry.profitFactor < 1 || entry.long.pnl <= 0 || entry.long.profitFactor < 1 || entry.short.pnl <= 0 || entry.short.profitFactor < 1
|
|
92
|
-
)) {
|
|
96
|
+
if (longWindows.some((entry) => !hasHistoricalEdge(entry))) {
|
|
93
97
|
blockers.push("HISTORICAL_EDGE_FAILED");
|
|
94
98
|
}
|
|
95
99
|
const stableTerminalWindows = REQUIRED_STABLE_TERMINAL_WINDOWS.map(
|
|
96
100
|
(days) => byDays.get(days)
|
|
97
101
|
).filter((entry) => Boolean(entry));
|
|
98
|
-
if (stableTerminalWindows.some(
|
|
99
|
-
(entry) => entry.pnl <= 0 || entry.profitFactor < 1 || entry.long.pnl <= 0 || entry.long.profitFactor < 1 || entry.short.pnl <= 0 || entry.short.profitFactor < 1
|
|
100
|
-
)) {
|
|
102
|
+
if (stableTerminalWindows.some((entry) => !hasHistoricalEdge(entry))) {
|
|
101
103
|
blockers.push("HISTORICAL_EDGE_FAILED");
|
|
102
104
|
}
|
|
103
105
|
if (blockers.length) {
|
|
@@ -107,8 +109,9 @@ var deriveStrategyReleaseResearchDecision = async (input) => {
|
|
|
107
109
|
repairAllowed: false,
|
|
108
110
|
targetDirection: input.recentFailure?.direction ?? null,
|
|
109
111
|
maxLossValue: null,
|
|
112
|
+
requiresRuntimeBinding: false,
|
|
110
113
|
blockers,
|
|
111
|
-
summary:
|
|
114
|
+
summary: `The final ${directionPolicy} composition does not have complete positive evidence for ALL and every active direction on the frozen 3y/4y/max-window matrix.`
|
|
112
115
|
};
|
|
113
116
|
}
|
|
114
117
|
const repairAllowed = Boolean(
|
|
@@ -121,6 +124,7 @@ var deriveStrategyReleaseResearchDecision = async (input) => {
|
|
|
121
124
|
repairAllowed: true,
|
|
122
125
|
targetDirection: input.recentFailure.direction,
|
|
123
126
|
maxLossValue: null,
|
|
127
|
+
requiresRuntimeBinding: false,
|
|
124
128
|
blockers: [],
|
|
125
129
|
summary: "Spend the single terminal-direction repair round on the preregistered causal mechanism, then rebuild the full evidence matrix."
|
|
126
130
|
};
|
|
@@ -139,6 +143,7 @@ var deriveStrategyReleaseResearchDecision = async (input) => {
|
|
|
139
143
|
repairAllowed: false,
|
|
140
144
|
targetDirection: input.recentFailure?.direction ?? null,
|
|
141
145
|
maxLossValue: null,
|
|
146
|
+
requiresRuntimeBinding: false,
|
|
142
147
|
blockers,
|
|
143
148
|
summary: "Complete the frozen candidate implementation and full-period chart before any micro-forward execution."
|
|
144
149
|
};
|
|
@@ -150,6 +155,9 @@ var deriveStrategyReleaseResearchDecision = async (input) => {
|
|
|
150
155
|
repairAllowed: false,
|
|
151
156
|
targetDirection: input.recentFailure?.direction ?? null,
|
|
152
157
|
maxLossValue: 1,
|
|
158
|
+
requiresRuntimeBinding: !hasExactRuntimeTarget(
|
|
159
|
+
input.forwardTest.runtimeTarget
|
|
160
|
+
),
|
|
153
161
|
blockers: ["FORWARD_NOT_AUTHORIZED"],
|
|
154
162
|
summary: "The candidate is ready for a separately authorized micro-forward test at MAX_LOSS_VALUE=1."
|
|
155
163
|
};
|
|
@@ -157,12 +165,13 @@ var deriveStrategyReleaseResearchDecision = async (input) => {
|
|
|
157
165
|
if (!hasExactRuntimeTarget(input.forwardTest.runtimeTarget)) {
|
|
158
166
|
return {
|
|
159
167
|
strategy: input.strategy,
|
|
160
|
-
action: "
|
|
168
|
+
action: "MICRO_FORWARD_READY",
|
|
161
169
|
repairAllowed: false,
|
|
162
170
|
targetDirection: input.recentFailure?.direction ?? null,
|
|
163
171
|
maxLossValue: 1,
|
|
164
|
-
|
|
165
|
-
|
|
172
|
+
requiresRuntimeBinding: true,
|
|
173
|
+
blockers: [],
|
|
174
|
+
summary: "The portable micro-forward handoff is ready. Bind it on the runtime server to that server's deployment/account ids and verify canonical composition fingerprints there."
|
|
166
175
|
};
|
|
167
176
|
}
|
|
168
177
|
return {
|
|
@@ -171,6 +180,7 @@ var deriveStrategyReleaseResearchDecision = async (input) => {
|
|
|
171
180
|
repairAllowed: false,
|
|
172
181
|
targetDirection: input.recentFailure?.direction ?? null,
|
|
173
182
|
maxLossValue: 1,
|
|
183
|
+
requiresRuntimeBinding: false,
|
|
174
184
|
blockers: [],
|
|
175
185
|
summary: "Start the exact frozen composition as a micro-forward test at MAX_LOSS_VALUE=1; do not retune an underpowered or exposed recent tail."
|
|
176
186
|
};
|
|
@@ -228,6 +238,7 @@ var deriveReleaseGates = (evidence) => {
|
|
|
228
238
|
};
|
|
229
239
|
};
|
|
230
240
|
var verifyEvidenceComposition = (strategy, composition, evidence) => {
|
|
241
|
+
const directionPolicy = composition.directionPolicy ?? "both";
|
|
231
242
|
const requireEqual = (actual, expected, label, artifactId) => assert(
|
|
232
243
|
actual === expected,
|
|
233
244
|
`evidence ${artifactId} ${label} does not match the frozen composition`
|
|
@@ -298,6 +309,14 @@ var verifyEvidenceComposition = (strategy, composition, evidence) => {
|
|
|
298
309
|
"gateContextFingerprint",
|
|
299
310
|
reference.artifactId
|
|
300
311
|
);
|
|
312
|
+
if (directionPolicy !== "both" || lineage?.directionPolicy != null) {
|
|
313
|
+
requireEqual(
|
|
314
|
+
lineage?.directionPolicy,
|
|
315
|
+
directionPolicy,
|
|
316
|
+
"directionPolicy",
|
|
317
|
+
reference.artifactId
|
|
318
|
+
);
|
|
319
|
+
}
|
|
301
320
|
assert(
|
|
302
321
|
lineage?.sourceSha256s.includes(composition.coreExportSha256),
|
|
303
322
|
`evidence ${reference.artifactId} does not use the frozen core export`
|
|
@@ -342,6 +361,14 @@ var verifyEvidenceComposition = (strategy, composition, evidence) => {
|
|
|
342
361
|
"MAX_LOSS_VALUE",
|
|
343
362
|
reference.artifactId
|
|
344
363
|
);
|
|
364
|
+
if (directionPolicy !== "both" || lineage?.directionPolicy != null) {
|
|
365
|
+
requireEqual(
|
|
366
|
+
lineage?.directionPolicy,
|
|
367
|
+
directionPolicy,
|
|
368
|
+
"directionPolicy",
|
|
369
|
+
reference.artifactId
|
|
370
|
+
);
|
|
371
|
+
}
|
|
345
372
|
continue;
|
|
346
373
|
}
|
|
347
374
|
if (reference.kind === "execution_calibration") {
|
|
@@ -382,6 +409,14 @@ var verifyEvidenceComposition = (strategy, composition, evidence) => {
|
|
|
382
409
|
"MAX_LOSS_VALUE",
|
|
383
410
|
reference.artifactId
|
|
384
411
|
);
|
|
412
|
+
if (directionPolicy !== "both" || lineage?.directionPolicy != null) {
|
|
413
|
+
requireEqual(
|
|
414
|
+
lineage?.directionPolicy,
|
|
415
|
+
directionPolicy,
|
|
416
|
+
"directionPolicy",
|
|
417
|
+
reference.artifactId
|
|
418
|
+
);
|
|
419
|
+
}
|
|
385
420
|
}
|
|
386
421
|
}
|
|
387
422
|
};
|
|
@@ -417,6 +452,12 @@ var createStrategyReleaseManifest = (input) => {
|
|
|
417
452
|
input.composition.longEnabled && input.composition.shortEnabled,
|
|
418
453
|
"LONG and SHORT must both remain enabled"
|
|
419
454
|
);
|
|
455
|
+
assert(
|
|
456
|
+
input.composition.directionPolicy == null || ["both", "long_only", "short_only", "direction_aware"].includes(
|
|
457
|
+
input.composition.directionPolicy
|
|
458
|
+
),
|
|
459
|
+
"composition.directionPolicy is invalid"
|
|
460
|
+
);
|
|
420
461
|
assert(
|
|
421
462
|
Number.isInteger(input.monitoring.minimumProspectiveClosedTrades) && input.monitoring.minimumProspectiveClosedTrades > 0,
|
|
422
463
|
"monitoring.minimumProspectiveClosedTrades must be positive"
|
|
@@ -481,7 +522,8 @@ var createStrategyReleaseManifest = (input) => {
|
|
|
481
522
|
gateFingerprint: input.composition.gateFingerprint,
|
|
482
523
|
gateContextFingerprint: input.composition.gateContextFingerprint,
|
|
483
524
|
runtimeContextFingerprint: input.composition.runtimeContextFingerprint,
|
|
484
|
-
maxLossValue: input.composition.maxLossValue
|
|
525
|
+
maxLossValue: input.composition.maxLossValue,
|
|
526
|
+
...input.composition.directionPolicy == null ? {} : { directionPolicy: input.composition.directionPolicy }
|
|
485
527
|
};
|
|
486
528
|
const compositionId = `${safeSegment(input.strategy, "strategy")}_${strategyReleaseSha256(compositionIdentity).slice(0, 16)}`;
|
|
487
529
|
const reasons = releaseReasons({ ...input, gates: evidenceGates });
|
|
@@ -571,7 +613,8 @@ var verifyStrategyReleaseEnvelope = async (valueOrPath) => {
|
|
|
571
613
|
runtimeContextFingerprint: manifest.composition.runtimeContextFingerprint,
|
|
572
614
|
maxLossValue: manifest.composition.maxLossValue,
|
|
573
615
|
longEnabled: manifest.composition.longEnabled,
|
|
574
|
-
shortEnabled: manifest.composition.shortEnabled
|
|
616
|
+
shortEnabled: manifest.composition.shortEnabled,
|
|
617
|
+
directionPolicy: manifest.composition.directionPolicy
|
|
575
618
|
},
|
|
576
619
|
marketWindow: manifest.marketWindow,
|
|
577
620
|
researchBudget: manifest.researchBudget,
|
|
@@ -951,6 +994,7 @@ var finiteNumber = (value) => {
|
|
|
951
994
|
const number = Number(value);
|
|
952
995
|
return Number.isFinite(number) ? number : null;
|
|
953
996
|
};
|
|
997
|
+
var finiteDirectionPolicy = (value) => ["both", "long_only", "short_only", "direction_aware"].includes(String(value)) ? value : null;
|
|
954
998
|
var uniqueStrings = (values) => [
|
|
955
999
|
...new Set(
|
|
956
1000
|
values.map(finiteString).filter((value) => value != null)
|
|
@@ -967,6 +1011,7 @@ var incompleteEvidenceLineage = () => ({
|
|
|
967
1011
|
gateContextFingerprint: null,
|
|
968
1012
|
runtimeContextFingerprint: null,
|
|
969
1013
|
maxLossValue: null,
|
|
1014
|
+
directionPolicy: null,
|
|
970
1015
|
sourceSha256s: []
|
|
971
1016
|
});
|
|
972
1017
|
var extractEvidenceLineage = ({
|
|
@@ -1029,6 +1074,7 @@ var extractEvidenceLineage = ({
|
|
|
1029
1074
|
gateConfigIdsFingerprint: finiteString(lineage?.configIdsFingerprint),
|
|
1030
1075
|
gateFingerprint: finiteString(lineage?.gateFingerprint),
|
|
1031
1076
|
gateContextFingerprint: finiteString(lineage?.contextFingerprint),
|
|
1077
|
+
directionPolicy: finiteDirectionPolicy(lineage?.directionPolicy),
|
|
1032
1078
|
sourceSha256s: uniqueStrings(array(lineage?.sourceSha256s))
|
|
1033
1079
|
};
|
|
1034
1080
|
}
|
|
@@ -1064,7 +1110,8 @@ var extractEvidenceLineage = ({
|
|
|
1064
1110
|
gateFingerprint: one("gateFingerprint"),
|
|
1065
1111
|
runtimeConfigFingerprint: one("configFingerprint"),
|
|
1066
1112
|
runtimeContextFingerprint: one("contextFingerprint"),
|
|
1067
|
-
maxLossValue: maxLossValues.length === 1 ? maxLossValues[0] : null
|
|
1113
|
+
maxLossValue: maxLossValues.length === 1 ? maxLossValues[0] : null,
|
|
1114
|
+
directionPolicy: finiteDirectionPolicy(one("directionPolicy"))
|
|
1068
1115
|
};
|
|
1069
1116
|
}
|
|
1070
1117
|
if (reference.kind === "execution_calibration") {
|
|
@@ -1101,6 +1148,7 @@ var extractEvidenceLineage = ({
|
|
|
1101
1148
|
runtimeConfigFingerprint: one("configFingerprint"),
|
|
1102
1149
|
runtimeContextFingerprint: one("contextFingerprint"),
|
|
1103
1150
|
maxLossValue: maxLossValues.length === 1 ? maxLossValues[0] : null,
|
|
1151
|
+
directionPolicy: finiteDirectionPolicy(one("directionPolicy")),
|
|
1104
1152
|
sourceSha256s: uniqueStrings([
|
|
1105
1153
|
...array(nested(artifact.sources)?.sha256s),
|
|
1106
1154
|
...array(artifact.sourceSha256s)
|
|
@@ -1237,9 +1285,10 @@ async function collectReleaseEvidenceReferences(references) {
|
|
|
1237
1285
|
if (reference.kind === "ai_gate") {
|
|
1238
1286
|
const outcome = nested(artifact.outcome);
|
|
1239
1287
|
const approvedRisk = nested(outcome?.approvedRisk);
|
|
1240
|
-
const
|
|
1241
|
-
|
|
1242
|
-
|
|
1288
|
+
const run = nested(artifact.run);
|
|
1289
|
+
const research = nested(artifact.research);
|
|
1290
|
+
const researchLineage = nested(research?.lineage);
|
|
1291
|
+
const terminalWindows = array(research?.terminalWindows).map(nested).filter((entry) => entry != null);
|
|
1243
1292
|
const hasLongShortDirections = (value) => {
|
|
1244
1293
|
const directions = new Map(
|
|
1245
1294
|
array(value).map(nested).filter(
|
|
@@ -1253,6 +1302,22 @@ async function collectReleaseEvidenceReferences(references) {
|
|
|
1253
1302
|
const directionalEvidenceComplete = hasLongShortDirections(artifact.byDirection) && terminalWindows.every(
|
|
1254
1303
|
(window) => hasLongShortDirections(window.byDirection)
|
|
1255
1304
|
);
|
|
1305
|
+
const directionPolicy = finiteDirectionPolicy(researchLineage?.directionPolicy) ?? "both";
|
|
1306
|
+
const runDirectionPolicy = finiteDirectionPolicy(run?.directionPolicy) ?? "both";
|
|
1307
|
+
const suppressedDirection = directionPolicy === "long_only" ? "SHORT" : directionPolicy === "short_only" ? "LONG" : null;
|
|
1308
|
+
const approvedForDirection = (value, direction) => {
|
|
1309
|
+
const entry = array(value).map(nested).find((candidate) => candidate?.direction === direction);
|
|
1310
|
+
return Number(nested(entry?.summary)?.approved ?? Number.NaN);
|
|
1311
|
+
};
|
|
1312
|
+
const directionPolicyValid = runDirectionPolicy === directionPolicy && (suppressedDirection == null || approvedForDirection(
|
|
1313
|
+
artifact.byDirection,
|
|
1314
|
+
suppressedDirection
|
|
1315
|
+
) === 0 && terminalWindows.every(
|
|
1316
|
+
(window) => approvedForDirection(
|
|
1317
|
+
window.byDirection,
|
|
1318
|
+
suppressedDirection
|
|
1319
|
+
) === 0
|
|
1320
|
+
));
|
|
1256
1321
|
const terminalsPass = terminalWindows.length > 0 && terminalWindows.every((window) => {
|
|
1257
1322
|
const terminalOutcome = nested(window.outcome);
|
|
1258
1323
|
return window.complete === true && Number(terminalOutcome?.approved ?? 0) > 0 && Number(
|
|
@@ -1260,7 +1325,7 @@ async function collectReleaseEvidenceReferences(references) {
|
|
|
1260
1325
|
) > 0;
|
|
1261
1326
|
});
|
|
1262
1327
|
return {
|
|
1263
|
-
aiGateAddsValue: Number(outcome?.expectancyDelta ?? Number.NEGATIVE_INFINITY) > 0 && Number(approvedRisk?.totalProfit ?? Number.NEGATIVE_INFINITY) > 0 && Number(approvedRisk?.profitFactor ?? Number.NEGATIVE_INFINITY) > 1 && directionalEvidenceComplete && terminalsPass
|
|
1328
|
+
aiGateAddsValue: Number(outcome?.expectancyDelta ?? Number.NEGATIVE_INFINITY) > 0 && Number(approvedRisk?.totalProfit ?? Number.NEGATIVE_INFINITY) > 0 && Number(approvedRisk?.profitFactor ?? Number.NEGATIVE_INFINITY) > 1 && directionalEvidenceComplete && directionPolicyValid && terminalsPass
|
|
1264
1329
|
};
|
|
1265
1330
|
}
|
|
1266
1331
|
if (reference.kind === "runtime_parity") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tradejs/cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Official CLI for the TradeJS TypeScript framework: infra setup, backtests, signals, bots, and ML workflows.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"tradejs",
|
|
@@ -29,14 +29,14 @@
|
|
|
29
29
|
"tradejs": "dist/cli.js"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@tradejs/base": "^
|
|
33
|
-
"@tradejs/connectors": "^
|
|
34
|
-
"@tradejs/core": "^
|
|
35
|
-
"@tradejs/indicators": "^
|
|
36
|
-
"@tradejs/infra": "^
|
|
37
|
-
"@tradejs/node": "^
|
|
38
|
-
"@tradejs/strategies": "^
|
|
39
|
-
"@tradejs/types": "^
|
|
32
|
+
"@tradejs/base": "^3.0.0",
|
|
33
|
+
"@tradejs/connectors": "^3.0.0",
|
|
34
|
+
"@tradejs/core": "^3.0.0",
|
|
35
|
+
"@tradejs/indicators": "^3.0.0",
|
|
36
|
+
"@tradejs/infra": "^3.0.0",
|
|
37
|
+
"@tradejs/node": "^3.0.0",
|
|
38
|
+
"@tradejs/strategies": "^3.0.0",
|
|
39
|
+
"@tradejs/types": "^3.0.0",
|
|
40
40
|
"args": "^5.0.3",
|
|
41
41
|
"bcryptjs": "^2.4.3",
|
|
42
42
|
"chalk": "4.1.2",
|