@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.
@@ -1653,112 +1653,8 @@ var import_constants3 = require("@tradejs/core/constants");
1653
1653
  var import_strategies6 = require("@tradejs/node/strategies");
1654
1654
  var import_time3 = require("@tradejs/core/time");
1655
1655
  var import_logger4 = require("@tradejs/infra/logger");
1656
- var import_redis5 = require("@tradejs/infra/redis");
1657
1656
  var import_tradingAccounts2 = require("@tradejs/infra/tradingAccounts");
1658
1657
 
1659
- // src/lib/runtimeSignalsStorage.ts
1660
- var import_constants = require("@tradejs/core/constants");
1661
- var import_time = require("@tradejs/core/time");
1662
- var SECONDS_PER_DAY = 86400;
1663
- var RUNTIME_SIGNAL_RETENTION_DAYS_ENV = "RUNTIME_SIGNAL_RETENTION_DAYS";
1664
- var DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS = import_constants.TTL_3D;
1665
- var RUNTIME_LINEAGE_SCOPE_RETENTION_TTL_SECONDS = import_constants.TTL_1M;
1666
- var parsePositiveNumber = (value) => {
1667
- if (value == null || value === "") {
1668
- return null;
1669
- }
1670
- const parsed = Number(value);
1671
- return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
1672
- };
1673
- var getRuntimeSignalRetentionTtlSeconds = () => {
1674
- const explicitDays = parsePositiveNumber(
1675
- process.env[RUNTIME_SIGNAL_RETENTION_DAYS_ENV]
1676
- );
1677
- if (explicitDays != null) {
1678
- return Math.ceil(explicitDays * SECONDS_PER_DAY);
1679
- }
1680
- return DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS;
1681
- };
1682
- var toRuntimeSignalBucketRef = (signal) => ({
1683
- signalId: signal.signalId,
1684
- symbol: signal.symbol,
1685
- strategy: signal.strategy,
1686
- timestamp: signal.timestamp,
1687
- ...signal.runtimeConfigId && signal.runtimeConfigId !== "config" ? { runtimeConfigId: signal.runtimeConfigId } : {}
1688
- });
1689
- var shouldStoreRuntimeSignalDiagnostics = (signal) => signal.orderStatus === "completed" || signal.orderStatus === "failed";
1690
- var toStoredRuntimeSignal = (signal) => {
1691
- if (shouldStoreRuntimeSignalDiagnostics(signal)) {
1692
- return signal;
1693
- }
1694
- const stored = { ...signal };
1695
- delete stored.figures;
1696
- delete stored.indicators;
1697
- delete stored.additionalIndicators;
1698
- return stored;
1699
- };
1700
- var normalizeRuntimeSignalSkipReason = (reason, fallbackSource) => {
1701
- let source = fallbackSource;
1702
- let normalizedReason = reason;
1703
- if (reason.startsWith("AI_QUALITY_BELOW_MIN")) {
1704
- source = "AI";
1705
- normalizedReason = "MIN_AI_QUALITY";
1706
- } else if (reason === "AI_QUALITY_UNAVAILABLE") {
1707
- source = "AI";
1708
- normalizedReason = "QUALITY_UNAVAILABLE";
1709
- } else if (reason === "ML_RESULT_UNAVAILABLE") {
1710
- source = "ML";
1711
- normalizedReason = "RESULT_UNAVAILABLE";
1712
- } else if (reason.startsWith("ML_THRESHOLD_NOT_MET")) {
1713
- source = "ML";
1714
- normalizedReason = "ML_THRESHOLD";
1715
- } else if (reason.startsWith("HOOK_BEFORE_ENTRY_GATE:")) {
1716
- source = "hook";
1717
- normalizedReason = `BEFORE_ENTRY_GATE:${reason.slice(
1718
- "HOOK_BEFORE_ENTRY_GATE:".length
1719
- )}`;
1720
- } else if (reason === "HOOK_BEFORE_ENTRY_GATE") {
1721
- source = "hook";
1722
- normalizedReason = "BEFORE_ENTRY_GATE";
1723
- } else if (reason === "MAKE_ORDERS_DISABLED" || reason === "ENTRY_POLICY_BLOCKED") {
1724
- source = "policy";
1725
- }
1726
- return {
1727
- source: `skip from ${source}`,
1728
- reason: normalizedReason
1729
- };
1730
- };
1731
- var buildStatsReasonField = (source, reason) => `reason:${source}:${reason}`;
1732
- var buildRuntimeSignalStatsIncrements = (evaluation) => {
1733
- const increments = {
1734
- evaluated: 1
1735
- };
1736
- if (evaluation.status === "signal") {
1737
- increments.signals = 1;
1738
- }
1739
- let skipReason = null;
1740
- let fallbackSource = "core";
1741
- if (evaluation.status === "skip") {
1742
- skipReason = evaluation.reason || "NO_SIGNAL";
1743
- fallbackSource = "core";
1744
- } else if (evaluation.status === "signal" && (evaluation.orderStatus === "skipped" || evaluation.orderStatus === "canceled")) {
1745
- skipReason = evaluation.orderSkipReason || evaluation.reason || `orderStatus=${evaluation.orderStatus}`;
1746
- fallbackSource = "runtime";
1747
- } else if (evaluation.status === "error") {
1748
- skipReason = evaluation.reason || "EVALUATION_ERROR";
1749
- fallbackSource = "runtime";
1750
- }
1751
- if (skipReason) {
1752
- const normalized = normalizeRuntimeSignalSkipReason(
1753
- skipReason,
1754
- fallbackSource
1755
- );
1756
- increments[buildStatsReasonField(normalized.source, normalized.reason)] = 1;
1757
- }
1758
- return increments;
1759
- };
1760
- var shouldStoreDetailedRuntimeSignalEvaluation = (evaluation) => evaluation.status !== "skip";
1761
-
1762
1658
  // src/lib/marketData/windows.ts
1763
1659
  var import_indicators = require("@tradejs/core/indicators");
1764
1660
  var resolveOpenTimestamp = (timestamp, intervalMs2) => Number.isFinite(intervalMs2) && intervalMs2 > 0 ? Math.floor(timestamp / intervalMs2) * intervalMs2 : timestamp;
@@ -1818,6 +1714,8 @@ var invokeAfterSignalsHooks = async (hooks, params) => {
1818
1714
 
1819
1715
  // src/lib/marketContextPrepare.ts
1820
1716
  var import_chalk4 = __toESM(require("chalk"));
1717
+ var import_timescale5 = require("@tradejs/infra/timescale");
1718
+ var import_registry = require("@tradejs/node/registry");
1821
1719
 
1822
1720
  // src/lib/binanceMarketContextBackfill.ts
1823
1721
  var import_progress = __toESM(require("progress"));
@@ -2618,7 +2516,7 @@ var backfillBinanceMarketContextForReplay = (params) => backfillBinanceMarketCon
2618
2516
  var import_chalk2 = __toESM(require("chalk"));
2619
2517
  var import_progress2 = __toESM(require("progress"));
2620
2518
  var import_async = require("@tradejs/core/async");
2621
- var import_constants2 = require("@tradejs/core/constants");
2519
+ var import_constants = require("@tradejs/core/constants");
2622
2520
  var import_indicators2 = require("@tradejs/core/indicators");
2623
2521
  var import_timescale2 = require("@tradejs/infra/timescale");
2624
2522
  var import_userSettings = require("@tradejs/infra/userSettings");
@@ -2653,7 +2551,7 @@ var normalizeSymbols = (symbols) => [
2653
2551
  ];
2654
2552
  var isDerivativesTargetContextEnabled = () => parseBooleanFlag(process.env.DERIVATIVES_CONTEXT_TARGET_ENABLED, false);
2655
2553
  var resolveDerivativesContextBackfillSymbols = (requestedSymbols = []) => {
2656
- const referenceSymbols = (0, import_constants2.resolveDerivativesContextReferenceSymbols)(
2554
+ const referenceSymbols = (0, import_constants.resolveDerivativesContextReferenceSymbols)(
2657
2555
  process.env.DERIVATIVES_CONTEXT_EXTRA_REFERENCE_SYMBOLS
2658
2556
  );
2659
2557
  return isDerivativesTargetContextEnabled() ? normalizeSymbols([...referenceSymbols, ...requestedSymbols]) : referenceSymbols;
@@ -5059,6 +4957,9 @@ var backfillHyperliquidWhaleContext = async (params) => {
5059
4957
  };
5060
4958
 
5061
4959
  // src/lib/marketContextPrepare.ts
4960
+ var uniqueReasons = (reasons) => [
4961
+ ...new Set(reasons)
4962
+ ];
5062
4963
  var shouldPrepareDerivativesContextForRun = (params) => {
5063
4964
  if (params.universe === "tradfi") return false;
5064
4965
  if (params.mode === "signals") {
@@ -5066,6 +4967,9 @@ var shouldPrepareDerivativesContextForRun = (params) => {
5066
4967
  cacheOnly: params.cacheOnly
5067
4968
  });
5068
4969
  }
4970
+ if (params.mode === "backtest" && !params.aiEnabled && !params.mlEnabled) {
4971
+ return false;
4972
+ }
5069
4973
  return shouldBackfillDerivativesContextForBacktest({
5070
4974
  aiEnabled: Boolean(params.aiEnabled),
5071
4975
  cacheOnly: params.cacheOnly,
@@ -5075,6 +4979,7 @@ var shouldPrepareDerivativesContextForRun = (params) => {
5075
4979
  var shouldPrepareBinanceMarketContextForRun = (params) => {
5076
4980
  if (params.universe === "tradfi") return false;
5077
4981
  if (params.mode === "backtest") {
4982
+ if (!params.aiEnabled && !params.mlEnabled) return false;
5078
4983
  return shouldBackfillBinanceMarketContextForBacktest({
5079
4984
  aiEnabled: Boolean(params.aiEnabled),
5080
4985
  cacheOnly: params.cacheOnly,
@@ -5093,6 +4998,7 @@ var shouldPrepareBinanceMarketContextForRun = (params) => {
5093
4998
  var shouldPrepareCoinMarketCapContextForRun = (params) => {
5094
4999
  if (params.universe === "tradfi") return false;
5095
5000
  if (params.mode === "backtest") {
5001
+ if (!params.aiEnabled && !params.mlEnabled) return false;
5096
5002
  return shouldBackfillCoinMarketCapContextForBacktest({
5097
5003
  aiEnabled: Boolean(params.aiEnabled),
5098
5004
  cacheOnly: params.cacheOnly,
@@ -5108,10 +5014,8 @@ var shouldPrepareCoinMarketCapContextForRun = (params) => {
5108
5014
  cacheOnly: params.cacheOnly
5109
5015
  });
5110
5016
  };
5111
- var shouldPrepareHyperliquidWhaleContextForRun = (params) => {
5112
- const strategyRequiresContext = params.strategyNames?.includes(
5113
- "HyperliquidConsensus"
5114
- );
5017
+ var shouldPrepareHyperliquidWhaleContextForRun = (params, coreContextSources = []) => {
5018
+ const strategyRequiresContext = coreContextSources.includes("hyperliquidWhales");
5115
5019
  if (params.universe === "tradfi" || params.mode === "signals" || params.cacheOnly || !params.aiEnabled && !params.mlEnabled && !strategyRequiresContext) {
5116
5020
  return false;
5117
5021
  }
@@ -5122,6 +5026,64 @@ var shouldPrepareHyperliquidWhaleContextForRun = (params) => {
5122
5026
  if (strategyRequiresContext) return true;
5123
5027
  return ["1", "true", "yes", "on"].includes(configured);
5124
5028
  };
5029
+ var resolveCoreContextSources = async (params) => {
5030
+ const strategyNames = [...new Set(params.strategyNames ?? [])];
5031
+ if (!strategyNames.length) return [];
5032
+ await (0, import_registry.ensureStrategyPluginsLoaded)(params.projectRoot);
5033
+ return [
5034
+ ...new Set(
5035
+ strategyNames.flatMap(
5036
+ (strategyName) => (0, import_registry.getStrategyManifest)(strategyName, params.projectRoot)?.contextRequirements?.core ?? []
5037
+ )
5038
+ )
5039
+ ];
5040
+ };
5041
+ var resolveMarketContextRunRequirements = async (params) => {
5042
+ const coreContextSources = await resolveCoreContextSources(params);
5043
+ const paramsWithCoreDemand = (source) => coreContextSources.includes(source) ? { ...params, aiEnabled: true } : params;
5044
+ const isCrypto = params.universe !== "tradfi";
5045
+ const capturesPayload = Boolean(params.aiEnabled || params.mlEnabled);
5046
+ const runtimeReadsContext = params.mode !== "backtest";
5047
+ const reasonsFor = (source) => uniqueReasons([
5048
+ ...coreContextSources.includes(source) ? ["core"] : [],
5049
+ ...params.aiEnabled ? ["ai"] : [],
5050
+ ...params.mlEnabled ? ["ml"] : [],
5051
+ ...runtimeReadsContext ? ["runtime"] : []
5052
+ ]);
5053
+ const readsStandardContext = isCrypto && (capturesPayload || runtimeReadsContext);
5054
+ const readsHyperliquid = isCrypto && (readsStandardContext || coreContextSources.includes("hyperliquidWhales"));
5055
+ return {
5056
+ binance: {
5057
+ read: readsStandardContext || coreContextSources.includes("binance"),
5058
+ backfill: shouldPrepareBinanceMarketContextForRun(
5059
+ paramsWithCoreDemand("binance")
5060
+ ),
5061
+ requiredBy: reasonsFor("binance")
5062
+ },
5063
+ coinmarketcap: {
5064
+ read: readsStandardContext || coreContextSources.includes("coinmarketcap"),
5065
+ backfill: shouldPrepareCoinMarketCapContextForRun(
5066
+ paramsWithCoreDemand("coinmarketcap")
5067
+ ),
5068
+ requiredBy: reasonsFor("coinmarketcap")
5069
+ },
5070
+ derivatives: {
5071
+ read: readsStandardContext || coreContextSources.includes("derivatives"),
5072
+ backfill: shouldPrepareDerivativesContextForRun(
5073
+ paramsWithCoreDemand("derivatives")
5074
+ ),
5075
+ requiredBy: reasonsFor("derivatives")
5076
+ },
5077
+ hyperliquidWhales: {
5078
+ read: readsHyperliquid,
5079
+ backfill: shouldPrepareHyperliquidWhaleContextForRun(
5080
+ params,
5081
+ coreContextSources
5082
+ ),
5083
+ requiredBy: reasonsFor("hyperliquidWhales")
5084
+ }
5085
+ };
5086
+ };
5125
5087
  var resolveCoinMarketCapBackfillForMode = (mode) => mode === "backtest" ? backfillCoinMarketCapContextForBacktest : mode === "signals" ? backfillCoinMarketCapContextForSignals : backfillCoinMarketCapContextForReplay;
5126
5088
  var buildCoinMarketCapBackfillParams = (params) => ({
5127
5089
  userName: params.userName,
@@ -5150,7 +5112,15 @@ var prepareMarketContextForRun = async (params) => {
5150
5112
  console.log(import_chalk4.default.gray(message));
5151
5113
  });
5152
5114
  const timeOperation3 = (label, operation) => timeOperation(label, operation, log);
5153
- if (shouldPrepareDerivativesContextForRun(params)) {
5115
+ const requirements = await resolveMarketContextRunRequirements(params);
5116
+ const readableSources = Object.entries(requirements).filter(([, requirement]) => requirement.read).map(([source]) => source);
5117
+ if (readableSources.length) {
5118
+ await timeOperation3(
5119
+ "market context schema ensure",
5120
+ () => (0, import_timescale5.ensureMarketContextSchemas)(readableSources)
5121
+ );
5122
+ }
5123
+ if (requirements.derivatives.backfill) {
5154
5124
  await timeOperation3(
5155
5125
  "derivatives context backfill",
5156
5126
  () => (params.mode === "signals" ? backfillDerivativesContextForSignals : backfillDerivativesContextForBacktest)(
@@ -5158,7 +5128,7 @@ var prepareMarketContextForRun = async (params) => {
5158
5128
  )
5159
5129
  );
5160
5130
  }
5161
- if (shouldPrepareHyperliquidWhaleContextForRun(params)) {
5131
+ if (requirements.hyperliquidWhales.backfill) {
5162
5132
  await timeOperation3(
5163
5133
  "hyperliquid whale context backfill",
5164
5134
  () => backfillHyperliquidWhaleContext({
@@ -5170,13 +5140,13 @@ var prepareMarketContextForRun = async (params) => {
5170
5140
  })
5171
5141
  );
5172
5142
  }
5173
- if (shouldPrepareBinanceMarketContextForRun(params)) {
5143
+ if (requirements.binance.backfill) {
5174
5144
  await timeOperation3("binance market context backfill", () => {
5175
5145
  const backfill = params.mode === "backtest" ? backfillBinanceMarketContextForBacktest : params.mode === "signals" ? backfillBinanceMarketContextForSignals : backfillBinanceMarketContextForReplay;
5176
5146
  return backfill(buildBinanceMarketBackfillParams(params));
5177
5147
  });
5178
5148
  }
5179
- if (shouldPrepareCoinMarketCapContextForRun(params)) {
5149
+ if (requirements.coinmarketcap.backfill) {
5180
5150
  await timeOperation3(
5181
5151
  "coinmarketcap historical context backfill",
5182
5152
  () => resolveCoinMarketCapBackfillForMode(params.mode)(
@@ -5184,6 +5154,7 @@ var prepareMarketContextForRun = async (params) => {
5184
5154
  )
5185
5155
  );
5186
5156
  }
5157
+ return requirements;
5187
5158
  };
5188
5159
 
5189
5160
  // src/lib/marketData/historyPrepare.ts
@@ -5347,7 +5318,7 @@ var import_strategies3 = require("@tradejs/node/strategies");
5347
5318
 
5348
5319
  // src/lib/runtimeRedis.ts
5349
5320
  var import_redis = require("@tradejs/infra/redis");
5350
- var import_time2 = require("@tradejs/core/time");
5321
+ var import_time = require("@tradejs/core/time");
5351
5322
  var RESERVED_STRATEGY_NAMES = /* @__PURE__ */ new Set(["charts"]);
5352
5323
  var RESERVED_CONFIG_IDS = /* @__PURE__ */ new Set(["results"]);
5353
5324
  var resolveStrategyConfigIdentityByKey = (userName, key) => {
@@ -5491,6 +5462,111 @@ var loadRuntimeStrategies = async ({
5491
5462
  // src/lib/signals/skipStats.ts
5492
5463
  var import_chalk6 = __toESM(require("chalk"));
5493
5464
  var import_logger2 = require("@tradejs/infra/logger");
5465
+
5466
+ // src/lib/runtimeSignalsStorage.ts
5467
+ var import_constants2 = require("@tradejs/core/constants");
5468
+ var import_time2 = require("@tradejs/core/time");
5469
+ var SECONDS_PER_DAY = 86400;
5470
+ var RUNTIME_SIGNAL_RETENTION_DAYS_ENV = "RUNTIME_SIGNAL_RETENTION_DAYS";
5471
+ var DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS = import_constants2.TTL_3D;
5472
+ var RUNTIME_LINEAGE_SCOPE_RETENTION_TTL_SECONDS = import_constants2.TTL_1M;
5473
+ var parsePositiveNumber = (value) => {
5474
+ if (value == null || value === "") {
5475
+ return null;
5476
+ }
5477
+ const parsed = Number(value);
5478
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
5479
+ };
5480
+ var getRuntimeSignalRetentionTtlSeconds = () => {
5481
+ const explicitDays = parsePositiveNumber(
5482
+ process.env[RUNTIME_SIGNAL_RETENTION_DAYS_ENV]
5483
+ );
5484
+ if (explicitDays != null) {
5485
+ return Math.ceil(explicitDays * SECONDS_PER_DAY);
5486
+ }
5487
+ return DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS;
5488
+ };
5489
+ var toRuntimeSignalBucketRef = (signal) => ({
5490
+ signalId: signal.signalId,
5491
+ symbol: signal.symbol,
5492
+ strategy: signal.strategy,
5493
+ timestamp: signal.timestamp,
5494
+ ...signal.runtimeConfigId && signal.runtimeConfigId !== "config" ? { runtimeConfigId: signal.runtimeConfigId } : {}
5495
+ });
5496
+ var shouldStoreRuntimeSignalDiagnostics = (signal) => signal.orderStatus === "completed" || signal.orderStatus === "failed";
5497
+ var toStoredRuntimeSignal = (signal) => {
5498
+ if (shouldStoreRuntimeSignalDiagnostics(signal)) {
5499
+ return signal;
5500
+ }
5501
+ const stored = { ...signal };
5502
+ delete stored.figures;
5503
+ delete stored.indicators;
5504
+ delete stored.additionalIndicators;
5505
+ return stored;
5506
+ };
5507
+ var normalizeRuntimeSignalSkipReason = (reason, fallbackSource) => {
5508
+ let source = fallbackSource;
5509
+ let normalizedReason = reason;
5510
+ if (reason.startsWith("AI_QUALITY_BELOW_MIN")) {
5511
+ source = "AI";
5512
+ normalizedReason = "MIN_AI_QUALITY";
5513
+ } else if (reason === "AI_QUALITY_UNAVAILABLE") {
5514
+ source = "AI";
5515
+ normalizedReason = "QUALITY_UNAVAILABLE";
5516
+ } else if (reason === "ML_RESULT_UNAVAILABLE") {
5517
+ source = "ML";
5518
+ normalizedReason = "RESULT_UNAVAILABLE";
5519
+ } else if (reason.startsWith("ML_THRESHOLD_NOT_MET")) {
5520
+ source = "ML";
5521
+ normalizedReason = "ML_THRESHOLD";
5522
+ } else if (reason.startsWith("HOOK_BEFORE_ENTRY_GATE:")) {
5523
+ source = "hook";
5524
+ normalizedReason = `BEFORE_ENTRY_GATE:${reason.slice(
5525
+ "HOOK_BEFORE_ENTRY_GATE:".length
5526
+ )}`;
5527
+ } else if (reason === "HOOK_BEFORE_ENTRY_GATE") {
5528
+ source = "hook";
5529
+ normalizedReason = "BEFORE_ENTRY_GATE";
5530
+ } else if (reason === "MAKE_ORDERS_DISABLED" || reason === "ENTRY_POLICY_BLOCKED") {
5531
+ source = "policy";
5532
+ }
5533
+ return {
5534
+ source: `skip from ${source}`,
5535
+ reason: normalizedReason
5536
+ };
5537
+ };
5538
+ var buildStatsReasonField = (source, reason) => `reason:${source}:${reason}`;
5539
+ var buildRuntimeSignalStatsIncrements = (evaluation) => {
5540
+ const increments = {
5541
+ evaluated: 1
5542
+ };
5543
+ if (evaluation.status === "signal") {
5544
+ increments.signals = 1;
5545
+ }
5546
+ let skipReason = null;
5547
+ let fallbackSource = "core";
5548
+ if (evaluation.status === "skip") {
5549
+ skipReason = evaluation.reason || "NO_SIGNAL";
5550
+ fallbackSource = "core";
5551
+ } else if (evaluation.status === "signal" && (evaluation.orderStatus === "skipped" || evaluation.orderStatus === "canceled")) {
5552
+ skipReason = evaluation.orderSkipReason || evaluation.reason || `orderStatus=${evaluation.orderStatus}`;
5553
+ fallbackSource = "runtime";
5554
+ } else if (evaluation.status === "error") {
5555
+ skipReason = evaluation.reason || "EVALUATION_ERROR";
5556
+ fallbackSource = "runtime";
5557
+ }
5558
+ if (skipReason) {
5559
+ const normalized = normalizeRuntimeSignalSkipReason(
5560
+ skipReason,
5561
+ fallbackSource
5562
+ );
5563
+ increments[buildStatsReasonField(normalized.source, normalized.reason)] = 1;
5564
+ }
5565
+ return increments;
5566
+ };
5567
+ var shouldStoreDetailedRuntimeSignalEvaluation = (evaluation) => evaluation.status !== "skip";
5568
+
5569
+ // src/lib/signals/skipStats.ts
5494
5570
  var createStrategySkipStats = (runtimeStrategies) => new Map(
5495
5571
  runtimeStrategies.map(({ strategyName }) => [
5496
5572
  strategyName,
@@ -5818,7 +5894,7 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
5818
5894
  if (evaluations.length === 0) return;
5819
5895
  const evaluationsByDay = /* @__PURE__ */ new Map();
5820
5896
  for (const evaluation of evaluations) {
5821
- const dayKey = (0, import_time.getRuntimeStorageDayKey)(evaluation.timestamp);
5897
+ const dayKey = (0, import_time2.getRuntimeStorageDayKey)(evaluation.timestamp);
5822
5898
  const dayEvaluations = evaluationsByDay.get(dayKey) ?? [];
5823
5899
  dayEvaluations.push(evaluation);
5824
5900
  evaluationsByDay.set(dayKey, dayEvaluations);
@@ -5926,13 +6002,47 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
5926
6002
  };
5927
6003
  var createRuntimeSignalEvaluationBuffer = () => {
5928
6004
  const evaluations = [];
6005
+ const signals2 = [];
5929
6006
  return {
5930
6007
  save: async (evaluation) => {
5931
6008
  evaluations.push(evaluation);
5932
6009
  },
6010
+ saveSignal: (userName, signal) => {
6011
+ signals2.push({ userName, signal });
6012
+ },
5933
6013
  flush: async () => {
5934
6014
  const pending = evaluations.splice(0);
5935
- await flushRuntimeSignalEvaluations(pending);
6015
+ const pendingSignals = signals2.splice(0);
6016
+ const runtimeSignalRetentionTtl = getRuntimeSignalRetentionTtlSeconds();
6017
+ const signalRefsByBucket = /* @__PURE__ */ new Map();
6018
+ for (const { userName, signal } of pendingSignals) {
6019
+ const bucket = import_redis3.redisKeys.runtimeSignalBucket(
6020
+ userName,
6021
+ (0, import_time2.getRuntimeStorageDayKey)(signal.timestamp),
6022
+ signal.strategy
6023
+ );
6024
+ const entries = signalRefsByBucket.get(bucket) ?? [];
6025
+ entries.push({
6026
+ field: signal.signalId,
6027
+ data: toRuntimeSignalBucketRef(signal)
6028
+ });
6029
+ signalRefsByBucket.set(bucket, entries);
6030
+ }
6031
+ await Promise.all([
6032
+ flushRuntimeSignalEvaluations(pending),
6033
+ ...pendingSignals.map(
6034
+ ({ signal }) => (0, import_redis3.setData)(
6035
+ import_redis3.redisKeys.storeSignal(signal.symbol, signal.signalId),
6036
+ toStoredRuntimeSignal(signal),
6037
+ { expire: runtimeSignalRetentionTtl }
6038
+ )
6039
+ ),
6040
+ ...[...signalRefsByBucket].map(
6041
+ ([bucket, entries]) => (0, import_redis3.setHashJsonFields)(bucket, entries, {
6042
+ expire: runtimeSignalRetentionTtl
6043
+ })
6044
+ )
6045
+ ]);
5936
6046
  }
5937
6047
  };
5938
6048
  };
@@ -6165,7 +6275,7 @@ var runSignalsDaemon = async ({
6165
6275
  // src/lib/signals/klineFeed.ts
6166
6276
  var import_connectors5 = require("@tradejs/connectors");
6167
6277
  var import_logger3 = require("@tradejs/infra/logger");
6168
- var import_timescale5 = require("@tradejs/infra/timescale");
6278
+ var import_timescale6 = require("@tradejs/infra/timescale");
6169
6279
 
6170
6280
  // src/lib/marketData/klineEvents.ts
6171
6281
  var import_ioredis = __toESM(require("ioredis"));
@@ -6185,9 +6295,9 @@ var createSignalsKlineFeed = async ({
6185
6295
  publish = publishMarketKlineEvent,
6186
6296
  persist = async (rows) => {
6187
6297
  const intervalMinutes = Number(interval2);
6188
- await (0, import_timescale5.upsertCandles)(
6298
+ await (0, import_timescale6.upsertCandles)(
6189
6299
  rows.flatMap(
6190
- ({ symbol, candle }) => (0, import_timescale5.toRows)("bybit", symbol, intervalMinutes, [candle])
6300
+ ({ symbol, candle }) => (0, import_timescale6.toRows)("bybit", symbol, intervalMinutes, [candle])
6191
6301
  )
6192
6302
  );
6193
6303
  },
@@ -6453,7 +6563,7 @@ var createSignalsSession = async (lifecycle, scope = {}) => {
6453
6563
  lifecycle
6454
6564
  };
6455
6565
  };
6456
- var findSignals = async (symbol, connectorName, connector, btcBinanceData, btcCoinbaseData, primaryBtcClosedData, primaryEthClosedData, primaryEthByTimestamp, runtimeStrategies, strategyStats, runtimeCloseNotifications, lifecycle, preloadStart, currentTimestamp, persistStrategyState, universe, interval2, intervalMs2, accountId, deploymentId, instrument, runtimeScopeUniverse, saveRuntimeSignalEvaluation) => {
6566
+ var findSignals = async (symbol, connectorName, connector, btcBinanceData, btcCoinbaseData, primaryBtcClosedData, primaryEthClosedData, primaryEthByTimestamp, runtimeStrategies, strategyStats, runtimeCloseNotifications, lifecycle, preloadStart, currentTimestamp, persistStrategyState, universe, interval2, intervalMs2, accountId, deploymentId, instrument, runtimeScopeUniverse, saveRuntimeSignalEvaluation, saveRuntimeSignal) => {
6457
6567
  const strategySignals = [];
6458
6568
  const loadCached = (cachedSymbol) => connector.kline({
6459
6569
  symbol: cachedSymbol,
@@ -6646,26 +6756,7 @@ var findSignals = async (symbol, connectorName, connector, btcBinanceData, btcCo
6646
6756
  );
6647
6757
  }
6648
6758
  strategySignals.push(signal);
6649
- const runtimeSignalRetentionTtl = getRuntimeSignalRetentionTtlSeconds();
6650
- await (0, import_redis5.setData)(
6651
- import_redis5.redisKeys.storeSignal(symbol, signal.signalId),
6652
- toStoredRuntimeSignal(signal),
6653
- {
6654
- expire: runtimeSignalRetentionTtl
6655
- }
6656
- );
6657
- await (0, import_redis5.setHashJsonField)(
6658
- import_redis5.redisKeys.runtimeSignalBucket(
6659
- flags.user,
6660
- (0, import_time.getRuntimeStorageDayKey)(signal.timestamp),
6661
- signal.strategy
6662
- ),
6663
- signal.signalId,
6664
- toRuntimeSignalBucketRef(signal),
6665
- {
6666
- expire: runtimeSignalRetentionTtl
6667
- }
6668
- );
6759
+ saveRuntimeSignal?.(flags.user, signal);
6669
6760
  await saveRuntimeSignalEvaluation?.({
6670
6761
  evaluationId: buildRuntimeSignalEvaluationId({
6671
6762
  strategyName,
@@ -6971,7 +7062,8 @@ var signals = async (options = {}) => {
6971
7062
  deployment?.id,
6972
7063
  instrumentsBySymbol.get(symbol),
6973
7064
  sessionUniverse,
6974
- runtimeSignalEvaluations.save
7065
+ runtimeSignalEvaluations.save,
7066
+ runtimeSignalEvaluations.saveSignal
6975
7067
  );
6976
7068
  if (strategySignals.length > 0) {
6977
7069
  signals2.push(...strategySignals);
@@ -370,6 +370,15 @@ var syncRuntimeTrades = async ({
370
370
  nextTrade,
371
371
  { expire: import_constants.TTL_1M }
372
372
  ),
373
+ (0, import_redis2.setHashJsonField)(
374
+ import_redis2.redisKeys.runtimeClosedTradeBucket(
375
+ userName,
376
+ (0, import_time2.getRuntimeStorageDayKey)(nextTrade.exitTimestamp)
377
+ ),
378
+ trade.orderId,
379
+ nextTrade,
380
+ { expire: import_constants.TTL_1M }
381
+ ),
373
382
  ...isCurrentActiveTrade ? [(0, import_redis2.delKey)(activeTradeKey)] : []
374
383
  ]);
375
384
  syncedTrades.push(nextTrade);
@@ -8,7 +8,9 @@ var import_ai = require("@tradejs/infra/ai");
8
8
  var import_logger = require("@tradejs/infra/logger");
9
9
  var import_ml = require("@tradejs/infra/ml");
10
10
  var import_redis = require("@tradejs/infra/redis");
11
+ var import_timescale = require("@tradejs/infra/timescale");
11
12
  var isProcessing = false;
13
+ (0, import_timescale.configureTimescaleMarketContextSchemaMode)("verify");
12
14
  var resolveTestSuite = async ({
13
15
  chunk,
14
16
  chunkId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tradejs/cli",
3
- "version": "2.0.14",
3
+ "version": "2.0.16",
4
4
  "description": "Official CLI for the TradeJS TypeScript framework: infra setup, backtests, signals, bots, and ML workflows.",
5
5
  "keywords": [
6
6
  "tradejs",
@@ -29,14 +29,14 @@
29
29
  "tradejs": "dist/cli.js"
30
30
  },
31
31
  "dependencies": {
32
- "@tradejs/base": "^2.0.14",
33
- "@tradejs/connectors": "^2.0.14",
34
- "@tradejs/core": "^2.0.14",
35
- "@tradejs/indicators": "^2.0.14",
36
- "@tradejs/infra": "^2.0.14",
37
- "@tradejs/node": "^2.0.14",
38
- "@tradejs/strategies": "^2.0.14",
39
- "@tradejs/types": "^2.0.14",
32
+ "@tradejs/base": "^2.0.16",
33
+ "@tradejs/connectors": "^2.0.16",
34
+ "@tradejs/core": "^2.0.16",
35
+ "@tradejs/indicators": "^2.0.16",
36
+ "@tradejs/infra": "^2.0.16",
37
+ "@tradejs/node": "^2.0.16",
38
+ "@tradejs/strategies": "^2.0.16",
39
+ "@tradejs/types": "^2.0.16",
40
40
  "args": "^5.0.3",
41
41
  "bcryptjs": "^2.4.3",
42
42
  "chalk": "4.1.2",
@@ -77,6 +77,8 @@
77
77
  "results": "yarn run run:dist results",
78
78
  "replay-runtime-evidence": "yarn run run:dist replay-runtime-evidence",
79
79
  "runtime-evidence": "yarn run run:dist runtime-evidence",
80
+ "runtime-evidence-sync": "yarn run run:dist runtime-evidence-sync",
81
+ "runtime-scorecard": "yarn run run:dist runtime-scorecard",
80
82
  "continuity": "yarn run run:dist continuity",
81
83
  "derivatives:ingest": "yarn run run:dist derivatives:ingest",
82
84
  "derivatives:ingest:coinalyze:all": "yarn run run:dist derivatives:ingest:coinalyze:all",