Files
qlib/docs/DEV_PLAN_v2.md
Simon 82240e383d docs: 同步操作说明/架构/路线图(归档操作、图表基座、数据库目标硬约束)
- USAGE 新增 §6.3.1「归档的日常操作」:查看 / 筛选(回写 URL)/ 导出完整 JSON /
  以此参数再跑 / 删除(写明「删除即失去结果,结果只存归档一份」)/
  历史归档用 restore_experiment_from_job 按原 id 重建;并说明归档完整度如何标注
- USAGE/README 更正技术栈与图表基座:TradingView Lightweight Charts 4.2.3 为唯一
  图表基座(ECharts 已从 package.json、pnpm-lock.yaml、node_modules、文档与
  架构图标注中全部清除),并记录实测证据(个股页图表根节点为
  div.tv-lightweight-charts,页面 canvas 无一来自其它图表库)
- ARCHITECTURE 更正「当前数据库」(原写 SQLite/未来 MySQL):现为本机 MariaDB 10.11,
  §6 补目标库硬约束;USAGE 补服务器身份与实测连接证据
- AGENT.md 新增 §0.1:数据库目标只允许本机 MariaDB,禁止 192.168.1.10,
  由 config.py::assert_db_target_allowed 硬拦截(命中直接抛错,不静默降级)
- DEV_PLAN_DIVIDEND_BACKTEST 记录三轮实施与验证、归档恢复边界(§12.5)、已知限制
- DEV_PLAN v2/v3 标注为历史记录(避免把当时的远端库地址当现状照抄);
  ROADMAP 更正 M3 图表选型
2026-09-20 07:31:20 +08:00

18 KiB
Raw Permalink Blame History

下阶段开发计划(架构 v2 落地 · 2026-09 · 选股系统为主线)

依据:ARCHITECTURE_v2.md(新架构 v2)、AGENT.md(开发约束)、 两份 2026-09 只读调研(后端研究/量化层、前端/Agent/数据层)与 docs/ROADMAP.md 的 M0–M5 记录。 本文件回答:M5(AI Agent Phase 5)之后,下一阶段做什么、按什么顺序做。 2026-09 更新(用户定调):下一阶段以「选股系统」为独立主线并提前执行 (原"研究层落地"后移为 M7 支撑),先交付可用的选股 MVP,再补因子层增强。


0. 当前状态与总判断

已完成(M0–M5):数据层(Tushare 首选 + 新浪兜底 + 审计)→ ResearchSpec → 本地研究引擎 (9 个行情因子 / IC·分层评估 / 低频 TopN 等权回测)→ Job/Experiment 异步归档 → Web 六页 → AI Agent(6 个受控 Tool)。数据库已于 2026-09 从 SQLite 全量迁移至 MySQL(见 §1)。

对照 v2 的核心差距(调研结论):

  1. 选股没有独立成系统:v2 强调 Factor Research / Composite Factor / Selection / Signal / Portfolio 分层;当前"选股"只是回测器内部 TopN 逻辑,无法单独回答 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 / universe / selection_rule / selection_result / signal_rule / strategy 等全缺。
  3. 口径与数据完整性风险待修:研究链路不消费复权因子(stock_daily 混合 tushare 不复权与新浪前复权行); universe 用"当前名称含 ST"近似、无历史成分/停牌;Job.stage 从未赋值、SSE 是查库轮询。
  4. 前端/Agent 待做实:Selection/Signal 页面不存在;Agent 工具仅 6 个, v2 §24 期望的 screen_stocks / explain_selection 等依赖尚未存在的选股引擎。

阶段命名:M6 选股系统(主线)→ M7 因子层落地(支撑)→ M8 引擎延伸与平台化。 数据库专项单独记 M-DB。


1. 执行状态(2026-09:M6–M8 主干已交付)

M6 选股系统、M7 因子层、M8(Signal/Portfolio/Strategy/Agent 工具/Web)均已完成并提交 (ROADMAP 里程碑行含 commit;每阶段全量 pytest + ruff + tsc 验证通过;Alembic 迁移已应用 MySQL)。 M8.4 Qlib 模型选股(Selection 模式 C)为「按需」项,未阻塞主线 —— 当前延后, 前置:qlib_adapter 特征/模型链路(Alpha158 + LightGBM walk-forward),启用时按 §5.4 推进。

2. M-DB:数据库 MySQL 迁移(已完成,本文件记录收尾项)

已交付(2026-09):

  • MySQL(192.168.1.10:3306,MariaDB 10.11,库 qlib,utf8mb4)建 8 张业务表 + alembic_version,

    ⚠️ 历史记录:该地址已禁止作为 DB 目标,现为本机 MariaDB(127.0.0.1:3306/qlib),见 AGENT.md §0.1。 全部经 alembic upgrade head(迁移 e4d18825…53113c80…91c4e27a…d3f6c9a2),结构由既有 ORM 模型保证。

  • scripts/migrate_sqlite_to_mysql.py:一次性工具 —— sqlite 直连 + pymysql chunk 多值 INSERT + keyset 分页续传 + 幂等(已迁则跳过)+ --verify-only 逐表 COUNT 与抽样比对;支持 --only <表> 并行大表、 --throttle-sec 低配服务器节流。
  • 配置: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。迁移数据量 16,057,265 行,--verify-only 校验全部一致。

遗留收尾(小任务,可随时做):

项 说明 建议
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 随各页面改动顺手清理

3. 下阶段总路线

M6  选股系统(主线)   Universe 选股范围 + Selection Engine(A条件/B评分)
     │                 + 结果落库/API + 回测共用引擎 + Web 选股页
     ↓
M7  因子层落地         因子定义入库 + Composite 模块化 + 研究口径修复(支撑选股增强)
     ↓
M8  引擎延伸与平台化    Signal Engine → Portfolio Engine → Strategy / Agent / Qlib 模型选股

设计红线(贯穿):v2 §25「历史回测与当前选股用同一套 Engine」、v2 §9 时间与未来函数防护、 v2 §26 Experiment 可复现、AGENT.md「简单优先 / 每阶段可运行 / 一个功能一个 commit / 先 Domain 后 UI / 带测试」。


4. M6:选股系统(下一阶段主线)

目标:交付一个独立、可解释、支持当前与历史日期的选股系统(v2 §14 Selection Engine), 让回测器与"现在该选什么"共用同一套引擎(v2 §25 一致性红线)。 MVP 全部复用现有数据与因子能力(stock / financial_indicator / stock_daily + 9 个内置行情因子 + composite_score),不依赖 M7 因子层,因此可最先做、尽快见到"选股结果"。

3.0 选股输入输出契约(先定 Domain / DTO)

  • Domain SelectionSpec(v2 §14.2):universe 范围 + filters(条件) + scoring(因子/复合因子/权重) + ranking(TopN / Top%) + as_of_date;沿用并扩展现有 ResearchSpec 的 universe/factors/selection 结构, 新增 as_of_date(缺省 = 最近交易日)。
  • Domain SelectionResult(v2 §14.3/§21.1):as_of_date, universe, candidates[{symbol, rank, score, factor_values, filter_status, selection_reason}], statistics —— 作为前后端/Agent 统一 DTO。
  • 核心用例:selection_service.select(spec, as_of=...) -> SelectionResult(历史/当前日期都可执行)。
  • 验收:SelectionResult 进入 frontend/web/lib/types.ts 与后端 entity 一一对应。

3.1 Universe:选股范围(MVP 规则化)

  • 把 ResearchService.filter_stocks 的过滤逻辑(market / exclude_st / min_listing_days / delist_date < as_of / 可选 symbol 白名单)抽成可复用的 UniverseFilter 执行器, 输入结构化条件(Pydantic),输出 as_of 时点下的股票范围。
  • MVP 不建 universe 大表:条件随 SelectionSpec 一起存入 selection_snapshot(快照 JSON, 可复现);universe / universe_rule / index_constituent_history 表推迟到 M8 需要历史成分时再落。
  • 明确近似并写进结果:ST 按当前名称快照判定;停牌/退市边界显式标注(unimplemented 或近似说明)。
  • 验收:同一组条件在"当前日"与"历史日"分别得到正确范围(含历史日上市/退市过滤); 现有回测 universe 语义不变(回归)。

3.2 Selection Engine:A 条件选股 + B 因子评分

  • A. 条件选股:结构化条件过滤(示例:roe > 15 且 pe… 且 close > ma60), 条件作用于现有字段/派生指标:
    • 静态/财务:market / industry / eps / roe / total_revenue / net_profit / gross_margin… (财务值按 announce_date ≤ as_of 可见,防未来函数);
    • 行情/技术:close > ma20 / ma60、momentum_20>0、volume_ratio…(复用 factors.py 的 compute_factor)。
    • MVP 条件用结构化 JSON(字段 + 比较符 + 值,可 and/or 嵌套),不做自由表达式 parser(避免提前造轮子)。
  • B. 因子评分选股:score = composite_score(截面 zscore × 方向 × 权重)(抽取 local_engine 现逻辑), 按 score 排序取 top_n / top_pct;同时支持阈值过滤。
  • C. 模型预测选股(Qlib/LightGBM)留 M8.3,引擎接口先留 mode 字段占位。
  • 每条候选输出 selection_reason(命中的条件 / 分数来源),支撑 v2「为什么选这只股票」。
  • 验收:同 spec 下 A/B 模式输出与人工核对一致;条件/评分模式均可对历史 as_of 执行。

3.3 结果持久化与 API

  • Model(→ Alembic → Repo,全部走 Model→Migration→Test):
    • selection_result(v2 §8:selection_id / strategy_id 占位 / as_of_date / symbol / rank / score / factor_values / filter_status / selection_reason / created_at);
    • selection_snapshot(spec 与 universe/条件快照 JSON,供复现与"历史某日选了什么"查询)。
    • selection_rule(可选的命名规则持久化,MVP 允许后置:snapshot 已含完整 spec)。
  • API(业务对象导向,AGENT.md §17):POST /api/selections(提交 spec+as_of,同步或 Job 计算并落库)、 GET /api/selections/{id}、GET /api/selections?as_of=&strategy_id=(历史选股查询)。
  • 验收:提交一次选股 → 结果落库可查;重建同 spec+as_of 得到相同结果(可复现)。

3.4 回测一致性改造(v2 §25 红线)

  • TopKBacktestRunner._rebalance 改为调用 Selection Engine(同 spec、同 as_of)得到候选 再进入成交/仓位逻辑——保证"当前选股 = 历史回测每期选股"用同一套代码。
  • 默认配置下回测数值与改造前逐项一致(回归测试锁住)。
  • 验收(v2 §27):select(strategy, 历史调仓日) 与回测当日持仓选股逐 symbol 一致。

3.5 Web 选股页(可后端就绪后并行)

  • 新路由 /selection(v2 §19/§21.1):选股条件面板(范围 + A 条件 + B 评分/权重/TopN)+ as_of 日期(当前/历史)→ SelectionResult 表格(rank/score/因子值/理由)→「一键以该 spec 发起回测」。
  • lib/types.ts 增加 SelectionSpec / SelectionResult;nav(app-shell)加入"股票筛选"入口。
  • 验收:页面完成"选范围 → 配条件/评分 → 当前/历史 TopN 结果 → 跳回测"闭环;tsc --noEmit 通过。

3.6 M6 验收(里程碑完成判定)

  • select(spec, as_of=历史日) 与回测该日调仓选股逐 symbol 一致(自动化测试)
  • A/B 两模式都可对"当前日 / 历史日"执行并落库,/api/selections 可查
  • 选股结果带 factor_values 与 selection_reason(可解释)
  • Web 选股页闭环可用;SelectionResult DTO 前后端一致
  • 全量 pytest 通过;相关迁移测试覆盖新表

5. M7:因子层落地(支撑选股增强,紧随 M6)

目标:把"因子"从代码注册表变成可入库、可版本化、可组合的研究资产,并修掉混合口径风险, 为选股 B 模式(更多/自定义因子)、因子研究做实提供基础。

  • 4.1 因子定义入库 + 版本化目录:Model factor_definition + seed 现有 9 因子元数据; GET /api/factors 改读库;自定义因子元数据 CRUD(计算仍走代码注册表)。
  • 4.2 Composite Factor Engine 模块化:抽出 quant/composite.py (CompositeSpec{components[name,weight,direction], method} → score 面板); factor_composite(+component) 表;选股 B 模式与回测共用同一实现。验收:默认 spec 回测结果与现一致。
  • 4.3 研究读取口径修复(横切):Repository 读路径按 source/adjust 过滤或提供 qfq 查询; ResearchSpec/SelectionSpec 增加 price_adjustment 字段并写入结果 config_snapshot。 验收:含新浪补缺行的股票回测/选股口径一致且可溯源。
  • 4.4 因子测试结果结构化(可选):Model factor_test,多因子 spec 不再静默只测第一个。

6. M8:引擎延伸与平台化(Signal / Portfolio / Strategy / Agent / 模型选股)

  • 5.1 Signal Engine(规则型):signal_rule / signal_event{signal_date, symbol, signal_type, trigger_reason, score, price};回测与选股都能产事件并落库;API POST /api/signals、历史查询。 验收:能回答「某策略某日为什么对这只股票给 BUY/SELL」。
  • 5.2 Portfolio Engine:把回测器 _rebalance 资金/仓位逻辑抽为 portfolio 模块 (等权起步 + 单股/行业上限/现金可选约束,约束默认关闭并如实标注 unimplemented)。 验收:默认配置回测数值与现一致。
  • 5.3 Strategy 模型与 /api/strategies:聚合 universe + selection(spec) + signal + portfolio + rebalance;Experiment 归档 strategy_version;Agent 增加 create_strategy。
  • 5.4 Qlib 模型链路 = 选股模式 C(按需):Alpha158 特征 + LightGBM walk-forward → 预测分 → Selection 模式 C。
  • 5.5 AI Agent 工具补齐(依赖上述引擎):inspect_factor / create_composite_factor / screen_stocks / explain_selection / generate_signals / create_strategy / get_backtest_result / create_experiment,全部经白名单 + Job/Experiment 链路;前端加最小 AI 对话入口。
  • 5.6 Web 页面做实(前端主线,随后端里程碑推进):回测页做实(trades/年度收益/持仓演化/成本参数/ config_snapshot,零新 API)、因子研究做实(IC 时序/对比)、Signal 页、Job SSE 进度 UI (需先补 executor stage 上报,见 §6)。

7. 基础设施专项(含本机 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.6 前端切 SSE 时(可先行)
行情/因子查询热点:全市场选股重复预热 因子结果 / 最新交易日缓存(key 带 as_of 失效) M6 选股高频查询后按 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 进度条的前提。


8. 建议的执行顺序(前 6 个 commit 序列)

按 AGENT.md「最小修改、每阶段可跑、一功能一 commit」,以选股 MVP 为起点的推荐顺序:

  1. M6.0 选股契约:Domain SelectionSpec / SelectionResult + selection_service.select(spec, as_of) 骨架(先同步执行、复用 ResearchService 数据装配)。验收:对最近交易日返回 TopN 候选 + 理由。
  2. M6.1 Universe 过滤执行器:抽 UniverseFilter(复用 filter_stocks 语义)。 验收:当前/历史日范围正确、回测回归不变。
  3. M6.2 Selection Engine A + B:条件选股(结构化条件 JSON)+ 因子评分 TopN(复用因子与 composite_score)。验收:A/B 模式可执行、结果含 factor_values/reason。
  4. M6.3 落库与 API:selection_result / selection_snapshot 表 + Repo + POST/GET /api/selections。 验收:迁移测试 + 提交可查、重建一致。
  5. M6.4 回测共用 Selection:TopKBacktestRunner 改调 Selection Engine。 验收:默认 spec 回测结果逐项一致 + 历史日一致性测试。
  6. M6.5 Web 选股页:/selection 路由 + SelectionResult 表 + 一键跳回测。

之后进入 M7(因子层落地)与 M8(Signal/Portfolio/Strategy/Agent/模型选股)与 §6 基础设施专项。

每步都遵循:Domain → Repository Protocol → Infra(Model→Migration→Repo) → Service → API → 前端 → 测试(相关模块)→ 更新文档;新增表一律 Model→Alembic→测试。


9. 风险清单(开发中持续检查)

  • 历史一致性:回测改用 Selection Engine 后默认结果必须与改造前一致(回归测试锁住,别让重构悄悄改策略语义)。
  • 未来函数 / 幸存者偏差:财务条件按 announce_date ≤ as_of 过滤;universe 的 ST/行业/成分历史 口径补齐前,结果须明示近似(ST 当前名称快照等)。
  • 数据口径:选股/回测所用行情口径(不复权 vs 前复权)必须一致并可溯源(M7.3 收口)。
  • 范围控制:M6 阶段不做自由表达式 parser / 不做模型选股(C 模式)/ 不建 universe 大表; 均留到后续里程碑,避免一次性跨层堆量。
  • MySQL 行为差异:TEXT 上限、批量 upsert 锁等待、pymysql 流式读取 —— 见 §1 遗留收尾表。
  • 前端契约:SelectionResult/SignalResult 先定 DTO(后端 entity)再画页面,避免前后端口径漂移。