@tradejs/infra 3.1.27 → 3.1.28-beta.251
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-CH5L3BE4.mjs → chunk-C5XG6VVP.mjs} +60 -36
- package/dist/timescale/candles.js +96 -36
- package/dist/timescale/candles.mjs +2 -1
- package/dist/timescale/client.js +39 -0
- package/dist/timescale/client.mjs +2 -1
- package/dist/timescale/marketContext.d.mts +17 -1
- package/dist/timescale/marketContext.d.ts +17 -1
- package/dist/timescale/marketContext.js +87 -0
- package/dist/timescale/marketContext.mjs +85 -0
- package/package.json +2 -2
|
@@ -4,8 +4,22 @@ import {
|
|
|
4
4
|
normalizeCandleProvider,
|
|
5
5
|
normalizeCandleSymbol
|
|
6
6
|
} from "./chunk-SSL36P7G.mjs";
|
|
7
|
+
import {
|
|
8
|
+
logger
|
|
9
|
+
} from "./chunk-LNFUOXDW.mjs";
|
|
7
10
|
|
|
8
11
|
// src/timescale/candles.ts
|
|
12
|
+
var CANDLE_UPSERT_MAX_ATTEMPTS = 3;
|
|
13
|
+
var CANDLE_DEADLOCK_RETRY_DELAY_MS = 25;
|
|
14
|
+
var isPostgresDeadlock = (error) => typeof error === "object" && error !== null && "code" in error && error.code === "40P01";
|
|
15
|
+
var wait = (delayMs) => new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
16
|
+
var normalizeAndSortCandleRows = (rows) => rows.map((row) => ({
|
|
17
|
+
...row,
|
|
18
|
+
provider: normalizeCandleProvider(row.provider),
|
|
19
|
+
symbol: normalizeCandleSymbol(row.symbol)
|
|
20
|
+
})).sort(
|
|
21
|
+
(left, right) => left.provider.localeCompare(right.provider) || left.symbol.localeCompare(right.symbol) || left.interval - right.interval || left.ts.getTime() - right.ts.getTime()
|
|
22
|
+
);
|
|
9
23
|
var toRows = (provider, symbol, interval, data) => {
|
|
10
24
|
const normalizedProvider = normalizeCandleProvider(provider);
|
|
11
25
|
if (!normalizedProvider) {
|
|
@@ -34,6 +48,7 @@ async function upsertCandles(rows) {
|
|
|
34
48
|
if (!rows.length) return;
|
|
35
49
|
await ensureCandlesSchema();
|
|
36
50
|
const pool = getPool();
|
|
51
|
+
const normalizedRows = normalizeAndSortCandleRows(rows);
|
|
37
52
|
const cols = [
|
|
38
53
|
"provider",
|
|
39
54
|
"symbol",
|
|
@@ -51,32 +66,28 @@ async function upsertCandles(rows) {
|
|
|
51
66
|
"taker_sell_quote_volume"
|
|
52
67
|
];
|
|
53
68
|
const maxRows = Math.floor(65535 / cols.length);
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
r.takerSellBaseVolume ?? null,
|
|
77
|
-
r.takerSellQuoteVolume ?? null
|
|
78
|
-
]);
|
|
79
|
-
const sql = `
|
|
69
|
+
for (let offset = 0; offset < normalizedRows.length; offset += maxRows) {
|
|
70
|
+
const chunk = normalizedRows.slice(offset, offset + maxRows);
|
|
71
|
+
const valuesSql = chunk.map(
|
|
72
|
+
(_, i) => `(${cols.map((__, j) => `$${i * cols.length + j + 1}`).join(",")})`
|
|
73
|
+
).join(",");
|
|
74
|
+
const flat = chunk.flatMap((r) => [
|
|
75
|
+
r.provider,
|
|
76
|
+
r.symbol,
|
|
77
|
+
r.interval,
|
|
78
|
+
r.ts,
|
|
79
|
+
r.open,
|
|
80
|
+
r.high,
|
|
81
|
+
r.low,
|
|
82
|
+
r.close,
|
|
83
|
+
r.volume ?? null,
|
|
84
|
+
r.turnover ?? null,
|
|
85
|
+
r.takerBuyBaseVolume ?? null,
|
|
86
|
+
r.takerBuyQuoteVolume ?? null,
|
|
87
|
+
r.takerSellBaseVolume ?? null,
|
|
88
|
+
r.takerSellQuoteVolume ?? null
|
|
89
|
+
]);
|
|
90
|
+
const sql = `
|
|
80
91
|
INSERT INTO candles (${cols.join(",")})
|
|
81
92
|
VALUES ${valuesSql}
|
|
82
93
|
ON CONFLICT (provider, symbol, interval, ts) DO UPDATE SET
|
|
@@ -91,16 +102,29 @@ async function upsertCandles(rows) {
|
|
|
91
102
|
taker_sell_base_volume = COALESCE(EXCLUDED.taker_sell_base_volume, candles.taker_sell_base_volume),
|
|
92
103
|
taker_sell_quote_volume = COALESCE(EXCLUDED.taker_sell_quote_volume, candles.taker_sell_quote_volume)
|
|
93
104
|
`;
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
105
|
+
for (let attempt = 1; attempt <= CANDLE_UPSERT_MAX_ATTEMPTS; attempt += 1) {
|
|
106
|
+
const client = await pool.connect();
|
|
107
|
+
try {
|
|
108
|
+
await client.query("BEGIN");
|
|
109
|
+
await client.query(sql, flat);
|
|
110
|
+
await client.query("COMMIT");
|
|
111
|
+
break;
|
|
112
|
+
} catch (error) {
|
|
113
|
+
await client.query("ROLLBACK").catch(() => void 0);
|
|
114
|
+
if (!isPostgresDeadlock(error) || attempt === CANDLE_UPSERT_MAX_ATTEMPTS) {
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
logger.warn(
|
|
118
|
+
"candle upsert deadlock (40P01); retrying transaction (attempt=%s/%s rows=%s)",
|
|
119
|
+
attempt + 1,
|
|
120
|
+
CANDLE_UPSERT_MAX_ATTEMPTS,
|
|
121
|
+
chunk.length
|
|
122
|
+
);
|
|
123
|
+
} finally {
|
|
124
|
+
client.release();
|
|
125
|
+
}
|
|
126
|
+
await wait(CANDLE_DEADLOCK_RETRY_DELAY_MS * 2 ** (attempt - 1));
|
|
127
|
+
}
|
|
104
128
|
}
|
|
105
129
|
}
|
|
106
130
|
async function getCandlesRange(provider, symbol, interval, startMs, endMs) {
|
|
@@ -31,6 +31,45 @@ __export(candles_exports, {
|
|
|
31
31
|
});
|
|
32
32
|
module.exports = __toCommonJS(candles_exports);
|
|
33
33
|
|
|
34
|
+
// src/logger.ts
|
|
35
|
+
var import_winston = require("winston");
|
|
36
|
+
var baseFormat = import_winston.format.combine(
|
|
37
|
+
import_winston.format.timestamp({ format: "DD MMM HH:mm:ss" }),
|
|
38
|
+
import_winston.format.splat()
|
|
39
|
+
);
|
|
40
|
+
var logger = (0, import_winston.createLogger)({
|
|
41
|
+
format: baseFormat,
|
|
42
|
+
transports: [
|
|
43
|
+
new import_winston.transports.Console({
|
|
44
|
+
format: import_winston.format.combine(
|
|
45
|
+
import_winston.format.colorize({ all: true }),
|
|
46
|
+
import_winston.format.printf(
|
|
47
|
+
({ level, timestamp, message }) => `${level}: ${timestamp}: ${message}`
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
}),
|
|
51
|
+
new import_winston.transports.File({
|
|
52
|
+
filename: "service.log",
|
|
53
|
+
format: import_winston.format.combine(
|
|
54
|
+
import_winston.format.uncolorize(),
|
|
55
|
+
import_winston.format.printf(
|
|
56
|
+
({ level, timestamp, message }) => `${level}: ${timestamp}: ${message}`
|
|
57
|
+
)
|
|
58
|
+
)
|
|
59
|
+
}),
|
|
60
|
+
new import_winston.transports.File({
|
|
61
|
+
filename: "error.log",
|
|
62
|
+
format: import_winston.format.combine(
|
|
63
|
+
import_winston.format.uncolorize(),
|
|
64
|
+
import_winston.format.printf(
|
|
65
|
+
({ level, timestamp, message }) => `${level}: ${timestamp}: ${message}`
|
|
66
|
+
)
|
|
67
|
+
),
|
|
68
|
+
level: "error"
|
|
69
|
+
})
|
|
70
|
+
]
|
|
71
|
+
});
|
|
72
|
+
|
|
34
73
|
// src/timescale/pool.ts
|
|
35
74
|
var import_pg = require("pg");
|
|
36
75
|
var getPool = () => {
|
|
@@ -145,6 +184,17 @@ var ensureCandlesSchema = async () => {
|
|
|
145
184
|
var marketContextSchemaMode = process.env.TRADEJS_TIMESCALE_READ_ONLY === "true" ? "verify" : "ensure";
|
|
146
185
|
|
|
147
186
|
// src/timescale/candles.ts
|
|
187
|
+
var CANDLE_UPSERT_MAX_ATTEMPTS = 3;
|
|
188
|
+
var CANDLE_DEADLOCK_RETRY_DELAY_MS = 25;
|
|
189
|
+
var isPostgresDeadlock = (error) => typeof error === "object" && error !== null && "code" in error && error.code === "40P01";
|
|
190
|
+
var wait = (delayMs) => new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
191
|
+
var normalizeAndSortCandleRows = (rows) => rows.map((row) => ({
|
|
192
|
+
...row,
|
|
193
|
+
provider: normalizeCandleProvider(row.provider),
|
|
194
|
+
symbol: normalizeCandleSymbol(row.symbol)
|
|
195
|
+
})).sort(
|
|
196
|
+
(left, right) => left.provider.localeCompare(right.provider) || left.symbol.localeCompare(right.symbol) || left.interval - right.interval || left.ts.getTime() - right.ts.getTime()
|
|
197
|
+
);
|
|
148
198
|
var toRows = (provider, symbol, interval, data) => {
|
|
149
199
|
const normalizedProvider = normalizeCandleProvider(provider);
|
|
150
200
|
if (!normalizedProvider) {
|
|
@@ -173,6 +223,7 @@ async function upsertCandles(rows) {
|
|
|
173
223
|
if (!rows.length) return;
|
|
174
224
|
await ensureCandlesSchema();
|
|
175
225
|
const pool = getPool();
|
|
226
|
+
const normalizedRows = normalizeAndSortCandleRows(rows);
|
|
176
227
|
const cols = [
|
|
177
228
|
"provider",
|
|
178
229
|
"symbol",
|
|
@@ -190,32 +241,28 @@ async function upsertCandles(rows) {
|
|
|
190
241
|
"taker_sell_quote_volume"
|
|
191
242
|
];
|
|
192
243
|
const maxRows = Math.floor(65535 / cols.length);
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
r.takerSellBaseVolume ?? null,
|
|
216
|
-
r.takerSellQuoteVolume ?? null
|
|
217
|
-
]);
|
|
218
|
-
const sql = `
|
|
244
|
+
for (let offset = 0; offset < normalizedRows.length; offset += maxRows) {
|
|
245
|
+
const chunk = normalizedRows.slice(offset, offset + maxRows);
|
|
246
|
+
const valuesSql = chunk.map(
|
|
247
|
+
(_, i) => `(${cols.map((__, j) => `$${i * cols.length + j + 1}`).join(",")})`
|
|
248
|
+
).join(",");
|
|
249
|
+
const flat = chunk.flatMap((r) => [
|
|
250
|
+
r.provider,
|
|
251
|
+
r.symbol,
|
|
252
|
+
r.interval,
|
|
253
|
+
r.ts,
|
|
254
|
+
r.open,
|
|
255
|
+
r.high,
|
|
256
|
+
r.low,
|
|
257
|
+
r.close,
|
|
258
|
+
r.volume ?? null,
|
|
259
|
+
r.turnover ?? null,
|
|
260
|
+
r.takerBuyBaseVolume ?? null,
|
|
261
|
+
r.takerBuyQuoteVolume ?? null,
|
|
262
|
+
r.takerSellBaseVolume ?? null,
|
|
263
|
+
r.takerSellQuoteVolume ?? null
|
|
264
|
+
]);
|
|
265
|
+
const sql = `
|
|
219
266
|
INSERT INTO candles (${cols.join(",")})
|
|
220
267
|
VALUES ${valuesSql}
|
|
221
268
|
ON CONFLICT (provider, symbol, interval, ts) DO UPDATE SET
|
|
@@ -230,16 +277,29 @@ async function upsertCandles(rows) {
|
|
|
230
277
|
taker_sell_base_volume = COALESCE(EXCLUDED.taker_sell_base_volume, candles.taker_sell_base_volume),
|
|
231
278
|
taker_sell_quote_volume = COALESCE(EXCLUDED.taker_sell_quote_volume, candles.taker_sell_quote_volume)
|
|
232
279
|
`;
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
280
|
+
for (let attempt = 1; attempt <= CANDLE_UPSERT_MAX_ATTEMPTS; attempt += 1) {
|
|
281
|
+
const client = await pool.connect();
|
|
282
|
+
try {
|
|
283
|
+
await client.query("BEGIN");
|
|
284
|
+
await client.query(sql, flat);
|
|
285
|
+
await client.query("COMMIT");
|
|
286
|
+
break;
|
|
287
|
+
} catch (error) {
|
|
288
|
+
await client.query("ROLLBACK").catch(() => void 0);
|
|
289
|
+
if (!isPostgresDeadlock(error) || attempt === CANDLE_UPSERT_MAX_ATTEMPTS) {
|
|
290
|
+
throw error;
|
|
291
|
+
}
|
|
292
|
+
logger.warn(
|
|
293
|
+
"candle upsert deadlock (40P01); retrying transaction (attempt=%s/%s rows=%s)",
|
|
294
|
+
attempt + 1,
|
|
295
|
+
CANDLE_UPSERT_MAX_ATTEMPTS,
|
|
296
|
+
chunk.length
|
|
297
|
+
);
|
|
298
|
+
} finally {
|
|
299
|
+
client.release();
|
|
300
|
+
}
|
|
301
|
+
await wait(CANDLE_DEADLOCK_RETRY_DELAY_MS * 2 ** (attempt - 1));
|
|
302
|
+
}
|
|
243
303
|
}
|
|
244
304
|
}
|
|
245
305
|
async function getCandlesRange(provider, symbol, interval, startMs, endMs) {
|
package/dist/timescale/client.js
CHANGED
|
@@ -111,6 +111,45 @@ var closeTimescalePool = async () => {
|
|
|
111
111
|
await closePool();
|
|
112
112
|
};
|
|
113
113
|
|
|
114
|
+
// src/logger.ts
|
|
115
|
+
var import_winston = require("winston");
|
|
116
|
+
var baseFormat = import_winston.format.combine(
|
|
117
|
+
import_winston.format.timestamp({ format: "DD MMM HH:mm:ss" }),
|
|
118
|
+
import_winston.format.splat()
|
|
119
|
+
);
|
|
120
|
+
var logger = (0, import_winston.createLogger)({
|
|
121
|
+
format: baseFormat,
|
|
122
|
+
transports: [
|
|
123
|
+
new import_winston.transports.Console({
|
|
124
|
+
format: import_winston.format.combine(
|
|
125
|
+
import_winston.format.colorize({ all: true }),
|
|
126
|
+
import_winston.format.printf(
|
|
127
|
+
({ level, timestamp, message }) => `${level}: ${timestamp}: ${message}`
|
|
128
|
+
)
|
|
129
|
+
)
|
|
130
|
+
}),
|
|
131
|
+
new import_winston.transports.File({
|
|
132
|
+
filename: "service.log",
|
|
133
|
+
format: import_winston.format.combine(
|
|
134
|
+
import_winston.format.uncolorize(),
|
|
135
|
+
import_winston.format.printf(
|
|
136
|
+
({ level, timestamp, message }) => `${level}: ${timestamp}: ${message}`
|
|
137
|
+
)
|
|
138
|
+
)
|
|
139
|
+
}),
|
|
140
|
+
new import_winston.transports.File({
|
|
141
|
+
filename: "error.log",
|
|
142
|
+
format: import_winston.format.combine(
|
|
143
|
+
import_winston.format.uncolorize(),
|
|
144
|
+
import_winston.format.printf(
|
|
145
|
+
({ level, timestamp, message }) => `${level}: ${timestamp}: ${message}`
|
|
146
|
+
)
|
|
147
|
+
),
|
|
148
|
+
level: "error"
|
|
149
|
+
})
|
|
150
|
+
]
|
|
151
|
+
});
|
|
152
|
+
|
|
114
153
|
// src/timescale/candles.ts
|
|
115
154
|
async function waitForDbReady(attempts = 20, delayMs = 1e3) {
|
|
116
155
|
const pool = getPool();
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
waitForDbReady
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-C5XG6VVP.mjs";
|
|
4
4
|
import {
|
|
5
5
|
closeTimescalePool,
|
|
6
6
|
configureTimescaleMarketContextSchemaMode
|
|
7
7
|
} from "../chunk-SSL36P7G.mjs";
|
|
8
|
+
import "../chunk-LNFUOXDW.mjs";
|
|
8
9
|
export {
|
|
9
10
|
closeTimescalePool,
|
|
10
11
|
configureTimescaleMarketContextSchemaMode,
|
|
@@ -45,6 +45,14 @@ declare function getLatestMarketTradeFlow(params: {
|
|
|
45
45
|
signal?: AbortSignal;
|
|
46
46
|
timeoutMs?: number;
|
|
47
47
|
}): Promise<MarketFeatureAsOf<MarketTradeFlowRow> | null>;
|
|
48
|
+
declare function getMarketTradeFlowRows(params: {
|
|
49
|
+
symbols: string[];
|
|
50
|
+
interval: MarketFeatureInterval;
|
|
51
|
+
fromMs: number;
|
|
52
|
+
toMs: number;
|
|
53
|
+
signal?: AbortSignal;
|
|
54
|
+
timeoutMs?: number;
|
|
55
|
+
}): Promise<MarketTradeFlowRow[]>;
|
|
48
56
|
declare function getLatestMarketBreadth(params: {
|
|
49
57
|
universe: string;
|
|
50
58
|
interval: MarketFeatureInterval;
|
|
@@ -53,6 +61,14 @@ declare function getLatestMarketBreadth(params: {
|
|
|
53
61
|
signal?: AbortSignal;
|
|
54
62
|
timeoutMs?: number;
|
|
55
63
|
}): Promise<MarketFeatureAsOf<MarketBreadthRow> | null>;
|
|
64
|
+
declare function getMarketBreadthRows(params: {
|
|
65
|
+
universes: string[];
|
|
66
|
+
interval: MarketFeatureInterval;
|
|
67
|
+
fromMs: number;
|
|
68
|
+
toMs: number;
|
|
69
|
+
signal?: AbortSignal;
|
|
70
|
+
timeoutMs?: number;
|
|
71
|
+
}): Promise<MarketBreadthRow[]>;
|
|
56
72
|
declare function getLatestMarketGlobalContext(params: {
|
|
57
73
|
source?: MarketGlobalContextRow['source'];
|
|
58
74
|
atMs: number;
|
|
@@ -189,4 +205,4 @@ declare function getMarketBreadthCoverage(params: {
|
|
|
189
205
|
btcAltMetricsRows: number;
|
|
190
206
|
} | null>;
|
|
191
207
|
|
|
192
|
-
export { type DeprecatedMarketContextCleanupItem, MarketFeatureAsOf, cleanupDeprecatedMarketContext, ensureBinanceMarketSchema, getLatestMarketBreadth, getLatestMarketCmcExchangeLiquidityContext, getLatestMarketCmcFearGreedContext, getLatestMarketCmcIndexContexts, getLatestMarketGlobalContext, getLatestMarketReferenceAssetContexts, getLatestMarketTradeFlow, getMarketBreadthCoverage, getMarketCmcExchangeLiquidityContextCoverage, getMarketCmcFearGreedContextCoverage, getMarketCmcIndexContextCoverage, getMarketContextBackfillCoverage, getMarketGlobalContextCoverage, getMarketReferenceAssetContextCoverage, getMarketTradeFlowCoverage, upsertMarketBreadthRows, upsertMarketCmcExchangeLiquidityContextRows, upsertMarketCmcFearGreedContextRows, upsertMarketCmcIndexContextRows, upsertMarketContextBackfillCoverage, upsertMarketGlobalContextRows, upsertMarketReferenceAssetContextRows, upsertMarketTradeFlowRows };
|
|
208
|
+
export { type DeprecatedMarketContextCleanupItem, MarketFeatureAsOf, cleanupDeprecatedMarketContext, ensureBinanceMarketSchema, getLatestMarketBreadth, getLatestMarketCmcExchangeLiquidityContext, getLatestMarketCmcFearGreedContext, getLatestMarketCmcIndexContexts, getLatestMarketGlobalContext, getLatestMarketReferenceAssetContexts, getLatestMarketTradeFlow, getMarketBreadthCoverage, getMarketBreadthRows, getMarketCmcExchangeLiquidityContextCoverage, getMarketCmcFearGreedContextCoverage, getMarketCmcIndexContextCoverage, getMarketContextBackfillCoverage, getMarketGlobalContextCoverage, getMarketReferenceAssetContextCoverage, getMarketTradeFlowCoverage, getMarketTradeFlowRows, upsertMarketBreadthRows, upsertMarketCmcExchangeLiquidityContextRows, upsertMarketCmcFearGreedContextRows, upsertMarketCmcIndexContextRows, upsertMarketContextBackfillCoverage, upsertMarketGlobalContextRows, upsertMarketReferenceAssetContextRows, upsertMarketTradeFlowRows };
|
|
@@ -45,6 +45,14 @@ declare function getLatestMarketTradeFlow(params: {
|
|
|
45
45
|
signal?: AbortSignal;
|
|
46
46
|
timeoutMs?: number;
|
|
47
47
|
}): Promise<MarketFeatureAsOf<MarketTradeFlowRow> | null>;
|
|
48
|
+
declare function getMarketTradeFlowRows(params: {
|
|
49
|
+
symbols: string[];
|
|
50
|
+
interval: MarketFeatureInterval;
|
|
51
|
+
fromMs: number;
|
|
52
|
+
toMs: number;
|
|
53
|
+
signal?: AbortSignal;
|
|
54
|
+
timeoutMs?: number;
|
|
55
|
+
}): Promise<MarketTradeFlowRow[]>;
|
|
48
56
|
declare function getLatestMarketBreadth(params: {
|
|
49
57
|
universe: string;
|
|
50
58
|
interval: MarketFeatureInterval;
|
|
@@ -53,6 +61,14 @@ declare function getLatestMarketBreadth(params: {
|
|
|
53
61
|
signal?: AbortSignal;
|
|
54
62
|
timeoutMs?: number;
|
|
55
63
|
}): Promise<MarketFeatureAsOf<MarketBreadthRow> | null>;
|
|
64
|
+
declare function getMarketBreadthRows(params: {
|
|
65
|
+
universes: string[];
|
|
66
|
+
interval: MarketFeatureInterval;
|
|
67
|
+
fromMs: number;
|
|
68
|
+
toMs: number;
|
|
69
|
+
signal?: AbortSignal;
|
|
70
|
+
timeoutMs?: number;
|
|
71
|
+
}): Promise<MarketBreadthRow[]>;
|
|
56
72
|
declare function getLatestMarketGlobalContext(params: {
|
|
57
73
|
source?: MarketGlobalContextRow['source'];
|
|
58
74
|
atMs: number;
|
|
@@ -189,4 +205,4 @@ declare function getMarketBreadthCoverage(params: {
|
|
|
189
205
|
btcAltMetricsRows: number;
|
|
190
206
|
} | null>;
|
|
191
207
|
|
|
192
|
-
export { type DeprecatedMarketContextCleanupItem, MarketFeatureAsOf, cleanupDeprecatedMarketContext, ensureBinanceMarketSchema, getLatestMarketBreadth, getLatestMarketCmcExchangeLiquidityContext, getLatestMarketCmcFearGreedContext, getLatestMarketCmcIndexContexts, getLatestMarketGlobalContext, getLatestMarketReferenceAssetContexts, getLatestMarketTradeFlow, getMarketBreadthCoverage, getMarketCmcExchangeLiquidityContextCoverage, getMarketCmcFearGreedContextCoverage, getMarketCmcIndexContextCoverage, getMarketContextBackfillCoverage, getMarketGlobalContextCoverage, getMarketReferenceAssetContextCoverage, getMarketTradeFlowCoverage, upsertMarketBreadthRows, upsertMarketCmcExchangeLiquidityContextRows, upsertMarketCmcFearGreedContextRows, upsertMarketCmcIndexContextRows, upsertMarketContextBackfillCoverage, upsertMarketGlobalContextRows, upsertMarketReferenceAssetContextRows, upsertMarketTradeFlowRows };
|
|
208
|
+
export { type DeprecatedMarketContextCleanupItem, MarketFeatureAsOf, cleanupDeprecatedMarketContext, ensureBinanceMarketSchema, getLatestMarketBreadth, getLatestMarketCmcExchangeLiquidityContext, getLatestMarketCmcFearGreedContext, getLatestMarketCmcIndexContexts, getLatestMarketGlobalContext, getLatestMarketReferenceAssetContexts, getLatestMarketTradeFlow, getMarketBreadthCoverage, getMarketBreadthRows, getMarketCmcExchangeLiquidityContextCoverage, getMarketCmcFearGreedContextCoverage, getMarketCmcIndexContextCoverage, getMarketContextBackfillCoverage, getMarketGlobalContextCoverage, getMarketReferenceAssetContextCoverage, getMarketTradeFlowCoverage, getMarketTradeFlowRows, upsertMarketBreadthRows, upsertMarketCmcExchangeLiquidityContextRows, upsertMarketCmcFearGreedContextRows, upsertMarketCmcIndexContextRows, upsertMarketContextBackfillCoverage, upsertMarketGlobalContextRows, upsertMarketReferenceAssetContextRows, upsertMarketTradeFlowRows };
|
|
@@ -32,6 +32,7 @@ __export(marketContext_exports, {
|
|
|
32
32
|
getLatestMarketReferenceAssetContexts: () => getLatestMarketReferenceAssetContexts,
|
|
33
33
|
getLatestMarketTradeFlow: () => getLatestMarketTradeFlow,
|
|
34
34
|
getMarketBreadthCoverage: () => getMarketBreadthCoverage,
|
|
35
|
+
getMarketBreadthRows: () => getMarketBreadthRows,
|
|
35
36
|
getMarketCmcExchangeLiquidityContextCoverage: () => getMarketCmcExchangeLiquidityContextCoverage,
|
|
36
37
|
getMarketCmcFearGreedContextCoverage: () => getMarketCmcFearGreedContextCoverage,
|
|
37
38
|
getMarketCmcIndexContextCoverage: () => getMarketCmcIndexContextCoverage,
|
|
@@ -39,6 +40,7 @@ __export(marketContext_exports, {
|
|
|
39
40
|
getMarketGlobalContextCoverage: () => getMarketGlobalContextCoverage,
|
|
40
41
|
getMarketReferenceAssetContextCoverage: () => getMarketReferenceAssetContextCoverage,
|
|
41
42
|
getMarketTradeFlowCoverage: () => getMarketTradeFlowCoverage,
|
|
43
|
+
getMarketTradeFlowRows: () => getMarketTradeFlowRows,
|
|
42
44
|
upsertMarketBreadthRows: () => upsertMarketBreadthRows,
|
|
43
45
|
upsertMarketCmcExchangeLiquidityContextRows: () => upsertMarketCmcExchangeLiquidityContextRows,
|
|
44
46
|
upsertMarketCmcFearGreedContextRows: () => upsertMarketCmcFearGreedContextRows,
|
|
@@ -1401,6 +1403,39 @@ async function getLatestMarketTradeFlow(params) {
|
|
|
1401
1403
|
stale: ageMs == null || params.maxAgeMs != null && ageMs > params.maxAgeMs
|
|
1402
1404
|
};
|
|
1403
1405
|
}
|
|
1406
|
+
async function getMarketTradeFlowRows(params) {
|
|
1407
|
+
const symbols = [
|
|
1408
|
+
...new Set(params.symbols.map((symbol) => symbol.trim().toUpperCase()))
|
|
1409
|
+
].filter(Boolean);
|
|
1410
|
+
if (!symbols.length || params.toMs < params.fromMs) return [];
|
|
1411
|
+
await prepareMarketContextSchemaForRead("binance");
|
|
1412
|
+
const res = await queryMarketContext(
|
|
1413
|
+
`
|
|
1414
|
+
SELECT
|
|
1415
|
+
symbol,
|
|
1416
|
+
interval,
|
|
1417
|
+
ts,
|
|
1418
|
+
trades::int AS trades,
|
|
1419
|
+
buy_base_volume AS "buyBaseVolume",
|
|
1420
|
+
sell_base_volume AS "sellBaseVolume",
|
|
1421
|
+
buy_quote_volume AS "buyQuoteVolume",
|
|
1422
|
+
sell_quote_volume AS "sellQuoteVolume",
|
|
1423
|
+
net_base_delta AS "netBaseDelta",
|
|
1424
|
+
net_quote_delta AS "netQuoteDelta",
|
|
1425
|
+
buy_pressure_pct AS "buyPressurePct",
|
|
1426
|
+
source
|
|
1427
|
+
FROM market_trade_flow
|
|
1428
|
+
WHERE symbol = ANY($1)
|
|
1429
|
+
AND interval = $2
|
|
1430
|
+
AND ts >= to_timestamp($3/1000.0)
|
|
1431
|
+
AND ts <= to_timestamp($4/1000.0)
|
|
1432
|
+
ORDER BY symbol, ts
|
|
1433
|
+
`,
|
|
1434
|
+
[symbols, params.interval, params.fromMs, params.toMs],
|
|
1435
|
+
params
|
|
1436
|
+
);
|
|
1437
|
+
return res.rows;
|
|
1438
|
+
}
|
|
1404
1439
|
async function getLatestMarketBreadth(params) {
|
|
1405
1440
|
await prepareMarketContextSchemaForRead("binance");
|
|
1406
1441
|
const res = await queryMarketContext(
|
|
@@ -1454,6 +1489,56 @@ async function getLatestMarketBreadth(params) {
|
|
|
1454
1489
|
stale: ageMs == null || params.maxAgeMs != null && ageMs > params.maxAgeMs
|
|
1455
1490
|
};
|
|
1456
1491
|
}
|
|
1492
|
+
async function getMarketBreadthRows(params) {
|
|
1493
|
+
const universes = [
|
|
1494
|
+
...new Set(params.universes.map((universe) => universe.trim()))
|
|
1495
|
+
].filter(Boolean);
|
|
1496
|
+
if (!universes.length || params.toMs < params.fromMs) return [];
|
|
1497
|
+
await prepareMarketContextSchemaForRead("binance");
|
|
1498
|
+
const res = await queryMarketContext(
|
|
1499
|
+
`
|
|
1500
|
+
SELECT
|
|
1501
|
+
universe,
|
|
1502
|
+
interval,
|
|
1503
|
+
ts,
|
|
1504
|
+
symbols_count::int AS "symbolsCount",
|
|
1505
|
+
advancers::int AS advancers,
|
|
1506
|
+
decliners::int AS decliners,
|
|
1507
|
+
unchanged::int AS unchanged,
|
|
1508
|
+
advance_decline_ratio AS "advanceDeclineRatio",
|
|
1509
|
+
pct_above_ma20 AS "pctAboveMa20",
|
|
1510
|
+
pct_above_ma50 AS "pctAboveMa50",
|
|
1511
|
+
equal_weighted_return AS "equalWeightedReturn",
|
|
1512
|
+
volume_weighted_return AS "volumeWeightedReturn",
|
|
1513
|
+
dispersion,
|
|
1514
|
+
btc_return_1h AS "btcReturn1h",
|
|
1515
|
+
btc_return_4h AS "btcReturn4h",
|
|
1516
|
+
btc_return_24h AS "btcReturn24h",
|
|
1517
|
+
alt_basket_return_1h AS "altBasketReturn1h",
|
|
1518
|
+
alt_basket_return_4h AS "altBasketReturn4h",
|
|
1519
|
+
alt_basket_return_24h AS "altBasketReturn24h",
|
|
1520
|
+
btc_vs_alt_return_1h AS "btcVsAltReturn1h",
|
|
1521
|
+
btc_vs_alt_return_4h AS "btcVsAltReturn4h",
|
|
1522
|
+
btc_vs_alt_return_24h AS "btcVsAltReturn24h",
|
|
1523
|
+
btc_turnover_share_1h AS "btcTurnoverShare1h",
|
|
1524
|
+
btc_turnover_share_24h AS "btcTurnoverShare24h",
|
|
1525
|
+
btc_turnover_share_change_24h AS "btcTurnoverShareChange24h",
|
|
1526
|
+
alt_vol_to_btc_vol_24h AS "altVolToBtcVol24h",
|
|
1527
|
+
alt_dispersion_24h AS "altDispersion24h",
|
|
1528
|
+
btc_alt_regime AS "btcAltRegime",
|
|
1529
|
+
source
|
|
1530
|
+
FROM market_breadth
|
|
1531
|
+
WHERE universe = ANY($1)
|
|
1532
|
+
AND interval = $2
|
|
1533
|
+
AND ts >= to_timestamp($3/1000.0)
|
|
1534
|
+
AND ts <= to_timestamp($4/1000.0)
|
|
1535
|
+
ORDER BY universe, ts
|
|
1536
|
+
`,
|
|
1537
|
+
[universes, params.interval, params.fromMs, params.toMs],
|
|
1538
|
+
params
|
|
1539
|
+
);
|
|
1540
|
+
return res.rows;
|
|
1541
|
+
}
|
|
1457
1542
|
async function getLatestMarketGlobalContext(params) {
|
|
1458
1543
|
await prepareMarketContextSchemaForRead("coinmarketcap");
|
|
1459
1544
|
const source = params.source ?? "coinmarketcap_global";
|
|
@@ -2083,6 +2168,7 @@ async function getMarketBreadthCoverage(params) {
|
|
|
2083
2168
|
getLatestMarketReferenceAssetContexts,
|
|
2084
2169
|
getLatestMarketTradeFlow,
|
|
2085
2170
|
getMarketBreadthCoverage,
|
|
2171
|
+
getMarketBreadthRows,
|
|
2086
2172
|
getMarketCmcExchangeLiquidityContextCoverage,
|
|
2087
2173
|
getMarketCmcFearGreedContextCoverage,
|
|
2088
2174
|
getMarketCmcIndexContextCoverage,
|
|
@@ -2090,6 +2176,7 @@ async function getMarketBreadthCoverage(params) {
|
|
|
2090
2176
|
getMarketGlobalContextCoverage,
|
|
2091
2177
|
getMarketReferenceAssetContextCoverage,
|
|
2092
2178
|
getMarketTradeFlowCoverage,
|
|
2179
|
+
getMarketTradeFlowRows,
|
|
2093
2180
|
upsertMarketBreadthRows,
|
|
2094
2181
|
upsertMarketCmcExchangeLiquidityContextRows,
|
|
2095
2182
|
upsertMarketCmcFearGreedContextRows,
|
|
@@ -592,6 +592,39 @@ async function getLatestMarketTradeFlow(params) {
|
|
|
592
592
|
stale: ageMs == null || params.maxAgeMs != null && ageMs > params.maxAgeMs
|
|
593
593
|
};
|
|
594
594
|
}
|
|
595
|
+
async function getMarketTradeFlowRows(params) {
|
|
596
|
+
const symbols = [
|
|
597
|
+
...new Set(params.symbols.map((symbol) => symbol.trim().toUpperCase()))
|
|
598
|
+
].filter(Boolean);
|
|
599
|
+
if (!symbols.length || params.toMs < params.fromMs) return [];
|
|
600
|
+
await prepareMarketContextSchemaForRead("binance");
|
|
601
|
+
const res = await queryMarketContext(
|
|
602
|
+
`
|
|
603
|
+
SELECT
|
|
604
|
+
symbol,
|
|
605
|
+
interval,
|
|
606
|
+
ts,
|
|
607
|
+
trades::int AS trades,
|
|
608
|
+
buy_base_volume AS "buyBaseVolume",
|
|
609
|
+
sell_base_volume AS "sellBaseVolume",
|
|
610
|
+
buy_quote_volume AS "buyQuoteVolume",
|
|
611
|
+
sell_quote_volume AS "sellQuoteVolume",
|
|
612
|
+
net_base_delta AS "netBaseDelta",
|
|
613
|
+
net_quote_delta AS "netQuoteDelta",
|
|
614
|
+
buy_pressure_pct AS "buyPressurePct",
|
|
615
|
+
source
|
|
616
|
+
FROM market_trade_flow
|
|
617
|
+
WHERE symbol = ANY($1)
|
|
618
|
+
AND interval = $2
|
|
619
|
+
AND ts >= to_timestamp($3/1000.0)
|
|
620
|
+
AND ts <= to_timestamp($4/1000.0)
|
|
621
|
+
ORDER BY symbol, ts
|
|
622
|
+
`,
|
|
623
|
+
[symbols, params.interval, params.fromMs, params.toMs],
|
|
624
|
+
params
|
|
625
|
+
);
|
|
626
|
+
return res.rows;
|
|
627
|
+
}
|
|
595
628
|
async function getLatestMarketBreadth(params) {
|
|
596
629
|
await prepareMarketContextSchemaForRead("binance");
|
|
597
630
|
const res = await queryMarketContext(
|
|
@@ -645,6 +678,56 @@ async function getLatestMarketBreadth(params) {
|
|
|
645
678
|
stale: ageMs == null || params.maxAgeMs != null && ageMs > params.maxAgeMs
|
|
646
679
|
};
|
|
647
680
|
}
|
|
681
|
+
async function getMarketBreadthRows(params) {
|
|
682
|
+
const universes = [
|
|
683
|
+
...new Set(params.universes.map((universe) => universe.trim()))
|
|
684
|
+
].filter(Boolean);
|
|
685
|
+
if (!universes.length || params.toMs < params.fromMs) return [];
|
|
686
|
+
await prepareMarketContextSchemaForRead("binance");
|
|
687
|
+
const res = await queryMarketContext(
|
|
688
|
+
`
|
|
689
|
+
SELECT
|
|
690
|
+
universe,
|
|
691
|
+
interval,
|
|
692
|
+
ts,
|
|
693
|
+
symbols_count::int AS "symbolsCount",
|
|
694
|
+
advancers::int AS advancers,
|
|
695
|
+
decliners::int AS decliners,
|
|
696
|
+
unchanged::int AS unchanged,
|
|
697
|
+
advance_decline_ratio AS "advanceDeclineRatio",
|
|
698
|
+
pct_above_ma20 AS "pctAboveMa20",
|
|
699
|
+
pct_above_ma50 AS "pctAboveMa50",
|
|
700
|
+
equal_weighted_return AS "equalWeightedReturn",
|
|
701
|
+
volume_weighted_return AS "volumeWeightedReturn",
|
|
702
|
+
dispersion,
|
|
703
|
+
btc_return_1h AS "btcReturn1h",
|
|
704
|
+
btc_return_4h AS "btcReturn4h",
|
|
705
|
+
btc_return_24h AS "btcReturn24h",
|
|
706
|
+
alt_basket_return_1h AS "altBasketReturn1h",
|
|
707
|
+
alt_basket_return_4h AS "altBasketReturn4h",
|
|
708
|
+
alt_basket_return_24h AS "altBasketReturn24h",
|
|
709
|
+
btc_vs_alt_return_1h AS "btcVsAltReturn1h",
|
|
710
|
+
btc_vs_alt_return_4h AS "btcVsAltReturn4h",
|
|
711
|
+
btc_vs_alt_return_24h AS "btcVsAltReturn24h",
|
|
712
|
+
btc_turnover_share_1h AS "btcTurnoverShare1h",
|
|
713
|
+
btc_turnover_share_24h AS "btcTurnoverShare24h",
|
|
714
|
+
btc_turnover_share_change_24h AS "btcTurnoverShareChange24h",
|
|
715
|
+
alt_vol_to_btc_vol_24h AS "altVolToBtcVol24h",
|
|
716
|
+
alt_dispersion_24h AS "altDispersion24h",
|
|
717
|
+
btc_alt_regime AS "btcAltRegime",
|
|
718
|
+
source
|
|
719
|
+
FROM market_breadth
|
|
720
|
+
WHERE universe = ANY($1)
|
|
721
|
+
AND interval = $2
|
|
722
|
+
AND ts >= to_timestamp($3/1000.0)
|
|
723
|
+
AND ts <= to_timestamp($4/1000.0)
|
|
724
|
+
ORDER BY universe, ts
|
|
725
|
+
`,
|
|
726
|
+
[universes, params.interval, params.fromMs, params.toMs],
|
|
727
|
+
params
|
|
728
|
+
);
|
|
729
|
+
return res.rows;
|
|
730
|
+
}
|
|
648
731
|
async function getLatestMarketGlobalContext(params) {
|
|
649
732
|
await prepareMarketContextSchemaForRead("coinmarketcap");
|
|
650
733
|
const source = params.source ?? "coinmarketcap_global";
|
|
@@ -1273,6 +1356,7 @@ export {
|
|
|
1273
1356
|
getLatestMarketReferenceAssetContexts,
|
|
1274
1357
|
getLatestMarketTradeFlow,
|
|
1275
1358
|
getMarketBreadthCoverage,
|
|
1359
|
+
getMarketBreadthRows,
|
|
1276
1360
|
getMarketCmcExchangeLiquidityContextCoverage,
|
|
1277
1361
|
getMarketCmcFearGreedContextCoverage,
|
|
1278
1362
|
getMarketCmcIndexContextCoverage,
|
|
@@ -1280,6 +1364,7 @@ export {
|
|
|
1280
1364
|
getMarketGlobalContextCoverage,
|
|
1281
1365
|
getMarketReferenceAssetContextCoverage,
|
|
1282
1366
|
getMarketTradeFlowCoverage,
|
|
1367
|
+
getMarketTradeFlowRows,
|
|
1283
1368
|
upsertMarketBreadthRows,
|
|
1284
1369
|
upsertMarketCmcExchangeLiquidityContextRows,
|
|
1285
1370
|
upsertMarketCmcFearGreedContextRows,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tradejs/infra",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.28-beta.251",
|
|
4
4
|
"description": "MIT-licensed server infrastructure adapters for TradeJS: Redis, Timescale, ML, logging, and IO.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"tradejs",
|
|
@@ -130,7 +130,7 @@
|
|
|
130
130
|
"dependencies": {
|
|
131
131
|
"@grpc/grpc-js": "^1.14.4",
|
|
132
132
|
"@grpc/proto-loader": "^0.8.1",
|
|
133
|
-
"@tradejs/types": "^3.1.
|
|
133
|
+
"@tradejs/types": "^3.1.28-beta.251",
|
|
134
134
|
"chalk": "4.1.2",
|
|
135
135
|
"date-fns": "^3.6.0",
|
|
136
136
|
"ioredis": "5.11.1",
|