@tradejs/cli 3.1.29-beta.282 → 3.1.29-beta.284

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.
@@ -39,6 +39,7 @@ var DAYS_PER_WEEK = 7;
39
39
  var DAYS_PER_MONTH = 30.4375;
40
40
  var PRODUCTION_CANDIDATE_MIN_EVENTS = 25;
41
41
  var OUTCOME_SEGMENTS = /* @__PURE__ */ new Set([
42
+ "researchentrydecision",
42
43
  "actual",
43
44
  "aiapproved",
44
45
  "approvalallowednow",
@@ -323,6 +324,8 @@ var classifyAiPocketFeaturePath = (path) => {
323
324
  if (METADATA_LEAVES.has(leaf) || leaf.endsWith("fingerprint") || normalized.some((segment) => segment === "metadata")) {
324
325
  return "metadata";
325
326
  }
327
+ if (normalized.some((segment) => OUTCOME_SEGMENTS.has(segment)))
328
+ return "derived-policy";
326
329
  if (DERIVED_POLICY_FRAGMENTS.some(
327
330
  (fragment) => !(fragment === "gatefeature" && isStrategySetupEvidence) && normalizedPath.includes(fragment)
328
331
  )) {
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var aiTrainResearch_exports = {};
32
32
  __export(aiTrainResearch_exports, {
33
33
  buildAiTrainLineage: () => buildAiTrainLineage,
34
+ fingerprintResearchContextEnvironment: () => fingerprintResearchContextEnvironment,
34
35
  fingerprintResearchValue: () => fingerprintResearchValue,
35
36
  summarizeAiTrainCoverage: () => summarizeAiTrainCoverage,
36
37
  summarizeAiTrainRejectReasons: () => summarizeAiTrainRejectReasons,
@@ -548,6 +549,11 @@ var getGitLineage = (projectRoot) => {
548
549
  return { gitSha: null, gitDirty: null };
549
550
  }
550
551
  };
552
+ var fingerprintResearchContextEnvironment = (env = process.env) => fingerprintResearchValue(
553
+ Object.fromEntries(
554
+ RESEARCH_CONTEXT_ENV_KEYS.map((key) => [key, env[key] ?? null])
555
+ )
556
+ );
551
557
  var buildAiTrainLineage = async ({
552
558
  projectRoot,
553
559
  strategyName,
@@ -588,6 +594,7 @@ var buildAiTrainLineage = async ({
588
594
  // Annotate the CommonJS export names for ESM import in node:
589
595
  0 && (module.exports = {
590
596
  buildAiTrainLineage,
597
+ fingerprintResearchContextEnvironment,
591
598
  fingerprintResearchValue,
592
599
  summarizeAiTrainCoverage,
593
600
  summarizeAiTrainRejectReasons,
@@ -45,7 +45,7 @@ __export(coreResearch_exports, {
45
45
  module.exports = __toCommonJS(coreResearch_exports);
46
46
 
47
47
  // src/lib/coreResearch/orchestrator.ts
48
- var import_promises4 = __toESM(require("fs/promises"));
48
+ var import_promises3 = __toESM(require("fs/promises"));
49
49
  var import_node_path6 = __toESM(require("path"));
50
50
  var import_node_child_process = require("child_process");
51
51
  var import_node_crypto5 = require("crypto");
@@ -1019,6 +1019,8 @@ var calendarClusterBootstrap = (params) => {
1019
1019
  const alpha = 1 - params.confidenceLevel;
1020
1020
  const positive = samples.filter((value) => value > 0).length;
1021
1021
  const probabilityPositive = positive / samples.length;
1022
+ const observed = deltas.reduce((sum, value) => sum + value, 0) / deltas.length;
1023
+ const oneSidedPValue = (1 + samples.filter((value) => value - observed >= observed - 1e-12).length) / (samples.length + 1);
1022
1024
  return {
1023
1025
  method: "calendar-cluster-bootstrap",
1024
1026
  clusterDays: params.clusterDays,
@@ -1030,7 +1032,7 @@ var calendarClusterBootstrap = (params) => {
1030
1032
  percentile(samples, 1 - alpha / 2)
1031
1033
  ],
1032
1034
  probabilityPositive,
1033
- oneSidedPValue: 1 - probabilityPositive,
1035
+ oneSidedPValue,
1034
1036
  holmAdjustedPValue: null
1035
1037
  };
1036
1038
  };
@@ -1097,7 +1099,7 @@ var erf = (value) => {
1097
1099
  return sign * y;
1098
1100
  };
1099
1101
  var normalCdf = (value) => 0.5 * (1 + erf(value / Math.SQRT2));
1100
- var deflatedSharpeDiagnostic = (returns, trials) => {
1102
+ var deflatedSharpeDiagnostic = (returns, trials, trialSharpes) => {
1101
1103
  const periods = returns.length;
1102
1104
  if (periods < 3) {
1103
1105
  return {
@@ -1122,9 +1124,18 @@ var deflatedSharpeDiagnostic = (returns, trials) => {
1122
1124
  const observedSharpe = mean / standardDeviation;
1123
1125
  const skew = centered.reduce((sum, value) => sum + (value / standardDeviation) ** 3, 0) / periods;
1124
1126
  const kurtosis = centered.reduce((sum, value) => sum + (value / standardDeviation) ** 4, 0) / periods;
1125
- const expectedMaximumSharpe = inverseNormalCdf(
1126
- (Math.max(1, trials) - 0.375) / (Math.max(1, trials) + 0.25)
1127
- );
1127
+ const available = trialSharpes?.filter(Number.isFinite) ?? [];
1128
+ if (trials > 1 && available.length < 2)
1129
+ return {
1130
+ periods,
1131
+ observedSharpe,
1132
+ expectedMaximumSharpe: null,
1133
+ probabilityAboveSelectionBias: null
1134
+ };
1135
+ const trialMean = available.length ? available.reduce((sum, value) => sum + value, 0) / available.length : 0;
1136
+ const trialVariance = available.length > 1 ? available.reduce((sum, value) => sum + (value - trialMean) ** 2, 0) / (available.length - 1) : 0;
1137
+ const gamma = 0.5772156649015329;
1138
+ const expectedMaximumSharpe = trials <= 1 ? 0 : Math.sqrt(trialVariance) * ((1 - gamma) * inverseNormalCdf(1 - 1 / trials) + gamma * inverseNormalCdf(1 - 1 / (trials * Math.E)));
1128
1139
  const standardError = Math.sqrt(
1129
1140
  Math.max(
1130
1141
  1e-12,
@@ -1142,6 +1153,27 @@ var deflatedSharpeDiagnostic = (returns, trials) => {
1142
1153
  };
1143
1154
  var combinations = (items, choose) => {
1144
1155
  const output = [];
1156
+ if (items.length > 10) {
1157
+ const random = createSeededRandom(`cscv-balanced-${items.length}`);
1158
+ const seen = /* @__PURE__ */ new Set();
1159
+ for (let attempt = 0; output.length < 512 && attempt < 1e5; attempt++) {
1160
+ const shuffled = [...items];
1161
+ for (let index = shuffled.length - 1; index > 0; index--) {
1162
+ const swap = Math.floor(random() * (index + 1));
1163
+ [shuffled[index], shuffled[swap]] = [shuffled[swap], shuffled[index]];
1164
+ }
1165
+ const selected = shuffled.slice(0, choose).sort((a, b) => a - b);
1166
+ const complement = items.filter((item) => !selected.includes(item));
1167
+ for (const half of [selected, complement]) {
1168
+ const key = half.join(",");
1169
+ if (!seen.has(key)) {
1170
+ seen.add(key);
1171
+ output.push(half);
1172
+ }
1173
+ }
1174
+ }
1175
+ return output;
1176
+ }
1145
1177
  const visit = (start, selected) => {
1146
1178
  if (output.length >= 512) return;
1147
1179
  if (selected.length === choose) {
@@ -1180,14 +1212,23 @@ var probabilityOfBacktestOverfitting = (performanceByVariant) => {
1180
1212
  const inScores = performanceByVariant.map(
1181
1213
  (performance) => inSample.reduce((sum, index) => sum + performance[index], 0)
1182
1214
  );
1183
- const selected = inScores.indexOf(Math.max(...inScores));
1215
+ const maximum = Math.max(...inScores);
1216
+ const selected = inScores.flatMap(
1217
+ (score, index) => score === maximum ? [index] : []
1218
+ );
1184
1219
  const outScores = performanceByVariant.map(
1185
1220
  (performance) => outSample.reduce((sum, index) => sum + performance[index], 0)
1186
1221
  );
1187
- const sorted = [...outScores].sort((left, right) => left - right);
1188
- const rankAscending = sorted.indexOf(outScores[selected]);
1189
- const omega = (rankAscending + 1) / (outScores.length + 1);
1190
- if (Math.log(omega / (1 - omega)) < 0) belowMedian += 1;
1222
+ for (const winner of selected) {
1223
+ const less = outScores.filter(
1224
+ (score) => score < outScores[winner]
1225
+ ).length;
1226
+ const equal = outScores.filter(
1227
+ (score) => score === outScores[winner]
1228
+ ).length;
1229
+ const omega = (less + (equal + 1) / 2) / (outScores.length + 1);
1230
+ if (omega < 0.5) belowMedian += 1 / selected.length;
1231
+ }
1191
1232
  }
1192
1233
  return {
1193
1234
  method: "CSCV",
@@ -2117,34 +2158,55 @@ var applyCoreResearchRobustnessGuardrails = (params) => {
2117
2158
  };
2118
2159
 
2119
2160
  // src/lib/coreResearch/trace.ts
2120
- var import_promises3 = __toESM(require("fs/promises"));
2161
+ var import_node_fs4 = require("fs");
2162
+ var import_node_readline2 = __toESM(require("readline"));
2121
2163
  var import_node_path4 = __toESM(require("path"));
2122
2164
  var summarizeCoreResearchTrace = async (filePaths = []) => {
2123
2165
  const events = /* @__PURE__ */ Object.create(null);
2124
2166
  const skipCounts = /* @__PURE__ */ Object.create(null);
2125
2167
  const exitCodes = /* @__PURE__ */ Object.create(null);
2168
+ const policyRejections = {};
2169
+ const policyExamples = [];
2126
2170
  for (const inputPath of filePaths) {
2127
2171
  const filePath = import_node_path4.default.resolve(inputPath);
2128
- const text = await import_promises3.default.readFile(filePath, "utf8");
2129
- for (const [index, line] of text.split("\n").entries()) {
2130
- if (!line.trim()) continue;
2131
- let event;
2132
- try {
2133
- event = JSON.parse(line);
2134
- } catch (error) {
2135
- throw new Error(
2136
- `${filePath}:${index + 1} contains invalid trace JSON: ${String(error)}`
2137
- );
2138
- }
2139
- events[event.event] = (events[event.event] ?? 0) + 1;
2140
- if (event.event === "skip_summary") {
2141
- for (const [reason, count] of Object.entries(event.skipCounts)) {
2142
- skipCounts[reason] = (skipCounts[reason] ?? 0) + count;
2172
+ const input = (0, import_node_fs4.createReadStream)(filePath);
2173
+ const lines = import_node_readline2.default.createInterface({ input, crlfDelay: Infinity });
2174
+ let index = -1;
2175
+ try {
2176
+ for await (const line of lines) {
2177
+ index++;
2178
+ if (!line.trim()) continue;
2179
+ let event;
2180
+ try {
2181
+ event = JSON.parse(line);
2182
+ } catch (error) {
2183
+ throw new Error(
2184
+ `${filePath}:${index + 1} contains invalid trace JSON: ${String(error)}`
2185
+ );
2186
+ }
2187
+ events[event.event] = (events[event.event] ?? 0) + 1;
2188
+ if (event.event === "skip_summary") {
2189
+ for (const [reason, count] of Object.entries(event.skipCounts)) {
2190
+ skipCounts[reason] = (skipCounts[reason] ?? 0) + count;
2191
+ }
2192
+ }
2193
+ if (event.event === "position_exited" && event.exitCode) {
2194
+ exitCodes[event.exitCode] = (exitCodes[event.exitCode] ?? 0) + 1;
2195
+ }
2196
+ if ("researchEntryDecision" in event && event.researchEntryDecision && !event.researchEntryDecision.allowed) {
2197
+ const reason = event.researchEntryDecision.reason;
2198
+ policyRejections[reason] = (policyRejections[reason] ?? 0) + 1;
2199
+ if (policyExamples.length < 20)
2200
+ policyExamples.push({
2201
+ timestamp: event.timestamp,
2202
+ symbol: event.symbol,
2203
+ decision: event.researchEntryDecision
2204
+ });
2143
2205
  }
2144
2206
  }
2145
- if (event.event === "position_exited" && event.exitCode) {
2146
- exitCodes[event.exitCode] = (exitCodes[event.exitCode] ?? 0) + 1;
2147
- }
2207
+ } finally {
2208
+ lines.close();
2209
+ input.destroy();
2148
2210
  }
2149
2211
  }
2150
2212
  return {
@@ -2158,7 +2220,13 @@ var summarizeCoreResearchTrace = async (filePaths = []) => {
2158
2220
  Object.entries(exitCodes).sort(
2159
2221
  ([leftKey, leftValue], [rightKey, rightValue]) => rightValue - leftValue || (leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0)
2160
2222
  )
2161
- )
2223
+ ),
2224
+ ...policyExamples.length ? {
2225
+ researchPolicy: {
2226
+ rejections: policyRejections,
2227
+ examples: policyExamples
2228
+ }
2229
+ } : {}
2162
2230
  };
2163
2231
  };
2164
2232
 
@@ -2417,7 +2485,14 @@ var analyzeCoreResearchPrepared = async (spec, options = {}) => {
2417
2485
  analysis.variant.id,
2418
2486
  deflatedSharpeDiagnostic(
2419
2487
  analysis.monthly.map((window) => window.cohorts.ALL.pnl),
2420
- Math.max(comparisons.length, familyHypotheses)
2488
+ Math.max(comparisons.length, familyHypotheses),
2489
+ analyses.flatMap((entry) => {
2490
+ const diagnostic = deflatedSharpeDiagnostic(
2491
+ entry.monthly.map((window) => window.cohorts.ALL.pnl),
2492
+ 1
2493
+ );
2494
+ return diagnostic.observedSharpe == null ? [] : [diagnostic.observedSharpe];
2495
+ })
2421
2496
  )
2422
2497
  ])
2423
2498
  ),
@@ -2442,7 +2517,7 @@ var analyzeCoreResearchPrepared = async (spec, options = {}) => {
2442
2517
  const tradesByVariant = new Map(
2443
2518
  windowed.map((entry) => [entry.variant.id, entry.trades])
2444
2519
  );
2445
- await import_promises4.default.writeFile(
2520
+ await import_promises3.default.writeFile(
2446
2521
  paths.reportPath,
2447
2522
  buildCoreResearchHtml({ spec, result, tradesByVariant }),
2448
2523
  "utf8"
@@ -2462,7 +2537,7 @@ var analyzeCoreResearchPrepared = async (spec, options = {}) => {
2462
2537
  if (error.code !== "ENOENT") throw error;
2463
2538
  }
2464
2539
  try {
2465
- for (const name of await import_promises4.default.readdir(paths.runLogDir)) {
2540
+ for (const name of await import_promises3.default.readdir(paths.runLogDir)) {
2466
2541
  if (!name.endsWith(".log")) continue;
2467
2542
  artifactHashes[`runs/${name}`] = await sha256File(
2468
2543
  import_node_path6.default.join(paths.runLogDir, name)
@@ -2662,7 +2737,7 @@ var createCoreResearchSpecTemplate = (params) => {
2662
2737
  };
2663
2738
  var runCommand = async (params) => {
2664
2739
  if (!params.command.length) throw new Error("Cannot run an empty command");
2665
- await import_promises4.default.mkdir(import_node_path6.default.dirname(params.logPath), { recursive: true });
2740
+ await import_promises3.default.mkdir(import_node_path6.default.dirname(params.logPath), { recursive: true });
2666
2741
  const chunks = [];
2667
2742
  const child = (0, import_node_child_process.spawn)(params.command[0], params.command.slice(1), {
2668
2743
  cwd: params.cwd,
@@ -2682,7 +2757,7 @@ var runCommand = async (params) => {
2682
2757
  child.once("close", (code) => resolve(code ?? 1));
2683
2758
  });
2684
2759
  const output = chunks.join("");
2685
- await import_promises4.default.writeFile(params.logPath, output, "utf8");
2760
+ await import_promises3.default.writeFile(params.logPath, output, "utf8");
2686
2761
  if (exitCode !== 0) {
2687
2762
  throw new Error(
2688
2763
  `Command failed with exit ${exitCode}: ${params.command.join(" ")}`
@@ -96,7 +96,7 @@ var buildMcpReplayProcess = async ({
96
96
  const heartbeat = JSON.parse(
97
97
  await import_promises.default.readFile(import_node_path.default.join(root, "heartbeat.json"), "utf8")
98
98
  );
99
- if (!Number.isFinite(heartbeat.observedAt) || Date.now() - heartbeat.observedAt < 0 || Date.now() - heartbeat.observedAt > 12e4)
99
+ if (heartbeat.protocolVersion !== 2 || !Number.isFinite(heartbeat.observedAt) || Date.now() - heartbeat.observedAt < 0 || Date.now() - heartbeat.observedAt > 12e4)
100
100
  throw new Error("Host replay runner heartbeat is stale");
101
101
  const bytes = await import_promises.default.readFile(evidencePath);
102
102
  const request = JSON.stringify({