Files
trade/data/exchanges/binance/rest.ts
T
Rekey 705a2f6ea0 feat: 接入 USDT-M 合约数据 — type 字段方案
- PairType 定义移至 types/kline.ts (spot/um/cm)
- Kline 接口新增 type 字段,全链路透传
- klines 5列复合主键 (exchange, symbol, type, interval, time)
- 拆出 BinanceFuturesRestClient (USDMClient)
- exchanges/index.ts 注册 binance_futures
- trading_pairs 唯一约束加 type,种子数据加合约对
- 12个连续聚合视图 SELECT/GROUP BY/INDEX 加 type
- 清理 bnkline.ts 废弃代码和 pair.ts 空函数
2026-06-16 18:39:40 +08:00

208 lines
6.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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";
// ============================================================
// Binance REST K 线 → 本系统标准化 Kline 转换
// ============================================================
/**
* Binance SDK Kline 元组格式(getKlines / getUIKlines 返回):
* [0] openTime: number — 开盘时间(Unix ms)
* [1] open: numberInString — 开盘价
* [2] high: numberInString — 最高价
* [3] low: numberInString — 最低价
* [4] close: numberInString — 收盘价
* [5] volume: numberInString — 成交量(base 币种)
* [6] closeTime: number — 收盘时间(Unix ms)
* [7] quoteVolume: numberInString — 成交额(quote 币种)
* [8] tradeCount: number — 成交笔数
* [9] takerBuyBaseVol: numberInString — 主动买入成交量
* [10] takerBuyQuoteVol: numberInString — 主动买入成交额
* [11] ignore: numberInString — 忽略字段
*
* numberInString = string | number,通过 String() 统一转换。
*
* 参考:node_modules/binance/lib/types/shared.d.ts:85-98
*/
function convertBinanceKline(
raw: BinanceRestKline,
symbol: string,
interval: KlineInterval,
type: PairType,
): Kline {
const [
openTime,
open,
high,
low,
close,
volume,
closeTime,
quoteVolume,
tradeCount,
takerBuyBaseVol,
takerBuyQuoteVol,
// [11] ignore — 丢弃
] = raw;
return {
exchange: "binance",
symbol,
type,
interval,
openTime,
closeTime,
open: String(open),
high: String(high),
low: String(low),
close: String(close),
volume: String(volume),
quoteVolume: String(quoteVolume),
takerBuyBaseVol: String(takerBuyBaseVol),
takerBuyQuoteVol: String(takerBuyQuoteVol),
tradeCount: String(tradeCount),
isClosed: true, // REST 返回的 K 线均为已闭合历史数据
};
}
// ============================================================
// BinanceRestClient
// ============================================================
export class BinanceRestClient extends BaseRestClient {
readonly exchange = "binance";
private client: MainClient;
constructor() {
super();
this.client = new MainClient(
{
api_key: exchange.binance.apiKey,
api_secret: exchange.binance.apiSecret,
},
{ timeout: 3000 },
);
}
/**
* 拉取 1m K 线并转换为本系统标准化 Kline。
*
* Binance 硬限制单次最多 1000 条,超限自动裁切。
* 高周期 K 线通过 TimescaleDB 连续聚合视图生成。
*
* @param symbol - 交易对(如 BTCUSDT
* @param startTime - 起始时间(Unix ms
* @param limit - 单次拉取条数,默认取自 config.defaultLimit
* @param endTime - 结束时间(Unix ms),可选
*/
async fetchKlines(
symbol: string,
startTime: number,
limit?: number,
endTime?: number,
): Promise<Kline[]> {
const effectiveLimit = limit ?? this.config.defaultLimit;
// Binance 硬限制:单次最多 1000 条
const safeLimit = Math.min(effectiveLimit, 1000);
// 限流
await this.throttle(`${symbol}:1m`);
const rawKlines = await this.client.getKlines({
symbol,
interval: "1m",
startTime,
endTime,
limit: safeLimit,
});
logger.debug(
{ symbol, interval: "1m", startTime, endTime, limit: safeLimit },
"[binance] fetchKlines 请求参数",
);
if (!rawKlines || rawKlines.length === 0) {
return [];
}
// 按 openTime 升序排序(防御性),转换为标准化 Kline
return rawKlines
.map((k) => convertBinanceKline(k, symbol, "1m", "spot"))
.sort((a, b) => a.openTime - b.openTime);
}
async fetchMarkets(): Promise<MarketInfo[]> {
// TODO: 调用 Binance /exchangeInfo 接口
return [];
}
}
// ============================================================
// BinanceFuturesRestClient — USDT-M 永续合约
// ============================================================
export class BinanceFuturesRestClient extends BaseRestClient {
readonly exchange = "binance_futures";
private client: USDMClient;
constructor() {
super();
this.client = new USDMClient(
{
api_key: exchange.binanceFutures.apiKey,
api_secret: exchange.binanceFutures.apiSecret,
},
{ timeout: 3000 },
);
}
/**
* 拉取 USDT-M 永续合约 1m K 线。
*
* USDMClient.getKlines() 返回与 MainClient 同构的 12 元组,
* convertBinanceKline 直接复用,type 固定为 'um'。
*/
async fetchKlines(
symbol: string,
startTime: number,
limit?: number,
endTime?: number,
): Promise<Kline[]> {
const effectiveLimit = limit ?? this.config.defaultLimit;
const safeLimit = Math.min(effectiveLimit, 1000);
await this.throttle(`${symbol}:1m`);
const rawKlines = await this.client.getKlines({
symbol,
interval: "1m",
startTime,
endTime,
limit: safeLimit,
});
logger.debug(
{ symbol, interval: "1m", startTime, endTime, limit: safeLimit },
"[binance_futures] fetchKlines 请求参数",
);
if (!rawKlines || rawKlines.length === 0) {
return [];
}
return rawKlines
.map((k) => convertBinanceKline(k, symbol, "1m", "um"))
.sort((a, b) => a.openTime - b.openTime);
}
async fetchMarkets(): Promise<MarketInfo[]> {
return [];
}
}