refactor(data): 重构交易所适配器,修复 Kline 实体复合主键

- 废弃旧 adapter 体系 (base/binance/types.ts),新增 base_rest/rest.ts
  基于 Binance 官方 SDK 实现 REST K 线拉取
- Kline 实体改为四列复合主键 (exchange/symbol/interval/time),
  修复单列 time PK 导致的跨 symbol 写入冲突
- 新增 filterConsecutive():K 线连续性过滤,首缺口截断策略
- 新增 service/kline.ts:批量 UPSERT K 线入库
- 新增 types/ 共享类型定义、example/ 示例、run/ 运行脚本
This commit is contained in:
Rekey
2026-06-08 18:18:16 +08:00
parent 85a0031a78
commit 5e385547c7
16 changed files with 829 additions and 1043 deletions
+290
View File
@@ -0,0 +1,290 @@
// ============================================================
// types.ts — 统一行情数据类型定义与 MarketDataFeed 接口
// ============================================================
// 所有交易所适配器共享的数据结构和接口契约。
// 适配器负责将交易所原生数据格式转换为以下标准化类型。
//
// 设计原则:
// - 字段语义与 Binance/OKX/Bybit 通用概念对齐
// - 时间戳统一使用 Unix 毫秒(number),便于排序和计算
// - 价格/数量使用 number 类型(JavaScript 64-bit float),
// 对精度敏感场景(如 orderbook 快照)保留原始字符串
// ============================================================
import type { Observable } from "rxjs";
import type { KlineInterval } from "./kline";
// ============================================================
// 标准化行情数据结构
// ============================================================
/** 24 小时滚动 Ticker 统计 */
export interface Ticker {
/** 交易所标识 */
exchange: string;
/** 交易对符号(大写,如 BTCUSDT) */
symbol: string;
/** 最新成交价 */
lastPrice: number;
/** 24h 开盘价 */
openPrice: number;
/** 24h 最高价 */
highPrice: number;
/** 24h 最低价 */
lowPrice: number;
/** 24h 成交量(base 币种) */
volume: number;
/** 24h 成交额(quote 币种) */
quoteVolume: number;
/** 24h 价格变化 */
priceChange: number;
/** 24h 价格变化百分比(0.05 = 5% */
priceChangePercent: number;
/** 买一价 */
bidPrice: number;
/** 买一量 */
bidQty: number;
/** 卖一价 */
askPrice: number;
/** 卖一量 */
askQty: number;
/** 事件发生时间(Unix ms */
eventTime: number;
/** 交易所收盘时间(Unix ms,用于判断 K 线是否闭合) */
closeTime: number;
}
/** 逐笔成交 */
export interface Trade {
/** 交易所标识 */
exchange: string;
/** 交易对符号 */
symbol: string;
/** 成交价 */
price: number;
/** 成交数量(base 币种) */
amount: number;
/** 成交额(quote 币种 = price × amount */
quoteAmount: number;
/** 成交时间(Unix ms */
timestamp: number;
/** 买方是否为挂单方(true = 主动卖出 / taker sell */
isBuyerMaker: boolean;
/** 交易所成交 ID(可能为字符串,如 Binance tradeId 为 bigint */
tradeId: string;
}
/** 订单簿深度快照 */
export interface OrderBook {
/** 交易所标识 */
exchange: string;
/** 交易对符号 */
symbol: string;
/** 买单列表 [[price, qty], ...],按价格降序(买一在前) */
bids: [number, number][];
/** 卖单列表 [[price, qty], ...],按价格升序(卖一在前) */
asks: [number, number][];
/** 上次更新 ID */
lastUpdateId: number;
/** 事件发生时间(Unix ms */
eventTime: number;
}
/** 标准化 K 线(OHLCV */
export interface Kline {
/** 交易所标识 */
exchange: string;
/** 交易对符号 */
symbol: string;
/** K 线周期 */
interval: KlineInterval;
/** 开盘时间(Unix ms */
openTime: number;
/** 收盘时间(Unix ms */
closeTime: number;
/** 开盘价 */
open: string;
/** 最高价 */
high: string;
/** 最低价 */
low: string;
/** 收盘价 */
close: string;
/** 成交量(base 币种) */
volume: string;
/** 成交额(quote 币种) */
quoteVolume: string;
/** 主动买入成交量(base 币种) */
takerBuyBaseVol: string;
/** 主动买入成交额(quote 币种) */
takerBuyQuoteVol: string;
/** 成交笔数 */
tradeCount: string;
/** 该 K 线是否已关闭(不再更新) */
isClosed: boolean;
}
/** K 线增量更新(仅推送最新一根 OHLCV 变化) */
export interface KlineDelta {
exchange: string;
symbol: string;
interval: KlineInterval;
openTime: number;
closeTime: number;
open: number;
high: number;
low: number;
close: number;
volume: number;
isClosed: boolean;
}
// ============================================================
// WebSocket 连接状态
// ============================================================
/** 连接状态枚举 */
export type ConnectionState =
| "disconnected"
| "connecting"
| "connected"
| "error";
// ============================================================
// REST 客户端配置
// ============================================================
/** REST 客户端通用配置(各交易所 SDK 共用) */
export interface RestClientConfig {
/** REST API 请求冷却时间(毫秒),默认 200 */
restRateLimitMs: number;
/** 单次请求默认拉取条数 */
defaultLimit: number;
}
/** 默认 REST 客户端配置 */
export const DEFAULT_REST_CONFIG: RestClientConfig = {
restRateLimitMs: 200,
defaultLimit: 500,
};
// ============================================================
// 适配器配置(含 WebSocket 重连)
// ============================================================
/** 交易所适配器完整配置(REST + WebSocket */
export interface AdapterConfig extends RestClientConfig {
/** 指数退避重连基数(毫秒),默认 3000 */
reconnectBaseDelayMs: number;
/** 最大重连次数,默认 10 */
maxReconnectAttempts: number;
}
/** 默认适配器配置 */
export const DEFAULT_ADAPTER_CONFIG: AdapterConfig = {
...DEFAULT_REST_CONFIG,
reconnectBaseDelayMs: 3000,
maxReconnectAttempts: 10,
};
// ============================================================
// MarketDataFeed 接口 —— 所有交易所适配器必须实现
// ============================================================
/**
* 统一行情数据源接口。
*
* 每个交易所适配器实现此接口,向上层管道暴露标准化数据流。
* 使用 RxJS Observable 作为统一推送机制,pipeline 层可自由
* 组合、过滤、分流各交易所数据。
*/
export interface MarketDataFeed {
/** 交易所标识(如 "binance" */
readonly exchange: string;
/** 当前连接状态 */
readonly connectionState: ConnectionState;
/** 建立 WebSocket 连接 */
connect(): Promise<void>;
/** 断开连接 */
disconnect(): Promise<void>;
/**
* 订阅 24h 滚动 Ticker 流。
* 每笔成交触发推送(Binance: <symbol>@ticker)。
*/
subscribeTicker(symbols: string[]): Observable<Ticker>;
/**
* 订阅逐笔成交流。
* 实时推送每笔撮合成交(Binance: <symbol>@trade)。
*/
subscribeTrade(symbols: string[]): Observable<Trade>;
/**
* 订阅订单簿深度。
* depth 参数指定档位(如 5/10/20),默认 20。
*/
subscribeOrderbook(symbol: string, depth?: number): Observable<OrderBook>;
/**
* REST 拉取历史 K 线(用于补齐缺失数据或回测)。
*
* @param symbol - 交易对符号
* @param interval - K 线周期
* @param startTime - 起始时间(Unix ms
* @param endTime - 结束时间(Unix ms
* @param limit - 最大返回条数(默认 500)
* @returns 标准化 K 线数组,按时间升序
*/
fetchKlines(
symbol: string,
interval: KlineInterval,
startTime: number,
endTime: number,
limit?: number,
): Promise<Kline[]>;
/**
* 获取交易所交易对信息(用于自动注册到 trading_pairs 表)。
* 返回标准化后的交易对元数据。
*/
fetchMarkets(): Promise<MarketInfo[]>;
}
/** 交易对元信息(从交易所 REST API 获取) */
export interface MarketInfo {
symbol: string;
baseAsset: string;
quoteAsset: string;
pricePrecision: number;
quantityPrecision: number;
minQty?: number;
stepSize?: number;
minNotional?: number;
}
// ============================================================
// 工具类型
// ============================================================
/** Binance WebSocket 原始 K 线数据(kline 事件中的 k 字段) */
export interface BinanceRawKline {
t: number; // K 线开始时间
T: number; // K 线结束时间
s: string; // 交易对
i: string; // 周期
o: string; // 开盘价
h: string; // 最高价
l: string; // 最低价
c: string; // 收盘价
v: string; // 成交量
n: number; // 成交笔数
x: boolean; // 是否已关闭
q: string; // 成交额
V: string; // 主动买入成交量
Q: string; // 主动买入成交额
}
+3
View File
@@ -0,0 +1,3 @@
export type * from './kline';
export type * from './base';
export * from './base';
+10
View File
@@ -0,0 +1,10 @@
/** K 线周期枚举 */
export type KlineInterval =
| "1m"
| "5m"
| "15m"
| "30m"
| "1h"
| "4h"
| "1d"
| "1w";