@tradejs/cli 2.0.14 → 2.0.16

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.
@@ -2594,6 +2594,20 @@ var DERIVATIVES_CONTEXT_DATA_MODEL = {
2594
2594
  hyperliquidWhaleMinCoveragePct: 0.8,
2595
2595
  hyperliquidWhaleDataModelVersion: import_types.HYPERLIQUID_WHALE_DATA_MODEL_VERSION
2596
2596
  };
2597
+ var writeAiTrainResearchSnapshot = async ({
2598
+ outputPath,
2599
+ result
2600
+ }) => {
2601
+ const resolvedPath = import_node_path.default.resolve(outputPath);
2602
+ await import_promises.default.mkdir(import_node_path.default.dirname(resolvedPath), { recursive: true });
2603
+ await import_promises.default.writeFile(
2604
+ resolvedPath,
2605
+ `${JSON.stringify(result, null, 2)}
2606
+ `,
2607
+ "utf8"
2608
+ );
2609
+ return resolvedPath;
2610
+ };
2597
2611
  var normalizeForStableJson = (value) => {
2598
2612
  if (Array.isArray(value)) {
2599
2613
  return value.map(normalizeForStableJson);
@@ -2708,6 +2722,7 @@ var resolveGateFingerprint = async (projectRoot, strategyName, gitSha) => {
2708
2722
  `packages/strategies/src/${strategyName}/guardrails.ts`,
2709
2723
  `packages/strategies/src/${strategyName}/pockets.ts`,
2710
2724
  `packages/strategies/src/${strategyName}/config.ts`,
2725
+ "packages/strategies/src/shared/localAiGate.ts",
2711
2726
  "packages/node/src/ai.ts"
2712
2727
  ];
2713
2728
  const optionalSourceEntries = await Promise.all(
@@ -3000,6 +3015,11 @@ import_args.default.option(
3000
3015
  false
3001
3016
  );
3002
3017
  import_args.default.option(["j", "json"], "Print structured JSON summary", false);
3018
+ import_args.default.option(
3019
+ ["O", "output"],
3020
+ "Write the structured JSON summary to a file and suppress table output",
3021
+ ""
3022
+ );
3003
3023
  import_args.default.option(
3004
3024
  ["S", "since"],
3005
3025
  "Only evaluate rows at or after this timestamp (ISO date or epoch ms)",
@@ -3358,7 +3378,9 @@ var main = async () => {
3358
3378
  const minQuality = normalizeInt(flags.minQuality, 4);
3359
3379
  const localOnly = Boolean(flags.localOnly);
3360
3380
  const saveChart = Boolean(flags.chart);
3361
- const jsonOutput = Boolean(flags.json);
3381
+ const printJsonOutput = Boolean(flags.json);
3382
+ const jsonOutputPath = String(flags.output || "").trim();
3383
+ const jsonOutput = printJsonOutput || Boolean(jsonOutputPath);
3362
3384
  const sinceInput = parseTimestampFilter(flags.since);
3363
3385
  const untilInput = parseTimestampFilter(flags.until);
3364
3386
  const trailingPeriodMs = parseTrailingPeriodMs(flags.period);
@@ -3638,6 +3660,12 @@ var main = async () => {
3638
3660
  providerErrors: errorMessages
3639
3661
  }
3640
3662
  };
3663
+ if (jsonOutputPath) {
3664
+ await writeAiTrainResearchSnapshot({
3665
+ outputPath: jsonOutputPath,
3666
+ result
3667
+ });
3668
+ }
3641
3669
  if (dumpEvaluationsPath) {
3642
3670
  await import_promises2.default.mkdir(import_path.default.dirname(import_path.default.resolve(dumpEvaluationsPath)), {
3643
3671
  recursive: true
@@ -3681,7 +3709,9 @@ var main = async () => {
3681
3709
  });
3682
3710
  }
3683
3711
  if (jsonOutput) {
3684
- console.log(JSON.stringify(result));
3712
+ if (printJsonOutput) {
3713
+ console.log(JSON.stringify(result));
3714
+ }
3685
3715
  process.exit(failed === selectedRows ? 1 : 0);
3686
3716
  }
3687
3717
  console.log("");
@@ -1705,6 +1705,8 @@ var timeOperation = async (label, operation, log = console.log) => {
1705
1705
 
1706
1706
  // src/lib/marketContextPrepare.ts
1707
1707
  var import_chalk4 = __toESM(require("chalk"));
1708
+ var import_timescale5 = require("@tradejs/infra/timescale");
1709
+ var import_registry = require("@tradejs/node/registry");
1708
1710
 
1709
1711
  // src/lib/binanceMarketContextBackfill.ts
1710
1712
  var import_progress = __toESM(require("progress"));
@@ -4946,6 +4948,9 @@ var backfillHyperliquidWhaleContext = async (params) => {
4946
4948
  };
4947
4949
 
4948
4950
  // src/lib/marketContextPrepare.ts
4951
+ var uniqueReasons = (reasons) => [
4952
+ ...new Set(reasons)
4953
+ ];
4949
4954
  var shouldPrepareDerivativesContextForRun = (params) => {
4950
4955
  if (params.universe === "tradfi") return false;
4951
4956
  if (params.mode === "signals") {
@@ -4953,6 +4958,9 @@ var shouldPrepareDerivativesContextForRun = (params) => {
4953
4958
  cacheOnly: params.cacheOnly
4954
4959
  });
4955
4960
  }
4961
+ if (params.mode === "backtest" && !params.aiEnabled && !params.mlEnabled) {
4962
+ return false;
4963
+ }
4956
4964
  return shouldBackfillDerivativesContextForBacktest({
4957
4965
  aiEnabled: Boolean(params.aiEnabled),
4958
4966
  cacheOnly: params.cacheOnly,
@@ -4962,6 +4970,7 @@ var shouldPrepareDerivativesContextForRun = (params) => {
4962
4970
  var shouldPrepareBinanceMarketContextForRun = (params) => {
4963
4971
  if (params.universe === "tradfi") return false;
4964
4972
  if (params.mode === "backtest") {
4973
+ if (!params.aiEnabled && !params.mlEnabled) return false;
4965
4974
  return shouldBackfillBinanceMarketContextForBacktest({
4966
4975
  aiEnabled: Boolean(params.aiEnabled),
4967
4976
  cacheOnly: params.cacheOnly,
@@ -4980,6 +4989,7 @@ var shouldPrepareBinanceMarketContextForRun = (params) => {
4980
4989
  var shouldPrepareCoinMarketCapContextForRun = (params) => {
4981
4990
  if (params.universe === "tradfi") return false;
4982
4991
  if (params.mode === "backtest") {
4992
+ if (!params.aiEnabled && !params.mlEnabled) return false;
4983
4993
  return shouldBackfillCoinMarketCapContextForBacktest({
4984
4994
  aiEnabled: Boolean(params.aiEnabled),
4985
4995
  cacheOnly: params.cacheOnly,
@@ -4995,10 +5005,8 @@ var shouldPrepareCoinMarketCapContextForRun = (params) => {
4995
5005
  cacheOnly: params.cacheOnly
4996
5006
  });
4997
5007
  };
4998
- var shouldPrepareHyperliquidWhaleContextForRun = (params) => {
4999
- const strategyRequiresContext = params.strategyNames?.includes(
5000
- "HyperliquidConsensus"
5001
- );
5008
+ var shouldPrepareHyperliquidWhaleContextForRun = (params, coreContextSources = []) => {
5009
+ const strategyRequiresContext = coreContextSources.includes("hyperliquidWhales");
5002
5010
  if (params.universe === "tradfi" || params.mode === "signals" || params.cacheOnly || !params.aiEnabled && !params.mlEnabled && !strategyRequiresContext) {
5003
5011
  return false;
5004
5012
  }
@@ -5009,6 +5017,64 @@ var shouldPrepareHyperliquidWhaleContextForRun = (params) => {
5009
5017
  if (strategyRequiresContext) return true;
5010
5018
  return ["1", "true", "yes", "on"].includes(configured);
5011
5019
  };
5020
+ var resolveCoreContextSources = async (params) => {
5021
+ const strategyNames = [...new Set(params.strategyNames ?? [])];
5022
+ if (!strategyNames.length) return [];
5023
+ await (0, import_registry.ensureStrategyPluginsLoaded)(params.projectRoot);
5024
+ return [
5025
+ ...new Set(
5026
+ strategyNames.flatMap(
5027
+ (strategyName) => (0, import_registry.getStrategyManifest)(strategyName, params.projectRoot)?.contextRequirements?.core ?? []
5028
+ )
5029
+ )
5030
+ ];
5031
+ };
5032
+ var resolveMarketContextRunRequirements = async (params) => {
5033
+ const coreContextSources = await resolveCoreContextSources(params);
5034
+ const paramsWithCoreDemand = (source) => coreContextSources.includes(source) ? { ...params, aiEnabled: true } : params;
5035
+ const isCrypto = params.universe !== "tradfi";
5036
+ const capturesPayload = Boolean(params.aiEnabled || params.mlEnabled);
5037
+ const runtimeReadsContext = params.mode !== "backtest";
5038
+ const reasonsFor = (source) => uniqueReasons([
5039
+ ...coreContextSources.includes(source) ? ["core"] : [],
5040
+ ...params.aiEnabled ? ["ai"] : [],
5041
+ ...params.mlEnabled ? ["ml"] : [],
5042
+ ...runtimeReadsContext ? ["runtime"] : []
5043
+ ]);
5044
+ const readsStandardContext = isCrypto && (capturesPayload || runtimeReadsContext);
5045
+ const readsHyperliquid = isCrypto && (readsStandardContext || coreContextSources.includes("hyperliquidWhales"));
5046
+ return {
5047
+ binance: {
5048
+ read: readsStandardContext || coreContextSources.includes("binance"),
5049
+ backfill: shouldPrepareBinanceMarketContextForRun(
5050
+ paramsWithCoreDemand("binance")
5051
+ ),
5052
+ requiredBy: reasonsFor("binance")
5053
+ },
5054
+ coinmarketcap: {
5055
+ read: readsStandardContext || coreContextSources.includes("coinmarketcap"),
5056
+ backfill: shouldPrepareCoinMarketCapContextForRun(
5057
+ paramsWithCoreDemand("coinmarketcap")
5058
+ ),
5059
+ requiredBy: reasonsFor("coinmarketcap")
5060
+ },
5061
+ derivatives: {
5062
+ read: readsStandardContext || coreContextSources.includes("derivatives"),
5063
+ backfill: shouldPrepareDerivativesContextForRun(
5064
+ paramsWithCoreDemand("derivatives")
5065
+ ),
5066
+ requiredBy: reasonsFor("derivatives")
5067
+ },
5068
+ hyperliquidWhales: {
5069
+ read: readsHyperliquid,
5070
+ backfill: shouldPrepareHyperliquidWhaleContextForRun(
5071
+ params,
5072
+ coreContextSources
5073
+ ),
5074
+ requiredBy: reasonsFor("hyperliquidWhales")
5075
+ }
5076
+ };
5077
+ };
5012
5078
  var resolveCoinMarketCapBackfillForMode = (mode) => mode === "backtest" ? backfillCoinMarketCapContextForBacktest : mode === "signals" ? backfillCoinMarketCapContextForSignals : backfillCoinMarketCapContextForReplay;
5013
5079
  var buildCoinMarketCapBackfillParams = (params) => ({
5014
5080
  userName: params.userName,
@@ -5037,7 +5103,15 @@ var prepareMarketContextForRun = async (params) => {
5037
5103
  console.log(import_chalk4.default.gray(message));
5038
5104
  });
5039
5105
  const timeOperation3 = (label, operation) => timeOperation(label, operation, log);
5040
- if (shouldPrepareDerivativesContextForRun(params)) {
5106
+ const requirements = await resolveMarketContextRunRequirements(params);
5107
+ const readableSources = Object.entries(requirements).filter(([, requirement]) => requirement.read).map(([source]) => source);
5108
+ if (readableSources.length) {
5109
+ await timeOperation3(
5110
+ "market context schema ensure",
5111
+ () => (0, import_timescale5.ensureMarketContextSchemas)(readableSources)
5112
+ );
5113
+ }
5114
+ if (requirements.derivatives.backfill) {
5041
5115
  await timeOperation3(
5042
5116
  "derivatives context backfill",
5043
5117
  () => (params.mode === "signals" ? backfillDerivativesContextForSignals : backfillDerivativesContextForBacktest)(
@@ -5045,7 +5119,7 @@ var prepareMarketContextForRun = async (params) => {
5045
5119
  )
5046
5120
  );
5047
5121
  }
5048
- if (shouldPrepareHyperliquidWhaleContextForRun(params)) {
5122
+ if (requirements.hyperliquidWhales.backfill) {
5049
5123
  await timeOperation3(
5050
5124
  "hyperliquid whale context backfill",
5051
5125
  () => backfillHyperliquidWhaleContext({
@@ -5057,13 +5131,13 @@ var prepareMarketContextForRun = async (params) => {
5057
5131
  })
5058
5132
  );
5059
5133
  }
5060
- if (shouldPrepareBinanceMarketContextForRun(params)) {
5134
+ if (requirements.binance.backfill) {
5061
5135
  await timeOperation3("binance market context backfill", () => {
5062
5136
  const backfill = params.mode === "backtest" ? backfillBinanceMarketContextForBacktest : params.mode === "signals" ? backfillBinanceMarketContextForSignals : backfillBinanceMarketContextForReplay;
5063
5137
  return backfill(buildBinanceMarketBackfillParams(params));
5064
5138
  });
5065
5139
  }
5066
- if (shouldPrepareCoinMarketCapContextForRun(params)) {
5140
+ if (requirements.coinmarketcap.backfill) {
5067
5141
  await timeOperation3(
5068
5142
  "coinmarketcap historical context backfill",
5069
5143
  () => resolveCoinMarketCapBackfillForMode(params.mode)(
@@ -5071,6 +5145,7 @@ var prepareMarketContextForRun = async (params) => {
5071
5145
  )
5072
5146
  );
5073
5147
  }
5148
+ return requirements;
5074
5149
  };
5075
5150
 
5076
5151
  // src/lib/runEnvironment.ts
@@ -2346,6 +2346,8 @@ var prepareRunEnvironment = async ({
2346
2346
 
2347
2347
  // src/lib/marketContextPrepare.ts
2348
2348
  var import_chalk7 = __toESM(require("chalk"));
2349
+ var import_timescale5 = require("@tradejs/infra/timescale");
2350
+ var import_registry = require("@tradejs/node/registry");
2349
2351
 
2350
2352
  // src/lib/binanceMarketContextBackfill.ts
2351
2353
  var import_progress = __toESM(require("progress"));
@@ -5587,6 +5589,9 @@ var backfillHyperliquidWhaleContext = async (params) => {
5587
5589
  };
5588
5590
 
5589
5591
  // src/lib/marketContextPrepare.ts
5592
+ var uniqueReasons = (reasons) => [
5593
+ ...new Set(reasons)
5594
+ ];
5590
5595
  var shouldPrepareDerivativesContextForRun = (params) => {
5591
5596
  if (params.universe === "tradfi") return false;
5592
5597
  if (params.mode === "signals") {
@@ -5594,6 +5599,9 @@ var shouldPrepareDerivativesContextForRun = (params) => {
5594
5599
  cacheOnly: params.cacheOnly
5595
5600
  });
5596
5601
  }
5602
+ if (params.mode === "backtest" && !params.aiEnabled && !params.mlEnabled) {
5603
+ return false;
5604
+ }
5597
5605
  return shouldBackfillDerivativesContextForBacktest({
5598
5606
  aiEnabled: Boolean(params.aiEnabled),
5599
5607
  cacheOnly: params.cacheOnly,
@@ -5603,6 +5611,7 @@ var shouldPrepareDerivativesContextForRun = (params) => {
5603
5611
  var shouldPrepareBinanceMarketContextForRun = (params) => {
5604
5612
  if (params.universe === "tradfi") return false;
5605
5613
  if (params.mode === "backtest") {
5614
+ if (!params.aiEnabled && !params.mlEnabled) return false;
5606
5615
  return shouldBackfillBinanceMarketContextForBacktest({
5607
5616
  aiEnabled: Boolean(params.aiEnabled),
5608
5617
  cacheOnly: params.cacheOnly,
@@ -5621,6 +5630,7 @@ var shouldPrepareBinanceMarketContextForRun = (params) => {
5621
5630
  var shouldPrepareCoinMarketCapContextForRun = (params) => {
5622
5631
  if (params.universe === "tradfi") return false;
5623
5632
  if (params.mode === "backtest") {
5633
+ if (!params.aiEnabled && !params.mlEnabled) return false;
5624
5634
  return shouldBackfillCoinMarketCapContextForBacktest({
5625
5635
  aiEnabled: Boolean(params.aiEnabled),
5626
5636
  cacheOnly: params.cacheOnly,
@@ -5636,10 +5646,8 @@ var shouldPrepareCoinMarketCapContextForRun = (params) => {
5636
5646
  cacheOnly: params.cacheOnly
5637
5647
  });
5638
5648
  };
5639
- var shouldPrepareHyperliquidWhaleContextForRun = (params) => {
5640
- const strategyRequiresContext = params.strategyNames?.includes(
5641
- "HyperliquidConsensus"
5642
- );
5649
+ var shouldPrepareHyperliquidWhaleContextForRun = (params, coreContextSources = []) => {
5650
+ const strategyRequiresContext = coreContextSources.includes("hyperliquidWhales");
5643
5651
  if (params.universe === "tradfi" || params.mode === "signals" || params.cacheOnly || !params.aiEnabled && !params.mlEnabled && !strategyRequiresContext) {
5644
5652
  return false;
5645
5653
  }
@@ -5650,6 +5658,64 @@ var shouldPrepareHyperliquidWhaleContextForRun = (params) => {
5650
5658
  if (strategyRequiresContext) return true;
5651
5659
  return ["1", "true", "yes", "on"].includes(configured);
5652
5660
  };
5661
+ var resolveCoreContextSources = async (params) => {
5662
+ const strategyNames = [...new Set(params.strategyNames ?? [])];
5663
+ if (!strategyNames.length) return [];
5664
+ await (0, import_registry.ensureStrategyPluginsLoaded)(params.projectRoot);
5665
+ return [
5666
+ ...new Set(
5667
+ strategyNames.flatMap(
5668
+ (strategyName) => (0, import_registry.getStrategyManifest)(strategyName, params.projectRoot)?.contextRequirements?.core ?? []
5669
+ )
5670
+ )
5671
+ ];
5672
+ };
5673
+ var resolveMarketContextRunRequirements = async (params) => {
5674
+ const coreContextSources = await resolveCoreContextSources(params);
5675
+ const paramsWithCoreDemand = (source) => coreContextSources.includes(source) ? { ...params, aiEnabled: true } : params;
5676
+ const isCrypto = params.universe !== "tradfi";
5677
+ const capturesPayload = Boolean(params.aiEnabled || params.mlEnabled);
5678
+ const runtimeReadsContext = params.mode !== "backtest";
5679
+ const reasonsFor = (source) => uniqueReasons([
5680
+ ...coreContextSources.includes(source) ? ["core"] : [],
5681
+ ...params.aiEnabled ? ["ai"] : [],
5682
+ ...params.mlEnabled ? ["ml"] : [],
5683
+ ...runtimeReadsContext ? ["runtime"] : []
5684
+ ]);
5685
+ const readsStandardContext = isCrypto && (capturesPayload || runtimeReadsContext);
5686
+ const readsHyperliquid = isCrypto && (readsStandardContext || coreContextSources.includes("hyperliquidWhales"));
5687
+ return {
5688
+ binance: {
5689
+ read: readsStandardContext || coreContextSources.includes("binance"),
5690
+ backfill: shouldPrepareBinanceMarketContextForRun(
5691
+ paramsWithCoreDemand("binance")
5692
+ ),
5693
+ requiredBy: reasonsFor("binance")
5694
+ },
5695
+ coinmarketcap: {
5696
+ read: readsStandardContext || coreContextSources.includes("coinmarketcap"),
5697
+ backfill: shouldPrepareCoinMarketCapContextForRun(
5698
+ paramsWithCoreDemand("coinmarketcap")
5699
+ ),
5700
+ requiredBy: reasonsFor("coinmarketcap")
5701
+ },
5702
+ derivatives: {
5703
+ read: readsStandardContext || coreContextSources.includes("derivatives"),
5704
+ backfill: shouldPrepareDerivativesContextForRun(
5705
+ paramsWithCoreDemand("derivatives")
5706
+ ),
5707
+ requiredBy: reasonsFor("derivatives")
5708
+ },
5709
+ hyperliquidWhales: {
5710
+ read: readsHyperliquid,
5711
+ backfill: shouldPrepareHyperliquidWhaleContextForRun(
5712
+ params,
5713
+ coreContextSources
5714
+ ),
5715
+ requiredBy: reasonsFor("hyperliquidWhales")
5716
+ }
5717
+ };
5718
+ };
5653
5719
  var resolveCoinMarketCapBackfillForMode = (mode) => mode === "backtest" ? backfillCoinMarketCapContextForBacktest : mode === "signals" ? backfillCoinMarketCapContextForSignals : backfillCoinMarketCapContextForReplay;
5654
5720
  var buildCoinMarketCapBackfillParams = (params) => ({
5655
5721
  userName: params.userName,
@@ -5678,7 +5744,15 @@ var prepareMarketContextForRun = async (params) => {
5678
5744
  console.log(import_chalk7.default.gray(message));
5679
5745
  });
5680
5746
  const timeOperation3 = (label, operation) => timeOperation(label, operation, log);
5681
- if (shouldPrepareDerivativesContextForRun(params)) {
5747
+ const requirements = await resolveMarketContextRunRequirements(params);
5748
+ const readableSources = Object.entries(requirements).filter(([, requirement]) => requirement.read).map(([source]) => source);
5749
+ if (readableSources.length) {
5750
+ await timeOperation3(
5751
+ "market context schema ensure",
5752
+ () => (0, import_timescale5.ensureMarketContextSchemas)(readableSources)
5753
+ );
5754
+ }
5755
+ if (requirements.derivatives.backfill) {
5682
5756
  await timeOperation3(
5683
5757
  "derivatives context backfill",
5684
5758
  () => (params.mode === "signals" ? backfillDerivativesContextForSignals : backfillDerivativesContextForBacktest)(
@@ -5686,7 +5760,7 @@ var prepareMarketContextForRun = async (params) => {
5686
5760
  )
5687
5761
  );
5688
5762
  }
5689
- if (shouldPrepareHyperliquidWhaleContextForRun(params)) {
5763
+ if (requirements.hyperliquidWhales.backfill) {
5690
5764
  await timeOperation3(
5691
5765
  "hyperliquid whale context backfill",
5692
5766
  () => backfillHyperliquidWhaleContext({
@@ -5698,13 +5772,13 @@ var prepareMarketContextForRun = async (params) => {
5698
5772
  })
5699
5773
  );
5700
5774
  }
5701
- if (shouldPrepareBinanceMarketContextForRun(params)) {
5775
+ if (requirements.binance.backfill) {
5702
5776
  await timeOperation3("binance market context backfill", () => {
5703
5777
  const backfill = params.mode === "backtest" ? backfillBinanceMarketContextForBacktest : params.mode === "signals" ? backfillBinanceMarketContextForSignals : backfillBinanceMarketContextForReplay;
5704
5778
  return backfill(buildBinanceMarketBackfillParams(params));
5705
5779
  });
5706
5780
  }
5707
- if (shouldPrepareCoinMarketCapContextForRun(params)) {
5781
+ if (requirements.coinmarketcap.backfill) {
5708
5782
  await timeOperation3(
5709
5783
  "coinmarketcap historical context backfill",
5710
5784
  () => resolveCoinMarketCapBackfillForMode(params.mode)(
@@ -5712,6 +5786,7 @@ var prepareMarketContextForRun = async (params) => {
5712
5786
  )
5713
5787
  );
5714
5788
  }
5789
+ return requirements;
5715
5790
  };
5716
5791
 
5717
5792
  // src/lib/backtest/runState.ts
@@ -7960,6 +8035,15 @@ var syncRuntimeTrades = async ({
7960
8035
  nextTrade,
7961
8036
  { expire: import_constants5.TTL_1M }
7962
8037
  ),
8038
+ (0, import_redis5.setHashJsonField)(
8039
+ import_redis5.redisKeys.runtimeClosedTradeBucket(
8040
+ userName,
8041
+ (0, import_time5.getRuntimeStorageDayKey)(nextTrade.exitTimestamp)
8042
+ ),
8043
+ trade.orderId,
8044
+ nextTrade,
8045
+ { expire: import_constants5.TTL_1M }
8046
+ ),
7963
8047
  ...isCurrentActiveTrade ? [(0, import_redis5.delKey)(activeTradeKey)] : []
7964
8048
  ]);
7965
8049
  syncedTrades.push(nextTrade);
@@ -2349,6 +2349,8 @@ var prepareRunEnvironment = async ({
2349
2349
 
2350
2350
  // src/lib/marketContextPrepare.ts
2351
2351
  var import_chalk7 = __toESM(require("chalk"));
2352
+ var import_timescale5 = require("@tradejs/infra/timescale");
2353
+ var import_registry = require("@tradejs/node/registry");
2352
2354
 
2353
2355
  // src/lib/binanceMarketContextBackfill.ts
2354
2356
  var import_progress = __toESM(require("progress"));
@@ -5590,6 +5592,9 @@ var backfillHyperliquidWhaleContext = async (params) => {
5590
5592
  };
5591
5593
 
5592
5594
  // src/lib/marketContextPrepare.ts
5595
+ var uniqueReasons = (reasons) => [
5596
+ ...new Set(reasons)
5597
+ ];
5593
5598
  var shouldPrepareDerivativesContextForRun = (params) => {
5594
5599
  if (params.universe === "tradfi") return false;
5595
5600
  if (params.mode === "signals") {
@@ -5597,6 +5602,9 @@ var shouldPrepareDerivativesContextForRun = (params) => {
5597
5602
  cacheOnly: params.cacheOnly
5598
5603
  });
5599
5604
  }
5605
+ if (params.mode === "backtest" && !params.aiEnabled && !params.mlEnabled) {
5606
+ return false;
5607
+ }
5600
5608
  return shouldBackfillDerivativesContextForBacktest({
5601
5609
  aiEnabled: Boolean(params.aiEnabled),
5602
5610
  cacheOnly: params.cacheOnly,
@@ -5606,6 +5614,7 @@ var shouldPrepareDerivativesContextForRun = (params) => {
5606
5614
  var shouldPrepareBinanceMarketContextForRun = (params) => {
5607
5615
  if (params.universe === "tradfi") return false;
5608
5616
  if (params.mode === "backtest") {
5617
+ if (!params.aiEnabled && !params.mlEnabled) return false;
5609
5618
  return shouldBackfillBinanceMarketContextForBacktest({
5610
5619
  aiEnabled: Boolean(params.aiEnabled),
5611
5620
  cacheOnly: params.cacheOnly,
@@ -5624,6 +5633,7 @@ var shouldPrepareBinanceMarketContextForRun = (params) => {
5624
5633
  var shouldPrepareCoinMarketCapContextForRun = (params) => {
5625
5634
  if (params.universe === "tradfi") return false;
5626
5635
  if (params.mode === "backtest") {
5636
+ if (!params.aiEnabled && !params.mlEnabled) return false;
5627
5637
  return shouldBackfillCoinMarketCapContextForBacktest({
5628
5638
  aiEnabled: Boolean(params.aiEnabled),
5629
5639
  cacheOnly: params.cacheOnly,
@@ -5639,10 +5649,8 @@ var shouldPrepareCoinMarketCapContextForRun = (params) => {
5639
5649
  cacheOnly: params.cacheOnly
5640
5650
  });
5641
5651
  };
5642
- var shouldPrepareHyperliquidWhaleContextForRun = (params) => {
5643
- const strategyRequiresContext = params.strategyNames?.includes(
5644
- "HyperliquidConsensus"
5645
- );
5652
+ var shouldPrepareHyperliquidWhaleContextForRun = (params, coreContextSources = []) => {
5653
+ const strategyRequiresContext = coreContextSources.includes("hyperliquidWhales");
5646
5654
  if (params.universe === "tradfi" || params.mode === "signals" || params.cacheOnly || !params.aiEnabled && !params.mlEnabled && !strategyRequiresContext) {
5647
5655
  return false;
5648
5656
  }
@@ -5653,6 +5661,64 @@ var shouldPrepareHyperliquidWhaleContextForRun = (params) => {
5653
5661
  if (strategyRequiresContext) return true;
5654
5662
  return ["1", "true", "yes", "on"].includes(configured);
5655
5663
  };
5664
+ var resolveCoreContextSources = async (params) => {
5665
+ const strategyNames = [...new Set(params.strategyNames ?? [])];
5666
+ if (!strategyNames.length) return [];
5667
+ await (0, import_registry.ensureStrategyPluginsLoaded)(params.projectRoot);
5668
+ return [
5669
+ ...new Set(
5670
+ strategyNames.flatMap(
5671
+ (strategyName) => (0, import_registry.getStrategyManifest)(strategyName, params.projectRoot)?.contextRequirements?.core ?? []
5672
+ )
5673
+ )
5674
+ ];
5675
+ };
5676
+ var resolveMarketContextRunRequirements = async (params) => {
5677
+ const coreContextSources = await resolveCoreContextSources(params);
5678
+ const paramsWithCoreDemand = (source) => coreContextSources.includes(source) ? { ...params, aiEnabled: true } : params;
5679
+ const isCrypto = params.universe !== "tradfi";
5680
+ const capturesPayload = Boolean(params.aiEnabled || params.mlEnabled);
5681
+ const runtimeReadsContext = params.mode !== "backtest";
5682
+ const reasonsFor = (source) => uniqueReasons([
5683
+ ...coreContextSources.includes(source) ? ["core"] : [],
5684
+ ...params.aiEnabled ? ["ai"] : [],
5685
+ ...params.mlEnabled ? ["ml"] : [],
5686
+ ...runtimeReadsContext ? ["runtime"] : []
5687
+ ]);
5688
+ const readsStandardContext = isCrypto && (capturesPayload || runtimeReadsContext);
5689
+ const readsHyperliquid = isCrypto && (readsStandardContext || coreContextSources.includes("hyperliquidWhales"));
5690
+ return {
5691
+ binance: {
5692
+ read: readsStandardContext || coreContextSources.includes("binance"),
5693
+ backfill: shouldPrepareBinanceMarketContextForRun(
5694
+ paramsWithCoreDemand("binance")
5695
+ ),
5696
+ requiredBy: reasonsFor("binance")
5697
+ },
5698
+ coinmarketcap: {
5699
+ read: readsStandardContext || coreContextSources.includes("coinmarketcap"),
5700
+ backfill: shouldPrepareCoinMarketCapContextForRun(
5701
+ paramsWithCoreDemand("coinmarketcap")
5702
+ ),
5703
+ requiredBy: reasonsFor("coinmarketcap")
5704
+ },
5705
+ derivatives: {
5706
+ read: readsStandardContext || coreContextSources.includes("derivatives"),
5707
+ backfill: shouldPrepareDerivativesContextForRun(
5708
+ paramsWithCoreDemand("derivatives")
5709
+ ),
5710
+ requiredBy: reasonsFor("derivatives")
5711
+ },
5712
+ hyperliquidWhales: {
5713
+ read: readsHyperliquid,
5714
+ backfill: shouldPrepareHyperliquidWhaleContextForRun(
5715
+ params,
5716
+ coreContextSources
5717
+ ),
5718
+ requiredBy: reasonsFor("hyperliquidWhales")
5719
+ }
5720
+ };
5721
+ };
5656
5722
  var resolveCoinMarketCapBackfillForMode = (mode) => mode === "backtest" ? backfillCoinMarketCapContextForBacktest : mode === "signals" ? backfillCoinMarketCapContextForSignals : backfillCoinMarketCapContextForReplay;
5657
5723
  var buildCoinMarketCapBackfillParams = (params) => ({
5658
5724
  userName: params.userName,
@@ -5681,7 +5747,15 @@ var prepareMarketContextForRun = async (params) => {
5681
5747
  console.log(import_chalk7.default.gray(message));
5682
5748
  });
5683
5749
  const timeOperation3 = (label, operation) => timeOperation(label, operation, log);
5684
- if (shouldPrepareDerivativesContextForRun(params)) {
5750
+ const requirements = await resolveMarketContextRunRequirements(params);
5751
+ const readableSources = Object.entries(requirements).filter(([, requirement]) => requirement.read).map(([source]) => source);
5752
+ if (readableSources.length) {
5753
+ await timeOperation3(
5754
+ "market context schema ensure",
5755
+ () => (0, import_timescale5.ensureMarketContextSchemas)(readableSources)
5756
+ );
5757
+ }
5758
+ if (requirements.derivatives.backfill) {
5685
5759
  await timeOperation3(
5686
5760
  "derivatives context backfill",
5687
5761
  () => (params.mode === "signals" ? backfillDerivativesContextForSignals : backfillDerivativesContextForBacktest)(
@@ -5689,7 +5763,7 @@ var prepareMarketContextForRun = async (params) => {
5689
5763
  )
5690
5764
  );
5691
5765
  }
5692
- if (shouldPrepareHyperliquidWhaleContextForRun(params)) {
5766
+ if (requirements.hyperliquidWhales.backfill) {
5693
5767
  await timeOperation3(
5694
5768
  "hyperliquid whale context backfill",
5695
5769
  () => backfillHyperliquidWhaleContext({
@@ -5701,13 +5775,13 @@ var prepareMarketContextForRun = async (params) => {
5701
5775
  })
5702
5776
  );
5703
5777
  }
5704
- if (shouldPrepareBinanceMarketContextForRun(params)) {
5778
+ if (requirements.binance.backfill) {
5705
5779
  await timeOperation3("binance market context backfill", () => {
5706
5780
  const backfill = params.mode === "backtest" ? backfillBinanceMarketContextForBacktest : params.mode === "signals" ? backfillBinanceMarketContextForSignals : backfillBinanceMarketContextForReplay;
5707
5781
  return backfill(buildBinanceMarketBackfillParams(params));
5708
5782
  });
5709
5783
  }
5710
- if (shouldPrepareCoinMarketCapContextForRun(params)) {
5784
+ if (requirements.coinmarketcap.backfill) {
5711
5785
  await timeOperation3(
5712
5786
  "coinmarketcap historical context backfill",
5713
5787
  () => resolveCoinMarketCapBackfillForMode(params.mode)(
@@ -5715,6 +5789,7 @@ var prepareMarketContextForRun = async (params) => {
5715
5789
  )
5716
5790
  );
5717
5791
  }
5792
+ return requirements;
5718
5793
  };
5719
5794
 
5720
5795
  // src/lib/backtest/runState.ts
@@ -7963,6 +8038,15 @@ var syncRuntimeTrades = async ({
7963
8038
  nextTrade,
7964
8039
  { expire: import_constants5.TTL_1M }
7965
8040
  ),
8041
+ (0, import_redis5.setHashJsonField)(
8042
+ import_redis5.redisKeys.runtimeClosedTradeBucket(
8043
+ userName,
8044
+ (0, import_time5.getRuntimeStorageDayKey)(nextTrade.exitTimestamp)
8045
+ ),
8046
+ trade.orderId,
8047
+ nextTrade,
8048
+ { expire: import_constants5.TTL_1M }
8049
+ ),
7966
8050
  ...isCurrentActiveTrade ? [(0, import_redis5.delKey)(activeTradeKey)] : []
7967
8051
  ]);
7968
8052
  syncedTrades.push(nextTrade);