# 数字货币量化交易系统开发计划 > 基于 Python + TypeScript 混合架构的数字货币量化交易系统 —— 核心模块划分与开发步骤 --- ## 目录 1. [项目概述](#项目概述) 2. [技术栈选型](#技术栈选型) 3. [架构方案对比](#架构方案对比) 4. [核心模块划分](#核心模块划分) 5. [TypeScript 数据模块详解](#typescript-数据模块详解) 6. [开发步骤](#开发步骤) 7. [项目目录结构](#项目目录结构) 8. [部署与运维](#部署与运维) 9. [注意事项与风险提示](#注意事项与风险提示) --- ## 项目概述 本项目的目标是构建一套**可扩展、低延迟、稳定可靠**的数字货币量化交易系统,支持: - 多交易所接入(Binance、OKX、Bybit 等) - 多策略并行运行 - 实时行情数据采集与存储 - 策略回测与参数优化 - 实盘交易执行与风控管理 - 可视化监控与告警 ### 架构策略 采用 **Python + TypeScript 混合架构**: | 层 | 语言 | 职责 | |---|------|------| | **数据层** | TypeScript (Bun) | 行情采集、WebSocket 连接管理、K 线合成、数据写入 | | **业务层** | Python 3.10+ | 策略引擎、回测、风控、交易执行 | | **接口层** | TypeScript / Python | FastAPI (Python) 或 NestJS (TS) 提供 REST/WS API | --- ## 技术栈选型 | 分类 | 技术 / 库 | 说明 | | ------------ | ---------------------------------- | -------------------------------------- | | **数据层语言** | **TypeScript 5.x (Bun)** | 行情采集、WebSocket 管理、数据管道 | | **业务层语言** | **Python 3.10+** | 策略引擎、回测、风控逻辑 | | **时序数据库** | **TimescaleDB (推荐)** | K 线数据存储,基于 PostgreSQL 扩展 | | 关系型数据库 | PostgreSQL 16+ | 订单、策略配置、用户数据等(TimescaleDB 基于 PG,可共用)| | 消息队列 | Redis / NATS | 跨语言数据流解耦,事件驱动 | | 异步框架(TS) | `ws` + `axios` + `bull` | WebSocket 客户端、HTTP 请求、任务队列 | | 异步框架(Py) | `asyncio` + `aiohttp` / `httpx` | 异步 I/O,高性能网络请求 | | 数据分析 | `pandas` + `numpy` + `ta` / `talib` | 技术指标计算与数据分析 | | 策略回测 | `backtrader` / `vectorbt` / 自研 | 高性能回测引擎 | | Web 框架 | FastAPI + Uvicorn | REST API 与 WebSocket 服务 | | 可视化 | Grafana + Streamlit | 监控仪表盘与交互式分析 | | 任务调度 | Celery / APScheduler / Bull | 定时任务与异步任务队列 | | 配置管理 | pydantic-settings + `.env` | 类型安全的配置管理 | | 日志监控 | loguru + winston + sentry | 结构化日志与异常追踪 | | **类型校验** | **Zod (TS) / Pydantic (Py)** | 运行时数据校验,双端类型一致性保证 | --- ## 架构方案对比 ### 方案 A:纯 Python(原方案) ``` [Python] 数据采集 → 策略引擎 → 交易执行 → 风控 ``` | 优势 | 劣势 | |------|------| | 单一语言,维护简单 | WebSocket 大规模连接管理不如 Node.js | | 数据分析生态无敌 | Python GIL 在 CPU 密集型场景受限 | | 量化金融社区成熟 | 异步生态相对 Node.js 较新 | ### 方案 B:TypeScript 数据模块 + Python 业务引擎 ✅ **(推荐)** ``` [TypeScript] 数据采集 → Redis/消息队列 → [Python] 策略引擎 → 交易执行 ``` | 优势 | 劣势 | |------|------| | Node.js WebSocket 性能优异,天然适合高并发连接 | 需要维护两套技术栈 | | TypeScript 类型系统在数据管道中减少运行时错误 | 跨语言调试稍复杂 | | **共享类型**:前后端可用同一套 TypeScript 类型定义 | 部署需要 Bun + Python 双运行时 | | 事件驱动模型与行情数据流天然匹配 | 团队需要双语言能力 | | npm 生态有成熟的交易 SDK(ccxt 等) | | ### 方案 C:全 TypeScript ``` [TypeScript] 数据采集 → 策略引擎 → 交易执行 → 风控 ``` | 优势 | 劣势 | |------|------| | 单一语言 | 回测/量化分析生态远不如 Python | | 类型安全 | 缺乏 pandas/numpy 级别的数据处理库 | | 全栈一致性(前后端 + 数据层共用 TS) | 量化社区资源少,需要自研大量基础设施 | --- ## 核心模块划分 系统划分为 **7 大核心模块**,模块间通过消息队列或 API 解耦,整体架构如下: ``` ┌─────────────────────────────────────────────────────────────┐ │ Web UI / Dashboard │ │ (FastAPI / Streamlit / Grafana) │ └──────────────┬──────────────────┬───────────────────────────┘ │ │ ┌──────────▼──────────┐ ┌───▼────────────────────┐ │ API Gateway │ │ 监控告警模块 │ │ (REST + WebSocket) │ │ (Prometheus + Alert) │ └──────────┬──────────┘ └────────────────────────┘ │ ┌──────────▼──────────────────────────────────────────┐ │ 策略引擎 🐍 Python │ │ (策略加载 / 生命周期管理 / 信号分发) │ └────┬──────────────┬──────────────┬─────────────────┘ │ │ │ ┌────▼────┐ ┌─────▼─────┐ ┌───▼──────────┐ │ 策略 A │ │ 策略 B │ │ 策略 C ... │ │(趋势) │ │ (网格) │ │ │ └────┬────┘ └─────┬─────┘ └───┬──────────┘ │ │ │ ┌────▼──────────────▼──────────────▼─────────────────┐ │ 交易执行模块 🐍 Python │ │ (订单管理 / 仓位管理 / 交易所适配器 / 重试机制) │ └────────────────────┬───────────────────────────────┘ │ ┌────────────────────▼───────────────────────────────┐ │ 消息队列 (Redis / NATS) │ │ ┌───────────── 数据管道 ─────────────┐ │ └────────────────────┬───────────────────────────────┘ │ ┌────────────────────▼───────────────────────────────┐ │ 数据模块 🟦 TypeScript │ │ (行情采集 / K线合成 / 数据存储 / 数据清洗 / 因子计算) │ └────────────────────┬───────────────────────────────┘ │ ┌────────────────────▼───────────────────────────────┐ │ 风控模块 🐍 Python │ │ (资金管理 / 仓位限制 / 最大回撤 / 异常交易熔断 / 白名单)│ └─────────────────────────────────────────────────────┘ ``` --- ### 1. 数据模块 (`data/`) — 🟦 TypeScript 负责从交易所获取和处理市场数据,是一切策略和决策的基础。 #### 为什么数据模块用 TypeScript? | 原因 | 说明 | |------|------| | **WebSocket 性能** | Node.js 事件循环天然适合处理大量 WebSocket 连接(单进程万级连接) | | **内存管理** | V8 引擎的垃圾回收机制在短生命周期对象场景表现优异 | | **类型安全** | TypeScript 编译期检查确保数据管道中的类型正确性 | | **流式处理** | Node.js Stream / Observable 模式与行情数据流完美匹配 | | **生态优势** | 各交易所官方/社区 SDK 对 Node.js 支持最好 | #### 子模块划分 | 子模块 | 职责 | 关键技术点 | | ---------------- | -------------------------------------------------------------------- | ----------------------------------------------- | | **行情采集器** | 通过 WebSocket 订阅实时行情(ticker、orderbook、trade),定时拉取K线 | `ws`、断线重连、心跳检测、连接池管理 | | **K 线合成器** | 将实时 tick/trade 流合成为 OHLCV K 线,支持多周期并行、补齐、异常处理 | 时间桶算法、增量更新、多级合成(1m→5m→15m→1h) | | **数据存储** | K 线数据持久化到 TimescaleDB,订单数据存入 PostgreSQL | TimescaleDB hypertable、批量 UPSERT、列式压缩 | | **数据清洗** | 去除异常 tick、填充缺失值、处理停盘数据 | 统计异常检测、插值法 | | **数据发布** | 将处理后的数据通过消息队列推送给 Python 策略引擎 | Redis Pub/Sub、NATS、协议缓冲(Protocol Buffers) | #### 关键接口 ```typescript // src/data/collector.ts export interface DataFeed { subscribeTicker(symbols: string[]): Promise; subscribeOrderbook(symbol: string, depth: number): Promise; fetchKlines(symbol: string, interval: KlineInterval, limit?: number): Promise; } // src/data/types.ts export interface Ticker { exchange: string; symbol: string; price: number; volume: number; timestamp: number; } export interface Kline { exchange: string; symbol: string; interval: KlineInterval; open: number; high: number; low: number; close: number; volume: number; timestamp: number; } // src/data/pipeline.ts export class DataPipeline { constructor( private collector: DataFeed, private klineSynthesizer: KlineSynthesizer, private storage: DataStorage, private publisher: DataPublisher, ) {} async start(): Promise { // 启动数据流管道 const tickerStream = await this.collector.subscribeTicker(['BTCUSDT', 'ETHUSDT']); tickerStream .pipe(this.klineSynthesizer.transform()) .pipe(this.storage.writeStream()) .pipe(this.publisher.publishStream()); } } ``` --- #### K 线合成器详解 K 线合成器是数据模块中最核心的组件之一,负责将交易所推送的 **实时离散数据** 合成为标准的 **OHLCV K 线**。以下是其完整的工作机制。 ##### 1. 输入数据源 合成器接收三种类型的原始数据: | 数据源 | 交易所推送频率 | 数据量 | 用途 | |--------|---------------|--------|------| | **Ticker**(最新成交价) | 100ms~1s/次 | 中等 | 实时价格监控,快速 K 线更新 | | **Trade**(逐笔成交) | 毫秒级 | 极大 | 精确 K 线合成(推荐) | | **K 线(原始)** | 1s~1min(交易所合成好的) | 小 | 直接使用,无需合成(最快方案) | > **推荐方案**:使用 `Trade`(逐笔成交)作为主数据源合成 K 线,精度最高;`Ticker` 作为辅助用于快速预览。 ##### 2. 核心算法:时间桶(Time Bucket) ``` 时间轴(1分钟 K 线为例): 时间桶 [t0, t0+1min) ┌─────────────────────────────────────────┐ │ Trade A Trade B Trade C │ │ p: 50000 p: 50020 p: 50010 │ │ v: 0.5 v: 1.2 v: 0.8 │ │ t: 00:10 t: 00:25 t: 00:45 │ │ │ │ OHLCV = { O: 50000, H: 50020, │ │ L: 50000, C: 50010, │ │ V: 2.5 } │ └─────────────────────────────────────────┘ ``` **算法步骤**: ``` 每收到一条 Trade/Ticker: 1. 计算时间桶索引:bucketIndex = floor(timestamp / interval_ms) 2. 如果 bucketIndex != currentBucketIndex: a. 关闭当前桶,emit K 线 b. 创建新桶,初始化 O=H=L=C=当前价格, V=当前成交量 3. 否则(仍在当前桶内): a. O = 不变(桶内第一条的价格) b. H = max(H, 当前价格) c. L = min(L, 当前价格) d. C = 当前价格 e. V += 当前成交量 ``` ##### 3. 多周期并行合成 系统需要同时维护多个周期的 K 线(1m、5m、15m、1h...),有两种实现策略: **策略 A:独立合成(每个周期独立计算)** ✅ 推荐 ``` Trade 流 ──► 1m 合成器 ──► 1m K 线 ├──► 5m 合成器 ──► 5m K 线 ├──► 15m 合成器 ─► 15m K 线 └──► 1h 合成器 ──► 1h K 线 ``` - 每个周期维护独立的时间桶 - 优点:实现简单,各周期互不影响 - 缺点:内存占用随周期数量线性增长 **策略 B:多级合成(从低周期聚合为高周期)** ``` Trade 流 ──► 1m 合成器 ──► 5m 合成器 ──► 15m 合成器 ──► 1h 合成器 ↑ 由 5 根 1m 合成 ↑ 由 3 根 5m 合成 ``` - 高周期 K 线由低周期 K 线聚合而成 - 优点:内存效率高,无需为每个周期维护独立的 Trade 缓存 - 缺点:低周期 K 线未完成时,高周期无法产生完整 K 线 **混合策略(推荐)**: ``` Trade 流 ──► 1m 合成器(实时,数据源为 Trade) │ ▼ 1m K 线 ──► 5m 聚合器(由 5 根 1m 聚合) │ ▼ 5m K 线 ──► 15m 聚合器 │ ▼ 15m K 线 ──► 1h 聚合器 ``` 1m 用 Trade 实时合成(精度最高),5m 及以上从 1m 聚合(计算量最小)。 ##### 4. K 线补齐与边界处理 这是合成器最容易被忽视但极其重要的部分: | 场景 | 问题 | 处理方案 | |------|------|----------| | **无成交周期** | 某个 1m 区间内没有任何 Trade | 使用前一根 K 线的 Close 作为当前 K 线的 OHLC(平盘),Volume=0 | | **交易所暂停** | 交易所临时停盘或维护 | 停止 emit 新 K 线,标记为 `gap: true`,策略端跳过 | | **周期边界错位** | 交易所 1h K 线从整点开始,本地合成可能有时区偏移 | 使用 UTC 时间统一对齐,`bucketIndex = floor(utcTimestamp / intervalMs) * intervalMs` | | **首根 K 线不完整** | 系统启动时间不在周期起点,第一根 K 线数据不足 | emit 时标记 `isPartial: true`,策略端可选择忽略或使用 | | **数据延迟/乱序** | Trade 到达顺序与发生顺序不一致 | 缓冲区保留 100ms 的排序窗口,按 timestamp 排序后处理 | ##### 5. 增量更新 vs 完整替换 ```typescript // 增量更新模式(推荐)—— 只推送变化部分,大幅减少数据量 interface KlineDelta { exchange: string; symbol: string; interval: KlineInterval; bucketTime: number; // 时间桶起始时间戳(毫秒) o: number; // Open(只在开桶时更新) h: number; // High l: number; // Low c: number; // Close v: number; // Volume isClosed: boolean; // 当前桶是否已关闭(不再变化) tradeCount: number; // 桶内的成交笔数 } // 完整 K 线(用于回测和历史数据) interface Kline { exchange: string; symbol: string; interval: KlineInterval; openTime: number; closeTime: number; open: number; high: number; low: number; close: number; volume: number; quoteVolume: number; // 成交额 takerBuyBaseVolume: number; // 主动买入量 takerBuyQuoteVolume: number; // 主动买入额 tradeCount: number; // 成交笔数 } ``` ##### 6. 完整实现示例 ```typescript // src/data/pipeline/kline-synthesizer.ts import { Subject, interval } from 'rxjs'; import { bufferTime, filter, map } from 'rxjs/operators'; type KlineInterval = '1m' | '5m' | '15m' | '30m' | '1h' | '4h' | '1d'; const INTERVAL_MS: Record = { '1m': 60_000, '5m': 300_000, '15m': 900_000, '30m': 1_800_000, '1h': 3_600_000, '4h': 14_400_000, '1d': 86_400_000, }; interface Trade { price: number; amount: number; timestamp: number; } interface KlineBucket { openTime: number; open: number; high: number; low: number; close: number; volume: number; tradeCount: number; isClosed: boolean; } export class KlineSynthesizer { private buckets = new Map(); /** * 核心合成方法:输入一条 Trade,输出(可能)闭合的 K 线 */ synthesize( symbol: string, interval: KlineInterval, trade: Trade, ): KlineBucket | null { const intervalMs = INTERVAL_MS[interval]; const bucketTime = Math.floor(trade.timestamp / intervalMs) * intervalMs; const key = `${symbol}:${interval}:${bucketTime}`; let bucket = this.buckets.get(key); if (!bucket) { // 新时间桶:初始化 bucket = { openTime: bucketTime, open: trade.price, high: trade.price, low: trade.price, close: trade.price, volume: trade.amount, tradeCount: 1, isClosed: false, }; this.buckets.set(key, bucket); return null; // 新桶第一条,暂不 emit } // 更新当前桶 bucket.high = Math.max(bucket.high, trade.price); bucket.low = Math.min(bucket.low, trade.price); bucket.close = trade.price; bucket.volume += trade.amount; bucket.tradeCount += 1; return null; // 桶未关闭 } /** * 关闭过期桶(由定时器调用),返回所有已关闭的 K 线 */ closeExpiredBuckets(now: number): KlineBucket[] { const closed: KlineBucket[] = []; for (const [key, bucket] of this.buckets.entries()) { if (!bucket.isClosed && now >= bucket.openTime + INTERVAL_MS['1m']) { bucket.isClosed = true; closed.push(bucket); this.buckets.delete(key); } } return closed; } /** * RxJS 操作符:将 Trade 流转换为 K 线流 */ transform(interval: KlineInterval) { return (source: Subject) => { const output = new Subject(); // 每笔 Trade 更新桶 source.subscribe((trade) => { this.synthesize('BTCUSDT', interval, trade); }); // 定时检查过期桶(每秒一次) interval(1000).subscribe(() => { const closed = this.closeExpiredBuckets(Date.now()); closed.forEach((k) => output.next(k)); }); return output; }; } } ``` ##### 7. 性能考量 | 指标 | 数据量 | 说明 | |------|--------|------| | **单币种 Trade/秒** | ~50~200 tps | BTCUSDT 高峰期 | | **单币种 Ticker/秒** | ~10 tps | Binance WebSocket 推送频率 | | **内存开销/币种** | ~1~5 MB | 100 个并行时间桶 | | **单笔合成耗时** | < 1 μs | Node.js V8 优化后 | | **10 币种 5 周期并行** | ~500 μs/轮 | 全部更新一次 | > **优化建议**:对于高频币种(BTC/ETH),可以采用 Trade 合成 + 1m K 线直接复用交易所推送的原始 K 线(无需自行合成)。 ##### 8. 与交易所 K 线的差异处理 | 差异项 | 交易所推送 K 线 | 本地合成 K 线 | |--------|---------------|-------------| | **延迟** | 通常延迟 1~5 秒 | 实时(Trade 到达即更新) | | **精度** | 包含所有成交数据 | 取决于 WebSocket 推送的 Trade 覆盖率 | | **完整性** | 最终数据,不可变 | 可能因断线导致缺失(需补齐) | | **自定义周期** | 仅支持标准周期 | 支持任意周期(如 3m、7m、自定义) | | **多交易所对齐** | 各交易所时间不同 | 使用 UTC 统一对齐 | > **最佳实践**:本地合成 K 线用于 **实时策略决策**,交易所 K 线用于 **回测和历史分析**。两者结合使用。 --- #### TimescaleDB 数据存储方案 K 线数据具有典型的时序特征:**持续写入、按时间范围查询、少有更新、数据量大**。TimescaleDB 作为 PostgreSQL 的时序扩展,相比 InfluxDB 有以下核心优势: | 对比维度 | TimescaleDB | InfluxDB | |---------|------------|----------| | **基础数据库** | PostgreSQL 扩展 | 独立时序数据库 | | **SQL 兼容** | 完整 SQL 支持(JOIN、子查询、窗口函数) | 类 SQL 但不完全兼容 | | **与业务数据关联** | K 线表和订单表可以在同一个 PG 实例中 JOIN | 需要跨数据库查询 | | **数据压缩** | 列式压缩,压缩比 90%+ | 压缩比约 85% | | **连续聚合** | 内置自动刷新物化视图 | 需要外部工具 | | **保留策略** | 内置自动删除(数据保留策略) | 内置 | | **生态集成** | 可与 PostGIS、pgvector 等 PG 扩展共存 | 独立生态 | | **运维成本** | 复用 PG 运维经验 | 需独立运维 | ##### 1. 表结构设计 ```sql -- 创建 TimescaleDB 扩展 CREATE EXTENSION IF NOT EXISTS timescaledb; CREATE EXTENSION IF NOT EXISTS timescaledb_toolkit; -- ============================================ -- 1. K 线主表(hypertable) -- ============================================ CREATE TABLE klines ( time TIMESTAMPTZ NOT NULL, -- K 线开盘时间 exchange TEXT NOT NULL, -- 交易所 (binance/okx/bybit) symbol TEXT NOT NULL, -- 交易对 (BTCUSDT/ETHUSDT) interval TEXT NOT NULL, -- 周期 (1m/5m/15m/1h/4h/1d) -- OHLCV open NUMERIC(20,8) NOT NULL, high NUMERIC(20,8) NOT NULL, low NUMERIC(20,8) NOT NULL, close NUMERIC(20,8) NOT NULL, volume NUMERIC(20,8) NOT NULL, -- 扩展字段 quote_volume NUMERIC(20,8), -- 成交额(计价币种) taker_buy_base_vol NUMERIC(20,8), -- 主动买入成交量 taker_buy_quote_vol NUMERIC(20,8), -- 主动买入成交额 trade_count INTEGER, -- 成交笔数 is_closed BOOLEAN DEFAULT TRUE, -- K 线是否已关闭 -- 元数据 created_at TIMESTAMPTZ DEFAULT NOW(), -- 记录创建时间 updated_at TIMESTAMPTZ DEFAULT NOW(), -- 最后更新时间 -- 分区键 -- time 是分区列,symbol + interval 是分布列 UNIQUE (time, exchange, symbol, interval) ); -- 转换为 hypertable(按时间和空间分区) SELECT create_hypertable( 'klines', 'time', -- 时间分区列 chunk_time_interval => INTERVAL '1 day', -- 每分区 1 天数据 partitioning_column => 'exchange', -- 空间分区列 number_partitions => 4, -- 4 个空间分区 if_not_exists => TRUE ); -- ============================================ -- 2. 索引设计 -- ============================================ -- 查询最频繁:按交易对+周期+时间范围查 CREATE INDEX idx_klines_lookup ON klines (exchange, symbol, interval, time DESC); -- 回测查询:按交易对+周期+时间范围 CREATE INDEX idx_klines_backtest ON klines (symbol, interval, time ASC); -- 最新 K 线查询 CREATE INDEX idx_klines_latest ON klines (exchange, symbol, interval, time DESC) WHERE is_closed = TRUE; -- ============================================ -- 3. 数据压缩策略 -- ============================================ -- 启用压缩(已关闭的 K 线自动压缩) ALTER TABLE klines SET ( timescaledb.compress, timescaledb.compress_segmentby = 'exchange, symbol, interval', timescaledb.compress_orderby = 'time DESC' ); -- 自动压缩策略:K 线关闭 7 天后压缩 SELECT add_compression_policy('klines', INTERVAL '7 days', if_not_exists => TRUE); -- ============================================ -- 4. 数据保留策略 -- ============================================ -- 1m K 线保留 30 天 SELECT add_retention_policy('klines', INTERVAL '30 days', if_not_exists => TRUE); ``` ##### 2. 连续聚合(Continuous Aggregates) 连续聚合是 TimescaleDB 最强大的功能之一 —— 自动从低周期 K 线聚合为高周期 K 线,无需手动维护。 ```sql -- ============================================ -- 5m K 线(从 1m 自动聚合) -- ============================================ CREATE MATERIALIZED VIEW klines_5m WITH (timescaledb.continuous) AS SELECT time_bucket('5 minutes', time) AS time, exchange, symbol, '5m'::TEXT AS interval, FIRST(open, time) AS open, MAX(high) AS high, MIN(low) AS low, LAST(close, time) AS close, SUM(volume) AS volume, SUM(quote_volume) AS quote_volume, SUM(taker_buy_base_vol) AS taker_buy_base_vol, SUM(taker_buy_quote_vol) AS taker_buy_quote_vol, SUM(trade_count) AS trade_count FROM klines WHERE interval = '1m' GROUP BY time_bucket('5 minutes', time), exchange, symbol; -- 自动刷新策略(每 1 分钟刷新最近 10 分钟数据) SELECT add_continuous_aggregate_policy('klines_5m', start_offset => INTERVAL '1 day', end_offset => INTERVAL '10 minutes', -- 留 10 分钟给延迟数据 schedule_interval => INTERVAL '1 minute', if_not_exists => TRUE ); -- ============================================ -- 15m K 线 -- ============================================ CREATE MATERIALIZED VIEW klines_15m WITH (timescaledb.continuous) AS SELECT time_bucket('15 minutes', time) AS time, exchange, symbol, '15m'::TEXT AS interval, FIRST(open, time), MAX(high), MIN(low), LAST(close, time), SUM(volume), SUM(quote_volume), SUM(taker_buy_base_vol), SUM(taker_buy_quote_vol), SUM(trade_count) FROM klines WHERE interval = '1m' GROUP BY time_bucket('15 minutes', time), exchange, symbol; SELECT add_continuous_aggregate_policy('klines_15m', start_offset => INTERVAL '2 days', end_offset => INTERVAL '30 minutes', schedule_interval => INTERVAL '5 minutes', if_not_exists => TRUE ); -- ============================================ -- 1h K 线 -- ============================================ CREATE MATERIALIZED VIEW klines_1h WITH (timescaledb.continuous) AS SELECT time_bucket('1 hour', time) AS time, exchange, symbol, '1h'::TEXT AS interval, FIRST(open, time), MAX(high), MIN(low), LAST(close, time), SUM(volume), SUM(quote_volume), SUM(taker_buy_base_vol), SUM(taker_buy_quote_vol), SUM(trade_count) FROM klines WHERE interval = '1m' GROUP BY time_bucket('1 hour', time), exchange, symbol; SELECT add_continuous_aggregate_policy('klines_1h', start_offset => INTERVAL '3 days', end_offset => INTERVAL '1 hour', schedule_interval => INTERVAL '5 minutes', if_not_exists => TRUE ); -- ============================================ -- 1d K 线(含周线、月线可在查询时用 time_bucket) -- ============================================ CREATE MATERIALIZED VIEW klines_1d WITH (timescaledb.continuous) AS SELECT time_bucket('1 day', time) AS time, exchange, symbol, '1d'::TEXT AS interval, FIRST(open, time), MAX(high), MIN(low), LAST(close, time), SUM(volume), SUM(quote_volume), SUM(taker_buy_base_vol), SUM(taker_buy_quote_vol), SUM(trade_count) FROM klines WHERE interval = '1m' GROUP BY time_bucket('1 day', time), exchange, symbol; SELECT add_continuous_aggregate_policy('klines_1d', start_offset => INTERVAL '7 days', end_offset => INTERVAL '2 hours', schedule_interval => INTERVAL '1 hour', if_not_exists => TRUE ); ``` **查询示例**: ```sql -- 查询 BTC 最近 100 根 1h K 线(自动走连续聚合) SELECT * FROM klines_1h WHERE symbol = 'BTCUSDT' AND exchange = 'binance' ORDER BY time DESC LIMIT 100; -- 查询 BTC 1h K 线的周线 SELECT time_bucket('1 week', time) AS week, FIRST(open, time), MAX(high), MIN(low), LAST(close, time), SUM(volume) FROM klines_1h WHERE symbol = 'BTCUSDT' GROUP BY week ORDER BY week DESC; -- 统计最近 7 天各币种波动率 SELECT symbol, MAX(high) / MIN(low) - 1 AS volatility FROM klines_1h WHERE time > NOW() - INTERVAL '7 days' GROUP BY symbol ORDER BY volatility DESC; ``` ##### 3. 空间与时间分区原理 ``` hypertable: klines ┌─────────────────────────────────────────────────────┐ │ 时间线 │ │ │ │ chunk_1 (2024-01-01) chunk_2 (2024-01-02) ... │ │ ┌──────────────────┐ ┌──────────────────┐ │ │ │ exchange=A │ │ exchange=A │ │ │ │ exchange=B │ │ exchange=B │ │ │ │ exchange=C │ │ exchange=C │ │ │ │ exchange=D │ │ exchange=D │ │ │ └──────────────────┘ └──────────────────┘ │ │ │ │ 每个 chunk 内部按 exchange 做空间分区 │ │ chunk 大小 ≈ 1 天数据(自动调整) │ └─────────────────────────────────────────────────────┘ ``` | 参数 | 值 | 说明 | |------|----|------| | `chunk_time_interval` | `1 day` | 每个 chunk 存储 1 天数据,约 100~500MB | | `number_partitions` | 4 | 按交易所分区,4 个空间分区 | | 空间分区列 | `exchange` | Binance/OKX/Bybit 等分布在不同分区 | ##### 4. TypeScript 写入实现 ```typescript // data/src/storage/timescaledb.ts import { Pool } from 'pg'; interface KlineRecord { time: Date; exchange: string; symbol: string; interval: string; open: number; high: number; low: number; close: number; volume: number; quoteVolume: number; takerBuyBaseVol: number; takerBuyQuoteVol: number; tradeCount: number; isClosed: boolean; } export class TimescaleDBStorage { private pool: Pool; private batchBuffer: KlineRecord[] = []; private batchSize: number; private flushInterval: number; constructor(config: { connectionString: string; batchSize?: number; // 批量写入条数,默认 500 flushIntervalMs?: number; // 最大等待时间,默认 1000ms }) { this.pool = new Pool({ connectionString: config.connectionString }); this.batchSize = config.batchSize ?? 500; this.flushInterval = config.flushIntervalMs ?? 1000; // 定时刷新缓冲区 setInterval(() => this.flush(), this.flushInterval); } /** * 写入单条 K 线(加入缓冲区,批量写入) */ async write(kline: KlineRecord): Promise { this.batchBuffer.push(kline); if (this.batchBuffer.length >= this.batchSize) { await this.flush(); } } /** * 批量刷新缓冲区 */ async flush(): Promise { if (this.batchBuffer.length === 0) return; const batch = this.batchBuffer.splice(0, this.batchSize); const client = await this.pool.connect(); try { // 使用 UNLOGGED 表写入时跳过 WAL 日志(提升写入性能) await client.query('SET LOCAL timescaledb.enable_skip_scan = ON'); // 批量 UPSERT:已存在的 K 线只更新 close/high/low(增量更新) const values = batch.map((k, i) => { const offset = i * 14; return `($${offset + 1}, $${offset + 2}, $${offset + 3}, $${offset + 4}, $${offset + 5}, $${offset + 6}, $${offset + 7}, $${offset + 8}, $${offset + 9}, $${offset + 10}, $${offset + 11}, $${offset + 12}, $${offset + 13}, $${offset + 14})`; }).join(','); const params = batch.flatMap(k => [ k.time, k.exchange, k.symbol, k.interval, k.open, k.high, k.low, k.close, k.volume, k.quoteVolume, k.takerBuyBaseVol, k.takerBuyQuoteVol, k.tradeCount, k.isClosed, ]); await client.query(` INSERT INTO klines ( time, exchange, symbol, interval, open, high, low, close, volume, quote_volume, taker_buy_base_vol, taker_buy_quote_vol, trade_count, is_closed ) VALUES ${values} ON CONFLICT (time, exchange, symbol, interval) DO UPDATE SET high = GREATEST(klines.high, EXCLUDED.high), low = LEAST(klines.low, EXCLUDED.low), close = EXCLUDED.close, volume = klines.volume + EXCLUDED.volume, trade_count = klines.trade_count + EXCLUDED.trade_count, is_closed = EXCLUDED.is_closed, updated_at = NOW() `, params); } finally { client.release(); } } /** * 查询 K 线 */ async query(options: { exchange: string; symbol: string; interval: string; startTime: Date; endTime: Date; limit?: number; }): Promise { const result = await this.pool.query(` SELECT * FROM klines WHERE exchange = $1 AND symbol = $2 AND interval = $3 AND time >= $4 AND time <= $5 ORDER BY time ASC LIMIT $6 `, [ options.exchange, options.symbol, options.interval, options.startTime, options.endTime, options.limit ?? 1000, ]); return result.rows; } async close(): Promise { await this.flush(); await this.pool.end(); } } ``` ##### 5. 写入性能优化策略 | 策略 | 说明 | 效果 | |------|------|------| | **批量写入** | 每 500 条或 1 秒批量写入一次 | 减少 99% 的数据库连接开销 | | **UPSERT** | 用 `ON CONFLICT DO UPDATE` 处理同一 K 线的增量更新 | 避免重复写入 | | **连接池** | `pg.Pool` 管理 10~20 个连接 | 控制并发,避免 PG 连接暴涨 | | **压缩** | 关闭 7 天后自动压缩 | 存储空间减少 90%+ | | **chunk 对齐** | chunk 按天分区,避免跨分区查询 | 查询性能提升 10x | | **索引** | 精确的复合索引覆盖所有查询模式 | 避免全表扫描 | ##### 6. Python 读取实现(策略引擎侧) ```python # engine/common/storage.py import asyncpg from datetime import datetime from typing import Optional class TimescaleDBReader: """Python 策略引擎侧的 K 线读取器""" def __init__(self, dsn: str): self.dsn = dsn self.pool: Optional[asyncpg.Pool] = None async def connect(self): self.pool = await asyncpg.create_pool( self.dsn, min_size=5, max_size=20, command_timeout=10, ) async def get_klines( self, symbol: str, interval: str, start_time: datetime, end_time: datetime, exchange: str = "binance", limit: int = 1000, ) -> list[dict]: """ 获取 K 线数据(自动路由到连续聚合视图) 当 interval >= '1h' 时,自动使用连续聚合视图查询, 性能远优于扫描原始 1m 表。 """ # 自动路由到对应的物化视图 view_map = { "5m": "klines_5m", "15m": "klines_15m", "30m": "klines_30m", "1h": "klines_1h", "4h": "klines_4h", "1d": "klines_1d", } table = view_map.get(interval, "klines") async with self.pool.acquire() as conn: rows = await conn.fetch( f""" SELECT time, open, high, low, close, volume, quote_volume, trade_count FROM {table} WHERE exchange = $1 AND symbol = $2 AND interval = $3 AND time >= $4 AND time <= $5 ORDER BY time ASC LIMIT $6 """, exchange, symbol, interval, start_time, end_time, limit, ) return [dict(row) for row in rows] async def get_latest_kline( self, symbol: str, interval: str, exchange: str = "binance", ) -> Optional[dict]: """获取最新一根 K 线""" async with self.pool.acquire() as conn: row = await conn.fetchrow( """ SELECT * FROM klines WHERE exchange = $1 AND symbol = $2 AND interval = $3 AND is_closed = TRUE ORDER BY time DESC LIMIT 1 """, exchange, symbol, interval, ) return dict(row) if row else None async def get_ohlcv_batch( self, symbol: str, interval: str, limit: int = 200, exchange: str = "binance", ) -> list[tuple]: """ 获取 OHLCV 数组(直接用于 pandas DataFrame 或 TA-Lib 计算) 返回格式: [(timestamp, open, high, low, close, volume), ...] """ view_map = { "5m": "klines_5m", "15m": "klines_15m", "1h": "klines_1h", "4h": "klines_4h", "1d": "klines_1d", } table = view_map.get(interval, "klines") async with self.pool.acquire() as conn: rows = await conn.fetch( f""" SELECT time, open, high, low, close, volume FROM {table} WHERE exchange = $1 AND symbol = $2 AND interval = $3 ORDER BY time DESC LIMIT $4 """, exchange, symbol, interval, limit, ) # 正序返回 return [ (row['time'], row['open'], row['high'], row['low'], row['close'], row['volume']) for row in reversed(rows) ] async def close(self): if self.pool: await self.pool.close() ``` ##### 7. 磁盘空间估算 ```sql -- 单币种单交易所 1m K 线行数 -- 1天 = 1440 行,1月 ≈ 43200 行,1年 ≈ 525600 行 -- 无压缩时: -- 1 行 ≈ 180 bytes -- 10 币种 × 1 年 × 180 B = 945 MB -- 启用压缩后(压缩比约 92%): -- 10 币种 × 1 年 × 180 B × 8% ≈ 75 MB -- 50 币种 × 1 年 ≈ 375 MB ``` | 数据量 | 未压缩 | 压缩后 (92%) | 连续聚合额外 | |--------|--------|-------------|------------| | 10 币种 / 1 年 | ~945 MB | ~75 MB | ~30 MB | | 50 币种 / 1 年 | ~4.7 GB | ~375 MB | ~150 MB | | 200 币种 / 1 年 | ~18.9 GB | ~1.5 GB | ~600 MB | ##### 8. Docker Compose 配置 ```yaml # docker-compose.yml version: '3.8' services: timescaledb: image: timescale/timescaledb:2-pg16 container_name: trade-timescaledb restart: unless-stopped ports: - "5432:5432" environment: POSTGRES_DB: trade POSTGRES_USER: trader POSTGRES_PASSWORD: ${DB_PASSWORD:-changeme} volumes: - timescaledb-data:/var/lib/postgresql/data - ./data/init-db:/docker-entrypoint-initdb.d # 自动执行建表 SQL command: > -c shared_buffers=1GB -c effective_cache_size=3GB -c maintenance_work_mem=256MB -c work_mem=64MB -c wal_buffers=64MB -c random_page_cost=1.1 -c effective_io_concurrency=200 -c max_connections=50 healthcheck: test: ["CMD-SHELL", "pg_isready -U trader -d trade"] interval: 10s timeout: 5s retries: 5 volumes: timescaledb-data: ``` > **初始化脚本**:将上述建表 SQL 保存为 `data/init-db/001_init.sql`,容器首次启动时自动执行。 --- #### 推荐的 npm 包 | 包名 | 用途 | |------|------| | `ws` | WebSocket 客户端(轻量、高性能) | | `ccxt` | 统一交易所 API(支持 100+ 交易所) | | `pg` | PostgreSQL 客户端(TimescaleDB 兼容) | | `ioredis` | Redis 客户端(支持集群、哨兵) | | `zod` | 运行时数据校验,与 Python Pydantic 对应 | | `pino` | 高性能结构化日志 | | `rxjs` | 响应式编程,优雅处理数据流 | | `bull` / `bullmq` | Redis 任务队列,用于定时采集任务 | | `technicalindicators` | 技术指标计算(备选) | --- ### 2. 策略引擎 (`engine/`) — 🐍 Python 核心调度中心,负责策略的生命周期管理和信号分发。 #### 子模块划分 | 子模块 | 职责 | 关键技术点 | | -------------- | ---------------------------------------------- | -------------------------------------- | | **通用模块** | 策略基类、数据模型、日志、配置 | `engine/common/` 目录,基础类型定义 | | **策略管理器** | 策略注册、启动、停止、热加载 | 插件化架构、动态导入、`importlib` | | **信号分发器** | 将策略产生的交易信号分发到交易执行模块 | 事件总线、消息队列 | | **回测引擎** | 使用历史数据模拟策略执行,评估收益、回撤等指标 | `vectorbt` / `backtrader`、事件驱动 | | **参数优化器** | 网格搜索 / 贝叶斯优化策略参数 | `scipy.optimize`、`optuna`、并行计算 | #### 策略基类设计 ```python from common import BaseStrategy, StrategyConfig, Signal from common.models import Kline, Ticker, OrderBook class MyStrategy(BaseStrategy): """策略示例 — 所有策略继承 BaseStrategy""" strategy_type = "my_strategy" async def on_kline(self, kline: Kline) -> Signal | None: ... ``` --- ### 3. 交易执行模块 (`executor/`) — 🐍 Python 负责将策略信号转化为实际订单,管理交易所连接和仓位。虽然数据采集在 TypeScript 层完成,但下单操作仍需在 Python 侧执行,因为策略引擎和风控逻辑同在 Python 侧,减少跨语言通信延迟。 #### 子模块划分 | 子模块 | 职责 | 关键技术点 | | ---------------- | ------------------------------------------------------ | ---------------------------------------- | | **交易所适配器** | 封装不同交易所的 REST / WebSocket API,提供统一接口 | 适配器模式、限频控制、签名算法 | | **订单管理器** | 下单、撤单、查询订单状态;支持限价单、市价单、条件单 | 订单状态机、幂等性、超时处理 | | **仓位管理器** | 跟踪当前持仓、可用余额、未实现盈亏 | 实时同步、增量更新 | | **执行算法** | TWAP、VWAP、冰山订单等高级执行算法(降低滑点) | 切片算法、时间加权 | #### 交易所适配器接口 ```python class ExchangeAdapter(ABC): """交易所统一适配器接口""" @abstractmethod async def create_order(self, order: Order) -> OrderResult: ... @abstractmethod async def cancel_order(self, order_id: str) -> bool: ... @abstractmethod async def get_balance(self) -> dict[str, float]: ... @abstractmethod async def get_position(self, symbol: str) -> Position: ... ``` --- ### 4. 风控模块 (`risk/`) — 🐍 Python 系统的安全防线,在交易前、交易中、交易后全链路控制风险。 | 功能 | 说明 | | ------------------ | ------------------------------------------------------------------ | | **资金管理** | 单笔下单量限制、总仓位比例限制、逐仓/全仓模式支持 | | **最大回撤控制** | 当日/累计回撤超过阈值时自动暂停策略 | | **异常检测** | 检测价格异常波动、交易所 API 异常、网络延迟过高 | | **熔断机制** | 极端行情下自动停止所有交易,进入只平不开模式 | | **订单频率限制** | 控制单位时间内的下单次数,防止过度交易 | | **白名单/黑名单** | 限制可交易的币种和交易对 | ```python class RiskManager: async def check_order(self, order: Order, context: TradingContext) -> RiskResult: """下单前风控检查""" checks = [ self._check_max_position, self._check_order_value, self._check_order_frequency, self._check_drawdown_limit, self._check_price_deviation, ] for check in checks: result = await check(order, context) if not result.passed: return result return RiskResult(passed=True) ``` --- ### 5. 监控告警模块 (`monitor/`) 保障系统运行的可观测性。 | 功能 | 说明 | | ---------------- | -------------------------------------------------- | | **指标收集** | 收集策略收益、胜率、夏普比率、最大回撤等绩效指标 | | **系统监控** | CPU / 内存 / 网络延迟 / API 调用量 | | **告警通知** | 通过钉钉、Telegram、飞书等发送告警 | | **日志管理** | 结构化日志存储,支持按级别、模块检索 | | **Web 仪表盘** | 实时展示资金曲线、持仓、交易记录、策略状态等 | --- ### 6. API 网关 (`api/`) 对外提供统一接口,支持浏览器端和移动端访问。 | 功能 | 说明 | | ------------------ | ----------------------------------- | | **REST API** | 策略管理、查询持仓、查看交易记录 | | **WebSocket** | 实时推送行情、交易信号和通知 | | **用户认证** | JWT 认证、API Key 管理 | | **权限管理** | 多用户角色(管理员 / 只读用户) | --- ### 7. 配置与工具模块 (`common/`) 提供跨模块共享的基础设施。 | 模块 | 功能 | | -------- | ---------------------------------------- | | **配置** | 基于 `pydantic-settings` 的环境配置管理 | | **日志** | 基于 `loguru` 的统一日志配置 | | **工具** | 时间工具、重试装饰器、限流器、加解密工具 | | **模型** | 共享的 Pydantic 数据模型(订单、K 线等) | | **常量** | 交易所枚举、订单类型、时间周期等定义 | --- ## TypeScript 数据模块详解 ### 核心架构 ``` ┌─────────────────────────────────────────────────────────────┐ │ TypeScript 数据模块 │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ Binance │ │ OKX │ │ Bybit │ │ │ │ WS Adapter │ │ WS Adapter │ │ WS Adapter │ │ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │ │ │ │ │ │ └───────────────────┼───────────────────┘ │ │ ▼ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ 统一行情流 (RxJS Observable) │ │ │ │ ┌───────────┐ ┌──────────┐ ┌─────────────────┐ │ │ │ │ │ ticker$ │ │ trade$ │ │ orderbook$ │ │ │ │ │ └─────┬─────┘ └────┬─────┘ └───────┬─────────┘ │ │ │ └────────┼──────────────┼────────────────┼─────────────┘ │ │ ▼ ▼ ▼ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ K 线合成器 (时间桶算法) │ │ │ │ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │ │ │ │ │ 1m │ │ 5m │ │ 15m │ │ 1h │ ... │ │ │ │ └──┬───┘ └──┬───┘ └──┬───┘ └──┬───┘ │ │ │ └────────┼────────┼────────┼────────┼─────────────────┘ │ │ ▼ ▼ ▼ ▼ │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ 数据清洗 & 异常检测 │ │ │ └───────────────┬──────────────────┬───────────────────┘ │ │ ▼ ▼ │ │ ┌─────────────────────┐ ┌─────────────────────────┐ │ │ │ TimescaleDB 写入器 │ │ Redis Pub/Sub 发布者 │ │ │ └─────────────────────┘ └───────────┬─────────────┘ │ └────────────────────────────────────────┼───────────────────┘ │ ▼ ┌────────────────────┐ │ Python 策略引擎 │ │ (消费实时行情) │ └────────────────────┘ ``` ### 数据流生命周期 ``` 交易所 WS ──► TypeScript 采集器 ──► RxJS 管道 │ ┌───────────┴───────────┐ │ │ ▼ ▼ TimescaleDB 持久化 Redis Pub/Sub │ │ ▼ ▼ Grafana 查询 Python 策略消费 (历史分析) (实时交易) ``` ### TypeScript 数据模块目录结构 ``` data/ ├── package.json ├── tsconfig.json ├── vitest.config.ts ├── src/ │ ├── index.ts # 模块入口 │ ├── config.ts # 配置(基于 zod 校验) │ ├── logger.ts # 日志(pino) │ │ │ ├── exchanges/ # 交易所适配器 │ │ ├── types.ts # 交易所统一接口 │ │ ├── base.ts # 抽象基类 │ │ ├── binance.ts # Binance 实现 │ │ ├── okx.ts # OKX 实现 │ │ └── bybit.ts # Bybit 实现 │ │ │ ├── pipeline/ # 数据管道 │ │ ├── kline-synthesizer.ts # K 线合成器 │ │ ├── cleaner.ts # 数据清洗 │ │ └── transformer.ts # 数据转换 │ │ │ ├── storage/ # 数据存储 │ │ ├── timescaledb.ts # TimescaleDB 写入器 │ │ └── postgres.ts # PostgreSQL 客户端(业务数据) │ │ │ ├── publisher/ # 数据发布 │ │ ├── redis.ts # Redis Pub/Sub │ │ └── nats.ts # NATS(可选) │ │ │ ├── types/ # 类型定义 │ │ ├── market.ts # Ticker, Kline, Trade… │ │ ├── exchange.ts # 交易所枚举 │ │ └── enums.ts # 常量枚举 │ │ │ └── utils/ # 工具函数 │ ├── retry.ts # 重试逻辑 │ ├── rate-limiter.ts # 限频器 │ └── time.ts # 时间工具 │ └── tests/ ├── exchanges/ ├── pipeline/ └── storage/ ``` ### Python ↔ TypeScript 跨语言通信 ``` ┌──────────────────┐ Redis/NATS ┌──────────────────┐ │ 🟦 TypeScript │ ──────────────────────────► │ 🐍 Python │ │ 数据模块 │ Pub: "market:ticker" │ 策略引擎 │ │ │ Pub: "market:kline:1m" │ │ │ │ Pub: "market:trade" │ Sub: 消费行情 │ └──────────────────┘ └──────────────────┘ │ │ │ Redis Pub/Sub 频道设计 │ │ ───────────────────── │ │ market:ticker:{exchange}:{symbol} │ │ market:kline:{exchange}:{symbol}:{interval} │ │ market:trade:{exchange}:{symbol} │ │ system:heartbeat:{module} │ │ system:error:{module} │ │ │ └─────────────────────────────────────────────────┘ ``` **数据序列化格式**:推荐使用 **Protocol Buffers** 或 **MessagePack**,比 JSON 更小、更快: ```typescript // TypeScript 侧 - 发布 const data = { symbol: 'BTCUSDT', price: 50000, timestamp: Date.now() }; const encoded = msgpack.encode(data); // 比 JSON 小 30-50% await redis.publish('market:ticker:binance:BTCUSDT', encoded); ``` ```python # Python 侧 - 消费 import msgpack async with redis.pubsub() as pubsub: await pubsub.subscribe('market:ticker:binance:BTCUSDT') async for msg in pubsub.listen(): if msg['type'] == 'message': data = msgpack.unpackb(msg['data']) await strategy.on_ticker(Ticker(**data)) ``` --- ## 开发步骤 ### 第一阶段:基础建设(第 1-2 周) **目标**:搭建项目骨架,完成 TypeScript 数据模块的基础能力和 Python 项目基础。 | 步骤 | 任务 | 产出物 | | ---- | ------------------------------------------------------------ | ----------------------------------- | | 1.1 | 初始化 TypeScript 数据项目(`data/`),配置 `package.json` | 项目结构,ESLint + Prettier 配置 | | 1.2 | 初始化 Python 项目,配置 `poetry` / `uv` 依赖管理 | `pyproject.toml`,项目目录结构 | | 1.3 | 定义共享类型:TypeScript `types/` + Python `engine/common/models.py` | 双端对齐的数据模型 | | 1.4 | 实现 TS 交易所适配器基类 + Binance 适配器(`data/src/exchanges/`) | 统一接口 + WebSocket 连接 | | 1.5 | 实现 TS 行情采集器,WebSocket 订阅实时 ticker 和 K 线 | 实时行情流入 | | 1.6 | 实现 TS K 线合成器(`data/src/pipeline/kline-synthesizer.ts`) | 多周期 K 线实时合成 | | 1.7 | 实现 TS 数据存储模块,写入 TimescaleDB | 数据持久化 | | 1.8 | 实现 TS → Redis 数据发布 | 实时行情推送到消息队列 | | 1.9 | Python 侧实现配置管理 + 日志 + Redis 订阅消费者 | 消费端就绪 | | 1.10 | Docker Compose 编排基础服务(TimescaleDB / Redis) | `docker-compose.yml` | ### 第二阶段:策略与回测(第 3-4 周) **目标**:实现策略框架和回测引擎,能编写策略并进行回测验证。 | 步骤 | 任务 | 产出物 | | ---- | ------------------------------------------------------------ | -------------------------------- | | 2.1 | 实现策略基类(`engine/common/base.py`) | `BaseStrategy` 抽象基类 | | 2.2 | 实现策略管理器(`engine/manager.py`),支持策略注册和生命周期 | 策略热加载、启动/停止控制 | | 2.3 | 实现信号分发器(`engine/signals.py`) | 事件总线,策略到执行器的信号传递 | | 2.4 | 实现回测引擎(`engine/backtest.py`) | 历史数据回测,收益曲线、回撤等指标 | | 2.5 | 实现技术指标计算(`data/indicators.py`) | MA、MACD、RSI 等常用指标 | | 2.6 | 编写示例策略 1:双均线交叉策略 | 可运行的策略示例 | | 2.7 | 编写示例策略 2:网格交易策略 | 可运行的策略示例 | | 2.8 | 实现参数优化器(`engine/optimizer.py`) | 基于 Optuna 的参数搜索 | ### 第三阶段:交易执行与风控(第 5-6 周) **目标**:连接实盘交易通道,实现完整的交易执行流程和风控体系。 | 步骤 | 任务 | 产出物 | | ---- | ------------------------------------------------------------ | --------------------------------- | | 3.1 | 实现订单管理器(`executor/order_manager.py`) | 下单、撤单、订单状态更新 | | 3.2 | 实现仓位管理器(`executor/position_manager.py`) | 实时仓位跟踪 | | 3.3 | 实现交易状态机(`executor/state_machine.py`) | 订单生命周期管理(Created→Filled) | | 3.4 | 实现重试与容错机制(`executor/retry.py`) | 网络异常自动重试、幂等性保证 | | 3.5 | 实现风控管理器(`risk/manager.py`) | 资金管理、仓位限制、熔断逻辑 | | 3.6 | 实现风控规则引擎(`risk/rules.py`) | 可扩展的风控规则链 | | 3.7 | 整合 TS 数据模块 → Redis → Python 策略引擎 → 交易执行的端到端流程 | 完整的跨语言运行流水线 | | 3.8 | 编写集成测试,模拟实盘环境验证 | 测试覆盖率报告 | ### 第四阶段:API 与监控(第 7-8 周) **目标**:完善系统的可观测性和对外接口。 | 步骤 | 任务 | 产出物 | | ---- | ------------------------------------------------------------ | ----------------------------------- | | 4.1 | 搭建 FastAPI 项目(`api/main.py`) | REST API 服务启动 | | 4.2 | 实现 WebSocket 实时推送(`api/ws.py`) | 行情和交易数据实时推送 | | 4.3 | 实现策略管理 API(`api/routes/strategies.py`) | 策略启停、参数修改接口 | | 4.4 | 实现交易记录查询 API(`api/routes/trades.py`) | 交易历史查询 | | 4.5 | 集成 Prometheus 指标(`monitor/metrics.py`) | 关键业务指标暴露 | | 4.6 | 配置 Grafana 仪表盘 | 可视化资金曲线、交易频率、策略绩效 | | 4.7 | 实现告警通知集成(`monitor/alerts.py`) | Telegram / 钉钉通知 | | 4.8 | JWT 用户认证与 API Key 管理 | 安全访问控制 | ### 第五阶段:优化与生产化(第 9-10 周) **目标**:性能优化,系统加固,上线准备。 | 步骤 | 任务 | 产出物 | | ---- | ------------------------------------------------------------ | -------------------------------- | | 5.1 | 性能优化:数据库批量写入、连接池调优、TS/Python 异步性能分析 | 性能测试报告 | | 5.2 | 增加单元测试与集成测试,目标覆盖率 > 80% | 测试报告 | | 5.3 | 编写部署文档(`docs/deployment.md`) | 部署手册 | | 5.4 | 编写使用文档(`docs/usage.md`) | 用户手册 | | 5.5 | CI/CD 流水线配置(GitHub Actions) — TS 和 Python 双管道 | 自动化测试、构建、部署 | | 5.6 | 压力测试与稳定性测试 | 压测报告 | | 5.7 | 生产环境部署与监控上線 | 线上系统 | --- ## 项目目录结构 ``` trade/ ├── pyproject.toml # Python 项目依赖与配置(Poetry / uv) ├── package.json # TS 项目根依赖(可选 monorepo 管理) ├── docker-compose.yml # 基础服务编排 ├── Dockerfile # 应用容器化 ├── .env.example # 环境变量模板 ├── .gitignore ├── README.md │ ├── config/ # Python 配置文件 │ ├── __init__.py │ ├── settings.py # pydantic-settings 全局配置 │ └── strategies/ # 策略配置文件 │ ├── ma_cross.yaml │ └── grid_trading.yaml │ ├── data/ # 🟦 TypeScript 数据模块 │ ├── package.json │ ├── tsconfig.json │ ├── vitest.config.ts │ ├── src/ │ │ ├── index.ts # 模块入口 │ │ ├── config.ts # 配置 │ │ ├── exchanges/ # 交易所适配器 │ │ │ ├── types.ts │ │ │ ├── base.ts │ │ │ ├── binance.ts │ │ │ ├── okx.ts │ │ │ └── bybit.ts │ │ ├── pipeline/ # 数据管道 │ │ │ ├── kline-synthesizer.ts │ │ │ ├── cleaner.ts │ │ │ └── transformer.ts │ │ ├── storage/ # 数据存储 │ │ │ ├── timescaledb.ts # TimescaleDB K 线写入 │ │ │ └── postgres.ts # PostgreSQL 其他数据 │ │ ├── publisher/ # 数据发布 │ │ │ ├── redis.ts │ │ │ └── nats.ts │ │ ├── types/ # 类型定义 │ │ │ ├── market.ts │ │ │ ├── exchange.ts │ │ │ └── enums.ts │ │ └── utils/ │ │ ├── retry.ts │ │ ├── rate-limiter.ts │ │ └── time.ts │ └── tests/ │ ├── engine/ # 🐍 Python 策略引擎 │ ├── __init__.py │ ├── env.yaml # 引擎环境配置 │ ├── common/ # 引擎通用模块 │ │ ├── __init__.py │ │ ├── base.py # BaseStrategy 基类 │ │ ├── models.py # 数据模型(Kline/Ticker/Trade/OrderBook) │ │ └── logger.py # 结构化日志 │ ├── manager.py # 策略管理器 │ ├── signals.py # 信号分发器 │ ├── backtest.py # 回测引擎 │ └── optimizer.py # 参数优化器 │ ├── executor/ # 🐍 Python 交易执行模块 │ ├── __init__.py │ ├── exchange.py # 交易所适配器(基类+实现) │ ├── order_manager.py # 订单管理器 │ ├── position_manager.py # 仓位管理器 │ ├── state_machine.py # 订单状态机 │ └── retry.py # 重试与容错 │ ├── risk/ # 🐍 Python 风控模块 │ ├── __init__.py │ ├── manager.py # 风控管理器 │ └── rules.py # 风控规则 │ ├── monitor/ # 监控告警模块 │ ├── __init__.py │ ├── metrics.py # Prometheus 指标 │ ├── alerts.py # 告警通知 │ └── dashboard.py # 仪表盘数据聚合 │ ├── api/ # API 网关 │ ├── __init__.py │ ├── main.py # FastAPI 入口 │ ├── ws.py # WebSocket 处理 │ ├── auth.py # 用户认证 │ └── routes/ # 路由 │ ├── __init__.py │ ├── strategies.py │ ├── trades.py │ └── account.py │ ├── strategies/ # 🐍 Python 策略实现 │ ├── __init__.py │ ├── ma_cross.py # 双均线交叉策略 │ ├── grid_trading.py # 网格交易策略 │ └── arbitrage.py # 套利策略(可选) │ ├── common/ # 🐍 Python 公共基础设施(跨模块共享配置、工具等) │ ├── __init__.py │ ├── config.py # 全局配置 │ ├── constants.py # 常量定义 │ └── utils.py # 工具函数 │ ├── tests/ # 🐍 Python 测试 │ ├── __init__.py │ ├── data/ │ ├── engine/ │ ├── executor/ │ ├── risk/ │ └── conftest.py # pytest fixtures │ └── docs/ # 文档 ├── deployment.md ├── usage.md └── api_reference.md ``` --- ## 部署与运维 ### 服务组件依赖 ``` ┌──────────────────┐ ┌──────────────┐ ┌─────────────┐ │ TS 数据模块 │ │ PostgreSQL │ │ Grafana │ │ (Bun 进程) │ │ (业务数据) │ │ (可视化) │ └──────┬───────────┘ └──────────────┘ └──────┬──────┘ │ │ │ ┌──────────────┐ │ ├────────────▶ TimescaleDB │◀────────────┤ │ │ (时序存储) │ │ │ └──────────────┘ │ │ │ │ ┌──────────────┐ │ ├────────────▶ Redis ├─────────────┤ │ │ (队列/缓存) │ │ │ └──────┬───────┘ │ │ │ │ │ ┌──────▼───────┐ │ └────────────▶ Python 引擎 │ │ │ (策略+执行) │ │ └──────────────┘ │ ``` ### 容器化方案 ```dockerfile # Dockerfile.data — TypeScript 数据模块 FROM node:20-alpine AS builder WORKDIR /app COPY data/package*.json ./ RUN npm ci COPY data/ . RUN npm run build FROM node:20-alpine AS runner WORKDIR /app COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules CMD ["node", "dist/index.js"] ``` ```dockerfile # Dockerfile.engine — Python 业务引擎 FROM python:3.11-slim WORKDIR /app COPY pyproject.toml poetry.lock ./ RUN pip install poetry && poetry install --no-dev COPY . . CMD ["poetry", "run", "python", "-m", "engine.main"] ``` ### 关键运维指标 - **API 延迟**:单次下单 < 500ms(网络延迟不计) - **数据延迟**:行情从交易所到 Redis < 500ms(TypeScript 采集) - **系统可用性**:核心交易链路 > 99.9% - **策略执行周期**:秒级 tick 策略 < 100ms / 次 - **跨语言通信延迟**:Redis Pub/Sub < 1ms(同机部署) --- ## 注意事项与风险提示 > ⚠️ **风险声明**:数字货币交易具有高风险,本系统仅为技术实现,不构成任何投资建议。使用前请充分了解风险。 ### 技术注意事项 1. **API 限频**:各交易所均有严格 API 限频,需实现本地限流器 2. **WebSocket 重连**:网络波动导致断线,需实现心跳检测和自动重连(指数退避策略) 3. **数据一致性**:订单状态需轮询确认,避免因异步回调丢失状态 4. **时间同步**:服务器需 NTP 同步,防止时间偏差导致下单失败 5. **日志安全**:避免记录 API Secret 等敏感信息到日志 6. **灰度发布**:新策略先在模拟盘运行,验证通过后方可接入实盘 7. **资金隔离**:不同策略的资金账户严格隔离,防止交叉影响 8. **跨语言类型一致性**:TypeScript 的 `zod` schema 和 Python 的 `Pydantic` model 需保持同步,建议用自动化脚本生成或共享 schema 文件 ### 开发建议 1. **模块化开发**:各模块独立开发、独立测试,通过接口契约协作 2. **代码质量**:TS 用 `ESLint` + `Prettier`,Python 用 `ruff` + `mypy` 3. **版本控制**:配置文件和策略参数不应硬编码,使用 `.env` 或配置文件 4. **日志先行**:先写好日志,再写业务逻辑,便于调试 5. **单元测试**:核心逻辑(风控、订单状态机、K 线合成器)必须有单元测试覆盖 6. **模拟盘先行**:实盘前至少 2 周模拟盘验证策略稳定性 7. **本地开发**:Python 和 TS 模块分别 `npm run dev` / `poetry run dev` 独立开发调试 8. **schema 驱动**:考虑使用 Protocol Buffers 的 `.proto` 文件同时生成 TS 和 Python 代码,保证类型完全一致 --- ## 许可证 MIT License