Files
sanguo_vnpy/research/docker/automated-backtest-service-design.md
T
2026-04-12 10:20:48 +08:00

392 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 自动化回测服务设计方案
## 需求背景
三国量化团队需要支持**自动化批量回测**场景:
- 将军们提交回测脚本和参数
- 系统自动运行回测
- 完成后返回结果给将军
- 不需要手动干预
## 现有环境基础
当前 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
**状态:✅ 设计实施完成,等待重新构建镜像**