@tradejs/node 3.1.29-beta.261 → 3.1.29-beta.263
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/backtest.js +189 -75
- package/dist/backtest.mjs +3 -3
- package/dist/{chunk-ZZQLTUTP.mjs → chunk-GHSLOJLE.mjs} +195 -69
- package/dist/chunk-JQOLTLEY.mjs +0 -0
- package/dist/{chunk-WGL7XJ3W.mjs → chunk-SKY2HNZU.mjs} +1 -1
- package/dist/{chunk-3BBXGZ7M.mjs → chunk-YR55Q355.mjs} +11 -11
- package/dist/chunk-ZUMUI37V.mjs +0 -0
- package/dist/connectors.mjs +1 -0
- package/dist/diagnostics.d.mts +182 -0
- package/dist/diagnostics.d.ts +182 -0
- package/dist/diagnostics.js +9060 -0
- package/dist/diagnostics.mjs +867 -0
- package/dist/jev.d.mts +64 -69
- package/dist/jev.d.ts +64 -69
- package/dist/jev.js +193 -75
- package/dist/jev.mjs +6 -2
- package/dist/{jevTraining-JYXUVAZG.mjs → jevTraining-J4Y4VCM4.mjs} +1 -1
- package/dist/mcp.d.mts +203 -0
- package/dist/mcp.d.ts +203 -0
- package/dist/mcp.js +9257 -0
- package/dist/mcp.mjs +584 -0
- package/dist/registry.js +189 -75
- package/dist/registry.mjs +4 -3
- package/dist/runtimeDashboard.js +189 -75
- package/dist/runtimeDashboard.mjs +7 -7
- package/dist/runtimeStrategies.js +189 -75
- package/dist/runtimeStrategies.mjs +3 -3
- package/dist/strategies.js +189 -75
- package/dist/strategies.mjs +3 -3
- package/package.json +17 -6
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
// src/jevTraining.ts
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
JEV_DIMENSIONS as JEV_DIMENSIONS2,
|
|
4
|
+
JEV_QUESTION_SET as JEV_QUESTION_SET2,
|
|
5
|
+
decideJev,
|
|
6
|
+
predictJevTree
|
|
7
|
+
} from "@tradejs/core/jev";
|
|
8
|
+
import {
|
|
9
|
+
jevScoreMaximum,
|
|
10
|
+
jevHash,
|
|
11
|
+
validateJevResponse
|
|
12
|
+
} from "@tradejs/infra/jev";
|
|
4
13
|
|
|
5
14
|
// src/jevInput.ts
|
|
6
15
|
import { intervalToMs } from "@tradejs/core/data";
|
|
7
|
-
import { JEV_DIMENSIONS } from "@tradejs/core/jev";
|
|
16
|
+
import { JEV_DIMENSIONS, JEV_QUESTION_SET } from "@tradejs/core/jev";
|
|
8
17
|
var object = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
9
18
|
var at = (value, ...path) => path.reduce((current, key) => object(current)[key], value);
|
|
10
19
|
var finite = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
@@ -83,6 +92,35 @@ var buildJevInput = (signal) => {
|
|
|
83
92
|
const stop = finite(signal.prices.stopLossPrice);
|
|
84
93
|
const target = finite(signal.prices.takeProfitPrice);
|
|
85
94
|
const atr = baseProblem ? null : finite(at(base, "raw", "volatility", "atr"));
|
|
95
|
+
if (!baseProblem) {
|
|
96
|
+
const open = finite(at(base, "candle", "open"));
|
|
97
|
+
const close = finite(at(base, "candle", "close"));
|
|
98
|
+
const high = finite(at(base, "candle", "high"));
|
|
99
|
+
const low = finite(at(base, "candle", "low"));
|
|
100
|
+
const sign = signal.direction === "LONG" ? 1 : -1;
|
|
101
|
+
const candleFact = {
|
|
102
|
+
knownAt: baseKnownAt,
|
|
103
|
+
scope: "target",
|
|
104
|
+
source: "baseContext.candle"
|
|
105
|
+
};
|
|
106
|
+
if (open != null && close != null && atr != null && atr > 0)
|
|
107
|
+
put("entry.directionalBodyAtr", sign * (close - open) / atr, {
|
|
108
|
+
...candleFact,
|
|
109
|
+
unit: "ATR"
|
|
110
|
+
});
|
|
111
|
+
if (high != null && low != null && close != null && high > low && close >= low && close <= high) {
|
|
112
|
+
put(
|
|
113
|
+
"entry.directionalCloseLocation",
|
|
114
|
+
signal.direction === "LONG" ? (close - low) / (high - low) : (high - close) / (high - low),
|
|
115
|
+
{ ...candleFact, unit: "ratio" }
|
|
116
|
+
);
|
|
117
|
+
if (atr != null && atr > 0)
|
|
118
|
+
put("entry.rangeAtr", (high - low) / atr, {
|
|
119
|
+
...candleFact,
|
|
120
|
+
unit: "ATR"
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
86
124
|
if (price == null || stop == null || target == null)
|
|
87
125
|
throw new Error("Invalid Jev signal prices");
|
|
88
126
|
put(
|
|
@@ -169,33 +207,30 @@ var buildJevInput = (signal) => {
|
|
|
169
207
|
);
|
|
170
208
|
const questions = JEV_DIMENSIONS.filter((dimension) => {
|
|
171
209
|
switch (dimension) {
|
|
210
|
+
case "signalStrength":
|
|
211
|
+
return has("signal.validLevels");
|
|
172
212
|
case "trend":
|
|
173
213
|
return has("market.trendBias");
|
|
174
214
|
case "swing":
|
|
175
|
-
return has("
|
|
215
|
+
return has("setup.retracementRatio");
|
|
176
216
|
case "participation":
|
|
177
|
-
return has("market.volumeRel20")
|
|
217
|
+
return has("market.volumeRel20");
|
|
178
218
|
case "setupParticipation":
|
|
179
219
|
return setupKeys.some(
|
|
180
220
|
(key) => /volume|delta|flow|liquidity|turnover/i.test(key)
|
|
181
221
|
);
|
|
182
222
|
case "extension":
|
|
183
|
-
return has("
|
|
223
|
+
return has("setup.breakoutDistanceAtr");
|
|
184
224
|
case "confirmation":
|
|
185
|
-
return
|
|
186
|
-
(key) => /stage|confirm|retest|accept/i.test(key)
|
|
187
|
-
);
|
|
225
|
+
return has("entry.directionalBodyAtr") && has("entry.directionalCloseLocation");
|
|
188
226
|
case "setupStrength":
|
|
189
|
-
return setupKeys.some(
|
|
190
|
-
(key) => key !== "setup.version" && !/volume|delta|flow|liquidity|turnover|stage|confirm|retest|accept|breakoutDistance/i.test(
|
|
191
|
-
key
|
|
192
|
-
)
|
|
193
|
-
);
|
|
227
|
+
return setupKeys.some((key) => /efficiencyRatio$/i.test(key));
|
|
194
228
|
case "geometry":
|
|
195
229
|
return geometryStatus !== "invalid" && Object.keys(features).some((key) => key.startsWith("geometry."));
|
|
196
230
|
}
|
|
197
231
|
});
|
|
198
232
|
return {
|
|
233
|
+
questionSet: JEV_QUESTION_SET,
|
|
199
234
|
schema: "tradejs-jev-input/v4",
|
|
200
235
|
strategy: signal.strategy,
|
|
201
236
|
symbol: signal.symbol,
|
|
@@ -209,68 +244,155 @@ var buildJevInput = (signal) => {
|
|
|
209
244
|
geometryStatus
|
|
210
245
|
};
|
|
211
246
|
};
|
|
212
|
-
var
|
|
213
|
-
`${subject} is clearly contradicted by the supplied facts.`,
|
|
214
|
-
`${subject} has a material conflict.`,
|
|
215
|
-
`${subject} is uncertain from the supplied facts.`,
|
|
216
|
-
`${subject} has adequate support.`,
|
|
217
|
-
`${subject} has strong support without a material conflict.`
|
|
218
|
-
];
|
|
219
|
-
var JEV_QUESTIONS = {
|
|
247
|
+
var JEV_COMPONENT_QUESTIONS = {
|
|
220
248
|
trend: {
|
|
221
249
|
type: "score",
|
|
222
|
-
instructions: "
|
|
223
|
-
criteria:
|
|
250
|
+
instructions: "Rate agreement of market.trendBias and market.swingBias with signal.direction: bull supports LONG, bear supports SHORT; neutral/unknown is inconclusive. No profit prediction.",
|
|
251
|
+
criteria: [
|
|
252
|
+
"Both oppose.",
|
|
253
|
+
"One opposes; other inconclusive.",
|
|
254
|
+
"Biases conflict, or both inconclusive.",
|
|
255
|
+
"One supports; other inconclusive.",
|
|
256
|
+
"Both support."
|
|
257
|
+
]
|
|
224
258
|
},
|
|
225
259
|
swing: {
|
|
226
260
|
type: "score",
|
|
227
|
-
instructions: "
|
|
228
|
-
criteria:
|
|
261
|
+
instructions: "Rate shallow correction using only setup.retracementRatio (correction / preceding impulse). Missing is inconclusive, not favorable.",
|
|
262
|
+
criteria: [
|
|
263
|
+
"Exceeds impulse (>1).",
|
|
264
|
+
"Deep (>0.65 to 1).",
|
|
265
|
+
"Moderate (>0.4 to 0.65), or missing.",
|
|
266
|
+
"Contained (>0.2 to 0.4).",
|
|
267
|
+
"Shallow (0 to 0.2)."
|
|
268
|
+
]
|
|
229
269
|
},
|
|
230
270
|
participation: {
|
|
231
271
|
type: "score",
|
|
232
|
-
instructions: "
|
|
233
|
-
criteria:
|
|
272
|
+
instructions: "Rate activity using only market.volumeRel20 (candle volume / trailing 20-candle mean). Not directional flow; ignore setup volume.",
|
|
273
|
+
criteria: [
|
|
274
|
+
"Very low (<0.5).",
|
|
275
|
+
"Below ordinary (0.5 to <0.8).",
|
|
276
|
+
"Ordinary (0.8 to <1.2), or missing.",
|
|
277
|
+
"Elevated (1.2 to <2).",
|
|
278
|
+
"Strong expansion (>=2)."
|
|
279
|
+
]
|
|
234
280
|
},
|
|
235
281
|
setupParticipation: {
|
|
236
282
|
type: "score",
|
|
237
|
-
instructions: "
|
|
238
|
-
criteria:
|
|
283
|
+
instructions: "Rate consolidation volume contraction using a supplied setup consolidation / preceding impulse volume ratio, e.g. setup.flagToPoleVolumeRatio. Smaller means stronger contraction. Ignore current volume and directional flow; unrelated setup semantics are inconclusive.",
|
|
284
|
+
criteria: [
|
|
285
|
+
"Exceeds impulse (>1.2).",
|
|
286
|
+
"No contraction (>0.9 to 1.2).",
|
|
287
|
+
"Mild (>0.7 to 0.9), or inconclusive.",
|
|
288
|
+
"Clear (>0.4 to 0.7).",
|
|
289
|
+
"Strong (0 to 0.4)."
|
|
290
|
+
]
|
|
239
291
|
},
|
|
240
292
|
extension: {
|
|
241
293
|
type: "score",
|
|
242
|
-
instructions: "
|
|
243
|
-
criteria:
|
|
294
|
+
instructions: "Rate absence of entry extension using only setup.breakoutDistanceAtr (distance past setup boundary in ATR). Stop/target distances are not extension.",
|
|
295
|
+
criteria: [
|
|
296
|
+
"Extreme (>2 ATR).",
|
|
297
|
+
"Large (>1 to 2 ATR).",
|
|
298
|
+
"Moderate (>0.5 to 1 ATR), or missing.",
|
|
299
|
+
"Small (>0.2 to 0.5 ATR).",
|
|
300
|
+
"Near boundary (0 to 0.2 ATR)."
|
|
301
|
+
]
|
|
244
302
|
},
|
|
245
303
|
confirmation: {
|
|
246
304
|
type: "score",
|
|
247
|
-
instructions: "
|
|
248
|
-
criteria:
|
|
305
|
+
instructions: "Rate closed entry candle conviction using entry.directionalBodyAtr (signed toward signal.direction) and entry.directionalCloseLocation (1=favorable extreme). Stage names alone prove neither conviction nor breakout retention.",
|
|
306
|
+
criteria: [
|
|
307
|
+
"Opposing body AND unfavorable-quarter close.",
|
|
308
|
+
"Opposing body OR unfavorable-half close.",
|
|
309
|
+
"Small body (<0.2 ATR), mixed or missing evidence.",
|
|
310
|
+
"Supporting body (>=0.2 ATR) AND favorable-half close.",
|
|
311
|
+
"Strong supporting body (>=0.5 ATR) AND favorable-quarter close."
|
|
312
|
+
]
|
|
249
313
|
},
|
|
250
314
|
setupStrength: {
|
|
251
315
|
type: "score",
|
|
252
|
-
instructions: "
|
|
253
|
-
criteria:
|
|
316
|
+
instructions: "Rate impulse persistence using only supplied setup efficiency (absolute net move / sum of absolute bar moves), e.g. setup.poleEfficiencyRatio. Exclude size, volume, correction and geometry.",
|
|
317
|
+
criteria: [
|
|
318
|
+
"Highly choppy (<0.2).",
|
|
319
|
+
"Weak (0.2 to <0.4).",
|
|
320
|
+
"Mixed (0.4 to <0.6), or missing.",
|
|
321
|
+
"Clear (0.6 to <0.8).",
|
|
322
|
+
"Highly persistent (0.8 to 1)."
|
|
323
|
+
]
|
|
254
324
|
},
|
|
255
325
|
geometry: {
|
|
256
326
|
type: "score",
|
|
257
|
-
instructions: "
|
|
258
|
-
criteria:
|
|
327
|
+
instructions: "Rate fitted consolidation boundaries jointly: geometry.upperR2 and lowerR2 (1=best fit), slopeDivergenceRatio (0=parallel). Use the weaker fit; exclude depth, width, volume and profit. Missing equivalent geometry is inconclusive.",
|
|
328
|
+
criteria: [
|
|
329
|
+
"Fit <0.5 OR divergence >0.5.",
|
|
330
|
+
"Fit <0.7 OR divergence >0.3.",
|
|
331
|
+
"Fit <0.85 OR divergence >0.15, or missing.",
|
|
332
|
+
"Both fits >=0.85 AND divergence <=0.15.",
|
|
333
|
+
"Both fits >=0.95 AND divergence <=0.05."
|
|
334
|
+
]
|
|
259
335
|
}
|
|
260
336
|
};
|
|
261
|
-
var
|
|
337
|
+
var JEV_SIGNAL_STRENGTH_QUESTION = {
|
|
338
|
+
type: "score",
|
|
339
|
+
instructions: "Rate overall support for entry now from signal-time facts: direction, market, setup, candle, placement, relevant geometry and stop/target plan. No other Jev answers, future prices or outcomes. Essential missing facts and conflicts weaken support; optional missing geometry alone does not disqualify. Invalid signal.validLevels cannot rate strong. Not win probability or execution permission. Criteria 1\u201310 map to API indices 0\u20139.",
|
|
340
|
+
criteria: [
|
|
341
|
+
"1: Invalid levels or decisive contrary evidence.",
|
|
342
|
+
"2: Severe directional/setup conflicts; poor support.",
|
|
343
|
+
"3: Substantial weaknesses outweigh support.",
|
|
344
|
+
"4: Some support; material weakness makes entry unattractive.",
|
|
345
|
+
"5: Balanced conflicts or essential evidence missing.",
|
|
346
|
+
"6: Modest coherent support; reasonable but limited evidence.",
|
|
347
|
+
"7: Convincing support from several facts; remaining weakness.",
|
|
348
|
+
"8: Strong coherent support, suitable placement/plan; no material conflict.",
|
|
349
|
+
"9: Very strong independent support; well-defined entry/stop/target plan.",
|
|
350
|
+
"10: Exceptional coherent support and plan; no visible material weakness, profit uncertain."
|
|
351
|
+
]
|
|
352
|
+
};
|
|
353
|
+
var JEV_QUESTIONS = {
|
|
354
|
+
...JEV_COMPONENT_QUESTIONS,
|
|
355
|
+
signalStrength: JEV_SIGNAL_STRENGTH_QUESTION
|
|
356
|
+
};
|
|
357
|
+
var questionsForJevInput = (input) => {
|
|
358
|
+
if (input.questionSet !== JEV_QUESTION_SET)
|
|
359
|
+
throw new Error(
|
|
360
|
+
"Unsupported Jev recording question set; rerun the backtest with --jev"
|
|
361
|
+
);
|
|
362
|
+
return Object.fromEntries(
|
|
363
|
+
input.questions.map((key) => {
|
|
364
|
+
const question = JEV_QUESTIONS[key];
|
|
365
|
+
if (!question) throw new Error("Unknown Jev question");
|
|
366
|
+
return [key, question];
|
|
367
|
+
})
|
|
368
|
+
);
|
|
369
|
+
};
|
|
370
|
+
var groupJevFields = (entries) => {
|
|
371
|
+
const groups = /* @__PURE__ */ new Map();
|
|
372
|
+
const flat = [];
|
|
373
|
+
for (const [key, value] of entries) {
|
|
374
|
+
const separator = key.indexOf(".");
|
|
375
|
+
if (separator < 0) {
|
|
376
|
+
flat.push([key, value]);
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
const namespace = key.slice(0, separator);
|
|
380
|
+
const group = groups.get(namespace) ?? [];
|
|
381
|
+
group.push([key.slice(separator + 1), value]);
|
|
382
|
+
groups.set(namespace, group);
|
|
383
|
+
}
|
|
384
|
+
return Object.fromEntries([
|
|
385
|
+
...flat,
|
|
386
|
+
...Array.from(groups, ([key, fields]) => [key, Object.fromEntries(fields)])
|
|
387
|
+
]);
|
|
388
|
+
};
|
|
262
389
|
var stateForJevInput = (input) => ({
|
|
263
|
-
schema: input.schema,
|
|
264
390
|
strategy: input.strategy,
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
missing: input.missing,
|
|
391
|
+
facts: groupJevFields(Object.entries(input.features)),
|
|
392
|
+
missing: groupJevFields(Object.entries(input.missing)),
|
|
268
393
|
geometryStatus: input.geometryStatus,
|
|
269
|
-
units:
|
|
394
|
+
units: groupJevFields(
|
|
270
395
|
Object.entries(input.provenance).filter(([, detail]) => detail.unit != null).map(([key, detail]) => [key, detail.unit])
|
|
271
|
-
),
|
|
272
|
-
sources: Object.fromEntries(
|
|
273
|
-
Object.entries(input.provenance).filter(([, detail]) => detail.source != null).map(([key, detail]) => [key, detail.source])
|
|
274
396
|
)
|
|
275
397
|
});
|
|
276
398
|
|
|
@@ -288,7 +410,7 @@ var validateJevStudy = (rows) => {
|
|
|
288
410
|
);
|
|
289
411
|
for (const row of rows) {
|
|
290
412
|
const record = row?.record;
|
|
291
|
-
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 !== jevHash(record.input) || record.questionsHash !== jevHash(questionsForJevInput(record.input)) || record.id !== jevHash({
|
|
413
|
+
if (row?.schema !== "tradejs-jev-study/v4" || !row.signalId || record?.schema !== "tradejs-jev-record/v4" || record.input?.schema !== "tradejs-jev-input/v4" || record.input.questionSet !== JEV_QUESTION_SET2 || !Number.isFinite(record.input.timestamp) || record.inputHash !== jevHash(record.input) || record.questionsHash !== jevHash(questionsForJevInput(record.input)) || record.id !== jevHash({
|
|
292
414
|
inputHash: record.inputHash,
|
|
293
415
|
questionsHash: record.questionsHash,
|
|
294
416
|
provider: record.provider
|
|
@@ -296,7 +418,7 @@ var validateJevStudy = (rows) => {
|
|
|
296
418
|
throw new Error("Invalid Jev study provenance");
|
|
297
419
|
validateJevResponse(record.response, record.input.questions);
|
|
298
420
|
for (const dimension of JEV_DIMENSIONS2) {
|
|
299
|
-
const expected = record.input.questions.includes(dimension) ? record.response.answers[dimension].score /
|
|
421
|
+
const expected = record.input.questions.includes(dimension) ? record.response.answers[dimension].score / jevScoreMaximum(dimension) : null;
|
|
300
422
|
if (record.scores[dimension] !== expected)
|
|
301
423
|
throw new Error("Jev study scores do not match the teacher response");
|
|
302
424
|
}
|
|
@@ -379,10 +501,12 @@ var trainTree = (rows, dimension, depth, minLeaf) => {
|
|
|
379
501
|
};
|
|
380
502
|
var validateJevGateModel = (value) => {
|
|
381
503
|
const model = value;
|
|
382
|
-
if (model?.schema !== "tradejs-jev-gate/v2" || model.inputSchema !== "tradejs-jev-input/v4" || typeof model.strategy !== "string" || !model.strategy || !model.teacherModel || !model.trees || model.
|
|
504
|
+
if (model?.schema !== "tradejs-jev-gate/v2" || model.inputSchema !== "tradejs-jev-input/v4" || typeof model.strategy !== "string" || !model.strategy || !model.teacherModel || !model.trees || model.questionSet !== JEV_QUESTION_SET2 || JEV_DIMENSIONS2.some((dimension) => !(dimension in model.trees)) || Object.keys(model.trees).some(
|
|
505
|
+
(dimension) => !JEV_DIMENSIONS2.includes(dimension)
|
|
506
|
+
) || model.questionsHash !== jevHash(JEV_QUESTIONS) || !model.training || !/^[a-f0-9]{64}$/.test(model.training.datasetHash))
|
|
383
507
|
throw new Error("Invalid local Jev gate");
|
|
384
508
|
const { trainEnd, validationEnd, testEnd } = model.training;
|
|
385
|
-
if (![trainEnd, validationEnd, testEnd].every(Number.isFinite) || !(trainEnd
|
|
509
|
+
if (![trainEnd, validationEnd, testEnd].every(Number.isFinite) || model.training.split !== "outer-60-40" || !(trainEnd === validationEnd && trainEnd < testEnd))
|
|
386
510
|
throw new Error("Invalid local Jev gate partitions");
|
|
387
511
|
const check = (tree, depth) => {
|
|
388
512
|
if (!tree || depth > 8) throw new Error("Invalid local Jev tree");
|
|
@@ -518,13 +642,10 @@ var trainJevGate = (input, options = {}) => {
|
|
|
518
642
|
];
|
|
519
643
|
if (rows.length < 30 || timestamps.length < 10)
|
|
520
644
|
throw new Error("Need at least 30 distinct samples across 10 signal times");
|
|
521
|
-
const trainEnd = timestamps[Math.floor(timestamps.length * 0.
|
|
522
|
-
const validationEnd =
|
|
645
|
+
const trainEnd = timestamps[timestamps.length - Math.max(1, Math.floor(timestamps.length * 0.4)) - 1];
|
|
646
|
+
const validationEnd = trainEnd;
|
|
523
647
|
const train = rows.filter((row) => row.record.input.timestamp <= trainEnd);
|
|
524
|
-
const
|
|
525
|
-
(row) => row.record.input.timestamp > trainEnd && row.record.input.timestamp <= validationEnd
|
|
526
|
-
);
|
|
527
|
-
const test = rows.filter((row) => row.record.input.timestamp > validationEnd);
|
|
648
|
+
const test = rows.filter((row) => row.record.input.timestamp > trainEnd);
|
|
528
649
|
const fit = (samples) => Object.fromEntries(
|
|
529
650
|
JEV_DIMENSIONS2.map((key) => {
|
|
530
651
|
const eligible = samples.filter(
|
|
@@ -541,10 +662,12 @@ var trainJevGate = (input, options = {}) => {
|
|
|
541
662
|
schema: "tradejs-jev-gate/v2",
|
|
542
663
|
inputSchema: "tradejs-jev-input/v4",
|
|
543
664
|
strategy: first.input.strategy,
|
|
665
|
+
questionSet: JEV_QUESTION_SET2,
|
|
544
666
|
questionsHash: jevHash(JEV_QUESTIONS),
|
|
545
667
|
teacherModel: first.response.model,
|
|
546
668
|
trees: fit(train),
|
|
547
669
|
training: {
|
|
670
|
+
split: "outer-60-40",
|
|
548
671
|
datasetHash: jevHash(
|
|
549
672
|
rows.map((row) => ({
|
|
550
673
|
id: row.record.id,
|
|
@@ -559,18 +682,20 @@ var trainJevGate = (input, options = {}) => {
|
|
|
559
682
|
}
|
|
560
683
|
};
|
|
561
684
|
validateJevGateModel(model);
|
|
562
|
-
const
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
685
|
+
const developmentTimes = timestamps.filter(
|
|
686
|
+
(timestamp) => timestamp <= trainEnd
|
|
687
|
+
);
|
|
688
|
+
const walkForward = [0.25, 0.5, 0.75].map((fraction) => {
|
|
689
|
+
const trainThrough = developmentTimes[Math.max(0, Math.floor(developmentTimes.length * fraction) - 1)];
|
|
690
|
+
const testThrough = developmentTimes[Math.min(
|
|
691
|
+
developmentTimes.length - 1,
|
|
692
|
+
Math.floor(developmentTimes.length * (fraction + 0.25)) - 1
|
|
568
693
|
)];
|
|
569
|
-
const fitting =
|
|
694
|
+
const fitting = train.filter(
|
|
570
695
|
(row) => row.record.input.timestamp <= trainThrough
|
|
571
696
|
);
|
|
572
|
-
const evaluation =
|
|
573
|
-
(row) => row.record.input.timestamp >
|
|
697
|
+
const evaluation = train.filter(
|
|
698
|
+
(row) => row.record.input.timestamp > trainThrough && row.record.input.timestamp <= testThrough
|
|
574
699
|
);
|
|
575
700
|
const fold = {
|
|
576
701
|
...model,
|
|
@@ -578,13 +703,13 @@ var trainJevGate = (input, options = {}) => {
|
|
|
578
703
|
training: {
|
|
579
704
|
...model.training,
|
|
580
705
|
trainEnd: trainThrough,
|
|
581
|
-
validationEnd:
|
|
706
|
+
validationEnd: trainThrough,
|
|
582
707
|
testEnd: testThrough
|
|
583
708
|
}
|
|
584
709
|
};
|
|
585
710
|
return {
|
|
586
711
|
trainThrough,
|
|
587
|
-
validationThrough,
|
|
712
|
+
validationThrough: trainThrough,
|
|
588
713
|
testThrough,
|
|
589
714
|
trainRows: fitting.length,
|
|
590
715
|
test: compareJevGate(fold, evaluation)
|
|
@@ -594,7 +719,8 @@ var trainJevGate = (input, options = {}) => {
|
|
|
594
719
|
model,
|
|
595
720
|
report: {
|
|
596
721
|
train: compareJevGate(model, train),
|
|
597
|
-
|
|
722
|
+
partitionScheme: "outer-60-40",
|
|
723
|
+
stabilityRole: "independently fitted folds inside development only",
|
|
598
724
|
test: compareJevGate(model, test),
|
|
599
725
|
walkForward,
|
|
600
726
|
promotion: "research-only; validate a sequential backtest and prospective signals before runtime admission"
|
|
File without changes
|
|
@@ -3,18 +3,21 @@ import {
|
|
|
3
3
|
buildJevInput,
|
|
4
4
|
questionsForJevInput,
|
|
5
5
|
stateForJevInput
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-GHSLOJLE.mjs";
|
|
7
7
|
|
|
8
8
|
// src/jev.ts
|
|
9
9
|
import fs from "fs/promises";
|
|
10
10
|
import path from "path";
|
|
11
11
|
import {
|
|
12
12
|
JEV_DIMENSIONS,
|
|
13
|
+
JEV_QUESTION_SET,
|
|
13
14
|
JEV_PROVIDERS,
|
|
14
15
|
parseJevConfig,
|
|
15
|
-
predictJevTree
|
|
16
|
+
predictJevTree,
|
|
17
|
+
jevAssessmentFeatures
|
|
16
18
|
} from "@tradejs/core/jev";
|
|
17
19
|
import {
|
|
20
|
+
jevScoreMaximum,
|
|
18
21
|
jevHash,
|
|
19
22
|
jevFileHash,
|
|
20
23
|
readJevArtifact,
|
|
@@ -48,6 +51,7 @@ var evaluateJevInput = async ({
|
|
|
48
51
|
const questions = questionsForJevInput(input);
|
|
49
52
|
const questionsHash = jevHash(questions);
|
|
50
53
|
const base = {
|
|
54
|
+
questionSet: JEV_QUESTION_SET,
|
|
51
55
|
schema: "tradejs-signal-assessment/v5",
|
|
52
56
|
source: config.source,
|
|
53
57
|
mode: config.mode,
|
|
@@ -73,7 +77,7 @@ var evaluateJevInput = async ({
|
|
|
73
77
|
);
|
|
74
78
|
if (jevFileHash(contents) !== config.modelSha256)
|
|
75
79
|
throw new Error("Local Jev gate checksum mismatch");
|
|
76
|
-
const { validateJevGateModel: validateJevGateModel2 } = await import("./jevTraining-
|
|
80
|
+
const { validateJevGateModel: validateJevGateModel2 } = await import("./jevTraining-J4Y4VCM4.mjs");
|
|
77
81
|
const model = validateJevGateModel2(JSON.parse(contents));
|
|
78
82
|
if (model.strategy !== input.strategy || model.questionsHash !== jevHash(JEV_QUESTIONS))
|
|
79
83
|
throw new Error("Local Jev gate strategy or question version mismatch");
|
|
@@ -104,7 +108,7 @@ var evaluateJevInput = async ({
|
|
|
104
108
|
throw new Error("Jev recording identity mismatch");
|
|
105
109
|
validateJevResponse(existing.response, input.questions);
|
|
106
110
|
for (const key of JEV_DIMENSIONS) {
|
|
107
|
-
const expected = input.questions.includes(key) ? existing.response.answers[key].score /
|
|
111
|
+
const expected = input.questions.includes(key) ? existing.response.answers[key].score / jevScoreMaximum(key) : null;
|
|
108
112
|
if (existing.scores[key] !== expected)
|
|
109
113
|
throw new Error("Jev recording scores do not match the response");
|
|
110
114
|
}
|
|
@@ -152,7 +156,7 @@ var evaluateJevInput = async ({
|
|
|
152
156
|
const scores = Object.fromEntries(
|
|
153
157
|
JEV_DIMENSIONS.map((key) => [
|
|
154
158
|
key,
|
|
155
|
-
input.questions.includes(key) ? response.answers[key].score /
|
|
159
|
+
input.questions.includes(key) ? response.answers[key].score / jevScoreMaximum(key) : null
|
|
156
160
|
])
|
|
157
161
|
);
|
|
158
162
|
const value = {
|
|
@@ -219,6 +223,7 @@ var assessSignalWithJev = async ({
|
|
|
219
223
|
} catch (error) {
|
|
220
224
|
if (strict) throw error;
|
|
221
225
|
assessment = {
|
|
226
|
+
questionSet: JEV_QUESTION_SET,
|
|
222
227
|
schema: "tradejs-signal-assessment/v5",
|
|
223
228
|
source: config.source,
|
|
224
229
|
mode: config.mode,
|
|
@@ -235,12 +240,7 @@ var assessSignalWithJev = async ({
|
|
|
235
240
|
signal.assessment = assessment;
|
|
236
241
|
signal.additionalIndicators = {
|
|
237
242
|
...signal.additionalIndicators,
|
|
238
|
-
jev:
|
|
239
|
-
schema: "tradejs-jev-features/v2",
|
|
240
|
-
status: assessment.status,
|
|
241
|
-
scores: assessment.scores,
|
|
242
|
-
confidence: assessment.confidence
|
|
243
|
-
}
|
|
243
|
+
jev: jevAssessmentFeatures(assessment)
|
|
244
244
|
};
|
|
245
245
|
const identity = {
|
|
246
246
|
signalId: signal.signalId,
|
|
File without changes
|
package/dist/connectors.mjs
CHANGED