@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);
@@ -342,14 +342,14 @@ __export(io_exports, {
342
342
  verifyResearchState: () => verifyResearchState,
343
343
  withResearchLock: () => withResearchLock
344
344
  });
345
- var import_promises2, import_node_path2, import_node_os, import_node_crypto2, researchDirectory, readJson, boundResearchPath, withResearchLock, verifyResearchState, verifyCompletedResearch, initializeResearchState, isRetainedDeletion;
345
+ var import_promises2, import_node_path2, import_node_os, import_node_crypto3, researchDirectory, readJson, boundResearchPath, withResearchLock, verifyResearchState, verifyCompletedResearch, initializeResearchState, isRetainedDeletion;
346
346
  var init_io2 = __esm({
347
347
  "src/lib/strategyResearch/io.ts"() {
348
348
  "use strict";
349
349
  import_promises2 = __toESM(require("fs/promises"));
350
350
  import_node_path2 = __toESM(require("path"));
351
351
  import_node_os = __toESM(require("os"));
352
- import_node_crypto2 = require("crypto");
352
+ import_node_crypto3 = require("crypto");
353
353
  init_io();
354
354
  researchDirectory = (projectRoot, spec) => import_node_path2.default.join(projectRoot, "data/research/strategy", spec.core.researchId);
355
355
  readJson = async (file) => JSON.parse(await import_promises2.default.readFile(file, "utf8"));
@@ -378,7 +378,7 @@ var init_io2 = __esm({
378
378
  "data/research/strategy/.lock"
379
379
  );
380
380
  await import_promises2.default.mkdir(import_node_path2.default.dirname(file), { recursive: true });
381
- const token = (0, import_node_crypto2.randomUUID)();
381
+ const token = (0, import_node_crypto3.randomUUID)();
382
382
  const acquire = () => import_promises2.default.writeFile(
383
383
  file,
384
384
  JSON.stringify({ pid: process.pid, host: import_node_os.default.hostname(), token }),
@@ -474,15 +474,15 @@ var init_io2 = __esm({
474
474
  await writeJsonAtomic(file, state);
475
475
  return state;
476
476
  };
477
- isRetainedDeletion = async (directory, file, hash) => {
477
+ isRetainedDeletion = async (directory, file, hash2) => {
478
478
  try {
479
479
  const inventory = await readJson(import_node_path2.default.join(directory, "retention.json"));
480
480
  const index = inventory.entries.findIndex(
481
- (entry) => entry.path === file && entry.sha256 === hash
481
+ (entry) => entry.path === file && entry.sha256 === hash2
482
482
  );
483
483
  if (index < 0) return false;
484
484
  const receipt = await readJson(import_node_path2.default.join(directory, "retention", `${index}.json`));
485
- return ["applying", "completed"].includes(receipt.status) && receipt.files[0]?.path === file && receipt.files[0]?.sha256 === hash;
485
+ return ["applying", "completed"].includes(receipt.status) && receipt.files[0]?.path === file && receipt.files[0]?.sha256 === hash2;
486
486
  } catch (error) {
487
487
  if (error.code === "ENOENT") return false;
488
488
  throw error;
@@ -504,6 +504,7 @@ var import_node_path16 = __toESM(require("path"));
504
504
  init_io();
505
505
 
506
506
  // src/lib/aiPocketSearch/features.ts
507
+ var import_aiFeatures = require("@tradejs/core/aiFeatures");
507
508
  var DAY_MS = 24 * 60 * 60 * 1e3;
508
509
  var DAYS_PER_WEEK = 7;
509
510
  var DAYS_PER_MONTH = 30.4375;
@@ -872,15 +873,25 @@ var addFlattenedFeatures = ({
872
873
  value,
873
874
  segments,
874
875
  maxDepth,
875
- shouldSkipPath = () => false
876
+ shouldSkipPath = () => false,
877
+ onFeatureExcluded
876
878
  }) => {
877
879
  if (!segments.length && !isPlainRecord(value)) {
878
880
  return;
879
881
  }
880
- if (segments.length > maxDepth || isOutcomePath(segments) || shouldSkipPath(segments)) {
882
+ if (segments.length > maxDepth || isOutcomePath(segments)) {
883
+ onFeatureExcluded?.({
884
+ path: segments.join("."),
885
+ classification: segments.length > maxDepth ? "depth-limit" : "derived-policy"
886
+ });
881
887
  return;
882
888
  }
889
+ if (shouldSkipPath(segments)) return;
883
890
  if (Array.isArray(value)) {
891
+ onFeatureExcluded?.({
892
+ path: segments.join("."),
893
+ classification: "unprojected-array"
894
+ });
884
895
  return;
885
896
  }
886
897
  if (isFeaturePrimitive(value)) {
@@ -902,7 +913,8 @@ var addFlattenedFeatures = ({
902
913
  value: child,
903
914
  segments: [...segments, key],
904
915
  maxDepth,
905
- shouldSkipPath
916
+ shouldSkipPath,
917
+ onFeatureExcluded
906
918
  });
907
919
  }
908
920
  };
@@ -1114,7 +1126,10 @@ var collectAiPocketFeatureSnapshot = ({
1114
1126
  value: source,
1115
1127
  segments: [],
1116
1128
  maxDepth: 8,
1129
+ onFeatureExcluded,
1117
1130
  shouldSkipPath: (segments) => {
1131
+ if (segments.join(".") === "additionalIndicators.baseContext.tickerProfile" || segments.join(".") === "additionalIndicators.tickerProfile" || segments.join(".") === "additionalIndicators.portfolioContext")
1132
+ return true;
1118
1133
  if (isOperatorExcluded(segments.join("."))) {
1119
1134
  return true;
1120
1135
  }
@@ -1132,6 +1147,10 @@ var collectAiPocketFeatureSnapshot = ({
1132
1147
  return true;
1133
1148
  }
1134
1149
  if (featureProfile === "compact" && isCompactFeaturePathSkipped(segments)) {
1150
+ onFeatureExcluded?.({
1151
+ path: segments.join("."),
1152
+ classification: "compact-profile"
1153
+ });
1135
1154
  return true;
1136
1155
  }
1137
1156
  if (featurePolicy !== "causal-stationary") {
@@ -1166,6 +1185,21 @@ var collectAiPocketFeatureSnapshot = ({
1166
1185
  delete features[key];
1167
1186
  }
1168
1187
  }
1188
+ const registered = payload.featureSnapshot;
1189
+ if (registered) {
1190
+ (0, import_aiFeatures.validateAiFeatureSnapshot)(registered);
1191
+ for (const definition of registered.definitions) {
1192
+ const path17 = `registered.${definition.id}`;
1193
+ if (isOperatorExcluded(path17)) continue;
1194
+ const value = registered.values[definition.id];
1195
+ 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";
1196
+ if (classification !== "eligible") {
1197
+ onFeatureExcluded?.({ path: path17, classification });
1198
+ continue;
1199
+ }
1200
+ features[path17] = value;
1201
+ }
1202
+ }
1169
1203
  return { features, featureCoverage };
1170
1204
  };
1171
1205
  var collectAiPocketFeatures = (options) => collectAiPocketFeatureSnapshot(options).features;
@@ -1784,15 +1818,15 @@ var createPocketResult = (rows, predicates, mask, validationRows, testRows, obje
1784
1818
  };
1785
1819
  };
1786
1820
  var hashMask = (mask) => {
1787
- let hash = 2166136261;
1821
+ let hash2 = 2166136261;
1788
1822
  for (let index = 0; index < mask.length; index += 1) {
1789
1823
  if (mask[index] !== 1) {
1790
1824
  continue;
1791
1825
  }
1792
- hash ^= index + 1;
1793
- hash = Math.imul(hash, 16777619);
1826
+ hash2 ^= index + 1;
1827
+ hash2 = Math.imul(hash2, 16777619);
1794
1828
  }
1795
- return (hash >>> 0).toString(36);
1829
+ return (hash2 >>> 0).toString(36);
1796
1830
  };
1797
1831
  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;
1798
1832
  var estimateCombinationCount = (poolSize, maxDepth, maxCombinations) => {
@@ -2489,6 +2523,51 @@ var policyVariant = (spec, coreId, policy, id, jev) => {
2489
2523
 
2490
2524
  // src/lib/strategyResearch/orchestrator.ts
2491
2525
  var import_promises5 = __toESM(require("fs/promises"));
2526
+
2527
+ // src/lib/researchMetricSnapshot.ts
2528
+ var import_node_crypto2 = require("crypto");
2529
+ var import_node_zlib = require("zlib");
2530
+ var MAX_BYTES = 256 * 1024 * 1024;
2531
+ var hash = (bytes) => (0, import_node_crypto2.createHash)("sha256").update(bytes).digest("hex");
2532
+ var validatePermanentResearchMetrics = (value) => {
2533
+ if (!value || typeof value !== "object") return;
2534
+ for (const [key, child] of Object.entries(value)) {
2535
+ if (["matchedPairs", "tradeRows", "allProjectedLeaves"].includes(key) && Array.isArray(child) && child.length)
2536
+ throw new Error(`Temporary payload ${key} is not a permanent metric`);
2537
+ if (key === "trades" && Array.isArray(child) && child.some(
2538
+ (row) => row && typeof row === "object" && ("signalId" in row || "entryTimestamp" in row)
2539
+ ))
2540
+ throw new Error("Temporary trade records are not permanent metrics");
2541
+ validatePermanentResearchMetrics(child);
2542
+ }
2543
+ };
2544
+ var encodeResearchMetricSnapshot = (metrics) => {
2545
+ validatePermanentResearchMetrics(metrics);
2546
+ const bytes = Buffer.from(JSON.stringify(metrics));
2547
+ if (bytes.length > MAX_BYTES)
2548
+ throw new Error("Metric snapshot exceeds decoded size limit");
2549
+ return {
2550
+ schema: "tradejs-metric-snapshot/v1",
2551
+ encoding: "gzip+base64",
2552
+ decodedBytes: bytes.length,
2553
+ sha256: hash(bytes),
2554
+ payload: (0, import_node_zlib.gzipSync)(bytes, { level: 9 }).toString("base64")
2555
+ };
2556
+ };
2557
+ var decodeResearchMetricSnapshot = (snapshot) => {
2558
+ 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")
2559
+ throw new Error("Invalid metric snapshot envelope");
2560
+ const bytes = (0, import_node_zlib.gunzipSync)(Buffer.from(snapshot.payload, "base64"), {
2561
+ maxOutputLength: snapshot.decodedBytes
2562
+ });
2563
+ if (bytes.length !== snapshot.decodedBytes || hash(bytes) !== snapshot.sha256)
2564
+ throw new Error("Metric snapshot checksum or size mismatch");
2565
+ const metrics = JSON.parse(bytes.toString("utf8"));
2566
+ validatePermanentResearchMetrics(metrics);
2567
+ return metrics;
2568
+ };
2569
+
2570
+ // src/lib/strategyResearch/orchestrator.ts
2492
2571
  var import_node_path6 = __toESM(require("path"));
2493
2572
 
2494
2573
  // src/lib/coreResearch/metrics.ts
@@ -3695,7 +3774,7 @@ var import_node_path5 = __toESM(require("path"));
3695
3774
  // src/lib/strategyRelease/researchRetention.ts
3696
3775
  var import_promises3 = __toESM(require("fs/promises"));
3697
3776
  var import_node_path4 = __toESM(require("path"));
3698
- var import_node_crypto3 = require("crypto");
3777
+ var import_node_crypto4 = require("crypto");
3699
3778
  var import_strategyReleaseEvidence = require("@tradejs/infra/strategyReleaseEvidence");
3700
3779
 
3701
3780
  // src/lib/strategyRelease/researchFileConsumers.ts
@@ -3844,7 +3923,7 @@ var mapFiles = async (entries, visit) => {
3844
3923
  if (worker.status === "rejected") throw worker.reason;
3845
3924
  return results;
3846
3925
  };
3847
- 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);
3926
+ 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);
3848
3927
  var inside = (root, file) => {
3849
3928
  const relative = import_node_path4.default.relative(root, file);
3850
3929
  return relative !== "" && !relative.startsWith(`..${import_node_path4.default.sep}`) && relative !== ".." && !import_node_path4.default.isAbsolute(relative);
@@ -3888,13 +3967,27 @@ var applyResearchRetention = async (options) => {
3888
3967
  const header = Buffer.alloc(4096);
3889
3968
  const { bytesRead } = await handle.read(header, 0, header.length, 0);
3890
3969
  const frontmatter = header.subarray(0, bytesRead).toString("utf8").match(/^---\r?\n([\s\S]*?)\r?\n---/);
3891
- if (!frontmatter || !/^schema:\s*tradejs-research\/v1\s*$/m.test(frontmatter[1]) || !(retired ? /^reproduction:\s*(?:complete|partial|blocked|legacy-partial)\s*$/m.test(
3970
+ if (!frontmatter || !/^schema:\s*tradejs-research\/v[12]\s*$/m.test(frontmatter[1]) || !(retired ? /^reproduction:\s*(?:complete|partial|blocked|legacy-partial)\s*$/m.test(
3892
3971
  frontmatter[1]
3893
3972
  ) : /^reproduction:\s*complete\s*$/m.test(frontmatter[1]))) {
3894
3973
  throw new Error(
3895
3974
  `Research note must have reproduction: complete: ${file}`
3896
3975
  );
3897
3976
  }
3977
+ if (!retired && /^schema:\s*tradejs-research\/v2\s*$/m.test(frontmatter[1])) {
3978
+ if ((await import_promises3.default.stat(file)).size > 8 * 1024 * 1024)
3979
+ throw new Error("Compact permanent note exceeds 8 MiB");
3980
+ const content = await import_promises3.default.readFile(file, "utf8");
3981
+ const section = content.split("## Metrics snapshot (machine-readable)")[1]?.split("## Reported metrics")[0] ?? "";
3982
+ const blocks = [
3983
+ ...section.matchAll(/```json\r?\n([\s\S]*?)\r?\n```/g)
3984
+ ];
3985
+ if (blocks.length !== 1)
3986
+ throw new Error(
3987
+ "Complete v2 note needs one verified metric snapshot"
3988
+ );
3989
+ decodeResearchMetricSnapshot(JSON.parse(blocks[0][1]));
3990
+ }
3898
3991
  } finally {
3899
3992
  await handle.close();
3900
3993
  }
@@ -4008,11 +4101,11 @@ var createResearchRetentionInventory = async (projectRoot, directory, spec, stat
4008
4101
  }
4009
4102
  const files = /* @__PURE__ */ new Map();
4010
4103
  for (const stage of Object.values(state.stages)) {
4011
- for (const [relative, hash] of Object.entries(stage.artifacts)) {
4104
+ for (const [relative, hash2] of Object.entries(stage.artifacts)) {
4012
4105
  const file = await boundResearchPath(directory, relative);
4013
4106
  if (relative.endsWith("/result.json")) {
4014
4107
  const result = await readJson(file);
4015
- if (result?.dataset) files.set(file, hash);
4108
+ if (result?.dataset) files.set(file, hash2);
4016
4109
  }
4017
4110
  if (relative.endsWith("/sources.json") || relative.endsWith("/payloads.json")) {
4018
4111
  const sources = await readJson(file);
@@ -4029,13 +4122,13 @@ var createResearchRetentionInventory = async (projectRoot, directory, spec, stat
4029
4122
  }
4030
4123
  }
4031
4124
  const entries = [];
4032
- for (const [file, hash] of files) {
4125
+ for (const [file, hash2] of files) {
4033
4126
  const stat = await import_promises4.default.stat(file);
4034
4127
  entries.push({
4035
4128
  path: file,
4036
4129
  kind: "research_payload",
4037
4130
  researchId: spec.core.researchId,
4038
- sha256: hash,
4131
+ sha256: hash2,
4039
4132
  bytes: stat.size,
4040
4133
  createdAt: stat.birthtimeMs,
4041
4134
  verified: true,
@@ -4779,7 +4872,7 @@ var runStrategyResearch = async (parameters) => {
4779
4872
  )
4780
4873
  };
4781
4874
  const note = `---
4782
- schema: tradejs-research/v1
4875
+ schema: tradejs-research/v2
4783
4876
  strategy: '${spec.core.strategy}'
4784
4877
  date: '${spec.core.createdAt.slice(0, 10)}'
4785
4878
  kind: 'backtest'
@@ -4812,7 +4905,7 @@ ${JSON.stringify(spec, null, 2)}
4812
4905
  ## Metrics snapshot (machine-readable)
4813
4906
 
4814
4907
  \`\`\`json
4815
- ${JSON.stringify(compact, null, 2)}
4908
+ ${JSON.stringify(encodeResearchMetricSnapshot(compact))}
4816
4909
  \`\`\`
4817
4910
 
4818
4911
  ## Reported metrics
@@ -4831,6 +4924,10 @@ ${directory}/report.html
4831
4924
 
4832
4925
  ${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.
4833
4926
  `;
4927
+ if (Buffer.byteLength(note) > 8 * 1024 * 1024)
4928
+ throw new Error(
4929
+ "Compact permanent note exceeds 8 MiB; archive source separately, never truncate metrics"
4930
+ );
4834
4931
  await import_promises5.default.writeFile(
4835
4932
  import_node_path6.default.join(
4836
4933
  noteDirectory,
@@ -4938,7 +5035,7 @@ var import_registry = require("@tradejs/node/registry");
4938
5035
  var import_connectors = require("@tradejs/node/connectors");
4939
5036
 
4940
5037
  // src/lib/coreResearch/dataset.ts
4941
- var import_node_crypto4 = require("crypto");
5038
+ var import_node_crypto5 = require("crypto");
4942
5039
  var import_node_fs2 = require("fs");
4943
5040
  var import_node_path7 = __toESM(require("path"));
4944
5041
  var import_node_readline = __toESM(require("readline"));
@@ -5153,8 +5250,8 @@ var readCoreResearchVariant = async (variant) => {
5153
5250
  for (const inputPath of variant.files) {
5154
5251
  const filePath = import_node_path7.default.resolve(inputPath);
5155
5252
  const input = (0, import_node_fs2.createReadStream)(filePath);
5156
- const hash = (0, import_node_crypto4.createHash)("sha256");
5157
- input.on("data", (chunk) => hash.update(chunk));
5253
+ const hash2 = (0, import_node_crypto5.createHash)("sha256");
5254
+ input.on("data", (chunk) => hash2.update(chunk));
5158
5255
  const lines = import_node_readline.default.createInterface({ input, crlfDelay: Infinity });
5159
5256
  const fileTrades = [];
5160
5257
  let lineNumber = 0;
@@ -5204,7 +5301,7 @@ var readCoreResearchVariant = async (variant) => {
5204
5301
  trades.push(trade);
5205
5302
  fileTrades.push(trade);
5206
5303
  }
5207
- const fileSha2562 = hash.digest("hex");
5304
+ const fileSha2562 = hash2.digest("hex");
5208
5305
  for (const trade of fileTrades) trade.sourceSha256 = fileSha2562;
5209
5306
  sources.push({
5210
5307
  path: filePath,
@@ -5224,7 +5321,7 @@ var readCoreResearchVariant = async (variant) => {
5224
5321
  };
5225
5322
 
5226
5323
  // src/lib/backtest/checkpoint.ts
5227
- var import_node_crypto5 = require("crypto");
5324
+ var import_node_crypto6 = require("crypto");
5228
5325
  var import_constants = require("@tradejs/core/constants");
5229
5326
  var import_backtest2 = require("@tradejs/core/backtest");
5230
5327
  var import_redis = require("@tradejs/infra/redis");
@@ -5424,6 +5521,7 @@ var extractSignalFromAiDatasetRow = (row) => {
5424
5521
  const { payload } = row;
5425
5522
  return {
5426
5523
  ...payload.signal,
5524
+ aiFeatureReplay: { snapshot: payload.featureSnapshot ?? null },
5427
5525
  strategy: payload.signal.strategy,
5428
5526
  figures: payload.figures ?? {},
5429
5527
  indicators: payload.indicators ?? {},
@@ -5437,7 +5535,7 @@ init_io2();
5437
5535
 
5438
5536
  // src/lib/aiTrainResearch.ts
5439
5537
  var import_node_child_process2 = require("child_process");
5440
- var import_node_crypto7 = require("crypto");
5538
+ var import_node_crypto8 = require("crypto");
5441
5539
  var import_promises7 = __toESM(require("fs/promises"));
5442
5540
  var import_node_path9 = __toESM(require("path"));
5443
5541
 
@@ -5449,7 +5547,7 @@ var import_strategies = require("@tradejs/node/strategies");
5449
5547
  var import_types2 = require("@tradejs/types");
5450
5548
 
5451
5549
  // src/lib/strategyGateFingerprint.ts
5452
- var import_node_crypto6 = require("crypto");
5550
+ var import_node_crypto7 = require("crypto");
5453
5551
  var import_node_module = require("module");
5454
5552
  var import_promises6 = __toESM(require("fs/promises"));
5455
5553
  var import_node_path8 = __toESM(require("path"));
@@ -5462,6 +5560,8 @@ var RESEARCH_CONTEXT_ENV_KEYS = [
5462
5560
  "INTERVAL",
5463
5561
  "DERIVATIVES_CONTEXT_ENABLED",
5464
5562
  "DERIVATIVES_CONTEXT_TARGET_ENABLED",
5563
+ "TICKER_PROFILES_PROVIDER",
5564
+ "TICKER_PROFILES_UNIVERSE",
5465
5565
  "DERIVATIVES_CONTEXT_LOOKBACK_HOURS",
5466
5566
  "DERIVATIVES_CONTEXT_EXTRA_REFERENCE_SYMBOLS",
5467
5567
  "DERIVATIVES_CONTEXT_EXCHANGE_PRIORITY",
@@ -5491,7 +5591,7 @@ var normalizeForStableJson = (value) => {
5491
5591
  }
5492
5592
  return value;
5493
5593
  };
5494
- var fingerprintResearchValue = (value) => (0, import_node_crypto7.createHash)("sha256").update(JSON.stringify(normalizeForStableJson(value))).digest("hex").slice(0, 16);
5594
+ var fingerprintResearchValue = (value) => (0, import_node_crypto8.createHash)("sha256").update(JSON.stringify(normalizeForStableJson(value))).digest("hex").slice(0, 16);
5495
5595
  var fingerprintResearchContextEnvironment = (env = process.env) => fingerprintResearchValue(
5496
5596
  Object.fromEntries(
5497
5597
  RESEARCH_CONTEXT_ENV_KEYS.map((key) => [key, env[key] ?? null])
@@ -6532,7 +6632,7 @@ init_io();
6532
6632
  var import_promises11 = __toESM(require("fs/promises"));
6533
6633
  var import_node_path15 = __toESM(require("path"));
6534
6634
  var import_node_child_process4 = require("child_process");
6535
- var import_node_crypto9 = require("crypto");
6635
+ var import_node_crypto10 = require("crypto");
6536
6636
  var import_coreResearch2 = require("@tradejs/infra/coreResearch");
6537
6637
 
6538
6638
  // src/lib/coreResearch/artifacts.ts
@@ -6546,7 +6646,7 @@ init_io();
6546
6646
  init_io();
6547
6647
 
6548
6648
  // src/lib/coreResearch/statistics.ts
6549
- var import_node_crypto8 = require("crypto");
6649
+ var import_node_crypto9 = require("crypto");
6550
6650
 
6551
6651
  // src/lib/coreResearch/orchestrator.ts
6552
6652
  init_types();
@@ -6584,7 +6684,7 @@ var createCoreResearchSpecTemplate = (params) => {
6584
6684
  gitSha,
6585
6685
  ...sourceRepository ? { sourceRepository } : {},
6586
6686
  ...dirtyDiff ? {
6587
- dirtyDiffSha256: (0, import_node_crypto9.createHash)("sha256").update(dirtyDiff).digest("hex")
6687
+ dirtyDiffSha256: (0, import_node_crypto10.createHash)("sha256").update(dirtyDiff).digest("hex")
6588
6688
  } : {}
6589
6689
  };
6590
6690
  } catch {
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/scripts/tickerProfiles.ts
31
+ var tickerProfiles_exports = {};
32
+ __export(tickerProfiles_exports, {
33
+ main: () => main
34
+ });
35
+ module.exports = __toCommonJS(tickerProfiles_exports);
36
+ var import_promises = __toESM(require("fs/promises"));
37
+
38
+ // src/lib/tickerProfiles.ts
39
+ var import_tickerProfiles = require("@tradejs/core/tickerProfiles");
40
+ var import_candles = require("@tradejs/infra/timescale/candles");
41
+ var import_tickerProfiles2 = require("@tradejs/infra/tickerProfiles");
42
+ var prepareTickerProfiles = async ({
43
+ projectRoot,
44
+ provider,
45
+ universeId,
46
+ symbols,
47
+ start,
48
+ end
49
+ }) => {
50
+ if (!Number.isSafeInteger(end) || end <= start)
51
+ throw new Error("Invalid profile time window");
52
+ if (!symbols.length || new Set(symbols).size !== symbols.length || symbols.some((s) => !/^[A-Z0-9]+USDT$/.test(s)))
53
+ throw new Error("Supply a unique explicit USDT universe");
54
+ if (!symbols.includes("BTCUSDT") || !symbols.includes("ETHUSDT"))
55
+ throw new Error("The universe must include BTCUSDT and ETHUSDT references");
56
+ const receipts = [];
57
+ for (let asOf = (0, import_tickerProfiles.tickerProfileMonth)(start); asOf < end; asOf = (0, import_tickerProfiles.tickerProfileNextMonth)(asOf)) {
58
+ const candlesBySymbol = {};
59
+ for (const symbol of [...symbols].sort())
60
+ candlesBySymbol[symbol] = await (0, import_candles.getKlineCandlesRange)(
61
+ provider,
62
+ symbol,
63
+ 60,
64
+ asOf - 721 * 36e5,
65
+ asOf - 36e5
66
+ );
67
+ const snapshot = (0, import_tickerProfiles.buildTickerProfileSnapshot)({
68
+ asOf,
69
+ provider,
70
+ universeId,
71
+ membership: "frozen-list",
72
+ candlesBySymbol
73
+ });
74
+ const snapshotId = await (0, import_tickerProfiles2.writeTickerProfiles)(projectRoot, snapshot);
75
+ receipts.push({
76
+ asOf,
77
+ snapshotId,
78
+ symbols: snapshot.profiles.length,
79
+ unknown: snapshot.profiles.filter(
80
+ (p) => p.moneyGroup === "unknown" || p.volatilityGroup === "unknown"
81
+ ).length
82
+ });
83
+ }
84
+ return receipts;
85
+ };
86
+
87
+ // src/scripts/tickerProfiles.ts
88
+ var main = async () => {
89
+ const argv = process.argv.slice(2);
90
+ const option = (name) => {
91
+ const i = argv.indexOf(`--${name}`);
92
+ if (i < 0 || !argv[i + 1] || argv[i + 1].startsWith("--"))
93
+ throw new Error(`--${name} is required`);
94
+ return argv[i + 1];
95
+ };
96
+ const symbols = JSON.parse(
97
+ await import_promises.default.readFile(option("symbolsFile"), "utf8")
98
+ );
99
+ if (!Array.isArray(symbols) || !symbols.every((s) => typeof s === "string"))
100
+ throw new Error("symbolsFile must be a JSON string array");
101
+ const receipts = await prepareTickerProfiles({
102
+ projectRoot: process.env.PROJECT_CWD || process.cwd(),
103
+ provider: option("provider"),
104
+ universeId: option("universeId"),
105
+ symbols,
106
+ start: Number(option("startTime")),
107
+ end: Number(option("endTime"))
108
+ });
109
+ console.log(JSON.stringify({ command: "ticker-profiles", receipts }));
110
+ };
111
+ // Annotate the CommonJS export names for ESM import in node:
112
+ 0 && (module.exports = {
113
+ main
114
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tradejs/cli",
3
- "version": "3.1.29-beta.289",
3
+ "version": "3.1.29-beta.291",
4
4
  "description": "Official CLI for the TradeJS TypeScript framework: infra setup, backtests, signals, bots, and ML workflows.",
5
5
  "keywords": [
6
6
  "tradejs",
@@ -37,13 +37,13 @@
37
37
  "tradejs": "dist/cli.js"
38
38
  },
39
39
  "dependencies": {
40
- "@tradejs/connectors": "^3.1.29-beta.289",
41
- "@tradejs/core": "^3.1.29-beta.289",
42
- "@tradejs/indicators": "^3.1.29-beta.289",
43
- "@tradejs/infra": "^3.1.29-beta.289",
44
- "@tradejs/node": "^3.1.29-beta.289",
40
+ "@tradejs/connectors": "^3.1.29-beta.291",
41
+ "@tradejs/core": "^3.1.29-beta.291",
42
+ "@tradejs/indicators": "^3.1.29-beta.291",
43
+ "@tradejs/infra": "^3.1.29-beta.291",
44
+ "@tradejs/node": "^3.1.29-beta.291",
45
45
  "@tradejs/strategy-kit": "3.0.6",
46
- "@tradejs/types": "^3.1.29-beta.289",
46
+ "@tradejs/types": "^3.1.29-beta.291",
47
47
  "args": "^5.0.3",
48
48
  "bcryptjs": "^2.4.3",
49
49
  "chalk": "4.1.2",
@@ -54,6 +54,7 @@
54
54
  "lodash": "^4.18.1",
55
55
  "pg": "8.22.0",
56
56
  "progress": "^2.0.3",
57
+ "sharp": "^0.35.3",
57
58
  "ws": "^8.21.1"
58
59
  },
59
60
  "devDependencies": {