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:
@@ -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; // 主动买入成交额
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export type * from './kline';
|
||||
export type * from './base';
|
||||
export * from './base';
|
||||
@@ -0,0 +1,10 @@
|
||||
/** K 线周期枚举 */
|
||||
export type KlineInterval =
|
||||
| "1m"
|
||||
| "5m"
|
||||
| "15m"
|
||||
| "30m"
|
||||
| "1h"
|
||||
| "4h"
|
||||
| "1d"
|
||||
| "1w";
|
||||
Reference in New Issue
Block a user