@tradejs/infra 2.0.15 → 2.0.16

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.
@@ -507,6 +507,8 @@ var redisKeys = {
507
507
  runtimeTrade: (userName, orderId) => `users:${userName}:runtime:trade-records:${orderId}`,
508
508
  runtimeTradeBuckets: (userName) => `users:${userName}:runtime:trade-records:days:`,
509
509
  runtimeTradeBucket: (userName, dayKey) => `users:${userName}:runtime:trade-records:days:${dayKey}`,
510
+ runtimeClosedTradeBuckets: (userName) => `users:${userName}:runtime:closed-trade-records:days:`,
511
+ runtimeClosedTradeBucket: (userName, dayKey) => `users:${userName}:runtime:closed-trade-records:days:${dayKey}`,
510
512
  runtimeActiveTrades: (userName) => `users:${userName}:runtime:active-trades:`,
511
513
  runtimeActiveTrade: (userName, symbol, scopeId) => scopeId ? `users:${userName}:runtime:active-trades:${scopeId}:${symbol}` : `users:${userName}:runtime:active-trades:${symbol}`,
512
514
  aiChatHistory: (userName, symbolKey) => `users:${userName}:ai:chats:${symbolKey}`,
package/dist/redis.d.mts CHANGED
@@ -75,6 +75,8 @@ declare const redisKeys: {
75
75
  runtimeTrade: (userName: string, orderId: string) => string;
76
76
  runtimeTradeBuckets: (userName: string) => string;
77
77
  runtimeTradeBucket: (userName: string, dayKey: string) => string;
78
+ runtimeClosedTradeBuckets: (userName: string) => string;
79
+ runtimeClosedTradeBucket: (userName: string, dayKey: string) => string;
78
80
  runtimeActiveTrades: (userName: string) => string;
79
81
  runtimeActiveTrade: (userName: string, symbol: string, scopeId?: string) => string;
80
82
  aiChatHistory: (userName: string, symbolKey: string) => string;
package/dist/redis.d.ts CHANGED
@@ -75,6 +75,8 @@ declare const redisKeys: {
75
75
  runtimeTrade: (userName: string, orderId: string) => string;
76
76
  runtimeTradeBuckets: (userName: string) => string;
77
77
  runtimeTradeBucket: (userName: string, dayKey: string) => string;
78
+ runtimeClosedTradeBuckets: (userName: string) => string;
79
+ runtimeClosedTradeBucket: (userName: string, dayKey: string) => string;
78
80
  runtimeActiveTrades: (userName: string) => string;
79
81
  runtimeActiveTrade: (userName: string, symbol: string, scopeId?: string) => string;
80
82
  aiChatHistory: (userName: string, symbolKey: string) => string;
package/dist/redis.js CHANGED
@@ -557,6 +557,8 @@ var redisKeys = {
557
557
  runtimeTrade: (userName, orderId) => `users:${userName}:runtime:trade-records:${orderId}`,
558
558
  runtimeTradeBuckets: (userName) => `users:${userName}:runtime:trade-records:days:`,
559
559
  runtimeTradeBucket: (userName, dayKey) => `users:${userName}:runtime:trade-records:days:${dayKey}`,
560
+ runtimeClosedTradeBuckets: (userName) => `users:${userName}:runtime:closed-trade-records:days:`,
561
+ runtimeClosedTradeBucket: (userName, dayKey) => `users:${userName}:runtime:closed-trade-records:days:${dayKey}`,
560
562
  runtimeActiveTrades: (userName) => `users:${userName}:runtime:active-trades:`,
561
563
  runtimeActiveTrade: (userName, symbol, scopeId) => scopeId ? `users:${userName}:runtime:active-trades:${scopeId}:${symbol}` : `users:${userName}:runtime:active-trades:${symbol}`,
562
564
  aiChatHistory: (userName, symbolKey) => `users:${userName}:ai:chats:${symbolKey}`,
package/dist/redis.mjs CHANGED
@@ -16,7 +16,7 @@ import {
16
16
  setData,
17
17
  setHashJsonField,
18
18
  setHashJsonFields
19
- } from "./chunk-EFCQ7NGN.mjs";
19
+ } from "./chunk-6ZKAUA4Q.mjs";
20
20
  export {
21
21
  RedisWriteBlockedError,
22
22
  closeRedisConnection,
@@ -20,9 +20,25 @@ type CandleRow = {
20
20
  takerSellBaseVolume?: number | null;
21
21
  takerSellQuoteVolume?: number | null;
22
22
  };
23
+ type TimescaleMarketContextSource = 'binance' | 'coinmarketcap' | 'derivatives' | 'hyperliquidWhales';
24
+ type TimescaleMarketContextQueryOptions = {
25
+ signal?: AbortSignal;
26
+ timeoutMs?: number;
27
+ };
28
+ declare const configureTimescaleMarketContextSchemaMode: (mode: "ensure" | "verify") => void;
23
29
  declare const closeTimescalePool: () => Promise<void>;
24
30
  declare const toRows: (provider: string, symbol: string, interval: number, data: KlineChartData) => CandleRow[];
25
31
  declare function upsertCandles(rows: CandleRow[]): Promise<void>;
32
+ declare const ensureDerivativesSchema: () => Promise<void>;
33
+ declare const ensureBinanceMarketSchema: () => Promise<void>;
34
+ declare const ensureHyperliquidWhaleSchema: () => Promise<void>;
35
+ /**
36
+ * CoinMarketCap tables currently share the historical market-context migration
37
+ * with the Binance tables. Keeping a source-specific entrypoint lets process
38
+ * composition own schema preparation without exposing that storage detail.
39
+ */
40
+ declare const ensureCoinMarketCapContextSchema: () => Promise<void>;
41
+ declare const ensureMarketContextSchemas: (sources: Iterable<TimescaleMarketContextSource>) => Promise<void>;
26
42
  declare function upsertDerivatives(rows: DerivativesRow[]): Promise<void>;
27
43
  declare function getDerivativesRangeForSymbols(symbols: string[], interval: DerivativesInterval, startMs: number, endMs: number): Promise<any[]>;
28
44
  declare function getDerivativesDataEdgesForSymbols(symbols: string[], interval: DerivativesInterval): Promise<Map<string, {
@@ -83,6 +99,8 @@ declare function getDerivativesWindow(params: {
83
99
  intervals: DerivativesInterval[];
84
100
  endMs: number;
85
101
  lookbackMs: number;
102
+ signal?: AbortSignal;
103
+ timeoutMs?: number;
86
104
  }): Promise<Partial<Record<DerivativesInterval, DerivativesRow[]>>>;
87
105
  declare function getDerivativesSummary(hours?: number, limit?: number, symbols?: string[]): Promise<{
88
106
  hours: number;
@@ -153,6 +171,8 @@ declare function getLatestMarketTradeFlow(params: {
153
171
  interval: MarketFeatureInterval;
154
172
  atMs: number;
155
173
  maxAgeMs?: number;
174
+ signal?: AbortSignal;
175
+ timeoutMs?: number;
156
176
  }): Promise<MarketFeatureAsOf<MarketTradeFlowRow> | null>;
157
177
  type HyperliquidWhaleWalletCoverageStatus = 'complete' | 'truncated' | 'failed';
158
178
  declare function getHyperliquidWhaleWalletCoverage(params: {
@@ -200,6 +220,8 @@ declare function getHyperliquidWhaleCoverageSeriesRows(params: {
200
220
  toMs: number;
201
221
  universeFingerprint: string;
202
222
  whaleRegistryFingerprint: string;
223
+ signal?: AbortSignal;
224
+ timeoutMs?: number;
203
225
  }): Promise<HyperliquidWhaleCoverageSeriesRow[]>;
204
226
  type HyperliquidWhaleFlowSeriesRow = {
205
227
  ts: Date;
@@ -224,6 +246,8 @@ declare function getHyperliquidWhaleFlowSeriesRows(params: {
224
246
  toMs: number;
225
247
  universeFingerprint: string;
226
248
  whaleRegistryFingerprint: string;
249
+ signal?: AbortSignal;
250
+ timeoutMs?: number;
227
251
  }): Promise<HyperliquidWhaleFlowSeriesRow[]>;
228
252
  type HyperliquidWhaleFlowAggregate = {
229
253
  symbol: string;
@@ -265,6 +289,8 @@ declare function getHyperliquidWhaleFlowAggregate(params: {
265
289
  universeFingerprint: string;
266
290
  whaleRegistryFingerprint: string;
267
291
  maxAgeMs?: number;
292
+ signal?: AbortSignal;
293
+ timeoutMs?: number;
268
294
  }): Promise<HyperliquidWhaleFlowAggregate | null>;
269
295
  declare function hasHyperliquidWhaleBackfillCoverage(params: {
270
296
  fromMs: number;
@@ -277,11 +303,15 @@ declare function getLatestMarketBreadth(params: {
277
303
  interval: MarketFeatureInterval;
278
304
  atMs: number;
279
305
  maxAgeMs?: number;
306
+ signal?: AbortSignal;
307
+ timeoutMs?: number;
280
308
  }): Promise<MarketFeatureAsOf<MarketBreadthRow> | null>;
281
309
  declare function getLatestMarketGlobalContext(params: {
282
310
  source?: MarketGlobalContextRow['source'];
283
311
  atMs: number;
284
312
  maxAgeMs?: number;
313
+ signal?: AbortSignal;
314
+ timeoutMs?: number;
285
315
  }): Promise<(MarketFeatureAsOf<MarketGlobalContextRow> & {
286
316
  btcDominanceChange24hPct: number | null;
287
317
  ethDominanceChange24hPct: number | null;
@@ -314,12 +344,16 @@ declare function getLatestMarketReferenceAssetContexts(params: {
314
344
  interval?: MarketReferenceAssetContextRow['interval'];
315
345
  atMs: number;
316
346
  maxAgeMs?: number;
347
+ signal?: AbortSignal;
348
+ timeoutMs?: number;
317
349
  }): Promise<Map<string, MarketFeatureAsOf<MarketReferenceAssetContextRow>>>;
318
350
  declare function getLatestMarketCmcExchangeLiquidityContext(params: {
319
351
  source?: MarketCmcExchangeLiquidityContextRow['source'];
320
352
  interval?: MarketCmcExchangeLiquidityContextRow['interval'];
321
353
  atMs: number;
322
354
  maxAgeMs?: number;
355
+ signal?: AbortSignal;
356
+ timeoutMs?: number;
323
357
  }): Promise<(MarketFeatureAsOf<MarketCmcExchangeLiquidityContextRow> & {
324
358
  totalVolumeChange24hPct: number | null;
325
359
  }) | null>;
@@ -329,6 +363,8 @@ declare function getLatestMarketCmcIndexContexts(params: {
329
363
  interval?: MarketCmcIndexContextRow['interval'];
330
364
  atMs: number;
331
365
  maxAgeMs?: number;
366
+ signal?: AbortSignal;
367
+ timeoutMs?: number;
332
368
  }): Promise<Map<MarketCmcIndexContextRow['indexSlug'], MarketFeatureAsOf<MarketCmcIndexContextRow> & {
333
369
  valueChange24hPct: number | null;
334
370
  }>>;
@@ -337,6 +373,8 @@ declare function getLatestMarketCmcFearGreedContext(params: {
337
373
  interval?: MarketCmcFearGreedContextRow['interval'];
338
374
  atMs: number;
339
375
  maxAgeMs?: number;
376
+ signal?: AbortSignal;
377
+ timeoutMs?: number;
340
378
  }): Promise<(MarketFeatureAsOf<MarketCmcFearGreedContextRow> & {
341
379
  valueChange24h: number | null;
342
380
  valueChange7d: number | null;
@@ -426,4 +464,4 @@ declare function findContinuityGap(provider: string, symbol: string, interval: n
426
464
  diffSeconds: number;
427
465
  } | null>;
428
466
 
429
- export { type CandleRow, type DeprecatedMarketContextCleanupItem, type DerivativesMetricCoverageMetric, type HyperliquidWhaleCoverageSeriesRow, type HyperliquidWhaleFlowAggregate, type HyperliquidWhaleFlowSeriesRow, type HyperliquidWhaleWalletCoverageStatus, type MarketFeatureAsOf, applyDerivativesMetricCoverage, cleanupDeprecatedMarketContext, closeTimescalePool, deleteCandles, findContinuityGap, getCandlesRange, getDataEdges, getDataEdgesForSymbols, getDerivativesBackfillCoverage, getDerivativesDataEdgesForSymbols, getDerivativesMetricCoverage, getDerivativesRangeForSymbols, getDerivativesSummary, getDerivativesWindow, getHyperliquidWhaleCoverageSeriesRows, getHyperliquidWhaleFlowAggregate, getHyperliquidWhaleFlowSeriesRows, getHyperliquidWhaleWalletCoverage, getLatestMarketBreadth, getLatestMarketCmcExchangeLiquidityContext, getLatestMarketCmcFearGreedContext, getLatestMarketCmcIndexContexts, getLatestMarketGlobalContext, getLatestMarketReferenceAssetContexts, getLatestMarketTradeFlow, getMarketBreadthCoverage, getMarketCmcExchangeLiquidityContextCoverage, getMarketCmcFearGreedContextCoverage, getMarketCmcIndexContextCoverage, getMarketContextBackfillCoverage, getMarketGlobalContextCoverage, getMarketReferenceAssetContextCoverage, getMarketTradeFlowCoverage, getSpreadRangeForSymbols, getSpreadSummary, hasHyperliquidWhaleBackfillCoverage, rebuildHyperliquidWhaleCoverageRows, rebuildHyperliquidWhaleFlowRows, toRows, upsertCandles, upsertDerivatives, upsertDerivativesBackfillCoverage, upsertHyperliquidWhaleCoverageRows, upsertHyperliquidWhaleFlowRows, upsertHyperliquidWhaleTradeEvents, upsertHyperliquidWhaleWalletCoverage, upsertMarketBreadthRows, upsertMarketCmcExchangeLiquidityContextRows, upsertMarketCmcFearGreedContextRows, upsertMarketCmcIndexContextRows, upsertMarketContextBackfillCoverage, upsertMarketGlobalContextRows, upsertMarketReferenceAssetContextRows, upsertMarketTradeFlowRows, upsertSpreadRows, waitForDbReady };
467
+ export { type CandleRow, type DeprecatedMarketContextCleanupItem, type DerivativesMetricCoverageMetric, type HyperliquidWhaleCoverageSeriesRow, type HyperliquidWhaleFlowAggregate, type HyperliquidWhaleFlowSeriesRow, type HyperliquidWhaleWalletCoverageStatus, type MarketFeatureAsOf, type TimescaleMarketContextQueryOptions, type TimescaleMarketContextSource, applyDerivativesMetricCoverage, cleanupDeprecatedMarketContext, closeTimescalePool, configureTimescaleMarketContextSchemaMode, deleteCandles, ensureBinanceMarketSchema, ensureCoinMarketCapContextSchema, ensureDerivativesSchema, ensureHyperliquidWhaleSchema, ensureMarketContextSchemas, findContinuityGap, getCandlesRange, getDataEdges, getDataEdgesForSymbols, getDerivativesBackfillCoverage, getDerivativesDataEdgesForSymbols, getDerivativesMetricCoverage, getDerivativesRangeForSymbols, getDerivativesSummary, getDerivativesWindow, getHyperliquidWhaleCoverageSeriesRows, getHyperliquidWhaleFlowAggregate, getHyperliquidWhaleFlowSeriesRows, getHyperliquidWhaleWalletCoverage, getLatestMarketBreadth, getLatestMarketCmcExchangeLiquidityContext, getLatestMarketCmcFearGreedContext, getLatestMarketCmcIndexContexts, getLatestMarketGlobalContext, getLatestMarketReferenceAssetContexts, getLatestMarketTradeFlow, getMarketBreadthCoverage, getMarketCmcExchangeLiquidityContextCoverage, getMarketCmcFearGreedContextCoverage, getMarketCmcIndexContextCoverage, getMarketContextBackfillCoverage, getMarketGlobalContextCoverage, getMarketReferenceAssetContextCoverage, getMarketTradeFlowCoverage, getSpreadRangeForSymbols, getSpreadSummary, hasHyperliquidWhaleBackfillCoverage, rebuildHyperliquidWhaleCoverageRows, rebuildHyperliquidWhaleFlowRows, toRows, upsertCandles, upsertDerivatives, upsertDerivativesBackfillCoverage, upsertHyperliquidWhaleCoverageRows, upsertHyperliquidWhaleFlowRows, upsertHyperliquidWhaleTradeEvents, upsertHyperliquidWhaleWalletCoverage, upsertMarketBreadthRows, upsertMarketCmcExchangeLiquidityContextRows, upsertMarketCmcFearGreedContextRows, upsertMarketCmcIndexContextRows, upsertMarketContextBackfillCoverage, upsertMarketGlobalContextRows, upsertMarketReferenceAssetContextRows, upsertMarketTradeFlowRows, upsertSpreadRows, waitForDbReady };
@@ -20,9 +20,25 @@ type CandleRow = {
20
20
  takerSellBaseVolume?: number | null;
21
21
  takerSellQuoteVolume?: number | null;
22
22
  };
23
+ type TimescaleMarketContextSource = 'binance' | 'coinmarketcap' | 'derivatives' | 'hyperliquidWhales';
24
+ type TimescaleMarketContextQueryOptions = {
25
+ signal?: AbortSignal;
26
+ timeoutMs?: number;
27
+ };
28
+ declare const configureTimescaleMarketContextSchemaMode: (mode: "ensure" | "verify") => void;
23
29
  declare const closeTimescalePool: () => Promise<void>;
24
30
  declare const toRows: (provider: string, symbol: string, interval: number, data: KlineChartData) => CandleRow[];
25
31
  declare function upsertCandles(rows: CandleRow[]): Promise<void>;
32
+ declare const ensureDerivativesSchema: () => Promise<void>;
33
+ declare const ensureBinanceMarketSchema: () => Promise<void>;
34
+ declare const ensureHyperliquidWhaleSchema: () => Promise<void>;
35
+ /**
36
+ * CoinMarketCap tables currently share the historical market-context migration
37
+ * with the Binance tables. Keeping a source-specific entrypoint lets process
38
+ * composition own schema preparation without exposing that storage detail.
39
+ */
40
+ declare const ensureCoinMarketCapContextSchema: () => Promise<void>;
41
+ declare const ensureMarketContextSchemas: (sources: Iterable<TimescaleMarketContextSource>) => Promise<void>;
26
42
  declare function upsertDerivatives(rows: DerivativesRow[]): Promise<void>;
27
43
  declare function getDerivativesRangeForSymbols(symbols: string[], interval: DerivativesInterval, startMs: number, endMs: number): Promise<any[]>;
28
44
  declare function getDerivativesDataEdgesForSymbols(symbols: string[], interval: DerivativesInterval): Promise<Map<string, {
@@ -83,6 +99,8 @@ declare function getDerivativesWindow(params: {
83
99
  intervals: DerivativesInterval[];
84
100
  endMs: number;
85
101
  lookbackMs: number;
102
+ signal?: AbortSignal;
103
+ timeoutMs?: number;
86
104
  }): Promise<Partial<Record<DerivativesInterval, DerivativesRow[]>>>;
87
105
  declare function getDerivativesSummary(hours?: number, limit?: number, symbols?: string[]): Promise<{
88
106
  hours: number;
@@ -153,6 +171,8 @@ declare function getLatestMarketTradeFlow(params: {
153
171
  interval: MarketFeatureInterval;
154
172
  atMs: number;
155
173
  maxAgeMs?: number;
174
+ signal?: AbortSignal;
175
+ timeoutMs?: number;
156
176
  }): Promise<MarketFeatureAsOf<MarketTradeFlowRow> | null>;
157
177
  type HyperliquidWhaleWalletCoverageStatus = 'complete' | 'truncated' | 'failed';
158
178
  declare function getHyperliquidWhaleWalletCoverage(params: {
@@ -200,6 +220,8 @@ declare function getHyperliquidWhaleCoverageSeriesRows(params: {
200
220
  toMs: number;
201
221
  universeFingerprint: string;
202
222
  whaleRegistryFingerprint: string;
223
+ signal?: AbortSignal;
224
+ timeoutMs?: number;
203
225
  }): Promise<HyperliquidWhaleCoverageSeriesRow[]>;
204
226
  type HyperliquidWhaleFlowSeriesRow = {
205
227
  ts: Date;
@@ -224,6 +246,8 @@ declare function getHyperliquidWhaleFlowSeriesRows(params: {
224
246
  toMs: number;
225
247
  universeFingerprint: string;
226
248
  whaleRegistryFingerprint: string;
249
+ signal?: AbortSignal;
250
+ timeoutMs?: number;
227
251
  }): Promise<HyperliquidWhaleFlowSeriesRow[]>;
228
252
  type HyperliquidWhaleFlowAggregate = {
229
253
  symbol: string;
@@ -265,6 +289,8 @@ declare function getHyperliquidWhaleFlowAggregate(params: {
265
289
  universeFingerprint: string;
266
290
  whaleRegistryFingerprint: string;
267
291
  maxAgeMs?: number;
292
+ signal?: AbortSignal;
293
+ timeoutMs?: number;
268
294
  }): Promise<HyperliquidWhaleFlowAggregate | null>;
269
295
  declare function hasHyperliquidWhaleBackfillCoverage(params: {
270
296
  fromMs: number;
@@ -277,11 +303,15 @@ declare function getLatestMarketBreadth(params: {
277
303
  interval: MarketFeatureInterval;
278
304
  atMs: number;
279
305
  maxAgeMs?: number;
306
+ signal?: AbortSignal;
307
+ timeoutMs?: number;
280
308
  }): Promise<MarketFeatureAsOf<MarketBreadthRow> | null>;
281
309
  declare function getLatestMarketGlobalContext(params: {
282
310
  source?: MarketGlobalContextRow['source'];
283
311
  atMs: number;
284
312
  maxAgeMs?: number;
313
+ signal?: AbortSignal;
314
+ timeoutMs?: number;
285
315
  }): Promise<(MarketFeatureAsOf<MarketGlobalContextRow> & {
286
316
  btcDominanceChange24hPct: number | null;
287
317
  ethDominanceChange24hPct: number | null;
@@ -314,12 +344,16 @@ declare function getLatestMarketReferenceAssetContexts(params: {
314
344
  interval?: MarketReferenceAssetContextRow['interval'];
315
345
  atMs: number;
316
346
  maxAgeMs?: number;
347
+ signal?: AbortSignal;
348
+ timeoutMs?: number;
317
349
  }): Promise<Map<string, MarketFeatureAsOf<MarketReferenceAssetContextRow>>>;
318
350
  declare function getLatestMarketCmcExchangeLiquidityContext(params: {
319
351
  source?: MarketCmcExchangeLiquidityContextRow['source'];
320
352
  interval?: MarketCmcExchangeLiquidityContextRow['interval'];
321
353
  atMs: number;
322
354
  maxAgeMs?: number;
355
+ signal?: AbortSignal;
356
+ timeoutMs?: number;
323
357
  }): Promise<(MarketFeatureAsOf<MarketCmcExchangeLiquidityContextRow> & {
324
358
  totalVolumeChange24hPct: number | null;
325
359
  }) | null>;
@@ -329,6 +363,8 @@ declare function getLatestMarketCmcIndexContexts(params: {
329
363
  interval?: MarketCmcIndexContextRow['interval'];
330
364
  atMs: number;
331
365
  maxAgeMs?: number;
366
+ signal?: AbortSignal;
367
+ timeoutMs?: number;
332
368
  }): Promise<Map<MarketCmcIndexContextRow['indexSlug'], MarketFeatureAsOf<MarketCmcIndexContextRow> & {
333
369
  valueChange24hPct: number | null;
334
370
  }>>;
@@ -337,6 +373,8 @@ declare function getLatestMarketCmcFearGreedContext(params: {
337
373
  interval?: MarketCmcFearGreedContextRow['interval'];
338
374
  atMs: number;
339
375
  maxAgeMs?: number;
376
+ signal?: AbortSignal;
377
+ timeoutMs?: number;
340
378
  }): Promise<(MarketFeatureAsOf<MarketCmcFearGreedContextRow> & {
341
379
  valueChange24h: number | null;
342
380
  valueChange7d: number | null;
@@ -426,4 +464,4 @@ declare function findContinuityGap(provider: string, symbol: string, interval: n
426
464
  diffSeconds: number;
427
465
  } | null>;
428
466
 
429
- export { type CandleRow, type DeprecatedMarketContextCleanupItem, type DerivativesMetricCoverageMetric, type HyperliquidWhaleCoverageSeriesRow, type HyperliquidWhaleFlowAggregate, type HyperliquidWhaleFlowSeriesRow, type HyperliquidWhaleWalletCoverageStatus, type MarketFeatureAsOf, applyDerivativesMetricCoverage, cleanupDeprecatedMarketContext, closeTimescalePool, deleteCandles, findContinuityGap, getCandlesRange, getDataEdges, getDataEdgesForSymbols, getDerivativesBackfillCoverage, getDerivativesDataEdgesForSymbols, getDerivativesMetricCoverage, getDerivativesRangeForSymbols, getDerivativesSummary, getDerivativesWindow, getHyperliquidWhaleCoverageSeriesRows, getHyperliquidWhaleFlowAggregate, getHyperliquidWhaleFlowSeriesRows, getHyperliquidWhaleWalletCoverage, getLatestMarketBreadth, getLatestMarketCmcExchangeLiquidityContext, getLatestMarketCmcFearGreedContext, getLatestMarketCmcIndexContexts, getLatestMarketGlobalContext, getLatestMarketReferenceAssetContexts, getLatestMarketTradeFlow, getMarketBreadthCoverage, getMarketCmcExchangeLiquidityContextCoverage, getMarketCmcFearGreedContextCoverage, getMarketCmcIndexContextCoverage, getMarketContextBackfillCoverage, getMarketGlobalContextCoverage, getMarketReferenceAssetContextCoverage, getMarketTradeFlowCoverage, getSpreadRangeForSymbols, getSpreadSummary, hasHyperliquidWhaleBackfillCoverage, rebuildHyperliquidWhaleCoverageRows, rebuildHyperliquidWhaleFlowRows, toRows, upsertCandles, upsertDerivatives, upsertDerivativesBackfillCoverage, upsertHyperliquidWhaleCoverageRows, upsertHyperliquidWhaleFlowRows, upsertHyperliquidWhaleTradeEvents, upsertHyperliquidWhaleWalletCoverage, upsertMarketBreadthRows, upsertMarketCmcExchangeLiquidityContextRows, upsertMarketCmcFearGreedContextRows, upsertMarketCmcIndexContextRows, upsertMarketContextBackfillCoverage, upsertMarketGlobalContextRows, upsertMarketReferenceAssetContextRows, upsertMarketTradeFlowRows, upsertSpreadRows, waitForDbReady };
467
+ export { type CandleRow, type DeprecatedMarketContextCleanupItem, type DerivativesMetricCoverageMetric, type HyperliquidWhaleCoverageSeriesRow, type HyperliquidWhaleFlowAggregate, type HyperliquidWhaleFlowSeriesRow, type HyperliquidWhaleWalletCoverageStatus, type MarketFeatureAsOf, type TimescaleMarketContextQueryOptions, type TimescaleMarketContextSource, applyDerivativesMetricCoverage, cleanupDeprecatedMarketContext, closeTimescalePool, configureTimescaleMarketContextSchemaMode, deleteCandles, ensureBinanceMarketSchema, ensureCoinMarketCapContextSchema, ensureDerivativesSchema, ensureHyperliquidWhaleSchema, ensureMarketContextSchemas, findContinuityGap, getCandlesRange, getDataEdges, getDataEdgesForSymbols, getDerivativesBackfillCoverage, getDerivativesDataEdgesForSymbols, getDerivativesMetricCoverage, getDerivativesRangeForSymbols, getDerivativesSummary, getDerivativesWindow, getHyperliquidWhaleCoverageSeriesRows, getHyperliquidWhaleFlowAggregate, getHyperliquidWhaleFlowSeriesRows, getHyperliquidWhaleWalletCoverage, getLatestMarketBreadth, getLatestMarketCmcExchangeLiquidityContext, getLatestMarketCmcFearGreedContext, getLatestMarketCmcIndexContexts, getLatestMarketGlobalContext, getLatestMarketReferenceAssetContexts, getLatestMarketTradeFlow, getMarketBreadthCoverage, getMarketCmcExchangeLiquidityContextCoverage, getMarketCmcFearGreedContextCoverage, getMarketCmcIndexContextCoverage, getMarketContextBackfillCoverage, getMarketGlobalContextCoverage, getMarketReferenceAssetContextCoverage, getMarketTradeFlowCoverage, getSpreadRangeForSymbols, getSpreadSummary, hasHyperliquidWhaleBackfillCoverage, rebuildHyperliquidWhaleCoverageRows, rebuildHyperliquidWhaleFlowRows, toRows, upsertCandles, upsertDerivatives, upsertDerivativesBackfillCoverage, upsertHyperliquidWhaleCoverageRows, upsertHyperliquidWhaleFlowRows, upsertHyperliquidWhaleTradeEvents, upsertHyperliquidWhaleWalletCoverage, upsertMarketBreadthRows, upsertMarketCmcExchangeLiquidityContextRows, upsertMarketCmcFearGreedContextRows, upsertMarketCmcIndexContextRows, upsertMarketContextBackfillCoverage, upsertMarketGlobalContextRows, upsertMarketReferenceAssetContextRows, upsertMarketTradeFlowRows, upsertSpreadRows, waitForDbReady };
package/dist/timescale.js CHANGED
@@ -23,7 +23,13 @@ __export(timescale_exports, {
23
23
  applyDerivativesMetricCoverage: () => applyDerivativesMetricCoverage,
24
24
  cleanupDeprecatedMarketContext: () => cleanupDeprecatedMarketContext,
25
25
  closeTimescalePool: () => closeTimescalePool,
26
+ configureTimescaleMarketContextSchemaMode: () => configureTimescaleMarketContextSchemaMode,
26
27
  deleteCandles: () => deleteCandles,
28
+ ensureBinanceMarketSchema: () => ensureBinanceMarketSchema,
29
+ ensureCoinMarketCapContextSchema: () => ensureCoinMarketCapContextSchema,
30
+ ensureDerivativesSchema: () => ensureDerivativesSchema,
31
+ ensureHyperliquidWhaleSchema: () => ensureHyperliquidWhaleSchema,
32
+ ensureMarketContextSchemas: () => ensureMarketContextSchemas,
27
33
  findContinuityGap: () => findContinuityGap,
28
34
  getCandlesRange: () => getCandlesRange,
29
35
  getDataEdges: () => getDataEdges,
@@ -114,6 +120,12 @@ var derivativesSchemaReadyPromise = null;
114
120
  var spreadSchemaReadyPromise = null;
115
121
  var binanceMarketSchemaReadyPromise = null;
116
122
  var hyperliquidWhaleSchemaReadyPromise = null;
123
+ var marketContextSchemaMode = "ensure";
124
+ var verifiedMarketContextSchemas = /* @__PURE__ */ new Set();
125
+ var configureTimescaleMarketContextSchemaMode = (mode) => {
126
+ marketContextSchemaMode = mode;
127
+ verifiedMarketContextSchemas.clear();
128
+ };
117
129
  var closeTimescalePool = async () => {
118
130
  const pool = global.__pgPool__;
119
131
  if (!pool) {
@@ -130,6 +142,7 @@ var closeTimescalePool = async () => {
130
142
  spreadSchemaReadyPromise = null;
131
143
  binanceMarketSchemaReadyPromise = null;
132
144
  hyperliquidWhaleSchemaReadyPromise = null;
145
+ verifiedMarketContextSchemas.clear();
133
146
  await pool.end();
134
147
  };
135
148
  var CANDLES_SCHEMA_LOCK_KEY = 61e4;
@@ -138,6 +151,70 @@ var SPREAD_SCHEMA_LOCK_KEY = 610002;
138
151
  var BINANCE_MARKET_SCHEMA_LOCK_KEY = 610003;
139
152
  var HYPERLIQUID_WHALE_SCHEMA_LOCK_KEY = 610004;
140
153
  var PG_SAFE_MAX_BIND_PARAMS = 3e4;
154
+ var resolveMarketContextQueryTimeoutMs = (override) => {
155
+ if (Number.isFinite(override) && Number(override) > 0) {
156
+ return Math.floor(Number(override));
157
+ }
158
+ const configured = Number(process.env.MARKET_CONTEXT_SQL_TIMEOUT_MS);
159
+ return Number.isFinite(configured) && configured > 0 ? Math.floor(configured) : 3e4;
160
+ };
161
+ var createMarketContextQueryError = (name, message) => {
162
+ const error = new Error(message);
163
+ error.name = name;
164
+ return error;
165
+ };
166
+ var queryMarketContext = async (text, values, options = {}) => {
167
+ const pool = getPool();
168
+ const client = await pool.connect();
169
+ const timeoutMs = resolveMarketContextQueryTimeoutMs(options.timeoutMs);
170
+ let released = false;
171
+ let rejectCancellation;
172
+ const release = (error) => {
173
+ if (released) return;
174
+ released = true;
175
+ client.release(error);
176
+ };
177
+ const cancellation = new Promise((_resolve, reject) => {
178
+ rejectCancellation = reject;
179
+ });
180
+ const cancel = (error) => {
181
+ release(error);
182
+ rejectCancellation?.(error);
183
+ };
184
+ const onAbort = () => cancel(
185
+ createMarketContextQueryError(
186
+ "AbortError",
187
+ "Timescale market-context query aborted"
188
+ )
189
+ );
190
+ const timer = setTimeout(
191
+ () => cancel(
192
+ createMarketContextQueryError(
193
+ "TimescaleQueryTimeoutError",
194
+ `Timescale market-context query exceeded ${timeoutMs}ms`
195
+ )
196
+ ),
197
+ timeoutMs
198
+ );
199
+ timer.unref?.();
200
+ options.signal?.addEventListener("abort", onAbort, { once: true });
201
+ try {
202
+ if (options.signal?.aborted) {
203
+ const error = createMarketContextQueryError(
204
+ "AbortError",
205
+ "Timescale market-context query aborted"
206
+ );
207
+ release(error);
208
+ throw error;
209
+ }
210
+ const query = client.query(text, values);
211
+ return await Promise.race([query, cancellation]);
212
+ } finally {
213
+ clearTimeout(timer);
214
+ options.signal?.removeEventListener("abort", onAbort);
215
+ release();
216
+ }
217
+ };
141
218
  var normalizeCandleProvider = (provider) => String(provider || "").trim().toLowerCase();
142
219
  var normalizeCandleSymbol = (symbol) => String(symbol || "").trim().toUpperCase();
143
220
  var getSafeBulkInsertRows = (columnsCount) => Math.max(1, Math.floor(PG_SAFE_MAX_BIND_PARAMS / columnsCount));
@@ -942,6 +1019,58 @@ var ensureHyperliquidWhaleSchema = async () => {
942
1019
  });
943
1020
  await hyperliquidWhaleSchemaReadyPromise;
944
1021
  };
1022
+ var ensureCoinMarketCapContextSchema = async () => ensureBinanceMarketSchema();
1023
+ var ensureMarketContextSchema = async (source) => {
1024
+ switch (source) {
1025
+ case "binance":
1026
+ return ensureBinanceMarketSchema();
1027
+ case "coinmarketcap":
1028
+ return ensureCoinMarketCapContextSchema();
1029
+ case "derivatives":
1030
+ return ensureDerivativesSchema();
1031
+ case "hyperliquidWhales":
1032
+ return ensureHyperliquidWhaleSchema();
1033
+ }
1034
+ };
1035
+ var MARKET_CONTEXT_SCHEMA_TABLES = {
1036
+ binance: ["market_trade_flow", "market_breadth"],
1037
+ coinmarketcap: [
1038
+ "market_global_context",
1039
+ "market_reference_asset_context",
1040
+ "market_cmc_exchange_liquidity_context",
1041
+ "market_cmc_fear_greed_context",
1042
+ "market_cmc_index_context"
1043
+ ],
1044
+ derivatives: ["derivatives_market"],
1045
+ hyperliquidWhales: [
1046
+ "hyperliquid_whale_flow",
1047
+ "hyperliquid_whale_coverage_1m"
1048
+ ]
1049
+ };
1050
+ var verifyMarketContextSchema = async (source) => {
1051
+ if (verifiedMarketContextSchemas.has(source)) return;
1052
+ const tables = MARKET_CONTEXT_SCHEMA_TABLES[source];
1053
+ const result = await queryMarketContext(
1054
+ `
1055
+ SELECT table_name AS "tableName"
1056
+ FROM unnest($1::text[]) AS requested(table_name)
1057
+ WHERE to_regclass(requested.table_name) IS NULL
1058
+ `,
1059
+ [tables]
1060
+ );
1061
+ if (result.rows.length) {
1062
+ throw new Error(
1063
+ `Timescale ${source} schema is not prepared; missing: ${result.rows.map((row) => row.tableName).filter(Boolean).join(", ")}`
1064
+ );
1065
+ }
1066
+ verifiedMarketContextSchemas.add(source);
1067
+ };
1068
+ var prepareMarketContextSchemaForRead = async (source) => marketContextSchemaMode === "verify" ? verifyMarketContextSchema(source) : ensureMarketContextSchema(source);
1069
+ var ensureMarketContextSchemas = async (sources) => {
1070
+ for (const source of new Set(sources)) {
1071
+ await ensureMarketContextSchema(source);
1072
+ }
1073
+ };
945
1074
  async function upsertDerivatives(rows) {
946
1075
  if (!rows.length) return;
947
1076
  await ensureDerivativesSchema();
@@ -1269,9 +1398,8 @@ async function getDerivativesWindow(params) {
1269
1398
  if (!normalizedSymbol || !normalizedIntervals.length) {
1270
1399
  return {};
1271
1400
  }
1272
- await ensureDerivativesSchema();
1401
+ await prepareMarketContextSchemaForRead("derivatives");
1273
1402
  const startMs = endMs - Math.max(0, lookbackMs);
1274
- const pool = getPool();
1275
1403
  const sql = `
1276
1404
  SELECT symbol, interval, ts, open_interest, funding_rate, liq_long, liq_short, liq_total, source
1277
1405
  FROM derivatives_market
@@ -1281,12 +1409,11 @@ async function getDerivativesWindow(params) {
1281
1409
  AND ts <= to_timestamp($4/1000.0)
1282
1410
  ORDER BY interval ASC, ts ASC
1283
1411
  `;
1284
- const res = await pool.query(sql, [
1285
- normalizedSymbol,
1286
- normalizedIntervals,
1287
- startMs,
1288
- endMs
1289
- ]);
1412
+ const res = await queryMarketContext(
1413
+ sql,
1414
+ [normalizedSymbol, normalizedIntervals, startMs, endMs],
1415
+ params
1416
+ );
1290
1417
  const rowsByInterval = {};
1291
1418
  for (const row of res.rows) {
1292
1419
  const interval = row.interval;
@@ -2493,9 +2620,8 @@ var toMarketFeatureAge = (rowTs, atMs) => {
2493
2620
  return Number.isFinite(ageMs) ? ageMs : null;
2494
2621
  };
2495
2622
  async function getLatestMarketTradeFlow(params) {
2496
- await ensureBinanceMarketSchema();
2497
- const pool = getPool();
2498
- const res = await pool.query(
2623
+ await prepareMarketContextSchemaForRead("binance");
2624
+ const res = await queryMarketContext(
2499
2625
  `
2500
2626
  SELECT
2501
2627
  symbol,
@@ -2517,7 +2643,8 @@ async function getLatestMarketTradeFlow(params) {
2517
2643
  ORDER BY ts DESC
2518
2644
  LIMIT 1
2519
2645
  `,
2520
- [params.symbol.toUpperCase(), params.interval, params.atMs]
2646
+ [params.symbol.toUpperCase(), params.interval, params.atMs],
2647
+ params
2521
2648
  );
2522
2649
  const row = res.rows[0];
2523
2650
  if (!row) return null;
@@ -2768,8 +2895,8 @@ async function upsertHyperliquidWhaleCoverageRows(rows) {
2768
2895
  );
2769
2896
  }
2770
2897
  async function getHyperliquidWhaleCoverageSeriesRows(params) {
2771
- await ensureHyperliquidWhaleSchema();
2772
- const result = await getPool().query(
2898
+ await prepareMarketContextSchemaForRead("hyperliquidWhales");
2899
+ const result = await queryMarketContext(
2773
2900
  `
2774
2901
  SELECT
2775
2902
  ts,
@@ -2790,7 +2917,8 @@ async function getHyperliquidWhaleCoverageSeriesRows(params) {
2790
2917
  import_types.HYPERLIQUID_WHALE_DATA_MODEL_VERSION,
2791
2918
  params.fromMs,
2792
2919
  params.toMs
2793
- ]
2920
+ ],
2921
+ params
2794
2922
  );
2795
2923
  return result.rows.map((row) => ({
2796
2924
  ts: new Date(row.ts),
@@ -2800,8 +2928,8 @@ async function getHyperliquidWhaleCoverageSeriesRows(params) {
2800
2928
  }));
2801
2929
  }
2802
2930
  async function getHyperliquidWhaleFlowSeriesRows(params) {
2803
- await ensureHyperliquidWhaleSchema();
2804
- const result = await getPool().query(
2931
+ await prepareMarketContextSchemaForRead("hyperliquidWhales");
2932
+ const result = await queryMarketContext(
2805
2933
  `
2806
2934
  SELECT
2807
2935
  ts,
@@ -2834,7 +2962,8 @@ async function getHyperliquidWhaleFlowSeriesRows(params) {
2834
2962
  params.whaleRegistryFingerprint,
2835
2963
  params.fromMs,
2836
2964
  params.toMs
2837
- ]
2965
+ ],
2966
+ params
2838
2967
  );
2839
2968
  return result.rows.map((row) => ({
2840
2969
  ts: new Date(row.ts),
@@ -2855,10 +2984,10 @@ async function getHyperliquidWhaleFlowSeriesRows(params) {
2855
2984
  }));
2856
2985
  }
2857
2986
  async function getHyperliquidWhaleFlowAggregate(params) {
2858
- await ensureHyperliquidWhaleSchema();
2987
+ await prepareMarketContextSchemaForRead("hyperliquidWhales");
2859
2988
  const intervalMs = HYPERLIQUID_CONTEXT_INTERVAL_MS[params.interval];
2860
2989
  const expectedBuckets = Math.ceil(intervalMs / 6e4);
2861
- const res = await getPool().query(
2990
+ const res = await queryMarketContext(
2862
2991
  `
2863
2992
  WITH coverage_rows AS (
2864
2993
  SELECT *
@@ -2973,7 +3102,8 @@ async function getHyperliquidWhaleFlowAggregate(params) {
2973
3102
  params.decisionTimeMs,
2974
3103
  intervalMs,
2975
3104
  import_types.HYPERLIQUID_WHALE_DATA_MODEL_VERSION
2976
- ]
3105
+ ],
3106
+ params
2977
3107
  );
2978
3108
  const row = res.rows[0];
2979
3109
  if (!row?.as_of_ts || Number(row.coverage_buckets) !== expectedBuckets || Number(row.covered_whales) <= 0) {
@@ -3046,9 +3176,8 @@ async function hasHyperliquidWhaleBackfillCoverage(params) {
3046
3176
  return Number(result.rows[0]?.buckets) === expectedBuckets && Number(result.rows[0]?.complete_buckets) === expectedBuckets;
3047
3177
  }
3048
3178
  async function getLatestMarketBreadth(params) {
3049
- await ensureBinanceMarketSchema();
3050
- const pool = getPool();
3051
- const res = await pool.query(
3179
+ await prepareMarketContextSchemaForRead("binance");
3180
+ const res = await queryMarketContext(
3052
3181
  `
3053
3182
  SELECT
3054
3183
  universe,
@@ -3087,7 +3216,8 @@ async function getLatestMarketBreadth(params) {
3087
3216
  ORDER BY ts DESC
3088
3217
  LIMIT 1
3089
3218
  `,
3090
- [params.universe, params.interval, params.atMs]
3219
+ [params.universe, params.interval, params.atMs],
3220
+ params
3091
3221
  );
3092
3222
  const row = res.rows[0];
3093
3223
  if (!row) return null;
@@ -3099,10 +3229,9 @@ async function getLatestMarketBreadth(params) {
3099
3229
  };
3100
3230
  }
3101
3231
  async function getLatestMarketGlobalContext(params) {
3102
- await ensureBinanceMarketSchema();
3103
- const pool = getPool();
3232
+ await prepareMarketContextSchemaForRead("coinmarketcap");
3104
3233
  const source = params.source ?? "coinmarketcap_global";
3105
- const res = await pool.query(
3234
+ const res = await queryMarketContext(
3106
3235
  `
3107
3236
  SELECT
3108
3237
  source,
@@ -3131,11 +3260,12 @@ async function getLatestMarketGlobalContext(params) {
3131
3260
  ORDER BY ts DESC
3132
3261
  LIMIT 1
3133
3262
  `,
3134
- [source, params.atMs]
3263
+ [source, params.atMs],
3264
+ params
3135
3265
  );
3136
3266
  const row = res.rows[0];
3137
3267
  if (!row) return null;
3138
- const previousRes = await pool.query(
3268
+ const previousRes = await queryMarketContext(
3139
3269
  `
3140
3270
  SELECT
3141
3271
  btc_dominance_pct AS "btcDominancePct",
@@ -3148,7 +3278,8 @@ async function getLatestMarketGlobalContext(params) {
3148
3278
  ORDER BY ts DESC
3149
3279
  LIMIT 1
3150
3280
  `,
3151
- [source, row.ts]
3281
+ [source, row.ts],
3282
+ params
3152
3283
  );
3153
3284
  const previousDominance = previousRes.rows[0]?.btcDominancePct == null ? null : Number(previousRes.rows[0].btcDominancePct);
3154
3285
  const previousEthDominance = previousRes.rows[0]?.ethDominancePct == null ? null : Number(previousRes.rows[0].ethDominancePct);
@@ -3241,9 +3372,8 @@ async function getLatestMarketReferenceAssetContexts(params) {
3241
3372
  ];
3242
3373
  const rows = /* @__PURE__ */ new Map();
3243
3374
  if (!symbols.length) return rows;
3244
- await ensureBinanceMarketSchema();
3245
- const pool = getPool();
3246
- const res = await pool.query(
3375
+ await prepareMarketContextSchemaForRead("coinmarketcap");
3376
+ const res = await queryMarketContext(
3247
3377
  `
3248
3378
  SELECT DISTINCT ON (symbol)
3249
3379
  source,
@@ -3268,7 +3398,8 @@ async function getLatestMarketReferenceAssetContexts(params) {
3268
3398
  END <= to_timestamp($4/1000.0)
3269
3399
  ORDER BY symbol ASC, ts DESC
3270
3400
  `,
3271
- [source, symbols, interval, params.atMs]
3401
+ [source, symbols, interval, params.atMs],
3402
+ params
3272
3403
  );
3273
3404
  for (const row of res.rows) {
3274
3405
  const ageMs = toMarketFeatureAge(row.ts, params.atMs);
@@ -3281,11 +3412,10 @@ async function getLatestMarketReferenceAssetContexts(params) {
3281
3412
  return rows;
3282
3413
  }
3283
3414
  async function getLatestMarketCmcExchangeLiquidityContext(params) {
3284
- await ensureBinanceMarketSchema();
3285
- const pool = getPool();
3415
+ await prepareMarketContextSchemaForRead("coinmarketcap");
3286
3416
  const source = params.source ?? "coinmarketcap_exchange_liquidity";
3287
3417
  const interval = params.interval ?? "1d";
3288
- const res = await pool.query(
3418
+ const res = await queryMarketContext(
3289
3419
  `
3290
3420
  SELECT
3291
3421
  source,
@@ -3308,11 +3438,12 @@ async function getLatestMarketCmcExchangeLiquidityContext(params) {
3308
3438
  ORDER BY ts DESC
3309
3439
  LIMIT 1
3310
3440
  `,
3311
- [source, interval, params.atMs]
3441
+ [source, interval, params.atMs],
3442
+ params
3312
3443
  );
3313
3444
  const row = res.rows[0];
3314
3445
  if (!row) return null;
3315
- const previousRes = await pool.query(
3446
+ const previousRes = await queryMarketContext(
3316
3447
  `
3317
3448
  SELECT total_volume_usd AS "totalVolumeUsd"
3318
3449
  FROM market_cmc_exchange_liquidity_context
@@ -3322,7 +3453,8 @@ async function getLatestMarketCmcExchangeLiquidityContext(params) {
3322
3453
  ORDER BY ts DESC
3323
3454
  LIMIT 1
3324
3455
  `,
3325
- [source, interval, row.ts]
3456
+ [source, interval, row.ts],
3457
+ params
3326
3458
  );
3327
3459
  const currentTotal = row.totalVolumeUsd == null ? null : Number(row.totalVolumeUsd);
3328
3460
  const previousTotal = previousRes.rows[0]?.totalVolumeUsd == null ? null : Number(previousRes.rows[0].totalVolumeUsd);
@@ -3346,9 +3478,8 @@ async function getLatestMarketCmcIndexContexts(params) {
3346
3478
  ];
3347
3479
  const rows = /* @__PURE__ */ new Map();
3348
3480
  if (!indexSlugs.length) return rows;
3349
- await ensureBinanceMarketSchema();
3350
- const pool = getPool();
3351
- const res = await pool.query(
3481
+ await prepareMarketContextSchemaForRead("coinmarketcap");
3482
+ const res = await queryMarketContext(
3352
3483
  `
3353
3484
  SELECT DISTINCT ON (index_slug)
3354
3485
  source,
@@ -3371,10 +3502,11 @@ async function getLatestMarketCmcIndexContexts(params) {
3371
3502
  END <= to_timestamp($4/1000.0)
3372
3503
  ORDER BY index_slug ASC, ts DESC
3373
3504
  `,
3374
- [source, indexSlugs, interval, params.atMs]
3505
+ [source, indexSlugs, interval, params.atMs],
3506
+ params
3375
3507
  );
3376
3508
  for (const row of res.rows) {
3377
- const previousRes = await pool.query(
3509
+ const previousRes = await queryMarketContext(
3378
3510
  `
3379
3511
  SELECT value
3380
3512
  FROM market_cmc_index_context
@@ -3385,7 +3517,8 @@ async function getLatestMarketCmcIndexContexts(params) {
3385
3517
  ORDER BY ts DESC
3386
3518
  LIMIT 1
3387
3519
  `,
3388
- [source, row.indexSlug, interval, row.ts]
3520
+ [source, row.indexSlug, interval, row.ts],
3521
+ params
3389
3522
  );
3390
3523
  const currentValue = row.value == null ? null : Number(row.value);
3391
3524
  const previousValue = previousRes.rows[0]?.value == null ? null : Number(previousRes.rows[0].value);
@@ -3400,11 +3533,10 @@ async function getLatestMarketCmcIndexContexts(params) {
3400
3533
  return rows;
3401
3534
  }
3402
3535
  async function getLatestMarketCmcFearGreedContext(params) {
3403
- await ensureBinanceMarketSchema();
3404
- const pool = getPool();
3536
+ await prepareMarketContextSchemaForRead("coinmarketcap");
3405
3537
  const source = params.source ?? "coinmarketcap_fear_greed";
3406
3538
  const interval = params.interval ?? "1d";
3407
- const res = await pool.query(
3539
+ const res = await queryMarketContext(
3408
3540
  `
3409
3541
  SELECT
3410
3542
  source,
@@ -3424,11 +3556,12 @@ async function getLatestMarketCmcFearGreedContext(params) {
3424
3556
  ORDER BY ts DESC
3425
3557
  LIMIT 1
3426
3558
  `,
3427
- [source, interval, params.atMs]
3559
+ [source, interval, params.atMs],
3560
+ params
3428
3561
  );
3429
3562
  const row = res.rows[0];
3430
3563
  if (!row) return null;
3431
- const previousRes = await pool.query(
3564
+ const previousRes = await queryMarketContext(
3432
3565
  `
3433
3566
  SELECT
3434
3567
  value::int AS value,
@@ -3440,9 +3573,10 @@ async function getLatestMarketCmcFearGreedContext(params) {
3440
3573
  ORDER BY ts DESC
3441
3574
  LIMIT 1
3442
3575
  `,
3443
- [source, interval, row.ts]
3576
+ [source, interval, row.ts],
3577
+ params
3444
3578
  );
3445
- const previous7dRes = await pool.query(
3579
+ const previous7dRes = await queryMarketContext(
3446
3580
  `
3447
3581
  SELECT value::int AS value
3448
3582
  FROM market_cmc_fear_greed_context
@@ -3452,7 +3586,8 @@ async function getLatestMarketCmcFearGreedContext(params) {
3452
3586
  ORDER BY ts DESC
3453
3587
  LIMIT 1
3454
3588
  `,
3455
- [source, interval, row.ts]
3589
+ [source, interval, row.ts],
3590
+ params
3456
3591
  );
3457
3592
  const previousValue = previousRes.rows[0]?.value == null ? null : Number(previousRes.rows[0].value);
3458
3593
  const previous7dValue = previous7dRes.rows[0]?.value == null ? null : Number(previous7dRes.rows[0].value);
@@ -3939,7 +4074,13 @@ async function findContinuityGap(provider, symbol, interval) {
3939
4074
  applyDerivativesMetricCoverage,
3940
4075
  cleanupDeprecatedMarketContext,
3941
4076
  closeTimescalePool,
4077
+ configureTimescaleMarketContextSchemaMode,
3942
4078
  deleteCandles,
4079
+ ensureBinanceMarketSchema,
4080
+ ensureCoinMarketCapContextSchema,
4081
+ ensureDerivativesSchema,
4082
+ ensureHyperliquidWhaleSchema,
4083
+ ensureMarketContextSchemas,
3943
4084
  findContinuityGap,
3944
4085
  getCandlesRange,
3945
4086
  getDataEdges,
@@ -37,6 +37,12 @@ var derivativesSchemaReadyPromise = null;
37
37
  var spreadSchemaReadyPromise = null;
38
38
  var binanceMarketSchemaReadyPromise = null;
39
39
  var hyperliquidWhaleSchemaReadyPromise = null;
40
+ var marketContextSchemaMode = "ensure";
41
+ var verifiedMarketContextSchemas = /* @__PURE__ */ new Set();
42
+ var configureTimescaleMarketContextSchemaMode = (mode) => {
43
+ marketContextSchemaMode = mode;
44
+ verifiedMarketContextSchemas.clear();
45
+ };
40
46
  var closeTimescalePool = async () => {
41
47
  const pool = global.__pgPool__;
42
48
  if (!pool) {
@@ -53,6 +59,7 @@ var closeTimescalePool = async () => {
53
59
  spreadSchemaReadyPromise = null;
54
60
  binanceMarketSchemaReadyPromise = null;
55
61
  hyperliquidWhaleSchemaReadyPromise = null;
62
+ verifiedMarketContextSchemas.clear();
56
63
  await pool.end();
57
64
  };
58
65
  var CANDLES_SCHEMA_LOCK_KEY = 61e4;
@@ -61,6 +68,70 @@ var SPREAD_SCHEMA_LOCK_KEY = 610002;
61
68
  var BINANCE_MARKET_SCHEMA_LOCK_KEY = 610003;
62
69
  var HYPERLIQUID_WHALE_SCHEMA_LOCK_KEY = 610004;
63
70
  var PG_SAFE_MAX_BIND_PARAMS = 3e4;
71
+ var resolveMarketContextQueryTimeoutMs = (override) => {
72
+ if (Number.isFinite(override) && Number(override) > 0) {
73
+ return Math.floor(Number(override));
74
+ }
75
+ const configured = Number(process.env.MARKET_CONTEXT_SQL_TIMEOUT_MS);
76
+ return Number.isFinite(configured) && configured > 0 ? Math.floor(configured) : 3e4;
77
+ };
78
+ var createMarketContextQueryError = (name, message) => {
79
+ const error = new Error(message);
80
+ error.name = name;
81
+ return error;
82
+ };
83
+ var queryMarketContext = async (text, values, options = {}) => {
84
+ const pool = getPool();
85
+ const client = await pool.connect();
86
+ const timeoutMs = resolveMarketContextQueryTimeoutMs(options.timeoutMs);
87
+ let released = false;
88
+ let rejectCancellation;
89
+ const release = (error) => {
90
+ if (released) return;
91
+ released = true;
92
+ client.release(error);
93
+ };
94
+ const cancellation = new Promise((_resolve, reject) => {
95
+ rejectCancellation = reject;
96
+ });
97
+ const cancel = (error) => {
98
+ release(error);
99
+ rejectCancellation?.(error);
100
+ };
101
+ const onAbort = () => cancel(
102
+ createMarketContextQueryError(
103
+ "AbortError",
104
+ "Timescale market-context query aborted"
105
+ )
106
+ );
107
+ const timer = setTimeout(
108
+ () => cancel(
109
+ createMarketContextQueryError(
110
+ "TimescaleQueryTimeoutError",
111
+ `Timescale market-context query exceeded ${timeoutMs}ms`
112
+ )
113
+ ),
114
+ timeoutMs
115
+ );
116
+ timer.unref?.();
117
+ options.signal?.addEventListener("abort", onAbort, { once: true });
118
+ try {
119
+ if (options.signal?.aborted) {
120
+ const error = createMarketContextQueryError(
121
+ "AbortError",
122
+ "Timescale market-context query aborted"
123
+ );
124
+ release(error);
125
+ throw error;
126
+ }
127
+ const query = client.query(text, values);
128
+ return await Promise.race([query, cancellation]);
129
+ } finally {
130
+ clearTimeout(timer);
131
+ options.signal?.removeEventListener("abort", onAbort);
132
+ release();
133
+ }
134
+ };
64
135
  var normalizeCandleProvider = (provider) => String(provider || "").trim().toLowerCase();
65
136
  var normalizeCandleSymbol = (symbol) => String(symbol || "").trim().toUpperCase();
66
137
  var getSafeBulkInsertRows = (columnsCount) => Math.max(1, Math.floor(PG_SAFE_MAX_BIND_PARAMS / columnsCount));
@@ -865,6 +936,58 @@ var ensureHyperliquidWhaleSchema = async () => {
865
936
  });
866
937
  await hyperliquidWhaleSchemaReadyPromise;
867
938
  };
939
+ var ensureCoinMarketCapContextSchema = async () => ensureBinanceMarketSchema();
940
+ var ensureMarketContextSchema = async (source) => {
941
+ switch (source) {
942
+ case "binance":
943
+ return ensureBinanceMarketSchema();
944
+ case "coinmarketcap":
945
+ return ensureCoinMarketCapContextSchema();
946
+ case "derivatives":
947
+ return ensureDerivativesSchema();
948
+ case "hyperliquidWhales":
949
+ return ensureHyperliquidWhaleSchema();
950
+ }
951
+ };
952
+ var MARKET_CONTEXT_SCHEMA_TABLES = {
953
+ binance: ["market_trade_flow", "market_breadth"],
954
+ coinmarketcap: [
955
+ "market_global_context",
956
+ "market_reference_asset_context",
957
+ "market_cmc_exchange_liquidity_context",
958
+ "market_cmc_fear_greed_context",
959
+ "market_cmc_index_context"
960
+ ],
961
+ derivatives: ["derivatives_market"],
962
+ hyperliquidWhales: [
963
+ "hyperliquid_whale_flow",
964
+ "hyperliquid_whale_coverage_1m"
965
+ ]
966
+ };
967
+ var verifyMarketContextSchema = async (source) => {
968
+ if (verifiedMarketContextSchemas.has(source)) return;
969
+ const tables = MARKET_CONTEXT_SCHEMA_TABLES[source];
970
+ const result = await queryMarketContext(
971
+ `
972
+ SELECT table_name AS "tableName"
973
+ FROM unnest($1::text[]) AS requested(table_name)
974
+ WHERE to_regclass(requested.table_name) IS NULL
975
+ `,
976
+ [tables]
977
+ );
978
+ if (result.rows.length) {
979
+ throw new Error(
980
+ `Timescale ${source} schema is not prepared; missing: ${result.rows.map((row) => row.tableName).filter(Boolean).join(", ")}`
981
+ );
982
+ }
983
+ verifiedMarketContextSchemas.add(source);
984
+ };
985
+ var prepareMarketContextSchemaForRead = async (source) => marketContextSchemaMode === "verify" ? verifyMarketContextSchema(source) : ensureMarketContextSchema(source);
986
+ var ensureMarketContextSchemas = async (sources) => {
987
+ for (const source of new Set(sources)) {
988
+ await ensureMarketContextSchema(source);
989
+ }
990
+ };
868
991
  async function upsertDerivatives(rows) {
869
992
  if (!rows.length) return;
870
993
  await ensureDerivativesSchema();
@@ -1192,9 +1315,8 @@ async function getDerivativesWindow(params) {
1192
1315
  if (!normalizedSymbol || !normalizedIntervals.length) {
1193
1316
  return {};
1194
1317
  }
1195
- await ensureDerivativesSchema();
1318
+ await prepareMarketContextSchemaForRead("derivatives");
1196
1319
  const startMs = endMs - Math.max(0, lookbackMs);
1197
- const pool = getPool();
1198
1320
  const sql = `
1199
1321
  SELECT symbol, interval, ts, open_interest, funding_rate, liq_long, liq_short, liq_total, source
1200
1322
  FROM derivatives_market
@@ -1204,12 +1326,11 @@ async function getDerivativesWindow(params) {
1204
1326
  AND ts <= to_timestamp($4/1000.0)
1205
1327
  ORDER BY interval ASC, ts ASC
1206
1328
  `;
1207
- const res = await pool.query(sql, [
1208
- normalizedSymbol,
1209
- normalizedIntervals,
1210
- startMs,
1211
- endMs
1212
- ]);
1329
+ const res = await queryMarketContext(
1330
+ sql,
1331
+ [normalizedSymbol, normalizedIntervals, startMs, endMs],
1332
+ params
1333
+ );
1213
1334
  const rowsByInterval = {};
1214
1335
  for (const row of res.rows) {
1215
1336
  const interval = row.interval;
@@ -2416,9 +2537,8 @@ var toMarketFeatureAge = (rowTs, atMs) => {
2416
2537
  return Number.isFinite(ageMs) ? ageMs : null;
2417
2538
  };
2418
2539
  async function getLatestMarketTradeFlow(params) {
2419
- await ensureBinanceMarketSchema();
2420
- const pool = getPool();
2421
- const res = await pool.query(
2540
+ await prepareMarketContextSchemaForRead("binance");
2541
+ const res = await queryMarketContext(
2422
2542
  `
2423
2543
  SELECT
2424
2544
  symbol,
@@ -2440,7 +2560,8 @@ async function getLatestMarketTradeFlow(params) {
2440
2560
  ORDER BY ts DESC
2441
2561
  LIMIT 1
2442
2562
  `,
2443
- [params.symbol.toUpperCase(), params.interval, params.atMs]
2563
+ [params.symbol.toUpperCase(), params.interval, params.atMs],
2564
+ params
2444
2565
  );
2445
2566
  const row = res.rows[0];
2446
2567
  if (!row) return null;
@@ -2691,8 +2812,8 @@ async function upsertHyperliquidWhaleCoverageRows(rows) {
2691
2812
  );
2692
2813
  }
2693
2814
  async function getHyperliquidWhaleCoverageSeriesRows(params) {
2694
- await ensureHyperliquidWhaleSchema();
2695
- const result = await getPool().query(
2815
+ await prepareMarketContextSchemaForRead("hyperliquidWhales");
2816
+ const result = await queryMarketContext(
2696
2817
  `
2697
2818
  SELECT
2698
2819
  ts,
@@ -2713,7 +2834,8 @@ async function getHyperliquidWhaleCoverageSeriesRows(params) {
2713
2834
  HYPERLIQUID_WHALE_DATA_MODEL_VERSION,
2714
2835
  params.fromMs,
2715
2836
  params.toMs
2716
- ]
2837
+ ],
2838
+ params
2717
2839
  );
2718
2840
  return result.rows.map((row) => ({
2719
2841
  ts: new Date(row.ts),
@@ -2723,8 +2845,8 @@ async function getHyperliquidWhaleCoverageSeriesRows(params) {
2723
2845
  }));
2724
2846
  }
2725
2847
  async function getHyperliquidWhaleFlowSeriesRows(params) {
2726
- await ensureHyperliquidWhaleSchema();
2727
- const result = await getPool().query(
2848
+ await prepareMarketContextSchemaForRead("hyperliquidWhales");
2849
+ const result = await queryMarketContext(
2728
2850
  `
2729
2851
  SELECT
2730
2852
  ts,
@@ -2757,7 +2879,8 @@ async function getHyperliquidWhaleFlowSeriesRows(params) {
2757
2879
  params.whaleRegistryFingerprint,
2758
2880
  params.fromMs,
2759
2881
  params.toMs
2760
- ]
2882
+ ],
2883
+ params
2761
2884
  );
2762
2885
  return result.rows.map((row) => ({
2763
2886
  ts: new Date(row.ts),
@@ -2778,10 +2901,10 @@ async function getHyperliquidWhaleFlowSeriesRows(params) {
2778
2901
  }));
2779
2902
  }
2780
2903
  async function getHyperliquidWhaleFlowAggregate(params) {
2781
- await ensureHyperliquidWhaleSchema();
2904
+ await prepareMarketContextSchemaForRead("hyperliquidWhales");
2782
2905
  const intervalMs = HYPERLIQUID_CONTEXT_INTERVAL_MS[params.interval];
2783
2906
  const expectedBuckets = Math.ceil(intervalMs / 6e4);
2784
- const res = await getPool().query(
2907
+ const res = await queryMarketContext(
2785
2908
  `
2786
2909
  WITH coverage_rows AS (
2787
2910
  SELECT *
@@ -2896,7 +3019,8 @@ async function getHyperliquidWhaleFlowAggregate(params) {
2896
3019
  params.decisionTimeMs,
2897
3020
  intervalMs,
2898
3021
  HYPERLIQUID_WHALE_DATA_MODEL_VERSION
2899
- ]
3022
+ ],
3023
+ params
2900
3024
  );
2901
3025
  const row = res.rows[0];
2902
3026
  if (!row?.as_of_ts || Number(row.coverage_buckets) !== expectedBuckets || Number(row.covered_whales) <= 0) {
@@ -2969,9 +3093,8 @@ async function hasHyperliquidWhaleBackfillCoverage(params) {
2969
3093
  return Number(result.rows[0]?.buckets) === expectedBuckets && Number(result.rows[0]?.complete_buckets) === expectedBuckets;
2970
3094
  }
2971
3095
  async function getLatestMarketBreadth(params) {
2972
- await ensureBinanceMarketSchema();
2973
- const pool = getPool();
2974
- const res = await pool.query(
3096
+ await prepareMarketContextSchemaForRead("binance");
3097
+ const res = await queryMarketContext(
2975
3098
  `
2976
3099
  SELECT
2977
3100
  universe,
@@ -3010,7 +3133,8 @@ async function getLatestMarketBreadth(params) {
3010
3133
  ORDER BY ts DESC
3011
3134
  LIMIT 1
3012
3135
  `,
3013
- [params.universe, params.interval, params.atMs]
3136
+ [params.universe, params.interval, params.atMs],
3137
+ params
3014
3138
  );
3015
3139
  const row = res.rows[0];
3016
3140
  if (!row) return null;
@@ -3022,10 +3146,9 @@ async function getLatestMarketBreadth(params) {
3022
3146
  };
3023
3147
  }
3024
3148
  async function getLatestMarketGlobalContext(params) {
3025
- await ensureBinanceMarketSchema();
3026
- const pool = getPool();
3149
+ await prepareMarketContextSchemaForRead("coinmarketcap");
3027
3150
  const source = params.source ?? "coinmarketcap_global";
3028
- const res = await pool.query(
3151
+ const res = await queryMarketContext(
3029
3152
  `
3030
3153
  SELECT
3031
3154
  source,
@@ -3054,11 +3177,12 @@ async function getLatestMarketGlobalContext(params) {
3054
3177
  ORDER BY ts DESC
3055
3178
  LIMIT 1
3056
3179
  `,
3057
- [source, params.atMs]
3180
+ [source, params.atMs],
3181
+ params
3058
3182
  );
3059
3183
  const row = res.rows[0];
3060
3184
  if (!row) return null;
3061
- const previousRes = await pool.query(
3185
+ const previousRes = await queryMarketContext(
3062
3186
  `
3063
3187
  SELECT
3064
3188
  btc_dominance_pct AS "btcDominancePct",
@@ -3071,7 +3195,8 @@ async function getLatestMarketGlobalContext(params) {
3071
3195
  ORDER BY ts DESC
3072
3196
  LIMIT 1
3073
3197
  `,
3074
- [source, row.ts]
3198
+ [source, row.ts],
3199
+ params
3075
3200
  );
3076
3201
  const previousDominance = previousRes.rows[0]?.btcDominancePct == null ? null : Number(previousRes.rows[0].btcDominancePct);
3077
3202
  const previousEthDominance = previousRes.rows[0]?.ethDominancePct == null ? null : Number(previousRes.rows[0].ethDominancePct);
@@ -3164,9 +3289,8 @@ async function getLatestMarketReferenceAssetContexts(params) {
3164
3289
  ];
3165
3290
  const rows = /* @__PURE__ */ new Map();
3166
3291
  if (!symbols.length) return rows;
3167
- await ensureBinanceMarketSchema();
3168
- const pool = getPool();
3169
- const res = await pool.query(
3292
+ await prepareMarketContextSchemaForRead("coinmarketcap");
3293
+ const res = await queryMarketContext(
3170
3294
  `
3171
3295
  SELECT DISTINCT ON (symbol)
3172
3296
  source,
@@ -3191,7 +3315,8 @@ async function getLatestMarketReferenceAssetContexts(params) {
3191
3315
  END <= to_timestamp($4/1000.0)
3192
3316
  ORDER BY symbol ASC, ts DESC
3193
3317
  `,
3194
- [source, symbols, interval, params.atMs]
3318
+ [source, symbols, interval, params.atMs],
3319
+ params
3195
3320
  );
3196
3321
  for (const row of res.rows) {
3197
3322
  const ageMs = toMarketFeatureAge(row.ts, params.atMs);
@@ -3204,11 +3329,10 @@ async function getLatestMarketReferenceAssetContexts(params) {
3204
3329
  return rows;
3205
3330
  }
3206
3331
  async function getLatestMarketCmcExchangeLiquidityContext(params) {
3207
- await ensureBinanceMarketSchema();
3208
- const pool = getPool();
3332
+ await prepareMarketContextSchemaForRead("coinmarketcap");
3209
3333
  const source = params.source ?? "coinmarketcap_exchange_liquidity";
3210
3334
  const interval = params.interval ?? "1d";
3211
- const res = await pool.query(
3335
+ const res = await queryMarketContext(
3212
3336
  `
3213
3337
  SELECT
3214
3338
  source,
@@ -3231,11 +3355,12 @@ async function getLatestMarketCmcExchangeLiquidityContext(params) {
3231
3355
  ORDER BY ts DESC
3232
3356
  LIMIT 1
3233
3357
  `,
3234
- [source, interval, params.atMs]
3358
+ [source, interval, params.atMs],
3359
+ params
3235
3360
  );
3236
3361
  const row = res.rows[0];
3237
3362
  if (!row) return null;
3238
- const previousRes = await pool.query(
3363
+ const previousRes = await queryMarketContext(
3239
3364
  `
3240
3365
  SELECT total_volume_usd AS "totalVolumeUsd"
3241
3366
  FROM market_cmc_exchange_liquidity_context
@@ -3245,7 +3370,8 @@ async function getLatestMarketCmcExchangeLiquidityContext(params) {
3245
3370
  ORDER BY ts DESC
3246
3371
  LIMIT 1
3247
3372
  `,
3248
- [source, interval, row.ts]
3373
+ [source, interval, row.ts],
3374
+ params
3249
3375
  );
3250
3376
  const currentTotal = row.totalVolumeUsd == null ? null : Number(row.totalVolumeUsd);
3251
3377
  const previousTotal = previousRes.rows[0]?.totalVolumeUsd == null ? null : Number(previousRes.rows[0].totalVolumeUsd);
@@ -3269,9 +3395,8 @@ async function getLatestMarketCmcIndexContexts(params) {
3269
3395
  ];
3270
3396
  const rows = /* @__PURE__ */ new Map();
3271
3397
  if (!indexSlugs.length) return rows;
3272
- await ensureBinanceMarketSchema();
3273
- const pool = getPool();
3274
- const res = await pool.query(
3398
+ await prepareMarketContextSchemaForRead("coinmarketcap");
3399
+ const res = await queryMarketContext(
3275
3400
  `
3276
3401
  SELECT DISTINCT ON (index_slug)
3277
3402
  source,
@@ -3294,10 +3419,11 @@ async function getLatestMarketCmcIndexContexts(params) {
3294
3419
  END <= to_timestamp($4/1000.0)
3295
3420
  ORDER BY index_slug ASC, ts DESC
3296
3421
  `,
3297
- [source, indexSlugs, interval, params.atMs]
3422
+ [source, indexSlugs, interval, params.atMs],
3423
+ params
3298
3424
  );
3299
3425
  for (const row of res.rows) {
3300
- const previousRes = await pool.query(
3426
+ const previousRes = await queryMarketContext(
3301
3427
  `
3302
3428
  SELECT value
3303
3429
  FROM market_cmc_index_context
@@ -3308,7 +3434,8 @@ async function getLatestMarketCmcIndexContexts(params) {
3308
3434
  ORDER BY ts DESC
3309
3435
  LIMIT 1
3310
3436
  `,
3311
- [source, row.indexSlug, interval, row.ts]
3437
+ [source, row.indexSlug, interval, row.ts],
3438
+ params
3312
3439
  );
3313
3440
  const currentValue = row.value == null ? null : Number(row.value);
3314
3441
  const previousValue = previousRes.rows[0]?.value == null ? null : Number(previousRes.rows[0].value);
@@ -3323,11 +3450,10 @@ async function getLatestMarketCmcIndexContexts(params) {
3323
3450
  return rows;
3324
3451
  }
3325
3452
  async function getLatestMarketCmcFearGreedContext(params) {
3326
- await ensureBinanceMarketSchema();
3327
- const pool = getPool();
3453
+ await prepareMarketContextSchemaForRead("coinmarketcap");
3328
3454
  const source = params.source ?? "coinmarketcap_fear_greed";
3329
3455
  const interval = params.interval ?? "1d";
3330
- const res = await pool.query(
3456
+ const res = await queryMarketContext(
3331
3457
  `
3332
3458
  SELECT
3333
3459
  source,
@@ -3347,11 +3473,12 @@ async function getLatestMarketCmcFearGreedContext(params) {
3347
3473
  ORDER BY ts DESC
3348
3474
  LIMIT 1
3349
3475
  `,
3350
- [source, interval, params.atMs]
3476
+ [source, interval, params.atMs],
3477
+ params
3351
3478
  );
3352
3479
  const row = res.rows[0];
3353
3480
  if (!row) return null;
3354
- const previousRes = await pool.query(
3481
+ const previousRes = await queryMarketContext(
3355
3482
  `
3356
3483
  SELECT
3357
3484
  value::int AS value,
@@ -3363,9 +3490,10 @@ async function getLatestMarketCmcFearGreedContext(params) {
3363
3490
  ORDER BY ts DESC
3364
3491
  LIMIT 1
3365
3492
  `,
3366
- [source, interval, row.ts]
3493
+ [source, interval, row.ts],
3494
+ params
3367
3495
  );
3368
- const previous7dRes = await pool.query(
3496
+ const previous7dRes = await queryMarketContext(
3369
3497
  `
3370
3498
  SELECT value::int AS value
3371
3499
  FROM market_cmc_fear_greed_context
@@ -3375,7 +3503,8 @@ async function getLatestMarketCmcFearGreedContext(params) {
3375
3503
  ORDER BY ts DESC
3376
3504
  LIMIT 1
3377
3505
  `,
3378
- [source, interval, row.ts]
3506
+ [source, interval, row.ts],
3507
+ params
3379
3508
  );
3380
3509
  const previousValue = previousRes.rows[0]?.value == null ? null : Number(previousRes.rows[0].value);
3381
3510
  const previous7dValue = previous7dRes.rows[0]?.value == null ? null : Number(previous7dRes.rows[0].value);
@@ -3861,7 +3990,13 @@ export {
3861
3990
  applyDerivativesMetricCoverage,
3862
3991
  cleanupDeprecatedMarketContext,
3863
3992
  closeTimescalePool,
3993
+ configureTimescaleMarketContextSchemaMode,
3864
3994
  deleteCandles,
3995
+ ensureBinanceMarketSchema,
3996
+ ensureCoinMarketCapContextSchema,
3997
+ ensureDerivativesSchema,
3998
+ ensureHyperliquidWhaleSchema,
3999
+ ensureMarketContextSchemas,
3865
4000
  findContinuityGap,
3866
4001
  getCandlesRange,
3867
4002
  getDataEdges,
@@ -351,6 +351,8 @@ var redisKeys = {
351
351
  runtimeTrade: (userName, orderId) => `users:${userName}:runtime:trade-records:${orderId}`,
352
352
  runtimeTradeBuckets: (userName) => `users:${userName}:runtime:trade-records:days:`,
353
353
  runtimeTradeBucket: (userName, dayKey) => `users:${userName}:runtime:trade-records:days:${dayKey}`,
354
+ runtimeClosedTradeBuckets: (userName) => `users:${userName}:runtime:closed-trade-records:days:`,
355
+ runtimeClosedTradeBucket: (userName, dayKey) => `users:${userName}:runtime:closed-trade-records:days:${dayKey}`,
354
356
  runtimeActiveTrades: (userName) => `users:${userName}:runtime:active-trades:`,
355
357
  runtimeActiveTrade: (userName, symbol, scopeId) => scopeId ? `users:${userName}:runtime:active-trades:${scopeId}:${symbol}` : `users:${userName}:runtime:active-trades:${symbol}`,
356
358
  aiChatHistory: (userName, symbolKey) => `users:${userName}:ai:chats:${symbolKey}`,
@@ -4,7 +4,7 @@ import {
4
4
  getKeys,
5
5
  redisKeys,
6
6
  setData
7
- } from "./chunk-EFCQ7NGN.mjs";
7
+ } from "./chunk-6ZKAUA4Q.mjs";
8
8
 
9
9
  // src/tradingAccounts.ts
10
10
  var normalizeId = (value, label) => {
@@ -583,6 +583,8 @@ var redisKeys = {
583
583
  runtimeTrade: (userName, orderId) => `users:${userName}:runtime:trade-records:${orderId}`,
584
584
  runtimeTradeBuckets: (userName) => `users:${userName}:runtime:trade-records:days:`,
585
585
  runtimeTradeBucket: (userName, dayKey) => `users:${userName}:runtime:trade-records:days:${dayKey}`,
586
+ runtimeClosedTradeBuckets: (userName) => `users:${userName}:runtime:closed-trade-records:days:`,
587
+ runtimeClosedTradeBucket: (userName, dayKey) => `users:${userName}:runtime:closed-trade-records:days:${dayKey}`,
586
588
  runtimeActiveTrades: (userName) => `users:${userName}:runtime:active-trades:`,
587
589
  runtimeActiveTrade: (userName, symbol, scopeId) => scopeId ? `users:${userName}:runtime:active-trades:${scopeId}:${symbol}` : `users:${userName}:runtime:active-trades:${symbol}`,
588
590
  aiChatHistory: (userName, symbolKey) => `users:${userName}:ai:chats:${symbolKey}`,
@@ -2,7 +2,7 @@ import {
2
2
  getData,
3
3
  redisKeys,
4
4
  setData
5
- } from "./chunk-EFCQ7NGN.mjs";
5
+ } from "./chunk-6ZKAUA4Q.mjs";
6
6
  import {
7
7
  DEFAULT_AI_RESPONSE_LANGUAGE,
8
8
  normalizeAiResponseLanguage
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tradejs/infra",
3
- "version": "2.0.15",
3
+ "version": "2.0.16",
4
4
  "description": "MIT-licensed server infrastructure adapters for TradeJS: Redis, Timescale, ML, logging, and IO.",
5
5
  "keywords": [
6
6
  "tradejs",
@@ -93,7 +93,7 @@
93
93
  "dependencies": {
94
94
  "@grpc/grpc-js": "^1.14.4",
95
95
  "@grpc/proto-loader": "^0.8.1",
96
- "@tradejs/types": "^2.0.15",
96
+ "@tradejs/types": "^2.0.16",
97
97
  "chalk": "4.1.2",
98
98
  "ioredis": "5.11.1",
99
99
  "pg": "8.22.0",