@tradejs/infra 2.0.15 → 2.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-EFCQ7NGN.mjs → chunk-6ZKAUA4Q.mjs} +2 -0
- package/dist/redis.d.mts +2 -0
- package/dist/redis.d.ts +2 -0
- package/dist/redis.js +2 -0
- package/dist/redis.mjs +1 -1
- package/dist/timescale.d.mts +48 -1
- package/dist/timescale.d.ts +48 -1
- package/dist/timescale.js +343 -119
- package/dist/timescale.mjs +337 -119
- package/dist/tradingAccounts.js +2 -0
- package/dist/tradingAccounts.mjs +1 -1
- package/dist/userSettings.js +2 -0
- package/dist/userSettings.mjs +1 -1
- package/package.json +2 -2
package/dist/timescale.mjs
CHANGED
|
@@ -37,6 +37,12 @@ var derivativesSchemaReadyPromise = null;
|
|
|
37
37
|
var spreadSchemaReadyPromise = null;
|
|
38
38
|
var binanceMarketSchemaReadyPromise = null;
|
|
39
39
|
var hyperliquidWhaleSchemaReadyPromise = null;
|
|
40
|
+
var marketContextSchemaMode = "ensure";
|
|
41
|
+
var verifiedMarketContextSchemas = /* @__PURE__ */ new Set();
|
|
42
|
+
var configureTimescaleMarketContextSchemaMode = (mode) => {
|
|
43
|
+
marketContextSchemaMode = mode;
|
|
44
|
+
verifiedMarketContextSchemas.clear();
|
|
45
|
+
};
|
|
40
46
|
var closeTimescalePool = async () => {
|
|
41
47
|
const pool = global.__pgPool__;
|
|
42
48
|
if (!pool) {
|
|
@@ -53,6 +59,7 @@ var closeTimescalePool = async () => {
|
|
|
53
59
|
spreadSchemaReadyPromise = null;
|
|
54
60
|
binanceMarketSchemaReadyPromise = null;
|
|
55
61
|
hyperliquidWhaleSchemaReadyPromise = null;
|
|
62
|
+
verifiedMarketContextSchemas.clear();
|
|
56
63
|
await pool.end();
|
|
57
64
|
};
|
|
58
65
|
var CANDLES_SCHEMA_LOCK_KEY = 61e4;
|
|
@@ -61,6 +68,70 @@ var SPREAD_SCHEMA_LOCK_KEY = 610002;
|
|
|
61
68
|
var BINANCE_MARKET_SCHEMA_LOCK_KEY = 610003;
|
|
62
69
|
var HYPERLIQUID_WHALE_SCHEMA_LOCK_KEY = 610004;
|
|
63
70
|
var PG_SAFE_MAX_BIND_PARAMS = 3e4;
|
|
71
|
+
var resolveMarketContextQueryTimeoutMs = (override) => {
|
|
72
|
+
if (Number.isFinite(override) && Number(override) > 0) {
|
|
73
|
+
return Math.floor(Number(override));
|
|
74
|
+
}
|
|
75
|
+
const configured = Number(process.env.MARKET_CONTEXT_SQL_TIMEOUT_MS);
|
|
76
|
+
return Number.isFinite(configured) && configured > 0 ? Math.floor(configured) : 3e4;
|
|
77
|
+
};
|
|
78
|
+
var createMarketContextQueryError = (name, message) => {
|
|
79
|
+
const error = new Error(message);
|
|
80
|
+
error.name = name;
|
|
81
|
+
return error;
|
|
82
|
+
};
|
|
83
|
+
var queryMarketContext = async (text, values, options = {}) => {
|
|
84
|
+
const pool = getPool();
|
|
85
|
+
const client = await pool.connect();
|
|
86
|
+
const timeoutMs = resolveMarketContextQueryTimeoutMs(options.timeoutMs);
|
|
87
|
+
let released = false;
|
|
88
|
+
let rejectCancellation;
|
|
89
|
+
const release = (error) => {
|
|
90
|
+
if (released) return;
|
|
91
|
+
released = true;
|
|
92
|
+
client.release(error);
|
|
93
|
+
};
|
|
94
|
+
const cancellation = new Promise((_resolve, reject) => {
|
|
95
|
+
rejectCancellation = reject;
|
|
96
|
+
});
|
|
97
|
+
const cancel = (error) => {
|
|
98
|
+
release(error);
|
|
99
|
+
rejectCancellation?.(error);
|
|
100
|
+
};
|
|
101
|
+
const onAbort = () => cancel(
|
|
102
|
+
createMarketContextQueryError(
|
|
103
|
+
"AbortError",
|
|
104
|
+
"Timescale market-context query aborted"
|
|
105
|
+
)
|
|
106
|
+
);
|
|
107
|
+
const timer = setTimeout(
|
|
108
|
+
() => cancel(
|
|
109
|
+
createMarketContextQueryError(
|
|
110
|
+
"TimescaleQueryTimeoutError",
|
|
111
|
+
`Timescale market-context query exceeded ${timeoutMs}ms`
|
|
112
|
+
)
|
|
113
|
+
),
|
|
114
|
+
timeoutMs
|
|
115
|
+
);
|
|
116
|
+
timer.unref?.();
|
|
117
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
118
|
+
try {
|
|
119
|
+
if (options.signal?.aborted) {
|
|
120
|
+
const error = createMarketContextQueryError(
|
|
121
|
+
"AbortError",
|
|
122
|
+
"Timescale market-context query aborted"
|
|
123
|
+
);
|
|
124
|
+
release(error);
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
const query = client.query(text, values);
|
|
128
|
+
return await Promise.race([query, cancellation]);
|
|
129
|
+
} finally {
|
|
130
|
+
clearTimeout(timer);
|
|
131
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
132
|
+
release();
|
|
133
|
+
}
|
|
134
|
+
};
|
|
64
135
|
var normalizeCandleProvider = (provider) => String(provider || "").trim().toLowerCase();
|
|
65
136
|
var normalizeCandleSymbol = (symbol) => String(symbol || "").trim().toUpperCase();
|
|
66
137
|
var getSafeBulkInsertRows = (columnsCount) => Math.max(1, Math.floor(PG_SAFE_MAX_BIND_PARAMS / columnsCount));
|
|
@@ -865,6 +936,58 @@ var ensureHyperliquidWhaleSchema = async () => {
|
|
|
865
936
|
});
|
|
866
937
|
await hyperliquidWhaleSchemaReadyPromise;
|
|
867
938
|
};
|
|
939
|
+
var ensureCoinMarketCapContextSchema = async () => ensureBinanceMarketSchema();
|
|
940
|
+
var ensureMarketContextSchema = async (source) => {
|
|
941
|
+
switch (source) {
|
|
942
|
+
case "binance":
|
|
943
|
+
return ensureBinanceMarketSchema();
|
|
944
|
+
case "coinmarketcap":
|
|
945
|
+
return ensureCoinMarketCapContextSchema();
|
|
946
|
+
case "derivatives":
|
|
947
|
+
return ensureDerivativesSchema();
|
|
948
|
+
case "hyperliquidWhales":
|
|
949
|
+
return ensureHyperliquidWhaleSchema();
|
|
950
|
+
}
|
|
951
|
+
};
|
|
952
|
+
var MARKET_CONTEXT_SCHEMA_TABLES = {
|
|
953
|
+
binance: ["market_trade_flow", "market_breadth"],
|
|
954
|
+
coinmarketcap: [
|
|
955
|
+
"market_global_context",
|
|
956
|
+
"market_reference_asset_context",
|
|
957
|
+
"market_cmc_exchange_liquidity_context",
|
|
958
|
+
"market_cmc_fear_greed_context",
|
|
959
|
+
"market_cmc_index_context"
|
|
960
|
+
],
|
|
961
|
+
derivatives: ["derivatives_market"],
|
|
962
|
+
hyperliquidWhales: [
|
|
963
|
+
"hyperliquid_whale_flow",
|
|
964
|
+
"hyperliquid_whale_coverage_1m"
|
|
965
|
+
]
|
|
966
|
+
};
|
|
967
|
+
var verifyMarketContextSchema = async (source) => {
|
|
968
|
+
if (verifiedMarketContextSchemas.has(source)) return;
|
|
969
|
+
const tables = MARKET_CONTEXT_SCHEMA_TABLES[source];
|
|
970
|
+
const result = await queryMarketContext(
|
|
971
|
+
`
|
|
972
|
+
SELECT table_name AS "tableName"
|
|
973
|
+
FROM unnest($1::text[]) AS requested(table_name)
|
|
974
|
+
WHERE to_regclass(requested.table_name) IS NULL
|
|
975
|
+
`,
|
|
976
|
+
[tables]
|
|
977
|
+
);
|
|
978
|
+
if (result.rows.length) {
|
|
979
|
+
throw new Error(
|
|
980
|
+
`Timescale ${source} schema is not prepared; missing: ${result.rows.map((row) => row.tableName).filter(Boolean).join(", ")}`
|
|
981
|
+
);
|
|
982
|
+
}
|
|
983
|
+
verifiedMarketContextSchemas.add(source);
|
|
984
|
+
};
|
|
985
|
+
var prepareMarketContextSchemaForRead = async (source) => marketContextSchemaMode === "verify" ? verifyMarketContextSchema(source) : ensureMarketContextSchema(source);
|
|
986
|
+
var ensureMarketContextSchemas = async (sources) => {
|
|
987
|
+
for (const source of new Set(sources)) {
|
|
988
|
+
await ensureMarketContextSchema(source);
|
|
989
|
+
}
|
|
990
|
+
};
|
|
868
991
|
async function upsertDerivatives(rows) {
|
|
869
992
|
if (!rows.length) return;
|
|
870
993
|
await ensureDerivativesSchema();
|
|
@@ -1192,9 +1315,8 @@ async function getDerivativesWindow(params) {
|
|
|
1192
1315
|
if (!normalizedSymbol || !normalizedIntervals.length) {
|
|
1193
1316
|
return {};
|
|
1194
1317
|
}
|
|
1195
|
-
await
|
|
1318
|
+
await prepareMarketContextSchemaForRead("derivatives");
|
|
1196
1319
|
const startMs = endMs - Math.max(0, lookbackMs);
|
|
1197
|
-
const pool = getPool();
|
|
1198
1320
|
const sql = `
|
|
1199
1321
|
SELECT symbol, interval, ts, open_interest, funding_rate, liq_long, liq_short, liq_total, source
|
|
1200
1322
|
FROM derivatives_market
|
|
@@ -1204,12 +1326,11 @@ async function getDerivativesWindow(params) {
|
|
|
1204
1326
|
AND ts <= to_timestamp($4/1000.0)
|
|
1205
1327
|
ORDER BY interval ASC, ts ASC
|
|
1206
1328
|
`;
|
|
1207
|
-
const res = await
|
|
1208
|
-
|
|
1209
|
-
normalizedIntervals,
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
]);
|
|
1329
|
+
const res = await queryMarketContext(
|
|
1330
|
+
sql,
|
|
1331
|
+
[normalizedSymbol, normalizedIntervals, startMs, endMs],
|
|
1332
|
+
params
|
|
1333
|
+
);
|
|
1213
1334
|
const rowsByInterval = {};
|
|
1214
1335
|
for (const row of res.rows) {
|
|
1215
1336
|
const interval = row.interval;
|
|
@@ -2416,9 +2537,8 @@ var toMarketFeatureAge = (rowTs, atMs) => {
|
|
|
2416
2537
|
return Number.isFinite(ageMs) ? ageMs : null;
|
|
2417
2538
|
};
|
|
2418
2539
|
async function getLatestMarketTradeFlow(params) {
|
|
2419
|
-
await
|
|
2420
|
-
const
|
|
2421
|
-
const res = await pool.query(
|
|
2540
|
+
await prepareMarketContextSchemaForRead("binance");
|
|
2541
|
+
const res = await queryMarketContext(
|
|
2422
2542
|
`
|
|
2423
2543
|
SELECT
|
|
2424
2544
|
symbol,
|
|
@@ -2440,7 +2560,8 @@ async function getLatestMarketTradeFlow(params) {
|
|
|
2440
2560
|
ORDER BY ts DESC
|
|
2441
2561
|
LIMIT 1
|
|
2442
2562
|
`,
|
|
2443
|
-
[params.symbol.toUpperCase(), params.interval, params.atMs]
|
|
2563
|
+
[params.symbol.toUpperCase(), params.interval, params.atMs],
|
|
2564
|
+
params
|
|
2444
2565
|
);
|
|
2445
2566
|
const row = res.rows[0];
|
|
2446
2567
|
if (!row) return null;
|
|
@@ -2568,71 +2689,154 @@ async function upsertHyperliquidWhaleWalletCoverage(params) {
|
|
|
2568
2689
|
async function rebuildHyperliquidWhaleCoverageRows(params) {
|
|
2569
2690
|
await ensureHyperliquidWhaleSchema();
|
|
2570
2691
|
if (params.toMs <= params.fromMs) return 0;
|
|
2571
|
-
const
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2692
|
+
const minuteMs = 6e4;
|
|
2693
|
+
const defaultChunkMinutes = 7 * 24 * 60;
|
|
2694
|
+
const chunkMinutes = Number.isFinite(params.chunkMinutes) && Number(params.chunkMinutes) > 0 ? Math.floor(Number(params.chunkMinutes)) : defaultChunkMinutes;
|
|
2695
|
+
const chunkMs = chunkMinutes * minuteMs;
|
|
2696
|
+
const totalBuckets = Math.ceil((params.toMs - params.fromMs) / minuteMs);
|
|
2697
|
+
const totalChunks = Math.ceil((params.toMs - params.fromMs) / chunkMs);
|
|
2698
|
+
let completedBuckets = 0;
|
|
2699
|
+
let rows = 0;
|
|
2700
|
+
for (let chunkIndex = 0, chunkFromMs = params.fromMs; chunkFromMs < params.toMs; chunkIndex += 1, chunkFromMs += chunkMs) {
|
|
2701
|
+
const chunkToMs = Math.min(params.toMs, chunkFromMs + chunkMs);
|
|
2702
|
+
const result = await getPool().query(
|
|
2703
|
+
`
|
|
2704
|
+
WITH normalized_ranges AS (
|
|
2705
|
+
SELECT
|
|
2706
|
+
address,
|
|
2707
|
+
GREATEST(
|
|
2708
|
+
to_timestamp($3/1000.0),
|
|
2709
|
+
date_trunc('minute', covered_from_ts) +
|
|
2710
|
+
CASE
|
|
2711
|
+
WHEN covered_from_ts = date_trunc('minute', covered_from_ts)
|
|
2712
|
+
THEN interval '0 minutes'
|
|
2713
|
+
ELSE interval '1 minute'
|
|
2714
|
+
END
|
|
2715
|
+
) AS range_start,
|
|
2716
|
+
LEAST(
|
|
2717
|
+
to_timestamp($4/1000.0),
|
|
2718
|
+
date_trunc('minute', covered_to_ts)
|
|
2719
|
+
) AS range_end
|
|
2720
|
+
FROM hyperliquid_whale_wallet_coverage
|
|
2721
|
+
WHERE universe_fingerprint = $1
|
|
2722
|
+
AND whale_registry_fingerprint = $2
|
|
2723
|
+
AND data_model_version = $6
|
|
2724
|
+
AND status IN ('complete', 'truncated')
|
|
2725
|
+
AND covered_from_ts < to_timestamp($4/1000.0)
|
|
2726
|
+
AND covered_to_ts > to_timestamp($3/1000.0)
|
|
2727
|
+
), eligible_ranges AS (
|
|
2728
|
+
SELECT *
|
|
2729
|
+
FROM normalized_ranges
|
|
2730
|
+
WHERE range_start < range_end
|
|
2731
|
+
), ordered_ranges AS (
|
|
2732
|
+
SELECT
|
|
2733
|
+
*,
|
|
2734
|
+
MAX(range_end) OVER (
|
|
2735
|
+
PARTITION BY address
|
|
2736
|
+
ORDER BY range_start, range_end
|
|
2737
|
+
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
|
|
2738
|
+
) AS previous_max_end
|
|
2739
|
+
FROM eligible_ranges
|
|
2740
|
+
), marked_ranges AS (
|
|
2741
|
+
SELECT
|
|
2742
|
+
*,
|
|
2743
|
+
SUM(
|
|
2744
|
+
CASE
|
|
2745
|
+
WHEN previous_max_end IS NULL OR range_start > previous_max_end
|
|
2746
|
+
THEN 1
|
|
2747
|
+
ELSE 0
|
|
2748
|
+
END
|
|
2749
|
+
) OVER (
|
|
2750
|
+
PARTITION BY address
|
|
2751
|
+
ORDER BY range_start, range_end
|
|
2752
|
+
) AS range_group
|
|
2753
|
+
FROM ordered_ranges
|
|
2754
|
+
), merged_ranges AS (
|
|
2755
|
+
SELECT
|
|
2756
|
+
address,
|
|
2757
|
+
MIN(range_start) AS range_start,
|
|
2758
|
+
MAX(range_end) AS range_end
|
|
2759
|
+
FROM marked_ranges
|
|
2760
|
+
GROUP BY address, range_group
|
|
2761
|
+
), deltas AS (
|
|
2762
|
+
SELECT range_start AS ts, 1 AS delta
|
|
2763
|
+
FROM merged_ranges
|
|
2764
|
+
UNION ALL
|
|
2765
|
+
SELECT range_end AS ts, -1 AS delta
|
|
2766
|
+
FROM merged_ranges
|
|
2767
|
+
), bucket_deltas AS (
|
|
2768
|
+
SELECT ts, SUM(delta)::int AS delta
|
|
2769
|
+
FROM deltas
|
|
2770
|
+
GROUP BY ts
|
|
2771
|
+
), buckets AS (
|
|
2772
|
+
SELECT generate_series(
|
|
2773
|
+
to_timestamp($3/1000.0),
|
|
2774
|
+
to_timestamp($4/1000.0) - interval '1 minute',
|
|
2775
|
+
interval '1 minute'
|
|
2776
|
+
) AS ts
|
|
2777
|
+
), coverage AS (
|
|
2778
|
+
SELECT
|
|
2779
|
+
buckets.ts,
|
|
2780
|
+
SUM(COALESCE(bucket_deltas.delta, 0)) OVER (
|
|
2781
|
+
ORDER BY buckets.ts
|
|
2782
|
+
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
|
2783
|
+
)::int AS covered_whales
|
|
2784
|
+
FROM buckets
|
|
2785
|
+
LEFT JOIN bucket_deltas USING (ts)
|
|
2786
|
+
)
|
|
2787
|
+
INSERT INTO hyperliquid_whale_coverage_1m (
|
|
2788
|
+
ts,
|
|
2789
|
+
covered_whales,
|
|
2790
|
+
expected_whales,
|
|
2791
|
+
coverage_pct,
|
|
2792
|
+
universe_fingerprint,
|
|
2793
|
+
whale_registry_fingerprint,
|
|
2794
|
+
source,
|
|
2795
|
+
data_model_version
|
|
2796
|
+
)
|
|
2580
2797
|
SELECT
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
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;
|
|
2798
|
+
ts,
|
|
2799
|
+
covered_whales,
|
|
2800
|
+
$5,
|
|
2801
|
+
CASE WHEN $5 > 0 THEN covered_whales::double precision / $5 ELSE 0 END,
|
|
2802
|
+
$1,
|
|
2803
|
+
$2,
|
|
2804
|
+
'hyperliquid_user_fills',
|
|
2805
|
+
$6
|
|
2806
|
+
FROM coverage
|
|
2807
|
+
ON CONFLICT (
|
|
2808
|
+
universe_fingerprint,
|
|
2809
|
+
whale_registry_fingerprint,
|
|
2810
|
+
ts
|
|
2811
|
+
) DO UPDATE SET
|
|
2812
|
+
covered_whales = EXCLUDED.covered_whales,
|
|
2813
|
+
expected_whales = EXCLUDED.expected_whales,
|
|
2814
|
+
coverage_pct = EXCLUDED.coverage_pct,
|
|
2815
|
+
source = EXCLUDED.source,
|
|
2816
|
+
data_model_version = EXCLUDED.data_model_version,
|
|
2817
|
+
ingested_at = now()
|
|
2818
|
+
`,
|
|
2819
|
+
[
|
|
2820
|
+
params.universeFingerprint,
|
|
2821
|
+
params.whaleRegistryFingerprint,
|
|
2822
|
+
chunkFromMs,
|
|
2823
|
+
chunkToMs,
|
|
2824
|
+
params.expectedWhales,
|
|
2825
|
+
HYPERLIQUID_WHALE_DATA_MODEL_VERSION
|
|
2826
|
+
]
|
|
2827
|
+
);
|
|
2828
|
+
const chunkBuckets = Math.ceil((chunkToMs - chunkFromMs) / minuteMs);
|
|
2829
|
+
completedBuckets = Math.min(totalBuckets, completedBuckets + chunkBuckets);
|
|
2830
|
+
rows += result.rowCount ?? 0;
|
|
2831
|
+
params.onProgress?.({
|
|
2832
|
+
chunkIndex: chunkIndex + 1,
|
|
2833
|
+
totalChunks,
|
|
2834
|
+
completedBuckets,
|
|
2835
|
+
totalBuckets,
|
|
2836
|
+
rows
|
|
2837
|
+
});
|
|
2838
|
+
}
|
|
2839
|
+
return rows;
|
|
2636
2840
|
}
|
|
2637
2841
|
async function upsertHyperliquidWhaleCoverageRows(rows) {
|
|
2638
2842
|
if (!rows.length) return;
|
|
@@ -2691,8 +2895,8 @@ async function upsertHyperliquidWhaleCoverageRows(rows) {
|
|
|
2691
2895
|
);
|
|
2692
2896
|
}
|
|
2693
2897
|
async function getHyperliquidWhaleCoverageSeriesRows(params) {
|
|
2694
|
-
await
|
|
2695
|
-
const result = await
|
|
2898
|
+
await prepareMarketContextSchemaForRead("hyperliquidWhales");
|
|
2899
|
+
const result = await queryMarketContext(
|
|
2696
2900
|
`
|
|
2697
2901
|
SELECT
|
|
2698
2902
|
ts,
|
|
@@ -2713,7 +2917,8 @@ async function getHyperliquidWhaleCoverageSeriesRows(params) {
|
|
|
2713
2917
|
HYPERLIQUID_WHALE_DATA_MODEL_VERSION,
|
|
2714
2918
|
params.fromMs,
|
|
2715
2919
|
params.toMs
|
|
2716
|
-
]
|
|
2920
|
+
],
|
|
2921
|
+
params
|
|
2717
2922
|
);
|
|
2718
2923
|
return result.rows.map((row) => ({
|
|
2719
2924
|
ts: new Date(row.ts),
|
|
@@ -2723,8 +2928,8 @@ async function getHyperliquidWhaleCoverageSeriesRows(params) {
|
|
|
2723
2928
|
}));
|
|
2724
2929
|
}
|
|
2725
2930
|
async function getHyperliquidWhaleFlowSeriesRows(params) {
|
|
2726
|
-
await
|
|
2727
|
-
const result = await
|
|
2931
|
+
await prepareMarketContextSchemaForRead("hyperliquidWhales");
|
|
2932
|
+
const result = await queryMarketContext(
|
|
2728
2933
|
`
|
|
2729
2934
|
SELECT
|
|
2730
2935
|
ts,
|
|
@@ -2757,7 +2962,8 @@ async function getHyperliquidWhaleFlowSeriesRows(params) {
|
|
|
2757
2962
|
params.whaleRegistryFingerprint,
|
|
2758
2963
|
params.fromMs,
|
|
2759
2964
|
params.toMs
|
|
2760
|
-
]
|
|
2965
|
+
],
|
|
2966
|
+
params
|
|
2761
2967
|
);
|
|
2762
2968
|
return result.rows.map((row) => ({
|
|
2763
2969
|
ts: new Date(row.ts),
|
|
@@ -2778,10 +2984,10 @@ async function getHyperliquidWhaleFlowSeriesRows(params) {
|
|
|
2778
2984
|
}));
|
|
2779
2985
|
}
|
|
2780
2986
|
async function getHyperliquidWhaleFlowAggregate(params) {
|
|
2781
|
-
await
|
|
2987
|
+
await prepareMarketContextSchemaForRead("hyperliquidWhales");
|
|
2782
2988
|
const intervalMs = HYPERLIQUID_CONTEXT_INTERVAL_MS[params.interval];
|
|
2783
2989
|
const expectedBuckets = Math.ceil(intervalMs / 6e4);
|
|
2784
|
-
const res = await
|
|
2990
|
+
const res = await queryMarketContext(
|
|
2785
2991
|
`
|
|
2786
2992
|
WITH coverage_rows AS (
|
|
2787
2993
|
SELECT *
|
|
@@ -2896,7 +3102,8 @@ async function getHyperliquidWhaleFlowAggregate(params) {
|
|
|
2896
3102
|
params.decisionTimeMs,
|
|
2897
3103
|
intervalMs,
|
|
2898
3104
|
HYPERLIQUID_WHALE_DATA_MODEL_VERSION
|
|
2899
|
-
]
|
|
3105
|
+
],
|
|
3106
|
+
params
|
|
2900
3107
|
);
|
|
2901
3108
|
const row = res.rows[0];
|
|
2902
3109
|
if (!row?.as_of_ts || Number(row.coverage_buckets) !== expectedBuckets || Number(row.covered_whales) <= 0) {
|
|
@@ -2969,9 +3176,8 @@ async function hasHyperliquidWhaleBackfillCoverage(params) {
|
|
|
2969
3176
|
return Number(result.rows[0]?.buckets) === expectedBuckets && Number(result.rows[0]?.complete_buckets) === expectedBuckets;
|
|
2970
3177
|
}
|
|
2971
3178
|
async function getLatestMarketBreadth(params) {
|
|
2972
|
-
await
|
|
2973
|
-
const
|
|
2974
|
-
const res = await pool.query(
|
|
3179
|
+
await prepareMarketContextSchemaForRead("binance");
|
|
3180
|
+
const res = await queryMarketContext(
|
|
2975
3181
|
`
|
|
2976
3182
|
SELECT
|
|
2977
3183
|
universe,
|
|
@@ -3010,7 +3216,8 @@ async function getLatestMarketBreadth(params) {
|
|
|
3010
3216
|
ORDER BY ts DESC
|
|
3011
3217
|
LIMIT 1
|
|
3012
3218
|
`,
|
|
3013
|
-
[params.universe, params.interval, params.atMs]
|
|
3219
|
+
[params.universe, params.interval, params.atMs],
|
|
3220
|
+
params
|
|
3014
3221
|
);
|
|
3015
3222
|
const row = res.rows[0];
|
|
3016
3223
|
if (!row) return null;
|
|
@@ -3022,10 +3229,9 @@ async function getLatestMarketBreadth(params) {
|
|
|
3022
3229
|
};
|
|
3023
3230
|
}
|
|
3024
3231
|
async function getLatestMarketGlobalContext(params) {
|
|
3025
|
-
await
|
|
3026
|
-
const pool = getPool();
|
|
3232
|
+
await prepareMarketContextSchemaForRead("coinmarketcap");
|
|
3027
3233
|
const source = params.source ?? "coinmarketcap_global";
|
|
3028
|
-
const res = await
|
|
3234
|
+
const res = await queryMarketContext(
|
|
3029
3235
|
`
|
|
3030
3236
|
SELECT
|
|
3031
3237
|
source,
|
|
@@ -3054,11 +3260,12 @@ async function getLatestMarketGlobalContext(params) {
|
|
|
3054
3260
|
ORDER BY ts DESC
|
|
3055
3261
|
LIMIT 1
|
|
3056
3262
|
`,
|
|
3057
|
-
[source, params.atMs]
|
|
3263
|
+
[source, params.atMs],
|
|
3264
|
+
params
|
|
3058
3265
|
);
|
|
3059
3266
|
const row = res.rows[0];
|
|
3060
3267
|
if (!row) return null;
|
|
3061
|
-
const previousRes = await
|
|
3268
|
+
const previousRes = await queryMarketContext(
|
|
3062
3269
|
`
|
|
3063
3270
|
SELECT
|
|
3064
3271
|
btc_dominance_pct AS "btcDominancePct",
|
|
@@ -3071,7 +3278,8 @@ async function getLatestMarketGlobalContext(params) {
|
|
|
3071
3278
|
ORDER BY ts DESC
|
|
3072
3279
|
LIMIT 1
|
|
3073
3280
|
`,
|
|
3074
|
-
[source, row.ts]
|
|
3281
|
+
[source, row.ts],
|
|
3282
|
+
params
|
|
3075
3283
|
);
|
|
3076
3284
|
const previousDominance = previousRes.rows[0]?.btcDominancePct == null ? null : Number(previousRes.rows[0].btcDominancePct);
|
|
3077
3285
|
const previousEthDominance = previousRes.rows[0]?.ethDominancePct == null ? null : Number(previousRes.rows[0].ethDominancePct);
|
|
@@ -3164,9 +3372,8 @@ async function getLatestMarketReferenceAssetContexts(params) {
|
|
|
3164
3372
|
];
|
|
3165
3373
|
const rows = /* @__PURE__ */ new Map();
|
|
3166
3374
|
if (!symbols.length) return rows;
|
|
3167
|
-
await
|
|
3168
|
-
const
|
|
3169
|
-
const res = await pool.query(
|
|
3375
|
+
await prepareMarketContextSchemaForRead("coinmarketcap");
|
|
3376
|
+
const res = await queryMarketContext(
|
|
3170
3377
|
`
|
|
3171
3378
|
SELECT DISTINCT ON (symbol)
|
|
3172
3379
|
source,
|
|
@@ -3191,7 +3398,8 @@ async function getLatestMarketReferenceAssetContexts(params) {
|
|
|
3191
3398
|
END <= to_timestamp($4/1000.0)
|
|
3192
3399
|
ORDER BY symbol ASC, ts DESC
|
|
3193
3400
|
`,
|
|
3194
|
-
[source, symbols, interval, params.atMs]
|
|
3401
|
+
[source, symbols, interval, params.atMs],
|
|
3402
|
+
params
|
|
3195
3403
|
);
|
|
3196
3404
|
for (const row of res.rows) {
|
|
3197
3405
|
const ageMs = toMarketFeatureAge(row.ts, params.atMs);
|
|
@@ -3204,11 +3412,10 @@ async function getLatestMarketReferenceAssetContexts(params) {
|
|
|
3204
3412
|
return rows;
|
|
3205
3413
|
}
|
|
3206
3414
|
async function getLatestMarketCmcExchangeLiquidityContext(params) {
|
|
3207
|
-
await
|
|
3208
|
-
const pool = getPool();
|
|
3415
|
+
await prepareMarketContextSchemaForRead("coinmarketcap");
|
|
3209
3416
|
const source = params.source ?? "coinmarketcap_exchange_liquidity";
|
|
3210
3417
|
const interval = params.interval ?? "1d";
|
|
3211
|
-
const res = await
|
|
3418
|
+
const res = await queryMarketContext(
|
|
3212
3419
|
`
|
|
3213
3420
|
SELECT
|
|
3214
3421
|
source,
|
|
@@ -3231,11 +3438,12 @@ async function getLatestMarketCmcExchangeLiquidityContext(params) {
|
|
|
3231
3438
|
ORDER BY ts DESC
|
|
3232
3439
|
LIMIT 1
|
|
3233
3440
|
`,
|
|
3234
|
-
[source, interval, params.atMs]
|
|
3441
|
+
[source, interval, params.atMs],
|
|
3442
|
+
params
|
|
3235
3443
|
);
|
|
3236
3444
|
const row = res.rows[0];
|
|
3237
3445
|
if (!row) return null;
|
|
3238
|
-
const previousRes = await
|
|
3446
|
+
const previousRes = await queryMarketContext(
|
|
3239
3447
|
`
|
|
3240
3448
|
SELECT total_volume_usd AS "totalVolumeUsd"
|
|
3241
3449
|
FROM market_cmc_exchange_liquidity_context
|
|
@@ -3245,7 +3453,8 @@ async function getLatestMarketCmcExchangeLiquidityContext(params) {
|
|
|
3245
3453
|
ORDER BY ts DESC
|
|
3246
3454
|
LIMIT 1
|
|
3247
3455
|
`,
|
|
3248
|
-
[source, interval, row.ts]
|
|
3456
|
+
[source, interval, row.ts],
|
|
3457
|
+
params
|
|
3249
3458
|
);
|
|
3250
3459
|
const currentTotal = row.totalVolumeUsd == null ? null : Number(row.totalVolumeUsd);
|
|
3251
3460
|
const previousTotal = previousRes.rows[0]?.totalVolumeUsd == null ? null : Number(previousRes.rows[0].totalVolumeUsd);
|
|
@@ -3269,9 +3478,8 @@ async function getLatestMarketCmcIndexContexts(params) {
|
|
|
3269
3478
|
];
|
|
3270
3479
|
const rows = /* @__PURE__ */ new Map();
|
|
3271
3480
|
if (!indexSlugs.length) return rows;
|
|
3272
|
-
await
|
|
3273
|
-
const
|
|
3274
|
-
const res = await pool.query(
|
|
3481
|
+
await prepareMarketContextSchemaForRead("coinmarketcap");
|
|
3482
|
+
const res = await queryMarketContext(
|
|
3275
3483
|
`
|
|
3276
3484
|
SELECT DISTINCT ON (index_slug)
|
|
3277
3485
|
source,
|
|
@@ -3294,10 +3502,11 @@ async function getLatestMarketCmcIndexContexts(params) {
|
|
|
3294
3502
|
END <= to_timestamp($4/1000.0)
|
|
3295
3503
|
ORDER BY index_slug ASC, ts DESC
|
|
3296
3504
|
`,
|
|
3297
|
-
[source, indexSlugs, interval, params.atMs]
|
|
3505
|
+
[source, indexSlugs, interval, params.atMs],
|
|
3506
|
+
params
|
|
3298
3507
|
);
|
|
3299
3508
|
for (const row of res.rows) {
|
|
3300
|
-
const previousRes = await
|
|
3509
|
+
const previousRes = await queryMarketContext(
|
|
3301
3510
|
`
|
|
3302
3511
|
SELECT value
|
|
3303
3512
|
FROM market_cmc_index_context
|
|
@@ -3308,7 +3517,8 @@ async function getLatestMarketCmcIndexContexts(params) {
|
|
|
3308
3517
|
ORDER BY ts DESC
|
|
3309
3518
|
LIMIT 1
|
|
3310
3519
|
`,
|
|
3311
|
-
[source, row.indexSlug, interval, row.ts]
|
|
3520
|
+
[source, row.indexSlug, interval, row.ts],
|
|
3521
|
+
params
|
|
3312
3522
|
);
|
|
3313
3523
|
const currentValue = row.value == null ? null : Number(row.value);
|
|
3314
3524
|
const previousValue = previousRes.rows[0]?.value == null ? null : Number(previousRes.rows[0].value);
|
|
@@ -3323,11 +3533,10 @@ async function getLatestMarketCmcIndexContexts(params) {
|
|
|
3323
3533
|
return rows;
|
|
3324
3534
|
}
|
|
3325
3535
|
async function getLatestMarketCmcFearGreedContext(params) {
|
|
3326
|
-
await
|
|
3327
|
-
const pool = getPool();
|
|
3536
|
+
await prepareMarketContextSchemaForRead("coinmarketcap");
|
|
3328
3537
|
const source = params.source ?? "coinmarketcap_fear_greed";
|
|
3329
3538
|
const interval = params.interval ?? "1d";
|
|
3330
|
-
const res = await
|
|
3539
|
+
const res = await queryMarketContext(
|
|
3331
3540
|
`
|
|
3332
3541
|
SELECT
|
|
3333
3542
|
source,
|
|
@@ -3347,11 +3556,12 @@ async function getLatestMarketCmcFearGreedContext(params) {
|
|
|
3347
3556
|
ORDER BY ts DESC
|
|
3348
3557
|
LIMIT 1
|
|
3349
3558
|
`,
|
|
3350
|
-
[source, interval, params.atMs]
|
|
3559
|
+
[source, interval, params.atMs],
|
|
3560
|
+
params
|
|
3351
3561
|
);
|
|
3352
3562
|
const row = res.rows[0];
|
|
3353
3563
|
if (!row) return null;
|
|
3354
|
-
const previousRes = await
|
|
3564
|
+
const previousRes = await queryMarketContext(
|
|
3355
3565
|
`
|
|
3356
3566
|
SELECT
|
|
3357
3567
|
value::int AS value,
|
|
@@ -3363,9 +3573,10 @@ async function getLatestMarketCmcFearGreedContext(params) {
|
|
|
3363
3573
|
ORDER BY ts DESC
|
|
3364
3574
|
LIMIT 1
|
|
3365
3575
|
`,
|
|
3366
|
-
[source, interval, row.ts]
|
|
3576
|
+
[source, interval, row.ts],
|
|
3577
|
+
params
|
|
3367
3578
|
);
|
|
3368
|
-
const previous7dRes = await
|
|
3579
|
+
const previous7dRes = await queryMarketContext(
|
|
3369
3580
|
`
|
|
3370
3581
|
SELECT value::int AS value
|
|
3371
3582
|
FROM market_cmc_fear_greed_context
|
|
@@ -3375,7 +3586,8 @@ async function getLatestMarketCmcFearGreedContext(params) {
|
|
|
3375
3586
|
ORDER BY ts DESC
|
|
3376
3587
|
LIMIT 1
|
|
3377
3588
|
`,
|
|
3378
|
-
[source, interval, row.ts]
|
|
3589
|
+
[source, interval, row.ts],
|
|
3590
|
+
params
|
|
3379
3591
|
);
|
|
3380
3592
|
const previousValue = previousRes.rows[0]?.value == null ? null : Number(previousRes.rows[0].value);
|
|
3381
3593
|
const previous7dValue = previous7dRes.rows[0]?.value == null ? null : Number(previous7dRes.rows[0].value);
|
|
@@ -3861,7 +4073,13 @@ export {
|
|
|
3861
4073
|
applyDerivativesMetricCoverage,
|
|
3862
4074
|
cleanupDeprecatedMarketContext,
|
|
3863
4075
|
closeTimescalePool,
|
|
4076
|
+
configureTimescaleMarketContextSchemaMode,
|
|
3864
4077
|
deleteCandles,
|
|
4078
|
+
ensureBinanceMarketSchema,
|
|
4079
|
+
ensureCoinMarketCapContextSchema,
|
|
4080
|
+
ensureDerivativesSchema,
|
|
4081
|
+
ensureHyperliquidWhaleSchema,
|
|
4082
|
+
ensureMarketContextSchemas,
|
|
3865
4083
|
findContinuityGap,
|
|
3866
4084
|
getCandlesRange,
|
|
3867
4085
|
getDataEdges,
|