# 数字货币量化交易系统开发计划

> 基于 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<void>;
  subscribeOrderbook(symbol: string, depth: number): Promise<void>;
  fetchKlines(symbol: string, interval: KlineInterval, limit?: number): Promise<Kline[]>;
}

// 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<void> {
    // 启动数据流管道
    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<KlineInterval, number> = {
  '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<string, KlineBucket>();

  /**
   * 核心合成方法：输入一条 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<Trade>) => {
      const output = new Subject<KlineBucket>();

      // 每笔 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<void> {
    this.batchBuffer.push(kline);

    if (this.batchBuffer.length >= this.batchSize) {
      await this.flush();
    }
  }

  /**
   * 批量刷新缓冲区
   */
  async flush(): Promise<void> {
    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<KlineRecord[]> {
    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<void> {
    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
