4. 金融数据基础与Qlib数据格式
在Qlib中做深度学习量化预测,第一步不是搭模型,而是把数据“喂对”。Qlib对金融数据有一套明确的抽象:行情数据描述市场状态,因子数据描述可计算的解释变量,标签数据描述预测目标,而表达式引擎负责把原始字段组合成因子和标签。理解这四者的关系,才能正确使用Qlib的数据存储格式与查询接口。
一、核心原理:Qlib的数据分层
Qlib把数据组织成“字段—表达式—数据集”三层。最底层是字段,例如收盘价、成交量、换手率;中间层是表达式,例如 Ref($close, 1) 表示昨日收盘价;最上层是数据集,例如 Alpha158、Alpha360,它们把大量表达式批量计算并缓存为可训练的特征矩阵。
行情数据通常包含时间序列上的OHLCV、复权因子、涨跌停状态等。因子数据是从行情数据派生出的数值特征,例如动量、波动率、均线偏离。标签数据则是未来收益或未来价格方向,例如 Ref($close, -2)/Ref($close, -1)-1 表示未来一期的收益率。三者共享同一套时间索引和股票代码索引,因此Qlib使用统一的多级索引:第一级是日期,第二级是股票代码。
关键点:Qlib中所有数据查询最终都返回带 datetime 和 instrument 两级索引的 DataFrame。模型训练时,特征和标签必须按同一时间轴对齐,否则会出现“未来函数”或标签错位。
二、方法流程:从原始数据到可训练数据集
一个标准流程如下:
- 准备原始数据:通常使用
scripts/get_data.py下载Qlib官方数据,或把自有CSV转换为Qlib格式。 - 定义表达式:在配置文件中用字符串表达式描述因子和标签,例如
"$close/Ref($close,1)-1"。 - 计算并缓存:Qlib的表达式引擎会解析表达式,按股票和时间批量计算,并写入磁盘缓存。
- 查询数据集:通过
Dataset或DataHandler接口按时间区间、股票池、特征列查询。 - 送入模型:得到
X和y,再按时间切分训练集、验证集、测试集。
下面是一个最小示例,展示如何用Qlib查询行情数据并计算简单因子:
import qlib
from qlib.data import D
qlib.init(provider_uri="~/.qlib/qlib_data/cn_data", region="cn")
# 查询行情数据
df = D.features(
instruments=["SH600000"],
fields=["$open", "$high", "$low", "$close", "$volume"],
start_time="2023-01-01",
end_time="2023-03-31"
)
print(df.head())
# 查询表达式因子
factor = D.features(
instruments=["SH600000"],
fields=["$close/Ref($close,1)-1", "Mean($close,5)"],
start_time="2023-01-01",
end_time="2023-03-31"
)
print(factor.tail())
运行后,返回的DataFrame索引为 (datetime, instrument),列名为字段表达式。若表达式写错,Qlib会在解析阶段报错,而不是静默返回空值,这一点对调试非常友好。
三、数据存储格式与查询接口
Qlib默认使用二进制列式存储,每个字段按股票分别存储为 .bin 文件,并配合 instruments 目录下的股票池文件和 calendars 目录下的交易日历。这种设计使按时间区间和股票池的切片查询非常快,适合深度学习反复读取大批量特征。
| 数据类别 | 典型字段/表达式 | 存储位置 | 查询接口 |
|---|---|---|---|
| 行情数据 | $open, $high, $low, $close, $volume | features/<instrument>/*.bin | D.features |
| 因子数据 | Ref($close,1), Mean($close,5), Std($close,20) | 运行时计算或缓存 | D.features / DataHandler |
| 标签数据 | Ref($close,-2)/Ref($close,-1)-1 | 与因子共用表达式引擎 | DataHandlerLP |
| 股票池 | csi300, csi500, all | instruments/*.txt | D.instruments |
| 交易日历 | 交易日列表 | calendars/day.txt | D.calendar |
查询接口方面,D.features 适合快速取数;DataHandler 适合把原始字段转成模型输入;Dataset 则进一步封装了训练/验证/测试切分。实际项目中,建议先用 D.features 验证表达式正确性,再写入配置文件交给 DataHandler 批量处理。
四、案例与误区复盘
假设我们要预测未来5日收益率,并构造一个动量因子。正确做法是:
# 标签:未来5日收益率
label = "Ref($close, -5)/$close - 1"
# 因子:过去5日动量
factor = "$close/Ref($close, 5) - 1"
# 在配置中同时声明
data_handler_config = {
"start_time": "2018-01-01",
"end_time": "2023-12-31",
"fit_start_time": "2018-01-01",
"fit_end_time": "2020-12-31",
"instruments": "csi300",
"fields": [factor, label]
}
常见误区有三个:
- 标签方向写反:
Ref($close, -5)表示未来第5日,若写成Ref($close, 5)就变成过去,模型会学到“用历史预测历史”。 - 忽略复权:未复权价格在除权日会出现跳空,因子和标签都会被污染。Qlib官方数据已做复权处理,自有数据需自行处理。
- 训练集与测试集时间重叠:深度学习模型对时间泄漏非常敏感,必须按时间切分,不能随机打乱。
调试建议:先用单只股票、短时间区间查询,打印 df.head() 和 df.tail(),确认索引、列名和数值范围符合预期,再扩展到全市场。这样能快速定位表达式错误或数据缺失问题。
注意:Qlib的表达式引擎区分大小写,字段名如 $close 必须小写,函数名如 Ref、Mean 必须首字母大写。写错时不会自动纠正,而是直接报错或返回空列。