@tradejs/cli 2.0.1 → 2.0.2

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.
@@ -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(
@@ -3224,6 +3224,38 @@ var resolveDerivativesContextMissingFetchFromMs = (params) => {
3224
3224
  return null;
3225
3225
  };
3226
3226
  var resolveDerivativesContextFetchFromMs = (params) => resolveDerivativesContextMissingFetchFromMs(params) ?? (params.refreshClosedTail ? params.toMs : null);
3227
+ var resolveDerivativesContextRequiredFetchFromMs = (params) => {
3228
+ const dataCoverageFromMs = params.dataCoverageKeyExists ? null : params.mode === "signals" ? params.fromMs : resolveDerivativesContextMissingCoverageFetchFromMs({
3229
+ ranges: params.dataCoverageRanges,
3230
+ fromMs: params.fromMs,
3231
+ toMs: params.toMs,
3232
+ intervalMs: params.intervalMs
3233
+ });
3234
+ const edgesFromMs = dataCoverageFromMs == null ? null : resolveDerivativesContextFetchFromMs({
3235
+ edges: params.edges,
3236
+ fromMs: params.fromMs,
3237
+ toMs: params.toMs,
3238
+ intervalMs: params.intervalMs,
3239
+ refreshClosedTail: params.mode === "signals"
3240
+ });
3241
+ const dataFetchFromMs = dataCoverageFromMs != null && edgesFromMs != null ? Math.max(dataCoverageFromMs, edgesFromMs) : null;
3242
+ const confirmedLiquidationWindow = (0, import_indicators.resolveCoinalyzeConfirmedIntradayCoverage)({
3243
+ interval: params.interval,
3244
+ fromMs: params.fromMs,
3245
+ toMs: params.toMs,
3246
+ nowMs: params.nowMs
3247
+ });
3248
+ const liquidationFetchFromMs = confirmedLiquidationWindow ? resolveDerivativesContextMissingCoverageFetchFromMs({
3249
+ ranges: params.liquidationCoverageRanges,
3250
+ fromMs: confirmedLiquidationWindow.fromMs,
3251
+ toMs: confirmedLiquidationWindow.toMs,
3252
+ intervalMs: params.intervalMs
3253
+ }) : null;
3254
+ const fetchStarts = [dataFetchFromMs, liquidationFetchFromMs].filter(
3255
+ (value) => value != null
3256
+ );
3257
+ return fetchStarts.length ? Math.min(...fetchStarts) : null;
3258
+ };
3227
3259
  var countBackfillWindows = (params) => {
3228
3260
  let count = 0;
3229
3261
  let cursor = params.fromMs;
@@ -3559,7 +3591,8 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3559
3591
  mode,
3560
3592
  startMs,
3561
3593
  endMs,
3562
- preloadStartMs: params.preloadStartMs
3594
+ preloadStartMs: params.preloadStartMs,
3595
+ nowMs: Date.now()
3563
3596
  });
3564
3597
  if (safeEndMs <= fromMs) {
3565
3598
  return skippedBackfillResult2();
@@ -3593,15 +3626,33 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3593
3626
  );
3594
3627
  const coverageKeysByInterval = /* @__PURE__ */ new Map();
3595
3628
  const coverageRangesByInterval = /* @__PURE__ */ new Map();
3629
+ const liquidationCoverageRangesByInterval = /* @__PURE__ */ new Map();
3630
+ const coverageNowMs = Date.now();
3596
3631
  await Promise.all(
3597
3632
  intervalWindows.map(async (window2) => {
3598
- const coverageRows = await (0, import_timescale2.getDerivativesBackfillCoverage)({
3599
- source: "coinalyze",
3600
- symbols,
3633
+ const confirmedLiquidationWindow = (0, import_indicators.resolveCoinalyzeConfirmedIntradayCoverage)({
3601
3634
  interval: window2.interval,
3602
3635
  fromMs: window2.fromMs,
3603
- toMs: window2.toMs
3636
+ toMs: window2.toMs,
3637
+ nowMs: coverageNowMs
3604
3638
  });
3639
+ const [coverageRows, liquidationCoverageRows] = await Promise.all([
3640
+ (0, import_timescale2.getDerivativesBackfillCoverage)({
3641
+ source: "coinalyze",
3642
+ symbols,
3643
+ interval: window2.interval,
3644
+ fromMs: window2.fromMs,
3645
+ toMs: window2.toMs
3646
+ }),
3647
+ confirmedLiquidationWindow ? (0, import_timescale2.getDerivativesMetricCoverage)({
3648
+ source: "coinalyze",
3649
+ metric: "liquidation",
3650
+ symbols,
3651
+ interval: window2.interval,
3652
+ fromMs: confirmedLiquidationWindow.fromMs,
3653
+ toMs: confirmedLiquidationWindow.toMs
3654
+ }) : Promise.resolve([])
3655
+ ]);
3605
3656
  coverageKeysByInterval.set(
3606
3657
  window2.interval,
3607
3658
  new Set(
@@ -3623,6 +3674,17 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3623
3674
  rangesBySymbol.set(symbol, ranges);
3624
3675
  }
3625
3676
  coverageRangesByInterval.set(window2.interval, rangesBySymbol);
3677
+ const liquidationRangesBySymbol = /* @__PURE__ */ new Map();
3678
+ for (const row of liquidationCoverageRows) {
3679
+ const symbol = row.symbol.toUpperCase();
3680
+ const ranges = liquidationRangesBySymbol.get(symbol) ?? [];
3681
+ ranges.push({ fromMs: row.fromMs, toMs: row.toMs });
3682
+ liquidationRangesBySymbol.set(symbol, ranges);
3683
+ }
3684
+ liquidationCoverageRangesByInterval.set(
3685
+ window2.interval,
3686
+ liquidationRangesBySymbol
3687
+ );
3626
3688
  })
3627
3689
  );
3628
3690
  const cachedWindows = intervalWindows.reduce(
@@ -3647,7 +3709,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3647
3709
  (symbol) => backfillWindows.every((backfillWindow) => {
3648
3710
  const normalizedSymbol = symbol.toUpperCase();
3649
3711
  const coverageRanges = coverageRangesByInterval.get(window2.interval)?.get(normalizedSymbol) ?? [];
3650
- return hasDerivativesWindowCoverage({
3712
+ const dataCovered = hasDerivativesWindowCoverage({
3651
3713
  edges: edgesBySymbol?.get(normalizedSymbol),
3652
3714
  fromMs: backfillWindow.fromMs,
3653
3715
  toMs: backfillWindow.toMs
@@ -3664,6 +3726,19 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3664
3726
  toMs: backfillWindow.toMs
3665
3727
  })
3666
3728
  );
3729
+ const confirmedLiquidationWindow = (0, import_indicators.resolveCoinalyzeConfirmedIntradayCoverage)({
3730
+ interval: window2.interval,
3731
+ fromMs: backfillWindow.fromMs,
3732
+ toMs: backfillWindow.toMs,
3733
+ nowMs: coverageNowMs
3734
+ });
3735
+ const liquidationCovered = confirmedLiquidationWindow == null || resolveDerivativesContextMissingCoverageFetchFromMs({
3736
+ ranges: liquidationCoverageRangesByInterval.get(window2.interval)?.get(normalizedSymbol) ?? [],
3737
+ fromMs: confirmedLiquidationWindow.fromMs,
3738
+ toMs: confirmedLiquidationWindow.toMs,
3739
+ intervalMs: window2.intervalMs
3740
+ }) == null;
3741
+ return dataCovered && liquidationCovered;
3667
3742
  })
3668
3743
  );
3669
3744
  });
@@ -3748,6 +3823,11 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3748
3823
  coverageKeysByInterval.set(interval, coverageKeys);
3749
3824
  const coverageRangesBySymbol = coverageRangesByInterval.get(interval) ?? /* @__PURE__ */ new Map();
3750
3825
  coverageRangesByInterval.set(interval, coverageRangesBySymbol);
3826
+ const liquidationCoverageRangesBySymbol = liquidationCoverageRangesByInterval.get(interval) ?? /* @__PURE__ */ new Map();
3827
+ liquidationCoverageRangesByInterval.set(
3828
+ interval,
3829
+ liquidationCoverageRangesBySymbol
3830
+ );
3751
3831
  for (let batchIdx = 0; batchIdx < symbolBatches.length; batchIdx += 1) {
3752
3832
  const batch = symbolBatches[batchIdx];
3753
3833
  let cursor = window2.fromMs;
@@ -3760,33 +3840,25 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3760
3840
  fromMs: cursor,
3761
3841
  toMs
3762
3842
  });
3763
- if (coverageKeys.has(key)) {
3764
- return null;
3765
- }
3766
3843
  const normalizedSymbol = item.symbol.toUpperCase();
3767
- const coverageFromMs = mode === "signals" ? cursor : resolveDerivativesContextMissingCoverageFetchFromMs({
3768
- ranges: coverageRangesBySymbol.get(normalizedSymbol),
3769
- fromMs: cursor,
3770
- toMs,
3771
- intervalMs
3772
- });
3773
- if (coverageFromMs == null) {
3774
- return null;
3775
- }
3776
- const edges = edgesBySymbol.get(normalizedSymbol);
3777
- const edgesFromMs = resolveDerivativesContextFetchFromMs({
3778
- edges,
3779
- fromMs: cursor,
3780
- toMs,
3781
- intervalMs,
3782
- refreshClosedTail: mode === "signals"
3783
- });
3784
- if (edgesFromMs == null) {
3785
- return null;
3786
- }
3844
+ const requiredFromMs = resolveDerivativesContextRequiredFetchFromMs(
3845
+ {
3846
+ mode,
3847
+ interval,
3848
+ intervalMs,
3849
+ fromMs: cursor,
3850
+ toMs,
3851
+ nowMs: coverageNowMs,
3852
+ dataCoverageKeyExists: coverageKeys.has(key),
3853
+ dataCoverageRanges: coverageRangesBySymbol.get(normalizedSymbol),
3854
+ liquidationCoverageRanges: liquidationCoverageRangesBySymbol.get(normalizedSymbol),
3855
+ edges: edgesBySymbol.get(normalizedSymbol)
3856
+ }
3857
+ );
3858
+ if (requiredFromMs == null) return null;
3787
3859
  return {
3788
3860
  item,
3789
- fromMs: Math.max(coverageFromMs, edgesFromMs)
3861
+ fromMs: requiredFromMs
3790
3862
  };
3791
3863
  }).filter(
3792
3864
  (item) => item != null
@@ -3811,6 +3883,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3811
3883
  );
3812
3884
  let rows = [];
3813
3885
  let missingClosedSymbols = [];
3886
+ let liquidationRowsByMarket = /* @__PURE__ */ new Map();
3814
3887
  for (let attempt = 1; attempt <= maxClosedBarAttempts; attempt += 1) {
3815
3888
  const oiMap = await fetchMetricBatch({
3816
3889
  endpoint: oiPath,
@@ -3830,7 +3903,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3830
3903
  fromMs: group.fromMs,
3831
3904
  toMs
3832
3905
  });
3833
- const liqMap = await fetchMetricBatch({
3906
+ liquidationRowsByMarket = await fetchMetricBatch({
3834
3907
  endpoint: liqPath,
3835
3908
  metric: "liq",
3836
3909
  marketSymbols,
@@ -3845,7 +3918,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3845
3918
  symbol: item.symbol,
3846
3919
  oiRaw: oiMap.get(marketSymbol) ?? [],
3847
3920
  fundingRaw: fundingMap.get(marketSymbol) ?? [],
3848
- liqRaw: liqMap.get(marketSymbol) ?? []
3921
+ liqRaw: liquidationRowsByMarket.get(marketSymbol) ?? []
3849
3922
  });
3850
3923
  return (0, import_indicators.coinalyzePointsToRows)(points, interval, "coinalyze");
3851
3924
  });
@@ -3868,6 +3941,42 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3868
3941
  await (0, import_timescale2.upsertDerivatives)(rows);
3869
3942
  totalRows += rows.length;
3870
3943
  }
3944
+ const confirmedLiquidationWindow = (0, import_indicators.resolveCoinalyzeConfirmedIntradayCoverage)({
3945
+ interval,
3946
+ fromMs: group.fromMs,
3947
+ toMs,
3948
+ nowMs: coverageNowMs
3949
+ });
3950
+ if (confirmedLiquidationWindow) {
3951
+ const metricCoverageRows = missingBatch.map((item) => {
3952
+ const marketSymbol = item.marketSymbol.toUpperCase();
3953
+ const eventRowsCount = (liquidationRowsByMarket.get(marketSymbol) ?? []).filter((point) => {
3954
+ const timestamp = (0, import_indicators.toCoinalyzeTimestampMs)(
3955
+ point.t ?? point.ts ?? point.time ?? point.timestamp
3956
+ );
3957
+ return timestamp != null && timestamp >= confirmedLiquidationWindow.fromMs && timestamp <= confirmedLiquidationWindow.toMs;
3958
+ }).length;
3959
+ return {
3960
+ source: "coinalyze",
3961
+ metric: "liquidation",
3962
+ symbol: item.symbol,
3963
+ interval,
3964
+ fromMs: confirmedLiquidationWindow.fromMs,
3965
+ toMs: confirmedLiquidationWindow.toMs,
3966
+ eventRowsCount
3967
+ };
3968
+ });
3969
+ await (0, import_timescale2.applyDerivativesMetricCoverage)(metricCoverageRows);
3970
+ for (const coverageRow of metricCoverageRows) {
3971
+ const symbol = coverageRow.symbol.toUpperCase();
3972
+ const ranges = liquidationCoverageRangesBySymbol.get(symbol) ?? [];
3973
+ ranges.push({
3974
+ fromMs: coverageRow.fromMs,
3975
+ toMs: coverageRow.toMs
3976
+ });
3977
+ liquidationCoverageRangesBySymbol.set(symbol, ranges);
3978
+ }
3979
+ }
3871
3980
  if (mode === "backtest") {
3872
3981
  const rowsCountBySymbol = /* @__PURE__ */ new Map();
3873
3982
  for (const row of rows) {