2. Qlib基础入门
在正式进入风险模型与组合优化之前,必须先建立对 Qlib 的整体认知。Qlib 是微软开源的量化投资平台,它并不是一个“策略脚本集合”,而是一套围绕数据、表达式、模型、回测四要素构建的工程化框架。理解它的架构与数据组织方式,决定了后续因子研究、风险建模和组合优化能否顺畅落地。
一、核心架构:分层解耦的设计
Qlib 的架构可以概括为“三层一引擎”。底层是数据层,负责行情、财务、因子等数据的存储与检索;中间是表达层,通过表达式引擎把原始字段组合成可计算的因子;上层是工作流层,用配置驱动的方式串联数据处理、模型训练、预测与回测。贯穿其中的是表达式引擎,它是 Qlib 区别于普通回测框架的关键。
这种分层带来的直接好处是:数据格式统一后,因子、模型、策略可以独立替换。你更换一个因子表达式,不需要改动数据加载代码;你更换模型,也不需要重写回测逻辑。
二、数据格式:二进制存储与表达式字段
Qlib 官方推荐的数据格式是 .bin 二进制格式,配合日历文件与 instruments 文件使用。相比 CSV,二进制格式在读取速度和内存占用上有明显优势,尤其适合全市场、长时间跨度的因子计算。
一个标准的数据目录通常包含以下结构:
qlib_data/
├── calendars/
│ └── day.txt
├── instruments/
│ └── all.txt
└── features/
└── sh600000/
├── open.day.bin
├── close.day.bin
├── volume.day.bin
└── ...
其中 calendars/day.txt 定义交易日历,instruments/all.txt 定义股票池及起止时间,features/ 下按股票代码分目录存放各字段的二进制文件。字段名如 $close、$volume、$factor 在表达式中直接引用。
| 组成部分 | 作用 | 常见文件 |
|---|---|---|
| 日历 | 定义交易日序列,对齐所有时间索引 | calendars/day.txt |
| 股票池 | 定义可交易标的及有效区间 | instruments/all.txt |
| 特征数据 | 存储行情、财务、因子等字段 | features/*.bin |
| 表达式缓存 | 加速复杂因子重复计算 | 由引擎自动管理 |
三、表达式引擎:Qlib 的“因子语言”
表达式引擎允许你用类似公式的字符串描述因子,例如 Ref($close, 1) 表示昨日收盘价,Mean($close, 5) 表示 5 日均价。引擎会自动解析依赖、按时间对齐、处理缺失值,并支持缓存。
常见运算符与函数包括:
- 算术运算:
+ - * /,支持字段与常数混合运算。 - 时序函数:
Ref(滞后)、Mean(均值)、Std(标准差)、Corr(相关性)。 - 截面函数:
Rank(排名)、ZScore(标准化),用于横截面比较。 - 逻辑函数:
If、Greater、Less,用于条件筛选。
例如,一个简单的动量因子可以写成:
Ref($close, 1) / Ref($close, 21) - 1
这表示过去 20 个交易日的收益率。引擎会自动完成对齐与计算,你无需手动处理停牌、缺失等问题。
$ 前缀表示字段引用。字段名必须与数据目录中的 .bin 文件名一致。若字段不存在,引擎会报错而非静默返回空值,这有助于尽早发现数据问题。
四、快速上手案例:从数据到预测
下面用一个最小工作流演示 Qlib 的典型使用方式。假设已完成数据准备,目标是训练一个简单模型并生成预测。
import qlib
from qlib.constant import REG_CN
from qlib.utils import init_instance_by_config
qlib.init(provider_uri="~/.qlib/qlib_data/cn_data", region=REG_CN)
config = {
"model": {
"class": "LGBModel",
"module_path": "qlib.contrib.model.gbdt",
"kwargs": {"loss": "mse", "num_leaves": 31}
},
"dataset": {
"class": "DatasetH",
"module_path": "qlib.data.dataset",
"kwargs": {
"handler": {
"class": "Alpha158",
"module_path": "qlib.contrib.data.handler",
"kwargs": {
"start_time": "2018-01-01",
"end_time": "2020-12-31",
"fit_start_time": "2018-01-01",
"fit_end_time": "2019-12-31",
"instruments": "csi300"
}
},
"segments": {
"train": ("2018-01-01", "2019-12-31"),
"valid": ("2020-01-01", "2020-06-30"),
"test": ("2020-07-01", "2020-12-31")
}
}
}
}
model = init_instance_by_config(config["model"])
dataset = init_instance_by_config(config["dataset"])
model.fit(dataset)
pred = model.predict(dataset)
这段代码体现了 Qlib 的核心工作流:初始化 → 构建数据集 → 训练模型 → 预测。其中 Alpha158 是内置的因子集,内部大量使用表达式引擎生成因子。你可以把它替换成自定义表达式集合,从而控制因子内容。
Alpha158 的源码来加因子,这会导致升级困难且难以复现。正确做法是继承 Handler 或使用 DataHandlerLP 自定义表达式列表,把因子定义与框架代码分离。
五、复盘与建议
Qlib 的学习曲线主要集中在前期的数据准备和表达式理解上。一旦数据格式正确、表达式写对,后续的模型与回测会非常顺畅。建议按以下顺序推进:
- 先用官方数据跑通一个完整案例,确认环境与数据无误。
- 再尝试用表达式引擎手写 3~5 个因子,观察输出结构。
- 最后替换模型或因子集,理解配置驱动的替换机制。
需要特别注意的是,Qlib 的表达式引擎虽然强大,但并非所有复杂逻辑都能用表达式表达。涉及跨股票、跨时间的复杂状态时,仍需在 Python 层处理。理解边界,才能用好工具。