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