Simon e58367af27 feat(web): 因子研究/因子组合/股票筛选接入统一作业反馈;阶段圆点按作业类型区分
接着 /backtest 的那次改造,把其余会跑异步 Job 的页面也切到同一套
`useJobRunner` + `JobProgress`(用户原话:包括因子测试等所有测试都帮我完善用户反馈):

- **/factors**(一个因子一个 Job 的批量场景):删掉 `running/processed/current/jobId` 与
  按「已处理数 ÷ 总数」自算的 `Progress` **假百分比**;每轮把 `第 i/共 n 个` 交给反馈条,
  阶段/作业号/已用秒数全部来自后端。取消 = 用户明确意图 → 保留已出的报告、不再跑后续因子,
  不报错;单个因子失败仍继续跑其余因子,并把后端原文累积成**批级清单**(多因子时反馈条
  只能显示最后一个作业,前几个失败不能丢)。
- **/factors/compose**:删掉 `value={30}` 的假进度条;`archiveId` / 复用 `BacktestResultView` /
  「新页面放大」全部保留;failed/cancelled 交给反馈条,页面 error 只留参数与目录错误
  (同一失败不在两处各说一遍)。
- **/selection**:异步选股切反馈条;**同步**的「执行选股」保留原 loading(`POST /selections`
  没有 job_id,套上会去 `GET /jobs/{signal}` 撞 404)。
- **/signals**:`POST /api/signals` 是同步接口,**不套**作业反馈(不编作业号、不编阶段),
  改为点击即现的 `role="status"` 提示,如实写明「同步请求、请求期间不能关页、无阶段无取消、
  出错显示后端原文」。
- **阶段圆点按作业类型区分**(修掉一个真实缺陷):原来全站共用一张含 `queued/done` 的
  `STAGE_ORDER`,因子测试页实测出现过**裸英文** `factor_calculation` 且 4 个圆点全灰
  (`indexOf` = -1),还画出了因子测试根本不存在的「逐择股日选股 / 撮合与净值结算」。
  现在 `STAGE_PIPELINES = { factor_test: [加载→计算因子值→汇总], backtest: [加载→撮合→汇总],
  selection: [逐择股日选股] }`(阶段序列**不含 queued/done**,那是作业状态不是阶段),
  未知阶段显示「执行中(stage)」并保留已推进的圆点,不整排灰、不露裸枚举。

验证(真实浏览器 CDP,读数原文已记录):
- /factors:点击后 0.2s 内 `submitting`→`queued` + 作业号;+30s Pill「计算因子值」、
  圆点 `✓ 加载行情与因子数据 / ● 计算因子值 / ○ 汇总指标与曲线`(无「选股/撮合」、无英文枚举);
  成功态给「去对比 / 打开归档」。
- /selection:圆点只有 `逐择股日选股` 一段;取消 → 「已取消,没有归档」;
  另实测撞并发上限时如实显示后端原文「系统繁忙:并发研究任务已达上限」。
- /factors/compose:`submitting→queued→撮合与净值结算→success(EXP-…)`,业务结果与 5 处放大入口照旧。
- /backtest 回归:圆点由 4 个变 3 个(去掉后端**从不上报**的 selection 阶段),取消仍「已取消 + 没归档」。
- 自检产生的 12 个实验归档已全部删除(bulk-delete count:12,复查无残留)。
2026-10-01 18:37:25 +08:00
2026-09-06 15:54:00 +08:00

qlib-platform

个人 A 股量化研究平台:股票筛选 · 因子研究 · 交易信号 · 选股回测,面向低频 / 中低频交易研究。

核心量化引擎:Qlib · 首选数据源:Tushare(新浪财经为备用)· 当前数据库:MySQL(业务层无感切换;SQLite 仅兜底)

定位

  • 个人开发项目,中低频选股 / 因子 / 组合回测
  • 不做高频交易、不做过度的微服务化
  • 以 Qlib 为研究引擎,但 Qlib 只是计算引擎而不是整个系统
  • AI Agent(Phase 5)只能通过受控 Tool 调用研究能力

架构

详见 AGENT.md(开发约束)与 docs/ARCHITECTURE.md(架构文档),改代码前必须阅读。

Web 前端 (frontend/web:总览 / 股票池 / 股票筛选 / 因子研究 / 因子组合 / 交易信号 / 选股回测 / 实验 / 归档详情)
   ↓ REST / SSE(异步 Job 状态机)
FastAPI (backend:业务对象 API,见下「核心能力」)
   ↓ Research Specification(统一研究契约)
Application Service(SelectionService / SignalService / ResearchService / Strategy …)
   ├── Selection Engine(条件选股 / 因子评分 / as_of 历史与当前一致)
   ├── Signal Engine(BUY / WATCH / SELL + 理由)
   ├── Portfolio Engine(等权;约束预留并如实标注)
   └── Quant Service ── Composite Engine ── Qlib Adapter ── Qlib
              ↓                        ↓
        Repository / DAO           MySQL(默认)/ Parquet

目录结构

qlib/
├── backend/            # FastAPI 后端(uv 管理,Python 3.12)
│   ├── app/
│   │   ├── api/                # HTTP API 层(面向业务对象,禁止暴露 Qlib/SQL)
│   │   ├── application/        # 用例 / 应用服务(编排,不含框架细节)
│   │   ├── domain/             # 领域实体 + Repository Protocol
│   │   ├── infrastructure/     # SQLAlchemy / Alembic 等基础设施实现
│   │   ├── quant/             # 因子 / composite / universe / selection / signal / portfolio 引擎
│   │   │   └── qlib_adapter/     # Qlib 适配层(业务禁止直接 import qlib)
│   │   ├── agent/              # AI Research Agent(Phase 5,仅 Tool 访问)
│   │   └── core/               # 配置、通用组件
│   └── tests/
├── frontend/web/       # React / Next.js 前端(Phase 3 初始化)
├── data/               # raw / normalized / parquet / qlib(不入库,gitignore)
├── experiments/        # 实验产物(不入库,gitignore)
├── scripts/            # 数据同步、运维脚本
├── docker/             # 部署(Phase 后引入,第一阶段不过度工程)
├── docs/
├── AGENT.md
├── config.yaml         # 可配置项(无密钥)
└── .env.example        # 密钥模板(复制为 .env,勿提交)

使用说明

完整使用文档(安装 / 配置 / 数据同步 / API / Agent / 常见问题)见 docs/USAGE.md。

快速开始(后端)

前置:安装 uv(pip install uv 或官方脚本)。

# 1. 配置密钥(复制模板,填入 TUSHARE_TOKEN 等)
cp .env.example .env

# 2. 安装依赖(自动使用 Python 3.12,见 backend/.python-version)
cd backend
uv sync   # 含 pyqlib(GitHub 源码依赖,固定 commit)。若网络下载困难/超时,按 AGENT.md §0 设置代理 192.168.1.160:3128 后重试

# 3. 运行测试
uv run pytest                       # 全量 388 条
uv run ruff check app tests

# 3b. 端到端契约自检(真实提交回测 Job,验证页面↔后端字段不漂移)
PYTHONPATH=. .venv/bin/python ../scripts/verify_strategy_workspace.py       # 策略库/说明/名称/选股直通/归档链路(59 项)
PYTHONPATH=. .venv/bin/python ../scripts/verify_backtest_page_contract.py   # 回测结果结构契约
python3 ../scripts/verify_ui_alignment.py                                  # UI 对齐与控件一致性(140 项,需前端已启动)

# 4. 启动开发服务
uv run uvicorn app.main:app --reload --port 8000
# 健康检查:http://127.0.0.1:8000/api/health
# 交互文档:http://127.0.0.1:8000/docs

数据库(默认 MySQL)

  • 连接在根目录 config.yaml → database.mysql(host/port/db/user/charset),密码放 .env 的 MYSQL_PASSWORD;URL 优先级:DATABASE_URL 环境变量 > database.mysql > SQLite 兜底。
  • 结构变更(Model → Migration → Test):
cd backend
uv run alembic upgrade head          # 在当前配置指向的库上建表(默认 MySQL qlib)
uv run alembic revision --autogenerate -m "add xxx table"

核心能力(现状)

能力 说明 / 入口
股票筛选 POST /api/selections(A 条件/B 评分,as_of 历史/当前,可解释);POST /api/selections/jobs 全市场异步;Web /selection
因子目录 factor_definition 落库;GET /api/factors;组合可保存复用 POST /api/composites
因子研究 Job 异步 IC / RankIC / 分层测试(/api/jobs)
交易信号 POST /api/signals:评分排名 + 趋势规则 → BUY/WATCH/SELL + 理由;Web /signals
选股回测 POST /api/backtests(成本/涨跌停近似;与当前选股共用同一评分引擎,v2 §25 一致性)
定期调仓回测 两级截断(候选池 n → 持仓 x)+ 双周期(每 m 月择股 / 每 y 月调仓)+ 复权口径(none/qfq/hfq)+ 组合条件过滤 + 最低佣金;输出净值/个股曲线并标注买卖点;Web /backtest(含高股息案例预设)、scripts/run_dividend_case.py
每日指标 daily_basic(股息率 dv_ratio/dv_ttm、PE/PB/市值)+ dividend_yield 因子;sync daily_basic 回补
退市股与时点股票池 sync basic --include-delisted 入库已退市/暂停上市(status/delist_date)+ 定向 sync daily 补行情;filter_stocks 按 as_of 正确纳入/排除(实测 338 只)
时点 ST / 名称历史 sync namechange 入库名称生效区间(14,213 行);exclude_st 在每个择股日按当时名称判定(回测与 /api/selections 同口径),消除「曾高股息后 ST」的股息陷阱隐藏偏差(实测 3.70pp)
策略库 strategy 落库 + /api/strategies CRUD/PUT 原地更新/展开为回测 spec;每个策略有后端推导的「一句话说明 + 计算公式 + 执行步骤 + 注意事项」(describe_strategy,与引擎实执行规则同源);一键回测;Web /strategies
Experiment / 归档 完整存档:每次回测(异步 Job 与同步接口都算)把结果 + spec + code_version + data_version 数据快照指纹写入 experiment 表,个股收益曲线默认全量保存(超出体积预算才裁剪并显式标注);列表支持 kind/q 过滤且总数经 X-Total-Count 暴露(不再静默截断);DELETE /api/experiments/{id} + python -m app.cli.prune_experiments --keep N(默认 dry-run,删除即失去结果,建议先导出)治理体积;实验对比:勾选 2~3 个 → 归一化净值曲线叠加 + 指标差值表 + config_snapshot 参数 diff;归档详情页 /experiments/{id}(Server Component)只读复看完整结果,并明确回答「选股条件」与「交易执行依据」;归档页可导出完整 JSON / 以此参数再跑 / 删除(确认框写明后果);体积用 app.cli.prune_experiments(默认 dry-run)治理,删错的历史归档可用 app.cli.restore_experiment_from_job 从 Job 副本按原 id 重建
图表基座 统一 TradingView Lightweight Charts 4.2.3:components/charts/LwChart.tsx(折线/面积/柱状 + 买卖点标记 + tooltip + 可点击图例)与 components/StockChart/CandleChartLW.tsx(个股 K 线 + 成交量 + MA);ECharts 已完全下线(package.json、pnpm-lock.yaml、node_modules、文档与架构图标注均已清除)。实测个股页图表根节点是 Lightweight Charts 自身的 div.tv-lightweight-charts,页面 canvas 无一来自其它图表库
代码即名称 任何出现股票代码的位置都成对显示股票名称且可点击进入个股页(基本信息 + 走势图);后端在 SelectionCandidate/SymbolCurve/ActionRecord/RankedPick/Position/Trade 上填充 name,前端 GET /api/stocks/names 一次性缓存兜底
AI Agent POST /api/agent/chat,14 个受控 Tool(v3 §25 全清单)

里程碑(详见 ROADMAP.md 与 docs/DEV_PLAN_v2.md)

  • M0–M5:工程骨架 → 数据层 → 研究引擎 → Web → Experiment/Job → AI Agent
  • M-DB:SQLite 全量迁移 MySQL(config.yaml 配置化 + 迁移工具 + 一致性校验)
  • M6:选股系统主线(Universe + Selection A/B + 落库/API + 回测共用 + Web)
  • M7:因子层(因子定义入库 + Composite 模块化 + 行情口径显式化)
  • M8:Signal / Portfolio / Strategy / Agent 工具 / Web 做实(M8.4 Qlib 模型选股按需延后)
  • V3(见 DEV_PLAN_v3):Chart Service/K线与成交点、Signal↔Fill 区分、个股研究页、 复权口径、Bar Replay、指数历史成分、因子相关性、组合单股上限、Job stage/取消、 选股异步化、Agent 14 工具(B2/B3 停牌/ST/报表与 C3 模型选股/D3 Redis 按需延后)

约定速查

  • 回答与文档使用中文
  • 一切时间相关数据防「未来函数」:财务数据区分 report_date / announce_date,查询支持 as_of_date
  • 数据源必须经 MarketDataProvider(Tushare 优先,Sina 兜底且记录来源),业务层禁止直接 import 新浪 / Tushare 实现
  • 业务层禁止直接操作 sqlite3 / SQL / SQLAlchemy Session,只能走 Repository
  • 新表:Model → Alembic Migration → Test
  • 禁止修改 site-packages/qlib 源码,一律走 Adapter / Wrapper
  • API 输入输出用 Pydantic DTO,禁止把 ORM Model 直接暴露给前端
S
Description
Qlib base的选股和回测平台
Readme GPL-3.0
6.2 MiB
Languages
Python 68.5%
TypeScript 22.9%
CSS 7.8%
Shell 0.8%