@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/backtest.mjs CHANGED
@@ -5,14 +5,16 @@ import {
5
5
  import {
6
6
  buildMlPayload,
7
7
  enrichSignalWithMarketContextStages
8
- } from "./chunk-JDFPF2RG.mjs";
8
+ } from "./chunk-WGL7XJ3W.mjs";
9
9
  import {
10
10
  buildAiPayload,
11
11
  getStrategyCreator
12
- } from "./chunk-UITD6LG6.mjs";
12
+ } from "./chunk-UL5Z46JL.mjs";
13
13
  import {
14
14
  getTradejsProjectCwd
15
15
  } from "./chunk-LZDXRXIU.mjs";
16
+ import "./chunk-3BBXGZ7M.mjs";
17
+ import "./chunk-ZZQLTUTP.mjs";
16
18
  import "./chunk-Y6FXYEAI.mjs";
17
19
 
18
20
  // src/backtest.ts
@@ -1364,6 +1366,7 @@ var buildReplayEvaluation = ({
1364
1366
  orderStatus: signal.orderStatus,
1365
1367
  orderSkipReason: signal.orderSkipReason,
1366
1368
  aiAnalysis: signal.aiAnalysis ?? null,
1369
+ assessment: signal.assessment,
1367
1370
  ml: signal.ml
1368
1371
  };
1369
1372
  };
@@ -1502,7 +1505,7 @@ var createBacktestSession = async ({
1502
1505
  })
1503
1506
  );
1504
1507
  }
1505
- if (test.ai && signal && typeof signal !== "string" && signal.signalId) {
1508
+ if (test.ai && signal && typeof signal !== "string" && signal.signalId && !(signal.assessment && signal.orderStatus === "skipped")) {
1506
1509
  pendingAiRowBySignalId.set(signal.signalId, {
1507
1510
  signalId: signal.signalId,
1508
1511
  strategyName: signal.strategy || test.strategyName,
@@ -1510,6 +1513,7 @@ var createBacktestSession = async ({
1510
1513
  direction: signal.direction,
1511
1514
  timestamp: signal.timestamp,
1512
1515
  signal: cloneSignal(signal),
1516
+ assessment: signal.assessment,
1513
1517
  testId: test.testId,
1514
1518
  testSuiteId: test.testSuiteId,
1515
1519
  testName: test.name,
@@ -0,0 +1,271 @@
1
+ import {
2
+ JEV_QUESTIONS,
3
+ buildJevInput,
4
+ questionsForJevInput,
5
+ stateForJevInput
6
+ } from "./chunk-ZZQLTUTP.mjs";
7
+
8
+ // src/jev.ts
9
+ import fs from "fs/promises";
10
+ import path from "path";
11
+ import {
12
+ JEV_DIMENSIONS,
13
+ JEV_PROVIDERS,
14
+ parseJevConfig,
15
+ predictJevTree
16
+ } from "@tradejs/core/jev";
17
+ import {
18
+ jevHash,
19
+ jevFileHash,
20
+ readJevArtifact,
21
+ writeJevArtifact,
22
+ requestJev,
23
+ validateJevResponse
24
+ } from "@tradejs/infra/jev";
25
+ import { getUserSettings } from "@tradejs/infra/userSettings";
26
+ var resolveJevProvider = async (userName) => {
27
+ const settings = await getUserSettings(userName);
28
+ const provider = {
29
+ endpoint: settings.JEV_API_ENDPOINT || JEV_PROVIDERS[0].endpoint,
30
+ model: settings.JEV_MODEL || JEV_PROVIDERS[0].model
31
+ };
32
+ parseJevConfig({ source: "provider", mode: "observe", provider });
33
+ if (!settings.JEV_API_KEY)
34
+ throw new Error("Configure the Jev API key in Account settings");
35
+ return provider;
36
+ };
37
+ var emptyScores = () => Object.fromEntries(JEV_DIMENSIONS.map((key) => [key, null]));
38
+ var pending = /* @__PURE__ */ new Map();
39
+ var pendingResponses = /* @__PURE__ */ new Map();
40
+ var evaluateJevInput = async ({
41
+ input,
42
+ config,
43
+ userName,
44
+ projectRoot
45
+ }) => {
46
+ config = parseJevConfig(config);
47
+ const inputHash = jevHash(input);
48
+ const questions = questionsForJevInput(input);
49
+ const questionsHash = jevHash(questions);
50
+ const base = {
51
+ schema: "tradejs-signal-assessment/v5",
52
+ source: config.source,
53
+ mode: config.mode,
54
+ inputHash,
55
+ questionsHash,
56
+ geometryStatus: input.geometryStatus,
57
+ levelsValid: typeof input.features["signal.validLevels"] === "boolean" ? input.features["signal.validLevels"] : null
58
+ };
59
+ if (!input.questions.length)
60
+ return {
61
+ assessment: {
62
+ ...base,
63
+ status: "unavailable",
64
+ model: "",
65
+ scores: emptyScores(),
66
+ confidence: emptyScores()
67
+ }
68
+ };
69
+ if (config.source === "local") {
70
+ const contents = await fs.readFile(
71
+ path.resolve(projectRoot, config.modelFile),
72
+ "utf8"
73
+ );
74
+ if (jevFileHash(contents) !== config.modelSha256)
75
+ throw new Error("Local Jev gate checksum mismatch");
76
+ const { validateJevGateModel: validateJevGateModel2 } = await import("./jevTraining-JYXUVAZG.mjs");
77
+ const model = validateJevGateModel2(JSON.parse(contents));
78
+ if (model.strategy !== input.strategy || model.questionsHash !== jevHash(JEV_QUESTIONS))
79
+ throw new Error("Local Jev gate strategy or question version mismatch");
80
+ const scores = Object.fromEntries(
81
+ JEV_DIMENSIONS.map((key) => [
82
+ key,
83
+ input.questions.includes(key) && model.trees[key] ? predictJevTree(model.trees[key], input.features) : null
84
+ ])
85
+ );
86
+ return {
87
+ assessment: {
88
+ ...base,
89
+ status: "available",
90
+ model: config.modelSha256,
91
+ scores,
92
+ confidence: emptyScores()
93
+ }
94
+ };
95
+ }
96
+ const provider = config.provider ?? await resolveJevProvider(userName);
97
+ const id = jevHash({ inputHash, questionsHash, provider });
98
+ const dir = path.resolve(projectRoot, config.recordsDir ?? "data/ai/jev");
99
+ const file = path.join(dir, "records", `${id}.json`);
100
+ const getRecord = async () => {
101
+ const existing = await readJevArtifact(file);
102
+ if (existing) {
103
+ if (existing.schema !== "tradejs-jev-record/v4" || existing.id !== id || existing.inputHash !== inputHash || existing.questionsHash !== questionsHash || jevHash(existing.input) !== inputHash || jevHash(existing.provider) !== jevHash(provider))
104
+ throw new Error("Jev recording identity mismatch");
105
+ validateJevResponse(existing.response, input.questions);
106
+ for (const key of JEV_DIMENSIONS) {
107
+ const expected = input.questions.includes(key) ? existing.response.answers[key].score / 4 : null;
108
+ if (existing.scores[key] !== expected)
109
+ throw new Error("Jev recording scores do not match the response");
110
+ }
111
+ return existing;
112
+ }
113
+ if (config.source === "recorded")
114
+ throw new Error(
115
+ `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.`
116
+ );
117
+ const settings = await getUserSettings(userName);
118
+ if (!settings.JEV_API_KEY)
119
+ throw new Error("Configure the Jev API key in Account settings");
120
+ const apiKey = settings.JEV_API_KEY;
121
+ const configuredProvider = await resolveJevProvider(userName);
122
+ if (configuredProvider.endpoint !== provider.endpoint)
123
+ throw new Error(
124
+ "Jev account endpoint changed; update the frozen provider configuration"
125
+ );
126
+ const started = Date.now();
127
+ const state = stateForJevInput(input);
128
+ const responseId = jevHash({
129
+ state,
130
+ questionsHash,
131
+ provider,
132
+ user: jevHash(userName)
133
+ });
134
+ const responseFile = path.join(dir, "responses", `${responseId}.json`);
135
+ let responsePromise = pendingResponses.get(responseFile);
136
+ if (!responsePromise) {
137
+ responsePromise = (async () => {
138
+ const saved = await readJevArtifact(responseFile);
139
+ if (saved) return validateJevResponse(saved, input.questions);
140
+ const fresh = await requestJev(provider, apiKey, state, questions);
141
+ await writeJevArtifact(responseFile, fresh);
142
+ return fresh;
143
+ })();
144
+ pendingResponses.set(responseFile, responsePromise);
145
+ }
146
+ let response;
147
+ try {
148
+ response = await responsePromise;
149
+ } finally {
150
+ pendingResponses.delete(responseFile);
151
+ }
152
+ const scores = Object.fromEntries(
153
+ JEV_DIMENSIONS.map((key) => [
154
+ key,
155
+ input.questions.includes(key) ? response.answers[key].score / 4 : null
156
+ ])
157
+ );
158
+ const value = {
159
+ schema: "tradejs-jev-record/v4",
160
+ id,
161
+ inputHash,
162
+ questionsHash,
163
+ provider,
164
+ input,
165
+ response,
166
+ scores,
167
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
168
+ elapsedMs: Date.now() - started
169
+ };
170
+ await writeJevArtifact(file, value);
171
+ return await readJevArtifact(file);
172
+ };
173
+ const pendingKey = `${file}:${jevHash(userName)}`;
174
+ let promise = pending.get(pendingKey);
175
+ if (!promise) {
176
+ promise = getRecord();
177
+ pending.set(pendingKey, promise);
178
+ }
179
+ let record;
180
+ try {
181
+ record = await promise;
182
+ } finally {
183
+ pending.delete(pendingKey);
184
+ }
185
+ return {
186
+ record,
187
+ assessment: {
188
+ ...base,
189
+ status: "available",
190
+ recordId: id,
191
+ model: record.response.model,
192
+ scores: record.scores,
193
+ confidence: Object.fromEntries(
194
+ JEV_DIMENSIONS.map((key) => [
195
+ key,
196
+ record.response.answers[key]?.confidence ?? null
197
+ ])
198
+ )
199
+ }
200
+ };
201
+ };
202
+ var assessSignalWithJev = async ({
203
+ signal,
204
+ config,
205
+ userName,
206
+ projectRoot,
207
+ strict,
208
+ scope = "runtime"
209
+ }) => {
210
+ let assessment;
211
+ try {
212
+ const input = buildJevInput(signal);
213
+ ({ assessment } = await evaluateJevInput({
214
+ input,
215
+ config,
216
+ userName,
217
+ projectRoot
218
+ }));
219
+ } catch (error) {
220
+ if (strict) throw error;
221
+ assessment = {
222
+ schema: "tradejs-signal-assessment/v5",
223
+ source: config.source,
224
+ mode: config.mode,
225
+ status: "unavailable",
226
+ inputHash: "",
227
+ questionsHash: "",
228
+ geometryStatus: "absent",
229
+ levelsValid: null,
230
+ model: config.provider?.model ?? config.modelSha256 ?? "",
231
+ scores: emptyScores(),
232
+ confidence: emptyScores()
233
+ };
234
+ }
235
+ signal.assessment = assessment;
236
+ signal.additionalIndicators = {
237
+ ...signal.additionalIndicators,
238
+ jev: {
239
+ schema: "tradejs-jev-features/v2",
240
+ status: assessment.status,
241
+ scores: assessment.scores,
242
+ confidence: assessment.confidence
243
+ }
244
+ };
245
+ const identity = {
246
+ signalId: signal.signalId,
247
+ strategy: signal.strategy,
248
+ symbol: signal.symbol,
249
+ timestamp: signal.timestamp,
250
+ scope,
251
+ user: jevHash(userName),
252
+ config,
253
+ assessment
254
+ };
255
+ await writeJevArtifact(
256
+ path.resolve(
257
+ projectRoot,
258
+ config.recordsDir ?? "data/ai/jev",
259
+ "decisions",
260
+ `${jevHash(identity)}.json`
261
+ ),
262
+ identity
263
+ );
264
+ return assessment;
265
+ };
266
+
267
+ export {
268
+ resolveJevProvider,
269
+ evaluateJevInput,
270
+ assessSignalWithJev
271
+ };
@@ -2,12 +2,13 @@ import {
2
2
  getStrategyCreator,
3
3
  getStrategyEntry,
4
4
  getStrategyPluginSource
5
- } from "./chunk-UITD6LG6.mjs";
5
+ } from "./chunk-UL5Z46JL.mjs";
6
6
  import {
7
7
  loadTradejsConfig
8
8
  } from "./chunk-LZDXRXIU.mjs";
9
9
 
10
10
  // src/runtimeStrategies.ts
11
+ import { parseJevConfig } from "@tradejs/core/jev";
11
12
  import { createHash } from "crypto";
12
13
  import { readFile } from "fs/promises";
13
14
  import path from "path";
@@ -347,7 +348,15 @@ var resolveStrategyComposition = async ({
347
348
  `Strategy config parser returned a non-object: ${strategyName}`
348
349
  );
349
350
  }
350
- const strategyConfig = parsedConfig;
351
+ const jev = parseJevConfig(declaration.config.JEV);
352
+ if (jev && jev.source === "provider" && !jev.provider)
353
+ throw new Error(
354
+ "Runtime JEV requires a frozen provider endpoint and model"
355
+ );
356
+ const strategyConfig = {
357
+ ...parsedConfig,
358
+ ...jev ? { JEV: jev } : {}
359
+ };
351
360
  const selection = resolveStrategySelection({
352
361
  deployment,
353
362
  strategy: declaration
@@ -1034,8 +1034,11 @@ var toRecord2 = (value) => {
1034
1034
  return value;
1035
1035
  };
1036
1036
  var buildBaseAiPayload = (signal) => {
1037
+ const { jevEvidence: _jevEvidence, ...signalAdditionalIndicators } = toRecord2(
1038
+ signal.additionalIndicators
1039
+ );
1037
1040
  const additionalIndicators = {
1038
- ...toRecord2(signal.additionalIndicators),
1041
+ ...signalAdditionalIndicators,
1039
1042
  marketContext: buildAiMarketContext(signal)
1040
1043
  };
1041
1044
  return {
@@ -5,11 +5,14 @@ import {
5
5
  resolveStrategyPolicyProfile,
6
6
  runAiPromptLocal,
7
7
  setStrategyRuntimeFactory
8
- } from "./chunk-UITD6LG6.mjs";
8
+ } from "./chunk-UL5Z46JL.mjs";
9
9
  import {
10
10
  getTradejsProjectCwd,
11
11
  loadTradejsConfig
12
12
  } from "./chunk-LZDXRXIU.mjs";
13
+ import {
14
+ assessSignalWithJev
15
+ } from "./chunk-3BBXGZ7M.mjs";
13
16
 
14
17
  // src/binanceBreadthUniverses.ts
15
18
  import { createHash } from "crypto";
@@ -3104,7 +3107,8 @@ var executeEntryOrder = async ({
3104
3107
  runtimeConfigId: signal.runtimeConfigId,
3105
3108
  strategyRevision: signal.strategyRevision,
3106
3109
  runtimeLineage: signal.runtimeLineage,
3107
- ...signal.aiAnalysis ? { aiAnalysis: signal.aiAnalysis } : {}
3110
+ ...signal.aiAnalysis ? { aiAnalysis: signal.aiAnalysis } : {},
3111
+ ...signal.assessment ? { assessment: signal.assessment } : {}
3108
3112
  });
3109
3113
  }
3110
3114
  if (orderPlaced && recordRuntimeTrade && isPositionIncrease) {
@@ -3176,6 +3180,7 @@ var resolveStrategyConfig = async ({
3176
3180
  };
3177
3181
 
3178
3182
  // src/strategyRuntime.ts
3183
+ import { parseJevConfig } from "@tradejs/core/jev";
3179
3184
  import {
3180
3185
  BACKTEST_EXECUTION_DELAY_MS,
3181
3186
  BACKTEST_LOWER_TIMEFRAME_EXECUTION_ENABLED
@@ -3847,6 +3852,7 @@ var handleExitDecision = async ({
3847
3852
  onRuntimeClose?.({
3848
3853
  userName,
3849
3854
  strategy: strategyName,
3855
+ deploymentId: connector.deploymentId,
3850
3856
  openedByStrategy: trade.strategy,
3851
3857
  symbol,
3852
3858
  direction: trade.direction,
@@ -4139,6 +4145,11 @@ var createStrategyRuntime = ({
4139
4145
  const projectConfig = await loadTradejsConfig(projectRoot);
4140
4146
  const projectHooks = projectConfig.hooks;
4141
4147
  const env = String(config.ENV ?? "BACKTEST");
4148
+ const jev = parseJevConfig(config.JEV);
4149
+ if (jev && env !== "BACKTEST" && jev.source === "provider" && !jev.provider)
4150
+ throw new Error(
4151
+ "Runtime JEV requires a frozen provider endpoint and model"
4152
+ );
4142
4153
  const backtestPriceMode = config.BACKTEST_PRICE_MODE ?? "open";
4143
4154
  const backtestEntryDelayBars = env === "BACKTEST" ? resolveBacktestEntryDelayBars(config.BACKTEST_ENTRY_DELAY_BARS) : 0;
4144
4155
  const resolvedBacktestExecutionInterval = config.BACKTEST_EXECUTION_INTERVAL ?? backtestExecutionMarketData?.interval ?? resolveBacktestExecutionIntervalForPrimary(config.INTERVAL ?? "15");
@@ -4846,6 +4857,16 @@ var createStrategyRuntime = ({
4846
4857
  env,
4847
4858
  includeHyperliquidWhales: false
4848
4859
  });
4860
+ if (jev) {
4861
+ await assessSignalWithJev({
4862
+ signal,
4863
+ config: jev,
4864
+ userName,
4865
+ projectRoot,
4866
+ strict: env === "BACKTEST",
4867
+ scope: runtimeLineage?.deploymentCompositionId ?? (env === "BACKTEST" ? "backtest" : "runtime")
4868
+ });
4869
+ }
4849
4870
  quality = await enrichSignalWithAi({
4850
4871
  signal,
4851
4872
  userName,
@@ -4915,6 +4936,7 @@ var createStrategyRuntime = ({
4915
4936
  if (!shouldMakeOrder) {
4916
4937
  if (signal) {
4917
4938
  const skipReason = getEntrySkipReason({
4939
+ signal,
4918
4940
  makeOrdersEnabled,
4919
4941
  env,
4920
4942
  ml,