尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

gplearn实战:用遗传规划自动化因子挖掘与回测优化

gplearn实战:用遗传规划自动化因子挖掘与回测优化 简介量化投资中因子挖掘是策略研发的关键环节传统人工方式效率低且易受主观经验限制。遗传规划GP作为一种启发式搜索算法能够自动组合数学算子与原始数据特征生成可解释的因子表达式。通过引入gplearn库研究者可以在sklearn生态中便捷地实现因子自动搜索并通过自定义评估函数将金融语义指标如IC、收益回撤比嵌入进化过程。本文系统讲解了时序因子与选股因子的生成模块设计、函数集定制、5日IC优化、因子回测框架以及可视化审计工具并结合实战踩坑经验帮助量化研究者构建从因子挖掘到回测验证的高效流水线。关注遗传规划在量化投研中的应用价值掌握gplearn的核心参数与工程化封装是提升因子研发效率的关键路径。 从我个人的实际经验来说做量化投研的人尤其是中小型团队或者独立研究者最容易卡住的地方不是策略逻辑而是因子挖掘的效率。人工去手工计算各种技术指标、财务指标的组合再一个个做单因子回测那个工作量我说句实话足够把一个研究员的热情消磨掉大半。所以当我第一次把遗传规划算法gplearn引入到因子挖掘流程里时最大的感受就是这玩意儿不是在帮你做因子它是在帮你做因子的大脑。它自动去组合、筛选、进化出那些你根本想不到的表达式而你要做的是把它的产出用正确的姿势接进你的回测和评估框架里。这套系统我当时整理的版本核心分了两大块时序因子生成和选股因子生成。再加上收益回撤比评估、5日IC指标优化以及配套的因子回测框架和可视化工具算是一整套比较完整的工程化方案。今天把这套东西拆开聊一聊从gplearn的原理边界、到因子生成模块的架构设计、再到评估与可视化的落地细节最后是那些实战里踩过的坑。希望对你搭建自己的因子挖掘流水线有直接帮助。1. 为什么是gplearn来做因子挖掘从人工范式到程序化搜索的转变量化研究的第一步也是最费时间的步骤就是找有效因子。早年我们做多因子模型的时候一个研究员可能要花一两个星期去手搓一个量价因子出来先看文献再进行逻辑推演然后写代码回测接着调整参数最后分行业做中性化处理。运气好一两个星期能出一个逻辑自洽且能用的运气不好推演了大半个月回测曲线出来一看收益是有了回撤也大得离谱直接推翻重来。但换个角度想因子的本质是什么它就是一组基于原始行情数据、财务数据、甚至另类数据的数学表达式。既然是表达式那就存在一个搜索空间。人工搜索靠的是经验和灵感而程序化搜索靠的是优化算法。遗传规划Genetic Programming, GP就是专门干这个的它把表达式当成一棵树树的节点是算子加、减、乘、除、以及其他技术指标函数叶子是变量收盘价、成交量、跌幅、换手率等然后通过选择、交叉、变异不断迭代演化出适应度得分更高的因子表达式。那为什么是gplearn而不是其他的符号回归库说实话gplearn在业界当然不算工业级大规模分布式方案但它的优势非常契合个人研究员和小团队的工作流完美融入sklearn生态API风格就是fit、predict、transform那一套所以不需要额外的学习成本接入现有数据管线非常方便。内置了并行处理的机制n_jobs参数我在多核机器上跑时序因子生成速度提升非常明显。支持自定义算子这很关键。因为量价因子很多函数是gplearn默认函数集里没有的比如滚动相关系数、滚动回归残差、衰减加权均值等这些需要自己写函数注册进去。社区成熟资料多遇到参数调优或者函数集设置的问题基本上都能搜到对应的讨论。当然直接用gplearn裸跑也是可行的但实际效果和工程落地之间还有不小的距离。比如它生成的因子表达式可能极度复杂过拟合风险随之而来比如它的内置评估函数是以符号回归的MSE或者MAE为目标但你真正关心的其实是IC、ICIR、收益回撤比这些金融语义下的指标。所以我在系统里做了一层很关键的封装把gplearn的进化目标和输出结果跟金融因子评估体系对接起来。这也是标题里说到的收益回撤比评估和5日IC指标优化这两块是怎么嵌入到gplearn工作流里的核心关键。2. gplearn核心机制拆解公式树、遗传操作与函数集设计这一节先不急着上代码我觉得有必要把这几个机制聊透。因为你在用gplearn做因子挖掘的时候如果对底层机制理解不深很容易出现算法跑完了但结果完全没法用的情况。2.1 公式树结构因子表达式到底长什么样gplearn里的每个个体本质上是一棵二叉树。内部节点是函数叶子节点是变量或常数。举个最简单的例子如果你想表达经典的动量因子过去20日收益率它对应一棵很简单的树根节点是minus函数左边叶子是今日收盘价close右边叶子是20日前的收盘价close_lag20然后整体除以close_lag20。当然在gplearn里你可以把除法算子也用上直接构造出(close - close_lag20) / close_lag20这样的结构。理解了公式树之后你就能明白函数集function_set的选择为什么至关重要。函数集相当于可以使用的零件而变量集feature set相当于原材料。零件和原材料的选择直接决定了搜索空间的大小和搜索结果的可解释性。如果你一股脑儿把几十个函数、几十个变量都丢进去搜索空间会急剧膨胀跑一晚上可能都收敛不到理想结果如果你函数集太简单比如只有加减乘除那生成的因子也高级不到哪里去。我在系统里做了分层的函数集设计时序因子模块和选股因子模块各配了一套不同的函数集。时序因子更看重波动率、动量、均值回归这些特性选股因子则更看重截面上的排序、分位数、行业相对强弱等结构化算子。这一点后面再细说。2.2 遗传操作详解选择、交叉、变异如何配合gplearn的进化过程大致这样的种群初始化随机生成一批公式树深度和形状受init_depth参数控制。适应度评估评估每棵树在训练数据上的表现算出适应度得分。选择根据适应度得分用锦标赛选择tournament selection挑出部分优秀个体作为父代。交叉随机交换两棵树的某个子树生成子代。变异随机替换树中的某个节点或子树引入新的基因片段。新一代种群生成重复迭代。听起来不复杂但这里有几个参数在实际使用中相当关键如果只是用默认参数大概率效果平平population_size种群大小默认值是1000但如果你特征变量多、函数集大1000只远远不够。我实际使用中至少设到3000到5000否则搜索空间覆盖不足。代价是计算时间线性增长所以我一般是配合n_jobs并行一起用。generations迭代代数默认值是20对于金融因子挖掘来说偏少。我通常设在50到100之间。但代数的增加要配合早停机制early stopping否则后期很容易过拟合。tournament_size锦标赛大小这个参数决定了选择的压力。默认是3我习惯设在5或10。锦标赛规模越大选择压力越大种群收敛越快但也更容易早熟收敛需要权衡。parsimony_coefficient简约系数这个参数控制对复杂公式树的惩罚力度。如果这个值为0算法会很自然地趋向于生成越来越复杂的树因为复杂的树往往能更好地拟合训练数据。我一般把这个值设为0.01到0.05之间让算法在表达能力和复杂度之间取一个平衡。这个参数太重要了后面踩坑部分我会重点讲。2.3 函数集设计思路如何让gplearn生成有价值的候选因子我在设计函数集的时候参考了两类函数一类是跟技术指标相关的函数比如RSI、布林带、MACD这些经典指标的表达式被拆解成算子和函数另一类是时序算子比如滚动均值、滚动标准差、滚动斜率、滚动gamma等。为什么我不直接把20日滚动均值作为一个完整函数注册进去因为这样做的问题是固定窗口长度的算子会限制因子的灵活性。gplearn最好的地方就在于它可以自己进化出窗口长度。比如它会尝试构造rolling_mean(close, 10)和rolling_mean(close, 30)的差值这本质上就是一个均线交叉因子但它经历的路径比人工设计要丰富得多。所以我的经验是函数集里尽量注册原语级别的算子滚动窗口长度作为参数让遗传规划自己搜索。虽然这样会增加搜索空间的维度但找到的因子往往更具有创新性。下面是我在系统里常用的一个函数集快照供参考add: 加法sub: 减法mul: 乘法div: 除法带保护除数为0时返回1abs: 绝对值neg: 取反sqrt: 平方根带保护log: 自然对数带保护rolling_mean: 滚动均值窗口参数可进化rolling_std: 滚动标准差窗口参数可进化rolling_corr: 滚动相关系数两个序列之间的滚动相关窗口参数可进化ts_delta: 时序差分当前值减去N期前的值ts_rank: 时序排名当前值在过去N期中的百分位排名ts_argmax / ts_argmin: 过去N期内最大值/最小值发生的位置delta_ratio: 变动率选股因子那一块我还会额外加截面处理函数这个后面再详细说。设计好函数集之后你在终端里打印gplearn生成的公式会看到一串可读性不错的表达式大概是这样的sub(div(close, rolling_mean(close, 20)), delta_ratio(volume, 5))这个表达式代表的含义是当前收盘价偏离20日均线的比例减去成交量的5日变动率。这种因子天然带有量价背离的意味逻辑上比较容易解释。事实上我在实际运行中经常发现gplearn进化出来的很多因子回到金融逻辑上是可以自洽解释的这也说明它的搜索过程很多时候是在沿着某种有意义的路径前进。3. 时序因子生成模块从原始行情序列到候选因子库时序因子生成主要针对的是单只标的或者少数几个标的在一段时间内的数据通过时间序列上的运算构造因子。这个模块在系统里是用来做日内交易策略、或者中低频单标的择时因子挖掘的核心引擎。3.1 时序因子的问题定义和数据准备时序因子生成的数据结构是一张按时间排序的宽表每一行是一个交易日的快照每一列是一个属性open, high, low, close, volume, amount, turnover等。我一般在进入gplearn之前会先把原始数据做好预处理比如复权、去极值、缺失值处理然后生成一部分基础衍生变量比如涨跌幅、振幅、成交额对数、换手率的变化量等。但这里有一个容易踩的坑gplearn的默认流程是针对横截面数据设计的它把每一行当成一个独立样本。如果直接把时序数据丢进去它会把时间先后顺序完全打乱导致滚动窗口类的函数计算出错。这个问题的本质是你明明想要的是按时间序列滚动的结果但gplearn在交叉验证切分时对样本进行了shuffle这就破坏了时序的依赖关系。所以我做了两个处理第一在输入特征中直接加入大量的滞后变量。比如close_lag1, close_lag5, close_lag10, open_lag1, volume_lag5等这样即使gplearn对行做了shuffle它通过滞后变量仍然能拿到时间上的信息。第二后面我把适应度评估函数改成把样本按时间顺序排列后计算IC或收益这样就规避了行顺序被打乱的问题。3.2 时序因子的适应度函数设计时序因子模块里的适应度函数我主要用两个维度来评估IC信息系数即因子值和未来N日收益的Spearman或Pearson相关系数。这个指标衡量的是因子的预测能力。收益回撤比也就是把因子值划分为多头组合和空头组合之后的净值曲线计算年化收益与最大回撤的比值。这个指标更贴近实际交易逻辑比单纯的IC更稳健。我设计的适应度得分实际上是IC和收益回撤比的一个加权组合。一个简单版本是这样score 0.5 * np.nanmean(ic_series) 0.5 * np.tanh(max_drawdown_ratio)用np.tanh把收益回撤比压缩到(-1, 1)区间避免数值量纲差异带来的权重失衡。这样设计的好处是gplearn在进化过程中不只是关注因子的统计显著性还兼顾了实际交易中的收益风险特征生成的因子更容易直接用。这里要特别强调一下适应度函数的设计直接决定了gplearn搜索的方向。如果适应度函数只用了IC均值和ICIR你会得到一批统计上显著但实际交易起来很鸡肋的因子加上收益回撤比之后因子的实战性价比会明显提升。我的经验是两者权重大概五五开的时候效果最好。3.3 时序因子的输出格式和去重去冗余gplearn跑完之后输出的是一个SymbolicRegressor或者SymbolicTransformer对象里面会保存最佳的程序best_program或者一系列程序使用SymbolicTransformer可以生成多个因子。SymbolicTransformer的典型用法是你设置n_components10然后它输出10个衍生特征这10个特征再拼接到你的原始特征矩阵后面作为新的因子库。但有个问题需要处理gplearn生成的特征之间可能有很高的共线性或者特征与输入变量之间存在线性关系导致新特征的信息冗余度很高。我在系统里加了一步筛选逻辑对gplearn输出的所有特征做两两相关性分析设定一个阈值比如0.7去掉高度相关的特征然后再做一个简单的IC排序只保留排名靠前的那一批作为候选因子库。最终输出的时候系统会生成一个因子文件记录每个因子的表达式、IC均值、ICIR、收益回撤比、以及训练区间和测试区间的表现方便后续统一管理。4. 选股因子生成模块截面排序与Alpha挖掘如果说时序因子是对同一资产的过去和未来做文章那选股因子就是对同一时刻不同资产之间的横截面做文章。选股因子在多因子选股模型里的地位不用多说它是Alpha的主要来源。但选股因子的生成和时序因子在技术层面有一个本质区别gplearn本身并不支持按截面分组的运算它只支持逐行运算。而选股因子希望得到的是某个股票在全体股票中的相对位置比如收盘价在全市场中的分位数排名研发费用增速在行业内的排名等。这些横截面信息如果用gplearn原生的函数集是表达不出来的。4.1 通过截面化预处理把横截面数据转换成逐行特征我的一个做法是在数据预处理阶段先把所有股票的原始特征按交易日做截面处理生成一系列截面标准化特征。比如在每一天对所有股票的PE_ttm这一列做rank处理生成一个0到1之间的分位数排名项作为一个新特征。对这个截面排名特征gplearn在运算时就把它当作一个普通的数值变量处理但实际上它已经携带了横截面信息。类似的截面预处理特征还包括截面z-score行业相对排名股票在所属行业内做排名市值分组排名换手率的截面分位数通过这种方式gplearn就能在公式树里混合使用绝对数值和相对排名两类特征进化出截面选股因子。4.2 截面IC作为选股因子模块的评估基准在选股因子模块里IC的含义和时序模块不一样。时序模块的IC是单标的因子值序列与未来收益序列的相关系数选股因子模块的IC则是每个截面上因子值与未来收益的截面相关系数然后按时间平均。这就是行业内常说的RankIC。我的系统里在每一期截面上计算出RankIC然后对整个样本期做统计得到IC均值、IC标准差、ICIR同时也会算多空组合的收益回撤比。适应度函数的设计跟时序模块类似但对IC的权重会更高一些毕竟选股因子更核心的是选股能力而不是择时能力。4.3 中性化处理与因子正交化的实战要点选股因子模块还有一个必须处理的问题市场和行业风格的影响。一个因子如果只是市值因子的变体那它在样本内的表现会很不错但实际使用中很难产生增量Alpha。所以在评估和修正选股因子时我一般在系统里加入中性化处理流程把因子值对市值、行业虚拟变量做线性回归取残差作为中性化后的因子值。这一步的逻辑并不复杂但非常重要。因为我们的目标是找到有增量信息的Alpha因子而不是在已经拥挤的市值、行业因子上反复变异。如果gplearn进化出来的某个因子经过中性化后IC直接掉落非常多说明它的Alpha其实主要来自风格暴露这类因子我一般会降低优先级。顺带说一句如果你用的是gplearn的SymbolicTransformer来生成因子特征记得在transform输出的特征上同样跑一遍中性化和标准化。否则你在训练集上看到的好因子在真实样本外很可能是被风格暴露所裹挟的伪因子。5. 因子回测框架从单因子到组合层面的完整验证因子挖掘只是第一步更重要的在于验证这个因子到底能不能用。我的系统里搭了一个相对完整的因子回测框架分三层单因子分析、分层回测、以及多因子组合层面的初步验证。5.1 单因子分析模块的实现逻辑单因子分析做的事情是给定一个因子序列输出它的一系列统计指标包括因子覆盖率非缺失值占比因子值分布IC均值、IC标准差、ICIR单调性检验多空组合的累计净值曲线多空组合的收益回撤比、夏普比率这个模块的输入是日期-标的-因子值的长表输出是一份指标报告和一张净值曲线图。为了避免用到未来函数单因子分析里所有因子的计算都严格限定在当期及历史数据上这一点在因子计算引擎里会统一校验。技术上有一个比较容易出问题的地方是因子对齐因为不同股票上市时间不同交易状态不同停牌有些日期某些股票可能没有因子值。在计算截面IC的时候要把这些空值剔除同时保证剔除空值后参与截面计算的股票数量足够多否则算出来的IC波动会非常大。我一般会设定一个最少股票数量阈值比如30只低于这个数量就直接跳过该时点不参与IC汇总。5.2 分层回测判定因子的单调性和区分度单因子IC高不代表因子真的分层效果好。IC反映的是线性相关系数如果因子与收益的关系是非线性的IC可能不高但分层收益可能很漂亮。所以在系统里我也加入了分层回测模块每天按因子值把股票分成5组或10组等权或市值加权构建组合观察多空组合的累计收益差和稳定性。在分层回测结果里我最关注的是两个东西第一多空组合收益曲线的平稳性——如果曲线持续向上说明因子在各期都比较稳定地有效如果曲线在某些年份出现大幅回撤说明因子的有效性可能有明显的状态依赖性。第二分组净值的单调性——理想情况下第1组因子值最小到第5组因子值最大的净值应该单调递增或递减。如果出现中间组表现最好或两端表现好、中间差的情况这个因子的逻辑可能就需要进一步审视。分层回测还有一个附带作用就是帮我在系统里自动生成因子配置建议。比如第1组和第10组的多空收益差比较大并且多头组合的收益回撤比也优于基准那这个因子就可能适合作为多头信号的alpha因子。5.3 组合层面的检验因子收益的独立性与增量价值多因子组合层的验证我的做法比较简单直接把候选因子和已有的基准因子比如市值因子、估值因子、动量因子放在一起做一个多因子回归或简单的线性加权看看加入新因子之后模型整体的IC和多空收益回撤比是否有提升。如果提升明显说明这个因子有增量价值如果提升有限那它可能是已有因子的某种组合实际价值有限。这一步的工程实现相对轻量在我这个框架里不需要带weight训练太复杂的模型重点是快速判断增量。等候选因子真正入库后再上更复杂的指数增强模型或回归权重模型进行精细化配置。6. 5日IC指标优化的实现方式这是我觉得最顺手的一个环节标题里特意提到5日IC指标优化这个点值得单独拿出来讲。因为很多人在做因子挖掘时常用的IC计算未来收益窗口是5日或10日。我们经常会在研报里看到xx因子未来5日IC均值0.03ICIR 1.5这样的说法。这里的5日指的是因子值与未来5个交易日累积收益的相关系数。为什么我用5日而不是1日或20日这跟策略调仓周期有关。短线因子看1日IC会引入太多噪声看20日又会反应太慢5日在A股这种换手率较高的市场环境里是一个比较平衡的窗口。当然具体用几日的IC还是应该跟你的交易频率和策略逻辑保持一致。只是我在系统里默认用的是5日因为它的信噪比相对合理而且5日调仓的模型在多数情况下资金容量和交易成本的平衡也最优。6.1 通过修改gplearn的metric来嵌入5日IC评估gplearn的SymbolicRegressor和SymbolicTransformer都暴露了一个metric参数用来传入自定义的评估函数。这个评估函数的签名一般是function(y, y_pred, sample_weight)返回一个标量该标量越小代表适应度越好注意gplearn内部的metric默认是越小越好因为它底层是用负的适应度来排序的。在这个系统里我写了一个针对5日IC的metric。核心思路是把训练集数据按时间顺序重新排列计算出每个截面上的因子值和未来5日收益然后取Spearman相关系数的均值最后取负号作为metric的返回值。下面给出一个简化版的适应度函数实现思路供参考import numpy as np def ic5_metric(y, y_pred, sample_weight): 以5日IC均值取负作为适应度评估指标 df pd.DataFrame({ y: y, pred: y_pred, time: sample_weight }) df df.sort_values(time) ic_list [] for dt, sub_df in df.groupby(time): if len(sub_df) 30: continue ic sub_df[pred].corr(sub_df[y], methodspearman) if not np.isnan(ic): ic_list.append(ic) if len(ic_list) 20: return 1e9 return -np.mean(ic_list)这个函数里的sample_weight参数我在工程上是用时间戳来填充的。为什么要用sample_weight传时间因为gplearn在内部交叉验证和并行计算时会打乱样本顺序如果不在fit之前把时间信息通过sample_weight传进去单靠y和y_pred是无法恢复时间结构的。这里还有一个细节在计算未来5日收益时注意不要引入未来函数。也就是说在构建训练标签y时要确保因子值对应的时间点在未来5日收益之前是已知的。我在数据准备阶段专门做了shift操作确保标签对齐不会出问题。6.2 优化IC与优化收益回撤比的权变取舍5日IC优化和收益回撤比优化这两个目标并不总是完全一致的。有些因子IC均值很高但多空组合净值曲线的波动很大收益回撤比并不好反过来有些因子IC均值不高但策略净值曲线非常平滑收益回撤比反而很优秀。我在系统里的做法是进化阶段主要用IC为主的适应度函数让gplearn快速锁定一批预测能力强的因子在因子筛选阶段则引入收益回撤比做二次过滤。如果某个因子IC和收益回撤比都超过设定的阈值就把它加入候选因子库如果只有IC达标但收益回撤比很差我会再观察一下因子是否在特定市况下失效再做决定。说到底进化搜索阶段的目标是别跑偏而筛选阶段的目标是能用。这两步解耦之后整个流程会清晰很多。7. 可视化工具的设计与实现让因子表现一目了然可视化这一块在系统里不是花架子而是真正帮助我做决策的模块。gplearn跑完之后会生成大量因子每个因子都要看线条、看分布、看IC、看回撤纯靠肉眼一个个盯是不现实的。可视化工具把关键的几个图集中展示让我在几秒钟内判断一个因子是否值得继续跟踪。7.1 因子净值曲线与基准对比每个因子回测完之后系统会生成一张包含三条曲线的图多头组合净值、空头组合净值、多空组合净值。和基准比如沪深300指数或中证500指数放在同一张图上对比。这样能快速看到因子是否有超额收益超额收益的来源是来自多头端还是空头端以及曲线的回撤主要在什么时间段。这个图我一般是最先看的。如果多空曲线很平滑地上涨说明因子的有效性非常稳定如果曲线是阶梯状的说明因子在特定市场环境下才有效那就要结合市场状态去理解因子逻辑。7.2 IC序列与滚动IC可视化除了净值曲线IC序列图也是必看的。系统会画出因子在每个时点的IC值折线图同时叠加一个滚动20日的IC均值线。这张图的最大价值在于观察因子的IC稳定性。如果IC序列基本围绕0轴上下大幅波动那这个因子的信号质量大概率是不稳定的如果滚动IC均值能稳定在0.02以上那么这个因子就值得继续跟踪。另外我还会画IC的累计分布直方图来看IC的正负分布。正常情况下一个好因子的IC分布应该有一个明显的正偏移峰值在0附近但不是完全对称。如果IC分布是双峰的说明因子在某些阶段正相关很强、某些阶段负相关很强那很可能是因子存在状态切换需要做状态分析。7.3 因子表达式树的可视化与人工审计可视化工具里还有一个我个人很依赖的功能把gplearn生成的公式树以图的形式展示出来。这个功能的实际价值在于人工审计。虽然我们信任算法但一个没有任何金融含义表达式的因子即便统计上很显著也不太敢直接用。公式树可视化之后我可以一眼看出这个因子是由哪些原始变量和操作符组成的逻辑上是否说得通。比如一棵树的根节点是sub左子树是close右子树是rolling_mean(close, 30)那这个因子的逻辑就是当前价格相对30日均线的溢价是典型的动量类因子。这样的因子我敢用、也愿意做更多测试。但如果有棵树的结构极其诡异比如pow(volume, close)再除以一个什么奇怪的组合那我基本不会把它纳入因子库因为这种因子几乎必然过拟合。8. 实战中的踩坑记录从样本外失效到过拟合处理最后聊一聊我在使用这套系统过程中遇到过的几个真问题。这些问题你要是自己瞎跑一遍大概率也会遇到。直接把我踩过的坑写出来帮你省一点时间。8.1 gplearn的过拟合比想象中更严重遗传规划本质上是一种贪心的搜索算法它天然就带着过拟合倾向。我在第一版系统里用训练集拟合出来的因子IC均值能达到0.08甚至0.1看起来很漂亮。但一到样本外测试IC直接掉到0.01甚至变成负的。后来我做了几个改进第一增加训练数据的时间长度。因子挖掘至少要覆盖两种以上的市场状态比如震荡市和趋势市。如果训练样本只有一年那挖掘出来的因子很可能只是针对某一年的行情特征做了过拟合。我在系统里默认训练区间不少于3年的日线数据。第二在适应度函数里加入复杂度惩罚。前文提到的parsimony_coefficient就是干这个的。调大这个参数后gplearn会更倾向于生成简洁的表达式过拟合风险会明显下降。我跑了几组对照实验parsimony_coefficient从0.0001调到0.01之后样本外IC的衰减幅度明显减小。第三交叉验证维度。gplearn本身跑的是单一训练集但我在评估候选因子时会把时间序列切分成前段和后段做前段训练、后段验证的walk-forward校验。如果因子在前段训练和后段验证上的表现差异太大就直接筛掉。8.2 因子大量相似导致的多重共线性问题gplearn进化出来的因子在结构上可能会有很多同源的变体。比如一个因子是动量因子A另外一个动量因子A加上了一个很小的常数还有一个是动量因子A的倒数。这些因子在数学上不是完全相同但语义上是高度重复的相关性极高。如果直接把所有因子都作为独立特征塞进下游模型会引发严重的多重共线性问题导致模型权重不稳定。我后来在系统里专门加了一步聚类去冗余的逻辑对候选因子逐一计算相关性矩阵然后使用简单的层次聚类或者贪心策略筛选确保保留的因子两两相关性不超过0.7。这跟前面提到因子库管理里的去重是一个流程。8.3 函数集的陷阱除法和对数的边界问题gplearn在生成表达式时如果函数集里包含除法和对数它有可能生成除0、对负数取对数这类无意义运算。如果不做处理运行时就会报错或者产生inf、NaN进而污染整个适应度评估。我的做法是自定义保护函数除法在分母绝对值小于某个阈值比如1e-6时返回1对数在输入小于等于0时返回0平方根在输入为负数时返回0。这里的关键不在于保护本身而在于保护返回值的选择会影响搜索方向。如果你在除零时返回0gplearn可能会学会利用这个常数0来构造一些看似有效但实际无意义的表达式这一点要特别注意。更合理的方式是返回一个较大的异常值比如1e9这样gplearn在进化过程中会逐渐学会避开这些无意义的运算路径。这个细节可以显著提高生成因子的有效性。8.4 样本权重与时间对齐的问题这个坑早前已经提到过gplearn在内部实现中对样本顺序不做保留尤其是在多进程并行的时候。如果不把时间信息显式传进去所有依赖时序窗口的函数不管是rolling_mean还是ts_delta都会计算出错。我在做时序因子模块时这一点感触最深。解决方案除了将滞后变量显式加入特征之外还有一个更根本的思路在自定义metric里重组数据。通过sample_weight传入时间戳然后在metric函数内部按时间戳排序构造出正确的时序关系。这样做虽然会增加一些计算开销但能确保因子表达式的语义正确。9. 系统的整体运行流程与后续扩展思路整套系统落地的运行流程简单来说是这样的第一步配置数据源。系统从本地或数据库加载行情数据、财务数据完成复权、去极值、缺失值填充等预处理。第二步运行时序因子生成模块对单标的或少数标的进行时序因子挖掘。第三步运行选股因子生成模块基于全市场的截面数据挖掘选股因子。第四步两边的候选因子统一送到因子回测框架做单因子分析、分层回测和组合层面的增量测试。第五步通过可视化工具体检所有因子结合人工经验完成筛选入库。最后把入库的因子接入到实盘策略或模拟盘策略里持续跟踪表现。如果你不想从零开始搭这套框架一个比较快的做法是先把gplearn的SymbolicTransformer跑通然后用几行代码把IC评估和去冗逻辑串起来先跑出一批因子看看效果再逐步扩展成完整的模块。我是建议从小的闭环开始别一上来就想把所有东西都做好否则调试成本会很高。从这个项目往后走我觉得有两个方向值得进一步探索。一个是把gplearn生成的因子和深度学习模型结合看符号特征能否提升NN模型的预测效果另一个是引入更多另类数据源比如舆情数据、资金流数据看遗传规划能否发现一些传统金融数据之外的Alpha信号。当然这些都是后话先把基础框架跑稳把因子管理流程做好才是更关键的一步。我个人在实际操作中最深的体会就是在量化投研里工具和算法只是放大器最终决定系统实战价值的是对数据逻辑的理解和对过拟合的敬畏。gplearn能帮你打开因子空间的大门但门的另一边还是要靠你去审慎地走每一步。本文还有配套的精品资源点击获取
返回列表