Files
qlib/docs/DEV_PLAN_v2.md
T
Simon 0ffd574f30 docs: 下阶段开发计划(架构 v2 落地 M6-M8)+ MySQL 迁移文档同步
- docs/DEV_PLAN_v2.md:基于 ARCHITECTURE_v2 与 M0-M5 现状的下一阶段计划
  (M-DB 迁移收尾 → M6 因子定义入库/复合因子/口径修复 → M7 Selection/Signal/
  Portfolio 引擎分层 → M8 Strategy 平台化/Web 做实/Agent 工具补齐;含本机 Redis
  127.0.0.1:6379 的接入触发点与执行顺序)
- ROADMAP.md:登记 M-DB 里程碑并指向 DEV_PLAN_v2
- USAGE.md / README.md:数据库描述由 SQLite 更新为 MySQL(config.yaml database.mysql)
2026-09-08 23:58:51 +08:00

242 lines
16 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.
# 下阶段开发计划(架构 v2 落地 · 2026-09)
> 依据:[ARCHITECTURE_v2.md](./ARCHITECTURE_v2.md)(新架构 v2)、[AGENT.md](../AGENT.md)(开发约束)、
> 两份 2026-09 只读调研(后端研究/量化层、前端/Agent/数据层)与 docs/ROADMAP.md 的 M0–M5 记录。
> 本文件回答:**M5(AI Agent Phase 5)之后,下一阶段做什么、按什么顺序做。**
---
## 0. 当前状态与总判断
**已完成(M0–M5)**:数据层(Tushare 首选 + 新浪兜底 + 审计)→ ResearchSpec → 本地研究引擎
(9 个行情因子 / IC·分层评估 / 低频 TopN 等权回测)→ Job/Experiment 异步归档 → Web 六页 →
AI Agent(6 个受控 Tool)。**数据库已于 2026-09 从 SQLite 全量迁移至 MySQL**(见 §5)。
**对照 v2 的核心差距**(调研结论):
1. **研究是一条"单行道"**:ResearchSpec → 取数 → 因子 → TopN 回测 一次性跑完;
v2 强调的 Factor Research / **Composite Factor / Selection / Signal / Portfolio 分层**尚未拆分,
"选股"只是回测器内部逻辑,无法单独回答 v2 §8 的两个问题:
「2023-08-15 为什么选这只股票」「2026-09-08 当前有哪些股票满足策略」。
2. **因子定义 / 策略等研究元数据未入库**:DB 只有 8 张表
(stock / stock_daily / adjust_factor / financial_indicator / trading_calendar / sync_log / job / experiment),
v2 §8 期望的 factor_definition / factor_test / factor_composite / universe / selection_rule /
signal_rule / strategy / portfolio 等全缺;factor_test 与回测结果只以 Experiment 的 result_json 快照存在。
3. **口径与数据完整性风险待修**:研究链路不消费复权因子(stock_daily 混合 tushare 不复权与新浪前复权行);
universe 用"当前名称含 ST"近似、无历史成分/停牌;Job.stage 从未赋值、SSE 是查库轮询。
4. **前端/Agent 待做实**:Selection/Signal 页面不存在;因子组合页无持久化实体;回测结果字段大量未渲染;
Agent 工具仅 6 个,v2 §24 期望的 screen_stocks / explain_selection / generate_signals /
create_strategy / create_composite_factor 等依赖尚未存在的引擎。
**阶段命名延续 ROADMAP**:M6 研究层落地 → M7 引擎分层 → M8 平台化与 Agent 增强。
数据库专项单独记 M-DB。
---
## 1. M-DB:数据库 MySQL 迁移(已完成,本文件记录收尾项)
**已交付(2026-09)**:
- MySQL(192.168.1.10:3306,MariaDB 10.11,库 `qlib`,utf8mb4)建 8 张业务表 + alembic_version,
全部经 `alembic upgrade head`(迁移 e4d18825…53113c80…91c4e27a…d3f6c9a2),结构由既有 ORM 模型保证。
- `scripts/migrate_sqlite_to_mysql.py`:一次性工具 —— sqlite 直连 + pymysql chunk 多值 INSERT +
keyset 分页续传 + 幂等(已迁则跳过)+ `--verify-only` 逐表 COUNT 与抽样比对;支持 `--only <表>` 并行大表。
- 配置:`config.yaml → database.mysql`(host/port/db/user/charset 明文),密码经 `.env` 的
`MYSQL_PASSWORD`(AGENT.md §33:密钥不进 config/git);URL 优先级
`DATABASE_URL 环境变量 > config.yaml database.mysql > sqlite:///./data/quant.db 兜底`;
实现集中在 `backend/app/core/config.py::_build_mysql_url`,业务层零改动(DAO/Repository 隔离兑现)。
- 测试隔离:`backend/tests/conftest.py` 强制每进程 `/tmp/qlib-pytest-<pid>.db` SQLite(测试绝不触 MySQL)。
- 驱动:`pymysql>=1.1` 已入 `backend/pyproject.toml`。
**遗留收尾(小任务,可随时做)**:
| 项 | 说明 | 建议 |
|---|---|---|
| `job/experiment.result_json` 用 MySQL `TEXT`(≤65,535B) | SQLite TEXT 无上限、测试测不出 | 日级回测结果变长后改 `MEDIUMTEXT`(新 Alembic 迁移 + Model 类型 variant) |
| Repository 批量 upsert 在 MySQL 的压测 | `tuple_.in_()` 行值、`add_all` 单事务无分块 | 全市场日线增量同步跑一次,观察锁等待/包大小 |
| MySQL 路径无自动化测试 | 测试全 SQLite 方言 | 视需要加一个「连 MySQL 的集成冒烟」开关(默认关,显式 env 开启) |
| `scripts/migrate_sqlite_to_mysql.py` 属一次性工具 | 保留供重建目标库 | 文档标注不可再对生产执行(除 --verify-only) |
| 前端/文档残留 "SQLite" 文案 | app-shell / page.tsx / sync.py docstring | 随各页面改动顺手清理 |
---
## 2. 下阶段总路线
```text
M6 研究层落地 因子元数据入库 + 复合因子模块 + 口径修复
↓
M7 引擎分层 Selection Engine → Signal Engine → Portfolio Engine
↓
M8 平台化 Strategy 模型 + /api/strategies + Agent 增强 + 页面做实
```
设计红线(贯穿):v2 §25「历史回测与当前选股用同一套 Engine」、§26 Experiment 可复现、
AGENT.md「简单优先 / 每阶段可运行 / 一个功能一个 commit / 先 Domain 后 UI / 带测试」。
---
## 3. M6:研究层落地(因子目录、复合因子、口径)
> 目标:把"因子"从代码注册表变成**可入库、可版本化、可组合**的研究资产,并修掉混合口径风险。
> 本阶段只做研究和评估能力,不引入选股/信号新概念 —— 避免一次改动跨太多层。
### 3.1 因子定义入库 + 版本化目录
- Domain:`FactorDefinition`(name/description/formula/input_data/frequency/lookback/direction/
normalization/neutralization/availability_rule/version/requires…);`FactorDefinitionRepository`(Protocol)。
- Infra:Model `factor_definition` → Alembic → Repo;注册表 seeding:把 `quant/factors.py` 现有 9 个
内置因子的元数据灌库(一次性 seed 脚本或 `sync factors` CLI 子命令),代码仅保留计算函数映射
(name → compute)。
- API:`GET /api/factors` 改读库;`POST/PUT /api/factors`(管理员自定义因子元数据,计算待 M6.3 表达式引擎)。
- 验收:迁移测试通过;`/api/factors` 展示入库元数据;未知因子抛 `FactorError`;
现有因子研究 Job 结果与改动前一致(回归)。
### 3.2 Composite Factor Engine 模块化
- 现状:`composite_score` 内联在 local_engine(截面 zscore × 方向 × 权重 求和)。
- 目标:抽出独立模块 `quant/composite.py`:`CompositeSpec{components[{name,weight,direction}], method}`
→ 输出 score 面板(先固定权重 + zscore;Rank/Z/IC 加权留接口,不提前实现)。
- Model 表 `factor_composite(+factor_composite_component)` 落库(供保存/复用)。
- 回测/因子测试改调同一 `composite_score` 函数。
- 验收:与现有默认 spec 的回测结果**逐项一致**的回归测试;API `GET/POST /api/composites` CRUD。
### 3.3 研究读取口径修复(横切前置,建议最先做)
- 决策:研究行情统一为**不复权 close 为主口径**,凡需除权处理处显式声明;
或在 Repository 读路径按 `source/adjust` 过滤/提供 qfq 查询(利用 adjust_factor 前复权)。
- ResearchSpec 增加 `price_adjustment` 字段并在 API/结果 config_snapshot 显式记录口径。
- 验收:同一只含新浪补缺行的股票,回测口径一致且 config_snapshot 可溯源;
文档声明口径;对现有结果差异给出解释(如有)。
### 3.4 因子测试结果结构化(可选,如时间紧可并入 M8)
- Model `factor_test`(结果结构化落库,替代只存 result_json);factor_test 多因子 spec 不再静默只测第一个。
---
## 4. M7:引擎分层(Selection → Signal → Portfolio)
> 目标:把回测器里的"选股/买卖/仓位"逻辑拆成 v2 定义的独立引擎,
> 让**当前选股与历史回测共用同一套代码**(v2 §25 一致性红线)。
### 4.1 Selection Engine(先做)
- Domain:`SelectionResult{as_of_date, symbol, rank, score, factor_values, filter_status, selection_reason}`
与用例 `select(spec, as_of)`(可对任意历史/当前日期执行)。
- 三种模式中先落地 A(条件选股:ROE/PE/涨跌幅等条件表达式)+ B(因子评分 TopN/Top%);
模式 C(模型预测)留待 Qlib 模型链路(M8.3)。
- `TopKBacktestRunner._rebalance` 改为**消费 Selection 结果**(同一引擎)。
- Model:`selection_result / selection_snapshot`(as_of、spec 快照、结果)。API:`POST /api/selections`
(提交即算)与 `GET /api/selections/{id}`。
- 验收(v2 §27):`select(as_of=历史日)` 与该日回测调仓选股**逐 symbol 一致**的测试;
API 可查"任意日 TopN 与理由";SelectionResult 成为前后端标准化 DTO。
### 4.2 Signal Engine(规则型)
- Domain:`SignalRule / SignalEvent{signal_date, symbol, signal_type(BUY/SELL/HOLD/WATCH),
trigger_reason, score, price}`;规则引擎执行"分数阈值 + 技术条件 + 市场状态"组合。
- Model:`signal_rule / signal_event`;回测与选股都能产事件并落库。
- API:`POST /api/signals`、`GET /api/signals/history`。
- 验收:能回答「某策略 2023-08-15 为什么对这只股票给 BUY/SELL」;与回测 signal_history 同引擎(一致性测试)。
### 4.3 Portfolio Engine
- 把回测器 `_rebalance` 的资金/仓位逻辑抽为 portfolio 模块:weighting(等权起步)、
单股/行业上限、现金管理;约束默认关闭并在结果 unimplemented 如实标注。
- Model:`portfolio / portfolio_position`(可选,先服务回测即可,不提前建表)。
- 验收:默认配置下回测数值与现实现一致;新约束开关正确进入 config_snapshot/unimplemented。
### 4.4 Universe 规则化(横切,可与 4.1 并行)
- Model `universe / universe_rule` + 规则执行器(把 filter_stocks 的 exclude_st/min_listing_days/
market 规则化入库并补 exclude_suspended 的显式处理或 unimplemented);
预留 `index_constituent_history`(历史成分,防幸存者偏差)。
- 验收:universe 可持久化、被 Spec 引用后结果与现有一致;文档声明 ST 快照近似。
---
## 5. M8:平台化与增强(Strategy / Agent / Web / 基础设施)
### 5.1 Strategy 模型与 /api/strategies
- Domain:Strategy 聚合 universe + factors/composite + selection + signal + portfolio + rebalance;
ResearchSpec 可由 strategy 展开。Model `strategy(+子表或 JSON 明细)`;API CRUD。
- Experiment 归档时记录 strategy_version;Agent 增加 `create_strategy` Tool(受控白名单)。
- 验收:Agent「建立策略 X」→ 策略入库 → 回测 → Experiment 带 strategy_version 可复跑一致。
### 5.2 Web 页面做实(前端主线,与 M6–M8 后端里程碑并行推进)
- 优先:**回测页做实**(零新后端 API):渲染 trades 明细/分年度收益/持仓演化/成本参数表单/config_snapshot,
`lib/types.ts` 补全 Trade entry/exit_price、FactorTestReport.factor_name 等缺失字段,
Experiment 类型收编进 lib/types.ts(result 改联合类型)。
- **因子研究做实**:IC 时序折线、因子横向对比(数据来自既有 DTO 扩展)。
- **Selection 页(新路由)**:M7.1 后端就绪后,选当前/历史日 → SelectionResult 表 + 一键跳回测。
- **Signal 页(新路由)**:M7.2 后端就绪后,信号时间线与理由、风险标志。
- Job 进度 UI 切换 SSE(需先给执行器补 stage 上报,见 §6)。
- 每页验收:tsc --noEmit 通过、无未渲染的标准字段、无 SQLite 字样残留。
### 5.3 Qlib 模型链路(模型选股 Selection 模式 C 的前置,按需)
- `qlib_adapter/feature.py` Alpha158 特征 + `model.py` LightGBM walk-forward(seed 固定);
输出预测分 → Selection 模式 C。作为可选增强,不阻塞 M6–M8 主干。
### 5.4 AI Agent 工具补齐(依赖 M6/M7/M8.1 引擎)
- 在引擎就绪后逐个补齐 v2 §24 工具:`inspect_factor / create_composite_factor / screen_stocks /
explain_selection / generate_signals / create_strategy / get_backtest_result / create_experiment`;
每个工具经白名单 + Job/Experiment 链路(不直接写库)。
- 前端加一个最小 AI 对话入口(后端 /api/agent/chat 已有)—— 可与 M8.2 任意页面里程碑合并。
---
## 6. 基础设施专项(含本机 Redis 的定位)
**本机已探测:Redis 服务在 127.0.0.1:6379 正常响应(无 redis-cli,连接经 socket 验证)。**
v2 §22 与 AGENT.md §19/§38 的原则是"第一版不引复杂队列;复杂后再引入 Redis + Celery/RQ"。
据此把 Redis 放入**明确的触发点**而非一开始就用:
| 触发场景 | Redis 用途 | 何时启用 |
|---|---|---|
| Job 并发/排队:subprocess 双并发上限开始频繁打满、丢任务需可恢复队列 | RQ/普通队列替代 FastAPI BackgroundTasks 调度 | M8 之后 / 出现排队丢失问题 |
| Job 细粒度 stage + SSE 长连:executor 每阶段写 stage 后广播 | Redis pub/sub 做 SSE 事件总线(替代 0.4s 查库轮询) | M8.2 前端切 SSE 时(可先行) |
| 行情/因子查询热点:全市场研究重复预热 | 因子结果 / 最新交易日缓存(key 带 as_of 失效) | 研究链路稳定后按 profile 决定 |
近期只需做一件准备工作:`config.yaml` 预留 `redis.url_env: "REDIS_URL"` + 可选
`redis.url: "redis://127.0.0.1:6379/0"` 配置段,代码不改;真正启用时再落基础设施代码。
**其余基础设施待办**(低优先):Job `cancelled` 取消 API 与 `/api/jobs` 列表端点;executor 增加 stage
上报(data_loading/factor_calculation/backtesting…)——这是前端做 SSE 进度条的前提。
---
## 7. 建议的执行顺序(前 6 个 commit 序列)
按 AGENT.md「最小修改、每阶段可跑、一功能一 commit」,给 M6 起步的推荐顺序:
1. **M6.3 口径修复**(横切前置,改动小收益大):Repository 读路径按 source/adjust 过滤 +
ResearchSpec 口径字段 + 文档。验收:混合行场景回测口径一致。
2. **M6.1 因子定义入库**:Model+迁移+seed+`GET /api/factors` 读库。验收:迁移测试 + 回归。
3. **M6.2 Composite Engine 模块化**:`quant/composite.py` + 回测改调 + `factor_composite` 表 CRUD。
验收:默认 spec 回测结果逐项一致的回归测试。
4. **M7.4 Universe 规则化**:`universe/universe_rule` 表 + 执行器(结果与现有一致)。可与 2/3 并行。
5. **M7.1 Selection Engine**:`select(spec, as_of)` + TopK 回测改消费 Selection +
`selection_result/snapshot` 表 + API。验收:v2 §27 历史日一致测试。
6. **M7.2 Signal Engine**:规则 + `signal_event` 落库 + API(回测/选股同引擎)。
之后进入 M8(Strategy 模型 → Web 页面做实 → Agent 工具补齐)与 §6 基础设施专项。
每步都遵循:Domain → Repository Protocol → Infra(Model→Migration→Repo) → Service → API → 前端 →
测试(相关模块)→ 更新文档;新增表一律 Model→Alembic→测试。
---
## 8. 风险清单(开发中持续检查)
- **历史一致性**:Selection/Backtest 拆分后默认结果必须与拆分前一致(用回归测试锁住,别让重构悄悄改策略语义)。
- **未来函数 / 幸存者偏差**:universe 的 ST/行业/成分历史口径补齐前,回测文档须明示近似;
财务数据始终按 announce_date 可见(已有表键支持)。
- **MySQL 行为差异**:TEXT 上限、批量 upsert 锁等待、pymysql 流式读取 —— 见 §1 遗留收尾表。
- **不要提前堆量**:M6 阶段不引入模型选股/ML;M7 不做多因子合成优化;M8 的 Qlib 模型链路按需推进。
- **前端契约**:SelectionResult/SignalResult 先定 DTO(后端 entity)再画页面,避免前后端口径漂移。