@tradejs/infra 2.0.21 → 3.0.1
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-DFMKDB2R.mjs
DELETED
|
@@ -1,1285 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ensureBinanceMarketSchema,
|
|
3
|
-
getPool,
|
|
4
|
-
getSafeBulkInsertRows,
|
|
5
|
-
prepareMarketContextSchemaForRead,
|
|
6
|
-
queryMarketContext,
|
|
7
|
-
toMarketFeatureAge
|
|
8
|
-
} from "./chunk-I2J6YDBD.mjs";
|
|
9
|
-
|
|
10
|
-
// src/timescale/marketContext.ts
|
|
11
|
-
async function upsertMarketTradeFlowRows(rows) {
|
|
12
|
-
if (!rows.length) return;
|
|
13
|
-
await ensureBinanceMarketSchema();
|
|
14
|
-
const pool = getPool();
|
|
15
|
-
const cols = [
|
|
16
|
-
"symbol",
|
|
17
|
-
"interval",
|
|
18
|
-
"ts",
|
|
19
|
-
"trades",
|
|
20
|
-
"buy_base_volume",
|
|
21
|
-
"sell_base_volume",
|
|
22
|
-
"buy_quote_volume",
|
|
23
|
-
"sell_quote_volume",
|
|
24
|
-
"net_base_delta",
|
|
25
|
-
"net_quote_delta",
|
|
26
|
-
"buy_pressure_pct",
|
|
27
|
-
"source"
|
|
28
|
-
];
|
|
29
|
-
const maxRows = getSafeBulkInsertRows(cols.length);
|
|
30
|
-
if (rows.length > maxRows) {
|
|
31
|
-
for (let i = 0; i < rows.length; i += maxRows) {
|
|
32
|
-
await upsertMarketTradeFlowRows(rows.slice(i, i + maxRows));
|
|
33
|
-
}
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
const valuesSql = rows.map(
|
|
37
|
-
(_, i) => `(${cols.map((__, j) => `$${i * cols.length + j + 1}`).join(",")})`
|
|
38
|
-
).join(",");
|
|
39
|
-
const flat = rows.flatMap((row) => [
|
|
40
|
-
row.symbol,
|
|
41
|
-
row.interval,
|
|
42
|
-
row.ts,
|
|
43
|
-
row.trades,
|
|
44
|
-
row.buyBaseVolume ?? null,
|
|
45
|
-
row.sellBaseVolume ?? null,
|
|
46
|
-
row.buyQuoteVolume ?? null,
|
|
47
|
-
row.sellQuoteVolume ?? null,
|
|
48
|
-
row.netBaseDelta ?? null,
|
|
49
|
-
row.netQuoteDelta ?? null,
|
|
50
|
-
row.buyPressurePct ?? null,
|
|
51
|
-
row.source ?? null
|
|
52
|
-
]);
|
|
53
|
-
await pool.query(
|
|
54
|
-
`
|
|
55
|
-
INSERT INTO market_trade_flow (${cols.join(",")})
|
|
56
|
-
VALUES ${valuesSql}
|
|
57
|
-
ON CONFLICT (symbol, interval, ts) DO UPDATE SET
|
|
58
|
-
trades = EXCLUDED.trades,
|
|
59
|
-
buy_base_volume = COALESCE(EXCLUDED.buy_base_volume, market_trade_flow.buy_base_volume),
|
|
60
|
-
sell_base_volume = COALESCE(EXCLUDED.sell_base_volume, market_trade_flow.sell_base_volume),
|
|
61
|
-
buy_quote_volume = COALESCE(EXCLUDED.buy_quote_volume, market_trade_flow.buy_quote_volume),
|
|
62
|
-
sell_quote_volume = COALESCE(EXCLUDED.sell_quote_volume, market_trade_flow.sell_quote_volume),
|
|
63
|
-
net_base_delta = COALESCE(EXCLUDED.net_base_delta, market_trade_flow.net_base_delta),
|
|
64
|
-
net_quote_delta = COALESCE(EXCLUDED.net_quote_delta, market_trade_flow.net_quote_delta),
|
|
65
|
-
buy_pressure_pct = COALESCE(EXCLUDED.buy_pressure_pct, market_trade_flow.buy_pressure_pct),
|
|
66
|
-
source = COALESCE(EXCLUDED.source, market_trade_flow.source),
|
|
67
|
-
ingested_at = now()
|
|
68
|
-
`,
|
|
69
|
-
flat
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
async function upsertMarketBreadthRows(rows) {
|
|
73
|
-
if (!rows.length) return;
|
|
74
|
-
await ensureBinanceMarketSchema();
|
|
75
|
-
const pool = getPool();
|
|
76
|
-
const cols = [
|
|
77
|
-
"universe",
|
|
78
|
-
"interval",
|
|
79
|
-
"ts",
|
|
80
|
-
"symbols_count",
|
|
81
|
-
"advancers",
|
|
82
|
-
"decliners",
|
|
83
|
-
"unchanged",
|
|
84
|
-
"advance_decline_ratio",
|
|
85
|
-
"pct_above_ma20",
|
|
86
|
-
"pct_above_ma50",
|
|
87
|
-
"equal_weighted_return",
|
|
88
|
-
"volume_weighted_return",
|
|
89
|
-
"dispersion",
|
|
90
|
-
"btc_return_1h",
|
|
91
|
-
"btc_return_4h",
|
|
92
|
-
"btc_return_24h",
|
|
93
|
-
"alt_basket_return_1h",
|
|
94
|
-
"alt_basket_return_4h",
|
|
95
|
-
"alt_basket_return_24h",
|
|
96
|
-
"btc_vs_alt_return_1h",
|
|
97
|
-
"btc_vs_alt_return_4h",
|
|
98
|
-
"btc_vs_alt_return_24h",
|
|
99
|
-
"btc_turnover_share_1h",
|
|
100
|
-
"btc_turnover_share_24h",
|
|
101
|
-
"btc_turnover_share_change_24h",
|
|
102
|
-
"alt_vol_to_btc_vol_24h",
|
|
103
|
-
"alt_dispersion_24h",
|
|
104
|
-
"btc_alt_regime",
|
|
105
|
-
"source"
|
|
106
|
-
];
|
|
107
|
-
const maxRows = getSafeBulkInsertRows(cols.length);
|
|
108
|
-
if (rows.length > maxRows) {
|
|
109
|
-
for (let i = 0; i < rows.length; i += maxRows) {
|
|
110
|
-
await upsertMarketBreadthRows(rows.slice(i, i + maxRows));
|
|
111
|
-
}
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
const valuesSql = rows.map(
|
|
115
|
-
(_, i) => `(${cols.map((__, j) => `$${i * cols.length + j + 1}`).join(",")})`
|
|
116
|
-
).join(",");
|
|
117
|
-
const flat = rows.flatMap((row) => [
|
|
118
|
-
row.universe,
|
|
119
|
-
row.interval,
|
|
120
|
-
row.ts,
|
|
121
|
-
row.symbolsCount,
|
|
122
|
-
row.advancers,
|
|
123
|
-
row.decliners,
|
|
124
|
-
row.unchanged,
|
|
125
|
-
row.advanceDeclineRatio ?? null,
|
|
126
|
-
row.pctAboveMa20 ?? null,
|
|
127
|
-
row.pctAboveMa50 ?? null,
|
|
128
|
-
row.equalWeightedReturn ?? null,
|
|
129
|
-
row.volumeWeightedReturn ?? null,
|
|
130
|
-
row.dispersion ?? null,
|
|
131
|
-
row.btcReturn1h ?? null,
|
|
132
|
-
row.btcReturn4h ?? null,
|
|
133
|
-
row.btcReturn24h ?? null,
|
|
134
|
-
row.altBasketReturn1h ?? null,
|
|
135
|
-
row.altBasketReturn4h ?? null,
|
|
136
|
-
row.altBasketReturn24h ?? null,
|
|
137
|
-
row.btcVsAltReturn1h ?? null,
|
|
138
|
-
row.btcVsAltReturn4h ?? null,
|
|
139
|
-
row.btcVsAltReturn24h ?? null,
|
|
140
|
-
row.btcTurnoverShare1h ?? null,
|
|
141
|
-
row.btcTurnoverShare24h ?? null,
|
|
142
|
-
row.btcTurnoverShareChange24h ?? null,
|
|
143
|
-
row.altVolToBtcVol24h ?? null,
|
|
144
|
-
row.altDispersion24h ?? null,
|
|
145
|
-
row.btcAltRegime ?? null,
|
|
146
|
-
row.source ?? null
|
|
147
|
-
]);
|
|
148
|
-
await pool.query(
|
|
149
|
-
`
|
|
150
|
-
INSERT INTO market_breadth (${cols.join(",")})
|
|
151
|
-
VALUES ${valuesSql}
|
|
152
|
-
ON CONFLICT (universe, interval, ts) DO UPDATE SET
|
|
153
|
-
symbols_count = EXCLUDED.symbols_count,
|
|
154
|
-
advancers = EXCLUDED.advancers,
|
|
155
|
-
decliners = EXCLUDED.decliners,
|
|
156
|
-
unchanged = EXCLUDED.unchanged,
|
|
157
|
-
advance_decline_ratio = COALESCE(EXCLUDED.advance_decline_ratio, market_breadth.advance_decline_ratio),
|
|
158
|
-
pct_above_ma20 = COALESCE(EXCLUDED.pct_above_ma20, market_breadth.pct_above_ma20),
|
|
159
|
-
pct_above_ma50 = COALESCE(EXCLUDED.pct_above_ma50, market_breadth.pct_above_ma50),
|
|
160
|
-
equal_weighted_return = COALESCE(EXCLUDED.equal_weighted_return, market_breadth.equal_weighted_return),
|
|
161
|
-
volume_weighted_return = COALESCE(EXCLUDED.volume_weighted_return, market_breadth.volume_weighted_return),
|
|
162
|
-
dispersion = COALESCE(EXCLUDED.dispersion, market_breadth.dispersion),
|
|
163
|
-
btc_return_1h = COALESCE(EXCLUDED.btc_return_1h, market_breadth.btc_return_1h),
|
|
164
|
-
btc_return_4h = COALESCE(EXCLUDED.btc_return_4h, market_breadth.btc_return_4h),
|
|
165
|
-
btc_return_24h = COALESCE(EXCLUDED.btc_return_24h, market_breadth.btc_return_24h),
|
|
166
|
-
alt_basket_return_1h = COALESCE(EXCLUDED.alt_basket_return_1h, market_breadth.alt_basket_return_1h),
|
|
167
|
-
alt_basket_return_4h = COALESCE(EXCLUDED.alt_basket_return_4h, market_breadth.alt_basket_return_4h),
|
|
168
|
-
alt_basket_return_24h = COALESCE(EXCLUDED.alt_basket_return_24h, market_breadth.alt_basket_return_24h),
|
|
169
|
-
btc_vs_alt_return_1h = COALESCE(EXCLUDED.btc_vs_alt_return_1h, market_breadth.btc_vs_alt_return_1h),
|
|
170
|
-
btc_vs_alt_return_4h = COALESCE(EXCLUDED.btc_vs_alt_return_4h, market_breadth.btc_vs_alt_return_4h),
|
|
171
|
-
btc_vs_alt_return_24h = COALESCE(EXCLUDED.btc_vs_alt_return_24h, market_breadth.btc_vs_alt_return_24h),
|
|
172
|
-
btc_turnover_share_1h = COALESCE(EXCLUDED.btc_turnover_share_1h, market_breadth.btc_turnover_share_1h),
|
|
173
|
-
btc_turnover_share_24h = COALESCE(EXCLUDED.btc_turnover_share_24h, market_breadth.btc_turnover_share_24h),
|
|
174
|
-
btc_turnover_share_change_24h = COALESCE(EXCLUDED.btc_turnover_share_change_24h, market_breadth.btc_turnover_share_change_24h),
|
|
175
|
-
alt_vol_to_btc_vol_24h = COALESCE(EXCLUDED.alt_vol_to_btc_vol_24h, market_breadth.alt_vol_to_btc_vol_24h),
|
|
176
|
-
alt_dispersion_24h = COALESCE(EXCLUDED.alt_dispersion_24h, market_breadth.alt_dispersion_24h),
|
|
177
|
-
btc_alt_regime = COALESCE(EXCLUDED.btc_alt_regime, market_breadth.btc_alt_regime),
|
|
178
|
-
source = COALESCE(EXCLUDED.source, market_breadth.source),
|
|
179
|
-
ingested_at = now()
|
|
180
|
-
`,
|
|
181
|
-
flat
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
async function upsertMarketGlobalContextRows(rows) {
|
|
185
|
-
if (!rows.length) return;
|
|
186
|
-
await ensureBinanceMarketSchema();
|
|
187
|
-
const pool = getPool();
|
|
188
|
-
const cols = [
|
|
189
|
-
"source",
|
|
190
|
-
"ts",
|
|
191
|
-
"updated_at_ts",
|
|
192
|
-
"active_cryptocurrencies",
|
|
193
|
-
"active_exchanges",
|
|
194
|
-
"active_market_pairs",
|
|
195
|
-
"markets",
|
|
196
|
-
"total_market_cap_usd",
|
|
197
|
-
"total_volume_usd",
|
|
198
|
-
"total_volume_reported_usd",
|
|
199
|
-
"btc_dominance_pct",
|
|
200
|
-
"eth_dominance_pct",
|
|
201
|
-
"alt_market_cap_usd",
|
|
202
|
-
"alt_volume_usd",
|
|
203
|
-
"alt_volume_reported_usd",
|
|
204
|
-
"btc_to_alt_market_cap_ratio",
|
|
205
|
-
"market_cap_change_pct_24h_usd"
|
|
206
|
-
];
|
|
207
|
-
const maxRows = getSafeBulkInsertRows(cols.length);
|
|
208
|
-
if (rows.length > maxRows) {
|
|
209
|
-
for (let i = 0; i < rows.length; i += maxRows) {
|
|
210
|
-
await upsertMarketGlobalContextRows(rows.slice(i, i + maxRows));
|
|
211
|
-
}
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
const valuesSql = rows.map(
|
|
215
|
-
(_, i) => `(${cols.map((__, j) => `$${i * cols.length + j + 1}`).join(",")})`
|
|
216
|
-
).join(",");
|
|
217
|
-
const flat = rows.flatMap((row) => [
|
|
218
|
-
row.source,
|
|
219
|
-
row.ts,
|
|
220
|
-
row.updatedAt ?? null,
|
|
221
|
-
row.activeCryptocurrencies ?? null,
|
|
222
|
-
row.activeExchanges ?? null,
|
|
223
|
-
row.activeMarketPairs ?? null,
|
|
224
|
-
row.markets ?? null,
|
|
225
|
-
row.totalMarketCapUsd ?? null,
|
|
226
|
-
row.totalVolumeUsd ?? null,
|
|
227
|
-
row.totalVolumeReportedUsd ?? null,
|
|
228
|
-
row.btcDominancePct ?? null,
|
|
229
|
-
row.ethDominancePct ?? null,
|
|
230
|
-
row.altMarketCapUsd ?? null,
|
|
231
|
-
row.altVolumeUsd ?? null,
|
|
232
|
-
row.altVolumeReportedUsd ?? null,
|
|
233
|
-
row.btcToAltMarketCapRatio ?? null,
|
|
234
|
-
row.marketCapChangePct24hUsd ?? null
|
|
235
|
-
]);
|
|
236
|
-
await pool.query(
|
|
237
|
-
`
|
|
238
|
-
INSERT INTO market_global_context (${cols.join(",")})
|
|
239
|
-
VALUES ${valuesSql}
|
|
240
|
-
ON CONFLICT (source, ts) DO UPDATE SET
|
|
241
|
-
updated_at_ts = COALESCE(EXCLUDED.updated_at_ts, market_global_context.updated_at_ts),
|
|
242
|
-
active_cryptocurrencies = COALESCE(EXCLUDED.active_cryptocurrencies, market_global_context.active_cryptocurrencies),
|
|
243
|
-
active_exchanges = COALESCE(EXCLUDED.active_exchanges, market_global_context.active_exchanges),
|
|
244
|
-
active_market_pairs = COALESCE(EXCLUDED.active_market_pairs, market_global_context.active_market_pairs),
|
|
245
|
-
markets = COALESCE(EXCLUDED.markets, market_global_context.markets),
|
|
246
|
-
total_market_cap_usd = COALESCE(EXCLUDED.total_market_cap_usd, market_global_context.total_market_cap_usd),
|
|
247
|
-
total_volume_usd = COALESCE(EXCLUDED.total_volume_usd, market_global_context.total_volume_usd),
|
|
248
|
-
total_volume_reported_usd = COALESCE(EXCLUDED.total_volume_reported_usd, market_global_context.total_volume_reported_usd),
|
|
249
|
-
btc_dominance_pct = COALESCE(EXCLUDED.btc_dominance_pct, market_global_context.btc_dominance_pct),
|
|
250
|
-
eth_dominance_pct = COALESCE(EXCLUDED.eth_dominance_pct, market_global_context.eth_dominance_pct),
|
|
251
|
-
alt_market_cap_usd = COALESCE(EXCLUDED.alt_market_cap_usd, market_global_context.alt_market_cap_usd),
|
|
252
|
-
alt_volume_usd = COALESCE(EXCLUDED.alt_volume_usd, market_global_context.alt_volume_usd),
|
|
253
|
-
alt_volume_reported_usd = COALESCE(EXCLUDED.alt_volume_reported_usd, market_global_context.alt_volume_reported_usd),
|
|
254
|
-
btc_to_alt_market_cap_ratio = COALESCE(EXCLUDED.btc_to_alt_market_cap_ratio, market_global_context.btc_to_alt_market_cap_ratio),
|
|
255
|
-
market_cap_change_pct_24h_usd = COALESCE(EXCLUDED.market_cap_change_pct_24h_usd, market_global_context.market_cap_change_pct_24h_usd),
|
|
256
|
-
ingested_at = now()
|
|
257
|
-
`,
|
|
258
|
-
flat
|
|
259
|
-
);
|
|
260
|
-
}
|
|
261
|
-
async function upsertMarketReferenceAssetContextRows(rows) {
|
|
262
|
-
if (!rows.length) return;
|
|
263
|
-
await ensureBinanceMarketSchema();
|
|
264
|
-
const pool = getPool();
|
|
265
|
-
const cols = [
|
|
266
|
-
"source",
|
|
267
|
-
"symbol",
|
|
268
|
-
"cmc_id",
|
|
269
|
-
"interval",
|
|
270
|
-
"ts",
|
|
271
|
-
"open_usd",
|
|
272
|
-
"high_usd",
|
|
273
|
-
"low_usd",
|
|
274
|
-
"close_usd",
|
|
275
|
-
"volume_usd",
|
|
276
|
-
"market_cap_usd"
|
|
277
|
-
];
|
|
278
|
-
const maxRows = getSafeBulkInsertRows(cols.length);
|
|
279
|
-
if (rows.length > maxRows) {
|
|
280
|
-
for (let i = 0; i < rows.length; i += maxRows) {
|
|
281
|
-
await upsertMarketReferenceAssetContextRows(rows.slice(i, i + maxRows));
|
|
282
|
-
}
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
const valuesSql = rows.map(
|
|
286
|
-
(_, i) => `(${cols.map((__, j) => `$${i * cols.length + j + 1}`).join(",")})`
|
|
287
|
-
).join(",");
|
|
288
|
-
const flat = rows.flatMap((row) => [
|
|
289
|
-
row.source,
|
|
290
|
-
row.symbol.trim().toUpperCase(),
|
|
291
|
-
Math.trunc(row.cmcId),
|
|
292
|
-
row.interval,
|
|
293
|
-
row.ts,
|
|
294
|
-
row.openUsd ?? null,
|
|
295
|
-
row.highUsd ?? null,
|
|
296
|
-
row.lowUsd ?? null,
|
|
297
|
-
row.closeUsd ?? null,
|
|
298
|
-
row.volumeUsd ?? null,
|
|
299
|
-
row.marketCapUsd ?? null
|
|
300
|
-
]);
|
|
301
|
-
await pool.query(
|
|
302
|
-
`
|
|
303
|
-
INSERT INTO market_reference_asset_context (${cols.join(",")})
|
|
304
|
-
VALUES ${valuesSql}
|
|
305
|
-
ON CONFLICT (source, symbol, interval, ts) DO UPDATE SET
|
|
306
|
-
cmc_id = EXCLUDED.cmc_id,
|
|
307
|
-
open_usd = COALESCE(EXCLUDED.open_usd, market_reference_asset_context.open_usd),
|
|
308
|
-
high_usd = COALESCE(EXCLUDED.high_usd, market_reference_asset_context.high_usd),
|
|
309
|
-
low_usd = COALESCE(EXCLUDED.low_usd, market_reference_asset_context.low_usd),
|
|
310
|
-
close_usd = COALESCE(EXCLUDED.close_usd, market_reference_asset_context.close_usd),
|
|
311
|
-
volume_usd = COALESCE(EXCLUDED.volume_usd, market_reference_asset_context.volume_usd),
|
|
312
|
-
market_cap_usd = COALESCE(EXCLUDED.market_cap_usd, market_reference_asset_context.market_cap_usd),
|
|
313
|
-
ingested_at = now()
|
|
314
|
-
`,
|
|
315
|
-
flat
|
|
316
|
-
);
|
|
317
|
-
}
|
|
318
|
-
async function upsertMarketCmcExchangeLiquidityContextRows(rows) {
|
|
319
|
-
if (!rows.length) return;
|
|
320
|
-
await ensureBinanceMarketSchema();
|
|
321
|
-
const pool = getPool();
|
|
322
|
-
const cols = [
|
|
323
|
-
"source",
|
|
324
|
-
"interval",
|
|
325
|
-
"ts",
|
|
326
|
-
"exchanges_count",
|
|
327
|
-
"total_volume_usd",
|
|
328
|
-
"binance_volume_usd",
|
|
329
|
-
"binance_volume_share",
|
|
330
|
-
"top_exchange_volume_share",
|
|
331
|
-
"liquidity_regime"
|
|
332
|
-
];
|
|
333
|
-
const maxRows = getSafeBulkInsertRows(cols.length);
|
|
334
|
-
if (rows.length > maxRows) {
|
|
335
|
-
for (let i = 0; i < rows.length; i += maxRows) {
|
|
336
|
-
await upsertMarketCmcExchangeLiquidityContextRows(
|
|
337
|
-
rows.slice(i, i + maxRows)
|
|
338
|
-
);
|
|
339
|
-
}
|
|
340
|
-
return;
|
|
341
|
-
}
|
|
342
|
-
const valuesSql = rows.map(
|
|
343
|
-
(_, i) => `(${cols.map((__, j) => `$${i * cols.length + j + 1}`).join(",")})`
|
|
344
|
-
).join(",");
|
|
345
|
-
const flat = rows.flatMap((row) => [
|
|
346
|
-
row.source,
|
|
347
|
-
row.interval,
|
|
348
|
-
row.ts,
|
|
349
|
-
Math.trunc(row.exchangesCount),
|
|
350
|
-
row.totalVolumeUsd ?? null,
|
|
351
|
-
row.binanceVolumeUsd ?? null,
|
|
352
|
-
row.binanceVolumeShare ?? null,
|
|
353
|
-
row.topExchangeVolumeShare ?? null,
|
|
354
|
-
row.liquidityRegime ?? null
|
|
355
|
-
]);
|
|
356
|
-
await pool.query(
|
|
357
|
-
`
|
|
358
|
-
INSERT INTO market_cmc_exchange_liquidity_context (${cols.join(",")})
|
|
359
|
-
VALUES ${valuesSql}
|
|
360
|
-
ON CONFLICT (source, interval, ts) DO UPDATE SET
|
|
361
|
-
exchanges_count = EXCLUDED.exchanges_count,
|
|
362
|
-
total_volume_usd = COALESCE(EXCLUDED.total_volume_usd, market_cmc_exchange_liquidity_context.total_volume_usd),
|
|
363
|
-
binance_volume_usd = COALESCE(EXCLUDED.binance_volume_usd, market_cmc_exchange_liquidity_context.binance_volume_usd),
|
|
364
|
-
binance_volume_share = COALESCE(EXCLUDED.binance_volume_share, market_cmc_exchange_liquidity_context.binance_volume_share),
|
|
365
|
-
top_exchange_volume_share = COALESCE(EXCLUDED.top_exchange_volume_share, market_cmc_exchange_liquidity_context.top_exchange_volume_share),
|
|
366
|
-
liquidity_regime = COALESCE(EXCLUDED.liquidity_regime, market_cmc_exchange_liquidity_context.liquidity_regime),
|
|
367
|
-
ingested_at = now()
|
|
368
|
-
`,
|
|
369
|
-
flat
|
|
370
|
-
);
|
|
371
|
-
}
|
|
372
|
-
async function upsertMarketCmcFearGreedContextRows(rows) {
|
|
373
|
-
if (!rows.length) return;
|
|
374
|
-
await ensureBinanceMarketSchema();
|
|
375
|
-
const pool = getPool();
|
|
376
|
-
const cols = [
|
|
377
|
-
"source",
|
|
378
|
-
"interval",
|
|
379
|
-
"ts",
|
|
380
|
-
"value",
|
|
381
|
-
"classification",
|
|
382
|
-
"sentiment_regime"
|
|
383
|
-
];
|
|
384
|
-
const maxRows = getSafeBulkInsertRows(cols.length);
|
|
385
|
-
if (rows.length > maxRows) {
|
|
386
|
-
for (let i = 0; i < rows.length; i += maxRows) {
|
|
387
|
-
await upsertMarketCmcFearGreedContextRows(rows.slice(i, i + maxRows));
|
|
388
|
-
}
|
|
389
|
-
return;
|
|
390
|
-
}
|
|
391
|
-
const valuesSql = rows.map(
|
|
392
|
-
(_, i) => `(${cols.map((__, j) => `$${i * cols.length + j + 1}`).join(",")})`
|
|
393
|
-
).join(",");
|
|
394
|
-
const flat = rows.flatMap((row) => [
|
|
395
|
-
row.source,
|
|
396
|
-
row.interval,
|
|
397
|
-
row.ts,
|
|
398
|
-
Math.trunc(row.value),
|
|
399
|
-
row.classification,
|
|
400
|
-
row.sentimentRegime
|
|
401
|
-
]);
|
|
402
|
-
await pool.query(
|
|
403
|
-
`
|
|
404
|
-
INSERT INTO market_cmc_fear_greed_context (${cols.join(",")})
|
|
405
|
-
VALUES ${valuesSql}
|
|
406
|
-
ON CONFLICT (source, interval, ts) DO UPDATE SET
|
|
407
|
-
value = EXCLUDED.value,
|
|
408
|
-
classification = EXCLUDED.classification,
|
|
409
|
-
sentiment_regime = EXCLUDED.sentiment_regime,
|
|
410
|
-
ingested_at = now()
|
|
411
|
-
`,
|
|
412
|
-
flat
|
|
413
|
-
);
|
|
414
|
-
}
|
|
415
|
-
async function upsertMarketCmcIndexContextRows(rows) {
|
|
416
|
-
if (!rows.length) return;
|
|
417
|
-
await ensureBinanceMarketSchema();
|
|
418
|
-
const pool = getPool();
|
|
419
|
-
const cols = [
|
|
420
|
-
"source",
|
|
421
|
-
"index_slug",
|
|
422
|
-
"interval",
|
|
423
|
-
"ts",
|
|
424
|
-
"value",
|
|
425
|
-
"constituents_count",
|
|
426
|
-
"top_constituent_symbol",
|
|
427
|
-
"top_constituent_weight_pct",
|
|
428
|
-
"constituents"
|
|
429
|
-
];
|
|
430
|
-
const maxRows = getSafeBulkInsertRows(cols.length);
|
|
431
|
-
if (rows.length > maxRows) {
|
|
432
|
-
for (let i = 0; i < rows.length; i += maxRows) {
|
|
433
|
-
await upsertMarketCmcIndexContextRows(rows.slice(i, i + maxRows));
|
|
434
|
-
}
|
|
435
|
-
return;
|
|
436
|
-
}
|
|
437
|
-
const valuesSql = rows.map(
|
|
438
|
-
(_, i) => `(${cols.map((__, j) => `$${i * cols.length + j + 1}`).join(",")})`
|
|
439
|
-
).join(",");
|
|
440
|
-
const flat = rows.flatMap((row) => [
|
|
441
|
-
row.source,
|
|
442
|
-
row.indexSlug,
|
|
443
|
-
row.interval,
|
|
444
|
-
row.ts,
|
|
445
|
-
row.value,
|
|
446
|
-
row.constituentsCount ?? null,
|
|
447
|
-
row.topConstituentSymbol ?? null,
|
|
448
|
-
row.topConstituentWeightPct ?? null,
|
|
449
|
-
row.constituents ? JSON.stringify(row.constituents) : null
|
|
450
|
-
]);
|
|
451
|
-
await pool.query(
|
|
452
|
-
`
|
|
453
|
-
INSERT INTO market_cmc_index_context (${cols.join(",")})
|
|
454
|
-
VALUES ${valuesSql}
|
|
455
|
-
ON CONFLICT (source, index_slug, interval, ts) DO UPDATE SET
|
|
456
|
-
value = EXCLUDED.value,
|
|
457
|
-
constituents_count = COALESCE(EXCLUDED.constituents_count, market_cmc_index_context.constituents_count),
|
|
458
|
-
top_constituent_symbol = COALESCE(EXCLUDED.top_constituent_symbol, market_cmc_index_context.top_constituent_symbol),
|
|
459
|
-
top_constituent_weight_pct = COALESCE(EXCLUDED.top_constituent_weight_pct, market_cmc_index_context.top_constituent_weight_pct),
|
|
460
|
-
constituents = COALESCE(EXCLUDED.constituents, market_cmc_index_context.constituents),
|
|
461
|
-
ingested_at = now()
|
|
462
|
-
`,
|
|
463
|
-
flat
|
|
464
|
-
);
|
|
465
|
-
}
|
|
466
|
-
async function getMarketContextBackfillCoverage(params) {
|
|
467
|
-
const source = String(params.source || "").trim().toLowerCase();
|
|
468
|
-
const scopes = [
|
|
469
|
-
...new Set(
|
|
470
|
-
params.scopes.map(
|
|
471
|
-
(scope) => String(scope || "").trim().toLowerCase()
|
|
472
|
-
).filter(Boolean)
|
|
473
|
-
)
|
|
474
|
-
];
|
|
475
|
-
const interval = String(params.interval || "").trim().toLowerCase();
|
|
476
|
-
if (!source || !scopes.length || !interval) return [];
|
|
477
|
-
await ensureBinanceMarketSchema();
|
|
478
|
-
const pool = getPool();
|
|
479
|
-
const res = await pool.query(
|
|
480
|
-
`
|
|
481
|
-
SELECT
|
|
482
|
-
source,
|
|
483
|
-
scope,
|
|
484
|
-
interval,
|
|
485
|
-
extract(epoch from from_ts)*1000 AS from_ms,
|
|
486
|
-
extract(epoch from to_ts)*1000 AS to_ms,
|
|
487
|
-
rows_count,
|
|
488
|
-
extract(epoch from checked_at)*1000 AS checked_at_ms
|
|
489
|
-
FROM market_context_backfill_coverage
|
|
490
|
-
WHERE source = $1
|
|
491
|
-
AND scope = ANY($2)
|
|
492
|
-
AND interval = $3
|
|
493
|
-
AND from_ts >= to_timestamp($4/1000.0)
|
|
494
|
-
AND to_ts <= to_timestamp($5/1000.0)
|
|
495
|
-
`,
|
|
496
|
-
[source, scopes, interval, params.fromMs, params.toMs]
|
|
497
|
-
);
|
|
498
|
-
return res.rows.map((row) => {
|
|
499
|
-
const checkedAtMs = Number(row.checked_at_ms);
|
|
500
|
-
return {
|
|
501
|
-
source: String(row.source).toLowerCase(),
|
|
502
|
-
scope: String(row.scope).toLowerCase(),
|
|
503
|
-
interval: String(row.interval).toLowerCase(),
|
|
504
|
-
fromMs: Number(row.from_ms),
|
|
505
|
-
toMs: Number(row.to_ms),
|
|
506
|
-
rowsCount: Number(row.rows_count ?? 0),
|
|
507
|
-
...Number.isFinite(checkedAtMs) ? { checkedAtMs } : {}
|
|
508
|
-
};
|
|
509
|
-
});
|
|
510
|
-
}
|
|
511
|
-
async function upsertMarketContextBackfillCoverage(rows) {
|
|
512
|
-
const normalizedRows = rows.map((row) => ({
|
|
513
|
-
source: String(row.source || "").trim().toLowerCase(),
|
|
514
|
-
scope: String(row.scope || "").trim().toLowerCase(),
|
|
515
|
-
interval: String(row.interval || "").trim().toLowerCase(),
|
|
516
|
-
fromMs: Math.trunc(row.fromMs),
|
|
517
|
-
toMs: Math.trunc(row.toMs),
|
|
518
|
-
rowsCount: Math.max(0, Math.trunc(row.rowsCount))
|
|
519
|
-
})).filter(
|
|
520
|
-
(row) => row.source && row.scope && row.interval && Number.isFinite(row.fromMs) && Number.isFinite(row.toMs) && row.toMs >= row.fromMs
|
|
521
|
-
);
|
|
522
|
-
if (!normalizedRows.length) return;
|
|
523
|
-
await ensureBinanceMarketSchema();
|
|
524
|
-
const pool = getPool();
|
|
525
|
-
const cols = [
|
|
526
|
-
"source",
|
|
527
|
-
"scope",
|
|
528
|
-
"interval",
|
|
529
|
-
"from_ts",
|
|
530
|
-
"to_ts",
|
|
531
|
-
"rows_count"
|
|
532
|
-
];
|
|
533
|
-
const valuesSql = normalizedRows.map(
|
|
534
|
-
(_, i) => `(${cols.map((__, j) => `$${i * cols.length + j + 1}`).join(",")})`
|
|
535
|
-
).join(",");
|
|
536
|
-
const flat = normalizedRows.flatMap((row) => [
|
|
537
|
-
row.source,
|
|
538
|
-
row.scope,
|
|
539
|
-
row.interval,
|
|
540
|
-
new Date(row.fromMs),
|
|
541
|
-
new Date(row.toMs),
|
|
542
|
-
row.rowsCount
|
|
543
|
-
]);
|
|
544
|
-
await pool.query(
|
|
545
|
-
`
|
|
546
|
-
INSERT INTO market_context_backfill_coverage (${cols.join(",")})
|
|
547
|
-
VALUES ${valuesSql}
|
|
548
|
-
ON CONFLICT (source, scope, interval, from_ts, to_ts) DO UPDATE SET
|
|
549
|
-
rows_count = EXCLUDED.rows_count,
|
|
550
|
-
checked_at = now()
|
|
551
|
-
`,
|
|
552
|
-
flat
|
|
553
|
-
);
|
|
554
|
-
}
|
|
555
|
-
async function getLatestMarketTradeFlow(params) {
|
|
556
|
-
await prepareMarketContextSchemaForRead("binance");
|
|
557
|
-
const res = await queryMarketContext(
|
|
558
|
-
`
|
|
559
|
-
SELECT
|
|
560
|
-
symbol,
|
|
561
|
-
interval,
|
|
562
|
-
ts,
|
|
563
|
-
trades::int AS trades,
|
|
564
|
-
buy_base_volume AS "buyBaseVolume",
|
|
565
|
-
sell_base_volume AS "sellBaseVolume",
|
|
566
|
-
buy_quote_volume AS "buyQuoteVolume",
|
|
567
|
-
sell_quote_volume AS "sellQuoteVolume",
|
|
568
|
-
net_base_delta AS "netBaseDelta",
|
|
569
|
-
net_quote_delta AS "netQuoteDelta",
|
|
570
|
-
buy_pressure_pct AS "buyPressurePct",
|
|
571
|
-
source
|
|
572
|
-
FROM market_trade_flow
|
|
573
|
-
WHERE symbol = $1
|
|
574
|
-
AND interval = $2
|
|
575
|
-
AND ts <= to_timestamp($3/1000.0)
|
|
576
|
-
ORDER BY ts DESC
|
|
577
|
-
LIMIT 1
|
|
578
|
-
`,
|
|
579
|
-
[params.symbol.toUpperCase(), params.interval, params.atMs],
|
|
580
|
-
params
|
|
581
|
-
);
|
|
582
|
-
const row = res.rows[0];
|
|
583
|
-
if (!row) return null;
|
|
584
|
-
const ageMs = toMarketFeatureAge(row.ts, params.atMs);
|
|
585
|
-
return {
|
|
586
|
-
...row,
|
|
587
|
-
ageMs,
|
|
588
|
-
stale: ageMs == null || params.maxAgeMs != null && ageMs > params.maxAgeMs
|
|
589
|
-
};
|
|
590
|
-
}
|
|
591
|
-
async function getLatestMarketBreadth(params) {
|
|
592
|
-
await prepareMarketContextSchemaForRead("binance");
|
|
593
|
-
const res = await queryMarketContext(
|
|
594
|
-
`
|
|
595
|
-
SELECT
|
|
596
|
-
universe,
|
|
597
|
-
interval,
|
|
598
|
-
ts,
|
|
599
|
-
symbols_count::int AS "symbolsCount",
|
|
600
|
-
advancers::int AS advancers,
|
|
601
|
-
decliners::int AS decliners,
|
|
602
|
-
unchanged::int AS unchanged,
|
|
603
|
-
advance_decline_ratio AS "advanceDeclineRatio",
|
|
604
|
-
pct_above_ma20 AS "pctAboveMa20",
|
|
605
|
-
pct_above_ma50 AS "pctAboveMa50",
|
|
606
|
-
equal_weighted_return AS "equalWeightedReturn",
|
|
607
|
-
volume_weighted_return AS "volumeWeightedReturn",
|
|
608
|
-
dispersion,
|
|
609
|
-
btc_return_1h AS "btcReturn1h",
|
|
610
|
-
btc_return_4h AS "btcReturn4h",
|
|
611
|
-
btc_return_24h AS "btcReturn24h",
|
|
612
|
-
alt_basket_return_1h AS "altBasketReturn1h",
|
|
613
|
-
alt_basket_return_4h AS "altBasketReturn4h",
|
|
614
|
-
alt_basket_return_24h AS "altBasketReturn24h",
|
|
615
|
-
btc_vs_alt_return_1h AS "btcVsAltReturn1h",
|
|
616
|
-
btc_vs_alt_return_4h AS "btcVsAltReturn4h",
|
|
617
|
-
btc_vs_alt_return_24h AS "btcVsAltReturn24h",
|
|
618
|
-
btc_turnover_share_1h AS "btcTurnoverShare1h",
|
|
619
|
-
btc_turnover_share_24h AS "btcTurnoverShare24h",
|
|
620
|
-
btc_turnover_share_change_24h AS "btcTurnoverShareChange24h",
|
|
621
|
-
alt_vol_to_btc_vol_24h AS "altVolToBtcVol24h",
|
|
622
|
-
alt_dispersion_24h AS "altDispersion24h",
|
|
623
|
-
btc_alt_regime AS "btcAltRegime",
|
|
624
|
-
source
|
|
625
|
-
FROM market_breadth
|
|
626
|
-
WHERE universe = $1
|
|
627
|
-
AND interval = $2
|
|
628
|
-
AND ts <= to_timestamp($3/1000.0)
|
|
629
|
-
ORDER BY ts DESC
|
|
630
|
-
LIMIT 1
|
|
631
|
-
`,
|
|
632
|
-
[params.universe, params.interval, params.atMs],
|
|
633
|
-
params
|
|
634
|
-
);
|
|
635
|
-
const row = res.rows[0];
|
|
636
|
-
if (!row) return null;
|
|
637
|
-
const ageMs = toMarketFeatureAge(row.ts, params.atMs);
|
|
638
|
-
return {
|
|
639
|
-
...row,
|
|
640
|
-
ageMs,
|
|
641
|
-
stale: ageMs == null || params.maxAgeMs != null && ageMs > params.maxAgeMs
|
|
642
|
-
};
|
|
643
|
-
}
|
|
644
|
-
async function getLatestMarketGlobalContext(params) {
|
|
645
|
-
await prepareMarketContextSchemaForRead("coinmarketcap");
|
|
646
|
-
const source = params.source ?? "coinmarketcap_global";
|
|
647
|
-
const res = await queryMarketContext(
|
|
648
|
-
`
|
|
649
|
-
SELECT
|
|
650
|
-
source,
|
|
651
|
-
ts,
|
|
652
|
-
updated_at_ts AS "updatedAt",
|
|
653
|
-
active_cryptocurrencies::int AS "activeCryptocurrencies",
|
|
654
|
-
active_exchanges::int AS "activeExchanges",
|
|
655
|
-
active_market_pairs::int AS "activeMarketPairs",
|
|
656
|
-
markets::int AS markets,
|
|
657
|
-
total_market_cap_usd AS "totalMarketCapUsd",
|
|
658
|
-
total_volume_usd AS "totalVolumeUsd",
|
|
659
|
-
total_volume_reported_usd AS "totalVolumeReportedUsd",
|
|
660
|
-
btc_dominance_pct AS "btcDominancePct",
|
|
661
|
-
eth_dominance_pct AS "ethDominancePct",
|
|
662
|
-
alt_market_cap_usd AS "altMarketCapUsd",
|
|
663
|
-
alt_volume_usd AS "altVolumeUsd",
|
|
664
|
-
alt_volume_reported_usd AS "altVolumeReportedUsd",
|
|
665
|
-
btc_to_alt_market_cap_ratio AS "btcToAltMarketCapRatio",
|
|
666
|
-
market_cap_change_pct_24h_usd AS "marketCapChangePct24hUsd"
|
|
667
|
-
FROM market_global_context
|
|
668
|
-
WHERE source = $1
|
|
669
|
-
AND ts + CASE
|
|
670
|
-
WHEN source = 'coinmarketcap_global' THEN interval '1 day'
|
|
671
|
-
ELSE interval '0 seconds'
|
|
672
|
-
END <= to_timestamp($2/1000.0)
|
|
673
|
-
ORDER BY ts DESC
|
|
674
|
-
LIMIT 1
|
|
675
|
-
`,
|
|
676
|
-
[source, params.atMs],
|
|
677
|
-
params
|
|
678
|
-
);
|
|
679
|
-
const row = res.rows[0];
|
|
680
|
-
if (!row) return null;
|
|
681
|
-
const previousRes = await queryMarketContext(
|
|
682
|
-
`
|
|
683
|
-
SELECT
|
|
684
|
-
btc_dominance_pct AS "btcDominancePct",
|
|
685
|
-
eth_dominance_pct AS "ethDominancePct",
|
|
686
|
-
alt_market_cap_usd AS "altMarketCapUsd",
|
|
687
|
-
alt_volume_usd AS "altVolumeUsd"
|
|
688
|
-
FROM market_global_context
|
|
689
|
-
WHERE source = $1
|
|
690
|
-
AND ts <= $2::timestamptz - interval '24 hours'
|
|
691
|
-
ORDER BY ts DESC
|
|
692
|
-
LIMIT 1
|
|
693
|
-
`,
|
|
694
|
-
[source, row.ts],
|
|
695
|
-
params
|
|
696
|
-
);
|
|
697
|
-
const previousDominance = previousRes.rows[0]?.btcDominancePct == null ? null : Number(previousRes.rows[0].btcDominancePct);
|
|
698
|
-
const previousEthDominance = previousRes.rows[0]?.ethDominancePct == null ? null : Number(previousRes.rows[0].ethDominancePct);
|
|
699
|
-
const previousAltMarketCap = previousRes.rows[0]?.altMarketCapUsd == null ? null : Number(previousRes.rows[0].altMarketCapUsd);
|
|
700
|
-
const previousAltVolume = previousRes.rows[0]?.altVolumeUsd == null ? null : Number(previousRes.rows[0].altVolumeUsd);
|
|
701
|
-
const currentDominance = row.btcDominancePct == null ? null : Number(row.btcDominancePct);
|
|
702
|
-
const currentEthDominance = row.ethDominancePct == null ? null : Number(row.ethDominancePct);
|
|
703
|
-
const currentAltMarketCap = row.altMarketCapUsd == null ? null : Number(row.altMarketCapUsd);
|
|
704
|
-
const currentAltVolume = row.altVolumeUsd == null ? null : Number(row.altVolumeUsd);
|
|
705
|
-
const ageMs = toMarketFeatureAge(row.ts, params.atMs);
|
|
706
|
-
return {
|
|
707
|
-
...row,
|
|
708
|
-
ageMs,
|
|
709
|
-
stale: ageMs == null || params.maxAgeMs != null && ageMs > params.maxAgeMs,
|
|
710
|
-
btcDominanceChange24hPct: currentDominance != null && previousDominance != null ? currentDominance - previousDominance : null,
|
|
711
|
-
ethDominanceChange24hPct: currentEthDominance != null && previousEthDominance != null ? currentEthDominance - previousEthDominance : null,
|
|
712
|
-
altMarketCapChange24hPct: currentAltMarketCap != null && previousAltMarketCap != null && previousAltMarketCap > 0 ? (currentAltMarketCap - previousAltMarketCap) / previousAltMarketCap : null,
|
|
713
|
-
altVolumeChange24hPct: currentAltVolume != null && previousAltVolume != null && previousAltVolume > 0 ? (currentAltVolume - previousAltVolume) / previousAltVolume : null
|
|
714
|
-
};
|
|
715
|
-
}
|
|
716
|
-
async function getMarketGlobalContextCoverage(params) {
|
|
717
|
-
await ensureBinanceMarketSchema();
|
|
718
|
-
const pool = getPool();
|
|
719
|
-
const res = await pool.query(
|
|
720
|
-
`
|
|
721
|
-
SELECT
|
|
722
|
-
extract(epoch from MIN(ts))*1000 AS first_ms,
|
|
723
|
-
extract(epoch from MAX(ts))*1000 AS last_ms,
|
|
724
|
-
COUNT(*)::int AS rows
|
|
725
|
-
FROM market_global_context
|
|
726
|
-
WHERE source = $1
|
|
727
|
-
AND ts >= to_timestamp($2/1000.0)
|
|
728
|
-
AND ts <= to_timestamp($3/1000.0)
|
|
729
|
-
`,
|
|
730
|
-
[params.source, params.startMs, params.endMs]
|
|
731
|
-
);
|
|
732
|
-
const row = res.rows[0];
|
|
733
|
-
const rows = Number(row?.rows ?? 0);
|
|
734
|
-
const firstMs = Number(row?.first_ms);
|
|
735
|
-
const lastMs = Number(row?.last_ms);
|
|
736
|
-
if (!rows || !Number.isFinite(firstMs) || !Number.isFinite(lastMs)) {
|
|
737
|
-
return null;
|
|
738
|
-
}
|
|
739
|
-
return { firstMs, lastMs, rows };
|
|
740
|
-
}
|
|
741
|
-
async function getMarketReferenceAssetContextCoverage(params) {
|
|
742
|
-
const symbols = [
|
|
743
|
-
...new Set(
|
|
744
|
-
params.symbols.map((symbol) => symbol.trim().toUpperCase()).filter(Boolean)
|
|
745
|
-
)
|
|
746
|
-
];
|
|
747
|
-
const coverage = /* @__PURE__ */ new Map();
|
|
748
|
-
if (!symbols.length) return coverage;
|
|
749
|
-
await ensureBinanceMarketSchema();
|
|
750
|
-
const pool = getPool();
|
|
751
|
-
const res = await pool.query(
|
|
752
|
-
`
|
|
753
|
-
SELECT
|
|
754
|
-
symbol,
|
|
755
|
-
extract(epoch from MIN(ts))*1000 AS first_ms,
|
|
756
|
-
extract(epoch from MAX(ts))*1000 AS last_ms,
|
|
757
|
-
COUNT(*)::int AS rows
|
|
758
|
-
FROM market_reference_asset_context
|
|
759
|
-
WHERE source = $1
|
|
760
|
-
AND symbol = ANY($2)
|
|
761
|
-
AND interval = $3
|
|
762
|
-
AND ts >= to_timestamp($4/1000.0)
|
|
763
|
-
AND ts <= to_timestamp($5/1000.0)
|
|
764
|
-
GROUP BY symbol
|
|
765
|
-
`,
|
|
766
|
-
[params.source, symbols, params.interval, params.startMs, params.endMs]
|
|
767
|
-
);
|
|
768
|
-
for (const row of res.rows) {
|
|
769
|
-
const firstMs = Number(row.first_ms);
|
|
770
|
-
const lastMs = Number(row.last_ms);
|
|
771
|
-
const rows = Number(row.rows);
|
|
772
|
-
if (Number.isFinite(firstMs) && Number.isFinite(lastMs) && rows > 0) {
|
|
773
|
-
coverage.set(row.symbol.toUpperCase(), { firstMs, lastMs, rows });
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
return coverage;
|
|
777
|
-
}
|
|
778
|
-
async function getLatestMarketReferenceAssetContexts(params) {
|
|
779
|
-
const source = params.source ?? "coinmarketcap_reference_asset";
|
|
780
|
-
const interval = params.interval ?? "1d";
|
|
781
|
-
const symbols = [
|
|
782
|
-
...new Set(
|
|
783
|
-
params.symbols.map((symbol) => symbol.trim().toUpperCase()).filter(Boolean)
|
|
784
|
-
)
|
|
785
|
-
];
|
|
786
|
-
const rows = /* @__PURE__ */ new Map();
|
|
787
|
-
if (!symbols.length) return rows;
|
|
788
|
-
await prepareMarketContextSchemaForRead("coinmarketcap");
|
|
789
|
-
const res = await queryMarketContext(
|
|
790
|
-
`
|
|
791
|
-
SELECT DISTINCT ON (symbol)
|
|
792
|
-
source,
|
|
793
|
-
symbol,
|
|
794
|
-
cmc_id AS "cmcId",
|
|
795
|
-
interval,
|
|
796
|
-
ts,
|
|
797
|
-
open_usd AS "openUsd",
|
|
798
|
-
high_usd AS "highUsd",
|
|
799
|
-
low_usd AS "lowUsd",
|
|
800
|
-
close_usd AS "closeUsd",
|
|
801
|
-
volume_usd AS "volumeUsd",
|
|
802
|
-
market_cap_usd AS "marketCapUsd"
|
|
803
|
-
FROM market_reference_asset_context
|
|
804
|
-
WHERE source = $1
|
|
805
|
-
AND symbol = ANY($2)
|
|
806
|
-
AND interval = $3
|
|
807
|
-
AND ts + CASE interval
|
|
808
|
-
WHEN '1d' THEN interval '1 day'
|
|
809
|
-
WHEN '1h' THEN interval '1 hour'
|
|
810
|
-
ELSE interval '0 seconds'
|
|
811
|
-
END <= to_timestamp($4/1000.0)
|
|
812
|
-
ORDER BY symbol ASC, ts DESC
|
|
813
|
-
`,
|
|
814
|
-
[source, symbols, interval, params.atMs],
|
|
815
|
-
params
|
|
816
|
-
);
|
|
817
|
-
for (const row of res.rows) {
|
|
818
|
-
const ageMs = toMarketFeatureAge(row.ts, params.atMs);
|
|
819
|
-
rows.set(row.symbol.toUpperCase(), {
|
|
820
|
-
...row,
|
|
821
|
-
ageMs,
|
|
822
|
-
stale: ageMs == null || params.maxAgeMs != null && ageMs > params.maxAgeMs
|
|
823
|
-
});
|
|
824
|
-
}
|
|
825
|
-
return rows;
|
|
826
|
-
}
|
|
827
|
-
async function getLatestMarketCmcExchangeLiquidityContext(params) {
|
|
828
|
-
await prepareMarketContextSchemaForRead("coinmarketcap");
|
|
829
|
-
const source = params.source ?? "coinmarketcap_exchange_liquidity";
|
|
830
|
-
const interval = params.interval ?? "1d";
|
|
831
|
-
const res = await queryMarketContext(
|
|
832
|
-
`
|
|
833
|
-
SELECT
|
|
834
|
-
source,
|
|
835
|
-
interval,
|
|
836
|
-
ts,
|
|
837
|
-
exchanges_count::int AS "exchangesCount",
|
|
838
|
-
total_volume_usd AS "totalVolumeUsd",
|
|
839
|
-
binance_volume_usd AS "binanceVolumeUsd",
|
|
840
|
-
binance_volume_share AS "binanceVolumeShare",
|
|
841
|
-
top_exchange_volume_share AS "topExchangeVolumeShare",
|
|
842
|
-
liquidity_regime AS "liquidityRegime"
|
|
843
|
-
FROM market_cmc_exchange_liquidity_context
|
|
844
|
-
WHERE source = $1
|
|
845
|
-
AND interval = $2
|
|
846
|
-
AND ts + CASE interval
|
|
847
|
-
WHEN '1d' THEN interval '1 day'
|
|
848
|
-
WHEN '1h' THEN interval '1 hour'
|
|
849
|
-
ELSE interval '0 seconds'
|
|
850
|
-
END <= to_timestamp($3/1000.0)
|
|
851
|
-
ORDER BY ts DESC
|
|
852
|
-
LIMIT 1
|
|
853
|
-
`,
|
|
854
|
-
[source, interval, params.atMs],
|
|
855
|
-
params
|
|
856
|
-
);
|
|
857
|
-
const row = res.rows[0];
|
|
858
|
-
if (!row) return null;
|
|
859
|
-
const previousRes = await queryMarketContext(
|
|
860
|
-
`
|
|
861
|
-
SELECT total_volume_usd AS "totalVolumeUsd"
|
|
862
|
-
FROM market_cmc_exchange_liquidity_context
|
|
863
|
-
WHERE source = $1
|
|
864
|
-
AND interval = $2
|
|
865
|
-
AND ts <= $3::timestamptz - interval '24 hours'
|
|
866
|
-
ORDER BY ts DESC
|
|
867
|
-
LIMIT 1
|
|
868
|
-
`,
|
|
869
|
-
[source, interval, row.ts],
|
|
870
|
-
params
|
|
871
|
-
);
|
|
872
|
-
const currentTotal = row.totalVolumeUsd == null ? null : Number(row.totalVolumeUsd);
|
|
873
|
-
const previousTotal = previousRes.rows[0]?.totalVolumeUsd == null ? null : Number(previousRes.rows[0].totalVolumeUsd);
|
|
874
|
-
const ageMs = toMarketFeatureAge(row.ts, params.atMs);
|
|
875
|
-
return {
|
|
876
|
-
...row,
|
|
877
|
-
ageMs,
|
|
878
|
-
stale: ageMs == null || params.maxAgeMs != null && ageMs > params.maxAgeMs,
|
|
879
|
-
totalVolumeChange24hPct: currentTotal != null && previousTotal != null && previousTotal > 0 ? (currentTotal - previousTotal) / previousTotal : null
|
|
880
|
-
};
|
|
881
|
-
}
|
|
882
|
-
async function getLatestMarketCmcIndexContexts(params) {
|
|
883
|
-
const source = params.source ?? "coinmarketcap_index";
|
|
884
|
-
const interval = params.interval ?? "1d";
|
|
885
|
-
const indexSlugs = [
|
|
886
|
-
...new Set(
|
|
887
|
-
params.indexSlugs.map((slug) => slug.trim().toLowerCase()).filter(
|
|
888
|
-
(slug) => ["cmc100", "cmc20"].includes(slug)
|
|
889
|
-
)
|
|
890
|
-
)
|
|
891
|
-
];
|
|
892
|
-
const rows = /* @__PURE__ */ new Map();
|
|
893
|
-
if (!indexSlugs.length) return rows;
|
|
894
|
-
await prepareMarketContextSchemaForRead("coinmarketcap");
|
|
895
|
-
const res = await queryMarketContext(
|
|
896
|
-
`
|
|
897
|
-
SELECT DISTINCT ON (index_slug)
|
|
898
|
-
source,
|
|
899
|
-
index_slug AS "indexSlug",
|
|
900
|
-
interval,
|
|
901
|
-
ts,
|
|
902
|
-
value,
|
|
903
|
-
constituents_count::int AS "constituentsCount",
|
|
904
|
-
top_constituent_symbol AS "topConstituentSymbol",
|
|
905
|
-
top_constituent_weight_pct AS "topConstituentWeightPct",
|
|
906
|
-
constituents
|
|
907
|
-
FROM market_cmc_index_context
|
|
908
|
-
WHERE source = $1
|
|
909
|
-
AND index_slug = ANY($2)
|
|
910
|
-
AND interval = $3
|
|
911
|
-
AND ts + CASE interval
|
|
912
|
-
WHEN '1d' THEN interval '1 day'
|
|
913
|
-
WHEN '1h' THEN interval '1 hour'
|
|
914
|
-
ELSE interval '0 seconds'
|
|
915
|
-
END <= to_timestamp($4/1000.0)
|
|
916
|
-
ORDER BY index_slug ASC, ts DESC
|
|
917
|
-
`,
|
|
918
|
-
[source, indexSlugs, interval, params.atMs],
|
|
919
|
-
params
|
|
920
|
-
);
|
|
921
|
-
for (const row of res.rows) {
|
|
922
|
-
const previousRes = await queryMarketContext(
|
|
923
|
-
`
|
|
924
|
-
SELECT value
|
|
925
|
-
FROM market_cmc_index_context
|
|
926
|
-
WHERE source = $1
|
|
927
|
-
AND index_slug = $2
|
|
928
|
-
AND interval = $3
|
|
929
|
-
AND ts <= $4::timestamptz - interval '24 hours'
|
|
930
|
-
ORDER BY ts DESC
|
|
931
|
-
LIMIT 1
|
|
932
|
-
`,
|
|
933
|
-
[source, row.indexSlug, interval, row.ts],
|
|
934
|
-
params
|
|
935
|
-
);
|
|
936
|
-
const currentValue = row.value == null ? null : Number(row.value);
|
|
937
|
-
const previousValue = previousRes.rows[0]?.value == null ? null : Number(previousRes.rows[0].value);
|
|
938
|
-
const ageMs = toMarketFeatureAge(row.ts, params.atMs);
|
|
939
|
-
rows.set(row.indexSlug, {
|
|
940
|
-
...row,
|
|
941
|
-
ageMs,
|
|
942
|
-
stale: ageMs == null || params.maxAgeMs != null && ageMs > params.maxAgeMs,
|
|
943
|
-
valueChange24hPct: currentValue != null && previousValue != null && previousValue > 0 ? (currentValue - previousValue) / previousValue : null
|
|
944
|
-
});
|
|
945
|
-
}
|
|
946
|
-
return rows;
|
|
947
|
-
}
|
|
948
|
-
async function getLatestMarketCmcFearGreedContext(params) {
|
|
949
|
-
await prepareMarketContextSchemaForRead("coinmarketcap");
|
|
950
|
-
const source = params.source ?? "coinmarketcap_fear_greed";
|
|
951
|
-
const interval = params.interval ?? "1d";
|
|
952
|
-
const res = await queryMarketContext(
|
|
953
|
-
`
|
|
954
|
-
SELECT
|
|
955
|
-
source,
|
|
956
|
-
interval,
|
|
957
|
-
ts,
|
|
958
|
-
value::int AS value,
|
|
959
|
-
classification,
|
|
960
|
-
sentiment_regime AS "sentimentRegime"
|
|
961
|
-
FROM market_cmc_fear_greed_context
|
|
962
|
-
WHERE source = $1
|
|
963
|
-
AND interval = $2
|
|
964
|
-
AND ts + CASE interval
|
|
965
|
-
WHEN '1d' THEN interval '1 day'
|
|
966
|
-
WHEN '1h' THEN interval '1 hour'
|
|
967
|
-
ELSE interval '0 seconds'
|
|
968
|
-
END <= to_timestamp($3/1000.0)
|
|
969
|
-
ORDER BY ts DESC
|
|
970
|
-
LIMIT 1
|
|
971
|
-
`,
|
|
972
|
-
[source, interval, params.atMs],
|
|
973
|
-
params
|
|
974
|
-
);
|
|
975
|
-
const row = res.rows[0];
|
|
976
|
-
if (!row) return null;
|
|
977
|
-
const previousRes = await queryMarketContext(
|
|
978
|
-
`
|
|
979
|
-
SELECT
|
|
980
|
-
value::int AS value,
|
|
981
|
-
'24h' AS bucket
|
|
982
|
-
FROM market_cmc_fear_greed_context
|
|
983
|
-
WHERE source = $1
|
|
984
|
-
AND interval = $2
|
|
985
|
-
AND ts <= $3::timestamptz - interval '24 hours'
|
|
986
|
-
ORDER BY ts DESC
|
|
987
|
-
LIMIT 1
|
|
988
|
-
`,
|
|
989
|
-
[source, interval, row.ts],
|
|
990
|
-
params
|
|
991
|
-
);
|
|
992
|
-
const previous7dRes = await queryMarketContext(
|
|
993
|
-
`
|
|
994
|
-
SELECT value::int AS value
|
|
995
|
-
FROM market_cmc_fear_greed_context
|
|
996
|
-
WHERE source = $1
|
|
997
|
-
AND interval = $2
|
|
998
|
-
AND ts <= $3::timestamptz - interval '7 days'
|
|
999
|
-
ORDER BY ts DESC
|
|
1000
|
-
LIMIT 1
|
|
1001
|
-
`,
|
|
1002
|
-
[source, interval, row.ts],
|
|
1003
|
-
params
|
|
1004
|
-
);
|
|
1005
|
-
const previousValue = previousRes.rows[0]?.value == null ? null : Number(previousRes.rows[0].value);
|
|
1006
|
-
const previous7dValue = previous7dRes.rows[0]?.value == null ? null : Number(previous7dRes.rows[0].value);
|
|
1007
|
-
const ageMs = toMarketFeatureAge(row.ts, params.atMs);
|
|
1008
|
-
return {
|
|
1009
|
-
...row,
|
|
1010
|
-
ageMs,
|
|
1011
|
-
stale: ageMs == null || params.maxAgeMs != null && ageMs > params.maxAgeMs,
|
|
1012
|
-
valueChange24h: previousValue == null ? null : row.value - previousValue,
|
|
1013
|
-
valueChange7d: previous7dValue == null ? null : row.value - previous7dValue
|
|
1014
|
-
};
|
|
1015
|
-
}
|
|
1016
|
-
async function getMarketCmcFearGreedContextCoverage(params) {
|
|
1017
|
-
await ensureBinanceMarketSchema();
|
|
1018
|
-
const pool = getPool();
|
|
1019
|
-
const res = await pool.query(
|
|
1020
|
-
`
|
|
1021
|
-
SELECT
|
|
1022
|
-
extract(epoch from MIN(ts))*1000 AS first_ms,
|
|
1023
|
-
extract(epoch from MAX(ts))*1000 AS last_ms,
|
|
1024
|
-
COUNT(*)::int AS rows
|
|
1025
|
-
FROM market_cmc_fear_greed_context
|
|
1026
|
-
WHERE source = $1
|
|
1027
|
-
AND interval = $2
|
|
1028
|
-
AND ts >= to_timestamp($3/1000.0)
|
|
1029
|
-
AND ts <= to_timestamp($4/1000.0)
|
|
1030
|
-
`,
|
|
1031
|
-
[params.source, params.interval, params.startMs, params.endMs]
|
|
1032
|
-
);
|
|
1033
|
-
const rows = Number(res.rows[0]?.rows ?? 0);
|
|
1034
|
-
const firstMs = Number(res.rows[0]?.first_ms);
|
|
1035
|
-
const lastMs = Number(res.rows[0]?.last_ms);
|
|
1036
|
-
if (!rows || !Number.isFinite(firstMs) || !Number.isFinite(lastMs)) {
|
|
1037
|
-
return null;
|
|
1038
|
-
}
|
|
1039
|
-
return { firstMs, lastMs, rows };
|
|
1040
|
-
}
|
|
1041
|
-
async function getMarketCmcExchangeLiquidityContextCoverage(params) {
|
|
1042
|
-
await ensureBinanceMarketSchema();
|
|
1043
|
-
const pool = getPool();
|
|
1044
|
-
const res = await pool.query(
|
|
1045
|
-
`
|
|
1046
|
-
SELECT
|
|
1047
|
-
extract(epoch from MIN(ts))*1000 AS first_ms,
|
|
1048
|
-
extract(epoch from MAX(ts))*1000 AS last_ms,
|
|
1049
|
-
COUNT(*)::int AS rows
|
|
1050
|
-
FROM market_cmc_exchange_liquidity_context
|
|
1051
|
-
WHERE source = $1
|
|
1052
|
-
AND interval = $2
|
|
1053
|
-
AND ts >= to_timestamp($3/1000.0)
|
|
1054
|
-
AND ts <= to_timestamp($4/1000.0)
|
|
1055
|
-
`,
|
|
1056
|
-
[params.source, params.interval, params.startMs, params.endMs]
|
|
1057
|
-
);
|
|
1058
|
-
const rows = Number(res.rows[0]?.rows ?? 0);
|
|
1059
|
-
const firstMs = Number(res.rows[0]?.first_ms);
|
|
1060
|
-
const lastMs = Number(res.rows[0]?.last_ms);
|
|
1061
|
-
if (!rows || !Number.isFinite(firstMs) || !Number.isFinite(lastMs)) {
|
|
1062
|
-
return null;
|
|
1063
|
-
}
|
|
1064
|
-
return { firstMs, lastMs, rows };
|
|
1065
|
-
}
|
|
1066
|
-
async function getMarketCmcIndexContextCoverage(params) {
|
|
1067
|
-
const indexSlugs = [
|
|
1068
|
-
...new Set(
|
|
1069
|
-
params.indexSlugs.map((slug) => slug.trim().toLowerCase()).filter(
|
|
1070
|
-
(slug) => ["cmc100", "cmc20"].includes(slug)
|
|
1071
|
-
)
|
|
1072
|
-
)
|
|
1073
|
-
];
|
|
1074
|
-
const coverage = /* @__PURE__ */ new Map();
|
|
1075
|
-
if (!indexSlugs.length) return coverage;
|
|
1076
|
-
await ensureBinanceMarketSchema();
|
|
1077
|
-
const pool = getPool();
|
|
1078
|
-
const res = await pool.query(
|
|
1079
|
-
`
|
|
1080
|
-
SELECT
|
|
1081
|
-
index_slug,
|
|
1082
|
-
extract(epoch from MIN(ts))*1000 AS first_ms,
|
|
1083
|
-
extract(epoch from MAX(ts))*1000 AS last_ms,
|
|
1084
|
-
COUNT(*)::int AS rows
|
|
1085
|
-
FROM market_cmc_index_context
|
|
1086
|
-
WHERE source = $1
|
|
1087
|
-
AND index_slug = ANY($2)
|
|
1088
|
-
AND interval = $3
|
|
1089
|
-
AND ts >= to_timestamp($4/1000.0)
|
|
1090
|
-
AND ts <= to_timestamp($5/1000.0)
|
|
1091
|
-
GROUP BY index_slug
|
|
1092
|
-
`,
|
|
1093
|
-
[params.source, indexSlugs, params.interval, params.startMs, params.endMs]
|
|
1094
|
-
);
|
|
1095
|
-
for (const row of res.rows) {
|
|
1096
|
-
const indexSlug = row.index_slug;
|
|
1097
|
-
const firstMs = Number(row.first_ms);
|
|
1098
|
-
const lastMs = Number(row.last_ms);
|
|
1099
|
-
const rows = Number(row.rows);
|
|
1100
|
-
if (Number.isFinite(firstMs) && Number.isFinite(lastMs) && rows > 0) {
|
|
1101
|
-
coverage.set(indexSlug, { firstMs, lastMs, rows });
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
return coverage;
|
|
1105
|
-
}
|
|
1106
|
-
async function getMarketTradeFlowCoverage(params) {
|
|
1107
|
-
const symbols = [
|
|
1108
|
-
...new Set(params.symbols.map((item) => item.toUpperCase()))
|
|
1109
|
-
];
|
|
1110
|
-
if (!symbols.length) return /* @__PURE__ */ new Map();
|
|
1111
|
-
await ensureBinanceMarketSchema();
|
|
1112
|
-
const pool = getPool();
|
|
1113
|
-
const res = await pool.query(
|
|
1114
|
-
`
|
|
1115
|
-
SELECT
|
|
1116
|
-
symbol,
|
|
1117
|
-
MIN(ts) AS first_ts,
|
|
1118
|
-
MAX(ts) AS last_ts,
|
|
1119
|
-
COUNT(*)::int AS rows
|
|
1120
|
-
FROM market_trade_flow
|
|
1121
|
-
WHERE symbol = ANY($1)
|
|
1122
|
-
AND interval = $2
|
|
1123
|
-
AND ts >= to_timestamp($3/1000.0)
|
|
1124
|
-
AND ts <= to_timestamp($4/1000.0)
|
|
1125
|
-
GROUP BY symbol
|
|
1126
|
-
`,
|
|
1127
|
-
[symbols, params.interval, params.startMs, params.endMs]
|
|
1128
|
-
);
|
|
1129
|
-
return new Map(
|
|
1130
|
-
res.rows.map((row) => [
|
|
1131
|
-
String(row.symbol).toUpperCase(),
|
|
1132
|
-
{
|
|
1133
|
-
firstMs: new Date(row.first_ts).getTime(),
|
|
1134
|
-
lastMs: new Date(row.last_ts).getTime(),
|
|
1135
|
-
rows: Number(row.rows) || 0
|
|
1136
|
-
}
|
|
1137
|
-
])
|
|
1138
|
-
);
|
|
1139
|
-
}
|
|
1140
|
-
var getTableRowCountIfExists = async (tableName) => {
|
|
1141
|
-
const pool = getPool();
|
|
1142
|
-
const exists = await pool.query("SELECT to_regclass($1) AS name", [
|
|
1143
|
-
tableName
|
|
1144
|
-
]);
|
|
1145
|
-
if (!exists.rows[0]?.name) return null;
|
|
1146
|
-
const count = await pool.query(
|
|
1147
|
-
`SELECT COUNT(*)::int AS rows FROM ${tableName}`
|
|
1148
|
-
);
|
|
1149
|
-
return Number(count.rows[0]?.rows ?? 0);
|
|
1150
|
-
};
|
|
1151
|
-
async function cleanupDeprecatedMarketContext(params = {}) {
|
|
1152
|
-
const apply = Boolean(params.apply);
|
|
1153
|
-
const pool = getPool();
|
|
1154
|
-
const items = [];
|
|
1155
|
-
const cleanupRows = async ({
|
|
1156
|
-
tableName,
|
|
1157
|
-
whereSql,
|
|
1158
|
-
name
|
|
1159
|
-
}) => {
|
|
1160
|
-
const tableRows = await getTableRowCountIfExists(tableName);
|
|
1161
|
-
if (tableRows == null) return;
|
|
1162
|
-
const count = await pool.query(
|
|
1163
|
-
`
|
|
1164
|
-
SELECT COUNT(*)::int AS rows
|
|
1165
|
-
FROM ${tableName}
|
|
1166
|
-
WHERE ${whereSql}
|
|
1167
|
-
`
|
|
1168
|
-
);
|
|
1169
|
-
const rows = Number(count.rows[0]?.rows ?? 0);
|
|
1170
|
-
if (rows <= 0) return;
|
|
1171
|
-
if (apply) {
|
|
1172
|
-
await pool.query(
|
|
1173
|
-
`
|
|
1174
|
-
DELETE FROM ${tableName}
|
|
1175
|
-
WHERE ${whereSql}
|
|
1176
|
-
`
|
|
1177
|
-
);
|
|
1178
|
-
}
|
|
1179
|
-
items.push({
|
|
1180
|
-
kind: "rows",
|
|
1181
|
-
name,
|
|
1182
|
-
rows,
|
|
1183
|
-
action: "delete_rows",
|
|
1184
|
-
applied: apply
|
|
1185
|
-
});
|
|
1186
|
-
};
|
|
1187
|
-
for (const tableName of ["market_order_book_depth", "onchain_flow_context"]) {
|
|
1188
|
-
const rows = await getTableRowCountIfExists(tableName);
|
|
1189
|
-
if (rows == null) continue;
|
|
1190
|
-
if (apply) {
|
|
1191
|
-
await pool.query(`DROP TABLE IF EXISTS ${tableName}`);
|
|
1192
|
-
}
|
|
1193
|
-
items.push({
|
|
1194
|
-
kind: "table",
|
|
1195
|
-
name: tableName,
|
|
1196
|
-
rows,
|
|
1197
|
-
action: "drop_table",
|
|
1198
|
-
applied: apply
|
|
1199
|
-
});
|
|
1200
|
-
}
|
|
1201
|
-
await cleanupRows({
|
|
1202
|
-
tableName: "market_global_context",
|
|
1203
|
-
whereSql: "source = 'coingecko_global'",
|
|
1204
|
-
name: "market_global_context/source=coingecko_global"
|
|
1205
|
-
});
|
|
1206
|
-
await cleanupRows({
|
|
1207
|
-
tableName: "market_global_context",
|
|
1208
|
-
whereSql: "source = 'coinmarketcap_global_hourly'",
|
|
1209
|
-
name: "market_global_context/source=coinmarketcap_global_hourly"
|
|
1210
|
-
});
|
|
1211
|
-
await cleanupRows({
|
|
1212
|
-
tableName: "market_reference_asset_context",
|
|
1213
|
-
whereSql: "source = 'coinmarketcap_reference_asset' AND interval = '1h'",
|
|
1214
|
-
name: "market_reference_asset_context/source=coinmarketcap_reference_asset/interval=1h"
|
|
1215
|
-
});
|
|
1216
|
-
await cleanupRows({
|
|
1217
|
-
tableName: "market_cmc_breadth_context",
|
|
1218
|
-
whereSql: "source = 'coinmarketcap_market_breadth'",
|
|
1219
|
-
name: "market_cmc_breadth_context/source=coinmarketcap_market_breadth"
|
|
1220
|
-
});
|
|
1221
|
-
await cleanupRows({
|
|
1222
|
-
tableName: "market_context_backfill_coverage",
|
|
1223
|
-
whereSql: "(source IN ('coinmarketcap_global_hourly', 'coinmarketcap_market_breadth') OR (source = 'coinmarketcap_reference_asset' AND interval = '1h'))",
|
|
1224
|
-
name: "market_context_backfill_coverage/deprecated_cmc_sources"
|
|
1225
|
-
});
|
|
1226
|
-
return items;
|
|
1227
|
-
}
|
|
1228
|
-
async function getMarketBreadthCoverage(params) {
|
|
1229
|
-
await ensureBinanceMarketSchema();
|
|
1230
|
-
const pool = getPool();
|
|
1231
|
-
const res = await pool.query(
|
|
1232
|
-
`
|
|
1233
|
-
SELECT
|
|
1234
|
-
MIN(ts) AS first_ts,
|
|
1235
|
-
MAX(ts) AS last_ts,
|
|
1236
|
-
COUNT(*)::int AS rows,
|
|
1237
|
-
COUNT(*) FILTER (
|
|
1238
|
-
WHERE btc_alt_regime IS NOT NULL
|
|
1239
|
-
AND btc_return_24h IS NOT NULL
|
|
1240
|
-
AND alt_basket_return_24h IS NOT NULL
|
|
1241
|
-
)::int AS btc_alt_metrics_rows
|
|
1242
|
-
FROM market_breadth
|
|
1243
|
-
WHERE universe = $1
|
|
1244
|
-
AND interval = $2
|
|
1245
|
-
AND ts >= to_timestamp($3/1000.0)
|
|
1246
|
-
AND ts <= to_timestamp($4/1000.0)
|
|
1247
|
-
`,
|
|
1248
|
-
[params.universe, params.interval, params.startMs, params.endMs]
|
|
1249
|
-
);
|
|
1250
|
-
const row = res.rows[0];
|
|
1251
|
-
if (!row?.first_ts || !row?.last_ts) return null;
|
|
1252
|
-
return {
|
|
1253
|
-
firstMs: new Date(row.first_ts).getTime(),
|
|
1254
|
-
lastMs: new Date(row.last_ts).getTime(),
|
|
1255
|
-
rows: Number(row.rows) || 0,
|
|
1256
|
-
btcAltMetricsRows: Number(row.btc_alt_metrics_rows) || 0
|
|
1257
|
-
};
|
|
1258
|
-
}
|
|
1259
|
-
|
|
1260
|
-
export {
|
|
1261
|
-
upsertMarketTradeFlowRows,
|
|
1262
|
-
upsertMarketBreadthRows,
|
|
1263
|
-
upsertMarketGlobalContextRows,
|
|
1264
|
-
upsertMarketReferenceAssetContextRows,
|
|
1265
|
-
upsertMarketCmcExchangeLiquidityContextRows,
|
|
1266
|
-
upsertMarketCmcFearGreedContextRows,
|
|
1267
|
-
upsertMarketCmcIndexContextRows,
|
|
1268
|
-
getMarketContextBackfillCoverage,
|
|
1269
|
-
upsertMarketContextBackfillCoverage,
|
|
1270
|
-
getLatestMarketTradeFlow,
|
|
1271
|
-
getLatestMarketBreadth,
|
|
1272
|
-
getLatestMarketGlobalContext,
|
|
1273
|
-
getMarketGlobalContextCoverage,
|
|
1274
|
-
getMarketReferenceAssetContextCoverage,
|
|
1275
|
-
getLatestMarketReferenceAssetContexts,
|
|
1276
|
-
getLatestMarketCmcExchangeLiquidityContext,
|
|
1277
|
-
getLatestMarketCmcIndexContexts,
|
|
1278
|
-
getLatestMarketCmcFearGreedContext,
|
|
1279
|
-
getMarketCmcFearGreedContextCoverage,
|
|
1280
|
-
getMarketCmcExchangeLiquidityContextCoverage,
|
|
1281
|
-
getMarketCmcIndexContextCoverage,
|
|
1282
|
-
getMarketTradeFlowCoverage,
|
|
1283
|
-
cleanupDeprecatedMarketContext,
|
|
1284
|
-
getMarketBreadthCoverage
|
|
1285
|
-
};
|