refactor(exchanges): fetchKlines 改为 params 对象签名,新增 type/exchange 参数
- 新增 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 合约交易对
This commit is contained in:
@@ -335,7 +335,9 @@ CROSS JOIN (
|
|||||||
('BNBUSDT', 'spot', 'BNB', 'USDT'),
|
('BNBUSDT', 'spot', 'BNB', 'USDT'),
|
||||||
('SOLUSDT', 'spot', 'SOL', 'USDT'),
|
('SOLUSDT', 'spot', 'SOL', 'USDT'),
|
||||||
('BTCUSDT', 'um', 'BTC', '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)
|
) AS sym(symbol, type, base, quote)
|
||||||
WHERE e.name = 'binance'
|
WHERE e.name = 'binance'
|
||||||
ON CONFLICT (exchange_id, symbol, type) DO NOTHING;
|
ON CONFLICT (exchange_id, symbol, type) DO NOTHING;
|
||||||
|
|||||||
+3
-11
@@ -13,7 +13,7 @@
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
import { logger } from "../utils/logger";
|
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";
|
import { DEFAULT_REST_CONFIG } from "../types/base";
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -83,17 +83,9 @@ export abstract class BaseRestClient {
|
|||||||
* 2. 将原始数据转换为本系统标准化 Kline 结构
|
* 2. 将原始数据转换为本系统标准化 Kline 结构
|
||||||
* 3. 处理分页逻辑(若时间跨度超过单次请求上限)
|
* 3. 处理分页逻辑(若时间跨度超过单次请求上限)
|
||||||
*
|
*
|
||||||
* @param symbol - 交易对符号(如 BTCUSDT)
|
* @param params - fetchKlines 统一参数对象
|
||||||
* @param startTime - 起始时间(Unix ms)
|
|
||||||
* @param limit - 单次最大条数(默认取自 config.defaultLimit)
|
|
||||||
* @param endTime - 结束时间(Unix ms),可选
|
|
||||||
*/
|
*/
|
||||||
abstract fetchKlines(
|
abstract fetchKlines(params: FetchKlinesParams): Promise<Kline[]>;
|
||||||
symbol: string,
|
|
||||||
startTime: number,
|
|
||||||
limit?: number,
|
|
||||||
endTime?: number,
|
|
||||||
): Promise<Kline[]>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取交易所交易对信息(REST)。
|
* 获取交易所交易对信息(REST)。
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { MainClient, USDMClient, type Kline as BinanceRestKline } from "binance"
|
|||||||
import { logger } from "../../utils/logger";
|
import { logger } from "../../utils/logger";
|
||||||
import { exchange } from "../../config";
|
import { exchange } from "../../config";
|
||||||
import { BaseRestClient } from "../base";
|
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 转换
|
// Binance REST K 线 → 本系统标准化 Kline 转换
|
||||||
@@ -95,17 +95,10 @@ export class BinanceRestClient extends BaseRestClient {
|
|||||||
* Binance 硬限制单次最多 1000 条,超限自动裁切。
|
* Binance 硬限制单次最多 1000 条,超限自动裁切。
|
||||||
* 高周期 K 线通过 TimescaleDB 连续聚合视图生成。
|
* 高周期 K 线通过 TimescaleDB 连续聚合视图生成。
|
||||||
*
|
*
|
||||||
* @param symbol - 交易对(如 BTCUSDT)
|
* @param params - fetchKlines 统一参数对象
|
||||||
* @param startTime - 起始时间(Unix ms)
|
|
||||||
* @param limit - 单次拉取条数,默认取自 config.defaultLimit
|
|
||||||
* @param endTime - 结束时间(Unix ms),可选
|
|
||||||
*/
|
*/
|
||||||
async fetchKlines(
|
async fetchKlines(params: FetchKlinesParams): Promise<Kline[]> {
|
||||||
symbol: string,
|
const { symbol, startTime, limit, endTime, type } = params;
|
||||||
startTime: number,
|
|
||||||
limit?: number,
|
|
||||||
endTime?: number,
|
|
||||||
): Promise<Kline[]> {
|
|
||||||
const effectiveLimit = limit ?? this.config.defaultLimit;
|
const effectiveLimit = limit ?? this.config.defaultLimit;
|
||||||
// Binance 硬限制:单次最多 1000 条
|
// Binance 硬限制:单次最多 1000 条
|
||||||
const safeLimit = Math.min(effectiveLimit, 1000);
|
const safeLimit = Math.min(effectiveLimit, 1000);
|
||||||
@@ -132,7 +125,7 @@ export class BinanceRestClient extends BaseRestClient {
|
|||||||
|
|
||||||
// 按 openTime 升序排序(防御性),转换为标准化 Kline
|
// 按 openTime 升序排序(防御性),转换为标准化 Kline
|
||||||
return rawKlines
|
return rawKlines
|
||||||
.map((k) => convertBinanceKline(k, symbol, "1m", "spot"))
|
.map((k) => convertBinanceKline(k, symbol, "1m", type))
|
||||||
.sort((a, b) => a.openTime - b.openTime);
|
.sort((a, b) => a.openTime - b.openTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,14 +159,10 @@ export class BinanceFuturesRestClient extends BaseRestClient {
|
|||||||
* 拉取 USDT-M 永续合约 1m K 线。
|
* 拉取 USDT-M 永续合约 1m K 线。
|
||||||
*
|
*
|
||||||
* USDMClient.getKlines() 返回与 MainClient 同构的 12 元组,
|
* USDMClient.getKlines() 返回与 MainClient 同构的 12 元组,
|
||||||
* convertBinanceKline 直接复用,type 固定为 'um'。
|
* convertBinanceKline 直接复用,type 由调用方通过 params 传入。
|
||||||
*/
|
*/
|
||||||
async fetchKlines(
|
async fetchKlines(params: FetchKlinesParams): Promise<Kline[]> {
|
||||||
symbol: string,
|
const { symbol, startTime, limit, endTime, type } = params;
|
||||||
startTime: number,
|
|
||||||
limit?: number,
|
|
||||||
endTime?: number,
|
|
||||||
): Promise<Kline[]> {
|
|
||||||
const effectiveLimit = limit ?? this.config.defaultLimit;
|
const effectiveLimit = limit ?? this.config.defaultLimit;
|
||||||
const safeLimit = Math.min(effectiveLimit, 1000);
|
const safeLimit = Math.min(effectiveLimit, 1000);
|
||||||
|
|
||||||
@@ -197,7 +186,7 @@ export class BinanceFuturesRestClient extends BaseRestClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return rawKlines
|
return rawKlines
|
||||||
.map((k) => convertBinanceKline(k, symbol, "1m", "um"))
|
.map((k) => convertBinanceKline(k, symbol, "1m", type))
|
||||||
.sort((a, b) => a.openTime - b.openTime);
|
.sort((a, b) => a.openTime - b.openTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { BaseRestClient } from "./base";
|
import { BaseRestClient } from "./base";
|
||||||
import { BinanceRestClient, BinanceFuturesRestClient } from "./binance/rest";
|
import { BinanceRestClient, BinanceFuturesRestClient } from "./binance/rest";
|
||||||
|
import type { FetchKlinesParams, Kline } from "../types";
|
||||||
|
|
||||||
/** 交易所 ID 到 RestClient 构造器的注册表 */
|
/** 交易所 ID 到 RestClient 构造器的注册表 */
|
||||||
const registry: Record<string, () => BaseRestClient> = {
|
const registry: Record<string, () => BaseRestClient> = {
|
||||||
@@ -28,6 +29,37 @@ export function createRestClient(exchangeId: string): BaseRestClient {
|
|||||||
return factory();
|
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<Kline[]> {
|
||||||
|
const exchangeId =
|
||||||
|
params.type === "spot"
|
||||||
|
? params.exchange
|
||||||
|
: `${params.exchange}_futures`;
|
||||||
|
const client = createRestClient(exchangeId);
|
||||||
|
return client.fetchKlines(params);
|
||||||
|
}
|
||||||
|
|
||||||
export { BaseRestClient } from "./base";
|
export { BaseRestClient } from "./base";
|
||||||
export { BinanceRestClient, BinanceFuturesRestClient } from "./binance/rest";
|
export { BinanceRestClient, BinanceFuturesRestClient } from "./binance/rest";
|
||||||
export { KLINE_INTERVAL_MS } from "./constants";
|
export { KLINE_INTERVAL_MS } from "./constants";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
import { getAllPairs, updatePairLastBackfillTime } from '../service/pair';
|
import { getAllPairs, updatePairLastBackfillTime } from '../service/pair';
|
||||||
import { upsertOrUpdateKlines } from "../service/kline";
|
import { upsertOrUpdateKlines } from "../service/kline";
|
||||||
import { createRestClient } from '../exchanges';
|
import { fetchKlines } from '../exchanges';
|
||||||
|
|
||||||
function getNowMinuteMS() {
|
function getNowMinuteMS() {
|
||||||
const minuteMS = 1000 * 60;
|
const minuteMS = 1000 * 60;
|
||||||
@@ -11,17 +11,17 @@ function getNowMinuteMS() {
|
|||||||
const allPairs = await getAllPairs();
|
const allPairs = await getAllPairs();
|
||||||
|
|
||||||
for (const pair of allPairs) {
|
for (const pair of allPairs) {
|
||||||
const exchangeId = pair.type === 'um' ? 'binance_futures' : 'binance';
|
|
||||||
const client = createRestClient(exchangeId);
|
|
||||||
let lastBackfillTime = pair.last_backfill_time.getTime();
|
let lastBackfillTime = pair.last_backfill_time.getTime();
|
||||||
try {
|
try {
|
||||||
while (lastBackfillTime < getNowMinuteMS()) {
|
while (lastBackfillTime < getNowMinuteMS()) {
|
||||||
console.log('lastBackfillTime', lastBackfillTime);
|
console.log('lastBackfillTime', lastBackfillTime);
|
||||||
const klines = await client.fetchKlines(
|
const klines = await fetchKlines({
|
||||||
pair.symbol,
|
exchange: 'binance',
|
||||||
lastBackfillTime,
|
type: pair.type,
|
||||||
500
|
symbol: pair.symbol,
|
||||||
);
|
startTime: lastBackfillTime,
|
||||||
|
limit: 500,
|
||||||
|
});
|
||||||
console.log(`拉取到 ${klines.length} 条 K 线`);
|
console.log(`拉取到 ${klines.length} 条 K 线`);
|
||||||
if (klines.length > 0) {
|
if (klines.length > 0) {
|
||||||
await upsertOrUpdateKlines(klines);
|
await upsertOrUpdateKlines(klines);
|
||||||
|
|||||||
+10
-6
@@ -1,13 +1,11 @@
|
|||||||
import { createRestClient } from "../exchanges";
|
import { fetchKlines as fetchKlinesFromExchange } from "../exchanges";
|
||||||
import type { Kline } from "../types";
|
import type { Kline } from "../types";
|
||||||
|
|
||||||
const client = createRestClient("binance");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取 Binance 1m K 线数据(基于 MainClient REST API)。
|
* 获取 Binance 1m K 线数据(基于 MainClient REST API)。
|
||||||
*
|
*
|
||||||
* 内部复用 Client(多交易所 REST 客户端)的 binance 实现,
|
* 内部复用 fetchKlines 静态方法,自动路由到正确的交易所客户端,
|
||||||
* 包含限流、Binance SDK 原生转换、连续性过滤等逻辑。
|
* 包含限流、Binance SDK 原生转换等逻辑。
|
||||||
* 返回本系统标准化 {@link Kline} 数组。
|
* 返回本系统标准化 {@link Kline} 数组。
|
||||||
*
|
*
|
||||||
* @param symbol - 交易对符号(如 "BTCUSDT")
|
* @param symbol - 交易对符号(如 "BTCUSDT")
|
||||||
@@ -19,5 +17,11 @@ export async function fetchKlines(
|
|||||||
startTime: number,
|
startTime: number,
|
||||||
limit = 500,
|
limit = 500,
|
||||||
): Promise<Kline[]> {
|
): Promise<Kline[]> {
|
||||||
return client.fetchKlines(symbol, startTime, limit);
|
return fetchKlinesFromExchange({
|
||||||
|
exchange: 'binance',
|
||||||
|
type: 'spot',
|
||||||
|
symbol,
|
||||||
|
startTime,
|
||||||
|
limit,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
+22
-10
@@ -171,6 +171,26 @@ export const DEFAULT_REST_CONFIG: RestClientConfig = {
|
|||||||
defaultLimit: 500,
|
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 重连)
|
// 适配器配置(含 WebSocket 重连)
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -235,18 +255,10 @@ export interface MarketDataFeed {
|
|||||||
/**
|
/**
|
||||||
* REST 拉取历史 K 线(用于补齐缺失数据或回测)。
|
* REST 拉取历史 K 线(用于补齐缺失数据或回测)。
|
||||||
*
|
*
|
||||||
* @param symbol - 交易对符号
|
* @param params - fetchKlines 统一参数对象
|
||||||
* @param startTime - 起始时间(Unix ms)
|
|
||||||
* @param endTime - 结束时间(Unix ms)
|
|
||||||
* @param limit - 最大返回条数(默认 500)
|
|
||||||
* @returns 标准化 K 线数组,按时间升序
|
* @returns 标准化 K 线数组,按时间升序
|
||||||
*/
|
*/
|
||||||
fetchKlines(
|
fetchKlines(params: FetchKlinesParams): Promise<Kline[]>;
|
||||||
symbol: string,
|
|
||||||
startTime: number,
|
|
||||||
endTime: number,
|
|
||||||
limit?: number,
|
|
||||||
): Promise<Kline[]>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取交易所交易对信息(用于自动注册到 trading_pairs 表)。
|
* 获取交易所交易对信息(用于自动注册到 trading_pairs 表)。
|
||||||
|
|||||||
Reference in New Issue
Block a user