docs(phase3b): 投研+回测 Web 控制台(Vue 前端)设计文档

- 4 期愿景:投研→回测→模拟→实盘(国金QMT),本期 B=投研+回测
- 对齐 vnpy client 回测模块 + 投研自有
- 技术栈 Vue3+Vite+TS+ElementPlus+ECharts+Pinia
- 部署:8000 切 sanguo_api,Vue 静态挂 FastAPI,不动端口/反代
- 含后端补 5 类接口(资金曲线/每日盈亏/成交/K线/报告)
- 4 切片 S0→S1→S2→S3
This commit is contained in:
2026-07-07 00:25:31 +08:00
parent 2398e859f5
commit f0f08d32c3
@@ -0,0 +1,279 @@
# Phase 3b:投研 + 回测 Web 控制台(Vue 前端)设计
> 日期:2026-07-07
> 阶段:Phase 3bB 期)
> 状态:设计待审阅
> 维护:Main Agent
---
## 1. 背景与目标
已交付:
- Phase 1 数据层(A 股 K 线 / SQLite 读取)
- Phase 2 因子 + 回测引擎(`sanguo_factor` / `sanguo_backtest`,真数据跑通)
- Phase 3a 研究 API`sanguo_api`JWT / 异步任务 / WebSocket / 结果查询),本机 + 容器 pytest 通过
两个缺口:
1. **没有前端**——只有 API,用户无法在网页上操作。
2. **`sanguo_api` 未挂公网**——实机查证:公网 `vnpy.mysanguo.top` → 容器:8000 现在跑的是**旧 `sanguo_web`(实盘交易 API,44 路由)**,没有回测/因子接口;Phase 3a 的 `sanguo_api` 只在容器里 pytest 跑过。
**本期目标**:建一个 Vue 前端控制台,对齐 vnpy 桌面 client 的**回测模块**功能 + 投研(因子)自有模块,并把 `sanguo_api` 切到公网 8000,使整条链路从 `vnpy.mysanguo.top` 可用。
---
## 2. 范围
**完整愿景(用户确认,4 期递进)**:投研 → 回测 → 模拟 → 实盘(实盘最后,走**国金证券 QMT** / xtquant)。
**本期 B= B1)范围**
| 类别 | 内容 |
|---|---|
| ✅ 投研 | 因子分析(多标的 / 多因子 / 日期)→ IC 表 + tears 报告 |
| ✅ 回测 | CTA 策略回测(对齐 vnpy client 回测模块:统计全表 / 资金曲线 / 每日盈亏 / 成交记录 / K线+买卖点)+ 参数优化 |
| ✅ 前端 | Vue 3 SPA |
| ✅ 后端补 | 5 类新接口 + 现有接口扩展 |
| ✅ 部署 | 切 `sanguo_api` 到 8000Vue 静态挂 FastAPI |
**不在本期(out of scope**
- ❌ 模拟盘(C 期,后端模拟引擎尚未建)
- ❌ 实盘交易(D 期,国金 QMT;旧 `sanguo_web` 交易路由本期下线,D 期合并回来)
- 导航**预留 4 入口**,模拟/实盘灰显"敬请期待",避免日后重写布局。
---
## 3. 整体架构
```
浏览器 (Vue 3 SPA)
↕ HTTPS vnpy.mysanguo.top ← 外网链路不动(frpc/socat/Caddy 不碰)
FastAPI sanguo_api (容器:8000,从 sanguo_web 切过来)
├─ / → Vue 静态文件 (StaticFilesSPA history fallback)
├─ /api/v1/* → 研究 APIauth / backtest / factor / task / ws
└─ 调后端引擎 → sanguo_backtest / sanguo_factor / sanguo_data
SQLite + A 股 K 线 (NAS /volume1/stock)
```
---
## 4. 前端技术栈
| 层 | 选型 | 备注 |
|---|---|---|
| 框架 | Vue 3 + `<script setup>` + Composition API | 用户指定 Vue |
| 语言 | TypeScript | 控制台体量需要,利维护 |
| 构建 | Vite | 快、标准 |
| UI 库 | Element Plus | 中文量化圈最常用,表格/表单/弹窗齐全 |
| 图表 | ECharts | K线 candlestick + markPoint(买卖点)/ 折线(资金曲线)/ 柱状(每日盈亏)/ 热力图(优化)|
| 状态 | Pinia | Vue 3 标准 |
| 路由 | Vue Router | 标准 |
| HTTP | Axios + JWT 拦截器 | 自动附 Authorization401 回登录 |
| 实时 | 原生 WebSocket | 对接已有 `/api/v1/ws/task/{id}` |
**前端代码目录**:新建 `frontend/`(仓库根),与 Python 包并列。`npm run build` 产物输出到 FastAPI 可挂载的静态目录。
---
## 5. 部署方案(守住红线)
**红线**(用户多次强调):
- 不改容器端口(8000 不变)
- 不动 `vnpy.mysanguo.top` 反向代理 / 转发
- 不碰 frpc / socat / Caddy
**本期改动(只动 NAS 容器内部)**
1. 容器 uvicorn 目标:`sanguo_web.api:app``sanguo_api.app:create_app`(用 `--factory`,传 db_path / file_dir / auth_config / max_workers
- 入口脚本:`docker/entrypoint.sh:283`(改 uvicorn 目标)
2. Vue 打包静态 → FastAPI `StaticFiles(directory=..., html=True)` 挂在 `/`,配 SPA history fallbackcatch-all 回 `index.html`
3. 开发期:本地 Vite dev server5173+ `vite.config.ts` proxy `/api``/ws` → 容器/本地 FastAPI
**部署流程**(沿用项目约定):本机改代码 → rsync 到 NAS 安装目录 → `docker restart sanguo_vnpy_v2`
> 已知问题:本 session rsync/scp 到 NAS 不稳(status 43 / Connection closed)。临时用 `ssh sanguo-nas "cat > /path" < local` 重定向;排期修 sftp 子系统。
---
## 6. 页面设计
### 6.1 页面地图
```
登录页 /login
└ 主控制台(左侧栏 4 入口)
├ 回测 ✅ (S1 / S3)
│ ├ 新建回测 /backtest/new
│ ├ 任务进度 /backtest/task/:id WS 实时阶段)
│ ├ 结果页 /backtest/result/:id (统计/曲线/盈亏/成交/K线买卖点)
│ ├ 参数优化 /backtest/optimize (S3)
│ └ 历史任务 /backtest/history (S3)
├ 投研 ✅ (S2)
│ ├ 新建因子分析 /factor/new
│ └ 结果页 /factor/result/:id IC 表 / tears 报告)
├ 模拟 ⬜ 敬请期待(C 期)
└ 实盘 ⬜ 敬请期待(D 期 · 国金 QMT)
```
### 6.2 页面详情
**登录页**:用户名 + 密码 → `POST /api/v1/auth/login` → 存 JWTlocalStorage)→ 跳主控制台。
**主控制台 shell**:顶栏(系统名 / 用户 / 登出)+ 左侧栏 4 入口(回测/投研点亮,模拟/实盘灰显)+ `<router-view>`
**回测-新建**
- 策略下拉(`GET /strategy/list`)→ 选中后按 `GET /strategy/{name}/params` 渲染动态参数表单
- 标的输入(如 600000)+ 日期区间 +(S3)费率/滑点/资金
- 提交 → `POST /api/v1/backtest/cta` → 拿 task_id → 跳进度页
**回测-进度**`GET /task/{id}` + `WS /ws/task/{id}`,显示状态(pending/running/done/failed+ 中文阶段("排队中/回测中/完成")。done → 跳结果页。
**回测-结果**(对齐 vnpy client 回测模块):
- 统计全表(sharpe / total_return / max_drawdown / win_rate / …,来自 `GET /task/{id}/result`
- 资金曲线(`GET /task/{id}/equity-curve` → ECharts 折线)
- 每日盈亏(`GET /task/{id}/daily-pnl` → ECharts 柱状)
- 成交记录表(`GET /task/{id}/trades` → Element Table
- K线 + 买卖点(`GET /kline?symbol&start&end` + 成交点 → ECharts candlestick + markPoint
**回测-优化**S3):策略 + 参数网格 → `POST /api/v1/backtest/optimize` → 结果表 + 热力图(`GET /task/{id}/optimization-results`)。
**回测-历史**S3):`GET /task?type=cta` → 任务列表,可点回看结果。
**投研-新建**:因子下拉(`GET /factor/list`,如 ma5/ma10/ma20/vol_ma5+ 多标的(多选,如 600000/000001/300750+ 日期 → `POST /api/v1/factor/analyze` → 进度。
**投研-结果**
- IC 表(`GET /task/{id}/ic-summary`:各周期 mean/std/icir/t_stat/count
- tears 报告内嵌(`GET /task/{id}/report/{factor}` 返回 HTML → iframe
---
## 7. 核心流程
### 7.1 回测流程
选策略(DoubleMaStrategy)→ 填参数(fast_window/slow_window)→ 选标的(600000+ 日期 → 提交 → 看进度"回测中" → 完成 → 结果页:统计全表 / 资金曲线 / 每日盈亏 / 成交表 / K线带买卖箭头。**体感对齐 vnpy client 回测模块。**
### 7.2 因子分析流程
选因子(ma5+ 多标的(600000/000001/300750alphalens 横截面需 ≥2)+ 日期 → 提交 → 进度 → 结果页:IC 表(1D/5D/10D mean/icir/t_stat+ tears 报告。
---
## 8. 后端 API
### 8.1 现有(Phase 3a,复用)
| 方法 | 路径 | 用途 |
|---|---|---|
| POST | `/api/v1/auth/login` | 登录取 JWT |
| POST | `/api/v1/backtest/cta` | 提交 CTA 回测 → task_id |
| POST | `/api/v1/backtest/optimize` | 提交参数优化 → task_id |
| POST | `/api/v1/factor/analyze` | 提交因子分析 → task_id |
| GET | `/api/v1/task/{id}` | 任务状态 + 阶段 |
| GET | `/api/v1/task/{id}/result` | 统计 statistics |
| WS | `/api/v1/ws/task/{id}?token=` | 实时阶段推送 |
### 8.2 本期新增(按切片)
| 切片 | 方法 | 路径 | 返回 | 说明 |
|---|---|---|---|---|
| S1 | GET | `/api/v1/strategy/list` | `[{name, class_name}]` | 枚举 vnpy_ctastrategy 可用策略 |
| S1 | GET | `/api/v1/strategy/{name}/params` | `{parameters:[...], defaults:{}}` | 读策略类 `.parameters` 渲染表单 |
| S1 | GET | `/api/v1/task/{id}/equity-curve` | `[{date, balance}]` | 暴露 BacktestResult.equity_curve |
| S1 | GET | `/api/v1/task/{id}/daily-pnl` | `[{date, pnl}]` | 每日盈亏 |
| S1 | GET | `/api/v1/task/{id}/trades` | `[{datetime, direction, offset, price, volume}]` | cta_engine 需实现成交记录(现为 None)|
| S1 | GET | `/api/v1/kline?symbol&start&end` | `[{datetime, open, high, low, close, volume}]` | 历史 K 线(读 A 股 DB;评估能否复用旧 sanguo_web `/market/kline`|
| S2 | GET | `/api/v1/factor/list` | `[{name, desc}]` | 枚举已注册因子(ma5/ma10/ma20/vol_ma5|
| S2 | GET | `/api/v1/task/{id}/ic-summary` | `{factor:{periodD:{mean,std,icir,t_stat,count}}}` | 暴露 FactorReport.ic_summary |
| S2 | GET | `/api/v1/task/{id}/report/{factor}` | HTML | tears 报告服务(StaticFiles 或 FileResponse|
| S3 | 扩展 | `POST /api/v1/backtest/cta` | — | schema 加 rate/slippage/capital 字段;cta_engine 接收 |
| S3 | GET | `/api/v1/task/{id}/optimization-results` | `[{params, statistics}]` | 优化结果结构化 |
| S3 | GET | `/api/v1/task?type=&status=` | `[task 摘要]` | 任务历史列表 |
---
## 9. 数据契约
### 9.1 BacktestResult(现有,`sanguo_backtest/result_store.py`
`task_id, type, status, strategy, symbol, params, start, end, statistics, equity_curve, trades, error_msg`
### 9.2 新增返回结构
- **equity-curve**`[{date: "YYYY-MM-DD", balance: number}]`
- **daily-pnl**`[{date, pnl: number}]`
- **trades**`[{datetime, direction: "多/空", offset: "开/平", price, volume, commission, ...}]`(对齐 vnpy TradeData
- **kline**`[{datetime, open, high, low, close, volume}]`
- **ic-summary**`{factor: {"1D"|"5D"|"10D": {mean, std, icir, t_stat, count}}}`
- **optimization-results**`[{params: {...}, statistics: {...}}]`
所有接口返回 JSON 安全值(Timestamp→str,已在 cta_engine statistics 处理过同样问题)。
---
## 10. 切片交付计划
每片独立可演示、可验收。S1 最重(vnpy client 对齐主战场)。
### S0 脚手架
- **前端**Vite + Vue3 + TS + Element Plus + ECharts + Pinia + Router + Axios 项目骨架;登录页 + JWT 拦截器;4 入口侧栏 shell(回测/投研点亮,模拟/实盘灰显);路由 + history fallback
- **后端**:容器 uvicorn 切 `sanguo_api`FastAPI 挂 SPA 静态
- **验收**:从 vnpy.mysanguo.top 能登录、看到空壳控制台
### S1 回测核心(对齐 vnpy client 回测)
- **前端**:新建回测 / 进度 / 结果页(统计全表 + 资金曲线 + 每日盈亏 + 成交表 + K线买卖点)
- **后端**`strategy/list``strategy/{name}/params``task/{id}/equity-curve``/daily-pnl``/trades``/kline`cta_engine 实现成交记录
- **验收**:跑 DoubleMaStrategy on 600000,结果页与 vnpy client 回测模块一致
### S2 投研核心
- **前端**:新建因子分析 / 结果页(IC 表 + tears 报告内嵌)
- **后端**`factor/list``task/{id}/ic-summary``task/{id}/report/{factor}`
- **验收**:跑 ma5(多标的),看 IC 表 + tears 报告
### S3 优化 + 收尾
- **前端**:参数优化(热力图)/ 历史任务 / 回测费率·滑点·资金可调
- **后端**`backtest/cta` 加参数;`optimization-results``GET /task` 列表
- **验收**:跑优化看热力图、查历史、费率可调
---
## 11. 非功能
- **安全**JWT(已有);SPA 用 localStorage 存 tokenAxios 拦截器附 `Authorization: Bearer`401 → 清 token 回登录;HTTPS 由外网链路保证
- **错误处理**API 错误统一 Element Plus `ElMessage`;任务 failed 展示 `error_msg`
- **测试**
- 前端:Vitest + Vue Test Utils(工具函数 / 关键组件)
- 后端:pytest(新接口单测 + 复用现有容器冒烟模式)
- 容器:`scripts/smoke_phase3b.py`(端到端:登录 → 提交回测 → WS 进度 → 结果页接口齐)
- **代码风格**:前端遵循 ECC coding-style(小文件、不可变、早返回、命名);后端沿用现有 sanguo_* 风格
---
## 12. 风险与约束
| 风险 / 约束 | 处理 |
|---|---|
| 切 8000 到 sanguo_api 会下线旧交易路由 | 已确认(A 方案);D 期合并回来 |
| 两套 auth 都占 `/api/v1/auth/login` | 本期只用 sanguo_api JWTsanguo_web 下线,无冲突 |
| rsync/scp 到 NAS 不稳 | ssh-exec 重定向;排期修 sftp 子系统 |
| 容器 Python 3.10 vs 本机 3.14 | 前端 Node 工具链独立;后端接口在容器测(沿用 Phase 3a 模式)|
| vnpy 零修改原则 | 不改 `vnpy_v4.4.0/`;策略/参数从类属性读 |
| NAS CPU 弱(J4125 无 AVX2| 已有 `POLARS_SKIP_CPU_CHECK`;前端构建在 Mac,产物部署 |
---
## 13. 未来(C / D 期,预留)
- **C 模拟盘**:新建模拟引擎(forward 纸面交易)+ 任务类型 `paper`;前端点亮"模拟"入口
- **D 实盘**:合并 `sanguo_web` 交易路由(需统一 auth,解决 `/api/v1/auth/login` 冲突)+ 接国金 QMTxtquant);前端点亮"实盘"入口
- 导航骨架已预留,C/D 无需重写布局
---
## 14. 开放项(实现阶段确认)
- `/kline` 是否复用旧 `sanguo_web``/api/v1/market/kline`(依赖行情网关 vs 历史 DB)—— S1 评估
- token 存 localStorage(简便)vs httpOnly cookie(更安全)—— S0 默认 localStorage,可调
- 策略参数表单的复杂参数类型(范围/枚举)支持深度 —— S1 按需
---
## 参考文档
- Phase 3a 设计:`docs/superpowers/specs/2026-07-06-phase3a-web-api-design.md`
- 部署实况:`docs/deployment/nas-deploy-plan.md`
- 旧 Web 部署设计:`docs/design/deployment/docker-web-deployment.md`