@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.
@@ -46,7 +46,7 @@ var import_cli2 = require("@tradejs/node/cli");
46
46
  var import_async3 = require("@tradejs/core/async");
47
47
  var import_strategies5 = require("@tradejs/core/strategies");
48
48
  var import_constants3 = require("@tradejs/core/constants");
49
- var import_time2 = require("@tradejs/core/time");
49
+ var import_time3 = require("@tradejs/core/time");
50
50
  var import_logger4 = require("@tradejs/infra/logger");
51
51
  var import_runtimeHeartbeats = require("@tradejs/infra/runtimeHeartbeats");
52
52
  var import_runtimeStrategies2 = require("@tradejs/node/runtimeStrategies");
@@ -63,6 +63,38 @@ var getClosedCandlesForInterval = (candles, currentTimestamp, intervalMs) => {
63
63
  };
64
64
  var alignSymbolWithBtcReference = (symbolCandles, btcCandles) => (0, import_indicators.alignSortedCandlesByTimestamp)(symbolCandles, btcCandles);
65
65
 
66
+ // src/lib/runtimeRedis.ts
67
+ var import_redis = require("@tradejs/infra/redis");
68
+ var import_time = require("@tradejs/core/time");
69
+ var import_runtimeStrategyConfigs = require("@tradejs/infra/runtimeStrategyConfigs");
70
+ var isRuntimeTradeRecord = (value) => {
71
+ if (!value || typeof value !== "object") {
72
+ return false;
73
+ }
74
+ const record = value;
75
+ 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");
76
+ };
77
+ var loadRuntimeActiveTradeOrderIds = async (userName) => {
78
+ const keys = await (0, import_redis.getKeys)(import_redis.redisKeys.runtimeActiveTrades(userName));
79
+ const refs = await Promise.all(keys.map((key) => (0, import_redis.getData)(key, null)));
80
+ return new Set(
81
+ refs.map(
82
+ (ref) => ref && typeof ref === "object" && "orderId" in ref && typeof ref.orderId === "string" && ref.orderId.trim() ? ref.orderId.trim() : null
83
+ ).filter((orderId) => orderId != null)
84
+ );
85
+ };
86
+ var loadRuntimeActiveTrades = async (userName) => {
87
+ const orderIds = await loadRuntimeActiveTradeOrderIds(userName);
88
+ const trades = await Promise.all(
89
+ [...orderIds].map(
90
+ (orderId) => (0, import_redis.getData)(import_redis.redisKeys.runtimeTrade(userName, orderId), null)
91
+ )
92
+ );
93
+ return trades.filter(
94
+ (trade) => isRuntimeTradeRecord(trade) && trade.status === "active"
95
+ );
96
+ };
97
+
66
98
  // src/lib/runFormatting.ts
67
99
  var import_date_fns = require("date-fns");
68
100
  var ListIt = require("list-it");
@@ -3744,7 +3776,7 @@ var import_logger = require("@tradejs/infra/logger");
3744
3776
 
3745
3777
  // src/lib/runtimeSignalsStorage.ts
3746
3778
  var import_constants2 = require("@tradejs/core/constants");
3747
- var import_time = require("@tradejs/core/time");
3779
+ var import_time2 = require("@tradejs/core/time");
3748
3780
  var SECONDS_PER_DAY = 86400;
3749
3781
  var RUNTIME_SIGNAL_RETENTION_DAYS_ENV = "RUNTIME_SIGNAL_RETENTION_DAYS";
3750
3782
  var DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS = import_constants2.TTL_3D;
@@ -3893,7 +3925,7 @@ var logStrategySkipStats = (runtimeStrategies, strategyStats) => {
3893
3925
  };
3894
3926
 
3895
3927
  // src/lib/signals/evaluations.ts
3896
- var import_redis = require("@tradejs/infra/redis");
3928
+ var import_redis2 = require("@tradejs/infra/redis");
3897
3929
 
3898
3930
  // src/lib/runtimeLineage.ts
3899
3931
  var import_node_child_process = require("child_process");
@@ -4245,7 +4277,7 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
4245
4277
  if (evaluations.length === 0) return;
4246
4278
  const evaluationsByDay = /* @__PURE__ */ new Map();
4247
4279
  for (const evaluation of evaluations) {
4248
- const dayKey = (0, import_time.getRuntimeStorageDayKey)(evaluation.timestamp);
4280
+ const dayKey = (0, import_time2.getRuntimeStorageDayKey)(evaluation.timestamp);
4249
4281
  const dayEvaluations = evaluationsByDay.get(dayKey) ?? [];
4250
4282
  dayEvaluations.push(evaluation);
4251
4283
  evaluationsByDay.set(dayKey, dayEvaluations);
@@ -4257,7 +4289,7 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
4257
4289
  runtimeLineageScopeCacheDayKey = dayKey;
4258
4290
  runtimeLineageScopeCacheLoaded = false;
4259
4291
  }
4260
- const lineageBucket = import_redis.redisKeys.runtimeLineageScopeBucket(
4292
+ const lineageBucket = import_redis2.redisKeys.runtimeLineageScopeBucket(
4261
4293
  dayEvaluations[0].userName,
4262
4294
  dayKey
4263
4295
  );
@@ -4265,7 +4297,7 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
4265
4297
  (evaluation) => evaluation.runtimeLineage != null
4266
4298
  );
4267
4299
  if (lineageEvaluations.length > 0 && !runtimeLineageScopeCacheLoaded) {
4268
- const storedScopes = await (0, import_redis.getHashJsonValues)(lineageBucket);
4300
+ const storedScopes = await (0, import_redis2.getHashJsonValues)(lineageBucket);
4269
4301
  for (const scope of storedScopes) {
4270
4302
  const lineageField = buildLineageField(scope);
4271
4303
  runtimeLineageScopeCache.set(`${lineageBucket}:${lineageField}`, scope);
@@ -4306,7 +4338,7 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
4306
4338
  lineageWrites.set(lineageField, scope);
4307
4339
  }
4308
4340
  if (lineageWrites.size > 0) {
4309
- await (0, import_redis.setHashJsonFields)(
4341
+ await (0, import_redis2.setHashJsonFields)(
4310
4342
  lineageBucket,
4311
4343
  [...lineageWrites].map(([field, data]) => ({ field, data })),
4312
4344
  {
@@ -4318,7 +4350,7 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
4318
4350
  const statsIncrements = /* @__PURE__ */ new Map();
4319
4351
  for (const evaluation of dayEvaluations) {
4320
4352
  if (shouldStoreDetailedRuntimeSignalEvaluation(evaluation)) {
4321
- const bucket = import_redis.redisKeys.runtimeSignalEvaluationBucket(
4353
+ const bucket = import_redis2.redisKeys.runtimeSignalEvaluationBucket(
4322
4354
  evaluation.userName,
4323
4355
  dayKey,
4324
4356
  evaluation.strategy
@@ -4330,7 +4362,7 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
4330
4362
  });
4331
4363
  detailedWrites.set(bucket, entries);
4332
4364
  }
4333
- const statsBucket = import_redis.redisKeys.runtimeSignalEvaluationStatsBucket(
4365
+ const statsBucket = import_redis2.redisKeys.runtimeSignalEvaluationStatsBucket(
4334
4366
  evaluation.userName,
4335
4367
  dayKey,
4336
4368
  evaluation.strategy,
@@ -4346,12 +4378,12 @@ var flushRuntimeSignalEvaluations = async (evaluations) => {
4346
4378
  }
4347
4379
  await Promise.all([
4348
4380
  ...[...detailedWrites].map(
4349
- ([bucket, entries]) => (0, import_redis.setHashJsonFields)(bucket, entries, {
4381
+ ([bucket, entries]) => (0, import_redis2.setHashJsonFields)(bucket, entries, {
4350
4382
  expire: runtimeSignalRetentionTtl
4351
4383
  })
4352
4384
  ),
4353
4385
  ...[...statsIncrements].map(
4354
- ([bucket, increments]) => (0, import_redis.incrHashFields)(bucket, increments, {
4386
+ ([bucket, increments]) => (0, import_redis2.incrHashFields)(bucket, increments, {
4355
4387
  expire: runtimeSignalRetentionTtl
4356
4388
  })
4357
4389
  )
@@ -4367,9 +4399,9 @@ var createRuntimeSignalEvaluationBuffer = () => {
4367
4399
  const runtimeSignalRetentionTtl = getRuntimeSignalRetentionTtlSeconds();
4368
4400
  const signalRefsByBucket = /* @__PURE__ */ new Map();
4369
4401
  for (const { userName, signal } of pendingSignals) {
4370
- const bucket = import_redis.redisKeys.runtimeSignalBucket(
4402
+ const bucket = import_redis2.redisKeys.runtimeSignalBucket(
4371
4403
  userName,
4372
- (0, import_time.getRuntimeStorageDayKey)(signal.timestamp),
4404
+ (0, import_time2.getRuntimeStorageDayKey)(signal.timestamp),
4373
4405
  signal.strategy
4374
4406
  );
4375
4407
  const entries = signalRefsByBucket.get(bucket) ?? [];
@@ -4381,14 +4413,14 @@ var createRuntimeSignalEvaluationBuffer = () => {
4381
4413
  }
4382
4414
  await Promise.all([
4383
4415
  ...pendingSignals.map(
4384
- ({ signal }) => (0, import_redis.setData)(
4385
- import_redis.redisKeys.storeSignal(signal.symbol, signal.signalId),
4416
+ ({ signal }) => (0, import_redis2.setData)(
4417
+ import_redis2.redisKeys.storeSignal(signal.symbol, signal.signalId),
4386
4418
  toStoredRuntimeSignal(signal),
4387
4419
  { expire: runtimeSignalRetentionTtl }
4388
4420
  )
4389
4421
  ),
4390
4422
  ...[...signalRefsByBucket].map(
4391
- ([bucket, entries]) => (0, import_redis.setHashJsonFields)(bucket, entries, {
4423
+ ([bucket, entries]) => (0, import_redis2.setHashJsonFields)(bucket, entries, {
4392
4424
  expire: runtimeSignalRetentionTtl
4393
4425
  })
4394
4426
  )
@@ -4627,9 +4659,9 @@ var import_candles = require("@tradejs/infra/timescale/candles");
4627
4659
 
4628
4660
  // src/lib/marketData/klineEvents.ts
4629
4661
  var import_ioredis = __toESM(require("ioredis"));
4630
- var import_redis2 = require("@tradejs/infra/redis");
4662
+ var import_redis3 = require("@tradejs/infra/redis");
4631
4663
  var MARKET_KLINE_CHANNEL = "tradejs:market:kline";
4632
- var publishMarketKlineEvent = (event) => (0, import_redis2.publishData)(MARKET_KLINE_CHANNEL, event);
4664
+ var publishMarketKlineEvent = (event) => (0, import_redis3.publishData)(MARKET_KLINE_CHANNEL, event);
4633
4665
 
4634
4666
  // src/lib/signals/klineFeed.ts
4635
4667
  var FLUSH_DELAY_MS = 100;
@@ -5035,6 +5067,23 @@ var createSignalsTickerEvaluator = ({
5035
5067
  // src/lib/signals/configuredScopes.ts
5036
5068
  var sorted = (values) => values ? [...values].sort() : [];
5037
5069
  var formatConfiguredStrategyIdentity = (strategy) => `${strategy.strategyName}@v${strategy.version}[${strategy.controlState}]`;
5070
+ var getConfiguredScopeActiveSymbols = ({
5071
+ trades,
5072
+ deploymentId,
5073
+ strategyNames,
5074
+ universe,
5075
+ accountId,
5076
+ interval
5077
+ }) => {
5078
+ const strategyNameSet = new Set(strategyNames);
5079
+ return [
5080
+ ...new Set(
5081
+ trades.filter(
5082
+ (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))
5083
+ ).map(({ symbol }) => symbol)
5084
+ )
5085
+ ].sort();
5086
+ };
5038
5087
  var buildConfiguredSignalsScopes = ({
5039
5088
  connectorName,
5040
5089
  deployment,
@@ -5051,21 +5100,30 @@ var buildConfiguredSignalsScopes = ({
5051
5100
  });
5052
5101
  const groups = /* @__PURE__ */ new Map();
5053
5102
  for (const strategy of strategies) {
5103
+ const selectionIdentity = JSON.stringify({
5104
+ tickers: sorted(strategy.selection?.tickers)
5105
+ });
5054
5106
  const baseKey = [
5055
5107
  connectorName,
5056
5108
  strategy.universe,
5057
5109
  strategy.accountId ?? "default",
5058
- strategy.interval
5110
+ strategy.interval,
5111
+ selectionIdentity
5059
5112
  ].join(":");
5060
5113
  const group = groups.get(baseKey) ?? {
5061
5114
  scope: {
5062
5115
  connectorName,
5063
5116
  universe: strategy.universe,
5064
5117
  accountId: strategy.accountId,
5065
- interval: strategy.interval
5118
+ interval: strategy.interval,
5119
+ strategyNames: [],
5120
+ ...strategy.selection ? {
5121
+ selection: { tickers: [...strategy.selection.tickers] }
5122
+ } : {}
5066
5123
  },
5067
5124
  strategyIdentities: []
5068
5125
  };
5126
+ group.scope.strategyNames.push(strategy.strategyName);
5069
5127
  group.strategyIdentities.push(formatConfiguredStrategyIdentity(strategy));
5070
5128
  groups.set(baseKey, group);
5071
5129
  }
@@ -5073,7 +5131,10 @@ var buildConfiguredSignalsScopes = ({
5073
5131
  key: [baseKey, deploymentIdentity, ...group.strategyIdentities.sort()].join(
5074
5132
  ":"
5075
5133
  ),
5076
- scope: group.scope
5134
+ scope: {
5135
+ ...group.scope,
5136
+ strategyNames: [...group.scope.strategyNames].sort()
5137
+ }
5077
5138
  }));
5078
5139
  };
5079
5140
 
@@ -5186,6 +5247,10 @@ var createSignalsRunner = (config2) => {
5186
5247
  interval: runtimeInterval,
5187
5248
  intervalMs: runtimeIntervalMs,
5188
5249
  deployment,
5250
+ ...scope.strategyNames ? { strategyNames: [...scope.strategyNames] } : {},
5251
+ ...scope.selection ? {
5252
+ selection: { tickers: [...scope.selection.tickers] }
5253
+ } : {},
5189
5254
  startedAt: Date.now(),
5190
5255
  marketConnector,
5191
5256
  btcReferences,
@@ -5215,13 +5280,31 @@ var createSignalsRunner = (config2) => {
5215
5280
  btcReferences,
5216
5281
  lifecycle,
5217
5282
  interval: interval2,
5218
- intervalMs
5283
+ intervalMs,
5284
+ strategyNames,
5285
+ selection
5219
5286
  } = session;
5220
5287
  const universe = sessionUniverse ?? marketConnector.universe ?? "crypto";
5288
+ const selectedActiveSymbols = !config2.tickers && selection?.tickers?.length && strategyNames?.length ? getConfiguredScopeActiveSymbols({
5289
+ trades: await loadRuntimeActiveTrades(config2.userName),
5290
+ deploymentId: deployment?.id ?? "",
5291
+ strategyNames,
5292
+ universe,
5293
+ accountId,
5294
+ interval: interval2
5295
+ }) : [];
5296
+ const selectedTickers = selection?.tickers ? [.../* @__PURE__ */ new Set([...selection.tickers, ...selectedActiveSymbols])] : void 0;
5297
+ if (selectedActiveSymbols.length) {
5298
+ import_logger4.logger.info(
5299
+ import_chalk7.default.gray(
5300
+ `strategy selection retained active symbols: ${selectedActiveSymbols.join(", ")}`
5301
+ )
5302
+ );
5303
+ }
5221
5304
  const tickers = await timeOperation2("tickers load", () => {
5222
5305
  const baseArgs = [
5223
5306
  marketConnector,
5224
- config2.tickers || deployment?.tickers?.join(","),
5307
+ config2.tickers || selectedTickers?.join(",") || deployment?.tickers?.join(","),
5225
5308
  config2.exclude,
5226
5309
  config2.tickersLimit,
5227
5310
  config2.chunk
@@ -5248,8 +5331,8 @@ var createSignalsRunner = (config2) => {
5248
5331
  }
5249
5332
  const projectConfig = await (0, import_cli2.loadTradejsConfig)(projectRoot);
5250
5333
  projectHooks = projectConfig.hooks;
5251
- const currentTimestamp = (0, import_time2.getTimestamp)();
5252
- const preloadStart = (0, import_time2.getTimestamp)(import_constants3.SIGNALS_CLI_PRELOAD_DAYS);
5334
+ const currentTimestamp = (0, import_time3.getTimestamp)();
5335
+ const preloadStart = (0, import_time3.getTimestamp)(import_constants3.SIGNALS_CLI_PRELOAD_DAYS);
5253
5336
  if (!config2.cacheOnly && session.klineFeed) {
5254
5337
  const expectedSymbols = universe === "crypto" ? [.../* @__PURE__ */ new Set([...tickers, "BTCUSDT", "ETHUSDT"])] : tickers;
5255
5338
  session.klineFeed.setSubscriptions(expectedSymbols);
@@ -5364,7 +5447,7 @@ var createSignalsRunner = (config2) => {
5364
5447
  const primaryEthByTimestamp = new Map(
5365
5448
  primaryEthClosedData.map((candle) => [candle.timestamp, candle])
5366
5449
  );
5367
- const runtimeStrategies = await (0, import_runtimeStrategies.loadResolvedRuntimeStrategies)({
5450
+ const configuredRuntimeStrategies = await (0, import_runtimeStrategies.loadResolvedRuntimeStrategies)({
5368
5451
  userName: config2.userName,
5369
5452
  projectRoot,
5370
5453
  deploymentId: deployment?.id ?? "",
@@ -5372,6 +5455,10 @@ var createSignalsRunner = (config2) => {
5372
5455
  accountId,
5373
5456
  interval: interval2
5374
5457
  });
5458
+ const selectedStrategyNames = strategyNames?.length ? new Set(strategyNames) : null;
5459
+ const runtimeStrategies = selectedStrategyNames ? configuredRuntimeStrategies.filter(
5460
+ ({ strategyName }) => selectedStrategyNames.has(strategyName)
5461
+ ) : configuredRuntimeStrategies;
5375
5462
  if (!runtimeStrategies.length) {
5376
5463
  lifecycle.clear();
5377
5464
  import_logger4.logger.warn(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tradejs/cli",
3
- "version": "3.1.10",
3
+ "version": "3.1.11-beta.216",
4
4
  "description": "Official CLI for the TradeJS TypeScript framework: infra setup, backtests, signals, bots, and ML workflows.",
5
5
  "keywords": [
6
6
  "tradejs",
@@ -32,13 +32,13 @@
32
32
  "tradejs": "dist/cli.js"
33
33
  },
34
34
  "dependencies": {
35
- "@tradejs/connectors": "^3.1.10",
36
- "@tradejs/core": "^3.1.10",
37
- "@tradejs/indicators": "^3.1.10",
38
- "@tradejs/infra": "^3.1.10",
39
- "@tradejs/node": "^3.1.10",
35
+ "@tradejs/connectors": "^3.1.11-beta.216",
36
+ "@tradejs/core": "^3.1.11-beta.216",
37
+ "@tradejs/indicators": "^3.1.11-beta.216",
38
+ "@tradejs/infra": "^3.1.11-beta.216",
39
+ "@tradejs/node": "^3.1.11-beta.216",
40
40
  "@tradejs/strategy-kit": "^3.0.0",
41
- "@tradejs/types": "^3.1.10",
41
+ "@tradejs/types": "^3.1.11-beta.216",
42
42
  "args": "^5.0.3",
43
43
  "bcryptjs": "^2.4.3",
44
44
  "chalk": "4.1.2",