@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.js
CHANGED
|
@@ -23,7 +23,13 @@ __export(timescale_exports, {
|
|
|
23
23
|
applyDerivativesMetricCoverage: () => applyDerivativesMetricCoverage,
|
|
24
24
|
cleanupDeprecatedMarketContext: () => cleanupDeprecatedMarketContext,
|
|
25
25
|
closeTimescalePool: () => closeTimescalePool,
|
|
26
|
+
configureTimescaleMarketContextSchemaMode: () => configureTimescaleMarketContextSchemaMode,
|
|
26
27
|
deleteCandles: () => deleteCandles,
|
|
28
|
+
ensureBinanceMarketSchema: () => ensureBinanceMarketSchema,
|
|
29
|
+
ensureCoinMarketCapContextSchema: () => ensureCoinMarketCapContextSchema,
|
|
30
|
+
ensureDerivativesSchema: () => ensureDerivativesSchema,
|
|
31
|
+
ensureHyperliquidWhaleSchema: () => ensureHyperliquidWhaleSchema,
|
|
32
|
+
ensureMarketContextSchemas: () => ensureMarketContextSchemas,
|
|
27
33
|
findContinuityGap: () => findContinuityGap,
|
|
28
34
|
getCandlesRange: () => getCandlesRange,
|
|
29
35
|
getDataEdges: () => getDataEdges,
|
|
@@ -114,6 +120,12 @@ var derivativesSchemaReadyPromise = null;
|
|
|
114
120
|
var spreadSchemaReadyPromise = null;
|
|
115
121
|
var binanceMarketSchemaReadyPromise = null;
|
|
116
122
|
var hyperliquidWhaleSchemaReadyPromise = null;
|
|
123
|
+
var marketContextSchemaMode = "ensure";
|
|
124
|
+
var verifiedMarketContextSchemas = /* @__PURE__ */ new Set();
|
|
125
|
+
var configureTimescaleMarketContextSchemaMode = (mode) => {
|
|
126
|
+
marketContextSchemaMode = mode;
|
|
127
|
+
verifiedMarketContextSchemas.clear();
|
|
128
|
+
};
|
|
117
129
|
var closeTimescalePool = async () => {
|
|
118
130
|
const pool = global.__pgPool__;
|
|
119
131
|
if (!pool) {
|
|
@@ -130,6 +142,7 @@ var closeTimescalePool = async () => {
|
|
|
130
142
|
spreadSchemaReadyPromise = null;
|
|
131
143
|
binanceMarketSchemaReadyPromise = null;
|
|
132
144
|
hyperliquidWhaleSchemaReadyPromise = null;
|
|
145
|
+
verifiedMarketContextSchemas.clear();
|
|
133
146
|
await pool.end();
|
|
134
147
|
};
|
|
135
148
|
var CANDLES_SCHEMA_LOCK_KEY = 61e4;
|
|
@@ -138,6 +151,70 @@ var SPREAD_SCHEMA_LOCK_KEY = 610002;
|
|
|
138
151
|
var BINANCE_MARKET_SCHEMA_LOCK_KEY = 610003;
|
|
139
152
|
var HYPERLIQUID_WHALE_SCHEMA_LOCK_KEY = 610004;
|
|
140
153
|
var PG_SAFE_MAX_BIND_PARAMS = 3e4;
|
|
154
|
+
var resolveMarketContextQueryTimeoutMs = (override) => {
|
|
155
|
+
if (Number.isFinite(override) && Number(override) > 0) {
|
|
156
|
+
return Math.floor(Number(override));
|
|
157
|
+
}
|
|
158
|
+
const configured = Number(process.env.MARKET_CONTEXT_SQL_TIMEOUT_MS);
|
|
159
|
+
return Number.isFinite(configured) && configured > 0 ? Math.floor(configured) : 3e4;
|
|
160
|
+
};
|
|
161
|
+
var createMarketContextQueryError = (name, message) => {
|
|
162
|
+
const error = new Error(message);
|
|
163
|
+
error.name = name;
|
|
164
|
+
return error;
|
|
165
|
+
};
|
|
166
|
+
var queryMarketContext = async (text, values, options = {}) => {
|
|
167
|
+
const pool = getPool();
|
|
168
|
+
const client = await pool.connect();
|
|
169
|
+
const timeoutMs = resolveMarketContextQueryTimeoutMs(options.timeoutMs);
|
|
170
|
+
let released = false;
|
|
171
|
+
let rejectCancellation;
|
|
172
|
+
const release = (error) => {
|
|
173
|
+
if (released) return;
|
|
174
|
+
released = true;
|
|
175
|
+
client.release(error);
|
|
176
|
+
};
|
|
177
|
+
const cancellation = new Promise((_resolve, reject) => {
|
|
178
|
+
rejectCancellation = reject;
|
|
179
|
+
});
|
|
180
|
+
const cancel = (error) => {
|
|
181
|
+
release(error);
|
|
182
|
+
rejectCancellation?.(error);
|
|
183
|
+
};
|
|
184
|
+
const onAbort = () => cancel(
|
|
185
|
+
createMarketContextQueryError(
|
|
186
|
+
"AbortError",
|
|
187
|
+
"Timescale market-context query aborted"
|
|
188
|
+
)
|
|
189
|
+
);
|
|
190
|
+
const timer = setTimeout(
|
|
191
|
+
() => cancel(
|
|
192
|
+
createMarketContextQueryError(
|
|
193
|
+
"TimescaleQueryTimeoutError",
|
|
194
|
+
`Timescale market-context query exceeded ${timeoutMs}ms`
|
|
195
|
+
)
|
|
196
|
+
),
|
|
197
|
+
timeoutMs
|
|
198
|
+
);
|
|
199
|
+
timer.unref?.();
|
|
200
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
201
|
+
try {
|
|
202
|
+
if (options.signal?.aborted) {
|
|
203
|
+
const error = createMarketContextQueryError(
|
|
204
|
+
"AbortError",
|
|
205
|
+
"Timescale market-context query aborted"
|
|
206
|
+
);
|
|
207
|
+
release(error);
|
|
208
|
+
throw error;
|
|
209
|
+
}
|
|
210
|
+
const query = client.query(text, values);
|
|
211
|
+
return await Promise.race([query, cancellation]);
|
|
212
|
+
} finally {
|
|
213
|
+
clearTimeout(timer);
|
|
214
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
215
|
+
release();
|
|
216
|
+
}
|
|
217
|
+
};
|
|
141
218
|
var normalizeCandleProvider = (provider) => String(provider || "").trim().toLowerCase();
|
|
142
219
|
var normalizeCandleSymbol = (symbol) => String(symbol || "").trim().toUpperCase();
|
|
143
220
|
var getSafeBulkInsertRows = (columnsCount) => Math.max(1, Math.floor(PG_SAFE_MAX_BIND_PARAMS / columnsCount));
|
|
@@ -942,6 +1019,58 @@ var ensureHyperliquidWhaleSchema = async () => {
|
|
|
942
1019
|
});
|
|
943
1020
|
await hyperliquidWhaleSchemaReadyPromise;
|
|
944
1021
|
};
|
|
1022
|
+
var ensureCoinMarketCapContextSchema = async () => ensureBinanceMarketSchema();
|
|
1023
|
+
var ensureMarketContextSchema = async (source) => {
|
|
1024
|
+
switch (source) {
|
|
1025
|
+
case "binance":
|
|
1026
|
+
return ensureBinanceMarketSchema();
|
|
1027
|
+
case "coinmarketcap":
|
|
1028
|
+
return ensureCoinMarketCapContextSchema();
|
|
1029
|
+
case "derivatives":
|
|
1030
|
+
return ensureDerivativesSchema();
|
|
1031
|
+
case "hyperliquidWhales":
|
|
1032
|
+
return ensureHyperliquidWhaleSchema();
|
|
1033
|
+
}
|
|
1034
|
+
};
|
|
1035
|
+
var MARKET_CONTEXT_SCHEMA_TABLES = {
|
|
1036
|
+
binance: ["market_trade_flow", "market_breadth"],
|
|
1037
|
+
coinmarketcap: [
|
|
1038
|
+
"market_global_context",
|
|
1039
|
+
"market_reference_asset_context",
|
|
1040
|
+
"market_cmc_exchange_liquidity_context",
|
|
1041
|
+
"market_cmc_fear_greed_context",
|
|
1042
|
+
"market_cmc_index_context"
|
|
1043
|
+
],
|
|
1044
|
+
derivatives: ["derivatives_market"],
|
|
1045
|
+
hyperliquidWhales: [
|
|
1046
|
+
"hyperliquid_whale_flow",
|
|
1047
|
+
"hyperliquid_whale_coverage_1m"
|
|
1048
|
+
]
|
|
1049
|
+
};
|
|
1050
|
+
var verifyMarketContextSchema = async (source) => {
|
|
1051
|
+
if (verifiedMarketContextSchemas.has(source)) return;
|
|
1052
|
+
const tables = MARKET_CONTEXT_SCHEMA_TABLES[source];
|
|
1053
|
+
const result = await queryMarketContext(
|
|
1054
|
+
`
|
|
1055
|
+
SELECT table_name AS "tableName"
|
|
1056
|
+
FROM unnest($1::text[]) AS requested(table_name)
|
|
1057
|
+
WHERE to_regclass(requested.table_name) IS NULL
|
|
1058
|
+
`,
|
|
1059
|
+
[tables]
|
|
1060
|
+
);
|
|
1061
|
+
if (result.rows.length) {
|
|
1062
|
+
throw new Error(
|
|
1063
|
+
`Timescale ${source} schema is not prepared; missing: ${result.rows.map((row) => row.tableName).filter(Boolean).join(", ")}`
|
|
1064
|
+
);
|
|
1065
|
+
}
|
|
1066
|
+
verifiedMarketContextSchemas.add(source);
|
|
1067
|
+
};
|
|
1068
|
+
var prepareMarketContextSchemaForRead = async (source) => marketContextSchemaMode === "verify" ? verifyMarketContextSchema(source) : ensureMarketContextSchema(source);
|
|
1069
|
+
var ensureMarketContextSchemas = async (sources) => {
|
|
1070
|
+
for (const source of new Set(sources)) {
|
|
1071
|
+
await ensureMarketContextSchema(source);
|
|
1072
|
+
}
|
|
1073
|
+
};
|
|
945
1074
|
async function upsertDerivatives(rows) {
|
|
946
1075
|
if (!rows.length) return;
|
|
947
1076
|
await ensureDerivativesSchema();
|
|
@@ -1269,9 +1398,8 @@ async function getDerivativesWindow(params) {
|
|
|
1269
1398
|
if (!normalizedSymbol || !normalizedIntervals.length) {
|
|
1270
1399
|
return {};
|
|
1271
1400
|
}
|
|
1272
|
-
await
|
|
1401
|
+
await prepareMarketContextSchemaForRead("derivatives");
|
|
1273
1402
|
const startMs = endMs - Math.max(0, lookbackMs);
|
|
1274
|
-
const pool = getPool();
|
|
1275
1403
|
const sql = `
|
|
1276
1404
|
SELECT symbol, interval, ts, open_interest, funding_rate, liq_long, liq_short, liq_total, source
|
|
1277
1405
|
FROM derivatives_market
|
|
@@ -1281,12 +1409,11 @@ async function getDerivativesWindow(params) {
|
|
|
1281
1409
|
AND ts <= to_timestamp($4/1000.0)
|
|
1282
1410
|
ORDER BY interval ASC, ts ASC
|
|
1283
1411
|
`;
|
|
1284
|
-
const res = await
|
|
1285
|
-
|
|
1286
|
-
normalizedIntervals,
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
]);
|
|
1412
|
+
const res = await queryMarketContext(
|
|
1413
|
+
sql,
|
|
1414
|
+
[normalizedSymbol, normalizedIntervals, startMs, endMs],
|
|
1415
|
+
params
|
|
1416
|
+
);
|
|
1290
1417
|
const rowsByInterval = {};
|
|
1291
1418
|
for (const row of res.rows) {
|
|
1292
1419
|
const interval = row.interval;
|
|
@@ -2493,9 +2620,8 @@ var toMarketFeatureAge = (rowTs, atMs) => {
|
|
|
2493
2620
|
return Number.isFinite(ageMs) ? ageMs : null;
|
|
2494
2621
|
};
|
|
2495
2622
|
async function getLatestMarketTradeFlow(params) {
|
|
2496
|
-
await
|
|
2497
|
-
const
|
|
2498
|
-
const res = await pool.query(
|
|
2623
|
+
await prepareMarketContextSchemaForRead("binance");
|
|
2624
|
+
const res = await queryMarketContext(
|
|
2499
2625
|
`
|
|
2500
2626
|
SELECT
|
|
2501
2627
|
symbol,
|
|
@@ -2517,7 +2643,8 @@ async function getLatestMarketTradeFlow(params) {
|
|
|
2517
2643
|
ORDER BY ts DESC
|
|
2518
2644
|
LIMIT 1
|
|
2519
2645
|
`,
|
|
2520
|
-
[params.symbol.toUpperCase(), params.interval, params.atMs]
|
|
2646
|
+
[params.symbol.toUpperCase(), params.interval, params.atMs],
|
|
2647
|
+
params
|
|
2521
2648
|
);
|
|
2522
2649
|
const row = res.rows[0];
|
|
2523
2650
|
if (!row) return null;
|
|
@@ -2645,71 +2772,154 @@ async function upsertHyperliquidWhaleWalletCoverage(params) {
|
|
|
2645
2772
|
async function rebuildHyperliquidWhaleCoverageRows(params) {
|
|
2646
2773
|
await ensureHyperliquidWhaleSchema();
|
|
2647
2774
|
if (params.toMs <= params.fromMs) return 0;
|
|
2648
|
-
const
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2775
|
+
const minuteMs = 6e4;
|
|
2776
|
+
const defaultChunkMinutes = 7 * 24 * 60;
|
|
2777
|
+
const chunkMinutes = Number.isFinite(params.chunkMinutes) && Number(params.chunkMinutes) > 0 ? Math.floor(Number(params.chunkMinutes)) : defaultChunkMinutes;
|
|
2778
|
+
const chunkMs = chunkMinutes * minuteMs;
|
|
2779
|
+
const totalBuckets = Math.ceil((params.toMs - params.fromMs) / minuteMs);
|
|
2780
|
+
const totalChunks = Math.ceil((params.toMs - params.fromMs) / chunkMs);
|
|
2781
|
+
let completedBuckets = 0;
|
|
2782
|
+
let rows = 0;
|
|
2783
|
+
for (let chunkIndex = 0, chunkFromMs = params.fromMs; chunkFromMs < params.toMs; chunkIndex += 1, chunkFromMs += chunkMs) {
|
|
2784
|
+
const chunkToMs = Math.min(params.toMs, chunkFromMs + chunkMs);
|
|
2785
|
+
const result = await getPool().query(
|
|
2786
|
+
`
|
|
2787
|
+
WITH normalized_ranges AS (
|
|
2788
|
+
SELECT
|
|
2789
|
+
address,
|
|
2790
|
+
GREATEST(
|
|
2791
|
+
to_timestamp($3/1000.0),
|
|
2792
|
+
date_trunc('minute', covered_from_ts) +
|
|
2793
|
+
CASE
|
|
2794
|
+
WHEN covered_from_ts = date_trunc('minute', covered_from_ts)
|
|
2795
|
+
THEN interval '0 minutes'
|
|
2796
|
+
ELSE interval '1 minute'
|
|
2797
|
+
END
|
|
2798
|
+
) AS range_start,
|
|
2799
|
+
LEAST(
|
|
2800
|
+
to_timestamp($4/1000.0),
|
|
2801
|
+
date_trunc('minute', covered_to_ts)
|
|
2802
|
+
) AS range_end
|
|
2803
|
+
FROM hyperliquid_whale_wallet_coverage
|
|
2804
|
+
WHERE universe_fingerprint = $1
|
|
2805
|
+
AND whale_registry_fingerprint = $2
|
|
2806
|
+
AND data_model_version = $6
|
|
2807
|
+
AND status IN ('complete', 'truncated')
|
|
2808
|
+
AND covered_from_ts < to_timestamp($4/1000.0)
|
|
2809
|
+
AND covered_to_ts > to_timestamp($3/1000.0)
|
|
2810
|
+
), eligible_ranges AS (
|
|
2811
|
+
SELECT *
|
|
2812
|
+
FROM normalized_ranges
|
|
2813
|
+
WHERE range_start < range_end
|
|
2814
|
+
), ordered_ranges AS (
|
|
2815
|
+
SELECT
|
|
2816
|
+
*,
|
|
2817
|
+
MAX(range_end) OVER (
|
|
2818
|
+
PARTITION BY address
|
|
2819
|
+
ORDER BY range_start, range_end
|
|
2820
|
+
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
|
|
2821
|
+
) AS previous_max_end
|
|
2822
|
+
FROM eligible_ranges
|
|
2823
|
+
), marked_ranges AS (
|
|
2824
|
+
SELECT
|
|
2825
|
+
*,
|
|
2826
|
+
SUM(
|
|
2827
|
+
CASE
|
|
2828
|
+
WHEN previous_max_end IS NULL OR range_start > previous_max_end
|
|
2829
|
+
THEN 1
|
|
2830
|
+
ELSE 0
|
|
2831
|
+
END
|
|
2832
|
+
) OVER (
|
|
2833
|
+
PARTITION BY address
|
|
2834
|
+
ORDER BY range_start, range_end
|
|
2835
|
+
) AS range_group
|
|
2836
|
+
FROM ordered_ranges
|
|
2837
|
+
), merged_ranges AS (
|
|
2838
|
+
SELECT
|
|
2839
|
+
address,
|
|
2840
|
+
MIN(range_start) AS range_start,
|
|
2841
|
+
MAX(range_end) AS range_end
|
|
2842
|
+
FROM marked_ranges
|
|
2843
|
+
GROUP BY address, range_group
|
|
2844
|
+
), deltas AS (
|
|
2845
|
+
SELECT range_start AS ts, 1 AS delta
|
|
2846
|
+
FROM merged_ranges
|
|
2847
|
+
UNION ALL
|
|
2848
|
+
SELECT range_end AS ts, -1 AS delta
|
|
2849
|
+
FROM merged_ranges
|
|
2850
|
+
), bucket_deltas AS (
|
|
2851
|
+
SELECT ts, SUM(delta)::int AS delta
|
|
2852
|
+
FROM deltas
|
|
2853
|
+
GROUP BY ts
|
|
2854
|
+
), buckets AS (
|
|
2855
|
+
SELECT generate_series(
|
|
2856
|
+
to_timestamp($3/1000.0),
|
|
2857
|
+
to_timestamp($4/1000.0) - interval '1 minute',
|
|
2858
|
+
interval '1 minute'
|
|
2859
|
+
) AS ts
|
|
2860
|
+
), coverage AS (
|
|
2861
|
+
SELECT
|
|
2862
|
+
buckets.ts,
|
|
2863
|
+
SUM(COALESCE(bucket_deltas.delta, 0)) OVER (
|
|
2864
|
+
ORDER BY buckets.ts
|
|
2865
|
+
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
|
2866
|
+
)::int AS covered_whales
|
|
2867
|
+
FROM buckets
|
|
2868
|
+
LEFT JOIN bucket_deltas USING (ts)
|
|
2869
|
+
)
|
|
2870
|
+
INSERT INTO hyperliquid_whale_coverage_1m (
|
|
2871
|
+
ts,
|
|
2872
|
+
covered_whales,
|
|
2873
|
+
expected_whales,
|
|
2874
|
+
coverage_pct,
|
|
2875
|
+
universe_fingerprint,
|
|
2876
|
+
whale_registry_fingerprint,
|
|
2877
|
+
source,
|
|
2878
|
+
data_model_version
|
|
2879
|
+
)
|
|
2657
2880
|
SELECT
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
ingested_at = now()
|
|
2701
|
-
RETURNING 1
|
|
2702
|
-
`,
|
|
2703
|
-
[
|
|
2704
|
-
params.universeFingerprint,
|
|
2705
|
-
params.whaleRegistryFingerprint,
|
|
2706
|
-
params.fromMs,
|
|
2707
|
-
params.toMs,
|
|
2708
|
-
params.expectedWhales,
|
|
2709
|
-
import_types.HYPERLIQUID_WHALE_DATA_MODEL_VERSION
|
|
2710
|
-
]
|
|
2711
|
-
);
|
|
2712
|
-
return result.rowCount ?? 0;
|
|
2881
|
+
ts,
|
|
2882
|
+
covered_whales,
|
|
2883
|
+
$5,
|
|
2884
|
+
CASE WHEN $5 > 0 THEN covered_whales::double precision / $5 ELSE 0 END,
|
|
2885
|
+
$1,
|
|
2886
|
+
$2,
|
|
2887
|
+
'hyperliquid_user_fills',
|
|
2888
|
+
$6
|
|
2889
|
+
FROM coverage
|
|
2890
|
+
ON CONFLICT (
|
|
2891
|
+
universe_fingerprint,
|
|
2892
|
+
whale_registry_fingerprint,
|
|
2893
|
+
ts
|
|
2894
|
+
) DO UPDATE SET
|
|
2895
|
+
covered_whales = EXCLUDED.covered_whales,
|
|
2896
|
+
expected_whales = EXCLUDED.expected_whales,
|
|
2897
|
+
coverage_pct = EXCLUDED.coverage_pct,
|
|
2898
|
+
source = EXCLUDED.source,
|
|
2899
|
+
data_model_version = EXCLUDED.data_model_version,
|
|
2900
|
+
ingested_at = now()
|
|
2901
|
+
`,
|
|
2902
|
+
[
|
|
2903
|
+
params.universeFingerprint,
|
|
2904
|
+
params.whaleRegistryFingerprint,
|
|
2905
|
+
chunkFromMs,
|
|
2906
|
+
chunkToMs,
|
|
2907
|
+
params.expectedWhales,
|
|
2908
|
+
import_types.HYPERLIQUID_WHALE_DATA_MODEL_VERSION
|
|
2909
|
+
]
|
|
2910
|
+
);
|
|
2911
|
+
const chunkBuckets = Math.ceil((chunkToMs - chunkFromMs) / minuteMs);
|
|
2912
|
+
completedBuckets = Math.min(totalBuckets, completedBuckets + chunkBuckets);
|
|
2913
|
+
rows += result.rowCount ?? 0;
|
|
2914
|
+
params.onProgress?.({
|
|
2915
|
+
chunkIndex: chunkIndex + 1,
|
|
2916
|
+
totalChunks,
|
|
2917
|
+
completedBuckets,
|
|
2918
|
+
totalBuckets,
|
|
2919
|
+
rows
|
|
2920
|
+
});
|
|
2921
|
+
}
|
|
2922
|
+
return rows;
|
|
2713
2923
|
}
|
|
2714
2924
|
async function upsertHyperliquidWhaleCoverageRows(rows) {
|
|
2715
2925
|
if (!rows.length) return;
|
|
@@ -2768,8 +2978,8 @@ async function upsertHyperliquidWhaleCoverageRows(rows) {
|
|
|
2768
2978
|
);
|
|
2769
2979
|
}
|
|
2770
2980
|
async function getHyperliquidWhaleCoverageSeriesRows(params) {
|
|
2771
|
-
await
|
|
2772
|
-
const result = await
|
|
2981
|
+
await prepareMarketContextSchemaForRead("hyperliquidWhales");
|
|
2982
|
+
const result = await queryMarketContext(
|
|
2773
2983
|
`
|
|
2774
2984
|
SELECT
|
|
2775
2985
|
ts,
|
|
@@ -2790,7 +3000,8 @@ async function getHyperliquidWhaleCoverageSeriesRows(params) {
|
|
|
2790
3000
|
import_types.HYPERLIQUID_WHALE_DATA_MODEL_VERSION,
|
|
2791
3001
|
params.fromMs,
|
|
2792
3002
|
params.toMs
|
|
2793
|
-
]
|
|
3003
|
+
],
|
|
3004
|
+
params
|
|
2794
3005
|
);
|
|
2795
3006
|
return result.rows.map((row) => ({
|
|
2796
3007
|
ts: new Date(row.ts),
|
|
@@ -2800,8 +3011,8 @@ async function getHyperliquidWhaleCoverageSeriesRows(params) {
|
|
|
2800
3011
|
}));
|
|
2801
3012
|
}
|
|
2802
3013
|
async function getHyperliquidWhaleFlowSeriesRows(params) {
|
|
2803
|
-
await
|
|
2804
|
-
const result = await
|
|
3014
|
+
await prepareMarketContextSchemaForRead("hyperliquidWhales");
|
|
3015
|
+
const result = await queryMarketContext(
|
|
2805
3016
|
`
|
|
2806
3017
|
SELECT
|
|
2807
3018
|
ts,
|
|
@@ -2834,7 +3045,8 @@ async function getHyperliquidWhaleFlowSeriesRows(params) {
|
|
|
2834
3045
|
params.whaleRegistryFingerprint,
|
|
2835
3046
|
params.fromMs,
|
|
2836
3047
|
params.toMs
|
|
2837
|
-
]
|
|
3048
|
+
],
|
|
3049
|
+
params
|
|
2838
3050
|
);
|
|
2839
3051
|
return result.rows.map((row) => ({
|
|
2840
3052
|
ts: new Date(row.ts),
|
|
@@ -2855,10 +3067,10 @@ async function getHyperliquidWhaleFlowSeriesRows(params) {
|
|
|
2855
3067
|
}));
|
|
2856
3068
|
}
|
|
2857
3069
|
async function getHyperliquidWhaleFlowAggregate(params) {
|
|
2858
|
-
await
|
|
3070
|
+
await prepareMarketContextSchemaForRead("hyperliquidWhales");
|
|
2859
3071
|
const intervalMs = HYPERLIQUID_CONTEXT_INTERVAL_MS[params.interval];
|
|
2860
3072
|
const expectedBuckets = Math.ceil(intervalMs / 6e4);
|
|
2861
|
-
const res = await
|
|
3073
|
+
const res = await queryMarketContext(
|
|
2862
3074
|
`
|
|
2863
3075
|
WITH coverage_rows AS (
|
|
2864
3076
|
SELECT *
|
|
@@ -2973,7 +3185,8 @@ async function getHyperliquidWhaleFlowAggregate(params) {
|
|
|
2973
3185
|
params.decisionTimeMs,
|
|
2974
3186
|
intervalMs,
|
|
2975
3187
|
import_types.HYPERLIQUID_WHALE_DATA_MODEL_VERSION
|
|
2976
|
-
]
|
|
3188
|
+
],
|
|
3189
|
+
params
|
|
2977
3190
|
);
|
|
2978
3191
|
const row = res.rows[0];
|
|
2979
3192
|
if (!row?.as_of_ts || Number(row.coverage_buckets) !== expectedBuckets || Number(row.covered_whales) <= 0) {
|
|
@@ -3046,9 +3259,8 @@ async function hasHyperliquidWhaleBackfillCoverage(params) {
|
|
|
3046
3259
|
return Number(result.rows[0]?.buckets) === expectedBuckets && Number(result.rows[0]?.complete_buckets) === expectedBuckets;
|
|
3047
3260
|
}
|
|
3048
3261
|
async function getLatestMarketBreadth(params) {
|
|
3049
|
-
await
|
|
3050
|
-
const
|
|
3051
|
-
const res = await pool.query(
|
|
3262
|
+
await prepareMarketContextSchemaForRead("binance");
|
|
3263
|
+
const res = await queryMarketContext(
|
|
3052
3264
|
`
|
|
3053
3265
|
SELECT
|
|
3054
3266
|
universe,
|
|
@@ -3087,7 +3299,8 @@ async function getLatestMarketBreadth(params) {
|
|
|
3087
3299
|
ORDER BY ts DESC
|
|
3088
3300
|
LIMIT 1
|
|
3089
3301
|
`,
|
|
3090
|
-
[params.universe, params.interval, params.atMs]
|
|
3302
|
+
[params.universe, params.interval, params.atMs],
|
|
3303
|
+
params
|
|
3091
3304
|
);
|
|
3092
3305
|
const row = res.rows[0];
|
|
3093
3306
|
if (!row) return null;
|
|
@@ -3099,10 +3312,9 @@ async function getLatestMarketBreadth(params) {
|
|
|
3099
3312
|
};
|
|
3100
3313
|
}
|
|
3101
3314
|
async function getLatestMarketGlobalContext(params) {
|
|
3102
|
-
await
|
|
3103
|
-
const pool = getPool();
|
|
3315
|
+
await prepareMarketContextSchemaForRead("coinmarketcap");
|
|
3104
3316
|
const source = params.source ?? "coinmarketcap_global";
|
|
3105
|
-
const res = await
|
|
3317
|
+
const res = await queryMarketContext(
|
|
3106
3318
|
`
|
|
3107
3319
|
SELECT
|
|
3108
3320
|
source,
|
|
@@ -3131,11 +3343,12 @@ async function getLatestMarketGlobalContext(params) {
|
|
|
3131
3343
|
ORDER BY ts DESC
|
|
3132
3344
|
LIMIT 1
|
|
3133
3345
|
`,
|
|
3134
|
-
[source, params.atMs]
|
|
3346
|
+
[source, params.atMs],
|
|
3347
|
+
params
|
|
3135
3348
|
);
|
|
3136
3349
|
const row = res.rows[0];
|
|
3137
3350
|
if (!row) return null;
|
|
3138
|
-
const previousRes = await
|
|
3351
|
+
const previousRes = await queryMarketContext(
|
|
3139
3352
|
`
|
|
3140
3353
|
SELECT
|
|
3141
3354
|
btc_dominance_pct AS "btcDominancePct",
|
|
@@ -3148,7 +3361,8 @@ async function getLatestMarketGlobalContext(params) {
|
|
|
3148
3361
|
ORDER BY ts DESC
|
|
3149
3362
|
LIMIT 1
|
|
3150
3363
|
`,
|
|
3151
|
-
[source, row.ts]
|
|
3364
|
+
[source, row.ts],
|
|
3365
|
+
params
|
|
3152
3366
|
);
|
|
3153
3367
|
const previousDominance = previousRes.rows[0]?.btcDominancePct == null ? null : Number(previousRes.rows[0].btcDominancePct);
|
|
3154
3368
|
const previousEthDominance = previousRes.rows[0]?.ethDominancePct == null ? null : Number(previousRes.rows[0].ethDominancePct);
|
|
@@ -3241,9 +3455,8 @@ async function getLatestMarketReferenceAssetContexts(params) {
|
|
|
3241
3455
|
];
|
|
3242
3456
|
const rows = /* @__PURE__ */ new Map();
|
|
3243
3457
|
if (!symbols.length) return rows;
|
|
3244
|
-
await
|
|
3245
|
-
const
|
|
3246
|
-
const res = await pool.query(
|
|
3458
|
+
await prepareMarketContextSchemaForRead("coinmarketcap");
|
|
3459
|
+
const res = await queryMarketContext(
|
|
3247
3460
|
`
|
|
3248
3461
|
SELECT DISTINCT ON (symbol)
|
|
3249
3462
|
source,
|
|
@@ -3268,7 +3481,8 @@ async function getLatestMarketReferenceAssetContexts(params) {
|
|
|
3268
3481
|
END <= to_timestamp($4/1000.0)
|
|
3269
3482
|
ORDER BY symbol ASC, ts DESC
|
|
3270
3483
|
`,
|
|
3271
|
-
[source, symbols, interval, params.atMs]
|
|
3484
|
+
[source, symbols, interval, params.atMs],
|
|
3485
|
+
params
|
|
3272
3486
|
);
|
|
3273
3487
|
for (const row of res.rows) {
|
|
3274
3488
|
const ageMs = toMarketFeatureAge(row.ts, params.atMs);
|
|
@@ -3281,11 +3495,10 @@ async function getLatestMarketReferenceAssetContexts(params) {
|
|
|
3281
3495
|
return rows;
|
|
3282
3496
|
}
|
|
3283
3497
|
async function getLatestMarketCmcExchangeLiquidityContext(params) {
|
|
3284
|
-
await
|
|
3285
|
-
const pool = getPool();
|
|
3498
|
+
await prepareMarketContextSchemaForRead("coinmarketcap");
|
|
3286
3499
|
const source = params.source ?? "coinmarketcap_exchange_liquidity";
|
|
3287
3500
|
const interval = params.interval ?? "1d";
|
|
3288
|
-
const res = await
|
|
3501
|
+
const res = await queryMarketContext(
|
|
3289
3502
|
`
|
|
3290
3503
|
SELECT
|
|
3291
3504
|
source,
|
|
@@ -3308,11 +3521,12 @@ async function getLatestMarketCmcExchangeLiquidityContext(params) {
|
|
|
3308
3521
|
ORDER BY ts DESC
|
|
3309
3522
|
LIMIT 1
|
|
3310
3523
|
`,
|
|
3311
|
-
[source, interval, params.atMs]
|
|
3524
|
+
[source, interval, params.atMs],
|
|
3525
|
+
params
|
|
3312
3526
|
);
|
|
3313
3527
|
const row = res.rows[0];
|
|
3314
3528
|
if (!row) return null;
|
|
3315
|
-
const previousRes = await
|
|
3529
|
+
const previousRes = await queryMarketContext(
|
|
3316
3530
|
`
|
|
3317
3531
|
SELECT total_volume_usd AS "totalVolumeUsd"
|
|
3318
3532
|
FROM market_cmc_exchange_liquidity_context
|
|
@@ -3322,7 +3536,8 @@ async function getLatestMarketCmcExchangeLiquidityContext(params) {
|
|
|
3322
3536
|
ORDER BY ts DESC
|
|
3323
3537
|
LIMIT 1
|
|
3324
3538
|
`,
|
|
3325
|
-
[source, interval, row.ts]
|
|
3539
|
+
[source, interval, row.ts],
|
|
3540
|
+
params
|
|
3326
3541
|
);
|
|
3327
3542
|
const currentTotal = row.totalVolumeUsd == null ? null : Number(row.totalVolumeUsd);
|
|
3328
3543
|
const previousTotal = previousRes.rows[0]?.totalVolumeUsd == null ? null : Number(previousRes.rows[0].totalVolumeUsd);
|
|
@@ -3346,9 +3561,8 @@ async function getLatestMarketCmcIndexContexts(params) {
|
|
|
3346
3561
|
];
|
|
3347
3562
|
const rows = /* @__PURE__ */ new Map();
|
|
3348
3563
|
if (!indexSlugs.length) return rows;
|
|
3349
|
-
await
|
|
3350
|
-
const
|
|
3351
|
-
const res = await pool.query(
|
|
3564
|
+
await prepareMarketContextSchemaForRead("coinmarketcap");
|
|
3565
|
+
const res = await queryMarketContext(
|
|
3352
3566
|
`
|
|
3353
3567
|
SELECT DISTINCT ON (index_slug)
|
|
3354
3568
|
source,
|
|
@@ -3371,10 +3585,11 @@ async function getLatestMarketCmcIndexContexts(params) {
|
|
|
3371
3585
|
END <= to_timestamp($4/1000.0)
|
|
3372
3586
|
ORDER BY index_slug ASC, ts DESC
|
|
3373
3587
|
`,
|
|
3374
|
-
[source, indexSlugs, interval, params.atMs]
|
|
3588
|
+
[source, indexSlugs, interval, params.atMs],
|
|
3589
|
+
params
|
|
3375
3590
|
);
|
|
3376
3591
|
for (const row of res.rows) {
|
|
3377
|
-
const previousRes = await
|
|
3592
|
+
const previousRes = await queryMarketContext(
|
|
3378
3593
|
`
|
|
3379
3594
|
SELECT value
|
|
3380
3595
|
FROM market_cmc_index_context
|
|
@@ -3385,7 +3600,8 @@ async function getLatestMarketCmcIndexContexts(params) {
|
|
|
3385
3600
|
ORDER BY ts DESC
|
|
3386
3601
|
LIMIT 1
|
|
3387
3602
|
`,
|
|
3388
|
-
[source, row.indexSlug, interval, row.ts]
|
|
3603
|
+
[source, row.indexSlug, interval, row.ts],
|
|
3604
|
+
params
|
|
3389
3605
|
);
|
|
3390
3606
|
const currentValue = row.value == null ? null : Number(row.value);
|
|
3391
3607
|
const previousValue = previousRes.rows[0]?.value == null ? null : Number(previousRes.rows[0].value);
|
|
@@ -3400,11 +3616,10 @@ async function getLatestMarketCmcIndexContexts(params) {
|
|
|
3400
3616
|
return rows;
|
|
3401
3617
|
}
|
|
3402
3618
|
async function getLatestMarketCmcFearGreedContext(params) {
|
|
3403
|
-
await
|
|
3404
|
-
const pool = getPool();
|
|
3619
|
+
await prepareMarketContextSchemaForRead("coinmarketcap");
|
|
3405
3620
|
const source = params.source ?? "coinmarketcap_fear_greed";
|
|
3406
3621
|
const interval = params.interval ?? "1d";
|
|
3407
|
-
const res = await
|
|
3622
|
+
const res = await queryMarketContext(
|
|
3408
3623
|
`
|
|
3409
3624
|
SELECT
|
|
3410
3625
|
source,
|
|
@@ -3424,11 +3639,12 @@ async function getLatestMarketCmcFearGreedContext(params) {
|
|
|
3424
3639
|
ORDER BY ts DESC
|
|
3425
3640
|
LIMIT 1
|
|
3426
3641
|
`,
|
|
3427
|
-
[source, interval, params.atMs]
|
|
3642
|
+
[source, interval, params.atMs],
|
|
3643
|
+
params
|
|
3428
3644
|
);
|
|
3429
3645
|
const row = res.rows[0];
|
|
3430
3646
|
if (!row) return null;
|
|
3431
|
-
const previousRes = await
|
|
3647
|
+
const previousRes = await queryMarketContext(
|
|
3432
3648
|
`
|
|
3433
3649
|
SELECT
|
|
3434
3650
|
value::int AS value,
|
|
@@ -3440,9 +3656,10 @@ async function getLatestMarketCmcFearGreedContext(params) {
|
|
|
3440
3656
|
ORDER BY ts DESC
|
|
3441
3657
|
LIMIT 1
|
|
3442
3658
|
`,
|
|
3443
|
-
[source, interval, row.ts]
|
|
3659
|
+
[source, interval, row.ts],
|
|
3660
|
+
params
|
|
3444
3661
|
);
|
|
3445
|
-
const previous7dRes = await
|
|
3662
|
+
const previous7dRes = await queryMarketContext(
|
|
3446
3663
|
`
|
|
3447
3664
|
SELECT value::int AS value
|
|
3448
3665
|
FROM market_cmc_fear_greed_context
|
|
@@ -3452,7 +3669,8 @@ async function getLatestMarketCmcFearGreedContext(params) {
|
|
|
3452
3669
|
ORDER BY ts DESC
|
|
3453
3670
|
LIMIT 1
|
|
3454
3671
|
`,
|
|
3455
|
-
[source, interval, row.ts]
|
|
3672
|
+
[source, interval, row.ts],
|
|
3673
|
+
params
|
|
3456
3674
|
);
|
|
3457
3675
|
const previousValue = previousRes.rows[0]?.value == null ? null : Number(previousRes.rows[0].value);
|
|
3458
3676
|
const previous7dValue = previous7dRes.rows[0]?.value == null ? null : Number(previous7dRes.rows[0].value);
|
|
@@ -3939,7 +4157,13 @@ async function findContinuityGap(provider, symbol, interval) {
|
|
|
3939
4157
|
applyDerivativesMetricCoverage,
|
|
3940
4158
|
cleanupDeprecatedMarketContext,
|
|
3941
4159
|
closeTimescalePool,
|
|
4160
|
+
configureTimescaleMarketContextSchemaMode,
|
|
3942
4161
|
deleteCandles,
|
|
4162
|
+
ensureBinanceMarketSchema,
|
|
4163
|
+
ensureCoinMarketCapContextSchema,
|
|
4164
|
+
ensureDerivativesSchema,
|
|
4165
|
+
ensureHyperliquidWhaleSchema,
|
|
4166
|
+
ensureMarketContextSchemas,
|
|
3943
4167
|
findContinuityGap,
|
|
3944
4168
|
getCandlesRange,
|
|
3945
4169
|
getDataEdges,
|