feat: 接入 USDT-M 合约数据 — type 字段方案

- PairType 定义移至 types/kline.ts (spot/um/cm)
- Kline 接口新增 type 字段,全链路透传
- klines 5列复合主键 (exchange, symbol, type, interval, time)
- 拆出 BinanceFuturesRestClient (USDMClient)
- exchanges/index.ts 注册 binance_futures
- trading_pairs 唯一约束加 type,种子数据加合约对
- 12个连续聚合视图 SELECT/GROUP BY/INDEX 加 type
- 清理 bnkline.ts 废弃代码和 pair.ts 空函数
This commit is contained in:
Rekey
2026-06-16 18:39:40 +08:00
parent 1adb093100
commit 705a2f6ea0
15 changed files with 442 additions and 209 deletions
+41 -30
View File
@@ -4,6 +4,8 @@
-- 从 klines(1m)基表创建分层连续聚合物化视图链:
-- 1m → 3m → 5m → 15m → 30m → 1h → 2h → 4h → 6h → 8h → 1d → 1w → 1mon
--
-- GROUP BY 包含 type 列,现货与合约数据隔离聚合。
--
-- 执行前提:
-- 1. klines hypertable 已创建(由 02-init-tables.sql 创建)
-- 2. klines 表中已有数据(至少一条,否则视图创建成功但无数据)
@@ -45,6 +47,7 @@ SELECT
time_bucket('3 minutes', time) AS time,
exchange,
symbol,
type,
'3m'::text AS interval,
FIRST(open, time) AS open,
MAX(high) AS high,
@@ -56,7 +59,7 @@ SELECT
SUM(taker_buy_quote_vol) AS taker_buy_quote_vol,
SUM(trade_count)::integer AS trade_count
FROM klines
GROUP BY time_bucket('3 minutes', klines.time), exchange, symbol
GROUP BY time_bucket('3 minutes', klines.time), exchange, symbol, type
WITH NO DATA;
-- 【模式 A 用户】取消下面注释以启用定时调度刷新
@@ -76,6 +79,7 @@ SELECT
time_bucket('5 minutes', time) AS time,
exchange,
symbol,
type,
'5m'::text AS interval,
FIRST(open, time) AS open,
MAX(high) AS high,
@@ -87,7 +91,7 @@ SELECT
SUM(taker_buy_quote_vol) AS taker_buy_quote_vol,
SUM(trade_count)::integer AS trade_count
FROM klines
GROUP BY time_bucket('5 minutes', klines.time), exchange, symbol
GROUP BY time_bucket('5 minutes', klines.time), exchange, symbol, type
WITH NO DATA;
-- 【模式 A 用户】取消下面注释以启用定时调度刷新
@@ -107,6 +111,7 @@ SELECT
time_bucket('15 minutes', time) AS time,
exchange,
symbol,
type,
'15m'::text AS interval,
FIRST(open, time) AS open,
MAX(high) AS high,
@@ -118,7 +123,7 @@ SELECT
SUM(taker_buy_quote_vol) AS taker_buy_quote_vol,
SUM(trade_count)::integer AS trade_count
FROM klines_5m
GROUP BY time_bucket('15 minutes', klines_5m.time), exchange, symbol
GROUP BY time_bucket('15 minutes', klines_5m.time), exchange, symbol, type
WITH NO DATA;
-- 【模式 A 用户】取消下面注释以启用定时调度刷新
@@ -138,6 +143,7 @@ SELECT
time_bucket('30 minutes', time) AS time,
exchange,
symbol,
type,
'30m'::text AS interval,
FIRST(open, time) AS open,
MAX(high) AS high,
@@ -149,7 +155,7 @@ SELECT
SUM(taker_buy_quote_vol) AS taker_buy_quote_vol,
SUM(trade_count)::integer AS trade_count
FROM klines_15m
GROUP BY time_bucket('30 minutes', klines_15m.time), exchange, symbol
GROUP BY time_bucket('30 minutes', klines_15m.time), exchange, symbol, type
WITH NO DATA;
-- 【模式 A 用户】取消下面注释以启用定时调度刷新
@@ -169,6 +175,7 @@ SELECT
time_bucket('1 hour', time) AS time,
exchange,
symbol,
type,
'1h'::text AS interval,
FIRST(open, time) AS open,
MAX(high) AS high,
@@ -180,7 +187,7 @@ SELECT
SUM(taker_buy_quote_vol) AS taker_buy_quote_vol,
SUM(trade_count)::integer AS trade_count
FROM klines_30m
GROUP BY time_bucket('1 hour', klines_30m.time), exchange, symbol
GROUP BY time_bucket('1 hour', klines_30m.time), exchange, symbol, type
WITH NO DATA;
-- 【模式 A 用户】取消下面注释以启用定时调度刷新
@@ -200,6 +207,7 @@ SELECT
time_bucket('2 hours', time) AS time,
exchange,
symbol,
type,
'2h'::text AS interval,
FIRST(open, time) AS open,
MAX(high) AS high,
@@ -211,7 +219,7 @@ SELECT
SUM(taker_buy_quote_vol) AS taker_buy_quote_vol,
SUM(trade_count)::integer AS trade_count
FROM klines_1h
GROUP BY time_bucket('2 hours', klines_1h.time), exchange, symbol
GROUP BY time_bucket('2 hours', klines_1h.time), exchange, symbol, type
WITH NO DATA;
-- 【模式 A 用户】取消下面注释以启用定时调度刷新
@@ -231,6 +239,7 @@ SELECT
time_bucket('4 hours', time) AS time,
exchange,
symbol,
type,
'4h'::text AS interval,
FIRST(open, time) AS open,
MAX(high) AS high,
@@ -242,7 +251,7 @@ SELECT
SUM(taker_buy_quote_vol) AS taker_buy_quote_vol,
SUM(trade_count)::integer AS trade_count
FROM klines_1h
GROUP BY time_bucket('4 hours', klines_1h.time), exchange, symbol
GROUP BY time_bucket('4 hours', klines_1h.time), exchange, symbol, type
WITH NO DATA;
-- 【模式 A 用户】取消下面注释以启用定时调度刷新
@@ -262,6 +271,7 @@ SELECT
time_bucket('6 hours', time) AS time,
exchange,
symbol,
type,
'6h'::text AS interval,
FIRST(open, time) AS open,
MAX(high) AS high,
@@ -273,7 +283,7 @@ SELECT
SUM(taker_buy_quote_vol) AS taker_buy_quote_vol,
SUM(trade_count)::integer AS trade_count
FROM klines_1h
GROUP BY time_bucket('6 hours', klines_1h.time), exchange, symbol
GROUP BY time_bucket('6 hours', klines_1h.time), exchange, symbol, type
WITH NO DATA;
-- 【模式 A 用户】取消下面注释以启用定时调度刷新
@@ -293,6 +303,7 @@ SELECT
time_bucket('8 hours', time) AS time,
exchange,
symbol,
type,
'8h'::text AS interval,
FIRST(open, time) AS open,
MAX(high) AS high,
@@ -304,7 +315,7 @@ SELECT
SUM(taker_buy_quote_vol) AS taker_buy_quote_vol,
SUM(trade_count)::integer AS trade_count
FROM klines_4h
GROUP BY time_bucket('8 hours', klines_4h.time), exchange, symbol
GROUP BY time_bucket('8 hours', klines_4h.time), exchange, symbol, type
WITH NO DATA;
-- 【模式 A 用户】取消下面注释以启用定时调度刷新
@@ -324,6 +335,7 @@ SELECT
time_bucket('1 day', time) AS time,
exchange,
symbol,
type,
'1d'::text AS interval,
FIRST(open, time) AS open,
MAX(high) AS high,
@@ -335,7 +347,7 @@ SELECT
SUM(taker_buy_quote_vol) AS taker_buy_quote_vol,
SUM(trade_count)::integer AS trade_count
FROM klines_4h
GROUP BY time_bucket('1 day', klines_4h.time), exchange, symbol
GROUP BY time_bucket('1 day', klines_4h.time), exchange, symbol, type
WITH NO DATA;
-- 【模式 A 用户】取消下面注释以启用定时调度刷新
@@ -355,6 +367,7 @@ SELECT
time_bucket('1 week', time) AS time,
exchange,
symbol,
type,
'1w'::text AS interval,
FIRST(open, time) AS open,
MAX(high) AS high,
@@ -366,7 +379,7 @@ SELECT
SUM(taker_buy_quote_vol) AS taker_buy_quote_vol,
SUM(trade_count)::integer AS trade_count
FROM klines_1d
GROUP BY time_bucket('1 week', klines_1d.time), exchange, symbol
GROUP BY time_bucket('1 week', klines_1d.time), exchange, symbol, type
WITH NO DATA;
-- 【模式 A 用户】取消下面注释以启用定时调度刷新
@@ -386,6 +399,7 @@ SELECT
time_bucket('1 month', time) AS time,
exchange,
symbol,
type,
'1mon'::text AS interval,
FIRST(open, time) AS open,
MAX(high) AS high,
@@ -397,7 +411,7 @@ SELECT
SUM(taker_buy_quote_vol) AS taker_buy_quote_vol,
SUM(trade_count)::integer AS trade_count
FROM klines_1d
GROUP BY time_bucket('1 month', klines_1d.time), exchange, symbol
GROUP BY time_bucket('1 month', klines_1d.time), exchange, symbol, type
WITH NO DATA;
-- 【模式 A 用户】取消下面注释以启用定时调度刷新
@@ -409,29 +423,26 @@ WITH NO DATA;
-- );
-- ============================================================
-- 推荐索引:加速按 symbol + time 的查询
-- 推荐索引:加速按 symbol + type + time 的查询
-- ============================================================
CREATE INDEX IF NOT EXISTS idx_klines_3m_symbol_time ON klines_3m (exchange, symbol, time DESC);
CREATE INDEX IF NOT EXISTS idx_klines_5m_symbol_time ON klines_5m (exchange, symbol, time DESC);
CREATE INDEX IF NOT EXISTS idx_klines_15m_symbol_time ON klines_15m (exchange, symbol, time DESC);
CREATE INDEX IF NOT EXISTS idx_klines_30m_symbol_time ON klines_30m (exchange, symbol, time DESC);
CREATE INDEX IF NOT EXISTS idx_klines_1h_symbol_time ON klines_1h (exchange, symbol, time DESC);
CREATE INDEX IF NOT EXISTS idx_klines_2h_symbol_time ON klines_2h (exchange, symbol, time DESC);
CREATE INDEX IF NOT EXISTS idx_klines_4h_symbol_time ON klines_4h (exchange, symbol, time DESC);
CREATE INDEX IF NOT EXISTS idx_klines_6h_symbol_time ON klines_6h (exchange, symbol, time DESC);
CREATE INDEX IF NOT EXISTS idx_klines_8h_symbol_time ON klines_8h (exchange, symbol, time DESC);
CREATE INDEX IF NOT EXISTS idx_klines_1d_symbol_time ON klines_1d (exchange, symbol, time DESC);
CREATE INDEX IF NOT EXISTS idx_klines_1w_symbol_time ON klines_1w (exchange, symbol, time DESC);
CREATE INDEX IF NOT EXISTS idx_klines_1mon_symbol_time ON klines_1mon (exchange, symbol, time DESC);
CREATE INDEX IF NOT EXISTS idx_klines_3m_symbol_time ON klines_3m (exchange, symbol, type, time DESC);
CREATE INDEX IF NOT EXISTS idx_klines_5m_symbol_time ON klines_5m (exchange, symbol, type, time DESC);
CREATE INDEX IF NOT EXISTS idx_klines_15m_symbol_time ON klines_15m (exchange, symbol, type, time DESC);
CREATE INDEX IF NOT EXISTS idx_klines_30m_symbol_time ON klines_30m (exchange, symbol, type, time DESC);
CREATE INDEX IF NOT EXISTS idx_klines_1h_symbol_time ON klines_1h (exchange, symbol, type, time DESC);
CREATE INDEX IF NOT EXISTS idx_klines_2h_symbol_time ON klines_2h (exchange, symbol, type, time DESC);
CREATE INDEX IF NOT EXISTS idx_klines_4h_symbol_time ON klines_4h (exchange, symbol, type, time DESC);
CREATE INDEX IF NOT EXISTS idx_klines_6h_symbol_time ON klines_6h (exchange, symbol, type, time DESC);
CREATE INDEX IF NOT EXISTS idx_klines_8h_symbol_time ON klines_8h (exchange, symbol, type, time DESC);
CREATE INDEX IF NOT EXISTS idx_klines_1d_symbol_time ON klines_1d (exchange, symbol, type, time DESC);
CREATE INDEX IF NOT EXISTS idx_klines_1w_symbol_time ON klines_1w (exchange, symbol, type, time DESC);
CREATE INDEX IF NOT EXISTS idx_klines_1mon_symbol_time ON klines_1mon (exchange, symbol, type, time DESC);
-- ============================================================
-- 截面查询索引:加速同一时间点多品种回测查询
-- 查询模式:WHERE exchange='binance' AND time='2024-01-01' AND symbol IN (…)
-- 回测中跨品种截面查询最常见于日线和周线,因此只在这两层建额外索引。
-- 如需其他周期(如 1h、4h)的截面查询,按同样模式扩展。
-- ============================================================
CREATE INDEX IF NOT EXISTS idx_klines_1d_exchange_time_symbol ON klines_1d (exchange, time DESC, symbol);
CREATE INDEX IF NOT EXISTS idx_klines_1w_exchange_time_symbol ON klines_1w (exchange, time DESC, symbol);
CREATE INDEX IF NOT EXISTS idx_klines_1d_exchange_time_symbol ON klines_1d (exchange, time DESC, symbol, type);
CREATE INDEX IF NOT EXISTS idx_klines_1w_exchange_time_symbol ON klines_1w (exchange, time DESC, symbol, type);
-- ============================================================
-- 首次创建后手动刷新所有视图(填充历史数据)