返回交易笔记
TR Day 75

qlib 入门 + AKShare / Tushare 数据栈

qlib 四层架构、AKShare / Tushare 数据源全景、A 股与美股市场机制关键差异

2026-07-23
Phase 3: 实盘+规模化+迁移
qlibAKShareTushareAShareCSI300HKShareCrossMarket

日期: 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 模型管线化」。它的几个原生优势:

  1. A 股原生支持:内置 CSI100/300/500/1000 票池、复权处理、停牌处理、ST 标记
  2. 截面操作 first-class:Rank / Group / Cross-sectional Normalize 都是一行表达式
  3. 数据存储为 bin 格式:比 CSV 快 50-100 倍,5000 只票一秒读完
  4. 模型管线化:Dataset → Handler → Model → Backtest 四层,每层都能换组件
  5. 配套 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
DataD.features() / D.calendar()pd.DataFrame数据源
Dataset/HandlerAlpha158 158 个内置因子自己写因子 indicator特征工程
ModelLGBModel / 自定义没有 ML 概念sklearn estimator
BacktestTopkDropoutStrategyPortfolio.from_signals没有(ML 不做交易)

2.3 跟 vectorbt 的哲学对比

哲学vectorbtqlib
主流场景单标的,时间序列扫一遍多标的,每日截面排序
信号"什么时候买""今天买谁"
交易频率任意(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 或更高

注意

  1. Python 3.9 - 3.11 推荐(3.12 部分依赖还不稳)
  2. Windows 装 LightGBM 可能要先装 VS Build Tools;Linux/Mac 一般顺
  3. 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 定位差异

维度AKShareTushare 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 RatioIR0.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%无概念
SettlementT+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 IV50ETF / 300ETF 期权 IVA 股只有指数期权,没有单股期权
13F 持仓北向资金 + 公募基金季报北向是 T+1 透明的
Reg-T MarginA 股两融,融资规则更严-

八、本周路线图(Day 75-81)

Day主题关键产出
75(今天)qlib + AKShare/Tushare 工具栈qlib hello world 跑通
76美股因子迁移 A 股(动量 / 反转 / 价值)三个核心因子的 IC 对比
77A 股事件驱动(业绩预告 / 高送转 / 解禁)事件回测脚手架
78可转债策略(A 股独有的"债+期权"组合)双低策略 baseline
79A 股回测的坑(停牌 / 复权 / 幸存者偏差)数据 sanity checklist
80港股市场(南向资金 / AH 溢价 / 仙股)港股票池接入
81Week 11 复盘跨市场对比报告

九、PM 视角:迁移作为一种产品挑战

跨市场迁移在 PM 词典里 = 跨平台移植(iOS → Android、Web → Mobile、SaaS → 私有部署)。共性:

  1. 表面看是技术问题,底层是 assumption 问题

    • iOS 假设有 Push,Android 中国版没有
    • 美股假设当天 T+0 卖出,A 股 T+1 直接破策略
    • 必须先列 assumption clash 表
  2. 抽象层的好坏决定迁移成本

    • 如果 vectorbt 把"交易规则"硬编死,迁移就是重写
    • qlib 把 limit_threshold 做成参数,迁移变成调配置
    • 好抽象 = 把市场差异当作参数而不是分支
  3. 本地化的隐性成本

    • 数据源切换(10% 工作量)
    • 数据清洗规则切换(30%)
    • 业务规则适配(40%)
    • 测试与回归(20%)
  4. 不要假设迁移后效果一致

    • 美股动量在 A 股可能反转
    • 财报漂移在 A 股可能根本不存在(披露不规则)
    • 必须重新跑因子 IC,不能"理论上应该一样所以不测了"
  5. 国情合规是硬约束,不是 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.md Day 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 字 今日完成度:理论 ✓ / 实操(你自己执行)/ 笔记 ✓