@tradejs/infra 2.0.16 → 2.0.17

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.
@@ -201,12 +201,21 @@ declare function upsertHyperliquidWhaleWalletCoverage(params: {
201
201
  universeFingerprint: string;
202
202
  whaleRegistryFingerprint: string;
203
203
  }): Promise<void>;
204
+ type HyperliquidWhaleCoverageRebuildProgress = {
205
+ chunkIndex: number;
206
+ totalChunks: number;
207
+ completedBuckets: number;
208
+ totalBuckets: number;
209
+ rows: number;
210
+ };
204
211
  declare function rebuildHyperliquidWhaleCoverageRows(params: {
205
212
  fromMs: number;
206
213
  toMs: number;
207
214
  expectedWhales: number;
208
215
  universeFingerprint: string;
209
216
  whaleRegistryFingerprint: string;
217
+ chunkMinutes?: number;
218
+ onProgress?: (progress: HyperliquidWhaleCoverageRebuildProgress) => void;
210
219
  }): Promise<number>;
211
220
  declare function upsertHyperliquidWhaleCoverageRows(rows: HyperliquidWhaleCoverageRow[]): Promise<void>;
212
221
  type HyperliquidWhaleCoverageSeriesRow = {
@@ -464,4 +473,4 @@ declare function findContinuityGap(provider: string, symbol: string, interval: n
464
473
  diffSeconds: number;
465
474
  } | null>;
466
475
 
467
- export { type CandleRow, type DeprecatedMarketContextCleanupItem, type DerivativesMetricCoverageMetric, type HyperliquidWhaleCoverageSeriesRow, type HyperliquidWhaleFlowAggregate, type HyperliquidWhaleFlowSeriesRow, type HyperliquidWhaleWalletCoverageStatus, type MarketFeatureAsOf, type TimescaleMarketContextQueryOptions, type TimescaleMarketContextSource, applyDerivativesMetricCoverage, cleanupDeprecatedMarketContext, closeTimescalePool, configureTimescaleMarketContextSchemaMode, deleteCandles, ensureBinanceMarketSchema, ensureCoinMarketCapContextSchema, ensureDerivativesSchema, ensureHyperliquidWhaleSchema, ensureMarketContextSchemas, findContinuityGap, getCandlesRange, getDataEdges, getDataEdgesForSymbols, getDerivativesBackfillCoverage, getDerivativesDataEdgesForSymbols, getDerivativesMetricCoverage, getDerivativesRangeForSymbols, getDerivativesSummary, getDerivativesWindow, getHyperliquidWhaleCoverageSeriesRows, getHyperliquidWhaleFlowAggregate, getHyperliquidWhaleFlowSeriesRows, getHyperliquidWhaleWalletCoverage, getLatestMarketBreadth, getLatestMarketCmcExchangeLiquidityContext, getLatestMarketCmcFearGreedContext, getLatestMarketCmcIndexContexts, getLatestMarketGlobalContext, getLatestMarketReferenceAssetContexts, getLatestMarketTradeFlow, getMarketBreadthCoverage, getMarketCmcExchangeLiquidityContextCoverage, getMarketCmcFearGreedContextCoverage, getMarketCmcIndexContextCoverage, getMarketContextBackfillCoverage, getMarketGlobalContextCoverage, getMarketReferenceAssetContextCoverage, getMarketTradeFlowCoverage, getSpreadRangeForSymbols, getSpreadSummary, hasHyperliquidWhaleBackfillCoverage, rebuildHyperliquidWhaleCoverageRows, rebuildHyperliquidWhaleFlowRows, toRows, upsertCandles, upsertDerivatives, upsertDerivativesBackfillCoverage, upsertHyperliquidWhaleCoverageRows, upsertHyperliquidWhaleFlowRows, upsertHyperliquidWhaleTradeEvents, upsertHyperliquidWhaleWalletCoverage, upsertMarketBreadthRows, upsertMarketCmcExchangeLiquidityContextRows, upsertMarketCmcFearGreedContextRows, upsertMarketCmcIndexContextRows, upsertMarketContextBackfillCoverage, upsertMarketGlobalContextRows, upsertMarketReferenceAssetContextRows, upsertMarketTradeFlowRows, upsertSpreadRows, waitForDbReady };
476
+ export { type CandleRow, type DeprecatedMarketContextCleanupItem, type DerivativesMetricCoverageMetric, type HyperliquidWhaleCoverageRebuildProgress, type HyperliquidWhaleCoverageSeriesRow, type HyperliquidWhaleFlowAggregate, type HyperliquidWhaleFlowSeriesRow, type HyperliquidWhaleWalletCoverageStatus, type MarketFeatureAsOf, type TimescaleMarketContextQueryOptions, type TimescaleMarketContextSource, applyDerivativesMetricCoverage, cleanupDeprecatedMarketContext, closeTimescalePool, configureTimescaleMarketContextSchemaMode, deleteCandles, ensureBinanceMarketSchema, ensureCoinMarketCapContextSchema, ensureDerivativesSchema, ensureHyperliquidWhaleSchema, ensureMarketContextSchemas, findContinuityGap, getCandlesRange, getDataEdges, getDataEdgesForSymbols, getDerivativesBackfillCoverage, getDerivativesDataEdgesForSymbols, getDerivativesMetricCoverage, getDerivativesRangeForSymbols, getDerivativesSummary, getDerivativesWindow, getHyperliquidWhaleCoverageSeriesRows, getHyperliquidWhaleFlowAggregate, getHyperliquidWhaleFlowSeriesRows, getHyperliquidWhaleWalletCoverage, getLatestMarketBreadth, getLatestMarketCmcExchangeLiquidityContext, getLatestMarketCmcFearGreedContext, getLatestMarketCmcIndexContexts, getLatestMarketGlobalContext, getLatestMarketReferenceAssetContexts, getLatestMarketTradeFlow, getMarketBreadthCoverage, getMarketCmcExchangeLiquidityContextCoverage, getMarketCmcFearGreedContextCoverage, getMarketCmcIndexContextCoverage, getMarketContextBackfillCoverage, getMarketGlobalContextCoverage, getMarketReferenceAssetContextCoverage, getMarketTradeFlowCoverage, getSpreadRangeForSymbols, getSpreadSummary, hasHyperliquidWhaleBackfillCoverage, rebuildHyperliquidWhaleCoverageRows, rebuildHyperliquidWhaleFlowRows, toRows, upsertCandles, upsertDerivatives, upsertDerivativesBackfillCoverage, upsertHyperliquidWhaleCoverageRows, upsertHyperliquidWhaleFlowRows, upsertHyperliquidWhaleTradeEvents, upsertHyperliquidWhaleWalletCoverage, upsertMarketBreadthRows, upsertMarketCmcExchangeLiquidityContextRows, upsertMarketCmcFearGreedContextRows, upsertMarketCmcIndexContextRows, upsertMarketContextBackfillCoverage, upsertMarketGlobalContextRows, upsertMarketReferenceAssetContextRows, upsertMarketTradeFlowRows, upsertSpreadRows, waitForDbReady };
@@ -201,12 +201,21 @@ declare function upsertHyperliquidWhaleWalletCoverage(params: {
201
201
  universeFingerprint: string;
202
202
  whaleRegistryFingerprint: string;
203
203
  }): Promise<void>;
204
+ type HyperliquidWhaleCoverageRebuildProgress = {
205
+ chunkIndex: number;
206
+ totalChunks: number;
207
+ completedBuckets: number;
208
+ totalBuckets: number;
209
+ rows: number;
210
+ };
204
211
  declare function rebuildHyperliquidWhaleCoverageRows(params: {
205
212
  fromMs: number;
206
213
  toMs: number;
207
214
  expectedWhales: number;
208
215
  universeFingerprint: string;
209
216
  whaleRegistryFingerprint: string;
217
+ chunkMinutes?: number;
218
+ onProgress?: (progress: HyperliquidWhaleCoverageRebuildProgress) => void;
210
219
  }): Promise<number>;
211
220
  declare function upsertHyperliquidWhaleCoverageRows(rows: HyperliquidWhaleCoverageRow[]): Promise<void>;
212
221
  type HyperliquidWhaleCoverageSeriesRow = {
@@ -464,4 +473,4 @@ declare function findContinuityGap(provider: string, symbol: string, interval: n
464
473
  diffSeconds: number;
465
474
  } | null>;
466
475
 
467
- export { type CandleRow, type DeprecatedMarketContextCleanupItem, type DerivativesMetricCoverageMetric, type HyperliquidWhaleCoverageSeriesRow, type HyperliquidWhaleFlowAggregate, type HyperliquidWhaleFlowSeriesRow, type HyperliquidWhaleWalletCoverageStatus, type MarketFeatureAsOf, type TimescaleMarketContextQueryOptions, type TimescaleMarketContextSource, applyDerivativesMetricCoverage, cleanupDeprecatedMarketContext, closeTimescalePool, configureTimescaleMarketContextSchemaMode, deleteCandles, ensureBinanceMarketSchema, ensureCoinMarketCapContextSchema, ensureDerivativesSchema, ensureHyperliquidWhaleSchema, ensureMarketContextSchemas, findContinuityGap, getCandlesRange, getDataEdges, getDataEdgesForSymbols, getDerivativesBackfillCoverage, getDerivativesDataEdgesForSymbols, getDerivativesMetricCoverage, getDerivativesRangeForSymbols, getDerivativesSummary, getDerivativesWindow, getHyperliquidWhaleCoverageSeriesRows, getHyperliquidWhaleFlowAggregate, getHyperliquidWhaleFlowSeriesRows, getHyperliquidWhaleWalletCoverage, getLatestMarketBreadth, getLatestMarketCmcExchangeLiquidityContext, getLatestMarketCmcFearGreedContext, getLatestMarketCmcIndexContexts, getLatestMarketGlobalContext, getLatestMarketReferenceAssetContexts, getLatestMarketTradeFlow, getMarketBreadthCoverage, getMarketCmcExchangeLiquidityContextCoverage, getMarketCmcFearGreedContextCoverage, getMarketCmcIndexContextCoverage, getMarketContextBackfillCoverage, getMarketGlobalContextCoverage, getMarketReferenceAssetContextCoverage, getMarketTradeFlowCoverage, getSpreadRangeForSymbols, getSpreadSummary, hasHyperliquidWhaleBackfillCoverage, rebuildHyperliquidWhaleCoverageRows, rebuildHyperliquidWhaleFlowRows, toRows, upsertCandles, upsertDerivatives, upsertDerivativesBackfillCoverage, upsertHyperliquidWhaleCoverageRows, upsertHyperliquidWhaleFlowRows, upsertHyperliquidWhaleTradeEvents, upsertHyperliquidWhaleWalletCoverage, upsertMarketBreadthRows, upsertMarketCmcExchangeLiquidityContextRows, upsertMarketCmcFearGreedContextRows, upsertMarketCmcIndexContextRows, upsertMarketContextBackfillCoverage, upsertMarketGlobalContextRows, upsertMarketReferenceAssetContextRows, upsertMarketTradeFlowRows, upsertSpreadRows, waitForDbReady };
476
+ export { type CandleRow, type DeprecatedMarketContextCleanupItem, type DerivativesMetricCoverageMetric, type HyperliquidWhaleCoverageRebuildProgress, type HyperliquidWhaleCoverageSeriesRow, type HyperliquidWhaleFlowAggregate, type HyperliquidWhaleFlowSeriesRow, type HyperliquidWhaleWalletCoverageStatus, type MarketFeatureAsOf, type TimescaleMarketContextQueryOptions, type TimescaleMarketContextSource, applyDerivativesMetricCoverage, cleanupDeprecatedMarketContext, closeTimescalePool, configureTimescaleMarketContextSchemaMode, deleteCandles, ensureBinanceMarketSchema, ensureCoinMarketCapContextSchema, ensureDerivativesSchema, ensureHyperliquidWhaleSchema, ensureMarketContextSchemas, findContinuityGap, getCandlesRange, getDataEdges, getDataEdgesForSymbols, getDerivativesBackfillCoverage, getDerivativesDataEdgesForSymbols, getDerivativesMetricCoverage, getDerivativesRangeForSymbols, getDerivativesSummary, getDerivativesWindow, getHyperliquidWhaleCoverageSeriesRows, getHyperliquidWhaleFlowAggregate, getHyperliquidWhaleFlowSeriesRows, getHyperliquidWhaleWalletCoverage, getLatestMarketBreadth, getLatestMarketCmcExchangeLiquidityContext, getLatestMarketCmcFearGreedContext, getLatestMarketCmcIndexContexts, getLatestMarketGlobalContext, getLatestMarketReferenceAssetContexts, getLatestMarketTradeFlow, getMarketBreadthCoverage, getMarketCmcExchangeLiquidityContextCoverage, getMarketCmcFearGreedContextCoverage, getMarketCmcIndexContextCoverage, getMarketContextBackfillCoverage, getMarketGlobalContextCoverage, getMarketReferenceAssetContextCoverage, getMarketTradeFlowCoverage, getSpreadRangeForSymbols, getSpreadSummary, hasHyperliquidWhaleBackfillCoverage, rebuildHyperliquidWhaleCoverageRows, rebuildHyperliquidWhaleFlowRows, toRows, upsertCandles, upsertDerivatives, upsertDerivativesBackfillCoverage, upsertHyperliquidWhaleCoverageRows, upsertHyperliquidWhaleFlowRows, upsertHyperliquidWhaleTradeEvents, upsertHyperliquidWhaleWalletCoverage, upsertMarketBreadthRows, upsertMarketCmcExchangeLiquidityContextRows, upsertMarketCmcFearGreedContextRows, upsertMarketCmcIndexContextRows, upsertMarketContextBackfillCoverage, upsertMarketGlobalContextRows, upsertMarketReferenceAssetContextRows, upsertMarketTradeFlowRows, upsertSpreadRows, waitForDbReady };
package/dist/timescale.js CHANGED
@@ -2772,71 +2772,154 @@ async function upsertHyperliquidWhaleWalletCoverage(params) {
2772
2772
  async function rebuildHyperliquidWhaleCoverageRows(params) {
2773
2773
  await ensureHyperliquidWhaleSchema();
2774
2774
  if (params.toMs <= params.fromMs) return 0;
2775
- const result = await getPool().query(
2776
- `
2777
- WITH buckets AS (
2778
- SELECT generate_series(
2779
- to_timestamp($3/1000.0),
2780
- to_timestamp($4/1000.0) - interval '1 minute',
2781
- interval '1 minute'
2782
- ) AS ts
2783
- ), coverage AS (
2775
+ const minuteMs = 6e4;
2776
+ const defaultChunkMinutes = 7 * 24 * 60;
2777
+ const chunkMinutes = Number.isFinite(params.chunkMinutes) && Number(params.chunkMinutes) > 0 ? Math.floor(Number(params.chunkMinutes)) : defaultChunkMinutes;
2778
+ const chunkMs = chunkMinutes * minuteMs;
2779
+ const totalBuckets = Math.ceil((params.toMs - params.fromMs) / minuteMs);
2780
+ const totalChunks = Math.ceil((params.toMs - params.fromMs) / chunkMs);
2781
+ let completedBuckets = 0;
2782
+ let rows = 0;
2783
+ for (let chunkIndex = 0, chunkFromMs = params.fromMs; chunkFromMs < params.toMs; chunkIndex += 1, chunkFromMs += chunkMs) {
2784
+ const chunkToMs = Math.min(params.toMs, chunkFromMs + chunkMs);
2785
+ const result = await getPool().query(
2786
+ `
2787
+ WITH normalized_ranges AS (
2788
+ SELECT
2789
+ address,
2790
+ GREATEST(
2791
+ to_timestamp($3/1000.0),
2792
+ date_trunc('minute', covered_from_ts) +
2793
+ CASE
2794
+ WHEN covered_from_ts = date_trunc('minute', covered_from_ts)
2795
+ THEN interval '0 minutes'
2796
+ ELSE interval '1 minute'
2797
+ END
2798
+ ) AS range_start,
2799
+ LEAST(
2800
+ to_timestamp($4/1000.0),
2801
+ date_trunc('minute', covered_to_ts)
2802
+ ) AS range_end
2803
+ FROM hyperliquid_whale_wallet_coverage
2804
+ WHERE universe_fingerprint = $1
2805
+ AND whale_registry_fingerprint = $2
2806
+ AND data_model_version = $6
2807
+ AND status IN ('complete', 'truncated')
2808
+ AND covered_from_ts < to_timestamp($4/1000.0)
2809
+ AND covered_to_ts > to_timestamp($3/1000.0)
2810
+ ), eligible_ranges AS (
2811
+ SELECT *
2812
+ FROM normalized_ranges
2813
+ WHERE range_start < range_end
2814
+ ), ordered_ranges AS (
2815
+ SELECT
2816
+ *,
2817
+ MAX(range_end) OVER (
2818
+ PARTITION BY address
2819
+ ORDER BY range_start, range_end
2820
+ ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
2821
+ ) AS previous_max_end
2822
+ FROM eligible_ranges
2823
+ ), marked_ranges AS (
2824
+ SELECT
2825
+ *,
2826
+ SUM(
2827
+ CASE
2828
+ WHEN previous_max_end IS NULL OR range_start > previous_max_end
2829
+ THEN 1
2830
+ ELSE 0
2831
+ END
2832
+ ) OVER (
2833
+ PARTITION BY address
2834
+ ORDER BY range_start, range_end
2835
+ ) AS range_group
2836
+ FROM ordered_ranges
2837
+ ), merged_ranges AS (
2838
+ SELECT
2839
+ address,
2840
+ MIN(range_start) AS range_start,
2841
+ MAX(range_end) AS range_end
2842
+ FROM marked_ranges
2843
+ GROUP BY address, range_group
2844
+ ), deltas AS (
2845
+ SELECT range_start AS ts, 1 AS delta
2846
+ FROM merged_ranges
2847
+ UNION ALL
2848
+ SELECT range_end AS ts, -1 AS delta
2849
+ FROM merged_ranges
2850
+ ), bucket_deltas AS (
2851
+ SELECT ts, SUM(delta)::int AS delta
2852
+ FROM deltas
2853
+ GROUP BY ts
2854
+ ), buckets AS (
2855
+ SELECT generate_series(
2856
+ to_timestamp($3/1000.0),
2857
+ to_timestamp($4/1000.0) - interval '1 minute',
2858
+ interval '1 minute'
2859
+ ) AS ts
2860
+ ), coverage AS (
2861
+ SELECT
2862
+ buckets.ts,
2863
+ SUM(COALESCE(bucket_deltas.delta, 0)) OVER (
2864
+ ORDER BY buckets.ts
2865
+ ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
2866
+ )::int AS covered_whales
2867
+ FROM buckets
2868
+ LEFT JOIN bucket_deltas USING (ts)
2869
+ )
2870
+ INSERT INTO hyperliquid_whale_coverage_1m (
2871
+ ts,
2872
+ covered_whales,
2873
+ expected_whales,
2874
+ coverage_pct,
2875
+ universe_fingerprint,
2876
+ whale_registry_fingerprint,
2877
+ source,
2878
+ data_model_version
2879
+ )
2784
2880
  SELECT
2785
- buckets.ts,
2786
- COUNT(DISTINCT wallets.address)::int AS covered_whales
2787
- FROM buckets
2788
- LEFT JOIN hyperliquid_whale_wallet_coverage wallets
2789
- ON wallets.universe_fingerprint = $1
2790
- AND wallets.whale_registry_fingerprint = $2
2791
- AND wallets.data_model_version = $6
2792
- AND wallets.status IN ('complete', 'truncated')
2793
- AND wallets.covered_from_ts <= buckets.ts
2794
- AND wallets.covered_to_ts >= buckets.ts + interval '1 minute'
2795
- GROUP BY buckets.ts
2796
- )
2797
- INSERT INTO hyperliquid_whale_coverage_1m (
2798
- ts,
2799
- covered_whales,
2800
- expected_whales,
2801
- coverage_pct,
2802
- universe_fingerprint,
2803
- whale_registry_fingerprint,
2804
- source
2805
- ,data_model_version
2806
- )
2807
- SELECT
2808
- ts,
2809
- covered_whales,
2810
- $5,
2811
- CASE WHEN $5 > 0 THEN covered_whales::double precision / $5 ELSE 0 END,
2812
- $1,
2813
- $2,
2814
- 'hyperliquid_user_fills',
2815
- $6
2816
- FROM coverage
2817
- ON CONFLICT (
2818
- universe_fingerprint,
2819
- whale_registry_fingerprint,
2820
- ts
2821
- ) DO UPDATE SET
2822
- covered_whales = EXCLUDED.covered_whales,
2823
- expected_whales = EXCLUDED.expected_whales,
2824
- coverage_pct = EXCLUDED.coverage_pct,
2825
- source = EXCLUDED.source,
2826
- data_model_version = EXCLUDED.data_model_version,
2827
- ingested_at = now()
2828
- RETURNING 1
2829
- `,
2830
- [
2831
- params.universeFingerprint,
2832
- params.whaleRegistryFingerprint,
2833
- params.fromMs,
2834
- params.toMs,
2835
- params.expectedWhales,
2836
- import_types.HYPERLIQUID_WHALE_DATA_MODEL_VERSION
2837
- ]
2838
- );
2839
- return result.rowCount ?? 0;
2881
+ ts,
2882
+ covered_whales,
2883
+ $5,
2884
+ CASE WHEN $5 > 0 THEN covered_whales::double precision / $5 ELSE 0 END,
2885
+ $1,
2886
+ $2,
2887
+ 'hyperliquid_user_fills',
2888
+ $6
2889
+ FROM coverage
2890
+ ON CONFLICT (
2891
+ universe_fingerprint,
2892
+ whale_registry_fingerprint,
2893
+ ts
2894
+ ) DO UPDATE SET
2895
+ covered_whales = EXCLUDED.covered_whales,
2896
+ expected_whales = EXCLUDED.expected_whales,
2897
+ coverage_pct = EXCLUDED.coverage_pct,
2898
+ source = EXCLUDED.source,
2899
+ data_model_version = EXCLUDED.data_model_version,
2900
+ ingested_at = now()
2901
+ `,
2902
+ [
2903
+ params.universeFingerprint,
2904
+ params.whaleRegistryFingerprint,
2905
+ chunkFromMs,
2906
+ chunkToMs,
2907
+ params.expectedWhales,
2908
+ import_types.HYPERLIQUID_WHALE_DATA_MODEL_VERSION
2909
+ ]
2910
+ );
2911
+ const chunkBuckets = Math.ceil((chunkToMs - chunkFromMs) / minuteMs);
2912
+ completedBuckets = Math.min(totalBuckets, completedBuckets + chunkBuckets);
2913
+ rows += result.rowCount ?? 0;
2914
+ params.onProgress?.({
2915
+ chunkIndex: chunkIndex + 1,
2916
+ totalChunks,
2917
+ completedBuckets,
2918
+ totalBuckets,
2919
+ rows
2920
+ });
2921
+ }
2922
+ return rows;
2840
2923
  }
2841
2924
  async function upsertHyperliquidWhaleCoverageRows(rows) {
2842
2925
  if (!rows.length) return;
@@ -2689,71 +2689,154 @@ async function upsertHyperliquidWhaleWalletCoverage(params) {
2689
2689
  async function rebuildHyperliquidWhaleCoverageRows(params) {
2690
2690
  await ensureHyperliquidWhaleSchema();
2691
2691
  if (params.toMs <= params.fromMs) return 0;
2692
- const result = await getPool().query(
2693
- `
2694
- WITH buckets AS (
2695
- SELECT generate_series(
2696
- to_timestamp($3/1000.0),
2697
- to_timestamp($4/1000.0) - interval '1 minute',
2698
- interval '1 minute'
2699
- ) AS ts
2700
- ), coverage AS (
2692
+ const minuteMs = 6e4;
2693
+ const defaultChunkMinutes = 7 * 24 * 60;
2694
+ const chunkMinutes = Number.isFinite(params.chunkMinutes) && Number(params.chunkMinutes) > 0 ? Math.floor(Number(params.chunkMinutes)) : defaultChunkMinutes;
2695
+ const chunkMs = chunkMinutes * minuteMs;
2696
+ const totalBuckets = Math.ceil((params.toMs - params.fromMs) / minuteMs);
2697
+ const totalChunks = Math.ceil((params.toMs - params.fromMs) / chunkMs);
2698
+ let completedBuckets = 0;
2699
+ let rows = 0;
2700
+ for (let chunkIndex = 0, chunkFromMs = params.fromMs; chunkFromMs < params.toMs; chunkIndex += 1, chunkFromMs += chunkMs) {
2701
+ const chunkToMs = Math.min(params.toMs, chunkFromMs + chunkMs);
2702
+ const result = await getPool().query(
2703
+ `
2704
+ WITH normalized_ranges AS (
2705
+ SELECT
2706
+ address,
2707
+ GREATEST(
2708
+ to_timestamp($3/1000.0),
2709
+ date_trunc('minute', covered_from_ts) +
2710
+ CASE
2711
+ WHEN covered_from_ts = date_trunc('minute', covered_from_ts)
2712
+ THEN interval '0 minutes'
2713
+ ELSE interval '1 minute'
2714
+ END
2715
+ ) AS range_start,
2716
+ LEAST(
2717
+ to_timestamp($4/1000.0),
2718
+ date_trunc('minute', covered_to_ts)
2719
+ ) AS range_end
2720
+ FROM hyperliquid_whale_wallet_coverage
2721
+ WHERE universe_fingerprint = $1
2722
+ AND whale_registry_fingerprint = $2
2723
+ AND data_model_version = $6
2724
+ AND status IN ('complete', 'truncated')
2725
+ AND covered_from_ts < to_timestamp($4/1000.0)
2726
+ AND covered_to_ts > to_timestamp($3/1000.0)
2727
+ ), eligible_ranges AS (
2728
+ SELECT *
2729
+ FROM normalized_ranges
2730
+ WHERE range_start < range_end
2731
+ ), ordered_ranges AS (
2732
+ SELECT
2733
+ *,
2734
+ MAX(range_end) OVER (
2735
+ PARTITION BY address
2736
+ ORDER BY range_start, range_end
2737
+ ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
2738
+ ) AS previous_max_end
2739
+ FROM eligible_ranges
2740
+ ), marked_ranges AS (
2741
+ SELECT
2742
+ *,
2743
+ SUM(
2744
+ CASE
2745
+ WHEN previous_max_end IS NULL OR range_start > previous_max_end
2746
+ THEN 1
2747
+ ELSE 0
2748
+ END
2749
+ ) OVER (
2750
+ PARTITION BY address
2751
+ ORDER BY range_start, range_end
2752
+ ) AS range_group
2753
+ FROM ordered_ranges
2754
+ ), merged_ranges AS (
2755
+ SELECT
2756
+ address,
2757
+ MIN(range_start) AS range_start,
2758
+ MAX(range_end) AS range_end
2759
+ FROM marked_ranges
2760
+ GROUP BY address, range_group
2761
+ ), deltas AS (
2762
+ SELECT range_start AS ts, 1 AS delta
2763
+ FROM merged_ranges
2764
+ UNION ALL
2765
+ SELECT range_end AS ts, -1 AS delta
2766
+ FROM merged_ranges
2767
+ ), bucket_deltas AS (
2768
+ SELECT ts, SUM(delta)::int AS delta
2769
+ FROM deltas
2770
+ GROUP BY ts
2771
+ ), buckets AS (
2772
+ SELECT generate_series(
2773
+ to_timestamp($3/1000.0),
2774
+ to_timestamp($4/1000.0) - interval '1 minute',
2775
+ interval '1 minute'
2776
+ ) AS ts
2777
+ ), coverage AS (
2778
+ SELECT
2779
+ buckets.ts,
2780
+ SUM(COALESCE(bucket_deltas.delta, 0)) OVER (
2781
+ ORDER BY buckets.ts
2782
+ ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
2783
+ )::int AS covered_whales
2784
+ FROM buckets
2785
+ LEFT JOIN bucket_deltas USING (ts)
2786
+ )
2787
+ INSERT INTO hyperliquid_whale_coverage_1m (
2788
+ ts,
2789
+ covered_whales,
2790
+ expected_whales,
2791
+ coverage_pct,
2792
+ universe_fingerprint,
2793
+ whale_registry_fingerprint,
2794
+ source,
2795
+ data_model_version
2796
+ )
2701
2797
  SELECT
2702
- buckets.ts,
2703
- COUNT(DISTINCT wallets.address)::int AS covered_whales
2704
- FROM buckets
2705
- LEFT JOIN hyperliquid_whale_wallet_coverage wallets
2706
- ON wallets.universe_fingerprint = $1
2707
- AND wallets.whale_registry_fingerprint = $2
2708
- AND wallets.data_model_version = $6
2709
- AND wallets.status IN ('complete', 'truncated')
2710
- AND wallets.covered_from_ts <= buckets.ts
2711
- AND wallets.covered_to_ts >= buckets.ts + interval '1 minute'
2712
- GROUP BY buckets.ts
2713
- )
2714
- INSERT INTO hyperliquid_whale_coverage_1m (
2715
- ts,
2716
- covered_whales,
2717
- expected_whales,
2718
- coverage_pct,
2719
- universe_fingerprint,
2720
- whale_registry_fingerprint,
2721
- source
2722
- ,data_model_version
2723
- )
2724
- SELECT
2725
- ts,
2726
- covered_whales,
2727
- $5,
2728
- CASE WHEN $5 > 0 THEN covered_whales::double precision / $5 ELSE 0 END,
2729
- $1,
2730
- $2,
2731
- 'hyperliquid_user_fills',
2732
- $6
2733
- FROM coverage
2734
- ON CONFLICT (
2735
- universe_fingerprint,
2736
- whale_registry_fingerprint,
2737
- ts
2738
- ) DO UPDATE SET
2739
- covered_whales = EXCLUDED.covered_whales,
2740
- expected_whales = EXCLUDED.expected_whales,
2741
- coverage_pct = EXCLUDED.coverage_pct,
2742
- source = EXCLUDED.source,
2743
- data_model_version = EXCLUDED.data_model_version,
2744
- ingested_at = now()
2745
- RETURNING 1
2746
- `,
2747
- [
2748
- params.universeFingerprint,
2749
- params.whaleRegistryFingerprint,
2750
- params.fromMs,
2751
- params.toMs,
2752
- params.expectedWhales,
2753
- HYPERLIQUID_WHALE_DATA_MODEL_VERSION
2754
- ]
2755
- );
2756
- return result.rowCount ?? 0;
2798
+ ts,
2799
+ covered_whales,
2800
+ $5,
2801
+ CASE WHEN $5 > 0 THEN covered_whales::double precision / $5 ELSE 0 END,
2802
+ $1,
2803
+ $2,
2804
+ 'hyperliquid_user_fills',
2805
+ $6
2806
+ FROM coverage
2807
+ ON CONFLICT (
2808
+ universe_fingerprint,
2809
+ whale_registry_fingerprint,
2810
+ ts
2811
+ ) DO UPDATE SET
2812
+ covered_whales = EXCLUDED.covered_whales,
2813
+ expected_whales = EXCLUDED.expected_whales,
2814
+ coverage_pct = EXCLUDED.coverage_pct,
2815
+ source = EXCLUDED.source,
2816
+ data_model_version = EXCLUDED.data_model_version,
2817
+ ingested_at = now()
2818
+ `,
2819
+ [
2820
+ params.universeFingerprint,
2821
+ params.whaleRegistryFingerprint,
2822
+ chunkFromMs,
2823
+ chunkToMs,
2824
+ params.expectedWhales,
2825
+ HYPERLIQUID_WHALE_DATA_MODEL_VERSION
2826
+ ]
2827
+ );
2828
+ const chunkBuckets = Math.ceil((chunkToMs - chunkFromMs) / minuteMs);
2829
+ completedBuckets = Math.min(totalBuckets, completedBuckets + chunkBuckets);
2830
+ rows += result.rowCount ?? 0;
2831
+ params.onProgress?.({
2832
+ chunkIndex: chunkIndex + 1,
2833
+ totalChunks,
2834
+ completedBuckets,
2835
+ totalBuckets,
2836
+ rows
2837
+ });
2838
+ }
2839
+ return rows;
2757
2840
  }
2758
2841
  async function upsertHyperliquidWhaleCoverageRows(rows) {
2759
2842
  if (!rows.length) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tradejs/infra",
3
- "version": "2.0.16",
3
+ "version": "2.0.17",
4
4
  "description": "MIT-licensed server infrastructure adapters for TradeJS: Redis, Timescale, ML, logging, and IO.",
5
5
  "keywords": [
6
6
  "tradejs",
@@ -93,7 +93,7 @@
93
93
  "dependencies": {
94
94
  "@grpc/grpc-js": "^1.14.4",
95
95
  "@grpc/proto-loader": "^0.8.1",
96
- "@tradejs/types": "^2.0.16",
96
+ "@tradejs/types": "^2.0.17",
97
97
  "chalk": "4.1.2",
98
98
  "ioredis": "5.11.1",
99
99
  "pg": "8.22.0",