@tradejs/node 3.1.28 → 3.1.29-beta.259
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/ai.js +4 -1
- package/dist/ai.mjs +1 -1
- package/dist/backtest.js +941 -35
- package/dist/backtest.mjs +7 -3
- package/dist/chunk-3BBXGZ7M.mjs +271 -0
- package/dist/{chunk-K2YQ3MQC.mjs → chunk-BO42QWAB.mjs} +11 -2
- package/dist/{chunk-UITD6LG6.mjs → chunk-UL5Z46JL.mjs} +4 -1
- package/dist/{chunk-JDFPF2RG.mjs → chunk-WGL7XJ3W.mjs} +24 -2
- package/dist/chunk-ZZQLTUTP.mjs +614 -0
- package/dist/cli.js +9 -1
- package/dist/cli.mjs +6 -1
- package/dist/jev.d.mts +342 -0
- package/dist/jev.d.ts +342 -0
- package/dist/jev.js +931 -0
- package/dist/jev.mjs +23 -0
- package/dist/jevTraining-JYXUVAZG.mjs +13 -0
- package/dist/registry.js +933 -29
- package/dist/registry.mjs +4 -2
- package/dist/runtimeDashboard.js +952 -39
- package/dist/runtimeDashboard.mjs +5 -3
- package/dist/runtimeStrategies.js +952 -39
- package/dist/runtimeStrategies.mjs +5 -3
- package/dist/strategies.js +931 -27
- package/dist/strategies.mjs +4 -2
- package/package.json +10 -5
|
@@ -5,6 +5,14 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res, err) => function __init() {
|
|
9
|
+
if (err) throw err[0];
|
|
10
|
+
try {
|
|
11
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
12
|
+
} catch (e) {
|
|
13
|
+
throw err = [e], e;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
8
16
|
var __export = (target, all) => {
|
|
9
17
|
for (var name in all)
|
|
10
18
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -27,6 +35,628 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
35
|
));
|
|
28
36
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
37
|
|
|
38
|
+
// src/jevInput.ts
|
|
39
|
+
var import_data, import_jev, object, at, finite, fact, buildJevInput, criteria, JEV_QUESTIONS, questionsForJevInput, stateForJevInput;
|
|
40
|
+
var init_jevInput = __esm({
|
|
41
|
+
"src/jevInput.ts"() {
|
|
42
|
+
"use strict";
|
|
43
|
+
import_data = require("@tradejs/core/data");
|
|
44
|
+
import_jev = require("@tradejs/core/jev");
|
|
45
|
+
object = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
46
|
+
at = (value, ...path4) => path4.reduce((current, key) => object(current)[key], value);
|
|
47
|
+
finite = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
48
|
+
fact = (value) => {
|
|
49
|
+
if (value == null || typeof value === "boolean") return value ?? null;
|
|
50
|
+
if (typeof value === "number") return finite(value);
|
|
51
|
+
if (typeof value === "string" && value.length <= 60) return value;
|
|
52
|
+
throw new Error("Invalid Jev evidence fact");
|
|
53
|
+
};
|
|
54
|
+
buildJevInput = (signal) => {
|
|
55
|
+
if (!["LONG", "SHORT"].includes(signal.direction ?? "") || !Number.isFinite(signal.timestamp))
|
|
56
|
+
throw new Error("Jev requires a dated directional signal");
|
|
57
|
+
const base = object(signal.additionalIndicators?.baseContext);
|
|
58
|
+
const baseTimestamp = at(base, "candle", "timestamp");
|
|
59
|
+
const baseKnownAt = finite(baseTimestamp);
|
|
60
|
+
const baseProblem = !Object.keys(base).length ? "no_base_context" : baseKnownAt == null ? "missing_timestamp" : baseKnownAt > signal.timestamp ? "future" : signal.timestamp - baseKnownAt > (0, import_data.intervalToMs)(signal.interval) ? "stale" : null;
|
|
61
|
+
const evidence = signal.jevEvidence;
|
|
62
|
+
const features = {};
|
|
63
|
+
const provenance = {};
|
|
64
|
+
const missing = {};
|
|
65
|
+
const put = (key, value, details, missingReason = "missing_value") => {
|
|
66
|
+
const normalized = fact(value);
|
|
67
|
+
if (normalized != null) {
|
|
68
|
+
features[key] = normalized;
|
|
69
|
+
provenance[key] = details;
|
|
70
|
+
} else {
|
|
71
|
+
missing[key] = missingReason;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
const signalFact = {
|
|
75
|
+
knownAt: signal.timestamp,
|
|
76
|
+
scope: "target",
|
|
77
|
+
source: "signal"
|
|
78
|
+
};
|
|
79
|
+
put("signal.direction", signal.direction, signalFact);
|
|
80
|
+
const marketKeys = [
|
|
81
|
+
"market.trendBias",
|
|
82
|
+
"market.swingBias",
|
|
83
|
+
"market.volumeRel20",
|
|
84
|
+
"market.deltaPct",
|
|
85
|
+
"market.fastMaDistanceAtr"
|
|
86
|
+
];
|
|
87
|
+
if (baseProblem) {
|
|
88
|
+
for (const key of marketKeys) missing[key] = baseProblem;
|
|
89
|
+
} else {
|
|
90
|
+
const marketFact = {
|
|
91
|
+
knownAt: baseKnownAt,
|
|
92
|
+
scope: "target",
|
|
93
|
+
source: "baseContext"
|
|
94
|
+
};
|
|
95
|
+
put("market.trendBias", at(base, "regime", "trend", "bias"), marketFact);
|
|
96
|
+
put("market.swingBias", at(base, "structure", "swing", "bias"), marketFact);
|
|
97
|
+
put(
|
|
98
|
+
"market.volumeRel20",
|
|
99
|
+
at(base, "participation", "volume", "volumeRel20"),
|
|
100
|
+
{ ...marketFact, unit: "ratio" }
|
|
101
|
+
);
|
|
102
|
+
const deltaSource = at(base, "participation", "delta", "source");
|
|
103
|
+
put(
|
|
104
|
+
"market.deltaPct",
|
|
105
|
+
at(base, "participation", "delta", "deltaPct"),
|
|
106
|
+
{
|
|
107
|
+
...marketFact,
|
|
108
|
+
source: typeof deltaSource === "string" ? `baseContext.participation.delta.${deltaSource}` : "baseContext.participation.delta",
|
|
109
|
+
unit: "ratio"
|
|
110
|
+
},
|
|
111
|
+
deltaSource === "ohlcv_proxy" ? "ohlcv_proxy_no_taker_volume" : "missing_value"
|
|
112
|
+
);
|
|
113
|
+
put(
|
|
114
|
+
"market.fastMaDistanceAtr",
|
|
115
|
+
at(base, "regime", "trend", "priceDistanceToMaFastAtr"),
|
|
116
|
+
{ ...marketFact, unit: "ATR" }
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
const price = finite(signal.prices.currentPrice);
|
|
120
|
+
const stop = finite(signal.prices.stopLossPrice);
|
|
121
|
+
const target = finite(signal.prices.takeProfitPrice);
|
|
122
|
+
const atr = baseProblem ? null : finite(at(base, "raw", "volatility", "atr"));
|
|
123
|
+
if (price == null || stop == null || target == null)
|
|
124
|
+
throw new Error("Invalid Jev signal prices");
|
|
125
|
+
put(
|
|
126
|
+
"signal.validLevels",
|
|
127
|
+
signal.direction === "LONG" ? stop < price && price < target : target < price && price < stop,
|
|
128
|
+
signalFact
|
|
129
|
+
);
|
|
130
|
+
if (atr != null && atr > 0) {
|
|
131
|
+
put("signal.stopDistanceAtr", Math.abs(price - stop) / atr, {
|
|
132
|
+
...signalFact,
|
|
133
|
+
unit: "ATR"
|
|
134
|
+
});
|
|
135
|
+
put("signal.targetDistanceAtr", Math.abs(price - target) / atr, {
|
|
136
|
+
...signalFact,
|
|
137
|
+
unit: "ATR"
|
|
138
|
+
});
|
|
139
|
+
} else {
|
|
140
|
+
missing["signal.stopDistanceAtr"] = baseProblem ?? "missing_atr";
|
|
141
|
+
missing["signal.targetDistanceAtr"] = baseProblem ?? "missing_atr";
|
|
142
|
+
}
|
|
143
|
+
let geometryStatus = "absent";
|
|
144
|
+
const figures = signal.figures;
|
|
145
|
+
const groups = [figures?.lines, figures?.points, figures?.zones];
|
|
146
|
+
const points = [
|
|
147
|
+
...figures?.trendLine?.points ?? [],
|
|
148
|
+
...figures?.trendLine?.touches ?? [],
|
|
149
|
+
...groups.flatMap(
|
|
150
|
+
(group) => Array.isArray(group) ? group.flatMap(
|
|
151
|
+
(item) => "points" in item ? item.points : "start" in item ? [item.start, item.end] : []
|
|
152
|
+
) : []
|
|
153
|
+
)
|
|
154
|
+
];
|
|
155
|
+
if (points.length > 128 || groups.some(
|
|
156
|
+
(group) => group && (!Array.isArray(group) || group.length > 24)
|
|
157
|
+
) || points.some(
|
|
158
|
+
(point) => !Number.isFinite(point.timestamp) || !Number.isFinite(point.value) || point.timestamp > signal.timestamp || object(point).knownAt != null && (!Number.isFinite(object(point).knownAt) || Number(object(point).knownAt) > signal.timestamp)
|
|
159
|
+
))
|
|
160
|
+
geometryStatus = "invalid";
|
|
161
|
+
else if (points.length) geometryStatus = "available";
|
|
162
|
+
if (evidence) {
|
|
163
|
+
if (typeof evidence !== "object" || Array.isArray(evidence) || !evidence.facts || typeof evidence.facts !== "object" || Array.isArray(evidence.facts) || evidence.geometry != null && (typeof evidence.geometry !== "object" || Array.isArray(evidence.geometry)))
|
|
164
|
+
throw new Error("Invalid Jev evidence");
|
|
165
|
+
if (!/^[a-zA-Z0-9._-]{1,40}$/.test(evidence.version) || !Number.isFinite(evidence.knownAt) || evidence.knownAt > signal.timestamp || Object.keys(evidence.facts ?? {}).length > 16 || Object.keys(evidence.geometry ?? {}).length > 16)
|
|
166
|
+
throw new Error("Invalid or oversized Jev evidence");
|
|
167
|
+
const strategyFact = {
|
|
168
|
+
knownAt: evidence.knownAt,
|
|
169
|
+
scope: "strategy",
|
|
170
|
+
source: "strategy.jevEvidence"
|
|
171
|
+
};
|
|
172
|
+
const detailFor = (key) => {
|
|
173
|
+
const detail = evidence.factDetails?.[key] ?? strategyFact;
|
|
174
|
+
if (!Number.isFinite(detail.knownAt) || detail.knownAt > signal.timestamp || !["target", "strategy"].includes(detail.scope) || detail.unit != null && !/^[a-zA-Z0-9%._/-]{1,20}$/.test(detail.unit) || detail.source != null && !/^[a-zA-Z0-9._/-]{1,60}$/.test(detail.source))
|
|
175
|
+
throw new Error("Invalid Jev fact provenance");
|
|
176
|
+
return { ...strategyFact, ...detail };
|
|
177
|
+
};
|
|
178
|
+
put("setup.version", evidence.version, strategyFact);
|
|
179
|
+
for (const [key, value] of Object.entries(evidence.facts)) {
|
|
180
|
+
if (!/^[a-zA-Z][a-zA-Z0-9_]{0,39}$/.test(key) || /^(?:profit|pnl|outcome|label|score|scores|quality|verdict|approval|decision|result)$/i.test(
|
|
181
|
+
key
|
|
182
|
+
))
|
|
183
|
+
throw new Error("Invalid Jev evidence key");
|
|
184
|
+
put(`setup.${key}`, value, detailFor(`setup.${key}`));
|
|
185
|
+
}
|
|
186
|
+
for (const [key, value] of Object.entries(evidence.geometry ?? {})) {
|
|
187
|
+
if (!/^[a-zA-Z][a-zA-Z0-9_]{0,39}$/.test(key))
|
|
188
|
+
throw new Error("Invalid Jev geometry key");
|
|
189
|
+
put(`geometry.${key}`, value, detailFor(`geometry.${key}`));
|
|
190
|
+
}
|
|
191
|
+
if (Object.keys(evidence.factDetails ?? {}).some(
|
|
192
|
+
(key) => !(key in provenance)
|
|
193
|
+
))
|
|
194
|
+
throw new Error("Jev provenance references an unavailable fact");
|
|
195
|
+
}
|
|
196
|
+
if (evidence?.geometry && Object.keys(evidence.geometry).length && geometryStatus === "absent")
|
|
197
|
+
geometryStatus = "facts_only";
|
|
198
|
+
if (geometryStatus === "absent") missing["geometry.figures"] = "no_geometry";
|
|
199
|
+
if (geometryStatus === "invalid")
|
|
200
|
+
missing["geometry.figures"] = "invalid_geometry";
|
|
201
|
+
if (Object.keys(features).length > 40 || JSON.stringify(features).length > 4e3)
|
|
202
|
+
throw new Error("Jev input exceeds the microdecision budget");
|
|
203
|
+
const has = (key) => key in features;
|
|
204
|
+
const setupKeys = Object.keys(features).filter(
|
|
205
|
+
(key) => key.startsWith("setup.")
|
|
206
|
+
);
|
|
207
|
+
const questions = import_jev.JEV_DIMENSIONS.filter((dimension) => {
|
|
208
|
+
switch (dimension) {
|
|
209
|
+
case "trend":
|
|
210
|
+
return has("market.trendBias");
|
|
211
|
+
case "swing":
|
|
212
|
+
return has("market.swingBias");
|
|
213
|
+
case "participation":
|
|
214
|
+
return has("market.volumeRel20") || has("market.deltaPct");
|
|
215
|
+
case "setupParticipation":
|
|
216
|
+
return setupKeys.some(
|
|
217
|
+
(key) => /volume|delta|flow|liquidity|turnover/i.test(key)
|
|
218
|
+
);
|
|
219
|
+
case "extension":
|
|
220
|
+
return has("market.fastMaDistanceAtr") || has("setup.breakoutDistanceAtr") || has("signal.stopDistanceAtr");
|
|
221
|
+
case "confirmation":
|
|
222
|
+
return setupKeys.some(
|
|
223
|
+
(key) => /stage|confirm|retest|accept/i.test(key)
|
|
224
|
+
);
|
|
225
|
+
case "setupStrength":
|
|
226
|
+
return setupKeys.some(
|
|
227
|
+
(key) => key !== "setup.version" && !/volume|delta|flow|liquidity|turnover|stage|confirm|retest|accept|breakoutDistance/i.test(
|
|
228
|
+
key
|
|
229
|
+
)
|
|
230
|
+
);
|
|
231
|
+
case "geometry":
|
|
232
|
+
return geometryStatus !== "invalid" && Object.keys(features).some((key) => key.startsWith("geometry."));
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
return {
|
|
236
|
+
schema: "tradejs-jev-input/v4",
|
|
237
|
+
strategy: signal.strategy,
|
|
238
|
+
symbol: signal.symbol,
|
|
239
|
+
interval: String(signal.interval),
|
|
240
|
+
timestamp: signal.timestamp,
|
|
241
|
+
direction: signal.direction,
|
|
242
|
+
features,
|
|
243
|
+
provenance,
|
|
244
|
+
missing,
|
|
245
|
+
questions,
|
|
246
|
+
geometryStatus
|
|
247
|
+
};
|
|
248
|
+
};
|
|
249
|
+
criteria = (subject) => [
|
|
250
|
+
`${subject} is clearly contradicted by the supplied facts.`,
|
|
251
|
+
`${subject} has a material conflict.`,
|
|
252
|
+
`${subject} is uncertain from the supplied facts.`,
|
|
253
|
+
`${subject} has adequate support.`,
|
|
254
|
+
`${subject} has strong support without a material conflict.`
|
|
255
|
+
];
|
|
256
|
+
JEV_QUESTIONS = {
|
|
257
|
+
trend: {
|
|
258
|
+
type: "score",
|
|
259
|
+
instructions: "Does market.trendBias support signal.direction? Judge only the target-asset trend, not swings or setup quality.",
|
|
260
|
+
criteria: criteria("Target-asset trend alignment")
|
|
261
|
+
},
|
|
262
|
+
swing: {
|
|
263
|
+
type: "score",
|
|
264
|
+
instructions: "Does market.swingBias support signal.direction? Judge only the target-asset swing structure, not the broader trend.",
|
|
265
|
+
criteria: criteria("Target-asset swing alignment")
|
|
266
|
+
},
|
|
267
|
+
participation: {
|
|
268
|
+
type: "score",
|
|
269
|
+
instructions: "Does the supplied market.volumeRel20 or market.deltaPct show target-asset participation supportive of this direction? Ignore strategy setup volume and do not infer missing flow.",
|
|
270
|
+
criteria: criteria("Current target-asset participation")
|
|
271
|
+
},
|
|
272
|
+
setupParticipation: {
|
|
273
|
+
type: "score",
|
|
274
|
+
instructions: "Do the strategy-defined setup volume, flow or liquidity facts support the named setup? Use only relevant setup.* facts, not market.volumeRel20; do not infer missing data.",
|
|
275
|
+
criteria: criteria("Participation within the setup")
|
|
276
|
+
},
|
|
277
|
+
extension: {
|
|
278
|
+
type: "score",
|
|
279
|
+
instructions: "Do the supplied ATR-normalized entry-distance facts suggest the entry is not excessively extended? Use only distance facts; do not calculate new ratios or judge confirmation.",
|
|
280
|
+
criteria: criteria("Entry location without excessive extension")
|
|
281
|
+
},
|
|
282
|
+
confirmation: {
|
|
283
|
+
type: "score",
|
|
284
|
+
instructions: "Do the supplied setup stage and confirmation facts indicate a confirmed rather than premature entry? Do not judge distance or predict the outcome.",
|
|
285
|
+
criteria: criteria("Setup confirmation at entry")
|
|
286
|
+
},
|
|
287
|
+
setupStrength: {
|
|
288
|
+
type: "score",
|
|
289
|
+
instructions: "Do the strategy-defined setup strength and quality facts support this candidate? Exclude volume, confirmation, entry distance and geometry; do not assume missing facts.",
|
|
290
|
+
criteria: criteria("Non-geometric setup strength")
|
|
291
|
+
},
|
|
292
|
+
geometry: {
|
|
293
|
+
type: "score",
|
|
294
|
+
instructions: "Do the supplied geometry.* facts support the named setup? Do not infer unseen figure points or certify mathematical validity.",
|
|
295
|
+
criteria: criteria("Setup geometry")
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
questionsForJevInput = (input) => Object.fromEntries(input.questions.map((key) => [key, JEV_QUESTIONS[key]]));
|
|
299
|
+
stateForJevInput = (input) => ({
|
|
300
|
+
schema: input.schema,
|
|
301
|
+
strategy: input.strategy,
|
|
302
|
+
direction: input.direction,
|
|
303
|
+
facts: input.features,
|
|
304
|
+
missing: input.missing,
|
|
305
|
+
geometryStatus: input.geometryStatus,
|
|
306
|
+
units: Object.fromEntries(
|
|
307
|
+
Object.entries(input.provenance).filter(([, detail]) => detail.unit != null).map(([key, detail]) => [key, detail.unit])
|
|
308
|
+
),
|
|
309
|
+
sources: Object.fromEntries(
|
|
310
|
+
Object.entries(input.provenance).filter(([, detail]) => detail.source != null).map(([key, detail]) => [key, detail.source])
|
|
311
|
+
)
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
// src/jevTraining.ts
|
|
317
|
+
var jevTraining_exports = {};
|
|
318
|
+
__export(jevTraining_exports, {
|
|
319
|
+
compareJevGate: () => compareJevGate,
|
|
320
|
+
trainJevGate: () => trainJevGate,
|
|
321
|
+
validateJevGateModel: () => validateJevGateModel,
|
|
322
|
+
validateJevStudy: () => validateJevStudy
|
|
323
|
+
});
|
|
324
|
+
var import_jev2, import_jev3, mean, variance, validateJevStudy, trainTree, validateJevGateModel, predict, economics, compareJevGate, trainJevGate;
|
|
325
|
+
var init_jevTraining = __esm({
|
|
326
|
+
"src/jevTraining.ts"() {
|
|
327
|
+
"use strict";
|
|
328
|
+
import_jev2 = require("@tradejs/core/jev");
|
|
329
|
+
import_jev3 = require("@tradejs/infra/jev");
|
|
330
|
+
init_jevInput();
|
|
331
|
+
mean = (values) => values.reduce((sum, value) => sum + value, 0) / values.length;
|
|
332
|
+
variance = (values) => {
|
|
333
|
+
const average = mean(values);
|
|
334
|
+
return values.reduce((sum, value) => sum + (value - average) ** 2, 0);
|
|
335
|
+
};
|
|
336
|
+
validateJevStudy = (rows) => {
|
|
337
|
+
const unique = /* @__PURE__ */ new Map();
|
|
338
|
+
if (rows.length > 2e4)
|
|
339
|
+
throw new Error(
|
|
340
|
+
"Jev training is bounded to 20000 rows; select a frozen sample first"
|
|
341
|
+
);
|
|
342
|
+
for (const row of rows) {
|
|
343
|
+
const record = row?.record;
|
|
344
|
+
if (row?.schema !== "tradejs-jev-study/v4" || !row.signalId || record?.schema !== "tradejs-jev-record/v4" || record.input?.schema !== "tradejs-jev-input/v4" || !Number.isFinite(record.input.timestamp) || record.inputHash !== (0, import_jev3.jevHash)(record.input) || record.questionsHash !== (0, import_jev3.jevHash)(questionsForJevInput(record.input)) || record.id !== (0, import_jev3.jevHash)({
|
|
345
|
+
inputHash: record.inputHash,
|
|
346
|
+
questionsHash: record.questionsHash,
|
|
347
|
+
provider: record.provider
|
|
348
|
+
}))
|
|
349
|
+
throw new Error("Invalid Jev study provenance");
|
|
350
|
+
(0, import_jev3.validateJevResponse)(record.response, record.input.questions);
|
|
351
|
+
for (const dimension of import_jev2.JEV_DIMENSIONS) {
|
|
352
|
+
const expected = record.input.questions.includes(dimension) ? record.response.answers[dimension].score / 4 : null;
|
|
353
|
+
if (record.scores[dimension] !== expected)
|
|
354
|
+
throw new Error("Jev study scores do not match the teacher response");
|
|
355
|
+
}
|
|
356
|
+
if (row.profit != null && !Number.isFinite(row.profit))
|
|
357
|
+
throw new Error("Invalid study outcome");
|
|
358
|
+
const prior = unique.get(record.inputHash);
|
|
359
|
+
if (prior && (prior.record.id !== record.id || prior.profit !== row.profit || (0, import_jev3.jevHash)(prior.record.response) !== (0, import_jev3.jevHash)(record.response)))
|
|
360
|
+
throw new Error("Conflicting duplicated Jev sample");
|
|
361
|
+
unique.set(record.inputHash, row);
|
|
362
|
+
}
|
|
363
|
+
const ordered = [...unique.values()].sort(
|
|
364
|
+
(a, b) => a.record.input.timestamp - b.record.input.timestamp || a.record.id.localeCompare(b.record.id)
|
|
365
|
+
);
|
|
366
|
+
if (!ordered.length) throw new Error("Empty Jev study");
|
|
367
|
+
const first = ordered[0].record;
|
|
368
|
+
if (ordered.some(
|
|
369
|
+
(row) => row.record.input.strategy !== first.input.strategy || row.record.response.model !== first.response.model || (0, import_jev3.jevHash)(row.record.provider) !== (0, import_jev3.jevHash)(first.provider)
|
|
370
|
+
))
|
|
371
|
+
throw new Error(
|
|
372
|
+
"Train one strategy and one frozen teacher/provider lineage at a time"
|
|
373
|
+
);
|
|
374
|
+
return ordered;
|
|
375
|
+
};
|
|
376
|
+
trainTree = (rows, dimension, depth, minLeaf) => {
|
|
377
|
+
const values = rows.map((row) => row.record.scores[dimension]);
|
|
378
|
+
const leaf = { value: mean(values), samples: rows.length };
|
|
379
|
+
if (depth === 0 || rows.length < minLeaf * 2 || variance(values) < 1e-8)
|
|
380
|
+
return leaf;
|
|
381
|
+
const keys = [
|
|
382
|
+
...new Set(rows.flatMap((row) => Object.keys(row.record.input.features)))
|
|
383
|
+
].sort().filter(
|
|
384
|
+
(key) => new Set(rows.map((row) => row.record.input.features[key])).size > 1
|
|
385
|
+
);
|
|
386
|
+
if (keys.length > 2e3) throw new Error("Too many Jev student features");
|
|
387
|
+
let best;
|
|
388
|
+
let bestLoss = variance(values);
|
|
389
|
+
for (const feature of keys) {
|
|
390
|
+
const observed = [
|
|
391
|
+
...new Set(
|
|
392
|
+
rows.map((row) => row.record.input.features[feature]).filter((value) => value != null)
|
|
393
|
+
)
|
|
394
|
+
];
|
|
395
|
+
const numeric = observed.every((value) => typeof value === "number");
|
|
396
|
+
const sorted = numeric ? observed.sort((a, b) => a - b) : observed.sort((a, b) => String(a).localeCompare(String(b)));
|
|
397
|
+
const candidates = sorted.filter(
|
|
398
|
+
(_2, index) => index % Math.max(1, Math.ceil(sorted.length / 12)) === 0
|
|
399
|
+
).slice(0, 12);
|
|
400
|
+
for (const value of candidates)
|
|
401
|
+
for (const missingLeft of [true, false]) {
|
|
402
|
+
const left = [], right = [];
|
|
403
|
+
for (const row of rows) {
|
|
404
|
+
const input = row.record.input.features[feature];
|
|
405
|
+
const goesLeft = input == null ? missingLeft : numeric ? typeof input === "number" && input <= value : input === value;
|
|
406
|
+
(goesLeft ? left : right).push(row);
|
|
407
|
+
}
|
|
408
|
+
if (left.length < minLeaf || right.length < minLeaf) continue;
|
|
409
|
+
const loss = variance(left.map((row) => row.record.scores[dimension])) + variance(right.map((row) => row.record.scores[dimension]));
|
|
410
|
+
if (loss < bestLoss - 1e-8) {
|
|
411
|
+
bestLoss = loss;
|
|
412
|
+
best = {
|
|
413
|
+
feature,
|
|
414
|
+
operator: numeric ? "lte" : "eq",
|
|
415
|
+
value,
|
|
416
|
+
missingLeft,
|
|
417
|
+
left,
|
|
418
|
+
right
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
if (!best) return leaf;
|
|
424
|
+
return {
|
|
425
|
+
feature: best.feature,
|
|
426
|
+
operator: best.operator,
|
|
427
|
+
value: best.value,
|
|
428
|
+
missingLeft: best.missingLeft,
|
|
429
|
+
left: trainTree(best.left, dimension, depth - 1, minLeaf),
|
|
430
|
+
right: trainTree(best.right, dimension, depth - 1, minLeaf)
|
|
431
|
+
};
|
|
432
|
+
};
|
|
433
|
+
validateJevGateModel = (value) => {
|
|
434
|
+
const model = value;
|
|
435
|
+
if (model?.schema !== "tradejs-jev-gate/v2" || model.inputSchema !== "tradejs-jev-input/v4" || typeof model.strategy !== "string" || !model.strategy || !model.teacherModel || !model.trees || model.questionsHash !== (0, import_jev3.jevHash)(JEV_QUESTIONS) || !model.training || !/^[a-f0-9]{64}$/.test(model.training.datasetHash))
|
|
436
|
+
throw new Error("Invalid local Jev gate");
|
|
437
|
+
const { trainEnd, validationEnd, testEnd } = model.training;
|
|
438
|
+
if (![trainEnd, validationEnd, testEnd].every(Number.isFinite) || !(trainEnd < validationEnd && validationEnd < testEnd))
|
|
439
|
+
throw new Error("Invalid local Jev gate partitions");
|
|
440
|
+
const check = (tree, depth) => {
|
|
441
|
+
if (!tree || depth > 8) throw new Error("Invalid local Jev tree");
|
|
442
|
+
if ("samples" in tree) {
|
|
443
|
+
if (!Number.isFinite(tree.value) || tree.value < 0 || tree.value > 1 || !Number.isInteger(tree.samples) || tree.samples < 1)
|
|
444
|
+
throw new Error("Invalid local Jev leaf");
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
if (typeof tree.feature !== "string" || !["lte", "eq"].includes(tree.operator) || typeof tree.missingLeft !== "boolean" || tree.operator === "lte" && !Number.isFinite(tree.value) || !["number", "boolean", "string"].includes(typeof tree.value))
|
|
448
|
+
throw new Error("Invalid local Jev split");
|
|
449
|
+
check(tree.left, depth + 1);
|
|
450
|
+
check(tree.right, depth + 1);
|
|
451
|
+
};
|
|
452
|
+
for (const dimension of import_jev2.JEV_DIMENSIONS) {
|
|
453
|
+
if (model.trees[dimension] != null) check(model.trees[dimension], 0);
|
|
454
|
+
}
|
|
455
|
+
return model;
|
|
456
|
+
};
|
|
457
|
+
predict = (model, row) => Object.fromEntries(
|
|
458
|
+
import_jev2.JEV_DIMENSIONS.map((key) => [
|
|
459
|
+
key,
|
|
460
|
+
model.trees[key] && row.record.input.questions.includes(key) ? (0, import_jev2.predictJevTree)(model.trees[key], row.record.input.features) : null
|
|
461
|
+
])
|
|
462
|
+
);
|
|
463
|
+
economics = (rows) => {
|
|
464
|
+
const known = rows.filter((row) => row.profit != null);
|
|
465
|
+
const profits = known.map((row) => row.profit);
|
|
466
|
+
const wins = profits.filter((value) => value > 0).reduce((sum, value) => sum + value, 0);
|
|
467
|
+
const losses = -profits.filter((value) => value < 0).reduce((sum, value) => sum + value, 0);
|
|
468
|
+
return {
|
|
469
|
+
rows: rows.length,
|
|
470
|
+
knownOutcomes: known.length,
|
|
471
|
+
pnl: profits.reduce((a, b) => a + b, 0),
|
|
472
|
+
pnlPerTrade: profits.length ? mean(profits) : null,
|
|
473
|
+
profitFactor: losses ? wins / losses : null,
|
|
474
|
+
winRate: profits.length ? profits.filter((value) => value > 0).length / profits.length : null
|
|
475
|
+
};
|
|
476
|
+
};
|
|
477
|
+
compareJevGate = (model, input, minScores = {}) => {
|
|
478
|
+
validateJevGateModel(model);
|
|
479
|
+
const rows = validateJevStudy(input);
|
|
480
|
+
if (rows.some(
|
|
481
|
+
(row) => row.record.input.strategy !== model.strategy || row.record.response.model !== model.teacherModel
|
|
482
|
+
))
|
|
483
|
+
throw new Error("Model and study lineage do not match");
|
|
484
|
+
const decisions = rows.map((row) => {
|
|
485
|
+
const policy = {
|
|
486
|
+
source: "local",
|
|
487
|
+
mode: "gate",
|
|
488
|
+
minScores
|
|
489
|
+
};
|
|
490
|
+
const scores = predict(model, row);
|
|
491
|
+
const student = (0, import_jev2.decideJev)(
|
|
492
|
+
scores,
|
|
493
|
+
policy,
|
|
494
|
+
row.record.input.geometryStatus,
|
|
495
|
+
row.record.input.features,
|
|
496
|
+
row.record.input.questions
|
|
497
|
+
).allowed;
|
|
498
|
+
const teacher = (0, import_jev2.decideJev)(
|
|
499
|
+
row.record.scores,
|
|
500
|
+
policy,
|
|
501
|
+
row.record.input.geometryStatus,
|
|
502
|
+
row.record.input.features,
|
|
503
|
+
row.record.input.questions
|
|
504
|
+
).allowed;
|
|
505
|
+
return { row, scores, student, teacher };
|
|
506
|
+
});
|
|
507
|
+
const dimensionErrors = Object.fromEntries(
|
|
508
|
+
import_jev2.JEV_DIMENSIONS.map((key) => {
|
|
509
|
+
const pairs = decisions.filter(
|
|
510
|
+
(item) => item.row.record.scores[key] != null && item.scores[key] != null
|
|
511
|
+
);
|
|
512
|
+
return [
|
|
513
|
+
key,
|
|
514
|
+
{
|
|
515
|
+
n: pairs.length,
|
|
516
|
+
mae: pairs.length ? mean(
|
|
517
|
+
pairs.map(
|
|
518
|
+
(item) => Math.abs(item.scores[key] - item.row.record.scores[key])
|
|
519
|
+
)
|
|
520
|
+
) : null
|
|
521
|
+
}
|
|
522
|
+
];
|
|
523
|
+
})
|
|
524
|
+
);
|
|
525
|
+
const selected = (predicate) => decisions.filter(predicate).map((value) => value.row);
|
|
526
|
+
return {
|
|
527
|
+
rows: rows.length,
|
|
528
|
+
start: rows[0].record.input.timestamp,
|
|
529
|
+
end: rows[rows.length - 1].record.input.timestamp,
|
|
530
|
+
agreement: decisions.filter((item) => item.student === item.teacher).length / rows.length,
|
|
531
|
+
teacherOnly: decisions.filter((item) => item.teacher && !item.student).length,
|
|
532
|
+
studentOnly: decisions.filter((item) => item.student && !item.teacher).length,
|
|
533
|
+
dimensionErrors,
|
|
534
|
+
outcomeScope: "Matched completed outcomes only; this is not a sequential portfolio backtest. Unexecuted candidates have no observed outcome.",
|
|
535
|
+
cohorts: Object.fromEntries(
|
|
536
|
+
["ALL", "LONG", "SHORT"].map((side) => [
|
|
537
|
+
side,
|
|
538
|
+
{
|
|
539
|
+
core: economics(
|
|
540
|
+
selected(
|
|
541
|
+
(item) => side === "ALL" || item.row.record.input.direction === side
|
|
542
|
+
)
|
|
543
|
+
),
|
|
544
|
+
baseline: economics(
|
|
545
|
+
selected(
|
|
546
|
+
(item) => item.row.baselineAllowed === true && (side === "ALL" || item.row.record.input.direction === side)
|
|
547
|
+
)
|
|
548
|
+
),
|
|
549
|
+
jev: economics(
|
|
550
|
+
selected(
|
|
551
|
+
(item) => item.teacher && (side === "ALL" || item.row.record.input.direction === side)
|
|
552
|
+
)
|
|
553
|
+
),
|
|
554
|
+
local: economics(
|
|
555
|
+
selected(
|
|
556
|
+
(item) => item.student && (side === "ALL" || item.row.record.input.direction === side)
|
|
557
|
+
)
|
|
558
|
+
)
|
|
559
|
+
}
|
|
560
|
+
])
|
|
561
|
+
)
|
|
562
|
+
};
|
|
563
|
+
};
|
|
564
|
+
trainJevGate = (input, options = {}) => {
|
|
565
|
+
const rows = validateJevStudy(input);
|
|
566
|
+
const maxDepth = options.maxDepth ?? 3, minLeaf = options.minLeaf ?? 10;
|
|
567
|
+
if (!Number.isInteger(maxDepth) || maxDepth < 1 || maxDepth > 6 || !Number.isInteger(minLeaf) || minLeaf < 2)
|
|
568
|
+
throw new Error("Invalid Jev training limits");
|
|
569
|
+
const timestamps = [
|
|
570
|
+
...new Set(rows.map((row) => row.record.input.timestamp))
|
|
571
|
+
];
|
|
572
|
+
if (rows.length < 30 || timestamps.length < 10)
|
|
573
|
+
throw new Error("Need at least 30 distinct samples across 10 signal times");
|
|
574
|
+
const trainEnd = timestamps[Math.floor(timestamps.length * 0.6) - 1];
|
|
575
|
+
const validationEnd = timestamps[Math.floor(timestamps.length * 0.8) - 1];
|
|
576
|
+
const train = rows.filter((row) => row.record.input.timestamp <= trainEnd);
|
|
577
|
+
const validation = rows.filter(
|
|
578
|
+
(row) => row.record.input.timestamp > trainEnd && row.record.input.timestamp <= validationEnd
|
|
579
|
+
);
|
|
580
|
+
const test = rows.filter((row) => row.record.input.timestamp > validationEnd);
|
|
581
|
+
const fit = (samples) => Object.fromEntries(
|
|
582
|
+
import_jev2.JEV_DIMENSIONS.map((key) => {
|
|
583
|
+
const eligible = samples.filter(
|
|
584
|
+
(row) => row.record.scores[key] != null
|
|
585
|
+
);
|
|
586
|
+
return [
|
|
587
|
+
key,
|
|
588
|
+
eligible.length ? trainTree(eligible, key, maxDepth, minLeaf) : null
|
|
589
|
+
];
|
|
590
|
+
})
|
|
591
|
+
);
|
|
592
|
+
const first = rows[0].record;
|
|
593
|
+
const model = {
|
|
594
|
+
schema: "tradejs-jev-gate/v2",
|
|
595
|
+
inputSchema: "tradejs-jev-input/v4",
|
|
596
|
+
strategy: first.input.strategy,
|
|
597
|
+
questionsHash: (0, import_jev3.jevHash)(JEV_QUESTIONS),
|
|
598
|
+
teacherModel: first.response.model,
|
|
599
|
+
trees: fit(train),
|
|
600
|
+
training: {
|
|
601
|
+
datasetHash: (0, import_jev3.jevHash)(
|
|
602
|
+
rows.map((row) => ({
|
|
603
|
+
id: row.record.id,
|
|
604
|
+
response: row.record.response
|
|
605
|
+
}))
|
|
606
|
+
),
|
|
607
|
+
trainEnd,
|
|
608
|
+
validationEnd,
|
|
609
|
+
testEnd: timestamps[timestamps.length - 1],
|
|
610
|
+
maxDepth,
|
|
611
|
+
minLeaf
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
validateJevGateModel(model);
|
|
615
|
+
const walkForward = [0.4, 0.6, 0.8].map((fraction) => {
|
|
616
|
+
const trainThrough = timestamps[Math.max(0, Math.floor(timestamps.length * (fraction - 0.1)) - 1)];
|
|
617
|
+
const validationThrough = timestamps[Math.floor(timestamps.length * fraction) - 1];
|
|
618
|
+
const testThrough = timestamps[Math.min(
|
|
619
|
+
timestamps.length - 1,
|
|
620
|
+
Math.round(timestamps.length * (fraction + 0.2)) - 1
|
|
621
|
+
)];
|
|
622
|
+
const fitting = rows.filter(
|
|
623
|
+
(row) => row.record.input.timestamp <= trainThrough
|
|
624
|
+
);
|
|
625
|
+
const evaluation = rows.filter(
|
|
626
|
+
(row) => row.record.input.timestamp > validationThrough && row.record.input.timestamp <= testThrough
|
|
627
|
+
);
|
|
628
|
+
const fold = {
|
|
629
|
+
...model,
|
|
630
|
+
trees: fit(fitting),
|
|
631
|
+
training: {
|
|
632
|
+
...model.training,
|
|
633
|
+
trainEnd: trainThrough,
|
|
634
|
+
validationEnd: validationThrough,
|
|
635
|
+
testEnd: testThrough
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
return {
|
|
639
|
+
trainThrough,
|
|
640
|
+
validationThrough,
|
|
641
|
+
testThrough,
|
|
642
|
+
trainRows: fitting.length,
|
|
643
|
+
test: compareJevGate(fold, evaluation)
|
|
644
|
+
};
|
|
645
|
+
});
|
|
646
|
+
return {
|
|
647
|
+
model,
|
|
648
|
+
report: {
|
|
649
|
+
train: compareJevGate(model, train),
|
|
650
|
+
validation: compareJevGate(model, validation),
|
|
651
|
+
test: compareJevGate(model, test),
|
|
652
|
+
walkForward,
|
|
653
|
+
promotion: "research-only; validate a sequential backtest and prospective signals before runtime admission"
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
|
|
30
660
|
// src/runtimeStrategies.ts
|
|
31
661
|
var runtimeStrategies_exports = {};
|
|
32
662
|
__export(runtimeStrategies_exports, {
|
|
@@ -41,12 +671,266 @@ __export(runtimeStrategies_exports, {
|
|
|
41
671
|
verifyRuntimeDeclaration: () => verifyRuntimeDeclaration
|
|
42
672
|
});
|
|
43
673
|
module.exports = __toCommonJS(runtimeStrategies_exports);
|
|
674
|
+
var import_jev8 = require("@tradejs/core/jev");
|
|
44
675
|
var import_node_crypto4 = require("crypto");
|
|
45
|
-
var
|
|
46
|
-
var
|
|
676
|
+
var import_promises2 = require("fs/promises");
|
|
677
|
+
var import_node_path2 = __toESM(require("path"));
|
|
47
678
|
var import_runtimeControls = require("@tradejs/infra/runtimeControls");
|
|
48
679
|
var import_tradingAccounts = require("@tradejs/infra/tradingAccounts");
|
|
49
680
|
|
|
681
|
+
// src/strategyRuntime.ts
|
|
682
|
+
var import_jev6 = require("@tradejs/core/jev");
|
|
683
|
+
|
|
684
|
+
// src/jev.ts
|
|
685
|
+
var import_promises = __toESM(require("fs/promises"));
|
|
686
|
+
var import_node_path = __toESM(require("path"));
|
|
687
|
+
var import_jev4 = require("@tradejs/core/jev");
|
|
688
|
+
var import_jev5 = require("@tradejs/infra/jev");
|
|
689
|
+
var import_userSettings = require("@tradejs/infra/userSettings");
|
|
690
|
+
init_jevInput();
|
|
691
|
+
init_jevInput();
|
|
692
|
+
init_jevTraining();
|
|
693
|
+
var resolveJevProvider = async (userName) => {
|
|
694
|
+
const settings = await (0, import_userSettings.getUserSettings)(userName);
|
|
695
|
+
const provider = {
|
|
696
|
+
endpoint: settings.JEV_API_ENDPOINT || import_jev4.JEV_PROVIDERS[0].endpoint,
|
|
697
|
+
model: settings.JEV_MODEL || import_jev4.JEV_PROVIDERS[0].model
|
|
698
|
+
};
|
|
699
|
+
(0, import_jev4.parseJevConfig)({ source: "provider", mode: "observe", provider });
|
|
700
|
+
if (!settings.JEV_API_KEY)
|
|
701
|
+
throw new Error("Configure the Jev API key in Account settings");
|
|
702
|
+
return provider;
|
|
703
|
+
};
|
|
704
|
+
var emptyScores = () => Object.fromEntries(import_jev4.JEV_DIMENSIONS.map((key) => [key, null]));
|
|
705
|
+
var pending = /* @__PURE__ */ new Map();
|
|
706
|
+
var pendingResponses = /* @__PURE__ */ new Map();
|
|
707
|
+
var evaluateJevInput = async ({
|
|
708
|
+
input,
|
|
709
|
+
config,
|
|
710
|
+
userName,
|
|
711
|
+
projectRoot
|
|
712
|
+
}) => {
|
|
713
|
+
config = (0, import_jev4.parseJevConfig)(config);
|
|
714
|
+
const inputHash = (0, import_jev5.jevHash)(input);
|
|
715
|
+
const questions = questionsForJevInput(input);
|
|
716
|
+
const questionsHash = (0, import_jev5.jevHash)(questions);
|
|
717
|
+
const base = {
|
|
718
|
+
schema: "tradejs-signal-assessment/v5",
|
|
719
|
+
source: config.source,
|
|
720
|
+
mode: config.mode,
|
|
721
|
+
inputHash,
|
|
722
|
+
questionsHash,
|
|
723
|
+
geometryStatus: input.geometryStatus,
|
|
724
|
+
levelsValid: typeof input.features["signal.validLevels"] === "boolean" ? input.features["signal.validLevels"] : null
|
|
725
|
+
};
|
|
726
|
+
if (!input.questions.length)
|
|
727
|
+
return {
|
|
728
|
+
assessment: {
|
|
729
|
+
...base,
|
|
730
|
+
status: "unavailable",
|
|
731
|
+
model: "",
|
|
732
|
+
scores: emptyScores(),
|
|
733
|
+
confidence: emptyScores()
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
if (config.source === "local") {
|
|
737
|
+
const contents = await import_promises.default.readFile(
|
|
738
|
+
import_node_path.default.resolve(projectRoot, config.modelFile),
|
|
739
|
+
"utf8"
|
|
740
|
+
);
|
|
741
|
+
if ((0, import_jev5.jevFileHash)(contents) !== config.modelSha256)
|
|
742
|
+
throw new Error("Local Jev gate checksum mismatch");
|
|
743
|
+
const { validateJevGateModel: validateJevGateModel2 } = await Promise.resolve().then(() => (init_jevTraining(), jevTraining_exports));
|
|
744
|
+
const model = validateJevGateModel2(JSON.parse(contents));
|
|
745
|
+
if (model.strategy !== input.strategy || model.questionsHash !== (0, import_jev5.jevHash)(JEV_QUESTIONS))
|
|
746
|
+
throw new Error("Local Jev gate strategy or question version mismatch");
|
|
747
|
+
const scores = Object.fromEntries(
|
|
748
|
+
import_jev4.JEV_DIMENSIONS.map((key) => [
|
|
749
|
+
key,
|
|
750
|
+
input.questions.includes(key) && model.trees[key] ? (0, import_jev4.predictJevTree)(model.trees[key], input.features) : null
|
|
751
|
+
])
|
|
752
|
+
);
|
|
753
|
+
return {
|
|
754
|
+
assessment: {
|
|
755
|
+
...base,
|
|
756
|
+
status: "available",
|
|
757
|
+
model: config.modelSha256,
|
|
758
|
+
scores,
|
|
759
|
+
confidence: emptyScores()
|
|
760
|
+
}
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
const provider = config.provider ?? await resolveJevProvider(userName);
|
|
764
|
+
const id = (0, import_jev5.jevHash)({ inputHash, questionsHash, provider });
|
|
765
|
+
const dir = import_node_path.default.resolve(projectRoot, config.recordsDir ?? "data/ai/jev");
|
|
766
|
+
const file = import_node_path.default.join(dir, "records", `${id}.json`);
|
|
767
|
+
const getRecord = async () => {
|
|
768
|
+
const existing = await (0, import_jev5.readJevArtifact)(file);
|
|
769
|
+
if (existing) {
|
|
770
|
+
if (existing.schema !== "tradejs-jev-record/v4" || existing.id !== id || existing.inputHash !== inputHash || existing.questionsHash !== questionsHash || (0, import_jev5.jevHash)(existing.input) !== inputHash || (0, import_jev5.jevHash)(existing.provider) !== (0, import_jev5.jevHash)(provider))
|
|
771
|
+
throw new Error("Jev recording identity mismatch");
|
|
772
|
+
(0, import_jev5.validateJevResponse)(existing.response, input.questions);
|
|
773
|
+
for (const key of import_jev4.JEV_DIMENSIONS) {
|
|
774
|
+
const expected = input.questions.includes(key) ? existing.response.answers[key].score / 4 : null;
|
|
775
|
+
if (existing.scores[key] !== expected)
|
|
776
|
+
throw new Error("Jev recording scores do not match the response");
|
|
777
|
+
}
|
|
778
|
+
return existing;
|
|
779
|
+
}
|
|
780
|
+
if (config.source === "recorded")
|
|
781
|
+
throw new Error(
|
|
782
|
+
`Missing Jev recording ${id} for ${input.strategy} ${input.symbol} at ${new Date(input.timestamp).toISOString()}. Recorded mode requires the same signal-time facts and provider; a shorter backtest can change warm-up indicators or prior trade state.`
|
|
783
|
+
);
|
|
784
|
+
const settings = await (0, import_userSettings.getUserSettings)(userName);
|
|
785
|
+
if (!settings.JEV_API_KEY)
|
|
786
|
+
throw new Error("Configure the Jev API key in Account settings");
|
|
787
|
+
const apiKey = settings.JEV_API_KEY;
|
|
788
|
+
const configuredProvider = await resolveJevProvider(userName);
|
|
789
|
+
if (configuredProvider.endpoint !== provider.endpoint)
|
|
790
|
+
throw new Error(
|
|
791
|
+
"Jev account endpoint changed; update the frozen provider configuration"
|
|
792
|
+
);
|
|
793
|
+
const started = Date.now();
|
|
794
|
+
const state = stateForJevInput(input);
|
|
795
|
+
const responseId = (0, import_jev5.jevHash)({
|
|
796
|
+
state,
|
|
797
|
+
questionsHash,
|
|
798
|
+
provider,
|
|
799
|
+
user: (0, import_jev5.jevHash)(userName)
|
|
800
|
+
});
|
|
801
|
+
const responseFile = import_node_path.default.join(dir, "responses", `${responseId}.json`);
|
|
802
|
+
let responsePromise = pendingResponses.get(responseFile);
|
|
803
|
+
if (!responsePromise) {
|
|
804
|
+
responsePromise = (async () => {
|
|
805
|
+
const saved = await (0, import_jev5.readJevArtifact)(responseFile);
|
|
806
|
+
if (saved) return (0, import_jev5.validateJevResponse)(saved, input.questions);
|
|
807
|
+
const fresh = await (0, import_jev5.requestJev)(provider, apiKey, state, questions);
|
|
808
|
+
await (0, import_jev5.writeJevArtifact)(responseFile, fresh);
|
|
809
|
+
return fresh;
|
|
810
|
+
})();
|
|
811
|
+
pendingResponses.set(responseFile, responsePromise);
|
|
812
|
+
}
|
|
813
|
+
let response;
|
|
814
|
+
try {
|
|
815
|
+
response = await responsePromise;
|
|
816
|
+
} finally {
|
|
817
|
+
pendingResponses.delete(responseFile);
|
|
818
|
+
}
|
|
819
|
+
const scores = Object.fromEntries(
|
|
820
|
+
import_jev4.JEV_DIMENSIONS.map((key) => [
|
|
821
|
+
key,
|
|
822
|
+
input.questions.includes(key) ? response.answers[key].score / 4 : null
|
|
823
|
+
])
|
|
824
|
+
);
|
|
825
|
+
const value = {
|
|
826
|
+
schema: "tradejs-jev-record/v4",
|
|
827
|
+
id,
|
|
828
|
+
inputHash,
|
|
829
|
+
questionsHash,
|
|
830
|
+
provider,
|
|
831
|
+
input,
|
|
832
|
+
response,
|
|
833
|
+
scores,
|
|
834
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
835
|
+
elapsedMs: Date.now() - started
|
|
836
|
+
};
|
|
837
|
+
await (0, import_jev5.writeJevArtifact)(file, value);
|
|
838
|
+
return await (0, import_jev5.readJevArtifact)(file);
|
|
839
|
+
};
|
|
840
|
+
const pendingKey = `${file}:${(0, import_jev5.jevHash)(userName)}`;
|
|
841
|
+
let promise = pending.get(pendingKey);
|
|
842
|
+
if (!promise) {
|
|
843
|
+
promise = getRecord();
|
|
844
|
+
pending.set(pendingKey, promise);
|
|
845
|
+
}
|
|
846
|
+
let record;
|
|
847
|
+
try {
|
|
848
|
+
record = await promise;
|
|
849
|
+
} finally {
|
|
850
|
+
pending.delete(pendingKey);
|
|
851
|
+
}
|
|
852
|
+
return {
|
|
853
|
+
record,
|
|
854
|
+
assessment: {
|
|
855
|
+
...base,
|
|
856
|
+
status: "available",
|
|
857
|
+
recordId: id,
|
|
858
|
+
model: record.response.model,
|
|
859
|
+
scores: record.scores,
|
|
860
|
+
confidence: Object.fromEntries(
|
|
861
|
+
import_jev4.JEV_DIMENSIONS.map((key) => [
|
|
862
|
+
key,
|
|
863
|
+
record.response.answers[key]?.confidence ?? null
|
|
864
|
+
])
|
|
865
|
+
)
|
|
866
|
+
}
|
|
867
|
+
};
|
|
868
|
+
};
|
|
869
|
+
var assessSignalWithJev = async ({
|
|
870
|
+
signal,
|
|
871
|
+
config,
|
|
872
|
+
userName,
|
|
873
|
+
projectRoot,
|
|
874
|
+
strict,
|
|
875
|
+
scope = "runtime"
|
|
876
|
+
}) => {
|
|
877
|
+
let assessment;
|
|
878
|
+
try {
|
|
879
|
+
const input = buildJevInput(signal);
|
|
880
|
+
({ assessment } = await evaluateJevInput({
|
|
881
|
+
input,
|
|
882
|
+
config,
|
|
883
|
+
userName,
|
|
884
|
+
projectRoot
|
|
885
|
+
}));
|
|
886
|
+
} catch (error) {
|
|
887
|
+
if (strict) throw error;
|
|
888
|
+
assessment = {
|
|
889
|
+
schema: "tradejs-signal-assessment/v5",
|
|
890
|
+
source: config.source,
|
|
891
|
+
mode: config.mode,
|
|
892
|
+
status: "unavailable",
|
|
893
|
+
inputHash: "",
|
|
894
|
+
questionsHash: "",
|
|
895
|
+
geometryStatus: "absent",
|
|
896
|
+
levelsValid: null,
|
|
897
|
+
model: config.provider?.model ?? config.modelSha256 ?? "",
|
|
898
|
+
scores: emptyScores(),
|
|
899
|
+
confidence: emptyScores()
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
signal.assessment = assessment;
|
|
903
|
+
signal.additionalIndicators = {
|
|
904
|
+
...signal.additionalIndicators,
|
|
905
|
+
jev: {
|
|
906
|
+
schema: "tradejs-jev-features/v2",
|
|
907
|
+
status: assessment.status,
|
|
908
|
+
scores: assessment.scores,
|
|
909
|
+
confidence: assessment.confidence
|
|
910
|
+
}
|
|
911
|
+
};
|
|
912
|
+
const identity = {
|
|
913
|
+
signalId: signal.signalId,
|
|
914
|
+
strategy: signal.strategy,
|
|
915
|
+
symbol: signal.symbol,
|
|
916
|
+
timestamp: signal.timestamp,
|
|
917
|
+
scope,
|
|
918
|
+
user: (0, import_jev5.jevHash)(userName),
|
|
919
|
+
config,
|
|
920
|
+
assessment
|
|
921
|
+
};
|
|
922
|
+
await (0, import_jev5.writeJevArtifact)(
|
|
923
|
+
import_node_path.default.resolve(
|
|
924
|
+
projectRoot,
|
|
925
|
+
config.recordsDir ?? "data/ai/jev",
|
|
926
|
+
"decisions",
|
|
927
|
+
`${(0, import_jev5.jevHash)(identity)}.json`
|
|
928
|
+
),
|
|
929
|
+
identity
|
|
930
|
+
);
|
|
931
|
+
return assessment;
|
|
932
|
+
};
|
|
933
|
+
|
|
50
934
|
// src/strategyRuntime.ts
|
|
51
935
|
var import_constants5 = require("@tradejs/core/constants");
|
|
52
936
|
var import_strategies6 = require("@tradejs/core/strategies");
|
|
@@ -1327,8 +2211,11 @@ var toRecord2 = (value) => {
|
|
|
1327
2211
|
return value;
|
|
1328
2212
|
};
|
|
1329
2213
|
var buildBaseAiPayload = (signal) => {
|
|
2214
|
+
const { jevEvidence: _jevEvidence, ...signalAdditionalIndicators } = toRecord2(
|
|
2215
|
+
signal.additionalIndicators
|
|
2216
|
+
);
|
|
1330
2217
|
const additionalIndicators = {
|
|
1331
|
-
...
|
|
2218
|
+
...signalAdditionalIndicators,
|
|
1332
2219
|
marketContext: buildAiMarketContext(signal)
|
|
1333
2220
|
};
|
|
1334
2221
|
return {
|
|
@@ -1382,7 +2269,7 @@ var postProcessLocalAiAnalysisByStrategy = (signal, analysis, payload = buildAiP
|
|
|
1382
2269
|
var import_aiEndpoints = require("@tradejs/core/aiEndpoints");
|
|
1383
2270
|
var import_aiModels = require("@tradejs/core/aiModels");
|
|
1384
2271
|
var import_aiLanguages = require("@tradejs/core/aiLanguages");
|
|
1385
|
-
var
|
|
2272
|
+
var import_userSettings2 = require("@tradejs/infra/userSettings");
|
|
1386
2273
|
var DEFAULT_AI_MODEL = "openai/gpt-5-mini";
|
|
1387
2274
|
var userSettingsCache = /* @__PURE__ */ new Map();
|
|
1388
2275
|
var aiModelCache = /* @__PURE__ */ new Map();
|
|
@@ -1430,7 +2317,7 @@ var getOpenRouterModelKwargs = (apiEndpoint) => {
|
|
|
1430
2317
|
var getAiUserSettings = async (userName = "root") => {
|
|
1431
2318
|
let settingsPromise = userSettingsCache.get(userName);
|
|
1432
2319
|
if (!settingsPromise) {
|
|
1433
|
-
settingsPromise = (0,
|
|
2320
|
+
settingsPromise = (0, import_userSettings2.getUserSettings)(userName).then((settings2) => {
|
|
1434
2321
|
const endpoint = (0, import_aiEndpoints.normalizeAiEndpoint)(settings2.AI_API_ENDPOINT);
|
|
1435
2322
|
return {
|
|
1436
2323
|
...settings2,
|
|
@@ -3390,7 +4277,7 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
3390
4277
|
|
|
3391
4278
|
// src/strategyHelpers/derivativesContext.ts
|
|
3392
4279
|
var import_indicators2 = require("@tradejs/core/indicators");
|
|
3393
|
-
var
|
|
4280
|
+
var import_data2 = require("@tradejs/core/data");
|
|
3394
4281
|
var import_strategies3 = require("@tradejs/core/strategies");
|
|
3395
4282
|
var import_constants2 = require("@tradejs/core/constants");
|
|
3396
4283
|
var import_derivatives = require("@tradejs/infra/timescale/derivatives");
|
|
@@ -3550,7 +4437,7 @@ var enrichSignalWithDerivativesContext = async (params) => {
|
|
|
3550
4437
|
const referenceSymbols = getDerivativesContextReferenceSymbols();
|
|
3551
4438
|
const targetSymbol = normalizeSymbol(signal.symbol);
|
|
3552
4439
|
const lookbackMs = parseLookbackMs();
|
|
3553
|
-
const decisionTimeMs = signal.timestamp + (0,
|
|
4440
|
+
const decisionTimeMs = signal.timestamp + (0, import_data2.intervalToMs)(signal.interval);
|
|
3554
4441
|
const derivativesEndMs = (0, import_indicators2.getLastClosedDerivativesBarStartMs)(
|
|
3555
4442
|
decisionTimeMs,
|
|
3556
4443
|
"15m"
|
|
@@ -3635,7 +4522,7 @@ var enrichSignalWithDerivativesContext = async (params) => {
|
|
|
3635
4522
|
};
|
|
3636
4523
|
|
|
3637
4524
|
// src/strategyHelpers/hyperliquidWhaleContext.ts
|
|
3638
|
-
var
|
|
4525
|
+
var import_data3 = require("@tradejs/core/data");
|
|
3639
4526
|
var import_strategies4 = require("@tradejs/core/strategies");
|
|
3640
4527
|
var import_hyperliquidWhales2 = require("@tradejs/infra/timescale/hyperliquidWhales");
|
|
3641
4528
|
var import_logger6 = require("@tradejs/infra/logger");
|
|
@@ -4109,7 +4996,7 @@ var loadHyperliquidWhaleFlowContext = async (params) => {
|
|
|
4109
4996
|
const symbol = resolveHyperliquidPerpFromSignalSymbol(params.symbol);
|
|
4110
4997
|
if (!symbol) return null;
|
|
4111
4998
|
const interval = params.marketInterval ?? signalIntervalToMarketInterval2(params.interval);
|
|
4112
|
-
const decisionTimeMs = params.timestamp + (0,
|
|
4999
|
+
const decisionTimeMs = params.timestamp + (0, import_data3.intervalToMs)(params.interval);
|
|
4113
5000
|
const maxAgeMs = params.maxAgeMs ?? DEFAULT_MAX_AGE_BY_INTERVAL2[interval];
|
|
4114
5001
|
const universe = getHyperliquidPerpUniverseSnapshot();
|
|
4115
5002
|
const whales = getHyperliquidWhaleRegistrySnapshot();
|
|
@@ -4136,9 +5023,9 @@ var loadHyperliquidWhaleFlowContext = async (params) => {
|
|
|
4136
5023
|
abortSignal: params.abortSignal
|
|
4137
5024
|
});
|
|
4138
5025
|
} else {
|
|
4139
|
-
let
|
|
4140
|
-
if (!
|
|
4141
|
-
|
|
5026
|
+
let pending2 = contextCache.get(cacheKey);
|
|
5027
|
+
if (!pending2) {
|
|
5028
|
+
pending2 = (0, import_hyperliquidWhales2.getHyperliquidWhaleFlowAggregate)({
|
|
4142
5029
|
symbol,
|
|
4143
5030
|
interval,
|
|
4144
5031
|
decisionTimeMs,
|
|
@@ -4147,9 +5034,9 @@ var loadHyperliquidWhaleFlowContext = async (params) => {
|
|
|
4147
5034
|
whaleRegistryFingerprint: whales.fingerprint,
|
|
4148
5035
|
...params.abortSignal ? { signal: params.abortSignal } : {}
|
|
4149
5036
|
});
|
|
4150
|
-
setBoundedCache2(cacheKey,
|
|
5037
|
+
setBoundedCache2(cacheKey, pending2);
|
|
4151
5038
|
}
|
|
4152
|
-
row = await
|
|
5039
|
+
row = await pending2;
|
|
4153
5040
|
}
|
|
4154
5041
|
if (!row) return null;
|
|
4155
5042
|
return toBaseHyperliquidWhaleFlowContext(row, minimumCoveragePct);
|
|
@@ -4710,7 +5597,8 @@ var executeEntryOrder = async ({
|
|
|
4710
5597
|
runtimeConfigId: signal.runtimeConfigId,
|
|
4711
5598
|
strategyRevision: signal.strategyRevision,
|
|
4712
5599
|
runtimeLineage: signal.runtimeLineage,
|
|
4713
|
-
...signal.aiAnalysis ? { aiAnalysis: signal.aiAnalysis } : {}
|
|
5600
|
+
...signal.aiAnalysis ? { aiAnalysis: signal.aiAnalysis } : {},
|
|
5601
|
+
...signal.assessment ? { assessment: signal.assessment } : {}
|
|
4714
5602
|
});
|
|
4715
5603
|
}
|
|
4716
5604
|
if (orderPlaced && recordRuntimeTrade && isPositionIncrease) {
|
|
@@ -4783,7 +5671,7 @@ var resolveStrategyConfig = async ({
|
|
|
4783
5671
|
|
|
4784
5672
|
// src/strategy/runtimeBacktestDelay.ts
|
|
4785
5673
|
var import_constants4 = require("@tradejs/core/constants");
|
|
4786
|
-
var
|
|
5674
|
+
var import_data4 = require("@tradejs/core/data");
|
|
4787
5675
|
var import_strategies5 = require("@tradejs/core/strategies");
|
|
4788
5676
|
var resolveBacktestEntryDelayBars = (value) => {
|
|
4789
5677
|
if (value == null || value === "") {
|
|
@@ -4811,7 +5699,7 @@ var resolveBacktestExecutionDelayMs = (value, fallbackDelayMs) => {
|
|
|
4811
5699
|
};
|
|
4812
5700
|
var safeIntervalToMs = (interval) => {
|
|
4813
5701
|
try {
|
|
4814
|
-
return (0,
|
|
5702
|
+
return (0, import_data4.intervalToMs)(interval);
|
|
4815
5703
|
} catch {
|
|
4816
5704
|
return null;
|
|
4817
5705
|
}
|
|
@@ -5435,6 +6323,7 @@ var handleExitDecision = async ({
|
|
|
5435
6323
|
onRuntimeClose?.({
|
|
5436
6324
|
userName,
|
|
5437
6325
|
strategy: strategyName,
|
|
6326
|
+
deploymentId: connector.deploymentId,
|
|
5438
6327
|
openedByStrategy: trade.strategy,
|
|
5439
6328
|
symbol,
|
|
5440
6329
|
direction: trade.direction,
|
|
@@ -5727,6 +6616,11 @@ var createStrategyRuntime = ({
|
|
|
5727
6616
|
const projectConfig = await loadTradejsConfig(projectRoot);
|
|
5728
6617
|
const projectHooks = projectConfig.hooks;
|
|
5729
6618
|
const env = String(config.ENV ?? "BACKTEST");
|
|
6619
|
+
const jev = (0, import_jev6.parseJevConfig)(config.JEV);
|
|
6620
|
+
if (jev && env !== "BACKTEST" && jev.source === "provider" && !jev.provider)
|
|
6621
|
+
throw new Error(
|
|
6622
|
+
"Runtime JEV requires a frozen provider endpoint and model"
|
|
6623
|
+
);
|
|
5730
6624
|
const backtestPriceMode = config.BACKTEST_PRICE_MODE ?? "open";
|
|
5731
6625
|
const backtestEntryDelayBars = env === "BACKTEST" ? resolveBacktestEntryDelayBars(config.BACKTEST_ENTRY_DELAY_BARS) : 0;
|
|
5732
6626
|
const resolvedBacktestExecutionInterval = config.BACKTEST_EXECUTION_INTERVAL ?? backtestExecutionMarketData?.interval ?? resolveBacktestExecutionIntervalForPrimary(config.INTERVAL ?? "15");
|
|
@@ -6147,44 +7041,44 @@ var createStrategyRuntime = ({
|
|
|
6147
7041
|
if (pendingBacktestEntry.delayBarsRemaining > 0) {
|
|
6148
7042
|
return `BACKTEST_ENTRY_DELAY_PENDING:${pendingBacktestEntry.delayBarsRemaining}`;
|
|
6149
7043
|
}
|
|
6150
|
-
const
|
|
7044
|
+
const pending2 = pendingBacktestEntry;
|
|
6151
7045
|
pendingBacktestEntry = null;
|
|
6152
7046
|
const executionCandleResolution = resolveBacktestExecutionCandle(
|
|
6153
7047
|
candle,
|
|
6154
7048
|
btcCandle
|
|
6155
7049
|
);
|
|
6156
7050
|
const execution = applyBacktestDelayedEntryExecution({
|
|
6157
|
-
decision:
|
|
7051
|
+
decision: pending2.decision,
|
|
6158
7052
|
execution: executionCandleResolution,
|
|
6159
7053
|
backtestPriceMode: executionCandleResolution.source === "primary_timeframe" ? "open" : backtestPriceMode,
|
|
6160
|
-
delayBars:
|
|
7054
|
+
delayBars: pending2.delayBars
|
|
6161
7055
|
});
|
|
6162
7056
|
if (execution.skipReason) {
|
|
6163
|
-
return
|
|
7057
|
+
return pending2.decision.signal ?? execution.skipReason;
|
|
6164
7058
|
}
|
|
6165
7059
|
if (!execution.executionCandle || !execution.btcExecutionCandle) {
|
|
6166
|
-
return
|
|
7060
|
+
return pending2.decision.signal ?? "BACKTEST_LOWER_EXECUTION_CANDLE_MISSING";
|
|
6167
7061
|
}
|
|
6168
7062
|
const market = {
|
|
6169
7063
|
candle: execution.executionCandle,
|
|
6170
7064
|
btcCandle: execution.btcExecutionCandle
|
|
6171
7065
|
};
|
|
6172
7066
|
const entry = buildHookEntry({
|
|
6173
|
-
decision:
|
|
6174
|
-
runtime:
|
|
7067
|
+
decision: pending2.decision,
|
|
7068
|
+
runtime: pending2.runtime
|
|
6175
7069
|
});
|
|
6176
7070
|
return executeEntryDecision({
|
|
6177
7071
|
connector,
|
|
6178
7072
|
symbol,
|
|
6179
|
-
decision:
|
|
6180
|
-
runtime:
|
|
6181
|
-
manifest:
|
|
6182
|
-
hookCtx:
|
|
7073
|
+
decision: pending2.decision,
|
|
7074
|
+
runtime: pending2.runtime,
|
|
7075
|
+
manifest: pending2.manifest,
|
|
7076
|
+
hookCtx: pending2.hookCtx,
|
|
6183
7077
|
market,
|
|
6184
7078
|
entry,
|
|
6185
|
-
policy:
|
|
6186
|
-
ml:
|
|
6187
|
-
ai:
|
|
7079
|
+
policy: pending2.policy,
|
|
7080
|
+
ml: pending2.ml,
|
|
7081
|
+
ai: pending2.ai,
|
|
6188
7082
|
recordRuntimeJournal,
|
|
6189
7083
|
invokeStageHooks,
|
|
6190
7084
|
notifyRuntimeError
|
|
@@ -6434,6 +7328,16 @@ var createStrategyRuntime = ({
|
|
|
6434
7328
|
env,
|
|
6435
7329
|
includeHyperliquidWhales: false
|
|
6436
7330
|
});
|
|
7331
|
+
if (jev) {
|
|
7332
|
+
await assessSignalWithJev({
|
|
7333
|
+
signal,
|
|
7334
|
+
config: jev,
|
|
7335
|
+
userName,
|
|
7336
|
+
projectRoot,
|
|
7337
|
+
strict: env === "BACKTEST",
|
|
7338
|
+
scope: runtimeLineage?.deploymentCompositionId ?? (env === "BACKTEST" ? "backtest" : "runtime")
|
|
7339
|
+
});
|
|
7340
|
+
}
|
|
6437
7341
|
quality = await enrichSignalWithAi({
|
|
6438
7342
|
signal,
|
|
6439
7343
|
userName,
|
|
@@ -6503,6 +7407,7 @@ var createStrategyRuntime = ({
|
|
|
6503
7407
|
if (!shouldMakeOrder) {
|
|
6504
7408
|
if (signal) {
|
|
6505
7409
|
const skipReason = getEntrySkipReason({
|
|
7410
|
+
signal,
|
|
6506
7411
|
makeOrdersEnabled,
|
|
6507
7412
|
env,
|
|
6508
7413
|
ml,
|
|
@@ -6716,10 +7621,10 @@ var computeDeploymentCompositionId = (value) => revision("dc1", {
|
|
|
6716
7621
|
}))
|
|
6717
7622
|
});
|
|
6718
7623
|
var readRuntimePackageManifest = async (projectRoot) => {
|
|
6719
|
-
const manifestPath = process.env.TRADEJS_RUNTIME_PACKAGE_MANIFEST?.trim() ||
|
|
7624
|
+
const manifestPath = process.env.TRADEJS_RUNTIME_PACKAGE_MANIFEST?.trim() || import_node_path2.default.join(projectRoot, "runtime-package-manifest.json");
|
|
6720
7625
|
let value;
|
|
6721
7626
|
try {
|
|
6722
|
-
value = JSON.parse(await (0,
|
|
7627
|
+
value = JSON.parse(await (0, import_promises2.readFile)(manifestPath, "utf8"));
|
|
6723
7628
|
} catch (error) {
|
|
6724
7629
|
throw new Error(
|
|
6725
7630
|
`Unable to read runtime package manifest ${manifestPath}: ${String(error)}`
|
|
@@ -6758,7 +7663,7 @@ var verifyInstalledPackageName = async ({
|
|
|
6758
7663
|
throw new Error("name is invalid");
|
|
6759
7664
|
}
|
|
6760
7665
|
const projectManifest = JSON.parse(
|
|
6761
|
-
await (0,
|
|
7666
|
+
await (0, import_promises2.readFile)(import_node_path2.default.join(projectRoot, "package.json"), "utf8")
|
|
6762
7667
|
);
|
|
6763
7668
|
const alias = parseNpmAliasReference(
|
|
6764
7669
|
projectManifest.dependencies?.[packageName]
|
|
@@ -6772,14 +7677,14 @@ var readInstalledPackageMetadata = async ({
|
|
|
6772
7677
|
packageName,
|
|
6773
7678
|
projectPackage
|
|
6774
7679
|
}) => {
|
|
6775
|
-
const packageJsonPath = projectPackage ?
|
|
7680
|
+
const packageJsonPath = projectPackage ? import_node_path2.default.join(projectRoot, "package.json") : import_node_path2.default.join(
|
|
6776
7681
|
projectRoot,
|
|
6777
7682
|
"node_modules",
|
|
6778
7683
|
...packageName.split("/"),
|
|
6779
7684
|
"package.json"
|
|
6780
7685
|
);
|
|
6781
7686
|
try {
|
|
6782
|
-
const packageJson = JSON.parse(await (0,
|
|
7687
|
+
const packageJson = JSON.parse(await (0, import_promises2.readFile)(packageJsonPath, "utf8"));
|
|
6783
7688
|
if (typeof packageJson.version !== "string" || !packageJson.version.trim()) {
|
|
6784
7689
|
throw new Error("version is invalid");
|
|
6785
7690
|
}
|
|
@@ -6854,12 +7759,12 @@ var resolveStrategyPackage = async ({
|
|
|
6854
7759
|
projectRoot
|
|
6855
7760
|
}) => {
|
|
6856
7761
|
if (!pluginSource) return null;
|
|
6857
|
-
if (!pluginSource.startsWith(".") && !
|
|
7762
|
+
if (!pluginSource.startsWith(".") && !import_node_path2.default.isAbsolute(pluginSource)) {
|
|
6858
7763
|
return { name: pluginSource, projectPackage: false };
|
|
6859
7764
|
}
|
|
6860
7765
|
try {
|
|
6861
7766
|
const packageJson = JSON.parse(
|
|
6862
|
-
await (0,
|
|
7767
|
+
await (0, import_promises2.readFile)(import_node_path2.default.join(projectRoot, "package.json"), "utf8")
|
|
6863
7768
|
);
|
|
6864
7769
|
return typeof packageJson.name === "string" && packageJson.name.trim() ? { name: packageJson.name, projectPackage: true } : null;
|
|
6865
7770
|
} catch {
|
|
@@ -6963,7 +7868,15 @@ var resolveStrategyComposition = async ({
|
|
|
6963
7868
|
`Strategy config parser returned a non-object: ${strategyName}`
|
|
6964
7869
|
);
|
|
6965
7870
|
}
|
|
6966
|
-
const
|
|
7871
|
+
const jev = (0, import_jev8.parseJevConfig)(declaration.config.JEV);
|
|
7872
|
+
if (jev && jev.source === "provider" && !jev.provider)
|
|
7873
|
+
throw new Error(
|
|
7874
|
+
"Runtime JEV requires a frozen provider endpoint and model"
|
|
7875
|
+
);
|
|
7876
|
+
const strategyConfig = {
|
|
7877
|
+
...parsedConfig,
|
|
7878
|
+
...jev ? { JEV: jev } : {}
|
|
7879
|
+
};
|
|
6967
7880
|
const selection = resolveStrategySelection({
|
|
6968
7881
|
deployment,
|
|
6969
7882
|
strategy: declaration
|