@tradejs/infra 2.0.13 → 2.0.15
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.
- package/dist/timescale.d.mts +129 -2
- package/dist/timescale.d.ts +129 -2
- package/dist/timescale.js +1258 -0
- package/dist/timescale.mjs +1249 -0
- package/package.json +2 -2
package/dist/timescale.mjs
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
// src/timescale.ts
|
|
2
2
|
import { Pool } from "pg";
|
|
3
|
+
import {
|
|
4
|
+
HYPERLIQUID_WHALE_DATA_MODEL_VERSION
|
|
5
|
+
} from "@tradejs/types";
|
|
3
6
|
var getPool = () => {
|
|
4
7
|
if (!global.__pgPool__) {
|
|
5
8
|
const host = process.env.PG_HOST || "127.0.0.1";
|
|
@@ -28,10 +31,12 @@ var candlesSchemaReady = false;
|
|
|
28
31
|
var derivativesSchemaReady = false;
|
|
29
32
|
var spreadSchemaReady = false;
|
|
30
33
|
var binanceMarketSchemaReady = false;
|
|
34
|
+
var hyperliquidWhaleSchemaReady = false;
|
|
31
35
|
var candlesSchemaReadyPromise = null;
|
|
32
36
|
var derivativesSchemaReadyPromise = null;
|
|
33
37
|
var spreadSchemaReadyPromise = null;
|
|
34
38
|
var binanceMarketSchemaReadyPromise = null;
|
|
39
|
+
var hyperliquidWhaleSchemaReadyPromise = null;
|
|
35
40
|
var closeTimescalePool = async () => {
|
|
36
41
|
const pool = global.__pgPool__;
|
|
37
42
|
if (!pool) {
|
|
@@ -42,16 +47,19 @@ var closeTimescalePool = async () => {
|
|
|
42
47
|
derivativesSchemaReady = false;
|
|
43
48
|
spreadSchemaReady = false;
|
|
44
49
|
binanceMarketSchemaReady = false;
|
|
50
|
+
hyperliquidWhaleSchemaReady = false;
|
|
45
51
|
candlesSchemaReadyPromise = null;
|
|
46
52
|
derivativesSchemaReadyPromise = null;
|
|
47
53
|
spreadSchemaReadyPromise = null;
|
|
48
54
|
binanceMarketSchemaReadyPromise = null;
|
|
55
|
+
hyperliquidWhaleSchemaReadyPromise = null;
|
|
49
56
|
await pool.end();
|
|
50
57
|
};
|
|
51
58
|
var CANDLES_SCHEMA_LOCK_KEY = 61e4;
|
|
52
59
|
var DERIVATIVES_SCHEMA_LOCK_KEY = 610001;
|
|
53
60
|
var SPREAD_SCHEMA_LOCK_KEY = 610002;
|
|
54
61
|
var BINANCE_MARKET_SCHEMA_LOCK_KEY = 610003;
|
|
62
|
+
var HYPERLIQUID_WHALE_SCHEMA_LOCK_KEY = 610004;
|
|
55
63
|
var PG_SAFE_MAX_BIND_PARAMS = 3e4;
|
|
56
64
|
var normalizeCandleProvider = (provider) => String(provider || "").trim().toLowerCase();
|
|
57
65
|
var normalizeCandleSymbol = (symbol) => String(symbol || "").trim().toUpperCase();
|
|
@@ -629,6 +637,234 @@ var ensureBinanceMarketSchema = async () => {
|
|
|
629
637
|
});
|
|
630
638
|
await binanceMarketSchemaReadyPromise;
|
|
631
639
|
};
|
|
640
|
+
var ensureHyperliquidWhaleSchema = async () => {
|
|
641
|
+
if (hyperliquidWhaleSchemaReady) return;
|
|
642
|
+
if (hyperliquidWhaleSchemaReadyPromise) {
|
|
643
|
+
await hyperliquidWhaleSchemaReadyPromise;
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
hyperliquidWhaleSchemaReadyPromise = withSchemaLock(
|
|
647
|
+
HYPERLIQUID_WHALE_SCHEMA_LOCK_KEY,
|
|
648
|
+
async () => {
|
|
649
|
+
if (hyperliquidWhaleSchemaReady) return;
|
|
650
|
+
const pool = getPool();
|
|
651
|
+
await pool.query("CREATE EXTENSION IF NOT EXISTS timescaledb");
|
|
652
|
+
await pool.query(`
|
|
653
|
+
CREATE TABLE IF NOT EXISTS hyperliquid_whale_trade_events (
|
|
654
|
+
symbol text NOT NULL,
|
|
655
|
+
ts timestamptz NOT NULL,
|
|
656
|
+
tid text NOT NULL,
|
|
657
|
+
price double precision NOT NULL,
|
|
658
|
+
size double precision NOT NULL,
|
|
659
|
+
notional_usd double precision NOT NULL,
|
|
660
|
+
buyer_address text,
|
|
661
|
+
seller_address text,
|
|
662
|
+
buyer_tracked boolean NOT NULL,
|
|
663
|
+
seller_tracked boolean NOT NULL,
|
|
664
|
+
buyer_start_position double precision,
|
|
665
|
+
buyer_end_position double precision,
|
|
666
|
+
buyer_position_action text,
|
|
667
|
+
buyer_closed_pnl double precision,
|
|
668
|
+
buyer_liquidation boolean,
|
|
669
|
+
seller_start_position double precision,
|
|
670
|
+
seller_end_position double precision,
|
|
671
|
+
seller_position_action text,
|
|
672
|
+
seller_closed_pnl double precision,
|
|
673
|
+
seller_liquidation boolean,
|
|
674
|
+
universe_fingerprint text NOT NULL,
|
|
675
|
+
whale_registry_fingerprint text NOT NULL,
|
|
676
|
+
source text,
|
|
677
|
+
ingested_at timestamptz NOT NULL DEFAULT now(),
|
|
678
|
+
PRIMARY KEY (
|
|
679
|
+
universe_fingerprint,
|
|
680
|
+
whale_registry_fingerprint,
|
|
681
|
+
symbol,
|
|
682
|
+
ts,
|
|
683
|
+
tid
|
|
684
|
+
)
|
|
685
|
+
)
|
|
686
|
+
`);
|
|
687
|
+
await pool.query(`
|
|
688
|
+
ALTER TABLE hyperliquid_whale_trade_events
|
|
689
|
+
ADD COLUMN IF NOT EXISTS buyer_start_position double precision,
|
|
690
|
+
ADD COLUMN IF NOT EXISTS buyer_end_position double precision,
|
|
691
|
+
ADD COLUMN IF NOT EXISTS buyer_position_action text,
|
|
692
|
+
ADD COLUMN IF NOT EXISTS buyer_closed_pnl double precision,
|
|
693
|
+
ADD COLUMN IF NOT EXISTS buyer_liquidation boolean,
|
|
694
|
+
ADD COLUMN IF NOT EXISTS seller_start_position double precision,
|
|
695
|
+
ADD COLUMN IF NOT EXISTS seller_end_position double precision,
|
|
696
|
+
ADD COLUMN IF NOT EXISTS seller_position_action text,
|
|
697
|
+
ADD COLUMN IF NOT EXISTS seller_closed_pnl double precision,
|
|
698
|
+
ADD COLUMN IF NOT EXISTS seller_liquidation boolean
|
|
699
|
+
`);
|
|
700
|
+
await pool.query(`
|
|
701
|
+
SELECT create_hypertable(
|
|
702
|
+
'hyperliquid_whale_trade_events',
|
|
703
|
+
'ts',
|
|
704
|
+
if_not_exists => TRUE,
|
|
705
|
+
chunk_time_interval => interval '1 day'
|
|
706
|
+
)
|
|
707
|
+
`);
|
|
708
|
+
await pool.query(`
|
|
709
|
+
CREATE INDEX IF NOT EXISTS hyperliquid_whale_events_lookup_idx
|
|
710
|
+
ON hyperliquid_whale_trade_events (
|
|
711
|
+
universe_fingerprint,
|
|
712
|
+
whale_registry_fingerprint,
|
|
713
|
+
symbol,
|
|
714
|
+
ts DESC
|
|
715
|
+
)
|
|
716
|
+
`);
|
|
717
|
+
await pool.query(`
|
|
718
|
+
CREATE TABLE IF NOT EXISTS hyperliquid_whale_flow (
|
|
719
|
+
symbol text NOT NULL,
|
|
720
|
+
interval text NOT NULL,
|
|
721
|
+
ts timestamptz NOT NULL,
|
|
722
|
+
trades integer NOT NULL,
|
|
723
|
+
whale_sides integer NOT NULL,
|
|
724
|
+
unique_whales integer NOT NULL,
|
|
725
|
+
whale_addresses text[] NOT NULL DEFAULT '{}',
|
|
726
|
+
buy_notional_usd double precision NOT NULL,
|
|
727
|
+
sell_notional_usd double precision NOT NULL,
|
|
728
|
+
net_notional_usd double precision NOT NULL,
|
|
729
|
+
buy_share_pct double precision,
|
|
730
|
+
position_aware_whale_sides integer NOT NULL DEFAULT 0,
|
|
731
|
+
long_entry_whale_addresses text[] NOT NULL DEFAULT '{}',
|
|
732
|
+
short_entry_whale_addresses text[] NOT NULL DEFAULT '{}',
|
|
733
|
+
long_exit_whale_addresses text[] NOT NULL DEFAULT '{}',
|
|
734
|
+
short_exit_whale_addresses text[] NOT NULL DEFAULT '{}',
|
|
735
|
+
long_entry_notional_usd double precision NOT NULL DEFAULT 0,
|
|
736
|
+
short_entry_notional_usd double precision NOT NULL DEFAULT 0,
|
|
737
|
+
long_exit_notional_usd double precision NOT NULL DEFAULT 0,
|
|
738
|
+
short_exit_notional_usd double precision NOT NULL DEFAULT 0,
|
|
739
|
+
entry_net_notional_usd double precision NOT NULL DEFAULT 0,
|
|
740
|
+
entry_long_share_pct double precision,
|
|
741
|
+
universe_fingerprint text NOT NULL,
|
|
742
|
+
whale_registry_fingerprint text NOT NULL,
|
|
743
|
+
source text,
|
|
744
|
+
ingested_at timestamptz NOT NULL DEFAULT now(),
|
|
745
|
+
PRIMARY KEY (
|
|
746
|
+
universe_fingerprint,
|
|
747
|
+
whale_registry_fingerprint,
|
|
748
|
+
symbol,
|
|
749
|
+
interval,
|
|
750
|
+
ts
|
|
751
|
+
)
|
|
752
|
+
)
|
|
753
|
+
`);
|
|
754
|
+
await pool.query(`
|
|
755
|
+
ALTER TABLE hyperliquid_whale_flow
|
|
756
|
+
ADD COLUMN IF NOT EXISTS position_aware_whale_sides integer NOT NULL DEFAULT 0,
|
|
757
|
+
ADD COLUMN IF NOT EXISTS long_entry_whale_addresses text[] NOT NULL DEFAULT '{}',
|
|
758
|
+
ADD COLUMN IF NOT EXISTS short_entry_whale_addresses text[] NOT NULL DEFAULT '{}',
|
|
759
|
+
ADD COLUMN IF NOT EXISTS long_exit_whale_addresses text[] NOT NULL DEFAULT '{}',
|
|
760
|
+
ADD COLUMN IF NOT EXISTS short_exit_whale_addresses text[] NOT NULL DEFAULT '{}',
|
|
761
|
+
ADD COLUMN IF NOT EXISTS long_entry_notional_usd double precision NOT NULL DEFAULT 0,
|
|
762
|
+
ADD COLUMN IF NOT EXISTS short_entry_notional_usd double precision NOT NULL DEFAULT 0,
|
|
763
|
+
ADD COLUMN IF NOT EXISTS long_exit_notional_usd double precision NOT NULL DEFAULT 0,
|
|
764
|
+
ADD COLUMN IF NOT EXISTS short_exit_notional_usd double precision NOT NULL DEFAULT 0,
|
|
765
|
+
ADD COLUMN IF NOT EXISTS entry_net_notional_usd double precision NOT NULL DEFAULT 0,
|
|
766
|
+
ADD COLUMN IF NOT EXISTS entry_long_share_pct double precision
|
|
767
|
+
`);
|
|
768
|
+
await pool.query(`
|
|
769
|
+
SELECT create_hypertable(
|
|
770
|
+
'hyperliquid_whale_flow',
|
|
771
|
+
'ts',
|
|
772
|
+
if_not_exists => TRUE,
|
|
773
|
+
chunk_time_interval => interval '7 days'
|
|
774
|
+
)
|
|
775
|
+
`);
|
|
776
|
+
await pool.query(`
|
|
777
|
+
CREATE INDEX IF NOT EXISTS hyperliquid_whale_flow_lookup_idx
|
|
778
|
+
ON hyperliquid_whale_flow (
|
|
779
|
+
universe_fingerprint,
|
|
780
|
+
whale_registry_fingerprint,
|
|
781
|
+
symbol,
|
|
782
|
+
interval,
|
|
783
|
+
ts DESC
|
|
784
|
+
)
|
|
785
|
+
`);
|
|
786
|
+
await pool.query(`
|
|
787
|
+
CREATE TABLE IF NOT EXISTS hyperliquid_whale_wallet_coverage (
|
|
788
|
+
universe_fingerprint text NOT NULL,
|
|
789
|
+
whale_registry_fingerprint text NOT NULL,
|
|
790
|
+
address text NOT NULL,
|
|
791
|
+
requested_from_ts timestamptz NOT NULL,
|
|
792
|
+
requested_to_ts timestamptz NOT NULL,
|
|
793
|
+
covered_from_ts timestamptz,
|
|
794
|
+
covered_to_ts timestamptz,
|
|
795
|
+
status text NOT NULL CHECK (status IN ('complete', 'truncated', 'failed')),
|
|
796
|
+
fills_count integer NOT NULL DEFAULT 0,
|
|
797
|
+
error text,
|
|
798
|
+
data_model_version integer NOT NULL DEFAULT 2,
|
|
799
|
+
checked_at timestamptz NOT NULL DEFAULT now(),
|
|
800
|
+
PRIMARY KEY (
|
|
801
|
+
universe_fingerprint,
|
|
802
|
+
whale_registry_fingerprint,
|
|
803
|
+
address,
|
|
804
|
+
requested_from_ts,
|
|
805
|
+
requested_to_ts
|
|
806
|
+
)
|
|
807
|
+
)
|
|
808
|
+
`);
|
|
809
|
+
await pool.query(`
|
|
810
|
+
ALTER TABLE hyperliquid_whale_wallet_coverage
|
|
811
|
+
ADD COLUMN IF NOT EXISTS data_model_version integer NOT NULL DEFAULT 2
|
|
812
|
+
`);
|
|
813
|
+
await pool.query(`
|
|
814
|
+
CREATE INDEX IF NOT EXISTS hyperliquid_whale_wallet_coverage_lookup_idx
|
|
815
|
+
ON hyperliquid_whale_wallet_coverage (
|
|
816
|
+
universe_fingerprint,
|
|
817
|
+
whale_registry_fingerprint,
|
|
818
|
+
address,
|
|
819
|
+
requested_from_ts,
|
|
820
|
+
requested_to_ts
|
|
821
|
+
)
|
|
822
|
+
`);
|
|
823
|
+
await pool.query(`
|
|
824
|
+
CREATE TABLE IF NOT EXISTS hyperliquid_whale_coverage_1m (
|
|
825
|
+
ts timestamptz NOT NULL,
|
|
826
|
+
covered_whales integer NOT NULL,
|
|
827
|
+
expected_whales integer NOT NULL,
|
|
828
|
+
coverage_pct double precision NOT NULL,
|
|
829
|
+
universe_fingerprint text NOT NULL,
|
|
830
|
+
whale_registry_fingerprint text NOT NULL,
|
|
831
|
+
source text,
|
|
832
|
+
data_model_version integer NOT NULL DEFAULT 2,
|
|
833
|
+
ingested_at timestamptz NOT NULL DEFAULT now(),
|
|
834
|
+
PRIMARY KEY (
|
|
835
|
+
universe_fingerprint,
|
|
836
|
+
whale_registry_fingerprint,
|
|
837
|
+
ts
|
|
838
|
+
)
|
|
839
|
+
)
|
|
840
|
+
`);
|
|
841
|
+
await pool.query(`
|
|
842
|
+
ALTER TABLE hyperliquid_whale_coverage_1m
|
|
843
|
+
ADD COLUMN IF NOT EXISTS data_model_version integer NOT NULL DEFAULT 2
|
|
844
|
+
`);
|
|
845
|
+
await pool.query(`
|
|
846
|
+
SELECT create_hypertable(
|
|
847
|
+
'hyperliquid_whale_coverage_1m',
|
|
848
|
+
'ts',
|
|
849
|
+
if_not_exists => TRUE,
|
|
850
|
+
chunk_time_interval => interval '7 days'
|
|
851
|
+
)
|
|
852
|
+
`);
|
|
853
|
+
await pool.query(`
|
|
854
|
+
CREATE INDEX IF NOT EXISTS hyperliquid_whale_coverage_1m_lookup_idx
|
|
855
|
+
ON hyperliquid_whale_coverage_1m (
|
|
856
|
+
universe_fingerprint,
|
|
857
|
+
whale_registry_fingerprint,
|
|
858
|
+
ts DESC
|
|
859
|
+
)
|
|
860
|
+
`);
|
|
861
|
+
hyperliquidWhaleSchemaReady = true;
|
|
862
|
+
}
|
|
863
|
+
).finally(() => {
|
|
864
|
+
hyperliquidWhaleSchemaReadyPromise = null;
|
|
865
|
+
});
|
|
866
|
+
await hyperliquidWhaleSchemaReadyPromise;
|
|
867
|
+
};
|
|
632
868
|
async function upsertDerivatives(rows) {
|
|
633
869
|
if (!rows.length) return;
|
|
634
870
|
await ensureDerivativesSchema();
|
|
@@ -1207,6 +1443,491 @@ async function upsertMarketTradeFlowRows(rows) {
|
|
|
1207
1443
|
flat
|
|
1208
1444
|
);
|
|
1209
1445
|
}
|
|
1446
|
+
async function upsertHyperliquidWhaleTradeEvents(rows) {
|
|
1447
|
+
if (!rows.length) return;
|
|
1448
|
+
await ensureHyperliquidWhaleSchema();
|
|
1449
|
+
const cols = [
|
|
1450
|
+
"symbol",
|
|
1451
|
+
"ts",
|
|
1452
|
+
"tid",
|
|
1453
|
+
"price",
|
|
1454
|
+
"size",
|
|
1455
|
+
"notional_usd",
|
|
1456
|
+
"buyer_address",
|
|
1457
|
+
"seller_address",
|
|
1458
|
+
"buyer_tracked",
|
|
1459
|
+
"seller_tracked",
|
|
1460
|
+
"buyer_start_position",
|
|
1461
|
+
"buyer_end_position",
|
|
1462
|
+
"buyer_position_action",
|
|
1463
|
+
"buyer_closed_pnl",
|
|
1464
|
+
"buyer_liquidation",
|
|
1465
|
+
"seller_start_position",
|
|
1466
|
+
"seller_end_position",
|
|
1467
|
+
"seller_position_action",
|
|
1468
|
+
"seller_closed_pnl",
|
|
1469
|
+
"seller_liquidation",
|
|
1470
|
+
"universe_fingerprint",
|
|
1471
|
+
"whale_registry_fingerprint",
|
|
1472
|
+
"source"
|
|
1473
|
+
];
|
|
1474
|
+
const maxRows = getSafeBulkInsertRows(cols.length);
|
|
1475
|
+
if (rows.length > maxRows) {
|
|
1476
|
+
for (let index = 0; index < rows.length; index += maxRows) {
|
|
1477
|
+
await upsertHyperliquidWhaleTradeEvents(
|
|
1478
|
+
rows.slice(index, index + maxRows)
|
|
1479
|
+
);
|
|
1480
|
+
}
|
|
1481
|
+
return;
|
|
1482
|
+
}
|
|
1483
|
+
const valuesSql = rows.map(
|
|
1484
|
+
(_, rowIndex) => `(${cols.map((__, colIndex) => `$${rowIndex * cols.length + colIndex + 1}`).join(",")})`
|
|
1485
|
+
).join(",");
|
|
1486
|
+
const flat = rows.flatMap((row) => [
|
|
1487
|
+
row.symbol,
|
|
1488
|
+
row.ts,
|
|
1489
|
+
row.tid,
|
|
1490
|
+
row.price,
|
|
1491
|
+
row.size,
|
|
1492
|
+
row.notionalUsd,
|
|
1493
|
+
row.buyerAddress ?? null,
|
|
1494
|
+
row.sellerAddress ?? null,
|
|
1495
|
+
row.buyerTracked,
|
|
1496
|
+
row.sellerTracked,
|
|
1497
|
+
row.buyerStartPosition ?? null,
|
|
1498
|
+
row.buyerEndPosition ?? null,
|
|
1499
|
+
row.buyerPositionAction ?? null,
|
|
1500
|
+
row.buyerClosedPnl ?? null,
|
|
1501
|
+
row.buyerLiquidation ?? null,
|
|
1502
|
+
row.sellerStartPosition ?? null,
|
|
1503
|
+
row.sellerEndPosition ?? null,
|
|
1504
|
+
row.sellerPositionAction ?? null,
|
|
1505
|
+
row.sellerClosedPnl ?? null,
|
|
1506
|
+
row.sellerLiquidation ?? null,
|
|
1507
|
+
row.universeFingerprint,
|
|
1508
|
+
row.whaleRegistryFingerprint,
|
|
1509
|
+
row.source ?? null
|
|
1510
|
+
]);
|
|
1511
|
+
await getPool().query(
|
|
1512
|
+
`
|
|
1513
|
+
INSERT INTO hyperliquid_whale_trade_events (${cols.join(",")})
|
|
1514
|
+
VALUES ${valuesSql}
|
|
1515
|
+
ON CONFLICT (
|
|
1516
|
+
universe_fingerprint,
|
|
1517
|
+
whale_registry_fingerprint,
|
|
1518
|
+
symbol,
|
|
1519
|
+
ts,
|
|
1520
|
+
tid
|
|
1521
|
+
) DO UPDATE SET
|
|
1522
|
+
buyer_address = COALESCE(
|
|
1523
|
+
hyperliquid_whale_trade_events.buyer_address,
|
|
1524
|
+
EXCLUDED.buyer_address
|
|
1525
|
+
),
|
|
1526
|
+
seller_address = COALESCE(
|
|
1527
|
+
hyperliquid_whale_trade_events.seller_address,
|
|
1528
|
+
EXCLUDED.seller_address
|
|
1529
|
+
),
|
|
1530
|
+
buyer_tracked = hyperliquid_whale_trade_events.buyer_tracked OR EXCLUDED.buyer_tracked,
|
|
1531
|
+
seller_tracked = hyperliquid_whale_trade_events.seller_tracked OR EXCLUDED.seller_tracked,
|
|
1532
|
+
buyer_start_position = COALESCE(
|
|
1533
|
+
hyperliquid_whale_trade_events.buyer_start_position,
|
|
1534
|
+
EXCLUDED.buyer_start_position
|
|
1535
|
+
),
|
|
1536
|
+
buyer_end_position = COALESCE(
|
|
1537
|
+
hyperliquid_whale_trade_events.buyer_end_position,
|
|
1538
|
+
EXCLUDED.buyer_end_position
|
|
1539
|
+
),
|
|
1540
|
+
buyer_position_action = COALESCE(
|
|
1541
|
+
hyperliquid_whale_trade_events.buyer_position_action,
|
|
1542
|
+
EXCLUDED.buyer_position_action
|
|
1543
|
+
),
|
|
1544
|
+
buyer_closed_pnl = COALESCE(
|
|
1545
|
+
hyperliquid_whale_trade_events.buyer_closed_pnl,
|
|
1546
|
+
EXCLUDED.buyer_closed_pnl
|
|
1547
|
+
),
|
|
1548
|
+
buyer_liquidation = COALESCE(
|
|
1549
|
+
hyperliquid_whale_trade_events.buyer_liquidation,
|
|
1550
|
+
EXCLUDED.buyer_liquidation
|
|
1551
|
+
),
|
|
1552
|
+
seller_start_position = COALESCE(
|
|
1553
|
+
hyperliquid_whale_trade_events.seller_start_position,
|
|
1554
|
+
EXCLUDED.seller_start_position
|
|
1555
|
+
),
|
|
1556
|
+
seller_end_position = COALESCE(
|
|
1557
|
+
hyperliquid_whale_trade_events.seller_end_position,
|
|
1558
|
+
EXCLUDED.seller_end_position
|
|
1559
|
+
),
|
|
1560
|
+
seller_position_action = COALESCE(
|
|
1561
|
+
hyperliquid_whale_trade_events.seller_position_action,
|
|
1562
|
+
EXCLUDED.seller_position_action
|
|
1563
|
+
),
|
|
1564
|
+
seller_closed_pnl = COALESCE(
|
|
1565
|
+
hyperliquid_whale_trade_events.seller_closed_pnl,
|
|
1566
|
+
EXCLUDED.seller_closed_pnl
|
|
1567
|
+
),
|
|
1568
|
+
seller_liquidation = COALESCE(
|
|
1569
|
+
hyperliquid_whale_trade_events.seller_liquidation,
|
|
1570
|
+
EXCLUDED.seller_liquidation
|
|
1571
|
+
),
|
|
1572
|
+
source = EXCLUDED.source,
|
|
1573
|
+
ingested_at = now()
|
|
1574
|
+
`,
|
|
1575
|
+
flat
|
|
1576
|
+
);
|
|
1577
|
+
}
|
|
1578
|
+
async function upsertHyperliquidWhaleFlowRows(rows) {
|
|
1579
|
+
if (!rows.length) return;
|
|
1580
|
+
await ensureHyperliquidWhaleSchema();
|
|
1581
|
+
const cols = [
|
|
1582
|
+
"symbol",
|
|
1583
|
+
"interval",
|
|
1584
|
+
"ts",
|
|
1585
|
+
"trades",
|
|
1586
|
+
"whale_sides",
|
|
1587
|
+
"unique_whales",
|
|
1588
|
+
"whale_addresses",
|
|
1589
|
+
"buy_notional_usd",
|
|
1590
|
+
"sell_notional_usd",
|
|
1591
|
+
"net_notional_usd",
|
|
1592
|
+
"buy_share_pct",
|
|
1593
|
+
"position_aware_whale_sides",
|
|
1594
|
+
"long_entry_whale_addresses",
|
|
1595
|
+
"short_entry_whale_addresses",
|
|
1596
|
+
"long_exit_whale_addresses",
|
|
1597
|
+
"short_exit_whale_addresses",
|
|
1598
|
+
"long_entry_notional_usd",
|
|
1599
|
+
"short_entry_notional_usd",
|
|
1600
|
+
"long_exit_notional_usd",
|
|
1601
|
+
"short_exit_notional_usd",
|
|
1602
|
+
"entry_net_notional_usd",
|
|
1603
|
+
"entry_long_share_pct",
|
|
1604
|
+
"universe_fingerprint",
|
|
1605
|
+
"whale_registry_fingerprint",
|
|
1606
|
+
"source"
|
|
1607
|
+
];
|
|
1608
|
+
const maxRows = getSafeBulkInsertRows(cols.length);
|
|
1609
|
+
if (rows.length > maxRows) {
|
|
1610
|
+
for (let index = 0; index < rows.length; index += maxRows) {
|
|
1611
|
+
await upsertHyperliquidWhaleFlowRows(rows.slice(index, index + maxRows));
|
|
1612
|
+
}
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1615
|
+
const valuesSql = rows.map(
|
|
1616
|
+
(_, rowIndex) => `(${cols.map((__, colIndex) => `$${rowIndex * cols.length + colIndex + 1}`).join(",")})`
|
|
1617
|
+
).join(",");
|
|
1618
|
+
const flat = rows.flatMap((row) => [
|
|
1619
|
+
row.symbol,
|
|
1620
|
+
row.interval,
|
|
1621
|
+
row.ts,
|
|
1622
|
+
row.trades,
|
|
1623
|
+
row.whaleSides,
|
|
1624
|
+
row.uniqueWhales,
|
|
1625
|
+
row.whaleAddresses ?? [],
|
|
1626
|
+
row.buyNotionalUsd,
|
|
1627
|
+
row.sellNotionalUsd,
|
|
1628
|
+
row.netNotionalUsd,
|
|
1629
|
+
row.buySharePct ?? null,
|
|
1630
|
+
row.positionAwareWhaleSides,
|
|
1631
|
+
row.longEntryWhaleAddresses ?? [],
|
|
1632
|
+
row.shortEntryWhaleAddresses ?? [],
|
|
1633
|
+
row.longExitWhaleAddresses ?? [],
|
|
1634
|
+
row.shortExitWhaleAddresses ?? [],
|
|
1635
|
+
row.longEntryNotionalUsd,
|
|
1636
|
+
row.shortEntryNotionalUsd,
|
|
1637
|
+
row.longExitNotionalUsd,
|
|
1638
|
+
row.shortExitNotionalUsd,
|
|
1639
|
+
row.entryNetNotionalUsd,
|
|
1640
|
+
row.entryLongSharePct ?? null,
|
|
1641
|
+
row.universeFingerprint,
|
|
1642
|
+
row.whaleRegistryFingerprint,
|
|
1643
|
+
row.source ?? null
|
|
1644
|
+
]);
|
|
1645
|
+
await getPool().query(
|
|
1646
|
+
`
|
|
1647
|
+
INSERT INTO hyperliquid_whale_flow (${cols.join(",")})
|
|
1648
|
+
VALUES ${valuesSql}
|
|
1649
|
+
ON CONFLICT (
|
|
1650
|
+
universe_fingerprint,
|
|
1651
|
+
whale_registry_fingerprint,
|
|
1652
|
+
symbol,
|
|
1653
|
+
interval,
|
|
1654
|
+
ts
|
|
1655
|
+
) DO UPDATE SET
|
|
1656
|
+
trades = EXCLUDED.trades,
|
|
1657
|
+
whale_sides = EXCLUDED.whale_sides,
|
|
1658
|
+
unique_whales = EXCLUDED.unique_whales,
|
|
1659
|
+
whale_addresses = EXCLUDED.whale_addresses,
|
|
1660
|
+
buy_notional_usd = EXCLUDED.buy_notional_usd,
|
|
1661
|
+
sell_notional_usd = EXCLUDED.sell_notional_usd,
|
|
1662
|
+
net_notional_usd = EXCLUDED.net_notional_usd,
|
|
1663
|
+
buy_share_pct = EXCLUDED.buy_share_pct,
|
|
1664
|
+
position_aware_whale_sides = EXCLUDED.position_aware_whale_sides,
|
|
1665
|
+
long_entry_whale_addresses = EXCLUDED.long_entry_whale_addresses,
|
|
1666
|
+
short_entry_whale_addresses = EXCLUDED.short_entry_whale_addresses,
|
|
1667
|
+
long_exit_whale_addresses = EXCLUDED.long_exit_whale_addresses,
|
|
1668
|
+
short_exit_whale_addresses = EXCLUDED.short_exit_whale_addresses,
|
|
1669
|
+
long_entry_notional_usd = EXCLUDED.long_entry_notional_usd,
|
|
1670
|
+
short_entry_notional_usd = EXCLUDED.short_entry_notional_usd,
|
|
1671
|
+
long_exit_notional_usd = EXCLUDED.long_exit_notional_usd,
|
|
1672
|
+
short_exit_notional_usd = EXCLUDED.short_exit_notional_usd,
|
|
1673
|
+
entry_net_notional_usd = EXCLUDED.entry_net_notional_usd,
|
|
1674
|
+
entry_long_share_pct = EXCLUDED.entry_long_share_pct,
|
|
1675
|
+
source = EXCLUDED.source,
|
|
1676
|
+
ingested_at = now()
|
|
1677
|
+
`,
|
|
1678
|
+
flat
|
|
1679
|
+
);
|
|
1680
|
+
}
|
|
1681
|
+
async function rebuildHyperliquidWhaleFlowRows(params) {
|
|
1682
|
+
await ensureHyperliquidWhaleSchema();
|
|
1683
|
+
const client = await getPool().connect();
|
|
1684
|
+
try {
|
|
1685
|
+
await client.query("BEGIN");
|
|
1686
|
+
const result = await client.query(
|
|
1687
|
+
`
|
|
1688
|
+
WITH source_events AS (
|
|
1689
|
+
SELECT *
|
|
1690
|
+
FROM hyperliquid_whale_trade_events
|
|
1691
|
+
WHERE universe_fingerprint = $1
|
|
1692
|
+
AND whale_registry_fingerprint = $2
|
|
1693
|
+
AND ts >= to_timestamp($3/1000.0)
|
|
1694
|
+
AND ts < to_timestamp($4/1000.0)
|
|
1695
|
+
), metrics AS (
|
|
1696
|
+
SELECT
|
|
1697
|
+
symbol,
|
|
1698
|
+
date_trunc('minute', ts) AS bucket_ts,
|
|
1699
|
+
COUNT(*)::int AS trades,
|
|
1700
|
+
SUM(buyer_tracked::int + seller_tracked::int)::int AS whale_sides,
|
|
1701
|
+
SUM(CASE WHEN buyer_tracked THEN notional_usd ELSE 0 END) AS buy_notional_usd,
|
|
1702
|
+
SUM(CASE WHEN seller_tracked THEN notional_usd ELSE 0 END) AS sell_notional_usd
|
|
1703
|
+
FROM source_events
|
|
1704
|
+
GROUP BY symbol, date_trunc('minute', ts)
|
|
1705
|
+
), position_legs AS (
|
|
1706
|
+
SELECT
|
|
1707
|
+
symbol,
|
|
1708
|
+
ts,
|
|
1709
|
+
price,
|
|
1710
|
+
buyer_address AS whale_address,
|
|
1711
|
+
buyer_start_position AS start_position,
|
|
1712
|
+
buyer_end_position AS end_position
|
|
1713
|
+
FROM source_events
|
|
1714
|
+
WHERE buyer_tracked
|
|
1715
|
+
AND buyer_address IS NOT NULL
|
|
1716
|
+
AND buyer_start_position IS NOT NULL
|
|
1717
|
+
AND buyer_end_position IS NOT NULL
|
|
1718
|
+
UNION ALL
|
|
1719
|
+
SELECT
|
|
1720
|
+
symbol,
|
|
1721
|
+
ts,
|
|
1722
|
+
price,
|
|
1723
|
+
seller_address AS whale_address,
|
|
1724
|
+
seller_start_position AS start_position,
|
|
1725
|
+
seller_end_position AS end_position
|
|
1726
|
+
FROM source_events
|
|
1727
|
+
WHERE seller_tracked
|
|
1728
|
+
AND seller_address IS NOT NULL
|
|
1729
|
+
AND seller_start_position IS NOT NULL
|
|
1730
|
+
AND seller_end_position IS NOT NULL
|
|
1731
|
+
), classified_legs AS (
|
|
1732
|
+
SELECT
|
|
1733
|
+
*,
|
|
1734
|
+
GREATEST(
|
|
1735
|
+
GREATEST(end_position, 0) - GREATEST(start_position, 0),
|
|
1736
|
+
0
|
|
1737
|
+
) AS long_entry_size,
|
|
1738
|
+
GREATEST(
|
|
1739
|
+
GREATEST(-end_position, 0) - GREATEST(-start_position, 0),
|
|
1740
|
+
0
|
|
1741
|
+
) AS short_entry_size,
|
|
1742
|
+
GREATEST(
|
|
1743
|
+
GREATEST(start_position, 0) - GREATEST(end_position, 0),
|
|
1744
|
+
0
|
|
1745
|
+
) AS long_exit_size,
|
|
1746
|
+
GREATEST(
|
|
1747
|
+
GREATEST(-start_position, 0) - GREATEST(-end_position, 0),
|
|
1748
|
+
0
|
|
1749
|
+
) AS short_exit_size
|
|
1750
|
+
FROM position_legs
|
|
1751
|
+
), position_metrics AS (
|
|
1752
|
+
SELECT
|
|
1753
|
+
symbol,
|
|
1754
|
+
date_trunc('minute', ts) AS bucket_ts,
|
|
1755
|
+
COUNT(*)::int AS position_aware_whale_sides,
|
|
1756
|
+
SUM(long_entry_size * price) AS long_entry_notional_usd,
|
|
1757
|
+
SUM(short_entry_size * price) AS short_entry_notional_usd,
|
|
1758
|
+
SUM(long_exit_size * price) AS long_exit_notional_usd,
|
|
1759
|
+
SUM(short_exit_size * price) AS short_exit_notional_usd,
|
|
1760
|
+
COALESCE(
|
|
1761
|
+
ARRAY_AGG(DISTINCT whale_address ORDER BY whale_address)
|
|
1762
|
+
FILTER (WHERE long_entry_size > 0),
|
|
1763
|
+
'{}'
|
|
1764
|
+
) AS long_entry_whale_addresses,
|
|
1765
|
+
COALESCE(
|
|
1766
|
+
ARRAY_AGG(DISTINCT whale_address ORDER BY whale_address)
|
|
1767
|
+
FILTER (WHERE short_entry_size > 0),
|
|
1768
|
+
'{}'
|
|
1769
|
+
) AS short_entry_whale_addresses,
|
|
1770
|
+
COALESCE(
|
|
1771
|
+
ARRAY_AGG(DISTINCT whale_address ORDER BY whale_address)
|
|
1772
|
+
FILTER (WHERE long_exit_size > 0),
|
|
1773
|
+
'{}'
|
|
1774
|
+
) AS long_exit_whale_addresses,
|
|
1775
|
+
COALESCE(
|
|
1776
|
+
ARRAY_AGG(DISTINCT whale_address ORDER BY whale_address)
|
|
1777
|
+
FILTER (WHERE short_exit_size > 0),
|
|
1778
|
+
'{}'
|
|
1779
|
+
) AS short_exit_whale_addresses
|
|
1780
|
+
FROM classified_legs
|
|
1781
|
+
GROUP BY symbol, date_trunc('minute', ts)
|
|
1782
|
+
), addresses AS (
|
|
1783
|
+
SELECT
|
|
1784
|
+
symbol,
|
|
1785
|
+
date_trunc('minute', ts) AS bucket_ts,
|
|
1786
|
+
ARRAY_AGG(DISTINCT whale_address ORDER BY whale_address) AS whale_addresses
|
|
1787
|
+
FROM source_events
|
|
1788
|
+
CROSS JOIN LATERAL UNNEST(ARRAY[
|
|
1789
|
+
CASE WHEN buyer_tracked THEN buyer_address END,
|
|
1790
|
+
CASE WHEN seller_tracked THEN seller_address END
|
|
1791
|
+
]) AS expanded(whale_address)
|
|
1792
|
+
WHERE whale_address IS NOT NULL
|
|
1793
|
+
GROUP BY symbol, date_trunc('minute', ts)
|
|
1794
|
+
)
|
|
1795
|
+
INSERT INTO hyperliquid_whale_flow (
|
|
1796
|
+
symbol,
|
|
1797
|
+
interval,
|
|
1798
|
+
ts,
|
|
1799
|
+
trades,
|
|
1800
|
+
whale_sides,
|
|
1801
|
+
unique_whales,
|
|
1802
|
+
whale_addresses,
|
|
1803
|
+
buy_notional_usd,
|
|
1804
|
+
sell_notional_usd,
|
|
1805
|
+
net_notional_usd,
|
|
1806
|
+
buy_share_pct,
|
|
1807
|
+
position_aware_whale_sides,
|
|
1808
|
+
long_entry_whale_addresses,
|
|
1809
|
+
short_entry_whale_addresses,
|
|
1810
|
+
long_exit_whale_addresses,
|
|
1811
|
+
short_exit_whale_addresses,
|
|
1812
|
+
long_entry_notional_usd,
|
|
1813
|
+
short_entry_notional_usd,
|
|
1814
|
+
long_exit_notional_usd,
|
|
1815
|
+
short_exit_notional_usd,
|
|
1816
|
+
entry_net_notional_usd,
|
|
1817
|
+
entry_long_share_pct,
|
|
1818
|
+
universe_fingerprint,
|
|
1819
|
+
whale_registry_fingerprint,
|
|
1820
|
+
source
|
|
1821
|
+
)
|
|
1822
|
+
SELECT
|
|
1823
|
+
metrics.symbol,
|
|
1824
|
+
'1m',
|
|
1825
|
+
metrics.bucket_ts,
|
|
1826
|
+
metrics.trades,
|
|
1827
|
+
metrics.whale_sides,
|
|
1828
|
+
COALESCE(CARDINALITY(addresses.whale_addresses), 0),
|
|
1829
|
+
COALESCE(addresses.whale_addresses, '{}'),
|
|
1830
|
+
metrics.buy_notional_usd,
|
|
1831
|
+
metrics.sell_notional_usd,
|
|
1832
|
+
metrics.buy_notional_usd - metrics.sell_notional_usd,
|
|
1833
|
+
CASE
|
|
1834
|
+
WHEN metrics.buy_notional_usd + metrics.sell_notional_usd > 0
|
|
1835
|
+
THEN metrics.buy_notional_usd /
|
|
1836
|
+
(metrics.buy_notional_usd + metrics.sell_notional_usd)
|
|
1837
|
+
ELSE NULL
|
|
1838
|
+
END,
|
|
1839
|
+
COALESCE(position_metrics.position_aware_whale_sides, 0),
|
|
1840
|
+
COALESCE(position_metrics.long_entry_whale_addresses, '{}'),
|
|
1841
|
+
COALESCE(position_metrics.short_entry_whale_addresses, '{}'),
|
|
1842
|
+
COALESCE(position_metrics.long_exit_whale_addresses, '{}'),
|
|
1843
|
+
COALESCE(position_metrics.short_exit_whale_addresses, '{}'),
|
|
1844
|
+
COALESCE(position_metrics.long_entry_notional_usd, 0),
|
|
1845
|
+
COALESCE(position_metrics.short_entry_notional_usd, 0),
|
|
1846
|
+
COALESCE(position_metrics.long_exit_notional_usd, 0),
|
|
1847
|
+
COALESCE(position_metrics.short_exit_notional_usd, 0),
|
|
1848
|
+
COALESCE(position_metrics.long_entry_notional_usd, 0) -
|
|
1849
|
+
COALESCE(position_metrics.short_entry_notional_usd, 0),
|
|
1850
|
+
CASE
|
|
1851
|
+
WHEN COALESCE(position_metrics.long_entry_notional_usd, 0) +
|
|
1852
|
+
COALESCE(position_metrics.short_entry_notional_usd, 0) > 0
|
|
1853
|
+
THEN COALESCE(position_metrics.long_entry_notional_usd, 0) /
|
|
1854
|
+
(
|
|
1855
|
+
COALESCE(position_metrics.long_entry_notional_usd, 0) +
|
|
1856
|
+
COALESCE(position_metrics.short_entry_notional_usd, 0)
|
|
1857
|
+
)
|
|
1858
|
+
ELSE NULL
|
|
1859
|
+
END,
|
|
1860
|
+
$1,
|
|
1861
|
+
$2,
|
|
1862
|
+
CASE
|
|
1863
|
+
WHEN COALESCE(position_metrics.position_aware_whale_sides, 0) > 0
|
|
1864
|
+
THEN 'hyperliquid_user_fills'
|
|
1865
|
+
ELSE 'hyperliquid_trades'
|
|
1866
|
+
END
|
|
1867
|
+
FROM metrics
|
|
1868
|
+
LEFT JOIN addresses USING (symbol, bucket_ts)
|
|
1869
|
+
LEFT JOIN position_metrics USING (symbol, bucket_ts)
|
|
1870
|
+
ON CONFLICT (
|
|
1871
|
+
universe_fingerprint,
|
|
1872
|
+
whale_registry_fingerprint,
|
|
1873
|
+
symbol,
|
|
1874
|
+
interval,
|
|
1875
|
+
ts
|
|
1876
|
+
) DO UPDATE SET
|
|
1877
|
+
trades = EXCLUDED.trades,
|
|
1878
|
+
whale_sides = EXCLUDED.whale_sides,
|
|
1879
|
+
unique_whales = EXCLUDED.unique_whales,
|
|
1880
|
+
whale_addresses = EXCLUDED.whale_addresses,
|
|
1881
|
+
buy_notional_usd = EXCLUDED.buy_notional_usd,
|
|
1882
|
+
sell_notional_usd = EXCLUDED.sell_notional_usd,
|
|
1883
|
+
net_notional_usd = EXCLUDED.net_notional_usd,
|
|
1884
|
+
buy_share_pct = EXCLUDED.buy_share_pct,
|
|
1885
|
+
position_aware_whale_sides = EXCLUDED.position_aware_whale_sides,
|
|
1886
|
+
long_entry_whale_addresses = EXCLUDED.long_entry_whale_addresses,
|
|
1887
|
+
short_entry_whale_addresses = EXCLUDED.short_entry_whale_addresses,
|
|
1888
|
+
long_exit_whale_addresses = EXCLUDED.long_exit_whale_addresses,
|
|
1889
|
+
short_exit_whale_addresses = EXCLUDED.short_exit_whale_addresses,
|
|
1890
|
+
long_entry_notional_usd = EXCLUDED.long_entry_notional_usd,
|
|
1891
|
+
short_entry_notional_usd = EXCLUDED.short_entry_notional_usd,
|
|
1892
|
+
long_exit_notional_usd = EXCLUDED.long_exit_notional_usd,
|
|
1893
|
+
short_exit_notional_usd = EXCLUDED.short_exit_notional_usd,
|
|
1894
|
+
entry_net_notional_usd = EXCLUDED.entry_net_notional_usd,
|
|
1895
|
+
entry_long_share_pct = EXCLUDED.entry_long_share_pct,
|
|
1896
|
+
source = EXCLUDED.source,
|
|
1897
|
+
ingested_at = now()
|
|
1898
|
+
RETURNING 1
|
|
1899
|
+
`,
|
|
1900
|
+
[
|
|
1901
|
+
params.universeFingerprint,
|
|
1902
|
+
params.whaleRegistryFingerprint,
|
|
1903
|
+
params.fromMs,
|
|
1904
|
+
params.toMs
|
|
1905
|
+
]
|
|
1906
|
+
);
|
|
1907
|
+
if (params.deleteEventsBeforeMs != null) {
|
|
1908
|
+
await client.query(
|
|
1909
|
+
`
|
|
1910
|
+
DELETE FROM hyperliquid_whale_trade_events
|
|
1911
|
+
WHERE universe_fingerprint = $1
|
|
1912
|
+
AND whale_registry_fingerprint = $2
|
|
1913
|
+
AND ts < to_timestamp($3/1000.0)
|
|
1914
|
+
`,
|
|
1915
|
+
[
|
|
1916
|
+
params.universeFingerprint,
|
|
1917
|
+
params.whaleRegistryFingerprint,
|
|
1918
|
+
params.deleteEventsBeforeMs
|
|
1919
|
+
]
|
|
1920
|
+
);
|
|
1921
|
+
}
|
|
1922
|
+
await client.query("COMMIT");
|
|
1923
|
+
return result.rowCount ?? 0;
|
|
1924
|
+
} catch (error) {
|
|
1925
|
+
await client.query("ROLLBACK");
|
|
1926
|
+
throw error;
|
|
1927
|
+
} finally {
|
|
1928
|
+
client.release();
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1210
1931
|
async function upsertMarketBreadthRows(rows) {
|
|
1211
1932
|
if (!rows.length) return;
|
|
1212
1933
|
await ensureBinanceMarketSchema();
|
|
@@ -1730,6 +2451,523 @@ async function getLatestMarketTradeFlow(params) {
|
|
|
1730
2451
|
stale: ageMs == null || params.maxAgeMs != null && ageMs > params.maxAgeMs
|
|
1731
2452
|
};
|
|
1732
2453
|
}
|
|
2454
|
+
var HYPERLIQUID_CONTEXT_INTERVAL_MS = {
|
|
2455
|
+
"1m": 6e4,
|
|
2456
|
+
"5m": 5 * 6e4,
|
|
2457
|
+
"15m": 15 * 6e4,
|
|
2458
|
+
"1h": 60 * 6e4
|
|
2459
|
+
};
|
|
2460
|
+
async function getHyperliquidWhaleWalletCoverage(params) {
|
|
2461
|
+
await ensureHyperliquidWhaleSchema();
|
|
2462
|
+
const result = await getPool().query(
|
|
2463
|
+
`
|
|
2464
|
+
SELECT
|
|
2465
|
+
status,
|
|
2466
|
+
covered_from_ts,
|
|
2467
|
+
covered_to_ts,
|
|
2468
|
+
fills_count,
|
|
2469
|
+
error,
|
|
2470
|
+
checked_at
|
|
2471
|
+
FROM hyperliquid_whale_wallet_coverage
|
|
2472
|
+
WHERE universe_fingerprint = $1
|
|
2473
|
+
AND whale_registry_fingerprint = $2
|
|
2474
|
+
AND address = $3
|
|
2475
|
+
AND data_model_version = $6
|
|
2476
|
+
AND (
|
|
2477
|
+
(
|
|
2478
|
+
covered_from_ts <= to_timestamp($4/1000.0)
|
|
2479
|
+
AND covered_to_ts >= to_timestamp($5/1000.0)
|
|
2480
|
+
)
|
|
2481
|
+
OR (
|
|
2482
|
+
requested_from_ts = to_timestamp($4/1000.0)
|
|
2483
|
+
AND requested_to_ts = to_timestamp($5/1000.0)
|
|
2484
|
+
)
|
|
2485
|
+
)
|
|
2486
|
+
ORDER BY checked_at DESC
|
|
2487
|
+
LIMIT 1
|
|
2488
|
+
`,
|
|
2489
|
+
[
|
|
2490
|
+
params.universeFingerprint,
|
|
2491
|
+
params.whaleRegistryFingerprint,
|
|
2492
|
+
params.address.toLowerCase(),
|
|
2493
|
+
params.fromMs,
|
|
2494
|
+
params.toMs,
|
|
2495
|
+
HYPERLIQUID_WHALE_DATA_MODEL_VERSION
|
|
2496
|
+
]
|
|
2497
|
+
);
|
|
2498
|
+
const row = result.rows[0];
|
|
2499
|
+
if (!row) return null;
|
|
2500
|
+
return {
|
|
2501
|
+
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),
|
|
2502
|
+
coveredFromMs: row.covered_from_ts == null ? null : new Date(row.covered_from_ts).getTime(),
|
|
2503
|
+
coveredToMs: row.covered_to_ts == null ? null : new Date(row.covered_to_ts).getTime(),
|
|
2504
|
+
fillsCount: Number(row.fills_count) || 0,
|
|
2505
|
+
error: row.error == null ? null : String(row.error),
|
|
2506
|
+
checkedAt: new Date(row.checked_at)
|
|
2507
|
+
};
|
|
2508
|
+
}
|
|
2509
|
+
async function upsertHyperliquidWhaleWalletCoverage(params) {
|
|
2510
|
+
await ensureHyperliquidWhaleSchema();
|
|
2511
|
+
await getPool().query(
|
|
2512
|
+
`
|
|
2513
|
+
INSERT INTO hyperliquid_whale_wallet_coverage (
|
|
2514
|
+
universe_fingerprint,
|
|
2515
|
+
whale_registry_fingerprint,
|
|
2516
|
+
address,
|
|
2517
|
+
requested_from_ts,
|
|
2518
|
+
requested_to_ts,
|
|
2519
|
+
covered_from_ts,
|
|
2520
|
+
covered_to_ts,
|
|
2521
|
+
status,
|
|
2522
|
+
fills_count,
|
|
2523
|
+
error,
|
|
2524
|
+
data_model_version
|
|
2525
|
+
) VALUES (
|
|
2526
|
+
$1,
|
|
2527
|
+
$2,
|
|
2528
|
+
$3,
|
|
2529
|
+
to_timestamp($4/1000.0),
|
|
2530
|
+
to_timestamp($5/1000.0),
|
|
2531
|
+
CASE WHEN $6::double precision IS NULL THEN NULL ELSE to_timestamp($6/1000.0) END,
|
|
2532
|
+
CASE WHEN $7::double precision IS NULL THEN NULL ELSE to_timestamp($7/1000.0) END,
|
|
2533
|
+
$8,
|
|
2534
|
+
$9,
|
|
2535
|
+
$10,
|
|
2536
|
+
$11
|
|
2537
|
+
)
|
|
2538
|
+
ON CONFLICT (
|
|
2539
|
+
universe_fingerprint,
|
|
2540
|
+
whale_registry_fingerprint,
|
|
2541
|
+
address,
|
|
2542
|
+
requested_from_ts,
|
|
2543
|
+
requested_to_ts
|
|
2544
|
+
) DO UPDATE SET
|
|
2545
|
+
covered_from_ts = EXCLUDED.covered_from_ts,
|
|
2546
|
+
covered_to_ts = EXCLUDED.covered_to_ts,
|
|
2547
|
+
status = EXCLUDED.status,
|
|
2548
|
+
fills_count = EXCLUDED.fills_count,
|
|
2549
|
+
error = EXCLUDED.error,
|
|
2550
|
+
data_model_version = EXCLUDED.data_model_version,
|
|
2551
|
+
checked_at = now()
|
|
2552
|
+
`,
|
|
2553
|
+
[
|
|
2554
|
+
params.universeFingerprint,
|
|
2555
|
+
params.whaleRegistryFingerprint,
|
|
2556
|
+
params.address.toLowerCase(),
|
|
2557
|
+
params.fromMs,
|
|
2558
|
+
params.toMs,
|
|
2559
|
+
params.coveredFromMs,
|
|
2560
|
+
params.coveredToMs,
|
|
2561
|
+
params.status,
|
|
2562
|
+
params.fillsCount,
|
|
2563
|
+
params.error ?? null,
|
|
2564
|
+
HYPERLIQUID_WHALE_DATA_MODEL_VERSION
|
|
2565
|
+
]
|
|
2566
|
+
);
|
|
2567
|
+
}
|
|
2568
|
+
async function rebuildHyperliquidWhaleCoverageRows(params) {
|
|
2569
|
+
await ensureHyperliquidWhaleSchema();
|
|
2570
|
+
if (params.toMs <= params.fromMs) return 0;
|
|
2571
|
+
const result = await getPool().query(
|
|
2572
|
+
`
|
|
2573
|
+
WITH buckets AS (
|
|
2574
|
+
SELECT generate_series(
|
|
2575
|
+
to_timestamp($3/1000.0),
|
|
2576
|
+
to_timestamp($4/1000.0) - interval '1 minute',
|
|
2577
|
+
interval '1 minute'
|
|
2578
|
+
) AS ts
|
|
2579
|
+
), coverage AS (
|
|
2580
|
+
SELECT
|
|
2581
|
+
buckets.ts,
|
|
2582
|
+
COUNT(DISTINCT wallets.address)::int AS covered_whales
|
|
2583
|
+
FROM buckets
|
|
2584
|
+
LEFT JOIN hyperliquid_whale_wallet_coverage wallets
|
|
2585
|
+
ON wallets.universe_fingerprint = $1
|
|
2586
|
+
AND wallets.whale_registry_fingerprint = $2
|
|
2587
|
+
AND wallets.data_model_version = $6
|
|
2588
|
+
AND wallets.status IN ('complete', 'truncated')
|
|
2589
|
+
AND wallets.covered_from_ts <= buckets.ts
|
|
2590
|
+
AND wallets.covered_to_ts >= buckets.ts + interval '1 minute'
|
|
2591
|
+
GROUP BY buckets.ts
|
|
2592
|
+
)
|
|
2593
|
+
INSERT INTO hyperliquid_whale_coverage_1m (
|
|
2594
|
+
ts,
|
|
2595
|
+
covered_whales,
|
|
2596
|
+
expected_whales,
|
|
2597
|
+
coverage_pct,
|
|
2598
|
+
universe_fingerprint,
|
|
2599
|
+
whale_registry_fingerprint,
|
|
2600
|
+
source
|
|
2601
|
+
,data_model_version
|
|
2602
|
+
)
|
|
2603
|
+
SELECT
|
|
2604
|
+
ts,
|
|
2605
|
+
covered_whales,
|
|
2606
|
+
$5,
|
|
2607
|
+
CASE WHEN $5 > 0 THEN covered_whales::double precision / $5 ELSE 0 END,
|
|
2608
|
+
$1,
|
|
2609
|
+
$2,
|
|
2610
|
+
'hyperliquid_user_fills',
|
|
2611
|
+
$6
|
|
2612
|
+
FROM coverage
|
|
2613
|
+
ON CONFLICT (
|
|
2614
|
+
universe_fingerprint,
|
|
2615
|
+
whale_registry_fingerprint,
|
|
2616
|
+
ts
|
|
2617
|
+
) DO UPDATE SET
|
|
2618
|
+
covered_whales = EXCLUDED.covered_whales,
|
|
2619
|
+
expected_whales = EXCLUDED.expected_whales,
|
|
2620
|
+
coverage_pct = EXCLUDED.coverage_pct,
|
|
2621
|
+
source = EXCLUDED.source,
|
|
2622
|
+
data_model_version = EXCLUDED.data_model_version,
|
|
2623
|
+
ingested_at = now()
|
|
2624
|
+
RETURNING 1
|
|
2625
|
+
`,
|
|
2626
|
+
[
|
|
2627
|
+
params.universeFingerprint,
|
|
2628
|
+
params.whaleRegistryFingerprint,
|
|
2629
|
+
params.fromMs,
|
|
2630
|
+
params.toMs,
|
|
2631
|
+
params.expectedWhales,
|
|
2632
|
+
HYPERLIQUID_WHALE_DATA_MODEL_VERSION
|
|
2633
|
+
]
|
|
2634
|
+
);
|
|
2635
|
+
return result.rowCount ?? 0;
|
|
2636
|
+
}
|
|
2637
|
+
async function upsertHyperliquidWhaleCoverageRows(rows) {
|
|
2638
|
+
if (!rows.length) return;
|
|
2639
|
+
await ensureHyperliquidWhaleSchema();
|
|
2640
|
+
const cols = [
|
|
2641
|
+
"ts",
|
|
2642
|
+
"covered_whales",
|
|
2643
|
+
"expected_whales",
|
|
2644
|
+
"coverage_pct",
|
|
2645
|
+
"universe_fingerprint",
|
|
2646
|
+
"whale_registry_fingerprint",
|
|
2647
|
+
"source",
|
|
2648
|
+
"data_model_version"
|
|
2649
|
+
];
|
|
2650
|
+
const maxRows = Math.floor(PG_SAFE_MAX_BIND_PARAMS / cols.length);
|
|
2651
|
+
if (rows.length > maxRows) {
|
|
2652
|
+
for (let index = 0; index < rows.length; index += maxRows) {
|
|
2653
|
+
await upsertHyperliquidWhaleCoverageRows(
|
|
2654
|
+
rows.slice(index, index + maxRows)
|
|
2655
|
+
);
|
|
2656
|
+
}
|
|
2657
|
+
return;
|
|
2658
|
+
}
|
|
2659
|
+
const values = [];
|
|
2660
|
+
const tuples = rows.map((row, rowIndex) => {
|
|
2661
|
+
const offset = rowIndex * cols.length;
|
|
2662
|
+
values.push(
|
|
2663
|
+
row.ts,
|
|
2664
|
+
row.coveredWhales,
|
|
2665
|
+
row.expectedWhales,
|
|
2666
|
+
row.coveragePct,
|
|
2667
|
+
row.universeFingerprint,
|
|
2668
|
+
row.whaleRegistryFingerprint,
|
|
2669
|
+
row.source ?? null,
|
|
2670
|
+
row.dataModelVersion ?? HYPERLIQUID_WHALE_DATA_MODEL_VERSION
|
|
2671
|
+
);
|
|
2672
|
+
return `(${cols.map((_, colIndex) => `$${offset + colIndex + 1}`).join(",")})`;
|
|
2673
|
+
});
|
|
2674
|
+
await getPool().query(
|
|
2675
|
+
`
|
|
2676
|
+
INSERT INTO hyperliquid_whale_coverage_1m (${cols.join(",")})
|
|
2677
|
+
VALUES ${tuples.join(",")}
|
|
2678
|
+
ON CONFLICT (
|
|
2679
|
+
universe_fingerprint,
|
|
2680
|
+
whale_registry_fingerprint,
|
|
2681
|
+
ts
|
|
2682
|
+
) DO UPDATE SET
|
|
2683
|
+
covered_whales = EXCLUDED.covered_whales,
|
|
2684
|
+
expected_whales = EXCLUDED.expected_whales,
|
|
2685
|
+
coverage_pct = EXCLUDED.coverage_pct,
|
|
2686
|
+
source = EXCLUDED.source,
|
|
2687
|
+
data_model_version = EXCLUDED.data_model_version,
|
|
2688
|
+
ingested_at = now()
|
|
2689
|
+
`,
|
|
2690
|
+
values
|
|
2691
|
+
);
|
|
2692
|
+
}
|
|
2693
|
+
async function getHyperliquidWhaleCoverageSeriesRows(params) {
|
|
2694
|
+
await ensureHyperliquidWhaleSchema();
|
|
2695
|
+
const result = await getPool().query(
|
|
2696
|
+
`
|
|
2697
|
+
SELECT
|
|
2698
|
+
ts,
|
|
2699
|
+
covered_whales,
|
|
2700
|
+
expected_whales,
|
|
2701
|
+
coverage_pct
|
|
2702
|
+
FROM hyperliquid_whale_coverage_1m
|
|
2703
|
+
WHERE universe_fingerprint = $1
|
|
2704
|
+
AND whale_registry_fingerprint = $2
|
|
2705
|
+
AND data_model_version = $3
|
|
2706
|
+
AND ts >= to_timestamp($4/1000.0)
|
|
2707
|
+
AND ts < to_timestamp($5/1000.0)
|
|
2708
|
+
ORDER BY ts
|
|
2709
|
+
`,
|
|
2710
|
+
[
|
|
2711
|
+
params.universeFingerprint,
|
|
2712
|
+
params.whaleRegistryFingerprint,
|
|
2713
|
+
HYPERLIQUID_WHALE_DATA_MODEL_VERSION,
|
|
2714
|
+
params.fromMs,
|
|
2715
|
+
params.toMs
|
|
2716
|
+
]
|
|
2717
|
+
);
|
|
2718
|
+
return result.rows.map((row) => ({
|
|
2719
|
+
ts: new Date(row.ts),
|
|
2720
|
+
coveredWhales: Number(row.covered_whales) || 0,
|
|
2721
|
+
expectedWhales: Number(row.expected_whales) || 0,
|
|
2722
|
+
coveragePct: Number(row.coverage_pct) || 0
|
|
2723
|
+
}));
|
|
2724
|
+
}
|
|
2725
|
+
async function getHyperliquidWhaleFlowSeriesRows(params) {
|
|
2726
|
+
await ensureHyperliquidWhaleSchema();
|
|
2727
|
+
const result = await getPool().query(
|
|
2728
|
+
`
|
|
2729
|
+
SELECT
|
|
2730
|
+
ts,
|
|
2731
|
+
trades,
|
|
2732
|
+
whale_sides,
|
|
2733
|
+
whale_addresses,
|
|
2734
|
+
buy_notional_usd,
|
|
2735
|
+
sell_notional_usd,
|
|
2736
|
+
position_aware_whale_sides,
|
|
2737
|
+
long_entry_whale_addresses,
|
|
2738
|
+
short_entry_whale_addresses,
|
|
2739
|
+
long_exit_whale_addresses,
|
|
2740
|
+
short_exit_whale_addresses,
|
|
2741
|
+
long_entry_notional_usd,
|
|
2742
|
+
short_entry_notional_usd,
|
|
2743
|
+
long_exit_notional_usd,
|
|
2744
|
+
short_exit_notional_usd
|
|
2745
|
+
FROM hyperliquid_whale_flow
|
|
2746
|
+
WHERE symbol = $1
|
|
2747
|
+
AND interval = '1m'
|
|
2748
|
+
AND universe_fingerprint = $2
|
|
2749
|
+
AND whale_registry_fingerprint = $3
|
|
2750
|
+
AND ts >= to_timestamp($4/1000.0)
|
|
2751
|
+
AND ts < to_timestamp($5/1000.0)
|
|
2752
|
+
ORDER BY ts
|
|
2753
|
+
`,
|
|
2754
|
+
[
|
|
2755
|
+
params.symbol,
|
|
2756
|
+
params.universeFingerprint,
|
|
2757
|
+
params.whaleRegistryFingerprint,
|
|
2758
|
+
params.fromMs,
|
|
2759
|
+
params.toMs
|
|
2760
|
+
]
|
|
2761
|
+
);
|
|
2762
|
+
return result.rows.map((row) => ({
|
|
2763
|
+
ts: new Date(row.ts),
|
|
2764
|
+
trades: Number(row.trades) || 0,
|
|
2765
|
+
whaleSides: Number(row.whale_sides) || 0,
|
|
2766
|
+
whaleAddresses: Array.isArray(row.whale_addresses) ? row.whale_addresses.map(String) : [],
|
|
2767
|
+
buyNotionalUsd: Number(row.buy_notional_usd) || 0,
|
|
2768
|
+
sellNotionalUsd: Number(row.sell_notional_usd) || 0,
|
|
2769
|
+
positionAwareWhaleSides: Number(row.position_aware_whale_sides) || 0,
|
|
2770
|
+
longEntryWhaleAddresses: Array.isArray(row.long_entry_whale_addresses) ? row.long_entry_whale_addresses.map(String) : [],
|
|
2771
|
+
shortEntryWhaleAddresses: Array.isArray(row.short_entry_whale_addresses) ? row.short_entry_whale_addresses.map(String) : [],
|
|
2772
|
+
longExitWhaleAddresses: Array.isArray(row.long_exit_whale_addresses) ? row.long_exit_whale_addresses.map(String) : [],
|
|
2773
|
+
shortExitWhaleAddresses: Array.isArray(row.short_exit_whale_addresses) ? row.short_exit_whale_addresses.map(String) : [],
|
|
2774
|
+
longEntryNotionalUsd: Number(row.long_entry_notional_usd) || 0,
|
|
2775
|
+
shortEntryNotionalUsd: Number(row.short_entry_notional_usd) || 0,
|
|
2776
|
+
longExitNotionalUsd: Number(row.long_exit_notional_usd) || 0,
|
|
2777
|
+
shortExitNotionalUsd: Number(row.short_exit_notional_usd) || 0
|
|
2778
|
+
}));
|
|
2779
|
+
}
|
|
2780
|
+
async function getHyperliquidWhaleFlowAggregate(params) {
|
|
2781
|
+
await ensureHyperliquidWhaleSchema();
|
|
2782
|
+
const intervalMs = HYPERLIQUID_CONTEXT_INTERVAL_MS[params.interval];
|
|
2783
|
+
const expectedBuckets = Math.ceil(intervalMs / 6e4);
|
|
2784
|
+
const res = await getPool().query(
|
|
2785
|
+
`
|
|
2786
|
+
WITH coverage_rows AS (
|
|
2787
|
+
SELECT *
|
|
2788
|
+
FROM hyperliquid_whale_coverage_1m
|
|
2789
|
+
WHERE universe_fingerprint = $2
|
|
2790
|
+
AND whale_registry_fingerprint = $3
|
|
2791
|
+
AND data_model_version = $6
|
|
2792
|
+
AND ts >= to_timestamp(
|
|
2793
|
+
($4::double precision - $5::double precision) / 1000.0
|
|
2794
|
+
)
|
|
2795
|
+
AND ts < to_timestamp($4/1000.0)
|
|
2796
|
+
), coverage_summary AS (
|
|
2797
|
+
SELECT
|
|
2798
|
+
COUNT(*)::int AS coverage_buckets,
|
|
2799
|
+
MAX(ts) AS coverage_as_of_ts,
|
|
2800
|
+
MIN(covered_whales)::int AS covered_whales,
|
|
2801
|
+
MAX(expected_whales)::int AS expected_whales,
|
|
2802
|
+
MIN(coverage_pct) AS coverage_pct
|
|
2803
|
+
FROM coverage_rows
|
|
2804
|
+
), window_rows AS (
|
|
2805
|
+
SELECT *
|
|
2806
|
+
FROM hyperliquid_whale_flow
|
|
2807
|
+
WHERE symbol = $1
|
|
2808
|
+
AND interval = '1m'
|
|
2809
|
+
AND universe_fingerprint = $2
|
|
2810
|
+
AND whale_registry_fingerprint = $3
|
|
2811
|
+
AND ts >= to_timestamp(
|
|
2812
|
+
($4::double precision - $5::double precision) / 1000.0
|
|
2813
|
+
)
|
|
2814
|
+
AND ts < to_timestamp($4/1000.0)
|
|
2815
|
+
), unique_addresses AS (
|
|
2816
|
+
SELECT COUNT(DISTINCT address)::int AS unique_whales
|
|
2817
|
+
FROM window_rows
|
|
2818
|
+
CROSS JOIN LATERAL UNNEST(whale_addresses) AS expanded(address)
|
|
2819
|
+
), directional_counts AS (
|
|
2820
|
+
SELECT
|
|
2821
|
+
(
|
|
2822
|
+
SELECT COUNT(DISTINCT address)::int
|
|
2823
|
+
FROM window_rows
|
|
2824
|
+
CROSS JOIN LATERAL UNNEST(long_entry_whale_addresses) AS expanded(address)
|
|
2825
|
+
) AS long_entry_whales,
|
|
2826
|
+
(
|
|
2827
|
+
SELECT COUNT(DISTINCT address)::int
|
|
2828
|
+
FROM window_rows
|
|
2829
|
+
CROSS JOIN LATERAL UNNEST(short_entry_whale_addresses) AS expanded(address)
|
|
2830
|
+
) AS short_entry_whales,
|
|
2831
|
+
(
|
|
2832
|
+
SELECT COUNT(DISTINCT address)::int
|
|
2833
|
+
FROM window_rows
|
|
2834
|
+
CROSS JOIN LATERAL UNNEST(long_exit_whale_addresses) AS expanded(address)
|
|
2835
|
+
) AS long_exit_whales,
|
|
2836
|
+
(
|
|
2837
|
+
SELECT COUNT(DISTINCT address)::int
|
|
2838
|
+
FROM window_rows
|
|
2839
|
+
CROSS JOIN LATERAL UNNEST(short_exit_whale_addresses) AS expanded(address)
|
|
2840
|
+
) AS short_exit_whales
|
|
2841
|
+
)
|
|
2842
|
+
SELECT
|
|
2843
|
+
$1::text AS symbol,
|
|
2844
|
+
coverage_summary.coverage_as_of_ts AS as_of_ts,
|
|
2845
|
+
coverage_summary.coverage_buckets,
|
|
2846
|
+
coverage_summary.covered_whales,
|
|
2847
|
+
coverage_summary.expected_whales,
|
|
2848
|
+
coverage_summary.coverage_pct,
|
|
2849
|
+
COALESCE((SELECT SUM(trades) FROM window_rows), 0)::int AS trades,
|
|
2850
|
+
COALESCE((SELECT SUM(whale_sides) FROM window_rows), 0)::int AS whale_sides,
|
|
2851
|
+
COALESCE((SELECT unique_whales FROM unique_addresses), 0)::int AS unique_whales,
|
|
2852
|
+
COALESCE((SELECT SUM(buy_notional_usd) FROM window_rows), 0) AS buy_notional_usd,
|
|
2853
|
+
COALESCE((SELECT SUM(sell_notional_usd) FROM window_rows), 0) AS sell_notional_usd,
|
|
2854
|
+
COALESCE((SELECT SUM(net_notional_usd) FROM window_rows), 0) AS net_notional_usd,
|
|
2855
|
+
CASE
|
|
2856
|
+
WHEN COALESCE((SELECT SUM(buy_notional_usd + sell_notional_usd) FROM window_rows), 0) > 0
|
|
2857
|
+
THEN (SELECT SUM(buy_notional_usd) FROM window_rows) /
|
|
2858
|
+
(SELECT SUM(buy_notional_usd + sell_notional_usd) FROM window_rows)
|
|
2859
|
+
ELSE NULL
|
|
2860
|
+
END AS buy_share_pct,
|
|
2861
|
+
COALESCE((SELECT SUM(position_aware_whale_sides) FROM window_rows), 0)::int
|
|
2862
|
+
AS position_aware_whale_sides,
|
|
2863
|
+
CASE
|
|
2864
|
+
WHEN COALESCE((SELECT SUM(whale_sides) FROM window_rows), 0) > 0
|
|
2865
|
+
THEN COALESCE((SELECT SUM(position_aware_whale_sides) FROM window_rows), 0)::double precision /
|
|
2866
|
+
(SELECT SUM(whale_sides) FROM window_rows)
|
|
2867
|
+
ELSE 0
|
|
2868
|
+
END AS position_aware_pct,
|
|
2869
|
+
COALESCE((SELECT long_entry_whales FROM directional_counts), 0)::int AS long_entry_whales,
|
|
2870
|
+
COALESCE((SELECT short_entry_whales FROM directional_counts), 0)::int AS short_entry_whales,
|
|
2871
|
+
COALESCE((SELECT long_exit_whales FROM directional_counts), 0)::int AS long_exit_whales,
|
|
2872
|
+
COALESCE((SELECT short_exit_whales FROM directional_counts), 0)::int AS short_exit_whales,
|
|
2873
|
+
COALESCE((SELECT SUM(long_entry_notional_usd) FROM window_rows), 0)
|
|
2874
|
+
AS long_entry_notional_usd,
|
|
2875
|
+
COALESCE((SELECT SUM(short_entry_notional_usd) FROM window_rows), 0)
|
|
2876
|
+
AS short_entry_notional_usd,
|
|
2877
|
+
COALESCE((SELECT SUM(long_exit_notional_usd) FROM window_rows), 0)
|
|
2878
|
+
AS long_exit_notional_usd,
|
|
2879
|
+
COALESCE((SELECT SUM(short_exit_notional_usd) FROM window_rows), 0)
|
|
2880
|
+
AS short_exit_notional_usd,
|
|
2881
|
+
COALESCE((SELECT SUM(entry_net_notional_usd) FROM window_rows), 0)
|
|
2882
|
+
AS entry_net_notional_usd,
|
|
2883
|
+
CASE
|
|
2884
|
+
WHEN COALESCE((SELECT SUM(long_entry_notional_usd + short_entry_notional_usd) FROM window_rows), 0) > 0
|
|
2885
|
+
THEN (SELECT SUM(long_entry_notional_usd) FROM window_rows) /
|
|
2886
|
+
(SELECT SUM(long_entry_notional_usd + short_entry_notional_usd) FROM window_rows)
|
|
2887
|
+
ELSE NULL
|
|
2888
|
+
END AS entry_long_share_pct,
|
|
2889
|
+
(SELECT MAX(source) FROM window_rows) AS source
|
|
2890
|
+
FROM coverage_summary
|
|
2891
|
+
`,
|
|
2892
|
+
[
|
|
2893
|
+
params.symbol,
|
|
2894
|
+
params.universeFingerprint,
|
|
2895
|
+
params.whaleRegistryFingerprint,
|
|
2896
|
+
params.decisionTimeMs,
|
|
2897
|
+
intervalMs,
|
|
2898
|
+
HYPERLIQUID_WHALE_DATA_MODEL_VERSION
|
|
2899
|
+
]
|
|
2900
|
+
);
|
|
2901
|
+
const row = res.rows[0];
|
|
2902
|
+
if (!row?.as_of_ts || Number(row.coverage_buckets) !== expectedBuckets || Number(row.covered_whales) <= 0) {
|
|
2903
|
+
return null;
|
|
2904
|
+
}
|
|
2905
|
+
const asOfTs = new Date(row.as_of_ts);
|
|
2906
|
+
const ageMs = params.decisionTimeMs - (asOfTs.getTime() + 6e4);
|
|
2907
|
+
return {
|
|
2908
|
+
symbol: params.symbol,
|
|
2909
|
+
interval: params.interval,
|
|
2910
|
+
asOfTs,
|
|
2911
|
+
windowEndTs: new Date(params.decisionTimeMs),
|
|
2912
|
+
trades: Number(row.trades) || 0,
|
|
2913
|
+
whaleSides: Number(row.whale_sides) || 0,
|
|
2914
|
+
uniqueWhales: Number(row.unique_whales) || 0,
|
|
2915
|
+
coveredWhales: Number(row.covered_whales) || 0,
|
|
2916
|
+
expectedWhales: Number(row.expected_whales) || 0,
|
|
2917
|
+
coveragePct: Number(row.coverage_pct) || 0,
|
|
2918
|
+
buyNotionalUsd: Number(row.buy_notional_usd) || 0,
|
|
2919
|
+
sellNotionalUsd: Number(row.sell_notional_usd) || 0,
|
|
2920
|
+
netNotionalUsd: Number(row.net_notional_usd) || 0,
|
|
2921
|
+
buySharePct: row.buy_share_pct == null ? null : Number(row.buy_share_pct) || 0,
|
|
2922
|
+
positionAwareWhaleSides: Number(row.position_aware_whale_sides) || 0,
|
|
2923
|
+
positionAwarePct: Number(row.position_aware_pct) || 0,
|
|
2924
|
+
longEntryWhales: Number(row.long_entry_whales) || 0,
|
|
2925
|
+
shortEntryWhales: Number(row.short_entry_whales) || 0,
|
|
2926
|
+
longExitWhales: Number(row.long_exit_whales) || 0,
|
|
2927
|
+
shortExitWhales: Number(row.short_exit_whales) || 0,
|
|
2928
|
+
longEntryNotionalUsd: Number(row.long_entry_notional_usd) || 0,
|
|
2929
|
+
shortEntryNotionalUsd: Number(row.short_entry_notional_usd) || 0,
|
|
2930
|
+
longExitNotionalUsd: Number(row.long_exit_notional_usd) || 0,
|
|
2931
|
+
shortExitNotionalUsd: Number(row.short_exit_notional_usd) || 0,
|
|
2932
|
+
entryNetNotionalUsd: Number(row.entry_net_notional_usd) || 0,
|
|
2933
|
+
entryLongSharePct: row.entry_long_share_pct == null ? null : Number(row.entry_long_share_pct) || 0,
|
|
2934
|
+
universeFingerprint: params.universeFingerprint,
|
|
2935
|
+
whaleRegistryFingerprint: params.whaleRegistryFingerprint,
|
|
2936
|
+
source: row.source == null ? null : String(row.source),
|
|
2937
|
+
ageMs,
|
|
2938
|
+
stale: ageMs < 0 || (params.maxAgeMs != null && Number.isFinite(params.maxAgeMs) ? ageMs > params.maxAgeMs : false)
|
|
2939
|
+
};
|
|
2940
|
+
}
|
|
2941
|
+
async function hasHyperliquidWhaleBackfillCoverage(params) {
|
|
2942
|
+
await ensureHyperliquidWhaleSchema();
|
|
2943
|
+
const expectedBuckets = Math.max(
|
|
2944
|
+
0,
|
|
2945
|
+
Math.ceil((params.toMs - params.fromMs) / 6e4)
|
|
2946
|
+
);
|
|
2947
|
+
const result = await getPool().query(
|
|
2948
|
+
`
|
|
2949
|
+
SELECT
|
|
2950
|
+
COUNT(*)::int AS buckets,
|
|
2951
|
+
COUNT(*) FILTER (
|
|
2952
|
+
WHERE covered_whales = expected_whales
|
|
2953
|
+
)::int AS complete_buckets
|
|
2954
|
+
FROM hyperliquid_whale_coverage_1m
|
|
2955
|
+
WHERE universe_fingerprint = $1
|
|
2956
|
+
AND whale_registry_fingerprint = $2
|
|
2957
|
+
AND data_model_version = $5
|
|
2958
|
+
AND ts >= to_timestamp($3/1000.0)
|
|
2959
|
+
AND ts < to_timestamp($4/1000.0)
|
|
2960
|
+
`,
|
|
2961
|
+
[
|
|
2962
|
+
params.universeFingerprint,
|
|
2963
|
+
params.whaleRegistryFingerprint,
|
|
2964
|
+
params.fromMs,
|
|
2965
|
+
params.toMs,
|
|
2966
|
+
HYPERLIQUID_WHALE_DATA_MODEL_VERSION
|
|
2967
|
+
]
|
|
2968
|
+
);
|
|
2969
|
+
return Number(result.rows[0]?.buckets) === expectedBuckets && Number(result.rows[0]?.complete_buckets) === expectedBuckets;
|
|
2970
|
+
}
|
|
1733
2971
|
async function getLatestMarketBreadth(params) {
|
|
1734
2972
|
await ensureBinanceMarketSchema();
|
|
1735
2973
|
const pool = getPool();
|
|
@@ -2634,6 +3872,10 @@ export {
|
|
|
2634
3872
|
getDerivativesRangeForSymbols,
|
|
2635
3873
|
getDerivativesSummary,
|
|
2636
3874
|
getDerivativesWindow,
|
|
3875
|
+
getHyperliquidWhaleCoverageSeriesRows,
|
|
3876
|
+
getHyperliquidWhaleFlowAggregate,
|
|
3877
|
+
getHyperliquidWhaleFlowSeriesRows,
|
|
3878
|
+
getHyperliquidWhaleWalletCoverage,
|
|
2637
3879
|
getLatestMarketBreadth,
|
|
2638
3880
|
getLatestMarketCmcExchangeLiquidityContext,
|
|
2639
3881
|
getLatestMarketCmcFearGreedContext,
|
|
@@ -2651,10 +3893,17 @@ export {
|
|
|
2651
3893
|
getMarketTradeFlowCoverage,
|
|
2652
3894
|
getSpreadRangeForSymbols,
|
|
2653
3895
|
getSpreadSummary,
|
|
3896
|
+
hasHyperliquidWhaleBackfillCoverage,
|
|
3897
|
+
rebuildHyperliquidWhaleCoverageRows,
|
|
3898
|
+
rebuildHyperliquidWhaleFlowRows,
|
|
2654
3899
|
toRows,
|
|
2655
3900
|
upsertCandles,
|
|
2656
3901
|
upsertDerivatives,
|
|
2657
3902
|
upsertDerivativesBackfillCoverage,
|
|
3903
|
+
upsertHyperliquidWhaleCoverageRows,
|
|
3904
|
+
upsertHyperliquidWhaleFlowRows,
|
|
3905
|
+
upsertHyperliquidWhaleTradeEvents,
|
|
3906
|
+
upsertHyperliquidWhaleWalletCoverage,
|
|
2658
3907
|
upsertMarketBreadthRows,
|
|
2659
3908
|
upsertMarketCmcExchangeLiquidityContextRows,
|
|
2660
3909
|
upsertMarketCmcFearGreedContextRows,
|