@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.
- package/dist/{chunk-YVIHTUV5.mjs → chunk-OLB6NQRG.mjs} +1 -1
- package/dist/{chunk-I2J6YDBD.mjs → chunk-ZXU7OYFU.mjs} +65 -38
- package/dist/internal-BuHqoCCV.d.mts +9 -0
- package/dist/internal-q48Torp4.d.ts +9 -0
- package/dist/ml.mjs +3 -3
- package/dist/query-tkHVD4Xo.d.mts +12 -0
- package/dist/query-tkHVD4Xo.d.ts +12 -0
- package/dist/timescale/candles.js +6 -4
- package/dist/timescale/candles.mjs +2 -2
- package/dist/timescale/client.d.mts +3 -2
- package/dist/timescale/client.d.ts +3 -2
- package/dist/timescale/client.js +40 -15
- package/dist/timescale/client.mjs +2 -2
- package/dist/timescale/derivatives.d.mts +4 -2
- package/dist/timescale/derivatives.d.ts +4 -2
- package/dist/timescale/derivatives.js +20 -12
- package/dist/timescale/derivatives.mjs +479 -13
- package/dist/timescale/hyperliquidWhales.d.mts +29 -25
- package/dist/timescale/hyperliquidWhales.d.ts +29 -25
- package/dist/timescale/hyperliquidWhales.js +84 -71
- package/dist/timescale/hyperliquidWhales.mjs +1103 -14
- package/dist/timescale/marketContext.d.mts +15 -11
- package/dist/timescale/marketContext.d.ts +15 -11
- package/dist/timescale/marketContext.js +67 -57
- package/dist/timescale/marketContext.mjs +1259 -28
- package/dist/timescale/spread.js +6 -4
- package/dist/timescale/spread.mjs +105 -5
- package/dist/userSettings.js +3 -316
- package/dist/userSettings.mjs +3 -21
- package/package.json +2 -22
- package/dist/aiEndpoints.d.mts +0 -10
- package/dist/aiEndpoints.d.ts +0 -10
- package/dist/aiEndpoints.js +0 -159
- package/dist/aiEndpoints.mjs +0 -12
- package/dist/aiLanguages.d.mts +0 -11
- package/dist/aiLanguages.d.ts +0 -11
- package/dist/aiLanguages.js +0 -71
- package/dist/aiLanguages.mjs +0 -12
- package/dist/aiModels.d.mts +0 -12
- package/dist/aiModels.d.ts +0 -12
- package/dist/aiModels.js +0 -272
- package/dist/aiModels.mjs +0 -17
- package/dist/chunk-2CZREG43.mjs +0 -112
- package/dist/chunk-CCC7DX2T.mjs +0 -44
- package/dist/chunk-DFMKDB2R.mjs +0 -1285
- package/dist/chunk-DTCLZIBM.mjs +0 -163
- package/dist/chunk-NWXFWTWU.mjs +0 -1114
- package/dist/chunk-SZQB7ER5.mjs +0 -492
- package/dist/chunk-XQ3YBULV.mjs +0 -132
- package/dist/internal-2coHaaos.d.mts +0 -26
- package/dist/internal-2coHaaos.d.ts +0 -26
- package/dist/timescale.d.mts +0 -9
- package/dist/timescale.d.ts +0 -9
- package/dist/timescale.js +0 -4228
- package/dist/timescale.mjs +0 -138
package/dist/chunk-NWXFWTWU.mjs
DELETED
|
@@ -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
|
-
};
|