feat(data): 实现 Binance WebSocket 适配器与架构重构

- 新增 exchanges/ 模块:MarketDataFeed 统一接口、BaseExchangeAdapter 抽象基类、
  BinanceAdapter 完整实现(WebSocket + REST)
- WebSocket 层基于 binance 官方 SDK 的 WebsocketClient,自动多路复用与断线重连
- REST 层使用 MainClient(Spot),实现 fetchKlines 自动分页补拉 + fetchMarkets 元数据解析
- 数据标准化:Ticker/Trade/OrderBook/Kline 类型定义与 Binance 原生格式互转
- 引入 RxJS Subject 作为统一事件流管道,按 eventType 运行时路由分发
- 重构 config/:YAML 驱动配置加载 + 零依赖运行时校验(fail-fast)
- 重构 db/:TypeORM DataSource 配置 + TimescaleDB K 线 Hypertable 实体
- 新增 utils/logger.ts:Pino 结构化日志(开发环境 pino-pretty 彩色输出)
- 新增 env.yaml 作为 TS/Python 共享的统一环境配置源
- 删除旧版手写 SQL schema 与散落配置文件,收敛到 TypeORM 实体管理
- 安装 rxjs@7.8.2 依赖
This commit is contained in:
Rekey
2026-06-08 01:24:48 +08:00
parent e91cad79e6
commit 85a0031a78
31 changed files with 4261 additions and 8757 deletions
+105
View File
@@ -0,0 +1,105 @@
// ============================================================
// trading-pair.entity.ts — 交易对配置实体
// ============================================================
// 映射到 PostgreSQL trading_pairs 表,存储各交易所的交易对元信息。
// 数据模块启动时从该表读取 active=true 的交易对列表,
// 决定 WebSocket 订阅范围和 K 线合成范围。
//
// 继承 CommonBaseEntityid (UUID) / created_at / updated_at
//
// 与 TimescaleDB klines 表的关系:
// klines.symbol → trading_pairs.symbol(逻辑外键,不做 DB 级约束)
// klines.exchange → exchanges.name(逻辑外键)
// ============================================================
import {
Entity,
Column,
ManyToOne,
JoinColumn,
Index,
} from "typeorm";
import { Exchange } from "./exchange.entity";
import { CommonBaseEntity } from "./common.entity";
@Entity("trading_pairs")
@Index(["exchange", "symbol"], { unique: true }) // 同一交易所下 symbol 唯一
@Index(["active"]) // 按激活状态快速筛选
export class TradingPair extends CommonBaseEntity {
/** 所属交易所 */
@ManyToOne(() => Exchange, { nullable: false })
@JoinColumn({ name: "exchange_id" })
exchange!: Exchange;
/** 交易对符号(如 BTCUSDT / ETHUSDT */
@Column("varchar", { length: 20 })
symbol!: string;
/** 基础币种(如 BTC */
@Column("varchar", { length: 10 })
base_asset!: string;
/** 计价币种(如 USDT */
@Column("varchar", { length: 10 })
quote_asset!: string;
/** 价格精度(小数位数) */
@Column("integer", { default: 10 })
price_precision!: number;
/** 数量精度(小数位数) */
@Column("integer", { default: 10 })
quantity_precision!: number;
/** 最小下单量 */
@Column("numeric", { precision: 32, scale: 8, nullable: true })
min_qty?: number;
/** 下单步长(数量增量) */
@Column("numeric", { precision: 32, scale: 8, nullable: true })
step_size?: number;
/** 最小名义价值(USDT */
@Column("numeric", { precision: 32, scale: 8, nullable: true })
min_notional?: number;
/** 是否激活数据订阅(false 时不采集该交易对行情) */
@Column("boolean", { default: true })
active!: boolean;
/** 是否启用 K 线合成(false 时仅采集原始行情,不合成) */
@Column("boolean", { default: true })
kline_synthesis_enabled!: boolean;
/** K 线合成周期列表(逗号分隔,如 "1m,5m,15m,1h,4h,1d" */
@Column("varchar", { length: 100, default: "1m,5m,15m,1h,4h,1d" })
kline_intervals!: string;
/**
* 历史 K 线最后补全时间(UTC)。
* 记录最近一次 REST 补拉 K 线的结束时间戳,
* 下次启动时从此时间点继续补全,避免重复拉取。
*
* 默认值为 Unix 01970-01-01T00:00:00.000Z),
* 新交易对从 epoch 起始时间开始全量补拉,
* 补全后更新为实际拉取到的最后时间。
*/
@Column("timestamptz", { default: () => "to_timestamp(0)" })
last_backfill_time!: Date;
/** 备注 */
@Column("text", { nullable: true })
notes?: string;
// ============================================================
// 工具方法
// ============================================================
/** 解析 kline_intervals 为周期数组 */
getIntervals(): string[] {
return this.kline_intervals
.split(",")
.map((s) => s.trim())
.filter(Boolean);
}
}