- 配置层:env.yaml 新增 binance_futures API Key 段,validators + config 同步 - 清理 TradingPair 实体:删除 kline_interval、kline_intervals、kline_synthesis_enabled - 删除 fetchKlines 系列函数的 interval 参数,硬编码为 1m - 更新 SQL seed 数据、example、base_rest 接口、types 接口 - 新增 AGENTS/08-boundaries.md 执行纪律 - 新增 PLAN-add-futures-data.md 方案文档
5.9 KiB
接入 USDT-M 合约数据 — 改造方案
设计思路
用 symbol 后缀区分账户类型,而非新增 account_type 列。核心原则是尽量不动已有表结构和聚合视图。
Symbol 命名约定
| 账户类型 | symbol 示例 | 说明 |
|---|---|---|
| 现货 | BTCUSDT |
不变 |
| USDT-M 永续 | BTCUSDT.P |
.P 后缀标记合约 |
| Coin-M 永续 | BTCUSDT_PERP |
预留 |
核心机制:对外(入库、查询、展示)统一用带后缀的 symbol;对内(调用 Binance SDK)自动 strip 后缀。
改动清单
1. 配置层(3 文件)
data/env.yaml
exchange:
binance: ← 保留不动(向后兼容)
api_key: "..."
api_secret: "..."
binance_futures: ← 新增
api_key: "..."
api_secret: "..."
data/config/validators.ts
ExchangeConfig接口中binance: ExchangeApiKeys保持不动- 新增
binance_futures: ExchangeApiKeys validateConfig()中新增解析exchange.binance_futures
data/config/index.ts
- 导出
exchange.binance(已有)+ 新增exchange.binanceFutures
2. REST 客户端(1 文件,核心改动)
data/exchanges/rest.ts
import { USDMClient } from "binance"; // 新增引入
// 工具函数:提取裸 symbol(移除 .P / _PERP 后缀)
function stripSuffix(symbol: string): string {
return symbol.replace(/\.(P|PERP)$/, "");
}
// 判断是否为合约 symbol
function isFuturesSymbol(symbol: string): boolean {
return symbol.endsWith(".P") || symbol.endsWith("_PERP");
}
现有 fetchBinanceKlines() 保持不变(处理现货)。新增 fetchFuturesKlines():
async function fetchFuturesKlines(
symbol: string, // BTCUSDT.P
interval: KlineInterval,
startTime: number,
endTime?: number,
limit = 500,
): Promise<Kline[]> {
const rawSymbol = stripSuffix(symbol); // BTCUSDT
const client = new USDMClient({
api_key: exchange.binanceFutures.apiKey,
api_secret: exchange.binanceFutures.apiSecret,
}, { timeout: 3000 });
const rawKlines = await client.getKlines({
symbol: rawSymbol,
interval,
startTime,
endTime,
limit: Math.min(limit, 1000),
});
return rawKlines.map(k => convertBinanceKline(k, symbol, interval));
}
Client 类 fetchKlines() 增加分支:
async fetchKlines(...): Promise<Kline[]> {
switch (this.exchange) {
case "binance":
if (isFuturesSymbol(symbol)) {
return fetchFuturesKlines(symbol, interval, startTime, endTime, effectiveLimit);
}
return fetchBinanceKlines(symbol, interval, startTime, endTime, effectiveLimit);
}
}
关键:convertBinanceKline() 传入原始 "BTCUSDT.P",转换结果中 kline.symbol 自然就是 "BTCUSDT.P",入库后与现货 "BTCUSDT" 不会 PK 冲突。
3. 服务层(1 文件)
data/service/kline.ts
- 不需任何改动。
upsertOrUpdateKlines()直接把Kline.symbol写入KlineEntity.symbol,后缀天然带进去。
4. 实体层(0 文件)
结论:不需要改。
kline.entity.ts的 4 列 PK 保持不变trading-pair.entity.ts不需要加account_type,用 symbol 本身的.P后缀标识即可
TradingPair 的 symbol 设成 "BTCUSDT.P",唯一约束 (exchange_id, symbol) 自动不冲突。
5. SQL 初始化脚本(1 文件)
data/db/init-db/02-init-tables.sql
种子数据新增合约交易对:
INSERT INTO trading_pairs (exchange_id, symbol, base_asset, quote_asset,
price_precision, quantity_precision, kline_interval, kline_intervals, active)
SELECT
e.id,
sym.symbol,
sym.base,
sym.quote,
2, 5, '1m', '1m,5m,15m,30m,1h,4h,1d,1w', TRUE
FROM exchanges e
CROSS JOIN (
VALUES
('BTCUSDT.P', 'BTC', 'USDT'),
('ETHUSDT.P', 'ETH', 'USDT')
) AS sym(symbol, base, quote)
WHERE e.name = 'binance'
ON CONFLICT (exchange_id, symbol) DO NOTHING;
klines 表结构和连续聚合视图:完全不动。 symbol 值不同,数据天然隔离。
6. 运行脚本(1 文件)
data/run/exchange.ts
getAllPairs()不受影响,返回的symbol本身就是"BTCUSDT.P"new Client("binance")的fetchKlines()内部已按 symbol 后缀分派到USDMClient- 回补循环逻辑不变,
lastBackfillTime追踪机制不变
注意:Binance 的 USDMClient.getKlines() 返回与 MainClient.getKlines() 同构的 12 元组,convertBinanceKline() 可以直接复用。
7. 类型定义(0 文件)
Kline.symbol 字段类型已是 string,直接存 "BTCUSDT.P"。无需改动。
改动汇总
| 文件 | 改动类型 | 行数估算 |
|---|---|---|
data/env.yaml |
新增 binance_futures 段 |
+4 |
data/config/validators.ts |
ExchangeConfig + validateConfig() 新增解析 |
+10 |
data/config/index.ts |
新增 exchange.binanceFutures 导出 |
+5 |
data/exchanges/rest.ts |
引入 USDMClient,新增 fetchFuturesKlines(),Client.fetchKlines() 增加分支 |
+40 |
data/db/init-db/02-init-tables.sql |
seed 数据插入合约交易对 | +15 |
data/run/exchange.ts |
无实质改动(后缀自动路由) | 0 |
不动:klines 表结构、复合主键、连续聚合视图、service/kline.ts、types 目录、实体层。
共 5-6 个文件,~70 行净改动。
工作顺序
- env.yaml — 配好 futures 的 API Key
- config/validators.ts + config/index.ts — 解析 + 导出 futures Key
- exchanges/rest.ts — 核心改动,USDMClient + 后缀分派
- 02-init-tables.sql — 种子数据
- 验证:跑
bun run data/run/exchange.ts看能否拉下BTCUSDT.P的 K 线 - Coin-M 同理扩展(只加 suffix 规则 + CoinMClient case)