qlib 入门 + AKShare / Tushare 数据栈
qlib 四层架构、AKShare / Tushare 数据源全景、A 股与美股市场机制关键差异
日期: 2026-07-23 方向: Phase 3 / A 股迁移 阶段: Phase 3: 实盘+规模化+迁移 标签: #qlib #AKShare #Tushare #AShare #CSI300 #HKShare #CrossMarket
今日目标
| 类型 | 内容 |
|---|---|
| 学习 | qlib 四层架构、AKShare / Tushare 数据源全景、A 股与美股市场机制关键差异 |
| 实操 | 装 qlib + 拉 CSI300 数据 + 跑通 LGB Baseline + AKShare 拉单股日线 |
| 产出 | TR-DAY75 笔记 + qlib_hello.py 完整可跑脚本 + A 股 vs 美股差异清单 |
一、为什么从 vectorbt 切到 qlib(做 A 股)
Phase 1-2 跑了 64 天美股,vectorbt 是我们主力回测引擎。它擅长「事件驱动单标的精细回测」——画 PnL、看每一笔成交、调试 stop loss 都很顺。但到 Phase 3 要做 A 股横截面因子时,vectorbt 的工程模式开始水土不服。
1.1 vectorbt 的局限(在 A 股场景下)
| 维度 | vectorbt 表现 | A 股场景的需求 |
|---|---|---|
| 单股事件回测 | 极好 | 偶尔用(事件驱动子策略) |
| 横截面排序(rank N 选 top K) | 可以但繁琐 | 核心需求(因子选股) |
| 数据源 | yfinance / IBKR 美股友好 | A 股需要自己接 AKShare/Tushare |
| A 股交易规则 | 不内置(涨跌停 / T+1 / ST) | 必须支持 |
| 截面 IC / Rank IC 工具 | 没有 | 是因子研究的核心指标 |
| 大宇宙(>1000 标的)速度 | 慢(Python loop) | 关键(A 股 5000+ 只票) |
1.2 qlib 是什么
qlib = Microsoft 开源的「面向 AI 的量化研究平台」,2020 年开源,工业级。设计目标就是「A 股因子研究 + 大规模截面计算 + ML 模型管线化」。它的几个原生优势:
- A 股原生支持:内置 CSI100/300/500/1000 票池、复权处理、停牌处理、ST 标记
- 截面操作 first-class:Rank / Group / Cross-sectional Normalize 都是一行表达式
- 数据存储为 bin 格式:比 CSV 快 50-100 倍,5000 只票一秒读完
- 模型管线化:Dataset → Handler → Model → Backtest 四层,每层都能换组件
- 配套 paper:微软 STL Research 跟着发了好多篇因子/RL/Alpha 论文,工具链跟得上前沿
1.3 不是非此即彼,是分工
我们的最终架构会是「双引擎」:
美股 / 期权事件驱动 / 实盘下单 → vectorbt + ib_insync(Phase 1-2 已成型)
A 股 / 港股 / 因子横截面 / 中等频率 → qlib + AKShare/Tushare(Phase 3 新增)
两边数据格式不互通,但研究方法论可以互相喂回。例如美股发现的动量因子,可以平移到 A 股用 qlib 验证;A 股验证出来的反转效应,也可以拿回 vectorbt 在美股回测。
1.4 学习曲线提示
qlib 不是周末能上手的工具。它的抽象层比 vectorbt 多一倍,配置文件用 YAML,表达式语言(Qlib Expression Engine)需要单独学。预期:
- Day 75(今天):装好 + 跑通 hello world
- Day 76-78:把美股因子迁移过来 + 写自定义因子表达式
- Day 79-80:跑全市场回测 + 调 Top-K 策略
- 1 周后才能算「能用」,2 周后才能算「顺手」
给 PM 的迁移思考:选工具就是选学习负债 + 抽象一致性。vectorbt 抽象浅但灵活,qlib 抽象深但 production-ready——对应到产品架构,就是「胶水脚本快速验证 vs 平台化沉淀」的取舍。
二、qlib 核心概念:四层架构
qlib 把量化研究拆成四层,每层都可独立替换。理解这四层比记 API 重要十倍。
2.1 四层架构图
┌────────────────────────────────────────────────────────┐
│ Backtest Layer │
│ 策略组合 + 交易成本 + 调仓频率 + PnL │
│ (类似 vectorbt 的 portfolio,但偏 cross-section) │
├────────────────────────────────────────────────────────┤
│ Model Layer │
│ LGB / XGB / LSTM / Transformer / Linear │
│ (只负责"预测 alpha",不管交易) │
├────────────────────────────────────────────────────────┤
│ Dataset Layer (Handler) │
│ 特征工程 + 标签构造 + 训练/验证/测试切分 │
│ (Alpha158 / Alpha360 / 自定义) │
├────────────────────────────────────────────────────────┤
│ Data Layer │
│ .bin 格式存储 + 复权 + 票池筛选 │
│ (原始 OHLCV → qlib 标准格式) │
└────────────────────────────────────────────────────────┘
2.2 每层关键概念
| 层 | 关键概念 | 类比 vectorbt | 类比传统 ML |
|---|---|---|---|
| Data | D.features() / D.calendar() | pd.DataFrame | 数据源 |
| Dataset/Handler | Alpha158 158 个内置因子 | 自己写因子 indicator | 特征工程 |
| Model | LGBModel / 自定义 | 没有 ML 概念 | sklearn estimator |
| Backtest | TopkDropoutStrategy | Portfolio.from_signals | 没有(ML 不做交易) |
2.3 跟 vectorbt 的哲学对比
| 哲学 | vectorbt | qlib |
|---|---|---|
| 主流场景 | 单标的,时间序列扫一遍 | 多标的,每日截面排序 |
| 信号 | "什么时候买" | "今天买谁" |
| 交易频率 | 任意(tick 到 月) | 日频为主 |
| 模型 | 规则(buy when MA cross) | ML(predict next-day return) |
| 调试体验 | 极好(每笔 trade 可视化) | 工程化(看 IC / annualized) |
| 学习曲线 | 平 | 陡 |
结论:vectorbt 适合「我已经有 alpha idea,要验证」,qlib 适合「我有一堆候选因子和一个 ML 模型,要批量筛」。
三、安装 + 环境准备
3.1 装 qlib
# 推荐独立 venv,因为 qlib 有自己一套 PyTorch + LightGBM 依赖
python -m venv venv_qlib
source venv_qlib/bin/activate # Windows: venv_qlib\Scripts\activate
pip install pyqlib
# 验证
python -c "import qlib; print(qlib.__version__)"
# 预期输出:0.9.x 或更高
注意:
- Python 3.9 - 3.11 推荐(3.12 部分依赖还不稳)
- Windows 装 LightGBM 可能要先装 VS Build Tools;Linux/Mac 一般顺
- pyqlib 装完会自带 LightGBM、PyTorch CPU 版本
3.2 下载 A 股数据
# qlib 提供脚本一键下载(约 2-3GB,2005 至今的日线)
python -m qlib.run.get_data qlib_data \
--target_dir ~/.qlib/qlib_data/cn_data \
--region cn
# 或者中国大陆网络慢的话,用社区镜像
# https://github.com/chenditc/investment_data 有 fork 加速版
国内下载慢的话,备选方案是自己用 AKShare 拉,下面会演示。
3.3 验证数据可读
import qlib
from qlib.constant import REG_CN
qlib.init(provider_uri='~/.qlib/qlib_data/cn_data', region=REG_CN)
from qlib.data import D
print(D.calendar(start_time='2020-01-01', end_time='2020-01-10'))
# 应该输出 7 个交易日(A 股春节前后)
跑通这步说明数据栈通了,Day 75 一半任务完成。
四、AKShare 全景
qlib 内置数据是个起点,但实战中你还需要主动拉数据(财报、龙虎榜、北向资金……)。AKShare 是 A 股最常用的免费数据库之一。
4.1 定位
| 维度 | AKShare |
|---|---|
| 性质 | 开源、免费、社区维护 |
| 覆盖 | A 股 / 港股 / 美股 / 期货 / 基金 / 加密 / 宏观 |
| 数据频率 | 日线为主,部分分钟 |
| 接口 | 纯 Python,pip install akshare |
| 限速 | 没有官方限速,但请求过快会被源头屏蔽 |
| 数据质量 | 参差——爬虫拼起来的,偶尔字段空、偶尔时间戳错 |
| 文档 | 中文齐全(akshare.akfamily.xyz) |
4.2 高频用到的接口
import akshare as ak
# A 股日线(前复权)
df = ak.stock_zh_a_hist(symbol="000001", period="daily",
start_date="20230101", end_date="20231231",
adjust="qfq")
# 列:日期 开盘 收盘 最高 最低 成交量 成交额 振幅 涨跌幅 涨跌额 换手率
# 全市场列表(含名字、代码、是否 ST)
stock_list = ak.stock_zh_a_spot_em()
# 港股日线
hk = ak.stock_hk_hist(symbol="00700", period="daily",
start_date="20230101", end_date="20231231",
adjust="qfq")
# 北向资金每日流入
north = ak.stock_hsgt_north_net_flow_in_em(indicator="沪股通")
# 财报披露时间表(关键,事件驱动用)
disclosure = ak.stock_zh_a_disclosure_report_cninfo(symbol="000001",
market="沪深京")
4.3 AKShare 的坑
| 坑 | 表现 | 对策 |
|---|---|---|
| 部分接口偶尔 503 | 抓 1000 只票,有 5-10 只会失败 | try/except + retry 3 次 |
| 列名偶尔变动 | 升级后列叫"涨幅"变"涨跌幅" | pin 死 akshare 版本,CI 跑回归 |
| 日期格式不统一 | 有的是 20230101,有的是 2023-01-01 | 入库前统一 pd.to_datetime |
| 复权口径不明 | 不同接口前复权基点可能不同 | 用同一个接口拉一整段历史 |
| 财报数据有延迟 | 披露完隔 1-2 天才进 AKShare | 重要事件用 Tushare Pro |
4.4 PM 视角:免费的真实成本
AKShare 表面"免费",实际成本是:
- 数据清洗时间(10-20% 的开发时间)
- 一次性脏数据带来的回测污染(最贵的成本——决策错了你不知道)
- 升级风险(接口被改 / 源头网站改版)
结论:研究阶段用 AKShare 性价比无敌;要做实盘前必须做"数据 sanity 检查脚本"——下面 Day 79 会展开。
五、Tushare:付费版 AKShare
5.1 定位差异
| 维度 | AKShare | Tushare Pro |
|---|---|---|
| 价格 | 免费 | 基础免费 + 高级 ¥500-2000/年 |
| 数据质量 | 中(爬虫) | 高(数据供应商对接) |
| 财务数据 | 有但不全 | 全 + 早 |
| 龙虎榜 / 大宗 | 部分 | 全 |
| 流式 / 实时 | 无 | 部分 |
| 限速 | 软限速 | 按积分制,明确额度 |
| 调用方式 | 纯 Python 函数 | 需要 token |
5.2 调用范式
import tushare as ts
ts.set_token('你的 token') # 在 tushare.pro 注册后获得
pro = ts.pro_api()
# 日线(无复权)
df = pro.daily(ts_code='000001.SZ',
start_date='20230101',
end_date='20231231')
# 复权因子
adj = pro.adj_factor(ts_code='000001.SZ',
start_date='20230101',
end_date='20231231')
# 财务指标(季度)
fina = pro.fina_indicator(ts_code='000001.SZ',
start_date='20230101',
end_date='20231231')
5.3 什么时候必须付费
| 场景 | AKShare 够吗 | Tushare Pro |
|---|---|---|
| 学习因子研究 | ✓ | 不需要 |
| 全市场日线回测 | ✓ | 不需要 |
| 财报事件驱动 | △(延迟) | ✓ 必须 |
| 龙虎榜分析 | △ | ✓ 推荐 |
| 实盘 | ✗(数据延迟太大) | ✓ 必须 |
我们的路径:Phase 3 学习阶段用 AKShare,到 Phase 4 准备港股/A 股实盘前再升 Tushare Pro。
六、首个 qlib 实验:CSI300 + LGB Baseline
下面是 Day 75 实战核心——把整个 qlib 流水线跑一遍,看到 IC 数字。
6.1 完整代码 qlib_hello.py
# qlib_hello.py
"""
Day 75 - qlib hello world.
跑 CSI300 票池 + Alpha158 内置因子 + LGB 模型,看 IC 和年化。
"""
import qlib
from qlib.constant import REG_CN
from qlib.utils import init_instance_by_config
from qlib.workflow import R
from qlib.workflow.record_temp import SignalRecord, PortAnaRecord
# ============================================================
# 1. 初始化 qlib(指向已下载的数据)
# ============================================================
qlib.init(provider_uri='~/.qlib/qlib_data/cn_data', region=REG_CN)
# ============================================================
# 2. Dataset 配置:Alpha158 + 时间切分
# ============================================================
dataset_config = {
"class": "DatasetH",
"module_path": "qlib.data.dataset",
"kwargs": {
"handler": {
"class": "Alpha158",
"module_path": "qlib.contrib.data.handler",
"kwargs": {
"start_time": "2015-01-01",
"end_time": "2022-12-31",
"fit_start_time": "2015-01-01",
"fit_end_time": "2020-12-31",
"instruments": "csi300", # 票池
},
},
"segments": {
"train": ("2015-01-01", "2019-12-31"),
"valid": ("2020-01-01", "2020-12-31"),
"test": ("2021-01-01", "2022-12-31"),
},
},
}
# ============================================================
# 3. Model 配置:LightGBM
# ============================================================
model_config = {
"class": "LGBModel",
"module_path": "qlib.contrib.model.gbdt",
"kwargs": {
"loss": "mse",
"colsample_bytree": 0.8879,
"learning_rate": 0.0421,
"subsample": 0.8789,
"lambda_l1": 205.6999,
"lambda_l2": 580.9768,
"max_depth": 8,
"num_leaves": 210,
"num_threads": 20,
},
}
# ============================================================
# 4. 训练 + 预测 + 评估
# ============================================================
dataset = init_instance_by_config(dataset_config)
model = init_instance_by_config(model_config)
with R.start(experiment_name="day75_hello"):
# 训练
model.fit(dataset)
R.save_objects(trained_model=model)
# 预测并保存信号
sr = SignalRecord(model, dataset, R.get_recorder())
sr.generate()
# 回测(Top 50 持仓 + 月度调仓)
port_analysis_config = {
"executor": {
"class": "SimulatorExecutor",
"module_path": "qlib.backtest.executor",
"kwargs": {"time_per_step": "day", "generate_portfolio_metrics": True},
},
"strategy": {
"class": "TopkDropoutStrategy",
"module_path": "qlib.contrib.strategy.signal_strategy",
"kwargs": {
"signal": (model, dataset),
"topk": 50, # 持仓 50 只
"n_drop": 5, # 每次换出 5 只
},
},
"backtest": {
"start_time": "2021-01-01",
"end_time": "2022-12-31",
"account": 100_000_000,
"benchmark": "SH000300",
"exchange_kwargs": {
"freq": "day",
"limit_threshold": 0.095, # 涨跌停模拟
"deal_price": "close",
"open_cost": 0.0005, # 万 5 单边佣金
"close_cost": 0.0015, # 卖出含千 1 印花税
"min_cost": 5, # 每笔最低 5 元
},
},
}
par = PortAnaRecord(R.get_recorder(), port_analysis_config, "day")
par.generate()
6.2 预期输出(参考量级)
跑完之后 qlib 会在 mlruns/ 下生成报告,关键指标:
| 指标 | 含义 | 合理区间(CSI300 Alpha158 + LGB) |
|---|---|---|
| IC | 信息系数(预测与实际相关性) | 0.03 - 0.06 |
| Rank IC | 排序 IC(更稳健) | 0.04 - 0.07 |
| Annualized Return | 策略年化(带交易成本) | 8% - 15% |
| Excess Return | 超额(vs CSI300) | 5% - 12% |
| Information Ratio | IR | 0.8 - 1.5 |
| Max Drawdown | 最大回撤 | -15% ~ -25% |
判断:如果你跑出来 IC<0.02 或 Annual Return 跟 benchmark 一样,多半是数据没下完整或时间窗设错。先别急着调参数,先看数据。
6.3 LGB 为什么是 qlib 的 baseline
| 模型 | 优势 | 在量化里的劣势 |
|---|---|---|
| Linear / Ridge | 解释性强、稳定 | 捕捉不到非线性 |
| LightGBM | 训练快、抗噪、能吃高维稀疏特征 | 解释性弱 |
| XGBoost | 类似 LGB 但更慢 | LGB 已替代 |
| LSTM | 能学时序 | 训练慢、过拟合严重 |
| Transformer | 当下流行 | 数据量要够大,A 股可能不够 |
LGB 是 qlib 论文中最常拿出来跟其他 ML 模型对比的基线,先跑通它,再换别的。
七、A 股 vs 美股:关键差异清单
迁移最大的坑不是 API 不同,是市场机制不同。下面这些 assumption 一定要重新校准。
7.1 交易机制对比
| 维度 | A 股 | 美股 |
|---|---|---|
| 交易时间 | 9:30-11:30 + 13:00-15:00(本地) | 9:30-16:00 ET(含盘前盘后) |
| 收盘价定义 | 14:57-15:00 集合竞价均价 | 16:00 最后一笔 |
| After-hours | 无 | 有(4:00-8:00 / 16:00-20:00) |
| 涨跌停 | 主板 ±10%,创业板/科创 ±20% | 无 |
| ST 股票 | 涨跌停 ±5% | 无概念 |
| Settlement | T+1(当天买不能卖) | T+1(但当天买当天可卖) |
| 卖空 | 个股有限(融券池小) | 普遍可做 |
| 衍生品 | 沪深 300、上证 50、中证 1000 指数期权 | 几乎所有股票都有期权 |
| 印花税 | 卖出千 1(单边) | 无 |
| 佣金 | 万 1-万 3 | $0 - $0.005/share |
| 财报披露 | 4 次/年,披露时间不规则 | 4 次/年,提前预告 |
7.2 回测里必须建模的 A 股特性
# 在 qlib backtest 配置里
exchange_kwargs = {
"limit_threshold": 0.095, # 涨跌停(板上不能买入,板下不能卖出)
"deal_price": "close", # 默认按收盘价成交
"open_cost": 0.0005, # 买入万 5(含佣金)
"close_cost": 0.0015, # 卖出含印花税
"min_cost": 5, # 单笔最低 5 元
"trade_unit": 100, # A 股最小 100 股(一手)
}
注意 trade_unit=100 这个细节——A 股最小买入单位是 100 股一手,10 万元买茅台只能买几手,资金少时很难做高分散。Top-K 持仓 K=50 在 100 万本金以下基本不可行。
7.3 T+1 对策略的实际影响
| 策略类型 | T+1 影响 |
|---|---|
| 月度因子选股 | 几乎无影响 |
| 周度再平衡 | 轻微(多 1 天 turnover 摩擦) |
| 日内反转 | 致命(当天不能卖) |
| 隔夜动量 | OK |
| 高频做市 | 不可行 |
我们 Phase 3 主战场是 日频到月频,T+1 影响可控。
7.4 ST 股票的处理
ST(Special Treatment)= 连续两年亏损被特别处理的股票,涨跌停 ±5%。两种做法:
- 学术派:保留 ST,但单独建模。理由:ST 摘帽事件本身是 alpha 来源
- 工程派:直接从票池剔除。理由:流动性差、信息噪声大、回测污染
我们 Phase 3 先剔除 ST,等基线 baseline 稳定再讨论是否加入。
# qlib 票池筛选时排除 ST
from qlib.data import D
instruments = D.list_instruments(
D.instruments(market='csi300'),
start_time='2021-01-01',
end_time='2022-12-31',
as_list=True,
)
# 再用 AKShare 拉 ST 列表过滤
7.5 PM 视角:跨市场迁移的"翻译表"
每次迁移到新市场,都要建一个 mental "翻译表":
| 美股概念 | A 股对应 | 差异程度 |
|---|---|---|
| SPY / QQQ | 沪深 300 ETF (510300) / 创业板 ETF (159915) | 几乎一致 |
| Earnings Surprise | 业绩预告偏离度 | A 股有"业绩预告"提前一个月 |
| Short Interest | 融券余额 | A 股融券池极小 |
| Dividend Capture | 红利再投资 | 但有 30% 个税(境外资金) |
| Options IV | 50ETF / 300ETF 期权 IV | A 股只有指数期权,没有单股期权 |
| 13F 持仓 | 北向资金 + 公募基金季报 | 北向是 T+1 透明的 |
| Reg-T Margin | A 股两融,融资规则更严 | - |
八、本周路线图(Day 75-81)
| Day | 主题 | 关键产出 |
|---|---|---|
| 75(今天) | qlib + AKShare/Tushare 工具栈 | qlib hello world 跑通 |
| 76 | 美股因子迁移 A 股(动量 / 反转 / 价值) | 三个核心因子的 IC 对比 |
| 77 | A 股事件驱动(业绩预告 / 高送转 / 解禁) | 事件回测脚手架 |
| 78 | 可转债策略(A 股独有的"债+期权"组合) | 双低策略 baseline |
| 79 | A 股回测的坑(停牌 / 复权 / 幸存者偏差) | 数据 sanity checklist |
| 80 | 港股市场(南向资金 / AH 溢价 / 仙股) | 港股票池接入 |
| 81 | Week 11 复盘 | 跨市场对比报告 |
九、PM 视角:迁移作为一种产品挑战
跨市场迁移在 PM 词典里 = 跨平台移植(iOS → Android、Web → Mobile、SaaS → 私有部署)。共性:
-
表面看是技术问题,底层是 assumption 问题
- iOS 假设有 Push,Android 中国版没有
- 美股假设当天 T+0 卖出,A 股 T+1 直接破策略
- 必须先列 assumption clash 表
-
抽象层的好坏决定迁移成本
- 如果 vectorbt 把"交易规则"硬编死,迁移就是重写
- qlib 把
limit_threshold做成参数,迁移变成调配置 - 好抽象 = 把市场差异当作参数而不是分支
-
本地化的隐性成本
- 数据源切换(10% 工作量)
- 数据清洗规则切换(30%)
- 业务规则适配(40%)
- 测试与回归(20%)
-
不要假设迁移后效果一致
- 美股动量在 A 股可能反转
- 财报漂移在 A 股可能根本不存在(披露不规则)
- 必须重新跑因子 IC,不能"理论上应该一样所以不测了"
-
国情合规是硬约束,不是 nice-to-have
- 印花税、个税、外汇额度、跨境数据传输
- 每一条都可能让"理论可行"变成"实操禁运"
一句话总结:迁移成本 ≈ 假设清单的长度 × 每个假设差异的幅度。Day 75 的所有工作,本质都是显式化这份清单。
十、Day 75 实际执行 Checklist
- (0) 读完这篇笔记
- (1) 创建
venv_qlib独立虚拟环境 - (2)
pip install pyqlib akshare tushare lightgbm - (3) 下载 qlib CSI 数据集(或用镜像)
- (4) 跑
qlib.init+D.calendar验证数据 - (5) 写完
qlib_hello.py并跑通 - (6) 记录三个指标:IC / Rank IC / Annualized Return
- (7) AKShare 拉
000001.SZ一年日线,画出来看一眼 - (8) 注册 Tushare Pro 账号拿 token(先不付费)
- (9) 把 A 股 vs 美股差异表打印贴墙
- (10) 更新
docs/daily/TR_PROGRESS.mdDay 75 标 ✅
十一、明日预告
Day 76: 把美股因子迁移到 A 股 — 动量 / 反转 / 价值的截面验证
- 美股 12-1 动量在 A 股表现:截面 IC 对比
- A 股短期反转效应(5 日 / 20 日)实证
- BM ratio / PE 价值因子在 A 股的失效与重生
- 用 qlib Expression Engine 写自定义因子
- 因子之间相关性矩阵 + 正交化
- 「迁移有效 / 迁移失效 / 迁移反转」三个案例
为什么这个顺序:今天搭好工具栈,明天就要让工具产出第一份「跨市场可比」的研究结论——这是 Phase 3 的核心交付物。
实际执行记录
启动一项填一项,时间戳 + 卡点。
- [hh:mm]
pip install pyqlib— 是否一次过?版本: - [hh:mm] CSI300 数据下载 — 大小约 2.x GB,耗时:
- [hh:mm]
qlib_hello.py首次跑通 — IC:__ / Rank IC:__ / Annual Return:__ - [hh:mm] AKShare 拉单股 — 接口是否被限速?
- [hh:mm] Tushare 注册 — 积分等级:
- 卡点 / 学到的:
总字数:约 6,400 字 今日完成度:理论 ✓ / 实操(你自己执行)/ 笔记 ✓