Files
qlib/docs/USAGE.md
T

19 KiB
Raw Blame History

qlib-platform 使用说明

适用代码版本:M0–M5 + M-DB + M6 选股系统 / M7 因子层 / M8(Signal·Portfolio·Strategy·Agent·Web) 全部完成(数据库已切换 MySQL);若文档与代码不一致,以代码与 ROADMAP.md / DEV_PLAN_v2.md 为准。

本文覆盖:安装配置、数据同步、启动前后端、研究 API、研究引擎与 Qlib 接入、 AI Agent、测试门禁、已知限制。


1. 项目概览(当前完成度)

个人 A 股中低频量化研究平台:

模块 位置 / 说明
数据层 backend/app/domain、infrastructure/data_sources:Tushare 首选 + 新浪备用(Failover 审计);默认 MySQL(config.yaml database.mysql)+ 日线 Parquet 导出
选股系统 quant/selection.py + application/services/selection_service.py:条件选股(A)/ 因子评分 TopN(B),as_of 当前/历史一致;结果落库可复现、可解释
因子层 factor_definition 入库(/api/factors 读库)+ Composite Engine(quant/composite.py,组合落库 /api/composites)+ 行情口径显式化(price_adjustment)
交易信号 quant/signal.py:评分排名 + 趋势规则 → BUY/WATCH/SELL + 理由,落库 /api/signals
研究/回测 backend/app/quant:ResearchSpec → 因子 → IC/RankIC/分层 → TopK 低频回测 → 标准化 BacktestResult;双引擎:LocalEngine(默认)与 QlibEngine v1
策略 strategy 落库 + /api/strategies(命名策略,可展开为回测 spec)
Web frontend/web:总览 / 股票池 / 股票筛选 / 因子研究 / 因子组合 / 交易信号 / 选股回测 / 实验
异步与归档 Job 状态机 + Experiment 自动归档 + 一键复跑 + SSE 进度
AI Agent 14 个受控工具(v3 §25 全清单)+ LLM 编排

技术栈:Python 3.12(uv) · FastAPI · SQLAlchemy 2 · Alembic · MySQL(pymysql)(SQLite 兜底)· pandas · pyarrow(Parquet) · pyqlib 0.9.8.dev32(源码安装) · LightGBM · Next.js 15 · ECharts · Redis(本机 127.0.0.1:6379 已就绪,触发时接入)


2. 环境准备

# 工具(本机已装则跳过)
python3 -m pip install uv
# Node 18+ 与 pnpm: https://pnpm.io/installation

# 1) 后端依赖(backend/.python-version 固定 3.12)
cd backend
uv sync                       # 基础依赖(含 Qlib/qlib 源码安装依赖)
uv sync --extra datasource-tushare   # 数据同步需要 Tushare SDK(首次)

前端:

cd frontend/web
pnpm install                  # 依赖使用 npmmirror 镜像时较快
cp .env.local.example .env.local   # NEXT_PUBLIC_API_BASE 默认 http://127.0.0.1:8000/api

Qlib 安装说明:本机(Linux aarch64 + CPython 3.12)经源码 git 安装并固定 commit, 见 backend/pyproject.toml 与 docs/QLIB_VERIFICATION.md。


3. 配置

3.1 根目录 .env(密钥,不入库)

cp .env.example .env
变量 必填 说明
TUSHARE_TOKEN 同步数据时必填 Tushare Pro token
LLM_API_KEY 使用 Agent 时必填 大模型 API Key(URL/模型名在 config.yaml)
DATABASE_URL 否 默认库 = config.yaml → database.mysql(MySQL 192.168.1.10/qlib);设此项可覆盖(如切回 SQLite)
MYSQL_PASSWORD 使用 MySQL 默认库时必填 MySQL 密码(config.yaml database.mysql.password_env 引用;host/db/user 在 config.yaml)
APP_SECRET_KEY 否 应用密钥(未接登录,可暂不改)

约定:密钥只放 .env;URL、模型名等可配置项放 config.yaml。.env 已被 gitignore,严禁提交。

3.2 根目录 config.yaml(可配置项,可入库)

常用字段(节选,完整见仓库根 config.yaml):

app: {name, version, debug, secret_key_env}
api: {prefix: "/api"}
database:
  url_env: "DATABASE_URL"
  migrations_dir: ...
  mysql: {enabled: true, host: "192.168.1.10", port: 3306,
         db: "qlib", user: "qlib", password_env: "MYSQL_PASSWORD", charset: "utf8mb4"}
# URL 优先级:DATABASE_URL 环境变量 > database.mysql 组装 > sqlite:///./data/quant.db 兜底
data_source: {primary: "tushare", fallback: "sina", tushare_token_env: "TUSHARE_TOKEN"}
storage: {parquet_dir: "data/parquet", qlib_dir: "data/qlib", ...}   # 相对项目根
agent:
  llm:                                      # ← AI Agent 模型接入
    base_url: "https://dashscope.aliyuncs.com/compatible-mode/v1"   # URL 在这里配
    model: "qwen-plus"                      # 模型名在这里配
    api_key_env: "LLM_API_KEY"              # Key 仍从 .env 读
  • 换模型:改 config.yaml → agent.llm.model(如 qwen-max / deepseek-chat 等 OpenAI 兼容接口模型),并按平台同步 base_url。
  • 也可用 .env 的 LLM_BASE_URL / LLM_MODEL 覆盖 yaml(env 优先)。

3.3 数据库迁移

cd backend
uv run alembic upgrade head      # 在 config 指定库上建表(默认 MySQL qlib;SQLite 兜底库为 data/quant.db)
# 开发中改 Model 后:
uv run alembic revision --autogenerate -m "desc"
uv run alembic upgrade head
# 对指定库执行(不随默认配置):
DATABASE_URL='mysql+pymysql://user:pass@host/db' uv run alembic upgrade head

2026-09 已把数据从 SQLite(data/quant.db)全量迁移至 MySQL(192.168.1.10:3306/qlib), 迁移工具与校验见 scripts/migrate_sqlite_to_mysql.py(--verify-only 可复查一致性)。


4. 数据同步与导出

cd backend
uv run python -m app.cli.sync basic                     # 股票基础信息(全市场)
uv run python -m app.cli.sync calendar --start 20240101 --end 20241231
uv run python -m app.cli.sync daily --symbols 600519.SH,000858.SZ --start 20230101
uv run python -m app.cli.sync daily --all --start 20240101 --resume   # 全市场 + 断点续传
uv run python -m app.cli.sync financial --all                       # 财务指标(默认增量)
uv run python -m app.cli.sync financial --all --full                 # 财务指标(强制全量重拉)
uv run python -m app.cli.sync verify --symbol 600519.SH               # 新浪交叉验证
uv run python -m app.cli.sync export [--years 2023,2024]              # 日线按年导出 Parquet
  • 每次拉取写入 sync_log 审计(来源 / 成功与否 / 行数 / 区间),禁止静默切换数据源。
  • daily 同时写入日线与复权因子;--resume 从本地最新交易日续传。financial 默认增量:本地已含「最新应披露报告期」(按 A 股披露节奏推算)的股票直接跳过; 逐只落库,中断/限速后重跑同一命令即可续传补齐(只丢当前一只)。
  • 新浪兜底带「两边一致」真实性校验:Tushare 报错时,只有当该股票本地历史与新浪 返回数据重叠部分一致,才把新浪新数据(本地缺失键)导入,并标记 source=sina(日线另标记 adjust=qfq 前复权;新浪不提供复权因子)。 校验口径:财务为重叠报告期的 eps / 销售毛利率逐期一致(ROE 两端口径不同, 不作依据);日线为最近重叠交易日的前复权价一致(老交易日在除权后不可比)。 本地无历史可对照或校验不一致 → 拒绝导入并告警,留待 Tushare 恢复后重跑补齐。
  • 限速时可加 --sleep 秒数 加大请求间隔;financial --full 重拉全部历史并覆盖既有行。
  • 新浪(verify)仅交叉验证,返回前复权口径,不会并入不复权主库。
  • 财务指标带 announce_date(公告日)与 source(tushare/sina);研究侧只允许 使用已公告数据(防未来函数),对同一报告期优先消费 source=tushare 的行。
  • export:data/parquet/stock_daily/<year>.parquet(列:symbol/trade_date/ohlc/volume/amount), 由 pyarrow 写出,可直接用 pandas 读取分析,或作为 Qlib 等引擎的后续数据源。

5. 启动

一键脚本(后端 + 前端同时管理):

scripts/dev.sh start      # 启动后端(8000) + 前端(3000)
scripts/dev.sh status     # 查看状态
scripts/dev.sh restart    # 重启
scripts/dev.sh stop       # 停止
scripts/dev.sh logs api   # 跟踪后端日志(web 同理)

# 局域网访问前端时(例如 http://192.168.1.160:3000):
# BACKEND_API_URL=http://192.168.1.160:8000 scripts/dev.sh start

手动分别启动见下:

# 后端(端口 8000)
cd backend
uv run uvicorn app.main:app --reload --port 8000
# 交互文档:http://127.0.0.1:8000/docs   健康检查:/api/health

# 前端(端口 3000)
cd frontend/web
pnpm dev
# 打开 http://127.0.0.1:3000

页面:总览 → 股票池(搜索/列表)→ 因子研究(目录 + 单因子 IC/RankIC/分层)→ 回测(参数 → 净值/回撤/月度/持仓/未建模标注)→ 实验(归档列表 / 详情 / 一键复跑)。


6. 研究 API 与研究引擎

6.0 引擎分层(M6–M8)

研究/选股链路自 M6 起分层:Composite Engine(因子复合分)→ Selection Engine (条件/评分选股)→ Signal Engine(买卖信号)→ Portfolio(组合权重)→ 回测。 当前选股与历史回测共用同一评分引擎(quant/composite.build_score_panel), 保证 v2 §25「历史回测与当前选股用同一套逻辑」(一致性由测试锁定)。

  • 选股:POST /api/selections,body 为 SelectionQuery(universe / factors / top_n / as_of / method);结果候选含 factor_values 与 selection_reason(为什么选它)。
  • 信号:POST /api/signals,body {query, rules};rules 含买入排名阈值/趋势 MA/卖出区间。
  • 策略:POST /api/strategies 保存命名策略 → POST /{id}/expand 补 period 展开为 spec 提交回测。

6.1 研究引擎:LocalEngine(默认)与 QlibEngine v1

服务通过 QuantEngine Protocol 注入引擎(backend/app/quant/engine.py):

  • LocalEngine(默认,研究服务与 API 使用):纯 pandas —— 因子注册表计算 → 截面 z-score 复合 → TopK 月/周调仓回测。无需 Qlib 数据。
  • QlibEngine v1(quant/qlib_adapter/engine.py):同一套因子与记账规则,但行情经 QlibDataset 供给 —— 把本地行情按 qlib 官方二进制格式落盘(默认 data/qlib, 可注入 QlibEngine(qlib_dir=...))→ qlib.init + D.features 读取 close → 回测。

切换示例(代码内):

from app.quant.engine import LocalEngine            # 默认
from app.quant.qlib_adapter.engine import QlibEngine  # Qlib 数据管线引擎
from app.quant.service import ResearchService

engine = LocalEngine()          # 或 QlibEngine()
service = ResearchService(stock_repo, daily_repo, engine)

注意:Qlib bin 以 float32 存储,QlibEngine 结果与 LocalEngine 存在极小数值差 (真实 20 股对照:-12.81% vs -12.97%),属预期精度差异,不影响结论方向。

6.2 API(curl 示例)

研究输入统一为 ResearchSpec(前端 / API / Agent 同构):

curl -X POST http://127.0.0.1:8000/api/backtests \
  -H 'Content-Type: application/json' \
  -d '{
    "type": "backtest",
    "universe": {"exclude_st": true, "min_listing_days": 0},
    "factors": [{"name": "momentum_60", "weight": 1.0}],
    "selection": {"top_n": 5},
    "rebalance": "monthly",
    "period": ["2024-03-01", "2024-12-31"]
  }'
端点 说明
GET /api/health 健康检查
GET /api/stocks?q=600519&limit=20 股票列表/搜索
GET /api/stocks/{symbol} 单只股票详情
GET /api/factors 因子目录(读 factor_definition 表;空表自动 seed)
POST /api/composites / GET /api/composites 因子组合保存 / 列表(方向由注册表填充)
POST /api/selections 执行选股(method=score 评分 TopN / condition 条件)→ SelectionResult 并落库
GET /api/selections/{id} / GET /api/selections 读回 / 历史选股(as_of、method 过滤)
POST /api/signals 生成 BUY/WATCH/SELL 信号(评分+规则)→ 落库
GET /api/signals/{id} / GET /api/signals 读回 / 历史信号
POST /api/strategies / GET /api/strategies 保存 / 列表命名策略
POST /api/strategies/{id}/expand 展开为回测 ResearchSpec(period + 初始资金)
POST /api/factor-tests 同步单因子测试 → FactorTestReport
POST /api/backtests 同步回测 → BacktestResult(默认 LocalEngine)
GET /api/backtests/last 最近一次同步回测
POST /api/jobs 创建异步 Job(同 spec),返回 {"job_id","status":"queued"}
GET /api/jobs/{id} 查询状态;成功时内嵌 result
GET /api/jobs/{id}/events SSE 进度(curl -N ...)
GET /api/experiments 实验归档列表(收益摘要/代码版本)
GET /api/experiments/{id} 实验详情(spec + 完整结果)
POST /api/experiments/{id}/rerun 一键复跑 → 新 Job
GET /api/stocks/{symbol}/chart|signals|selections Chart API:K线/量/MA/复权显示 + 该股历史标记
GET /api/backtests/{experiment_id}/stocks/{symbol}/chart|trades|positions 回测个股图(fills/成交)/明细
POST /api/factor-correlations 多因子横截面 Spearman 相关矩阵
POST /api/replays Bar Replay 线性重放(as_of 逐日,白名单≤40/区间≤90)
GET /api/jobs / POST /api/jobs/{id}/cancel Job 列表 / 取消(含 stage 字段上报)
POST /api/selections/jobs 全市场/长任务选股异步 Job(Web 页「异步」按钮)
POST /api/agent/chat AI 研究助手(见 §7,14 个受控工具)

防未来函数与真实性:回测在调仓日收盘成交、自次日起计收益;财务数据只使用 announce_date 之前已公告内容;成本(佣金/印花税/滑点)、涨跌停、停牌已建模; 未建模约束(如开盘一字路径)会写入结果的 unimplemented 数组,前端如实展示。


7. AI Agent

前置:config.yaml → agent.llm(URL/模型名默认百炼兼容端点 + qwen-plus), .env 中设置 LLM_API_KEY(仅需 Key)。

curl -X POST http://127.0.0.1:8000/api/agent/chat \
  -H 'Content-Type: application/json' \
  -d '{"message": "帮我测试 momentum_60 因子并跑一次 Top5 月度回测,评估是否值得深入"}'

返回:

{
  "reply": "(最终结论,中文)",
  "actions": [
    {"tool": "test_factor", "args": {...}, "output": "因子测试完成(Experiment EXP-…)..."},
    {"tool": "run_backtest", "args": {...}, "output": "回测完成(Experiment EXP-…)..."}
  ]
}

Agent 能力边界(10 个内置受控工具,只读 + 受控写库):

  • search_stocks / get_market_data:查询
  • test_factor / run_backtest:研究并自动归档 Experiment
  • screen_stocks / explain_selection:按因子评分选股(传 symbols 白名单避免全市场长任务)/ 解释某次选股理由
  • generate_signals:生成 BUY/WATCH/SELL 信号
  • create_strategy:保存命名策略
  • get_experiment / compare_experiments:读取/对比归档

不提供 shell / 任意代码执行 / 删改数据 / 修改配置与凭证。未配置 Key 时接口返回 400 引导信息。研究纪律写入系统提示:禁止仅凭单次样本内高收益判定策略有效,需说明 样本外、过拟合、look-ahead bias、成本、参数敏感性等(未验证项要明说)。


8. 测试与质量门禁

cd backend
uv run ruff check app tests && uv run ruff format --check app tests
uv run pytest                      # 全量测试(每个里程碑提交前均须通过)

cd frontend/web
pnpm run typecheck
pnpm run build

覆盖重点:Provider 归一化与 Failover 审计、Repository 幂等与「未来函数阻断」 (as_of_date/announce_date)、Alembic 迁移、因子/IC/分层、回测记账与成本/涨跌停、 QlibEngine 数据管线(bin 落盘格式/roundtrip/端到端回测)、Job 状态机与 Experiment 归档、Agent 工具白名单与编排、LLM 配置加载、API 端到端。


9. 已知限制与说明

  1. Qlib 模型选股(Selection 模式 C)按需延后(v3 §14.1C):当前选股为条件(A)/因子评分(B); 模型预测选股(Alpha158 + LightGBM walk-forward,M8.4)为「按需」延后项 (见 docs/DEV_PLAN_v2.md §6.4)。
  2. 全市场选股为同步请求:无白名单的全市场选股约需 60s+;Agent 工具要求传 symbols 白名单,全市场请在 Web 页执行(后续可迁异步 Job)。
  3. 数据规模:当前 MySQL 已同步全市场(stock 5556 只、stock_daily 约 780 万行、 adjust_factor 约 790 万行,2026-09 由 SQLite 迁移并校验一致)。
  4. 回测为近似建模:涨跌停按收盘相对上一有效收盘判定、成交假设调仓日收盘, Portfolio v1 仅等权(单股/行业上限约束字段已预留但未建模,设置后会在结果 unimplemented 如实标注)(详见结果 unimplemented)。
  5. Agent 结论质量取决于模型:编排只保证「经受控工具 + 归档留痕」,研究有效性判断 需要人复核;接不同厂商模型请核对 config.yaml 的 base_url 与 model 命名。
  6. SSE 与 Job 为单进程本地执行:重启进程后未完成 Job 需重新提交(第一阶段刻意 不引入 Redis/Celery;本机 Redis 已就绪,出现排队/长任务需求时按 DEV_PLAN §7 接入)。
  7. Qlib 数据为进程内单例初始化:qlib.init 以 provider_uri 为键幂等,切换不同 qlib_dir 会重新初始化;测试环境建议注入独立临时目录。

10. 常见问题(FAQ)

  • 同步报「缺少 TUSHARE_TOKEN」:把 token 填入根目录 .env 的 TUSHARE_TOKEN。
  • 同步报「未安装 tushare 客户端」:cd backend && uv sync --extra datasource-tushare。
  • Agent 报 400「未配置 LLM」:.env 设置 LLM_API_KEY;换模型改 config.yaml 的 agent.llm.model / base_url。
  • 回测默认用哪个引擎?怎么切 Qlib 引擎:默认 LocalEngine(纯 pandas)。需要在代码 中向 ResearchService 注入 QlibEngine() 启用 QlibDataset 数据管线(见 §6.1)。
  • Parquet 导出文件在哪、怎么读:data/parquet/stock_daily/<year>.parquet; pd.read_parquet(...) 直接读取(pyarrow 已随依赖安装)。
  • 前端连不上后端 / NetworkError:前端默认经 Next 同源代理访问 /api/* (next.config.ts rewrites → 127.0.0.1:8000,可用 BACKEND_API_URL 覆盖), 任意 IP 访问 :3000 都不需要 CORS 或硬编码后端地址;后端 CORS 开发期为 *。 若使用 SQLite 兜底库且日志出现 database is locked,多半是正在跑全市场数据 同步(长写事务),同步结束后自动恢复(SQLite 连接已加 busy_timeout); 默认 MySQL 库无此问题。
  • 数据库被改动想重置:默认 MySQL(qlib@192.168.1.10)时在远端重建后 uv run alembic upgrade head(行情需重新同步或从备份恢复);若切回 SQLite 兜底库则删除 data/quant.db 后重建。
  • 默认库已是 MySQL(config.yaml database.mysql,密码在 .env 的 MYSQL_PASSWORD); 想临时切回 SQLite:.env 设 DATABASE_URL=sqlite:///./data/quant.db。 业务层均无需改动(Repository / SQLAlchemy 已隔离方言差异)。