From ebaef5042e4b8a908a9add09cb94d0911d8a6db9 Mon Sep 17 00:00:00 2001 From: Rekey Date: Tue, 16 Jun 2026 19:02:16 +0800 Subject: [PATCH] =?UTF-8?q?refactor(exchanges):=20fetchKlines=20=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=20params=20=E5=AF=B9=E8=B1=A1=E7=AD=BE=E5=90=8D?= =?UTF-8?q?=EF=BC=8C=E6=96=B0=E5=A2=9E=20type/exchange=20=E5=8F=82?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 FetchKlinesParams 接口统一 fetchKlines 参数(exchange/type/symbol/startTime/limit/endTime) - BaseRestClient 抽象方法 + MarketDataFeed 接口改为 params 对象签名 - BinanceRestClient / BinanceFuturesRestClient 由调用方传入 type,不再硬编码 spot/um - index.ts 新增静态 fetchKlines(params) 便捷函数,按 exchange+type 自动路由 - exchange.ts / bnkline.ts 调用方适配新签名 - 初始化 SQL 补充 BNBUSDT/SOLUSDT 合约交易对 --- data/db/init-db/02-init-tables.sql | 4 +++- data/exchanges/base.ts | 14 +++---------- data/exchanges/binance/rest.ts | 29 +++++++++------------------ data/exchanges/index.ts | 32 ++++++++++++++++++++++++++++++ data/run/exchange.ts | 16 +++++++-------- data/service/bnkline.ts | 16 +++++++++------ data/types/base.ts | 32 ++++++++++++++++++++---------- 7 files changed, 87 insertions(+), 56 deletions(-) diff --git a/data/db/init-db/02-init-tables.sql b/data/db/init-db/02-init-tables.sql index b0aa34c..92b1522 100644 --- a/data/db/init-db/02-init-tables.sql +++ b/data/db/init-db/02-init-tables.sql @@ -335,7 +335,9 @@ CROSS JOIN ( ('BNBUSDT', 'spot', 'BNB', 'USDT'), ('SOLUSDT', 'spot', 'SOL', 'USDT'), ('BTCUSDT', 'um', 'BTC', 'USDT'), - ('ETHUSDT', 'um', 'ETH', 'USDT') + ('ETHUSDT', 'um', 'ETH', 'USDT'), + ('BNBUSDT', 'um', 'BNB', 'USDT'), + ('SOLUSDT', 'um', 'SOL', 'USDT') ) AS sym(symbol, type, base, quote) WHERE e.name = 'binance' ON CONFLICT (exchange_id, symbol, type) DO NOTHING; diff --git a/data/exchanges/base.ts b/data/exchanges/base.ts index 38a653f..04adfb4 100644 --- a/data/exchanges/base.ts +++ b/data/exchanges/base.ts @@ -13,7 +13,7 @@ // ============================================================ import { logger } from "../utils/logger"; -import type { Kline, MarketInfo, RestClientConfig } from "../types"; +import type { Kline, MarketInfo, FetchKlinesParams, RestClientConfig } from "../types"; import { DEFAULT_REST_CONFIG } from "../types/base"; // ============================================================ @@ -83,17 +83,9 @@ export abstract class BaseRestClient { * 2. 将原始数据转换为本系统标准化 Kline 结构 * 3. 处理分页逻辑(若时间跨度超过单次请求上限) * - * @param symbol - 交易对符号(如 BTCUSDT) - * @param startTime - 起始时间(Unix ms) - * @param limit - 单次最大条数(默认取自 config.defaultLimit) - * @param endTime - 结束时间(Unix ms),可选 + * @param params - fetchKlines 统一参数对象 */ - abstract fetchKlines( - symbol: string, - startTime: number, - limit?: number, - endTime?: number, - ): Promise; + abstract fetchKlines(params: FetchKlinesParams): Promise; /** * 获取交易所交易对信息(REST)。 diff --git a/data/exchanges/binance/rest.ts b/data/exchanges/binance/rest.ts index f486978..8af6951 100644 --- a/data/exchanges/binance/rest.ts +++ b/data/exchanges/binance/rest.ts @@ -3,7 +3,7 @@ import { MainClient, USDMClient, type Kline as BinanceRestKline } from "binance" import { logger } from "../../utils/logger"; import { exchange } from "../../config"; import { BaseRestClient } from "../base"; -import type { Kline, MarketInfo, KlineInterval, PairType } from "../../types"; +import type { Kline, MarketInfo, KlineInterval, PairType, FetchKlinesParams } from "../../types"; // ============================================================ // Binance REST K 线 → 本系统标准化 Kline 转换 @@ -95,17 +95,10 @@ export class BinanceRestClient extends BaseRestClient { * Binance 硬限制单次最多 1000 条,超限自动裁切。 * 高周期 K 线通过 TimescaleDB 连续聚合视图生成。 * - * @param symbol - 交易对(如 BTCUSDT) - * @param startTime - 起始时间(Unix ms) - * @param limit - 单次拉取条数,默认取自 config.defaultLimit - * @param endTime - 结束时间(Unix ms),可选 + * @param params - fetchKlines 统一参数对象 */ - async fetchKlines( - symbol: string, - startTime: number, - limit?: number, - endTime?: number, - ): Promise { + async fetchKlines(params: FetchKlinesParams): Promise { + const { symbol, startTime, limit, endTime, type } = params; const effectiveLimit = limit ?? this.config.defaultLimit; // Binance 硬限制:单次最多 1000 条 const safeLimit = Math.min(effectiveLimit, 1000); @@ -132,7 +125,7 @@ export class BinanceRestClient extends BaseRestClient { // 按 openTime 升序排序(防御性),转换为标准化 Kline return rawKlines - .map((k) => convertBinanceKline(k, symbol, "1m", "spot")) + .map((k) => convertBinanceKline(k, symbol, "1m", type)) .sort((a, b) => a.openTime - b.openTime); } @@ -166,14 +159,10 @@ export class BinanceFuturesRestClient extends BaseRestClient { * 拉取 USDT-M 永续合约 1m K 线。 * * USDMClient.getKlines() 返回与 MainClient 同构的 12 元组, - * convertBinanceKline 直接复用,type 固定为 'um'。 + * convertBinanceKline 直接复用,type 由调用方通过 params 传入。 */ - async fetchKlines( - symbol: string, - startTime: number, - limit?: number, - endTime?: number, - ): Promise { + async fetchKlines(params: FetchKlinesParams): Promise { + const { symbol, startTime, limit, endTime, type } = params; const effectiveLimit = limit ?? this.config.defaultLimit; const safeLimit = Math.min(effectiveLimit, 1000); @@ -197,7 +186,7 @@ export class BinanceFuturesRestClient extends BaseRestClient { } return rawKlines - .map((k) => convertBinanceKline(k, symbol, "1m", "um")) + .map((k) => convertBinanceKline(k, symbol, "1m", type)) .sort((a, b) => a.openTime - b.openTime); } diff --git a/data/exchanges/index.ts b/data/exchanges/index.ts index f54e177..4ca0451 100644 --- a/data/exchanges/index.ts +++ b/data/exchanges/index.ts @@ -1,5 +1,6 @@ import { BaseRestClient } from "./base"; import { BinanceRestClient, BinanceFuturesRestClient } from "./binance/rest"; +import type { FetchKlinesParams, Kline } from "../types"; /** 交易所 ID 到 RestClient 构造器的注册表 */ const registry: Record BaseRestClient> = { @@ -28,6 +29,37 @@ export function createRestClient(exchangeId: string): BaseRestClient { return factory(); } +/** + * 拉取历史 K 线(静态便捷方法)。 + * + * 根据 exchange + type 自动路由到正确的交易所客户端, + * 调用方无需手动创建 client 或选择 spot/futures 子类。 + * + * 路由规则: + * - type = "spot" → exchange(如 "binance") + * - type = "um"/"cm" → `${exchange}_futures`(如 "binance_futures") + * + * @param params - fetchKlines 统一参数对象 + * @returns 标准化 K 线数组,按时间升序 + * + * @example + * const klines = await fetchKlines({ + * exchange: 'binance', + * type: 'um', + * symbol: 'BTCUSDT', + * startTime: 1700000000000, + * limit: 500, + * }); + */ +export async function fetchKlines(params: FetchKlinesParams): Promise { + const exchangeId = + params.type === "spot" + ? params.exchange + : `${params.exchange}_futures`; + const client = createRestClient(exchangeId); + return client.fetchKlines(params); +} + export { BaseRestClient } from "./base"; export { BinanceRestClient, BinanceFuturesRestClient } from "./binance/rest"; export { KLINE_INTERVAL_MS } from "./constants"; diff --git a/data/run/exchange.ts b/data/run/exchange.ts index fc0b0ef..8e2c3e9 100644 --- a/data/run/exchange.ts +++ b/data/run/exchange.ts @@ -1,7 +1,7 @@ import { logger } from "../utils/logger"; import { getAllPairs, updatePairLastBackfillTime } from '../service/pair'; import { upsertOrUpdateKlines } from "../service/kline"; -import { createRestClient } from '../exchanges'; +import { fetchKlines } from '../exchanges'; function getNowMinuteMS() { const minuteMS = 1000 * 60; @@ -11,17 +11,17 @@ function getNowMinuteMS() { const allPairs = await getAllPairs(); for (const pair of allPairs) { - const exchangeId = pair.type === 'um' ? 'binance_futures' : 'binance'; - const client = createRestClient(exchangeId); let lastBackfillTime = pair.last_backfill_time.getTime(); try { while (lastBackfillTime < getNowMinuteMS()) { console.log('lastBackfillTime', lastBackfillTime); - const klines = await client.fetchKlines( - pair.symbol, - lastBackfillTime, - 500 - ); + const klines = await fetchKlines({ + exchange: 'binance', + type: pair.type, + symbol: pair.symbol, + startTime: lastBackfillTime, + limit: 500, + }); console.log(`拉取到 ${klines.length} 条 K 线`); if (klines.length > 0) { await upsertOrUpdateKlines(klines); diff --git a/data/service/bnkline.ts b/data/service/bnkline.ts index 5daaafd..79ea4c4 100644 --- a/data/service/bnkline.ts +++ b/data/service/bnkline.ts @@ -1,13 +1,11 @@ -import { createRestClient } from "../exchanges"; +import { fetchKlines as fetchKlinesFromExchange } from "../exchanges"; import type { Kline } from "../types"; -const client = createRestClient("binance"); - /** * 获取 Binance 1m K 线数据(基于 MainClient REST API)。 * - * 内部复用 Client(多交易所 REST 客户端)的 binance 实现, - * 包含限流、Binance SDK 原生转换、连续性过滤等逻辑。 + * 内部复用 fetchKlines 静态方法,自动路由到正确的交易所客户端, + * 包含限流、Binance SDK 原生转换等逻辑。 * 返回本系统标准化 {@link Kline} 数组。 * * @param symbol - 交易对符号(如 "BTCUSDT") @@ -19,5 +17,11 @@ export async function fetchKlines( startTime: number, limit = 500, ): Promise { - return client.fetchKlines(symbol, startTime, limit); + return fetchKlinesFromExchange({ + exchange: 'binance', + type: 'spot', + symbol, + startTime, + limit, + }); } \ No newline at end of file diff --git a/data/types/base.ts b/data/types/base.ts index b3d7430..76e5bdc 100644 --- a/data/types/base.ts +++ b/data/types/base.ts @@ -171,6 +171,26 @@ export const DEFAULT_REST_CONFIG: RestClientConfig = { defaultLimit: 500, }; +// ============================================================ +// fetchKlines 参数对象 +// ============================================================ + +/** fetchKlines 统一参数对象 */ +export interface FetchKlinesParams { + /** 交易所标识(如 "binance") */ + exchange: string; + /** 交易对符号(如 BTCUSDT) */ + symbol: string; + /** 交易对类型(spot / um / cm) */ + type: PairType; + /** 起始时间(Unix ms) */ + startTime: number; + /** 单次拉取条数,默认取自 config.defaultLimit */ + limit?: number; + /** 结束时间(Unix ms),可选 */ + endTime?: number; +} + // ============================================================ // 适配器配置(含 WebSocket 重连) // ============================================================ @@ -235,18 +255,10 @@ export interface MarketDataFeed { /** * REST 拉取历史 K 线(用于补齐缺失数据或回测)。 * - * @param symbol - 交易对符号 - * @param startTime - 起始时间(Unix ms) - * @param endTime - 结束时间(Unix ms) - * @param limit - 最大返回条数(默认 500) + * @param params - fetchKlines 统一参数对象 * @returns 标准化 K 线数组,按时间升序 */ - fetchKlines( - symbol: string, - startTime: number, - endTime: number, - limit?: number, - ): Promise; + fetchKlines(params: FetchKlinesParams): Promise; /** * 获取交易所交易对信息(用于自动注册到 trading_pairs 表)。