4. 金融数据获取与准备
在 Qlib 的完整工作流中,数据层是决定策略上限的第一道门槛。Qlib 本身并不直接抓取行情,而是通过一套约定好的二进制格式(.bin)与日历文件来组织数据,再由 DataHandler 统一读取。因此,本章的核心任务是:把外部数据源(A股、)转换成 Qlib 能识别、能高效读取、能持续更新的本地数据仓库。
一、核心原理:Qlib 的数据组织方式
Qlib 的数据目录遵循“按字段分目录、按标的存文件”的结构。每个交易日历由 calendars/day.txt 定义,每个字段(如 $close、$volume)存放在 features/<instrument>/<field>.day.bin 中。这种设计让读取时只需按行号对齐日历,避免了解析 CSV 的开销,也便于增量追加。
.bin 文件是“按日历行号索引”的定长浮点数组。只要日历顺序不变,新增一天数据只需在文件末尾追加一个 float32,无需重写整个文件。
因此,数据准备的本质是两件事:对齐日历与按字段落盘。只要这两步正确,后续的特征工程与回测就能稳定运行。
二、支持的数据源与获取方式
Qlib 官方提供了 scripts/get_data.py 用于下载社区整理好的数据,覆盖 A 股与部分。常见数据源对比如下:
| 数据源 | 覆盖市场 | 获取方式 | 适用场景 |
|---|---|---|---|
| Qlib 官方数据包 | A股(沪深) | python scripts/get_data.py qlib_data --target_dir ~/.qlib/qlib_data/cn_data --region cn |
快速验证、教学演示 |
| Yahoo Finance | 、部分A股 | yfinance 下载 CSV 后转换 |
策略、自定义标的 |
| Tushare / AkShare | A股 | API 拉取后转 CSV | 需要复权、财务等扩展字段 |
| 本地 CSV | 任意 | 自行整理后调用 dump_bin | 私有数据、因子数据 |
对于 A 股,官方数据包最省事;对于,通常先用 yfinance 下载,再走 CSV 转换流程。无论哪种来源,最终都要落到同一套 dump_bin 流程上。
三、方法流程:从 CSV 到 Qlib 二进制
假设我们已经用 yfinance 下载了 AAPL 的日线数据,保存为 aapl.csv,包含 date, open, high, low, close, volume 六列。转换流程如下:
- 整理 CSV:确保日期列名为
date,且按升序排列;缺失值用前值填充或删除。 - 调用
dump_bin.py:指定 CSV 目录、目标目录、字段映射与日历。 - 校验输出:检查
calendars/day.txt与features/aapl/close.day.bin是否生成。 - 在 Qlib 中初始化:设置
provider_uri指向目标目录,加载D.features验证。
# 1. 下载数据
python -c "
import yfinance as yf
df = yf.download('AAPL', start='2020-01-01', end='2024-12-31')
df = df.reset_index()
df.columns = [c.lower() for c in df.columns]
df.to_csv('csv_data/aapl.csv', index=False)
"
# 2. 转换为 Qlib 二进制
python scripts/dump_bin.py dump_all \
--csv_path ./csv_data \
--qlib_dir ~/.qlib/qlib_data/us_data \
--include_fields open,high,low,close,volume \
--date_field_name date \
--symbol_field_name symbol
# 3. 验证
python -c "
import qlib
from qlib.data import D
qlib.init(provider_uri='~/.qlib/qlib_data/us_data', region='us')
print(D.features(['AAPL'], ['\$close'], start_time='2024-01-01', end_time='2024-01-10'))
"
symbol 列,dump_bin 会默认用文件名作为标的代码。建议在 CSV 中显式增加 symbol 列,避免多标的合并时出错。
四、数据更新与增量维护
实盘或长期研究中,数据需要每日更新。Qlib 的增量维护有两种策略:
- 全量重刷:重新下载全部历史并覆盖。简单但耗时,适合数据量小或字段变更时。
- 增量追加:只下载最新交易日数据,追加到现有
.bin文件末尾,并同步更新日历。适合每日定时任务。
增量追加的关键是保证日历顺序一致。若新数据的日期已存在于日历中,则覆盖对应行;若不存在,则追加到末尾。Qlib 的 dump_bin 支持 --append 参数实现这一逻辑:
# 每日增量更新示例
python scripts/dump_bin.py dump_update \
--csv_path ./daily_csv \
--qlib_dir ~/.qlib/qlib_data/us_data \
--include_fields open,high,low,close,volume \
--date_field_name date \
--symbol_field_name symbol
calendars/day.txt。如果新数据的日期早于日历末尾,直接追加会导致行号错位,所有历史特征都会偏移。建议先用脚本校验日期单调递增。
五、常见误区与复盘
在实际教学中,学员最容易踩的坑集中在以下三点:
- 复权处理不一致:A 股数据若未做前复权,价格在除权日会出现跳空,导致动量因子失真。建议在 CSV 阶段就完成复权,再落盘。
- 日历不统一:A 股与交易日不同。若把两个市场的数据混入同一日历,会出现大量空值。正确做法是分市场建库,或使用 Qlib 的多市场日历机制。
- 字段命名不规范:Qlib 字段以
$开头,如$close。若 CSV 列名写成Close,转换后需在DataHandler中做映射,增加维护成本。
复盘建议:每次数据更新后,用一段固定脚本检查“日历长度、字段文件大小、最近一日数值”三项指标,形成数据质量日志。这样在策略回测出现异常时,能快速定位是数据问题还是模型问题。
完成本章后,你应该能够独立完成:从任意数据源获取 A 股或行情,转换为 Qlib 二进制格式,并建立可持续的增量更新流程。这是后续特征工程与模型训练的数据地基。