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,109 @@
|
||||
// ============================================================
|
||||
// base.ts — REST 客户端抽象基类
|
||||
// ============================================================
|
||||
// 各交易所适配器继承此类,复用 REST 请求限流、重试等通用逻辑。
|
||||
// 子类通过注入各交易所原生 SDK(binance / okx / bybit ...)
|
||||
// 实现具体的数据拉取,无需依赖 ccxt。
|
||||
//
|
||||
// 子类只需实现:
|
||||
// - fetchKlines() — 历史 K 线拉取(基于目标交易所 SDK)
|
||||
// - fetchMarkets() — 交易对元数据拉取(用于自动注册交易对)
|
||||
//
|
||||
// WebSocket 实时行情由各适配器自行管理,不在此基类范围内。
|
||||
// ============================================================
|
||||
|
||||
import { logger } from "../utils/logger";
|
||||
import type { Kline, KlineInterval, MarketInfo, RestClientConfig } from "../types";
|
||||
import { DEFAULT_REST_CONFIG } from "../types/base";
|
||||
|
||||
// ============================================================
|
||||
// 工具:异步 sleep
|
||||
// ============================================================
|
||||
|
||||
/** 返回一个在 ms 毫秒后 resolve 的 Promise */
|
||||
function sleep(ms: number): Promise<void> {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// BaseRestClient
|
||||
// ============================================================
|
||||
|
||||
export abstract class BaseRestClient {
|
||||
/** 交易所标识(子类必须覆盖) */
|
||||
abstract readonly exchange: string;
|
||||
|
||||
/** REST 客户端配置(可在子类构造函数中覆盖默认值) */
|
||||
protected readonly config: RestClientConfig;
|
||||
|
||||
/** REST 请求节流 Map(key → 上次请求时间戳) */
|
||||
private lastRestFetch = new Map<string, number>();
|
||||
|
||||
// ============================================================
|
||||
// 构造函数
|
||||
// ============================================================
|
||||
|
||||
constructor(config: Partial<RestClientConfig> = {}) {
|
||||
this.config = { ...DEFAULT_REST_CONFIG, ...config };
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 限流工具
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* 对同一 key 的 REST 请求进行冷却节流。
|
||||
* 若距离上次请求不足 restRateLimitMs,自动等待剩余时间。
|
||||
*
|
||||
* @param key - 节流标识(如 `${symbol}:${interval}`)
|
||||
*/
|
||||
protected async throttle(key: string): Promise<void> {
|
||||
const lastFetch = this.lastRestFetch.get(key) ?? 0;
|
||||
const elapsed = Date.now() - lastFetch;
|
||||
if (elapsed < this.config.restRateLimitMs) {
|
||||
const waitMs = this.config.restRateLimitMs - elapsed;
|
||||
logger.debug(
|
||||
{ exchange: this.exchange, key, waitMs },
|
||||
`[${this.exchange}] REST 限流等待 ${waitMs}ms`,
|
||||
);
|
||||
await sleep(waitMs);
|
||||
}
|
||||
this.lastRestFetch.set(key, Date.now());
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 抽象方法 —— 子类必须使用各自的交易所 SDK 实现
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* 拉取历史 K 线数据(REST)。
|
||||
*
|
||||
* 子类负责:
|
||||
* 1. 调用交易所原生 SDK 的 K 线接口
|
||||
* 2. 将原始数据转换为本系统标准化 Kline 结构
|
||||
* 3. 处理分页逻辑(若时间跨度超过单次请求上限)
|
||||
*
|
||||
* @param symbol - 交易对符号(如 BTCUSDT)
|
||||
* @param interval - K 线周期
|
||||
* @param startTime - 起始时间(Unix ms)
|
||||
* @param endTime - 结束时间(Unix ms)
|
||||
* @param limit - 单次最大条数(默认取自 config.defaultLimit)
|
||||
*/
|
||||
abstract fetchKlines(
|
||||
symbol: string,
|
||||
interval: KlineInterval,
|
||||
startTime: number,
|
||||
endTime: number,
|
||||
limit?: number,
|
||||
): Promise<Kline[]>;
|
||||
|
||||
/**
|
||||
* 获取交易所交易对信息(REST)。
|
||||
*
|
||||
* 子类负责调用交易所原生 SDK 的 /exchangeInfo 等价接口,
|
||||
* 并转换为本系统标准化 MarketInfo 结构。
|
||||
*/
|
||||
abstract fetchMarkets(): Promise<MarketInfo[]>;
|
||||
}
|
||||
|
||||
export default BaseRestClient;
|
||||
Reference in New Issue
Block a user