@tradejs/cli 2.0.7 → 2.0.9

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.
@@ -4707,7 +4707,7 @@ var buildCoinMarketCapBackfillParams = (params) => ({
4707
4707
  userName: params.userName,
4708
4708
  startMs: params.startMs,
4709
4709
  endMs: params.endMs,
4710
- preloadStartMs: params.preloadStartMs
4710
+ preloadStartMs: params.mode === "signals" ? void 0 : params.preloadStartMs
4711
4711
  });
4712
4712
  var buildBinanceMarketBackfillParams = (params) => ({
4713
4713
  userName: params.userName,
@@ -4716,7 +4716,7 @@ var buildBinanceMarketBackfillParams = (params) => ({
4716
4716
  interval: params.interval,
4717
4717
  startMs: params.startMs,
4718
4718
  endMs: params.endMs,
4719
- preloadStartMs: params.preloadStartMs
4719
+ preloadStartMs: params.mode === "signals" ? void 0 : params.preloadStartMs
4720
4720
  });
4721
4721
  var buildDerivativesBackfillParams = (params) => ({
4722
4722
  userName: params.userName,
@@ -5025,6 +5025,7 @@ var loadRuntimeStrategies = async ({
5025
5025
  universe: configUniverse,
5026
5026
  accountId: effectiveAccountId,
5027
5027
  strategyCreator,
5028
+ sourceStrategyConfig: strategyConfig,
5028
5029
  strategyConfig: {
5029
5030
  ...strategyConfig,
5030
5031
  ...deploymentStrategy?.config,
@@ -5313,79 +5314,140 @@ var runtimeLineageKey = (lineage) => [
5313
5314
  // src/lib/signals/evaluations.ts
5314
5315
  var runtimeLineageScopeCache = /* @__PURE__ */ new Map();
5315
5316
  var runtimeLineageScopeCacheDayKey = "";
5317
+ var runtimeLineageScopeCacheLoaded = false;
5316
5318
  var buildRuntimeSignalEvaluationId = ({
5317
5319
  strategyName,
5318
5320
  symbol,
5319
5321
  timestamp,
5320
5322
  runtimeConfigId
5321
5323
  }) => runtimeConfigId && runtimeConfigId !== "config" ? [strategyName, runtimeConfigId, symbol, timestamp].join(":") : [strategyName, symbol, timestamp].join(":");
5322
- var saveRuntimeSignalEvaluation = async (evaluation) => {
5323
- const dayKey = (0, import_time.getRuntimeStorageDayKey)(evaluation.timestamp);
5324
- if (runtimeLineageScopeCacheDayKey !== dayKey) {
5325
- runtimeLineageScopeCache.clear();
5326
- runtimeLineageScopeCacheDayKey = dayKey;
5324
+ var buildLineageField = (scope) => [
5325
+ scope.strategy,
5326
+ scope.runtimeConfigId ?? "config",
5327
+ scope.symbol,
5328
+ runtimeLineageKey(scope.lineage)
5329
+ ].join(":");
5330
+ var flushRuntimeSignalEvaluations = async (evaluations) => {
5331
+ if (evaluations.length === 0) return;
5332
+ const evaluationsByDay = /* @__PURE__ */ new Map();
5333
+ for (const evaluation of evaluations) {
5334
+ const dayKey = (0, import_time.getRuntimeStorageDayKey)(evaluation.timestamp);
5335
+ const dayEvaluations = evaluationsByDay.get(dayKey) ?? [];
5336
+ dayEvaluations.push(evaluation);
5337
+ evaluationsByDay.set(dayKey, dayEvaluations);
5327
5338
  }
5328
5339
  const runtimeSignalRetentionTtl = getRuntimeSignalRetentionTtlSeconds();
5329
- if (evaluation.runtimeLineage) {
5330
- const lineageField = [
5331
- evaluation.strategy,
5332
- evaluation.runtimeConfigId ?? "config",
5333
- evaluation.symbol,
5334
- runtimeLineageKey(evaluation.runtimeLineage)
5335
- ].join(":");
5340
+ for (const [dayKey, dayEvaluations] of evaluationsByDay) {
5341
+ if (runtimeLineageScopeCacheDayKey !== dayKey) {
5342
+ runtimeLineageScopeCache.clear();
5343
+ runtimeLineageScopeCacheDayKey = dayKey;
5344
+ runtimeLineageScopeCacheLoaded = false;
5345
+ }
5336
5346
  const lineageBucket = import_redis3.redisKeys.runtimeLineageScopeBucket(
5337
- evaluation.userName,
5347
+ dayEvaluations[0].userName,
5338
5348
  dayKey
5339
5349
  );
5340
- const cacheKey = `${lineageBucket}:${lineageField}`;
5341
- const existing = runtimeLineageScopeCache.get(cacheKey) ?? await (0, import_redis3.getHashJsonField)(
5342
- lineageBucket,
5343
- lineageField
5350
+ const lineageEvaluations = dayEvaluations.filter(
5351
+ (evaluation) => evaluation.runtimeLineage != null
5344
5352
  );
5345
- const scope = {
5346
- strategy: evaluation.strategy,
5347
- symbol: evaluation.symbol,
5348
- runtimeConfigId: evaluation.runtimeConfigId,
5349
- lineage: evaluation.runtimeLineage,
5350
- firstTimestamp: Math.min(
5351
- existing?.firstTimestamp ?? evaluation.timestamp,
5352
- evaluation.timestamp
5353
- ),
5354
- lastTimestamp: Math.max(
5355
- existing?.lastTimestamp ?? evaluation.timestamp,
5356
- evaluation.timestamp
5357
- )
5358
- };
5359
- runtimeLineageScopeCache.set(cacheKey, scope);
5360
- await (0, import_redis3.setHashJsonField)(lineageBucket, lineageField, scope, {
5361
- expire: RUNTIME_LINEAGE_SCOPE_RETENTION_TTL_SECONDS
5362
- });
5363
- }
5364
- if (shouldStoreDetailedRuntimeSignalEvaluation(evaluation)) {
5365
- await (0, import_redis3.setHashJsonField)(
5366
- import_redis3.redisKeys.runtimeSignalEvaluationBucket(
5353
+ if (lineageEvaluations.length > 0 && !runtimeLineageScopeCacheLoaded) {
5354
+ const storedScopes = await (0, import_redis3.getHashJsonValues)(lineageBucket);
5355
+ for (const scope of storedScopes) {
5356
+ const lineageField = buildLineageField(scope);
5357
+ runtimeLineageScopeCache.set(`${lineageBucket}:${lineageField}`, scope);
5358
+ }
5359
+ runtimeLineageScopeCacheLoaded = true;
5360
+ }
5361
+ const lineageWrites = /* @__PURE__ */ new Map();
5362
+ for (const evaluation of lineageEvaluations) {
5363
+ const lineageField = buildLineageField({
5364
+ strategy: evaluation.strategy,
5365
+ runtimeConfigId: evaluation.runtimeConfigId,
5366
+ symbol: evaluation.symbol,
5367
+ lineage: evaluation.runtimeLineage
5368
+ });
5369
+ const cacheKey = `${lineageBucket}:${lineageField}`;
5370
+ const existing = runtimeLineageScopeCache.get(cacheKey);
5371
+ const scope = {
5372
+ strategy: evaluation.strategy,
5373
+ symbol: evaluation.symbol,
5374
+ runtimeConfigId: evaluation.runtimeConfigId,
5375
+ lineage: evaluation.runtimeLineage,
5376
+ firstTimestamp: Math.min(
5377
+ existing?.firstTimestamp ?? evaluation.timestamp,
5378
+ evaluation.timestamp
5379
+ ),
5380
+ lastTimestamp: Math.max(
5381
+ existing?.lastTimestamp ?? evaluation.timestamp,
5382
+ evaluation.timestamp
5383
+ )
5384
+ };
5385
+ runtimeLineageScopeCache.set(cacheKey, scope);
5386
+ lineageWrites.set(lineageField, scope);
5387
+ }
5388
+ if (lineageWrites.size > 0) {
5389
+ await (0, import_redis3.setHashJsonFields)(
5390
+ lineageBucket,
5391
+ [...lineageWrites].map(([field, data]) => ({ field, data })),
5392
+ {
5393
+ expire: RUNTIME_LINEAGE_SCOPE_RETENTION_TTL_SECONDS
5394
+ }
5395
+ );
5396
+ }
5397
+ const detailedWrites = /* @__PURE__ */ new Map();
5398
+ const statsIncrements = /* @__PURE__ */ new Map();
5399
+ for (const evaluation of dayEvaluations) {
5400
+ if (shouldStoreDetailedRuntimeSignalEvaluation(evaluation)) {
5401
+ const bucket = import_redis3.redisKeys.runtimeSignalEvaluationBucket(
5402
+ evaluation.userName,
5403
+ dayKey,
5404
+ evaluation.strategy
5405
+ );
5406
+ const entries = detailedWrites.get(bucket) ?? [];
5407
+ entries.push({
5408
+ field: evaluation.evaluationId,
5409
+ data: evaluation
5410
+ });
5411
+ detailedWrites.set(bucket, entries);
5412
+ }
5413
+ const statsBucket = import_redis3.redisKeys.runtimeSignalEvaluationStatsBucket(
5367
5414
  evaluation.userName,
5368
5415
  dayKey,
5369
5416
  evaluation.strategy
5370
- ),
5371
- evaluation.evaluationId,
5372
- evaluation,
5373
- {
5374
- expire: runtimeSignalRetentionTtl
5417
+ );
5418
+ const increments = statsIncrements.get(statsBucket) ?? {};
5419
+ for (const [field, increment] of Object.entries(
5420
+ buildRuntimeSignalStatsIncrements(evaluation)
5421
+ )) {
5422
+ increments[field] = (increments[field] ?? 0) + increment;
5375
5423
  }
5376
- );
5424
+ statsIncrements.set(statsBucket, increments);
5425
+ }
5426
+ await Promise.all([
5427
+ ...[...detailedWrites].map(
5428
+ ([bucket, entries]) => (0, import_redis3.setHashJsonFields)(bucket, entries, {
5429
+ expire: runtimeSignalRetentionTtl
5430
+ })
5431
+ ),
5432
+ ...[...statsIncrements].map(
5433
+ ([bucket, increments]) => (0, import_redis3.incrHashFields)(bucket, increments, {
5434
+ expire: runtimeSignalRetentionTtl
5435
+ })
5436
+ )
5437
+ ]);
5377
5438
  }
5378
- await (0, import_redis3.incrHashFields)(
5379
- import_redis3.redisKeys.runtimeSignalEvaluationStatsBucket(
5380
- evaluation.userName,
5381
- dayKey,
5382
- evaluation.strategy
5383
- ),
5384
- buildRuntimeSignalStatsIncrements(evaluation),
5385
- {
5386
- expire: runtimeSignalRetentionTtl
5439
+ };
5440
+ var createRuntimeSignalEvaluationBuffer = () => {
5441
+ const evaluations = [];
5442
+ return {
5443
+ save: async (evaluation) => {
5444
+ evaluations.push(evaluation);
5445
+ },
5446
+ flush: async () => {
5447
+ const pending = evaluations.splice(0);
5448
+ await flushRuntimeSignalEvaluations(pending);
5387
5449
  }
5388
- );
5450
+ };
5389
5451
  };
5390
5452
 
5391
5453
  // src/lib/signals/telegram.ts
@@ -5497,8 +5559,8 @@ var createSignalsStrategyLifecycle = ({
5497
5559
  }
5498
5560
  try {
5499
5561
  const strategy = await create({
5500
- btcBinanceData: [...btcBinanceData ?? []],
5501
- btcCoinbaseData: [...btcCoinbaseData ?? []],
5562
+ btcBinanceData: btcBinanceData ?? [],
5563
+ btcCoinbaseData: btcCoinbaseData ?? [],
5502
5564
  onRuntimeClose
5503
5565
  });
5504
5566
  strategy.__tradejsUpdateReferenceData?.({
@@ -5904,7 +5966,7 @@ var createSignalsSession = async (lifecycle, scope = {}) => {
5904
5966
  lifecycle
5905
5967
  };
5906
5968
  };
5907
- var findSignals = async (symbol, connectorName, connector, btcBinanceData, btcCoinbaseData, runtimeStrategies, strategyStats, runtimeCloseNotifications, lifecycle, preloadStart, currentTimestamp, persistStrategyState, universe, interval2, intervalMs2, accountId, deploymentId, instrument, runtimeScopeUniverse) => {
5969
+ 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) => {
5908
5970
  const strategySignals = [];
5909
5971
  const loadCached = (cachedSymbol) => connector.kline({
5910
5972
  symbol: cachedSymbol,
@@ -5913,36 +5975,21 @@ var findSignals = async (symbol, connectorName, connector, btcBinanceData, btcCo
5913
5975
  cacheOnly: true,
5914
5976
  interval: interval2
5915
5977
  });
5916
- const [cachedData, btcCachedData, ethCachedData] = universe === "crypto" ? await Promise.all([
5917
- loadCached(symbol),
5918
- loadCached("BTCUSDT"),
5919
- loadCached("ETHUSDT")
5920
- ]) : [await loadCached(symbol), [], []];
5978
+ const cachedData = universe === "crypto" && symbol === "BTCUSDT" ? primaryBtcClosedData : universe === "crypto" && symbol === "ETHUSDT" ? primaryEthClosedData : await loadCached(symbol);
5921
5979
  const closedData = getClosedCandlesForInterval(
5922
5980
  cachedData,
5923
5981
  currentTimestamp,
5924
5982
  intervalMs2
5925
5983
  );
5926
- const closedBtcData = getClosedCandlesForInterval(
5927
- btcCachedData,
5928
- currentTimestamp,
5929
- intervalMs2
5930
- );
5931
- const closedEthData = getClosedCandlesForInterval(
5932
- ethCachedData,
5933
- currentTimestamp,
5934
- intervalMs2
5935
- );
5984
+ const closedBtcData = primaryBtcClosedData;
5985
+ const closedEthData = primaryEthClosedData;
5936
5986
  const { alignedCoinCandles, alignedBtcCandles } = universe === "crypto" ? alignSymbolWithBtcReference(closedData, closedBtcData) : { alignedCoinCandles: closedData, alignedBtcCandles: closedData };
5937
- const ethByTimestamp = new Map(
5938
- closedEthData.map((candle) => [candle.timestamp, candle])
5939
- );
5940
- const alignedEthCandles = alignedCoinCandles.map((candle) => ethByTimestamp.get(candle.timestamp)).filter(
5987
+ const alignedEthCandles = alignedCoinCandles.map((candle) => primaryEthByTimestamp.get(candle.timestamp)).filter(
5941
5988
  (candle) => Boolean(candle)
5942
5989
  );
5943
5990
  const lastCandle = alignedCoinCandles.at(-1);
5944
5991
  const btcLastCandle = alignedBtcCandles.at(-1);
5945
- const ethLastCandle = lastCandle == null ? void 0 : ethByTimestamp.get(lastCandle.timestamp);
5992
+ const ethLastCandle = lastCandle == null ? void 0 : primaryEthByTimestamp.get(lastCandle.timestamp);
5946
5993
  if (!lastCandle || !btcLastCandle) {
5947
5994
  return strategySignals;
5948
5995
  }
@@ -5956,6 +6003,7 @@ var findSignals = async (symbol, connectorName, connector, btcBinanceData, btcCo
5956
6003
  strategyName,
5957
6004
  configId,
5958
6005
  strategyCreator,
6006
+ sourceStrategyConfig,
5959
6007
  strategyConfig,
5960
6008
  strategyResults
5961
6009
  } = runtimeStrategy;
@@ -5990,46 +6038,61 @@ var findSignals = async (symbol, connectorName, connector, btcBinanceData, btcCo
5990
6038
  configId
5991
6039
  });
5992
6040
  const stats = strategyStats.get(strategyName);
5993
- const evaluation = await lifecycle.evaluate({
5994
- key: lifecycleKey,
5995
- timestamp: lastCandle.timestamp,
5996
- config: {
5997
- runtimeConfig,
5998
- symbolResultConfig: strategyResults?.[symbol]?.config ?? null
5999
- },
6000
- btcBinanceData,
6001
- btcCoinbaseData,
6002
- onRuntimeClose: (event) => {
6003
- runtimeCloseNotifications.push(event);
6004
- },
6005
- create: async ({
6006
- btcBinanceData: lifecycleBtcBinanceData,
6007
- btcCoinbaseData: lifecycleBtcCoinbaseData,
6008
- onRuntimeClose
6009
- }) => strategyCreator({
6010
- userName: flags.user,
6011
- connectorName,
6012
- runtimeConfigId: configId,
6013
- connector,
6014
- symbol,
6015
- ...runtimeScopeUniverse ? {
6016
- universe,
6017
- assetClass: instrument?.assetClass,
6018
- instrument,
6019
- accountId,
6020
- deploymentId
6021
- } : {},
6022
- data: [...previousData],
6023
- btcData: universe === "crypto" ? [...previousBtcData] : [],
6024
- ethData: universe === "crypto" ? [...previousEthData] : [],
6025
- btcBinanceData: lifecycleBtcBinanceData,
6026
- btcCoinbaseData: lifecycleBtcCoinbaseData,
6027
- config: runtimeConfig,
6028
- ...persistStrategyState ? { sharedStrategyStateKey: lifecycleKey } : {},
6029
- onRuntimeClose
6030
- }),
6031
- run: (strategy) => strategy(lastCandle, btcLastCandle, ethLastCandle)
6032
- });
6041
+ const initialDataLength = previousData.length;
6042
+ const initialBtcDataLength = previousBtcData.length;
6043
+ const initialEthDataLength = previousEthData.length;
6044
+ let evaluation;
6045
+ try {
6046
+ evaluation = await lifecycle.evaluate({
6047
+ key: lifecycleKey,
6048
+ timestamp: lastCandle.timestamp,
6049
+ config: {
6050
+ runtimeConfig,
6051
+ sourceStrategyConfig,
6052
+ symbolResultConfig: strategyResults?.[symbol]?.config ?? null
6053
+ },
6054
+ btcBinanceData,
6055
+ btcCoinbaseData,
6056
+ onRuntimeClose: (event) => {
6057
+ runtimeCloseNotifications.push(event);
6058
+ },
6059
+ create: async ({
6060
+ btcBinanceData: lifecycleBtcBinanceData,
6061
+ btcCoinbaseData: lifecycleBtcCoinbaseData,
6062
+ onRuntimeClose
6063
+ }) => strategyCreator({
6064
+ userName: flags.user,
6065
+ connectorName,
6066
+ runtimeConfigId: configId,
6067
+ runtimeConfigSnapshot: {
6068
+ userConfig: sourceStrategyConfig,
6069
+ symbolResultConfig: strategyResults?.[symbol]?.config ?? null
6070
+ },
6071
+ connector,
6072
+ symbol,
6073
+ ...runtimeScopeUniverse ? {
6074
+ universe,
6075
+ assetClass: instrument?.assetClass,
6076
+ instrument,
6077
+ accountId,
6078
+ deploymentId
6079
+ } : {},
6080
+ data: previousData,
6081
+ btcData: universe === "crypto" ? previousBtcData : [],
6082
+ ethData: universe === "crypto" ? previousEthData : [],
6083
+ btcBinanceData: lifecycleBtcBinanceData,
6084
+ btcCoinbaseData: lifecycleBtcCoinbaseData,
6085
+ config: runtimeConfig,
6086
+ ...persistStrategyState ? { sharedStrategyStateKey: lifecycleKey } : {},
6087
+ onRuntimeClose
6088
+ }),
6089
+ run: (strategy) => strategy(lastCandle, btcLastCandle, ethLastCandle)
6090
+ });
6091
+ } finally {
6092
+ previousData.length = initialDataLength;
6093
+ previousBtcData.length = initialBtcDataLength;
6094
+ previousEthData.length = initialEthDataLength;
6095
+ }
6033
6096
  if (evaluation.action === "duplicate" || evaluation.action === "stale") {
6034
6097
  continue;
6035
6098
  }
@@ -6050,7 +6113,7 @@ var findSignals = async (symbol, connectorName, connector, btcBinanceData, btcCo
6050
6113
  if (typeof signal === "string") {
6051
6114
  recordStrategyReason(strategyStats, strategyName, signal, "core");
6052
6115
  }
6053
- await saveRuntimeSignalEvaluation({
6116
+ await saveRuntimeSignalEvaluation?.({
6054
6117
  evaluationId: buildRuntimeSignalEvaluationId({
6055
6118
  strategyName,
6056
6119
  symbol,
@@ -6116,7 +6179,7 @@ var findSignals = async (symbol, connectorName, connector, btcBinanceData, btcCo
6116
6179
  expire: runtimeSignalRetentionTtl
6117
6180
  }
6118
6181
  );
6119
- await saveRuntimeSignalEvaluation({
6182
+ await saveRuntimeSignalEvaluation?.({
6120
6183
  evaluationId: buildRuntimeSignalEvaluationId({
6121
6184
  strategyName,
6122
6185
  symbol,
@@ -6158,6 +6221,7 @@ var signals = async (options = {}) => {
6158
6221
  let activeSession;
6159
6222
  let projectHooks;
6160
6223
  let afterSignalsHookContext = null;
6224
+ const runtimeSignalEvaluations = createRuntimeSignalEvaluationBuffer();
6161
6225
  try {
6162
6226
  const session = options.session ?? await createSignalsSession(createDefaultSignalsLifecycle());
6163
6227
  activeSession = session;
@@ -6269,7 +6333,12 @@ var signals = async (options = {}) => {
6269
6333
  if (flags.updateOnly) {
6270
6334
  return;
6271
6335
  }
6272
- const [btcBinanceData, btcCoinbaseData] = universe === "crypto" ? await timeOperation2(
6336
+ const [
6337
+ btcBinanceData,
6338
+ btcCoinbaseData,
6339
+ primaryBtcCachedData,
6340
+ primaryEthCachedData
6341
+ ] = universe === "crypto" ? await timeOperation2(
6273
6342
  "reference candles load",
6274
6343
  () => Promise.all([
6275
6344
  btcReferences.binance.kline({
@@ -6285,9 +6354,36 @@ var signals = async (options = {}) => {
6285
6354
  end: currentTimestamp,
6286
6355
  cacheOnly: true,
6287
6356
  interval: interval2
6357
+ }),
6358
+ marketConnector.kline({
6359
+ symbol: "BTCUSDT",
6360
+ start: preloadStart,
6361
+ end: currentTimestamp,
6362
+ cacheOnly: true,
6363
+ interval: interval2
6364
+ }),
6365
+ marketConnector.kline({
6366
+ symbol: "ETHUSDT",
6367
+ start: preloadStart,
6368
+ end: currentTimestamp,
6369
+ cacheOnly: true,
6370
+ interval: interval2
6288
6371
  })
6289
6372
  ])
6290
- ) : [[], []];
6373
+ ) : [[], [], [], []];
6374
+ const primaryBtcClosedData = getClosedCandlesForInterval(
6375
+ primaryBtcCachedData,
6376
+ currentTimestamp,
6377
+ intervalMs2
6378
+ );
6379
+ const primaryEthClosedData = getClosedCandlesForInterval(
6380
+ primaryEthCachedData,
6381
+ currentTimestamp,
6382
+ intervalMs2
6383
+ );
6384
+ const primaryEthByTimestamp = new Map(
6385
+ primaryEthClosedData.map((candle) => [candle.timestamp, candle])
6386
+ );
6291
6387
  const runtimeStrategies = await loadRuntimeStrategies({
6292
6388
  userName: flags.user,
6293
6389
  projectRoot,
@@ -6371,6 +6467,9 @@ var signals = async (options = {}) => {
6371
6467
  marketConnector,
6372
6468
  btcBinanceData,
6373
6469
  btcCoinbaseData,
6470
+ primaryBtcClosedData,
6471
+ primaryEthClosedData,
6472
+ primaryEthByTimestamp,
6374
6473
  runtimeStrategies,
6375
6474
  strategyStats,
6376
6475
  runtimeCloseNotifications,
@@ -6384,7 +6483,8 @@ var signals = async (options = {}) => {
6384
6483
  accountId,
6385
6484
  deployment?.id,
6386
6485
  instrumentsBySymbol.get(symbol),
6387
- sessionUniverse
6486
+ sessionUniverse,
6487
+ runtimeSignalEvaluations.save
6388
6488
  );
6389
6489
  if (strategySignals.length > 0) {
6390
6490
  signals2.push(...strategySignals);
@@ -6425,6 +6525,14 @@ var signals = async (options = {}) => {
6425
6525
  );
6426
6526
  throw error;
6427
6527
  } finally {
6528
+ try {
6529
+ await runtimeSignalEvaluations.flush();
6530
+ } catch (error) {
6531
+ import_logger4.logger.error(
6532
+ "runtime signal evaluation flush failed: %s",
6533
+ error?.message || String(error)
6534
+ );
6535
+ }
6428
6536
  const durationMs = Date.now() - startedAt;
6429
6537
  if (activeSession?.deployment) {
6430
6538
  await (0, import_tradingAccounts2.saveRuntimeDeploymentHeartbeat)(flags.user, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tradejs/cli",
3
- "version": "2.0.7",
3
+ "version": "2.0.9",
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.7",
33
- "@tradejs/connectors": "^2.0.7",
34
- "@tradejs/core": "^2.0.7",
35
- "@tradejs/indicators": "^2.0.7",
36
- "@tradejs/infra": "^2.0.7",
37
- "@tradejs/node": "^2.0.7",
38
- "@tradejs/strategies": "^2.0.7",
39
- "@tradejs/types": "^2.0.7",
32
+ "@tradejs/base": "^2.0.9",
33
+ "@tradejs/connectors": "^2.0.9",
34
+ "@tradejs/core": "^2.0.9",
35
+ "@tradejs/indicators": "^2.0.9",
36
+ "@tradejs/infra": "^2.0.9",
37
+ "@tradejs/node": "^2.0.9",
38
+ "@tradejs/strategies": "^2.0.9",
39
+ "@tradejs/types": "^2.0.9",
40
40
  "args": "^5.0.3",
41
41
  "bcryptjs": "^2.4.3",
42
42
  "chalk": "4.1.2",