尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

LightGBM量化选股实战:从特征工程到回测的完整指南

LightGBM量化选股实战:从特征工程到回测的完整指南 简介机器学习在量化投资中的应用日益广泛其中梯度提升树GBDT凭借强大的非线性拟合能力与稳健的特征交互学习机制成为多因子选股模型升级的重要方向。当传统线性模型难以捕捉量价因子间的复杂关系时LightGBM通过直方图算法与Leaf-wise生长策略在保证训练效率的同时显著提升预测稳定性。围绕真实量化场景技术核心在于特征工程从量价、资金流、基本面等维度构造有效因子并辅以滚动时间窗验证与严格的样本划分避免数据泄露。在回测环节合理的交易成本假设与信号生成逻辑是评估策略边界的关键。本文基于A股市场实践系统梳理LightGBM在量化选股中的完整建模流程涵盖因子筛选、参数调优、回测复盘及实盘化避坑要点为机器学习驱动的量化策略落地提供工程化参考。1. 为什么偏偏是LightGBM量化选股的模型选型逻辑先聊聊我做这个项目的起因。做量化投资的朋友应该都有同感传统多因子模型做到后期IC上不去、换手率降不下来、收益曲线平平无奇瓶颈感特别强。我自己的实盘策略在2022年上半年就卡在这个状态线性回归打分选股的策略在震荡市里被反复打脸。后来我把目光投向梯度提升树家族在XGBoost、LightGBM、CatBoost三个主流框架里折腾了大半个月最后把LightGBM定为主模型。现在回看这个选择本身的价值不亚于后面的策略逻辑。1.1 传统多因子模型的瓶颈传统多因子模型的核心假设是因子与未来收益之间呈线性关系或者至少是单调关系。但A股市场里大量量价因子实际上是非线性相关的而且因子与因子之间也有大量交叉效应。举个实际例子低换手率因子单独看有效但叠加高动量因子之后效果反而会反转。线性模型处理这种交互项需要手动构造而手动构造的因子往往只覆盖了少数已知组合大量潜在交互关系被遗漏。另一个问题是缺失值和异常值的处理。基本面因子天然有缺失量价因子天然有极值传统线性模型对这些情况非常敏感需要做大量的预处理工作。我见过不少同行在因子标准化和去极值上花的时间比建模本身还多。而树模型天然对缺失值有处理逻辑对异常值也有一定鲁棒性在数据质量参差不齐的量化场景里这个特性非常实用。1.2 GBDT在时序截面数据上的优势如果把沪深300成分股过去5年的数据切出来你会发现每个截面期大约有300个样本一年大约有240个截面整体训练样本量在几万这个量级。这个样本量下GBDT类模型的拟合能力非常强而且不需要做特征缩放数值型和类别型特征可以混用。关键点在于量化选股问题的本质是一个截面排序问题——在每个调仓日对所有股票的未来收益进行排序预测。LightGBM对每个样本输出的是一个连续分值这个分值可以直接用来排序。用同一套特征、同一套标签我把线性模型和LightGBM的IC均值做过对比线性模型IC均值大概在0.03左右LightGBM在相同特征下能做到0.055左右提升幅度接近一倍。1.3 与XGBoost、CatBoost的取舍我三个框架都跑了基准测试。XGBoost精度和LightGBM差不多但训练速度慢不少尤其是参数网格搜索的时候特别明显。我做过一次对比100轮网格搜索XGBoost跑了大概4小时LightGBM只用了40分钟。原因是LightGBM用了基于直方图的算法把连续特征离散化成直方图分桶在内存占用和计算效率上优势明显。CatBoost在处理类别特征上有优势但A股量化场景里类别特征很少行业代码算一个而且CatBoost的训练速度在三者中最慢。所以最终我选了LightGBM——在同样的特征工程下它的训练效率最高调参空间灵活社区资料也最丰富。2. 数据准备与特征工程策略90%的工作量在这里很多人一上来就调模型参数这是本末倒置。我在这个项目里踩过最大的坑就在这里——最初版本的数据没有做前复权处理导致所有涉及价格的因子全部失真回测结果虚高得吓人。千万别信那种年化收益100%以上的回测曲线大概率是数据没处理好。2.1 数据源选择与清洗数据源方面我用的是Tushare Pro的日线行情加上聚宽的基本面数据做补充。如果你有Wind或者Choice的权限当然更好但对个人研究者来说Tushare Plus和AkShare的组合完全够用。数据清洗有几个必做的步骤复权处理统一使用前复权数据否则除权除息日会出现价格跳空所有基于价格的因子全部出错。剔除ST和退市整理期股票这类股票的涨跌幅限制和正常股票不同而且存在流动性风险量化策略一般不碰。剔除上市不足60个交易日的次新股次新股波动特征特殊容易被模型当作高收益特征学进去。处理停牌股票调仓日停牌的股票无法交易需要在生成信号时就排除不能等调仓日再去判断。我还额外加了一道截面去极值的工序。虽然LightGBM对异常值有一定鲁棒性但极端值依然会影响直方图的分桶逻辑。我用的方法是MAD中位数绝对偏差法把偏离中位数3倍MAD以上的值拉回到边界值这个处理对量价因子的稳定性提升效果明显。2.2 因子构造从基础行情到衍生特征特征工程是这项目的重头戏。我从三个维度构造因子量价类因子包括过去5日、10日、20日的收益率动量、波动率、换手率均值、成交量变化率、最高价/收盘价比值等。这类因子捕捉的是短期市场行为特征是LightGBM最擅长学习的模式。资金流类因子用大单净流入占比、主力资金流向、北向资金持股变化等构造。这类因子在震荡市里特别有区分度——同样是股价横盘主力在吸筹还是在出货后市的走势完全不同。基本面类因子包括市盈率分位数、市净率、ROE变化率、营收增速等。基本面因子更新频率低但有效期长可以起到稳定器的作用。因子数量我控制在60个左右。不是越多越好——因子太多会增加过拟合风险而且LightGBM的特征筛选能力虽然强但输入的噪声特征越多训练时间越长。我的经验是先用单因子IC检验筛掉一批IC均值低于0.01且ICIR低于0.1的因子再送入模型。2.3 标签定义与样本划分的讲究标签定义直接决定策略的语义。很多人做量化选股喜欢用“未来5日收益率是否为正”作为二分类标签但我觉得A股市场短期噪声太大二分类标签的信息量不够。我的做法是用未来20日的收益率然后在每个截面期按收益率排序取前30%作为正样本后30%作为负样本中间40%的样本直接丢弃。这样处理的好处是拉大了正负样本的区分度模型更容易学到边界特征。样本划分上要特别小心千万不能用随机划分否则就是典型的数据泄露。时间序列数据必须按时间顺序划分前60%作为训练集中间20%作为验证集最后20%作为测试集。我见过有人把同一时间截面的数据一部分放训练集一部分放测试集回测结果漂亮得离谱实际上模型早就“见过”未来数据了。3. LightGBM模型构建与调参让模型真正学会选股模型构建这一步我建议先把逻辑跑通再做细致调参。很多人一上来就上贝叶斯优化搜索空间大到离谱跑了几天几夜结果是过拟合。我的做法是先固定一组大致合理的参数把整个流程跑通然后再做小范围的网格搜索。3.1 训练集与验证集的切分要避开的坑时间序列交叉验证和普通K折交叉验证有一个本质区别训练集的时间一定在验证集之前。我在项目中用的是滚动时间窗交叉验证比如用2018年1月到2020年12月的数据训练用2021年1月到2021年6月验证然后把训练集扩展到2021年6月用2021年7月到2021年12月验证以此类推共做4次滚动验证。这样做的好处是更接近实盘场景。模型在实盘中面对的都是“未来数据”所以验证时也必须保证时间顺序正确。我见过有些网上示例直接用TimeSeriesSplit做K折但K折内部的每组也是随机切分的仍然存在信息穿越的问题。最稳妥的做法是自己手写滚动窗口逻辑不要依赖现成的跨验证工具。3.2 核心超参数解读与初始值LightGBM的超参数很多但真正需要重点调的就那几个参数作用我的初始值调整心得num_leaves树的复杂度叶子节点数31这个参数对模型效果影响最大一般控制在16-64之间learning_rate学习率0.05我习惯用较小的学习率搭配更多的树n_estimators树的棵树1000配合早停配合早停使用不用手动设太大min_data_in_leaf叶子节点最少样本数100这个参数对防止过拟合很重要尤其是样本量不大的时候feature_fraction每棵树随机选取的特征比例0.8降低特征间的相关性影响bagging_fraction每棵树随机选取的样本比例0.8增加随机性防止过拟合lambda_l2L2正则化系数1.0量化场景中样本同质性高正则化系数要稍微大一些我给的这些参数不是凭感觉定的。量化选股的训练样本虽然有几万条但股票之间的相关性很高同一行业、同一风格的股票特征高度相似如果min_data_in_leaf设置太小模型会把个别股票的特异模式当作全局规律学进去这在样本外检验时就会暴露。训练代码我用的是Python标准写法import lightgbm as lgb from sklearn.model_selection import TimeSeriesSplit lgb_params { objective: binary, metric: auc, learning_rate: 0.05, num_leaves: 31, min_data_in_leaf: 100, feature_fraction: 0.8, bagging_fraction: 0.8, bagging_freq: 1, lambda_l2: 1.0, verbose: -1, seed: 42 } # 滚动时间窗训练 for train_idx, val_idx in tscv.split(X): X_train, X_val X.iloc[train_idx], X.iloc[val_idx] y_train, y_val y.iloc[train_idx], y.iloc[val_idx] d_train lgb.Dataset(X_train, labely_train) d_val lgb.Dataset(X_val, labely_val) model lgb.train( lgb_params, d_train, num_boost_round1000, valid_sets[d_val], callbacks[lgb.early_stopping(50)] )3.3 早停与特征重要性分析早停的轮次我设置了50。也就是说如果连续50轮验证集AUC没有提升就停止训练取验证集效果最好的那棵树。训练完之后我会用model.feature_importance()查看特征重要性这一步的价值被很多人低估了。特征重要性排序不仅能告诉我们模型主要依赖哪些因子还能反过来检验特征工程是否合理。我的经验是因子重要性排名前10的特征应该覆盖量价类、资金流类、基本面类至少两个维度如果某个维度完全没进前10说明该维度的因子构造有问题或者这些因子在当前市场环境下处在失效期。做特征筛选的时候要注意一个常见的坑不要一次性把所有重要性低的特征全部删除然后重新训练。因为树模型存在特征替代关系删除一个特征后与其相关的特征可能会顶上模型性能不一定下降。正确做法是每轮删除重要性最低的5到10个特征重新训练对比验证集AUC直到AUC出现明显下滑。4. 回测框架搭建从预测概率到交易信号模型训练好了预测分数也出来了但距离策略落地还差最关键的一步——回测。我见很多人直接在Notebook里写个循环把每天预测分数最高的前20只股票算收益这就完事了。这样做的回测结果和真实策略的收益差距能差出一倍以上。4.1 换仓周期与交易成本假设首先要明确换仓周期。我设计的是月频调仓每月最后一个交易日收盘后用最新数据预测未来20日的收益率生成下个月的持仓组合。为什么不用日频或者周频因为LightGBM模型学习的主要是中期趋势特征日频换仓会让交易成本吃掉大部分超额收益得不偿失。交易成本假设是回测里最敏感的参数。我用的成本模型包括几部分佣金按成交金额的万2.5计算最低5元。印花税卖出时按成交金额的千1计算现在A股印花税已调整需要实时更新。滑点按成交金额的千1计算用于模拟实际成交价格和信号价格之间的差异。冲击成本按成交金额的千1计算用于模拟大单交易对市场价格的冲击。综合下来单向交易成本大约千3.5换一次仓双边就是千7。这个假设可能偏保守但保守一点没有坏处——太乐观的成本假设会让你以为自己找到了印钞机实际上连手续费都跑不赢。4.2 信号生成与持仓权重分配按预测得分排序后我选择得分最高的前10%股票作为持仓等权配置权重。等权配置是量化策略最常用的方法相较于市值加权或打分加权等权配置更简单且不容易过度集中在某几只股票上。调仓规则上要注意细节为了保证回测的真实性调仓当天用收盘价成交并且用当天收盘后能拿到的数据来生成信号。这里有一个隐形数据泄露的坑——如果T日调仓用T日的收盘数据计算因子然后以T日收盘价成交这就已经用到了未来信息。正确做法是用T-1日收盘后的数据计算因子和信号在T日按开盘价成交或者更保守一点在T日按收盘价成交但使用T-1日生成信号。我采用的是后者T-1日数据生成信号T日收盘价成交。这个规则的收益会比T日数据生成信号低一些但更接近实际可执行的情况。核心回测循环的代码如下# 伪代码回测主循环 for trade_date in rebalance_dates: # 用 trade_date 前一天的因子数据预测 features get_features(up_to_datetrade_date - 1) scores model.predict(features) # 选前10%股票 threshold np.percentile(scores, 90) target_stocks features[scores threshold].index.tolist() # 计算调仓卖出不在目标组合的持仓买入新入选的股票 sell_list current_positions - set(target_stocks) buy_list set(target_stocks) - current_positions # 按T日收盘价模拟成交 executed_price get_close_price(trade_date) transaction_cost calculate_cost(executed_price, buy_list, sell_list) # 更新持仓与净值 current_positions target_stocks nav_curve.append(update_nav(current_positions, trade_date))4.3 回测指标计算与绩效归因回测结果不能只看年化收益率还需要看几个核心指标夏普比率、最大回撤、卡玛比率、胜率、盈亏比和换手率。以我的策略回测结果为例指标数值年化收益率18.7%年化波动率16.2%夏普比率1.15最大回撤12.3%卡玛比率1.52月胜率62.5%年化换手率约9倍这个结果不算惊艳但在A股全市场月度选股策略里算合格水平。更关键的是波动率和最大回撤控制住了说明模型输出的组合在风险维度上是可控的。绩效归因方面我会把组合收益拆成几个部分市场Beta收益用沪深300或中证500代表、行业配置收益组合行业分布与基准的差异、个股选择收益行业内选股的超额部分。这个拆解能帮我看清楚策略的超额收益到底来自哪里——是猜对了市场风格还是真的在行业内选出了好股票。5. 回测结果复盘收益曲线背后的真相回测走到这里策略已经形态完整了。但一切只是开始真正的功夫在于怎么解读回测结果。我对最终的收益曲线做了整整一周的复盘分了三层来看。5.1 基准对比与超额收益拆解我的基准是中证500等权指数。为什么不用沪深300因为策略的选股池是全部A股中小市值股票的占比很高中证500的市值风格和策略更匹配。基准不对后面所有的超额收益分析都是在自欺欺人。策略组合净值曲线在部分月份跑赢基准在部分月份跑输。我统计了相对基准的月胜率大约61%。这个数字看起来不错但要注意即使策略的长期超额收益为正中间也会有一段连续跑输基准的时间。我的策略在2022年1月到4月连续四个月跑输基准最大超额回撤达到7.2%。那段时间我差点怀疑整个模型训练出了问题。应对思路有两条一是接受策略在特定市场环境下阶段性失效这是任何量化策略都无法避免的二是通过叠加风格约束、行业中性化等手段降低策略在不利环境中的暴露度。我后来采用的是第二种思路——在选股时加入行业均衡约束单行业持仓占比不超过基准行业权重的1.5倍。5.2 回撤期分析模型失效还是市场风格切换2022年1月到4月那段回撤期我花了很多时间分析原因。把那段时期的预测得分和真实收益对比后我发现模型并没有完全失效——它的IC仍然是正的但幅度大幅下降从正常的0.05左右下降到0.015左右。这说明模型在这段时期依然有选股能力只是能力被大幅削弱。进一步分析后发现当时市场处于典型的防御性行情高股息、低波动风格占优而我的因子库中动量因子和资金流因子权重太高这两类因子在风格切换期会持续输出错误信号。这让我做了一个重要的调整增加低波动因子和股息率因子的权重同时把因子库的有效因子筛选频率从一年一次提升到季度一次。调整之后策略在2022年下半年的表现就恢复正常了。这个复盘给了我一个很重要的教训量化模型不是造好后就能一劳永逸的市场风格变化时需要对特征库和模型进行再调整。具体做法是每个季度末做一次因子有效性滚动扫描淘汰IC连续3个月为负的因子补充新的候选因子。5.3 IC/IR分析验证因子有效性IC信息系数分析是检验策略预测能力的核心方法。我计算了每日IC即当日全市场股票的预测得分与未来20日收益的Spearman秩相关系数。策略的日度IC均值在0.045左右ICIRIC均值除以IC标准差约为0.35。这个ICIR水平属于什么档次业界一般把ICIR大于0.5视为优秀策略0.3以上算合格。我的策略ICIR只有0.35属于合格但不算突出。真正让人放心的是IC的稳定性——在月度滚动窗口下IC为正的月份占比达到78%说明模型不是靠少数几个月的爆发表现撑起整个曲线的。IC衰减分析也是必做的一步把预测得分与未来5日、10日、20日、40日的收益分别做相关分析看预测能力的衰减速度。我的模型在5日和10日维度上衰减很快20日和40日维度上依然保持稳定这验证了月频调仓的正确性——模型学到的确实是中期趋势特征不是短期动量。6. 实盘化之前的几个坑与我的最后建议回测通过并不等于可以直接实盘。我在过去几年做过几个从回测到实盘的策略中间踩过的坑能写满一整页纸。LightGBM选股策略也不例外有几个问题在准备实盘之前必须想清楚。6.1 数据泄露的隐形路径量化策略最怕的不是模型效果差而是回测结果虚高让你误以为模型效果很好。LightGBM选股策略中常见的数据泄露路径包括用T日因子预测T日收益并模拟T日成交这是时间对齐错误。用全样本做了因子标准化再用标准化参数处理历史数据这是全局统计量泄露。正确做法是用滚动窗口计算标准化参数每次只用过去数据。回测筛选出来的最优参数再拿到回测集上跑一遍得到漂亮的曲线这是参数过拟合。第三点最隐蔽也最常见。很多人做参数寻优之后会用最优参数在全样本上重新跑一次回测然后把这个回测曲线当作策略的真实表现。这个做法在逻辑上是有问题的——你已经用过全样本的信息来选择参数了再拿全样本做验证等于用考题答案做模拟考。正确做法是参数寻优只在训练集和验证集上进行测试集只能跑一次。6.2 交易成本的敏感性测试回测做完之后我建议做一次交易成本的敏感性测试——把交易成本假设上调一倍和下调一半分别观察策略的年化收益和夏普比率变化。如果上调一倍后策略依然盈利说明策略有足够的安全边际如果上调后策略直接亏损说明策略的真实盈利能力很脆弱。我的策略在成本从千3.5上调到千7的情况下年化收益从18.7%下降到11.2%夏普从1.15下降到0.78但依然是正的。这让我对策略的实战执行有了一些信心。如果你的策略在成本敏感性测试中表现不佳优先优化换手率而不是试图找更低成本的券商。6.3 模型更新的频率与节奏模型不是训练完就结束的。我的做法是每个月调仓前用最新的最近12个月数据重新训练一次模型并使用最近3个月的数据作为验证集做早停判断。这样既能保证模型及时适应市场环境变化又不会因为更新太频繁导致模型不稳定。回测复盘时我发现如果模型超过3个月不更新策略的IC均值会从0.045下降到0.03附近。这说明市场规律确实在动态变化定期更新模型参数和重新训练是保持策略生命力的关键。我个人在实际操作中还有一个体会LightGBM模型在量化选股中的价值在于它能自动学习因子间的复杂交互关系但这不意味着特征工程可以简化。恰恰相反正因为模型能学习复杂关系特征工程的质量更直接决定了模型的上限。把时间花在因子构造和数据分析上比花在参数调优上更值得。最后再分享一个实际的小技巧每次训练完模型把特征重要性排序和验证集的IC按季度记录下来时间长了你会对市场风格的切换有一个很直观的感知。比如动量因子重要性上升、低波因子重要性下降往往意味着市场正在从防守切换到进攻。这种感知带来的预判能力是单纯依赖回测曲线很难获得的。策略代码、因子库和回测框架我已经整理成完整的工程文件。有想进一步交流细节的朋友欢迎在评论区留下具体的问题我会针对大家关心的方向再做深入拆解。本文还有配套的精品资源点击获取
返回列表