@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
package/dist/runtimeDashboard.js
CHANGED
|
@@ -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_data2, 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_data2 = 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_data2.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/runtimeDashboard.ts
|
|
31
661
|
var runtimeDashboard_exports = {};
|
|
32
662
|
__export(runtimeDashboard_exports, {
|
|
@@ -1333,12 +1963,266 @@ var syncRuntimeTrades = async ({
|
|
|
1333
1963
|
};
|
|
1334
1964
|
|
|
1335
1965
|
// src/runtimeStrategies.ts
|
|
1966
|
+
var import_jev8 = require("@tradejs/core/jev");
|
|
1336
1967
|
var import_node_crypto4 = require("crypto");
|
|
1337
|
-
var
|
|
1338
|
-
var
|
|
1968
|
+
var import_promises2 = require("fs/promises");
|
|
1969
|
+
var import_node_path2 = __toESM(require("path"));
|
|
1339
1970
|
var import_runtimeControls = require("@tradejs/infra/runtimeControls");
|
|
1340
1971
|
var import_tradingAccounts2 = require("@tradejs/infra/tradingAccounts");
|
|
1341
1972
|
|
|
1973
|
+
// src/strategyRuntime.ts
|
|
1974
|
+
var import_jev6 = require("@tradejs/core/jev");
|
|
1975
|
+
|
|
1976
|
+
// src/jev.ts
|
|
1977
|
+
var import_promises = __toESM(require("fs/promises"));
|
|
1978
|
+
var import_node_path = __toESM(require("path"));
|
|
1979
|
+
var import_jev4 = require("@tradejs/core/jev");
|
|
1980
|
+
var import_jev5 = require("@tradejs/infra/jev");
|
|
1981
|
+
var import_userSettings = require("@tradejs/infra/userSettings");
|
|
1982
|
+
init_jevInput();
|
|
1983
|
+
init_jevInput();
|
|
1984
|
+
init_jevTraining();
|
|
1985
|
+
var resolveJevProvider = async (userName) => {
|
|
1986
|
+
const settings = await (0, import_userSettings.getUserSettings)(userName);
|
|
1987
|
+
const provider = {
|
|
1988
|
+
endpoint: settings.JEV_API_ENDPOINT || import_jev4.JEV_PROVIDERS[0].endpoint,
|
|
1989
|
+
model: settings.JEV_MODEL || import_jev4.JEV_PROVIDERS[0].model
|
|
1990
|
+
};
|
|
1991
|
+
(0, import_jev4.parseJevConfig)({ source: "provider", mode: "observe", provider });
|
|
1992
|
+
if (!settings.JEV_API_KEY)
|
|
1993
|
+
throw new Error("Configure the Jev API key in Account settings");
|
|
1994
|
+
return provider;
|
|
1995
|
+
};
|
|
1996
|
+
var emptyScores = () => Object.fromEntries(import_jev4.JEV_DIMENSIONS.map((key) => [key, null]));
|
|
1997
|
+
var pending = /* @__PURE__ */ new Map();
|
|
1998
|
+
var pendingResponses = /* @__PURE__ */ new Map();
|
|
1999
|
+
var evaluateJevInput = async ({
|
|
2000
|
+
input,
|
|
2001
|
+
config,
|
|
2002
|
+
userName,
|
|
2003
|
+
projectRoot
|
|
2004
|
+
}) => {
|
|
2005
|
+
config = (0, import_jev4.parseJevConfig)(config);
|
|
2006
|
+
const inputHash = (0, import_jev5.jevHash)(input);
|
|
2007
|
+
const questions = questionsForJevInput(input);
|
|
2008
|
+
const questionsHash = (0, import_jev5.jevHash)(questions);
|
|
2009
|
+
const base = {
|
|
2010
|
+
schema: "tradejs-signal-assessment/v5",
|
|
2011
|
+
source: config.source,
|
|
2012
|
+
mode: config.mode,
|
|
2013
|
+
inputHash,
|
|
2014
|
+
questionsHash,
|
|
2015
|
+
geometryStatus: input.geometryStatus,
|
|
2016
|
+
levelsValid: typeof input.features["signal.validLevels"] === "boolean" ? input.features["signal.validLevels"] : null
|
|
2017
|
+
};
|
|
2018
|
+
if (!input.questions.length)
|
|
2019
|
+
return {
|
|
2020
|
+
assessment: {
|
|
2021
|
+
...base,
|
|
2022
|
+
status: "unavailable",
|
|
2023
|
+
model: "",
|
|
2024
|
+
scores: emptyScores(),
|
|
2025
|
+
confidence: emptyScores()
|
|
2026
|
+
}
|
|
2027
|
+
};
|
|
2028
|
+
if (config.source === "local") {
|
|
2029
|
+
const contents = await import_promises.default.readFile(
|
|
2030
|
+
import_node_path.default.resolve(projectRoot, config.modelFile),
|
|
2031
|
+
"utf8"
|
|
2032
|
+
);
|
|
2033
|
+
if ((0, import_jev5.jevFileHash)(contents) !== config.modelSha256)
|
|
2034
|
+
throw new Error("Local Jev gate checksum mismatch");
|
|
2035
|
+
const { validateJevGateModel: validateJevGateModel2 } = await Promise.resolve().then(() => (init_jevTraining(), jevTraining_exports));
|
|
2036
|
+
const model = validateJevGateModel2(JSON.parse(contents));
|
|
2037
|
+
if (model.strategy !== input.strategy || model.questionsHash !== (0, import_jev5.jevHash)(JEV_QUESTIONS))
|
|
2038
|
+
throw new Error("Local Jev gate strategy or question version mismatch");
|
|
2039
|
+
const scores = Object.fromEntries(
|
|
2040
|
+
import_jev4.JEV_DIMENSIONS.map((key) => [
|
|
2041
|
+
key,
|
|
2042
|
+
input.questions.includes(key) && model.trees[key] ? (0, import_jev4.predictJevTree)(model.trees[key], input.features) : null
|
|
2043
|
+
])
|
|
2044
|
+
);
|
|
2045
|
+
return {
|
|
2046
|
+
assessment: {
|
|
2047
|
+
...base,
|
|
2048
|
+
status: "available",
|
|
2049
|
+
model: config.modelSha256,
|
|
2050
|
+
scores,
|
|
2051
|
+
confidence: emptyScores()
|
|
2052
|
+
}
|
|
2053
|
+
};
|
|
2054
|
+
}
|
|
2055
|
+
const provider = config.provider ?? await resolveJevProvider(userName);
|
|
2056
|
+
const id = (0, import_jev5.jevHash)({ inputHash, questionsHash, provider });
|
|
2057
|
+
const dir = import_node_path.default.resolve(projectRoot, config.recordsDir ?? "data/ai/jev");
|
|
2058
|
+
const file = import_node_path.default.join(dir, "records", `${id}.json`);
|
|
2059
|
+
const getRecord = async () => {
|
|
2060
|
+
const existing = await (0, import_jev5.readJevArtifact)(file);
|
|
2061
|
+
if (existing) {
|
|
2062
|
+
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))
|
|
2063
|
+
throw new Error("Jev recording identity mismatch");
|
|
2064
|
+
(0, import_jev5.validateJevResponse)(existing.response, input.questions);
|
|
2065
|
+
for (const key of import_jev4.JEV_DIMENSIONS) {
|
|
2066
|
+
const expected = input.questions.includes(key) ? existing.response.answers[key].score / 4 : null;
|
|
2067
|
+
if (existing.scores[key] !== expected)
|
|
2068
|
+
throw new Error("Jev recording scores do not match the response");
|
|
2069
|
+
}
|
|
2070
|
+
return existing;
|
|
2071
|
+
}
|
|
2072
|
+
if (config.source === "recorded")
|
|
2073
|
+
throw new Error(
|
|
2074
|
+
`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.`
|
|
2075
|
+
);
|
|
2076
|
+
const settings = await (0, import_userSettings.getUserSettings)(userName);
|
|
2077
|
+
if (!settings.JEV_API_KEY)
|
|
2078
|
+
throw new Error("Configure the Jev API key in Account settings");
|
|
2079
|
+
const apiKey = settings.JEV_API_KEY;
|
|
2080
|
+
const configuredProvider = await resolveJevProvider(userName);
|
|
2081
|
+
if (configuredProvider.endpoint !== provider.endpoint)
|
|
2082
|
+
throw new Error(
|
|
2083
|
+
"Jev account endpoint changed; update the frozen provider configuration"
|
|
2084
|
+
);
|
|
2085
|
+
const started = Date.now();
|
|
2086
|
+
const state = stateForJevInput(input);
|
|
2087
|
+
const responseId = (0, import_jev5.jevHash)({
|
|
2088
|
+
state,
|
|
2089
|
+
questionsHash,
|
|
2090
|
+
provider,
|
|
2091
|
+
user: (0, import_jev5.jevHash)(userName)
|
|
2092
|
+
});
|
|
2093
|
+
const responseFile = import_node_path.default.join(dir, "responses", `${responseId}.json`);
|
|
2094
|
+
let responsePromise = pendingResponses.get(responseFile);
|
|
2095
|
+
if (!responsePromise) {
|
|
2096
|
+
responsePromise = (async () => {
|
|
2097
|
+
const saved = await (0, import_jev5.readJevArtifact)(responseFile);
|
|
2098
|
+
if (saved) return (0, import_jev5.validateJevResponse)(saved, input.questions);
|
|
2099
|
+
const fresh = await (0, import_jev5.requestJev)(provider, apiKey, state, questions);
|
|
2100
|
+
await (0, import_jev5.writeJevArtifact)(responseFile, fresh);
|
|
2101
|
+
return fresh;
|
|
2102
|
+
})();
|
|
2103
|
+
pendingResponses.set(responseFile, responsePromise);
|
|
2104
|
+
}
|
|
2105
|
+
let response;
|
|
2106
|
+
try {
|
|
2107
|
+
response = await responsePromise;
|
|
2108
|
+
} finally {
|
|
2109
|
+
pendingResponses.delete(responseFile);
|
|
2110
|
+
}
|
|
2111
|
+
const scores = Object.fromEntries(
|
|
2112
|
+
import_jev4.JEV_DIMENSIONS.map((key) => [
|
|
2113
|
+
key,
|
|
2114
|
+
input.questions.includes(key) ? response.answers[key].score / 4 : null
|
|
2115
|
+
])
|
|
2116
|
+
);
|
|
2117
|
+
const value = {
|
|
2118
|
+
schema: "tradejs-jev-record/v4",
|
|
2119
|
+
id,
|
|
2120
|
+
inputHash,
|
|
2121
|
+
questionsHash,
|
|
2122
|
+
provider,
|
|
2123
|
+
input,
|
|
2124
|
+
response,
|
|
2125
|
+
scores,
|
|
2126
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2127
|
+
elapsedMs: Date.now() - started
|
|
2128
|
+
};
|
|
2129
|
+
await (0, import_jev5.writeJevArtifact)(file, value);
|
|
2130
|
+
return await (0, import_jev5.readJevArtifact)(file);
|
|
2131
|
+
};
|
|
2132
|
+
const pendingKey = `${file}:${(0, import_jev5.jevHash)(userName)}`;
|
|
2133
|
+
let promise = pending.get(pendingKey);
|
|
2134
|
+
if (!promise) {
|
|
2135
|
+
promise = getRecord();
|
|
2136
|
+
pending.set(pendingKey, promise);
|
|
2137
|
+
}
|
|
2138
|
+
let record;
|
|
2139
|
+
try {
|
|
2140
|
+
record = await promise;
|
|
2141
|
+
} finally {
|
|
2142
|
+
pending.delete(pendingKey);
|
|
2143
|
+
}
|
|
2144
|
+
return {
|
|
2145
|
+
record,
|
|
2146
|
+
assessment: {
|
|
2147
|
+
...base,
|
|
2148
|
+
status: "available",
|
|
2149
|
+
recordId: id,
|
|
2150
|
+
model: record.response.model,
|
|
2151
|
+
scores: record.scores,
|
|
2152
|
+
confidence: Object.fromEntries(
|
|
2153
|
+
import_jev4.JEV_DIMENSIONS.map((key) => [
|
|
2154
|
+
key,
|
|
2155
|
+
record.response.answers[key]?.confidence ?? null
|
|
2156
|
+
])
|
|
2157
|
+
)
|
|
2158
|
+
}
|
|
2159
|
+
};
|
|
2160
|
+
};
|
|
2161
|
+
var assessSignalWithJev = async ({
|
|
2162
|
+
signal,
|
|
2163
|
+
config,
|
|
2164
|
+
userName,
|
|
2165
|
+
projectRoot,
|
|
2166
|
+
strict,
|
|
2167
|
+
scope = "runtime"
|
|
2168
|
+
}) => {
|
|
2169
|
+
let assessment;
|
|
2170
|
+
try {
|
|
2171
|
+
const input = buildJevInput(signal);
|
|
2172
|
+
({ assessment } = await evaluateJevInput({
|
|
2173
|
+
input,
|
|
2174
|
+
config,
|
|
2175
|
+
userName,
|
|
2176
|
+
projectRoot
|
|
2177
|
+
}));
|
|
2178
|
+
} catch (error) {
|
|
2179
|
+
if (strict) throw error;
|
|
2180
|
+
assessment = {
|
|
2181
|
+
schema: "tradejs-signal-assessment/v5",
|
|
2182
|
+
source: config.source,
|
|
2183
|
+
mode: config.mode,
|
|
2184
|
+
status: "unavailable",
|
|
2185
|
+
inputHash: "",
|
|
2186
|
+
questionsHash: "",
|
|
2187
|
+
geometryStatus: "absent",
|
|
2188
|
+
levelsValid: null,
|
|
2189
|
+
model: config.provider?.model ?? config.modelSha256 ?? "",
|
|
2190
|
+
scores: emptyScores(),
|
|
2191
|
+
confidence: emptyScores()
|
|
2192
|
+
};
|
|
2193
|
+
}
|
|
2194
|
+
signal.assessment = assessment;
|
|
2195
|
+
signal.additionalIndicators = {
|
|
2196
|
+
...signal.additionalIndicators,
|
|
2197
|
+
jev: {
|
|
2198
|
+
schema: "tradejs-jev-features/v2",
|
|
2199
|
+
status: assessment.status,
|
|
2200
|
+
scores: assessment.scores,
|
|
2201
|
+
confidence: assessment.confidence
|
|
2202
|
+
}
|
|
2203
|
+
};
|
|
2204
|
+
const identity = {
|
|
2205
|
+
signalId: signal.signalId,
|
|
2206
|
+
strategy: signal.strategy,
|
|
2207
|
+
symbol: signal.symbol,
|
|
2208
|
+
timestamp: signal.timestamp,
|
|
2209
|
+
scope,
|
|
2210
|
+
user: (0, import_jev5.jevHash)(userName),
|
|
2211
|
+
config,
|
|
2212
|
+
assessment
|
|
2213
|
+
};
|
|
2214
|
+
await (0, import_jev5.writeJevArtifact)(
|
|
2215
|
+
import_node_path.default.resolve(
|
|
2216
|
+
projectRoot,
|
|
2217
|
+
config.recordsDir ?? "data/ai/jev",
|
|
2218
|
+
"decisions",
|
|
2219
|
+
`${(0, import_jev5.jevHash)(identity)}.json`
|
|
2220
|
+
),
|
|
2221
|
+
identity
|
|
2222
|
+
);
|
|
2223
|
+
return assessment;
|
|
2224
|
+
};
|
|
2225
|
+
|
|
1342
2226
|
// src/strategyRuntime.ts
|
|
1343
2227
|
var import_constants7 = require("@tradejs/core/constants");
|
|
1344
2228
|
var import_strategies6 = require("@tradejs/core/strategies");
|
|
@@ -2330,8 +3214,11 @@ var toRecord2 = (value) => {
|
|
|
2330
3214
|
return value;
|
|
2331
3215
|
};
|
|
2332
3216
|
var buildBaseAiPayload = (signal) => {
|
|
3217
|
+
const { jevEvidence: _jevEvidence, ...signalAdditionalIndicators } = toRecord2(
|
|
3218
|
+
signal.additionalIndicators
|
|
3219
|
+
);
|
|
2333
3220
|
const additionalIndicators = {
|
|
2334
|
-
...
|
|
3221
|
+
...signalAdditionalIndicators,
|
|
2335
3222
|
marketContext: buildAiMarketContext(signal)
|
|
2336
3223
|
};
|
|
2337
3224
|
return {
|
|
@@ -2385,7 +3272,7 @@ var postProcessLocalAiAnalysisByStrategy = (signal, analysis, payload = buildAiP
|
|
|
2385
3272
|
var import_aiEndpoints = require("@tradejs/core/aiEndpoints");
|
|
2386
3273
|
var import_aiModels = require("@tradejs/core/aiModels");
|
|
2387
3274
|
var import_aiLanguages = require("@tradejs/core/aiLanguages");
|
|
2388
|
-
var
|
|
3275
|
+
var import_userSettings2 = require("@tradejs/infra/userSettings");
|
|
2389
3276
|
var DEFAULT_AI_MODEL = "openai/gpt-5-mini";
|
|
2390
3277
|
var userSettingsCache = /* @__PURE__ */ new Map();
|
|
2391
3278
|
var aiModelCache = /* @__PURE__ */ new Map();
|
|
@@ -2433,7 +3320,7 @@ var getOpenRouterModelKwargs = (apiEndpoint) => {
|
|
|
2433
3320
|
var getAiUserSettings = async (userName = "root") => {
|
|
2434
3321
|
let settingsPromise = userSettingsCache.get(userName);
|
|
2435
3322
|
if (!settingsPromise) {
|
|
2436
|
-
settingsPromise = (0,
|
|
3323
|
+
settingsPromise = (0, import_userSettings2.getUserSettings)(userName).then((settings2) => {
|
|
2437
3324
|
const endpoint = (0, import_aiEndpoints.normalizeAiEndpoint)(settings2.AI_API_ENDPOINT);
|
|
2438
3325
|
return {
|
|
2439
3326
|
...settings2,
|
|
@@ -4393,7 +5280,7 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
4393
5280
|
|
|
4394
5281
|
// src/strategyHelpers/derivativesContext.ts
|
|
4395
5282
|
var import_indicators2 = require("@tradejs/core/indicators");
|
|
4396
|
-
var
|
|
5283
|
+
var import_data3 = require("@tradejs/core/data");
|
|
4397
5284
|
var import_strategies3 = require("@tradejs/core/strategies");
|
|
4398
5285
|
var import_constants4 = require("@tradejs/core/constants");
|
|
4399
5286
|
var import_derivatives = require("@tradejs/infra/timescale/derivatives");
|
|
@@ -4553,7 +5440,7 @@ var enrichSignalWithDerivativesContext = async (params) => {
|
|
|
4553
5440
|
const referenceSymbols = getDerivativesContextReferenceSymbols();
|
|
4554
5441
|
const targetSymbol = normalizeSymbol(signal.symbol);
|
|
4555
5442
|
const lookbackMs = parseLookbackMs();
|
|
4556
|
-
const decisionTimeMs = signal.timestamp + (0,
|
|
5443
|
+
const decisionTimeMs = signal.timestamp + (0, import_data3.intervalToMs)(signal.interval);
|
|
4557
5444
|
const derivativesEndMs = (0, import_indicators2.getLastClosedDerivativesBarStartMs)(
|
|
4558
5445
|
decisionTimeMs,
|
|
4559
5446
|
"15m"
|
|
@@ -4638,7 +5525,7 @@ var enrichSignalWithDerivativesContext = async (params) => {
|
|
|
4638
5525
|
};
|
|
4639
5526
|
|
|
4640
5527
|
// src/strategyHelpers/hyperliquidWhaleContext.ts
|
|
4641
|
-
var
|
|
5528
|
+
var import_data4 = require("@tradejs/core/data");
|
|
4642
5529
|
var import_strategies4 = require("@tradejs/core/strategies");
|
|
4643
5530
|
var import_hyperliquidWhales2 = require("@tradejs/infra/timescale/hyperliquidWhales");
|
|
4644
5531
|
var import_logger9 = require("@tradejs/infra/logger");
|
|
@@ -5112,7 +5999,7 @@ var loadHyperliquidWhaleFlowContext = async (params) => {
|
|
|
5112
5999
|
const symbol = resolveHyperliquidPerpFromSignalSymbol(params.symbol);
|
|
5113
6000
|
if (!symbol) return null;
|
|
5114
6001
|
const interval = params.marketInterval ?? signalIntervalToMarketInterval2(params.interval);
|
|
5115
|
-
const decisionTimeMs = params.timestamp + (0,
|
|
6002
|
+
const decisionTimeMs = params.timestamp + (0, import_data4.intervalToMs)(params.interval);
|
|
5116
6003
|
const maxAgeMs = params.maxAgeMs ?? DEFAULT_MAX_AGE_BY_INTERVAL2[interval];
|
|
5117
6004
|
const universe = getHyperliquidPerpUniverseSnapshot();
|
|
5118
6005
|
const whales = getHyperliquidWhaleRegistrySnapshot();
|
|
@@ -5139,9 +6026,9 @@ var loadHyperliquidWhaleFlowContext = async (params) => {
|
|
|
5139
6026
|
abortSignal: params.abortSignal
|
|
5140
6027
|
});
|
|
5141
6028
|
} else {
|
|
5142
|
-
let
|
|
5143
|
-
if (!
|
|
5144
|
-
|
|
6029
|
+
let pending2 = contextCache.get(cacheKey);
|
|
6030
|
+
if (!pending2) {
|
|
6031
|
+
pending2 = (0, import_hyperliquidWhales2.getHyperliquidWhaleFlowAggregate)({
|
|
5145
6032
|
symbol,
|
|
5146
6033
|
interval,
|
|
5147
6034
|
decisionTimeMs,
|
|
@@ -5150,9 +6037,9 @@ var loadHyperliquidWhaleFlowContext = async (params) => {
|
|
|
5150
6037
|
whaleRegistryFingerprint: whales.fingerprint,
|
|
5151
6038
|
...params.abortSignal ? { signal: params.abortSignal } : {}
|
|
5152
6039
|
});
|
|
5153
|
-
setBoundedCache2(cacheKey,
|
|
6040
|
+
setBoundedCache2(cacheKey, pending2);
|
|
5154
6041
|
}
|
|
5155
|
-
row = await
|
|
6042
|
+
row = await pending2;
|
|
5156
6043
|
}
|
|
5157
6044
|
if (!row) return null;
|
|
5158
6045
|
return toBaseHyperliquidWhaleFlowContext(row, minimumCoveragePct);
|
|
@@ -5713,7 +6600,8 @@ var executeEntryOrder = async ({
|
|
|
5713
6600
|
runtimeConfigId: signal.runtimeConfigId,
|
|
5714
6601
|
strategyRevision: signal.strategyRevision,
|
|
5715
6602
|
runtimeLineage: signal.runtimeLineage,
|
|
5716
|
-
...signal.aiAnalysis ? { aiAnalysis: signal.aiAnalysis } : {}
|
|
6603
|
+
...signal.aiAnalysis ? { aiAnalysis: signal.aiAnalysis } : {},
|
|
6604
|
+
...signal.assessment ? { assessment: signal.assessment } : {}
|
|
5717
6605
|
});
|
|
5718
6606
|
}
|
|
5719
6607
|
if (orderPlaced && recordRuntimeTrade && isPositionIncrease) {
|
|
@@ -5786,7 +6674,7 @@ var resolveStrategyConfig = async ({
|
|
|
5786
6674
|
|
|
5787
6675
|
// src/strategy/runtimeBacktestDelay.ts
|
|
5788
6676
|
var import_constants6 = require("@tradejs/core/constants");
|
|
5789
|
-
var
|
|
6677
|
+
var import_data5 = require("@tradejs/core/data");
|
|
5790
6678
|
var import_strategies5 = require("@tradejs/core/strategies");
|
|
5791
6679
|
var resolveBacktestEntryDelayBars = (value) => {
|
|
5792
6680
|
if (value == null || value === "") {
|
|
@@ -5814,7 +6702,7 @@ var resolveBacktestExecutionDelayMs = (value, fallbackDelayMs) => {
|
|
|
5814
6702
|
};
|
|
5815
6703
|
var safeIntervalToMs = (interval) => {
|
|
5816
6704
|
try {
|
|
5817
|
-
return (0,
|
|
6705
|
+
return (0, import_data5.intervalToMs)(interval);
|
|
5818
6706
|
} catch {
|
|
5819
6707
|
return null;
|
|
5820
6708
|
}
|
|
@@ -6438,6 +7326,7 @@ var handleExitDecision = async ({
|
|
|
6438
7326
|
onRuntimeClose?.({
|
|
6439
7327
|
userName,
|
|
6440
7328
|
strategy: strategyName,
|
|
7329
|
+
deploymentId: connector.deploymentId,
|
|
6441
7330
|
openedByStrategy: trade.strategy,
|
|
6442
7331
|
symbol,
|
|
6443
7332
|
direction: trade.direction,
|
|
@@ -6730,6 +7619,11 @@ var createStrategyRuntime = ({
|
|
|
6730
7619
|
const projectConfig = await loadTradejsConfig(projectRoot);
|
|
6731
7620
|
const projectHooks = projectConfig.hooks;
|
|
6732
7621
|
const env = String(config.ENV ?? "BACKTEST");
|
|
7622
|
+
const jev = (0, import_jev6.parseJevConfig)(config.JEV);
|
|
7623
|
+
if (jev && env !== "BACKTEST" && jev.source === "provider" && !jev.provider)
|
|
7624
|
+
throw new Error(
|
|
7625
|
+
"Runtime JEV requires a frozen provider endpoint and model"
|
|
7626
|
+
);
|
|
6733
7627
|
const backtestPriceMode = config.BACKTEST_PRICE_MODE ?? "open";
|
|
6734
7628
|
const backtestEntryDelayBars = env === "BACKTEST" ? resolveBacktestEntryDelayBars(config.BACKTEST_ENTRY_DELAY_BARS) : 0;
|
|
6735
7629
|
const resolvedBacktestExecutionInterval = config.BACKTEST_EXECUTION_INTERVAL ?? backtestExecutionMarketData?.interval ?? resolveBacktestExecutionIntervalForPrimary(config.INTERVAL ?? "15");
|
|
@@ -7150,44 +8044,44 @@ var createStrategyRuntime = ({
|
|
|
7150
8044
|
if (pendingBacktestEntry.delayBarsRemaining > 0) {
|
|
7151
8045
|
return `BACKTEST_ENTRY_DELAY_PENDING:${pendingBacktestEntry.delayBarsRemaining}`;
|
|
7152
8046
|
}
|
|
7153
|
-
const
|
|
8047
|
+
const pending2 = pendingBacktestEntry;
|
|
7154
8048
|
pendingBacktestEntry = null;
|
|
7155
8049
|
const executionCandleResolution = resolveBacktestExecutionCandle(
|
|
7156
8050
|
candle,
|
|
7157
8051
|
btcCandle
|
|
7158
8052
|
);
|
|
7159
8053
|
const execution = applyBacktestDelayedEntryExecution({
|
|
7160
|
-
decision:
|
|
8054
|
+
decision: pending2.decision,
|
|
7161
8055
|
execution: executionCandleResolution,
|
|
7162
8056
|
backtestPriceMode: executionCandleResolution.source === "primary_timeframe" ? "open" : backtestPriceMode,
|
|
7163
|
-
delayBars:
|
|
8057
|
+
delayBars: pending2.delayBars
|
|
7164
8058
|
});
|
|
7165
8059
|
if (execution.skipReason) {
|
|
7166
|
-
return
|
|
8060
|
+
return pending2.decision.signal ?? execution.skipReason;
|
|
7167
8061
|
}
|
|
7168
8062
|
if (!execution.executionCandle || !execution.btcExecutionCandle) {
|
|
7169
|
-
return
|
|
8063
|
+
return pending2.decision.signal ?? "BACKTEST_LOWER_EXECUTION_CANDLE_MISSING";
|
|
7170
8064
|
}
|
|
7171
8065
|
const market = {
|
|
7172
8066
|
candle: execution.executionCandle,
|
|
7173
8067
|
btcCandle: execution.btcExecutionCandle
|
|
7174
8068
|
};
|
|
7175
8069
|
const entry = buildHookEntry({
|
|
7176
|
-
decision:
|
|
7177
|
-
runtime:
|
|
8070
|
+
decision: pending2.decision,
|
|
8071
|
+
runtime: pending2.runtime
|
|
7178
8072
|
});
|
|
7179
8073
|
return executeEntryDecision({
|
|
7180
8074
|
connector,
|
|
7181
8075
|
symbol,
|
|
7182
|
-
decision:
|
|
7183
|
-
runtime:
|
|
7184
|
-
manifest:
|
|
7185
|
-
hookCtx:
|
|
8076
|
+
decision: pending2.decision,
|
|
8077
|
+
runtime: pending2.runtime,
|
|
8078
|
+
manifest: pending2.manifest,
|
|
8079
|
+
hookCtx: pending2.hookCtx,
|
|
7186
8080
|
market,
|
|
7187
8081
|
entry,
|
|
7188
|
-
policy:
|
|
7189
|
-
ml:
|
|
7190
|
-
ai:
|
|
8082
|
+
policy: pending2.policy,
|
|
8083
|
+
ml: pending2.ml,
|
|
8084
|
+
ai: pending2.ai,
|
|
7191
8085
|
recordRuntimeJournal,
|
|
7192
8086
|
invokeStageHooks,
|
|
7193
8087
|
notifyRuntimeError
|
|
@@ -7437,6 +8331,16 @@ var createStrategyRuntime = ({
|
|
|
7437
8331
|
env,
|
|
7438
8332
|
includeHyperliquidWhales: false
|
|
7439
8333
|
});
|
|
8334
|
+
if (jev) {
|
|
8335
|
+
await assessSignalWithJev({
|
|
8336
|
+
signal,
|
|
8337
|
+
config: jev,
|
|
8338
|
+
userName,
|
|
8339
|
+
projectRoot,
|
|
8340
|
+
strict: env === "BACKTEST",
|
|
8341
|
+
scope: runtimeLineage?.deploymentCompositionId ?? (env === "BACKTEST" ? "backtest" : "runtime")
|
|
8342
|
+
});
|
|
8343
|
+
}
|
|
7440
8344
|
quality = await enrichSignalWithAi({
|
|
7441
8345
|
signal,
|
|
7442
8346
|
userName,
|
|
@@ -7506,6 +8410,7 @@ var createStrategyRuntime = ({
|
|
|
7506
8410
|
if (!shouldMakeOrder) {
|
|
7507
8411
|
if (signal) {
|
|
7508
8412
|
const skipReason = getEntrySkipReason({
|
|
8413
|
+
signal,
|
|
7509
8414
|
makeOrdersEnabled,
|
|
7510
8415
|
env,
|
|
7511
8416
|
ml,
|
|
@@ -7719,10 +8624,10 @@ var computeDeploymentCompositionId = (value) => revision("dc1", {
|
|
|
7719
8624
|
}))
|
|
7720
8625
|
});
|
|
7721
8626
|
var readRuntimePackageManifest = async (projectRoot) => {
|
|
7722
|
-
const manifestPath = process.env.TRADEJS_RUNTIME_PACKAGE_MANIFEST?.trim() ||
|
|
8627
|
+
const manifestPath = process.env.TRADEJS_RUNTIME_PACKAGE_MANIFEST?.trim() || import_node_path2.default.join(projectRoot, "runtime-package-manifest.json");
|
|
7723
8628
|
let value;
|
|
7724
8629
|
try {
|
|
7725
|
-
value = JSON.parse(await (0,
|
|
8630
|
+
value = JSON.parse(await (0, import_promises2.readFile)(manifestPath, "utf8"));
|
|
7726
8631
|
} catch (error) {
|
|
7727
8632
|
throw new Error(
|
|
7728
8633
|
`Unable to read runtime package manifest ${manifestPath}: ${String(error)}`
|
|
@@ -7761,7 +8666,7 @@ var verifyInstalledPackageName = async ({
|
|
|
7761
8666
|
throw new Error("name is invalid");
|
|
7762
8667
|
}
|
|
7763
8668
|
const projectManifest = JSON.parse(
|
|
7764
|
-
await (0,
|
|
8669
|
+
await (0, import_promises2.readFile)(import_node_path2.default.join(projectRoot, "package.json"), "utf8")
|
|
7765
8670
|
);
|
|
7766
8671
|
const alias = parseNpmAliasReference(
|
|
7767
8672
|
projectManifest.dependencies?.[packageName]
|
|
@@ -7775,14 +8680,14 @@ var readInstalledPackageMetadata = async ({
|
|
|
7775
8680
|
packageName,
|
|
7776
8681
|
projectPackage
|
|
7777
8682
|
}) => {
|
|
7778
|
-
const packageJsonPath = projectPackage ?
|
|
8683
|
+
const packageJsonPath = projectPackage ? import_node_path2.default.join(projectRoot, "package.json") : import_node_path2.default.join(
|
|
7779
8684
|
projectRoot,
|
|
7780
8685
|
"node_modules",
|
|
7781
8686
|
...packageName.split("/"),
|
|
7782
8687
|
"package.json"
|
|
7783
8688
|
);
|
|
7784
8689
|
try {
|
|
7785
|
-
const packageJson = JSON.parse(await (0,
|
|
8690
|
+
const packageJson = JSON.parse(await (0, import_promises2.readFile)(packageJsonPath, "utf8"));
|
|
7786
8691
|
if (typeof packageJson.version !== "string" || !packageJson.version.trim()) {
|
|
7787
8692
|
throw new Error("version is invalid");
|
|
7788
8693
|
}
|
|
@@ -7857,12 +8762,12 @@ var resolveStrategyPackage = async ({
|
|
|
7857
8762
|
projectRoot
|
|
7858
8763
|
}) => {
|
|
7859
8764
|
if (!pluginSource) return null;
|
|
7860
|
-
if (!pluginSource.startsWith(".") && !
|
|
8765
|
+
if (!pluginSource.startsWith(".") && !import_node_path2.default.isAbsolute(pluginSource)) {
|
|
7861
8766
|
return { name: pluginSource, projectPackage: false };
|
|
7862
8767
|
}
|
|
7863
8768
|
try {
|
|
7864
8769
|
const packageJson = JSON.parse(
|
|
7865
|
-
await (0,
|
|
8770
|
+
await (0, import_promises2.readFile)(import_node_path2.default.join(projectRoot, "package.json"), "utf8")
|
|
7866
8771
|
);
|
|
7867
8772
|
return typeof packageJson.name === "string" && packageJson.name.trim() ? { name: packageJson.name, projectPackage: true } : null;
|
|
7868
8773
|
} catch {
|
|
@@ -7966,7 +8871,15 @@ var resolveStrategyComposition = async ({
|
|
|
7966
8871
|
`Strategy config parser returned a non-object: ${strategyName}`
|
|
7967
8872
|
);
|
|
7968
8873
|
}
|
|
7969
|
-
const
|
|
8874
|
+
const jev = (0, import_jev8.parseJevConfig)(declaration.config.JEV);
|
|
8875
|
+
if (jev && jev.source === "provider" && !jev.provider)
|
|
8876
|
+
throw new Error(
|
|
8877
|
+
"Runtime JEV requires a frozen provider endpoint and model"
|
|
8878
|
+
);
|
|
8879
|
+
const strategyConfig = {
|
|
8880
|
+
...parsedConfig,
|
|
8881
|
+
...jev ? { JEV: jev } : {}
|
|
8882
|
+
};
|
|
7970
8883
|
const selection = resolveStrategySelection({
|
|
7971
8884
|
deployment,
|
|
7972
8885
|
strategy: declaration
|