@tradejs/infra 1.0.6 → 1.0.8

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/redis.d.mts CHANGED
@@ -17,6 +17,8 @@ declare class RedisWriteBlockedError extends Error {
17
17
  }
18
18
  declare const delKeyWithOptions: (key: string, options?: DelKeyOptions) => Promise<boolean>;
19
19
  declare const setData: <T>(key: string, data: T, options?: Options) => Promise<void>;
20
+ declare const createScreenshotSessionToken: (userName: string) => Promise<string | null>;
21
+ declare const consumeScreenshotSessionToken: (token: string) => Promise<string | null>;
20
22
  declare const redisKeys: {
21
23
  users: () => string;
22
24
  user: (userName: string) => string;
@@ -31,14 +33,28 @@ declare const redisKeys: {
31
33
  testOrders: (userName: string, strategyName: string, testName: string) => string;
32
34
  testConfig: (userName: string, strategyName: string, testName: string) => string;
33
35
  testStat: (userName: string, strategyName: string, testName: string) => string;
36
+ testSummaries: (userName: string) => string;
34
37
  cacheChunk: (userName: string, chunkId: string) => string;
35
38
  cacheOrders: (userName: string, orderLogId: string) => string;
36
39
  cachePositions: (userName: string, orderLogId: string) => string;
37
40
  signal: (symbol: string, signalId: string) => string;
38
41
  signalsBySymbol: (symbol: string) => string;
39
42
  storeSignal: (symbol: string, signalId: string) => string;
43
+ runtimeSignals: (userName: string) => string;
44
+ runtimeSignal: (userName: string, signalId: string) => string;
45
+ runtimeSignalEvaluations: (userName: string) => string;
46
+ runtimeSignalEvaluation: (userName: string, evaluationId: string) => string;
47
+ runtimeTrades: (userName: string) => string;
48
+ runtimeTrade: (userName: string, orderId: string) => string;
49
+ runtimeActiveTrades: (userName: string) => string;
50
+ runtimeActiveTrade: (userName: string, symbol: string) => string;
51
+ aiChatHistory: (userName: string, symbolKey: string) => string;
40
52
  analysis: (symbol: string, signalId: string) => string;
53
+ screenshotSessionToken: (token: string) => string;
41
54
  backtestResults: (userName: string, config: string, timestamp: string) => string;
55
+ researchRuns: (userName: string) => string;
56
+ researchRun: (userName: string, runId: string) => string;
57
+ researchLatestRun: (userName: string, strategyName: string) => string;
42
58
  mlSignalsByStrategy: (strategyName: string) => string;
43
59
  mlSignals: () => string;
44
60
  mlSignal: (strategyName: string, signalId: string) => string;
@@ -47,4 +63,4 @@ declare const redisKeys: {
47
63
  mlResult: (strategyName: string, signalId: string) => string;
48
64
  };
49
65
 
50
- export { RedisWriteBlockedError, delKey, delKeyWithOptions, getData, getKeys, redisKeys, setData };
66
+ export { RedisWriteBlockedError, consumeScreenshotSessionToken, createScreenshotSessionToken, delKey, delKeyWithOptions, getData, getKeys, redisKeys, setData };
package/dist/redis.d.ts CHANGED
@@ -17,6 +17,8 @@ declare class RedisWriteBlockedError extends Error {
17
17
  }
18
18
  declare const delKeyWithOptions: (key: string, options?: DelKeyOptions) => Promise<boolean>;
19
19
  declare const setData: <T>(key: string, data: T, options?: Options) => Promise<void>;
20
+ declare const createScreenshotSessionToken: (userName: string) => Promise<string | null>;
21
+ declare const consumeScreenshotSessionToken: (token: string) => Promise<string | null>;
20
22
  declare const redisKeys: {
21
23
  users: () => string;
22
24
  user: (userName: string) => string;
@@ -31,14 +33,28 @@ declare const redisKeys: {
31
33
  testOrders: (userName: string, strategyName: string, testName: string) => string;
32
34
  testConfig: (userName: string, strategyName: string, testName: string) => string;
33
35
  testStat: (userName: string, strategyName: string, testName: string) => string;
36
+ testSummaries: (userName: string) => string;
34
37
  cacheChunk: (userName: string, chunkId: string) => string;
35
38
  cacheOrders: (userName: string, orderLogId: string) => string;
36
39
  cachePositions: (userName: string, orderLogId: string) => string;
37
40
  signal: (symbol: string, signalId: string) => string;
38
41
  signalsBySymbol: (symbol: string) => string;
39
42
  storeSignal: (symbol: string, signalId: string) => string;
43
+ runtimeSignals: (userName: string) => string;
44
+ runtimeSignal: (userName: string, signalId: string) => string;
45
+ runtimeSignalEvaluations: (userName: string) => string;
46
+ runtimeSignalEvaluation: (userName: string, evaluationId: string) => string;
47
+ runtimeTrades: (userName: string) => string;
48
+ runtimeTrade: (userName: string, orderId: string) => string;
49
+ runtimeActiveTrades: (userName: string) => string;
50
+ runtimeActiveTrade: (userName: string, symbol: string) => string;
51
+ aiChatHistory: (userName: string, symbolKey: string) => string;
40
52
  analysis: (symbol: string, signalId: string) => string;
53
+ screenshotSessionToken: (token: string) => string;
41
54
  backtestResults: (userName: string, config: string, timestamp: string) => string;
55
+ researchRuns: (userName: string) => string;
56
+ researchRun: (userName: string, runId: string) => string;
57
+ researchLatestRun: (userName: string, strategyName: string) => string;
42
58
  mlSignalsByStrategy: (strategyName: string) => string;
43
59
  mlSignals: () => string;
44
60
  mlSignal: (strategyName: string, signalId: string) => string;
@@ -47,4 +63,4 @@ declare const redisKeys: {
47
63
  mlResult: (strategyName: string, signalId: string) => string;
48
64
  };
49
65
 
50
- export { RedisWriteBlockedError, delKey, delKeyWithOptions, getData, getKeys, redisKeys, setData };
66
+ export { RedisWriteBlockedError, consumeScreenshotSessionToken, createScreenshotSessionToken, delKey, delKeyWithOptions, getData, getKeys, redisKeys, setData };
package/dist/redis.js CHANGED
@@ -31,6 +31,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var redis_exports = {};
32
32
  __export(redis_exports, {
33
33
  RedisWriteBlockedError: () => RedisWriteBlockedError,
34
+ consumeScreenshotSessionToken: () => consumeScreenshotSessionToken,
35
+ createScreenshotSessionToken: () => createScreenshotSessionToken,
34
36
  delKey: () => delKey,
35
37
  delKeyWithOptions: () => delKeyWithOptions,
36
38
  getData: () => getData,
@@ -39,8 +41,10 @@ __export(redis_exports, {
39
41
  setData: () => setData
40
42
  });
41
43
  module.exports = __toCommonJS(redis_exports);
44
+ var import_crypto = require("crypto");
42
45
  var import_ioredis = __toESM(require("ioredis"));
43
46
  var TTL_1D = 86400;
47
+ var SCREENSHOT_TOKEN_TTL_SECONDS = 15 * 60;
44
48
  var toJson = (value) => JSON.stringify(value);
45
49
  var logger = {
46
50
  log: (level, message, ...args) => {
@@ -296,6 +300,36 @@ var setData = async (key, data, options = {}) => {
296
300
  }
297
301
  }
298
302
  };
303
+ var createScreenshotSessionToken = async (userName) => {
304
+ const token = (0, import_crypto.randomBytes)(24).toString("hex");
305
+ await setData(
306
+ redisKeys.screenshotSessionToken(token),
307
+ {
308
+ userName,
309
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
310
+ },
311
+ {
312
+ expire: SCREENSHOT_TOKEN_TTL_SECONDS
313
+ }
314
+ );
315
+ return token;
316
+ };
317
+ var consumeScreenshotSessionToken = async (token) => {
318
+ const normalizedToken = token.trim();
319
+ if (!normalizedToken) {
320
+ return null;
321
+ }
322
+ const key = redisKeys.screenshotSessionToken(normalizedToken);
323
+ const payload = await getData(
324
+ key,
325
+ null
326
+ );
327
+ if (!payload?.userName) {
328
+ return null;
329
+ }
330
+ await delKey(key);
331
+ return payload.userName;
332
+ };
299
333
  var redisKeys = {
300
334
  users: () => "users:index:",
301
335
  user: (userName) => `users:index:${userName}`,
@@ -310,14 +344,28 @@ var redisKeys = {
310
344
  testOrders: (userName, strategyName, testName) => `users:${userName}:tests:${strategyName}:${testName}:orders`,
311
345
  testConfig: (userName, strategyName, testName) => `users:${userName}:tests:${strategyName}:${testName}:config`,
312
346
  testStat: (userName, strategyName, testName) => `users:${userName}:tests:${strategyName}:${testName}:stat`,
347
+ testSummaries: (userName) => `users:${userName}:tests:index:summary`,
313
348
  cacheChunk: (userName, chunkId) => `users:${userName}:cache:tests:chunks:${chunkId}`,
314
349
  cacheOrders: (userName, orderLogId) => `users:${userName}:cache:tests:orders:${orderLogId}`,
315
350
  cachePositions: (userName, orderLogId) => `users:${userName}:cache:tests:positions:${orderLogId}`,
316
351
  signal: (symbol, signalId) => `signals:${symbol}:${signalId}`,
317
352
  signalsBySymbol: (symbol) => `signals:${symbol}:`,
318
353
  storeSignal: (symbol, signalId) => `store:signals:${symbol}:${signalId}`,
354
+ runtimeSignals: (userName) => `users:${userName}:runtime:signals:`,
355
+ runtimeSignal: (userName, signalId) => `users:${userName}:runtime:signals:${signalId}`,
356
+ runtimeSignalEvaluations: (userName) => `users:${userName}:runtime:signal-evaluations:`,
357
+ runtimeSignalEvaluation: (userName, evaluationId) => `users:${userName}:runtime:signal-evaluations:${evaluationId}`,
358
+ runtimeTrades: (userName) => `users:${userName}:runtime:trade-records:`,
359
+ runtimeTrade: (userName, orderId) => `users:${userName}:runtime:trade-records:${orderId}`,
360
+ runtimeActiveTrades: (userName) => `users:${userName}:runtime:active-trades:`,
361
+ runtimeActiveTrade: (userName, symbol) => `users:${userName}:runtime:active-trades:${symbol}`,
362
+ aiChatHistory: (userName, symbolKey) => `users:${userName}:ai:chats:${symbolKey}`,
319
363
  analysis: (symbol, signalId) => `analysis:${symbol}:${signalId}`,
364
+ screenshotSessionToken: (token) => `auth:screenshot:${token}`,
320
365
  backtestResults: (userName, config, timestamp) => `users:${userName}:backtests:results:${config}:${timestamp}`,
366
+ researchRuns: (userName) => `users:${userName}:research:runs:`,
367
+ researchRun: (userName, runId) => `users:${userName}:research:runs:${runId}`,
368
+ researchLatestRun: (userName, strategyName) => `users:${userName}:research:latest:${strategyName}`,
321
369
  mlSignalsByStrategy: (strategyName) => `ml:${strategyName}:signals:`,
322
370
  mlSignals: () => "ml:",
323
371
  mlSignal: (strategyName, signalId) => `ml:${strategyName}:signals:${signalId}`,
@@ -328,6 +376,8 @@ var redisKeys = {
328
376
  // Annotate the CommonJS export names for ESM import in node:
329
377
  0 && (module.exports = {
330
378
  RedisWriteBlockedError,
379
+ consumeScreenshotSessionToken,
380
+ createScreenshotSessionToken,
331
381
  delKey,
332
382
  delKeyWithOptions,
333
383
  getData,
package/dist/redis.mjs CHANGED
@@ -1,14 +1,18 @@
1
1
  import {
2
2
  RedisWriteBlockedError,
3
+ consumeScreenshotSessionToken,
4
+ createScreenshotSessionToken,
3
5
  delKey,
4
6
  delKeyWithOptions,
5
7
  getData,
6
8
  getKeys,
7
9
  redisKeys,
8
10
  setData
9
- } from "./chunk-EFARW5QE.mjs";
11
+ } from "./chunk-DSTV67R5.mjs";
10
12
  export {
11
13
  RedisWriteBlockedError,
14
+ consumeScreenshotSessionToken,
15
+ createScreenshotSessionToken,
12
16
  delKey,
13
17
  delKeyWithOptions,
14
18
  getData,
@@ -1,10 +1,11 @@
1
1
  import { Pool } from 'pg';
2
- import { DerivativesInterval, KlineChartData, DerivativesRow, SpreadRow } from '@tradejs/types';
2
+ import { DerivativesInterval, DerivativesRow, KlineChartData, SpreadRow } from '@tradejs/types';
3
3
 
4
4
  declare global {
5
5
  var __pgPool__: Pool | undefined;
6
6
  }
7
7
  type CandleRow = {
8
+ provider: string;
8
9
  symbol: string;
9
10
  interval: number;
10
11
  ts: Date;
@@ -15,10 +16,20 @@ type CandleRow = {
15
16
  volume?: number | null;
16
17
  turnover?: number | null;
17
18
  };
18
- declare const toRows: (symbol: string, interval: number, data: KlineChartData) => CandleRow[];
19
+ declare const toRows: (provider: string, symbol: string, interval: number, data: KlineChartData) => CandleRow[];
19
20
  declare function upsertCandles(rows: CandleRow[]): Promise<void>;
20
21
  declare function upsertDerivatives(rows: DerivativesRow[]): Promise<void>;
21
22
  declare function getDerivativesRangeForSymbols(symbols: string[], interval: DerivativesInterval, startMs: number, endMs: number): Promise<any[]>;
23
+ declare function getDerivativesDataEdgesForSymbols(symbols: string[], interval: DerivativesInterval): Promise<Map<string, {
24
+ min?: number;
25
+ max?: number;
26
+ }>>;
27
+ declare function getDerivativesWindow(params: {
28
+ symbol: string;
29
+ intervals: DerivativesInterval[];
30
+ endMs: number;
31
+ lookbackMs: number;
32
+ }): Promise<Partial<Record<DerivativesInterval, DerivativesRow[]>>>;
22
33
  declare function getDerivativesSummary(hours?: number, limit?: number): Promise<{
23
34
  rows: any[];
24
35
  aggregates: any[];
@@ -31,17 +42,17 @@ declare function getSpreadSummary(hours?: number, limit?: number): Promise<{
31
42
  aggregates: any[];
32
43
  hours: number;
33
44
  }>;
34
- declare function getCandlesRange(symbol: string, interval: number, startMs: number, endMs: number): Promise<any[]>;
35
- declare function getDataEdges(symbol: string, interval: number): Promise<{
45
+ declare function getCandlesRange(provider: string, symbol: string, interval: number, startMs: number, endMs: number): Promise<any[]>;
46
+ declare function getDataEdges(provider: string, symbol: string, interval: number): Promise<{
36
47
  min: number | undefined;
37
48
  max: number | undefined;
38
49
  }>;
39
50
  declare function waitForDbReady(attempts?: number, delayMs?: number): Promise<void>;
40
- declare function deleteCandles(symbol: string, interval: number): Promise<void>;
41
- declare function findContinuityGap(symbol: string, interval: number): Promise<{
51
+ declare function deleteCandles(provider: string, symbol: string, interval: number): Promise<void>;
52
+ declare function findContinuityGap(provider: string, symbol: string, interval: number): Promise<{
42
53
  ts: number;
43
54
  prevTs: number;
44
55
  diffSeconds: number;
45
56
  } | null>;
46
57
 
47
- export { type CandleRow, deleteCandles, findContinuityGap, getCandlesRange, getDataEdges, getDerivativesRangeForSymbols, getDerivativesSummary, getSpreadRangeForSymbols, getSpreadSummary, toRows, upsertCandles, upsertDerivatives, upsertSpreadRows, waitForDbReady };
58
+ export { type CandleRow, deleteCandles, findContinuityGap, getCandlesRange, getDataEdges, getDerivativesDataEdgesForSymbols, getDerivativesRangeForSymbols, getDerivativesSummary, getDerivativesWindow, getSpreadRangeForSymbols, getSpreadSummary, toRows, upsertCandles, upsertDerivatives, upsertSpreadRows, waitForDbReady };
@@ -1,10 +1,11 @@
1
1
  import { Pool } from 'pg';
2
- import { DerivativesInterval, KlineChartData, DerivativesRow, SpreadRow } from '@tradejs/types';
2
+ import { DerivativesInterval, DerivativesRow, KlineChartData, SpreadRow } from '@tradejs/types';
3
3
 
4
4
  declare global {
5
5
  var __pgPool__: Pool | undefined;
6
6
  }
7
7
  type CandleRow = {
8
+ provider: string;
8
9
  symbol: string;
9
10
  interval: number;
10
11
  ts: Date;
@@ -15,10 +16,20 @@ type CandleRow = {
15
16
  volume?: number | null;
16
17
  turnover?: number | null;
17
18
  };
18
- declare const toRows: (symbol: string, interval: number, data: KlineChartData) => CandleRow[];
19
+ declare const toRows: (provider: string, symbol: string, interval: number, data: KlineChartData) => CandleRow[];
19
20
  declare function upsertCandles(rows: CandleRow[]): Promise<void>;
20
21
  declare function upsertDerivatives(rows: DerivativesRow[]): Promise<void>;
21
22
  declare function getDerivativesRangeForSymbols(symbols: string[], interval: DerivativesInterval, startMs: number, endMs: number): Promise<any[]>;
23
+ declare function getDerivativesDataEdgesForSymbols(symbols: string[], interval: DerivativesInterval): Promise<Map<string, {
24
+ min?: number;
25
+ max?: number;
26
+ }>>;
27
+ declare function getDerivativesWindow(params: {
28
+ symbol: string;
29
+ intervals: DerivativesInterval[];
30
+ endMs: number;
31
+ lookbackMs: number;
32
+ }): Promise<Partial<Record<DerivativesInterval, DerivativesRow[]>>>;
22
33
  declare function getDerivativesSummary(hours?: number, limit?: number): Promise<{
23
34
  rows: any[];
24
35
  aggregates: any[];
@@ -31,17 +42,17 @@ declare function getSpreadSummary(hours?: number, limit?: number): Promise<{
31
42
  aggregates: any[];
32
43
  hours: number;
33
44
  }>;
34
- declare function getCandlesRange(symbol: string, interval: number, startMs: number, endMs: number): Promise<any[]>;
35
- declare function getDataEdges(symbol: string, interval: number): Promise<{
45
+ declare function getCandlesRange(provider: string, symbol: string, interval: number, startMs: number, endMs: number): Promise<any[]>;
46
+ declare function getDataEdges(provider: string, symbol: string, interval: number): Promise<{
36
47
  min: number | undefined;
37
48
  max: number | undefined;
38
49
  }>;
39
50
  declare function waitForDbReady(attempts?: number, delayMs?: number): Promise<void>;
40
- declare function deleteCandles(symbol: string, interval: number): Promise<void>;
41
- declare function findContinuityGap(symbol: string, interval: number): Promise<{
51
+ declare function deleteCandles(provider: string, symbol: string, interval: number): Promise<void>;
52
+ declare function findContinuityGap(provider: string, symbol: string, interval: number): Promise<{
42
53
  ts: number;
43
54
  prevTs: number;
44
55
  diffSeconds: number;
45
56
  } | null>;
46
57
 
47
- export { type CandleRow, deleteCandles, findContinuityGap, getCandlesRange, getDataEdges, getDerivativesRangeForSymbols, getDerivativesSummary, getSpreadRangeForSymbols, getSpreadSummary, toRows, upsertCandles, upsertDerivatives, upsertSpreadRows, waitForDbReady };
58
+ export { type CandleRow, deleteCandles, findContinuityGap, getCandlesRange, getDataEdges, getDerivativesDataEdgesForSymbols, getDerivativesRangeForSymbols, getDerivativesSummary, getDerivativesWindow, getSpreadRangeForSymbols, getSpreadSummary, toRows, upsertCandles, upsertDerivatives, upsertSpreadRows, waitForDbReady };
package/dist/timescale.js CHANGED
@@ -24,8 +24,10 @@ __export(timescale_exports, {
24
24
  findContinuityGap: () => findContinuityGap,
25
25
  getCandlesRange: () => getCandlesRange,
26
26
  getDataEdges: () => getDataEdges,
27
+ getDerivativesDataEdgesForSymbols: () => getDerivativesDataEdgesForSymbols,
27
28
  getDerivativesRangeForSymbols: () => getDerivativesRangeForSymbols,
28
29
  getDerivativesSummary: () => getDerivativesSummary,
30
+ getDerivativesWindow: () => getDerivativesWindow,
29
31
  getSpreadRangeForSymbols: () => getSpreadRangeForSymbols,
30
32
  getSpreadSummary: () => getSpreadSummary,
31
33
  toRows: () => toRows,
@@ -58,22 +60,33 @@ var getPool = () => {
58
60
  };
59
61
  var derivativesSchemaReady = false;
60
62
  var spreadSchemaReady = false;
61
- var toRows = (symbol, interval, data) => data.map((i) => ({
62
- symbol,
63
- interval,
64
- ts: new Date(i.timestamp),
65
- // ms -> Date
66
- open: i.open,
67
- high: i.high,
68
- low: i.low,
69
- close: i.close,
70
- volume: i.volume ?? null,
71
- turnover: i.turnover ?? null
72
- }));
63
+ var normalizeCandleProvider = (provider) => String(provider || "").trim().toLowerCase();
64
+ var normalizeCandleSymbol = (symbol) => String(symbol || "").trim().toUpperCase();
65
+ var toRows = (provider, symbol, interval, data) => {
66
+ const normalizedProvider = normalizeCandleProvider(provider);
67
+ if (!normalizedProvider) {
68
+ throw new Error("Candle provider is required");
69
+ }
70
+ const normalizedSymbol = normalizeCandleSymbol(symbol);
71
+ return data.map((i) => ({
72
+ provider: normalizedProvider,
73
+ symbol: normalizedSymbol,
74
+ interval,
75
+ ts: new Date(i.timestamp),
76
+ // ms -> Date
77
+ open: i.open,
78
+ high: i.high,
79
+ low: i.low,
80
+ close: i.close,
81
+ volume: i.volume ?? null,
82
+ turnover: i.turnover ?? null
83
+ }));
84
+ };
73
85
  async function upsertCandles(rows) {
74
86
  if (!rows.length) return;
75
87
  const pool = getPool();
76
88
  const cols = [
89
+ "provider",
77
90
  "symbol",
78
91
  "interval",
79
92
  "ts",
@@ -95,7 +108,8 @@ async function upsertCandles(rows) {
95
108
  (_, i) => `(${cols.map((__, j) => `$${i * cols.length + j + 1}`).join(",")})`
96
109
  ).join(",");
97
110
  const flat = rows.flatMap((r) => [
98
- r.symbol,
111
+ normalizeCandleProvider(r.provider),
112
+ normalizeCandleSymbol(r.symbol),
99
113
  r.interval,
100
114
  r.ts,
101
115
  r.open,
@@ -108,7 +122,7 @@ async function upsertCandles(rows) {
108
122
  const sql = `
109
123
  INSERT INTO candles (${cols.join(",")})
110
124
  VALUES ${valuesSql}
111
- ON CONFLICT (symbol, interval, ts) DO UPDATE SET
125
+ ON CONFLICT (provider, symbol, interval, ts) DO UPDATE SET
112
126
  open = EXCLUDED.open,
113
127
  high = EXCLUDED.high,
114
128
  low = EXCLUDED.low,
@@ -259,6 +273,82 @@ async function getDerivativesRangeForSymbols(symbols, interval, startMs, endMs)
259
273
  const res = await pool.query(sql, [symbols, interval, startMs, endMs]);
260
274
  return res.rows;
261
275
  }
276
+ async function getDerivativesDataEdgesForSymbols(symbols, interval) {
277
+ const normalizedSymbols = [
278
+ ...new Set(
279
+ symbols.map(
280
+ (symbol) => String(symbol || "").trim().toUpperCase()
281
+ ).filter(Boolean)
282
+ )
283
+ ];
284
+ const edges = /* @__PURE__ */ new Map();
285
+ if (!normalizedSymbols.length) return edges;
286
+ await ensureDerivativesSchema();
287
+ const pool = getPool();
288
+ const sql = `
289
+ SELECT
290
+ symbol,
291
+ extract(epoch from MIN(ts))*1000 AS min,
292
+ extract(epoch from MAX(ts))*1000 AS max
293
+ FROM derivatives_market
294
+ WHERE symbol = ANY($1)
295
+ AND interval = $2
296
+ GROUP BY symbol
297
+ `;
298
+ const res = await pool.query(sql, [normalizedSymbols, interval]);
299
+ for (const row of res.rows) {
300
+ const min = Number(row.min);
301
+ const max = Number(row.max);
302
+ edges.set(String(row.symbol).toUpperCase(), {
303
+ min: Number.isFinite(min) ? min : void 0,
304
+ max: Number.isFinite(max) ? max : void 0
305
+ });
306
+ }
307
+ return edges;
308
+ }
309
+ async function getDerivativesWindow(params) {
310
+ const { symbol, intervals, endMs, lookbackMs } = params;
311
+ const normalizedSymbol = String(symbol || "").trim().toUpperCase();
312
+ const normalizedIntervals = [...new Set(intervals)].filter(Boolean);
313
+ if (!normalizedSymbol || !normalizedIntervals.length) {
314
+ return {};
315
+ }
316
+ await ensureDerivativesSchema();
317
+ const startMs = endMs - Math.max(0, lookbackMs);
318
+ const pool = getPool();
319
+ const sql = `
320
+ SELECT symbol, interval, ts, open_interest, funding_rate, liq_long, liq_short, liq_total, source
321
+ FROM derivatives_market
322
+ WHERE symbol = $1
323
+ AND interval = ANY($2)
324
+ AND ts >= to_timestamp($3/1000.0)
325
+ AND ts <= to_timestamp($4/1000.0)
326
+ ORDER BY interval ASC, ts ASC
327
+ `;
328
+ const res = await pool.query(sql, [
329
+ normalizedSymbol,
330
+ normalizedIntervals,
331
+ startMs,
332
+ endMs
333
+ ]);
334
+ const rowsByInterval = {};
335
+ for (const row of res.rows) {
336
+ const interval = row.interval;
337
+ rowsByInterval[interval] ??= [];
338
+ rowsByInterval[interval]?.push({
339
+ symbol: row.symbol,
340
+ interval,
341
+ ts: row.ts,
342
+ openInterest: row.open_interest,
343
+ fundingRate: row.funding_rate,
344
+ liqLong: row.liq_long,
345
+ liqShort: row.liq_short,
346
+ liqTotal: row.liq_total,
347
+ source: row.source
348
+ });
349
+ }
350
+ return rowsByInterval;
351
+ }
262
352
  async function getDerivativesSummary(hours = 24, limit = 500) {
263
353
  await ensureDerivativesSchema();
264
354
  const pool = getPool();
@@ -398,39 +488,49 @@ async function getSpreadSummary(hours = 24, limit = 500) {
398
488
  hours: cappedHours
399
489
  };
400
490
  }
401
- async function getCandlesRange(symbol, interval, startMs, endMs) {
491
+ async function getCandlesRange(provider, symbol, interval, startMs, endMs) {
402
492
  const pool = getPool();
493
+ const normalizedProvider = normalizeCandleProvider(provider);
494
+ const normalizedSymbol = normalizeCandleSymbol(symbol);
403
495
  const sql = `
404
496
  SELECT symbol, interval, ts,
405
497
  open, high, low, close, volume, turnover
406
498
  FROM candles
407
- WHERE symbol = $1 AND interval = $2
408
- AND ts >= to_timestamp($3/1000.0)
409
- AND ts <= to_timestamp($4/1000.0)
499
+ WHERE provider = $1 AND symbol = $2 AND interval = $3
500
+ AND ts >= to_timestamp($4/1000.0)
501
+ AND ts <= to_timestamp($5/1000.0)
410
502
  ORDER BY ts ASC
411
503
  `;
412
- const res = await pool.query(sql, [symbol, interval, startMs, endMs]);
504
+ const res = await pool.query(sql, [
505
+ normalizedProvider,
506
+ normalizedSymbol,
507
+ interval,
508
+ startMs,
509
+ endMs
510
+ ]);
413
511
  return res.rows;
414
512
  }
415
- async function getDataEdges(symbol, interval) {
513
+ async function getDataEdges(provider, symbol, interval) {
416
514
  const pool = getPool();
515
+ const normalizedProvider = normalizeCandleProvider(provider);
516
+ const normalizedSymbol = normalizeCandleSymbol(symbol);
417
517
  const sqlMin = `
418
518
  SELECT extract(epoch from ts)*1000 AS ms
419
519
  FROM candles
420
- WHERE symbol=$1 AND interval=$2
520
+ WHERE provider=$1 AND symbol=$2 AND interval=$3
421
521
  ORDER BY ts ASC
422
522
  LIMIT 1
423
523
  `;
424
524
  const sqlMax = `
425
525
  SELECT extract(epoch from ts)*1000 AS ms
426
526
  FROM candles
427
- WHERE symbol=$1 AND interval=$2
527
+ WHERE provider=$1 AND symbol=$2 AND interval=$3
428
528
  ORDER BY ts DESC
429
529
  LIMIT 1
430
530
  `;
431
531
  const [minQ, maxQ] = await Promise.all([
432
- pool.query(sqlMin, [symbol, interval]),
433
- pool.query(sqlMax, [symbol, interval])
532
+ pool.query(sqlMin, [normalizedProvider, normalizedSymbol, interval]),
533
+ pool.query(sqlMax, [normalizedProvider, normalizedSymbol, interval])
434
534
  ]);
435
535
  const minRaw = minQ.rows[0]?.ms;
436
536
  const maxRaw = maxQ.rows[0]?.ms;
@@ -452,16 +552,20 @@ async function waitForDbReady(attempts = 20, delayMs = 1e3) {
452
552
  }
453
553
  throw lastError;
454
554
  }
455
- async function deleteCandles(symbol, interval) {
555
+ async function deleteCandles(provider, symbol, interval) {
456
556
  const pool = getPool();
557
+ const normalizedProvider = normalizeCandleProvider(provider);
558
+ const normalizedSymbol = normalizeCandleSymbol(symbol);
457
559
  const sql = `
458
560
  DELETE FROM candles
459
- WHERE symbol = $1 AND interval = $2
561
+ WHERE provider = $1 AND symbol = $2 AND interval = $3
460
562
  `;
461
- await pool.query(sql, [symbol, interval]);
563
+ await pool.query(sql, [normalizedProvider, normalizedSymbol, interval]);
462
564
  }
463
- async function findContinuityGap(symbol, interval) {
565
+ async function findContinuityGap(provider, symbol, interval) {
464
566
  const pool = getPool();
567
+ const normalizedProvider = normalizeCandleProvider(provider);
568
+ const normalizedSymbol = normalizeCandleSymbol(symbol);
465
569
  const expectedSeconds = interval * 60;
466
570
  const sql = `
467
571
  WITH ordered AS (
@@ -469,7 +573,7 @@ async function findContinuityGap(symbol, interval) {
469
573
  ts,
470
574
  LAG(ts) OVER (ORDER BY ts) AS prev_ts
471
575
  FROM candles
472
- WHERE symbol = $1 AND interval = $2
576
+ WHERE provider = $1 AND symbol = $2 AND interval = $3
473
577
  )
474
578
  SELECT
475
579
  ts,
@@ -477,11 +581,16 @@ async function findContinuityGap(symbol, interval) {
477
581
  EXTRACT(EPOCH FROM (ts - prev_ts))::int AS diff_seconds
478
582
  FROM ordered
479
583
  WHERE prev_ts IS NOT NULL
480
- AND EXTRACT(EPOCH FROM (ts - prev_ts))::int <> $3
584
+ AND EXTRACT(EPOCH FROM (ts - prev_ts))::int <> $4
481
585
  ORDER BY ts ASC
482
586
  LIMIT 1
483
587
  `;
484
- const res = await pool.query(sql, [symbol, interval, expectedSeconds]);
588
+ const res = await pool.query(sql, [
589
+ normalizedProvider,
590
+ normalizedSymbol,
591
+ interval,
592
+ expectedSeconds
593
+ ]);
485
594
  const row = res.rows[0];
486
595
  if (!row) return null;
487
596
  return {
@@ -496,8 +605,10 @@ async function findContinuityGap(symbol, interval) {
496
605
  findContinuityGap,
497
606
  getCandlesRange,
498
607
  getDataEdges,
608
+ getDerivativesDataEdgesForSymbols,
499
609
  getDerivativesRangeForSymbols,
500
610
  getDerivativesSummary,
611
+ getDerivativesWindow,
501
612
  getSpreadRangeForSymbols,
502
613
  getSpreadSummary,
503
614
  toRows,