@tradejs/cli 2.0.13 → 2.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1093,7 +1093,7 @@ var require_common = __commonJS({
1093
1093
  let enableOverride = null;
1094
1094
  let namespacesCache;
1095
1095
  let enabledCache;
1096
- function debug(...args31) {
1096
+ function debug(...args33) {
1097
1097
  if (!debug.enabled) {
1098
1098
  return;
1099
1099
  }
@@ -1104,28 +1104,28 @@ var require_common = __commonJS({
1104
1104
  self.prev = prevTime;
1105
1105
  self.curr = curr;
1106
1106
  prevTime = curr;
1107
- args31[0] = createDebug.coerce(args31[0]);
1108
- if (typeof args31[0] !== "string") {
1109
- args31.unshift("%O");
1107
+ args33[0] = createDebug.coerce(args33[0]);
1108
+ if (typeof args33[0] !== "string") {
1109
+ args33.unshift("%O");
1110
1110
  }
1111
1111
  let index = 0;
1112
- args31[0] = args31[0].replace(/%([a-zA-Z%])/g, (match, format2) => {
1112
+ args33[0] = args33[0].replace(/%([a-zA-Z%])/g, (match, format2) => {
1113
1113
  if (match === "%%") {
1114
1114
  return "%";
1115
1115
  }
1116
1116
  index++;
1117
1117
  const formatter = createDebug.formatters[format2];
1118
1118
  if (typeof formatter === "function") {
1119
- const val = args31[index];
1119
+ const val = args33[index];
1120
1120
  match = formatter.call(self, val);
1121
- args31.splice(index, 1);
1121
+ args33.splice(index, 1);
1122
1122
  index--;
1123
1123
  }
1124
1124
  return match;
1125
1125
  });
1126
- createDebug.formatArgs.call(self, args31);
1126
+ createDebug.formatArgs.call(self, args33);
1127
1127
  const logFn = self.log || createDebug.log;
1128
- logFn.apply(self, args31);
1128
+ logFn.apply(self, args33);
1129
1129
  }
1130
1130
  debug.namespace = namespace;
1131
1131
  debug.useColors = createDebug.useColors();
@@ -1348,16 +1348,16 @@ var require_browser = __commonJS({
1348
1348
  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
1349
1349
  typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/);
1350
1350
  }
1351
- function formatArgs(args31) {
1352
- args31[0] = (this.useColors ? "%c" : "") + this.namespace + (this.useColors ? " %c" : " ") + args31[0] + (this.useColors ? "%c " : " ") + "+" + module2.exports.humanize(this.diff);
1351
+ function formatArgs(args33) {
1352
+ args33[0] = (this.useColors ? "%c" : "") + this.namespace + (this.useColors ? " %c" : " ") + args33[0] + (this.useColors ? "%c " : " ") + "+" + module2.exports.humanize(this.diff);
1353
1353
  if (!this.useColors) {
1354
1354
  return;
1355
1355
  }
1356
1356
  const c = "color: " + this.color;
1357
- args31.splice(1, 0, c, "color: inherit");
1357
+ args33.splice(1, 0, c, "color: inherit");
1358
1358
  let index = 0;
1359
1359
  let lastC = 0;
1360
- args31[0].replace(/%[a-zA-Z%]/g, (match) => {
1360
+ args33[0].replace(/%[a-zA-Z%]/g, (match) => {
1361
1361
  if (match === "%%") {
1362
1362
  return;
1363
1363
  }
@@ -1366,7 +1366,7 @@ var require_browser = __commonJS({
1366
1366
  lastC = index;
1367
1367
  }
1368
1368
  });
1369
- args31.splice(lastC, 0, c);
1369
+ args33.splice(lastC, 0, c);
1370
1370
  }
1371
1371
  exports2.log = console.debug || console.log || (() => {
1372
1372
  });
@@ -1648,16 +1648,16 @@ var require_node = __commonJS({
1648
1648
  function useColors() {
1649
1649
  return "colors" in exports2.inspectOpts ? Boolean(exports2.inspectOpts.colors) : tty.isatty(process.stderr.fd);
1650
1650
  }
1651
- function formatArgs(args31) {
1651
+ function formatArgs(args33) {
1652
1652
  const { namespace: name, useColors: useColors2 } = this;
1653
1653
  if (useColors2) {
1654
1654
  const c = this.color;
1655
1655
  const colorCode = "\x1B[3" + (c < 8 ? c : "8;5;" + c);
1656
1656
  const prefix = ` ${colorCode};1m${name} \x1B[0m`;
1657
- args31[0] = prefix + args31[0].split("\n").join("\n" + prefix);
1658
- args31.push(colorCode + "m+" + module2.exports.humanize(this.diff) + "\x1B[0m");
1657
+ args33[0] = prefix + args33[0].split("\n").join("\n" + prefix);
1658
+ args33.push(colorCode + "m+" + module2.exports.humanize(this.diff) + "\x1B[0m");
1659
1659
  } else {
1660
- args31[0] = getDate() + name + " " + args31[0];
1660
+ args33[0] = getDate() + name + " " + args33[0];
1661
1661
  }
1662
1662
  }
1663
1663
  function getDate() {
@@ -1666,8 +1666,8 @@ var require_node = __commonJS({
1666
1666
  }
1667
1667
  return (/* @__PURE__ */ new Date()).toISOString() + " ";
1668
1668
  }
1669
- function log(...args31) {
1670
- return process.stderr.write(util.formatWithOptions(exports2.inspectOpts, ...args31) + "\n");
1669
+ function log(...args33) {
1670
+ return process.stderr.write(util.formatWithOptions(exports2.inspectOpts, ...args33) + "\n");
1671
1671
  }
1672
1672
  function save(namespaces) {
1673
1673
  if (namespaces) {
@@ -6432,7 +6432,7 @@ var init_aiTrainEvaluationDump = __esm({
6432
6432
  });
6433
6433
 
6434
6434
  // src/lib/aiTrainResearch.ts
6435
- var import_node_child_process2, import_node_crypto2, import_promises5, import_node_path3, DAY_MS3, RESEARCH_CONTEXT_ENV_KEYS, DERIVATIVES_CONTEXT_DATA_MODEL, normalizeForStableJson, fingerprintResearchValue, getTimestampRange, summarizeAiTrainCoverage, summarizeAiTrainRejectReasons, summarizeAiTrainTerminalWindows, readOptionalFile, getGitLineage, resolveGateFingerprint, buildAiTrainLineage;
6435
+ var import_node_child_process2, import_node_crypto2, import_promises5, import_node_path3, import_strategies2, import_types, DAY_MS3, RESEARCH_CONTEXT_ENV_KEYS, DERIVATIVES_CONTEXT_DATA_MODEL, normalizeForStableJson, fingerprintResearchValue, getTimestampRange, summarizeAiTrainCoverage, summarizeAiTrainRejectReasons, summarizeAiTrainTerminalWindows, readOptionalFile, getGitLineage, resolveGateFingerprint, buildAiTrainLineage;
6436
6436
  var init_aiTrainResearch = __esm({
6437
6437
  "src/lib/aiTrainResearch.ts"() {
6438
6438
  "use strict";
@@ -6441,6 +6441,8 @@ var init_aiTrainResearch = __esm({
6441
6441
  import_promises5 = __toESM(require("fs/promises"));
6442
6442
  import_node_path3 = __toESM(require("path"));
6443
6443
  init_aiTrainMetrics();
6444
+ import_strategies2 = require("@tradejs/node/strategies");
6445
+ import_types = require("@tradejs/types");
6444
6446
  DAY_MS3 = 24 * 60 * 60 * 1e3;
6445
6447
  RESEARCH_CONTEXT_ENV_KEYS = [
6446
6448
  "AI_MODE",
@@ -6459,13 +6461,22 @@ var init_aiTrainResearch = __esm({
6459
6461
  "COINMARKETCAP_CONTEXT_HISTORICAL_ACCESS_MONTHS",
6460
6462
  "COINMARKETCAP_CONTEXT_BACKFILL_ENABLED",
6461
6463
  "COINMARKETCAP_CONTEXT_BACKFILL_MAX_DAYS",
6462
- "COINMARKETCAP_CONTEXT_BACKFILL_WARMUP_DAYS"
6464
+ "COINMARKETCAP_CONTEXT_BACKFILL_WARMUP_DAYS",
6465
+ "HYPERLIQUID_WHALE_CONTEXT_ENABLED",
6466
+ "HYPERLIQUID_WHALE_BACKFILL_ENABLED",
6467
+ "HYPERLIQUID_WHALE_MIN_COVERAGE_PCT",
6468
+ "HYPERLIQUID_WHALE_CONCURRENCY",
6469
+ "HYPERLIQUID_WHALE_RATE_LIMIT_WEIGHT"
6463
6470
  ];
6464
6471
  DERIVATIVES_CONTEXT_DATA_MODEL = {
6465
6472
  derivativesSourceIntervals: "15m",
6466
6473
  derivativesDerivedIntervals: "1h",
6467
6474
  derivativesHourlyFallback: "stored-1h",
6468
- derivativesDataModelVersion: 2
6475
+ derivativesDataModelVersion: 2,
6476
+ hyperliquidWhaleCanonicalInterval: "1m",
6477
+ hyperliquidWhaleGateMinNotionalUsd: 5e4,
6478
+ hyperliquidWhaleMinCoveragePct: 0.8,
6479
+ hyperliquidWhaleDataModelVersion: import_types.HYPERLIQUID_WHALE_DATA_MODEL_VERSION
6469
6480
  };
6470
6481
  normalizeForStableJson = (value) => {
6471
6482
  if (Array.isArray(value)) {
@@ -6622,12 +6633,16 @@ var init_aiTrainResearch = __esm({
6622
6633
  const { gitSha, gitDirty } = getGitLineage(projectRoot14);
6623
6634
  const gate = await resolveGateFingerprint(projectRoot14, strategyName, gitSha);
6624
6635
  const normalizedConfigIds = [...new Set(configIds.filter(Boolean))].sort();
6636
+ const hyperliquidPerpUniverseFingerprint = typeof import_strategies2.getHyperliquidPerpUniverseSnapshot === "function" ? (0, import_strategies2.getHyperliquidPerpUniverseSnapshot)().fingerprint : null;
6637
+ const hyperliquidWhaleRegistryFingerprint = typeof import_strategies2.getHyperliquidWhaleRegistrySnapshot === "function" ? (0, import_strategies2.getHyperliquidWhaleRegistrySnapshot)().fingerprint : null;
6625
6638
  const context = {
6626
6639
  ...Object.fromEntries(
6627
6640
  RESEARCH_CONTEXT_ENV_KEYS.map((key) => [key, env[key] ?? null])
6628
6641
  ),
6629
6642
  ...runContext,
6630
- ...DERIVATIVES_CONTEXT_DATA_MODEL
6643
+ ...DERIVATIVES_CONTEXT_DATA_MODEL,
6644
+ hyperliquidPerpUniverseFingerprint,
6645
+ hyperliquidWhaleRegistryFingerprint
6631
6646
  };
6632
6647
  return {
6633
6648
  gitSha,
@@ -8439,7 +8454,7 @@ var init_binanceMarketData = __esm({
8439
8454
  });
8440
8455
 
8441
8456
  // src/lib/binanceMarketContextBackfill.ts
8442
- var import_progress4, import_chalk6, import_connectors, import_connectors2, import_strategies2, import_timescale, DAY_MS5, sleep, asInt, asFloat, parseEnabledFlag, uniqueSymbols, getReferenceSymbols, intervalToMarketFeatureInterval, marketIntervalToConnectorInterval, resolveBinanceMarketContextBackfillWindow, hasCoverage, buildBreadthBackfillChunks, buildTradeFlowBackfillChunks, filterMissingBreadthBackfillChunks, fetchAggTradesForWindow, getBinanceConnector, shouldBackfillBinanceMarketContextForBacktest, shouldBackfillBinanceMarketContextForSignals, shouldBackfillBinanceMarketContextForReplay, skippedBackfillResult, backfillBinanceMarketContext, backfillBinanceMarketContextForBacktest, backfillBinanceMarketContextForSignals, backfillBinanceMarketContextForReplay;
8457
+ var import_progress4, import_chalk6, import_connectors, import_connectors2, import_strategies3, import_timescale, DAY_MS5, sleep, asInt, asFloat, parseEnabledFlag, uniqueSymbols, getReferenceSymbols, intervalToMarketFeatureInterval, marketIntervalToConnectorInterval, resolveBinanceMarketContextBackfillWindow, hasCoverage, buildBreadthBackfillChunks, buildTradeFlowBackfillChunks, filterMissingBreadthBackfillChunks, fetchAggTradesForWindow, getBinanceConnector, shouldBackfillBinanceMarketContextForBacktest, shouldBackfillBinanceMarketContextForSignals, shouldBackfillBinanceMarketContextForReplay, skippedBackfillResult, backfillBinanceMarketContext, backfillBinanceMarketContextForBacktest, backfillBinanceMarketContextForSignals, backfillBinanceMarketContextForReplay;
8443
8458
  var init_binanceMarketContextBackfill = __esm({
8444
8459
  "src/lib/binanceMarketContextBackfill.ts"() {
8445
8460
  "use strict";
@@ -8447,7 +8462,7 @@ var init_binanceMarketContextBackfill = __esm({
8447
8462
  import_chalk6 = __toESM(require("chalk"));
8448
8463
  import_connectors = require("@tradejs/connectors");
8449
8464
  import_connectors2 = require("@tradejs/node/connectors");
8450
- import_strategies2 = require("@tradejs/node/strategies");
8465
+ import_strategies3 = require("@tradejs/node/strategies");
8451
8466
  import_timescale = require("@tradejs/infra/timescale");
8452
8467
  init_binanceMarketData();
8453
8468
  DAY_MS5 = 864e5;
@@ -8799,7 +8814,7 @@ var init_binanceMarketContextBackfill = __esm({
8799
8814
  }
8800
8815
  }
8801
8816
  if (includeBreadth) {
8802
- const breadthUniverses = (0, import_strategies2.getBinanceBreadthUniverses)();
8817
+ const breadthUniverses = (0, import_strategies3.getBinanceBreadthUniverses)();
8803
8818
  const breadthSymbols = breadthUniverses.at(-1)?.symbols ?? [];
8804
8819
  const connectorInterval = marketIntervalToConnectorInterval(interval5);
8805
8820
  const chunks = buildBreadthBackfillChunks({
@@ -10985,8 +11000,432 @@ var init_coinMarketCapContextBackfill = __esm({
10985
11000
  }
10986
11001
  });
10987
11002
 
11003
+ // src/lib/hyperliquidWhaleData.ts
11004
+ var HYPERLIQUID_WHALE_BUCKET_MS, finiteNumber, POSITION_EPSILON, normalizePosition, classifyHyperliquidPositionChange, normalizeAddress, normalizeTradeFields, normalizeHyperliquidUserFill;
11005
+ var init_hyperliquidWhaleData = __esm({
11006
+ "src/lib/hyperliquidWhaleData.ts"() {
11007
+ "use strict";
11008
+ HYPERLIQUID_WHALE_BUCKET_MS = 6e4;
11009
+ finiteNumber = (value) => {
11010
+ const numeric = typeof value === "number" ? value : Number(value);
11011
+ return Number.isFinite(numeric) ? numeric : null;
11012
+ };
11013
+ POSITION_EPSILON = 1e-12;
11014
+ normalizePosition = (value) => Math.abs(value) <= POSITION_EPSILON ? 0 : value;
11015
+ classifyHyperliquidPositionChange = (params) => {
11016
+ const startValue = finiteNumber(params.startPosition);
11017
+ const size = finiteNumber(params.size);
11018
+ const side = String(params.side ?? "").toUpperCase();
11019
+ if (startValue == null || size == null || size <= 0) return null;
11020
+ if (side !== "B" && side !== "A") return null;
11021
+ const startPosition = normalizePosition(startValue);
11022
+ const endPosition = normalizePosition(
11023
+ startPosition + (side === "B" ? size : -size)
11024
+ );
11025
+ const startLong = Math.max(startPosition, 0);
11026
+ const endLong = Math.max(endPosition, 0);
11027
+ const startShort = Math.max(-startPosition, 0);
11028
+ const endShort = Math.max(-endPosition, 0);
11029
+ const longEntrySize = Math.max(0, endLong - startLong);
11030
+ const shortEntrySize = Math.max(0, endShort - startShort);
11031
+ const longExitSize = Math.max(0, startLong - endLong);
11032
+ const shortExitSize = Math.max(0, startShort - endShort);
11033
+ const flipped = startPosition * endPosition < 0;
11034
+ const opened = startPosition === 0;
11035
+ const closed = endPosition === 0;
11036
+ const increased = Math.sign(startPosition) === Math.sign(endPosition) && Math.abs(endPosition) > Math.abs(startPosition);
11037
+ return {
11038
+ startPosition,
11039
+ endPosition,
11040
+ action: flipped ? "flip" : opened ? "open" : closed ? "close" : increased ? "increase" : "reduce",
11041
+ longEntrySize,
11042
+ shortEntrySize,
11043
+ longExitSize,
11044
+ shortExitSize
11045
+ };
11046
+ };
11047
+ normalizeAddress = (value) => typeof value === "string" && /^0x[0-9a-fA-F]{40}$/.test(value) ? value.toLowerCase() : null;
11048
+ normalizeTradeFields = (value) => {
11049
+ const symbol = typeof value.coin === "string" ? value.coin.trim() : "";
11050
+ const price = finiteNumber(value.px);
11051
+ const size = finiteNumber(value.sz);
11052
+ const timestamp = finiteNumber(value.time);
11053
+ const tid = typeof value.tid === "string" || typeof value.tid === "number" ? String(value.tid) : "";
11054
+ if (!symbol || price == null || size == null || timestamp == null || !tid || price <= 0 || size <= 0) {
11055
+ return null;
11056
+ }
11057
+ return {
11058
+ symbol,
11059
+ price,
11060
+ size,
11061
+ timestamp,
11062
+ tid,
11063
+ notionalUsd: price * size
11064
+ };
11065
+ };
11066
+ normalizeHyperliquidUserFill = (params) => {
11067
+ const normalized = normalizeTradeFields(params.fill);
11068
+ if (!normalized || !params.trackedSymbols.has(normalized.symbol)) return null;
11069
+ const address = normalizeAddress(params.address);
11070
+ if (!address) return null;
11071
+ const side = String(params.fill.side ?? "").toUpperCase();
11072
+ if (side !== "B" && side !== "A") return null;
11073
+ const positionChange = classifyHyperliquidPositionChange({
11074
+ startPosition: params.fill.startPosition,
11075
+ side,
11076
+ size: normalized.size
11077
+ });
11078
+ const closedPnl = finiteNumber(params.fill.closedPnl);
11079
+ const liquidation = params.fill.liquidation != null && typeof params.fill.liquidation === "object";
11080
+ const positionFields = positionChange ? side === "B" ? {
11081
+ buyerStartPosition: positionChange.startPosition,
11082
+ buyerEndPosition: positionChange.endPosition,
11083
+ buyerPositionAction: positionChange.action,
11084
+ buyerClosedPnl: closedPnl,
11085
+ buyerLiquidation: liquidation
11086
+ } : {
11087
+ sellerStartPosition: positionChange.startPosition,
11088
+ sellerEndPosition: positionChange.endPosition,
11089
+ sellerPositionAction: positionChange.action,
11090
+ sellerClosedPnl: closedPnl,
11091
+ sellerLiquidation: liquidation
11092
+ } : {};
11093
+ return {
11094
+ symbol: normalized.symbol,
11095
+ ts: new Date(normalized.timestamp),
11096
+ tid: normalized.tid,
11097
+ price: normalized.price,
11098
+ size: normalized.size,
11099
+ notionalUsd: normalized.notionalUsd,
11100
+ buyerAddress: side === "B" ? address : null,
11101
+ sellerAddress: side === "A" ? address : null,
11102
+ buyerTracked: side === "B",
11103
+ sellerTracked: side === "A",
11104
+ ...positionFields,
11105
+ ...params.identity,
11106
+ source: "hyperliquid_user_fills"
11107
+ };
11108
+ };
11109
+ }
11110
+ });
11111
+
11112
+ // src/lib/hyperliquidRateLimiter.ts
11113
+ var DEFAULT_WEIGHT_PER_MINUTE, WINDOW_MS, sleep2, HyperliquidInfoRateLimiter, hyperliquidInfoResponseWeight;
11114
+ var init_hyperliquidRateLimiter = __esm({
11115
+ "src/lib/hyperliquidRateLimiter.ts"() {
11116
+ "use strict";
11117
+ DEFAULT_WEIGHT_PER_MINUTE = 1e3;
11118
+ WINDOW_MS = 6e4;
11119
+ sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
11120
+ HyperliquidInfoRateLimiter = class {
11121
+ constructor(capacity = DEFAULT_WEIGHT_PER_MINUTE, now2 = Date.now, wait = sleep2) {
11122
+ this.capacity = capacity;
11123
+ this.now = now2;
11124
+ this.wait = wait;
11125
+ this.reservations = [];
11126
+ this.queue = Promise.resolve();
11127
+ }
11128
+ reserve(weight) {
11129
+ const normalizedWeight = Math.max(1, Math.ceil(weight));
11130
+ const pending = this.queue.then(
11131
+ () => this.reserveInternal(normalizedWeight)
11132
+ );
11133
+ this.queue = pending.catch(() => void 0);
11134
+ return pending;
11135
+ }
11136
+ async reserveInternal(weight) {
11137
+ if (weight > this.capacity) {
11138
+ throw new Error(
11139
+ `Hyperliquid request weight ${weight} exceeds limiter capacity ${this.capacity}`
11140
+ );
11141
+ }
11142
+ for (; ; ) {
11143
+ const nowMs = this.now();
11144
+ this.reservations = this.reservations.filter(
11145
+ (reservation) => reservation.atMs > nowMs - WINDOW_MS
11146
+ );
11147
+ const used = this.reservations.reduce(
11148
+ (sum2, reservation) => sum2 + reservation.weight,
11149
+ 0
11150
+ );
11151
+ if (used + weight <= this.capacity) {
11152
+ this.reservations.push({ atMs: nowMs, weight });
11153
+ return;
11154
+ }
11155
+ const oldest = this.reservations[0];
11156
+ await this.wait(Math.max(1, oldest.atMs + WINDOW_MS - nowMs));
11157
+ }
11158
+ }
11159
+ };
11160
+ hyperliquidInfoResponseWeight = (rows) => Math.max(0, Math.ceil(Math.max(0, rows) / 20));
11161
+ }
11162
+ });
11163
+
11164
+ // src/lib/hyperliquidWhaleBackfill.ts
11165
+ var import_timescale4, import_strategies4, DEFAULT_BASE_URL, DEFAULT_CONCURRENCY, DEFAULT_RATE_LIMIT_WEIGHT, MAX_PAGE_SIZE, MAX_PAGES_PER_WHALE, REQUEST_WEIGHT, sleep3, positiveInt, minuteCeil, nextMinute, fetchHyperliquidUserFillsByTime, runConcurrent, backfillHyperliquidWhaleContext;
11166
+ var init_hyperliquidWhaleBackfill = __esm({
11167
+ "src/lib/hyperliquidWhaleBackfill.ts"() {
11168
+ "use strict";
11169
+ import_timescale4 = require("@tradejs/infra/timescale");
11170
+ import_strategies4 = require("@tradejs/node/strategies");
11171
+ init_hyperliquidWhaleData();
11172
+ init_hyperliquidRateLimiter();
11173
+ DEFAULT_BASE_URL = "https://api.hyperliquid.xyz";
11174
+ DEFAULT_CONCURRENCY = 2;
11175
+ DEFAULT_RATE_LIMIT_WEIGHT = 1e3;
11176
+ MAX_PAGE_SIZE = 2e3;
11177
+ MAX_PAGES_PER_WHALE = 5;
11178
+ REQUEST_WEIGHT = 20;
11179
+ sleep3 = (ms) => new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
11180
+ positiveInt = (value, fallback) => {
11181
+ const parsed = Number.parseInt(String(value ?? ""), 10);
11182
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
11183
+ };
11184
+ minuteCeil = (value) => Math.ceil(value / HYPERLIQUID_WHALE_BUCKET_MS) * HYPERLIQUID_WHALE_BUCKET_MS;
11185
+ nextMinute = (value) => (Math.floor(value / HYPERLIQUID_WHALE_BUCKET_MS) + 1) * HYPERLIQUID_WHALE_BUCKET_MS;
11186
+ fetchHyperliquidUserFillsByTime = async (params) => {
11187
+ const fetchImpl = params.fetchImpl ?? fetch;
11188
+ const limiter = params.rateLimiter ?? new HyperliquidInfoRateLimiter(
11189
+ positiveInt(
11190
+ process.env.HYPERLIQUID_WHALE_RATE_LIMIT_WEIGHT,
11191
+ DEFAULT_RATE_LIMIT_WEIGHT
11192
+ ),
11193
+ Date.now,
11194
+ params.wait ?? sleep3
11195
+ );
11196
+ const fills = [];
11197
+ let cursor = params.startTime;
11198
+ for (let page = 0; page < (params.maxPages ?? MAX_PAGES_PER_WHALE) && cursor <= params.endTime; page += 1) {
11199
+ await limiter.reserve(REQUEST_WEIGHT);
11200
+ const response = await fetchImpl(
11201
+ `${process.env.HYPERLIQUID_API_BASE_URL?.trim() || DEFAULT_BASE_URL}/info`,
11202
+ {
11203
+ method: "POST",
11204
+ headers: { "content-type": "application/json" },
11205
+ body: JSON.stringify({
11206
+ type: "userFillsByTime",
11207
+ user: params.address,
11208
+ startTime: cursor,
11209
+ endTime: params.endTime,
11210
+ aggregateByTime: true
11211
+ })
11212
+ }
11213
+ );
11214
+ if (!response.ok) {
11215
+ throw new Error(
11216
+ `Hyperliquid userFillsByTime ${response.status}: ${await response.text()}`
11217
+ );
11218
+ }
11219
+ const raw = await response.json();
11220
+ const pageRows = Array.isArray(raw) ? raw : [];
11221
+ const responseWeight = hyperliquidInfoResponseWeight(pageRows.length);
11222
+ if (responseWeight > 0) await limiter.reserve(responseWeight);
11223
+ fills.push(...pageRows);
11224
+ if (pageRows.length < MAX_PAGE_SIZE) {
11225
+ return { fills, truncated: false, coveredFromMs: params.startTime };
11226
+ }
11227
+ const lastTime = Math.max(
11228
+ ...pageRows.map((row) => Number(row.time)).filter(Number.isFinite)
11229
+ );
11230
+ if (!Number.isFinite(lastTime) || lastTime < cursor) {
11231
+ throw new Error(
11232
+ `Hyperliquid fills pagination stalled for ${params.address}`
11233
+ );
11234
+ }
11235
+ cursor = lastTime + 1;
11236
+ }
11237
+ if (cursor > params.endTime) {
11238
+ return { fills, truncated: false, coveredFromMs: params.startTime };
11239
+ }
11240
+ const firstAvailableTime = Math.min(
11241
+ ...fills.map((fill) => Number(fill.time)).filter(Number.isFinite)
11242
+ );
11243
+ return {
11244
+ fills,
11245
+ truncated: true,
11246
+ coveredFromMs: Math.min(params.endTime + 1, nextMinute(firstAvailableTime))
11247
+ };
11248
+ };
11249
+ runConcurrent = async (params) => {
11250
+ let nextIndex = 0;
11251
+ await Promise.all(
11252
+ Array.from(
11253
+ { length: Math.min(params.concurrency, params.items.length) },
11254
+ async () => {
11255
+ for (; ; ) {
11256
+ const index = nextIndex;
11257
+ nextIndex += 1;
11258
+ if (index >= params.items.length) return;
11259
+ await params.worker(params.items[index], index);
11260
+ }
11261
+ }
11262
+ )
11263
+ );
11264
+ };
11265
+ backfillHyperliquidWhaleContext = async (params) => {
11266
+ const universe = (0, import_strategies4.getHyperliquidPerpUniverseSnapshot)();
11267
+ const whales = (0, import_strategies4.getHyperliquidWhaleRegistrySnapshot)();
11268
+ const trackedSymbols = new Set(universe.symbols);
11269
+ const identity = {
11270
+ universeFingerprint: universe.fingerprint,
11271
+ whaleRegistryFingerprint: whales.fingerprint
11272
+ };
11273
+ const fromMs = Math.floor(params.startMs / HYPERLIQUID_WHALE_BUCKET_MS) * HYPERLIQUID_WHALE_BUCKET_MS;
11274
+ const toMs = minuteCeil(params.endMs);
11275
+ const cached = await (0, import_timescale4.hasHyperliquidWhaleBackfillCoverage)({
11276
+ fromMs,
11277
+ toMs,
11278
+ ...identity
11279
+ });
11280
+ const emptyResult = () => ({
11281
+ cached,
11282
+ cacheOnly: params.cacheOnly,
11283
+ fills: 0,
11284
+ events: 0,
11285
+ buckets: 0,
11286
+ coverageBuckets: 0,
11287
+ complete: cached,
11288
+ walletsProcessed: 0,
11289
+ walletsCached: 0,
11290
+ completeWallets: 0,
11291
+ truncatedWallets: 0,
11292
+ failedWallets: 0,
11293
+ fromMs,
11294
+ toMs
11295
+ });
11296
+ if (cached || params.cacheOnly) return emptyResult();
11297
+ const limiter = new HyperliquidInfoRateLimiter(
11298
+ positiveInt(
11299
+ process.env.HYPERLIQUID_WHALE_RATE_LIMIT_WEIGHT,
11300
+ DEFAULT_RATE_LIMIT_WEIGHT
11301
+ ),
11302
+ Date.now,
11303
+ params.wait ?? sleep3
11304
+ );
11305
+ const concurrency = Math.min(
11306
+ 4,
11307
+ positiveInt(
11308
+ params.concurrency ?? process.env.HYPERLIQUID_WHALE_CONCURRENCY,
11309
+ DEFAULT_CONCURRENCY
11310
+ )
11311
+ );
11312
+ let fills = 0;
11313
+ let events = 0;
11314
+ let walletsProcessed = 0;
11315
+ let walletsCached = 0;
11316
+ let completeWallets = 0;
11317
+ let truncatedWallets = 0;
11318
+ let failedWallets = 0;
11319
+ const failures = [];
11320
+ await runConcurrent({
11321
+ items: whales.addresses,
11322
+ concurrency,
11323
+ worker: async (address) => {
11324
+ const previous = await (0, import_timescale4.getHyperliquidWhaleWalletCoverage)({
11325
+ address,
11326
+ fromMs,
11327
+ toMs,
11328
+ ...identity
11329
+ });
11330
+ if (previous && previous.status !== "failed") {
11331
+ walletsCached += 1;
11332
+ if (previous.status === "complete") completeWallets += 1;
11333
+ else truncatedWallets += 1;
11334
+ } else {
11335
+ try {
11336
+ const result2 = await fetchHyperliquidUserFillsByTime({
11337
+ address,
11338
+ startTime: fromMs,
11339
+ endTime: toMs - 1,
11340
+ fetchImpl: params.fetchImpl,
11341
+ rateLimiter: limiter
11342
+ });
11343
+ const addressEvents = result2.fills.map(
11344
+ (fill) => normalizeHyperliquidUserFill({
11345
+ fill,
11346
+ address,
11347
+ trackedSymbols,
11348
+ identity
11349
+ })
11350
+ ).filter((row) => row != null);
11351
+ await (0, import_timescale4.upsertHyperliquidWhaleTradeEvents)(addressEvents);
11352
+ await (0, import_timescale4.upsertHyperliquidWhaleWalletCoverage)({
11353
+ address,
11354
+ fromMs,
11355
+ toMs,
11356
+ coveredFromMs: result2.coveredFromMs,
11357
+ coveredToMs: toMs,
11358
+ status: result2.truncated ? "truncated" : "complete",
11359
+ fillsCount: result2.fills.length,
11360
+ ...identity
11361
+ });
11362
+ fills += result2.fills.length;
11363
+ events += addressEvents.length;
11364
+ if (result2.truncated) truncatedWallets += 1;
11365
+ else completeWallets += 1;
11366
+ } catch (error) {
11367
+ const normalizedError = error instanceof Error ? error : new Error(String(error));
11368
+ failures.push(normalizedError);
11369
+ failedWallets += 1;
11370
+ await (0, import_timescale4.upsertHyperliquidWhaleWalletCoverage)({
11371
+ address,
11372
+ fromMs,
11373
+ toMs,
11374
+ coveredFromMs: null,
11375
+ coveredToMs: null,
11376
+ status: "failed",
11377
+ fillsCount: 0,
11378
+ error: normalizedError.message,
11379
+ ...identity
11380
+ });
11381
+ }
11382
+ }
11383
+ walletsProcessed += 1;
11384
+ params.log?.(
11385
+ `Hyperliquid whales ${walletsProcessed}/${whales.addresses.length}: complete=${completeWallets}, truncated=${truncatedWallets}, failed=${failedWallets}, fills=${fills}, events=${events}`
11386
+ );
11387
+ }
11388
+ });
11389
+ const buckets = await (0, import_timescale4.rebuildHyperliquidWhaleFlowRows)({
11390
+ fromMs,
11391
+ toMs,
11392
+ ...identity
11393
+ });
11394
+ const coverageBuckets = await (0, import_timescale4.rebuildHyperliquidWhaleCoverageRows)({
11395
+ fromMs,
11396
+ toMs,
11397
+ expectedWhales: whales.addresses.length,
11398
+ ...identity
11399
+ });
11400
+ const result = {
11401
+ cached: false,
11402
+ cacheOnly: false,
11403
+ fills,
11404
+ events,
11405
+ buckets,
11406
+ coverageBuckets,
11407
+ complete: truncatedWallets === 0 && failedWallets === 0,
11408
+ walletsProcessed,
11409
+ walletsCached,
11410
+ completeWallets,
11411
+ truncatedWallets,
11412
+ failedWallets,
11413
+ fromMs,
11414
+ toMs
11415
+ };
11416
+ if (params.strict !== false && failures.length) {
11417
+ throw new AggregateError(
11418
+ failures,
11419
+ `Hyperliquid whale backfill failed for ${failures.length}/${whales.addresses.length} wallets`
11420
+ );
11421
+ }
11422
+ return result;
11423
+ };
11424
+ }
11425
+ });
11426
+
10988
11427
  // src/lib/marketContextPrepare.ts
10989
- var import_chalk9, shouldPrepareDerivativesContextForRun, shouldPrepareBinanceMarketContextForRun, shouldPrepareCoinMarketCapContextForRun, resolveCoinMarketCapBackfillForMode, buildCoinMarketCapBackfillParams, buildBinanceMarketBackfillParams, buildDerivativesBackfillParams, prepareMarketContextForRun;
11428
+ var import_chalk9, shouldPrepareDerivativesContextForRun, shouldPrepareBinanceMarketContextForRun, shouldPrepareCoinMarketCapContextForRun, shouldPrepareHyperliquidWhaleContextForRun, resolveCoinMarketCapBackfillForMode, buildCoinMarketCapBackfillParams, buildBinanceMarketBackfillParams, buildDerivativesBackfillParams, prepareMarketContextForRun;
10990
11429
  var init_marketContextPrepare = __esm({
10991
11430
  "src/lib/marketContextPrepare.ts"() {
10992
11431
  "use strict";
@@ -10995,6 +11434,7 @@ var init_marketContextPrepare = __esm({
10995
11434
  init_derivativesContextBackfill();
10996
11435
  init_coinMarketCapContextBackfill();
10997
11436
  init_runFormatting();
11437
+ init_hyperliquidWhaleBackfill();
10998
11438
  shouldPrepareDerivativesContextForRun = (params) => {
10999
11439
  if (params.universe === "tradfi") return false;
11000
11440
  if (params.mode === "signals") {
@@ -11044,6 +11484,20 @@ var init_marketContextPrepare = __esm({
11044
11484
  cacheOnly: params.cacheOnly
11045
11485
  });
11046
11486
  };
11487
+ shouldPrepareHyperliquidWhaleContextForRun = (params) => {
11488
+ const strategyRequiresContext = params.strategyNames?.includes(
11489
+ "HyperliquidConsensus"
11490
+ );
11491
+ if (params.universe === "tradfi" || params.mode === "signals" || params.cacheOnly || !params.aiEnabled && !params.mlEnabled && !strategyRequiresContext) {
11492
+ return false;
11493
+ }
11494
+ const configured = String(
11495
+ process.env.HYPERLIQUID_WHALE_BACKFILL_ENABLED ?? ""
11496
+ ).trim().toLowerCase();
11497
+ if (["0", "false", "no", "off"].includes(configured)) return false;
11498
+ if (strategyRequiresContext) return true;
11499
+ return ["1", "true", "yes", "on"].includes(configured);
11500
+ };
11047
11501
  resolveCoinMarketCapBackfillForMode = (mode) => mode === "backtest" ? backfillCoinMarketCapContextForBacktest : mode === "signals" ? backfillCoinMarketCapContextForSignals : backfillCoinMarketCapContextForReplay;
11048
11502
  buildCoinMarketCapBackfillParams = (params) => ({
11049
11503
  userName: params.userName,
@@ -11080,6 +11534,18 @@ var init_marketContextPrepare = __esm({
11080
11534
  )
11081
11535
  );
11082
11536
  }
11537
+ if (shouldPrepareHyperliquidWhaleContextForRun(params)) {
11538
+ await timeOperation4(
11539
+ "hyperliquid whale context backfill",
11540
+ () => backfillHyperliquidWhaleContext({
11541
+ startMs: params.preloadStartMs ?? params.startMs,
11542
+ endMs: params.endMs,
11543
+ cacheOnly: params.cacheOnly,
11544
+ strict: false,
11545
+ log
11546
+ })
11547
+ );
11548
+ }
11083
11549
  if (shouldPrepareBinanceMarketContextForRun(params)) {
11084
11550
  await timeOperation4("binance market context backfill", () => {
11085
11551
  const backfill = params.mode === "backtest" ? backfillBinanceMarketContextForBacktest : params.mode === "signals" ? backfillBinanceMarketContextForSignals : backfillBinanceMarketContextForReplay;
@@ -11855,13 +12321,13 @@ var init_cliArgs = __esm({
11855
12321
  });
11856
12322
 
11857
12323
  // src/lib/backtest/cliConfig.ts
11858
- var import_args5, import_os, import_types, import_constants7, BYTES_IN_MB, MAX_PARALLEL, resolveDefaultWorkerHeapMb, resolveDefaultParallel, DEFAULT_WORKER_HEAP_MB, DEFAULT_PARALLEL, DEFAULT_BACKTEST_ENTRY_DELAY_BARS, resolveWorkerHeapMb, resolveEffectiveParallel, resolveRequestedTestsLimit, hasCliFlag, stripIgnoredFlags, normalizedArgv, flags5, interval, marketUniverse, resolveBacktestPriceMode, backtestPriceMode, resolveBacktestEntryDelayBars, backtestEntryDelayBars, progressStep, testsLimit, testsSkip, hasExplicitTestsLimit, isUpdateOnlyRun, isContinueRun, continueRunId, isFastMode, testItemTimeoutMs, workerHeapMb, effectiveParallel, resultArtifactsIoConcurrency, userName, projectRoot3;
12324
+ var import_args5, import_os, import_types2, import_constants7, BYTES_IN_MB, MAX_PARALLEL, resolveDefaultWorkerHeapMb, resolveDefaultParallel, DEFAULT_WORKER_HEAP_MB, DEFAULT_PARALLEL, DEFAULT_BACKTEST_ENTRY_DELAY_BARS, resolveWorkerHeapMb, resolveEffectiveParallel, resolveRequestedTestsLimit, hasCliFlag, stripIgnoredFlags, normalizedArgv, flags5, interval, marketUniverse, resolveBacktestPriceMode, backtestPriceMode, resolveBacktestEntryDelayBars, backtestEntryDelayBars, progressStep, testsLimit, testsSkip, hasExplicitTestsLimit, isUpdateOnlyRun, isContinueRun, continueRunId, isFastMode, testItemTimeoutMs, workerHeapMb, effectiveParallel, resultArtifactsIoConcurrency, userName, projectRoot3;
11859
12325
  var init_cliConfig = __esm({
11860
12326
  "src/lib/backtest/cliConfig.ts"() {
11861
12327
  "use strict";
11862
12328
  import_args5 = __toESM(require("args"));
11863
12329
  import_os = __toESM(require("os"));
11864
- import_types = require("@tradejs/types");
12330
+ import_types2 = require("@tradejs/types");
11865
12331
  import_constants7 = require("@tradejs/core/constants");
11866
12332
  init_cliArgs();
11867
12333
  BYTES_IN_MB = 1024 * 1024;
@@ -11990,7 +12456,7 @@ var init_cliConfig = __esm({
11990
12456
  interval = String(flags5.timeframe ?? 15);
11991
12457
  marketUniverse = (() => {
11992
12458
  const value = String(flags5.universe ?? "crypto");
11993
- if (!(0, import_types.isMarketUniverse)(value)) {
12459
+ if (!(0, import_types2.isMarketUniverse)(value)) {
11994
12460
  throw new Error(`Unknown market universe: ${value}`);
11995
12461
  }
11996
12462
  return value;
@@ -12039,15 +12505,15 @@ var init_cliConfig = __esm({
12039
12505
  });
12040
12506
 
12041
12507
  // src/lib/backtest/runState.ts
12042
- var import_types2, successTests, errorTests, warningCounts, errorMessages, topResults, bestTickerResults, configResultBuckets, progressStats, replayResultsByStrategyAndTicker, persistedTestSummaryByKey, runStartedAt, testsStartedAt, runtimeCompareConnector, runtimeCompareConnectorName, runtimeCompareWindow, getResultNetProfit, addResultToAggregate, addResultWarnings, getAggregateAverageProfit, getAggregateWinRate, incrementSuccessTests, incrementErrorTests, recordRunError, getRunStartedAt, getTestsStartedAt, markTestsStarted, getRunCounters, getTopResults, replaceTopResults, getBestTickerResults, getBestTickerResultForSymbol, setBestTickerResultForSymbol, recordResultAggregates, getProgressStats, getConfigResultBuckets, getTopConfigResultBuckets, getPersistedTestSummariesMap, setPersistedTestSummary, setRuntimeCompareContext, getRuntimeCompareContext, resetRunState;
12508
+ var import_types3, successTests, errorTests, warningCounts, errorMessages, topResults, bestTickerResults, configResultBuckets, progressStats, replayResultsByStrategyAndTicker, persistedTestSummaryByKey, runStartedAt, testsStartedAt, runtimeCompareConnector, runtimeCompareConnectorName, runtimeCompareWindow, getResultNetProfit, addResultToAggregate, addResultWarnings, getAggregateAverageProfit, getAggregateWinRate, incrementSuccessTests, incrementErrorTests, recordRunError, getRunStartedAt, getTestsStartedAt, markTestsStarted, getRunCounters, getTopResults, replaceTopResults, getBestTickerResults, getBestTickerResultForSymbol, setBestTickerResultForSymbol, recordResultAggregates, getProgressStats, getConfigResultBuckets, getTopConfigResultBuckets, getPersistedTestSummariesMap, setPersistedTestSummary, setRuntimeCompareContext, getRuntimeCompareContext, resetRunState;
12043
12509
  var init_runState = __esm({
12044
12510
  "src/lib/backtest/runState.ts"() {
12045
12511
  "use strict";
12046
- import_types2 = require("@tradejs/types");
12512
+ import_types3 = require("@tradejs/types");
12047
12513
  successTests = 0;
12048
12514
  errorTests = 0;
12049
12515
  warningCounts = {
12050
- [import_types2.BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY]: 0
12516
+ [import_types3.BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY]: 0
12051
12517
  };
12052
12518
  errorMessages = [];
12053
12519
  topResults = [];
@@ -12096,7 +12562,7 @@ var init_runState = __esm({
12096
12562
  }
12097
12563
  };
12098
12564
  addResultWarnings = (result) => {
12099
- for (const code of Object.values(import_types2.BACKTEST_WARNING_CODES)) {
12565
+ for (const code of Object.values(import_types3.BACKTEST_WARNING_CODES)) {
12100
12566
  const count = Number(result.warningCounts?.[code] ?? 0);
12101
12567
  if (Number.isFinite(count) && count > 0) {
12102
12568
  warningCounts[code] = (warningCounts[code] ?? 0) + count;
@@ -12197,7 +12663,7 @@ var init_runState = __esm({
12197
12663
  resetRunState = () => {
12198
12664
  successTests = 0;
12199
12665
  errorTests = 0;
12200
- for (const code of Object.values(import_types2.BACKTEST_WARNING_CODES)) {
12666
+ for (const code of Object.values(import_types3.BACKTEST_WARNING_CODES)) {
12201
12667
  warningCounts[code] = 0;
12202
12668
  }
12203
12669
  errorMessages.length = 0;
@@ -12376,7 +12842,7 @@ var init_workerPool = __esm({
12376
12842
  });
12377
12843
 
12378
12844
  // src/lib/backtest/runnerCore.ts
12379
- var import_fs, import_path4, import_chalk14, import_backtest, import_constants8, import_time3, import_backtestArtifacts, import_redis6, import_tradingAccounts, import_types3, formatWindowDays, getResultAmount, getResultNetProfit2, recordError, insertTopResult, updateTopResults, updateBestTickerResult, chunkTestSuiteBySymbol, mergePersistedTestSummaries, getLogsById, resolveResultArtifacts, setTestData, persistTestSummariesIndex, validateBacktestRuntimeDeployment, prepareRunEnvironment2, buildPreparedTestSuite, trackTopResult, buildRunIntroLines, resolveTesterWorker, executeTestSuite, printRunOutro;
12845
+ var import_fs, import_path4, import_chalk14, import_backtest, import_constants8, import_time3, import_backtestArtifacts, import_redis6, import_tradingAccounts, import_types4, formatWindowDays, getResultAmount, getResultNetProfit2, recordError, insertTopResult, updateTopResults, updateBestTickerResult, chunkTestSuiteBySymbol, mergePersistedTestSummaries, getLogsById, resolveResultArtifacts, setTestData, persistTestSummariesIndex, validateBacktestRuntimeDeployment, prepareRunEnvironment2, buildPreparedTestSuite, trackTopResult, buildRunIntroLines, resolveTesterWorker, executeTestSuite, printRunOutro;
12380
12846
  var init_runnerCore = __esm({
12381
12847
  "src/lib/backtest/runnerCore.ts"() {
12382
12848
  "use strict";
@@ -12389,7 +12855,7 @@ var init_runnerCore = __esm({
12389
12855
  import_backtestArtifacts = require("@tradejs/infra/backtestArtifacts");
12390
12856
  import_redis6 = require("@tradejs/infra/redis");
12391
12857
  import_tradingAccounts = require("@tradejs/infra/tradingAccounts");
12392
- import_types3 = require("@tradejs/types");
12858
+ import_types4 = require("@tradejs/types");
12393
12859
  init_runFormatting();
12394
12860
  init_marketContextPrepare();
12395
12861
  init_runEnvironment();
@@ -12694,6 +13160,9 @@ var init_runnerCore = __esm({
12694
13160
  cacheOnly: Boolean(flags5.cacheOnly),
12695
13161
  aiEnabled,
12696
13162
  mlEnabled,
13163
+ strategyNames: Array.from(
13164
+ new Set(preparedSuite.map((test) => test.strategyName))
13165
+ ),
12697
13166
  log: (message) => console.log(import_chalk14.default.gray(message))
12698
13167
  });
12699
13168
  return preparedSuite;
@@ -12829,10 +13298,10 @@ var init_runnerCore = __esm({
12829
13298
  const { successTests: successTests2, errorTests: errorTests2, warningCounts: warningCounts2 } = getRunCounters();
12830
13299
  console.log(`${import_chalk14.default.green("SUCCESS TESTS")}: ${successTests2}`);
12831
13300
  console.log(`${import_chalk14.default.red("ERRORS")}: ${errorTests2}`);
12832
- const takeProfitCrossedWarnings = warningCounts2[import_types3.BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY] ?? 0;
13301
+ const takeProfitCrossedWarnings = warningCounts2[import_types4.BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY] ?? 0;
12833
13302
  console.log(
12834
13303
  import_chalk14.default.yellow(
12835
- `WARNINGS (${import_types3.BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY}): ${takeProfitCrossedWarnings}`
13304
+ `WARNINGS (${import_types4.BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY}): ${takeProfitCrossedWarnings}`
12836
13305
  )
12837
13306
  );
12838
13307
  console.log("");
@@ -13258,6 +13727,9 @@ var init_backtest = __esm({
13258
13727
  cacheOnly: manifest.flags.cacheOnly,
13259
13728
  aiEnabled: manifest.flags.ai,
13260
13729
  mlEnabled: manifest.flags.ml,
13730
+ strategyNames: Array.from(
13731
+ new Set(remainingSuite.map((test) => test.strategyName))
13732
+ ),
13261
13733
  log: (message) => console.log(import_chalk15.default.gray(message))
13262
13734
  });
13263
13735
  };
@@ -13402,7 +13874,7 @@ __export(binanceBreadthUniversesUpdate_exports, {
13402
13874
  BINANCE_BREADTH_UNIVERSES_RELATIVE_PATH: () => BINANCE_BREADTH_UNIVERSES_RELATIVE_PATH,
13403
13875
  main: () => main6
13404
13876
  });
13405
- var import_config, import_args6, import_promises7, import_node_path4, import_chalk16, import_connectors6, import_connectors7, import_strategies3, flags6, BINANCE_BREADTH_UNIVERSES_RELATIVE_PATH, main6;
13877
+ var import_config, import_args6, import_promises7, import_node_path4, import_chalk16, import_connectors6, import_connectors7, import_strategies5, flags6, BINANCE_BREADTH_UNIVERSES_RELATIVE_PATH, main6;
13406
13878
  var init_binanceBreadthUniversesUpdate = __esm({
13407
13879
  "src/scripts/binanceBreadthUniversesUpdate.ts"() {
13408
13880
  "use strict";
@@ -13413,7 +13885,7 @@ var init_binanceBreadthUniversesUpdate = __esm({
13413
13885
  import_chalk16 = __toESM(require("chalk"));
13414
13886
  import_connectors6 = require("@tradejs/connectors");
13415
13887
  import_connectors7 = require("@tradejs/node/connectors");
13416
- import_strategies3 = require("@tradejs/node/strategies");
13888
+ import_strategies5 = require("@tradejs/node/strategies");
13417
13889
  init_binanceMarketData();
13418
13890
  import_args6.default.example(
13419
13891
  "yarn breadth:universes:update --write",
@@ -13444,7 +13916,7 @@ var init_binanceBreadthUniversesUpdate = __esm({
13444
13916
  await connector.getTickers(),
13445
13917
  100
13446
13918
  );
13447
- const snapshot = (0, import_strategies3.buildBinanceBreadthUniverseSnapshot)({ rankedSymbols });
13919
+ const snapshot = (0, import_strategies5.buildBinanceBreadthUniverseSnapshot)({ rankedSymbols });
13448
13920
  const serialized = `${JSON.stringify(snapshot, null, 2)}
13449
13921
  `;
13450
13922
  console.log(
@@ -13479,7 +13951,7 @@ var binanceMarketIngest_exports = {};
13479
13951
  __export(binanceMarketIngest_exports, {
13480
13952
  main: () => main7
13481
13953
  });
13482
- var import_config2, import_args7, import_chalk17, import_connectors8, import_strategies4, import_timescale4, flags7, asFloat2, asInt4, sleep2, intervalToConnectorInterval, fetchAggTradesForWindow2, printEstimate, main7;
13954
+ var import_config2, import_args7, import_chalk17, import_connectors8, import_strategies6, import_timescale5, flags7, asFloat2, asInt4, sleep4, intervalToConnectorInterval, fetchAggTradesForWindow2, printEstimate, main7;
13483
13955
  var init_binanceMarketIngest = __esm({
13484
13956
  "src/scripts/binanceMarketIngest.ts"() {
13485
13957
  "use strict";
@@ -13487,8 +13959,8 @@ var init_binanceMarketIngest = __esm({
13487
13959
  import_args7 = __toESM(require("args"));
13488
13960
  import_chalk17 = __toESM(require("chalk"));
13489
13961
  import_connectors8 = require("@tradejs/connectors");
13490
- import_strategies4 = require("@tradejs/node/strategies");
13491
- import_timescale4 = require("@tradejs/infra/timescale");
13962
+ import_strategies6 = require("@tradejs/node/strategies");
13963
+ import_timescale5 = require("@tradejs/infra/timescale");
13492
13964
  init_binanceMarketData();
13493
13965
  import_args7.default.example(
13494
13966
  "yarn cli:node8g binance:market-ingest --all --symbols BTCUSDT,ETHUSDT --days 0.05 --interval 1m --write",
@@ -13531,7 +14003,7 @@ var init_binanceMarketIngest = __esm({
13531
14003
  const parsed = Number.parseInt(String(value ?? ""), 10);
13532
14004
  return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
13533
14005
  };
13534
- sleep2 = (ms) => ms > 0 ? new Promise((resolve) => setTimeout(resolve, ms)) : Promise.resolve();
14006
+ sleep4 = (ms) => ms > 0 ? new Promise((resolve) => setTimeout(resolve, ms)) : Promise.resolve();
13535
14007
  intervalToConnectorInterval = (interval5) => {
13536
14008
  if (interval5 === "1m") return "1";
13537
14009
  if (interval5 === "5m") return "5";
@@ -13567,10 +14039,10 @@ var init_binanceMarketIngest = __esm({
13567
14039
  const nextCursor = Math.max(pageCursor + 1, Number(lastTs) + 1);
13568
14040
  if (nextCursor > endTime) break;
13569
14041
  pageCursor = nextCursor;
13570
- await sleep2(requestDelayMs);
14042
+ await sleep4(requestDelayMs);
13571
14043
  }
13572
14044
  cursor = endTime + 1;
13573
- await sleep2(requestDelayMs);
14045
+ await sleep4(requestDelayMs);
13574
14046
  }
13575
14047
  return rows;
13576
14048
  };
@@ -13591,7 +14063,7 @@ var init_binanceMarketIngest = __esm({
13591
14063
  const interval5 = normalizeMarketFeatureInterval(flags7.interval);
13592
14064
  const hours = flags7.hours == null ? null : asFloat2(flags7.hours, 0);
13593
14065
  const days = hours != null && hours > 0 ? hours / 24 : asFloat2(flags7.days, 1);
13594
- const breadthUniverses = (0, import_strategies4.getBinanceBreadthUniverses)();
14066
+ const breadthUniverses = (0, import_strategies6.getBinanceBreadthUniverses)();
13595
14067
  const batchMinutes = asInt4(flags7.batchMinutes, 15);
13596
14068
  const requestDelayMs = asInt4(flags7.requestDelayMs, 75);
13597
14069
  const includeAll = Boolean(flags7.all);
@@ -13616,7 +14088,7 @@ var init_binanceMarketIngest = __esm({
13616
14088
  console.log(import_chalk17.default.yellow("Dry run only. Pass --write to ingest."));
13617
14089
  return;
13618
14090
  }
13619
- await (0, import_timescale4.waitForDbReady)();
14091
+ await (0, import_timescale5.waitForDbReady)();
13620
14092
  const connector = await import_connectors8.connectors[import_connectors8.ConnectorNames.Binance]({
13621
14093
  userName: "root"
13622
14094
  });
@@ -13638,7 +14110,7 @@ var init_binanceMarketIngest = __esm({
13638
14110
  requestDelayMs
13639
14111
  });
13640
14112
  const rows = aggregateAggTradesToRows({ symbol, interval: interval5, trades });
13641
- await (0, import_timescale4.upsertMarketTradeFlowRows)(rows);
14113
+ await (0, import_timescale5.upsertMarketTradeFlowRows)(rows);
13642
14114
  aggTradesRaw += trades.length;
13643
14115
  tradeFlowRows += rows.length;
13644
14116
  process.stdout.write(
@@ -13685,7 +14157,7 @@ var init_binanceMarketIngest = __esm({
13685
14157
  candlesBySymbol: universeCandles,
13686
14158
  btcCandles
13687
14159
  });
13688
- await (0, import_timescale4.upsertMarketBreadthRows)(rows);
14160
+ await (0, import_timescale5.upsertMarketBreadthRows)(rows);
13689
14161
  breadthRows += rows.length;
13690
14162
  }
13691
14163
  }
@@ -13702,14 +14174,14 @@ var init_binanceMarketIngest = __esm({
13702
14174
  });
13703
14175
 
13704
14176
  // src/lib/runBot.ts
13705
- var import_lodash, import_connectors9, import_data2, import_strategies5, import_async3, import_constants10, import_time4, import_logger, import_redis8, projectRoot4, runBot;
14177
+ var import_lodash, import_connectors9, import_data2, import_strategies7, import_async3, import_constants10, import_time4, import_logger, import_redis8, projectRoot4, runBot;
13706
14178
  var init_runBot = __esm({
13707
14179
  "src/lib/runBot.ts"() {
13708
14180
  "use strict";
13709
14181
  import_lodash = __toESM(require("lodash"));
13710
14182
  import_connectors9 = require("@tradejs/node/connectors");
13711
14183
  import_data2 = require("@tradejs/core/data");
13712
- import_strategies5 = require("@tradejs/node/strategies");
14184
+ import_strategies7 = require("@tradejs/node/strategies");
13713
14185
  import_async3 = require("@tradejs/core/async");
13714
14186
  import_constants10 = require("@tradejs/core/constants");
13715
14187
  import_time4 = require("@tradejs/core/time");
@@ -13741,7 +14213,7 @@ var init_runBot = __esm({
13741
14213
  continue;
13742
14214
  }
13743
14215
  try {
13744
- const strategyCreator = await (0, import_strategies5.getStrategyCreator)(
14216
+ const strategyCreator = await (0, import_strategies7.getStrategyCreator)(
13745
14217
  strategyName,
13746
14218
  projectRoot4
13747
14219
  );
@@ -14605,7 +15077,7 @@ var require_binaryParsers = __commonJS({
14605
15077
  };
14606
15078
  var parseArray = function(value) {
14607
15079
  var dim = parseBits(value, 32);
14608
- var flags29 = parseBits(value, 32, 32);
15080
+ var flags31 = parseBits(value, 32, 32);
14609
15081
  var elementType = parseBits(value, 32, 64);
14610
15082
  var offset = 96;
14611
15083
  var dims = [];
@@ -17303,8 +17775,8 @@ var require_helper = __commonJS({
17303
17775
  function warn() {
17304
17776
  var isWritable = warnStream instanceof Stream && true === warnStream.writable;
17305
17777
  if (isWritable) {
17306
- var args31 = Array.prototype.slice.call(arguments).concat("\n");
17307
- warnStream.write(util.format.apply(util, args31));
17778
+ var args33 = Array.prototype.slice.call(arguments).concat("\n");
17779
+ warnStream.write(util.format.apply(util, args33));
17308
17780
  }
17309
17781
  }
17310
17782
  Object.defineProperty(module2.exports, "isWin", {
@@ -19521,14 +19993,14 @@ var init_cleanTests = __esm({
19521
19993
  });
19522
19994
 
19523
19995
  // src/lib/continuity.ts
19524
- var import_types4, parseContinuityUniverse, resolveContinuityUniverses, findRepairableContinuityGap;
19996
+ var import_types5, parseContinuityUniverse, resolveContinuityUniverses, findRepairableContinuityGap;
19525
19997
  var init_continuity = __esm({
19526
19998
  "src/lib/continuity.ts"() {
19527
19999
  "use strict";
19528
- import_types4 = require("@tradejs/types");
20000
+ import_types5 = require("@tradejs/types");
19529
20001
  parseContinuityUniverse = (value) => {
19530
20002
  const normalized = String(value ?? "all").trim().toLowerCase();
19531
- if (normalized === "all" || (0, import_types4.isMarketUniverse)(normalized)) {
20003
+ if (normalized === "all" || (0, import_types5.isMarketUniverse)(normalized)) {
19532
20004
  return normalized;
19533
20005
  }
19534
20006
  throw new Error(
@@ -19560,7 +20032,7 @@ var continuity_exports = {};
19560
20032
  __export(continuity_exports, {
19561
20033
  main: () => main13
19562
20034
  });
19563
- var import_args12, import_progress7, import_chalk20, import_connectors10, import_cli8, import_constants11, import_time5, import_logger2, import_timescale5, flags12, interval2, intervalKey, projectRoot5, parseProviders, runContinuity, main13;
20035
+ var import_args12, import_progress7, import_chalk20, import_connectors10, import_cli8, import_constants11, import_time5, import_logger2, import_timescale6, flags12, interval2, intervalKey, projectRoot5, parseProviders, runContinuity, main13;
19564
20036
  var init_continuity2 = __esm({
19565
20037
  "src/scripts/continuity.ts"() {
19566
20038
  "use strict";
@@ -19572,7 +20044,7 @@ var init_continuity2 = __esm({
19572
20044
  import_constants11 = require("@tradejs/core/constants");
19573
20045
  import_time5 = require("@tradejs/core/time");
19574
20046
  import_logger2 = require("@tradejs/infra/logger");
19575
- import_timescale5 = require("@tradejs/infra/timescale");
20047
+ import_timescale6 = require("@tradejs/infra/timescale");
19576
20048
  init_continuity();
19577
20049
  import_args12.default.option(["t", "tickers"], "Selected tickers");
19578
20050
  import_args12.default.option(["f", "timeframe"], "Timeframe", 15);
@@ -19656,7 +20128,7 @@ var init_continuity2 = __esm({
19656
20128
  (0, import_time5.formatUnix)(gap.ts),
19657
20129
  gap.diffSeconds
19658
20130
  );
19659
- await (0, import_timescale5.deleteCandles)(provider, symbol, interval2);
20131
+ await (0, import_timescale6.deleteCandles)(provider, symbol, interval2);
19660
20132
  const reloaded = await connector.kline({
19661
20133
  symbol,
19662
20134
  interval: intervalKey,
@@ -19686,7 +20158,7 @@ var init_continuity2 = __esm({
19686
20158
  import_logger2.logger.error("Invalid timeframe: %s", flags12.timeframe);
19687
20159
  process.exit(1);
19688
20160
  }
19689
- await (0, import_timescale5.waitForDbReady)();
20161
+ await (0, import_timescale6.waitForDbReady)();
19690
20162
  const reloadStart = (0, import_time5.getTimestamp)(import_constants11.PRELOAD_DAYS);
19691
20163
  const reloadEnd = (0, import_time5.getTimestamp)();
19692
20164
  let requestedUniverse;
@@ -19774,7 +20246,7 @@ var derivativesIngest_exports = {};
19774
20246
  __export(derivativesIngest_exports, {
19775
20247
  main: () => main14
19776
20248
  });
19777
- var import_config4, import_args13, import_chalk21, import_indicators2, import_timescale6, import_userSettings4, import_connectors11, flags13, asInt5, main14;
20249
+ var import_config4, import_args13, import_chalk21, import_indicators2, import_timescale7, import_userSettings4, import_connectors11, flags13, asInt5, main14;
19778
20250
  var init_derivativesIngest = __esm({
19779
20251
  "src/scripts/derivativesIngest.ts"() {
19780
20252
  "use strict";
@@ -19782,7 +20254,7 @@ var init_derivativesIngest = __esm({
19782
20254
  import_args13 = __toESM(require("args"));
19783
20255
  import_chalk21 = __toESM(require("chalk"));
19784
20256
  import_indicators2 = require("@tradejs/core/indicators");
19785
- import_timescale6 = require("@tradejs/infra/timescale");
20257
+ import_timescale7 = require("@tradejs/infra/timescale");
19786
20258
  import_userSettings4 = require("@tradejs/infra/userSettings");
19787
20259
  import_connectors11 = require("@tradejs/connectors");
19788
20260
  import_args13.default.example(
@@ -19820,7 +20292,7 @@ var init_derivativesIngest = __esm({
19820
20292
  const coinalyzeApiKey = providerName === "coinalyze" ? (await (0, import_userSettings4.getUserSettings)(userName2)).COINALYZE_API_KEY : void 0;
19821
20293
  if (!symbols.length) throw new Error("No symbols provided");
19822
20294
  if (!intervals.length) throw new Error("No intervals provided");
19823
- await (0, import_timescale6.waitForDbReady)();
20295
+ await (0, import_timescale7.waitForDbReady)();
19824
20296
  const now2 = Date.now();
19825
20297
  const fromMs = now2 - days * 24 * 60 * 60 * 1e3;
19826
20298
  let totalDerivativesRows = 0;
@@ -19849,12 +20321,12 @@ var init_derivativesIngest = __esm({
19849
20321
  });
19850
20322
  const derivativesRows = window2.derivativesRows ?? [];
19851
20323
  if (derivativesRows.length) {
19852
- await (0, import_timescale6.upsertDerivatives)(derivativesRows);
20324
+ await (0, import_timescale7.upsertDerivatives)(derivativesRows);
19853
20325
  totalDerivativesRows += derivativesRows.length;
19854
20326
  }
19855
20327
  const spreadRows = window2.spreadRows ?? [];
19856
20328
  if (spreadRows.length) {
19857
- await (0, import_timescale6.upsertSpreadRows)(spreadRows);
20329
+ await (0, import_timescale7.upsertSpreadRows)(spreadRows);
19858
20330
  totalSpreadRows += spreadRows.length;
19859
20331
  }
19860
20332
  cursor = toMs + 1;
@@ -19876,7 +20348,7 @@ var derivativesIngestCoinalyzeAll_exports = {};
19876
20348
  __export(derivativesIngestCoinalyzeAll_exports, {
19877
20349
  main: () => main15
19878
20350
  });
19879
- var import_config5, import_args14, import_chalk22, import_progress8, import_lodash2, import_connectors12, import_indicators3, import_async4, import_userSettings5, import_cli9, import_timescale7, coinalyzeIntervalMap2, flags14, asInt6, parseList2, coinalyzeBaseUrl, coinalyzeMaxRetries, lastRequestTs3, fetchJsonWithRateLimit2, fetchCoinalyzeMarkets2, selectBestMarket2, buildMatches2, normalizeMetricPoint2, toSeriesMap2, fetchMetricBatch2, main15;
20351
+ var import_config5, import_args14, import_chalk22, import_progress8, import_lodash2, import_connectors12, import_indicators3, import_async4, import_userSettings5, import_cli9, import_timescale8, coinalyzeIntervalMap2, flags14, asInt6, parseList2, coinalyzeBaseUrl, coinalyzeMaxRetries, lastRequestTs3, fetchJsonWithRateLimit2, fetchCoinalyzeMarkets2, selectBestMarket2, buildMatches2, normalizeMetricPoint2, toSeriesMap2, fetchMetricBatch2, main15;
19880
20352
  var init_derivativesIngestCoinalyzeAll = __esm({
19881
20353
  "src/scripts/derivativesIngestCoinalyzeAll.ts"() {
19882
20354
  "use strict";
@@ -19890,7 +20362,7 @@ var init_derivativesIngestCoinalyzeAll = __esm({
19890
20362
  import_async4 = require("@tradejs/core/async");
19891
20363
  import_userSettings5 = require("@tradejs/infra/userSettings");
19892
20364
  import_cli9 = require("@tradejs/node/cli");
19893
- import_timescale7 = require("@tradejs/infra/timescale");
20365
+ import_timescale8 = require("@tradejs/infra/timescale");
19894
20366
  coinalyzeIntervalMap2 = {
19895
20367
  "15m": "15min",
19896
20368
  "1h": "1hour"
@@ -20154,7 +20626,7 @@ var init_derivativesIngestCoinalyzeAll = __esm({
20154
20626
  );
20155
20627
  return;
20156
20628
  }
20157
- await (0, import_timescale7.waitForDbReady)();
20629
+ await (0, import_timescale8.waitForDbReady)();
20158
20630
  const now2 = Date.now();
20159
20631
  const fromMs = now2 - days * 24 * 60 * 60 * 1e3;
20160
20632
  const symbolBatches = import_lodash2.default.chunk(matches, symbolBatchSize);
@@ -20222,7 +20694,7 @@ var init_derivativesIngestCoinalyzeAll = __esm({
20222
20694
  return (0, import_indicators3.coinalyzePointsToRows)(points, interval5, "coinalyze");
20223
20695
  });
20224
20696
  if (rows.length) {
20225
- await (0, import_timescale7.upsertDerivatives)(rows);
20697
+ await (0, import_timescale8.upsertDerivatives)(rows);
20226
20698
  totalRows += rows.length;
20227
20699
  }
20228
20700
  const confirmedLiquidationWindow = (0, import_indicators3.resolveCoinalyzeConfirmedIntradayCoverage)({
@@ -20232,7 +20704,7 @@ var init_derivativesIngestCoinalyzeAll = __esm({
20232
20704
  nowMs: now2
20233
20705
  });
20234
20706
  if (confirmedLiquidationWindow) {
20235
- await (0, import_timescale7.applyDerivativesMetricCoverage)(
20707
+ await (0, import_timescale8.applyDerivativesMetricCoverage)(
20236
20708
  batch.map((item) => {
20237
20709
  const marketSymbol = item.marketSymbol.toUpperCase();
20238
20710
  const eventRowsCount = (liqMap.get(marketSymbol) ?? []).filter(
@@ -20466,7 +20938,7 @@ var init_doctor = __esm({
20466
20938
  });
20467
20939
 
20468
20940
  // src/lib/executionCalibration.ts
20469
- var import_constants12, import_data3, import_trade, roundMetric, asRecord2, asArray, finiteNumber, finiteString, finiteDirection, metricValues, quantile, summarizeNumbers, getIntervalMs, getSignalKey, addSignalToMap, getRuntimePayload, extractRuntimeTradeRows, normalizeReplayMatch, extractReplayMatches, buildReplayMatchMaps, getSignalCandles, adverseEntryBps, feeBps, latencyDelta, resolveTelemetryQuality, finiteTelemetryQuality, countBy, toLatencyBucket, toSpreadBucket, buildSample, buildReplayOnlySample, hasTelemetry, hasFullTelemetry, buildGroupSummary, groupSamples, positiveMetricValues, buildRecommendation, buildExecutionCalibrationReport;
20941
+ var import_constants12, import_data3, import_trade, roundMetric, asRecord2, asArray, finiteNumber2, finiteString, finiteDirection, metricValues, quantile, summarizeNumbers, getIntervalMs, getSignalKey, addSignalToMap, getRuntimePayload, extractRuntimeTradeRows, normalizeReplayMatch, extractReplayMatches, buildReplayMatchMaps, getSignalCandles, adverseEntryBps, feeBps, latencyDelta, resolveTelemetryQuality, finiteTelemetryQuality, countBy, toLatencyBucket, toSpreadBucket, buildSample, buildReplayOnlySample, hasTelemetry, hasFullTelemetry, buildGroupSummary, groupSamples, positiveMetricValues, buildRecommendation, buildExecutionCalibrationReport;
20470
20942
  var init_executionCalibration = __esm({
20471
20943
  "src/lib/executionCalibration.ts"() {
20472
20944
  "use strict";
@@ -20476,7 +20948,7 @@ var init_executionCalibration = __esm({
20476
20948
  roundMetric = (value, decimals = 6) => typeof value === "number" && Number.isFinite(value) ? Number(value.toFixed(decimals)) : null;
20477
20949
  asRecord2 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
20478
20950
  asArray = (value) => Array.isArray(value) ? value : [];
20479
- finiteNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
20951
+ finiteNumber2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
20480
20952
  finiteString = (value) => typeof value === "string" && value.trim() ? value.trim() : null;
20481
20953
  finiteDirection = (value) => value === "LONG" || value === "SHORT" ? value : null;
20482
20954
  metricValues = (samples, key) => samples.map((sample) => sample[key]).filter(
@@ -20552,7 +21024,7 @@ var init_executionCalibration = __esm({
20552
21024
  const shapeKey = getSignalKey({
20553
21025
  strategy: finiteString(signal.strategy),
20554
21026
  symbol: finiteString(signal.symbol),
20555
- timestamp: finiteNumber(signal.timestamp)
21027
+ timestamp: finiteNumber2(signal.timestamp)
20556
21028
  });
20557
21029
  if (shapeKey) {
20558
21030
  signalsByShape.set(shapeKey, signal);
@@ -20594,7 +21066,7 @@ var init_executionCalibration = __esm({
20594
21066
  const shapeKey = getSignalKey({
20595
21067
  strategy: finiteString(trade.strategy),
20596
21068
  symbol: finiteString(trade.symbol),
20597
- timestamp: finiteNumber(trade.signalTimestamp ?? trade.entryTimestamp)
21069
+ timestamp: finiteNumber2(trade.signalTimestamp ?? trade.entryTimestamp)
20598
21070
  });
20599
21071
  const signal = asRecord2(redisValues?.signal) ?? (signalId ? signalsById.get(signalId) ?? null : null) ?? (shapeKey ? signalsByShape.get(shapeKey) ?? null : null);
20600
21072
  return {
@@ -20623,8 +21095,8 @@ var init_executionCalibration = __esm({
20623
21095
  direction: finiteDirection(
20624
21096
  item.direction ?? backtest2?.direction ?? runtime?.direction
20625
21097
  ),
20626
- backtestPrice: finiteNumber(item.backtestPrice ?? backtest2?.price),
20627
- runtimePrice: finiteNumber(item.runtimePrice ?? runtime?.price)
21098
+ backtestPrice: finiteNumber2(item.backtestPrice ?? backtest2?.price),
21099
+ runtimePrice: finiteNumber2(item.runtimePrice ?? runtime?.price)
20628
21100
  };
20629
21101
  };
20630
21102
  extractReplayMatches = (replayEvidenceArtifact) => {
@@ -20773,24 +21245,24 @@ var init_executionCalibration = __esm({
20773
21245
  const direction = finiteDirection(trade.direction ?? signal?.direction);
20774
21246
  const orderId = finiteString(trade.orderId);
20775
21247
  const signalId = finiteString(trade.signalId ?? signal?.signalId);
20776
- const qty = finiteNumber(trade.qty);
20777
- const signalTimestamp = finiteNumber(
21248
+ const qty = finiteNumber2(trade.qty);
21249
+ const signalTimestamp = finiteNumber2(
20778
21250
  trade.signalTimestamp ?? signal?.timestamp
20779
21251
  );
20780
21252
  const intervalMs2 = getIntervalMs(interval5);
20781
21253
  const signalCloseTimestamp = signalTimestamp != null && intervalMs2 != null ? signalTimestamp + intervalMs2 : signalTimestamp;
20782
- const signalClosePrice = finiteNumber(trade.signalClosePrice);
20783
- const arrivalSnapshotTime = finiteNumber(trade.arrivalSnapshotTime);
21254
+ const signalClosePrice = finiteNumber2(trade.signalClosePrice);
21255
+ const arrivalSnapshotTime = finiteNumber2(trade.arrivalSnapshotTime);
20784
21256
  const arrivalSource = finiteString(trade.arrivalSource);
20785
- const arrivalMid = finiteNumber(trade.arrivalMid);
20786
- const bid = finiteNumber(trade.bid);
20787
- const ask = finiteNumber(trade.ask);
20788
- const spreadBps = finiteNumber(trade.spreadBps);
20789
- const orderSubmitTime = finiteNumber(trade.orderSubmitTime);
20790
- const orderAckTime = finiteNumber(trade.orderAckTime);
20791
- const fillAvgPrice = finiteNumber(trade.fillAvgPrice);
21257
+ const arrivalMid = finiteNumber2(trade.arrivalMid);
21258
+ const bid = finiteNumber2(trade.bid);
21259
+ const ask = finiteNumber2(trade.ask);
21260
+ const spreadBps = finiteNumber2(trade.spreadBps);
21261
+ const orderSubmitTime = finiteNumber2(trade.orderSubmitTime);
21262
+ const orderAckTime = finiteNumber2(trade.orderAckTime);
21263
+ const fillAvgPrice = finiteNumber2(trade.fillAvgPrice);
20792
21264
  const fillSource = finiteString(trade.fillSource);
20793
- const fillTime = finiteNumber(trade.fillTime);
21265
+ const fillTime = finiteNumber2(trade.fillTime);
20794
21266
  const telemetryQuality = finiteTelemetryQuality(trade.telemetryQuality) ?? resolveTelemetryQuality({
20795
21267
  signalClosePrice,
20796
21268
  arrivalMid,
@@ -20799,7 +21271,7 @@ var init_executionCalibration = __esm({
20799
21271
  fillAvgPrice,
20800
21272
  fillTime
20801
21273
  });
20802
- const fee = finiteNumber(trade.fee ?? trade.openFee);
21274
+ const fee = finiteNumber2(trade.fee ?? trade.openFee);
20803
21275
  const arrivalSnapshotAgeMs = latencyDelta(
20804
21276
  orderSubmitTime,
20805
21277
  arrivalSnapshotTime
@@ -21331,8 +21803,8 @@ volumes:
21331
21803
  `${DEV_COMPOSE_FILE} not found in project root. Run "npx @tradejs/cli infra-init" first.`
21332
21804
  );
21333
21805
  };
21334
- runDockerCompose = (composePath, args31) => {
21335
- const result = (0, import_node_child_process3.spawnSync)("docker", ["compose", "-f", composePath, ...args31], {
21806
+ runDockerCompose = (composePath, args33) => {
21807
+ const result = (0, import_node_child_process3.spawnSync)("docker", ["compose", "-f", composePath, ...args33], {
21336
21808
  stdio: "inherit"
21337
21809
  });
21338
21810
  if (result.error) {
@@ -21416,14 +21888,14 @@ var cleanupMarketContext_exports = {};
21416
21888
  __export(cleanupMarketContext_exports, {
21417
21889
  main: () => main21
21418
21890
  });
21419
- var import_config7, import_args17, import_chalk29, import_timescale8, flags17, main21;
21891
+ var import_config7, import_args17, import_chalk29, import_timescale9, flags17, main21;
21420
21892
  var init_cleanupMarketContext = __esm({
21421
21893
  "src/scripts/cleanupMarketContext.ts"() {
21422
21894
  "use strict";
21423
21895
  import_config7 = require("dotenv/config");
21424
21896
  import_args17 = __toESM(require("args"));
21425
21897
  import_chalk29 = __toESM(require("chalk"));
21426
- import_timescale8 = require("@tradejs/infra/timescale");
21898
+ import_timescale9 = require("@tradejs/infra/timescale");
21427
21899
  import_args17.default.example(
21428
21900
  "yarn cli maintenance:cleanup-market-context --apply",
21429
21901
  "Remove deprecated snapshot-only market context data from Timescale"
@@ -21432,8 +21904,8 @@ var init_cleanupMarketContext = __esm({
21432
21904
  flags17 = import_args17.default.parse(process.argv);
21433
21905
  main21 = async () => {
21434
21906
  const apply = Boolean(flags17.apply);
21435
- await (0, import_timescale8.waitForDbReady)();
21436
- const items = await (0, import_timescale8.cleanupDeprecatedMarketContext)({ apply });
21907
+ await (0, import_timescale9.waitForDbReady)();
21908
+ const items = await (0, import_timescale9.cleanupDeprecatedMarketContext)({ apply });
21437
21909
  if (!items.length) {
21438
21910
  console.log(import_chalk29.default.green("No deprecated market context data found."));
21439
21911
  return;
@@ -21458,7 +21930,7 @@ var migration_exports = {};
21458
21930
  __export(migration_exports, {
21459
21931
  main: () => main22
21460
21932
  });
21461
- var import_progress9, import_chalk30, import_logger3, import_files, import_timescale9, DIR, re, BATCH, projectRoot7, migrateFile, main22;
21933
+ var import_progress9, import_chalk30, import_logger3, import_files, import_timescale10, DIR, re, BATCH, projectRoot7, migrateFile, main22;
21462
21934
  var init_migration = __esm({
21463
21935
  "src/scripts/migration.ts"() {
21464
21936
  "use strict";
@@ -21466,7 +21938,7 @@ var init_migration = __esm({
21466
21938
  import_chalk30 = __toESM(require("chalk"));
21467
21939
  import_logger3 = require("@tradejs/infra/logger");
21468
21940
  import_files = require("@tradejs/infra/files");
21469
- import_timescale9 = require("@tradejs/infra/timescale");
21941
+ import_timescale10 = require("@tradejs/infra/timescale");
21470
21942
  DIR = "data/history";
21471
21943
  re = /^(.+?)_(\d+)\.json$/;
21472
21944
  BATCH = 2e3;
@@ -21482,9 +21954,9 @@ var init_migration = __esm({
21482
21954
  [],
21483
21955
  projectRoot7
21484
21956
  );
21485
- const rows = (0, import_timescale9.toRows)("bybit", symbol, interval5, data);
21957
+ const rows = (0, import_timescale10.toRows)("bybit", symbol, interval5, data);
21486
21958
  for (let i = 0; i < rows.length; i += BATCH) {
21487
- await (0, import_timescale9.upsertCandles)(rows.slice(i, i + BATCH));
21959
+ await (0, import_timescale10.upsertCandles)(rows.slice(i, i + BATCH));
21488
21960
  }
21489
21961
  };
21490
21962
  main22 = async () => {
@@ -21513,21 +21985,21 @@ var init_migration = __esm({
21513
21985
  });
21514
21986
 
21515
21987
  // src/lib/marketData/klineEvents.ts
21516
- var import_ioredis2, import_redis10, MARKET_KLINE_CHANNEL, positiveInt, redisOptions, buildMarketKlineTopic, publishMarketKlineEvent, createMarketKlineSubscriber, parseMarketKlineEvent;
21988
+ var import_ioredis2, import_redis10, MARKET_KLINE_CHANNEL, positiveInt2, redisOptions, buildMarketKlineTopic, publishMarketKlineEvent, createMarketKlineSubscriber, parseMarketKlineEvent;
21517
21989
  var init_klineEvents = __esm({
21518
21990
  "src/lib/marketData/klineEvents.ts"() {
21519
21991
  "use strict";
21520
21992
  import_ioredis2 = __toESM(require("ioredis"));
21521
21993
  import_redis10 = require("@tradejs/infra/redis");
21522
21994
  MARKET_KLINE_CHANNEL = "tradejs:market:kline";
21523
- positiveInt = (value, fallback) => {
21995
+ positiveInt2 = (value, fallback) => {
21524
21996
  const parsed = Number(value);
21525
21997
  return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
21526
21998
  };
21527
21999
  redisOptions = () => ({
21528
22000
  host: process.env.REDIS_HOST || "127.0.0.1",
21529
- port: positiveInt(process.env.REDIS_PORT, 6379),
21530
- connectTimeout: positiveInt(process.env.REDIS_CONNECT_TIMEOUT_MS, 3e3),
22001
+ port: positiveInt2(process.env.REDIS_PORT, 6379),
22002
+ connectTimeout: positiveInt2(process.env.REDIS_CONNECT_TIMEOUT_MS, 3e3),
21531
22003
  maxRetriesPerRequest: null,
21532
22004
  retryStrategy: (attempt) => Math.min(attempt * 200, 2e3)
21533
22005
  });
@@ -21718,45 +22190,524 @@ var init_marketWs = __esm({
21718
22190
  }
21719
22191
  });
21720
22192
 
22193
+ // src/scripts/hyperliquidWhaleBackfill.ts
22194
+ var hyperliquidWhaleBackfill_exports = {};
22195
+ __export(hyperliquidWhaleBackfill_exports, {
22196
+ main: () => main24
22197
+ });
22198
+ var import_args18, flags18, parseTime, main24;
22199
+ var init_hyperliquidWhaleBackfill2 = __esm({
22200
+ "src/scripts/hyperliquidWhaleBackfill.ts"() {
22201
+ "use strict";
22202
+ import_args18 = __toESM(require("args"));
22203
+ init_hyperliquidWhaleBackfill();
22204
+ import_args18.default.option(["d", "days"], "Historical lookback in days", 1);
22205
+ import_args18.default.option(["f", "from"], "Inclusive ISO timestamp");
22206
+ import_args18.default.option(["t", "to"], "Exclusive ISO timestamp");
22207
+ import_args18.default.option(
22208
+ ["c", "cacheOnly"],
22209
+ "Do not call Hyperliquid when coverage is missing"
22210
+ );
22211
+ flags18 = import_args18.default.parse(process.argv);
22212
+ parseTime = (value, fallback) => {
22213
+ if (value == null || value === "") return fallback;
22214
+ const parsed = Date.parse(String(value));
22215
+ if (!Number.isFinite(parsed)) throw new Error(`Invalid timestamp: ${value}`);
22216
+ return parsed;
22217
+ };
22218
+ main24 = async () => {
22219
+ const toMs = parseTime(flags18.to, Date.now());
22220
+ const days = Number(flags18.days);
22221
+ const lookbackMs = (Number.isFinite(days) && days > 0 ? days : 1) * 864e5;
22222
+ const startMs = parseTime(flags18.from, toMs - lookbackMs);
22223
+ if (startMs >= toMs) throw new Error("--from must be before --to");
22224
+ const result = await backfillHyperliquidWhaleContext({
22225
+ startMs,
22226
+ endMs: toMs,
22227
+ cacheOnly: Boolean(flags18.cacheOnly),
22228
+ log: console.log
22229
+ });
22230
+ console.log(JSON.stringify(result, null, 2));
22231
+ };
22232
+ }
22233
+ });
22234
+
22235
+ // src/lib/hyperliquidWhaleStream.ts
22236
+ var import_ws2, import_timescale11, import_strategies8, DEFAULT_WS_URL, EVENT_BATCH_SIZE, FLUSH_INTERVAL_MS, FINALIZE_INTERVAL_MS, FINALIZE_LAG_MS, REBUILD_LOOKBACK_MS, RAW_EVENT_RETENTION_MS, sleep5, parseHyperliquidUserFillsMessage, runHyperliquidWhaleStream;
22237
+ var init_hyperliquidWhaleStream = __esm({
22238
+ "src/lib/hyperliquidWhaleStream.ts"() {
22239
+ "use strict";
22240
+ import_ws2 = require("ws");
22241
+ import_timescale11 = require("@tradejs/infra/timescale");
22242
+ import_strategies8 = require("@tradejs/node/strategies");
22243
+ init_hyperliquidWhaleData();
22244
+ DEFAULT_WS_URL = "wss://api.hyperliquid.xyz/ws";
22245
+ EVENT_BATCH_SIZE = 500;
22246
+ FLUSH_INTERVAL_MS = 2e3;
22247
+ FINALIZE_INTERVAL_MS = 15e3;
22248
+ FINALIZE_LAG_MS = 5e3;
22249
+ REBUILD_LOOKBACK_MS = 10 * 6e4;
22250
+ RAW_EVENT_RETENTION_MS = 48 * 60 * 6e4;
22251
+ sleep5 = (ms, signal) => new Promise((resolve) => {
22252
+ if (signal.aborted) return resolve();
22253
+ const timer = setTimeout(resolve, ms);
22254
+ signal.addEventListener(
22255
+ "abort",
22256
+ () => {
22257
+ clearTimeout(timer);
22258
+ resolve();
22259
+ },
22260
+ { once: true }
22261
+ );
22262
+ });
22263
+ parseHyperliquidUserFillsMessage = (raw) => {
22264
+ try {
22265
+ const message = JSON.parse(raw);
22266
+ if (message.channel !== "userFills" || !message.data || typeof message.data !== "object" || Array.isArray(message.data)) {
22267
+ return null;
22268
+ }
22269
+ const data = message.data;
22270
+ if (typeof data.user !== "string" || !Array.isArray(data.fills)) {
22271
+ return null;
22272
+ }
22273
+ return {
22274
+ address: data.user,
22275
+ isSnapshot: data.isSnapshot === true,
22276
+ fills: data.fills
22277
+ };
22278
+ } catch {
22279
+ return null;
22280
+ }
22281
+ };
22282
+ runHyperliquidWhaleStream = async (params) => {
22283
+ const universe = (0, import_strategies8.getHyperliquidPerpUniverseSnapshot)();
22284
+ const whales = (0, import_strategies8.getHyperliquidWhaleRegistrySnapshot)();
22285
+ const trackedSymbols = new Set(universe.symbols);
22286
+ const identity = {
22287
+ universeFingerprint: universe.fingerprint,
22288
+ whaleRegistryFingerprint: whales.fingerprint
22289
+ };
22290
+ const buffer = [];
22291
+ let writes = Promise.resolve();
22292
+ let streamCoverageFromMs = null;
22293
+ let streamCoverageToMs = 0;
22294
+ const flush = () => {
22295
+ if (!buffer.length) return writes;
22296
+ const batch = buffer.splice(0, buffer.length);
22297
+ writes = writes.then(() => (0, import_timescale11.upsertHyperliquidWhaleTradeEvents)(batch));
22298
+ return writes;
22299
+ };
22300
+ const recordStreamCoverage = (toMs) => {
22301
+ if (streamCoverageFromMs == null) return writes;
22302
+ const fromMs = Math.max(streamCoverageFromMs, streamCoverageToMs);
22303
+ if (toMs <= fromMs) return writes;
22304
+ const rows = Array.from(
22305
+ { length: Math.floor((toMs - fromMs) / HYPERLIQUID_WHALE_BUCKET_MS) },
22306
+ (_3, index) => ({
22307
+ ts: new Date(fromMs + index * HYPERLIQUID_WHALE_BUCKET_MS),
22308
+ coveredWhales: whales.addresses.length,
22309
+ expectedWhales: whales.addresses.length,
22310
+ coveragePct: 1,
22311
+ source: "hyperliquid_user_fills_ws",
22312
+ ...identity
22313
+ })
22314
+ );
22315
+ streamCoverageToMs = toMs;
22316
+ writes = writes.then(() => (0, import_timescale11.upsertHyperliquidWhaleCoverageRows)(rows));
22317
+ return writes;
22318
+ };
22319
+ const finalize = () => {
22320
+ const toMs = Math.floor((Date.now() - FINALIZE_LAG_MS) / HYPERLIQUID_WHALE_BUCKET_MS) * HYPERLIQUID_WHALE_BUCKET_MS;
22321
+ const fromMs = Math.max(0, toMs - REBUILD_LOOKBACK_MS);
22322
+ recordStreamCoverage(toMs);
22323
+ writes = writes.then(
22324
+ () => (0, import_timescale11.rebuildHyperliquidWhaleFlowRows)({
22325
+ fromMs,
22326
+ toMs,
22327
+ deleteEventsBeforeMs: toMs - RAW_EVENT_RETENTION_MS,
22328
+ ...identity
22329
+ }).then((buckets) => {
22330
+ params.log?.(`Hyperliquid whale-flow finalized buckets=${buckets}`);
22331
+ })
22332
+ );
22333
+ return writes;
22334
+ };
22335
+ const flushTimer = setInterval(() => void flush(), FLUSH_INTERVAL_MS);
22336
+ const finalizeTimer = setInterval(
22337
+ () => void flush().then(finalize),
22338
+ FINALIZE_INTERVAL_MS
22339
+ );
22340
+ let reconnectDelayMs = 1e3;
22341
+ try {
22342
+ while (!params.signal.aborted) {
22343
+ await new Promise((resolve) => {
22344
+ const ws = new import_ws2.WebSocket(
22345
+ process.env.HYPERLIQUID_WS_URL?.trim() || DEFAULT_WS_URL
22346
+ );
22347
+ let settled = false;
22348
+ const finish = () => {
22349
+ if (settled) return;
22350
+ settled = true;
22351
+ resolve();
22352
+ };
22353
+ const pingTimer = setInterval(() => {
22354
+ if (ws.readyState === import_ws2.WebSocket.OPEN) {
22355
+ ws.send(JSON.stringify({ method: "ping" }));
22356
+ }
22357
+ }, 3e4);
22358
+ const close = () => {
22359
+ clearInterval(pingTimer);
22360
+ if (ws.readyState === import_ws2.WebSocket.OPEN) ws.close();
22361
+ else if (ws.readyState === import_ws2.WebSocket.CONNECTING) ws.terminate();
22362
+ finish();
22363
+ };
22364
+ params.signal.addEventListener("abort", close, { once: true });
22365
+ ws.on("open", () => {
22366
+ reconnectDelayMs = 1e3;
22367
+ streamCoverageFromMs = Math.ceil(Date.now() / HYPERLIQUID_WHALE_BUCKET_MS) * HYPERLIQUID_WHALE_BUCKET_MS;
22368
+ streamCoverageToMs = streamCoverageFromMs;
22369
+ for (const address of whales.addresses) {
22370
+ ws.send(
22371
+ JSON.stringify({
22372
+ method: "subscribe",
22373
+ subscription: { type: "userFills", user: address }
22374
+ })
22375
+ );
22376
+ }
22377
+ params.log?.(
22378
+ `Hyperliquid whale stream connected userFills subscriptions=${whales.addresses.length}`
22379
+ );
22380
+ });
22381
+ ws.on("message", (data) => {
22382
+ const message = parseHyperliquidUserFillsMessage(data.toString());
22383
+ if (!message) return;
22384
+ for (const fill of message.fills) {
22385
+ const event = normalizeHyperliquidUserFill({
22386
+ fill,
22387
+ address: message.address,
22388
+ trackedSymbols,
22389
+ identity
22390
+ });
22391
+ if (event) buffer.push(event);
22392
+ }
22393
+ if (buffer.length >= EVENT_BATCH_SIZE) void flush();
22394
+ });
22395
+ ws.on("error", (error) => {
22396
+ params.log?.(`Hyperliquid whale stream error: ${String(error)}`);
22397
+ close();
22398
+ });
22399
+ ws.on("close", () => {
22400
+ recordStreamCoverage(
22401
+ Math.floor(Date.now() / HYPERLIQUID_WHALE_BUCKET_MS) * HYPERLIQUID_WHALE_BUCKET_MS
22402
+ );
22403
+ streamCoverageFromMs = null;
22404
+ clearInterval(pingTimer);
22405
+ params.signal.removeEventListener("abort", close);
22406
+ finish();
22407
+ });
22408
+ });
22409
+ if (!params.signal.aborted) {
22410
+ await sleep5(reconnectDelayMs, params.signal);
22411
+ reconnectDelayMs = Math.min(3e4, reconnectDelayMs * 2);
22412
+ }
22413
+ }
22414
+ } finally {
22415
+ clearInterval(flushTimer);
22416
+ clearInterval(finalizeTimer);
22417
+ await flush();
22418
+ await finalize();
22419
+ }
22420
+ };
22421
+ }
22422
+ });
22423
+
22424
+ // src/scripts/hyperliquidWhaleIngest.ts
22425
+ var hyperliquidWhaleIngest_exports = {};
22426
+ __export(hyperliquidWhaleIngest_exports, {
22427
+ main: () => main25
22428
+ });
22429
+ var main25;
22430
+ var init_hyperliquidWhaleIngest = __esm({
22431
+ "src/scripts/hyperliquidWhaleIngest.ts"() {
22432
+ "use strict";
22433
+ init_hyperliquidWhaleStream();
22434
+ main25 = async () => {
22435
+ const controller = new AbortController();
22436
+ const stop = () => controller.abort();
22437
+ process.once("SIGINT", stop);
22438
+ process.once("SIGTERM", stop);
22439
+ try {
22440
+ await runHyperliquidWhaleStream({
22441
+ signal: controller.signal,
22442
+ log: console.log
22443
+ });
22444
+ } finally {
22445
+ process.removeListener("SIGINT", stop);
22446
+ process.removeListener("SIGTERM", stop);
22447
+ }
22448
+ };
22449
+ }
22450
+ });
22451
+
22452
+ // src/lib/hyperliquidWhaleSelection.ts
22453
+ var HYPERLIQUID_WHALE_SELECTION, finiteNumber3, median, evaluateHyperliquidWhaleStructure, rankHyperliquidStructuralWhales;
22454
+ var init_hyperliquidWhaleSelection = __esm({
22455
+ "src/lib/hyperliquidWhaleSelection.ts"() {
22456
+ "use strict";
22457
+ HYPERLIQUID_WHALE_SELECTION = {
22458
+ minimumAccountValueUsd: 5e5,
22459
+ minimumActiveDays: 1,
22460
+ maximumRawFillsPerDay: 20,
22461
+ maximumDirectionalExecutionsPerDay: 10,
22462
+ minimumMedianNotionalUsd: 0,
22463
+ minimumMedianInterExecutionMinutes: 0,
22464
+ minimumTop30NotionalShare: 1e-3,
22465
+ minimumTurnoverToEquity: 0.05,
22466
+ maximumTurnoverToEquity: 5
22467
+ };
22468
+ finiteNumber3 = (value) => {
22469
+ const parsed = Number(value);
22470
+ return Number.isFinite(parsed) ? parsed : 0;
22471
+ };
22472
+ median = (values) => {
22473
+ if (!values.length) return 0;
22474
+ const sorted = [...values].sort((left, right) => left - right);
22475
+ const middle = Math.floor(sorted.length / 2);
22476
+ return sorted.length % 2 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2;
22477
+ };
22478
+ evaluateHyperliquidWhaleStructure = (params) => {
22479
+ const fills = params.fills.filter((fill) => {
22480
+ const time = finiteNumber3(fill.time);
22481
+ return time >= params.calibrationFromMs && time < params.calibrationToMs;
22482
+ }).sort((left, right) => finiteNumber3(left.time) - finiteNumber3(right.time));
22483
+ const days = Math.max(
22484
+ 1,
22485
+ (params.calibrationToMs - params.calibrationFromMs) / 864e5
22486
+ );
22487
+ const executions = fills.reduce((clusters, fill) => {
22488
+ const coin = String(fill.coin);
22489
+ const side = String(fill.side);
22490
+ const time = finiteNumber3(fill.time);
22491
+ const notionalUsd = Math.abs(finiteNumber3(fill.px) * finiteNumber3(fill.sz));
22492
+ const previous = clusters.at(-1);
22493
+ if (previous && previous.coin === coin && previous.side === side && time - previous.time <= 6e4) {
22494
+ previous.time = time;
22495
+ previous.notionalUsd += notionalUsd;
22496
+ } else {
22497
+ clusters.push({ coin, side, time, notionalUsd });
22498
+ }
22499
+ return clusters;
22500
+ }, []);
22501
+ const notionals = executions.map((execution) => execution.notionalUsd);
22502
+ const totalNotional = notionals.reduce((sum2, value) => sum2 + value, 0);
22503
+ const top30Notional = executions.reduce(
22504
+ (sum2, execution) => sum2 + (params.top30Symbols.has(execution.coin) ? execution.notionalUsd : 0),
22505
+ 0
22506
+ );
22507
+ const interExecutionMinutes = executions.slice(1).map(
22508
+ (execution, index) => (execution.time - executions[index].time) / 6e4
22509
+ ).filter((value) => value >= 0);
22510
+ const activeDays = new Set(
22511
+ fills.map(
22512
+ (fill) => new Date(finiteNumber3(fill.time)).toISOString().slice(0, 10)
22513
+ )
22514
+ ).size;
22515
+ const rawFillsPerDay = fills.length / days;
22516
+ const directionalExecutionsPerDay = executions.length / days;
22517
+ const medianNotionalUsd = median(notionals);
22518
+ const medianInterExecutionMinutes = median(interExecutionMinutes);
22519
+ const top30NotionalShare = totalNotional > 0 ? top30Notional / totalNotional : 0;
22520
+ const eligible = params.accountValueUsd >= HYPERLIQUID_WHALE_SELECTION.minimumAccountValueUsd && activeDays >= HYPERLIQUID_WHALE_SELECTION.minimumActiveDays && rawFillsPerDay <= HYPERLIQUID_WHALE_SELECTION.maximumRawFillsPerDay && directionalExecutionsPerDay <= HYPERLIQUID_WHALE_SELECTION.maximumDirectionalExecutionsPerDay && medianNotionalUsd >= HYPERLIQUID_WHALE_SELECTION.minimumMedianNotionalUsd && medianInterExecutionMinutes >= HYPERLIQUID_WHALE_SELECTION.minimumMedianInterExecutionMinutes && top30NotionalShare >= HYPERLIQUID_WHALE_SELECTION.minimumTop30NotionalShare;
22521
+ const score = Math.log1p(params.accountValueUsd) * Math.log1p(medianNotionalUsd) * Math.log1p(Math.max(1, medianInterExecutionMinutes)) * Math.sqrt(Math.max(0, activeDays)) * top30NotionalShare / Math.sqrt(1 + rawFillsPerDay);
22522
+ return {
22523
+ address: params.address.toLowerCase(),
22524
+ accountValueUsd: params.accountValueUsd,
22525
+ rawFills: fills.length,
22526
+ rawFillsPerDay,
22527
+ directionalExecutions: executions.length,
22528
+ directionalExecutionsPerDay,
22529
+ activeDays,
22530
+ medianNotionalUsd,
22531
+ medianInterExecutionMinutes,
22532
+ top30NotionalShare,
22533
+ score,
22534
+ eligible
22535
+ };
22536
+ };
22537
+ rankHyperliquidStructuralWhales = (metrics, limit = 100) => metrics.filter((row) => row.eligible).sort(
22538
+ (left, right) => right.score - left.score || left.address.localeCompare(right.address)
22539
+ ).slice(0, limit);
22540
+ }
22541
+ });
22542
+
22543
+ // src/scripts/hyperliquidWhalesUpdate.ts
22544
+ var hyperliquidWhalesUpdate_exports = {};
22545
+ __export(hyperliquidWhalesUpdate_exports, {
22546
+ main: () => main26
22547
+ });
22548
+ var import_config8, import_args19, import_node_crypto4, import_strategies9, LEADERBOARD_URL, DAY_MS8, CANDIDATE_LIMIT, flags19, fingerprint, runConcurrent2, main26;
22549
+ var init_hyperliquidWhalesUpdate = __esm({
22550
+ "src/scripts/hyperliquidWhalesUpdate.ts"() {
22551
+ "use strict";
22552
+ import_config8 = require("dotenv/config");
22553
+ import_args19 = __toESM(require("args"));
22554
+ import_node_crypto4 = require("crypto");
22555
+ import_strategies9 = require("@tradejs/node/strategies");
22556
+ init_hyperliquidWhaleBackfill();
22557
+ init_hyperliquidRateLimiter();
22558
+ init_hyperliquidWhaleSelection();
22559
+ LEADERBOARD_URL = "https://stats-data.hyperliquid.xyz/Mainnet/leaderboard";
22560
+ DAY_MS8 = 864e5;
22561
+ CANDIDATE_LIMIT = 400;
22562
+ import_args19.default.option(
22563
+ ["t", "to"],
22564
+ "Exclusive calibration end (ISO); defaults to the current UTC day"
22565
+ );
22566
+ import_args19.default.option(["d", "days"], "Calibration window in days", 7);
22567
+ flags19 = import_args19.default.parse(process.argv);
22568
+ fingerprint = (addresses) => (0, import_node_crypto4.createHash)("sha256").update(JSON.stringify(addresses)).digest("hex").slice(0, 16);
22569
+ runConcurrent2 = async (params) => {
22570
+ let nextIndex = 0;
22571
+ await Promise.all(
22572
+ Array.from({ length: params.concurrency }, async () => {
22573
+ for (; ; ) {
22574
+ const index = nextIndex;
22575
+ nextIndex += 1;
22576
+ if (index >= params.items.length) return;
22577
+ await params.worker(params.items[index]);
22578
+ }
22579
+ })
22580
+ );
22581
+ };
22582
+ main26 = async () => {
22583
+ const explicitTo = flags19.to == null ? Number.NaN : Date.parse(String(flags19.to));
22584
+ const calibrationToMs = Number.isFinite(explicitTo) ? explicitTo : Math.floor(Date.now() / DAY_MS8) * DAY_MS8;
22585
+ const calibrationDays = Math.max(1, Number(flags19.days) || 7);
22586
+ const calibrationFromMs = calibrationToMs - calibrationDays * DAY_MS8;
22587
+ const response = await fetch(LEADERBOARD_URL);
22588
+ if (!response.ok) {
22589
+ throw new Error(`Hyperliquid leaderboard ${response.status}`);
22590
+ }
22591
+ const payload = await response.json();
22592
+ const candidates = (payload.leaderboardRows ?? []).map((row) => {
22593
+ const address = String(row.ethAddress ?? "").toLowerCase();
22594
+ const accountValueUsd = Number(row.accountValue);
22595
+ const weeklyVolumeUsd = Number(
22596
+ row.windowPerformances?.find(([window2]) => window2 === "week")?.[1].vlm
22597
+ );
22598
+ return {
22599
+ address,
22600
+ accountValueUsd,
22601
+ turnoverToEquity: accountValueUsd > 0 ? weeklyVolumeUsd / accountValueUsd : 0
22602
+ };
22603
+ }).filter(
22604
+ (row) => /^0x[0-9a-f]{40}$/.test(row.address) && row.accountValueUsd >= HYPERLIQUID_WHALE_SELECTION.minimumAccountValueUsd && row.turnoverToEquity >= HYPERLIQUID_WHALE_SELECTION.minimumTurnoverToEquity && row.turnoverToEquity <= HYPERLIQUID_WHALE_SELECTION.maximumTurnoverToEquity
22605
+ ).sort(
22606
+ (left, right) => left.turnoverToEquity - right.turnoverToEquity || right.accountValueUsd - left.accountValueUsd || left.address.localeCompare(right.address)
22607
+ ).slice(0, CANDIDATE_LIMIT);
22608
+ const top30Symbols = new Set((0, import_strategies9.getHyperliquidPerpUniverseSnapshot)().symbols);
22609
+ const limiter = new HyperliquidInfoRateLimiter();
22610
+ const metrics = [];
22611
+ let completed = 0;
22612
+ await runConcurrent2({
22613
+ items: candidates,
22614
+ concurrency: 2,
22615
+ worker: async (candidate) => {
22616
+ const result = await fetchHyperliquidUserFillsByTime({
22617
+ address: candidate.address,
22618
+ startTime: calibrationFromMs,
22619
+ endTime: calibrationToMs - 1,
22620
+ rateLimiter: limiter,
22621
+ maxPages: 1
22622
+ });
22623
+ metrics.push(
22624
+ evaluateHyperliquidWhaleStructure({
22625
+ address: candidate.address,
22626
+ accountValueUsd: candidate.accountValueUsd,
22627
+ fills: result.fills,
22628
+ calibrationFromMs,
22629
+ calibrationToMs,
22630
+ top30Symbols
22631
+ })
22632
+ );
22633
+ completed += 1;
22634
+ if (completed % 10 === 0 || completed === candidates.length) {
22635
+ console.log(
22636
+ `Hyperliquid whale selection ${completed}/${candidates.length}; eligible=${metrics.filter((row) => row.eligible).length}`
22637
+ );
22638
+ }
22639
+ }
22640
+ });
22641
+ const selected = rankHyperliquidStructuralWhales(metrics, 100);
22642
+ if (selected.length !== 100) {
22643
+ throw new Error(
22644
+ `Only ${selected.length} structural whales passed selection from ${candidates.length} candidates`
22645
+ );
22646
+ }
22647
+ const addresses = selected.map((row) => row.address);
22648
+ const timestamp = new Date(calibrationToMs).toISOString();
22649
+ const snapshot = {
22650
+ schemaVersion: 2,
22651
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
22652
+ source: "hyperliquid_structural_fills_snapshot",
22653
+ selection: {
22654
+ calibrationFrom: new Date(calibrationFromMs).toISOString(),
22655
+ calibrationTo: timestamp,
22656
+ effectiveFrom: timestamp,
22657
+ candidateLimit: CANDIDATE_LIMIT,
22658
+ ...HYPERLIQUID_WHALE_SELECTION,
22659
+ score: "log1p(accountValue)*log1p(medianExecutionNotional)*log1p(max(1,medianInterExecutionMinutes))*sqrt(activeDays)*top30Share/sqrt(1+rawFillsPerDay)",
22660
+ forbiddenSelectionMetrics: ["pnl", "roi", "winRate", "closedPnl"]
22661
+ },
22662
+ fingerprint: fingerprint(addresses),
22663
+ size: 100,
22664
+ addresses
22665
+ };
22666
+ console.log(JSON.stringify(snapshot, null, 2));
22667
+ return snapshot;
22668
+ };
22669
+ }
22670
+ });
22671
+
21721
22672
  // src/scripts/mlExport.ts
21722
22673
  var mlExport_exports = {};
21723
22674
  __export(mlExport_exports, {
21724
- main: () => main24
22675
+ main: () => main27
21725
22676
  });
21726
- var import_args18, import_chalk31, import_promises10, import_path7, import_ml, flags18, resolveRunScopedMlChunks, main24;
22677
+ var import_args20, import_chalk31, import_promises10, import_path7, import_ml, flags20, resolveRunScopedMlChunks, main27;
21727
22678
  var init_mlExport = __esm({
21728
22679
  "src/scripts/mlExport.ts"() {
21729
22680
  "use strict";
21730
- import_args18 = __toESM(require("args"));
22681
+ import_args20 = __toESM(require("args"));
21731
22682
  import_chalk31 = __toESM(require("chalk"));
21732
22683
  import_promises10 = __toESM(require("fs/promises"));
21733
22684
  import_path7 = __toESM(require("path"));
21734
22685
  import_ml = require("@tradejs/infra/ml");
21735
22686
  init_checkpoint();
21736
22687
  init_resolveExportStrategy();
21737
- import_args18.default.example(
22688
+ import_args20.default.example(
21738
22689
  "yarn ts-node ./src/scripts/mlExport --strategy trendline",
21739
22690
  "Merge per-worker ML chunks into one dataset JSONL"
21740
22691
  );
21741
- import_args18.default.option(["o", "outDir"], "Dataset directory", "data/ml/export");
21742
- import_args18.default.option(["s", "strategy"], "Strategy name", "");
21743
- import_args18.default.option(
22692
+ import_args20.default.option(["o", "outDir"], "Dataset directory", "data/ml/export");
22693
+ import_args20.default.option(["s", "strategy"], "Strategy name", "");
22694
+ import_args20.default.option(
21744
22695
  ["k", "keepChunks"],
21745
22696
  "Keep source chunk files after successful merge",
21746
22697
  false
21747
22698
  );
21748
- import_args18.default.option(["R", "runId"], "Backtest run id to export");
21749
- import_args18.default.option(
22699
+ import_args20.default.option(["R", "runId"], "Backtest run id to export");
22700
+ import_args20.default.option(
21750
22701
  ["u", "user"],
21751
22702
  "Backtest user for checkpoint-filtered export",
21752
22703
  "root"
21753
22704
  );
21754
- flags18 = import_args18.default.parse(process.argv);
22705
+ flags20 = import_args20.default.parse(process.argv);
21755
22706
  resolveRunScopedMlChunks = async ({
21756
22707
  outDir,
21757
22708
  strategyName
21758
22709
  }) => {
21759
- const requestedRunId = typeof flags18.runId === "string" && flags18.runId.trim() ? flags18.runId.trim() : "";
22710
+ const requestedRunId = typeof flags20.runId === "string" && flags20.runId.trim() ? flags20.runId.trim() : "";
21760
22711
  const runIds = requestedRunId ? [requestedRunId] : await (0, import_ml.listMlChunkRunIds)({ strategyName, outDir });
21761
22712
  const runId = runIds.at(-1) ?? "";
21762
22713
  if (!runId) {
@@ -21770,7 +22721,7 @@ var init_mlExport = __esm({
21770
22721
  completedAttemptKeys: /* @__PURE__ */ new Set()
21771
22722
  };
21772
22723
  }
21773
- const userName2 = String(flags18.user || "root");
22724
+ const userName2 = String(flags20.user || "root");
21774
22725
  const completed = await loadBacktestCheckpointResults({ runId, userName: userName2 });
21775
22726
  return {
21776
22727
  runId,
@@ -21778,11 +22729,11 @@ var init_mlExport = __esm({
21778
22729
  completedAttemptKeys: buildCompletedBacktestDatasetAttemptKeys(completed)
21779
22730
  };
21780
22731
  };
21781
- main24 = async () => {
21782
- const outDir = String(flags18.outDir || "data/ml/export");
22732
+ main27 = async () => {
22733
+ const outDir = String(flags20.outDir || "data/ml/export");
21783
22734
  await import_promises10.default.mkdir(outDir, { recursive: true });
21784
22735
  const strategyName = await resolveExportStrategy({
21785
- explicitStrategy: String(flags18.strategy || ""),
22736
+ explicitStrategy: String(flags20.strategy || ""),
21786
22737
  outDir,
21787
22738
  datasetLabel: "ML",
21788
22739
  promptLabel: "Select ML export strategy",
@@ -21822,7 +22773,7 @@ var init_mlExport = __esm({
21822
22773
  runScopedChunks.completedAttemptKeys
21823
22774
  ) : void 0
21824
22775
  });
21825
- const shouldDeleteChunks = !Boolean(flags18.keepChunks);
22776
+ const shouldDeleteChunks = !Boolean(flags20.keepChunks);
21826
22777
  if (shouldDeleteChunks) {
21827
22778
  for (const filePath of chunkFiles) {
21828
22779
  await import_promises10.default.rm(filePath, { force: true });
@@ -21844,35 +22795,35 @@ var init_mlExport = __esm({
21844
22795
  // src/scripts/mlInspect.ts
21845
22796
  var mlInspect_exports = {};
21846
22797
  __export(mlInspect_exports, {
21847
- main: () => main25
22798
+ main: () => main28
21848
22799
  });
21849
- var import_args19, import_chalk32, import_promises11, import_fs3, import_path8, import_readline2, import_child_process2, flags19, toMode, toInspectTool, selectInspectTool, asPositiveInt, readRowsHead, readRowsTail, readRowsSample, quantileSorted, mean2, std, findLatestDataset, buildNumericStats, formatNumber4, runQuickInspect, runYDataInspect, main25;
22800
+ var import_args21, import_chalk32, import_promises11, import_fs3, import_path8, import_readline2, import_child_process2, flags21, toMode, toInspectTool, selectInspectTool, asPositiveInt, readRowsHead, readRowsTail, readRowsSample, quantileSorted, mean2, std, findLatestDataset, buildNumericStats, formatNumber4, runQuickInspect, runYDataInspect, main28;
21850
22801
  var init_mlInspect = __esm({
21851
22802
  "src/scripts/mlInspect.ts"() {
21852
22803
  "use strict";
21853
- import_args19 = __toESM(require("args"));
22804
+ import_args21 = __toESM(require("args"));
21854
22805
  import_chalk32 = __toESM(require("chalk"));
21855
22806
  import_promises11 = __toESM(require("fs/promises"));
21856
22807
  import_fs3 = require("fs");
21857
22808
  import_path8 = __toESM(require("path"));
21858
22809
  import_readline2 = __toESM(require("readline"));
21859
22810
  import_child_process2 = require("child_process");
21860
- import_args19.default.example(
22811
+ import_args21.default.example(
21861
22812
  "yarn ml-inspect --rows 10000 --mode sample",
21862
22813
  "Inspect the latest ML dataset chunk and highlight problematic features"
21863
22814
  );
21864
- import_args19.default.option(["d", "dir"], "Dataset directory", "data/ml/export");
21865
- import_args19.default.option(["r", "rows"], "Rows to inspect", 1e4);
21866
- import_args19.default.option(["m", "mode"], "head | tail | sample", "sample");
21867
- import_args19.default.option(["S", "strategy"], "Strategy token in dataset filename", "");
21868
- import_args19.default.option(
22815
+ import_args21.default.option(["d", "dir"], "Dataset directory", "data/ml/export");
22816
+ import_args21.default.option(["r", "rows"], "Rows to inspect", 1e4);
22817
+ import_args21.default.option(["m", "mode"], "head | tail | sample", "sample");
22818
+ import_args21.default.option(["S", "strategy"], "Strategy token in dataset filename", "");
22819
+ import_args21.default.option(
21869
22820
  ["f", "file"],
21870
22821
  "Explicit dataset file path (overrides auto-select)"
21871
22822
  );
21872
- import_args19.default.option(["L", "limitIssues"], "How many fields to print in report", 25);
21873
- import_args19.default.option(["M", "minFieldValues"], "Min valid values per numeric field", 50);
21874
- import_args19.default.option(["T", "tool"], "quick | ydata", "");
21875
- flags19 = import_args19.default.parse(process.argv);
22823
+ import_args21.default.option(["L", "limitIssues"], "How many fields to print in report", 25);
22824
+ import_args21.default.option(["M", "minFieldValues"], "Min valid values per numeric field", 50);
22825
+ import_args21.default.option(["T", "tool"], "quick | ydata", "");
22826
+ flags21 = import_args21.default.parse(process.argv);
21876
22827
  toMode = (value) => {
21877
22828
  const mode = String(value || "sample").toLowerCase();
21878
22829
  if (mode === "head" || mode === "tail" || mode === "sample") {
@@ -22086,7 +23037,7 @@ var init_mlInspect = __esm({
22086
23037
  }
22087
23038
  const sorted = [...values].sort((a, b) => a - b);
22088
23039
  const q1 = quantileSorted(sorted, 0.25);
22089
- const median = quantileSorted(sorted, 0.5);
23040
+ const median2 = quantileSorted(sorted, 0.5);
22090
23041
  const q3 = quantileSorted(sorted, 0.75);
22091
23042
  const p95 = quantileSorted(sorted, 0.95);
22092
23043
  const p99 = quantileSorted(sorted, 0.99);
@@ -22109,7 +23060,7 @@ var init_mlInspect = __esm({
22109
23060
  max: sorted[sorted.length - 1],
22110
23061
  mean: valuesMean,
22111
23062
  std: valuesStd,
22112
- median,
23063
+ median: median2,
22113
23064
  q1,
22114
23065
  q3,
22115
23066
  p95,
@@ -22338,15 +23289,15 @@ var init_mlInspect = __esm({
22338
23289
  }
22339
23290
  console.log(import_chalk32.default.green(`Profile report saved: ${reportPath}`));
22340
23291
  };
22341
- main25 = async () => {
22342
- const dir = String(flags19.dir || "data/ml/export");
22343
- const rowsToInspect = asPositiveInt(flags19.rows, 1e4);
22344
- const mode = toMode(flags19.mode);
22345
- const strategy = String(flags19.strategy || "");
22346
- const limitIssues = asPositiveInt(flags19.limitIssues, 25);
22347
- const minFieldValues = asPositiveInt(flags19.minFieldValues, 50);
22348
- const toolFromFlags = toInspectTool(flags19.tool);
22349
- const explicitFile = flags19.file ? String(flags19.file) : "";
23292
+ main28 = async () => {
23293
+ const dir = String(flags21.dir || "data/ml/export");
23294
+ const rowsToInspect = asPositiveInt(flags21.rows, 1e4);
23295
+ const mode = toMode(flags21.mode);
23296
+ const strategy = String(flags21.strategy || "");
23297
+ const limitIssues = asPositiveInt(flags21.limitIssues, 25);
23298
+ const minFieldValues = asPositiveInt(flags21.minFieldValues, 50);
23299
+ const toolFromFlags = toInspectTool(flags21.tool);
23300
+ const explicitFile = flags21.file ? String(flags21.file) : "";
22350
23301
  const datasetPath = explicitFile || await findLatestDataset({
22351
23302
  dir,
22352
23303
  strategy
@@ -22380,9 +23331,9 @@ var init_mlInspect = __esm({
22380
23331
  // src/scripts/mlTrainLatestSelect.ts
22381
23332
  var mlTrainLatestSelect_exports = {};
22382
23333
  __export(mlTrainLatestSelect_exports, {
22383
- main: () => main26
23334
+ main: () => main29
22384
23335
  });
22385
- var import_path9, import_promises12, import_fs4, import_crypto2, import_child_process3, import_events, import_readline3, import_args20, import_chalk33, import_ml2, toFileToken5, asBool, asInt7, asNonNegativeInt, formatBytes, getMlContainerMemUsage, MODEL_TYPES, STRATEGIES, flags20, parseStrategy, parseModelType, selectModelType, listDatasetFiles, concatFiles, pipeNormalized, DAY_MS8, SPLIT_PROGRESS_EVERY, parseTimestampMs, scanMaxLabeledTimestampMs, scanMaxTrainTimestampMs, hashFileNameSha1, prepareTrainWindowFiles, main26;
23336
+ var import_path9, import_promises12, import_fs4, import_crypto2, import_child_process3, import_events, import_readline3, import_args22, import_chalk33, import_ml2, toFileToken5, asBool, asInt7, asNonNegativeInt, formatBytes, getMlContainerMemUsage, MODEL_TYPES, STRATEGIES, flags22, parseStrategy, parseModelType, selectModelType, listDatasetFiles, concatFiles, pipeNormalized, DAY_MS9, SPLIT_PROGRESS_EVERY, parseTimestampMs, scanMaxLabeledTimestampMs, scanMaxTrainTimestampMs, hashFileNameSha1, prepareTrainWindowFiles, main29;
22386
23337
  var init_mlTrainLatestSelect = __esm({
22387
23338
  "src/scripts/mlTrainLatestSelect.ts"() {
22388
23339
  "use strict";
@@ -22393,7 +23344,7 @@ var init_mlTrainLatestSelect = __esm({
22393
23344
  import_child_process3 = require("child_process");
22394
23345
  import_events = require("events");
22395
23346
  import_readline3 = __toESM(require("readline"));
22396
- import_args20 = __toESM(require("args"));
23347
+ import_args22 = __toESM(require("args"));
22397
23348
  import_chalk33 = __toESM(require("chalk"));
22398
23349
  init_selectStrategy();
22399
23350
  import_ml2 = require("@tradejs/infra/ml");
@@ -22457,17 +23408,17 @@ var init_mlTrainLatestSelect = __esm({
22457
23408
  "VolumeDivergence",
22458
23409
  "any"
22459
23410
  ];
22460
- import_args20.default.option(["s", "strategy"], "Strategy name (e.g. TrendLine)");
22461
- import_args20.default.option(
23411
+ import_args22.default.option(["s", "strategy"], "Strategy name (e.g. TrendLine)");
23412
+ import_args22.default.option(
22462
23413
  ["m", "model"],
22463
23414
  "Model type: catboost | random_forest | extra_trees | xgboost | lightgbm"
22464
23415
  );
22465
- import_args20.default.option(
23416
+ import_args22.default.option(
22466
23417
  ["L", "latestOnly"],
22467
23418
  "Use only latest dataset file (overrides ML_TRAIN_USE_LATEST_ONLY)",
22468
23419
  false
22469
23420
  );
22470
- flags20 = import_args20.default.parse(process.argv);
23421
+ flags22 = import_args22.default.parse(process.argv);
22471
23422
  parseStrategy = (value) => {
22472
23423
  const raw = String(value ?? "").trim();
22473
23424
  if (!raw) return null;
@@ -22573,7 +23524,7 @@ var init_mlTrainLatestSelect = __esm({
22573
23524
  }
22574
23525
  };
22575
23526
  };
22576
- DAY_MS8 = 24 * 60 * 60 * 1e3;
23527
+ DAY_MS9 = 24 * 60 * 60 * 1e3;
22577
23528
  SPLIT_PROGRESS_EVERY = 2e5;
22578
23529
  parseTimestampMs = (value) => {
22579
23530
  const numeric = Number(value);
@@ -22790,7 +23741,7 @@ var init_mlTrainLatestSelect = __esm({
22790
23741
  }
22791
23742
  console.log("[split] phase 1/3: scanning max labeled timestamp...");
22792
23743
  const maxLabeledTs = await scanMaxLabeledTimestampMs(inputPath);
22793
- const holdoutCutoffMs = maxLabeledTs - testDays * DAY_MS8;
23744
+ const holdoutCutoffMs = maxLabeledTs - testDays * DAY_MS9;
22794
23745
  console.log("[split] phase 2/3: scanning max train timestamp...");
22795
23746
  const maxTrainTs = await scanMaxTrainTimestampMs(inputPath, holdoutCutoffMs);
22796
23747
  const {
@@ -23103,16 +24054,16 @@ var init_mlTrainLatestSelect = __esm({
23103
24054
  }
23104
24055
  };
23105
24056
  };
23106
- main26 = async () => {
23107
- const selectedFromCli = parseStrategy(flags20.strategy);
24057
+ main29 = async () => {
24058
+ const selectedFromCli = parseStrategy(flags22.strategy);
23108
24059
  const selected = selectedFromCli ?? await selectStrategy();
23109
24060
  const envModelType = (process.env.ML_MODEL_TYPE ?? "random_forest").trim().toLowerCase();
23110
24061
  const defaultModelType = MODEL_TYPES.includes(
23111
24062
  envModelType
23112
24063
  ) ? envModelType : "random_forest";
23113
- const modelFromCli = parseModelType(flags20.model);
24064
+ const modelFromCli = parseModelType(flags22.model);
23114
24065
  const modelType = modelFromCli ?? await selectModelType(defaultModelType);
23115
- const useLatestOnly = Boolean(flags20.latestOnly) || asBool(process.env.ML_TRAIN_USE_LATEST_ONLY);
24066
+ const useLatestOnly = Boolean(flags22.latestOnly) || asBool(process.env.ML_TRAIN_USE_LATEST_ONLY);
23116
24067
  const exportDir = import_path9.default.join(process.cwd(), "data", "ml", "export");
23117
24068
  const modelDirRoot = import_path9.default.join(process.cwd(), "data", "ml");
23118
24069
  const datasetFiles = await listDatasetFiles(exportDir, selected);
@@ -23367,7 +24318,7 @@ __export(researchAuto_exports, {
23367
24318
  isProcessAlive: () => isProcessAlive,
23368
24319
  listMergedFiles: () => listMergedFiles3,
23369
24320
  listRuntimeStrategyNames: () => listRuntimeStrategyNames,
23370
- main: () => main27,
24321
+ main: () => main30,
23371
24322
  parseJsonOutput: () => parseJsonOutput,
23372
24323
  releaseResearchAutoLock: () => releaseResearchAutoLock,
23373
24324
  resolveHeapMbFromEnv: () => resolveHeapMbFromEnv,
@@ -23377,11 +24328,11 @@ __export(researchAuto_exports, {
23377
24328
  toStrategyConfigGrid: () => toStrategyConfigGrid2,
23378
24329
  trimTextTail: () => trimTextTail2
23379
24330
  });
23380
- var import_args21, import_chalk34, import_node_child_process4, import_promises13, import_node_os2, import_node_path7, import_constants13, import_ai7, import_logger5, import_redis11, import_strategies6, flags21, projectRoot8, dotenvPath, getResearchAutoLockPath, readResearchAutoLock, isProcessAlive, acquireResearchAutoLock, releaseResearchAutoLock, resolvePositiveInteger, resolveHeapMbFromEnv, buildNodeOptions, RESEARCH_AUTO_PARENT_HEAP_MB, RESEARCH_AUTO_BACKTEST_PARENT_HEAP_MB, RESEARCH_AUTO_AI_TRAIN_PARENT_HEAP_MB, RESEARCH_AUTO_AGENT_PARENT_HEAP_MB, trimTextTail2, escapeHtml2, createEmptySteps, saveRun, listMergedFiles3, getBacktestResultsPrefix, listRuntimeStrategyNames, toStrategyConfigGrid2, formatMskDateTime, buildTelegramReport, runCliCommand, resolveTarget, parseJsonOutput, logResearch, executeNonBlockingStep, main27;
24331
+ var import_args23, import_chalk34, import_node_child_process4, import_promises13, import_node_os2, import_node_path7, import_constants13, import_ai7, import_logger5, import_redis11, import_strategies10, flags23, projectRoot8, dotenvPath, getResearchAutoLockPath, readResearchAutoLock, isProcessAlive, acquireResearchAutoLock, releaseResearchAutoLock, resolvePositiveInteger, resolveHeapMbFromEnv, buildNodeOptions, RESEARCH_AUTO_PARENT_HEAP_MB, RESEARCH_AUTO_BACKTEST_PARENT_HEAP_MB, RESEARCH_AUTO_AI_TRAIN_PARENT_HEAP_MB, RESEARCH_AUTO_AGENT_PARENT_HEAP_MB, trimTextTail2, escapeHtml2, createEmptySteps, saveRun, listMergedFiles3, getBacktestResultsPrefix, listRuntimeStrategyNames, toStrategyConfigGrid2, formatMskDateTime, buildTelegramReport, runCliCommand, resolveTarget, parseJsonOutput, logResearch, executeNonBlockingStep, main30;
23381
24332
  var init_researchAuto = __esm({
23382
24333
  "src/scripts/researchAuto.ts"() {
23383
24334
  "use strict";
23384
- import_args21 = __toESM(require("args"));
24335
+ import_args23 = __toESM(require("args"));
23385
24336
  import_chalk34 = __toESM(require("chalk"));
23386
24337
  import_node_child_process4 = require("child_process");
23387
24338
  import_promises13 = __toESM(require("fs/promises"));
@@ -23391,32 +24342,32 @@ var init_researchAuto = __esm({
23391
24342
  import_ai7 = require("@tradejs/infra/ai");
23392
24343
  import_logger5 = require("@tradejs/infra/logger");
23393
24344
  import_redis11 = require("@tradejs/infra/redis");
23394
- import_strategies6 = require("@tradejs/strategies");
24345
+ import_strategies10 = require("@tradejs/strategies");
23395
24346
  init_runtimeRedis();
23396
24347
  init_runtimeStrategyBacktest();
23397
24348
  init_telegramReports();
23398
24349
  init_runtimeRedis();
23399
- import_args21.default.example(
24350
+ import_args23.default.example(
23400
24351
  "yarn cli:node8g research:auto --strategy TrendLine --days 45",
23401
24352
  "Run the nightly backtest -> ai-export -> ai-train local research pipeline and save structured results to Redis"
23402
24353
  );
23403
- import_args21.default.option(["s", "strategy"], "Strategy name");
23404
- import_args21.default.option(["c", "config"], "Backtest config name");
23405
- import_args21.default.option(["U", "user"], "Use user config", "root");
23406
- import_args21.default.option(["o", "connector"], "Backtest connector", "bybit");
23407
- import_args21.default.option(["f", "timeframe"], "Backtest timeframe", "15");
23408
- import_args21.default.option(["d", "days"], "Backtest days window", 45);
23409
- import_args21.default.option(["n", "recent"], "AI train recent rows window", 1e3);
23410
- import_args21.default.option(["k", "skip"], "AI train skip rows from end", 0);
23411
- import_args21.default.option(
24354
+ import_args23.default.option(["s", "strategy"], "Strategy name");
24355
+ import_args23.default.option(["c", "config"], "Backtest config name");
24356
+ import_args23.default.option(["U", "user"], "Use user config", "root");
24357
+ import_args23.default.option(["o", "connector"], "Backtest connector", "bybit");
24358
+ import_args23.default.option(["f", "timeframe"], "Backtest timeframe", "15");
24359
+ import_args23.default.option(["d", "days"], "Backtest days window", 45);
24360
+ import_args23.default.option(["n", "recent"], "AI train recent rows window", 1e3);
24361
+ import_args23.default.option(["k", "skip"], "AI train skip rows from end", 0);
24362
+ import_args23.default.option(
23412
24363
  ["M", "minQuality"],
23413
24364
  "Minimum AI quality required to approve entry",
23414
24365
  4
23415
24366
  );
23416
- import_args21.default.option(["O", "outDir"], "AI export output directory", "data/ai/export");
23417
- import_args21.default.option(["A", "skipAgent"], "Skip direct research agent invocation", false);
23418
- import_args21.default.option(["j", "json"], "Print structured JSON summary", false);
23419
- flags21 = import_args21.default.parse(process.argv);
24367
+ import_args23.default.option(["O", "outDir"], "AI export output directory", "data/ai/export");
24368
+ import_args23.default.option(["A", "skipAgent"], "Skip direct research agent invocation", false);
24369
+ import_args23.default.option(["j", "json"], "Print structured JSON summary", false);
24370
+ flags23 = import_args23.default.parse(process.argv);
23420
24371
  projectRoot8 = String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
23421
24372
  dotenvPath = String(
23422
24373
  process.env.DOTENV_CONFIG_PATH || import_node_path7.default.join(projectRoot8, ".env")
@@ -23799,8 +24750,8 @@ var init_researchAuto = __esm({
23799
24750
  });
23800
24751
  };
23801
24752
  resolveTarget = async () => {
23802
- const userName2 = String(flags21.user || "root").trim() || "root";
23803
- const explicitConfig = String(flags21.config || "").trim();
24753
+ const userName2 = String(flags23.user || "root").trim() || "root";
24754
+ const explicitConfig = String(flags23.config || "").trim();
23804
24755
  if (explicitConfig) {
23805
24756
  return {
23806
24757
  strategy: explicitConfig.split(":")[0] || explicitConfig,
@@ -23808,7 +24759,7 @@ var init_researchAuto = __esm({
23808
24759
  selectedBy: "config"
23809
24760
  };
23810
24761
  }
23811
- const explicitStrategy = String(flags21.strategy || "").trim();
24762
+ const explicitStrategy = String(flags23.strategy || "").trim();
23812
24763
  if (explicitStrategy) {
23813
24764
  return {
23814
24765
  strategy: explicitStrategy,
@@ -23897,23 +24848,23 @@ var init_researchAuto = __esm({
23897
24848
  );
23898
24849
  return result;
23899
24850
  };
23900
- main27 = async () => {
23901
- const userName2 = String(flags21.user || "root").trim() || "root";
23902
- const connector = String(flags21.connector || "bybit").trim() || "bybit";
23903
- const timeframe = String(flags21.timeframe || "15").trim() || "15";
23904
- const days = Math.max(1, Number.parseInt(String(flags21.days ?? 45), 10) || 45);
24851
+ main30 = async () => {
24852
+ const userName2 = String(flags23.user || "root").trim() || "root";
24853
+ const connector = String(flags23.connector || "bybit").trim() || "bybit";
24854
+ const timeframe = String(flags23.timeframe || "15").trim() || "15";
24855
+ const days = Math.max(1, Number.parseInt(String(flags23.days ?? 45), 10) || 45);
23905
24856
  const recent = Math.max(
23906
24857
  0,
23907
- Number.parseInt(String(flags21.recent ?? 1e3), 10) || 1e3
24858
+ Number.parseInt(String(flags23.recent ?? 1e3), 10) || 1e3
23908
24859
  );
23909
- const skip = Math.max(0, Number.parseInt(String(flags21.skip ?? 0), 10) || 0);
24860
+ const skip = Math.max(0, Number.parseInt(String(flags23.skip ?? 0), 10) || 0);
23910
24861
  const minQuality = Math.max(
23911
24862
  0,
23912
- Number.parseInt(String(flags21.minQuality ?? 4), 10) || 4
24863
+ Number.parseInt(String(flags23.minQuality ?? 4), 10) || 4
23913
24864
  );
23914
- const jsonOutput = Boolean(flags21.json);
23915
- const skipAgent = Boolean(flags21.skipAgent);
23916
- const outDirArg = String(flags21.outDir || "data/ai/export").trim() || "data/ai/export";
24865
+ const jsonOutput = Boolean(flags23.json);
24866
+ const skipAgent = Boolean(flags23.skipAgent);
24867
+ const outDirArg = String(flags23.outDir || "data/ai/export").trim() || "data/ai/export";
23917
24868
  const outDir = import_node_path7.default.resolve(projectRoot8, outDirArg);
23918
24869
  const cleanDirArg = import_node_path7.default.relative(import_node_path7.default.join(projectRoot8, "data"), outDir).replace(/\\/g, "/");
23919
24870
  const target = await resolveTarget();
@@ -24004,7 +24955,7 @@ var init_researchAuto = __esm({
24004
24955
  target.strategy
24005
24956
  );
24006
24957
  const backtestConfigKey = import_redis11.redisKeys.backtestConfig(userName2, target.config);
24007
- const defaults2 = (0, import_strategies6.getBuiltInStrategyDefaultConfig)(target.strategy) || {};
24958
+ const defaults2 = (0, import_strategies10.getBuiltInStrategyDefaultConfig)(target.strategy) || {};
24008
24959
  const userStrategyConfig = await (0, import_redis11.getData)(
24009
24960
  strategyConfigKey,
24010
24961
  {}
@@ -24251,31 +25202,31 @@ Telegram report failed: ${suffix}`;
24251
25202
  // src/scripts/results.ts
24252
25203
  var results_exports = {};
24253
25204
  __export(results_exports, {
24254
- main: () => main28,
25205
+ main: () => main31,
24255
25206
  results: () => results
24256
25207
  });
24257
- var import_args22, import_chalk35, import_connectors13, import_cli10, import_redis12, ListIt4, flags22, MIN_PROFIT, MIN_WIN_RATE, MIN_ORDERS_PER_MONTH, createListIt4, formatPercent3, getMonthlyReturn, toStrategyResultEntry, toStrategyResults, getTestConfigs, buildBestResults, getSavedProfitsBySymbol, getCoverageRow, results, main28;
25208
+ var import_args24, import_chalk35, import_connectors13, import_cli10, import_redis12, ListIt4, flags24, MIN_PROFIT, MIN_WIN_RATE, MIN_ORDERS_PER_MONTH, createListIt4, formatPercent3, getMonthlyReturn, toStrategyResultEntry, toStrategyResults, getTestConfigs, buildBestResults, getSavedProfitsBySymbol, getCoverageRow, results, main31;
24258
25209
  var init_results = __esm({
24259
25210
  "src/scripts/results.ts"() {
24260
25211
  "use strict";
24261
- import_args22 = __toESM(require("args"));
25212
+ import_args24 = __toESM(require("args"));
24262
25213
  import_chalk35 = __toESM(require("chalk"));
24263
25214
  import_connectors13 = require("@tradejs/connectors");
24264
25215
  import_cli10 = require("@tradejs/node/cli");
24265
25216
  import_redis12 = require("@tradejs/infra/redis");
24266
25217
  ListIt4 = require_list_it();
24267
- import_args22.default.example(
25218
+ import_args24.default.example(
24268
25219
  "yarn results --strategy TrendLine --coverage",
24269
25220
  "Show best results per symbol and coverage"
24270
25221
  );
24271
- import_args22.default.option(["s", "strategy"], "Strategy name");
24272
- import_args22.default.option(["C", "coverage"], "Show coverage table", false);
24273
- import_args22.default.option(["u", "update"], "Update results config in redis", false);
24274
- import_args22.default.option(["m", "merge"], "Merge results config in redis", false);
24275
- import_args22.default.option(["c", "clear"], "Clear results config in redis", false);
24276
- import_args22.default.option(["V", "verbose"], "Verbose output", false);
24277
- import_args22.default.option(["U", "user"], "Use user config", "root");
24278
- flags22 = import_args22.default.parse(process.argv);
25222
+ import_args24.default.option(["s", "strategy"], "Strategy name");
25223
+ import_args24.default.option(["C", "coverage"], "Show coverage table", false);
25224
+ import_args24.default.option(["u", "update"], "Update results config in redis", false);
25225
+ import_args24.default.option(["m", "merge"], "Merge results config in redis", false);
25226
+ import_args24.default.option(["c", "clear"], "Clear results config in redis", false);
25227
+ import_args24.default.option(["V", "verbose"], "Verbose output", false);
25228
+ import_args24.default.option(["U", "user"], "Use user config", "root");
25229
+ flags24 = import_args24.default.parse(process.argv);
24279
25230
  MIN_PROFIT = 2;
24280
25231
  MIN_WIN_RATE = 40;
24281
25232
  MIN_ORDERS_PER_MONTH = 1;
@@ -24415,25 +25366,25 @@ var init_results = __esm({
24415
25366
  };
24416
25367
  };
24417
25368
  results = async () => {
24418
- if (!flags22.strategy) {
25369
+ if (!flags24.strategy) {
24419
25370
  console.error(import_chalk35.default.red("Missing --strategy"));
24420
25371
  process.exit(1);
24421
25372
  }
24422
- const strategyName = flags22.strategy;
24423
- const userName2 = flags22.user;
24424
- if (flags22.clear) {
25373
+ const strategyName = flags24.strategy;
25374
+ const userName2 = flags24.user;
25375
+ if (flags24.clear) {
24425
25376
  const cleared = await (0, import_redis12.delKey)(
24426
25377
  import_redis12.redisKeys.strategyResults(userName2, strategyName)
24427
25378
  );
24428
25379
  console.log(
24429
25380
  cleared ? import_chalk35.default.green(`Cleared results:${strategyName}`) : import_chalk35.default.yellow(`No results to clear for ${strategyName}`)
24430
25381
  );
24431
- if (!flags22.coverage && !flags22.update && !flags22.merge) {
25382
+ if (!flags24.coverage && !flags24.update && !flags24.merge) {
24432
25383
  process.exit();
24433
25384
  }
24434
25385
  }
24435
25386
  const bestBySymbol = await buildBestResults(userName2, strategyName);
24436
- if (flags22.coverage) {
25387
+ if (flags24.coverage) {
24437
25388
  const rows = [...bestBySymbol.values()].sort((a, b) => b.profit - a.profit).map((row) => [
24438
25389
  import_chalk35.default.yellow(row.symbol),
24439
25390
  import_chalk35.default.green(formatPercent3(row.profit)),
@@ -24464,9 +25415,9 @@ var init_results = __esm({
24464
25415
  );
24465
25416
  console.log("");
24466
25417
  }
24467
- if (flags22.update || flags22.merge) {
25418
+ if (flags24.update || flags24.merge) {
24468
25419
  const resultsConfig = toStrategyResults(bestBySymbol.values());
24469
- if (flags22.merge) {
25420
+ if (flags24.merge) {
24470
25421
  if (Object.keys(resultsConfig).length === 0) {
24471
25422
  console.log(import_chalk35.default.yellow("No good results to merge."));
24472
25423
  process.exit(0);
@@ -24497,7 +25448,7 @@ var init_results = __esm({
24497
25448
  await (0, import_redis12.setData)(import_redis12.redisKeys.strategyResults(userName2, strategyName), merged, {
24498
25449
  expire: 0
24499
25450
  });
24500
- if (flags22.verbose) {
25451
+ if (flags24.verbose) {
24501
25452
  const updateRows = updates.sort((a, b) => {
24502
25453
  const prevA = savedProfitBySymbol.get(a.symbol) ?? -Infinity;
24503
25454
  const prevB = savedProfitBySymbol.get(b.symbol) ?? -Infinity;
@@ -24550,7 +25501,7 @@ var init_results = __esm({
24550
25501
  }
24551
25502
  process.exit();
24552
25503
  };
24553
- main28 = results;
25504
+ main31 = results;
24554
25505
  }
24555
25506
  });
24556
25507
 
@@ -25211,43 +26162,43 @@ var init_support = __esm({
25211
26162
  // src/scripts/replayRuntimeEvidence.ts
25212
26163
  var replayRuntimeEvidence_exports = {};
25213
26164
  __export(replayRuntimeEvidence_exports, {
25214
- main: () => main29,
26165
+ main: () => main32,
25215
26166
  replayRuntimeEvidence: () => replayRuntimeEvidence
25216
26167
  });
25217
- var import_promises14, import_node_path8, import_args23, import_chalk37, import_redis15, flags23, projectRoot9, toEpochMs2, resolveWindow, parseStrategyFilter, asRecord3, asArray2, sum, groupCount, compactMatched, resolveItemStrategy, summarizeReplayComparison, loadReplayResult, getReplayStrategies, loadRuntimePayload, replayRuntimeEvidence, main29;
26168
+ var import_promises14, import_node_path8, import_args25, import_chalk37, import_redis15, flags25, projectRoot9, toEpochMs2, resolveWindow, parseStrategyFilter, asRecord3, asArray2, sum, groupCount, compactMatched, resolveItemStrategy, summarizeReplayComparison, loadReplayResult, getReplayStrategies, loadRuntimePayload, replayRuntimeEvidence, main32;
25218
26169
  var init_replayRuntimeEvidence = __esm({
25219
26170
  "src/scripts/replayRuntimeEvidence.ts"() {
25220
26171
  "use strict";
25221
26172
  import_promises14 = __toESM(require("fs/promises"));
25222
26173
  import_node_path8 = __toESM(require("path"));
25223
- import_args23 = __toESM(require("args"));
26174
+ import_args25 = __toESM(require("args"));
25224
26175
  import_chalk37 = __toESM(require("chalk"));
25225
26176
  import_redis15 = require("@tradejs/infra/redis");
25226
26177
  init_runtimeDebugEvidence();
25227
26178
  init_support();
25228
- import_args23.default.option(["u", "user"], "Use user config", "root");
25229
- import_args23.default.option(
26179
+ import_args25.default.option(["u", "user"], "Use user config", "root");
26180
+ import_args25.default.option(
25230
26181
  ["S", "startTime"],
25231
26182
  "Window start timestamp, seconds/ms, or ISO date"
25232
26183
  );
25233
- import_args23.default.option(["E", "endTime"], "Window end timestamp, seconds/ms, or ISO date");
25234
- import_args23.default.option(
26184
+ import_args25.default.option(["E", "endTime"], "Window end timestamp, seconds/ms, or ISO date");
26185
+ import_args25.default.option(
25235
26186
  ["H", "hours"],
25236
26187
  "Fallback window in hours when start/end are omitted",
25237
26188
  24
25238
26189
  );
25239
- import_args23.default.option(["s", "strategy"], "Strategy filter, comma-separated");
25240
- import_args23.default.option("replayKey", "Exact replay result Redis key");
25241
- import_args23.default.option(
26190
+ import_args25.default.option(["s", "strategy"], "Strategy filter, comma-separated");
26191
+ import_args25.default.option("replayKey", "Exact replay result Redis key");
26192
+ import_args25.default.option(
25242
26193
  "runtimeEvidence",
25243
26194
  "Runtime evidence JSON collected on the runtime server"
25244
26195
  );
25245
- import_args23.default.option(
26196
+ import_args25.default.option(
25246
26197
  ["o", "out"],
25247
26198
  "Output JSON path",
25248
26199
  "output/replay-runtime-evidence.json"
25249
26200
  );
25250
- flags23 = import_args23.default.parse(process.argv);
26201
+ flags25 = import_args25.default.parse(process.argv);
25251
26202
  projectRoot9 = String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
25252
26203
  toEpochMs2 = (value) => {
25253
26204
  if (value == null || value === "") {
@@ -25262,12 +26213,12 @@ var init_replayRuntimeEvidence = __esm({
25262
26213
  return Number.isFinite(parsed) ? parsed : null;
25263
26214
  };
25264
26215
  resolveWindow = () => {
25265
- const endTime = toEpochMs2(flags23.endTime) ?? Date.now();
26216
+ const endTime = toEpochMs2(flags25.endTime) ?? Date.now();
25266
26217
  const hours = Math.max(
25267
26218
  1,
25268
- Number.parseInt(String(flags23.hours ?? 24), 10) || 24
26219
+ Number.parseInt(String(flags25.hours ?? 24), 10) || 24
25269
26220
  );
25270
- const startTime = toEpochMs2(flags23.startTime) ?? endTime - hours * 60 * 60 * 1e3;
26221
+ const startTime = toEpochMs2(flags25.startTime) ?? endTime - hours * 60 * 60 * 1e3;
25271
26222
  if (startTime >= endTime) {
25272
26223
  throw new Error(
25273
26224
  `Invalid window: startTime (${startTime}) must be less than endTime (${endTime})`
@@ -25276,7 +26227,7 @@ var init_replayRuntimeEvidence = __esm({
25276
26227
  return {
25277
26228
  startTime,
25278
26229
  endTime,
25279
- source: flags23.startTime != null || flags23.endTime != null ? "explicit" : "hours"
26230
+ source: flags25.startTime != null || flags25.endTime != null ? "explicit" : "hours"
25280
26231
  };
25281
26232
  };
25282
26233
  parseStrategyFilter = (value) => String(value ?? "").split(",").map((item) => item.trim()).filter(Boolean);
@@ -25460,7 +26411,7 @@ var init_replayRuntimeEvidence = __esm({
25460
26411
  endTime,
25461
26412
  strategies
25462
26413
  }) => {
25463
- const runtimeEvidencePath = typeof flags23.runtimeEvidence === "string" && flags23.runtimeEvidence.trim() ? import_node_path8.default.resolve(projectRoot9, flags23.runtimeEvidence.trim()) : null;
26414
+ const runtimeEvidencePath = typeof flags25.runtimeEvidence === "string" && flags25.runtimeEvidence.trim() ? import_node_path8.default.resolve(projectRoot9, flags25.runtimeEvidence.trim()) : null;
25464
26415
  if (runtimeEvidencePath) {
25465
26416
  const artifact = JSON.parse(
25466
26417
  await import_promises14.default.readFile(runtimeEvidencePath, "utf8")
@@ -25472,7 +26423,7 @@ var init_replayRuntimeEvidence = __esm({
25472
26423
  };
25473
26424
  }
25474
26425
  const runtimeEvidence2 = await collectRuntimeDebugEvidence({
25475
- userName: flags23.user,
26426
+ userName: flags25.user,
25476
26427
  startTime,
25477
26428
  endTime,
25478
26429
  strategies: strategies.length ? strategies : void 0
@@ -25481,7 +26432,7 @@ var init_replayRuntimeEvidence = __esm({
25481
26432
  source: "local-redis",
25482
26433
  path: null,
25483
26434
  payload: await buildRuntimeDebugReportPayload({
25484
- userName: flags23.user,
26435
+ userName: flags25.user,
25485
26436
  startTime,
25486
26437
  endTime,
25487
26438
  signals: runtimeEvidence2.signals,
@@ -25495,10 +26446,10 @@ var init_replayRuntimeEvidence = __esm({
25495
26446
  replayRuntimeEvidence = async () => {
25496
26447
  const { startTime, endTime, source } = resolveWindow();
25497
26448
  const replay = await loadReplayResult({
25498
- userName: flags23.user,
25499
- replayKey: typeof flags23.replayKey === "string" && flags23.replayKey.trim() ? flags23.replayKey.trim() : void 0
26449
+ userName: flags25.user,
26450
+ replayKey: typeof flags25.replayKey === "string" && flags25.replayKey.trim() ? flags25.replayKey.trim() : void 0
25500
26451
  });
25501
- const explicitStrategies = parseStrategyFilter(flags23.strategy);
26452
+ const explicitStrategies = parseStrategyFilter(flags25.strategy);
25502
26453
  const strategies = explicitStrategies.length ? explicitStrategies : getReplayStrategies(replay.value);
25503
26454
  const runtime = await loadRuntimePayload({
25504
26455
  startTime,
@@ -25509,7 +26460,7 @@ var init_replayRuntimeEvidence = __esm({
25509
26460
  const artifact = {
25510
26461
  reportType: "replay-runtime-evidence",
25511
26462
  generatedAt: Date.now(),
25512
- userName: flags23.user,
26463
+ userName: flags25.user,
25513
26464
  window: {
25514
26465
  startTime,
25515
26466
  endTime,
@@ -25537,7 +26488,7 @@ var init_replayRuntimeEvidence = __esm({
25537
26488
  },
25538
26489
  runtime: runtime.payload
25539
26490
  };
25540
- const outPath = import_node_path8.default.resolve(projectRoot9, String(flags23.out));
26491
+ const outPath = import_node_path8.default.resolve(projectRoot9, String(flags25.out));
25541
26492
  await import_promises14.default.mkdir(import_node_path8.default.dirname(outPath), { recursive: true });
25542
26493
  await import_promises14.default.writeFile(outPath, `${JSON.stringify(artifact, null, 2)}
25543
26494
  `, "utf8");
@@ -25557,39 +26508,39 @@ var init_replayRuntimeEvidence = __esm({
25557
26508
  )
25558
26509
  );
25559
26510
  };
25560
- main29 = replayRuntimeEvidence;
26511
+ main32 = replayRuntimeEvidence;
25561
26512
  }
25562
26513
  });
25563
26514
 
25564
26515
  // src/scripts/runtimeEvidence.ts
25565
26516
  var runtimeEvidence_exports = {};
25566
26517
  __export(runtimeEvidence_exports, {
25567
- main: () => main30,
26518
+ main: () => main33,
25568
26519
  runtimeEvidence: () => runtimeEvidence
25569
26520
  });
25570
- var import_promises15, import_node_path9, import_args24, import_chalk38, flags24, projectRoot10, toEpochMs3, resolveWindow2, parseStrategyFilter2, runtimeEvidence, main30;
26521
+ var import_promises15, import_node_path9, import_args26, import_chalk38, flags26, projectRoot10, toEpochMs3, resolveWindow2, parseStrategyFilter2, runtimeEvidence, main33;
25571
26522
  var init_runtimeEvidence = __esm({
25572
26523
  "src/scripts/runtimeEvidence.ts"() {
25573
26524
  "use strict";
25574
26525
  import_promises15 = __toESM(require("fs/promises"));
25575
26526
  import_node_path9 = __toESM(require("path"));
25576
- import_args24 = __toESM(require("args"));
26527
+ import_args26 = __toESM(require("args"));
25577
26528
  import_chalk38 = __toESM(require("chalk"));
25578
26529
  init_runtimeDebugEvidence();
25579
- import_args24.default.option(["u", "user"], "Use user config", "root");
25580
- import_args24.default.option(
26530
+ import_args26.default.option(["u", "user"], "Use user config", "root");
26531
+ import_args26.default.option(
25581
26532
  ["S", "startTime"],
25582
26533
  "Window start timestamp, seconds/ms, or ISO date"
25583
26534
  );
25584
- import_args24.default.option(["E", "endTime"], "Window end timestamp, seconds/ms, or ISO date");
25585
- import_args24.default.option(
26535
+ import_args26.default.option(["E", "endTime"], "Window end timestamp, seconds/ms, or ISO date");
26536
+ import_args26.default.option(
25586
26537
  ["H", "hours"],
25587
26538
  "Fallback window in hours when start/end are omitted",
25588
26539
  24
25589
26540
  );
25590
- import_args24.default.option(["s", "strategy"], "Strategy filter, comma-separated");
25591
- import_args24.default.option(["o", "out"], "Output JSON path", "output/runtime-evidence.json");
25592
- flags24 = import_args24.default.parse(process.argv);
26541
+ import_args26.default.option(["s", "strategy"], "Strategy filter, comma-separated");
26542
+ import_args26.default.option(["o", "out"], "Output JSON path", "output/runtime-evidence.json");
26543
+ flags26 = import_args26.default.parse(process.argv);
25593
26544
  projectRoot10 = String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
25594
26545
  toEpochMs3 = (value) => {
25595
26546
  if (value == null || value === "") {
@@ -25604,12 +26555,12 @@ var init_runtimeEvidence = __esm({
25604
26555
  return Number.isFinite(parsed) ? parsed : null;
25605
26556
  };
25606
26557
  resolveWindow2 = () => {
25607
- const endTime = toEpochMs3(flags24.endTime) ?? Date.now();
26558
+ const endTime = toEpochMs3(flags26.endTime) ?? Date.now();
25608
26559
  const hours = Math.max(
25609
26560
  1,
25610
- Number.parseInt(String(flags24.hours ?? 24), 10) || 24
26561
+ Number.parseInt(String(flags26.hours ?? 24), 10) || 24
25611
26562
  );
25612
- const startTime = toEpochMs3(flags24.startTime) ?? endTime - hours * 60 * 60 * 1e3;
26563
+ const startTime = toEpochMs3(flags26.startTime) ?? endTime - hours * 60 * 60 * 1e3;
25613
26564
  if (startTime >= endTime) {
25614
26565
  throw new Error(
25615
26566
  `Invalid window: startTime (${startTime}) must be less than endTime (${endTime})`
@@ -25618,21 +26569,21 @@ var init_runtimeEvidence = __esm({
25618
26569
  return {
25619
26570
  startTime,
25620
26571
  endTime,
25621
- source: flags24.startTime != null || flags24.endTime != null ? "explicit" : "hours"
26572
+ source: flags26.startTime != null || flags26.endTime != null ? "explicit" : "hours"
25622
26573
  };
25623
26574
  };
25624
26575
  parseStrategyFilter2 = (value) => String(value ?? "").split(",").map((item) => item.trim()).filter(Boolean);
25625
26576
  runtimeEvidence = async () => {
25626
26577
  const { startTime, endTime, source } = resolveWindow2();
25627
- const strategies = parseStrategyFilter2(flags24.strategy);
26578
+ const strategies = parseStrategyFilter2(flags26.strategy);
25628
26579
  const evidence = await collectRuntimeDebugEvidence({
25629
- userName: flags24.user,
26580
+ userName: flags26.user,
25630
26581
  startTime,
25631
26582
  endTime,
25632
26583
  strategies: strategies.length ? strategies : void 0
25633
26584
  });
25634
26585
  const runtime = await buildRuntimeDebugReportPayload({
25635
- userName: flags24.user,
26586
+ userName: flags26.user,
25636
26587
  startTime,
25637
26588
  endTime,
25638
26589
  signals: evidence.signals,
@@ -25644,7 +26595,7 @@ var init_runtimeEvidence = __esm({
25644
26595
  const artifact = {
25645
26596
  reportType: "runtime-evidence",
25646
26597
  generatedAt: Date.now(),
25647
- userName: flags24.user,
26598
+ userName: flags26.user,
25648
26599
  window: {
25649
26600
  startTime,
25650
26601
  endTime,
@@ -25655,7 +26606,7 @@ var init_runtimeEvidence = __esm({
25655
26606
  strategies,
25656
26607
  runtime
25657
26608
  };
25658
- const outPath = import_node_path9.default.resolve(projectRoot10, String(flags24.out));
26609
+ const outPath = import_node_path9.default.resolve(projectRoot10, String(flags26.out));
25659
26610
  await import_promises15.default.mkdir(import_node_path9.default.dirname(outPath), { recursive: true });
25660
26611
  await import_promises15.default.writeFile(outPath, `${JSON.stringify(artifact, null, 2)}
25661
26612
  `, "utf8");
@@ -25671,7 +26622,7 @@ var init_runtimeEvidence = __esm({
25671
26622
  )
25672
26623
  );
25673
26624
  };
25674
- main30 = runtimeEvidence;
26625
+ main33 = runtimeEvidence;
25675
26626
  }
25676
26627
  });
25677
26628
 
@@ -25683,18 +26634,18 @@ __export(serverHealth_exports, {
25683
26634
  buildServerHealthDiagnostics: () => buildServerHealthDiagnostics,
25684
26635
  collectServerHealthSnapshot: () => collectServerHealthSnapshot,
25685
26636
  evaluateServerHealth: () => evaluateServerHealth,
25686
- main: () => main31,
26637
+ main: () => main34,
25687
26638
  resolveServerHealthThresholds: () => resolveServerHealthThresholds
25688
26639
  });
25689
- var import_config8, import_node_child_process5, import_node_os3, import_node_fs2, import_args25, import_redis16, DEFAULT_STATE, DEFAULT_THRESHOLDS, escapeHtml3, formatPctValue, formatLoadPerCore, formatGiB, formatDuration2, readDiskUsage, parseMeminfoBytes, readMemorySnapshot, runDiagnosticCommand, runDiagnosticCommands, collectServerHealthSnapshot, buildHumanSummaryLines, evaluateServerHealth, shouldRecover, buildAlertMessage, buildRecoveryMessage, buildServerHealthDiagnostics, getStateKey, toFiniteNumber3, resolveServerHealthThresholds, main31;
26640
+ var import_config9, import_node_child_process5, import_node_os3, import_node_fs2, import_args27, import_redis16, DEFAULT_STATE, DEFAULT_THRESHOLDS, escapeHtml3, formatPctValue, formatLoadPerCore, formatGiB, formatDuration2, readDiskUsage, parseMeminfoBytes, readMemorySnapshot, runDiagnosticCommand, runDiagnosticCommands, collectServerHealthSnapshot, buildHumanSummaryLines, evaluateServerHealth, shouldRecover, buildAlertMessage, buildRecoveryMessage, buildServerHealthDiagnostics, getStateKey, toFiniteNumber3, resolveServerHealthThresholds, main34;
25690
26641
  var init_serverHealth = __esm({
25691
26642
  "src/scripts/serverHealth.ts"() {
25692
26643
  "use strict";
25693
- import_config8 = require("dotenv/config");
26644
+ import_config9 = require("dotenv/config");
25694
26645
  import_node_child_process5 = require("child_process");
25695
26646
  import_node_os3 = __toESM(require("os"));
25696
26647
  import_node_fs2 = require("fs");
25697
- import_args25 = __toESM(require("args"));
26648
+ import_args27 = __toESM(require("args"));
25698
26649
  import_redis16 = require("@tradejs/infra/redis");
25699
26650
  init_telegramReports();
25700
26651
  DEFAULT_STATE = {
@@ -25806,8 +26757,8 @@ var init_serverHealth = __esm({
25806
26757
  };
25807
26758
  }
25808
26759
  };
25809
- runDiagnosticCommand = (command, args31) => {
25810
- const output = (0, import_node_child_process5.execFileSync)(command, args31, {
26760
+ runDiagnosticCommand = (command, args33) => {
26761
+ const output = (0, import_node_child_process5.execFileSync)(command, args33, {
25811
26762
  encoding: "utf8",
25812
26763
  timeout: 5e3,
25813
26764
  maxBuffer: 1024 * 1024,
@@ -26057,57 +27008,57 @@ var init_serverHealth = __esm({
26057
27008
  diskRecoverPct: diskRecoverPct != null && diskRecoverPct > 0 ? diskRecoverPct : DEFAULT_THRESHOLDS.diskRecoverPct
26058
27009
  };
26059
27010
  };
26060
- import_args25.default.option(["u", "user"], "Use user config", "root");
26061
- import_args25.default.option(["P", "printOnly"], "Print report instead of Telegram", false);
26062
- import_args25.default.option(
27011
+ import_args27.default.option(["u", "user"], "Use user config", "root");
27012
+ import_args27.default.option(["P", "printOnly"], "Print report instead of Telegram", false);
27013
+ import_args27.default.option(
26063
27014
  ["d", "diskPath"],
26064
27015
  "Filesystem path to inspect for disk pressure",
26065
27016
  "/"
26066
27017
  );
26067
- import_args25.default.option(
27018
+ import_args27.default.option(
26068
27019
  ["c", "cooldownSec"],
26069
27020
  "Minimum seconds between repeated unhealthy reminders",
26070
27021
  900
26071
27022
  );
26072
- import_args25.default.option(
27023
+ import_args27.default.option(
26073
27024
  ["l", "loadPerCpuWarn"],
26074
27025
  "Alert when 1m load per CPU reaches this value",
26075
27026
  String(DEFAULT_THRESHOLDS.loadPerCpuWarn)
26076
27027
  );
26077
- import_args25.default.option(
27028
+ import_args27.default.option(
26078
27029
  ["L", "loadPerCpuRecover"],
26079
27030
  "Recovery threshold for 1m load per CPU",
26080
27031
  String(DEFAULT_THRESHOLDS.loadPerCpuRecover)
26081
27032
  );
26082
- import_args25.default.option(
27033
+ import_args27.default.option(
26083
27034
  ["m", "memoryWarnPct"],
26084
27035
  "Alert when memory used percent reaches this value",
26085
27036
  String(DEFAULT_THRESHOLDS.memoryWarnPct)
26086
27037
  );
26087
- import_args25.default.option(
27038
+ import_args27.default.option(
26088
27039
  ["M", "memoryRecoverPct"],
26089
27040
  "Recovery threshold for memory used percent",
26090
27041
  String(DEFAULT_THRESHOLDS.memoryRecoverPct)
26091
27042
  );
26092
- import_args25.default.option(
27043
+ import_args27.default.option(
26093
27044
  ["D", "diskWarnPct"],
26094
27045
  "Alert when disk used percent reaches this value",
26095
27046
  String(DEFAULT_THRESHOLDS.diskWarnPct)
26096
27047
  );
26097
- import_args25.default.option(
27048
+ import_args27.default.option(
26098
27049
  ["R", "diskRecoverPct"],
26099
27050
  "Recovery threshold for disk used percent",
26100
27051
  String(DEFAULT_THRESHOLDS.diskRecoverPct)
26101
27052
  );
26102
- main31 = async () => {
26103
- const flags29 = import_args25.default.parse(process.argv);
26104
- const thresholds = resolveServerHealthThresholds(flags29);
26105
- const diskPath = String(flags29.diskPath || "/");
27053
+ main34 = async () => {
27054
+ const flags31 = import_args27.default.parse(process.argv);
27055
+ const thresholds = resolveServerHealthThresholds(flags31);
27056
+ const diskPath = String(flags31.diskPath || "/");
26106
27057
  const snapshot = collectServerHealthSnapshot(diskPath);
26107
- const stateKey = getStateKey(String(flags29.user), snapshot.hostname);
27058
+ const stateKey = getStateKey(String(flags31.user), snapshot.hostname);
26108
27059
  const prevState = await (0, import_redis16.getData)(stateKey, DEFAULT_STATE) ?? DEFAULT_STATE;
26109
27060
  const evaluation = evaluateServerHealth(snapshot, thresholds);
26110
- const cooldownMs = Math.max(60, Number(flags29.cooldownSec)) * 1e3;
27061
+ const cooldownMs = Math.max(60, Number(flags31.cooldownSec)) * 1e3;
26111
27062
  const now2 = snapshot.timestamp;
26112
27063
  if (!evaluation.isHealthy) {
26113
27064
  const reminder = prevState.status === "alerting" && typeof prevState.lastAlertAt === "number" && now2 - prevState.lastAlertAt < cooldownMs ? false : prevState.status === "alerting";
@@ -26125,7 +27076,7 @@ var init_serverHealth = __esm({
26125
27076
  prevState,
26126
27077
  reminder
26127
27078
  });
26128
- if (flags29.printOnly) {
27079
+ if (flags31.printOnly) {
26129
27080
  console.log(message);
26130
27081
  console.log("");
26131
27082
  console.log(
@@ -26135,7 +27086,7 @@ var init_serverHealth = __esm({
26135
27086
  })
26136
27087
  );
26137
27088
  } else {
26138
- await sendTelegramReport(message, { userName: flags29.user });
27089
+ await sendTelegramReport(message, { userName: flags31.user });
26139
27090
  }
26140
27091
  }
26141
27092
  return;
@@ -26150,10 +27101,10 @@ var init_serverHealth = __esm({
26150
27101
  await (0, import_redis16.setData)(stateKey, nextState, { expire: 0 });
26151
27102
  if (recovered) {
26152
27103
  const message = buildRecoveryMessage({ snapshot, prevState });
26153
- if (flags29.printOnly) {
27104
+ if (flags31.printOnly) {
26154
27105
  console.log(message);
26155
27106
  } else {
26156
- await sendTelegramReport(message, { userName: flags29.user });
27107
+ await sendTelegramReport(message, { userName: flags31.user });
26157
27108
  }
26158
27109
  }
26159
27110
  };
@@ -26976,16 +27927,16 @@ var init_classification = __esm({
26976
27927
  var runtimeParity_exports = {};
26977
27928
  __export(runtimeParity_exports, {
26978
27929
  buildRuntimeParityMessage: () => buildRuntimeParityMessage,
26979
- main: () => main32,
27930
+ main: () => main35,
26980
27931
  runtimeParity: () => runtimeParity
26981
27932
  });
26982
- var import_config9, import_node_crypto4, import_args26, import_chalk39, import_constants15, import_time7, import_logger6, import_redis17, import_cli11, import_backtest3, import_connectors14, flags25, interval3, projectRoot11, DEFAULT_LOOKBACK_DAYS, DETAIL_LIMIT, TELEGRAM_DETAIL_LIMIT, SUMMARY_TIMEZONE, SUMMARY_TIMEZONE_LABEL, DEFAULT_REPLAY_ENV, escapeHtml4, formatMskDateTime2, resolveParityConnectorName, createConnector, buildReplayAiAnalyses, formatReplayEnvSummary, buildReplayConfig, warmReplayHistory, formatPercent4, formatMinutes2, formatEntryLabel, formatRuntimeEntriesSummary, formatSourceCountsSummary, normalizeSummaryText, pickFirstText, buildMismatchSummaryRows, buildStrategyIssueRows, toSerializableTradeParityEntry, toSerializableSignal, toSerializableRuntimeSignalEvaluation, getRuntimeOnlyLikelyCause, getBacktestOnlyLikelyCause, getRuntimeOnlyRecommendedChecks, getBacktestOnlyRecommendedChecks, buildRuntimeParityMismatchAttachment, printRuntimeDuplicateDetails, printClassifiedBacktestOnlyDetails, printClassifiedRuntimeOnlyDetails, summarizeByStrategy, buildRuntimeParityMessage, buildRuntimeParityNoTargetsMessage, runtimeParity, main32;
27933
+ var import_config10, import_node_crypto5, import_args28, import_chalk39, import_constants15, import_time7, import_logger6, import_redis17, import_cli11, import_backtest3, import_connectors14, flags27, interval3, projectRoot11, DEFAULT_LOOKBACK_DAYS, DETAIL_LIMIT, TELEGRAM_DETAIL_LIMIT, SUMMARY_TIMEZONE, SUMMARY_TIMEZONE_LABEL, DEFAULT_REPLAY_ENV, escapeHtml4, formatMskDateTime2, resolveParityConnectorName, createConnector, buildReplayAiAnalyses, formatReplayEnvSummary, buildReplayConfig, warmReplayHistory, formatPercent4, formatMinutes2, formatEntryLabel, formatRuntimeEntriesSummary, formatSourceCountsSummary, normalizeSummaryText, pickFirstText, buildMismatchSummaryRows, buildStrategyIssueRows, toSerializableTradeParityEntry, toSerializableSignal, toSerializableRuntimeSignalEvaluation, getRuntimeOnlyLikelyCause, getBacktestOnlyLikelyCause, getRuntimeOnlyRecommendedChecks, getBacktestOnlyRecommendedChecks, buildRuntimeParityMismatchAttachment, printRuntimeDuplicateDetails, printClassifiedBacktestOnlyDetails, printClassifiedRuntimeOnlyDetails, summarizeByStrategy, buildRuntimeParityMessage, buildRuntimeParityNoTargetsMessage, runtimeParity, main35;
26983
27934
  var init_runtimeParity2 = __esm({
26984
27935
  "src/scripts/runtimeParity.ts"() {
26985
27936
  "use strict";
26986
- import_config9 = require("dotenv/config");
26987
- import_node_crypto4 = require("crypto");
26988
- import_args26 = __toESM(require("args"));
27937
+ import_config10 = require("dotenv/config");
27938
+ import_node_crypto5 = require("crypto");
27939
+ import_args28 = __toESM(require("args"));
26989
27940
  import_chalk39 = __toESM(require("chalk"));
26990
27941
  import_constants15 = require("@tradejs/core/constants");
26991
27942
  import_time7 = require("@tradejs/core/time");
@@ -27007,45 +27958,45 @@ var init_runtimeParity2 = __esm({
27007
27958
  init_targets();
27008
27959
  init_classification();
27009
27960
  init_runtimeModeConfig();
27010
- import_args26.default.option(["u", "user"], "Use user config", "root");
27011
- import_args26.default.option(
27961
+ import_args28.default.option(["u", "user"], "Use user config", "root");
27962
+ import_args28.default.option(
27012
27963
  ["o", "connector"],
27013
27964
  "Connector provider or name for parity replay (e.g. bybit, binance, coinbase, custom)",
27014
27965
  "bybit"
27015
27966
  );
27016
- import_args26.default.option(["d", "days"], "Replay window in days", 3);
27017
- import_args26.default.option(
27967
+ import_args28.default.option(["d", "days"], "Replay window in days", 3);
27968
+ import_args28.default.option(
27018
27969
  ["b", "startTime"],
27019
27970
  "Explicit replay start timestamp (ms or seconds)"
27020
27971
  );
27021
- import_args26.default.option(["e", "endTime"], "Explicit replay end timestamp (ms or seconds)");
27022
- import_args26.default.option(["s", "strategy"], "Only compare one strategy");
27023
- import_args26.default.option(
27972
+ import_args28.default.option(["e", "endTime"], "Explicit replay end timestamp (ms or seconds)");
27973
+ import_args28.default.option(["s", "strategy"], "Only compare one strategy");
27974
+ import_args28.default.option(
27024
27975
  ["t", "tickers"],
27025
27976
  "Replay comma-separated symbols for all configured strategies"
27026
27977
  );
27027
- import_args26.default.option(
27978
+ import_args28.default.option(
27028
27979
  ["C", "cacheOnly"],
27029
27980
  "Do not refresh market history before replay",
27030
27981
  false
27031
27982
  );
27032
- import_args26.default.option(
27983
+ import_args28.default.option(
27033
27984
  ["a", "toleranceBars"],
27034
27985
  "Allowed entry timestamp drift in bars when matching runtime vs backtest",
27035
27986
  1
27036
27987
  );
27037
- import_args26.default.option(
27988
+ import_args28.default.option(
27038
27989
  "fullUniverse",
27039
27990
  "Replay every configured strategy across the full connector universe. By default parity replays only runtime-traded and strategy-results symbols unless --tickers is provided.",
27040
27991
  false
27041
27992
  );
27042
- import_args26.default.option(
27993
+ import_args28.default.option(
27043
27994
  "runtimeGates",
27044
27995
  "Force runtime AI/ML gates for all replay targets. Runtime-gated configs are replayed with gates by default.",
27045
27996
  false
27046
27997
  );
27047
- import_args26.default.option(["N", "notify"], "Send parity summary to Telegram", false);
27048
- import_args26.default.option(["D", "details"], "Print unmatched entry details (capped)", false);
27998
+ import_args28.default.option(["N", "notify"], "Send parity summary to Telegram", false);
27999
+ import_args28.default.option(["D", "details"], "Print unmatched entry details (capped)", false);
27049
28000
  process.argv = normalizeCliArgv(process.argv, {
27050
28001
  "-C": "--cacheOnly",
27051
28002
  "-D": "--details",
@@ -27054,7 +28005,7 @@ var init_runtimeParity2 = __esm({
27054
28005
  "-S": "--startTime",
27055
28006
  "-T": "--toleranceBars"
27056
28007
  });
27057
- flags25 = import_args26.default.parse(process.argv);
28008
+ flags27 = import_args28.default.parse(process.argv);
27058
28009
  interval3 = "15";
27059
28010
  projectRoot11 = String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
27060
28011
  DEFAULT_LOOKBACK_DAYS = 3;
@@ -27062,7 +28013,7 @@ var init_runtimeParity2 = __esm({
27062
28013
  TELEGRAM_DETAIL_LIMIT = 5;
27063
28014
  SUMMARY_TIMEZONE = "Europe/Moscow";
27064
28015
  SUMMARY_TIMEZONE_LABEL = "MSK";
27065
- DEFAULT_REPLAY_ENV = flags25.runtimeGates ? "PARITY" : "BACKTEST";
28016
+ DEFAULT_REPLAY_ENV = flags27.runtimeGates ? "PARITY" : "BACKTEST";
27066
28017
  escapeHtml4 = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
27067
28018
  formatMskDateTime2 = (timestamp) => new Intl.DateTimeFormat("ru-RU", {
27068
28019
  timeZone: SUMMARY_TIMEZONE,
@@ -27185,7 +28136,7 @@ var init_runtimeParity2 = __esm({
27185
28136
  strategyConfig,
27186
28137
  env: resolveReplayStrategyEnv({
27187
28138
  strategyConfig,
27188
- forceRuntimeGates: Boolean(flags25.runtimeGates)
28139
+ forceRuntimeGates: Boolean(flags27.runtimeGates)
27189
28140
  }),
27190
28141
  interval: interval3,
27191
28142
  makeOrders: true,
@@ -27944,48 +28895,48 @@ var init_runtimeParity2 = __esm({
27944
28895
  ].join("\n");
27945
28896
  runtimeParity = async () => {
27946
28897
  const window2 = resolveTimeWindow({
27947
- days: flags25.days ?? DEFAULT_LOOKBACK_DAYS,
27948
- startTime: flags25.startTime,
27949
- endTime: flags25.endTime,
28898
+ days: flags27.days ?? DEFAULT_LOOKBACK_DAYS,
28899
+ startTime: flags27.startTime,
28900
+ endTime: flags27.endTime,
27950
28901
  defaultStartMs: Date.now() - DEFAULT_LOOKBACK_DAYS * 24 * 60 * 60 * 1e3,
27951
28902
  defaultEndMs: Date.now()
27952
28903
  });
27953
28904
  const toleranceBars = Math.max(
27954
28905
  0,
27955
- Number.parseInt(String(flags25.toleranceBars ?? 1), 10) || 0
28906
+ Number.parseInt(String(flags27.toleranceBars ?? 1), 10) || 0
27956
28907
  );
27957
28908
  const toleranceMs = toleranceBars * 15 * 60 * 1e3;
27958
- const connectorName = await resolveParityConnectorName(flags25.connector);
27959
- const requestedSymbols = parseSymbolsFromCLI2(String(flags25.tickers || ""));
28909
+ const connectorName = await resolveParityConnectorName(flags27.connector);
28910
+ const requestedSymbols = parseSymbolsFromCLI2(String(flags27.tickers || ""));
27960
28911
  const requestedSymbolSet = requestedSymbols.length ? new Set(requestedSymbols) : null;
27961
28912
  let replayErrors = [];
27962
28913
  try {
27963
28914
  const parityConnector = await createConnector({
27964
- userName: flags25.user,
28915
+ userName: flags27.user,
27965
28916
  connectorName
27966
28917
  });
27967
28918
  const [allRuntimeTrades, allRuntimeSignals, allRuntimeSignalEvaluations] = await Promise.all([
27968
- loadRuntimeTrades(flags25.user, {
28919
+ loadRuntimeTrades(flags27.user, {
27969
28920
  startTime: window2.start,
27970
28921
  endTime: window2.end
27971
28922
  }),
27972
- loadRuntimeSignals(flags25.user, {
28923
+ loadRuntimeSignals(flags27.user, {
27973
28924
  startTime: window2.start,
27974
28925
  endTime: window2.end
27975
28926
  }),
27976
- loadRuntimeSignalEvaluations(flags25.user, {
28927
+ loadRuntimeSignalEvaluations(flags27.user, {
27977
28928
  startTime: window2.start,
27978
28929
  endTime: window2.end
27979
28930
  })
27980
28931
  ]);
27981
28932
  const runtimeTrades = allRuntimeTrades.filter(
27982
- (trade) => trade.entryTimestamp >= window2.start && trade.entryTimestamp <= window2.end && (!flags25.strategy || trade.strategy === flags25.strategy) && (!requestedSymbolSet || requestedSymbolSet.has(trade.symbol))
28933
+ (trade) => trade.entryTimestamp >= window2.start && trade.entryTimestamp <= window2.end && (!flags27.strategy || trade.strategy === flags27.strategy) && (!requestedSymbolSet || requestedSymbolSet.has(trade.symbol))
27983
28934
  );
27984
28935
  const runtimeSignals = allRuntimeSignals.filter(
27985
- (signal) => signal.timestamp >= window2.start && signal.timestamp <= window2.end && (!flags25.strategy || signal.strategy === flags25.strategy) && (!requestedSymbolSet || requestedSymbolSet.has(signal.symbol))
28936
+ (signal) => signal.timestamp >= window2.start && signal.timestamp <= window2.end && (!flags27.strategy || signal.strategy === flags27.strategy) && (!requestedSymbolSet || requestedSymbolSet.has(signal.symbol))
27986
28937
  );
27987
28938
  const runtimeSignalEvaluations = allRuntimeSignalEvaluations.filter(
27988
- (evaluation) => evaluation.timestamp >= window2.start && evaluation.timestamp <= window2.end && (!flags25.strategy || evaluation.strategy === flags25.strategy) && (!requestedSymbolSet || requestedSymbolSet.has(evaluation.symbol))
28939
+ (evaluation) => evaluation.timestamp >= window2.start && evaluation.timestamp <= window2.end && (!flags27.strategy || evaluation.strategy === flags27.strategy) && (!requestedSymbolSet || requestedSymbolSet.has(evaluation.symbol))
27989
28940
  );
27990
28941
  const connectorSymbols = requestedSymbols.length ? requestedSymbols : await (0, import_cli11.getTickers)(parityConnector);
27991
28942
  const replayInputsIndex = buildReplayInputsIndex({
@@ -27993,29 +28944,29 @@ var init_runtimeParity2 = __esm({
27993
28944
  runtimeSignalEvaluations
27994
28945
  });
27995
28946
  const replayTargets = await buildReplayTargets({
27996
- userName: flags25.user,
28947
+ userName: flags27.user,
27997
28948
  runtimeTrades,
27998
28949
  connectorSymbols,
27999
- strategyFilter: String(flags25.strategy || "").trim() || void 0,
28950
+ strategyFilter: String(flags27.strategy || "").trim() || void 0,
28000
28951
  explicitSymbols: requestedSymbols,
28001
- includeConnectorUniverse: Boolean(flags25.fullUniverse)
28952
+ includeConnectorUniverse: Boolean(flags27.fullUniverse)
28002
28953
  });
28003
28954
  if (!replayTargets.length) {
28004
28955
  console.log(
28005
28956
  import_chalk39.default.yellow(
28006
- `No replay targets found for ${flags25.user} in ${(0, import_time7.formatUnix)(window2.start)} -> ${(0, import_time7.formatUnix)(window2.end)}.`
28957
+ `No replay targets found for ${flags27.user} in ${(0, import_time7.formatUnix)(window2.start)} -> ${(0, import_time7.formatUnix)(window2.end)}.`
28007
28958
  )
28008
28959
  );
28009
- if (flags25.notify) {
28960
+ if (flags27.notify) {
28010
28961
  await sendTelegramReport(
28011
28962
  buildRuntimeParityNoTargetsMessage({
28012
28963
  window: window2,
28013
28964
  connectorName,
28014
28965
  replayEnv: DEFAULT_REPLAY_ENV,
28015
- runtimeGatesEnabled: Boolean(flags25.runtimeGates),
28016
- userName: String(flags25.user)
28966
+ runtimeGatesEnabled: Boolean(flags27.runtimeGates),
28967
+ userName: String(flags27.user)
28017
28968
  }),
28018
- { userName: flags25.user }
28969
+ { userName: flags27.user }
28019
28970
  );
28020
28971
  }
28021
28972
  return;
@@ -28031,13 +28982,13 @@ var init_runtimeParity2 = __esm({
28031
28982
  ).size;
28032
28983
  console.log(
28033
28984
  import_chalk39.default.gray(
28034
- `Replay queue: ${replayTargets.length} target(s), ${replaySymbolsCount} symbol(s), mode=${flags25.fullUniverse ? "full-universe" : requestedSymbols.length ? "explicit-tickers" : "runtime+results"}`
28985
+ `Replay queue: ${replayTargets.length} target(s), ${replaySymbolsCount} symbol(s), mode=${flags27.fullUniverse ? "full-universe" : requestedSymbols.length ? "explicit-tickers" : "runtime+results"}`
28035
28986
  )
28036
28987
  );
28037
28988
  const replayConfigs = /* @__PURE__ */ new Map();
28038
28989
  for (const target of replayTargets) {
28039
28990
  const replayConfig = await buildReplayConfig({
28040
- userName: flags25.user,
28991
+ userName: flags27.user,
28041
28992
  strategy: target.strategy,
28042
28993
  symbol: target.symbol,
28043
28994
  replayInputsIndex,
@@ -28054,21 +29005,22 @@ var init_runtimeParity2 = __esm({
28054
29005
  );
28055
29006
  await prepareMarketContextForRun({
28056
29007
  mode: "parity",
28057
- userName: flags25.user,
29008
+ userName: flags27.user,
28058
29009
  projectRoot: projectRoot11,
28059
29010
  symbols: replayTargets.map((target) => target.symbol),
28060
29011
  interval: interval3,
28061
29012
  startMs: window2.start,
28062
29013
  endMs: window2.end,
28063
29014
  preloadStartMs: preloadStart,
28064
- cacheOnly: Boolean(flags25.cacheOnly),
29015
+ cacheOnly: Boolean(flags27.cacheOnly),
28065
29016
  aiEnabled: hasParityAiTargets,
28066
29017
  mlEnabled: hasParityMlTargets,
29018
+ strategyNames: replayTargets.map((target) => target.strategy),
28067
29019
  log: (message) => console.log(import_chalk39.default.gray(message))
28068
29020
  });
28069
- if (!flags25.cacheOnly) {
29021
+ if (!flags27.cacheOnly) {
28070
29022
  await warmReplayHistory({
28071
- userName: flags25.user,
29023
+ userName: flags27.user,
28072
29024
  connectorName,
28073
29025
  targets: replayTargets,
28074
29026
  preloadStart,
@@ -28095,15 +29047,15 @@ var init_runtimeParity2 = __esm({
28095
29047
  );
28096
29048
  }
28097
29049
  const result = await (0, import_backtest3.testing)({
28098
- userName: flags25.user,
29050
+ userName: flags27.user,
28099
29051
  symbol: target.symbol,
28100
29052
  options: {
28101
29053
  start: window2.start,
28102
29054
  end: window2.end
28103
29055
  },
28104
- name: `${target.symbol}_${target.strategy}_${(0, import_node_crypto4.randomUUID)().slice(0, 8)}`,
28105
- testId: (0, import_node_crypto4.randomUUID)().slice(0, 8),
28106
- testSuiteId: (0, import_node_crypto4.randomUUID)().slice(0, 8),
29056
+ name: `${target.symbol}_${target.strategy}_${(0, import_node_crypto5.randomUUID)().slice(0, 8)}`,
29057
+ testId: (0, import_node_crypto5.randomUUID)().slice(0, 8),
29058
+ testSuiteId: (0, import_node_crypto5.randomUUID)().slice(0, 8),
28107
29059
  strategyName: target.strategy,
28108
29060
  strategyConfig: replayConfig,
28109
29061
  connectorName,
@@ -28125,7 +29077,7 @@ var init_runtimeParity2 = __esm({
28125
29077
  } finally {
28126
29078
  (0, import_backtest3.releaseTestingSymbolCache)({
28127
29079
  cwd: projectRoot11,
28128
- userName: flags25.user,
29080
+ userName: flags27.user,
28129
29081
  connectorName,
28130
29082
  symbol: target.symbol
28131
29083
  });
@@ -28170,7 +29122,7 @@ var init_runtimeParity2 = __esm({
28170
29122
  );
28171
29123
  console.log(`Connector: ${connectorName}`);
28172
29124
  console.log(
28173
- `Replay env: ${replayEnvSummary}${flags25.runtimeGates || replayEnvSummary.includes("PARITY") ? " (runtime AI/ML gates enabled)" : " (core/backtest gates only)"}`
29125
+ `Replay env: ${replayEnvSummary}${flags27.runtimeGates || replayEnvSummary.includes("PARITY") ? " (runtime AI/ML gates enabled)" : " (core/backtest gates only)"}`
28174
29126
  );
28175
29127
  console.log(
28176
29128
  `Tolerance: ${toleranceBars} bar(s) / ${(toleranceMs / 6e4).toFixed(0)}m`
@@ -28244,7 +29196,7 @@ var init_runtimeParity2 = __esm({
28244
29196
  `Strategies: clean ${strategyRows.length}/${strategyRows.length}`
28245
29197
  );
28246
29198
  }
28247
- if (runtimeGateWarningCounts.size && !flags25.runtimeGates) {
29199
+ if (runtimeGateWarningCounts.size && !flags27.runtimeGates) {
28248
29200
  console.log("");
28249
29201
  console.log(import_chalk39.default.yellow("Warnings"));
28250
29202
  for (const [strategy, count] of [
@@ -28265,12 +29217,12 @@ var init_runtimeParity2 = __esm({
28265
29217
  console.log(`- ... ${replayErrors.length - DETAIL_LIMIT} more`);
28266
29218
  }
28267
29219
  }
28268
- if (flags25.details) {
29220
+ if (flags27.details) {
28269
29221
  printRuntimeDuplicateDetails(runtimeDedupe.duplicateGroups);
28270
29222
  printClassifiedRuntimeOnlyDetails(classifiedRuntimeOnly);
28271
29223
  printClassifiedBacktestOnlyDetails(classifiedBacktestOnly);
28272
29224
  }
28273
- if (flags25.notify) {
29225
+ if (flags27.notify) {
28274
29226
  const mismatchAttachment = buildRuntimeParityMismatchAttachment({
28275
29227
  window: window2,
28276
29228
  connectorName,
@@ -28299,7 +29251,7 @@ var init_runtimeParity2 = __esm({
28299
29251
  window: window2,
28300
29252
  connectorName,
28301
29253
  replayEnv: replayEnvSummary,
28302
- runtimeGatesEnabled: Boolean(flags25.runtimeGates) || replayEnvSummary.includes("PARITY"),
29254
+ runtimeGatesEnabled: Boolean(flags27.runtimeGates) || replayEnvSummary.includes("PARITY"),
28303
29255
  toleranceBars,
28304
29256
  toleranceMs,
28305
29257
  replayTargetsCount: replayTargets.length,
@@ -28321,7 +29273,7 @@ var init_runtimeParity2 = __esm({
28321
29273
  runtimeGateWarningCounts
28322
29274
  }),
28323
29275
  {
28324
- userName: flags25.user,
29276
+ userName: flags27.user,
28325
29277
  attachments: mismatchAttachment ? [mismatchAttachment] : void 0
28326
29278
  }
28327
29279
  );
@@ -28330,7 +29282,7 @@ var init_runtimeParity2 = __esm({
28330
29282
  (0, import_backtest3.resetTestingKlineCache)(projectRoot11);
28331
29283
  }
28332
29284
  };
28333
- main32 = runtimeParity;
29285
+ main35 = runtimeParity;
28334
29286
  }
28335
29287
  });
28336
29288
 
@@ -28400,14 +29352,14 @@ var init_hooks = __esm({
28400
29352
  });
28401
29353
 
28402
29354
  // src/lib/signals/runtimeStrategies.ts
28403
- var import_logger7, import_redis18, import_tradingAccounts2, import_strategies7, loadRuntimeStrategies;
29355
+ var import_logger7, import_redis18, import_tradingAccounts2, import_strategies11, loadRuntimeStrategies;
28404
29356
  var init_runtimeStrategies = __esm({
28405
29357
  "src/lib/signals/runtimeStrategies.ts"() {
28406
29358
  "use strict";
28407
29359
  import_logger7 = require("@tradejs/infra/logger");
28408
29360
  import_redis18 = require("@tradejs/infra/redis");
28409
29361
  import_tradingAccounts2 = require("@tradejs/infra/tradingAccounts");
28410
- import_strategies7 = require("@tradejs/node/strategies");
29362
+ import_strategies11 = require("@tradejs/node/strategies");
28411
29363
  init_runtimeRedis();
28412
29364
  loadRuntimeStrategies = async ({
28413
29365
  userName: userName2,
@@ -28451,7 +29403,7 @@ var init_runtimeStrategies = __esm({
28451
29403
  });
28452
29404
  const effectiveAccountId = resolvedAccount?.id ?? requestedAccountId;
28453
29405
  const [strategyCreator, strategyResults] = await Promise.all([
28454
- (0, import_strategies7.getStrategyCreator)(strategyName, projectRoot14),
29406
+ (0, import_strategies11.getStrategyCreator)(strategyName, projectRoot14),
28455
29407
  (0, import_redis18.getData)(import_redis18.redisKeys.strategyResults(userName2, strategyName), {})
28456
29408
  ]);
28457
29409
  if (!strategyCreator) {
@@ -28554,14 +29506,16 @@ var init_skipStats = __esm({
28554
29506
  });
28555
29507
 
28556
29508
  // src/lib/runtimeLineage.ts
28557
- var import_node_child_process6, import_node_crypto5, import_promises16, import_node_path10, RUNTIME_CONTEXT_ENV_KEYS, RUNTIME_CONTEXT_DATA_MODEL, normalizeForStableJson2, fingerprintRuntimeValue, gitLineageCache, gateFingerprintCache, binanceBreadthFingerprintCache, resolveGitLineage, readOptionalFile2, resolveBinanceBreadthFingerprint, resolveGateFingerprint2, buildRuntimeLineage, runtimeLineageKey, runtimeLineagesMatch;
29509
+ var import_node_child_process6, import_node_crypto6, import_promises16, import_node_path10, import_types6, import_strategies12, RUNTIME_CONTEXT_ENV_KEYS, RUNTIME_CONTEXT_DATA_MODEL, normalizeForStableJson2, resolveRuntimeMaxLossValue, fingerprintRuntimeValue, gitLineageCache, gateFingerprintCache, binanceBreadthFingerprintCache, snapshotFingerprintCache, resolveGitLineage, readOptionalFile2, resolveBinanceBreadthFingerprint, resolveSnapshotFingerprint, resolveGateFingerprint2, buildRuntimeLineage, runtimeLineageKey, runtimeLineagesMatch;
28558
29510
  var init_runtimeLineage = __esm({
28559
29511
  "src/lib/runtimeLineage.ts"() {
28560
29512
  "use strict";
28561
29513
  import_node_child_process6 = require("child_process");
28562
- import_node_crypto5 = require("crypto");
29514
+ import_node_crypto6 = require("crypto");
28563
29515
  import_promises16 = __toESM(require("fs/promises"));
28564
29516
  import_node_path10 = __toESM(require("path"));
29517
+ import_types6 = require("@tradejs/types");
29518
+ import_strategies12 = require("@tradejs/node/strategies");
28565
29519
  RUNTIME_CONTEXT_ENV_KEYS = [
28566
29520
  "AI_MODE",
28567
29521
  "MIN_AI_QUALITY",
@@ -28579,7 +29533,12 @@ var init_runtimeLineage = __esm({
28579
29533
  "COINMARKETCAP_CONTEXT_HISTORICAL_ACCESS_MONTHS",
28580
29534
  "COINMARKETCAP_CONTEXT_BACKFILL_ENABLED",
28581
29535
  "COINMARKETCAP_CONTEXT_BACKFILL_MAX_DAYS",
28582
- "COINMARKETCAP_CONTEXT_BACKFILL_WARMUP_DAYS"
29536
+ "COINMARKETCAP_CONTEXT_BACKFILL_WARMUP_DAYS",
29537
+ "HYPERLIQUID_WHALE_CONTEXT_ENABLED",
29538
+ "HYPERLIQUID_WHALE_BACKFILL_ENABLED",
29539
+ "HYPERLIQUID_WHALE_MIN_COVERAGE_PCT",
29540
+ "HYPERLIQUID_WHALE_CONCURRENCY",
29541
+ "HYPERLIQUID_WHALE_RATE_LIMIT_WEIGHT"
28583
29542
  ];
28584
29543
  RUNTIME_CONTEXT_DATA_MODEL = {
28585
29544
  derivativesSourceIntervals: "15m",
@@ -28589,7 +29548,11 @@ var init_runtimeLineage = __esm({
28589
29548
  coinMarketCapAvailabilityModel: "closed-interval-all-daily",
28590
29549
  coinMarketCapDataModelVersion: 3,
28591
29550
  binanceBreadthUniverseModel: "fixed-versioned-top5-top10-top30-top50-top100",
28592
- binanceBreadthDataModelVersion: 2
29551
+ binanceBreadthDataModelVersion: 2,
29552
+ hyperliquidWhaleCanonicalInterval: "1m",
29553
+ hyperliquidWhaleGateMinNotionalUsd: 5e4,
29554
+ hyperliquidWhaleMinCoveragePct: 0.8,
29555
+ hyperliquidWhaleDataModelVersion: import_types6.HYPERLIQUID_WHALE_DATA_MODEL_VERSION
28593
29556
  };
28594
29557
  normalizeForStableJson2 = (value) => {
28595
29558
  if (Array.isArray(value)) {
@@ -28602,10 +29565,24 @@ var init_runtimeLineage = __esm({
28602
29565
  }
28603
29566
  return value;
28604
29567
  };
28605
- fingerprintRuntimeValue = (value) => (0, import_node_crypto5.createHash)("sha256").update(JSON.stringify(normalizeForStableJson2(value))).digest("hex").slice(0, 16);
29568
+ resolveRuntimeMaxLossValue = (config) => {
29569
+ if (!config || typeof config !== "object" || Array.isArray(config)) {
29570
+ return null;
29571
+ }
29572
+ const strategyConfig = config.strategyConfig;
29573
+ if (!strategyConfig || typeof strategyConfig !== "object" || Array.isArray(strategyConfig)) {
29574
+ return null;
29575
+ }
29576
+ const maxLossValue = Number(
29577
+ strategyConfig.MAX_LOSS_VALUE
29578
+ );
29579
+ return Number.isFinite(maxLossValue) ? maxLossValue : null;
29580
+ };
29581
+ fingerprintRuntimeValue = (value) => (0, import_node_crypto6.createHash)("sha256").update(JSON.stringify(normalizeForStableJson2(value))).digest("hex").slice(0, 16);
28606
29582
  gitLineageCache = /* @__PURE__ */ new Map();
28607
29583
  gateFingerprintCache = /* @__PURE__ */ new Map();
28608
29584
  binanceBreadthFingerprintCache = /* @__PURE__ */ new Map();
29585
+ snapshotFingerprintCache = /* @__PURE__ */ new Map();
28609
29586
  resolveGitLineage = (projectRoot14) => {
28610
29587
  const cached = gitLineageCache.get(projectRoot14);
28611
29588
  if (cached) {
@@ -28679,6 +29656,24 @@ var init_runtimeLineage = __esm({
28679
29656
  binanceBreadthFingerprintCache.set(projectRoot14, result);
28680
29657
  return result;
28681
29658
  };
29659
+ resolveSnapshotFingerprint = async (projectRoot14, relativePath) => {
29660
+ const cacheKey = `${projectRoot14}:${relativePath}`;
29661
+ if (snapshotFingerprintCache.has(cacheKey)) {
29662
+ return snapshotFingerprintCache.get(cacheKey) ?? null;
29663
+ }
29664
+ const content = await readOptionalFile2(import_node_path10.default.join(projectRoot14, relativePath));
29665
+ let result = null;
29666
+ if (content) {
29667
+ try {
29668
+ const parsed = JSON.parse(content.toString("utf8"));
29669
+ result = typeof parsed.fingerprint === "string" && parsed.fingerprint.trim() ? parsed.fingerprint.trim() : null;
29670
+ } catch {
29671
+ result = null;
29672
+ }
29673
+ }
29674
+ snapshotFingerprintCache.set(cacheKey, result);
29675
+ return result;
29676
+ };
28682
29677
  resolveGateFingerprint2 = async ({
28683
29678
  projectRoot: projectRoot14,
28684
29679
  strategyName,
@@ -28694,7 +29689,8 @@ var init_runtimeLineage = __esm({
28694
29689
  `packages/strategies/src/${strategyName}/guardrails.ts`,
28695
29690
  `packages/strategies/src/${strategyName}/pockets.ts`,
28696
29691
  `packages/strategies/src/${strategyName}/config.ts`,
28697
- "packages/node/src/ai.ts"
29692
+ "packages/node/src/ai.ts",
29693
+ "packages/core/src/utils/strategyHelpers/signalBuilders.ts"
28698
29694
  ];
28699
29695
  const optionalEntries = await Promise.all(
28700
29696
  relativeCandidates.map(async (relativePath) => {
@@ -28710,7 +29706,7 @@ var init_runtimeLineage = __esm({
28710
29706
  sourceEntries.push(entry);
28711
29707
  }
28712
29708
  }
28713
- const hash = (0, import_node_crypto5.createHash)("sha256");
29709
+ const hash = (0, import_node_crypto6.createHash)("sha256");
28714
29710
  hash.update(strategyName);
28715
29711
  if (sourceEntries.length) {
28716
29712
  for (const entry of sourceEntries) {
@@ -28720,9 +29716,9 @@ var init_runtimeLineage = __esm({
28720
29716
  } else {
28721
29717
  hash.update(gitSha ?? "unknown-git-sha");
28722
29718
  }
28723
- const fingerprint = hash.digest("hex").slice(0, 16);
28724
- gateFingerprintCache.set(cacheKey, fingerprint);
28725
- return fingerprint;
29719
+ const fingerprint2 = hash.digest("hex").slice(0, 16);
29720
+ gateFingerprintCache.set(cacheKey, fingerprint2);
29721
+ return fingerprint2;
28726
29722
  };
28727
29723
  buildRuntimeLineage = async ({
28728
29724
  projectRoot: projectRoot14,
@@ -28732,12 +29728,22 @@ var init_runtimeLineage = __esm({
28732
29728
  env = process.env
28733
29729
  }) => {
28734
29730
  const git = resolveGitLineage(projectRoot14);
29731
+ const hyperliquidPerpUniverseFingerprint = typeof import_strategies12.getHyperliquidPerpUniverseSnapshot === "function" ? (0, import_strategies12.getHyperliquidPerpUniverseSnapshot)().fingerprint : await resolveSnapshotFingerprint(
29732
+ projectRoot14,
29733
+ "packages/node/src/config/hyperliquidPerpUniverse.json"
29734
+ );
29735
+ const hyperliquidWhaleRegistryFingerprint = typeof import_strategies12.getHyperliquidWhaleRegistrySnapshot === "function" ? (0, import_strategies12.getHyperliquidWhaleRegistrySnapshot)().fingerprint : await resolveSnapshotFingerprint(
29736
+ projectRoot14,
29737
+ "packages/node/src/config/hyperliquidWhales.json"
29738
+ );
28735
29739
  const context = {
28736
29740
  ...Object.fromEntries(
28737
29741
  RUNTIME_CONTEXT_ENV_KEYS.map((key) => [key, env[key] ?? null])
28738
29742
  ),
28739
29743
  ...RUNTIME_CONTEXT_DATA_MODEL,
28740
29744
  binanceBreadthUniverseFingerprint: await resolveBinanceBreadthFingerprint(projectRoot14),
29745
+ hyperliquidPerpUniverseFingerprint,
29746
+ hyperliquidWhaleRegistryFingerprint,
28741
29747
  ...runContext
28742
29748
  };
28743
29749
  return {
@@ -28749,7 +29755,8 @@ var init_runtimeLineage = __esm({
28749
29755
  gitSha: git.gitSha
28750
29756
  }),
28751
29757
  configFingerprint: fingerprintRuntimeValue(config),
28752
- contextFingerprint: fingerprintRuntimeValue(context)
29758
+ contextFingerprint: fingerprintRuntimeValue(context),
29759
+ maxLossValue: resolveRuntimeMaxLossValue(config)
28753
29760
  };
28754
29761
  };
28755
29762
  runtimeLineageKey = (lineage) => [
@@ -29139,13 +30146,13 @@ var init_daemon = __esm({
29139
30146
  });
29140
30147
 
29141
30148
  // src/lib/signals/klineFeed.ts
29142
- var import_connectors15, import_logger9, import_timescale10, FLUSH_DELAY_MS, normalizeSymbols2, createSignalsKlineFeed;
30149
+ var import_connectors15, import_logger9, import_timescale12, FLUSH_DELAY_MS, normalizeSymbols2, createSignalsKlineFeed;
29143
30150
  var init_klineFeed = __esm({
29144
30151
  "src/lib/signals/klineFeed.ts"() {
29145
30152
  "use strict";
29146
30153
  import_connectors15 = require("@tradejs/connectors");
29147
30154
  import_logger9 = require("@tradejs/infra/logger");
29148
- import_timescale10 = require("@tradejs/infra/timescale");
30155
+ import_timescale12 = require("@tradejs/infra/timescale");
29149
30156
  init_klineEvents();
29150
30157
  FLUSH_DELAY_MS = 100;
29151
30158
  normalizeSymbols2 = (symbols) => [...new Set(symbols.map((symbol) => symbol.trim().toUpperCase()))].filter(
@@ -29158,9 +30165,9 @@ var init_klineFeed = __esm({
29158
30165
  publish = publishMarketKlineEvent,
29159
30166
  persist = async (rows) => {
29160
30167
  const intervalMinutes = Number(interval5);
29161
- await (0, import_timescale10.upsertCandles)(
30168
+ await (0, import_timescale12.upsertCandles)(
29162
30169
  rows.flatMap(
29163
- ({ symbol, candle }) => (0, import_timescale10.toRows)("bybit", symbol, intervalMinutes, [candle])
30170
+ ({ symbol, candle }) => (0, import_timescale12.toRows)("bybit", symbol, intervalMinutes, [candle])
29164
30171
  )
29165
30172
  );
29166
30173
  },
@@ -29273,25 +30280,25 @@ var init_klineFeed = __esm({
29273
30280
  var signals_exports = {};
29274
30281
  __export(signals_exports, {
29275
30282
  createSignalsSession: () => createSignalsSession,
29276
- main: () => main33,
30283
+ main: () => main36,
29277
30284
  signals: () => signals,
29278
30285
  signalsConfiguredScopesOnce: () => signalsConfiguredScopesOnce,
29279
30286
  signalsDaemon: () => signalsDaemon
29280
30287
  });
29281
- var import_config10, import_args27, import_progress10, import_chalk41, import_connectors16, import_cli12, import_async5, import_strategies8, import_constants16, import_strategies9, import_time8, import_logger10, import_redis20, import_tradingAccounts3, SLOW_SIGNALS_WARNING_MS, projectRoot12, flags26, interval4, intervalMs, formatDuration3, resolvePositiveInteger2, resolveNonNegativeInteger, resolveSignalsDaemonMaxLiveBars, isSignalsKlineWsEnabled, resolveSignalsKlineWsWaitMs, createDefaultSignalsLifecycle, timeOperation3, formatSlowSignalsWarning, resolveSignalsConnectorName, createSignalsSession, findSignals, signals, loadConfiguredSignalsScopes, hasExplicitSignalsScope, signalsConfiguredScopesOnce, signalsDaemon, main33;
30288
+ var import_config11, import_args29, import_progress10, import_chalk41, import_connectors16, import_cli12, import_async5, import_strategies13, import_constants16, import_strategies14, import_time8, import_logger10, import_redis20, import_tradingAccounts3, SLOW_SIGNALS_WARNING_MS, projectRoot12, flags28, interval4, intervalMs, formatDuration3, resolvePositiveInteger2, resolveNonNegativeInteger, resolveSignalsDaemonMaxLiveBars, isSignalsKlineWsEnabled, resolveSignalsKlineWsWaitMs, createDefaultSignalsLifecycle, timeOperation3, formatSlowSignalsWarning, resolveSignalsConnectorName, createSignalsSession, findSignals, signals, loadConfiguredSignalsScopes, hasExplicitSignalsScope, signalsConfiguredScopesOnce, signalsDaemon, main36;
29282
30289
  var init_signals = __esm({
29283
30290
  "src/scripts/signals.ts"() {
29284
30291
  "use strict";
29285
- import_config10 = require("dotenv/config");
29286
- import_args27 = __toESM(require("args"));
30292
+ import_config11 = require("dotenv/config");
30293
+ import_args29 = __toESM(require("args"));
29287
30294
  import_progress10 = __toESM(require("progress"));
29288
30295
  import_chalk41 = __toESM(require("chalk"));
29289
30296
  import_connectors16 = require("@tradejs/node/connectors");
29290
30297
  import_cli12 = require("@tradejs/node/cli");
29291
30298
  import_async5 = require("@tradejs/core/async");
29292
- import_strategies8 = require("@tradejs/core/strategies");
30299
+ import_strategies13 = require("@tradejs/core/strategies");
29293
30300
  import_constants16 = require("@tradejs/core/constants");
29294
- import_strategies9 = require("@tradejs/node/strategies");
30301
+ import_strategies14 = require("@tradejs/node/strategies");
29295
30302
  import_time8 = require("@tradejs/core/time");
29296
30303
  import_logger10 = require("@tradejs/infra/logger");
29297
30304
  import_redis20 = require("@tradejs/infra/redis");
@@ -29311,46 +30318,46 @@ var init_signals = __esm({
29311
30318
  init_runtimeLifecycle();
29312
30319
  init_daemon();
29313
30320
  init_klineFeed();
29314
- import_args27.default.option(["t", "tickers"], "Selected tickers");
29315
- import_args27.default.option(["e", "exclude"], "Exclude tickers from tests");
29316
- import_args27.default.option(["l", "tickersLimit"], "Tickers limit");
29317
- import_args27.default.option(["f", "timeframe"], "Timeframe", 15);
29318
- import_args27.default.option(["m", "makeOrders"], "Make orders");
29319
- import_args27.default.option(["N", "notify"], "Send message in Telegram", false);
29320
- import_args27.default.option(["S", "skipScreenshots"], "Skip screenshot generation", false);
29321
- import_args27.default.option(["u", "updateOnly"], "Only update tickers history", false);
29322
- import_args27.default.option(["C", "cacheOnly"], "Do not update tickers history", false);
29323
- import_args27.default.option(["L", "showTickersList"], "Just show only ticker list", false);
29324
- import_args27.default.option(
30321
+ import_args29.default.option(["t", "tickers"], "Selected tickers");
30322
+ import_args29.default.option(["e", "exclude"], "Exclude tickers from tests");
30323
+ import_args29.default.option(["l", "tickersLimit"], "Tickers limit");
30324
+ import_args29.default.option(["f", "timeframe"], "Timeframe", 15);
30325
+ import_args29.default.option(["m", "makeOrders"], "Make orders");
30326
+ import_args29.default.option(["N", "notify"], "Send message in Telegram", false);
30327
+ import_args29.default.option(["S", "skipScreenshots"], "Skip screenshot generation", false);
30328
+ import_args29.default.option(["u", "updateOnly"], "Only update tickers history", false);
30329
+ import_args29.default.option(["C", "cacheOnly"], "Do not update tickers history", false);
30330
+ import_args29.default.option(["L", "showTickersList"], "Just show only ticker list", false);
30331
+ import_args29.default.option(
29325
30332
  ["p", "parallel"],
29326
30333
  "Signal evaluation worker count",
29327
30334
  Number(process.env.SIGNALS_PARALLEL || 4)
29328
30335
  );
29329
- import_args27.default.option(
30336
+ import_args29.default.option(
29330
30337
  ["R", "showSkipStats"],
29331
30338
  "Show aggregated skip stats by strategy",
29332
30339
  false
29333
30340
  );
29334
- import_args27.default.option(["c", "chunk"], "Split by chunks, ex. 1/3");
29335
- import_args27.default.option(["U", "user"], "Use user confg", "root");
29336
- import_args27.default.option(["w", "watch"], "Keep signals running on candle boundaries", false);
29337
- import_args27.default.option(
30341
+ import_args29.default.option(["c", "chunk"], "Split by chunks, ex. 1/3");
30342
+ import_args29.default.option(["U", "user"], "Use user confg", "root");
30343
+ import_args29.default.option(["w", "watch"], "Keep signals running on candle boundaries", false);
30344
+ import_args29.default.option(
29338
30345
  ["d", "settleDelayMs"],
29339
30346
  "Delay after candle close before a daemon cycle",
29340
30347
  Number(process.env.SIGNALS_DAEMON_SETTLE_DELAY_MS || 5e3)
29341
30348
  );
29342
- import_args27.default.option(
30349
+ import_args29.default.option(
29343
30350
  ["o", "connector"],
29344
30351
  "Connector provider or name for signals (e.g. bybit, binance, coinbase, custom)",
29345
30352
  "bybit"
29346
30353
  );
29347
- import_args27.default.option(["V", "universe"], "Market universe (crypto or tradfi)", "crypto");
29348
- import_args27.default.option(["A", "account"], "Trading account id");
29349
- import_args27.default.option(["D", "deployment"], "Runtime deployment id");
30354
+ import_args29.default.option(["V", "universe"], "Market universe (crypto or tradfi)", "crypto");
30355
+ import_args29.default.option(["A", "account"], "Trading account id");
30356
+ import_args29.default.option(["D", "deployment"], "Runtime deployment id");
29350
30357
  SLOW_SIGNALS_WARNING_MS = 10 * 6e4;
29351
30358
  projectRoot12 = String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
29352
- flags26 = import_args27.default.parse(process.argv);
29353
- interval4 = flags26.timeframe.toString();
30359
+ flags28 = import_args29.default.parse(process.argv);
30360
+ interval4 = flags28.timeframe.toString();
29354
30361
  intervalMs = Number(interval4) * 6e4;
29355
30362
  formatDuration3 = (durationMs) => `${(durationMs / 1e3).toFixed(1)}s`;
29356
30363
  resolvePositiveInteger2 = (value, fallback) => {
@@ -29385,7 +30392,7 @@ var init_signals = __esm({
29385
30392
  createDefaultSignalsLifecycle = (runtimeInterval = interval4) => createSignalsStrategyLifecycle({
29386
30393
  intervalMs: Number(runtimeInterval) * 6e4,
29387
30394
  maxLiveBars: resolveSignalsDaemonMaxLiveBars(runtimeInterval),
29388
- releaseState: import_strategies8.releaseStrategyReplayCache
30395
+ releaseState: import_strategies13.releaseStrategyReplayCache
29389
30396
  });
29390
30397
  timeOperation3 = (label, operation) => timeOperation(
29391
30398
  label,
@@ -29412,9 +30419,9 @@ var init_signals = __esm({
29412
30419
  return import_connectors16.DEFAULT_CONNECTOR_NAME;
29413
30420
  };
29414
30421
  createSignalsSession = async (lifecycle, scope = {}) => {
29415
- const deployment = flags26.deployment ? await (0, import_tradingAccounts3.getRuntimeDeployment)(flags26.user, String(flags26.deployment)) : null;
29416
- if (flags26.deployment && !deployment) {
29417
- throw new Error(`Runtime deployment not found: ${flags26.deployment}`);
30422
+ const deployment = flags28.deployment ? await (0, import_tradingAccounts3.getRuntimeDeployment)(flags28.user, String(flags28.deployment)) : null;
30423
+ if (flags28.deployment && !deployment) {
30424
+ throw new Error(`Runtime deployment not found: ${flags28.deployment}`);
29418
30425
  }
29419
30426
  if (deployment && !deployment.enabled) {
29420
30427
  throw new Error(`Runtime deployment is disabled: ${deployment.id}`);
@@ -29431,10 +30438,10 @@ var init_signals = __esm({
29431
30438
  );
29432
30439
  }
29433
30440
  const connectorName = await resolveSignalsConnectorName(
29434
- deployment?.connectorName ?? scope.connectorName ?? flags26.connector
30441
+ deployment?.connectorName ?? scope.connectorName ?? flags28.connector
29435
30442
  );
29436
- const universe = deployment?.universe ?? scope.universe ?? flags26.universe;
29437
- const requestedAccountId = deployment?.accountId ?? scope.accountId ?? flags26.account;
30443
+ const universe = deployment?.universe ?? scope.universe ?? flags28.universe;
30444
+ const requestedAccountId = deployment?.accountId ?? scope.accountId ?? flags28.account;
29438
30445
  const connectorFactory = await (0, import_connectors16.getConnectorCreatorByName)(
29439
30446
  connectorName,
29440
30447
  projectRoot12
@@ -29443,7 +30450,7 @@ var init_signals = __esm({
29443
30450
  throw new Error(`Connector "${connectorName}" is not registered`);
29444
30451
  }
29445
30452
  const marketConnector = await connectorFactory({
29446
- userName: flags26.user,
30453
+ userName: flags28.user,
29447
30454
  universe,
29448
30455
  accountId: requestedAccountId,
29449
30456
  deploymentId: deployment?.id
@@ -29451,7 +30458,7 @@ var init_signals = __esm({
29451
30458
  const btcReferences = await loadBtcReferenceConnectors({
29452
30459
  connectorName,
29453
30460
  marketConnector,
29454
- userName: flags26.user,
30461
+ userName: flags28.user,
29455
30462
  projectRoot: projectRoot12,
29456
30463
  shouldUseDedicatedReferences: universe === "crypto" && connectorName.toLowerCase() === import_connectors16.DEFAULT_CONNECTOR_NAME.toLowerCase(),
29457
30464
  warn: (message) => import_logger10.logger.warn(message)
@@ -29514,7 +30521,7 @@ var init_signals = __esm({
29514
30521
  strategyConfig,
29515
30522
  env: "CRON",
29516
30523
  interval: interval5,
29517
- makeOrders: flags26.makeOrders
30524
+ makeOrders: flags28.makeOrders
29518
30525
  });
29519
30526
  const runtimeLineage = await buildRuntimeLineage({
29520
30527
  projectRoot: projectRoot12,
@@ -29564,7 +30571,7 @@ var init_signals = __esm({
29564
30571
  btcCoinbaseData: lifecycleBtcCoinbaseData,
29565
30572
  onRuntimeClose
29566
30573
  }) => strategyCreator({
29567
- userName: flags26.user,
30574
+ userName: flags28.user,
29568
30575
  connectorName,
29569
30576
  runtimeConfigId: configId,
29570
30577
  runtimeConfigSnapshot: {
@@ -29623,7 +30630,7 @@ var init_signals = __esm({
29623
30630
  timestamp: lastCandle.timestamp,
29624
30631
  runtimeConfigId: configId
29625
30632
  }),
29626
- userName: flags26.user,
30633
+ userName: flags28.user,
29627
30634
  strategy: strategyName,
29628
30635
  runtimeConfigId: configId,
29629
30636
  runtimeLineage,
@@ -29649,7 +30656,7 @@ var init_signals = __esm({
29649
30656
  if (configId && configId !== "config" && !signal.signalId.endsWith(`:${configId}`)) {
29650
30657
  signal.signalId = `${signal.signalId}:${configId}`;
29651
30658
  }
29652
- await (0, import_strategies9.enrichSignalWithBinanceMarketContext)({
30659
+ await (0, import_strategies14.enrichSignalWithBinanceMarketContext)({
29653
30660
  signal,
29654
30661
  env: "CRON"
29655
30662
  });
@@ -29672,7 +30679,7 @@ var init_signals = __esm({
29672
30679
  );
29673
30680
  await (0, import_redis20.setHashJsonField)(
29674
30681
  import_redis20.redisKeys.runtimeSignalBucket(
29675
- flags26.user,
30682
+ flags28.user,
29676
30683
  (0, import_time6.getRuntimeStorageDayKey)(signal.timestamp),
29677
30684
  signal.strategy
29678
30685
  ),
@@ -29689,7 +30696,7 @@ var init_signals = __esm({
29689
30696
  timestamp: lastCandle.timestamp,
29690
30697
  runtimeConfigId: configId
29691
30698
  }),
29692
- userName: flags26.user,
30699
+ userName: flags28.user,
29693
30700
  strategy: strategyName,
29694
30701
  runtimeConfigId: configId,
29695
30702
  runtimeLineage,
@@ -29744,10 +30751,10 @@ var init_signals = __esm({
29744
30751
  const tickers = await timeOperation3("tickers load", () => {
29745
30752
  const baseArgs = [
29746
30753
  marketConnector,
29747
- flags26.tickers || deployment?.tickers?.join(","),
29748
- flags26.exclude,
29749
- flags26.tickersLimit,
29750
- flags26.chunk
30754
+ flags28.tickers || deployment?.tickers?.join(","),
30755
+ flags28.exclude,
30756
+ flags28.tickersLimit,
30757
+ flags28.chunk
29751
30758
  ];
29752
30759
  return sessionUniverse || accountId || deployment ? (0, import_cli12.getTickers)(...baseArgs, {
29753
30760
  universe,
@@ -29765,7 +30772,7 @@ var init_signals = __esm({
29765
30772
  const instrumentsBySymbol = new Map(
29766
30773
  instruments.map((instrument) => [instrument.symbol, instrument])
29767
30774
  );
29768
- if (flags26.showTickersList) {
30775
+ if (flags28.showTickersList) {
29769
30776
  console.log(import_chalk41.default.gray(JSON.stringify(tickers.sort(), null, 2)));
29770
30777
  return;
29771
30778
  }
@@ -29773,7 +30780,7 @@ var init_signals = __esm({
29773
30780
  projectHooks = projectConfig.hooks;
29774
30781
  const currentTimestamp = (0, import_time8.getTimestamp)();
29775
30782
  const preloadStart = (0, import_time8.getTimestamp)(import_constants16.SIGNALS_CLI_PRELOAD_DAYS);
29776
- if (!flags26.cacheOnly && session.klineFeed) {
30783
+ if (!flags28.cacheOnly && session.klineFeed) {
29777
30784
  const expectedSymbols = universe === "crypto" ? [.../* @__PURE__ */ new Set([...tickers, "BTCUSDT", "ETHUSDT"])] : tickers;
29778
30785
  session.klineFeed.setSubscriptions(expectedSymbols);
29779
30786
  const targetTimestamp = Math.floor(currentTimestamp / intervalMs2) * intervalMs2 - intervalMs2;
@@ -29808,7 +30815,7 @@ var init_signals = __esm({
29808
30815
  universe,
29809
30816
  log: (message) => import_logger10.logger.info(import_chalk41.default.gray(message))
29810
30817
  });
29811
- } else if (!flags26.cacheOnly) {
30818
+ } else if (!flags28.cacheOnly) {
29812
30819
  await updateMarketHistoryWithBtcReferences({
29813
30820
  marketConnector,
29814
30821
  connectorName,
@@ -29822,7 +30829,7 @@ var init_signals = __esm({
29822
30829
  }
29823
30830
  await prepareMarketContextForRun({
29824
30831
  mode: "signals",
29825
- userName: flags26.user,
30832
+ userName: flags28.user,
29826
30833
  projectRoot: projectRoot12,
29827
30834
  symbols: tickers,
29828
30835
  universe,
@@ -29830,10 +30837,10 @@ var init_signals = __esm({
29830
30837
  startMs: currentTimestamp,
29831
30838
  endMs: currentTimestamp,
29832
30839
  preloadStartMs: preloadStart,
29833
- cacheOnly: Boolean(flags26.cacheOnly),
30840
+ cacheOnly: Boolean(flags28.cacheOnly),
29834
30841
  log: (message) => import_logger10.logger.info(import_chalk41.default.gray(message))
29835
30842
  });
29836
- if (flags26.updateOnly) {
30843
+ if (flags28.updateOnly) {
29837
30844
  return;
29838
30845
  }
29839
30846
  const [
@@ -29888,7 +30895,7 @@ var init_signals = __esm({
29888
30895
  primaryEthClosedData.map((candle) => [candle.timestamp, candle])
29889
30896
  );
29890
30897
  const runtimeStrategies = await loadRuntimeStrategies({
29891
- userName: flags26.user,
30898
+ userName: flags28.user,
29892
30899
  projectRoot: projectRoot12,
29893
30900
  deployment,
29894
30901
  connectorName,
@@ -29900,7 +30907,7 @@ var init_signals = __esm({
29900
30907
  lifecycle.clear();
29901
30908
  import_logger10.logger.warn(
29902
30909
  "No strategy configs found by users:%s:strategies:*:config",
29903
- flags26.user
30910
+ flags28.user
29904
30911
  );
29905
30912
  return;
29906
30913
  }
@@ -29924,13 +30931,13 @@ var init_signals = __esm({
29924
30931
  );
29925
30932
  import_logger10.logger.info(
29926
30933
  import_chalk41.default.yellow(
29927
- `loaded strategies (user=${flags26.user}): ${runtimeStrategies.map((s) => s.strategyName).join(", ")}`
30934
+ `loaded strategies (user=${flags28.user}): ${runtimeStrategies.map((s) => s.strategyName).join(", ")}`
29928
30935
  )
29929
30936
  );
29930
30937
  afterSignalsHookContext = {
29931
30938
  connector: marketConnector,
29932
30939
  connectorName,
29933
- userName: flags26.user,
30940
+ userName: flags28.user,
29934
30941
  interval: interval5,
29935
30942
  tickers: [...tickers],
29936
30943
  runtimeStrategies: runtimeStrategies.map(
@@ -29960,7 +30967,7 @@ var init_signals = __esm({
29960
30967
  }
29961
30968
  );
29962
30969
  import_logger10.logger.info(import_chalk41.default.yellow(`tickers: ${tickers.length}`));
29963
- const signalsParallel = resolvePositiveInteger2(flags26.parallel, 4);
30970
+ const signalsParallel = resolvePositiveInteger2(flags28.parallel, 4);
29964
30971
  import_logger10.logger.info(import_chalk41.default.yellow(`signal workers: ${signalsParallel}`));
29965
30972
  await timeOperation3("strategy evaluation", async () => {
29966
30973
  await (0, import_async5.runWithConcurrency)(tickers, signalsParallel, async (symbol) => {
@@ -29998,15 +31005,15 @@ var init_signals = __esm({
29998
31005
  });
29999
31006
  });
30000
31007
  });
30001
- if (flags26.notify) {
31008
+ if (flags28.notify) {
30002
31009
  const telegramSignals = getTelegramDeliverableSignals(signals2);
30003
- if (!flags26.skipScreenshots && telegramSignals.length > 0) {
30004
- await (0, import_cli12.makeScreenshots)(telegramSignals, interval5, flags26.user);
31010
+ if (!flags28.skipScreenshots && telegramSignals.length > 0) {
31011
+ await (0, import_cli12.makeScreenshots)(telegramSignals, interval5, flags28.user);
30005
31012
  }
30006
- await (0, import_cli12.sendToTG)(telegramSignals, interval5, flags26.user);
31013
+ await (0, import_cli12.sendToTG)(telegramSignals, interval5, flags28.user);
30007
31014
  await (0, import_cli12.sendRuntimeCloseNotificationsToTG)(
30008
31015
  runtimeCloseNotifications,
30009
- flags26.user
31016
+ flags28.user
30010
31017
  );
30011
31018
  }
30012
31019
  import_logger10.logger.info(
@@ -30016,7 +31023,7 @@ var init_signals = __esm({
30016
31023
  2
30017
31024
  )
30018
31025
  );
30019
- if (flags26.showSkipStats) {
31026
+ if (flags28.showSkipStats) {
30020
31027
  logStrategySkipStats(runtimeStrategies, strategyStats);
30021
31028
  }
30022
31029
  } catch (error) {
@@ -30038,11 +31045,11 @@ var init_signals = __esm({
30038
31045
  }
30039
31046
  const durationMs = Date.now() - startedAt;
30040
31047
  if (activeSession?.deployment) {
30041
- await (0, import_tradingAccounts3.saveRuntimeDeploymentHeartbeat)(flags26.user, {
31048
+ await (0, import_tradingAccounts3.saveRuntimeDeploymentHeartbeat)(flags28.user, {
30042
31049
  deploymentId: activeSession.deployment.id,
30043
31050
  status: getSignalsHeartbeatStatus({
30044
31051
  cycleStatus: status,
30045
- continuous: options.session != null || Boolean(flags26.watch)
31052
+ continuous: options.session != null || Boolean(flags28.watch)
30046
31053
  }),
30047
31054
  pid: process.pid,
30048
31055
  startedAt: activeSession.startedAt,
@@ -30065,7 +31072,7 @@ var init_signals = __esm({
30065
31072
  );
30066
31073
  }
30067
31074
  }
30068
- if (flags26.notify && durationMs > SLOW_SIGNALS_WARNING_MS) {
31075
+ if (flags28.notify && durationMs > SLOW_SIGNALS_WARNING_MS) {
30069
31076
  try {
30070
31077
  await (0, import_cli12.sendTextToTG)(
30071
31078
  formatSlowSignalsWarning({
@@ -30073,7 +31080,7 @@ var init_signals = __esm({
30073
31080
  status,
30074
31081
  found: signals2.length
30075
31082
  }),
30076
- { userName: flags26.user }
31083
+ { userName: flags28.user }
30077
31084
  );
30078
31085
  } catch (error) {
30079
31086
  import_logger10.logger.error(
@@ -30094,13 +31101,13 @@ var init_signals = __esm({
30094
31101
  };
30095
31102
  loadConfiguredSignalsScopes = async (deployment) => {
30096
31103
  const configuredStrategies = await loadRuntimeStrategies({
30097
- userName: flags26.user,
31104
+ userName: flags28.user,
30098
31105
  projectRoot: projectRoot12,
30099
31106
  deployment,
30100
- connectorName: String(flags26.connector)
31107
+ connectorName: String(flags28.connector)
30101
31108
  });
30102
31109
  const connectorName = await resolveSignalsConnectorName(
30103
- deployment?.connectorName ?? flags26.connector
31110
+ deployment?.connectorName ?? flags28.connector
30104
31111
  );
30105
31112
  const scopes = /* @__PURE__ */ new Map();
30106
31113
  for (const strategy of configuredStrategies) {
@@ -30134,7 +31141,7 @@ var init_signals = __esm({
30134
31141
  )
30135
31142
  );
30136
31143
  signalsConfiguredScopesOnce = async () => {
30137
- if (flags26.updateOnly || flags26.showTickersList || flags26.deployment || hasExplicitSignalsScope()) {
31144
+ if (flags28.updateOnly || flags28.showTickersList || flags28.deployment || hasExplicitSignalsScope()) {
30138
31145
  return signals();
30139
31146
  }
30140
31147
  const scopes = await loadConfiguredSignalsScopes();
@@ -30153,15 +31160,15 @@ var init_signals = __esm({
30153
31160
  }
30154
31161
  };
30155
31162
  signalsDaemon = async () => {
30156
- if (flags26.updateOnly || flags26.showTickersList) {
31163
+ if (flags28.updateOnly || flags28.showTickersList) {
30157
31164
  throw new Error(
30158
31165
  "Signals daemon does not support updateOnly or showTickersList mode"
30159
31166
  );
30160
31167
  }
30161
31168
  const abortController = new AbortController();
30162
- const daemonDeployment = flags26.deployment ? await (0, import_tradingAccounts3.getRuntimeDeployment)(flags26.user, String(flags26.deployment)) : null;
30163
- if (flags26.deployment && !daemonDeployment) {
30164
- throw new Error(`Runtime deployment not found: ${flags26.deployment}`);
31169
+ const daemonDeployment = flags28.deployment ? await (0, import_tradingAccounts3.getRuntimeDeployment)(flags28.user, String(flags28.deployment)) : null;
31170
+ if (flags28.deployment && !daemonDeployment) {
31171
+ throw new Error(`Runtime deployment not found: ${flags28.deployment}`);
30165
31172
  }
30166
31173
  const sessions = /* @__PURE__ */ new Map();
30167
31174
  const lastBoundaryByScope = /* @__PURE__ */ new Map();
@@ -30180,7 +31187,7 @@ var init_signals = __esm({
30180
31187
  try {
30181
31188
  await runSignalsDaemon({
30182
31189
  intervalMs: 6e4,
30183
- settleDelayMs: resolveNonNegativeInteger(flags26.settleDelayMs, 5e3),
31190
+ settleDelayMs: resolveNonNegativeInteger(flags28.settleDelayMs, 5e3),
30184
31191
  signal: abortController.signal,
30185
31192
  runCycle: async () => {
30186
31193
  const scopes = await loadConfiguredSignalsScopes(daemonDeployment);
@@ -30206,7 +31213,7 @@ var init_signals = __esm({
30206
31213
  if (!session.klineFeed && isSignalsKlineWsEnabled() && session.connectorName.toLowerCase() === "bybit") {
30207
31214
  session.klineFeed = await createSignalsKlineFeed({
30208
31215
  config: {
30209
- userName: flags26.user,
31216
+ userName: flags28.user,
30210
31217
  universe: session.universe,
30211
31218
  accountId: session.accountId,
30212
31219
  deploymentId: session.deployment?.id
@@ -30259,7 +31266,7 @@ var init_signals = __esm({
30259
31266
  await session.klineFeed?.close();
30260
31267
  session.lifecycle.clear();
30261
31268
  if (session.deployment) {
30262
- await (0, import_tradingAccounts3.saveRuntimeDeploymentHeartbeat)(flags26.user, {
31269
+ await (0, import_tradingAccounts3.saveRuntimeDeploymentHeartbeat)(flags28.user, {
30263
31270
  deploymentId: session.deployment.id,
30264
31271
  status: "stopped",
30265
31272
  pid: process.pid,
@@ -30270,30 +31277,30 @@ var init_signals = __esm({
30270
31277
  }
30271
31278
  }
30272
31279
  };
30273
- main33 = () => flags26.watch ? signalsDaemon() : signalsConfiguredScopesOnce();
31280
+ main36 = () => flags28.watch ? signalsDaemon() : signalsConfiguredScopesOnce();
30274
31281
  }
30275
31282
  });
30276
31283
 
30277
31284
  // src/scripts/signalsDaemon.ts
30278
31285
  var signalsDaemon_exports = {};
30279
31286
  __export(signalsDaemon_exports, {
30280
- main: () => main34
31287
+ main: () => main37
30281
31288
  });
30282
- var main34;
31289
+ var main37;
30283
31290
  var init_signalsDaemon = __esm({
30284
31291
  "src/scripts/signalsDaemon.ts"() {
30285
31292
  "use strict";
30286
31293
  init_signals();
30287
- main34 = signalsDaemon;
31294
+ main37 = signalsDaemon;
30288
31295
  }
30289
31296
  });
30290
31297
 
30291
31298
  // src/lib/replay/cliConfig.ts
30292
- var import_args28, REPLAY_UNSUPPORTED_FLAGS, hasCliFlag2, assertNoUnsupportedReplayFlags, replayNormalizedArgv, replayFlags, replayInterval, replayUserName, replayProjectRoot, isReplayUpdateOnlyRun;
31299
+ var import_args30, REPLAY_UNSUPPORTED_FLAGS, hasCliFlag2, assertNoUnsupportedReplayFlags, replayNormalizedArgv, replayFlags, replayInterval, replayUserName, replayProjectRoot, isReplayUpdateOnlyRun;
30293
31300
  var init_cliConfig2 = __esm({
30294
31301
  "src/lib/replay/cliConfig.ts"() {
30295
31302
  "use strict";
30296
- import_args28 = __toESM(require("args"));
31303
+ import_args30 = __toESM(require("args"));
30297
31304
  init_cliArgs();
30298
31305
  REPLAY_UNSUPPORTED_FLAGS = [
30299
31306
  ["--live", "`yarn replay` is already the replacement for legacy `--live`."],
@@ -30327,27 +31334,27 @@ var init_cliConfig2 = __esm({
30327
31334
  }
30328
31335
  }
30329
31336
  };
30330
- import_args28.default.example(
31337
+ import_args30.default.example(
30331
31338
  "yarn replay --days 7 --cacheOnly",
30332
31339
  "Replay active runtime strategies on historical data"
30333
31340
  );
30334
- import_args28.default.option(["t", "tickers"], "Selected tickers");
30335
- import_args28.default.option(["e", "exclude"], "Exclude tickers from replay");
30336
- import_args28.default.option(["l", "tickersLimit"], "Tickers limit");
30337
- import_args28.default.option(["f", "timeframe"], "Timeframe", 15);
30338
- import_args28.default.option(["d", "days"], "Run replay only for the last N days");
30339
- import_args28.default.option("startTime", "Explicit replay start timestamp (ms or seconds)");
30340
- import_args28.default.option("endTime", "Explicit replay end timestamp (ms or seconds)");
30341
- import_args28.default.option(["u", "updateOnly"], "Only update tickers history", false);
30342
- import_args28.default.option(["C", "cacheOnly"], "Do not update tickers history", false);
30343
- import_args28.default.option(
31341
+ import_args30.default.option(["t", "tickers"], "Selected tickers");
31342
+ import_args30.default.option(["e", "exclude"], "Exclude tickers from replay");
31343
+ import_args30.default.option(["l", "tickersLimit"], "Tickers limit");
31344
+ import_args30.default.option(["f", "timeframe"], "Timeframe", 15);
31345
+ import_args30.default.option(["d", "days"], "Run replay only for the last N days");
31346
+ import_args30.default.option("startTime", "Explicit replay start timestamp (ms or seconds)");
31347
+ import_args30.default.option("endTime", "Explicit replay end timestamp (ms or seconds)");
31348
+ import_args30.default.option(["u", "updateOnly"], "Only update tickers history", false);
31349
+ import_args30.default.option(["C", "cacheOnly"], "Do not update tickers history", false);
31350
+ import_args30.default.option(
30344
31351
  "chart",
30345
31352
  "Save compact replay chart data for the strategies UI",
30346
31353
  false
30347
31354
  );
30348
- import_args28.default.option(["L", "showTickersList"], "Just show only ticker list", false);
30349
- import_args28.default.option(["U", "user"], "Use user config", "root");
30350
- import_args28.default.option(
31355
+ import_args30.default.option(["L", "showTickersList"], "Just show only ticker list", false);
31356
+ import_args30.default.option(["U", "user"], "Use user config", "root");
31357
+ import_args30.default.option(
30351
31358
  ["o", "connector"],
30352
31359
  "Connector provider or name for replay (e.g. bybit, binance, coinbase, custom)",
30353
31360
  "bybit"
@@ -30359,7 +31366,7 @@ var init_cliConfig2 = __esm({
30359
31366
  "-U": "--user"
30360
31367
  });
30361
31368
  assertNoUnsupportedReplayFlags(replayNormalizedArgv);
30362
- replayFlags = import_args28.default.parse(replayNormalizedArgv);
31369
+ replayFlags = import_args30.default.parse(replayNormalizedArgv);
30363
31370
  replayInterval = String(replayFlags.timeframe ?? 15);
30364
31371
  replayUserName = String(replayFlags.user ?? "root");
30365
31372
  replayProjectRoot = String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
@@ -30778,20 +31785,20 @@ var init_portfolioReplayConnector = __esm({
30778
31785
  });
30779
31786
 
30780
31787
  // src/lib/replay/historicalSignalsReplay.ts
30781
- var import_chalk42, import_progress11, import_backtest4, import_strategies10, import_time9, import_logger11, import_redis21, import_connectors17, import_cli13, import_strategies11, loadRuntimeStrategies2, loadReferenceConnector, buildPreparedData, buildAfterSignalsContext, buildSymbolSharedReplayKey, runHistoricalSignalsReplay;
31788
+ var import_chalk42, import_progress11, import_backtest4, import_strategies15, import_time9, import_logger11, import_redis21, import_connectors17, import_cli13, import_strategies16, loadRuntimeStrategies2, loadReferenceConnector, buildPreparedData, buildAfterSignalsContext, buildSymbolSharedReplayKey, runHistoricalSignalsReplay;
30782
31789
  var init_historicalSignalsReplay = __esm({
30783
31790
  "src/lib/replay/historicalSignalsReplay.ts"() {
30784
31791
  "use strict";
30785
31792
  import_chalk42 = __toESM(require("chalk"));
30786
31793
  import_progress11 = __toESM(require("progress"));
30787
31794
  import_backtest4 = require("@tradejs/core/backtest");
30788
- import_strategies10 = require("@tradejs/core/strategies");
31795
+ import_strategies15 = require("@tradejs/core/strategies");
30789
31796
  import_time9 = require("@tradejs/core/time");
30790
31797
  import_logger11 = require("@tradejs/infra/logger");
30791
31798
  import_redis21 = require("@tradejs/infra/redis");
30792
31799
  import_connectors17 = require("@tradejs/node/connectors");
30793
31800
  import_cli13 = require("@tradejs/node/cli");
30794
- import_strategies11 = require("@tradejs/node/strategies");
31801
+ import_strategies16 = require("@tradejs/node/strategies");
30795
31802
  init_cliConfig2();
30796
31803
  init_support();
30797
31804
  init_portfolioReplayConnector();
@@ -30801,7 +31808,7 @@ var init_historicalSignalsReplay = __esm({
30801
31808
  loadRuntimeStrategies2 = async (runtimeStrategies) => {
30802
31809
  const strategies = await Promise.all(
30803
31810
  runtimeStrategies.map(async ({ strategyName, strategyConfig }) => {
30804
- const strategyCreator = await (0, import_strategies11.getStrategyCreator)(
31811
+ const strategyCreator = await (0, import_strategies16.getStrategyCreator)(
30805
31812
  strategyName,
30806
31813
  replayProjectRoot
30807
31814
  );
@@ -31147,7 +32154,7 @@ var init_historicalSignalsReplay = __esm({
31147
32154
  );
31148
32155
  if (result && typeof result !== "string") {
31149
32156
  result.runtimeLineage = strategyRuntime.runtimeLineage;
31150
- await (0, import_strategies11.enrichSignalWithBinanceMarketContext)({
32157
+ await (0, import_strategies16.enrichSignalWithBinanceMarketContext)({
31151
32158
  signal: result,
31152
32159
  env: "PARITY"
31153
32160
  });
@@ -31171,8 +32178,8 @@ var init_historicalSignalsReplay = __esm({
31171
32178
  }
31172
32179
  } finally {
31173
32180
  for (const keyPrefix of sharedReplayKeyPrefixes) {
31174
- (0, import_strategies10.releaseStrategyIndicatorsReplayCache)(keyPrefix);
31175
- (0, import_strategies10.releaseStrategyReplayCache)(keyPrefix);
32181
+ (0, import_strategies15.releaseStrategyIndicatorsReplayCache)(keyPrefix);
32182
+ (0, import_strategies15.releaseStrategyReplayCache)(keyPrefix);
31176
32183
  }
31177
32184
  }
31178
32185
  const artifacts = replayConnector.getReplayArtifacts();
@@ -33846,6 +34853,7 @@ var init_replayRunner = __esm({
33846
34853
  cacheOnly: Boolean(replayFlags.cacheOnly),
33847
34854
  aiEnabled: preparedRun.aiEnabled,
33848
34855
  mlEnabled: preparedRun.mlEnabled,
34856
+ strategyNames: preparedRun.strategyNames,
33849
34857
  log: (message) => console.log(import_chalk45.default.gray(message))
33850
34858
  });
33851
34859
  };
@@ -33983,7 +34991,8 @@ var init_replayRunner = __esm({
33983
34991
  ),
33984
34992
  mlEnabled: replayStrategies.some(
33985
34993
  ({ strategyConfig }) => Boolean(strategyConfig.ML_ENABLED)
33986
- )
34994
+ ),
34995
+ strategyNames: replayStrategies.map(({ strategyName }) => strategyName)
33987
34996
  });
33988
34997
  console.log(import_chalk45.default.yellow(`tickers: ${preparedRun.tickers.length}`));
33989
34998
  console.log(
@@ -34013,14 +35022,14 @@ var init_replayRunner = __esm({
34013
35022
  // src/scripts/replay.ts
34014
35023
  var replay_exports = {};
34015
35024
  __export(replay_exports, {
34016
- main: () => main35
35025
+ main: () => main38
34017
35026
  });
34018
- var main35;
35027
+ var main38;
34019
35028
  var init_replay = __esm({
34020
35029
  "src/scripts/replay.ts"() {
34021
35030
  "use strict";
34022
35031
  init_replayRunner();
34023
- main35 = async () => {
35032
+ main38 = async () => {
34024
35033
  await replayBacktest();
34025
35034
  };
34026
35035
  }
@@ -34029,15 +35038,15 @@ var init_replay = __esm({
34029
35038
  // src/scripts/signalsSummary.ts
34030
35039
  var signalsSummary_exports = {};
34031
35040
  __export(signalsSummary_exports, {
34032
- main: () => main36,
35041
+ main: () => main39,
34033
35042
  signalsSummary: () => signalsSummary
34034
35043
  });
34035
- var import_config11, import_args29, import_logger12, import_connectors18, import_tradingAccounts4, flags27, projectRoot13, SUMMARY_TIMEZONE2, SUMMARY_TIMEZONE_LABEL2, escapeHtml5, formatMskDateTime3, formatSigned3, formatPnlMoneyText, formatWinRateText, resolveSummaryTitle, normalizeStatus, shouldAttachDebugReport, getSummaryDateParts, formatSummaryIsoDayKey, unescapeHtml, stripTelegramHtml, buildSignalsSummaryAttachment, appendReportAttachmentSummary, resolveSummaryConnectorName, groupRuntimeTradesByConnectorScope, syncRuntimeTradeScopes, createRuntimeSignalStats, mergeRuntimeSignalStats, buildSummaryPrelude, resolveTradeStatusEmoji, isTimestampInWindow, isRuntimeTradeClosedInWindow, buildSummaryMessages, signalsSummary, main36;
35044
+ var import_config12, import_args31, import_logger12, import_connectors18, import_tradingAccounts4, flags29, projectRoot13, SUMMARY_TIMEZONE2, SUMMARY_TIMEZONE_LABEL2, escapeHtml5, formatMskDateTime3, formatSigned3, formatPnlMoneyText, formatWinRateText, resolveSummaryTitle, normalizeStatus, shouldAttachDebugReport, getSummaryDateParts, formatSummaryIsoDayKey, unescapeHtml, stripTelegramHtml, buildSignalsSummaryAttachment, appendReportAttachmentSummary, resolveSummaryConnectorName, groupRuntimeTradesByConnectorScope, syncRuntimeTradeScopes, createRuntimeSignalStats, mergeRuntimeSignalStats, buildSummaryPrelude, resolveTradeStatusEmoji, isTimestampInWindow, isRuntimeTradeClosedInWindow, buildSummaryMessages, signalsSummary, main39;
34036
35045
  var init_signalsSummary = __esm({
34037
35046
  "src/scripts/signalsSummary.ts"() {
34038
35047
  "use strict";
34039
- import_config11 = require("dotenv/config");
34040
- import_args29 = __toESM(require("args"));
35048
+ import_config12 = require("dotenv/config");
35049
+ import_args31 = __toESM(require("args"));
34041
35050
  import_logger12 = require("@tradejs/infra/logger");
34042
35051
  import_connectors18 = require("@tradejs/node/connectors");
34043
35052
  init_runtimeRedis();
@@ -34047,20 +35056,20 @@ var init_signalsSummary = __esm({
34047
35056
  init_runtimeSignalsStorage();
34048
35057
  init_runtimeDebugEvidence();
34049
35058
  import_tradingAccounts4 = require("@tradejs/infra/tradingAccounts");
34050
- import_args29.default.option(["u", "user"], "Use user config", "root");
34051
- import_args29.default.option(
35059
+ import_args31.default.option(["u", "user"], "Use user config", "root");
35060
+ import_args31.default.option(
34052
35061
  "connector",
34053
35062
  "Connector provider or name for summary (e.g. bybit, binance, coinbase, custom)",
34054
35063
  "bybit"
34055
35064
  );
34056
- import_args29.default.option(["H", "hours"], "Summary window in hours", 24);
34057
- import_args29.default.option(["P", "printOnly"], "Print summary instead of Telegram", false);
34058
- import_args29.default.option(
35065
+ import_args31.default.option(["H", "hours"], "Summary window in hours", 24);
35066
+ import_args31.default.option(["P", "printOnly"], "Print summary instead of Telegram", false);
35067
+ import_args31.default.option(
34059
35068
  "debugAttachment",
34060
35069
  "Attach runtime Redis debug JSON for replay diagnostics",
34061
35070
  true
34062
35071
  );
34063
- flags27 = import_args29.default.parse(process.argv);
35072
+ flags29 = import_args31.default.parse(process.argv);
34064
35073
  projectRoot13 = String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
34065
35074
  SUMMARY_TIMEZONE2 = "Europe/Moscow";
34066
35075
  SUMMARY_TIMEZONE_LABEL2 = "MSK";
@@ -34566,12 +35575,12 @@ none`
34566
35575
  signalsSummary = async () => {
34567
35576
  const hours = Math.max(
34568
35577
  1,
34569
- Number.parseInt(String(flags27.hours ?? 24), 10) || 24
35578
+ Number.parseInt(String(flags29.hours ?? 24), 10) || 24
34570
35579
  );
34571
35580
  const endTime = Date.now();
34572
35581
  const startTime = endTime - hours * 60 * 60 * 1e3;
34573
35582
  const fallbackConnectorName = await resolveSummaryConnectorName(
34574
- flags27.connector
35583
+ flags29.connector
34575
35584
  );
34576
35585
  const [
34577
35586
  configuredStrategyNames,
@@ -34580,25 +35589,25 @@ none`
34580
35589
  trades,
34581
35590
  runtimeDeployments
34582
35591
  ] = await Promise.all([
34583
- loadRuntimeStrategyNames(flags27.user),
34584
- loadRuntimeSignals(flags27.user, { startTime, endTime }),
34585
- loadRuntimeSignalEvaluationStatsBuckets(flags27.user),
34586
- loadRuntimeTrades(flags27.user),
34587
- (0, import_tradingAccounts4.listRuntimeDeployments)(flags27.user)
35592
+ loadRuntimeStrategyNames(flags29.user),
35593
+ loadRuntimeSignals(flags29.user, { startTime, endTime }),
35594
+ loadRuntimeSignalEvaluationStatsBuckets(flags29.user),
35595
+ loadRuntimeTrades(flags29.user),
35596
+ (0, import_tradingAccounts4.listRuntimeDeployments)(flags29.user)
34588
35597
  ]);
34589
35598
  const {
34590
35599
  trades: syncedTrades,
34591
35600
  connectorNames,
34592
35601
  scopesCount
34593
35602
  } = await syncRuntimeTradeScopes({
34594
- userName: flags27.user,
35603
+ userName: flags29.user,
34595
35604
  startTime,
34596
35605
  endTime,
34597
35606
  fallbackConnectorName,
34598
35607
  trades,
34599
35608
  deployments: runtimeDeployments
34600
35609
  });
34601
- const activeTradeOrderIds = await loadRuntimeActiveTradeOrderIds(flags27.user);
35610
+ const activeTradeOrderIds = await loadRuntimeActiveTradeOrderIds(flags29.user);
34602
35611
  const windowSignals = signals2.filter(
34603
35612
  (signal) => signal.timestamp >= startTime && signal.timestamp < endTime
34604
35613
  );
@@ -34646,10 +35655,10 @@ none`
34646
35655
  windowClosedTrades.length,
34647
35656
  connectorNames.join(",") || fallbackConnectorName,
34648
35657
  scopesCount,
34649
- flags27.user
35658
+ flags29.user
34650
35659
  );
34651
- const debugAttachment = shouldAttachDebugReport(flags27.debugAttachment) ? await buildRuntimeDebugReportAttachment({
34652
- userName: flags27.user,
35660
+ const debugAttachment = shouldAttachDebugReport(flags29.debugAttachment) ? await buildRuntimeDebugReportAttachment({
35661
+ userName: flags29.user,
34653
35662
  startTime,
34654
35663
  endTime,
34655
35664
  signals: debugSignals,
@@ -34657,7 +35666,7 @@ none`
34657
35666
  trades: windowTrades
34658
35667
  }) : null;
34659
35668
  const signalsAttachment = buildSignalsSummaryAttachment({
34660
- userName: flags27.user,
35669
+ userName: flags29.user,
34661
35670
  endTime,
34662
35671
  content: signalsMessage
34663
35672
  });
@@ -34673,7 +35682,7 @@ none`
34673
35682
  signalsAttachment,
34674
35683
  ...debugAttachment ? [debugAttachment] : []
34675
35684
  ];
34676
- if (flags27.printOnly) {
35685
+ if (flags29.printOnly) {
34677
35686
  console.log(tradesMessageWithAttachmentSummary);
34678
35687
  console.log("");
34679
35688
  console.log(`Signals attachment: ${signalsAttachment.filename}`);
@@ -34686,27 +35695,27 @@ none`
34686
35695
  return;
34687
35696
  }
34688
35697
  await sendTelegramReport(tradesMessageWithAttachmentSummary, {
34689
- userName: flags27.user,
35698
+ userName: flags29.user,
34690
35699
  attachments
34691
35700
  });
34692
35701
  };
34693
- main36 = signalsSummary;
35702
+ main39 = signalsSummary;
34694
35703
  }
34695
35704
  });
34696
35705
 
34697
35706
  // src/scripts/test-ml.ts
34698
35707
  var test_ml_exports = {};
34699
35708
  __export(test_ml_exports, {
34700
- main: () => main37
35709
+ main: () => main40
34701
35710
  });
34702
- var import_config12, import_constants20, import_ml3, import_strategies12, now, INTERVAL_MIN, CANDLES, makeSeries, makeCandles, buildTrendline, main37;
35711
+ var import_config13, import_constants20, import_ml3, import_strategies17, now, INTERVAL_MIN, CANDLES, makeSeries, makeCandles, buildTrendline, main40;
34703
35712
  var init_test_ml = __esm({
34704
35713
  "src/scripts/test-ml.ts"() {
34705
35714
  "use strict";
34706
- import_config12 = require("dotenv/config");
35715
+ import_config13 = require("dotenv/config");
34707
35716
  import_constants20 = require("@tradejs/core/constants");
34708
35717
  import_ml3 = require("@tradejs/infra/ml");
34709
- import_strategies12 = require("@tradejs/strategies");
35718
+ import_strategies17 = require("@tradejs/strategies");
34710
35719
  now = Date.now();
34711
35720
  INTERVAL_MIN = 15;
34712
35721
  CANDLES = import_constants20.ML_BASE_CANDLES_WINDOW;
@@ -34749,7 +35758,7 @@ var init_test_ml = __esm({
34749
35758
  { timestamp: entryTs - 2 * INTERVAL_MIN * 6e4, value: price * 0.995 }
34750
35759
  ]
34751
35760
  });
34752
- main37 = async () => {
35761
+ main40 = async () => {
34753
35762
  const startTs = now - CANDLES * INTERVAL_MIN * 6e4;
34754
35763
  const candles = makeCandles(120, startTs);
34755
35764
  const btcCandles = makeCandles(42e3, startTs);
@@ -34800,7 +35809,7 @@ var init_test_ml = __esm({
34800
35809
  volume24h: makeSeries(10, 8e3, 200)
34801
35810
  }
34802
35811
  };
34803
- const { TRENDLINE, HIGHS, LOWS, ML_THRESHOLD } = import_strategies12.trendLineDefaultConfig;
35812
+ const { TRENDLINE, HIGHS, LOWS, ML_THRESHOLD } = import_strategies17.trendLineDefaultConfig;
34804
35813
  const fullRow = (0, import_ml3.buildMlTrainingRow)(
34805
35814
  {
34806
35815
  signal,
@@ -34836,19 +35845,19 @@ var init_test_ml = __esm({
34836
35845
  // src/scripts/test.ts
34837
35846
  var test_exports = {};
34838
35847
  __export(test_exports, {
34839
- main: () => main38
35848
+ main: () => main41
34840
35849
  });
34841
- var import_config13, import_connectors19, getPositions, main38;
35850
+ var import_config14, import_connectors19, getPositions, main41;
34842
35851
  var init_test = __esm({
34843
35852
  "src/scripts/test.ts"() {
34844
35853
  "use strict";
34845
- import_config13 = require("dotenv/config");
35854
+ import_config14 = require("dotenv/config");
34846
35855
  import_connectors19 = require("@tradejs/connectors");
34847
35856
  getPositions = async (connector) => {
34848
35857
  const res = await connector.getPositions();
34849
35858
  console.log("res", res);
34850
35859
  };
34851
- main38 = async () => {
35860
+ main41 = async () => {
34852
35861
  const byBitConnector = await import_connectors19.connectors.ByBit({
34853
35862
  userName: "root"
34854
35863
  });
@@ -34860,23 +35869,23 @@ var init_test = __esm({
34860
35869
  // src/scripts/user-add.ts
34861
35870
  var user_add_exports = {};
34862
35871
  __export(user_add_exports, {
34863
- main: () => main39
35872
+ main: () => main42
34864
35873
  });
34865
- var import_args30, import_chalk46, import_bcryptjs, import_redis24, flags28, main39;
35874
+ var import_args32, import_chalk46, import_bcryptjs, import_redis24, flags30, main42;
34866
35875
  var init_user_add = __esm({
34867
35876
  "src/scripts/user-add.ts"() {
34868
35877
  "use strict";
34869
- import_args30 = __toESM(require("args"));
35878
+ import_args32 = __toESM(require("args"));
34870
35879
  import_chalk46 = __toESM(require("chalk"));
34871
35880
  import_bcryptjs = __toESM(require("bcryptjs"));
34872
35881
  import_redis24 = require("@tradejs/infra/redis");
34873
- import_args30.default.example("yarn user-add -u myname -p 123456", "Create or update user");
34874
- import_args30.default.option(["u", "user"], "Username", "");
34875
- import_args30.default.option(["p", "password"], "Password (plain text)", "");
34876
- flags28 = import_args30.default.parse(process.argv);
34877
- main39 = async () => {
34878
- const userName2 = String(flags28.user || "").trim();
34879
- const password = String(flags28.password || "");
35882
+ import_args32.default.example("yarn user-add -u myname -p 123456", "Create or update user");
35883
+ import_args32.default.option(["u", "user"], "Username", "");
35884
+ import_args32.default.option(["p", "password"], "Password (plain text)", "");
35885
+ flags30 = import_args32.default.parse(process.argv);
35886
+ main42 = async () => {
35887
+ const userName2 = String(flags30.user || "").trim();
35888
+ const password = String(flags30.password || "");
34880
35889
  if (!userName2 || !password) {
34881
35890
  console.error(import_chalk46.default.red("Missing -U <user> or -P <password>"));
34882
35891
  process.exit(1);
@@ -34902,14 +35911,14 @@ var init_user_add = __esm({
34902
35911
  var cli_exports = {};
34903
35912
  __export(cli_exports, {
34904
35913
  cleanupCliResources: () => cleanupCliResources,
34905
- main: () => main40,
35914
+ main: () => main43,
34906
35915
  printUsage: () => printUsage,
34907
35916
  runLoadedModule: () => runLoadedModule
34908
35917
  });
34909
35918
  module.exports = __toCommonJS(cli_exports);
34910
- var import_config14 = require("dotenv/config");
35919
+ var import_config15 = require("dotenv/config");
34911
35920
  var import_redis25 = require("@tradejs/infra/redis");
34912
- var import_timescale11 = require("@tradejs/infra/timescale");
35921
+ var import_timescale13 = require("@tradejs/infra/timescale");
34913
35922
  var scriptLoaders = {
34914
35923
  "agent-run": () => Promise.resolve().then(() => (init_agentRun(), agentRun_exports)),
34915
35924
  "ai-export": () => Promise.resolve().then(() => (init_aiExport2(), aiExport_exports)),
@@ -34934,6 +35943,9 @@ var scriptLoaders = {
34934
35943
  "maintenance:cleanup-market-context": () => Promise.resolve().then(() => (init_cleanupMarketContext(), cleanupMarketContext_exports)),
34935
35944
  migration: () => Promise.resolve().then(() => (init_migration(), migration_exports)),
34936
35945
  "market-ws": () => Promise.resolve().then(() => (init_marketWs(), marketWs_exports)),
35946
+ "hyperliquid:whale-backfill": () => Promise.resolve().then(() => (init_hyperliquidWhaleBackfill2(), hyperliquidWhaleBackfill_exports)),
35947
+ "hyperliquid:whale-ingest": () => Promise.resolve().then(() => (init_hyperliquidWhaleIngest(), hyperliquidWhaleIngest_exports)),
35948
+ "hyperliquid:whales:update": () => Promise.resolve().then(() => (init_hyperliquidWhalesUpdate(), hyperliquidWhalesUpdate_exports)),
34937
35949
  "ml-export": () => Promise.resolve().then(() => (init_mlExport(), mlExport_exports)),
34938
35950
  "ml-inspect": () => Promise.resolve().then(() => (init_mlInspect(), mlInspect_exports)),
34939
35951
  "ml-train:latest": () => Promise.resolve().then(() => (init_mlTrainLatestSelect(), mlTrainLatestSelect_exports)),
@@ -34965,16 +35977,16 @@ var runLoadedModule = async (loaded) => {
34965
35977
  }
34966
35978
  };
34967
35979
  var cleanupCliResources = async () => {
34968
- await Promise.allSettled([(0, import_redis25.closeRedisConnection)(), (0, import_timescale11.closeTimescalePool)()]);
35980
+ await Promise.allSettled([(0, import_redis25.closeRedisConnection)(), (0, import_timescale13.closeTimescalePool)()]);
34969
35981
  };
34970
- var main40 = async () => {
34971
- const [, scriptPath, command = "", ...args31] = process.argv;
35982
+ var main43 = async () => {
35983
+ const [, scriptPath, command = "", ...args33] = process.argv;
34972
35984
  const loader = scriptLoaders[command];
34973
35985
  if (!loader) {
34974
35986
  printUsage();
34975
35987
  process.exit(command ? 1 : 0);
34976
35988
  }
34977
- const nextArgs = [...args31];
35989
+ const nextArgs = [...args33];
34978
35990
  if (command === "spread:ingest") {
34979
35991
  nextArgs.push("--provider", "binance_coinbase_spread");
34980
35992
  }
@@ -34987,7 +35999,7 @@ var main40 = async () => {
34987
35999
  }
34988
36000
  };
34989
36001
  if (require.main === module) {
34990
- main40().catch((error) => {
36002
+ main43().catch((error) => {
34991
36003
  console.error(error);
34992
36004
  process.exit(1);
34993
36005
  });