docs(phase2): 因子/回测层 design(brainstorming 3 轮确认,待 review)

This commit is contained in:
2026-07-05 23:47:07 +08:00
parent 6da8a55b5e
commit 83ca15b57b
@@ -0,0 +1,235 @@
# Phase 2 因子/回测层 Design(架构设计)
> **对应 PRD**`docs/superpowers/specs/2026-07-05-vnpy-quant-platform-prd.md`
> **承接**Phase 1 数据层(已 DONE,见 `docs/superpowers/reports/2026-07-05-phase1-data-layer-completion.md`
> **范围**:因子层 + CTA 回测层 + 任务编排 + 轻量 FastAPI
> **日期**2026-07-05
> **状态**Draft(待用户 review
---
## 0. 范围决策(brainstorming 已确认)
| 决策点 | 选择 | 理由 |
|--------|------|------|
| Phase 2 范围 | 因子层 + CTA 回测层 + 任务编排 + 轻量 FastAPI(完整闭环) | 用户要"因子+回测全做" |
| 回测范式 | 因子分层回测 + CTA 策略回测两条都要 | 覆盖多因子研究 + 策略回测两个闭环 |
| 编排边界 | 含轻量 FastAPI(提交/查询,无 JWT/WS/前端) | 计算层稳了再套完整 Web(Phase 3 |
| 代码组织 | 方案 A 分层解耦(4 包) | 单一职责、独立可测、vnpy 零改造 |
---
## 1. 架构总览
```
┌──────────────────────────────────────────────┐
│ sanguo_api (轻量 FastAPI) │
│ POST /backtest/cta · /optimize · /factor │
│ GET /task/{id} · /task/{id}/result │
└──────────────┬───────────────────────────────┘
│ submit_task
┌──────────────┴───────────────────────────────┐
│ sanguo_orchestrator (编排层) │
│ multiprocessing pool · 任务状态机 · runner │
└──────┬───────────────────────────┬───────────┘
│ │
┌──────┴────────────┐ ┌──────────┴────────────┐
│ sanguo_factor │ │ sanguo_backtest │
│ vnpy.alpha 适配 │ │ BacktestingEngine │
│ 因子注册(可插拔) │ │ run_optimization │
│ Alphalens 分析 │ │ result_store │
└──────┬────────────┘ └──────────┬────────────┘
│ │
└───────────┬───────────────┘
│ read_db_daily / read_parquet_daily
┌──────────────────┴───────────────────────────┐
│ Phase 1 数据层(已 DONE
└──────────────────────────────────────────────┘
```
**核心原则**(继承自 PRD/design):
- vnpy 引擎层零改造(ADR-3),适配层只包装
- 因子层可插拔(ADR-8
- 多进程 + Ray(不用 CeleryADR-2
- 不引 Qlib(用 vnpy.alphaADR-1
**两条回测路径物理隔离**
- 因子分层回测 → `sanguo_factor.analyzer`Alphalens IC/IR/分层收益)
- CTA 策略回测 → `sanguo_backtest.cta_engine`BacktestingEngine 时间序列)
---
## 2. 组件设计
### 2.1 `sanguo_factor/`(因子层)
| 模块 | 职责 | 关键接口 |
|------|------|---------|
| `alpha_adapter.py` | vnpy.alpha 适配(DataProxy + cs_function 范式) | 内部,封装 vnpy.alpha 数据接入 |
| `registry.py` | 因子注册机制(`@register_factor` 装饰器,ADR-8 可插拔) | `register_factor(name, fn)` / `get_factor(name)` |
| `library.py` | Alpha158/101 内置因子 + 自定义因子统一注册 | `list_factors()` / `list_categories()` |
| `analyzer.py` | Alphalens 适配(IC/IR/分层回测),analyzer 接口可插拔 | `compute_factors(symbols, names, start, end) → DataFrame`<br>`analyze_factor(values, fwd_returns) → FactorReport` |
**因子注册范式**(遵循 vnpy.alpha):
```python
@register_factor("ma5", category="trend")
def ma5(proxy: DataProxy, symbol: str, dt: datetime) -> float:
# cs_function 范式
...
```
自定义因子按此接口注册,与 Alpha158/101 内置因子统一管理。
**扩展点**:analyzer 接口可插拔——未来加非 Alphalens 分析器不改架构。
### 2.2 `sanguo_backtest/`(回测层,CTA 路径)
| 模块 | 职责 | 关键接口 |
|------|------|---------|
| `cta_engine.py` | BacktestingEngine wrapper(单标的策略回测,headless | `run_cta_backtest(strategy, symbol, params, start, end) → BacktestResult` |
| `cta_optimizer.py` | run_optimization wrapper(参数网格,`max_workers``output=False` | `run_cta_optimization(strategy, symbol, grid, start, end) → list[OptResult]` |
| `result_store.py` | 结果存储:SQLite 统计 + parquet 净值/成交 | `save_result(result) → id` / `load_result(id)` / `list_results(filter)` |
**结果存储 schema**SQLite `backtest_stats` 表):
- 任务字段:`id, task_id, type(cta/optimize/factor), status, created_at`
- 策略字段:`strategy, symbol, params(json), start, end`
- 指标字段:`return, sharpe, max_drawdown, win_rate, ...`
- 文件指针:`equity_path(parquet), trades_path(parquet)`
- 错误信息:`error_msg(text, 任务 failed 时记录异常)`
> 净值曲线 + 成交明细不入库(大数据),存 parquet 文件,db 存路径指针。
**因子层范围边界**:本 design 因子层只到 **Alphalens 分层分析**(IC/IR/分层收益),**不含组合回测**(选股→加权→调仓的主动组合管理)。组合回测是更复杂的范式,留待未来 phase。
### 2.3 `sanguo_orchestrator/`(编排层)
| 模块 | 职责 |
|------|------|
| `pool.py` | multiprocessing pool(基于 S2 spike 结果定 max_workers 策略) |
| `task.py` | 任务状态机:`pending → running → done/failed` |
| `runner.py` | 调度器:分发 cta_backtest / cta_optimization / factor_analysis |
**接口**
- `submit_task(task_spec) → task_id`(异步,立即返回)
- `get_task_status(task_id) → TaskStatus`
- `get_task_result(task_id) → Result`
### 2.4 `sanguo_api/`(轻量 FastAPI
| 路由 | 功能 |
|------|------|
| `POST /api/v1/backtest/cta` | 提交 CTA 回测任务 |
| `POST /api/v1/backtest/optimize` | 提交参数优化任务 |
| `POST /api/v1/factor/analyze` | 提交因子分析任务 |
| `GET /api/v1/task/{id}` | 查询任务状态 |
| `GET /api/v1/task/{id}/result` | 获取任务结果 |
**不含**(留 Phase 3):JWT 认证、WS 进度推送、Vue 前端。
---
## 3. 数据流
```
因子分析闭环:
read_db_daily → compute_factors(多 symbol) → analyze_factor(Alphalens)
→ FactorReport(IC/IR/分层收益) → 报告文件(parquet + html)
CTA 回测闭环:
read_db_daily → BacktestingEngine(strategy, params)
→ 统计指标入 SQLite + 净值/成交入 parquet
参数优化闭环:
read_db_daily → run_optimization(grid, max_workers)
→ 多组结果入 SQLite + 最优组合标记
API 触发链:
FastAPI 收请求 → orchestrator.submit_task → pool 异步执行
→ 结果落库 → GET /task/{id} 轮询状态/结果
```
---
## 4. Spike 前置(Phase 2 第一步,落地前必做)
| 顺序 | Spike | 验证内容 | 风险 | Fail 的 fallback |
|------|-------|---------|------|-----------------|
| **S3 先** | peewee 版本冲突 | Alphalens(empyrical-reloaded 要 peewee<3.17.4) vs vnpy_sqlite(peewee 4.1.1) 能否共存 | 中 | alphalens-reloaded 替代 / venv 隔离 / 降级 peewee |
| **S1** | vnpy.alpha A 股支撑度 | T+1/100 股/涨跌停/停牌 在 alpha 模块的支撑度 | 中 | 自建轻量因子层(pandas + ta-lib |
| **S2** | run_optimization 多进程 | 容器内 `max_workers` 行为 + NAS 弱 CPU 资源占用 | 低 | 降级单进程 + 自建 pool |
**S3 最先**:依赖冲突不解决,Alphalens 装不上,S1 无法跑。
**S1 关键路径**:决定因子层方案 A 可行性,fail 则 fallback 自建因子层(影响范围限 `sanguo_factor`,不影响 `sanguo_backtest`)。
---
## 5. 错误处理
| 场景 | 策略 |
|------|------|
| 数据缺失 | 复用 Phase 1 `validator`,缺失数据任务直接 `failed` |
| 因子计算异常 | 单因子失败隔离(try/except + 记录),不影响其他因子 |
| 回测异常 | 捕获 → 任务状态 `failed` → 错误信息入 `backtest_stats.error_msg` |
| 多进程子进程崩溃 | 主进程感知(`Pool.apply_async` callback/errback)→ 任务标记 `failed` |
| API 参数非法 | Pydantic 校验 → 422 返回明确错误 |
---
## 6. 测试策略
| 类型 | 范围 | 工具 |
|------|------|------|
| 单元测试 | 每组件独立(mock vnpy 引擎、mock pool | pytest,复用 Phase 1 conftest |
| 集成测试 | 因子计算 + CTA 回测端到端(真实 vnpy,小数据集) | pytest + 真实 vnpy_v4.4.0 |
| Spike | S1/S2/S3 风险探测(非 pass/fail 测试,是验证 task | 独立 spike script |
| 覆盖率 | ≥ 80% | pytest-cov |
---
## 7. 关键风险
| 风险 | 等级 | 缓解 |
|------|------|------|
| vnpy.alpha 不支持 A 股特性 | 中 | S1 spike 前置,fail 则 `sanguo_factor` fallback 自建 |
| NAS 弱 CPUbraswell 2 核)海量回测慢 | 中 | S2 评估 max_workers,接口预留 Ray 分布式(Phase 3+ |
| peewee 冲突阻塞 Alphalens | 中 | S3 前置解决 |
| BacktestingEngine headless 调用坑(fork/Qt | 低 | Phase 1 已验证 Python 3.14 + vnpy 4.4.0 不拉 QtS2 复验 |
---
## 8. 与 PRD ADR 对应
| ADR | 本 design 落点 |
|-----|---------------|
| ADR-1 不引 Qlib(用 vnpy.alpha | §2.1 因子层基于 vnpy.alpha |
| ADR-2 multiprocessing + Ray(不用 Celery | §2.3 pool.py,§7 Ray 预留 |
| ADR-3 vnpy 引擎零改造 | §1 核心原则,§2 各 wrapper 只包装 |
| ADR-4 复用 v1 数据丢弃 v1 回测 | §3 数据流复用 Phase 1 接口 |
| ADR-8 因子层可插拔 | §2.1 registry.py + analyzer 接口 |
---
## 9. Phase 1 衔接
Phase 2 全部基于 Phase 1 提供的下游接口构建:
- `read_db_daily(symbol, start, end, cfg) → list[BarData]` — vnpy 原生 BarData,直接喂 vnpy.alpha / BacktestingEngine
- `read_parquet_daily(...)` — parquet 快速读取路径(因子批量计算用)
- `UpdateScheduler` — 数据增量更新(回测前确保数据新鲜)
---
## 10. 实施顺序(概要,详细 plan 由 writing-plans 出)
1. **Spike 前置**S3(peewee) → S1(vnpy.alpha A 股) → S2(run_optimization)
2. **因子层** `sanguo_factor`alpha_adapter + registry + library + analyzer
3. **回测层** `sanguo_backtest`cta_engine + cta_optimizer + result_store
4. **编排层** `sanguo_orchestrator`pool + task + runner
5. **API 层** `sanguo_api`5 路由 FastAPI
6. **集成 + 端到端冒烟**
---
## 变更记录
| 日期 | 变更 | 来源 |
|------|------|------|
| 2026-07-05 | 初稿,基于 brainstorming 3 轮决策 | superpowers brainstorming |