@tradejs/cli 2.0.15 → 2.0.17

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.
@@ -1643,7 +1643,7 @@ module.exports = __toCommonJS(signalsDaemon_exports);
1643
1643
  // src/scripts/signals.ts
1644
1644
  var import_config = require("dotenv/config");
1645
1645
  var import_args = __toESM(require("args"));
1646
- var import_progress3 = __toESM(require("progress"));
1646
+ var import_progress4 = __toESM(require("progress"));
1647
1647
  var import_chalk7 = __toESM(require("chalk"));
1648
1648
  var import_connectors6 = require("@tradejs/node/connectors");
1649
1649
  var import_cli2 = require("@tradejs/node/cli");
@@ -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;
@@ -4653,6 +4551,7 @@ var backfillCoinMarketCapContextForReplay = backfillCoinMarketCapContext;
4653
4551
  var backfillCoinMarketCapContextForSignals = (params) => backfillCoinMarketCapContext({ ...params, refreshStaleFearGreed: true });
4654
4552
 
4655
4553
  // src/lib/hyperliquidWhaleBackfill.ts
4554
+ var import_progress3 = __toESM(require("progress"));
4656
4555
  var import_timescale4 = require("@tradejs/infra/timescale");
4657
4556
  var import_strategies2 = require("@tradejs/node/strategies");
4658
4557
 
@@ -5027,11 +4926,36 @@ var backfillHyperliquidWhaleContext = async (params) => {
5027
4926
  toMs,
5028
4927
  ...identity
5029
4928
  });
4929
+ const totalCoverageBuckets = Math.ceil(
4930
+ (toMs - fromMs) / HYPERLIQUID_WHALE_BUCKET_MS
4931
+ );
4932
+ const coverageBar = new import_progress3.default(
4933
+ "Hyperliquid coverage :current/:total [:bar][:percent] :eta(s) rows=:rows chunk=:chunk",
4934
+ {
4935
+ total: Math.max(1, totalCoverageBuckets),
4936
+ width: 24
4937
+ }
4938
+ );
4939
+ let displayedCoverageBuckets = 0;
5030
4940
  const coverageBuckets = await (0, import_timescale4.rebuildHyperliquidWhaleCoverageRows)({
5031
4941
  fromMs,
5032
4942
  toMs,
5033
4943
  expectedWhales: whales.addresses.length,
5034
- ...identity
4944
+ ...identity,
4945
+ onProgress: (progress) => {
4946
+ const next = Math.max(
4947
+ displayedCoverageBuckets,
4948
+ Math.min(progress.completedBuckets, coverageBar.total)
4949
+ );
4950
+ const delta = next - displayedCoverageBuckets;
4951
+ if (delta > 0) {
4952
+ coverageBar.tick(delta, {
4953
+ rows: progress.rows,
4954
+ chunk: `${progress.chunkIndex}/${progress.totalChunks}`
4955
+ });
4956
+ displayedCoverageBuckets = next;
4957
+ }
4958
+ }
5035
4959
  });
5036
4960
  const result = {
5037
4961
  cached: false,
@@ -5059,6 +4983,9 @@ var backfillHyperliquidWhaleContext = async (params) => {
5059
4983
  };
5060
4984
 
5061
4985
  // src/lib/marketContextPrepare.ts
4986
+ var uniqueReasons = (reasons) => [
4987
+ ...new Set(reasons)
4988
+ ];
5062
4989
  var shouldPrepareDerivativesContextForRun = (params) => {
5063
4990
  if (params.universe === "tradfi") return false;
5064
4991
  if (params.mode === "signals") {
@@ -5066,6 +4993,9 @@ var shouldPrepareDerivativesContextForRun = (params) => {
5066
4993
  cacheOnly: params.cacheOnly
5067
4994
  });
5068
4995
  }
4996
+ if (params.mode === "backtest" && !params.aiEnabled && !params.mlEnabled) {
4997
+ return false;
4998
+ }
5069
4999
  return shouldBackfillDerivativesContextForBacktest({
5070
5000
  aiEnabled: Boolean(params.aiEnabled),
5071
5001
  cacheOnly: params.cacheOnly,
@@ -5075,6 +5005,7 @@ var shouldPrepareDerivativesContextForRun = (params) => {
5075
5005
  var shouldPrepareBinanceMarketContextForRun = (params) => {
5076
5006
  if (params.universe === "tradfi") return false;
5077
5007
  if (params.mode === "backtest") {
5008
+ if (!params.aiEnabled && !params.mlEnabled) return false;
5078
5009
  return shouldBackfillBinanceMarketContextForBacktest({
5079
5010
  aiEnabled: Boolean(params.aiEnabled),
5080
5011
  cacheOnly: params.cacheOnly,
@@ -5093,6 +5024,7 @@ var shouldPrepareBinanceMarketContextForRun = (params) => {
5093
5024
  var shouldPrepareCoinMarketCapContextForRun = (params) => {
5094
5025
  if (params.universe === "tradfi") return false;
5095
5026
  if (params.mode === "backtest") {
5027
+ if (!params.aiEnabled && !params.mlEnabled) return false;
5096
5028
  return shouldBackfillCoinMarketCapContextForBacktest({
5097
5029
  aiEnabled: Boolean(params.aiEnabled),
5098
5030
  cacheOnly: params.cacheOnly,
@@ -5108,10 +5040,8 @@ var shouldPrepareCoinMarketCapContextForRun = (params) => {
5108
5040
  cacheOnly: params.cacheOnly
5109
5041
  });
5110
5042
  };
5111
- var shouldPrepareHyperliquidWhaleContextForRun = (params) => {
5112
- const strategyRequiresContext = params.strategyNames?.includes(
5113
- "HyperliquidConsensus"
5114
- );
5043
+ var shouldPrepareHyperliquidWhaleContextForRun = (params, coreContextSources = []) => {
5044
+ const strategyRequiresContext = coreContextSources.includes("hyperliquidWhales");
5115
5045
  if (params.universe === "tradfi" || params.mode === "signals" || params.cacheOnly || !params.aiEnabled && !params.mlEnabled && !strategyRequiresContext) {
5116
5046
  return false;
5117
5047
  }
@@ -5122,6 +5052,64 @@ var shouldPrepareHyperliquidWhaleContextForRun = (params) => {
5122
5052
  if (strategyRequiresContext) return true;
5123
5053
  return ["1", "true", "yes", "on"].includes(configured);
5124
5054
  };
5055
+ var resolveCoreContextSources = async (params) => {
5056
+ const strategyNames = [...new Set(params.strategyNames ?? [])];
5057
+ if (!strategyNames.length) return [];
5058
+ await (0, import_registry.ensureStrategyPluginsLoaded)(params.projectRoot);
5059
+ return [
5060
+ ...new Set(
5061
+ strategyNames.flatMap(
5062
+ (strategyName) => (0, import_registry.getStrategyManifest)(strategyName, params.projectRoot)?.contextRequirements?.core ?? []
5063
+ )
5064
+ )
5065
+ ];
5066
+ };
5067
+ var resolveMarketContextRunRequirements = async (params) => {
5068
+ const coreContextSources = await resolveCoreContextSources(params);
5069
+ const paramsWithCoreDemand = (source) => coreContextSources.includes(source) ? { ...params, aiEnabled: true } : params;
5070
+ const isCrypto = params.universe !== "tradfi";
5071
+ const capturesPayload = Boolean(params.aiEnabled || params.mlEnabled);
5072
+ const runtimeReadsContext = params.mode !== "backtest";
5073
+ const reasonsFor = (source) => uniqueReasons([
5074
+ ...coreContextSources.includes(source) ? ["core"] : [],
5075
+ ...params.aiEnabled ? ["ai"] : [],
5076
+ ...params.mlEnabled ? ["ml"] : [],
5077
+ ...runtimeReadsContext ? ["runtime"] : []
5078
+ ]);
5079
+ const readsStandardContext = isCrypto && (capturesPayload || runtimeReadsContext);
5080
+ const readsHyperliquid = isCrypto && (readsStandardContext || coreContextSources.includes("hyperliquidWhales"));
5081
+ return {
5082
+ binance: {
5083
+ read: readsStandardContext || coreContextSources.includes("binance"),
5084
+ backfill: shouldPrepareBinanceMarketContextForRun(
5085
+ paramsWithCoreDemand("binance")
5086
+ ),
5087
+ requiredBy: reasonsFor("binance")
5088
+ },
5089
+ coinmarketcap: {
5090
+ read: readsStandardContext || coreContextSources.includes("coinmarketcap"),
5091
+ backfill: shouldPrepareCoinMarketCapContextForRun(
5092
+ paramsWithCoreDemand("coinmarketcap")
5093
+ ),
5094
+ requiredBy: reasonsFor("coinmarketcap")
5095
+ },
5096
+ derivatives: {
5097
+ read: readsStandardContext || coreContextSources.includes("derivatives"),
5098
+ backfill: shouldPrepareDerivativesContextForRun(
5099
+ paramsWithCoreDemand("derivatives")
5100
+ ),
5101
+ requiredBy: reasonsFor("derivatives")
5102
+ },
5103
+ hyperliquidWhales: {
5104
+ read: readsHyperliquid,
5105
+ backfill: shouldPrepareHyperliquidWhaleContextForRun(
5106
+ params,
5107
+ coreContextSources
5108
+ ),
5109
+ requiredBy: reasonsFor("hyperliquidWhales")
5110
+ }
5111
+ };
5112
+ };
5125
5113
  var resolveCoinMarketCapBackfillForMode = (mode) => mode === "backtest" ? backfillCoinMarketCapContextForBacktest : mode === "signals" ? backfillCoinMarketCapContextForSignals : backfillCoinMarketCapContextForReplay;
5126
5114
  var buildCoinMarketCapBackfillParams = (params) => ({
5127
5115
  userName: params.userName,
@@ -5150,7 +5138,15 @@ var prepareMarketContextForRun = async (params) => {
5150
5138
  console.log(import_chalk4.default.gray(message));
5151
5139
  });
5152
5140
  const timeOperation3 = (label, operation) => timeOperation(label, operation, log);
5153
- if (shouldPrepareDerivativesContextForRun(params)) {
5141
+ const requirements = await resolveMarketContextRunRequirements(params);
5142
+ const readableSources = Object.entries(requirements).filter(([, requirement]) => requirement.read).map(([source]) => source);
5143
+ if (readableSources.length) {
5144
+ await timeOperation3(
5145
+ "market context schema ensure",
5146
+ () => (0, import_timescale5.ensureMarketContextSchemas)(readableSources)
5147
+ );
5148
+ }
5149
+ if (requirements.derivatives.backfill) {
5154
5150
  await timeOperation3(
5155
5151
  "derivatives context backfill",
5156
5152
  () => (params.mode === "signals" ? backfillDerivativesContextForSignals : backfillDerivativesContextForBacktest)(
@@ -5158,7 +5154,7 @@ var prepareMarketContextForRun = async (params) => {
5158
5154
  )
5159
5155
  );
5160
5156
  }
5161
- if (shouldPrepareHyperliquidWhaleContextForRun(params)) {
5157
+ if (requirements.hyperliquidWhales.backfill) {
5162
5158
  await timeOperation3(
5163
5159
  "hyperliquid whale context backfill",
5164
5160
  () => backfillHyperliquidWhaleContext({
@@ -5170,13 +5166,13 @@ var prepareMarketContextForRun = async (params) => {
5170
5166
  })
5171
5167
  );
5172
5168
  }
5173
- if (shouldPrepareBinanceMarketContextForRun(params)) {
5169
+ if (requirements.binance.backfill) {
5174
5170
  await timeOperation3("binance market context backfill", () => {
5175
5171
  const backfill = params.mode === "backtest" ? backfillBinanceMarketContextForBacktest : params.mode === "signals" ? backfillBinanceMarketContextForSignals : backfillBinanceMarketContextForReplay;
5176
5172
  return backfill(buildBinanceMarketBackfillParams(params));
5177
5173
  });
5178
5174
  }
5179
- if (shouldPrepareCoinMarketCapContextForRun(params)) {
5175
+ if (requirements.coinmarketcap.backfill) {
5180
5176
  await timeOperation3(
5181
5177
  "coinmarketcap historical context backfill",
5182
5178
  () => resolveCoinMarketCapBackfillForMode(params.mode)(
@@ -5184,6 +5180,7 @@ var prepareMarketContextForRun = async (params) => {
5184
5180
  )
5185
5181
  );
5186
5182
  }
5183
+ return requirements;
5187
5184
  };
5188
5185
 
5189
5186
  // src/lib/marketData/historyPrepare.ts
@@ -5347,7 +5344,7 @@ var import_strategies3 = require("@tradejs/node/strategies");
5347
5344
 
5348
5345
  // src/lib/runtimeRedis.ts
5349
5346
  var import_redis = require("@tradejs/infra/redis");
5350
- var import_time2 = require("@tradejs/core/time");
5347
+ var import_time = require("@tradejs/core/time");
5351
5348
  var RESERVED_STRATEGY_NAMES = /* @__PURE__ */ new Set(["charts"]);
5352
5349
  var RESERVED_CONFIG_IDS = /* @__PURE__ */ new Set(["results"]);
5353
5350
  var resolveStrategyConfigIdentityByKey = (userName, key) => {
@@ -5491,6 +5488,111 @@ var loadRuntimeStrategies = async ({
5491
5488
  // src/lib/signals/skipStats.ts
5492
5489
  var import_chalk6 = __toESM(require("chalk"));
5493
5490
  var import_logger2 = require("@tradejs/infra/logger");
5491
+
5492
+ // src/lib/runtimeSignalsStorage.ts
5493
+ var import_constants2 = require("@tradejs/core/constants");
5494
+ var import_time2 = require("@tradejs/core/time");
5495
+ var SECONDS_PER_DAY = 86400;
5496
+ var RUNTIME_SIGNAL_RETENTION_DAYS_ENV = "RUNTIME_SIGNAL_RETENTION_DAYS";
5497
+ var DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS = import_constants2.TTL_3D;
5498
+ var RUNTIME_LINEAGE_SCOPE_RETENTION_TTL_SECONDS = import_constants2.TTL_1M;
5499
+ var parsePositiveNumber = (value) => {
5500
+ if (value == null || value === "") {
5501
+ return null;
5502
+ }
5503
+ const parsed = Number(value);
5504
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
5505
+ };
5506
+ var getRuntimeSignalRetentionTtlSeconds = () => {
5507
+ const explicitDays = parsePositiveNumber(
5508
+ process.env[RUNTIME_SIGNAL_RETENTION_DAYS_ENV]
5509
+ );
5510
+ if (explicitDays != null) {
5511
+ return Math.ceil(explicitDays * SECONDS_PER_DAY);
5512
+ }
5513
+ return DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS;
5514
+ };
5515
+ var toRuntimeSignalBucketRef = (signal) => ({
5516
+ signalId: signal.signalId,
5517
+ symbol: signal.symbol,
5518
+ strategy: signal.strategy,
5519
+ timestamp: signal.timestamp,
5520
+ ...signal.runtimeConfigId && signal.runtimeConfigId !== "config" ? { runtimeConfigId: signal.runtimeConfigId } : {}
5521
+ });
5522
+ var shouldStoreRuntimeSignalDiagnostics = (signal) => signal.orderStatus === "completed" || signal.orderStatus === "failed";
5523
+ var toStoredRuntimeSignal = (signal) => {
5524
+ if (shouldStoreRuntimeSignalDiagnostics(signal)) {
5525
+ return signal;
5526
+ }
5527
+ const stored = { ...signal };
5528
+ delete stored.figures;
5529
+ delete stored.indicators;
5530
+ delete stored.additionalIndicators;
5531
+ return stored;
5532
+ };
5533
+ var normalizeRuntimeSignalSkipReason = (reason, fallbackSource) => {
5534
+ let source = fallbackSource;
5535
+ let normalizedReason = reason;
5536
+ if (reason.startsWith("AI_QUALITY_BELOW_MIN")) {
5537
+ source = "AI";
5538
+ normalizedReason = "MIN_AI_QUALITY";
5539
+ } else if (reason === "AI_QUALITY_UNAVAILABLE") {
5540
+ source = "AI";
5541
+ normalizedReason = "QUALITY_UNAVAILABLE";
5542
+ } else if (reason === "ML_RESULT_UNAVAILABLE") {
5543
+ source = "ML";
5544
+ normalizedReason = "RESULT_UNAVAILABLE";
5545
+ } else if (reason.startsWith("ML_THRESHOLD_NOT_MET")) {
5546
+ source = "ML";
5547
+ normalizedReason = "ML_THRESHOLD";
5548
+ } else if (reason.startsWith("HOOK_BEFORE_ENTRY_GATE:")) {
5549
+ source = "hook";
5550
+ normalizedReason = `BEFORE_ENTRY_GATE:${reason.slice(
5551
+ "HOOK_BEFORE_ENTRY_GATE:".length
5552
+ )}`;
5553
+ } else if (reason === "HOOK_BEFORE_ENTRY_GATE") {
5554
+ source = "hook";
5555
+ normalizedReason = "BEFORE_ENTRY_GATE";
5556
+ } else if (reason === "MAKE_ORDERS_DISABLED" || reason === "ENTRY_POLICY_BLOCKED") {
5557
+ source = "policy";
5558
+ }
5559
+ return {
5560
+ source: `skip from ${source}`,
5561
+ reason: normalizedReason
5562
+ };
5563
+ };
5564
+ var buildStatsReasonField = (source, reason) => `reason:${source}:${reason}`;
5565
+ var buildRuntimeSignalStatsIncrements = (evaluation) => {
5566
+ const increments = {
5567
+ evaluated: 1
5568
+ };
5569
+ if (evaluation.status === "signal") {
5570
+ increments.signals = 1;
5571
+ }
5572
+ let skipReason = null;
5573
+ let fallbackSource = "core";
5574
+ if (evaluation.status === "skip") {
5575
+ skipReason = evaluation.reason || "NO_SIGNAL";
5576
+ fallbackSource = "core";
5577
+ } else if (evaluation.status === "signal" && (evaluation.orderStatus === "skipped" || evaluation.orderStatus === "canceled")) {
5578
+ skipReason = evaluation.orderSkipReason || evaluation.reason || `orderStatus=${evaluation.orderStatus}`;
5579
+ fallbackSource = "runtime";
5580
+ } else if (evaluation.status === "error") {
5581
+ skipReason = evaluation.reason || "EVALUATION_ERROR";
5582
+ fallbackSource = "runtime";
5583
+ }
5584
+ if (skipReason) {
5585
+ const normalized = normalizeRuntimeSignalSkipReason(
5586
+ skipReason,
5587
+ fallbackSource
5588
+ );
5589
+ increments[buildStatsReasonField(normalized.source, normalized.reason)] = 1;
5590
+ }
5591
+ return increments;
5592
+ };
5593
+ var shouldStoreDetailedRuntimeSignalEvaluation = (evaluation) => evaluation.status !== "skip";
5594
+
5595
+ // src/lib/signals/skipStats.ts
5494
5596
  var createStrategySkipStats = (runtimeStrategies) => new Map(
5495
5597
  runtimeStrategies.map(({ strategyName }) => [
5496
5598
  strategyName,
@@ -5818,7 +5920,7 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
5818
5920
  if (evaluations.length === 0) return;
5819
5921
  const evaluationsByDay = /* @__PURE__ */ new Map();
5820
5922
  for (const evaluation of evaluations) {
5821
- const dayKey = (0, import_time.getRuntimeStorageDayKey)(evaluation.timestamp);
5923
+ const dayKey = (0, import_time2.getRuntimeStorageDayKey)(evaluation.timestamp);
5822
5924
  const dayEvaluations = evaluationsByDay.get(dayKey) ?? [];
5823
5925
  dayEvaluations.push(evaluation);
5824
5926
  evaluationsByDay.set(dayKey, dayEvaluations);
@@ -5926,13 +6028,47 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
5926
6028
  };
5927
6029
  var createRuntimeSignalEvaluationBuffer = () => {
5928
6030
  const evaluations = [];
6031
+ const signals2 = [];
5929
6032
  return {
5930
6033
  save: async (evaluation) => {
5931
6034
  evaluations.push(evaluation);
5932
6035
  },
6036
+ saveSignal: (userName, signal) => {
6037
+ signals2.push({ userName, signal });
6038
+ },
5933
6039
  flush: async () => {
5934
6040
  const pending = evaluations.splice(0);
5935
- await flushRuntimeSignalEvaluations(pending);
6041
+ const pendingSignals = signals2.splice(0);
6042
+ const runtimeSignalRetentionTtl = getRuntimeSignalRetentionTtlSeconds();
6043
+ const signalRefsByBucket = /* @__PURE__ */ new Map();
6044
+ for (const { userName, signal } of pendingSignals) {
6045
+ const bucket = import_redis3.redisKeys.runtimeSignalBucket(
6046
+ userName,
6047
+ (0, import_time2.getRuntimeStorageDayKey)(signal.timestamp),
6048
+ signal.strategy
6049
+ );
6050
+ const entries = signalRefsByBucket.get(bucket) ?? [];
6051
+ entries.push({
6052
+ field: signal.signalId,
6053
+ data: toRuntimeSignalBucketRef(signal)
6054
+ });
6055
+ signalRefsByBucket.set(bucket, entries);
6056
+ }
6057
+ await Promise.all([
6058
+ flushRuntimeSignalEvaluations(pending),
6059
+ ...pendingSignals.map(
6060
+ ({ signal }) => (0, import_redis3.setData)(
6061
+ import_redis3.redisKeys.storeSignal(signal.symbol, signal.signalId),
6062
+ toStoredRuntimeSignal(signal),
6063
+ { expire: runtimeSignalRetentionTtl }
6064
+ )
6065
+ ),
6066
+ ...[...signalRefsByBucket].map(
6067
+ ([bucket, entries]) => (0, import_redis3.setHashJsonFields)(bucket, entries, {
6068
+ expire: runtimeSignalRetentionTtl
6069
+ })
6070
+ )
6071
+ ]);
5936
6072
  }
5937
6073
  };
5938
6074
  };
@@ -6165,7 +6301,7 @@ var runSignalsDaemon = async ({
6165
6301
  // src/lib/signals/klineFeed.ts
6166
6302
  var import_connectors5 = require("@tradejs/connectors");
6167
6303
  var import_logger3 = require("@tradejs/infra/logger");
6168
- var import_timescale5 = require("@tradejs/infra/timescale");
6304
+ var import_timescale6 = require("@tradejs/infra/timescale");
6169
6305
 
6170
6306
  // src/lib/marketData/klineEvents.ts
6171
6307
  var import_ioredis = __toESM(require("ioredis"));
@@ -6185,9 +6321,9 @@ var createSignalsKlineFeed = async ({
6185
6321
  publish = publishMarketKlineEvent,
6186
6322
  persist = async (rows) => {
6187
6323
  const intervalMinutes = Number(interval2);
6188
- await (0, import_timescale5.upsertCandles)(
6324
+ await (0, import_timescale6.upsertCandles)(
6189
6325
  rows.flatMap(
6190
- ({ symbol, candle }) => (0, import_timescale5.toRows)("bybit", symbol, intervalMinutes, [candle])
6326
+ ({ symbol, candle }) => (0, import_timescale6.toRows)("bybit", symbol, intervalMinutes, [candle])
6191
6327
  )
6192
6328
  );
6193
6329
  },
@@ -6453,7 +6589,7 @@ var createSignalsSession = async (lifecycle, scope = {}) => {
6453
6589
  lifecycle
6454
6590
  };
6455
6591
  };
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) => {
6592
+ 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
6593
  const strategySignals = [];
6458
6594
  const loadCached = (cachedSymbol) => connector.kline({
6459
6595
  symbol: cachedSymbol,
@@ -6646,26 +6782,7 @@ var findSignals = async (symbol, connectorName, connector, btcBinanceData, btcCo
6646
6782
  );
6647
6783
  }
6648
6784
  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
- );
6785
+ saveRuntimeSignal?.(flags.user, signal);
6669
6786
  await saveRuntimeSignalEvaluation?.({
6670
6787
  evaluationId: buildRuntimeSignalEvaluationId({
6671
6788
  strategyName,
@@ -6936,7 +7053,7 @@ var signals = async (options = {}) => {
6936
7053
  return;
6937
7054
  }
6938
7055
  const strategyStats = createStrategySkipStats(runtimeStrategies);
6939
- const bar = new import_progress3.default(
7056
+ const bar = new import_progress4.default(
6940
7057
  ":current/:total [:bar][:percent] :found :eta(s) :symbol",
6941
7058
  {
6942
7059
  total: tickers.length,
@@ -6971,7 +7088,8 @@ var signals = async (options = {}) => {
6971
7088
  deployment?.id,
6972
7089
  instrumentsBySymbol.get(symbol),
6973
7090
  sessionUniverse,
6974
- runtimeSignalEvaluations.save
7091
+ runtimeSignalEvaluations.save,
7092
+ runtimeSignalEvaluations.saveSignal
6975
7093
  );
6976
7094
  if (strategySignals.length > 0) {
6977
7095
  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.15",
3
+ "version": "2.0.17",
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.15",
33
- "@tradejs/connectors": "^2.0.15",
34
- "@tradejs/core": "^2.0.15",
35
- "@tradejs/indicators": "^2.0.15",
36
- "@tradejs/infra": "^2.0.15",
37
- "@tradejs/node": "^2.0.15",
38
- "@tradejs/strategies": "^2.0.15",
39
- "@tradejs/types": "^2.0.15",
32
+ "@tradejs/base": "^2.0.17",
33
+ "@tradejs/connectors": "^2.0.17",
34
+ "@tradejs/core": "^2.0.17",
35
+ "@tradejs/indicators": "^2.0.17",
36
+ "@tradejs/infra": "^2.0.17",
37
+ "@tradejs/node": "^2.0.17",
38
+ "@tradejs/strategies": "^2.0.17",
39
+ "@tradejs/types": "^2.0.17",
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",