@tradejs/cli 2.0.16 → 2.0.18

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
@@ -3678,8 +3678,8 @@ var init_aiPocketSearch = __esm({
3678
3678
  "volumeusd",
3679
3679
  "vwap"
3680
3680
  ]);
3681
- classifyAiPocketFeaturePath = (path24) => {
3682
- const segments = (Array.isArray(path24) ? path24 : path24.split(".")).filter(
3681
+ classifyAiPocketFeaturePath = (path25) => {
3682
+ const segments = (Array.isArray(path25) ? path25 : path25.split(".")).filter(
3683
3683
  Boolean
3684
3684
  );
3685
3685
  const normalized = segments.map((segment) => segment.toLowerCase());
@@ -10868,27 +10868,27 @@ var init_coinMarketCapContextBackfill = __esm({
10868
10868
  totalCredits: 0,
10869
10869
  byPath: /* @__PURE__ */ new Map()
10870
10870
  });
10871
- getCoinMarketCapPathLabel = (path24) => {
10872
- if (path24.includes("global-metrics")) return "global";
10873
- if (path24.includes("cryptocurrency/quotes")) return "reference";
10874
- if (path24.includes("exchange/quotes")) return "exchangeLiquidity";
10875
- if (path24.includes("fear-and-greed")) return "fearGreed";
10876
- if (path24.includes("cmc100")) return "cmc100";
10877
- if (path24.includes("cmc20")) return "cmc20";
10878
- return path24;
10879
- };
10880
- recordCoinMarketCapFetchStats = (stats, path24, creditCount) => {
10871
+ getCoinMarketCapPathLabel = (path25) => {
10872
+ if (path25.includes("global-metrics")) return "global";
10873
+ if (path25.includes("cryptocurrency/quotes")) return "reference";
10874
+ if (path25.includes("exchange/quotes")) return "exchangeLiquidity";
10875
+ if (path25.includes("fear-and-greed")) return "fearGreed";
10876
+ if (path25.includes("cmc100")) return "cmc100";
10877
+ if (path25.includes("cmc20")) return "cmc20";
10878
+ return path25;
10879
+ };
10880
+ recordCoinMarketCapFetchStats = (stats, path25, creditCount) => {
10881
10881
  if (!stats) return;
10882
- const existing = stats.byPath.get(path24) ?? { requests: 0, credits: 0 };
10882
+ const existing = stats.byPath.get(path25) ?? { requests: 0, credits: 0 };
10883
10883
  existing.requests += 1;
10884
10884
  existing.credits += creditCount ?? 0;
10885
10885
  stats.requests += 1;
10886
10886
  stats.totalCredits += creditCount ?? 0;
10887
- stats.byPath.set(path24, existing);
10887
+ stats.byPath.set(path25, existing);
10888
10888
  };
10889
10889
  formatCoinMarketCapFetchStats = (stats) => {
10890
10890
  const byPath = [...stats.byPath.entries()].map(
10891
- ([path24, item]) => `${getCoinMarketCapPathLabel(path24)}=${item.requests}req/${item.credits}cr`
10891
+ ([path25, item]) => `${getCoinMarketCapPathLabel(path25)}=${item.requests}req/${item.credits}cr`
10892
10892
  ).join(" ");
10893
10893
  return `apiRequests=${stats.requests} apiCredits=${stats.totalCredits}${byPath ? ` ${byPath}` : ""}`;
10894
10894
  };
@@ -11865,10 +11865,11 @@ var init_hyperliquidRateLimiter = __esm({
11865
11865
  });
11866
11866
 
11867
11867
  // src/lib/hyperliquidWhaleBackfill.ts
11868
- 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;
11868
+ var import_progress6, 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;
11869
11869
  var init_hyperliquidWhaleBackfill = __esm({
11870
11870
  "src/lib/hyperliquidWhaleBackfill.ts"() {
11871
11871
  "use strict";
11872
+ import_progress6 = __toESM(require("progress"));
11872
11873
  import_timescale4 = require("@tradejs/infra/timescale");
11873
11874
  import_strategies4 = require("@tradejs/node/strategies");
11874
11875
  init_hyperliquidWhaleData();
@@ -12094,11 +12095,36 @@ var init_hyperliquidWhaleBackfill = __esm({
12094
12095
  toMs,
12095
12096
  ...identity
12096
12097
  });
12098
+ const totalCoverageBuckets = Math.ceil(
12099
+ (toMs - fromMs) / HYPERLIQUID_WHALE_BUCKET_MS
12100
+ );
12101
+ const coverageBar = new import_progress6.default(
12102
+ "Hyperliquid coverage :current/:total [:bar][:percent] :eta(s) rows=:rows chunk=:chunk",
12103
+ {
12104
+ total: Math.max(1, totalCoverageBuckets),
12105
+ width: 24
12106
+ }
12107
+ );
12108
+ let displayedCoverageBuckets = 0;
12097
12109
  const coverageBuckets = await (0, import_timescale4.rebuildHyperliquidWhaleCoverageRows)({
12098
12110
  fromMs,
12099
12111
  toMs,
12100
12112
  expectedWhales: whales.addresses.length,
12101
- ...identity
12113
+ ...identity,
12114
+ onProgress: (progress) => {
12115
+ const next = Math.max(
12116
+ displayedCoverageBuckets,
12117
+ Math.min(progress.completedBuckets, coverageBar.total)
12118
+ );
12119
+ const delta = next - displayedCoverageBuckets;
12120
+ if (delta > 0) {
12121
+ coverageBar.tick(delta, {
12122
+ rows: progress.rows,
12123
+ chunk: `${progress.chunkIndex}/${progress.totalChunks}`
12124
+ });
12125
+ displayedCoverageBuckets = next;
12126
+ }
12127
+ }
12102
12128
  });
12103
12129
  const result = {
12104
12130
  cached: false,
@@ -13490,14 +13516,31 @@ var init_runState = __esm({
13490
13516
  });
13491
13517
 
13492
13518
  // src/lib/backtest/workerPool.ts
13493
- var import_progress6, import_child_process, import_chalk13, import_node_crypto3, executeBacktestWorkerPool;
13519
+ var import_progress7, import_child_process, import_chalk13, import_node_crypto3, DEFAULT_PG_POOL_MAX, FAST_AI_PG_CONNECTION_BUDGET, MIN_FAST_AI_WORKER_PG_POOL_MAX, resolveFastAiWorkerPgPoolMax, executeBacktestWorkerPool;
13494
13520
  var init_workerPool = __esm({
13495
13521
  "src/lib/backtest/workerPool.ts"() {
13496
13522
  "use strict";
13497
- import_progress6 = __toESM(require("progress"));
13523
+ import_progress7 = __toESM(require("progress"));
13498
13524
  import_child_process = require("child_process");
13499
13525
  import_chalk13 = __toESM(require("chalk"));
13500
13526
  import_node_crypto3 = require("crypto");
13527
+ DEFAULT_PG_POOL_MAX = 10;
13528
+ FAST_AI_PG_CONNECTION_BUDGET = 18;
13529
+ MIN_FAST_AI_WORKER_PG_POOL_MAX = 3;
13530
+ resolveFastAiWorkerPgPoolMax = (testSuite, workerCount, configuredPoolMax) => {
13531
+ if (configuredPoolMax?.trim() || workerCount <= 1 || testSuite.length === 0 || testSuite.some((test) => !test.fast || !test.ai)) {
13532
+ return void 0;
13533
+ }
13534
+ return String(
13535
+ Math.min(
13536
+ DEFAULT_PG_POOL_MAX,
13537
+ Math.max(
13538
+ MIN_FAST_AI_WORKER_PG_POOL_MAX,
13539
+ Math.floor(FAST_AI_PG_CONNECTION_BUDGET / workerCount)
13540
+ )
13541
+ )
13542
+ );
13543
+ };
13501
13544
  executeBacktestWorkerPool = async ({
13502
13545
  testSuite,
13503
13546
  userName: userName2,
@@ -13516,6 +13559,12 @@ var init_workerPool = __esm({
13516
13559
  totalTests
13517
13560
  }) => {
13518
13561
  const chunks = chunkTestSuite(testSuite);
13562
+ const fastAiWorkerPgPoolMax = resolveFastAiWorkerPgPoolMax(
13563
+ testSuite,
13564
+ chunks.length,
13565
+ process.env.PG_POOL_MAX
13566
+ );
13567
+ const workerEnv = fastAiWorkerPgPoolMax ? { ...process.env, PG_POOL_MAX: fastAiWorkerPgPoolMax } : void 0;
13519
13568
  const totalExpectedTests = Math.max(
13520
13569
  testSuite.length,
13521
13570
  totalTests ?? testSuite.length,
@@ -13573,7 +13622,7 @@ var init_workerPool = __esm({
13573
13622
  console.log(line);
13574
13623
  }
13575
13624
  console.log("");
13576
- const bar = new import_progress6.default(
13625
+ const bar = new import_progress7.default(
13577
13626
  ":current/:total [:bar][:percent] avg :amount win :winRate trades :trades :eta(s)",
13578
13627
  {
13579
13628
  total: totalExpectedTests,
@@ -13601,6 +13650,7 @@ var init_workerPool = __esm({
13601
13650
  const chunkId = runId ? `${runId}-${attemptId}` : attemptId;
13602
13651
  const chunkWithId = chunk.map((test) => ({ ...test, chunkId }));
13603
13652
  const tester = (0, import_child_process.fork)(testerWorkerPath, [], {
13653
+ ...workerEnv ? { env: workerEnv } : {},
13604
13654
  execArgv: testerNeedsTsRuntime ? [
13605
13655
  `--max-old-space-size=${workerHeapMb2}`,
13606
13656
  "-r",
@@ -16728,15 +16778,15 @@ var require_pg_connection_string = __commonJS({
16728
16778
  if (config.sslnegotiation === "direct" && config.ssl === void 0) {
16729
16779
  config.ssl = true;
16730
16780
  }
16731
- const fs23 = config.sslcert || config.sslkey || config.sslrootcert ? require("fs") : null;
16781
+ const fs24 = config.sslcert || config.sslkey || config.sslrootcert ? require("fs") : null;
16732
16782
  if (config.sslcert) {
16733
- config.ssl.cert = fs23.readFileSync(config.sslcert).toString();
16783
+ config.ssl.cert = fs24.readFileSync(config.sslcert).toString();
16734
16784
  }
16735
16785
  if (config.sslkey) {
16736
- config.ssl.key = fs23.readFileSync(config.sslkey).toString();
16786
+ config.ssl.key = fs24.readFileSync(config.sslkey).toString();
16737
16787
  }
16738
16788
  if (config.sslrootcert) {
16739
- config.ssl.ca = fs23.readFileSync(config.sslrootcert).toString();
16789
+ config.ssl.ca = fs24.readFileSync(config.sslrootcert).toString();
16740
16790
  }
16741
16791
  if (options.useLibpqCompat && config.uselibpqcompat) {
16742
16792
  throw new Error("Both useLibpqCompat and uselibpqcompat are set. Please use only one of them.");
@@ -18556,7 +18606,7 @@ var require_split2 = __commonJS({
18556
18606
  var require_helper = __commonJS({
18557
18607
  "../../node_modules/pgpass/lib/helper.js"(exports2, module2) {
18558
18608
  "use strict";
18559
- var path24 = require("path");
18609
+ var path25 = require("path");
18560
18610
  var Stream = require("stream").Stream;
18561
18611
  var split = require_split2();
18562
18612
  var util = require("util");
@@ -18595,7 +18645,7 @@ var require_helper = __commonJS({
18595
18645
  };
18596
18646
  module2.exports.getFileName = function(rawEnv) {
18597
18647
  var env = rawEnv || process.env;
18598
- var file = env.PGPASSFILE || (isWin ? path24.join(env.APPDATA || "./", "postgresql", "pgpass.conf") : path24.join(env.HOME || "./", ".pgpass"));
18648
+ var file = env.PGPASSFILE || (isWin ? path25.join(env.APPDATA || "./", "postgresql", "pgpass.conf") : path25.join(env.HOME || "./", ".pgpass"));
18599
18649
  return file;
18600
18650
  };
18601
18651
  module2.exports.usePgPass = function(stats, fname) {
@@ -18727,16 +18777,16 @@ var require_helper = __commonJS({
18727
18777
  var require_lib = __commonJS({
18728
18778
  "../../node_modules/pgpass/lib/index.js"(exports2, module2) {
18729
18779
  "use strict";
18730
- var path24 = require("path");
18731
- var fs23 = require("fs");
18780
+ var path25 = require("path");
18781
+ var fs24 = require("fs");
18732
18782
  var helper = require_helper();
18733
18783
  module2.exports = function(connInfo, cb) {
18734
18784
  var file = helper.getFileName();
18735
- fs23.stat(file, function(err, stat) {
18785
+ fs24.stat(file, function(err, stat) {
18736
18786
  if (err || !helper.usePgPass(stat, file)) {
18737
18787
  return cb(void 0);
18738
18788
  }
18739
- var st = fs23.createReadStream(file);
18789
+ var st = fs24.createReadStream(file);
18740
18790
  helper.getPassword(connInfo, st, cb);
18741
18791
  });
18742
18792
  };
@@ -20833,12 +20883,12 @@ var continuity_exports = {};
20833
20883
  __export(continuity_exports, {
20834
20884
  main: () => main13
20835
20885
  });
20836
- var import_args12, import_progress7, import_chalk20, import_connectors10, import_cli8, import_constants11, import_time5, import_logger2, import_timescale7, flags12, interval2, intervalKey, projectRoot5, parseProviders, runContinuity, main13;
20886
+ var import_args12, import_progress8, import_chalk20, import_connectors10, import_cli8, import_constants11, import_time5, import_logger2, import_timescale7, flags12, interval2, intervalKey, projectRoot5, parseProviders, runContinuity, main13;
20837
20887
  var init_continuity2 = __esm({
20838
20888
  "src/scripts/continuity.ts"() {
20839
20889
  "use strict";
20840
20890
  import_args12 = __toESM(require("args"));
20841
- import_progress7 = __toESM(require("progress"));
20891
+ import_progress8 = __toESM(require("progress"));
20842
20892
  import_chalk20 = __toESM(require("chalk"));
20843
20893
  import_connectors10 = require("@tradejs/node/connectors");
20844
20894
  import_cli8 = require("@tradejs/node/cli");
@@ -20895,7 +20945,7 @@ var init_continuity2 = __esm({
20895
20945
  void 0,
20896
20946
  { universe }
20897
20947
  );
20898
- const bar = new import_progress7.default(
20948
+ const bar = new import_progress8.default(
20899
20949
  ":current/:total [:bar][:percent] broken::broken fixed::fixed :eta(s) :symbol",
20900
20950
  {
20901
20951
  total: tickers.length,
@@ -21149,14 +21199,14 @@ var derivativesIngestCoinalyzeAll_exports = {};
21149
21199
  __export(derivativesIngestCoinalyzeAll_exports, {
21150
21200
  main: () => main15
21151
21201
  });
21152
- var import_config5, import_args14, import_chalk22, import_progress8, import_lodash2, import_connectors12, import_indicators3, import_async4, import_userSettings5, import_cli9, import_timescale9, coinalyzeIntervalMap2, flags14, asInt6, parseList2, coinalyzeBaseUrl, coinalyzeMaxRetries, lastRequestTs3, fetchJsonWithRateLimit2, fetchCoinalyzeMarkets2, selectBestMarket2, buildMatches2, normalizeMetricPoint2, toSeriesMap2, fetchMetricBatch2, main15;
21202
+ var import_config5, import_args14, import_chalk22, import_progress9, import_lodash2, import_connectors12, import_indicators3, import_async4, import_userSettings5, import_cli9, import_timescale9, coinalyzeIntervalMap2, flags14, asInt6, parseList2, coinalyzeBaseUrl, coinalyzeMaxRetries, lastRequestTs3, fetchJsonWithRateLimit2, fetchCoinalyzeMarkets2, selectBestMarket2, buildMatches2, normalizeMetricPoint2, toSeriesMap2, fetchMetricBatch2, main15;
21153
21203
  var init_derivativesIngestCoinalyzeAll = __esm({
21154
21204
  "src/scripts/derivativesIngestCoinalyzeAll.ts"() {
21155
21205
  "use strict";
21156
21206
  import_config5 = require("dotenv/config");
21157
21207
  import_args14 = __toESM(require("args"));
21158
21208
  import_chalk22 = __toESM(require("chalk"));
21159
- import_progress8 = __toESM(require("progress"));
21209
+ import_progress9 = __toESM(require("progress"));
21160
21210
  import_lodash2 = __toESM(require("lodash"));
21161
21211
  import_connectors12 = require("@tradejs/connectors");
21162
21212
  import_indicators3 = require("@tradejs/core/indicators");
@@ -21433,7 +21483,7 @@ var init_derivativesIngestCoinalyzeAll = __esm({
21433
21483
  const symbolBatches = import_lodash2.default.chunk(matches, symbolBatchSize);
21434
21484
  const windowsPerPair = Math.ceil(days / batchDays);
21435
21485
  const totalWindows = symbolBatches.length * intervals.length * windowsPerPair;
21436
- const bar = new import_progress8.default(
21486
+ const bar = new import_progress9.default(
21437
21487
  ":current/:total [:bar][:percent] :eta(s) :batch rows=:rows fail=:fail",
21438
21488
  {
21439
21489
  total: Math.max(1, totalWindows),
@@ -22731,11 +22781,11 @@ var migration_exports = {};
22731
22781
  __export(migration_exports, {
22732
22782
  main: () => main22
22733
22783
  });
22734
- var import_progress9, import_chalk30, import_logger3, import_files, import_timescale11, DIR, re, BATCH, projectRoot7, migrateFile, main22;
22784
+ var import_progress10, import_chalk30, import_logger3, import_files, import_timescale11, DIR, re, BATCH, projectRoot7, migrateFile, main22;
22735
22785
  var init_migration = __esm({
22736
22786
  "src/scripts/migration.ts"() {
22737
22787
  "use strict";
22738
- import_progress9 = __toESM(require("progress"));
22788
+ import_progress10 = __toESM(require("progress"));
22739
22789
  import_chalk30 = __toESM(require("chalk"));
22740
22790
  import_logger3 = require("@tradejs/infra/logger");
22741
22791
  import_files = require("@tradejs/infra/files");
@@ -22762,7 +22812,7 @@ var init_migration = __esm({
22762
22812
  };
22763
22813
  main22 = async () => {
22764
22814
  const files = await (0, import_files.getFiles)(DIR, projectRoot7);
22765
- const bar = new import_progress9.default(
22815
+ const bar = new import_progress10.default(
22766
22816
  ":current/:total [:bar][:percent] :eta(s) :file",
22767
22817
  {
22768
22818
  total: files.length,
@@ -27365,7 +27415,7 @@ var init_replayRuntimeEvidence = __esm({
27365
27415
  });
27366
27416
 
27367
27417
  // src/lib/runtimeEvidenceArtifacts.ts
27368
- var import_node_crypto5, import_promises15, import_node_path8, RUNTIME_EVIDENCE_MANIFEST_FILE, RUNTIME_EVIDENCE_PAYLOAD_FILE, RUNTIME_EVIDENCE_COMPLETE_FILE, asRecord4, sha256, isExistingDestinationError, safeSegment, compactTimestamp, dateParts, assertFiniteTimestamp, parseManifest, publishRuntimeEvidenceBundle, verifyRuntimeEvidenceBundle, discoverRuntimeEvidenceBundles;
27418
+ var import_node_crypto5, import_promises15, import_node_path8, RUNTIME_EVIDENCE_MANIFEST_FILE, RUNTIME_EVIDENCE_PAYLOAD_FILE, RUNTIME_EVIDENCE_COMPLETE_FILE, asRecord4, sha256, runtimeEvidenceIdentitySha256, isExistingDestinationError, safeSegment, compactTimestamp, dateParts, assertFiniteTimestamp, parseManifest, publishRuntimeEvidenceBundle, verifyRuntimeEvidenceBundle, discoverRuntimeEvidenceBundles;
27369
27419
  var init_runtimeEvidenceArtifacts = __esm({
27370
27420
  "src/lib/runtimeEvidenceArtifacts.ts"() {
27371
27421
  "use strict";
@@ -27377,6 +27427,17 @@ var init_runtimeEvidenceArtifacts = __esm({
27377
27427
  RUNTIME_EVIDENCE_COMPLETE_FILE = ".complete";
27378
27428
  asRecord4 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
27379
27429
  sha256 = (value) => (0, import_node_crypto5.createHash)("sha256").update(value).digest("hex");
27430
+ runtimeEvidenceIdentitySha256 = (artifact) => {
27431
+ const identityArtifact = { ...artifact };
27432
+ delete identityArtifact.generatedAt;
27433
+ const runtime = asRecord4(identityArtifact.runtime);
27434
+ if (runtime) {
27435
+ const identityRuntime = { ...runtime };
27436
+ delete identityRuntime.generatedAt;
27437
+ identityArtifact.runtime = identityRuntime;
27438
+ }
27439
+ return sha256(JSON.stringify(identityArtifact));
27440
+ };
27380
27441
  isExistingDestinationError = (error) => {
27381
27442
  const code = error.code;
27382
27443
  return code === "EEXIST" || code === "ENOTEMPTY";
@@ -27447,11 +27508,12 @@ var init_runtimeEvidenceArtifacts = __esm({
27447
27508
  const payload = `${JSON.stringify(artifact, null, 2)}
27448
27509
  `;
27449
27510
  const payloadSha256 = sha256(payload);
27511
+ const identitySha256 = runtimeEvidenceIdentitySha256(artifact);
27450
27512
  const safeDeploymentId = safeSegment(deploymentId, "default");
27451
27513
  const artifactId = [
27452
27514
  compactTimestamp(startTime),
27453
27515
  compactTimestamp(endTime),
27454
- payloadSha256.slice(0, 16)
27516
+ identitySha256.slice(0, 16)
27455
27517
  ].join("_");
27456
27518
  const createdAt = Date.now();
27457
27519
  const manifest = {
@@ -28858,21 +28920,21 @@ var init_serverHealth = __esm({
28858
28920
  }
28859
28921
  return `${minutes}m`;
28860
28922
  };
28861
- readDiskUsage = (path24) => {
28923
+ readDiskUsage = (path25) => {
28862
28924
  try {
28863
- const stats = (0, import_node_fs2.statfsSync)(path24);
28925
+ const stats = (0, import_node_fs2.statfsSync)(path25);
28864
28926
  const totalBlocks = Number(stats.blocks ?? 0);
28865
28927
  const availableBlocks = Number(stats.bavail ?? 0);
28866
28928
  if (totalBlocks <= 0) {
28867
- return { path: path24, usedPct: null };
28929
+ return { path: path25, usedPct: null };
28868
28930
  }
28869
28931
  const usedPct = (totalBlocks - Math.max(0, availableBlocks)) / totalBlocks * 100;
28870
28932
  return {
28871
- path: path24,
28933
+ path: path25,
28872
28934
  usedPct: Number.isFinite(usedPct) ? usedPct : null
28873
28935
  };
28874
28936
  } catch {
28875
- return { path: path24, usedPct: null };
28937
+ return { path: path25, usedPct: null };
28876
28938
  }
28877
28939
  };
28878
28940
  parseMeminfoBytes = (value) => {
@@ -32232,13 +32294,13 @@ __export(signals_exports, {
32232
32294
  signalsConfiguredScopesOnce: () => signalsConfiguredScopesOnce,
32233
32295
  signalsDaemon: () => signalsDaemon
32234
32296
  });
32235
- var import_config11, import_args31, import_progress10, import_chalk43, import_connectors16, import_cli12, import_async5, import_strategies13, import_constants16, import_strategies14, import_time8, import_logger10, import_tradingAccounts3, SLOW_SIGNALS_WARNING_MS, projectRoot14, flags30, interval4, intervalMs, formatDuration3, resolvePositiveInteger2, resolveNonNegativeInteger, resolveSignalsDaemonMaxLiveBars, isSignalsKlineWsEnabled, resolveSignalsKlineWsWaitMs, createDefaultSignalsLifecycle, timeOperation3, formatSlowSignalsWarning, resolveSignalsConnectorName, createSignalsSession, findSignals, signals, loadConfiguredSignalsScopes, hasExplicitSignalsScope, signalsConfiguredScopesOnce, signalsDaemon, main38;
32297
+ var import_config11, import_args31, import_progress11, import_chalk43, import_connectors16, import_cli12, import_async5, import_strategies13, import_constants16, import_strategies14, import_time8, import_logger10, import_tradingAccounts3, SLOW_SIGNALS_WARNING_MS, projectRoot14, flags30, interval4, intervalMs, formatDuration3, resolvePositiveInteger2, resolveNonNegativeInteger, resolveSignalsDaemonMaxLiveBars, isSignalsKlineWsEnabled, resolveSignalsKlineWsWaitMs, createDefaultSignalsLifecycle, timeOperation3, formatSlowSignalsWarning, resolveSignalsConnectorName, createSignalsSession, findSignals, signals, loadConfiguredSignalsScopes, hasExplicitSignalsScope, signalsConfiguredScopesOnce, signalsDaemon, main38;
32236
32298
  var init_signals = __esm({
32237
32299
  "src/scripts/signals.ts"() {
32238
32300
  "use strict";
32239
32301
  import_config11 = require("dotenv/config");
32240
32302
  import_args31 = __toESM(require("args"));
32241
- import_progress10 = __toESM(require("progress"));
32303
+ import_progress11 = __toESM(require("progress"));
32242
32304
  import_chalk43 = __toESM(require("chalk"));
32243
32305
  import_connectors16 = require("@tradejs/node/connectors");
32244
32306
  import_cli12 = require("@tradejs/node/cli");
@@ -32885,7 +32947,7 @@ var init_signals = __esm({
32885
32947
  return;
32886
32948
  }
32887
32949
  const strategyStats = createStrategySkipStats(runtimeStrategies);
32888
- const bar = new import_progress10.default(
32950
+ const bar = new import_progress11.default(
32889
32951
  ":current/:total [:bar][:percent] :found :eta(s) :symbol",
32890
32952
  {
32891
32953
  total: tickers.length,
@@ -33223,7 +33285,7 @@ var init_signalsDaemon = __esm({
33223
33285
  });
33224
33286
 
33225
33287
  // src/lib/replay/cliConfig.ts
33226
- var import_args32, REPLAY_UNSUPPORTED_FLAGS, hasCliFlag2, assertNoUnsupportedReplayFlags, replayNormalizedArgv, replayFlags, replayInterval, replayUserName, replayProjectRoot, isReplayUpdateOnlyRun;
33288
+ var import_args32, REPLAY_UNSUPPORTED_FLAGS, hasCliFlag2, assertNoUnsupportedReplayFlags, replayNormalizedArgv, replayFlags, replayInterval, replayUserName, replayProjectRoot, replayRuntimeEvidencePath, isReplayUpdateOnlyRun;
33227
33289
  var init_cliConfig2 = __esm({
33228
33290
  "src/lib/replay/cliConfig.ts"() {
33229
33291
  "use strict";
@@ -33272,6 +33334,10 @@ var init_cliConfig2 = __esm({
33272
33334
  import_args32.default.option(["d", "days"], "Run replay only for the last N days");
33273
33335
  import_args32.default.option("startTime", "Explicit replay start timestamp (ms or seconds)");
33274
33336
  import_args32.default.option("endTime", "Explicit replay end timestamp (ms or seconds)");
33337
+ import_args32.default.option(
33338
+ "runtimeEvidence",
33339
+ "Immutable runtime evidence JSON used for offline runtime comparison"
33340
+ );
33275
33341
  import_args32.default.option(["u", "updateOnly"], "Only update tickers history", false);
33276
33342
  import_args32.default.option(["C", "cacheOnly"], "Do not update tickers history", false);
33277
33343
  import_args32.default.option(
@@ -33297,6 +33363,7 @@ var init_cliConfig2 = __esm({
33297
33363
  replayInterval = String(replayFlags.timeframe ?? 15);
33298
33364
  replayUserName = String(replayFlags.user ?? "root");
33299
33365
  replayProjectRoot = String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
33366
+ replayRuntimeEvidencePath = typeof replayFlags.runtimeEvidence === "string" && replayFlags.runtimeEvidence.trim() ? replayFlags.runtimeEvidence.trim() : null;
33300
33367
  isReplayUpdateOnlyRun = Boolean(replayFlags.updateOnly);
33301
33368
  }
33302
33369
  });
@@ -33712,12 +33779,12 @@ var init_portfolioReplayConnector = __esm({
33712
33779
  });
33713
33780
 
33714
33781
  // src/lib/replay/historicalSignalsReplay.ts
33715
- var import_chalk44, import_progress11, import_backtest4, import_strategies15, import_time9, import_logger11, import_redis20, import_connectors17, import_cli13, import_strategies16, loadRuntimeStrategies2, loadReferenceConnector, buildPreparedData, buildAfterSignalsContext, buildSymbolSharedReplayKey, runHistoricalSignalsReplay;
33782
+ var import_chalk44, import_progress12, import_backtest4, import_strategies15, import_time9, import_logger11, import_redis20, import_connectors17, import_cli13, import_strategies16, loadRuntimeStrategies2, loadReferenceConnector, buildPreparedData, buildAfterSignalsContext, buildSymbolSharedReplayKey, runHistoricalSignalsReplay;
33716
33783
  var init_historicalSignalsReplay = __esm({
33717
33784
  "src/lib/replay/historicalSignalsReplay.ts"() {
33718
33785
  "use strict";
33719
33786
  import_chalk44 = __toESM(require("chalk"));
33720
- import_progress11 = __toESM(require("progress"));
33787
+ import_progress12 = __toESM(require("progress"));
33721
33788
  import_backtest4 = require("@tradejs/core/backtest");
33722
33789
  import_strategies15 = require("@tradejs/core/strategies");
33723
33790
  import_time9 = require("@tradejs/core/time");
@@ -33897,7 +33964,7 @@ var init_historicalSignalsReplay = __esm({
33897
33964
  const sharedReplayKeyPrefixes = [];
33898
33965
  let preparedSymbols = 0;
33899
33966
  let skippedSymbols = 0;
33900
- const prepareBar = new import_progress11.default(
33967
+ const prepareBar = new import_progress12.default(
33901
33968
  "prepare :current/:total [:bar][:percent] skipped=:skipped :etas(s) :symbol",
33902
33969
  {
33903
33970
  total: preparedRun.tickers.length,
@@ -34019,7 +34086,7 @@ var init_historicalSignalsReplay = __esm({
34019
34086
  interval: interval5
34020
34087
  });
34021
34088
  let abortedCycles = 0;
34022
- const cycleBar = new import_progress11.default(
34089
+ const cycleBar = new import_progress12.default(
34023
34090
  "cycles :current/:total [:bar][:percent] sig=:signals abort=:aborted :etas(s) :ts",
34024
34091
  {
34025
34092
  total: orderedTimestamps.length,
@@ -35324,6 +35391,121 @@ var init_runtimeParityDetails = __esm({
35324
35391
  }
35325
35392
  });
35326
35393
 
35394
+ // src/lib/replay/runtimeEvidenceSource.ts
35395
+ var import_promises20, import_node_path14, asRecord6, asFiniteNumber, unwrapRows2, buildRuntimeEvidenceLineageScopes, loadReplayRuntimeEvidenceSource;
35396
+ var init_runtimeEvidenceSource = __esm({
35397
+ "src/lib/replay/runtimeEvidenceSource.ts"() {
35398
+ "use strict";
35399
+ import_promises20 = __toESM(require("fs/promises"));
35400
+ import_node_path14 = __toESM(require("path"));
35401
+ init_runtimeLineage();
35402
+ asRecord6 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
35403
+ asFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
35404
+ unwrapRows2 = (value, field) => (Array.isArray(value) ? value : []).map((item) => {
35405
+ const row = asRecord6(item);
35406
+ return asRecord6(row?.[field]) ?? row;
35407
+ }).filter((item) => item != null);
35408
+ buildRuntimeEvidenceLineageScopes = ({
35409
+ signals: signals2,
35410
+ evaluations
35411
+ }) => {
35412
+ const scopes = /* @__PURE__ */ new Map();
35413
+ const add = ({
35414
+ strategy,
35415
+ symbol,
35416
+ runtimeConfigId,
35417
+ timestamp,
35418
+ lineage
35419
+ }) => {
35420
+ if (!lineage || !Number.isFinite(timestamp)) {
35421
+ return;
35422
+ }
35423
+ const key = `${strategy}::${symbol}::${runtimeLineageKey(lineage)}`;
35424
+ const existing = scopes.get(key);
35425
+ scopes.set(key, {
35426
+ strategy,
35427
+ symbol,
35428
+ ...runtimeConfigId ? { runtimeConfigId } : {},
35429
+ lineage,
35430
+ firstTimestamp: existing ? Math.min(existing.firstTimestamp, timestamp) : timestamp,
35431
+ lastTimestamp: existing ? Math.max(existing.lastTimestamp, timestamp) : timestamp
35432
+ });
35433
+ };
35434
+ for (const signal of signals2) {
35435
+ add({
35436
+ strategy: signal.strategy,
35437
+ symbol: signal.symbol,
35438
+ runtimeConfigId: signal.runtimeConfigId,
35439
+ timestamp: signal.timestamp,
35440
+ lineage: signal.runtimeLineage
35441
+ });
35442
+ }
35443
+ for (const evaluation of evaluations) {
35444
+ add({
35445
+ strategy: evaluation.strategy,
35446
+ symbol: evaluation.symbol,
35447
+ runtimeConfigId: evaluation.runtimeConfigId,
35448
+ timestamp: evaluation.timestamp,
35449
+ lineage: evaluation.runtimeLineage
35450
+ });
35451
+ }
35452
+ return [...scopes.values()].sort(
35453
+ (left, right) => left.strategy.localeCompare(right.strategy) || left.symbol.localeCompare(right.symbol) || left.firstTimestamp - right.firstTimestamp
35454
+ );
35455
+ };
35456
+ loadReplayRuntimeEvidenceSource = async ({
35457
+ filePath,
35458
+ projectRoot: projectRoot16,
35459
+ expectedUserName,
35460
+ expectedWindow
35461
+ }) => {
35462
+ const resolvedPath = import_node_path14.default.resolve(projectRoot16, filePath);
35463
+ const root = asRecord6(
35464
+ JSON.parse(await import_promises20.default.readFile(resolvedPath, "utf8"))
35465
+ );
35466
+ const runtime = asRecord6(root?.runtime) ?? root;
35467
+ if (!root || !runtime) {
35468
+ throw new Error(`Invalid runtime evidence JSON: ${resolvedPath}`);
35469
+ }
35470
+ const rootWindow = asRecord6(root.window);
35471
+ const runtimeWindow2 = asRecord6(runtime.window);
35472
+ const startTime = asFiniteNumber(rootWindow?.startTime) ?? asFiniteNumber(runtimeWindow2?.startTime);
35473
+ const endTime = asFiniteNumber(rootWindow?.endTime) ?? asFiniteNumber(runtimeWindow2?.endTime);
35474
+ const userName2 = String(root.userName ?? runtime.userName ?? "").trim();
35475
+ if (!userName2 || userName2 !== expectedUserName) {
35476
+ throw new Error(
35477
+ `Runtime evidence user mismatch: expected=${expectedUserName}, actual=${userName2 || "missing"}`
35478
+ );
35479
+ }
35480
+ const endTimeMatches = endTime === expectedWindow.end || endTime === expectedWindow.end + 1;
35481
+ if (startTime !== expectedWindow.start || !endTimeMatches) {
35482
+ throw new Error(
35483
+ `Runtime evidence window mismatch: expected=${expectedWindow.start}..${expectedWindow.end}, actual=${startTime ?? "missing"}..${endTime ?? "missing"}`
35484
+ );
35485
+ }
35486
+ const trades = unwrapRows2(runtime.trades, "trade");
35487
+ const signals2 = unwrapRows2(runtime.signals, "signal");
35488
+ const evaluations = unwrapRows2(
35489
+ runtime.evaluations,
35490
+ "evaluation"
35491
+ );
35492
+ return {
35493
+ path: resolvedPath,
35494
+ userName: userName2,
35495
+ startTime,
35496
+ endTime,
35497
+ trades,
35498
+ signals: signals2,
35499
+ evaluations,
35500
+ lineageScopes: buildRuntimeEvidenceLineageScopes({
35501
+ signals: signals2,
35502
+ evaluations
35503
+ })
35504
+ };
35505
+ };
35506
+ }
35507
+ });
35508
+
35327
35509
  // src/lib/replay/runtimeComparison.ts
35328
35510
  var import_chalk46, import_time11, getReplayEntryTimestampCompareOffsetMs, buildReplaySignalEvaluations, lineageScopeKey, buildExpectedLineageByScope, hasExpectedLineage, filterReplayComparisonByLineage, formatDrilldownSummary, toFiniteNumberOrNull4, sumOptionalNumbers, firstFiniteNumber, firstString, summarizeBacktestPnlByStrategy, getExchangeExecutionMergeKey, mergeExchangeExecutionGroup, mergeExchangeEntryExecutions, loadExchangeEntryRows, buildPriceDeltaPct3, loadExchangeEntriesForComparison, compareExchangeEntriesToBacktest, findExchangeRuntimeOutcome, splitExchangeMatchesByRuntimeOrderStatus, saveAndPrintReplayExchangeComparison, saveAndPrintReplayRuntimeComparison;
35329
35511
  var init_runtimeComparison = __esm({
@@ -35340,6 +35522,7 @@ var init_runtimeComparison = __esm({
35340
35522
  init_runtimeParityDetails();
35341
35523
  init_runState();
35342
35524
  init_cliConfig2();
35525
+ init_runtimeEvidenceSource();
35343
35526
  init_support();
35344
35527
  init_runtimeLineage();
35345
35528
  getReplayEntryTimestampCompareOffsetMs = () => {
@@ -36110,7 +36293,8 @@ var init_runtimeComparison = __esm({
36110
36293
  liveStrategySummaries,
36111
36294
  backtestEntries,
36112
36295
  replaySignals,
36113
- replayLineages
36296
+ replayLineages,
36297
+ runtimeEvidencePath
36114
36298
  }) => {
36115
36299
  const { connector, connectorName, window: window2 } = getRuntimeCompareContext();
36116
36300
  if (!connector || !window2) {
@@ -36120,12 +36304,23 @@ var init_runtimeComparison = __esm({
36120
36304
  liveStrategySummaries.map((summary) => summary.strategyName)
36121
36305
  );
36122
36306
  const replaySignalEvaluations = buildReplaySignalEvaluations(replaySignals);
36307
+ const runtimeEvidence2 = runtimeEvidencePath ? await loadReplayRuntimeEvidenceSource({
36308
+ filePath: runtimeEvidencePath,
36309
+ projectRoot: replayProjectRoot,
36310
+ expectedUserName: replayUserName,
36311
+ expectedWindow: window2
36312
+ }) : null;
36123
36313
  const [
36124
36314
  rawRuntimeTrades,
36125
36315
  runtimeSignals,
36126
36316
  runtimeSignalEvaluations,
36127
36317
  runtimeLineageScopes
36128
- ] = await Promise.all([
36318
+ ] = runtimeEvidence2 ? [
36319
+ runtimeEvidence2.trades,
36320
+ runtimeEvidence2.signals,
36321
+ runtimeEvidence2.evaluations,
36322
+ runtimeEvidence2.lineageScopes
36323
+ ] : await Promise.all([
36129
36324
  loadRuntimeTrades(replayUserName, {
36130
36325
  startTime: window2.start,
36131
36326
  endTime: window2.end
@@ -36143,7 +36338,14 @@ var init_runtimeComparison = __esm({
36143
36338
  endTime: window2.end
36144
36339
  })
36145
36340
  ]);
36146
- const syncedRuntimeTrades = await syncRuntimeTrades({
36341
+ if (runtimeEvidence2) {
36342
+ console.log(
36343
+ import_chalk46.default.gray(
36344
+ `runtime compare: using immutable evidence ${runtimeEvidence2.path}`
36345
+ )
36346
+ );
36347
+ }
36348
+ const syncedRuntimeTrades = runtimeEvidence2 ? rawRuntimeTrades : await syncRuntimeTrades({
36147
36349
  userName: replayUserName,
36148
36350
  connector,
36149
36351
  trades: rawRuntimeTrades,
@@ -36194,7 +36396,7 @@ var init_runtimeComparison = __esm({
36194
36396
  const windowRuntimeTrades = comparable.runtimeTrades.filter(
36195
36397
  (trade) => trade.entryTimestamp >= window2.start && trade.entryTimestamp < window2.end && relevantStrategies.has(trade.strategy)
36196
36398
  );
36197
- if (!windowRuntimeTrades.length) {
36399
+ if (!windowRuntimeTrades.length && !runtimeEvidence2) {
36198
36400
  console.log("");
36199
36401
  console.log(
36200
36402
  import_chalk46.default.yellow(
@@ -36328,12 +36530,12 @@ var init_runtimeComparison = __esm({
36328
36530
  });
36329
36531
 
36330
36532
  // src/lib/replay/outputReport.ts
36331
- var import_promises20, import_node_path14, import_data4, formatMsk, formatNumber5, formatMoney2, formatPct, tableCell, createMarkdownTable2, entryStrategy, entryKey, buildDrilldownMap, buildPerTradeAnalysis, renderMatchedRows, renderRuntimeOnlyRows, renderOrderFailedRows, renderBacktestOnlyRows, renderStrategyRows, renderLineageRows, writeReplayOutputReport;
36533
+ var import_promises21, import_node_path15, import_data4, formatMsk, formatNumber5, formatMoney2, formatPct, tableCell, createMarkdownTable2, entryStrategy, entryKey, buildDrilldownMap, buildPerTradeAnalysis, renderMatchedRows, renderRuntimeOnlyRows, renderOrderFailedRows, renderBacktestOnlyRows, renderStrategyRows, renderLineageRows, writeReplayOutputReport;
36332
36534
  var init_outputReport = __esm({
36333
36535
  "src/lib/replay/outputReport.ts"() {
36334
36536
  "use strict";
36335
- import_promises20 = __toESM(require("fs/promises"));
36336
- import_node_path14 = __toESM(require("path"));
36537
+ import_promises21 = __toESM(require("fs/promises"));
36538
+ import_node_path15 = __toESM(require("path"));
36337
36539
  import_data4 = require("@tradejs/core/data");
36338
36540
  init_support();
36339
36541
  formatMsk = (timestamp) => typeof timestamp === "number" && Number.isFinite(timestamp) ? new Intl.DateTimeFormat("ru-RU", {
@@ -36638,8 +36840,8 @@ var init_outputReport = __esm({
36638
36840
  strategySnapshot,
36639
36841
  runtimeComparison
36640
36842
  }) => {
36641
- const outputDir = import_node_path14.default.resolve(projectRoot16, "data/replay/output");
36642
- await import_promises20.default.mkdir(outputDir, { recursive: true });
36843
+ const outputDir = import_node_path15.default.resolve(projectRoot16, "data/replay/output");
36844
+ await import_promises21.default.mkdir(outputDir, { recursive: true });
36643
36845
  const perTradeAnalysis = buildPerTradeAnalysis(runtimeComparison);
36644
36846
  const report = {
36645
36847
  reportType: "replay-output",
@@ -36674,8 +36876,8 @@ var init_outputReport = __esm({
36674
36876
  runtimeComparison,
36675
36877
  perTradeAnalysis
36676
36878
  };
36677
- const markdownPath = import_node_path14.default.join(outputDir, `${timestamp}-replay-report.md`);
36678
- const jsonPath = import_node_path14.default.join(outputDir, `${timestamp}-replay-report.json`);
36879
+ const markdownPath = import_node_path15.default.join(outputDir, `${timestamp}-replay-report.md`);
36880
+ const jsonPath = import_node_path15.default.join(outputDir, `${timestamp}-replay-report.json`);
36679
36881
  const lines = [
36680
36882
  "# Replay Report",
36681
36883
  "",
@@ -36741,8 +36943,8 @@ var init_outputReport = __esm({
36741
36943
  ""
36742
36944
  ];
36743
36945
  await Promise.all([
36744
- import_promises20.default.writeFile(markdownPath, lines.join("\n"), "utf8"),
36745
- import_promises20.default.writeFile(jsonPath, `${(0, import_data4.toJson)(report, true)}
36946
+ import_promises21.default.writeFile(markdownPath, lines.join("\n"), "utf8"),
36947
+ import_promises21.default.writeFile(jsonPath, `${(0, import_data4.toJson)(report, true)}
36746
36948
  `, "utf8")
36747
36949
  ]);
36748
36950
  return {
@@ -36807,7 +37009,8 @@ var init_replayRunner = __esm({
36807
37009
  liveStrategySummaries: replayStrategySnapshot.summaries,
36808
37010
  backtestEntries: replayStrategySnapshot.backtestEntries,
36809
37011
  replaySignals: replayResult.signals,
36810
- replayLineages: replayResult.runtimeLineages
37012
+ replayLineages: replayResult.runtimeLineages,
37013
+ runtimeEvidencePath: replayRuntimeEvidencePath
36811
37014
  });
36812
37015
  const finishedAt = /* @__PURE__ */ new Date();
36813
37016
  const durationSeconds = Number(