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

12 KiB
Raw Blame History

自动化回测服务设计方案

需求背景

三国量化团队需要支持自动化批量回测场景:

  • 将军们提交回测脚本和参数
  • 系统自动运行回测
  • 完成后返回结果给将军
  • 不需要手动干预

现有环境基础

当前 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

请求体:

{
  "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"
}

响应:

{
  "code": 0,
  "msg": "success",
  "data": {
    "task_id": "a1b2c3d4-1234-5678-90ef-ghijklmnopqr",
    "status": "pending"
  }
}

2. 查询任务状态

接口: GET /api/backtest/status/{task_id}

响应:

{
  "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}

响应:

{
  "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

响应:

{
  "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 做进程池,限制并发回测数量,避免资源耗尽:

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

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

启动方式

手动启动(开发环境)

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

后台运行

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

后续实现步骤

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

参考文档


文档记录时间: 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 访问不稳定,二进制下载失败
    解决:跳过预安装,保留配置,由用户启动容器后手动安装

容器启动命令

# 停止并删除旧容器
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
状态: 设计实施完成,等待重新构建镜像