ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Qlib量化投资框架上手实践:从环境搭建到LightGBM回测全解析

Qlib量化投资框架上手实践:从环境搭建到LightGBM回测全解析 1. 为什么一个做量化的朋友劝我从Qlib入门而不是自己造轮子先说个背景。我之前自己写过一阵子策略回测用的方式是取行情数据后自己拼DataFrame、自己算因子、自己写分层回测代码越写越长逻辑越来越绕。尤其是引入了机器学习模型之后因子标准化、滚动训练、预测结果对齐、换仓调仓这些事全部堆在一起每次改一个参数都要动好几个模块最后连自己都看不懂自己写的代码。后来一个做量化研究的朋友跟我讲别自己造轮子了去试一下微软开源的Qlib你想要的这些东西它基本都有。这是我和Qlib的第一次接触。Qlib是一个AI量化投资平台核心目标是把量化研究里从数据处理、因子计算、模型训练到回测分析这一整条链路全部标准化。它不是一个只能跑策略的回测框架而是更偏AI方向支持机器学习、深度学习模型的训练和评估。官方内置了Alpha158和Alpha360因子集、LightGBM/GRU等基准模型而且提供了一套配置化的运行机制你只需要写一个YAML文件就能把数据、模型、回测串起来跑完一遍全流程。我当时是被这么几个点打动才决定认真上手的第一它把“因子库构建”这件事做成了标准流程不用我每次手工处理缺失值、做行业中性化第二它内置了几十种模型从经典机器学习到深度学习都有方便做基准对比这是自己造轮子很难做到的第三它支持把研究的整个流程用配置文件固定下来换参数重新实验非常方便相当于把不可复现的“研究草稿纸”变成统一流水线。这篇文章主要写给三类人准备入行AI量化但还没找到合适框架的人已经在用Qlib但只跑通了模板流程、对内部机制一知半解的人以及想评估“要不要把自研回测框架迁移到Qlib”的开发者。我会把这两周上手Qlib的真实过程、踩过的坑、对核心机制的理解全部记录下来包括安装环境时最容易卡住的地方、数据目录的底层逻辑、配置文件每一段是干什么的以及一次完整回测从启动到出结果的全程拆解。2. 环境安装README明明只有三行我却折腾了整个下午Qlib官方README给的安装命令确实简单pip install pyqlib一句就能装上基础版本。但真到自己动手的时候坑远比想象中多。我先说我的环境Windows 10系统Python 3.9平时用Anaconda管理虚拟环境。装的时候第一条遇到的是Python版本兼容问题Qlib官方推荐Python 3.8以上但有些旧版本依赖包跟Python 3.11以上不一定兼容所以如果你用的是最新的Python 3.12或者3.13建议先不要硬试老老实实建一个3.9或者3.10的conda环境。2.1 安装命令背后的隐藏依赖pip install pyqlib并不是一个“装完即用”的安装过程它会附带装很多机器学习相关依赖numpy、pandas、scikit-learn、lightgbm、xgboost、torch、tables、sacred等。其中比较麻烦的是tables这个包它底层依赖HDF5的C库在Windows上经常出现编译错误。我当时遇到的报错是error: Microsoft Visual C 14.0 is required这就是典型的C扩展编译问题。我的解决思路依赖两个方法都不需要太折腾第一先用conda装tables再装pyqlib。因为conda的预编译包对Windows支持很友好装好后pip会识别到已满足依赖不再尝试编译。conda create -n qlib python3.9 conda activate qlib conda install -c conda-forge tables pip install pyqlib第二如果只想用机器学习模型而不需要PyTorch的深度学习模型可以装精简版。但这里要特别提醒一句很多内置模型和示例脚本默认依赖torch这个包你要是只装CPU版torch还好装不上torch的话后面跑示例代码会各种ModuleNotFoundError。所以老老实实把torch装上更稳妥pip install torch --index-url https://download.pytorch.org/whl/cpu2.2 Windows用户绕不开的问题gplearn和部分C扩展Qlib里Alpha158因子集有一个衍生因子的功能依赖于gplearn这个库这个库同样在Windows下编译有坑。第一次我跑模型训练时直接报了ModuleNotFoundError: No module named gplearn。我一开始以为这个库不是必需的功能后来发现想用完整的Alpha158因子集还真绕不开。解决方式是先升级一下scikit-learn的版本因为gplearn对某些sklearn版本不兼容再把gplearn装进环境pip install scikit-learn -U pip install cython pip install gplearn上面是我的实际操作过程整体下来我的建议是如果你想快速上手直接参考下面的完整环境安装流程这是我踩完所有坑后总结的最顺路径conda create -n qlib python3.9 -y conda activate qlib conda install -c conda-forge tables -y pip install --upgrade pip pip install pyqlib pip install torch --index-url https://download.pytorch.org/whl/cpu pip install gplearn装完以后建议跑一句python -c import qlib; print(qlib.__version__)验证一下如果正常输出版本号环境这一步就过了。如果报错信息是ImportError: DLL load failed while importing qlib大概率是numpy和pandas版本混了可以用pip install numpy1.24.4 pandas2.0.3固定版本试试。3. 数据准备是第一个被低估的门槛目录、下载和qdump_bin真正开始用Qlib的时候我才发现模型训练前的数据准备比我预想的复杂。Qlib用的不是普通CSV而是它自己的二进制数据格式。这样做的好处是读取速度快、内存占用低而且在做因子计算的时候不用每次去解析文本文件。但这个设计也让新手困惑我明明有行情数据为什么不能直接传个DataFrame进去3.1 Qlib数据的目录结构长什么样当你用官方脚本下载完数据后会得到一个qdata目录里面最基本的结构是qdata/ ├── features/ │ ├── csv/ │ │ ├── sh600000.csv │ │ ├── sh600004.csv │ │ └── ... │ └── bin/ │ └── sh600000/ │ └── all.pkl ├── calendars/ │ ├── day.txt │ └── ... └── instruments/ ├── all.txt └── ...features/csv是原始K线数据features/bin是转换后的二进制数据calendars是日历文件instruments是股票列表和交易时间段信息。Qlib训练时默认读取的是bin目录csv目录只是用来存放原始数据的。如果你用的是官方下载脚本执行起来非常简单python scripts/get_data.py qlib_data --target_dir ~/.qlib/qlib_data/cn_data --region cn但这里我踩了一个大坑从GitHub下载的打包数据文件有时候会非常慢或者直接卡住不动。原因是国内网络访问AWS S3存储的限速问题。这个问题的处理办法有两个选一个就能解决一是配置代理环境变量再执行下载脚本二是手动从网盘找已经打包好的Qlib数据包解压后放到对应目录。手动放数据要注意一点目录结构必须是qlib_data/cn_data这一层里面才是features、calendars、instruments三个子目录。如果你解压多套了一层目录Qlib会因为找不到日历文件而直接报错。3.2 怎么把自定义数据导入成Qlib格式如果你不想用A股数据想用期货、加密货币或者自己清洗过的数据那就需要走自定义数据导入流程。Qlib提供了一个dump_bin工具它能把标准格式的CSV文件转换成二进制数据。这里我简单把过程写一下后面有需要的人可以按这个走第一步准备好CSV文件每一列至少包含date、symbol、open、high、low、close、volume这些字段date格式建议是yyyy-mm-dd。第二步写一个映射文件告诉Qlib哪些列对应哪些字段。第三步执行dump_bin命令。不过如果你第一周只是想跑通示例流程不建议一上来就搞自定义数据。先用官方A股数据把流程跑通等熟悉了内部机制再换自己的数据这样排查问题会简单很多。我自己就是一开始想直接导入自己的数据结果绕了一大圈最后还是老老实实换回官方数据才把第一个模型跑通。4. 搞懂Qlib的“流水线”设计DataHandler、Dataset、Model、RecordQlib的核心思想其实非常像工厂里的流水线。每一段做一件事前一段的输出是后一段的输入最终出来的是回测结果和评估指标。当你理解了这条流水线上的每一个环节后看Qlib的任何示例代码都会觉得“原来是这么回事”。4.1 我先用一句话概括每个组件的职责用一个通俗的类比来说DataHandler像是一个食材处理工它把原始行情和因子数据加工成模型能吃的“干净食材”Dataset则是一个配菜员它按照你的要求把食材切好拼盘决定训练集、验证集、测试集怎么划分Model是掌勺的厨师它拿拼好的菜去学习和预测Record是记录员它把训练过程、预测结果、回测指标全部记录下来给你看。下面这张表可以帮你快速理解每个组件的位置组件职责典型配置关键词DataHandler加载原始数据、补充缺失值、计算因子class,start_time,end_timeDataset切分数据集、批量生成样本class,segments,step_lenModel定义训练用的模型结构和训练方式class,kwargs,lossRecord记录模型训练和回测过程class,model,dataset4.2 为什么配置驱动比代码驱动更适合研究场景Qlib的示例大部分是通过YAML配置文件来驱动整个流程的执行命令是qrun configs/benchmarks/LightGBM/workflow_config_lightgbm_alpha158.yaml这条命令会读取YAML里的配置创建对应的组件实例并依次跑完整条流水线。我第一次看到这个YAML时一度觉得“这不就是一堆字符串吗哪有直接写代码方便”但深入使用后发现这个设计在量化研究场景下有巨大优势每次实验的配置全部固化在一个文件里不会出现“代码改了一行忘了改哪里”的情况跑对比实验时只需要把YAML里的模型名称换掉其他不用动提交实验记录时直接附带YAML文件同事一眼就能看懂你的实验设置。配置驱动的另一个好处是降低了框架本身的耦合度。你想换一个因子集只需改DataHandler的类名想换模型只需改Model的类名和参数想调整回测时间段只需改segments配置。这种“插拔式”的设计让整个研究过程像拼积木一样清晰。5. 完整跑通一次LightGBM回测每一步都拆开看讲完概念之后我觉得最有价值的还是完整跑通一次回测边跑边解释每段配置到底在干什么。下面是我用官方默认的LightGBM Alpha158配置跑通的完整过程。5.1 先下载并确认数据完整我用的数据目录是~/.qlib/qlib_data/cn_data。下载完成后我先做了个快速检查确认calendars/day.txt里最后一行日期能对上最近的交易日tail -5 ~/.qlib/qlib_data/cn_data/calendars/day.txt这一步虽然简单但能提前避免一个特恶心的报错当你的数据日历文件和回测配置里的end_time不匹配时Qlib会直接说没有数据可跑但不会告诉你具体是哪一天的数据缺失。5.2 打开YAML配置文件逐段解读我用的是configs/benchmarks/LightGBM/workflow_config_lightgbm_alpha158.yaml核心内容大致拆解如下qlib_init: provider: local # 从本地读取数据 region: cn # 市场区域中国A股 dataset_cache: ~/.qlib/qlib_data/cache # 缓存目录这段是初始化配置。它告诉Qlib读取本地A股数据并指定一个缓存目录来存放因子计算过程中的中间结果。如果你在代码里直接qinit(providerlocal, regioncn)也是等价的。接下来是数据加工部分data_handler_config: start_time: 2008-01-01 end_time: 2020-12-31 infer_process_kwargs: - class: RobustZScoreNorm - class: Fillna learn_process_kwargs: - class: DropnaLabel - class: CSZScoreNorm - class: Fillna这个配置说明了对因子数据的处理流程训练时先做ZScore标准化再补缺失值推理时用鲁棒ZScore防止极端值干扰。很多人一开始不理解为啥要区分learn_process和infer_process原因是训练时可以用全量数据的均值方差做标准化但在测试集上做推理时不能用未来数据的信息所以要做独立的标准化处理。模型部分model: 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 early_stopping_rounds: 50这里指定的是LightGBM模型和它的一组超参数。说实话这些超参数不是手调的而是Qlib官方用某种搜索算法调出来的结果直接拿来用就能得到一个相对合理的基准效果。回测和记录部分record: - class: SignalRecord kwargs: model: model dataset: dataset label_col: LABEL0 - class: SigAnaRecord kwargs: ana_long_short: true ana_long_only: true - class: PortAnaRecord kwargs: config: port_analysis_config这里依次做了三件事保存预测信号、分析预测信号的质量比如IC值、用预测信号做策略回测并输出资金曲线和收益指标。5.3 跑起来之后输出的关键指标怎么读执行完Qrun命令后终端日志里会出现一堆指标。我重点看这几个指标含义我的实际参考经验IC预测值与未来收益的相关性绝对值超过0.03就可初步使用Rank IC秩相关系数比IC更稳健能避免极端值干扰Annualized Return策略年化收益需要结合回撤一起看单看收益没意义Max Drawdown最大回撤超过20%要警惕策略稳定性Information Ratio信息比率是超额收益与跟踪误差的比值越大越好我跑出来的结果里RankIC大概在0.04左右年化收益在14%附近最大回撤约9%。作为没有精细调参的基准结果这个水平是我能接受的。关键是这套流程从数据到结果全程只靠一条命令接下来不管换成GRU、MLP还是Transformer模型操作成本都很低。6. 踩坑记录这些问题官方文档不会主动告诉你上手Qlib的这两周里我遇到了不少奇奇怪怪的问题。有些在GitHub Issues里找到了解答有些是自己一点点试出来的还有一些现在提起来都头大。我把最典型的几个整理在这里希望能帮你少走弯路。6.1 因子缓存不更新改了因子却一直跑出旧结果有一次我改了自定义因子的计算公式重新跑流程后结果却跟之前一模一样。排查了很久终于发现是Qlib的dataset缓存机制在捣鬼。它会默认把处理好的数据缓存到本地目录如果你改了因子逻辑但没有清缓存下一次运行时Qlib直接读缓存根本不会重新计算。解决方法是把配置文件里的dataset_cache临时指向一个新的空目录或者直接删除旧的缓存目录。我自己习惯用的是每次改完因子逻辑后删掉~/.qlib/qlib_data/cache简单粗暴但是有效。6.2 交易日历不匹配导致的“无数据”错误这个坑非常隐蔽。我在调试一个策略时把回测的end_time设成了2023年底但下载的数据包只更新到2021年。运行Qlib时它不会直接报“数据没更新”而是说没有足够的预测样本或者提示某个股票在这个时间范围没数据。这个错误信息很容易让你误以为是代码写错了实际上只是数据没跟上时间设置。我的排查经验是先看calendars/day.txt的日期范围再对照配置文件里的start_time和end_time确保两边时间对得上。6.3 Windows下多进程训练崩溃加一行if __name__防护在Windows上运行WSL或者VSCode调试时我遇到过好几次RuntimeError: An attempt has been made to start a new process before the current process has finished its bootstrapping phase。这个问题本质上是Windows下多进程启动机制和Linux不同导致的分叉错误。解决方法是在入口脚本加上if __name__ __main__: qinit(providerlocal, regioncn) # 其他主要逻辑这行在Linux下有没有都行但在Windows下不加就是不行。如果你在Windows上总是遇到奇怪的训练中断先看看是不是这个原因。6.4 内存占用过高小内存机器怎么优化Alpha158因子集加上全市场3000多只股票的数据在模型训练前Dataset会把所有样本一次性加载进内存。我的电脑只有16G内存跑的时候经常卡到鼠标都不动。后来把配置里的step_len调大、把batch切分方式调整了一下问题缓解了不少。具体来说有两个控制内存的配置位置一个是DataHandler里的infer_process_kwargs可以减少同时处理的股票数量另一个是Dataset的segment设置把训练集样本量限制在一个合理范围。如果你的数据本身覆盖多年可以先用start_time把时间起点往后再挪一点比如从2015年开始等验证完流程再放宽时间范围。7. 接下来可以怎么继续深入我的个人扩展方向把这套官方LightGBM流程跑通之后Qlib对我来说算是“入门”了。但入门只是开始怎么把它用到自己的实际研究里才是真正值得思考的部分。我这里分享三个我确定会继续深入的方向也可以当作你下一步的参考。7.1 替换成自己的因子集官方Alpha158是一个全市场通用的价量因子集但它不一定适合所有场景。如果你的策略依赖基本面数据、分析师预期或者另类数据就需要在DataHandler阶段把这些自定义因子注入进去。Qlib提供了Alpha158和Alpha360两个现成类也支持继承DataHandler重写get_feature_config方法加入自己的因子列。我准备下一步从公司的财务数据出发先用get_fea函数把ROE、营收增速这类因子拉进来再做标准化和缺失值填充最后合入Qlib的因子管道。这个过程核心要理解的是Qlib会把DataHandler输出的一整张表当作因子数据所以只要你的表格式对齐股票代码、日期、因子列它能自动处理后续的一切。7.2 换模型跑对比实验Qlib内置了很多基准模型LightGBM、XGBoost、CatBoost、GRU、LSTM、Transformer甚至一些强化学习模型。想换个模型做对比实验非常容易——新建一个YAML配置文件把model.class和module_path改了就可以。model: class: GRU module_path: qlib.contrib.model.rnn kwargs: d_feat: 158 hidden_size: 64 num_layers: 2唯一要注意的是不同模型对输入格式有不同要求。Alpha158因子集是按横截面组织的表格数据LightGBM可以直接吃但GRU这类时序模型需要的是按股票切片的时间序列所以Qlib里对应使用了TSDatasetH这类专门处理时序样本的Dataset。换模型之前先确认Dataset和模型是否匹配不然会报shape不对的错误。7.3 用qrun和NNI做超参数自动搜索官方配置文件里那组LightGBM的超参数其实是用NNI微软开源的自动调参工具调出来的。Qlib和NNI的结合很自然你只需要在配置里声明一个tuner部分指定搜索空间和优化目标比如最大化RankICQlib就会在每次迭代中自动调整超参数并重新训练模型。我准备在下一步把模型从LightGBM换成CatBoost然后用NNI去做一轮超参搜索看看能不能把RankIC从0.04提升到接近0.06的水平。这个方向目前还在摸索中等有了结果再单独写一篇记录。7.4 最后的实战建议如果你是一个刚开始接触Qlib的人我建议你按照这样的顺序来先用官方数据跑通一次完整流程感受一下YAML配置的驱动方式然后尝试改一个参数比如把LightGBM的num_leaves调大看看结果怎么变接着换一个模型跑同样的数据对比不同模型的差异最后才是接入自己的数据。前两步半天就能完成但会帮你建立对整套框架的直观认识。踩过一次“数据没更新导致的无数据报错”、经历过一次“改因子忘记清缓存”之后你对Qlib的理解会比看十篇文档都深。我自己就是在这种反复折腾的过程中才慢慢把Qlib的流水线机制、数据组织和配置体系真正串起来的。
返回列表