@tradejs/cli 2.0.1 → 2.0.3

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.
@@ -1653,6 +1653,7 @@ var divideOrNull = (num, denom) => {
1653
1653
  var DAY_MS = 24 * 60 * 60 * 1e3;
1654
1654
  var DAYS_PER_WEEK = 7;
1655
1655
  var DAYS_PER_MONTH = 30.4375;
1656
+ var DAYS_PER_YEAR = 365;
1656
1657
  var getEvaluationPeriodDays = (evaluations) => {
1657
1658
  let minTimestamp = null;
1658
1659
  let maxTimestamp = null;
@@ -1674,6 +1675,40 @@ var getEvaluationPeriodDays = (evaluations) => {
1674
1675
  return Math.max((maxTimestamp - minTimestamp) / DAY_MS, 1);
1675
1676
  };
1676
1677
  var qualitySortKey = (quality) => quality == null ? Number.POSITIVE_INFINITY : quality;
1678
+ var calculateTradePnlRiskRatios = ({
1679
+ profits,
1680
+ totalProfit,
1681
+ maxDrawdown,
1682
+ periodDays
1683
+ }) => {
1684
+ if (!profits.length || periodDays == null || periodDays <= 0) {
1685
+ return {
1686
+ sharpeRatio: null,
1687
+ sortinoRatio: null,
1688
+ calmarRatio: null
1689
+ };
1690
+ }
1691
+ const meanProfit = totalProfit / profits.length;
1692
+ const variance = profits.reduce((sum, profit) => {
1693
+ const diff = profit - meanProfit;
1694
+ return sum + diff * diff;
1695
+ }, 0) / profits.length;
1696
+ const stdDev = Math.sqrt(variance);
1697
+ const downsideDeviation = Math.sqrt(
1698
+ profits.reduce(
1699
+ (sum, profit) => profit < 0 ? sum + profit * profit : sum,
1700
+ 0
1701
+ ) / profits.length
1702
+ );
1703
+ const annualizationScale = Math.sqrt(
1704
+ profits.length / periodDays * DAYS_PER_YEAR
1705
+ );
1706
+ return {
1707
+ sharpeRatio: stdDev > 0 && annualizationScale > 0 ? meanProfit / stdDev * annualizationScale : null,
1708
+ sortinoRatio: downsideDeviation > 0 && annualizationScale > 0 ? meanProfit / downsideDeviation * annualizationScale : null,
1709
+ calmarRatio: maxDrawdown > 0 ? totalProfit / periodDays * DAYS_PER_YEAR / maxDrawdown : null
1710
+ };
1711
+ };
1677
1712
  var emptyRiskSummary = () => ({
1678
1713
  trades: 0,
1679
1714
  totalProfit: 0,
@@ -1689,12 +1724,15 @@ var emptyRiskSummary = () => ({
1689
1724
  maxDrawdown: 0,
1690
1725
  maxDrawdownPctOfGrossProfit: null,
1691
1726
  maxDrawdownPctOfTotalProfit: null,
1727
+ sharpeRatio: null,
1728
+ sortinoRatio: null,
1729
+ calmarRatio: null,
1692
1730
  recoveryFactor: null,
1693
1731
  ulcerIndex: null,
1694
1732
  maxConsecutiveWins: 0,
1695
1733
  maxConsecutiveLosses: 0
1696
1734
  });
1697
- var summarizeApprovedRisk = (evaluations) => {
1735
+ var summarizeApprovedRisk = (evaluations, periodDays = getEvaluationPeriodDays(evaluations)) => {
1698
1736
  const approvedEvaluations = evaluations.filter((evaluation) => evaluation.aiApproved).sort((left, right) => {
1699
1737
  const leftTimestamp = typeof left.timestamp === "number" && Number.isFinite(left.timestamp) ? left.timestamp : Number.POSITIVE_INFINITY;
1700
1738
  const rightTimestamp = typeof right.timestamp === "number" && Number.isFinite(right.timestamp) ? right.timestamp : Number.POSITIVE_INFINITY;
@@ -1717,8 +1755,10 @@ var summarizeApprovedRisk = (evaluations) => {
1717
1755
  let currentLossStreak = 0;
1718
1756
  let maxConsecutiveWins = 0;
1719
1757
  let maxConsecutiveLosses = 0;
1758
+ const approvedProfits = [];
1720
1759
  for (const evaluation of approvedEvaluations) {
1721
1760
  const profit = evaluation.profit;
1761
+ approvedProfits.push(profit);
1722
1762
  if (profit > 0) {
1723
1763
  grossProfit += profit;
1724
1764
  wins += 1;
@@ -1746,6 +1786,12 @@ var summarizeApprovedRisk = (evaluations) => {
1746
1786
  const totalProfit = grossProfit - grossLoss;
1747
1787
  const avgWin = divideOrNull(grossProfit, wins);
1748
1788
  const avgLoss = divideOrNull(grossLoss, losses);
1789
+ const riskRatios = calculateTradePnlRiskRatios({
1790
+ profits: approvedProfits,
1791
+ totalProfit,
1792
+ maxDrawdown,
1793
+ periodDays
1794
+ });
1749
1795
  return {
1750
1796
  trades: approvedEvaluations.length,
1751
1797
  totalProfit,
@@ -1761,6 +1807,7 @@ var summarizeApprovedRisk = (evaluations) => {
1761
1807
  maxDrawdown,
1762
1808
  maxDrawdownPctOfGrossProfit: grossProfit > 0 ? maxDrawdown / grossProfit : null,
1763
1809
  maxDrawdownPctOfTotalProfit: totalProfit > 0 ? maxDrawdown / totalProfit : null,
1810
+ ...riskRatios,
1764
1811
  recoveryFactor: maxDrawdown > 0 ? totalProfit / maxDrawdown : null,
1765
1812
  ulcerIndex: Math.sqrt(drawdownSquares / approvedEvaluations.length),
1766
1813
  maxConsecutiveWins,
@@ -1859,7 +1906,7 @@ var summarizeAiTrainEvaluations = (evaluations) => {
1859
1906
  avgApprovedTradesPerDay,
1860
1907
  avgApprovedTradesPerWeek,
1861
1908
  expectancyDelta,
1862
- approvedRisk: summarizeApprovedRisk(evaluations),
1909
+ approvedRisk: summarizeApprovedRisk(evaluations, periodDays),
1863
1910
  qualityBuckets: [...bucketMap.values()].sort(
1864
1911
  (a, b) => qualitySortKey(a.quality) - qualitySortKey(b.quality)
1865
1912
  )
@@ -2523,6 +2570,12 @@ var RESEARCH_CONTEXT_ENV_KEYS = [
2523
2570
  "COINMARKETCAP_CONTEXT_BACKFILL_MAX_DAYS",
2524
2571
  "COINMARKETCAP_CONTEXT_BACKFILL_WARMUP_DAYS"
2525
2572
  ];
2573
+ var DERIVATIVES_CONTEXT_DATA_MODEL = {
2574
+ derivativesSourceIntervals: "15m",
2575
+ derivativesDerivedIntervals: "1h",
2576
+ derivativesHourlyFallback: "stored-1h",
2577
+ derivativesDataModelVersion: 2
2578
+ };
2526
2579
  var normalizeForStableJson = (value) => {
2527
2580
  if (Array.isArray(value)) {
2528
2581
  return value.map(normalizeForStableJson);
@@ -2635,6 +2688,7 @@ var resolveGateFingerprint = async (projectRoot, strategyName, gitSha) => {
2635
2688
  const relativeCandidates = [
2636
2689
  `packages/strategies/src/${strategyName}/adapters/ai.ts`,
2637
2690
  `packages/strategies/src/${strategyName}/guardrails.ts`,
2691
+ `packages/strategies/src/${strategyName}/pockets.ts`,
2638
2692
  `packages/strategies/src/${strategyName}/config.ts`,
2639
2693
  "packages/node/src/ai.ts"
2640
2694
  ];
@@ -2681,7 +2735,8 @@ var buildAiTrainLineage = async ({
2681
2735
  ...Object.fromEntries(
2682
2736
  RESEARCH_CONTEXT_ENV_KEYS.map((key) => [key, env[key] ?? null])
2683
2737
  ),
2684
- ...runContext
2738
+ ...runContext,
2739
+ ...DERIVATIVES_CONTEXT_DATA_MODEL
2685
2740
  };
2686
2741
  return {
2687
2742
  gitSha,
@@ -3741,6 +3796,9 @@ var main = async () => {
3741
3796
  import_chalk.default.gray("CALENDAR/D"),
3742
3797
  import_chalk.default.gray("WR"),
3743
3798
  import_chalk.default.gray("PF"),
3799
+ import_chalk.default.gray("SHARPE"),
3800
+ import_chalk.default.gray("SORTINO"),
3801
+ import_chalk.default.gray("CALMAR"),
3744
3802
  import_chalk.default.gray("PNL"),
3745
3803
  import_chalk.default.gray("MAX_DD"),
3746
3804
  import_chalk.default.gray("TOP REJECT")
@@ -3755,6 +3813,9 @@ var main = async () => {
3755
3813
  colorizeMetricNumber(window2.approvedPerCalendarDay),
3756
3814
  colorizeRatio(window2.outcome.approvedRisk.winRate),
3757
3815
  colorizeMetricNumber(window2.outcome.approvedRisk.profitFactor),
3816
+ colorizeMetricNumber(window2.outcome.approvedRisk.sharpeRatio),
3817
+ colorizeMetricNumber(window2.outcome.approvedRisk.sortinoRatio),
3818
+ colorizeMetricNumber(window2.outcome.approvedRisk.calmarRatio),
3758
3819
  colorizeProfit(window2.outcome.approvedRisk.totalProfit),
3759
3820
  colorizeProfit(-window2.outcome.approvedRisk.maxDrawdown),
3760
3821
  import_chalk.default.gray(
@@ -3803,6 +3864,18 @@ var main = async () => {
3803
3864
  "profit_factor",
3804
3865
  colorizeMetricNumber(summary.approvedRisk.profitFactor)
3805
3866
  ],
3867
+ [
3868
+ "sharpe_ratio",
3869
+ colorizeMetricNumber(summary.approvedRisk.sharpeRatio)
3870
+ ],
3871
+ [
3872
+ "sortino_ratio",
3873
+ colorizeMetricNumber(summary.approvedRisk.sortinoRatio)
3874
+ ],
3875
+ [
3876
+ "calmar_ratio",
3877
+ colorizeMetricNumber(summary.approvedRisk.calmarRatio)
3878
+ ],
3806
3879
  [
3807
3880
  "payoff_ratio",
3808
3881
  colorizeMetricNumber(summary.approvedRisk.payoffRatio)
@@ -2613,6 +2613,38 @@ var resolveDerivativesContextMissingFetchFromMs = (params) => {
2613
2613
  return null;
2614
2614
  };
2615
2615
  var resolveDerivativesContextFetchFromMs = (params) => resolveDerivativesContextMissingFetchFromMs(params) ?? (params.refreshClosedTail ? params.toMs : null);
2616
+ var resolveDerivativesContextRequiredFetchFromMs = (params) => {
2617
+ const dataCoverageFromMs = params.dataCoverageKeyExists ? null : params.mode === "signals" ? params.fromMs : resolveDerivativesContextMissingCoverageFetchFromMs({
2618
+ ranges: params.dataCoverageRanges,
2619
+ fromMs: params.fromMs,
2620
+ toMs: params.toMs,
2621
+ intervalMs: params.intervalMs
2622
+ });
2623
+ const edgesFromMs = dataCoverageFromMs == null ? null : resolveDerivativesContextFetchFromMs({
2624
+ edges: params.edges,
2625
+ fromMs: params.fromMs,
2626
+ toMs: params.toMs,
2627
+ intervalMs: params.intervalMs,
2628
+ refreshClosedTail: params.mode === "signals"
2629
+ });
2630
+ const dataFetchFromMs = dataCoverageFromMs != null && edgesFromMs != null ? Math.max(dataCoverageFromMs, edgesFromMs) : null;
2631
+ const confirmedLiquidationWindow = (0, import_indicators.resolveCoinalyzeConfirmedIntradayCoverage)({
2632
+ interval: params.interval,
2633
+ fromMs: params.fromMs,
2634
+ toMs: params.toMs,
2635
+ nowMs: params.nowMs
2636
+ });
2637
+ const liquidationFetchFromMs = confirmedLiquidationWindow ? resolveDerivativesContextMissingCoverageFetchFromMs({
2638
+ ranges: params.liquidationCoverageRanges,
2639
+ fromMs: confirmedLiquidationWindow.fromMs,
2640
+ toMs: confirmedLiquidationWindow.toMs,
2641
+ intervalMs: params.intervalMs
2642
+ }) : null;
2643
+ const fetchStarts = [dataFetchFromMs, liquidationFetchFromMs].filter(
2644
+ (value) => value != null
2645
+ );
2646
+ return fetchStarts.length ? Math.min(...fetchStarts) : null;
2647
+ };
2616
2648
  var countBackfillWindows = (params) => {
2617
2649
  let count = 0;
2618
2650
  let cursor = params.fromMs;
@@ -2948,7 +2980,8 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
2948
2980
  mode,
2949
2981
  startMs,
2950
2982
  endMs,
2951
- preloadStartMs: params.preloadStartMs
2983
+ preloadStartMs: params.preloadStartMs,
2984
+ nowMs: Date.now()
2952
2985
  });
2953
2986
  if (safeEndMs <= fromMs) {
2954
2987
  return skippedBackfillResult2();
@@ -2982,15 +3015,33 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
2982
3015
  );
2983
3016
  const coverageKeysByInterval = /* @__PURE__ */ new Map();
2984
3017
  const coverageRangesByInterval = /* @__PURE__ */ new Map();
3018
+ const liquidationCoverageRangesByInterval = /* @__PURE__ */ new Map();
3019
+ const coverageNowMs = Date.now();
2985
3020
  await Promise.all(
2986
3021
  intervalWindows.map(async (window2) => {
2987
- const coverageRows = await (0, import_timescale2.getDerivativesBackfillCoverage)({
2988
- source: "coinalyze",
2989
- symbols,
3022
+ const confirmedLiquidationWindow = (0, import_indicators.resolveCoinalyzeConfirmedIntradayCoverage)({
2990
3023
  interval: window2.interval,
2991
3024
  fromMs: window2.fromMs,
2992
- toMs: window2.toMs
3025
+ toMs: window2.toMs,
3026
+ nowMs: coverageNowMs
2993
3027
  });
3028
+ const [coverageRows, liquidationCoverageRows] = await Promise.all([
3029
+ (0, import_timescale2.getDerivativesBackfillCoverage)({
3030
+ source: "coinalyze",
3031
+ symbols,
3032
+ interval: window2.interval,
3033
+ fromMs: window2.fromMs,
3034
+ toMs: window2.toMs
3035
+ }),
3036
+ confirmedLiquidationWindow ? (0, import_timescale2.getDerivativesMetricCoverage)({
3037
+ source: "coinalyze",
3038
+ metric: "liquidation",
3039
+ symbols,
3040
+ interval: window2.interval,
3041
+ fromMs: confirmedLiquidationWindow.fromMs,
3042
+ toMs: confirmedLiquidationWindow.toMs
3043
+ }) : Promise.resolve([])
3044
+ ]);
2994
3045
  coverageKeysByInterval.set(
2995
3046
  window2.interval,
2996
3047
  new Set(
@@ -3012,6 +3063,17 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3012
3063
  rangesBySymbol.set(symbol, ranges);
3013
3064
  }
3014
3065
  coverageRangesByInterval.set(window2.interval, rangesBySymbol);
3066
+ const liquidationRangesBySymbol = /* @__PURE__ */ new Map();
3067
+ for (const row of liquidationCoverageRows) {
3068
+ const symbol = row.symbol.toUpperCase();
3069
+ const ranges = liquidationRangesBySymbol.get(symbol) ?? [];
3070
+ ranges.push({ fromMs: row.fromMs, toMs: row.toMs });
3071
+ liquidationRangesBySymbol.set(symbol, ranges);
3072
+ }
3073
+ liquidationCoverageRangesByInterval.set(
3074
+ window2.interval,
3075
+ liquidationRangesBySymbol
3076
+ );
3015
3077
  })
3016
3078
  );
3017
3079
  const cachedWindows = intervalWindows.reduce(
@@ -3036,7 +3098,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3036
3098
  (symbol) => backfillWindows.every((backfillWindow) => {
3037
3099
  const normalizedSymbol = symbol.toUpperCase();
3038
3100
  const coverageRanges = coverageRangesByInterval.get(window2.interval)?.get(normalizedSymbol) ?? [];
3039
- return hasDerivativesWindowCoverage({
3101
+ const dataCovered = hasDerivativesWindowCoverage({
3040
3102
  edges: edgesBySymbol?.get(normalizedSymbol),
3041
3103
  fromMs: backfillWindow.fromMs,
3042
3104
  toMs: backfillWindow.toMs
@@ -3053,6 +3115,19 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3053
3115
  toMs: backfillWindow.toMs
3054
3116
  })
3055
3117
  );
3118
+ const confirmedLiquidationWindow = (0, import_indicators.resolveCoinalyzeConfirmedIntradayCoverage)({
3119
+ interval: window2.interval,
3120
+ fromMs: backfillWindow.fromMs,
3121
+ toMs: backfillWindow.toMs,
3122
+ nowMs: coverageNowMs
3123
+ });
3124
+ const liquidationCovered = confirmedLiquidationWindow == null || resolveDerivativesContextMissingCoverageFetchFromMs({
3125
+ ranges: liquidationCoverageRangesByInterval.get(window2.interval)?.get(normalizedSymbol) ?? [],
3126
+ fromMs: confirmedLiquidationWindow.fromMs,
3127
+ toMs: confirmedLiquidationWindow.toMs,
3128
+ intervalMs: window2.intervalMs
3129
+ }) == null;
3130
+ return dataCovered && liquidationCovered;
3056
3131
  })
3057
3132
  );
3058
3133
  });
@@ -3137,6 +3212,11 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3137
3212
  coverageKeysByInterval.set(interval2, coverageKeys);
3138
3213
  const coverageRangesBySymbol = coverageRangesByInterval.get(interval2) ?? /* @__PURE__ */ new Map();
3139
3214
  coverageRangesByInterval.set(interval2, coverageRangesBySymbol);
3215
+ const liquidationCoverageRangesBySymbol = liquidationCoverageRangesByInterval.get(interval2) ?? /* @__PURE__ */ new Map();
3216
+ liquidationCoverageRangesByInterval.set(
3217
+ interval2,
3218
+ liquidationCoverageRangesBySymbol
3219
+ );
3140
3220
  for (let batchIdx = 0; batchIdx < symbolBatches.length; batchIdx += 1) {
3141
3221
  const batch = symbolBatches[batchIdx];
3142
3222
  let cursor = window2.fromMs;
@@ -3149,33 +3229,25 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3149
3229
  fromMs: cursor,
3150
3230
  toMs
3151
3231
  });
3152
- if (coverageKeys.has(key)) {
3153
- return null;
3154
- }
3155
3232
  const normalizedSymbol = item.symbol.toUpperCase();
3156
- const coverageFromMs = mode === "signals" ? cursor : resolveDerivativesContextMissingCoverageFetchFromMs({
3157
- ranges: coverageRangesBySymbol.get(normalizedSymbol),
3158
- fromMs: cursor,
3159
- toMs,
3160
- intervalMs
3161
- });
3162
- if (coverageFromMs == null) {
3163
- return null;
3164
- }
3165
- const edges = edgesBySymbol.get(normalizedSymbol);
3166
- const edgesFromMs = resolveDerivativesContextFetchFromMs({
3167
- edges,
3168
- fromMs: cursor,
3169
- toMs,
3170
- intervalMs,
3171
- refreshClosedTail: mode === "signals"
3172
- });
3173
- if (edgesFromMs == null) {
3174
- return null;
3175
- }
3233
+ const requiredFromMs = resolveDerivativesContextRequiredFetchFromMs(
3234
+ {
3235
+ mode,
3236
+ interval: interval2,
3237
+ intervalMs,
3238
+ fromMs: cursor,
3239
+ toMs,
3240
+ nowMs: coverageNowMs,
3241
+ dataCoverageKeyExists: coverageKeys.has(key),
3242
+ dataCoverageRanges: coverageRangesBySymbol.get(normalizedSymbol),
3243
+ liquidationCoverageRanges: liquidationCoverageRangesBySymbol.get(normalizedSymbol),
3244
+ edges: edgesBySymbol.get(normalizedSymbol)
3245
+ }
3246
+ );
3247
+ if (requiredFromMs == null) return null;
3176
3248
  return {
3177
3249
  item,
3178
- fromMs: Math.max(coverageFromMs, edgesFromMs)
3250
+ fromMs: requiredFromMs
3179
3251
  };
3180
3252
  }).filter(
3181
3253
  (item) => item != null
@@ -3200,6 +3272,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3200
3272
  );
3201
3273
  let rows = [];
3202
3274
  let missingClosedSymbols = [];
3275
+ let liquidationRowsByMarket = /* @__PURE__ */ new Map();
3203
3276
  for (let attempt = 1; attempt <= maxClosedBarAttempts; attempt += 1) {
3204
3277
  const oiMap = await fetchMetricBatch({
3205
3278
  endpoint: oiPath,
@@ -3219,7 +3292,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3219
3292
  fromMs: group.fromMs,
3220
3293
  toMs
3221
3294
  });
3222
- const liqMap = await fetchMetricBatch({
3295
+ liquidationRowsByMarket = await fetchMetricBatch({
3223
3296
  endpoint: liqPath,
3224
3297
  metric: "liq",
3225
3298
  marketSymbols,
@@ -3234,7 +3307,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3234
3307
  symbol: item.symbol,
3235
3308
  oiRaw: oiMap.get(marketSymbol) ?? [],
3236
3309
  fundingRaw: fundingMap.get(marketSymbol) ?? [],
3237
- liqRaw: liqMap.get(marketSymbol) ?? []
3310
+ liqRaw: liquidationRowsByMarket.get(marketSymbol) ?? []
3238
3311
  });
3239
3312
  return (0, import_indicators.coinalyzePointsToRows)(points, interval2, "coinalyze");
3240
3313
  });
@@ -3257,6 +3330,42 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3257
3330
  await (0, import_timescale2.upsertDerivatives)(rows);
3258
3331
  totalRows += rows.length;
3259
3332
  }
3333
+ const confirmedLiquidationWindow = (0, import_indicators.resolveCoinalyzeConfirmedIntradayCoverage)({
3334
+ interval: interval2,
3335
+ fromMs: group.fromMs,
3336
+ toMs,
3337
+ nowMs: coverageNowMs
3338
+ });
3339
+ if (confirmedLiquidationWindow) {
3340
+ const metricCoverageRows = missingBatch.map((item) => {
3341
+ const marketSymbol = item.marketSymbol.toUpperCase();
3342
+ const eventRowsCount = (liquidationRowsByMarket.get(marketSymbol) ?? []).filter((point) => {
3343
+ const timestamp = (0, import_indicators.toCoinalyzeTimestampMs)(
3344
+ point.t ?? point.ts ?? point.time ?? point.timestamp
3345
+ );
3346
+ return timestamp != null && timestamp >= confirmedLiquidationWindow.fromMs && timestamp <= confirmedLiquidationWindow.toMs;
3347
+ }).length;
3348
+ return {
3349
+ source: "coinalyze",
3350
+ metric: "liquidation",
3351
+ symbol: item.symbol,
3352
+ interval: interval2,
3353
+ fromMs: confirmedLiquidationWindow.fromMs,
3354
+ toMs: confirmedLiquidationWindow.toMs,
3355
+ eventRowsCount
3356
+ };
3357
+ });
3358
+ await (0, import_timescale2.applyDerivativesMetricCoverage)(metricCoverageRows);
3359
+ for (const coverageRow of metricCoverageRows) {
3360
+ const symbol = coverageRow.symbol.toUpperCase();
3361
+ const ranges = liquidationCoverageRangesBySymbol.get(symbol) ?? [];
3362
+ ranges.push({
3363
+ fromMs: coverageRow.fromMs,
3364
+ toMs: coverageRow.toMs
3365
+ });
3366
+ liquidationCoverageRangesBySymbol.set(symbol, ranges);
3367
+ }
3368
+ }
3260
3369
  if (mode === "backtest") {
3261
3370
  const rowsCountBySymbol = /* @__PURE__ */ new Map();
3262
3371
  for (const row of rows) {
@@ -378,6 +378,36 @@ var main = async () => {
378
378
  await (0, import_timescale.upsertDerivatives)(rows);
379
379
  totalRows += rows.length;
380
380
  }
381
+ const confirmedLiquidationWindow = (0, import_indicators.resolveCoinalyzeConfirmedIntradayCoverage)({
382
+ interval,
383
+ fromMs: cursor,
384
+ toMs,
385
+ nowMs: now
386
+ });
387
+ if (confirmedLiquidationWindow) {
388
+ await (0, import_timescale.applyDerivativesMetricCoverage)(
389
+ batch.map((item) => {
390
+ const marketSymbol = item.marketSymbol.toUpperCase();
391
+ const eventRowsCount = (liqMap.get(marketSymbol) ?? []).filter(
392
+ (point) => {
393
+ const timestamp = (0, import_indicators.toCoinalyzeTimestampMs)(
394
+ point.t ?? point.ts ?? point.time ?? point.timestamp
395
+ );
396
+ return timestamp != null && timestamp >= confirmedLiquidationWindow.fromMs && timestamp <= confirmedLiquidationWindow.toMs;
397
+ }
398
+ ).length;
399
+ return {
400
+ source: "coinalyze",
401
+ metric: "liquidation",
402
+ symbol: item.symbol,
403
+ interval,
404
+ fromMs: confirmedLiquidationWindow.fromMs,
405
+ toMs: confirmedLiquidationWindow.toMs,
406
+ eventRowsCount
407
+ };
408
+ })
409
+ );
410
+ }
381
411
  } catch (error) {
382
412
  failedWindows += 1;
383
413
  console.error(
@@ -1697,7 +1697,8 @@ var getStrategyChoices = async () => {
1697
1697
  "MaStrategy",
1698
1698
  "AdaptiveMomentumRibbon",
1699
1699
  "TrendLine",
1700
- "VolumeDivergence"
1700
+ "VolumeDivergence",
1701
+ "Grid"
1701
1702
  ];
1702
1703
  };
1703
1704
  var selectStrategy = async (promptLabel = "Select strategy", options) => {
@@ -54,7 +54,8 @@ var getStrategyChoices = async () => {
54
54
  "MaStrategy",
55
55
  "AdaptiveMomentumRibbon",
56
56
  "TrendLine",
57
- "VolumeDivergence"
57
+ "VolumeDivergence",
58
+ "Grid"
58
59
  ];
59
60
  };
60
61
  var selectStrategy = async (promptLabel = "Select strategy", options) => {
@@ -63,7 +63,8 @@ var getStrategyChoices = async () => {
63
63
  "MaStrategy",
64
64
  "AdaptiveMomentumRibbon",
65
65
  "TrendLine",
66
- "VolumeDivergence"
66
+ "VolumeDivergence",
67
+ "Grid"
67
68
  ];
68
69
  };
69
70
  var selectStrategy = async (promptLabel = "Select strategy", options) => {