@tradejs/node 3.1.29-beta.260 → 3.1.29-beta.262

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/jev.js CHANGED
@@ -36,7 +36,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
36
36
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
37
37
 
38
38
  // src/jevInput.ts
39
- var import_data, import_jev, object, at, finite, fact, buildJevInput, criteria, JEV_QUESTIONS, questionsForJevInput, stateForJevInput;
39
+ var import_data, import_jev, object, at, finite, fact, buildJevInput, JEV_COMPONENT_QUESTIONS, JEV_SIGNAL_STRENGTH_QUESTION, JEV_QUESTIONS, questionsForJevInput, groupJevFields, stateForJevInput;
40
40
  var init_jevInput = __esm({
41
41
  "src/jevInput.ts"() {
42
42
  "use strict";
@@ -120,6 +120,35 @@ var init_jevInput = __esm({
120
120
  const stop = finite(signal.prices.stopLossPrice);
121
121
  const target = finite(signal.prices.takeProfitPrice);
122
122
  const atr = baseProblem ? null : finite(at(base, "raw", "volatility", "atr"));
123
+ if (!baseProblem) {
124
+ const open = finite(at(base, "candle", "open"));
125
+ const close = finite(at(base, "candle", "close"));
126
+ const high = finite(at(base, "candle", "high"));
127
+ const low = finite(at(base, "candle", "low"));
128
+ const sign = signal.direction === "LONG" ? 1 : -1;
129
+ const candleFact = {
130
+ knownAt: baseKnownAt,
131
+ scope: "target",
132
+ source: "baseContext.candle"
133
+ };
134
+ if (open != null && close != null && atr != null && atr > 0)
135
+ put("entry.directionalBodyAtr", sign * (close - open) / atr, {
136
+ ...candleFact,
137
+ unit: "ATR"
138
+ });
139
+ if (high != null && low != null && close != null && high > low && close >= low && close <= high) {
140
+ put(
141
+ "entry.directionalCloseLocation",
142
+ signal.direction === "LONG" ? (close - low) / (high - low) : (high - close) / (high - low),
143
+ { ...candleFact, unit: "ratio" }
144
+ );
145
+ if (atr != null && atr > 0)
146
+ put("entry.rangeAtr", (high - low) / atr, {
147
+ ...candleFact,
148
+ unit: "ATR"
149
+ });
150
+ }
151
+ }
123
152
  if (price == null || stop == null || target == null)
124
153
  throw new Error("Invalid Jev signal prices");
125
154
  put(
@@ -206,33 +235,30 @@ var init_jevInput = __esm({
206
235
  );
207
236
  const questions = import_jev.JEV_DIMENSIONS.filter((dimension) => {
208
237
  switch (dimension) {
238
+ case "signalStrength":
239
+ return has("signal.validLevels");
209
240
  case "trend":
210
241
  return has("market.trendBias");
211
242
  case "swing":
212
- return has("market.swingBias");
243
+ return has("setup.retracementRatio");
213
244
  case "participation":
214
- return has("market.volumeRel20") || has("market.deltaPct");
245
+ return has("market.volumeRel20");
215
246
  case "setupParticipation":
216
247
  return setupKeys.some(
217
248
  (key) => /volume|delta|flow|liquidity|turnover/i.test(key)
218
249
  );
219
250
  case "extension":
220
- return has("market.fastMaDistanceAtr") || has("setup.breakoutDistanceAtr") || has("signal.stopDistanceAtr");
251
+ return has("setup.breakoutDistanceAtr");
221
252
  case "confirmation":
222
- return setupKeys.some(
223
- (key) => /stage|confirm|retest|accept/i.test(key)
224
- );
253
+ return has("entry.directionalBodyAtr") && has("entry.directionalCloseLocation");
225
254
  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
- );
255
+ return setupKeys.some((key) => /efficiencyRatio$/i.test(key));
231
256
  case "geometry":
232
257
  return geometryStatus !== "invalid" && Object.keys(features).some((key) => key.startsWith("geometry."));
233
258
  }
234
259
  });
235
260
  return {
261
+ questionSet: import_jev.JEV_QUESTION_SET,
236
262
  schema: "tradejs-jev-input/v4",
237
263
  strategy: signal.strategy,
238
264
  symbol: signal.symbol,
@@ -246,68 +272,155 @@ var init_jevInput = __esm({
246
272
  geometryStatus
247
273
  };
248
274
  };
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 = {
275
+ JEV_COMPONENT_QUESTIONS = {
257
276
  trend: {
258
277
  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")
278
+ 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.",
279
+ criteria: [
280
+ "Both oppose.",
281
+ "One opposes; other inconclusive.",
282
+ "Biases conflict, or both inconclusive.",
283
+ "One supports; other inconclusive.",
284
+ "Both support."
285
+ ]
261
286
  },
262
287
  swing: {
263
288
  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")
289
+ instructions: "Rate shallow correction using only setup.retracementRatio (correction / preceding impulse). Missing is inconclusive, not favorable.",
290
+ criteria: [
291
+ "Exceeds impulse (>1).",
292
+ "Deep (>0.65 to 1).",
293
+ "Moderate (>0.4 to 0.65), or missing.",
294
+ "Contained (>0.2 to 0.4).",
295
+ "Shallow (0 to 0.2)."
296
+ ]
266
297
  },
267
298
  participation: {
268
299
  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")
300
+ instructions: "Rate activity using only market.volumeRel20 (candle volume / trailing 20-candle mean). Not directional flow; ignore setup volume.",
301
+ criteria: [
302
+ "Very low (<0.5).",
303
+ "Below ordinary (0.5 to <0.8).",
304
+ "Ordinary (0.8 to <1.2), or missing.",
305
+ "Elevated (1.2 to <2).",
306
+ "Strong expansion (>=2)."
307
+ ]
271
308
  },
272
309
  setupParticipation: {
273
310
  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")
311
+ 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.",
312
+ criteria: [
313
+ "Exceeds impulse (>1.2).",
314
+ "No contraction (>0.9 to 1.2).",
315
+ "Mild (>0.7 to 0.9), or inconclusive.",
316
+ "Clear (>0.4 to 0.7).",
317
+ "Strong (0 to 0.4)."
318
+ ]
276
319
  },
277
320
  extension: {
278
321
  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")
322
+ instructions: "Rate absence of entry extension using only setup.breakoutDistanceAtr (distance past setup boundary in ATR). Stop/target distances are not extension.",
323
+ criteria: [
324
+ "Extreme (>2 ATR).",
325
+ "Large (>1 to 2 ATR).",
326
+ "Moderate (>0.5 to 1 ATR), or missing.",
327
+ "Small (>0.2 to 0.5 ATR).",
328
+ "Near boundary (0 to 0.2 ATR)."
329
+ ]
281
330
  },
282
331
  confirmation: {
283
332
  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")
333
+ 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.",
334
+ criteria: [
335
+ "Opposing body AND unfavorable-quarter close.",
336
+ "Opposing body OR unfavorable-half close.",
337
+ "Small body (<0.2 ATR), mixed or missing evidence.",
338
+ "Supporting body (>=0.2 ATR) AND favorable-half close.",
339
+ "Strong supporting body (>=0.5 ATR) AND favorable-quarter close."
340
+ ]
286
341
  },
287
342
  setupStrength: {
288
343
  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")
344
+ 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.",
345
+ criteria: [
346
+ "Highly choppy (<0.2).",
347
+ "Weak (0.2 to <0.4).",
348
+ "Mixed (0.4 to <0.6), or missing.",
349
+ "Clear (0.6 to <0.8).",
350
+ "Highly persistent (0.8 to 1)."
351
+ ]
291
352
  },
292
353
  geometry: {
293
354
  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")
355
+ 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.",
356
+ criteria: [
357
+ "Fit <0.5 OR divergence >0.5.",
358
+ "Fit <0.7 OR divergence >0.3.",
359
+ "Fit <0.85 OR divergence >0.15, or missing.",
360
+ "Both fits >=0.85 AND divergence <=0.15.",
361
+ "Both fits >=0.95 AND divergence <=0.05."
362
+ ]
363
+ }
364
+ };
365
+ JEV_SIGNAL_STRENGTH_QUESTION = {
366
+ type: "score",
367
+ 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.",
368
+ criteria: [
369
+ "1: Invalid levels or decisive contrary evidence.",
370
+ "2: Severe directional/setup conflicts; poor support.",
371
+ "3: Substantial weaknesses outweigh support.",
372
+ "4: Some support; material weakness makes entry unattractive.",
373
+ "5: Balanced conflicts or essential evidence missing.",
374
+ "6: Modest coherent support; reasonable but limited evidence.",
375
+ "7: Convincing support from several facts; remaining weakness.",
376
+ "8: Strong coherent support, suitable placement/plan; no material conflict.",
377
+ "9: Very strong independent support; well-defined entry/stop/target plan.",
378
+ "10: Exceptional coherent support and plan; no visible material weakness, profit uncertain."
379
+ ]
380
+ };
381
+ JEV_QUESTIONS = {
382
+ ...JEV_COMPONENT_QUESTIONS,
383
+ signalStrength: JEV_SIGNAL_STRENGTH_QUESTION
384
+ };
385
+ questionsForJevInput = (input) => {
386
+ if (input.questionSet !== import_jev.JEV_QUESTION_SET)
387
+ throw new Error(
388
+ "Unsupported Jev recording question set; rerun the backtest with --jev"
389
+ );
390
+ return Object.fromEntries(
391
+ input.questions.map((key) => {
392
+ const question = JEV_QUESTIONS[key];
393
+ if (!question) throw new Error("Unknown Jev question");
394
+ return [key, question];
395
+ })
396
+ );
397
+ };
398
+ groupJevFields = (entries) => {
399
+ const groups = /* @__PURE__ */ new Map();
400
+ const flat = [];
401
+ for (const [key, value] of entries) {
402
+ const separator = key.indexOf(".");
403
+ if (separator < 0) {
404
+ flat.push([key, value]);
405
+ continue;
406
+ }
407
+ const namespace = key.slice(0, separator);
408
+ const group = groups.get(namespace) ?? [];
409
+ group.push([key.slice(separator + 1), value]);
410
+ groups.set(namespace, group);
296
411
  }
412
+ return Object.fromEntries([
413
+ ...flat,
414
+ ...Array.from(groups, ([key, fields]) => [key, Object.fromEntries(fields)])
415
+ ]);
297
416
  };
298
- questionsForJevInput = (input) => Object.fromEntries(input.questions.map((key) => [key, JEV_QUESTIONS[key]]));
299
417
  stateForJevInput = (input) => ({
300
- schema: input.schema,
301
418
  strategy: input.strategy,
302
- direction: input.direction,
303
- facts: input.features,
304
- missing: input.missing,
419
+ facts: groupJevFields(Object.entries(input.features)),
420
+ missing: groupJevFields(Object.entries(input.missing)),
305
421
  geometryStatus: input.geometryStatus,
306
- units: Object.fromEntries(
422
+ units: groupJevFields(
307
423
  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
424
  )
312
425
  });
313
426
  }
@@ -341,7 +454,7 @@ var init_jevTraining = __esm({
341
454
  );
342
455
  for (const row of rows) {
343
456
  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)({
457
+ 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 !== import_jev2.JEV_QUESTION_SET || !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
458
  inputHash: record.inputHash,
346
459
  questionsHash: record.questionsHash,
347
460
  provider: record.provider
@@ -349,7 +462,7 @@ var init_jevTraining = __esm({
349
462
  throw new Error("Invalid Jev study provenance");
350
463
  (0, import_jev3.validateJevResponse)(record.response, record.input.questions);
351
464
  for (const dimension of import_jev2.JEV_DIMENSIONS) {
352
- const expected = record.input.questions.includes(dimension) ? record.response.answers[dimension].score / 4 : null;
465
+ const expected = record.input.questions.includes(dimension) ? record.response.answers[dimension].score / (0, import_jev3.jevScoreMaximum)(dimension) : null;
353
466
  if (record.scores[dimension] !== expected)
354
467
  throw new Error("Jev study scores do not match the teacher response");
355
468
  }
@@ -432,10 +545,12 @@ var init_jevTraining = __esm({
432
545
  };
433
546
  validateJevGateModel = (value) => {
434
547
  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))
548
+ 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 !== import_jev2.JEV_QUESTION_SET || import_jev2.JEV_DIMENSIONS.some((dimension) => !(dimension in model.trees)) || Object.keys(model.trees).some(
549
+ (dimension) => !import_jev2.JEV_DIMENSIONS.includes(dimension)
550
+ ) || model.questionsHash !== (0, import_jev3.jevHash)(JEV_QUESTIONS) || !model.training || !/^[a-f0-9]{64}$/.test(model.training.datasetHash))
436
551
  throw new Error("Invalid local Jev gate");
437
552
  const { trainEnd, validationEnd, testEnd } = model.training;
438
- if (![trainEnd, validationEnd, testEnd].every(Number.isFinite) || !(trainEnd < validationEnd && validationEnd < testEnd))
553
+ if (![trainEnd, validationEnd, testEnd].every(Number.isFinite) || model.training.split !== "outer-60-40" || !(trainEnd === validationEnd && trainEnd < testEnd))
439
554
  throw new Error("Invalid local Jev gate partitions");
440
555
  const check = (tree, depth) => {
441
556
  if (!tree || depth > 8) throw new Error("Invalid local Jev tree");
@@ -571,13 +686,10 @@ var init_jevTraining = __esm({
571
686
  ];
572
687
  if (rows.length < 30 || timestamps.length < 10)
573
688
  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];
689
+ const trainEnd = timestamps[timestamps.length - Math.max(1, Math.floor(timestamps.length * 0.4)) - 1];
690
+ const validationEnd = trainEnd;
576
691
  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);
692
+ const test = rows.filter((row) => row.record.input.timestamp > trainEnd);
581
693
  const fit = (samples) => Object.fromEntries(
582
694
  import_jev2.JEV_DIMENSIONS.map((key) => {
583
695
  const eligible = samples.filter(
@@ -594,10 +706,12 @@ var init_jevTraining = __esm({
594
706
  schema: "tradejs-jev-gate/v2",
595
707
  inputSchema: "tradejs-jev-input/v4",
596
708
  strategy: first.input.strategy,
709
+ questionSet: import_jev2.JEV_QUESTION_SET,
597
710
  questionsHash: (0, import_jev3.jevHash)(JEV_QUESTIONS),
598
711
  teacherModel: first.response.model,
599
712
  trees: fit(train),
600
713
  training: {
714
+ split: "outer-60-40",
601
715
  datasetHash: (0, import_jev3.jevHash)(
602
716
  rows.map((row) => ({
603
717
  id: row.record.id,
@@ -612,18 +726,20 @@ var init_jevTraining = __esm({
612
726
  }
613
727
  };
614
728
  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
729
+ const developmentTimes = timestamps.filter(
730
+ (timestamp) => timestamp <= trainEnd
731
+ );
732
+ const walkForward = [0.25, 0.5, 0.75].map((fraction) => {
733
+ const trainThrough = developmentTimes[Math.max(0, Math.floor(developmentTimes.length * fraction) - 1)];
734
+ const testThrough = developmentTimes[Math.min(
735
+ developmentTimes.length - 1,
736
+ Math.floor(developmentTimes.length * (fraction + 0.25)) - 1
621
737
  )];
622
- const fitting = rows.filter(
738
+ const fitting = train.filter(
623
739
  (row) => row.record.input.timestamp <= trainThrough
624
740
  );
625
- const evaluation = rows.filter(
626
- (row) => row.record.input.timestamp > validationThrough && row.record.input.timestamp <= testThrough
741
+ const evaluation = train.filter(
742
+ (row) => row.record.input.timestamp > trainThrough && row.record.input.timestamp <= testThrough
627
743
  );
628
744
  const fold = {
629
745
  ...model,
@@ -631,13 +747,13 @@ var init_jevTraining = __esm({
631
747
  training: {
632
748
  ...model.training,
633
749
  trainEnd: trainThrough,
634
- validationEnd: validationThrough,
750
+ validationEnd: trainThrough,
635
751
  testEnd: testThrough
636
752
  }
637
753
  };
638
754
  return {
639
755
  trainThrough,
640
- validationThrough,
756
+ validationThrough: trainThrough,
641
757
  testThrough,
642
758
  trainRows: fitting.length,
643
759
  test: compareJevGate(fold, evaluation)
@@ -647,7 +763,8 @@ var init_jevTraining = __esm({
647
763
  model,
648
764
  report: {
649
765
  train: compareJevGate(model, train),
650
- validation: compareJevGate(model, validation),
766
+ partitionScheme: "outer-60-40",
767
+ stabilityRole: "independently fitted folds inside development only",
651
768
  test: compareJevGate(model, test),
652
769
  walkForward,
653
770
  promotion: "research-only; validate a sequential backtest and prospective signals before runtime admission"
@@ -665,7 +782,9 @@ __export(jev_exports, {
665
782
  buildJevInput: () => buildJevInput,
666
783
  compareJevGate: () => compareJevGate,
667
784
  evaluateJevInput: () => evaluateJevInput,
785
+ questionsForJevInput: () => questionsForJevInput,
668
786
  resolveJevProvider: () => resolveJevProvider,
787
+ stateForJevInput: () => stateForJevInput,
669
788
  trainJevGate: () => trainJevGate,
670
789
  validateJevGateModel: () => validateJevGateModel
671
790
  });
@@ -703,6 +822,7 @@ var evaluateJevInput = async ({
703
822
  const questions = questionsForJevInput(input);
704
823
  const questionsHash = (0, import_jev5.jevHash)(questions);
705
824
  const base = {
825
+ questionSet: import_jev4.JEV_QUESTION_SET,
706
826
  schema: "tradejs-signal-assessment/v5",
707
827
  source: config.source,
708
828
  mode: config.mode,
@@ -759,7 +879,7 @@ var evaluateJevInput = async ({
759
879
  throw new Error("Jev recording identity mismatch");
760
880
  (0, import_jev5.validateJevResponse)(existing.response, input.questions);
761
881
  for (const key of import_jev4.JEV_DIMENSIONS) {
762
- const expected = input.questions.includes(key) ? existing.response.answers[key].score / 4 : null;
882
+ const expected = input.questions.includes(key) ? existing.response.answers[key].score / (0, import_jev5.jevScoreMaximum)(key) : null;
763
883
  if (existing.scores[key] !== expected)
764
884
  throw new Error("Jev recording scores do not match the response");
765
885
  }
@@ -807,7 +927,7 @@ var evaluateJevInput = async ({
807
927
  const scores = Object.fromEntries(
808
928
  import_jev4.JEV_DIMENSIONS.map((key) => [
809
929
  key,
810
- input.questions.includes(key) ? response.answers[key].score / 4 : null
930
+ input.questions.includes(key) ? response.answers[key].score / (0, import_jev5.jevScoreMaximum)(key) : null
811
931
  ])
812
932
  );
813
933
  const value = {
@@ -874,6 +994,7 @@ var assessSignalWithJev = async ({
874
994
  } catch (error) {
875
995
  if (strict) throw error;
876
996
  assessment = {
997
+ questionSet: import_jev4.JEV_QUESTION_SET,
877
998
  schema: "tradejs-signal-assessment/v5",
878
999
  source: config.source,
879
1000
  mode: config.mode,
@@ -890,12 +1011,7 @@ var assessSignalWithJev = async ({
890
1011
  signal.assessment = assessment;
891
1012
  signal.additionalIndicators = {
892
1013
  ...signal.additionalIndicators,
893
- jev: {
894
- schema: "tradejs-jev-features/v2",
895
- status: assessment.status,
896
- scores: assessment.scores,
897
- confidence: assessment.confidence
898
- }
1014
+ jev: (0, import_jev4.jevAssessmentFeatures)(assessment)
899
1015
  };
900
1016
  const identity = {
901
1017
  signalId: signal.signalId,
@@ -925,7 +1041,9 @@ var assessSignalWithJev = async ({
925
1041
  buildJevInput,
926
1042
  compareJevGate,
927
1043
  evaluateJevInput,
1044
+ questionsForJevInput,
928
1045
  resolveJevProvider,
1046
+ stateForJevInput,
929
1047
  trainJevGate,
930
1048
  validateJevGateModel
931
1049
  });
package/dist/jev.mjs CHANGED
@@ -2,14 +2,16 @@ import {
2
2
  assessSignalWithJev,
3
3
  evaluateJevInput,
4
4
  resolveJevProvider
5
- } from "./chunk-3BBXGZ7M.mjs";
5
+ } from "./chunk-YR55Q355.mjs";
6
6
  import {
7
7
  JEV_QUESTIONS,
8
8
  buildJevInput,
9
9
  compareJevGate,
10
+ questionsForJevInput,
11
+ stateForJevInput,
10
12
  trainJevGate,
11
13
  validateJevGateModel
12
- } from "./chunk-ZZQLTUTP.mjs";
14
+ } from "./chunk-GHSLOJLE.mjs";
13
15
  import "./chunk-Y6FXYEAI.mjs";
14
16
  export {
15
17
  JEV_QUESTIONS,
@@ -17,7 +19,9 @@ export {
17
19
  buildJevInput,
18
20
  compareJevGate,
19
21
  evaluateJevInput,
22
+ questionsForJevInput,
20
23
  resolveJevProvider,
24
+ stateForJevInput,
21
25
  trainJevGate,
22
26
  validateJevGateModel
23
27
  };
@@ -3,7 +3,7 @@ import {
3
3
  trainJevGate,
4
4
  validateJevGateModel,
5
5
  validateJevStudy
6
- } from "./chunk-ZZQLTUTP.mjs";
6
+ } from "./chunk-GHSLOJLE.mjs";
7
7
  import "./chunk-Y6FXYEAI.mjs";
8
8
  export {
9
9
  compareJevGate,