# 自动化回测服务设计方案

## 需求背景

三国量化团队需要支持**自动化批量回测**场景：
- 将军们提交回测脚本和参数
- 系统自动运行回测
- 完成后返回结果给将军
- 不需要手动干预

## 现有环境基础

当前 Docker 容器环境已经提供：
- ✅ Python 3.10
- ✅ 完整 vnpy 框架
- ✅ `vnpy.trader.backtesting.BacktestingEngine` 原生回测引擎
- ✅ FastAPI（已被 vnpy_webtrader 依赖）
- ✅ SSH 远程访问
- ✅ Jupyter Lab 结果分析

## 设计原则

1. **尽量基于vnpy原生**：核心回测使用原生 `BacktestingEngine`，不重复造轮子
2. **轻量级实现**：不引入过重的第三方任务队列（如 Redis/RabbitMQ）
3. **保持简单**：够用就好，便于维护
4. **不强制自动启动**：作为可选服务，需要时手动启动

## 架构设计

```
┌─────────────┐
│   将军      │
└──────┬──────┘
       │ POST /api/backtest/submit
       ↓
┌──────────────────────────┐
│  FastAPI 回测服务        │ ← 监听 8088 端口
│  - 接收任务              │
│  - 任务排队              │
│  - 进程池执行            │
│  - 保存结果              │
└──────┬───────────────────┘
       │ 调用
       ↓
┌──────────────────────────┐
│  vnpy 原生 BacktestingEngine ← 核心回测计算
│  - 加载历史数据          │
│  - 运行策略回测          │
│  - 计算统计结果          │
│  - 输出图表              │
└──────┬───────────────────┘
       │ 保存结果
       ↓
┌──────────────────────────┐
│  文件系统存储            │
│  - 任务元数据 JSON       │
│  - 回测结果 CSV/JSON    │
│  - 收益曲线图片          │
└──────┬───────────────────┘
       │ GET /api/backtest/result/{task_id}
       ↓
┌─────────────┐
│   将军      │ ← 获取结果分析
└─────────────┘
```

## 端口分配

| 服务 | 端口 | 用途 | 状态 |
|------|------|------|------|
| SSH | 22 → 2222 | 远程登录 | ✅ 已映射 |
| Jupyter Lab | 8888 | Notebook开发 | ✅ 已映射 |
| code-server | 8080 | Web VS Code | ✅ 已映射 |
| VNPY Web Trader | 8000 | 交易Web API | ✅ 已映射 |
| VNPY RPC 交易核心 | 2018 | RPC 请求 | ✅ 已映射 |
| VNPY RPC 交易核心 | 4102 | RPC 订阅 | ✅ 已映射 |
| **自动化回测服务** | **8088** | **回测API** | ✅ 已映射 |

## API 接口设计

### 1. 提交回测任务

**接口：** `POST /api/backtest/submit`

**请求体：**
```json
{
  "strategy_name": "double_ma",
  "strategy_code": "from vnpy.trader.strategy import ...",
  "parameters": {
    "fast_window": 5,
    "slow_window": 20
  },
  "start_date": "2020-01-01",
  "end_date": "2024-01-01",
  "symbol": "IF888",
  "interval": "1h"
}
```

**响应：**
```json
{
  "code": 0,
  "msg": "success",
  "data": {
    "task_id": "a1b2c3d4-1234-5678-90ef-ghijklmnopqr",
    "status": "pending"
  }
}
```

### 2. 查询任务状态

**接口：** `GET /api/backtest/status/{task_id}`

**响应：**
```json
{
  "code": 0,
  "msg": "success",
  "data": {
    "task_id": "a1b2c3d4-1234-5678-90ef-ghijklmnopqr",
    "status": "running|completed|failed",
    "progress": 75,
    "message": "正在计算统计结果"
  }
}
```

### 3. 获取回测结果

**接口：** `GET /api/backtest/result/{task_id}`

**响应：**
```json
{
  "code": 0,
  "msg": "success",
  "data": {
    "task_id": "...",
    "status": "completed",
    "statistics": {
      "total_trades": 100,
      "win_rate": 0.6,
      "sharpe_ratio": 1.8,
      "max_drawdown": 0.15,
      "total_return": 0.35,
      "annual_return": 0.08
    },
    "result_file": "/app/backtest_results/.../result.csv",
    "chart_file": "/app/backtest_results/.../equity_curve.png",
    "completed_at": "2026-04-11T22:00:00Z"
  }
}
```

### 4. 列出所有任务

**接口：** `GET /api/backtest/list`

**响应：**
```json
{
  "code": 0,
  "msg": "success",
  "data": {
    "tasks": [
      {
        "task_id": "...",
        "strategy_name": "...",
        "status": "...",
        "created_at": "..."
      }
    ]
  }
}
```

## 目录结构

```
/app/
├── scripts/
│   └── backtest-service/
│       ├── main.py           # FastAPI 入口
│       ├── task_queue.py     # 任务队列实现
│       ├── executor.py       # 任务执行器
│       ├── models.py         # 数据模型
│       └── README.md         # 使用说明
└── backtest_jobs/
    ├── pending/              # 等待执行
    ├── running/              # 执行中
    ├── completed/            # 已完成
    └── failed/               # 执行失败
```

## 任务队列实现

使用 Python 内置 `multiprocessing.Pool` 做进程池，限制并发回测数量，避免资源耗尽：

```python
# 配置示例
MAX_WORKERS = 2  # 最大并发回测数，根据CPU核数调整
```

每个回测任务在独立进程中运行，互不干扰，一个失败不影响其他任务。

## 启动方式

### 手动启动（开发环境）

```bash
ssh -p 2222 vnpy@192.168.2.153
cd /app/scripts/backtest-service
python main.py
```

### 后台运行

```bash
nohup python main.py > backtest-service.log 2>&1 &
```

## 后续实现步骤

1. [ ] 创建目录结构
2. [ ] 编写 Pydantic 数据模型
3. [ ] 编写任务队列
4. [ ] 编写任务执行器
5. [ ] 编写 FastAPI 路由
6. [ ] 编写 README 使用说明
7. [ ] 测试验证

## 参考文档

- [vnpy-webtrader-research-report.md](./../task-20260331-vnpy-webtrader-research/final/vnpy-webtrader-research-report.md)
- [docker-dependency-cache-scheme.md](./docker-dependency-cache-scheme.md)

---

**文档记录时间：** 2026-04-11 22:21 GMT+8
**记录人：** 姜维 伯约
**状态：** 设计完成，待实现

---

## 成功部署记录 - 2026-04-12

### 部署信息
- **部署目标：** Synology NAS @ 192.168.2.154
- **镜像名称：** `sanguo_vnpy:latest`
- **镜像ID：** `a77f153853b9`
- **容器ID：** `8fccf1eb15ae`
- **基础镜像：** `python:3.10-slim`

### 成功部署的服务

| 服务 | 地址 | 状态 | 验证结果 |
|------|------|------|----------|
| Jupyter Lab | http://192.168.2.154:8888/lab?token=sanguo123 | ✅ 运行正常 | **已验证可访问** |
| SSH | `ssh vnpy@192.168.2.154 -p 2222` (密码: sanguo123) | ✅ 运行正常 | 已修复 hostkey 问题 |
| VNPY Web Trader | http://192.168.2.154:8000 | ⚪ 待手动启动 | 端口已映射 |
| code-server | http://192.168.2.154:8080 | ⚪ 待手动安装 | 配置已预生成 |
| VNPY RPC 请求 | 192.168.2.154:2018 | ✅ 端口就绪 | 已映射 |
| VNPY RPC 订阅 | 192.168.2.154:4102 | ✅ 端口就绪 | 已映射 |
| 自动化回测服务 | http://192.168.2.154:8088 | ✅ 端口就绪 | 已映射，预留 |

### 关键修复

1. **openssh-server 配置文件缺失问题**  
   问题：`openssh-server` 因依赖 systemd 安装报错，`/etc/ssh/sshd_config` 缺失  
   解决：在 Dockerfile 添加判断，如果配置文件不存在则生成默认配置  

2. **sshd hostkey 缺失问题**  
   问题：容器启动时 `sshd: no hostkeys available -- exiting`  
   解决：容器启动后在容器内执行 `ssh-keygen -A` 生成 hostkeys，重启后正常  

3. **code-server 网络安装失败问题**  
   问题：GitHub 访问不稳定，二进制下载失败  
   解决：跳过预安装，保留配置，由用户启动容器后手动安装  

### 容器启动命令

```bash
# 停止并删除旧容器
echo "Ccf7561523" | sudo -S docker stop sanguo_vnpy
echo "Ccf7561523" | sudo -S docker rm sanguo_vnpy

# 启动新容器
echo "Ccf7561523" | sudo -S docker run -d \
  -p 8888:8888 \
  -p 8000:8000 \
  -p 8080:8080 \
  -p 8088:8088 \
  -p 2018:2018 \
  -p 4102:4102 \
  -p 2222:22 \
  --name sanguo_vnpy \
  sanguo_vnpy:latest
```

### 账号信息

| 账号 | 密码 | 用途 |
|------|------|------|
| vnpy (container) | sanguo123 | SSH 登录容器 |
| Jupyter token | sanguo123 | Jupyter Lab 访问 |
| code-server | sanguo123 | code-server 访问（预配置） |

### 验证结果
- ✅ Jupyter Lab 已验证可正常访问
- ✅ 所有端口映射正确配置
- ✅ SSH 已修复可正常连接
- ✅ 完整 vnpy 开发环境已就绪
- ✅ 所有依赖包安装成功

### 记录人
姜维 伯约  
2026-04-12 09:29 GMT+8  
**状态：✅ 部署成功，Jupyter Lab 可访问**

---

## 实施完成记录 - 2026-04-12

### 已完成工作

#### 1. Dockerfile 更新
- ✅ code-server 改为预装，不再跳过
- ✅ 添加 vnpy_webtrader 到 requirements-base.txt
- ✅ 更新 EXPOSE 暴露所有需要的端口（22 8000 8080 8888 2018 4102 8088）

#### 2. 自动化回测服务代码实现
完整目录结构：
```
/app/scripts/backtest-service/
├── config.py           # 配置
├── models.py           # Pydantic 数据模型
├── task_queue.py       # 任务队列管理器
├── executor.py         # 回测执行器（调用 vnpy 原生 BacktestingEngine）
├── result_storage.py   # 结果存储
├── api.py              # FastAPI 路由
├── main.py             # 服务入口
└── README.md           # 使用说明
```

所有代码按照设计方案实现完成：
- ✅ 严格遵循 vnpy 原生设计，不修改核心架构
- ✅ 只做外层封装，完全复用原生 BacktestingEngine
- ✅ multiprocessing.Pool 做并发控制，可配置 MAX_WORKERS
- ✅ 每个回测独立进程，隔离性好，一个失败不影响其他
- ✅ 完整的 CRUD API，支持提交/查询/结果获取

#### 3. 准备好各个服务启动脚本
```
/app/scripts/
├── start_rpc_server.py      # VNPY 交易核心 RPC 服务启动
├── start_web_trader.py      # VNPY Web Trader 服务启动
└── start_backtest_service.py # 自动化回测服务启动
```

按照 vnpy 官方双进程架构：
1. 第一步：启动 `start_rpc_server.py` → RPC 服务端监听 2018/4102
2. 第二步：启动 `start_web_trader.py` → Web 服务监听 8000
3. 需要回测时：启动 `start_backtest_service.py` → API 服务监听 8088

### 当前代码结构确认

| 文件路径 | 功能 | 状态 |
|----------|------|------|
| `scripts/backtest-service/config.py` | 配置 | ✅ 完成 |
| `scripts/backtest-service/models.py` | 数据模型 | ✅ 完成 |
| `scripts/backtest-service/task_queue.py` | 任务队列 | ✅ 完成 |
| `scripts/backtest-service/executor.py` | 回测执行器 | ✅ 完成 |
| `scripts/backtest-service/result_storage.py` | 结果存储 | ✅ 完成 |
| `scripts/backtest-service/api.py` | API 路由 | ✅ 完成 |
| `scripts/backtest-service/main.py` | 服务入口 | ✅ 完成 |
| `scripts/backtest-service/README.md` | 使用说明 | ✅ 完成 |
| `scripts/start_rpc_server.py` | VNPY RPC 启动 | ✅ 完成 |
| `scripts/start_web_trader.py` | VNPY Web Trader 启动 | ✅ 完成 |
| `scripts/start_backtest_service.py` | 自动化回测服务启动 | ✅ 完成 |

### 待执行
- [ ] 重新构建 Docker 镜像
- [ ] 测试验证各个服务启动正常
- [ ] 运行一个示例回测验证功能

**记录人：** 姜维 伯约  
2026-04-12 10:17 GMT+8  
**状态：✅ 设计实施完成，等待重新构建镜像**
