@tradejs/cli 3.1.29-beta.289 → 3.1.29-beta.291

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.
@@ -75,10 +75,10 @@ var init_io = __esm({
75
75
  sha256Text = (value) => (0, import_node_crypto.createHash)("sha256").update(value).digest("hex");
76
76
  sha256Json = (value) => sha256Text(canonicalJson(value));
77
77
  sha256File = async (filePath) => {
78
- const hash = (0, import_node_crypto.createHash)("sha256");
78
+ const hash2 = (0, import_node_crypto.createHash)("sha256");
79
79
  const input = (0, import_node_fs.createReadStream)(filePath);
80
- for await (const chunk of input) hash.update(chunk);
81
- return hash.digest("hex");
80
+ for await (const chunk of input) hash2.update(chunk);
81
+ return hash2.digest("hex");
82
82
  };
83
83
  assertFinite = (value, name) => {
84
84
  assert(typeof value === "number" && Number.isFinite(value), name);
@@ -336,14 +336,14 @@ __export(io_exports, {
336
336
  verifyResearchState: () => verifyResearchState,
337
337
  withResearchLock: () => withResearchLock
338
338
  });
339
- var import_promises2, import_node_path2, import_node_os, import_node_crypto2, researchDirectory, readJson, boundResearchPath, withResearchLock, verifyResearchState, verifyCompletedResearch, initializeResearchState, isRetainedDeletion;
339
+ var import_promises2, import_node_path2, import_node_os, import_node_crypto3, researchDirectory, readJson, boundResearchPath, withResearchLock, verifyResearchState, verifyCompletedResearch, initializeResearchState, isRetainedDeletion;
340
340
  var init_io2 = __esm({
341
341
  "src/lib/strategyResearch/io.ts"() {
342
342
  "use strict";
343
343
  import_promises2 = __toESM(require("fs/promises"));
344
344
  import_node_path2 = __toESM(require("path"));
345
345
  import_node_os = __toESM(require("os"));
346
- import_node_crypto2 = require("crypto");
346
+ import_node_crypto3 = require("crypto");
347
347
  init_io();
348
348
  researchDirectory = (projectRoot, spec) => import_node_path2.default.join(projectRoot, "data/research/strategy", spec.core.researchId);
349
349
  readJson = async (file) => JSON.parse(await import_promises2.default.readFile(file, "utf8"));
@@ -372,7 +372,7 @@ var init_io2 = __esm({
372
372
  "data/research/strategy/.lock"
373
373
  );
374
374
  await import_promises2.default.mkdir(import_node_path2.default.dirname(file), { recursive: true });
375
- const token = (0, import_node_crypto2.randomUUID)();
375
+ const token = (0, import_node_crypto3.randomUUID)();
376
376
  const acquire = () => import_promises2.default.writeFile(
377
377
  file,
378
378
  JSON.stringify({ pid: process.pid, host: import_node_os.default.hostname(), token }),
@@ -468,15 +468,15 @@ var init_io2 = __esm({
468
468
  await writeJsonAtomic(file, state);
469
469
  return state;
470
470
  };
471
- isRetainedDeletion = async (directory, file, hash) => {
471
+ isRetainedDeletion = async (directory, file, hash2) => {
472
472
  try {
473
473
  const inventory = await readJson(import_node_path2.default.join(directory, "retention.json"));
474
474
  const index = inventory.entries.findIndex(
475
- (entry) => entry.path === file && entry.sha256 === hash
475
+ (entry) => entry.path === file && entry.sha256 === hash2
476
476
  );
477
477
  if (index < 0) return false;
478
478
  const receipt = await readJson(import_node_path2.default.join(directory, "retention", `${index}.json`));
479
- return ["applying", "completed"].includes(receipt.status) && receipt.files[0]?.path === file && receipt.files[0]?.sha256 === hash;
479
+ return ["applying", "completed"].includes(receipt.status) && receipt.files[0]?.path === file && receipt.files[0]?.sha256 === hash2;
480
480
  } catch (error) {
481
481
  if (error.code === "ENOENT") return false;
482
482
  throw error;
@@ -499,6 +499,7 @@ module.exports = __toCommonJS(strategyResearch_exports);
499
499
  init_io();
500
500
 
501
501
  // src/lib/aiPocketSearch/features.ts
502
+ var import_aiFeatures = require("@tradejs/core/aiFeatures");
502
503
  var DAY_MS = 24 * 60 * 60 * 1e3;
503
504
  var DAYS_PER_WEEK = 7;
504
505
  var DAYS_PER_MONTH = 30.4375;
@@ -867,15 +868,25 @@ var addFlattenedFeatures = ({
867
868
  value,
868
869
  segments,
869
870
  maxDepth,
870
- shouldSkipPath = () => false
871
+ shouldSkipPath = () => false,
872
+ onFeatureExcluded
871
873
  }) => {
872
874
  if (!segments.length && !isPlainRecord(value)) {
873
875
  return;
874
876
  }
875
- if (segments.length > maxDepth || isOutcomePath(segments) || shouldSkipPath(segments)) {
877
+ if (segments.length > maxDepth || isOutcomePath(segments)) {
878
+ onFeatureExcluded?.({
879
+ path: segments.join("."),
880
+ classification: segments.length > maxDepth ? "depth-limit" : "derived-policy"
881
+ });
876
882
  return;
877
883
  }
884
+ if (shouldSkipPath(segments)) return;
878
885
  if (Array.isArray(value)) {
886
+ onFeatureExcluded?.({
887
+ path: segments.join("."),
888
+ classification: "unprojected-array"
889
+ });
879
890
  return;
880
891
  }
881
892
  if (isFeaturePrimitive(value)) {
@@ -897,7 +908,8 @@ var addFlattenedFeatures = ({
897
908
  value: child,
898
909
  segments: [...segments, key],
899
910
  maxDepth,
900
- shouldSkipPath
911
+ shouldSkipPath,
912
+ onFeatureExcluded
901
913
  });
902
914
  }
903
915
  };
@@ -1109,7 +1121,10 @@ var collectAiPocketFeatureSnapshot = ({
1109
1121
  value: source,
1110
1122
  segments: [],
1111
1123
  maxDepth: 8,
1124
+ onFeatureExcluded,
1112
1125
  shouldSkipPath: (segments) => {
1126
+ if (segments.join(".") === "additionalIndicators.baseContext.tickerProfile" || segments.join(".") === "additionalIndicators.tickerProfile" || segments.join(".") === "additionalIndicators.portfolioContext")
1127
+ return true;
1113
1128
  if (isOperatorExcluded(segments.join("."))) {
1114
1129
  return true;
1115
1130
  }
@@ -1127,6 +1142,10 @@ var collectAiPocketFeatureSnapshot = ({
1127
1142
  return true;
1128
1143
  }
1129
1144
  if (featureProfile === "compact" && isCompactFeaturePathSkipped(segments)) {
1145
+ onFeatureExcluded?.({
1146
+ path: segments.join("."),
1147
+ classification: "compact-profile"
1148
+ });
1130
1149
  return true;
1131
1150
  }
1132
1151
  if (featurePolicy !== "causal-stationary") {
@@ -1161,6 +1180,21 @@ var collectAiPocketFeatureSnapshot = ({
1161
1180
  delete features[key];
1162
1181
  }
1163
1182
  }
1183
+ const registered = payload.featureSnapshot;
1184
+ if (registered) {
1185
+ (0, import_aiFeatures.validateAiFeatureSnapshot)(registered);
1186
+ for (const definition of registered.definitions) {
1187
+ const path13 = `registered.${definition.id}`;
1188
+ if (isOperatorExcluded(path13)) continue;
1189
+ const value = registered.values[definition.id];
1190
+ const classification = definition.role === "outcome" || definition.role === "policy" ? "derived-policy" : definition.role !== "input" ? definition.role : featurePolicy === "causal-stationary" && !definition.stationary ? "raw-nonstationary" : value === null || value === "unknown" ? "data-quality" : "eligible";
1191
+ if (classification !== "eligible") {
1192
+ onFeatureExcluded?.({ path: path13, classification });
1193
+ continue;
1194
+ }
1195
+ features[path13] = value;
1196
+ }
1197
+ }
1164
1198
  return { features, featureCoverage };
1165
1199
  };
1166
1200
  var collectAiPocketFeatures = (options) => collectAiPocketFeatureSnapshot(options).features;
@@ -1779,15 +1813,15 @@ var createPocketResult = (rows, predicates, mask, validationRows, testRows, obje
1779
1813
  };
1780
1814
  };
1781
1815
  var hashMask = (mask) => {
1782
- let hash = 2166136261;
1816
+ let hash2 = 2166136261;
1783
1817
  for (let index = 0; index < mask.length; index += 1) {
1784
1818
  if (mask[index] !== 1) {
1785
1819
  continue;
1786
1820
  }
1787
- hash ^= index + 1;
1788
- hash = Math.imul(hash, 16777619);
1821
+ hash2 ^= index + 1;
1822
+ hash2 = Math.imul(hash2, 16777619);
1789
1823
  }
1790
- return (hash >>> 0).toString(36);
1824
+ return (hash2 >>> 0).toString(36);
1791
1825
  };
1792
1826
  var isBetterRepresentativePocket = (candidate, existing, compare) => candidate.depth < existing.depth || candidate.depth === existing.depth && candidate.condition.length < existing.condition.length || candidate.depth === existing.depth && candidate.condition.length === existing.condition.length && compare(candidate, existing) < 0;
1793
1827
  var estimateCombinationCount = (poolSize, maxDepth, maxCombinations) => {
@@ -2484,6 +2518,51 @@ var policyVariant = (spec, coreId, policy, id, jev) => {
2484
2518
 
2485
2519
  // src/lib/strategyResearch/orchestrator.ts
2486
2520
  var import_promises5 = __toESM(require("fs/promises"));
2521
+
2522
+ // src/lib/researchMetricSnapshot.ts
2523
+ var import_node_crypto2 = require("crypto");
2524
+ var import_node_zlib = require("zlib");
2525
+ var MAX_BYTES = 256 * 1024 * 1024;
2526
+ var hash = (bytes) => (0, import_node_crypto2.createHash)("sha256").update(bytes).digest("hex");
2527
+ var validatePermanentResearchMetrics = (value) => {
2528
+ if (!value || typeof value !== "object") return;
2529
+ for (const [key, child] of Object.entries(value)) {
2530
+ if (["matchedPairs", "tradeRows", "allProjectedLeaves"].includes(key) && Array.isArray(child) && child.length)
2531
+ throw new Error(`Temporary payload ${key} is not a permanent metric`);
2532
+ if (key === "trades" && Array.isArray(child) && child.some(
2533
+ (row) => row && typeof row === "object" && ("signalId" in row || "entryTimestamp" in row)
2534
+ ))
2535
+ throw new Error("Temporary trade records are not permanent metrics");
2536
+ validatePermanentResearchMetrics(child);
2537
+ }
2538
+ };
2539
+ var encodeResearchMetricSnapshot = (metrics) => {
2540
+ validatePermanentResearchMetrics(metrics);
2541
+ const bytes = Buffer.from(JSON.stringify(metrics));
2542
+ if (bytes.length > MAX_BYTES)
2543
+ throw new Error("Metric snapshot exceeds decoded size limit");
2544
+ return {
2545
+ schema: "tradejs-metric-snapshot/v1",
2546
+ encoding: "gzip+base64",
2547
+ decodedBytes: bytes.length,
2548
+ sha256: hash(bytes),
2549
+ payload: (0, import_node_zlib.gzipSync)(bytes, { level: 9 }).toString("base64")
2550
+ };
2551
+ };
2552
+ var decodeResearchMetricSnapshot = (snapshot) => {
2553
+ if (!snapshot || typeof snapshot !== "object" || !("schema" in snapshot) || snapshot.schema !== "tradejs-metric-snapshot/v1" || !("encoding" in snapshot) || snapshot.encoding !== "gzip+base64" || !("decodedBytes" in snapshot) || typeof snapshot.decodedBytes !== "number" || !Number.isSafeInteger(snapshot.decodedBytes) || snapshot.decodedBytes <= 0 || snapshot.decodedBytes > MAX_BYTES || !("sha256" in snapshot) || typeof snapshot.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(snapshot.sha256) || !("payload" in snapshot) || typeof snapshot.payload !== "string")
2554
+ throw new Error("Invalid metric snapshot envelope");
2555
+ const bytes = (0, import_node_zlib.gunzipSync)(Buffer.from(snapshot.payload, "base64"), {
2556
+ maxOutputLength: snapshot.decodedBytes
2557
+ });
2558
+ if (bytes.length !== snapshot.decodedBytes || hash(bytes) !== snapshot.sha256)
2559
+ throw new Error("Metric snapshot checksum or size mismatch");
2560
+ const metrics = JSON.parse(bytes.toString("utf8"));
2561
+ validatePermanentResearchMetrics(metrics);
2562
+ return metrics;
2563
+ };
2564
+
2565
+ // src/lib/strategyResearch/orchestrator.ts
2487
2566
  var import_node_path6 = __toESM(require("path"));
2488
2567
 
2489
2568
  // src/lib/coreResearch/metrics.ts
@@ -3690,7 +3769,7 @@ var import_node_path5 = __toESM(require("path"));
3690
3769
  // src/lib/strategyRelease/researchRetention.ts
3691
3770
  var import_promises3 = __toESM(require("fs/promises"));
3692
3771
  var import_node_path4 = __toESM(require("path"));
3693
- var import_node_crypto3 = require("crypto");
3772
+ var import_node_crypto4 = require("crypto");
3694
3773
  var import_strategyReleaseEvidence = require("@tradejs/infra/strategyReleaseEvidence");
3695
3774
 
3696
3775
  // src/lib/strategyRelease/researchFileConsumers.ts
@@ -3839,7 +3918,7 @@ var mapFiles = async (entries, visit) => {
3839
3918
  if (worker.status === "rejected") throw worker.reason;
3840
3919
  return results;
3841
3920
  };
3842
- var fileSha256 = async (file, bytes) => bytes <= 1024 * 1024 ? (0, import_node_crypto3.createHash)("sha256").update(await import_promises3.default.readFile(file)).digest("hex") : (0, import_strategyReleaseEvidence.strategyEvidenceFileSha256)(file);
3921
+ var fileSha256 = async (file, bytes) => bytes <= 1024 * 1024 ? (0, import_node_crypto4.createHash)("sha256").update(await import_promises3.default.readFile(file)).digest("hex") : (0, import_strategyReleaseEvidence.strategyEvidenceFileSha256)(file);
3843
3922
  var inside = (root, file) => {
3844
3923
  const relative = import_node_path4.default.relative(root, file);
3845
3924
  return relative !== "" && !relative.startsWith(`..${import_node_path4.default.sep}`) && relative !== ".." && !import_node_path4.default.isAbsolute(relative);
@@ -3883,13 +3962,27 @@ var applyResearchRetention = async (options) => {
3883
3962
  const header = Buffer.alloc(4096);
3884
3963
  const { bytesRead } = await handle.read(header, 0, header.length, 0);
3885
3964
  const frontmatter = header.subarray(0, bytesRead).toString("utf8").match(/^---\r?\n([\s\S]*?)\r?\n---/);
3886
- if (!frontmatter || !/^schema:\s*tradejs-research\/v1\s*$/m.test(frontmatter[1]) || !(retired ? /^reproduction:\s*(?:complete|partial|blocked|legacy-partial)\s*$/m.test(
3965
+ if (!frontmatter || !/^schema:\s*tradejs-research\/v[12]\s*$/m.test(frontmatter[1]) || !(retired ? /^reproduction:\s*(?:complete|partial|blocked|legacy-partial)\s*$/m.test(
3887
3966
  frontmatter[1]
3888
3967
  ) : /^reproduction:\s*complete\s*$/m.test(frontmatter[1]))) {
3889
3968
  throw new Error(
3890
3969
  `Research note must have reproduction: complete: ${file}`
3891
3970
  );
3892
3971
  }
3972
+ if (!retired && /^schema:\s*tradejs-research\/v2\s*$/m.test(frontmatter[1])) {
3973
+ if ((await import_promises3.default.stat(file)).size > 8 * 1024 * 1024)
3974
+ throw new Error("Compact permanent note exceeds 8 MiB");
3975
+ const content = await import_promises3.default.readFile(file, "utf8");
3976
+ const section = content.split("## Metrics snapshot (machine-readable)")[1]?.split("## Reported metrics")[0] ?? "";
3977
+ const blocks = [
3978
+ ...section.matchAll(/```json\r?\n([\s\S]*?)\r?\n```/g)
3979
+ ];
3980
+ if (blocks.length !== 1)
3981
+ throw new Error(
3982
+ "Complete v2 note needs one verified metric snapshot"
3983
+ );
3984
+ decodeResearchMetricSnapshot(JSON.parse(blocks[0][1]));
3985
+ }
3893
3986
  } finally {
3894
3987
  await handle.close();
3895
3988
  }
@@ -4003,11 +4096,11 @@ var createResearchRetentionInventory = async (projectRoot, directory, spec, stat
4003
4096
  }
4004
4097
  const files = /* @__PURE__ */ new Map();
4005
4098
  for (const stage of Object.values(state.stages)) {
4006
- for (const [relative, hash] of Object.entries(stage.artifacts)) {
4099
+ for (const [relative, hash2] of Object.entries(stage.artifacts)) {
4007
4100
  const file = await boundResearchPath(directory, relative);
4008
4101
  if (relative.endsWith("/result.json")) {
4009
4102
  const result = await readJson(file);
4010
- if (result?.dataset) files.set(file, hash);
4103
+ if (result?.dataset) files.set(file, hash2);
4011
4104
  }
4012
4105
  if (relative.endsWith("/sources.json") || relative.endsWith("/payloads.json")) {
4013
4106
  const sources = await readJson(file);
@@ -4024,13 +4117,13 @@ var createResearchRetentionInventory = async (projectRoot, directory, spec, stat
4024
4117
  }
4025
4118
  }
4026
4119
  const entries = [];
4027
- for (const [file, hash] of files) {
4120
+ for (const [file, hash2] of files) {
4028
4121
  const stat = await import_promises4.default.stat(file);
4029
4122
  entries.push({
4030
4123
  path: file,
4031
4124
  kind: "research_payload",
4032
4125
  researchId: spec.core.researchId,
4033
- sha256: hash,
4126
+ sha256: hash2,
4034
4127
  bytes: stat.size,
4035
4128
  createdAt: stat.birthtimeMs,
4036
4129
  verified: true,
@@ -4774,7 +4867,7 @@ var runStrategyResearch = async (parameters) => {
4774
4867
  )
4775
4868
  };
4776
4869
  const note = `---
4777
- schema: tradejs-research/v1
4870
+ schema: tradejs-research/v2
4778
4871
  strategy: '${spec.core.strategy}'
4779
4872
  date: '${spec.core.createdAt.slice(0, 10)}'
4780
4873
  kind: 'backtest'
@@ -4807,7 +4900,7 @@ ${JSON.stringify(spec, null, 2)}
4807
4900
  ## Metrics snapshot (machine-readable)
4808
4901
 
4809
4902
  \`\`\`json
4810
- ${JSON.stringify(compact, null, 2)}
4903
+ ${JSON.stringify(encodeResearchMetricSnapshot(compact))}
4811
4904
  \`\`\`
4812
4905
 
4813
4906
  ## Reported metrics
@@ -4826,6 +4919,10 @@ ${directory}/report.html
4826
4919
 
4827
4920
  ${conclusion.nextAction} Current-snapshot universe; no deployment authorization. Temporary payloads are deleted only after the permanent record is verified; new trade-level questions require a frozen rerun.
4828
4921
  `;
4922
+ if (Buffer.byteLength(note) > 8 * 1024 * 1024)
4923
+ throw new Error(
4924
+ "Compact permanent note exceeds 8 MiB; archive source separately, never truncate metrics"
4925
+ );
4829
4926
  await import_promises5.default.writeFile(
4830
4927
  import_node_path6.default.join(
4831
4928
  noteDirectory,
@@ -4933,7 +5030,7 @@ var import_registry = require("@tradejs/node/registry");
4933
5030
  var import_connectors = require("@tradejs/node/connectors");
4934
5031
 
4935
5032
  // src/lib/coreResearch/dataset.ts
4936
- var import_node_crypto4 = require("crypto");
5033
+ var import_node_crypto5 = require("crypto");
4937
5034
  var import_node_fs2 = require("fs");
4938
5035
  var import_node_path7 = __toESM(require("path"));
4939
5036
  var import_node_readline = __toESM(require("readline"));
@@ -5148,8 +5245,8 @@ var readCoreResearchVariant = async (variant) => {
5148
5245
  for (const inputPath of variant.files) {
5149
5246
  const filePath = import_node_path7.default.resolve(inputPath);
5150
5247
  const input = (0, import_node_fs2.createReadStream)(filePath);
5151
- const hash = (0, import_node_crypto4.createHash)("sha256");
5152
- input.on("data", (chunk) => hash.update(chunk));
5248
+ const hash2 = (0, import_node_crypto5.createHash)("sha256");
5249
+ input.on("data", (chunk) => hash2.update(chunk));
5153
5250
  const lines = import_node_readline.default.createInterface({ input, crlfDelay: Infinity });
5154
5251
  const fileTrades = [];
5155
5252
  let lineNumber = 0;
@@ -5199,7 +5296,7 @@ var readCoreResearchVariant = async (variant) => {
5199
5296
  trades.push(trade);
5200
5297
  fileTrades.push(trade);
5201
5298
  }
5202
- const fileSha2562 = hash.digest("hex");
5299
+ const fileSha2562 = hash2.digest("hex");
5203
5300
  for (const trade of fileTrades) trade.sourceSha256 = fileSha2562;
5204
5301
  sources.push({
5205
5302
  path: filePath,
@@ -5219,7 +5316,7 @@ var readCoreResearchVariant = async (variant) => {
5219
5316
  };
5220
5317
 
5221
5318
  // src/lib/backtest/checkpoint.ts
5222
- var import_node_crypto5 = require("crypto");
5319
+ var import_node_crypto6 = require("crypto");
5223
5320
  var import_constants = require("@tradejs/core/constants");
5224
5321
  var import_backtest2 = require("@tradejs/core/backtest");
5225
5322
  var import_redis = require("@tradejs/infra/redis");
@@ -5419,6 +5516,7 @@ var extractSignalFromAiDatasetRow = (row) => {
5419
5516
  const { payload } = row;
5420
5517
  return {
5421
5518
  ...payload.signal,
5519
+ aiFeatureReplay: { snapshot: payload.featureSnapshot ?? null },
5422
5520
  strategy: payload.signal.strategy,
5423
5521
  figures: payload.figures ?? {},
5424
5522
  indicators: payload.indicators ?? {},
@@ -5432,7 +5530,7 @@ init_io2();
5432
5530
 
5433
5531
  // src/lib/aiTrainResearch.ts
5434
5532
  var import_node_child_process2 = require("child_process");
5435
- var import_node_crypto7 = require("crypto");
5533
+ var import_node_crypto8 = require("crypto");
5436
5534
  var import_promises7 = __toESM(require("fs/promises"));
5437
5535
  var import_node_path9 = __toESM(require("path"));
5438
5536
 
@@ -5444,7 +5542,7 @@ var import_strategies = require("@tradejs/node/strategies");
5444
5542
  var import_types2 = require("@tradejs/types");
5445
5543
 
5446
5544
  // src/lib/strategyGateFingerprint.ts
5447
- var import_node_crypto6 = require("crypto");
5545
+ var import_node_crypto7 = require("crypto");
5448
5546
  var import_node_module = require("module");
5449
5547
  var import_promises6 = __toESM(require("fs/promises"));
5450
5548
  var import_node_path8 = __toESM(require("path"));
@@ -5457,6 +5555,8 @@ var RESEARCH_CONTEXT_ENV_KEYS = [
5457
5555
  "INTERVAL",
5458
5556
  "DERIVATIVES_CONTEXT_ENABLED",
5459
5557
  "DERIVATIVES_CONTEXT_TARGET_ENABLED",
5558
+ "TICKER_PROFILES_PROVIDER",
5559
+ "TICKER_PROFILES_UNIVERSE",
5460
5560
  "DERIVATIVES_CONTEXT_LOOKBACK_HOURS",
5461
5561
  "DERIVATIVES_CONTEXT_EXTRA_REFERENCE_SYMBOLS",
5462
5562
  "DERIVATIVES_CONTEXT_EXCHANGE_PRIORITY",
@@ -5486,7 +5586,7 @@ var normalizeForStableJson = (value) => {
5486
5586
  }
5487
5587
  return value;
5488
5588
  };
5489
- var fingerprintResearchValue = (value) => (0, import_node_crypto7.createHash)("sha256").update(JSON.stringify(normalizeForStableJson(value))).digest("hex").slice(0, 16);
5589
+ var fingerprintResearchValue = (value) => (0, import_node_crypto8.createHash)("sha256").update(JSON.stringify(normalizeForStableJson(value))).digest("hex").slice(0, 16);
5490
5590
  var fingerprintResearchContextEnvironment = (env = process.env) => fingerprintResearchValue(
5491
5591
  Object.fromEntries(
5492
5592
  RESEARCH_CONTEXT_ENV_KEYS.map((key) => [key, env[key] ?? null])
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/lib/tickerProfiles.ts
21
+ var tickerProfiles_exports = {};
22
+ __export(tickerProfiles_exports, {
23
+ prepareTickerProfiles: () => prepareTickerProfiles
24
+ });
25
+ module.exports = __toCommonJS(tickerProfiles_exports);
26
+ var import_tickerProfiles = require("@tradejs/core/tickerProfiles");
27
+ var import_candles = require("@tradejs/infra/timescale/candles");
28
+ var import_tickerProfiles2 = require("@tradejs/infra/tickerProfiles");
29
+ var prepareTickerProfiles = async ({
30
+ projectRoot,
31
+ provider,
32
+ universeId,
33
+ symbols,
34
+ start,
35
+ end
36
+ }) => {
37
+ if (!Number.isSafeInteger(end) || end <= start)
38
+ throw new Error("Invalid profile time window");
39
+ if (!symbols.length || new Set(symbols).size !== symbols.length || symbols.some((s) => !/^[A-Z0-9]+USDT$/.test(s)))
40
+ throw new Error("Supply a unique explicit USDT universe");
41
+ if (!symbols.includes("BTCUSDT") || !symbols.includes("ETHUSDT"))
42
+ throw new Error("The universe must include BTCUSDT and ETHUSDT references");
43
+ const receipts = [];
44
+ for (let asOf = (0, import_tickerProfiles.tickerProfileMonth)(start); asOf < end; asOf = (0, import_tickerProfiles.tickerProfileNextMonth)(asOf)) {
45
+ const candlesBySymbol = {};
46
+ for (const symbol of [...symbols].sort())
47
+ candlesBySymbol[symbol] = await (0, import_candles.getKlineCandlesRange)(
48
+ provider,
49
+ symbol,
50
+ 60,
51
+ asOf - 721 * 36e5,
52
+ asOf - 36e5
53
+ );
54
+ const snapshot = (0, import_tickerProfiles.buildTickerProfileSnapshot)({
55
+ asOf,
56
+ provider,
57
+ universeId,
58
+ membership: "frozen-list",
59
+ candlesBySymbol
60
+ });
61
+ const snapshotId = await (0, import_tickerProfiles2.writeTickerProfiles)(projectRoot, snapshot);
62
+ receipts.push({
63
+ asOf,
64
+ snapshotId,
65
+ symbols: snapshot.profiles.length,
66
+ unknown: snapshot.profiles.filter(
67
+ (p) => p.moneyGroup === "unknown" || p.volatilityGroup === "unknown"
68
+ ).length
69
+ });
70
+ }
71
+ return receipts;
72
+ };
73
+ // Annotate the CommonJS export names for ESM import in node:
74
+ 0 && (module.exports = {
75
+ prepareTickerProfiles
76
+ });
@@ -592,6 +592,7 @@ var import_progress = __toESM(require("progress"));
592
592
  var import_ai = require("@tradejs/infra/ai");
593
593
 
594
594
  // src/lib/aiPocketSearch/features.ts
595
+ var import_aiFeatures = require("@tradejs/core/aiFeatures");
595
596
  var DAY_MS2 = 24 * 60 * 60 * 1e3;
596
597
  var DAYS_PER_WEEK = 7;
597
598
  var DAYS_PER_MONTH = 30.4375;
@@ -960,15 +961,25 @@ var addFlattenedFeatures = ({
960
961
  value,
961
962
  segments,
962
963
  maxDepth,
963
- shouldSkipPath = () => false
964
+ shouldSkipPath = () => false,
965
+ onFeatureExcluded
964
966
  }) => {
965
967
  if (!segments.length && !isPlainRecord(value)) {
966
968
  return;
967
969
  }
968
- if (segments.length > maxDepth || isOutcomePath(segments) || shouldSkipPath(segments)) {
970
+ if (segments.length > maxDepth || isOutcomePath(segments)) {
971
+ onFeatureExcluded?.({
972
+ path: segments.join("."),
973
+ classification: segments.length > maxDepth ? "depth-limit" : "derived-policy"
974
+ });
969
975
  return;
970
976
  }
977
+ if (shouldSkipPath(segments)) return;
971
978
  if (Array.isArray(value)) {
979
+ onFeatureExcluded?.({
980
+ path: segments.join("."),
981
+ classification: "unprojected-array"
982
+ });
972
983
  return;
973
984
  }
974
985
  if (isFeaturePrimitive(value)) {
@@ -990,7 +1001,8 @@ var addFlattenedFeatures = ({
990
1001
  value: child,
991
1002
  segments: [...segments, key],
992
1003
  maxDepth,
993
- shouldSkipPath
1004
+ shouldSkipPath,
1005
+ onFeatureExcluded
994
1006
  });
995
1007
  }
996
1008
  };
@@ -1202,7 +1214,10 @@ var collectAiPocketFeatureSnapshot = ({
1202
1214
  value: source,
1203
1215
  segments: [],
1204
1216
  maxDepth: 8,
1217
+ onFeatureExcluded,
1205
1218
  shouldSkipPath: (segments) => {
1219
+ if (segments.join(".") === "additionalIndicators.baseContext.tickerProfile" || segments.join(".") === "additionalIndicators.tickerProfile" || segments.join(".") === "additionalIndicators.portfolioContext")
1220
+ return true;
1206
1221
  if (isOperatorExcluded(segments.join("."))) {
1207
1222
  return true;
1208
1223
  }
@@ -1220,6 +1235,10 @@ var collectAiPocketFeatureSnapshot = ({
1220
1235
  return true;
1221
1236
  }
1222
1237
  if (featureProfile === "compact" && isCompactFeaturePathSkipped(segments)) {
1238
+ onFeatureExcluded?.({
1239
+ path: segments.join("."),
1240
+ classification: "compact-profile"
1241
+ });
1223
1242
  return true;
1224
1243
  }
1225
1244
  if (featurePolicy !== "causal-stationary") {
@@ -1254,6 +1273,21 @@ var collectAiPocketFeatureSnapshot = ({
1254
1273
  delete features[key];
1255
1274
  }
1256
1275
  }
1276
+ const registered = payload.featureSnapshot;
1277
+ if (registered) {
1278
+ (0, import_aiFeatures.validateAiFeatureSnapshot)(registered);
1279
+ for (const definition of registered.definitions) {
1280
+ const path3 = `registered.${definition.id}`;
1281
+ if (isOperatorExcluded(path3)) continue;
1282
+ const value = registered.values[definition.id];
1283
+ const classification = definition.role === "outcome" || definition.role === "policy" ? "derived-policy" : definition.role !== "input" ? definition.role : featurePolicy === "causal-stationary" && !definition.stationary ? "raw-nonstationary" : value === null || value === "unknown" ? "data-quality" : "eligible";
1284
+ if (classification !== "eligible") {
1285
+ onFeatureExcluded?.({ path: path3, classification });
1286
+ continue;
1287
+ }
1288
+ features[path3] = value;
1289
+ }
1290
+ }
1257
1291
  return { features, featureCoverage };
1258
1292
  };
1259
1293
 
@@ -3667,6 +3701,7 @@ var extractSignalFromAiDatasetRow = (row) => {
3667
3701
  const { payload } = row;
3668
3702
  return {
3669
3703
  ...payload.signal,
3704
+ aiFeatureReplay: { snapshot: payload.featureSnapshot ?? null },
3670
3705
  strategy: payload.signal.strategy,
3671
3706
  figures: payload.figures ?? {},
3672
3707
  indicators: payload.indicators ?? {},