news 2026/10/27 1:43:00

Qlib实战:从CSV数据到LightGBM选股回测全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Qlib实战:从CSV数据到LightGBM选股回测全流程

简介:这份PDF资料是华泰证券金工研究团队2020年12月发布的深度研究报告,聚焦微软AI量化投资平台Qlib的实操体验,面向量化投资研究者、金融从业者及希望降低AI算法使用门槛的学习者。报告以港股日频量价因子LightGBM选股策略为案例,分Qlib安装、数据准备、因子生成、策略构建、结果展示等步骤讲解基础功能,并进一步介绍自定义因子、标签、数据预处理方式与AI模型等进阶组件,同时对比传统量化策略开发流程,提炼Qlib在数据存储方案、表达式引擎和缓存系统上的工程创新。资源包共1个PDF文件,约4.71MB,内容完整、结构清晰,便于按章节检索学习。目前已有1173人学习下载,适合希望快速上手Qlib、理解其优势与局限并借鉴案例设计新应用场景的读者参考。

1. 从一份港股 CSV 到一条完整策略流水线

手里有一批港股日频行情 CSV,想跑通一条「量价因子 + LightGBM 选股 + 回测」的完整链路,最省事的路径是什么?自己拼 pandas、sklearn、backtrader,光数据对齐和因子计算就能耗掉一周。Qlib 这个项目把这条链路收进了一个统一框架:数据落盘、因子表达式、模型训练、回测评估,全在一套 API 里完成。它面向的是想用 AI 做量价选股的开发者,尤其是手里已有 CSV 数据、想快速验证想法的人。这篇笔记按「装起来 → 数据灌进去 → 因子跑出来 → 策略回测 → 组件替换」的顺序拆一遍,中间夹几段我踩过的坑,方便你直接抄作业。

2. 环境搭建与数据入库:从 CSV 到 bin 的完整落地

2.1 安装路径选择:为什么我推荐 setup.py 而不是 pip

Qlib 官方给了两条安装路:pip install pyqlib和源码setup.py install。pip 装的是稳定版,省事,但我在 Windows 和 Mac 上都遇到过同一个问题——pip 装完之后想跑官方示例脚本,发现qlib.tests模块缺失,get_data.py直接报ModuleNotFoundError。源码安装会把 tests 目录一起带进来,后续下载官方数据、跑示例都顺。

源码安装前有两个前置条件。第一是 Python 3.7 及以上;第二是 Windows 下必须装 Microsoft C++ 生成工具,注意是「生成工具」不是「运行库」——只装 Redistributable 解决不了Microsoft Visual C++ 14.0 is required这个报错,得装完整的 Build Tools。装的时候在安装器里勾选「使用 C++ 的桌面开发」那一组组件。

# 先更新两个基础依赖,numpy 和 cython 版本太旧会在编译阶段翻车 pip install numpy pip install --upgrade cython # 方式一:有 git 环境 git clone https://github.com/microsoft/qlib.git && cd qlib python setup.py install # 方式二:没 git,下载 zip 解压到任意路径,确保 setup.py 在该目录下 cd /path/to/qlib python setup.py install

setup.py install装的是开发版,会比 pip 的稳定版新一些。装完在 Python 里import qlib不报错就算过了。这里有个细节:如果你用 conda 环境,确认setup.py跑在目标环境里,别装到 base 去了,后面get_data找不到模块多半是这个原因。

2.2 官方数据下载与用户 CSV 的 dump_all 转换

装好之后先拿官方数据练手,确认整条链路是通的。在 Qlib 安装目录下执行:

# 下载 A 股日频数据到默认目录 ~/.qlib/qlib_data/cn_data python scripts/get_data.py qlib_data --target_dir ~/.qlib/qlib_data/cn_data --region cn

--target_dir是落盘路径,--region选cn或us。下载完目录下会有calendars、features、instruments三块,这是 Qlib 数据层的标准结构,后面所有读取都基于它。

真正要落地自己的策略,得把手里 CSV 转成同样的结构。Qlib 用dump_bin.py的dump_all子命令做这件事。假设港股数据按「一只股票一个 CSV、文件名为股票代码」组织在hk_data目录下,每个 CSV 至少要有这些列:

字段含义备注
stock_code股票代码与文件名一致,列名可自定义
date交易日期列名可自定义
open / high / low / close复权价建议用复权价
volume / money成交量 / 成交额量价因子必需
vwap复权均价回测交易价参考
factor复权因子强烈建议保留
change日涨跌幅强烈建议保留

转换命令:

python scripts/dump_bin.py dump_all \ --csv_path ~/.qlib/csv_data/hk_data \ --qlib_dir ~/.qlib/qlib_data/hk_data \ --symbol_field_name stock_code \ --date_field_name date \ --include_fields open,high,low,close,volume,money,factor,vwap,change

参数逐个说:--csv_path是原始 CSV 目录,--qlib_dir是转换后 bin 数据的落盘目录,--symbol_field_name指定 CSV 里哪一列是股票代码,--date_field_name指定日期列,--include_fields列出要保留的字段。这里有个血泪经验:--include_fields的逗号后面不能有空格,写成open, high, low会直接转换失败,报错信息还不明显,我第一次卡了半小时才反应过来。

转换完成后--qlib_dir下会出现calendars、features、instruments三个目录,和官方数据结构一致。指数数据(比如恒生指数)用同样的方式单独存一个 CSV,文件名当代码用,一起丢进hk_data目录参与转换即可,回测时拿它当基准。

3. 因子生成与 LightGBM 策略构建:Alpha158 跑通全流程

3.1 初始化环境与自定义股票池

数据就位后,第一步是初始化 Qlib 运行环境,告诉它数据在哪、用哪个市场:

import qlib from qlib.constant import REG_HK # provider_uri 指向 dump_all 生成的目录,region 决定交易日历和代码格式 qlib.init(provider_uri="~/.qlib/qlib_data/hk_data", region=REG_HK)

provider_uri就是上一步的--qlib_dir,region决定用哪套交易日历。初始化之后就能用D(Data)对象取数了:

from qlib.data import D # 取交易日历和全部标的 calendars = D.calendar(start_time="2010-01-01", end_time="2020-11-30") instruments = D.instruments(market="all") # 取指定股票、指定字段的时序数据 data = D.features( instruments=["0700.HK"], fields=["$close", "$volume"], start_time="2020-01-01", end_time="2020-11-30" )

$close、$volume这种带$的写法是 Qlib 的表达式语法,字段名对应你 dump 进去的列。实际做策略时不会用all,而是自己圈一个股票池,常见做法是写一个 instruments 文件,每行「代码 + 起止日期」,然后在D.instruments里引用这个文件名。这样能剔除低价股、低流动性标的,回测结果才不至于失真。

3.2 Alpha158 因子库与标签生成

Qlib 内置了 Alpha158 和 Alpha360 两套因子库,前者是 158 个量价因子,后者是 360 个。对日频选股来说 Alpha158 够用,它把 K 线形态、动量、波动率这些常见量价特征都包进去了,不用自己一个个写。

from qlib.contrib.data.handler import Alpha158 # handler 负责把原始行情加工成因子矩阵和标签 handler = Alpha158( instruments="csi300", # 股票池,换成你自己的 start_time="2010-01-01", end_time="2020-11-30", fit_start_time="2010-01-01", # 预处理器拟合区间 fit_end_time="2018-12-31", infer_processors=[], # 推理阶段的预处理 learn_processors=["DropnaLabel"] # 训练阶段的预处理 ) # 拿到特征(当期因子)和标签(下期收益) features = handler.fetch(col_set="feature") labels = handler.fetch(col_set="label")

fit_start_time/fit_end_time是给标准化、去极值这类预处理器拟合参数用的,必须落在训练集范围内,否则就是未来函数。learn_processors里DropnaLabel把标签缺失的样本丢掉,避免训练时被 NaN 污染。标签默认是下期收益率,具体用 close 还是 vwap 计算,在 handler 源码里能改。

3.3 LightGBM 训练参数与 task 配置

Qlib 把「因子生成 → 模型训练 → 回测」串成一个 workflow,每个环节用字典配置。先配模型参数:

from qlib.contrib.model.gbdt import LGBModel model = LGBModel( loss="mse", learning_rate=0.05, max_depth=6, num_leaves=64, num_threads=8, early_stopping_rounds=50 )

loss="mse"对应回归任务,预测收益率;learning_rate和max_depth是 LightGBM 的常规调参项,日频量价数据噪声大,树深别开太深,6 层左右比较稳;early_stopping_rounds配合验证集防过拟合。

然后是数据集配置,把 handler 和训练/验证/测试的时间切分绑在一起:

from qlib.data.dataset import DatasetH from qlib.data.dataset.handler import DataHandlerLP dataset = DatasetH( handler=Alpha158( instruments="csi300", start_time="2010-01-01", end_time="2020-11-30", fit_start_time="2010-01-01", fit_end_time="2018-12-31" ), segments={ "train": ("2010-01-01", "2017-12-31"), "valid": ("2018-01-01", "2018-12-31"), "test": ("2019-01-01", "2020-11-30"), } )

segments的三段切分是整条流水线的骨架,训练集拟合模型、验证集早停、测试集出绩效。时间上必须严格递增,任何重叠都会让回测结果虚高。这套配置可以直接喂给task_train或自己写训练循环,跑完会得到一个模型文件,后面回测直接加载。

4. 回测评估与结果解读:从 IC 到净值曲线

4.1 回测参数配置与执行

模型训完,下一步是拿测试集预测值做组合回测。Qlib 的回测配置集中在port_analysis_config里:

from qlib.contrib.evaluate import backtest_daily from qlib.contrib.strategy import TopkDropoutStrategy # 策略:每天持有预测分数最高的 topk 只,每期换掉 drop 只 strategy = TopkDropoutStrategy( signal=pred_score, # 模型预测分数,Series 或 DataFrame topk=50, n_drop=5 ) # 回测:初始资金、交易成本、基准 report, positions = backtest_daily( start_time="2019-01-01", end_time="2020-11-30", strategy=strategy, account=100000000, benchmark="SH000300", exchange_kwargs={ "freq": "day", "limit_threshold": 0.095, "deal_price": "close", "open_cost": 0.0005, "close_cost": 0.0015, "min_cost": 5 } )

topk=50是持仓数量,n_drop=5是每期换手只数,这两个参数直接决定换手率和交易成本。open_cost/close_cost是买卖双边费率,港股和 A 股不一样,按实际市场填。limit_threshold是涨跌停限制,港股没有涨跌停就设大一点或者不设。deal_price选close表示按收盘价成交,也可以选vwap,更贴近真实成交。

4.2 绩效指标读取与结果判读

回测返回的report里是一堆绩效指标,常用的几个:

# 年化收益、最大回撤、夏普、信息比率 print(report[["annualized_return", "max_drawdown", "information_ratio", "sharpe"]])

判读的时候别只看年化收益。先看 IC 和 Rank IC——模型预测分数和真实下期收益的相关系数,IC 在 0.03 以上、Rank IC 在 0.05 以上算有点信号,低于这个量级基本是噪声。再看净值曲线和超额收益曲线是否平稳,如果超额收益集中在某几个月、其余时间横盘,多半是过拟合到某段行情。最后看换手率,日频策略换手率动辄几百个百分点,交易成本吃掉大半收益是常态,n_drop调小能降换手但也会削弱信号。

提示:回测结果只反映历史,测试集时间太短(比如只有一年)时指标波动极大,别拿单次回测下结论。

5. 避坑与常见问题排查

5.1 安装阶段:C++ 生成工具与模块缺失

现象:pip install pyqlib或setup.py install报Microsoft Visual C++ 14.0 is required。原因:Qlib 有 Cython 扩展需要编译,Windows 缺编译器。解决:装 Microsoft C++ 生成工具,勾选「使用 C++ 的桌面开发」,不是装运行库。

现象:跑get_data.py报No module named 'qlib.tests'。原因:pip 安装的包不含 tests 目录。解决:改用源码setup.py install,或从源码包把 tests 目录拷到安装路径下。

5.2 数据转换:字段空格与复权价缺失

现象:dump_all执行到一半报错退出。原因:--include_fields逗号后有空格。解决:写成open,high,low,close紧凑格式,一个空格都不要留。

现象:回测净值和收益异常,比如净值曲线突然跳变。原因:CSV 里缺factor或change字段,或者用了未复权价格。解决:补上复权因子和涨跌幅字段,价格统一用复权价,前后复权都行但全库要一致。

5.3 训练与回测:时间切分泄漏

现象:测试集 IC 高得离谱,实盘一跑就崩。原因:fit_start_time/fit_end_time跨到了测试集,预处理器用到了未来数据。解决:拟合区间严格落在训练集内,segments三段不重叠,改完重跑。

现象:回测换手率极高、扣费后收益为负。原因:n_drop设太大或信号噪声大。解决:降n_drop、加信号平滑、或把调仓频率从日频改成周频。

6. 二次开发:替换组件与验证改造是否有效

Qlib 的扩展点设计得比较规整,每个组件都有对应源码和参数入口,改起来有两条路:改参数、继承重写。先说最省事的参数路线。想换模型,把LGBModel换成qlib.contrib.model下任意一个内置模型即可,比如线性回归、XGBoost、MLP,参数名基本对齐各自库的命名。想换预处理,在 handler 的learn_processors里加RobustZScoreNorm、Fillna这类内置处理器,顺序有讲究——先填充再标准化,反了会报错。

参数不够用就继承。自定义因子的常见做法是写一个 handler 子类,重写get_feature_config,在里面用表达式拼新因子:

from qlib.contrib.data.handler import Alpha158 class MyHandler(Alpha158): def get_feature_config(self): fields, names = super().get_feature_config() # 追加一个 5 日动量因子 fields.append("Ref($close, 5) / $close - 1") names.append("MOM5") return fields, names

Ref($close, 5)是表达式引擎的滞后函数,取 5 日前的收盘价,整个表达式就是 5 日收益率。表达式引擎的好处是因子计算下推到数据层,不用把全量数据拉到内存里用 pandas 算,数据量大时性能差距明显。自定义标签同理,改 handler 里 label 的表达式即可,比如把下期收益换成下期收益的排序分位。

换模型就继承Model基类,实现fit和predict两个方法,把 sklearn 风格的模型包一层就能接进 workflow。验证改造是否有效,我的习惯是固定其他所有变量,只改一个组件,跑同一段测试集,对比 IC、Rank IC 和扣费后年化。改造前后 IC 提升不到 0.005 的基本可以认为是噪声,别急着上线。

有个坑我印象很深:早期我自定义因子时直接在get_feature_config里返回了 numpy 数组而不是表达式字符串,训练能跑通,但回测阶段数据对齐全乱了,净值曲线像心电图。从那以后我每次加因子都强制走一遍「小样本单因子 IC 验证」,确认表达式在D.features里能正确取数再进 workflow。希望这些能帮你少走几步弯路。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/27 1:40:41

全球DEM数据校验、格式选型与GIS地形分析实操

简介:面向GIS从业者与地理信息学习者的全球DEM地形数据获取文档,聚焦全球高程数据下载渠道分散、免费工具与学习资料难配套的问题。压缩包仅含1个docx文件,约12KB,篇幅精炼,集中整理全球范围DEM数据的百度网盘与天翼云…

作者头像 李华
网站建设 2026/10/27 1:36:09

nginx部署教程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/27 1:35:41

docker重新加载docer-compose.yml文件(nginx配置)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/27 1:34:34

【RHCA+】logout命令(注销登录)

“ 目录:RHCA -> 11 系统管理 -> 15 logout命令(注销登录) ”注销登录;语法格式:logout和exit、quit等命令效果一样[rootwuzuniao ~]# logout #注销登录

作者头像 李华
网站建设 2026/10/27 1:33:08

Linux运维(3)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/27 1:30:45

dsh-purge完全指南:如何给模型换破甲提示词

dsh-purge完全指南:如何给模型换破甲提示词 【免费下载链接】dsh-purge DeepSeek Harness 破甲:让所有模型都能破甲,不同模型可换不同提示词;默认提示词面向国模「小码酱」。Jailbreak for every model — swap prompts per model…

作者头像 李华