@tradejs/cli 2.0.5 → 2.0.7

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.
@@ -34,9 +34,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
34
34
  ));
35
35
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
36
36
 
37
- // ../../node_modules/debug/node_modules/ms/index.js
37
+ // ../../node_modules/ms/index.js
38
38
  var require_ms = __commonJS({
39
- "../../node_modules/debug/node_modules/ms/index.js"(exports2, module2) {
39
+ "../../node_modules/ms/index.js"(exports2, module2) {
40
40
  "use strict";
41
41
  var s = 1e3;
42
42
  var m = s * 60;
@@ -151,9 +151,9 @@ var require_ms = __commonJS({
151
151
  }
152
152
  });
153
153
 
154
- // ../../node_modules/debug/src/common.js
154
+ // ../../node_modules/list-it/node_modules/debug/src/common.js
155
155
  var require_common = __commonJS({
156
- "../../node_modules/debug/src/common.js"(exports2, module2) {
156
+ "../../node_modules/list-it/node_modules/debug/src/common.js"(exports2, module2) {
157
157
  "use strict";
158
158
  function setup(env) {
159
159
  createDebug.debug = createDebug;
@@ -255,50 +255,64 @@ var require_common = __commonJS({
255
255
  createDebug.namespaces = namespaces;
256
256
  createDebug.names = [];
257
257
  createDebug.skips = [];
258
- let i;
259
- const split = (typeof namespaces === "string" ? namespaces : "").split(/[\s,]+/);
260
- const len = split.length;
261
- for (i = 0; i < len; i++) {
262
- if (!split[i]) {
263
- continue;
258
+ const split = (typeof namespaces === "string" ? namespaces : "").trim().replace(/\s+/g, ",").split(",").filter(Boolean);
259
+ for (const ns of split) {
260
+ if (ns[0] === "-") {
261
+ createDebug.skips.push(ns.slice(1));
262
+ } else {
263
+ createDebug.names.push(ns);
264
264
  }
265
- namespaces = split[i].replace(/\*/g, ".*?");
266
- if (namespaces[0] === "-") {
267
- createDebug.skips.push(new RegExp("^" + namespaces.slice(1) + "$"));
265
+ }
266
+ }
267
+ function matchesTemplate(search, template) {
268
+ let searchIndex = 0;
269
+ let templateIndex = 0;
270
+ let starIndex = -1;
271
+ let matchIndex = 0;
272
+ while (searchIndex < search.length) {
273
+ if (templateIndex < template.length && (template[templateIndex] === search[searchIndex] || template[templateIndex] === "*")) {
274
+ if (template[templateIndex] === "*") {
275
+ starIndex = templateIndex;
276
+ matchIndex = searchIndex;
277
+ templateIndex++;
278
+ } else {
279
+ searchIndex++;
280
+ templateIndex++;
281
+ }
282
+ } else if (starIndex !== -1) {
283
+ templateIndex = starIndex + 1;
284
+ matchIndex++;
285
+ searchIndex = matchIndex;
268
286
  } else {
269
- createDebug.names.push(new RegExp("^" + namespaces + "$"));
287
+ return false;
270
288
  }
271
289
  }
290
+ while (templateIndex < template.length && template[templateIndex] === "*") {
291
+ templateIndex++;
292
+ }
293
+ return templateIndex === template.length;
272
294
  }
273
295
  function disable() {
274
296
  const namespaces = [
275
- ...createDebug.names.map(toNamespace),
276
- ...createDebug.skips.map(toNamespace).map((namespace) => "-" + namespace)
297
+ ...createDebug.names,
298
+ ...createDebug.skips.map((namespace) => "-" + namespace)
277
299
  ].join(",");
278
300
  createDebug.enable("");
279
301
  return namespaces;
280
302
  }
281
303
  function enabled(name) {
282
- if (name[name.length - 1] === "*") {
283
- return true;
284
- }
285
- let i;
286
- let len;
287
- for (i = 0, len = createDebug.skips.length; i < len; i++) {
288
- if (createDebug.skips[i].test(name)) {
304
+ for (const skip of createDebug.skips) {
305
+ if (matchesTemplate(name, skip)) {
289
306
  return false;
290
307
  }
291
308
  }
292
- for (i = 0, len = createDebug.names.length; i < len; i++) {
293
- if (createDebug.names[i].test(name)) {
309
+ for (const ns of createDebug.names) {
310
+ if (matchesTemplate(name, ns)) {
294
311
  return true;
295
312
  }
296
313
  }
297
314
  return false;
298
315
  }
299
- function toNamespace(regexp) {
300
- return regexp.toString().substring(2, regexp.toString().length - 2).replace(/\.\*\?$/, "*");
301
- }
302
316
  function coerce(val) {
303
317
  if (val instanceof Error) {
304
318
  return val.stack || val.message;
@@ -315,9 +329,9 @@ var require_common = __commonJS({
315
329
  }
316
330
  });
317
331
 
318
- // ../../node_modules/debug/src/browser.js
332
+ // ../../node_modules/list-it/node_modules/debug/src/browser.js
319
333
  var require_browser = __commonJS({
320
- "../../node_modules/debug/src/browser.js"(exports2, module2) {
334
+ "../../node_modules/list-it/node_modules/debug/src/browser.js"(exports2, module2) {
321
335
  "use strict";
322
336
  exports2.formatArgs = formatArgs;
323
337
  exports2.save = save;
@@ -418,10 +432,11 @@ var require_browser = __commonJS({
418
432
  if (typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) {
419
433
  return false;
420
434
  }
435
+ let m;
421
436
  return typeof document !== "undefined" && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance || // Is firebug? http://stackoverflow.com/a/398120/376773
422
437
  typeof window !== "undefined" && window.console && (window.console.firebug || window.console.exception && window.console.table) || // Is firefox >= v31?
423
438
  // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
424
- typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31 || // Double check webkit in userAgent just in case we are in a worker
439
+ typeof navigator !== "undefined" && navigator.userAgent && (m = navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)) && parseInt(m[1], 10) >= 31 || // Double check webkit in userAgent just in case we are in a worker
425
440
  typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/);
426
441
  }
427
442
  function formatArgs(args2) {
@@ -459,7 +474,7 @@ var require_browser = __commonJS({
459
474
  function load() {
460
475
  let r;
461
476
  try {
462
- r = exports2.storage.getItem("debug");
477
+ r = exports2.storage.getItem("debug") || exports2.storage.getItem("DEBUG");
463
478
  } catch (error) {
464
479
  }
465
480
  if (!r && typeof process !== "undefined" && "env" in process) {
@@ -600,9 +615,9 @@ var require_supports_color = __commonJS({
600
615
  }
601
616
  });
602
617
 
603
- // ../../node_modules/debug/src/node.js
618
+ // ../../node_modules/list-it/node_modules/debug/src/node.js
604
619
  var require_node = __commonJS({
605
- "../../node_modules/debug/src/node.js"(exports2, module2) {
620
+ "../../node_modules/list-it/node_modules/debug/src/node.js"(exports2, module2) {
606
621
  "use strict";
607
622
  var tty = require("tty");
608
623
  var util = require("util");
@@ -743,7 +758,7 @@ var require_node = __commonJS({
743
758
  return (/* @__PURE__ */ new Date()).toISOString() + " ";
744
759
  }
745
760
  function log(...args2) {
746
- return process.stderr.write(util.format(...args2) + "\n");
761
+ return process.stderr.write(util.formatWithOptions(exports2.inspectOpts, ...args2) + "\n");
747
762
  }
748
763
  function save(namespaces) {
749
764
  if (namespaces) {
@@ -775,9 +790,9 @@ var require_node = __commonJS({
775
790
  }
776
791
  });
777
792
 
778
- // ../../node_modules/debug/src/index.js
793
+ // ../../node_modules/list-it/node_modules/debug/src/index.js
779
794
  var require_src = __commonJS({
780
- "../../node_modules/debug/src/index.js"(exports2, module2) {
795
+ "../../node_modules/list-it/node_modules/debug/src/index.js"(exports2, module2) {
781
796
  "use strict";
782
797
  if (typeof process === "undefined" || process.type === "renderer" || process.browser === true || process.__nwjs) {
783
798
  module2.exports = require_browser();
@@ -1635,9 +1650,9 @@ var import_chalk7 = __toESM(require("chalk"));
1635
1650
  var import_connectors6 = require("@tradejs/node/connectors");
1636
1651
  var import_cli2 = require("@tradejs/node/cli");
1637
1652
  var import_async3 = require("@tradejs/core/async");
1638
- var import_strategies2 = require("@tradejs/core/strategies");
1653
+ var import_strategies3 = require("@tradejs/core/strategies");
1639
1654
  var import_constants3 = require("@tradejs/core/constants");
1640
- var import_strategies3 = require("@tradejs/node/strategies");
1655
+ var import_strategies4 = require("@tradejs/node/strategies");
1641
1656
  var import_time3 = require("@tradejs/core/time");
1642
1657
  var import_logger4 = require("@tradejs/infra/logger");
1643
1658
  var import_redis5 = require("@tradejs/infra/redis");
@@ -1649,6 +1664,7 @@ var import_time = require("@tradejs/core/time");
1649
1664
  var SECONDS_PER_DAY = 86400;
1650
1665
  var RUNTIME_SIGNAL_RETENTION_DAYS_ENV = "RUNTIME_SIGNAL_RETENTION_DAYS";
1651
1666
  var DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS = import_constants.TTL_3D;
1667
+ var RUNTIME_LINEAGE_SCOPE_RETENTION_TTL_SECONDS = import_constants.TTL_1M;
1652
1668
  var parsePositiveNumber = (value) => {
1653
1669
  if (value == null || value === "") {
1654
1670
  return null;
@@ -1810,6 +1826,7 @@ var import_progress = __toESM(require("progress"));
1810
1826
  var import_chalk = __toESM(require("chalk"));
1811
1827
  var import_connectors = require("@tradejs/connectors");
1812
1828
  var import_connectors2 = require("@tradejs/node/connectors");
1829
+ var import_strategies = require("@tradejs/node/strategies");
1813
1830
  var import_timescale = require("@tradejs/infra/timescale");
1814
1831
 
1815
1832
  // src/lib/binanceMarketData.ts
@@ -1819,14 +1836,6 @@ var MARKET_FEATURE_INTERVAL_MS = {
1819
1836
  "15m": 9e5,
1820
1837
  "1h": 36e5
1821
1838
  };
1822
- var STABLE_QUOTE_SYMBOLS = /* @__PURE__ */ new Set([
1823
- "USDCUSDT",
1824
- "FDUSDUSDT",
1825
- "TUSDUSDT",
1826
- "BUSDUSDT",
1827
- "USDPUSDT",
1828
- "DAIUSDT"
1829
- ]);
1830
1839
  var safeDivide = (num, den) => Number.isFinite(num) && Number.isFinite(den) && den !== 0 ? num / den : null;
1831
1840
  var mean = (values) => values.length ? values.reduce((sum, value) => sum + value, 0) / values.length : null;
1832
1841
  var standardDeviation = (values) => {
@@ -1870,9 +1879,6 @@ var classifyBtcAltRegime = ({
1870
1879
  if (btcVsAltReturn24h < -5e-3) return "alt_lead";
1871
1880
  return "neutral";
1872
1881
  };
1873
- var selectBreadthUniverseFromTickers = (tickers, limit) => tickers.filter(
1874
- (ticker) => ticker.symbol.endsWith("USDT") && ticker.symbol !== "BTCUSDT" && !STABLE_QUOTE_SYMBOLS.has(ticker.symbol) && Number.isFinite(ticker.turnover24h) && ticker.turnover24h > 0
1875
- ).sort((a, b) => b.turnover24h - a.turnover24h).slice(0, Math.max(0, limit)).map((ticker) => ticker.symbol);
1876
1882
  var aggregateAggTradesToRows = ({
1877
1883
  symbol,
1878
1884
  interval: interval2,
@@ -2354,10 +2360,6 @@ var backfillBinanceMarketContext = async (params, enabled) => {
2354
2360
  process.env.BINANCE_MARKET_CONTEXT_TRADE_FLOW_SYMBOL_LIMIT,
2355
2361
  2
2356
2362
  );
2357
- const breadthLimit = asInt(
2358
- process.env.BINANCE_MARKET_CONTEXT_BREADTH_LIMIT,
2359
- 30
2360
- );
2361
2363
  const breadthChunkDays = asFloat(
2362
2364
  process.env.BINANCE_MARKET_CONTEXT_BREADTH_CHUNK_DAYS,
2363
2365
  30
@@ -2480,88 +2482,113 @@ var backfillBinanceMarketContext = async (params, enabled) => {
2480
2482
  }
2481
2483
  }
2482
2484
  if (includeBreadth) {
2483
- const tickers = await connector.getTickers();
2484
- const breadthSymbols = selectBreadthUniverseFromTickers(
2485
- tickers,
2486
- breadthLimit
2487
- );
2488
- const universe = `binance_top${breadthSymbols.length}_usdt`;
2489
- const coverage = await (0, import_timescale.getMarketBreadthCoverage)({
2490
- universe,
2491
- interval: interval2,
2492
- startMs: breadthStartMs,
2493
- endMs
2494
- });
2495
- const hasBtcAltMetrics = (coverage?.btcAltMetricsRows ?? 0) >= Math.floor(Math.max(1, coverage?.rows ?? 0) * 0.9);
2496
- if (!hasCoverage({
2497
- coverage,
2485
+ const breadthUniverses = (0, import_strategies.getBinanceBreadthUniverses)();
2486
+ const breadthSymbols = breadthUniverses.at(-1)?.symbols ?? [];
2487
+ const connectorInterval = marketIntervalToConnectorInterval(interval2);
2488
+ const chunks = buildBreadthBackfillChunks({
2498
2489
  startMs: breadthStartMs,
2499
2490
  endMs,
2500
- intervalMs: intervalMs2
2501
- }) || !hasBtcAltMetrics) {
2502
- const connectorInterval = marketIntervalToConnectorInterval(interval2);
2503
- const chunks = buildBreadthBackfillChunks({
2504
- startMs: breadthStartMs,
2505
- endMs,
2506
- intervalMs: intervalMs2,
2507
- chunkDays: breadthChunkDays
2508
- });
2509
- const missingChunks = filterMissingBreadthBackfillChunks({
2510
- chunks,
2511
- coverage,
2512
- intervalMs: intervalMs2
2513
- });
2514
- const bar = new import_progress.default(
2515
- "breadth :current/:total [:bar][:percent] :etas(s) candles=:candles skip=:skip chunk=:chunk :symbol",
2516
- {
2517
- total: Math.max(1, missingChunks.length * breadthSymbols.length),
2518
- width: 24
2519
- }
2520
- );
2521
- let candlesRead = 0;
2522
- let skippedBreadthChunks = chunks.length - missingChunks.length;
2523
- if (!missingChunks.length) {
2524
- bar.tick(1, {
2525
- candles: 0,
2526
- skip: skippedBreadthChunks,
2527
- chunk: "cached",
2528
- symbol: universe
2491
+ intervalMs: intervalMs2,
2492
+ chunkDays: breadthChunkDays
2493
+ });
2494
+ const plans = await Promise.all(
2495
+ breadthUniverses.map(async (definition) => {
2496
+ const coverage = await (0, import_timescale.getMarketBreadthCoverage)({
2497
+ universe: definition.universe,
2498
+ interval: interval2,
2499
+ startMs: breadthStartMs,
2500
+ endMs
2529
2501
  });
2530
- }
2531
- for (let chunkIndex = 0; chunkIndex < missingChunks.length; chunkIndex += 1) {
2532
- const chunk = missingChunks[chunkIndex];
2533
- const originalChunkIndex = chunks.findIndex(
2534
- (item) => item.startMs === chunk.startMs && item.endMs === chunk.endMs
2535
- );
2536
- const candlesBySymbol = {};
2537
- for (const symbol of breadthSymbols) {
2538
- const candles = await connector.kline({
2539
- symbol,
2540
- interval: connectorInterval,
2541
- start: chunk.fetchStartMs,
2542
- end: chunk.endMs,
2543
- silent: true
2544
- });
2545
- candlesBySymbol[symbol] = candles;
2546
- candlesRead += candles.length;
2547
- bar.tick(1, {
2548
- candles: candlesRead,
2549
- skip: skippedBreadthChunks,
2550
- chunk: `${originalChunkIndex + 1}/${chunks.length}`,
2551
- symbol
2552
- });
2502
+ const hasBtcAltMetrics = (coverage?.btcAltMetricsRows ?? 0) >= Math.floor(Math.max(1, coverage?.rows ?? 0) * 0.9);
2503
+ const missingChunks2 = !hasCoverage({
2504
+ coverage,
2505
+ startMs: breadthStartMs,
2506
+ endMs,
2507
+ intervalMs: intervalMs2
2508
+ }) || !hasBtcAltMetrics ? !hasBtcAltMetrics ? chunks : filterMissingBreadthBackfillChunks({
2509
+ chunks,
2510
+ coverage,
2511
+ intervalMs: intervalMs2
2512
+ }) : [];
2513
+ if (!missingChunks2.length) {
2514
+ console.log(
2515
+ import_chalk.default.gray(`breadth cached: universe=${definition.universe}`)
2516
+ );
2553
2517
  }
2554
- const btcCandles = await connector.kline({
2555
- symbol: "BTCUSDT",
2518
+ return {
2519
+ definition,
2520
+ missingChunkKeys: new Set(
2521
+ missingChunks2.map((chunk) => `${chunk.startMs}:${chunk.endMs}`)
2522
+ )
2523
+ };
2524
+ })
2525
+ );
2526
+ const missingChunks = chunks.filter((chunk) => {
2527
+ const key = `${chunk.startMs}:${chunk.endMs}`;
2528
+ return plans.some((plan) => plan.missingChunkKeys.has(key));
2529
+ });
2530
+ const bar = new import_progress.default(
2531
+ "breadth :current/:total [:bar][:percent] :etas(s) candles=:candles skip=:skip chunk=:chunk :symbol",
2532
+ {
2533
+ total: Math.max(1, missingChunks.length * breadthSymbols.length),
2534
+ width: 24
2535
+ }
2536
+ );
2537
+ let candlesRead = 0;
2538
+ const skippedBreadthChunks = chunks.length - missingChunks.length;
2539
+ if (!missingChunks.length) {
2540
+ bar.tick(1, {
2541
+ candles: 0,
2542
+ skip: skippedBreadthChunks,
2543
+ chunk: "cached",
2544
+ symbol: "top5/top10/top30/top50/top100"
2545
+ });
2546
+ }
2547
+ for (let chunkIndex = 0; chunkIndex < missingChunks.length; chunkIndex += 1) {
2548
+ const chunk = missingChunks[chunkIndex];
2549
+ const originalChunkIndex = chunks.findIndex(
2550
+ (item) => item.startMs === chunk.startMs && item.endMs === chunk.endMs
2551
+ );
2552
+ const candlesBySymbol = {};
2553
+ for (const symbol of breadthSymbols) {
2554
+ const candles = await connector.kline({
2555
+ symbol,
2556
2556
  interval: connectorInterval,
2557
2557
  start: chunk.fetchStartMs,
2558
2558
  end: chunk.endMs,
2559
2559
  silent: true
2560
2560
  });
2561
+ candlesBySymbol[symbol] = candles;
2562
+ candlesRead += candles.length;
2563
+ bar.tick(1, {
2564
+ candles: candlesRead,
2565
+ skip: skippedBreadthChunks,
2566
+ chunk: `${originalChunkIndex + 1}/${chunks.length}`,
2567
+ symbol
2568
+ });
2569
+ }
2570
+ const btcCandles = await connector.kline({
2571
+ symbol: "BTCUSDT",
2572
+ interval: connectorInterval,
2573
+ start: chunk.fetchStartMs,
2574
+ end: chunk.endMs,
2575
+ silent: true
2576
+ });
2577
+ const chunkKey = `${chunk.startMs}:${chunk.endMs}`;
2578
+ for (const plan of plans) {
2579
+ if (!plan.missingChunkKeys.has(chunkKey)) {
2580
+ continue;
2581
+ }
2582
+ const symbolSet = new Set(plan.definition.symbols);
2583
+ const universeCandles = Object.fromEntries(
2584
+ Object.entries(candlesBySymbol).filter(
2585
+ ([symbol]) => symbolSet.has(symbol)
2586
+ )
2587
+ );
2561
2588
  const rows = buildMarketBreadthRows({
2562
- universe,
2589
+ universe: plan.definition.universe,
2563
2590
  interval: interval2,
2564
- candlesBySymbol,
2591
+ candlesBySymbol: universeCandles,
2565
2592
  btcCandles
2566
2593
  }).filter((row) => {
2567
2594
  const ts = row.ts.getTime();
@@ -2570,8 +2597,6 @@ var backfillBinanceMarketContext = async (params, enabled) => {
2570
2597
  await (0, import_timescale.upsertMarketBreadthRows)(rows);
2571
2598
  breadthRows += rows.length;
2572
2599
  }
2573
- } else {
2574
- console.log(import_chalk.default.gray(`breadth cached: universe=${universe}`));
2575
2600
  }
2576
2601
  }
2577
2602
  console.log(
@@ -3796,27 +3821,27 @@ var createCoinMarketCapFetchStats = () => ({
3796
3821
  totalCredits: 0,
3797
3822
  byPath: /* @__PURE__ */ new Map()
3798
3823
  });
3799
- var getCoinMarketCapPathLabel = (path) => {
3800
- if (path.includes("global-metrics")) return "global";
3801
- if (path.includes("cryptocurrency/quotes")) return "reference";
3802
- if (path.includes("exchange/quotes")) return "exchangeLiquidity";
3803
- if (path.includes("fear-and-greed")) return "fearGreed";
3804
- if (path.includes("cmc100")) return "cmc100";
3805
- if (path.includes("cmc20")) return "cmc20";
3806
- return path;
3807
- };
3808
- var recordCoinMarketCapFetchStats = (stats, path, creditCount) => {
3824
+ var getCoinMarketCapPathLabel = (path2) => {
3825
+ if (path2.includes("global-metrics")) return "global";
3826
+ if (path2.includes("cryptocurrency/quotes")) return "reference";
3827
+ if (path2.includes("exchange/quotes")) return "exchangeLiquidity";
3828
+ if (path2.includes("fear-and-greed")) return "fearGreed";
3829
+ if (path2.includes("cmc100")) return "cmc100";
3830
+ if (path2.includes("cmc20")) return "cmc20";
3831
+ return path2;
3832
+ };
3833
+ var recordCoinMarketCapFetchStats = (stats, path2, creditCount) => {
3809
3834
  if (!stats) return;
3810
- const existing = stats.byPath.get(path) ?? { requests: 0, credits: 0 };
3835
+ const existing = stats.byPath.get(path2) ?? { requests: 0, credits: 0 };
3811
3836
  existing.requests += 1;
3812
3837
  existing.credits += creditCount ?? 0;
3813
3838
  stats.requests += 1;
3814
3839
  stats.totalCredits += creditCount ?? 0;
3815
- stats.byPath.set(path, existing);
3840
+ stats.byPath.set(path2, existing);
3816
3841
  };
3817
3842
  var formatCoinMarketCapFetchStats = (stats) => {
3818
3843
  const byPath = [...stats.byPath.entries()].map(
3819
- ([path, item]) => `${getCoinMarketCapPathLabel(path)}=${item.requests}req/${item.credits}cr`
3844
+ ([path2, item]) => `${getCoinMarketCapPathLabel(path2)}=${item.requests}req/${item.credits}cr`
3820
3845
  ).join(" ");
3821
3846
  return `apiRequests=${stats.requests} apiCredits=${stats.totalCredits}${byPath ? ` ${byPath}` : ""}`;
3822
3847
  };
@@ -4888,7 +4913,7 @@ var updateBtcReferenceHistory = async ({
4888
4913
  var import_logger = require("@tradejs/infra/logger");
4889
4914
  var import_redis2 = require("@tradejs/infra/redis");
4890
4915
  var import_tradingAccounts = require("@tradejs/infra/tradingAccounts");
4891
- var import_strategies = require("@tradejs/node/strategies");
4916
+ var import_strategies2 = require("@tradejs/node/strategies");
4892
4917
 
4893
4918
  // src/lib/runtimeRedis.ts
4894
4919
  var import_redis = require("@tradejs/infra/redis");
@@ -4988,7 +5013,7 @@ var loadRuntimeStrategies = async ({
4988
5013
  });
4989
5014
  const effectiveAccountId = resolvedAccount?.id ?? requestedAccountId;
4990
5015
  const [strategyCreator, strategyResults] = await Promise.all([
4991
- (0, import_strategies.getStrategyCreator)(strategyName, projectRoot2),
5016
+ (0, import_strategies2.getStrategyCreator)(strategyName, projectRoot2),
4992
5017
  (0, import_redis2.getData)(import_redis2.redisKeys.strategyResults(userName, strategyName), {})
4993
5018
  ]);
4994
5019
  if (!strategyCreator) {
@@ -5082,6 +5107,214 @@ var logStrategySkipStats = (runtimeStrategies, strategyStats) => {
5082
5107
 
5083
5108
  // src/lib/signals/evaluations.ts
5084
5109
  var import_redis3 = require("@tradejs/infra/redis");
5110
+
5111
+ // src/lib/runtimeLineage.ts
5112
+ var import_node_child_process = require("child_process");
5113
+ var import_node_crypto = require("crypto");
5114
+ var import_promises = __toESM(require("fs/promises"));
5115
+ var import_node_path = __toESM(require("path"));
5116
+ var RUNTIME_CONTEXT_ENV_KEYS = [
5117
+ "AI_MODE",
5118
+ "MIN_AI_QUALITY",
5119
+ "INTERVAL",
5120
+ "DERIVATIVES_CONTEXT_ENABLED",
5121
+ "DERIVATIVES_CONTEXT_TARGET_ENABLED",
5122
+ "DERIVATIVES_CONTEXT_LOOKBACK_HOURS",
5123
+ "DERIVATIVES_CONTEXT_EXTRA_REFERENCE_SYMBOLS",
5124
+ "DERIVATIVES_CONTEXT_EXCHANGE_PRIORITY",
5125
+ "COINMARKETCAP_CONTEXT_ENABLED",
5126
+ "COINMARKETCAP_CONTEXT_MAX_AGE_MS",
5127
+ "COINMARKETCAP_CONTEXT_EXCHANGE_LIQUIDITY_ENABLED",
5128
+ "COINMARKETCAP_CONTEXT_EXCHANGE_SLUGS",
5129
+ "COINMARKETCAP_CONTEXT_FEAR_GREED_ENABLED",
5130
+ "COINMARKETCAP_CONTEXT_HISTORICAL_ACCESS_MONTHS",
5131
+ "COINMARKETCAP_CONTEXT_BACKFILL_ENABLED",
5132
+ "COINMARKETCAP_CONTEXT_BACKFILL_MAX_DAYS",
5133
+ "COINMARKETCAP_CONTEXT_BACKFILL_WARMUP_DAYS"
5134
+ ];
5135
+ var RUNTIME_CONTEXT_DATA_MODEL = {
5136
+ derivativesSourceIntervals: "15m",
5137
+ derivativesDerivedIntervals: "1h",
5138
+ derivativesHourlyFallback: "stored-1h",
5139
+ derivativesDataModelVersion: 2,
5140
+ coinMarketCapAvailabilityModel: "closed-interval-all-daily",
5141
+ coinMarketCapDataModelVersion: 3,
5142
+ binanceBreadthUniverseModel: "fixed-versioned-top5-top10-top30-top50-top100",
5143
+ binanceBreadthDataModelVersion: 2
5144
+ };
5145
+ var normalizeForStableJson = (value) => {
5146
+ if (Array.isArray(value)) {
5147
+ return value.map(normalizeForStableJson);
5148
+ }
5149
+ if (value && typeof value === "object") {
5150
+ return Object.fromEntries(
5151
+ Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => [key, normalizeForStableJson(entry)])
5152
+ );
5153
+ }
5154
+ return value;
5155
+ };
5156
+ var fingerprintRuntimeValue = (value) => (0, import_node_crypto.createHash)("sha256").update(JSON.stringify(normalizeForStableJson(value))).digest("hex").slice(0, 16);
5157
+ var gitLineageCache = /* @__PURE__ */ new Map();
5158
+ var gateFingerprintCache = /* @__PURE__ */ new Map();
5159
+ var binanceBreadthFingerprintCache = /* @__PURE__ */ new Map();
5160
+ var resolveGitLineage = (projectRoot2) => {
5161
+ const cached = gitLineageCache.get(projectRoot2);
5162
+ if (cached) {
5163
+ return cached;
5164
+ }
5165
+ const envSha = String(process.env.TRADEJS_GIT_SHA ?? "").trim();
5166
+ if (envSha && envSha !== "unknown") {
5167
+ const result = {
5168
+ gitSha: envSha,
5169
+ gitDirty: false
5170
+ };
5171
+ gitLineageCache.set(projectRoot2, result);
5172
+ return result;
5173
+ }
5174
+ try {
5175
+ const gitSha = (0, import_node_child_process.execFileSync)("git", ["rev-parse", "HEAD"], {
5176
+ cwd: projectRoot2,
5177
+ encoding: "utf8",
5178
+ stdio: ["ignore", "pipe", "ignore"]
5179
+ }).trim();
5180
+ const status = (0, import_node_child_process.execFileSync)(
5181
+ "git",
5182
+ ["status", "--porcelain", "--untracked-files=normal"],
5183
+ {
5184
+ cwd: projectRoot2,
5185
+ encoding: "utf8",
5186
+ stdio: ["ignore", "pipe", "ignore"]
5187
+ }
5188
+ ).trim();
5189
+ const result = {
5190
+ gitSha: gitSha || null,
5191
+ gitDirty: status.length > 0
5192
+ };
5193
+ gitLineageCache.set(projectRoot2, result);
5194
+ return result;
5195
+ } catch {
5196
+ const result = {
5197
+ gitSha: null,
5198
+ gitDirty: null
5199
+ };
5200
+ gitLineageCache.set(projectRoot2, result);
5201
+ return result;
5202
+ }
5203
+ };
5204
+ var readOptionalFile = async (filePath) => {
5205
+ try {
5206
+ return await import_promises.default.readFile(filePath);
5207
+ } catch {
5208
+ return null;
5209
+ }
5210
+ };
5211
+ var resolveBinanceBreadthFingerprint = async (projectRoot2) => {
5212
+ if (binanceBreadthFingerprintCache.has(projectRoot2)) {
5213
+ return binanceBreadthFingerprintCache.get(projectRoot2) ?? null;
5214
+ }
5215
+ const content = await readOptionalFile(
5216
+ import_node_path.default.join(
5217
+ projectRoot2,
5218
+ "packages/node/src/config/binanceBreadthUniverses.json"
5219
+ )
5220
+ );
5221
+ let result = null;
5222
+ if (content) {
5223
+ try {
5224
+ const parsed = JSON.parse(content.toString("utf8"));
5225
+ result = typeof parsed.fingerprint === "string" && parsed.fingerprint.trim() ? parsed.fingerprint.trim() : null;
5226
+ } catch {
5227
+ result = null;
5228
+ }
5229
+ }
5230
+ binanceBreadthFingerprintCache.set(projectRoot2, result);
5231
+ return result;
5232
+ };
5233
+ var resolveGateFingerprint = async ({
5234
+ projectRoot: projectRoot2,
5235
+ strategyName,
5236
+ gitSha
5237
+ }) => {
5238
+ const cacheKey = `${projectRoot2}:${strategyName}:${gitSha ?? "unknown"}`;
5239
+ const cached = gateFingerprintCache.get(cacheKey);
5240
+ if (cached) {
5241
+ return cached;
5242
+ }
5243
+ const relativeCandidates = [
5244
+ `packages/strategies/src/${strategyName}/adapters/ai.ts`,
5245
+ `packages/strategies/src/${strategyName}/guardrails.ts`,
5246
+ `packages/strategies/src/${strategyName}/pockets.ts`,
5247
+ `packages/strategies/src/${strategyName}/config.ts`,
5248
+ "packages/node/src/ai.ts"
5249
+ ];
5250
+ const optionalEntries = await Promise.all(
5251
+ relativeCandidates.map(async (relativePath) => {
5252
+ const content = await readOptionalFile(
5253
+ import_node_path.default.join(projectRoot2, relativePath)
5254
+ );
5255
+ return content == null ? null : { relativePath, content };
5256
+ })
5257
+ );
5258
+ const sourceEntries = [];
5259
+ for (const entry of optionalEntries) {
5260
+ if (entry != null) {
5261
+ sourceEntries.push(entry);
5262
+ }
5263
+ }
5264
+ const hash = (0, import_node_crypto.createHash)("sha256");
5265
+ hash.update(strategyName);
5266
+ if (sourceEntries.length) {
5267
+ for (const entry of sourceEntries) {
5268
+ hash.update(entry.relativePath);
5269
+ hash.update(entry.content);
5270
+ }
5271
+ } else {
5272
+ hash.update(gitSha ?? "unknown-git-sha");
5273
+ }
5274
+ const fingerprint = hash.digest("hex").slice(0, 16);
5275
+ gateFingerprintCache.set(cacheKey, fingerprint);
5276
+ return fingerprint;
5277
+ };
5278
+ var buildRuntimeLineage = async ({
5279
+ projectRoot: projectRoot2,
5280
+ strategyName,
5281
+ config,
5282
+ runContext,
5283
+ env = process.env
5284
+ }) => {
5285
+ const git = resolveGitLineage(projectRoot2);
5286
+ const context = {
5287
+ ...Object.fromEntries(
5288
+ RUNTIME_CONTEXT_ENV_KEYS.map((key) => [key, env[key] ?? null])
5289
+ ),
5290
+ ...RUNTIME_CONTEXT_DATA_MODEL,
5291
+ binanceBreadthUniverseFingerprint: await resolveBinanceBreadthFingerprint(projectRoot2),
5292
+ ...runContext
5293
+ };
5294
+ return {
5295
+ schemaVersion: 1,
5296
+ ...git,
5297
+ gateFingerprint: await resolveGateFingerprint({
5298
+ projectRoot: projectRoot2,
5299
+ strategyName,
5300
+ gitSha: git.gitSha
5301
+ }),
5302
+ configFingerprint: fingerprintRuntimeValue(config),
5303
+ contextFingerprint: fingerprintRuntimeValue(context)
5304
+ };
5305
+ };
5306
+ var runtimeLineageKey = (lineage) => [
5307
+ lineage.schemaVersion,
5308
+ lineage.gitSha ?? "unknown",
5309
+ lineage.gitDirty == null ? "unknown" : lineage.gitDirty ? "dirty" : "clean",
5310
+ lineage.gateFingerprint,
5311
+ lineage.configFingerprint,
5312
+ lineage.contextFingerprint
5313
+ ].join(":");
5314
+
5315
+ // src/lib/signals/evaluations.ts
5316
+ var runtimeLineageScopeCache = /* @__PURE__ */ new Map();
5317
+ var runtimeLineageScopeCacheDayKey = "";
5085
5318
  var buildRuntimeSignalEvaluationId = ({
5086
5319
  strategyName,
5087
5320
  symbol,
@@ -5090,7 +5323,46 @@ var buildRuntimeSignalEvaluationId = ({
5090
5323
  }) => runtimeConfigId && runtimeConfigId !== "config" ? [strategyName, runtimeConfigId, symbol, timestamp].join(":") : [strategyName, symbol, timestamp].join(":");
5091
5324
  var saveRuntimeSignalEvaluation = async (evaluation) => {
5092
5325
  const dayKey = (0, import_time.getRuntimeStorageDayKey)(evaluation.timestamp);
5326
+ if (runtimeLineageScopeCacheDayKey !== dayKey) {
5327
+ runtimeLineageScopeCache.clear();
5328
+ runtimeLineageScopeCacheDayKey = dayKey;
5329
+ }
5093
5330
  const runtimeSignalRetentionTtl = getRuntimeSignalRetentionTtlSeconds();
5331
+ if (evaluation.runtimeLineage) {
5332
+ const lineageField = [
5333
+ evaluation.strategy,
5334
+ evaluation.runtimeConfigId ?? "config",
5335
+ evaluation.symbol,
5336
+ runtimeLineageKey(evaluation.runtimeLineage)
5337
+ ].join(":");
5338
+ const lineageBucket = import_redis3.redisKeys.runtimeLineageScopeBucket(
5339
+ evaluation.userName,
5340
+ dayKey
5341
+ );
5342
+ const cacheKey = `${lineageBucket}:${lineageField}`;
5343
+ const existing = runtimeLineageScopeCache.get(cacheKey) ?? await (0, import_redis3.getHashJsonField)(
5344
+ lineageBucket,
5345
+ lineageField
5346
+ );
5347
+ const scope = {
5348
+ strategy: evaluation.strategy,
5349
+ symbol: evaluation.symbol,
5350
+ runtimeConfigId: evaluation.runtimeConfigId,
5351
+ lineage: evaluation.runtimeLineage,
5352
+ firstTimestamp: Math.min(
5353
+ existing?.firstTimestamp ?? evaluation.timestamp,
5354
+ evaluation.timestamp
5355
+ ),
5356
+ lastTimestamp: Math.max(
5357
+ existing?.lastTimestamp ?? evaluation.timestamp,
5358
+ evaluation.timestamp
5359
+ )
5360
+ };
5361
+ runtimeLineageScopeCache.set(cacheKey, scope);
5362
+ await (0, import_redis3.setHashJsonField)(lineageBucket, lineageField, scope, {
5363
+ expire: RUNTIME_LINEAGE_SCOPE_RETENTION_TTL_SECONDS
5364
+ });
5365
+ }
5094
5366
  if (shouldStoreDetailedRuntimeSignalEvaluation(evaluation)) {
5095
5367
  await (0, import_redis3.setHashJsonField)(
5096
5368
  import_redis3.redisKeys.runtimeSignalEvaluationBucket(
@@ -5550,7 +5822,7 @@ var resolveSignalsKlineWsWaitMs = () => resolveNonNegativeInteger(process.env.SI
5550
5822
  var createDefaultSignalsLifecycle = (runtimeInterval = interval) => createSignalsStrategyLifecycle({
5551
5823
  intervalMs: Number(runtimeInterval) * 6e4,
5552
5824
  maxLiveBars: resolveSignalsDaemonMaxLiveBars(runtimeInterval),
5553
- releaseState: import_strategies2.releaseStrategyReplayCache
5825
+ releaseState: import_strategies3.releaseStrategyReplayCache
5554
5826
  });
5555
5827
  var timeOperation2 = (label, operation) => timeOperation(
5556
5828
  label,
@@ -5695,6 +5967,20 @@ var findSignals = async (symbol, connectorName, connector, btcBinanceData, btcCo
5695
5967
  interval: interval2,
5696
5968
  makeOrders: flags.makeOrders
5697
5969
  });
5970
+ const runtimeLineage = await buildRuntimeLineage({
5971
+ projectRoot,
5972
+ strategyName,
5973
+ config: {
5974
+ configId,
5975
+ strategyConfig,
5976
+ symbolResultConfig: strategyResults?.[symbol]?.config ?? null
5977
+ },
5978
+ runContext: {
5979
+ connectorName: connectorName.toLowerCase(),
5980
+ interval: String(interval2),
5981
+ universe
5982
+ }
5983
+ });
5698
5984
  const lifecycleKey = buildSignalsStrategyLifecycleKey({
5699
5985
  connectorName,
5700
5986
  universe: runtimeScopeUniverse,
@@ -5776,6 +6062,7 @@ var findSignals = async (symbol, connectorName, connector, btcBinanceData, btcCo
5776
6062
  userName: flags.user,
5777
6063
  strategy: strategyName,
5778
6064
  runtimeConfigId: configId,
6065
+ runtimeLineage,
5779
6066
  symbol,
5780
6067
  interval: interval2,
5781
6068
  universe,
@@ -5794,10 +6081,11 @@ var findSignals = async (symbol, connectorName, connector, btcBinanceData, btcCo
5794
6081
  stats.signals += 1;
5795
6082
  }
5796
6083
  signal.runtimeConfigId = configId;
6084
+ signal.runtimeLineage = runtimeLineage;
5797
6085
  if (configId && configId !== "config" && !signal.signalId.endsWith(`:${configId}`)) {
5798
6086
  signal.signalId = `${signal.signalId}:${configId}`;
5799
6087
  }
5800
- await (0, import_strategies3.enrichSignalWithBinanceMarketContext)({
6088
+ await (0, import_strategies4.enrichSignalWithBinanceMarketContext)({
5801
6089
  signal,
5802
6090
  env: "CRON"
5803
6091
  });
@@ -5840,6 +6128,7 @@ var findSignals = async (symbol, connectorName, connector, btcBinanceData, btcCo
5840
6128
  userName: flags.user,
5841
6129
  strategy: strategyName,
5842
6130
  runtimeConfigId: configId,
6131
+ runtimeLineage,
5843
6132
  symbol,
5844
6133
  interval: interval2,
5845
6134
  universe,