@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.
@@ -3227,6 +3227,38 @@ var resolveDerivativesContextMissingFetchFromMs = (params) => {
3227
3227
  return null;
3228
3228
  };
3229
3229
  var resolveDerivativesContextFetchFromMs = (params) => resolveDerivativesContextMissingFetchFromMs(params) ?? (params.refreshClosedTail ? params.toMs : null);
3230
+ var resolveDerivativesContextRequiredFetchFromMs = (params) => {
3231
+ const dataCoverageFromMs = params.dataCoverageKeyExists ? null : params.mode === "signals" ? params.fromMs : resolveDerivativesContextMissingCoverageFetchFromMs({
3232
+ ranges: params.dataCoverageRanges,
3233
+ fromMs: params.fromMs,
3234
+ toMs: params.toMs,
3235
+ intervalMs: params.intervalMs
3236
+ });
3237
+ const edgesFromMs = dataCoverageFromMs == null ? null : resolveDerivativesContextFetchFromMs({
3238
+ edges: params.edges,
3239
+ fromMs: params.fromMs,
3240
+ toMs: params.toMs,
3241
+ intervalMs: params.intervalMs,
3242
+ refreshClosedTail: params.mode === "signals"
3243
+ });
3244
+ const dataFetchFromMs = dataCoverageFromMs != null && edgesFromMs != null ? Math.max(dataCoverageFromMs, edgesFromMs) : null;
3245
+ const confirmedLiquidationWindow = (0, import_indicators.resolveCoinalyzeConfirmedIntradayCoverage)({
3246
+ interval: params.interval,
3247
+ fromMs: params.fromMs,
3248
+ toMs: params.toMs,
3249
+ nowMs: params.nowMs
3250
+ });
3251
+ const liquidationFetchFromMs = confirmedLiquidationWindow ? resolveDerivativesContextMissingCoverageFetchFromMs({
3252
+ ranges: params.liquidationCoverageRanges,
3253
+ fromMs: confirmedLiquidationWindow.fromMs,
3254
+ toMs: confirmedLiquidationWindow.toMs,
3255
+ intervalMs: params.intervalMs
3256
+ }) : null;
3257
+ const fetchStarts = [dataFetchFromMs, liquidationFetchFromMs].filter(
3258
+ (value) => value != null
3259
+ );
3260
+ return fetchStarts.length ? Math.min(...fetchStarts) : null;
3261
+ };
3230
3262
  var countBackfillWindows = (params) => {
3231
3263
  let count = 0;
3232
3264
  let cursor = params.fromMs;
@@ -3562,7 +3594,8 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3562
3594
  mode,
3563
3595
  startMs,
3564
3596
  endMs,
3565
- preloadStartMs: params.preloadStartMs
3597
+ preloadStartMs: params.preloadStartMs,
3598
+ nowMs: Date.now()
3566
3599
  });
3567
3600
  if (safeEndMs <= fromMs) {
3568
3601
  return skippedBackfillResult2();
@@ -3596,15 +3629,33 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3596
3629
  );
3597
3630
  const coverageKeysByInterval = /* @__PURE__ */ new Map();
3598
3631
  const coverageRangesByInterval = /* @__PURE__ */ new Map();
3632
+ const liquidationCoverageRangesByInterval = /* @__PURE__ */ new Map();
3633
+ const coverageNowMs = Date.now();
3599
3634
  await Promise.all(
3600
3635
  intervalWindows.map(async (window2) => {
3601
- const coverageRows = await (0, import_timescale2.getDerivativesBackfillCoverage)({
3602
- source: "coinalyze",
3603
- symbols,
3636
+ const confirmedLiquidationWindow = (0, import_indicators.resolveCoinalyzeConfirmedIntradayCoverage)({
3604
3637
  interval: window2.interval,
3605
3638
  fromMs: window2.fromMs,
3606
- toMs: window2.toMs
3639
+ toMs: window2.toMs,
3640
+ nowMs: coverageNowMs
3607
3641
  });
3642
+ const [coverageRows, liquidationCoverageRows] = await Promise.all([
3643
+ (0, import_timescale2.getDerivativesBackfillCoverage)({
3644
+ source: "coinalyze",
3645
+ symbols,
3646
+ interval: window2.interval,
3647
+ fromMs: window2.fromMs,
3648
+ toMs: window2.toMs
3649
+ }),
3650
+ confirmedLiquidationWindow ? (0, import_timescale2.getDerivativesMetricCoverage)({
3651
+ source: "coinalyze",
3652
+ metric: "liquidation",
3653
+ symbols,
3654
+ interval: window2.interval,
3655
+ fromMs: confirmedLiquidationWindow.fromMs,
3656
+ toMs: confirmedLiquidationWindow.toMs
3657
+ }) : Promise.resolve([])
3658
+ ]);
3608
3659
  coverageKeysByInterval.set(
3609
3660
  window2.interval,
3610
3661
  new Set(
@@ -3626,6 +3677,17 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3626
3677
  rangesBySymbol.set(symbol, ranges);
3627
3678
  }
3628
3679
  coverageRangesByInterval.set(window2.interval, rangesBySymbol);
3680
+ const liquidationRangesBySymbol = /* @__PURE__ */ new Map();
3681
+ for (const row of liquidationCoverageRows) {
3682
+ const symbol = row.symbol.toUpperCase();
3683
+ const ranges = liquidationRangesBySymbol.get(symbol) ?? [];
3684
+ ranges.push({ fromMs: row.fromMs, toMs: row.toMs });
3685
+ liquidationRangesBySymbol.set(symbol, ranges);
3686
+ }
3687
+ liquidationCoverageRangesByInterval.set(
3688
+ window2.interval,
3689
+ liquidationRangesBySymbol
3690
+ );
3629
3691
  })
3630
3692
  );
3631
3693
  const cachedWindows = intervalWindows.reduce(
@@ -3650,7 +3712,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3650
3712
  (symbol) => backfillWindows.every((backfillWindow) => {
3651
3713
  const normalizedSymbol = symbol.toUpperCase();
3652
3714
  const coverageRanges = coverageRangesByInterval.get(window2.interval)?.get(normalizedSymbol) ?? [];
3653
- return hasDerivativesWindowCoverage({
3715
+ const dataCovered = hasDerivativesWindowCoverage({
3654
3716
  edges: edgesBySymbol?.get(normalizedSymbol),
3655
3717
  fromMs: backfillWindow.fromMs,
3656
3718
  toMs: backfillWindow.toMs
@@ -3667,6 +3729,19 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3667
3729
  toMs: backfillWindow.toMs
3668
3730
  })
3669
3731
  );
3732
+ const confirmedLiquidationWindow = (0, import_indicators.resolveCoinalyzeConfirmedIntradayCoverage)({
3733
+ interval: window2.interval,
3734
+ fromMs: backfillWindow.fromMs,
3735
+ toMs: backfillWindow.toMs,
3736
+ nowMs: coverageNowMs
3737
+ });
3738
+ const liquidationCovered = confirmedLiquidationWindow == null || resolveDerivativesContextMissingCoverageFetchFromMs({
3739
+ ranges: liquidationCoverageRangesByInterval.get(window2.interval)?.get(normalizedSymbol) ?? [],
3740
+ fromMs: confirmedLiquidationWindow.fromMs,
3741
+ toMs: confirmedLiquidationWindow.toMs,
3742
+ intervalMs: window2.intervalMs
3743
+ }) == null;
3744
+ return dataCovered && liquidationCovered;
3670
3745
  })
3671
3746
  );
3672
3747
  });
@@ -3751,6 +3826,11 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3751
3826
  coverageKeysByInterval.set(interval, coverageKeys);
3752
3827
  const coverageRangesBySymbol = coverageRangesByInterval.get(interval) ?? /* @__PURE__ */ new Map();
3753
3828
  coverageRangesByInterval.set(interval, coverageRangesBySymbol);
3829
+ const liquidationCoverageRangesBySymbol = liquidationCoverageRangesByInterval.get(interval) ?? /* @__PURE__ */ new Map();
3830
+ liquidationCoverageRangesByInterval.set(
3831
+ interval,
3832
+ liquidationCoverageRangesBySymbol
3833
+ );
3754
3834
  for (let batchIdx = 0; batchIdx < symbolBatches.length; batchIdx += 1) {
3755
3835
  const batch = symbolBatches[batchIdx];
3756
3836
  let cursor = window2.fromMs;
@@ -3763,33 +3843,25 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3763
3843
  fromMs: cursor,
3764
3844
  toMs
3765
3845
  });
3766
- if (coverageKeys.has(key)) {
3767
- return null;
3768
- }
3769
3846
  const normalizedSymbol = item.symbol.toUpperCase();
3770
- const coverageFromMs = mode === "signals" ? cursor : resolveDerivativesContextMissingCoverageFetchFromMs({
3771
- ranges: coverageRangesBySymbol.get(normalizedSymbol),
3772
- fromMs: cursor,
3773
- toMs,
3774
- intervalMs
3775
- });
3776
- if (coverageFromMs == null) {
3777
- return null;
3778
- }
3779
- const edges = edgesBySymbol.get(normalizedSymbol);
3780
- const edgesFromMs = resolveDerivativesContextFetchFromMs({
3781
- edges,
3782
- fromMs: cursor,
3783
- toMs,
3784
- intervalMs,
3785
- refreshClosedTail: mode === "signals"
3786
- });
3787
- if (edgesFromMs == null) {
3788
- return null;
3789
- }
3847
+ const requiredFromMs = resolveDerivativesContextRequiredFetchFromMs(
3848
+ {
3849
+ mode,
3850
+ interval,
3851
+ intervalMs,
3852
+ fromMs: cursor,
3853
+ toMs,
3854
+ nowMs: coverageNowMs,
3855
+ dataCoverageKeyExists: coverageKeys.has(key),
3856
+ dataCoverageRanges: coverageRangesBySymbol.get(normalizedSymbol),
3857
+ liquidationCoverageRanges: liquidationCoverageRangesBySymbol.get(normalizedSymbol),
3858
+ edges: edgesBySymbol.get(normalizedSymbol)
3859
+ }
3860
+ );
3861
+ if (requiredFromMs == null) return null;
3790
3862
  return {
3791
3863
  item,
3792
- fromMs: Math.max(coverageFromMs, edgesFromMs)
3864
+ fromMs: requiredFromMs
3793
3865
  };
3794
3866
  }).filter(
3795
3867
  (item) => item != null
@@ -3814,6 +3886,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3814
3886
  );
3815
3887
  let rows = [];
3816
3888
  let missingClosedSymbols = [];
3889
+ let liquidationRowsByMarket = /* @__PURE__ */ new Map();
3817
3890
  for (let attempt = 1; attempt <= maxClosedBarAttempts; attempt += 1) {
3818
3891
  const oiMap = await fetchMetricBatch({
3819
3892
  endpoint: oiPath,
@@ -3833,7 +3906,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3833
3906
  fromMs: group.fromMs,
3834
3907
  toMs
3835
3908
  });
3836
- const liqMap = await fetchMetricBatch({
3909
+ liquidationRowsByMarket = await fetchMetricBatch({
3837
3910
  endpoint: liqPath,
3838
3911
  metric: "liq",
3839
3912
  marketSymbols,
@@ -3848,7 +3921,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3848
3921
  symbol: item.symbol,
3849
3922
  oiRaw: oiMap.get(marketSymbol) ?? [],
3850
3923
  fundingRaw: fundingMap.get(marketSymbol) ?? [],
3851
- liqRaw: liqMap.get(marketSymbol) ?? []
3924
+ liqRaw: liquidationRowsByMarket.get(marketSymbol) ?? []
3852
3925
  });
3853
3926
  return (0, import_indicators.coinalyzePointsToRows)(points, interval, "coinalyze");
3854
3927
  });
@@ -3871,6 +3944,42 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3871
3944
  await (0, import_timescale2.upsertDerivatives)(rows);
3872
3945
  totalRows += rows.length;
3873
3946
  }
3947
+ const confirmedLiquidationWindow = (0, import_indicators.resolveCoinalyzeConfirmedIntradayCoverage)({
3948
+ interval,
3949
+ fromMs: group.fromMs,
3950
+ toMs,
3951
+ nowMs: coverageNowMs
3952
+ });
3953
+ if (confirmedLiquidationWindow) {
3954
+ const metricCoverageRows = missingBatch.map((item) => {
3955
+ const marketSymbol = item.marketSymbol.toUpperCase();
3956
+ const eventRowsCount = (liquidationRowsByMarket.get(marketSymbol) ?? []).filter((point) => {
3957
+ const timestamp = (0, import_indicators.toCoinalyzeTimestampMs)(
3958
+ point.t ?? point.ts ?? point.time ?? point.timestamp
3959
+ );
3960
+ return timestamp != null && timestamp >= confirmedLiquidationWindow.fromMs && timestamp <= confirmedLiquidationWindow.toMs;
3961
+ }).length;
3962
+ return {
3963
+ source: "coinalyze",
3964
+ metric: "liquidation",
3965
+ symbol: item.symbol,
3966
+ interval,
3967
+ fromMs: confirmedLiquidationWindow.fromMs,
3968
+ toMs: confirmedLiquidationWindow.toMs,
3969
+ eventRowsCount
3970
+ };
3971
+ });
3972
+ await (0, import_timescale2.applyDerivativesMetricCoverage)(metricCoverageRows);
3973
+ for (const coverageRow of metricCoverageRows) {
3974
+ const symbol = coverageRow.symbol.toUpperCase();
3975
+ const ranges = liquidationCoverageRangesBySymbol.get(symbol) ?? [];
3976
+ ranges.push({
3977
+ fromMs: coverageRow.fromMs,
3978
+ toMs: coverageRow.toMs
3979
+ });
3980
+ liquidationCoverageRangesBySymbol.set(symbol, ranges);
3981
+ }
3982
+ }
3874
3983
  if (mode === "backtest") {
3875
3984
  const rowsCountBySymbol = /* @__PURE__ */ new Map();
3876
3985
  for (const row of rows) {
@@ -3232,6 +3232,38 @@ var resolveDerivativesContextMissingFetchFromMs = (params) => {
3232
3232
  return null;
3233
3233
  };
3234
3234
  var resolveDerivativesContextFetchFromMs = (params) => resolveDerivativesContextMissingFetchFromMs(params) ?? (params.refreshClosedTail ? params.toMs : null);
3235
+ var resolveDerivativesContextRequiredFetchFromMs = (params) => {
3236
+ const dataCoverageFromMs = params.dataCoverageKeyExists ? null : params.mode === "signals" ? params.fromMs : resolveDerivativesContextMissingCoverageFetchFromMs({
3237
+ ranges: params.dataCoverageRanges,
3238
+ fromMs: params.fromMs,
3239
+ toMs: params.toMs,
3240
+ intervalMs: params.intervalMs
3241
+ });
3242
+ const edgesFromMs = dataCoverageFromMs == null ? null : resolveDerivativesContextFetchFromMs({
3243
+ edges: params.edges,
3244
+ fromMs: params.fromMs,
3245
+ toMs: params.toMs,
3246
+ intervalMs: params.intervalMs,
3247
+ refreshClosedTail: params.mode === "signals"
3248
+ });
3249
+ const dataFetchFromMs = dataCoverageFromMs != null && edgesFromMs != null ? Math.max(dataCoverageFromMs, edgesFromMs) : null;
3250
+ const confirmedLiquidationWindow = (0, import_indicators.resolveCoinalyzeConfirmedIntradayCoverage)({
3251
+ interval: params.interval,
3252
+ fromMs: params.fromMs,
3253
+ toMs: params.toMs,
3254
+ nowMs: params.nowMs
3255
+ });
3256
+ const liquidationFetchFromMs = confirmedLiquidationWindow ? resolveDerivativesContextMissingCoverageFetchFromMs({
3257
+ ranges: params.liquidationCoverageRanges,
3258
+ fromMs: confirmedLiquidationWindow.fromMs,
3259
+ toMs: confirmedLiquidationWindow.toMs,
3260
+ intervalMs: params.intervalMs
3261
+ }) : null;
3262
+ const fetchStarts = [dataFetchFromMs, liquidationFetchFromMs].filter(
3263
+ (value) => value != null
3264
+ );
3265
+ return fetchStarts.length ? Math.min(...fetchStarts) : null;
3266
+ };
3235
3267
  var countBackfillWindows = (params) => {
3236
3268
  let count = 0;
3237
3269
  let cursor = params.fromMs;
@@ -3567,7 +3599,8 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3567
3599
  mode,
3568
3600
  startMs,
3569
3601
  endMs,
3570
- preloadStartMs: params.preloadStartMs
3602
+ preloadStartMs: params.preloadStartMs,
3603
+ nowMs: Date.now()
3571
3604
  });
3572
3605
  if (safeEndMs <= fromMs) {
3573
3606
  return skippedBackfillResult2();
@@ -3601,15 +3634,33 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3601
3634
  );
3602
3635
  const coverageKeysByInterval = /* @__PURE__ */ new Map();
3603
3636
  const coverageRangesByInterval = /* @__PURE__ */ new Map();
3637
+ const liquidationCoverageRangesByInterval = /* @__PURE__ */ new Map();
3638
+ const coverageNowMs = Date.now();
3604
3639
  await Promise.all(
3605
3640
  intervalWindows.map(async (window2) => {
3606
- const coverageRows = await (0, import_timescale2.getDerivativesBackfillCoverage)({
3607
- source: "coinalyze",
3608
- symbols,
3641
+ const confirmedLiquidationWindow = (0, import_indicators.resolveCoinalyzeConfirmedIntradayCoverage)({
3609
3642
  interval: window2.interval,
3610
3643
  fromMs: window2.fromMs,
3611
- toMs: window2.toMs
3644
+ toMs: window2.toMs,
3645
+ nowMs: coverageNowMs
3612
3646
  });
3647
+ const [coverageRows, liquidationCoverageRows] = await Promise.all([
3648
+ (0, import_timescale2.getDerivativesBackfillCoverage)({
3649
+ source: "coinalyze",
3650
+ symbols,
3651
+ interval: window2.interval,
3652
+ fromMs: window2.fromMs,
3653
+ toMs: window2.toMs
3654
+ }),
3655
+ confirmedLiquidationWindow ? (0, import_timescale2.getDerivativesMetricCoverage)({
3656
+ source: "coinalyze",
3657
+ metric: "liquidation",
3658
+ symbols,
3659
+ interval: window2.interval,
3660
+ fromMs: confirmedLiquidationWindow.fromMs,
3661
+ toMs: confirmedLiquidationWindow.toMs
3662
+ }) : Promise.resolve([])
3663
+ ]);
3613
3664
  coverageKeysByInterval.set(
3614
3665
  window2.interval,
3615
3666
  new Set(
@@ -3631,6 +3682,17 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3631
3682
  rangesBySymbol.set(symbol, ranges);
3632
3683
  }
3633
3684
  coverageRangesByInterval.set(window2.interval, rangesBySymbol);
3685
+ const liquidationRangesBySymbol = /* @__PURE__ */ new Map();
3686
+ for (const row of liquidationCoverageRows) {
3687
+ const symbol = row.symbol.toUpperCase();
3688
+ const ranges = liquidationRangesBySymbol.get(symbol) ?? [];
3689
+ ranges.push({ fromMs: row.fromMs, toMs: row.toMs });
3690
+ liquidationRangesBySymbol.set(symbol, ranges);
3691
+ }
3692
+ liquidationCoverageRangesByInterval.set(
3693
+ window2.interval,
3694
+ liquidationRangesBySymbol
3695
+ );
3634
3696
  })
3635
3697
  );
3636
3698
  const cachedWindows = intervalWindows.reduce(
@@ -3655,7 +3717,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3655
3717
  (symbol) => backfillWindows.every((backfillWindow) => {
3656
3718
  const normalizedSymbol = symbol.toUpperCase();
3657
3719
  const coverageRanges = coverageRangesByInterval.get(window2.interval)?.get(normalizedSymbol) ?? [];
3658
- return hasDerivativesWindowCoverage({
3720
+ const dataCovered = hasDerivativesWindowCoverage({
3659
3721
  edges: edgesBySymbol?.get(normalizedSymbol),
3660
3722
  fromMs: backfillWindow.fromMs,
3661
3723
  toMs: backfillWindow.toMs
@@ -3672,6 +3734,19 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3672
3734
  toMs: backfillWindow.toMs
3673
3735
  })
3674
3736
  );
3737
+ const confirmedLiquidationWindow = (0, import_indicators.resolveCoinalyzeConfirmedIntradayCoverage)({
3738
+ interval: window2.interval,
3739
+ fromMs: backfillWindow.fromMs,
3740
+ toMs: backfillWindow.toMs,
3741
+ nowMs: coverageNowMs
3742
+ });
3743
+ const liquidationCovered = confirmedLiquidationWindow == null || resolveDerivativesContextMissingCoverageFetchFromMs({
3744
+ ranges: liquidationCoverageRangesByInterval.get(window2.interval)?.get(normalizedSymbol) ?? [],
3745
+ fromMs: confirmedLiquidationWindow.fromMs,
3746
+ toMs: confirmedLiquidationWindow.toMs,
3747
+ intervalMs: window2.intervalMs
3748
+ }) == null;
3749
+ return dataCovered && liquidationCovered;
3675
3750
  })
3676
3751
  );
3677
3752
  });
@@ -3756,6 +3831,11 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3756
3831
  coverageKeysByInterval.set(interval2, coverageKeys);
3757
3832
  const coverageRangesBySymbol = coverageRangesByInterval.get(interval2) ?? /* @__PURE__ */ new Map();
3758
3833
  coverageRangesByInterval.set(interval2, coverageRangesBySymbol);
3834
+ const liquidationCoverageRangesBySymbol = liquidationCoverageRangesByInterval.get(interval2) ?? /* @__PURE__ */ new Map();
3835
+ liquidationCoverageRangesByInterval.set(
3836
+ interval2,
3837
+ liquidationCoverageRangesBySymbol
3838
+ );
3759
3839
  for (let batchIdx = 0; batchIdx < symbolBatches.length; batchIdx += 1) {
3760
3840
  const batch = symbolBatches[batchIdx];
3761
3841
  let cursor = window2.fromMs;
@@ -3768,33 +3848,25 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3768
3848
  fromMs: cursor,
3769
3849
  toMs
3770
3850
  });
3771
- if (coverageKeys.has(key)) {
3772
- return null;
3773
- }
3774
3851
  const normalizedSymbol = item.symbol.toUpperCase();
3775
- const coverageFromMs = mode === "signals" ? cursor : resolveDerivativesContextMissingCoverageFetchFromMs({
3776
- ranges: coverageRangesBySymbol.get(normalizedSymbol),
3777
- fromMs: cursor,
3778
- toMs,
3779
- intervalMs
3780
- });
3781
- if (coverageFromMs == null) {
3782
- return null;
3783
- }
3784
- const edges = edgesBySymbol.get(normalizedSymbol);
3785
- const edgesFromMs = resolveDerivativesContextFetchFromMs({
3786
- edges,
3787
- fromMs: cursor,
3788
- toMs,
3789
- intervalMs,
3790
- refreshClosedTail: mode === "signals"
3791
- });
3792
- if (edgesFromMs == null) {
3793
- return null;
3794
- }
3852
+ const requiredFromMs = resolveDerivativesContextRequiredFetchFromMs(
3853
+ {
3854
+ mode,
3855
+ interval: interval2,
3856
+ intervalMs,
3857
+ fromMs: cursor,
3858
+ toMs,
3859
+ nowMs: coverageNowMs,
3860
+ dataCoverageKeyExists: coverageKeys.has(key),
3861
+ dataCoverageRanges: coverageRangesBySymbol.get(normalizedSymbol),
3862
+ liquidationCoverageRanges: liquidationCoverageRangesBySymbol.get(normalizedSymbol),
3863
+ edges: edgesBySymbol.get(normalizedSymbol)
3864
+ }
3865
+ );
3866
+ if (requiredFromMs == null) return null;
3795
3867
  return {
3796
3868
  item,
3797
- fromMs: Math.max(coverageFromMs, edgesFromMs)
3869
+ fromMs: requiredFromMs
3798
3870
  };
3799
3871
  }).filter(
3800
3872
  (item) => item != null
@@ -3819,6 +3891,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3819
3891
  );
3820
3892
  let rows = [];
3821
3893
  let missingClosedSymbols = [];
3894
+ let liquidationRowsByMarket = /* @__PURE__ */ new Map();
3822
3895
  for (let attempt = 1; attempt <= maxClosedBarAttempts; attempt += 1) {
3823
3896
  const oiMap = await fetchMetricBatch({
3824
3897
  endpoint: oiPath,
@@ -3838,7 +3911,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3838
3911
  fromMs: group.fromMs,
3839
3912
  toMs
3840
3913
  });
3841
- const liqMap = await fetchMetricBatch({
3914
+ liquidationRowsByMarket = await fetchMetricBatch({
3842
3915
  endpoint: liqPath,
3843
3916
  metric: "liq",
3844
3917
  marketSymbols,
@@ -3853,7 +3926,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3853
3926
  symbol: item.symbol,
3854
3927
  oiRaw: oiMap.get(marketSymbol) ?? [],
3855
3928
  fundingRaw: fundingMap.get(marketSymbol) ?? [],
3856
- liqRaw: liqMap.get(marketSymbol) ?? []
3929
+ liqRaw: liquidationRowsByMarket.get(marketSymbol) ?? []
3857
3930
  });
3858
3931
  return (0, import_indicators.coinalyzePointsToRows)(points, interval2, "coinalyze");
3859
3932
  });
@@ -3876,6 +3949,42 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3876
3949
  await (0, import_timescale2.upsertDerivatives)(rows);
3877
3950
  totalRows += rows.length;
3878
3951
  }
3952
+ const confirmedLiquidationWindow = (0, import_indicators.resolveCoinalyzeConfirmedIntradayCoverage)({
3953
+ interval: interval2,
3954
+ fromMs: group.fromMs,
3955
+ toMs,
3956
+ nowMs: coverageNowMs
3957
+ });
3958
+ if (confirmedLiquidationWindow) {
3959
+ const metricCoverageRows = missingBatch.map((item) => {
3960
+ const marketSymbol = item.marketSymbol.toUpperCase();
3961
+ const eventRowsCount = (liquidationRowsByMarket.get(marketSymbol) ?? []).filter((point) => {
3962
+ const timestamp = (0, import_indicators.toCoinalyzeTimestampMs)(
3963
+ point.t ?? point.ts ?? point.time ?? point.timestamp
3964
+ );
3965
+ return timestamp != null && timestamp >= confirmedLiquidationWindow.fromMs && timestamp <= confirmedLiquidationWindow.toMs;
3966
+ }).length;
3967
+ return {
3968
+ source: "coinalyze",
3969
+ metric: "liquidation",
3970
+ symbol: item.symbol,
3971
+ interval: interval2,
3972
+ fromMs: confirmedLiquidationWindow.fromMs,
3973
+ toMs: confirmedLiquidationWindow.toMs,
3974
+ eventRowsCount
3975
+ };
3976
+ });
3977
+ await (0, import_timescale2.applyDerivativesMetricCoverage)(metricCoverageRows);
3978
+ for (const coverageRow of metricCoverageRows) {
3979
+ const symbol = coverageRow.symbol.toUpperCase();
3980
+ const ranges = liquidationCoverageRangesBySymbol.get(symbol) ?? [];
3981
+ ranges.push({
3982
+ fromMs: coverageRow.fromMs,
3983
+ toMs: coverageRow.toMs
3984
+ });
3985
+ liquidationCoverageRangesBySymbol.set(symbol, ranges);
3986
+ }
3987
+ }
3879
3988
  if (mode === "backtest") {
3880
3989
  const rowsCountBySymbol = /* @__PURE__ */ new Map();
3881
3990
  for (const row of rows) {