@tradejs/cli 3.1.10 → 3.1.11-beta.216

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.
@@ -499,6 +499,18 @@ var resolveRunConnectorName = async ({
499
499
  );
500
500
  return import_connectors3.DEFAULT_CONNECTOR_NAME;
501
501
  };
502
+ var mergeRuntimeStrategySelections = (strategies) => {
503
+ if (strategies.some((strategy) => !strategy.selection?.tickers)) {
504
+ return void 0;
505
+ }
506
+ return {
507
+ tickers: [
508
+ ...new Set(
509
+ strategies.flatMap((strategy) => strategy.selection?.tickers ?? [])
510
+ )
511
+ ]
512
+ };
513
+ };
502
514
  var loadDeploymentReplayStrategies = async ({
503
515
  userName,
504
516
  projectRoot,
@@ -518,14 +530,16 @@ var loadDeploymentReplayStrategies = async ({
518
530
  strategyPackage,
519
531
  strategyPackageVersion,
520
532
  runtimePackageVersion,
521
- sourceStrategyConfig
533
+ sourceStrategyConfig,
534
+ selection
522
535
  }) => ({
523
536
  strategyName,
524
537
  version,
525
538
  strategyPackage,
526
539
  strategyPackageVersion,
527
540
  runtimePackageVersion,
528
- strategyConfig: sourceStrategyConfig
541
+ strategyConfig: sourceStrategyConfig,
542
+ ...selection ? { selection } : {}
529
543
  })
530
544
  ) : [];
531
545
  if (!strategies.length) {
@@ -4663,6 +4677,7 @@ var INTERVALS = /* @__PURE__ */ new Set([
4663
4677
  "M"
4664
4678
  ]);
4665
4679
  var UNIVERSES = /* @__PURE__ */ new Set(["crypto", "tradfi"]);
4680
+ var isRuntimeStrategySelection = (value) => value === void 0 || isRecord(value) && Object.keys(value).length === 1 && Object.keys(value).every((key) => key === "tickers") && Array.isArray(value.tickers) && value.tickers.length > 0 && value.tickers.every((ticker) => isNonEmptyString(ticker));
4666
4681
  var parseRuntimeEvidenceDeploymentSnapshot = (value) => {
4667
4682
  if (!isRecord(value) || value.schemaVersion !== 1 || !isNonEmptyString(value.id) || !isNonEmptyString(value.label) || !isNonEmptyString(value.connectorName) || !isNonEmptyString(value.provider) || !isNonEmptyString(value.accountId) || typeof value.enabled !== "boolean" || !Array.isArray(value.strategies)) {
4668
4683
  throw new Error(
@@ -4670,7 +4685,7 @@ var parseRuntimeEvidenceDeploymentSnapshot = (value) => {
4670
4685
  );
4671
4686
  }
4672
4687
  const strategies = value.strategies.map((candidate) => {
4673
- if (!isRecord(candidate) || !isNonEmptyString(candidate.strategyName) || !Number.isSafeInteger(candidate.version) || Number(candidate.version) <= 0 || typeof candidate.enabled !== "boolean" || !["active", "entries_paused"].includes(String(candidate.controlState)) || !INTERVALS.has(String(candidate.interval)) || !UNIVERSES.has(String(candidate.universe)) || !isNonEmptyString(candidate.strategyPackage) || !isNonEmptyString(candidate.strategyPackageVersion) || !isNonEmptyString(candidate.runtimePackageVersion) || !isRecord(candidate.strategyConfig)) {
4688
+ if (!isRecord(candidate) || !isNonEmptyString(candidate.strategyName) || !Number.isSafeInteger(candidate.version) || Number(candidate.version) <= 0 || typeof candidate.enabled !== "boolean" || !["active", "entries_paused"].includes(String(candidate.controlState)) || !INTERVALS.has(String(candidate.interval)) || !UNIVERSES.has(String(candidate.universe)) || !isNonEmptyString(candidate.strategyPackage) || !isNonEmptyString(candidate.strategyPackageVersion) || !isNonEmptyString(candidate.runtimePackageVersion) || !isRecord(candidate.strategyConfig) || !isRuntimeStrategySelection(candidate.selection)) {
4674
4689
  throw new Error(
4675
4690
  "Runtime evidence deployment strategy snapshot is invalid"
4676
4691
  );
@@ -4710,11 +4725,12 @@ var runtimeDeploymentFromEvidence = (deployment) => ({
4710
4725
  accountId: deployment.accountId,
4711
4726
  enabled: deployment.enabled,
4712
4727
  strategies: deployment.strategies.map(
4713
- ({ strategyName, version, enabled, controlState }) => ({
4728
+ ({ strategyName, version, enabled, controlState, selection }) => ({
4714
4729
  strategyName,
4715
4730
  version,
4716
4731
  enabled,
4717
- controlState
4732
+ controlState,
4733
+ ...selection ? { selection } : {}
4718
4734
  })
4719
4735
  ),
4720
4736
  ...deployment.assetClasses ? { assetClasses: [...deployment.assetClasses] } : {},
@@ -5479,7 +5495,8 @@ var loadRuntimeStrategies = async (runtimeStrategies) => {
5479
5495
  strategyPackage,
5480
5496
  strategyPackageVersion,
5481
5497
  runtimePackageVersion,
5482
- strategyConfig
5498
+ strategyConfig,
5499
+ selection
5483
5500
  } = runtimeStrategy;
5484
5501
  const strategyCreator = await (0, import_strategies5.getStrategyCreator)(
5485
5502
  strategyName,
@@ -5509,7 +5526,8 @@ var loadRuntimeStrategies = async (runtimeStrategies) => {
5509
5526
  runtimePackageVersion,
5510
5527
  strategyCreator,
5511
5528
  strategyConfig,
5512
- strategyResults
5529
+ strategyResults,
5530
+ ...selection ? { selection } : {}
5513
5531
  };
5514
5532
  })
5515
5533
  );
@@ -5594,6 +5612,14 @@ var buildAfterSignalsContext = ({
5594
5612
  })
5595
5613
  )
5596
5614
  });
5615
+ var strategyIncludesSymbol = (strategy, symbol) => {
5616
+ const tickers = strategy.selection?.tickers;
5617
+ if (!tickers) return true;
5618
+ const normalizedSymbol = symbol.trim().toUpperCase();
5619
+ return tickers.some(
5620
+ (ticker) => ticker.trim().toUpperCase() === normalizedSymbol
5621
+ );
5622
+ };
5597
5623
  var buildSymbolSharedReplayKey = ({
5598
5624
  connectorName,
5599
5625
  interval,
@@ -5697,7 +5723,7 @@ var runHistoricalSignalsReplay = async ({
5697
5723
  });
5698
5724
  sharedReplayKeyPrefixes.push(sharedIndicatorsReplayKey);
5699
5725
  const strategiesForSymbol = await Promise.all(
5700
- loadedStrategies.map(
5726
+ loadedStrategies.filter((strategy) => strategyIncludesSymbol(strategy, symbol)).map(
5701
5727
  async ({
5702
5728
  strategyName,
5703
5729
  version,
@@ -9104,14 +9130,16 @@ var replayBacktest = async () => {
9104
9130
  strategyPackage,
9105
9131
  strategyPackageVersion,
9106
9132
  runtimePackageVersion,
9107
- strategyConfig
9133
+ strategyConfig,
9134
+ selection
9108
9135
  }) => ({
9109
9136
  strategyName,
9110
9137
  version,
9111
9138
  strategyPackage,
9112
9139
  strategyPackageVersion,
9113
9140
  runtimePackageVersion,
9114
- strategyConfig
9141
+ strategyConfig,
9142
+ ...selection ? { selection } : {}
9115
9143
  })
9116
9144
  )
9117
9145
  } : await loadDeploymentReplayStrategies({
@@ -9143,10 +9171,11 @@ var replayBacktest = async () => {
9143
9171
  `Replay requires one deployment universe: ${[...strategyUniverses].join(",")}`
9144
9172
  );
9145
9173
  }
9174
+ const replaySelection = mergeRuntimeStrategySelections(replayStrategies);
9146
9175
  const preparedRun = await prepareRunEnvironment({
9147
9176
  connector: deployment.connectorName,
9148
9177
  userName: replayUserName,
9149
- tickers: replayFlags.tickers ?? deployment.tickers,
9178
+ tickers: replayFlags.tickers ?? replaySelection?.tickers?.join(",") ?? deployment.tickers?.join(","),
9150
9179
  exclude: replayFlags.exclude,
9151
9180
  tickersLimit: replayFlags.tickersLimit,
9152
9181
  showTickersList: replayFlags.showTickersList,
@@ -9194,7 +9223,9 @@ var replayBacktest = async () => {
9194
9223
  const replayResult = await runHistoricalSignalsReplay({
9195
9224
  preparedRun,
9196
9225
  interval: replayInterval,
9197
- runtimeStrategies: replayStrategies
9226
+ runtimeStrategies: replayFlags.tickers ? replayStrategies.map(
9227
+ ({ selection: _selection, ...strategy }) => strategy
9228
+ ) : replayStrategies
9198
9229
  });
9199
9230
  for (const _strategy of replayResult.strategies) {
9200
9231
  incrementSuccessTests();
@@ -502,6 +502,18 @@ var resolveRunConnectorName = async ({
502
502
  );
503
503
  return import_connectors3.DEFAULT_CONNECTOR_NAME;
504
504
  };
505
+ var mergeRuntimeStrategySelections = (strategies) => {
506
+ if (strategies.some((strategy) => !strategy.selection?.tickers)) {
507
+ return void 0;
508
+ }
509
+ return {
510
+ tickers: [
511
+ ...new Set(
512
+ strategies.flatMap((strategy) => strategy.selection?.tickers ?? [])
513
+ )
514
+ ]
515
+ };
516
+ };
505
517
  var loadDeploymentReplayStrategies = async ({
506
518
  userName,
507
519
  projectRoot,
@@ -521,14 +533,16 @@ var loadDeploymentReplayStrategies = async ({
521
533
  strategyPackage,
522
534
  strategyPackageVersion,
523
535
  runtimePackageVersion,
524
- sourceStrategyConfig
536
+ sourceStrategyConfig,
537
+ selection
525
538
  }) => ({
526
539
  strategyName,
527
540
  version,
528
541
  strategyPackage,
529
542
  strategyPackageVersion,
530
543
  runtimePackageVersion,
531
- strategyConfig: sourceStrategyConfig
544
+ strategyConfig: sourceStrategyConfig,
545
+ ...selection ? { selection } : {}
532
546
  })
533
547
  ) : [];
534
548
  if (!strategies.length) {
@@ -4666,6 +4680,7 @@ var INTERVALS = /* @__PURE__ */ new Set([
4666
4680
  "M"
4667
4681
  ]);
4668
4682
  var UNIVERSES = /* @__PURE__ */ new Set(["crypto", "tradfi"]);
4683
+ var isRuntimeStrategySelection = (value) => value === void 0 || isRecord(value) && Object.keys(value).length === 1 && Object.keys(value).every((key) => key === "tickers") && Array.isArray(value.tickers) && value.tickers.length > 0 && value.tickers.every((ticker) => isNonEmptyString(ticker));
4669
4684
  var parseRuntimeEvidenceDeploymentSnapshot = (value) => {
4670
4685
  if (!isRecord(value) || value.schemaVersion !== 1 || !isNonEmptyString(value.id) || !isNonEmptyString(value.label) || !isNonEmptyString(value.connectorName) || !isNonEmptyString(value.provider) || !isNonEmptyString(value.accountId) || typeof value.enabled !== "boolean" || !Array.isArray(value.strategies)) {
4671
4686
  throw new Error(
@@ -4673,7 +4688,7 @@ var parseRuntimeEvidenceDeploymentSnapshot = (value) => {
4673
4688
  );
4674
4689
  }
4675
4690
  const strategies = value.strategies.map((candidate) => {
4676
- if (!isRecord(candidate) || !isNonEmptyString(candidate.strategyName) || !Number.isSafeInteger(candidate.version) || Number(candidate.version) <= 0 || typeof candidate.enabled !== "boolean" || !["active", "entries_paused"].includes(String(candidate.controlState)) || !INTERVALS.has(String(candidate.interval)) || !UNIVERSES.has(String(candidate.universe)) || !isNonEmptyString(candidate.strategyPackage) || !isNonEmptyString(candidate.strategyPackageVersion) || !isNonEmptyString(candidate.runtimePackageVersion) || !isRecord(candidate.strategyConfig)) {
4691
+ if (!isRecord(candidate) || !isNonEmptyString(candidate.strategyName) || !Number.isSafeInteger(candidate.version) || Number(candidate.version) <= 0 || typeof candidate.enabled !== "boolean" || !["active", "entries_paused"].includes(String(candidate.controlState)) || !INTERVALS.has(String(candidate.interval)) || !UNIVERSES.has(String(candidate.universe)) || !isNonEmptyString(candidate.strategyPackage) || !isNonEmptyString(candidate.strategyPackageVersion) || !isNonEmptyString(candidate.runtimePackageVersion) || !isRecord(candidate.strategyConfig) || !isRuntimeStrategySelection(candidate.selection)) {
4677
4692
  throw new Error(
4678
4693
  "Runtime evidence deployment strategy snapshot is invalid"
4679
4694
  );
@@ -4713,11 +4728,12 @@ var runtimeDeploymentFromEvidence = (deployment) => ({
4713
4728
  accountId: deployment.accountId,
4714
4729
  enabled: deployment.enabled,
4715
4730
  strategies: deployment.strategies.map(
4716
- ({ strategyName, version, enabled, controlState }) => ({
4731
+ ({ strategyName, version, enabled, controlState, selection }) => ({
4717
4732
  strategyName,
4718
4733
  version,
4719
4734
  enabled,
4720
- controlState
4735
+ controlState,
4736
+ ...selection ? { selection } : {}
4721
4737
  })
4722
4738
  ),
4723
4739
  ...deployment.assetClasses ? { assetClasses: [...deployment.assetClasses] } : {},
@@ -5482,7 +5498,8 @@ var loadRuntimeStrategies = async (runtimeStrategies) => {
5482
5498
  strategyPackage,
5483
5499
  strategyPackageVersion,
5484
5500
  runtimePackageVersion,
5485
- strategyConfig
5501
+ strategyConfig,
5502
+ selection
5486
5503
  } = runtimeStrategy;
5487
5504
  const strategyCreator = await (0, import_strategies5.getStrategyCreator)(
5488
5505
  strategyName,
@@ -5512,7 +5529,8 @@ var loadRuntimeStrategies = async (runtimeStrategies) => {
5512
5529
  runtimePackageVersion,
5513
5530
  strategyCreator,
5514
5531
  strategyConfig,
5515
- strategyResults
5532
+ strategyResults,
5533
+ ...selection ? { selection } : {}
5516
5534
  };
5517
5535
  })
5518
5536
  );
@@ -5597,6 +5615,14 @@ var buildAfterSignalsContext = ({
5597
5615
  })
5598
5616
  )
5599
5617
  });
5618
+ var strategyIncludesSymbol = (strategy, symbol) => {
5619
+ const tickers = strategy.selection?.tickers;
5620
+ if (!tickers) return true;
5621
+ const normalizedSymbol = symbol.trim().toUpperCase();
5622
+ return tickers.some(
5623
+ (ticker) => ticker.trim().toUpperCase() === normalizedSymbol
5624
+ );
5625
+ };
5600
5626
  var buildSymbolSharedReplayKey = ({
5601
5627
  connectorName,
5602
5628
  interval,
@@ -5700,7 +5726,7 @@ var runHistoricalSignalsReplay = async ({
5700
5726
  });
5701
5727
  sharedReplayKeyPrefixes.push(sharedIndicatorsReplayKey);
5702
5728
  const strategiesForSymbol = await Promise.all(
5703
- loadedStrategies.map(
5729
+ loadedStrategies.filter((strategy) => strategyIncludesSymbol(strategy, symbol)).map(
5704
5730
  async ({
5705
5731
  strategyName,
5706
5732
  version,
@@ -9107,14 +9133,16 @@ var replayBacktest = async () => {
9107
9133
  strategyPackage,
9108
9134
  strategyPackageVersion,
9109
9135
  runtimePackageVersion,
9110
- strategyConfig
9136
+ strategyConfig,
9137
+ selection
9111
9138
  }) => ({
9112
9139
  strategyName,
9113
9140
  version,
9114
9141
  strategyPackage,
9115
9142
  strategyPackageVersion,
9116
9143
  runtimePackageVersion,
9117
- strategyConfig
9144
+ strategyConfig,
9145
+ ...selection ? { selection } : {}
9118
9146
  })
9119
9147
  )
9120
9148
  } : await loadDeploymentReplayStrategies({
@@ -9146,10 +9174,11 @@ var replayBacktest = async () => {
9146
9174
  `Replay requires one deployment universe: ${[...strategyUniverses].join(",")}`
9147
9175
  );
9148
9176
  }
9177
+ const replaySelection = mergeRuntimeStrategySelections(replayStrategies);
9149
9178
  const preparedRun = await prepareRunEnvironment({
9150
9179
  connector: deployment.connectorName,
9151
9180
  userName: replayUserName,
9152
- tickers: replayFlags.tickers ?? deployment.tickers,
9181
+ tickers: replayFlags.tickers ?? replaySelection?.tickers?.join(",") ?? deployment.tickers?.join(","),
9153
9182
  exclude: replayFlags.exclude,
9154
9183
  tickersLimit: replayFlags.tickersLimit,
9155
9184
  showTickersList: replayFlags.showTickersList,
@@ -9197,7 +9226,9 @@ var replayBacktest = async () => {
9197
9226
  const replayResult = await runHistoricalSignalsReplay({
9198
9227
  preparedRun,
9199
9228
  interval: replayInterval,
9200
- runtimeStrategies: replayStrategies
9229
+ runtimeStrategies: replayFlags.tickers ? replayStrategies.map(
9230
+ ({ selection: _selection, ...strategy }) => strategy
9231
+ ) : replayStrategies
9201
9232
  });
9202
9233
  for (const _strategy of replayResult.strategies) {
9203
9234
  incrementSuccessTests();
@@ -656,7 +656,8 @@ var resolveRuntimeEvidenceDeploymentSnapshot = async ({
656
656
  strategyPackage,
657
657
  strategyPackageVersion,
658
658
  runtimePackageVersion,
659
- sourceStrategyConfig
659
+ sourceStrategyConfig,
660
+ selection
660
661
  }) => ({
661
662
  strategyName,
662
663
  version,
@@ -668,7 +669,8 @@ var resolveRuntimeEvidenceDeploymentSnapshot = async ({
668
669
  strategyPackage,
669
670
  strategyPackageVersion,
670
671
  runtimePackageVersion,
671
- strategyConfig: sourceStrategyConfig
672
+ strategyConfig: sourceStrategyConfig,
673
+ ...selection ? { selection } : {}
672
674
  })
673
675
  ),
674
676
  ...deployment.assetClasses ? { assetClasses: [...deployment.assetClasses] } : {},
@@ -48,7 +48,7 @@ var import_cli2 = require("@tradejs/node/cli");
48
48
  var import_async3 = require("@tradejs/core/async");
49
49
  var import_strategies5 = require("@tradejs/core/strategies");
50
50
  var import_constants3 = require("@tradejs/core/constants");
51
- var import_time2 = require("@tradejs/core/time");
51
+ var import_time3 = require("@tradejs/core/time");
52
52
  var import_logger4 = require("@tradejs/infra/logger");
53
53
  var import_runtimeHeartbeats = require("@tradejs/infra/runtimeHeartbeats");
54
54
  var import_runtimeStrategies2 = require("@tradejs/node/runtimeStrategies");
@@ -65,6 +65,38 @@ var getClosedCandlesForInterval = (candles, currentTimestamp, intervalMs) => {
65
65
  };
66
66
  var alignSymbolWithBtcReference = (symbolCandles, btcCandles) => (0, import_indicators.alignSortedCandlesByTimestamp)(symbolCandles, btcCandles);
67
67
 
68
+ // src/lib/runtimeRedis.ts
69
+ var import_redis = require("@tradejs/infra/redis");
70
+ var import_time = require("@tradejs/core/time");
71
+ var import_runtimeStrategyConfigs = require("@tradejs/infra/runtimeStrategyConfigs");
72
+ var isRuntimeTradeRecord = (value) => {
73
+ if (!value || typeof value !== "object") {
74
+ return false;
75
+ }
76
+ const record = value;
77
+ return typeof record.orderId === "string" && typeof record.strategy === "string" && typeof record.symbol === "string" && typeof record.entryTimestamp === "number" && typeof record.entryPrice === "number" && typeof record.qty === "number" && (record.direction === "LONG" || record.direction === "SHORT");
78
+ };
79
+ var loadRuntimeActiveTradeOrderIds = async (userName) => {
80
+ const keys = await (0, import_redis.getKeys)(import_redis.redisKeys.runtimeActiveTrades(userName));
81
+ const refs = await Promise.all(keys.map((key) => (0, import_redis.getData)(key, null)));
82
+ return new Set(
83
+ refs.map(
84
+ (ref) => ref && typeof ref === "object" && "orderId" in ref && typeof ref.orderId === "string" && ref.orderId.trim() ? ref.orderId.trim() : null
85
+ ).filter((orderId) => orderId != null)
86
+ );
87
+ };
88
+ var loadRuntimeActiveTrades = async (userName) => {
89
+ const orderIds = await loadRuntimeActiveTradeOrderIds(userName);
90
+ const trades = await Promise.all(
91
+ [...orderIds].map(
92
+ (orderId) => (0, import_redis.getData)(import_redis.redisKeys.runtimeTrade(userName, orderId), null)
93
+ )
94
+ );
95
+ return trades.filter(
96
+ (trade) => isRuntimeTradeRecord(trade) && trade.status === "active"
97
+ );
98
+ };
99
+
68
100
  // src/lib/runFormatting.ts
69
101
  var import_date_fns = require("date-fns");
70
102
  var ListIt = require("list-it");
@@ -3746,7 +3778,7 @@ var import_logger = require("@tradejs/infra/logger");
3746
3778
 
3747
3779
  // src/lib/runtimeSignalsStorage.ts
3748
3780
  var import_constants2 = require("@tradejs/core/constants");
3749
- var import_time = require("@tradejs/core/time");
3781
+ var import_time2 = require("@tradejs/core/time");
3750
3782
  var SECONDS_PER_DAY = 86400;
3751
3783
  var RUNTIME_SIGNAL_RETENTION_DAYS_ENV = "RUNTIME_SIGNAL_RETENTION_DAYS";
3752
3784
  var DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS = import_constants2.TTL_3D;
@@ -3895,7 +3927,7 @@ var logStrategySkipStats = (runtimeStrategies, strategyStats) => {
3895
3927
  };
3896
3928
 
3897
3929
  // src/lib/signals/evaluations.ts
3898
- var import_redis = require("@tradejs/infra/redis");
3930
+ var import_redis2 = require("@tradejs/infra/redis");
3899
3931
 
3900
3932
  // src/lib/runtimeLineage.ts
3901
3933
  var import_node_child_process = require("child_process");
@@ -4247,7 +4279,7 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
4247
4279
  if (evaluations.length === 0) return;
4248
4280
  const evaluationsByDay = /* @__PURE__ */ new Map();
4249
4281
  for (const evaluation of evaluations) {
4250
- const dayKey = (0, import_time.getRuntimeStorageDayKey)(evaluation.timestamp);
4282
+ const dayKey = (0, import_time2.getRuntimeStorageDayKey)(evaluation.timestamp);
4251
4283
  const dayEvaluations = evaluationsByDay.get(dayKey) ?? [];
4252
4284
  dayEvaluations.push(evaluation);
4253
4285
  evaluationsByDay.set(dayKey, dayEvaluations);
@@ -4259,7 +4291,7 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
4259
4291
  runtimeLineageScopeCacheDayKey = dayKey;
4260
4292
  runtimeLineageScopeCacheLoaded = false;
4261
4293
  }
4262
- const lineageBucket = import_redis.redisKeys.runtimeLineageScopeBucket(
4294
+ const lineageBucket = import_redis2.redisKeys.runtimeLineageScopeBucket(
4263
4295
  dayEvaluations[0].userName,
4264
4296
  dayKey
4265
4297
  );
@@ -4267,7 +4299,7 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
4267
4299
  (evaluation) => evaluation.runtimeLineage != null
4268
4300
  );
4269
4301
  if (lineageEvaluations.length > 0 && !runtimeLineageScopeCacheLoaded) {
4270
- const storedScopes = await (0, import_redis.getHashJsonValues)(lineageBucket);
4302
+ const storedScopes = await (0, import_redis2.getHashJsonValues)(lineageBucket);
4271
4303
  for (const scope of storedScopes) {
4272
4304
  const lineageField = buildLineageField(scope);
4273
4305
  runtimeLineageScopeCache.set(`${lineageBucket}:${lineageField}`, scope);
@@ -4308,7 +4340,7 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
4308
4340
  lineageWrites.set(lineageField, scope);
4309
4341
  }
4310
4342
  if (lineageWrites.size > 0) {
4311
- await (0, import_redis.setHashJsonFields)(
4343
+ await (0, import_redis2.setHashJsonFields)(
4312
4344
  lineageBucket,
4313
4345
  [...lineageWrites].map(([field, data]) => ({ field, data })),
4314
4346
  {
@@ -4320,7 +4352,7 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
4320
4352
  const statsIncrements = /* @__PURE__ */ new Map();
4321
4353
  for (const evaluation of dayEvaluations) {
4322
4354
  if (shouldStoreDetailedRuntimeSignalEvaluation(evaluation)) {
4323
- const bucket = import_redis.redisKeys.runtimeSignalEvaluationBucket(
4355
+ const bucket = import_redis2.redisKeys.runtimeSignalEvaluationBucket(
4324
4356
  evaluation.userName,
4325
4357
  dayKey,
4326
4358
  evaluation.strategy
@@ -4332,7 +4364,7 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
4332
4364
  });
4333
4365
  detailedWrites.set(bucket, entries);
4334
4366
  }
4335
- const statsBucket = import_redis.redisKeys.runtimeSignalEvaluationStatsBucket(
4367
+ const statsBucket = import_redis2.redisKeys.runtimeSignalEvaluationStatsBucket(
4336
4368
  evaluation.userName,
4337
4369
  dayKey,
4338
4370
  evaluation.strategy,
@@ -4348,12 +4380,12 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
4348
4380
  }
4349
4381
  await Promise.all([
4350
4382
  ...[...detailedWrites].map(
4351
- ([bucket, entries]) => (0, import_redis.setHashJsonFields)(bucket, entries, {
4383
+ ([bucket, entries]) => (0, import_redis2.setHashJsonFields)(bucket, entries, {
4352
4384
  expire: runtimeSignalRetentionTtl
4353
4385
  })
4354
4386
  ),
4355
4387
  ...[...statsIncrements].map(
4356
- ([bucket, increments]) => (0, import_redis.incrHashFields)(bucket, increments, {
4388
+ ([bucket, increments]) => (0, import_redis2.incrHashFields)(bucket, increments, {
4357
4389
  expire: runtimeSignalRetentionTtl
4358
4390
  })
4359
4391
  )
@@ -4369,9 +4401,9 @@ var createRuntimeSignalEvaluationBuffer = () => {
4369
4401
  const runtimeSignalRetentionTtl = getRuntimeSignalRetentionTtlSeconds();
4370
4402
  const signalRefsByBucket = /* @__PURE__ */ new Map();
4371
4403
  for (const { userName, signal } of pendingSignals) {
4372
- const bucket = import_redis.redisKeys.runtimeSignalBucket(
4404
+ const bucket = import_redis2.redisKeys.runtimeSignalBucket(
4373
4405
  userName,
4374
- (0, import_time.getRuntimeStorageDayKey)(signal.timestamp),
4406
+ (0, import_time2.getRuntimeStorageDayKey)(signal.timestamp),
4375
4407
  signal.strategy
4376
4408
  );
4377
4409
  const entries = signalRefsByBucket.get(bucket) ?? [];
@@ -4383,14 +4415,14 @@ var createRuntimeSignalEvaluationBuffer = () => {
4383
4415
  }
4384
4416
  await Promise.all([
4385
4417
  ...pendingSignals.map(
4386
- ({ signal }) => (0, import_redis.setData)(
4387
- import_redis.redisKeys.storeSignal(signal.symbol, signal.signalId),
4418
+ ({ signal }) => (0, import_redis2.setData)(
4419
+ import_redis2.redisKeys.storeSignal(signal.symbol, signal.signalId),
4388
4420
  toStoredRuntimeSignal(signal),
4389
4421
  { expire: runtimeSignalRetentionTtl }
4390
4422
  )
4391
4423
  ),
4392
4424
  ...[...signalRefsByBucket].map(
4393
- ([bucket, entries]) => (0, import_redis.setHashJsonFields)(bucket, entries, {
4425
+ ([bucket, entries]) => (0, import_redis2.setHashJsonFields)(bucket, entries, {
4394
4426
  expire: runtimeSignalRetentionTtl
4395
4427
  })
4396
4428
  )
@@ -4629,9 +4661,9 @@ var import_candles = require("@tradejs/infra/timescale/candles");
4629
4661
 
4630
4662
  // src/lib/marketData/klineEvents.ts
4631
4663
  var import_ioredis = __toESM(require("ioredis"));
4632
- var import_redis2 = require("@tradejs/infra/redis");
4664
+ var import_redis3 = require("@tradejs/infra/redis");
4633
4665
  var MARKET_KLINE_CHANNEL = "tradejs:market:kline";
4634
- var publishMarketKlineEvent = (event) => (0, import_redis2.publishData)(MARKET_KLINE_CHANNEL, event);
4666
+ var publishMarketKlineEvent = (event) => (0, import_redis3.publishData)(MARKET_KLINE_CHANNEL, event);
4635
4667
 
4636
4668
  // src/lib/signals/klineFeed.ts
4637
4669
  var FLUSH_DELAY_MS = 100;
@@ -5037,6 +5069,23 @@ var createSignalsTickerEvaluator = ({
5037
5069
  // src/lib/signals/configuredScopes.ts
5038
5070
  var sorted = (values) => values ? [...values].sort() : [];
5039
5071
  var formatConfiguredStrategyIdentity = (strategy) => `${strategy.strategyName}@v${strategy.version}[${strategy.controlState}]`;
5072
+ var getConfiguredScopeActiveSymbols = ({
5073
+ trades,
5074
+ deploymentId,
5075
+ strategyNames,
5076
+ universe,
5077
+ accountId,
5078
+ interval
5079
+ }) => {
5080
+ const strategyNameSet = new Set(strategyNames);
5081
+ return [
5082
+ ...new Set(
5083
+ trades.filter(
5084
+ (trade) => trade.status === "active" && trade.deploymentId === deploymentId && strategyNameSet.has(trade.strategy) && (trade.universe ?? "crypto") === universe && (!trade.accountId || trade.accountId === accountId) && (!trade.interval || String(trade.interval) === String(interval))
5085
+ ).map(({ symbol }) => symbol)
5086
+ )
5087
+ ].sort();
5088
+ };
5040
5089
  var buildConfiguredSignalsScopes = ({
5041
5090
  connectorName,
5042
5091
  deployment,
@@ -5053,21 +5102,30 @@ var buildConfiguredSignalsScopes = ({
5053
5102
  });
5054
5103
  const groups = /* @__PURE__ */ new Map();
5055
5104
  for (const strategy of strategies) {
5105
+ const selectionIdentity = JSON.stringify({
5106
+ tickers: sorted(strategy.selection?.tickers)
5107
+ });
5056
5108
  const baseKey = [
5057
5109
  connectorName,
5058
5110
  strategy.universe,
5059
5111
  strategy.accountId ?? "default",
5060
- strategy.interval
5112
+ strategy.interval,
5113
+ selectionIdentity
5061
5114
  ].join(":");
5062
5115
  const group = groups.get(baseKey) ?? {
5063
5116
  scope: {
5064
5117
  connectorName,
5065
5118
  universe: strategy.universe,
5066
5119
  accountId: strategy.accountId,
5067
- interval: strategy.interval
5120
+ interval: strategy.interval,
5121
+ strategyNames: [],
5122
+ ...strategy.selection ? {
5123
+ selection: { tickers: [...strategy.selection.tickers] }
5124
+ } : {}
5068
5125
  },
5069
5126
  strategyIdentities: []
5070
5127
  };
5128
+ group.scope.strategyNames.push(strategy.strategyName);
5071
5129
  group.strategyIdentities.push(formatConfiguredStrategyIdentity(strategy));
5072
5130
  groups.set(baseKey, group);
5073
5131
  }
@@ -5075,7 +5133,10 @@ var buildConfiguredSignalsScopes = ({
5075
5133
  key: [baseKey, deploymentIdentity, ...group.strategyIdentities.sort()].join(
5076
5134
  ":"
5077
5135
  ),
5078
- scope: group.scope
5136
+ scope: {
5137
+ ...group.scope,
5138
+ strategyNames: [...group.scope.strategyNames].sort()
5139
+ }
5079
5140
  }));
5080
5141
  };
5081
5142
 
@@ -5188,6 +5249,10 @@ var createSignalsRunner = (config2) => {
5188
5249
  interval: runtimeInterval,
5189
5250
  intervalMs: runtimeIntervalMs,
5190
5251
  deployment,
5252
+ ...scope.strategyNames ? { strategyNames: [...scope.strategyNames] } : {},
5253
+ ...scope.selection ? {
5254
+ selection: { tickers: [...scope.selection.tickers] }
5255
+ } : {},
5191
5256
  startedAt: Date.now(),
5192
5257
  marketConnector,
5193
5258
  btcReferences,
@@ -5217,13 +5282,31 @@ var createSignalsRunner = (config2) => {
5217
5282
  btcReferences,
5218
5283
  lifecycle,
5219
5284
  interval: interval2,
5220
- intervalMs
5285
+ intervalMs,
5286
+ strategyNames,
5287
+ selection
5221
5288
  } = session;
5222
5289
  const universe = sessionUniverse ?? marketConnector.universe ?? "crypto";
5290
+ const selectedActiveSymbols = !config2.tickers && selection?.tickers?.length && strategyNames?.length ? getConfiguredScopeActiveSymbols({
5291
+ trades: await loadRuntimeActiveTrades(config2.userName),
5292
+ deploymentId: deployment?.id ?? "",
5293
+ strategyNames,
5294
+ universe,
5295
+ accountId,
5296
+ interval: interval2
5297
+ }) : [];
5298
+ const selectedTickers = selection?.tickers ? [.../* @__PURE__ */ new Set([...selection.tickers, ...selectedActiveSymbols])] : void 0;
5299
+ if (selectedActiveSymbols.length) {
5300
+ import_logger4.logger.info(
5301
+ import_chalk7.default.gray(
5302
+ `strategy selection retained active symbols: ${selectedActiveSymbols.join(", ")}`
5303
+ )
5304
+ );
5305
+ }
5223
5306
  const tickers = await timeOperation2("tickers load", () => {
5224
5307
  const baseArgs = [
5225
5308
  marketConnector,
5226
- config2.tickers || deployment?.tickers?.join(","),
5309
+ config2.tickers || selectedTickers?.join(",") || deployment?.tickers?.join(","),
5227
5310
  config2.exclude,
5228
5311
  config2.tickersLimit,
5229
5312
  config2.chunk
@@ -5250,8 +5333,8 @@ var createSignalsRunner = (config2) => {
5250
5333
  }
5251
5334
  const projectConfig = await (0, import_cli2.loadTradejsConfig)(projectRoot);
5252
5335
  projectHooks = projectConfig.hooks;
5253
- const currentTimestamp = (0, import_time2.getTimestamp)();
5254
- const preloadStart = (0, import_time2.getTimestamp)(import_constants3.SIGNALS_CLI_PRELOAD_DAYS);
5336
+ const currentTimestamp = (0, import_time3.getTimestamp)();
5337
+ const preloadStart = (0, import_time3.getTimestamp)(import_constants3.SIGNALS_CLI_PRELOAD_DAYS);
5255
5338
  if (!config2.cacheOnly && session.klineFeed) {
5256
5339
  const expectedSymbols = universe === "crypto" ? [.../* @__PURE__ */ new Set([...tickers, "BTCUSDT", "ETHUSDT"])] : tickers;
5257
5340
  session.klineFeed.setSubscriptions(expectedSymbols);
@@ -5366,7 +5449,7 @@ var createSignalsRunner = (config2) => {
5366
5449
  const primaryEthByTimestamp = new Map(
5367
5450
  primaryEthClosedData.map((candle) => [candle.timestamp, candle])
5368
5451
  );
5369
- const runtimeStrategies = await (0, import_runtimeStrategies.loadResolvedRuntimeStrategies)({
5452
+ const configuredRuntimeStrategies = await (0, import_runtimeStrategies.loadResolvedRuntimeStrategies)({
5370
5453
  userName: config2.userName,
5371
5454
  projectRoot,
5372
5455
  deploymentId: deployment?.id ?? "",
@@ -5374,6 +5457,10 @@ var createSignalsRunner = (config2) => {
5374
5457
  accountId,
5375
5458
  interval: interval2
5376
5459
  });
5460
+ const selectedStrategyNames = strategyNames?.length ? new Set(strategyNames) : null;
5461
+ const runtimeStrategies = selectedStrategyNames ? configuredRuntimeStrategies.filter(
5462
+ ({ strategyName }) => selectedStrategyNames.has(strategyName)
5463
+ ) : configuredRuntimeStrategies;
5377
5464
  if (!runtimeStrategies.length) {
5378
5465
  lifecycle.clear();
5379
5466
  import_logger4.logger.warn(