5e385547c7
- 废弃旧 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/ 运行脚本
110 lines
4.2 KiB
TypeScript
110 lines
4.2 KiB
TypeScript
// ============================================================
|
||
// 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;
|