@tradejs/infra 2.0.21 → 3.0.0

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.
Files changed (55) hide show
  1. package/dist/{chunk-YVIHTUV5.mjs → chunk-OLB6NQRG.mjs} +1 -1
  2. package/dist/{chunk-I2J6YDBD.mjs → chunk-ZXU7OYFU.mjs} +65 -38
  3. package/dist/internal-BuHqoCCV.d.mts +9 -0
  4. package/dist/internal-q48Torp4.d.ts +9 -0
  5. package/dist/ml.mjs +3 -3
  6. package/dist/query-tkHVD4Xo.d.mts +12 -0
  7. package/dist/query-tkHVD4Xo.d.ts +12 -0
  8. package/dist/timescale/candles.js +6 -4
  9. package/dist/timescale/candles.mjs +2 -2
  10. package/dist/timescale/client.d.mts +3 -2
  11. package/dist/timescale/client.d.ts +3 -2
  12. package/dist/timescale/client.js +40 -15
  13. package/dist/timescale/client.mjs +2 -2
  14. package/dist/timescale/derivatives.d.mts +4 -2
  15. package/dist/timescale/derivatives.d.ts +4 -2
  16. package/dist/timescale/derivatives.js +20 -12
  17. package/dist/timescale/derivatives.mjs +479 -13
  18. package/dist/timescale/hyperliquidWhales.d.mts +29 -25
  19. package/dist/timescale/hyperliquidWhales.d.ts +29 -25
  20. package/dist/timescale/hyperliquidWhales.js +84 -71
  21. package/dist/timescale/hyperliquidWhales.mjs +1103 -14
  22. package/dist/timescale/marketContext.d.mts +15 -11
  23. package/dist/timescale/marketContext.d.ts +15 -11
  24. package/dist/timescale/marketContext.js +67 -57
  25. package/dist/timescale/marketContext.mjs +1259 -28
  26. package/dist/timescale/spread.js +6 -4
  27. package/dist/timescale/spread.mjs +105 -5
  28. package/dist/userSettings.js +3 -316
  29. package/dist/userSettings.mjs +3 -21
  30. package/package.json +2 -22
  31. package/dist/aiEndpoints.d.mts +0 -10
  32. package/dist/aiEndpoints.d.ts +0 -10
  33. package/dist/aiEndpoints.js +0 -159
  34. package/dist/aiEndpoints.mjs +0 -12
  35. package/dist/aiLanguages.d.mts +0 -11
  36. package/dist/aiLanguages.d.ts +0 -11
  37. package/dist/aiLanguages.js +0 -71
  38. package/dist/aiLanguages.mjs +0 -12
  39. package/dist/aiModels.d.mts +0 -12
  40. package/dist/aiModels.d.ts +0 -12
  41. package/dist/aiModels.js +0 -272
  42. package/dist/aiModels.mjs +0 -17
  43. package/dist/chunk-2CZREG43.mjs +0 -112
  44. package/dist/chunk-CCC7DX2T.mjs +0 -44
  45. package/dist/chunk-DFMKDB2R.mjs +0 -1285
  46. package/dist/chunk-DTCLZIBM.mjs +0 -163
  47. package/dist/chunk-NWXFWTWU.mjs +0 -1114
  48. package/dist/chunk-SZQB7ER5.mjs +0 -492
  49. package/dist/chunk-XQ3YBULV.mjs +0 -132
  50. package/dist/internal-2coHaaos.d.mts +0 -26
  51. package/dist/internal-2coHaaos.d.ts +0 -26
  52. package/dist/timescale.d.mts +0 -9
  53. package/dist/timescale.d.ts +0 -9
  54. package/dist/timescale.js +0 -4228
  55. package/dist/timescale.mjs +0 -138
@@ -1,1114 +0,0 @@
1
- import {
2
- ensureHyperliquidWhaleSchema,
3
- getPool,
4
- getSafeBulkInsertRows,
5
- prepareMarketContextSchemaForRead,
6
- queryMarketContext
7
- } from "./chunk-I2J6YDBD.mjs";
8
-
9
- // src/timescale/hyperliquidWhales.ts
10
- import {
11
- HYPERLIQUID_WHALE_DATA_MODEL_VERSION
12
- } from "@tradejs/types";
13
- var HYPERLIQUID_CONTEXT_INTERVAL_MS = {
14
- "1m": 6e4,
15
- "5m": 5 * 6e4,
16
- "15m": 15 * 6e4,
17
- "1h": 60 * 6e4
18
- };
19
- async function upsertHyperliquidWhaleTradeEvents(rows) {
20
- if (!rows.length) return;
21
- await ensureHyperliquidWhaleSchema();
22
- const cols = [
23
- "symbol",
24
- "ts",
25
- "tid",
26
- "price",
27
- "size",
28
- "notional_usd",
29
- "buyer_address",
30
- "seller_address",
31
- "buyer_tracked",
32
- "seller_tracked",
33
- "buyer_start_position",
34
- "buyer_end_position",
35
- "buyer_position_action",
36
- "buyer_closed_pnl",
37
- "buyer_liquidation",
38
- "seller_start_position",
39
- "seller_end_position",
40
- "seller_position_action",
41
- "seller_closed_pnl",
42
- "seller_liquidation",
43
- "universe_fingerprint",
44
- "whale_registry_fingerprint",
45
- "source"
46
- ];
47
- const maxRows = getSafeBulkInsertRows(cols.length);
48
- if (rows.length > maxRows) {
49
- for (let index = 0; index < rows.length; index += maxRows) {
50
- await upsertHyperliquidWhaleTradeEvents(
51
- rows.slice(index, index + maxRows)
52
- );
53
- }
54
- return;
55
- }
56
- const valuesSql = rows.map(
57
- (_, rowIndex) => `(${cols.map((__, colIndex) => `$${rowIndex * cols.length + colIndex + 1}`).join(",")})`
58
- ).join(",");
59
- const flat = rows.flatMap((row) => [
60
- row.symbol,
61
- row.ts,
62
- row.tid,
63
- row.price,
64
- row.size,
65
- row.notionalUsd,
66
- row.buyerAddress ?? null,
67
- row.sellerAddress ?? null,
68
- row.buyerTracked,
69
- row.sellerTracked,
70
- row.buyerStartPosition ?? null,
71
- row.buyerEndPosition ?? null,
72
- row.buyerPositionAction ?? null,
73
- row.buyerClosedPnl ?? null,
74
- row.buyerLiquidation ?? null,
75
- row.sellerStartPosition ?? null,
76
- row.sellerEndPosition ?? null,
77
- row.sellerPositionAction ?? null,
78
- row.sellerClosedPnl ?? null,
79
- row.sellerLiquidation ?? null,
80
- row.universeFingerprint,
81
- row.whaleRegistryFingerprint,
82
- row.source ?? null
83
- ]);
84
- await getPool().query(
85
- `
86
- INSERT INTO hyperliquid_whale_trade_events (${cols.join(",")})
87
- VALUES ${valuesSql}
88
- ON CONFLICT (
89
- universe_fingerprint,
90
- whale_registry_fingerprint,
91
- symbol,
92
- ts,
93
- tid
94
- ) DO UPDATE SET
95
- buyer_address = COALESCE(
96
- hyperliquid_whale_trade_events.buyer_address,
97
- EXCLUDED.buyer_address
98
- ),
99
- seller_address = COALESCE(
100
- hyperliquid_whale_trade_events.seller_address,
101
- EXCLUDED.seller_address
102
- ),
103
- buyer_tracked = hyperliquid_whale_trade_events.buyer_tracked OR EXCLUDED.buyer_tracked,
104
- seller_tracked = hyperliquid_whale_trade_events.seller_tracked OR EXCLUDED.seller_tracked,
105
- buyer_start_position = COALESCE(
106
- hyperliquid_whale_trade_events.buyer_start_position,
107
- EXCLUDED.buyer_start_position
108
- ),
109
- buyer_end_position = COALESCE(
110
- hyperliquid_whale_trade_events.buyer_end_position,
111
- EXCLUDED.buyer_end_position
112
- ),
113
- buyer_position_action = COALESCE(
114
- hyperliquid_whale_trade_events.buyer_position_action,
115
- EXCLUDED.buyer_position_action
116
- ),
117
- buyer_closed_pnl = COALESCE(
118
- hyperliquid_whale_trade_events.buyer_closed_pnl,
119
- EXCLUDED.buyer_closed_pnl
120
- ),
121
- buyer_liquidation = COALESCE(
122
- hyperliquid_whale_trade_events.buyer_liquidation,
123
- EXCLUDED.buyer_liquidation
124
- ),
125
- seller_start_position = COALESCE(
126
- hyperliquid_whale_trade_events.seller_start_position,
127
- EXCLUDED.seller_start_position
128
- ),
129
- seller_end_position = COALESCE(
130
- hyperliquid_whale_trade_events.seller_end_position,
131
- EXCLUDED.seller_end_position
132
- ),
133
- seller_position_action = COALESCE(
134
- hyperliquid_whale_trade_events.seller_position_action,
135
- EXCLUDED.seller_position_action
136
- ),
137
- seller_closed_pnl = COALESCE(
138
- hyperliquid_whale_trade_events.seller_closed_pnl,
139
- EXCLUDED.seller_closed_pnl
140
- ),
141
- seller_liquidation = COALESCE(
142
- hyperliquid_whale_trade_events.seller_liquidation,
143
- EXCLUDED.seller_liquidation
144
- ),
145
- source = EXCLUDED.source,
146
- ingested_at = now()
147
- `,
148
- flat
149
- );
150
- }
151
- async function upsertHyperliquidWhaleFlowRows(rows) {
152
- if (!rows.length) return;
153
- await ensureHyperliquidWhaleSchema();
154
- const cols = [
155
- "symbol",
156
- "interval",
157
- "ts",
158
- "trades",
159
- "whale_sides",
160
- "unique_whales",
161
- "whale_addresses",
162
- "buy_notional_usd",
163
- "sell_notional_usd",
164
- "net_notional_usd",
165
- "buy_share_pct",
166
- "position_aware_whale_sides",
167
- "long_entry_whale_addresses",
168
- "short_entry_whale_addresses",
169
- "long_exit_whale_addresses",
170
- "short_exit_whale_addresses",
171
- "long_entry_notional_usd",
172
- "short_entry_notional_usd",
173
- "long_exit_notional_usd",
174
- "short_exit_notional_usd",
175
- "entry_net_notional_usd",
176
- "entry_long_share_pct",
177
- "universe_fingerprint",
178
- "whale_registry_fingerprint",
179
- "source"
180
- ];
181
- const maxRows = getSafeBulkInsertRows(cols.length);
182
- if (rows.length > maxRows) {
183
- for (let index = 0; index < rows.length; index += maxRows) {
184
- await upsertHyperliquidWhaleFlowRows(rows.slice(index, index + maxRows));
185
- }
186
- return;
187
- }
188
- const valuesSql = rows.map(
189
- (_, rowIndex) => `(${cols.map((__, colIndex) => `$${rowIndex * cols.length + colIndex + 1}`).join(",")})`
190
- ).join(",");
191
- const flat = rows.flatMap((row) => [
192
- row.symbol,
193
- row.interval,
194
- row.ts,
195
- row.trades,
196
- row.whaleSides,
197
- row.uniqueWhales,
198
- row.whaleAddresses ?? [],
199
- row.buyNotionalUsd,
200
- row.sellNotionalUsd,
201
- row.netNotionalUsd,
202
- row.buySharePct ?? null,
203
- row.positionAwareWhaleSides,
204
- row.longEntryWhaleAddresses ?? [],
205
- row.shortEntryWhaleAddresses ?? [],
206
- row.longExitWhaleAddresses ?? [],
207
- row.shortExitWhaleAddresses ?? [],
208
- row.longEntryNotionalUsd,
209
- row.shortEntryNotionalUsd,
210
- row.longExitNotionalUsd,
211
- row.shortExitNotionalUsd,
212
- row.entryNetNotionalUsd,
213
- row.entryLongSharePct ?? null,
214
- row.universeFingerprint,
215
- row.whaleRegistryFingerprint,
216
- row.source ?? null
217
- ]);
218
- await getPool().query(
219
- `
220
- INSERT INTO hyperliquid_whale_flow (${cols.join(",")})
221
- VALUES ${valuesSql}
222
- ON CONFLICT (
223
- universe_fingerprint,
224
- whale_registry_fingerprint,
225
- symbol,
226
- interval,
227
- ts
228
- ) DO UPDATE SET
229
- trades = EXCLUDED.trades,
230
- whale_sides = EXCLUDED.whale_sides,
231
- unique_whales = EXCLUDED.unique_whales,
232
- whale_addresses = EXCLUDED.whale_addresses,
233
- buy_notional_usd = EXCLUDED.buy_notional_usd,
234
- sell_notional_usd = EXCLUDED.sell_notional_usd,
235
- net_notional_usd = EXCLUDED.net_notional_usd,
236
- buy_share_pct = EXCLUDED.buy_share_pct,
237
- position_aware_whale_sides = EXCLUDED.position_aware_whale_sides,
238
- long_entry_whale_addresses = EXCLUDED.long_entry_whale_addresses,
239
- short_entry_whale_addresses = EXCLUDED.short_entry_whale_addresses,
240
- long_exit_whale_addresses = EXCLUDED.long_exit_whale_addresses,
241
- short_exit_whale_addresses = EXCLUDED.short_exit_whale_addresses,
242
- long_entry_notional_usd = EXCLUDED.long_entry_notional_usd,
243
- short_entry_notional_usd = EXCLUDED.short_entry_notional_usd,
244
- long_exit_notional_usd = EXCLUDED.long_exit_notional_usd,
245
- short_exit_notional_usd = EXCLUDED.short_exit_notional_usd,
246
- entry_net_notional_usd = EXCLUDED.entry_net_notional_usd,
247
- entry_long_share_pct = EXCLUDED.entry_long_share_pct,
248
- source = EXCLUDED.source,
249
- ingested_at = now()
250
- `,
251
- flat
252
- );
253
- }
254
- async function rebuildHyperliquidWhaleFlowRows(params) {
255
- await ensureHyperliquidWhaleSchema();
256
- const client = await getPool().connect();
257
- try {
258
- await client.query("BEGIN");
259
- const result = await client.query(
260
- `
261
- WITH source_events AS (
262
- SELECT *
263
- FROM hyperliquid_whale_trade_events
264
- WHERE universe_fingerprint = $1
265
- AND whale_registry_fingerprint = $2
266
- AND ts >= to_timestamp($3/1000.0)
267
- AND ts < to_timestamp($4/1000.0)
268
- ), metrics AS (
269
- SELECT
270
- symbol,
271
- date_trunc('minute', ts) AS bucket_ts,
272
- COUNT(*)::int AS trades,
273
- SUM(buyer_tracked::int + seller_tracked::int)::int AS whale_sides,
274
- SUM(CASE WHEN buyer_tracked THEN notional_usd ELSE 0 END) AS buy_notional_usd,
275
- SUM(CASE WHEN seller_tracked THEN notional_usd ELSE 0 END) AS sell_notional_usd
276
- FROM source_events
277
- GROUP BY symbol, date_trunc('minute', ts)
278
- ), position_legs AS (
279
- SELECT
280
- symbol,
281
- ts,
282
- price,
283
- buyer_address AS whale_address,
284
- buyer_start_position AS start_position,
285
- buyer_end_position AS end_position
286
- FROM source_events
287
- WHERE buyer_tracked
288
- AND buyer_address IS NOT NULL
289
- AND buyer_start_position IS NOT NULL
290
- AND buyer_end_position IS NOT NULL
291
- UNION ALL
292
- SELECT
293
- symbol,
294
- ts,
295
- price,
296
- seller_address AS whale_address,
297
- seller_start_position AS start_position,
298
- seller_end_position AS end_position
299
- FROM source_events
300
- WHERE seller_tracked
301
- AND seller_address IS NOT NULL
302
- AND seller_start_position IS NOT NULL
303
- AND seller_end_position IS NOT NULL
304
- ), classified_legs AS (
305
- SELECT
306
- *,
307
- GREATEST(
308
- GREATEST(end_position, 0) - GREATEST(start_position, 0),
309
- 0
310
- ) AS long_entry_size,
311
- GREATEST(
312
- GREATEST(-end_position, 0) - GREATEST(-start_position, 0),
313
- 0
314
- ) AS short_entry_size,
315
- GREATEST(
316
- GREATEST(start_position, 0) - GREATEST(end_position, 0),
317
- 0
318
- ) AS long_exit_size,
319
- GREATEST(
320
- GREATEST(-start_position, 0) - GREATEST(-end_position, 0),
321
- 0
322
- ) AS short_exit_size
323
- FROM position_legs
324
- ), position_metrics AS (
325
- SELECT
326
- symbol,
327
- date_trunc('minute', ts) AS bucket_ts,
328
- COUNT(*)::int AS position_aware_whale_sides,
329
- SUM(long_entry_size * price) AS long_entry_notional_usd,
330
- SUM(short_entry_size * price) AS short_entry_notional_usd,
331
- SUM(long_exit_size * price) AS long_exit_notional_usd,
332
- SUM(short_exit_size * price) AS short_exit_notional_usd,
333
- COALESCE(
334
- ARRAY_AGG(DISTINCT whale_address ORDER BY whale_address)
335
- FILTER (WHERE long_entry_size > 0),
336
- '{}'
337
- ) AS long_entry_whale_addresses,
338
- COALESCE(
339
- ARRAY_AGG(DISTINCT whale_address ORDER BY whale_address)
340
- FILTER (WHERE short_entry_size > 0),
341
- '{}'
342
- ) AS short_entry_whale_addresses,
343
- COALESCE(
344
- ARRAY_AGG(DISTINCT whale_address ORDER BY whale_address)
345
- FILTER (WHERE long_exit_size > 0),
346
- '{}'
347
- ) AS long_exit_whale_addresses,
348
- COALESCE(
349
- ARRAY_AGG(DISTINCT whale_address ORDER BY whale_address)
350
- FILTER (WHERE short_exit_size > 0),
351
- '{}'
352
- ) AS short_exit_whale_addresses
353
- FROM classified_legs
354
- GROUP BY symbol, date_trunc('minute', ts)
355
- ), addresses AS (
356
- SELECT
357
- symbol,
358
- date_trunc('minute', ts) AS bucket_ts,
359
- ARRAY_AGG(DISTINCT whale_address ORDER BY whale_address) AS whale_addresses
360
- FROM source_events
361
- CROSS JOIN LATERAL UNNEST(ARRAY[
362
- CASE WHEN buyer_tracked THEN buyer_address END,
363
- CASE WHEN seller_tracked THEN seller_address END
364
- ]) AS expanded(whale_address)
365
- WHERE whale_address IS NOT NULL
366
- GROUP BY symbol, date_trunc('minute', ts)
367
- )
368
- INSERT INTO hyperliquid_whale_flow (
369
- symbol,
370
- interval,
371
- ts,
372
- trades,
373
- whale_sides,
374
- unique_whales,
375
- whale_addresses,
376
- buy_notional_usd,
377
- sell_notional_usd,
378
- net_notional_usd,
379
- buy_share_pct,
380
- position_aware_whale_sides,
381
- long_entry_whale_addresses,
382
- short_entry_whale_addresses,
383
- long_exit_whale_addresses,
384
- short_exit_whale_addresses,
385
- long_entry_notional_usd,
386
- short_entry_notional_usd,
387
- long_exit_notional_usd,
388
- short_exit_notional_usd,
389
- entry_net_notional_usd,
390
- entry_long_share_pct,
391
- universe_fingerprint,
392
- whale_registry_fingerprint,
393
- source
394
- )
395
- SELECT
396
- metrics.symbol,
397
- '1m',
398
- metrics.bucket_ts,
399
- metrics.trades,
400
- metrics.whale_sides,
401
- COALESCE(CARDINALITY(addresses.whale_addresses), 0),
402
- COALESCE(addresses.whale_addresses, '{}'),
403
- metrics.buy_notional_usd,
404
- metrics.sell_notional_usd,
405
- metrics.buy_notional_usd - metrics.sell_notional_usd,
406
- CASE
407
- WHEN metrics.buy_notional_usd + metrics.sell_notional_usd > 0
408
- THEN metrics.buy_notional_usd /
409
- (metrics.buy_notional_usd + metrics.sell_notional_usd)
410
- ELSE NULL
411
- END,
412
- COALESCE(position_metrics.position_aware_whale_sides, 0),
413
- COALESCE(position_metrics.long_entry_whale_addresses, '{}'),
414
- COALESCE(position_metrics.short_entry_whale_addresses, '{}'),
415
- COALESCE(position_metrics.long_exit_whale_addresses, '{}'),
416
- COALESCE(position_metrics.short_exit_whale_addresses, '{}'),
417
- COALESCE(position_metrics.long_entry_notional_usd, 0),
418
- COALESCE(position_metrics.short_entry_notional_usd, 0),
419
- COALESCE(position_metrics.long_exit_notional_usd, 0),
420
- COALESCE(position_metrics.short_exit_notional_usd, 0),
421
- COALESCE(position_metrics.long_entry_notional_usd, 0) -
422
- COALESCE(position_metrics.short_entry_notional_usd, 0),
423
- CASE
424
- WHEN COALESCE(position_metrics.long_entry_notional_usd, 0) +
425
- COALESCE(position_metrics.short_entry_notional_usd, 0) > 0
426
- THEN COALESCE(position_metrics.long_entry_notional_usd, 0) /
427
- (
428
- COALESCE(position_metrics.long_entry_notional_usd, 0) +
429
- COALESCE(position_metrics.short_entry_notional_usd, 0)
430
- )
431
- ELSE NULL
432
- END,
433
- $1,
434
- $2,
435
- CASE
436
- WHEN COALESCE(position_metrics.position_aware_whale_sides, 0) > 0
437
- THEN 'hyperliquid_user_fills'
438
- ELSE 'hyperliquid_trades'
439
- END
440
- FROM metrics
441
- LEFT JOIN addresses USING (symbol, bucket_ts)
442
- LEFT JOIN position_metrics USING (symbol, bucket_ts)
443
- ON CONFLICT (
444
- universe_fingerprint,
445
- whale_registry_fingerprint,
446
- symbol,
447
- interval,
448
- ts
449
- ) DO UPDATE SET
450
- trades = EXCLUDED.trades,
451
- whale_sides = EXCLUDED.whale_sides,
452
- unique_whales = EXCLUDED.unique_whales,
453
- whale_addresses = EXCLUDED.whale_addresses,
454
- buy_notional_usd = EXCLUDED.buy_notional_usd,
455
- sell_notional_usd = EXCLUDED.sell_notional_usd,
456
- net_notional_usd = EXCLUDED.net_notional_usd,
457
- buy_share_pct = EXCLUDED.buy_share_pct,
458
- position_aware_whale_sides = EXCLUDED.position_aware_whale_sides,
459
- long_entry_whale_addresses = EXCLUDED.long_entry_whale_addresses,
460
- short_entry_whale_addresses = EXCLUDED.short_entry_whale_addresses,
461
- long_exit_whale_addresses = EXCLUDED.long_exit_whale_addresses,
462
- short_exit_whale_addresses = EXCLUDED.short_exit_whale_addresses,
463
- long_entry_notional_usd = EXCLUDED.long_entry_notional_usd,
464
- short_entry_notional_usd = EXCLUDED.short_entry_notional_usd,
465
- long_exit_notional_usd = EXCLUDED.long_exit_notional_usd,
466
- short_exit_notional_usd = EXCLUDED.short_exit_notional_usd,
467
- entry_net_notional_usd = EXCLUDED.entry_net_notional_usd,
468
- entry_long_share_pct = EXCLUDED.entry_long_share_pct,
469
- source = EXCLUDED.source,
470
- ingested_at = now()
471
- RETURNING 1
472
- `,
473
- [
474
- params.universeFingerprint,
475
- params.whaleRegistryFingerprint,
476
- params.fromMs,
477
- params.toMs
478
- ]
479
- );
480
- if (params.deleteEventsBeforeMs != null) {
481
- await client.query(
482
- `
483
- DELETE FROM hyperliquid_whale_trade_events
484
- WHERE universe_fingerprint = $1
485
- AND whale_registry_fingerprint = $2
486
- AND ts < to_timestamp($3/1000.0)
487
- `,
488
- [
489
- params.universeFingerprint,
490
- params.whaleRegistryFingerprint,
491
- params.deleteEventsBeforeMs
492
- ]
493
- );
494
- }
495
- await client.query("COMMIT");
496
- return result.rowCount ?? 0;
497
- } catch (error) {
498
- await client.query("ROLLBACK");
499
- throw error;
500
- } finally {
501
- client.release();
502
- }
503
- }
504
- async function getHyperliquidWhaleWalletCoverage(params) {
505
- await ensureHyperliquidWhaleSchema();
506
- const result = await getPool().query(
507
- `
508
- SELECT
509
- status,
510
- covered_from_ts,
511
- covered_to_ts,
512
- fills_count,
513
- error,
514
- checked_at
515
- FROM hyperliquid_whale_wallet_coverage
516
- WHERE universe_fingerprint = $1
517
- AND whale_registry_fingerprint = $2
518
- AND address = $3
519
- AND data_model_version = $6
520
- AND (
521
- (
522
- covered_from_ts <= to_timestamp($4/1000.0)
523
- AND covered_to_ts >= to_timestamp($5/1000.0)
524
- )
525
- OR (
526
- requested_from_ts = to_timestamp($4/1000.0)
527
- AND requested_to_ts = to_timestamp($5/1000.0)
528
- )
529
- )
530
- ORDER BY checked_at DESC
531
- LIMIT 1
532
- `,
533
- [
534
- params.universeFingerprint,
535
- params.whaleRegistryFingerprint,
536
- params.address.toLowerCase(),
537
- params.fromMs,
538
- params.toMs,
539
- HYPERLIQUID_WHALE_DATA_MODEL_VERSION
540
- ]
541
- );
542
- const row = result.rows[0];
543
- if (!row) return null;
544
- return {
545
- status: row.covered_from_ts != null && row.covered_to_ts != null && new Date(row.covered_from_ts).getTime() <= params.fromMs && new Date(row.covered_to_ts).getTime() >= params.toMs ? "complete" : String(row.status),
546
- coveredFromMs: row.covered_from_ts == null ? null : new Date(row.covered_from_ts).getTime(),
547
- coveredToMs: row.covered_to_ts == null ? null : new Date(row.covered_to_ts).getTime(),
548
- fillsCount: Number(row.fills_count) || 0,
549
- error: row.error == null ? null : String(row.error),
550
- checkedAt: new Date(row.checked_at)
551
- };
552
- }
553
- async function upsertHyperliquidWhaleWalletCoverage(params) {
554
- await ensureHyperliquidWhaleSchema();
555
- await getPool().query(
556
- `
557
- INSERT INTO hyperliquid_whale_wallet_coverage (
558
- universe_fingerprint,
559
- whale_registry_fingerprint,
560
- address,
561
- requested_from_ts,
562
- requested_to_ts,
563
- covered_from_ts,
564
- covered_to_ts,
565
- status,
566
- fills_count,
567
- error,
568
- data_model_version
569
- ) VALUES (
570
- $1,
571
- $2,
572
- $3,
573
- to_timestamp($4/1000.0),
574
- to_timestamp($5/1000.0),
575
- CASE WHEN $6::double precision IS NULL THEN NULL ELSE to_timestamp($6/1000.0) END,
576
- CASE WHEN $7::double precision IS NULL THEN NULL ELSE to_timestamp($7/1000.0) END,
577
- $8,
578
- $9,
579
- $10,
580
- $11
581
- )
582
- ON CONFLICT (
583
- universe_fingerprint,
584
- whale_registry_fingerprint,
585
- address,
586
- requested_from_ts,
587
- requested_to_ts
588
- ) DO UPDATE SET
589
- covered_from_ts = EXCLUDED.covered_from_ts,
590
- covered_to_ts = EXCLUDED.covered_to_ts,
591
- status = EXCLUDED.status,
592
- fills_count = EXCLUDED.fills_count,
593
- error = EXCLUDED.error,
594
- data_model_version = EXCLUDED.data_model_version,
595
- checked_at = now()
596
- `,
597
- [
598
- params.universeFingerprint,
599
- params.whaleRegistryFingerprint,
600
- params.address.toLowerCase(),
601
- params.fromMs,
602
- params.toMs,
603
- params.coveredFromMs,
604
- params.coveredToMs,
605
- params.status,
606
- params.fillsCount,
607
- params.error ?? null,
608
- HYPERLIQUID_WHALE_DATA_MODEL_VERSION
609
- ]
610
- );
611
- }
612
- async function rebuildHyperliquidWhaleCoverageRows(params) {
613
- await ensureHyperliquidWhaleSchema();
614
- if (params.toMs <= params.fromMs) return 0;
615
- const minuteMs = 6e4;
616
- const defaultChunkMinutes = 7 * 24 * 60;
617
- const chunkMinutes = Number.isFinite(params.chunkMinutes) && Number(params.chunkMinutes) > 0 ? Math.floor(Number(params.chunkMinutes)) : defaultChunkMinutes;
618
- const chunkMs = chunkMinutes * minuteMs;
619
- const totalBuckets = Math.ceil((params.toMs - params.fromMs) / minuteMs);
620
- const totalChunks = Math.ceil((params.toMs - params.fromMs) / chunkMs);
621
- let completedBuckets = 0;
622
- let rows = 0;
623
- for (let chunkIndex = 0, chunkFromMs = params.fromMs; chunkFromMs < params.toMs; chunkIndex += 1, chunkFromMs += chunkMs) {
624
- const chunkToMs = Math.min(params.toMs, chunkFromMs + chunkMs);
625
- const result = await getPool().query(
626
- `
627
- WITH normalized_ranges AS (
628
- SELECT
629
- address,
630
- GREATEST(
631
- to_timestamp($3/1000.0),
632
- date_trunc('minute', covered_from_ts) +
633
- CASE
634
- WHEN covered_from_ts = date_trunc('minute', covered_from_ts)
635
- THEN interval '0 minutes'
636
- ELSE interval '1 minute'
637
- END
638
- ) AS range_start,
639
- LEAST(
640
- to_timestamp($4/1000.0),
641
- date_trunc('minute', covered_to_ts)
642
- ) AS range_end
643
- FROM hyperliquid_whale_wallet_coverage
644
- WHERE universe_fingerprint = $1
645
- AND whale_registry_fingerprint = $2
646
- AND data_model_version = $6
647
- AND status IN ('complete', 'truncated')
648
- AND covered_from_ts < to_timestamp($4/1000.0)
649
- AND covered_to_ts > to_timestamp($3/1000.0)
650
- ), eligible_ranges AS (
651
- SELECT *
652
- FROM normalized_ranges
653
- WHERE range_start < range_end
654
- ), ordered_ranges AS (
655
- SELECT
656
- *,
657
- MAX(range_end) OVER (
658
- PARTITION BY address
659
- ORDER BY range_start, range_end
660
- ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
661
- ) AS previous_max_end
662
- FROM eligible_ranges
663
- ), marked_ranges AS (
664
- SELECT
665
- *,
666
- SUM(
667
- CASE
668
- WHEN previous_max_end IS NULL OR range_start > previous_max_end
669
- THEN 1
670
- ELSE 0
671
- END
672
- ) OVER (
673
- PARTITION BY address
674
- ORDER BY range_start, range_end
675
- ) AS range_group
676
- FROM ordered_ranges
677
- ), merged_ranges AS (
678
- SELECT
679
- address,
680
- MIN(range_start) AS range_start,
681
- MAX(range_end) AS range_end
682
- FROM marked_ranges
683
- GROUP BY address, range_group
684
- ), deltas AS (
685
- SELECT range_start AS ts, 1 AS delta
686
- FROM merged_ranges
687
- UNION ALL
688
- SELECT range_end AS ts, -1 AS delta
689
- FROM merged_ranges
690
- ), bucket_deltas AS (
691
- SELECT ts, SUM(delta)::int AS delta
692
- FROM deltas
693
- GROUP BY ts
694
- ), buckets AS (
695
- SELECT generate_series(
696
- to_timestamp($3/1000.0),
697
- to_timestamp($4/1000.0) - interval '1 minute',
698
- interval '1 minute'
699
- ) AS ts
700
- ), coverage AS (
701
- SELECT
702
- buckets.ts,
703
- SUM(COALESCE(bucket_deltas.delta, 0)) OVER (
704
- ORDER BY buckets.ts
705
- ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
706
- )::int AS covered_whales
707
- FROM buckets
708
- LEFT JOIN bucket_deltas USING (ts)
709
- )
710
- INSERT INTO hyperliquid_whale_coverage_1m (
711
- ts,
712
- covered_whales,
713
- expected_whales,
714
- coverage_pct,
715
- universe_fingerprint,
716
- whale_registry_fingerprint,
717
- source,
718
- data_model_version
719
- )
720
- SELECT
721
- ts,
722
- covered_whales,
723
- $5,
724
- CASE WHEN $5 > 0 THEN covered_whales::double precision / $5 ELSE 0 END,
725
- $1,
726
- $2,
727
- 'hyperliquid_user_fills',
728
- $6
729
- FROM coverage
730
- ON CONFLICT (
731
- universe_fingerprint,
732
- whale_registry_fingerprint,
733
- ts
734
- ) DO UPDATE SET
735
- covered_whales = EXCLUDED.covered_whales,
736
- expected_whales = EXCLUDED.expected_whales,
737
- coverage_pct = EXCLUDED.coverage_pct,
738
- source = EXCLUDED.source,
739
- data_model_version = EXCLUDED.data_model_version,
740
- ingested_at = now()
741
- `,
742
- [
743
- params.universeFingerprint,
744
- params.whaleRegistryFingerprint,
745
- chunkFromMs,
746
- chunkToMs,
747
- params.expectedWhales,
748
- HYPERLIQUID_WHALE_DATA_MODEL_VERSION
749
- ]
750
- );
751
- const chunkBuckets = Math.ceil((chunkToMs - chunkFromMs) / minuteMs);
752
- completedBuckets = Math.min(totalBuckets, completedBuckets + chunkBuckets);
753
- rows += result.rowCount ?? 0;
754
- params.onProgress?.({
755
- chunkIndex: chunkIndex + 1,
756
- totalChunks,
757
- completedBuckets,
758
- totalBuckets,
759
- rows
760
- });
761
- }
762
- return rows;
763
- }
764
- async function upsertHyperliquidWhaleCoverageRows(rows) {
765
- if (!rows.length) return;
766
- await ensureHyperliquidWhaleSchema();
767
- const cols = [
768
- "ts",
769
- "covered_whales",
770
- "expected_whales",
771
- "coverage_pct",
772
- "universe_fingerprint",
773
- "whale_registry_fingerprint",
774
- "source",
775
- "data_model_version"
776
- ];
777
- const maxRows = getSafeBulkInsertRows(cols.length);
778
- if (rows.length > maxRows) {
779
- for (let index = 0; index < rows.length; index += maxRows) {
780
- await upsertHyperliquidWhaleCoverageRows(
781
- rows.slice(index, index + maxRows)
782
- );
783
- }
784
- return;
785
- }
786
- const values = [];
787
- const tuples = rows.map((row, rowIndex) => {
788
- const offset = rowIndex * cols.length;
789
- values.push(
790
- row.ts,
791
- row.coveredWhales,
792
- row.expectedWhales,
793
- row.coveragePct,
794
- row.universeFingerprint,
795
- row.whaleRegistryFingerprint,
796
- row.source ?? null,
797
- row.dataModelVersion ?? HYPERLIQUID_WHALE_DATA_MODEL_VERSION
798
- );
799
- return `(${cols.map((_, colIndex) => `$${offset + colIndex + 1}`).join(",")})`;
800
- });
801
- await getPool().query(
802
- `
803
- INSERT INTO hyperliquid_whale_coverage_1m (${cols.join(",")})
804
- VALUES ${tuples.join(",")}
805
- ON CONFLICT (
806
- universe_fingerprint,
807
- whale_registry_fingerprint,
808
- ts
809
- ) DO UPDATE SET
810
- covered_whales = EXCLUDED.covered_whales,
811
- expected_whales = EXCLUDED.expected_whales,
812
- coverage_pct = EXCLUDED.coverage_pct,
813
- source = EXCLUDED.source,
814
- data_model_version = EXCLUDED.data_model_version,
815
- ingested_at = now()
816
- `,
817
- values
818
- );
819
- }
820
- async function getHyperliquidWhaleCoverageSeriesRows(params) {
821
- await prepareMarketContextSchemaForRead("hyperliquidWhales");
822
- const result = await queryMarketContext(
823
- `
824
- SELECT
825
- ts,
826
- covered_whales,
827
- expected_whales,
828
- coverage_pct
829
- FROM hyperliquid_whale_coverage_1m
830
- WHERE universe_fingerprint = $1
831
- AND whale_registry_fingerprint = $2
832
- AND data_model_version = $3
833
- AND ts >= to_timestamp($4/1000.0)
834
- AND ts < to_timestamp($5/1000.0)
835
- ORDER BY ts
836
- `,
837
- [
838
- params.universeFingerprint,
839
- params.whaleRegistryFingerprint,
840
- HYPERLIQUID_WHALE_DATA_MODEL_VERSION,
841
- params.fromMs,
842
- params.toMs
843
- ],
844
- params
845
- );
846
- return result.rows.map((row) => ({
847
- ts: new Date(row.ts),
848
- coveredWhales: Number(row.covered_whales) || 0,
849
- expectedWhales: Number(row.expected_whales) || 0,
850
- coveragePct: Number(row.coverage_pct) || 0
851
- }));
852
- }
853
- async function getHyperliquidWhaleFlowSeriesRows(params) {
854
- await prepareMarketContextSchemaForRead("hyperliquidWhales");
855
- const result = await queryMarketContext(
856
- `
857
- SELECT
858
- ts,
859
- trades,
860
- whale_sides,
861
- whale_addresses,
862
- buy_notional_usd,
863
- sell_notional_usd,
864
- position_aware_whale_sides,
865
- long_entry_whale_addresses,
866
- short_entry_whale_addresses,
867
- long_exit_whale_addresses,
868
- short_exit_whale_addresses,
869
- long_entry_notional_usd,
870
- short_entry_notional_usd,
871
- long_exit_notional_usd,
872
- short_exit_notional_usd
873
- FROM hyperliquid_whale_flow
874
- WHERE symbol = $1
875
- AND interval = '1m'
876
- AND universe_fingerprint = $2
877
- AND whale_registry_fingerprint = $3
878
- AND ts >= to_timestamp($4/1000.0)
879
- AND ts < to_timestamp($5/1000.0)
880
- ORDER BY ts
881
- `,
882
- [
883
- params.symbol,
884
- params.universeFingerprint,
885
- params.whaleRegistryFingerprint,
886
- params.fromMs,
887
- params.toMs
888
- ],
889
- params
890
- );
891
- return result.rows.map((row) => ({
892
- ts: new Date(row.ts),
893
- trades: Number(row.trades) || 0,
894
- whaleSides: Number(row.whale_sides) || 0,
895
- whaleAddresses: Array.isArray(row.whale_addresses) ? row.whale_addresses.map(String) : [],
896
- buyNotionalUsd: Number(row.buy_notional_usd) || 0,
897
- sellNotionalUsd: Number(row.sell_notional_usd) || 0,
898
- positionAwareWhaleSides: Number(row.position_aware_whale_sides) || 0,
899
- longEntryWhaleAddresses: Array.isArray(row.long_entry_whale_addresses) ? row.long_entry_whale_addresses.map(String) : [],
900
- shortEntryWhaleAddresses: Array.isArray(row.short_entry_whale_addresses) ? row.short_entry_whale_addresses.map(String) : [],
901
- longExitWhaleAddresses: Array.isArray(row.long_exit_whale_addresses) ? row.long_exit_whale_addresses.map(String) : [],
902
- shortExitWhaleAddresses: Array.isArray(row.short_exit_whale_addresses) ? row.short_exit_whale_addresses.map(String) : [],
903
- longEntryNotionalUsd: Number(row.long_entry_notional_usd) || 0,
904
- shortEntryNotionalUsd: Number(row.short_entry_notional_usd) || 0,
905
- longExitNotionalUsd: Number(row.long_exit_notional_usd) || 0,
906
- shortExitNotionalUsd: Number(row.short_exit_notional_usd) || 0
907
- }));
908
- }
909
- async function getHyperliquidWhaleFlowAggregate(params) {
910
- await prepareMarketContextSchemaForRead("hyperliquidWhales");
911
- const intervalMs = HYPERLIQUID_CONTEXT_INTERVAL_MS[params.interval];
912
- const expectedBuckets = Math.ceil(intervalMs / 6e4);
913
- const res = await queryMarketContext(
914
- `
915
- WITH coverage_rows AS (
916
- SELECT *
917
- FROM hyperliquid_whale_coverage_1m
918
- WHERE universe_fingerprint = $2
919
- AND whale_registry_fingerprint = $3
920
- AND data_model_version = $6
921
- AND ts >= to_timestamp(
922
- ($4::double precision - $5::double precision) / 1000.0
923
- )
924
- AND ts < to_timestamp($4/1000.0)
925
- ), coverage_summary AS (
926
- SELECT
927
- COUNT(*)::int AS coverage_buckets,
928
- MAX(ts) AS coverage_as_of_ts,
929
- MIN(covered_whales)::int AS covered_whales,
930
- MAX(expected_whales)::int AS expected_whales,
931
- MIN(coverage_pct) AS coverage_pct
932
- FROM coverage_rows
933
- ), window_rows AS (
934
- SELECT *
935
- FROM hyperliquid_whale_flow
936
- WHERE symbol = $1
937
- AND interval = '1m'
938
- AND universe_fingerprint = $2
939
- AND whale_registry_fingerprint = $3
940
- AND ts >= to_timestamp(
941
- ($4::double precision - $5::double precision) / 1000.0
942
- )
943
- AND ts < to_timestamp($4/1000.0)
944
- ), unique_addresses AS (
945
- SELECT COUNT(DISTINCT address)::int AS unique_whales
946
- FROM window_rows
947
- CROSS JOIN LATERAL UNNEST(whale_addresses) AS expanded(address)
948
- ), directional_counts AS (
949
- SELECT
950
- (
951
- SELECT COUNT(DISTINCT address)::int
952
- FROM window_rows
953
- CROSS JOIN LATERAL UNNEST(long_entry_whale_addresses) AS expanded(address)
954
- ) AS long_entry_whales,
955
- (
956
- SELECT COUNT(DISTINCT address)::int
957
- FROM window_rows
958
- CROSS JOIN LATERAL UNNEST(short_entry_whale_addresses) AS expanded(address)
959
- ) AS short_entry_whales,
960
- (
961
- SELECT COUNT(DISTINCT address)::int
962
- FROM window_rows
963
- CROSS JOIN LATERAL UNNEST(long_exit_whale_addresses) AS expanded(address)
964
- ) AS long_exit_whales,
965
- (
966
- SELECT COUNT(DISTINCT address)::int
967
- FROM window_rows
968
- CROSS JOIN LATERAL UNNEST(short_exit_whale_addresses) AS expanded(address)
969
- ) AS short_exit_whales
970
- )
971
- SELECT
972
- $1::text AS symbol,
973
- coverage_summary.coverage_as_of_ts AS as_of_ts,
974
- coverage_summary.coverage_buckets,
975
- coverage_summary.covered_whales,
976
- coverage_summary.expected_whales,
977
- coverage_summary.coverage_pct,
978
- COALESCE((SELECT SUM(trades) FROM window_rows), 0)::int AS trades,
979
- COALESCE((SELECT SUM(whale_sides) FROM window_rows), 0)::int AS whale_sides,
980
- COALESCE((SELECT unique_whales FROM unique_addresses), 0)::int AS unique_whales,
981
- COALESCE((SELECT SUM(buy_notional_usd) FROM window_rows), 0) AS buy_notional_usd,
982
- COALESCE((SELECT SUM(sell_notional_usd) FROM window_rows), 0) AS sell_notional_usd,
983
- COALESCE((SELECT SUM(net_notional_usd) FROM window_rows), 0) AS net_notional_usd,
984
- CASE
985
- WHEN COALESCE((SELECT SUM(buy_notional_usd + sell_notional_usd) FROM window_rows), 0) > 0
986
- THEN (SELECT SUM(buy_notional_usd) FROM window_rows) /
987
- (SELECT SUM(buy_notional_usd + sell_notional_usd) FROM window_rows)
988
- ELSE NULL
989
- END AS buy_share_pct,
990
- COALESCE((SELECT SUM(position_aware_whale_sides) FROM window_rows), 0)::int
991
- AS position_aware_whale_sides,
992
- CASE
993
- WHEN COALESCE((SELECT SUM(whale_sides) FROM window_rows), 0) > 0
994
- THEN COALESCE((SELECT SUM(position_aware_whale_sides) FROM window_rows), 0)::double precision /
995
- (SELECT SUM(whale_sides) FROM window_rows)
996
- ELSE 0
997
- END AS position_aware_pct,
998
- COALESCE((SELECT long_entry_whales FROM directional_counts), 0)::int AS long_entry_whales,
999
- COALESCE((SELECT short_entry_whales FROM directional_counts), 0)::int AS short_entry_whales,
1000
- COALESCE((SELECT long_exit_whales FROM directional_counts), 0)::int AS long_exit_whales,
1001
- COALESCE((SELECT short_exit_whales FROM directional_counts), 0)::int AS short_exit_whales,
1002
- COALESCE((SELECT SUM(long_entry_notional_usd) FROM window_rows), 0)
1003
- AS long_entry_notional_usd,
1004
- COALESCE((SELECT SUM(short_entry_notional_usd) FROM window_rows), 0)
1005
- AS short_entry_notional_usd,
1006
- COALESCE((SELECT SUM(long_exit_notional_usd) FROM window_rows), 0)
1007
- AS long_exit_notional_usd,
1008
- COALESCE((SELECT SUM(short_exit_notional_usd) FROM window_rows), 0)
1009
- AS short_exit_notional_usd,
1010
- COALESCE((SELECT SUM(entry_net_notional_usd) FROM window_rows), 0)
1011
- AS entry_net_notional_usd,
1012
- CASE
1013
- WHEN COALESCE((SELECT SUM(long_entry_notional_usd + short_entry_notional_usd) FROM window_rows), 0) > 0
1014
- THEN (SELECT SUM(long_entry_notional_usd) FROM window_rows) /
1015
- (SELECT SUM(long_entry_notional_usd + short_entry_notional_usd) FROM window_rows)
1016
- ELSE NULL
1017
- END AS entry_long_share_pct,
1018
- (SELECT MAX(source) FROM window_rows) AS source
1019
- FROM coverage_summary
1020
- `,
1021
- [
1022
- params.symbol,
1023
- params.universeFingerprint,
1024
- params.whaleRegistryFingerprint,
1025
- params.decisionTimeMs,
1026
- intervalMs,
1027
- HYPERLIQUID_WHALE_DATA_MODEL_VERSION
1028
- ],
1029
- params
1030
- );
1031
- const row = res.rows[0];
1032
- if (!row?.as_of_ts || Number(row.coverage_buckets) !== expectedBuckets || Number(row.covered_whales) <= 0) {
1033
- return null;
1034
- }
1035
- const asOfTs = new Date(row.as_of_ts);
1036
- const ageMs = params.decisionTimeMs - (asOfTs.getTime() + 6e4);
1037
- return {
1038
- symbol: params.symbol,
1039
- interval: params.interval,
1040
- asOfTs,
1041
- windowEndTs: new Date(params.decisionTimeMs),
1042
- trades: Number(row.trades) || 0,
1043
- whaleSides: Number(row.whale_sides) || 0,
1044
- uniqueWhales: Number(row.unique_whales) || 0,
1045
- coveredWhales: Number(row.covered_whales) || 0,
1046
- expectedWhales: Number(row.expected_whales) || 0,
1047
- coveragePct: Number(row.coverage_pct) || 0,
1048
- buyNotionalUsd: Number(row.buy_notional_usd) || 0,
1049
- sellNotionalUsd: Number(row.sell_notional_usd) || 0,
1050
- netNotionalUsd: Number(row.net_notional_usd) || 0,
1051
- buySharePct: row.buy_share_pct == null ? null : Number(row.buy_share_pct) || 0,
1052
- positionAwareWhaleSides: Number(row.position_aware_whale_sides) || 0,
1053
- positionAwarePct: Number(row.position_aware_pct) || 0,
1054
- longEntryWhales: Number(row.long_entry_whales) || 0,
1055
- shortEntryWhales: Number(row.short_entry_whales) || 0,
1056
- longExitWhales: Number(row.long_exit_whales) || 0,
1057
- shortExitWhales: Number(row.short_exit_whales) || 0,
1058
- longEntryNotionalUsd: Number(row.long_entry_notional_usd) || 0,
1059
- shortEntryNotionalUsd: Number(row.short_entry_notional_usd) || 0,
1060
- longExitNotionalUsd: Number(row.long_exit_notional_usd) || 0,
1061
- shortExitNotionalUsd: Number(row.short_exit_notional_usd) || 0,
1062
- entryNetNotionalUsd: Number(row.entry_net_notional_usd) || 0,
1063
- entryLongSharePct: row.entry_long_share_pct == null ? null : Number(row.entry_long_share_pct) || 0,
1064
- universeFingerprint: params.universeFingerprint,
1065
- whaleRegistryFingerprint: params.whaleRegistryFingerprint,
1066
- source: row.source == null ? null : String(row.source),
1067
- ageMs,
1068
- stale: ageMs < 0 || (params.maxAgeMs != null && Number.isFinite(params.maxAgeMs) ? ageMs > params.maxAgeMs : false)
1069
- };
1070
- }
1071
- async function hasHyperliquidWhaleBackfillCoverage(params) {
1072
- await ensureHyperliquidWhaleSchema();
1073
- const expectedBuckets = Math.max(
1074
- 0,
1075
- Math.ceil((params.toMs - params.fromMs) / 6e4)
1076
- );
1077
- const result = await getPool().query(
1078
- `
1079
- SELECT
1080
- COUNT(*)::int AS buckets,
1081
- COUNT(*) FILTER (
1082
- WHERE covered_whales = expected_whales
1083
- )::int AS complete_buckets
1084
- FROM hyperliquid_whale_coverage_1m
1085
- WHERE universe_fingerprint = $1
1086
- AND whale_registry_fingerprint = $2
1087
- AND data_model_version = $5
1088
- AND ts >= to_timestamp($3/1000.0)
1089
- AND ts < to_timestamp($4/1000.0)
1090
- `,
1091
- [
1092
- params.universeFingerprint,
1093
- params.whaleRegistryFingerprint,
1094
- params.fromMs,
1095
- params.toMs,
1096
- HYPERLIQUID_WHALE_DATA_MODEL_VERSION
1097
- ]
1098
- );
1099
- return Number(result.rows[0]?.buckets) === expectedBuckets && Number(result.rows[0]?.complete_buckets) === expectedBuckets;
1100
- }
1101
-
1102
- export {
1103
- upsertHyperliquidWhaleTradeEvents,
1104
- upsertHyperliquidWhaleFlowRows,
1105
- rebuildHyperliquidWhaleFlowRows,
1106
- getHyperliquidWhaleWalletCoverage,
1107
- upsertHyperliquidWhaleWalletCoverage,
1108
- rebuildHyperliquidWhaleCoverageRows,
1109
- upsertHyperliquidWhaleCoverageRows,
1110
- getHyperliquidWhaleCoverageSeriesRows,
1111
- getHyperliquidWhaleFlowSeriesRows,
1112
- getHyperliquidWhaleFlowAggregate,
1113
- hasHyperliquidWhaleBackfillCoverage
1114
- };