大概在去年这个时候,我们内部一个模拟盘底部的组合策略卡住了:权重配置怎么调,回测曲线的夏普比率都停在0.8附近,全年净值走势贴着基准线来回磨。后来做了一次比较大的架构改动,把组合优化这一步从传统的均值方差框架换成随机森林驱动的信号叠加,验证集上的年化收益相对提升了18%左右,最大回撤反而收窄了。这篇文章就把这次从数据、特征、模型到策略链路的完整改造过程拆开讲一遍,重点聊聊随机森林在投资组合优化里真正起作用的那几个环节,以及为什么它不是“用一个模型替代另一个模型”那么简单。如果你正在做AI应用架构、量化策略落地,或者单纯想搞清楚机器学习在资产配置里到底能干什么,这篇应该能给你一些可以落地的思路。
1. 从均值方差到随机森林:组合优化思路的一次换血
1.1 传统线性框架的三大痛点
我们的旧组合构建流程很典型:先给每只候选标的算底层因子暴露,然后在线性框架下求一个满足约束的权重解,核心等价于均值-方差优化或它的变体。这套方法在市场平稳、资产间相关性结构稳定的时候非常好用,但一旦遇到风格切换或者流动性突变,协方差矩阵的估计误差会被放大得非常快。当时的策略就是在这一点上栽了跟头:行业中性约束都在正常范围内,但风格因子集中在同一类资产上,宏观数据一有风吹草动,整个组合一起挨打,分散化的效果只停留在纸面上。
经过复盘,我把传统线性框架的痛点归纳为三个:
- 线性假设导致交互项全靠人工构造,资产之间的非线性联动很难被刻画。
- 协方差矩阵在样本外估计极不稳定,权重解经常在调仓边界附近剧烈摆动。
- 模型输出的是“绝对预期收益”,但组合优化真正需要的是“相对强弱排序”,两者天然错位。
1.2 随机森林踩中要点的三个特性
我们评估了一圈机器学习模型,最后选随机森林,主要原因有三个。第一,它天然能捕捉非线性交互。组合优化最困难的部分不是单资产预测,而是资产之间的相互作用,随机森林在分裂时会自动寻找特征的高阶组合,比如“波动率收缩+成交额放大+短线动量转强”这类联合条件,不需要我们手工构造交叉项。第二,随机森林对低信噪比环境的容忍度较高。金融数据噪声极大,单棵决策树几乎肯定拟合了一堆噪声,但通过Bagging对多棵树取平均,这部分影响会被明显削弱。当时我们做过一个小实验,在标签里混入20%随机扰动,随机森林在验证集上的排序相关度衰减要比线性模型温和得多。第三,特征重要性的可解释性非常好。组合策略过评审时,模型必须能回答“你到底想往哪些方向倾斜”,随机森林对特征重要性的度量是自带的,上下游协作时拿它做归因逻辑闭环,非常方便。
1.3 架构层先把“预测”和“优化”拆开
那次改造里最值得一提的,是我们从架构上做了一层解耦:把随机森林当作整个链路中的一个模块,而不是端到端的黑箱。架构自下而上分为数据接入层、特征生成层、模型预测层、信号合成层、组合优化层、回测执行层。随机森林只负责模型预测层,输出每只候选标的一段时间内的收益排序信号;组合优化层拿到排序信号后,再结合风险约束生成最终权重。
这个拆法帮助很大。预测与优化解耦之后,任何一层升级都不需要其他层返工。比如后来我们把回测引擎换成支持分钟级撮合的新版本,完全没有影响预测层的特征逻辑。对“AI应用架构师”这个角色来说,设计好各层的边界和接口,比单独调一个模型重要得多。
2. 数据与标签:随机森林真正要吃的素材
2.1 数据清洗里最容易埋雷的环节
这个项目覆盖了约800个标的,时间跨度从2016年到2022年,数据源是公开行情加圈内另类数据接口。清洗阶段有几个坑让人印象深刻。第一是复权问题,绝对不能直接用未复权价格构造特征,前复权计算必须嵌在因子流水线里统一完成,否则历史价格的断点会被模型当成真实信号。第二是停牌与涨跌停样本,当天无法成交的样本如果混进训练集,模型学到的大量“特征”实际是不可交易的,回测时看着有效,实盘根本复现不了。第三是对齐问题,所有特征必须统一在交易日序列上做边缘对齐,少了一天错位,未来信息就可能通过特征加工过程悄悄泄漏进训练集。
2.2 特征工程:从行情序列到横截面因子
我们把每只标的的特征分成三组:价格波动类、资金成交类、情绪时间类。这里列一个精简后的特征清单,实际训练时每一类还会衍生出多个窗口版本。
| 特征组 | 代表特征 | 窗口参数 |
|---|---|---|
| 价格波动 | 收盘价、MA5偏离度、MA20偏离度、ATR、RSI | 5/10/20日 |
| 资金成交 | 成交额变化率、换手率、大单净流入、量比 | 1/5/20日 |
| 情绪时间 | 振幅、距涨跌停幅度、连涨天数、隔夜跳空幅度 | 1/5日 |
最终得到的原始特征维度在120个左右。随机森林对“中等维度、高度冗余”的特征空间相当友好,不需要做大规模降维也能取得不错效果。但特征越多噪声也越多,树的深度和分裂阈值配不好,少数异常样本就会牵着模型走,这一点在调参部分会详细展开。
2.3 标签设计:排序预测比绝对收益预测稳定得多
标签设计这个环节很多人会忽略,但它对最终结果的影响比模型选择更大。我们对比过两种标签:
- 简单远期收益:标的在T+5的绝对收益百分比,即今天收盘价与5天后收盘价的差。
- 排序标签:在同一时间截面上,把所有标的的T+5收益做横截面排名,以排名分位数作为标签。
第一种标签训出来的模型输出“绝对涨跌幅”,噪声很大,容易被单只极端标的带偏。第二种标签训出来的模型输出更贴近组合优化真正需要的“相对强弱信息”,噪声过滤效果明显更好。我们最终选的是第二种。理由很直接:组合优化层关心的是哪些标的该超配、哪些该低配,相对排序比绝对预测稳得多。
2.4 时间切分:数据泄露是回测结果虚高的元凶
时间序列数据最怕的就是拿未来信息训练当下模型。我们的切分方式非常严格:按时间顺序切训练集、验证集、测试集,前约70%时间区间做训练,紧接其后的15%做验证调参,最后15%做最终效果评估。绝对不允许随机打散再分割,否则同一时间段内的样本会同时出现在训练集和测试集里,回测结果乐观到失真,上实盘立刻翻车。
另外还有一个约束成了团队铁律:所有特征都必须在当日收盘后才可获取,任何盘中数据在做特征时必须按“当日信号、次日执行”的假设处理,一旦出现未来函数,策略逻辑就完全不可信了。
3. 训练调参:随机森林的容错区间与边界
3.1 基线参数与三个关键超参的调法
先给出基线参数:n_estimators=300,max_depth=8,min_samples_leaf=20。训练样本约80万条,规模对随机森林来说很轻量,单机内存可以轻松跑完。调参时我们主要盯三个方向。
- n_estimators:100到1000都试过,500之后增益变得很小,训练时间却线性上升,最终停在500。
- max_depth:6到12之间变化最敏感。太浅学不出特征交互,验证集排序能力平平;太深会出现一个经典现象:交叉验证分数很高,但按时间切片展开后段明显衰退。
- min_samples_leaf:直接控制叶节点最小样本数,设在40能让每棵树更稳定,也减少对个别异常样本的过拟合。
调参并不需要把每个参数都搜一遍。实践中,先把这三个核心参数确定好,剩下的比如分裂条件、最大特征数,按默认值去跑基本不会带来质变。
3.2 特征重要性的实际使用方式
跑完基线模型后,我们画出特征重要性排序,前15个特征占了总重要性的60%以上。比较反直觉的是,成交额变化率、波动率类相关特征排在最前面,而直觉上很受关注的“涨跌家数比”反而排得很靠后。把后50%重要性的特征去掉重新训练,验证集排序相关度几乎不变,训练时间减少了近一半。
这个结果让我们意识到一件事:特征精度比特征数量重要。与其堆更多低频财报特征,不如把几个核心高频特征的位置算准、窗口选对、极值处理扎实。后面做模型轻量化部署时,这个精简动作帮了大忙。
3.3 模型横向对比:为什么没选XGBoost
同一数据集上,我们对比了随机森林、XGBoost、逻辑回归和小型MLP。验证集上的IC均值(信息系数,即排序相关系数)数据如下:
| 模型 | 验证集IC均值 | 训练耗时 | 部署复杂度 |
|---|---|---|---|
| 随机森林 | 0.062 | 中等 | 低 |
| XGBoost | 0.065 | 较高 | 中 |
| 逻辑回归 | 0.038 | 低 | 低 |
| 小型MLP | 0.058 | 高 | 高 |
IC差距并不大,随机森林的优势在工程侧:无需特征归一化、没有特殊依赖、单机可训练、推理速度快。对一个需要快速上线、快速迭代的应用架构团队来说,这三点比0.003的IC差距更值钱。如果线上资源极度紧张,XGBoost可能更合适,但在我们的交易频率和样本量下,随机森林的性价比已经足够。
3.4 过拟合的伪装:高IC却持续衰退
调参过程中踩过一次典型陷阱:验证集IC很高,但按时间切片展开后,最近12个月的表现持续下降。归因后发现,特征和标签里都包含了非常强的市场整体趋势信息,模型实质上在预测大盘方向,而不是标的之间的相对强弱。修法是去掉所有与市场均值直接相关的特征,改用“相对市场基准的超额收益”作为标签。结果整体IC略降,但时间切片上稳定了很多。
这里最重要的一条经验是:IC不是越高越好,稳定的、不随时间剧烈漂移的IC才是可交易的。
4. 信号落地:从模型预测到账户权重的中间链路
4.1 排序分到五档仓位的转换规则
模型输出的是横截面排序分,不能直接当仓位百分比使用。我们做了两步转换:先把排序分按分位数分成五档,超配、标配上沿、标配、标配下沿、低配,然后限制每一档的最大持仓权重。这样做是因为模型的排序比较可靠,但绝对分值不可靠,分档相当于削掉绝对分值里的噪声。
具体参数上,超配档单标的最大权重上限3%,低配档单标的最多保留1%基准权重。这个约束避免少数几只标的预测突变引发组合极端波动。回测和实盘运行都验证了这种稳定性对最终收益的贡献。
4.2 带约束的权重求解为什么不能交给随机森林
组合权重的最终生成,我们使用的是带约束的二次规划求解。目标函数是最大化组合的相对预期得分,由随机森林预测分映射而来,同时满足行业中性、风格中性、总仓位等于1、单票上限等约束。这里的预期得分和传统多因子模型里的因子暴露打分很像,只是打分来源从人工因子换成了随机森林输出。
为什么不直接让随机森林输出权重?权重本质上是带约束的决策变量,随机森林是预测器,它只能输出每只标的的排序分,无法保证权重组合满足约束。数据建模和运筹优化在这里是两个层次的问题,不能混为一谈。
4.3 回测引擎的撮合细节与成本假设
回测框架一开始用简化撮合:每日收盘价下单、次日开盘成交。后来升级成分钟级成交价模拟,加入涨跌停无法成交、停牌无法交易、手续费与滑点等成本项。一个重要结论是,简化撮合下不错的收益在加入执行成本后通常要缩水一截。我们的做法是用最保守的成本假设做压力测试,只有在压力测试下收益还能保持,策略才值得部署。
回测周期设为3年,调仓周期分别测了每日、每3日、每5日三档。每5日调仓在扣除手续费后净值增长最佳;每日调仓原始信号更强,但成本把超额收益吃掉太多。这个结论在更换不同特征组之后依然稳定,说明它更多来自策略逻辑本身,而不是特定周期下的巧合。
4.4 调仓周期与两级风控
风险控制分两层。第一层是常规风控:回撤阈值监控、行业暴露限定、单票最大亏损止损。第二层是模型风险控制:当随机森林的输出分布整体偏离历史均值超过两个标准差时,组合统一减半仓位。第二层在实盘中尤其有用,因为模型会在市场风格剧烈切换时连续给出系统性偏多信号,与其去猜模型是否失效,不如先砍规模,等信号分布回到合理区间再加回去。
5. 回测与归因:18%收益提升是怎么被确认的
5.1 基准对照与评价指标选取
这次改造要回答的核心问题,是随机森林带来的收益提升到底有多大、有多稳。我们在同一段测试区间(2021到2023年,约两年)内分别运行旧版组合方案和新版随机森林方案,交易成本、持仓约束、执行撮合假设完全一致。
衡量指标重点看四个:年化收益、夏普比率、最大回撤、IC稳定率。其中IC稳定率定义为预测排序方向与真实方向一致的时间占比,是对模型一致性比较直观的度量。
5.2 两年测试区间的关键数据
完整回测结果如下:
| 指标 | 均值方差基准 | 随机森林方案 | 变化 |
|---|---|---|---|
| 年化收益 | 8.2% | 9.7% | 相对提升约18.3% |
| 夏普比率 | 0.81 | 1.02 | +0.21 |
| 最大回撤 | 12.3% | 9.8% | 收窄2.5% |
| 月换手率 | 42% | 38% | -4% |
算一下这里的提升口径:旧方案年化8.2%,新方案9.7%,绝对差距1.5个百分点;1.5除以8.2约等于18.3%,所以标题里的“收益提升18%”指的是相对提升比例,而不是把年化拉高18个百分点。这个口径在对外汇报时一定要写清楚,否则容易被误读。
更关键的是,收益提升不是靠抬高风险换来的:夏普比率升高、最大回撤收窄、换手率反而下降。这和我们最初的预期不太一样,当初以为随机森林会把交易做得更频繁,结果反而是更精准了。
5.3 收益归因:优势来自避开弱势标的
归因拆解做了两步。第一步按行业分解超额收益来源,电子、医药、消费三个行业贡献了大部分超额收益;第二步按持仓时间分解,持有3到5天的调仓周期贡献了70%以上的超额收益。这两个结果指向同一个逻辑:随机森林模型的核心价值不在长期选赛道,而在中短期的相对强弱切换。
另有一个反直觉发现:模型对后30%弱标的的排序能力,明显强于对前30%强标的的排序能力。我们猜测这是因为金融数据本身分布导致的,下跌时的特征信号往往比上涨时更显著。策略因此天然偏防御,超额的很大一部分来自“避开了该避开的东西”,而不只是“抓住了该抓住的东西”。
5.4 稳健性检验与可交易性判断
为了确认结果不是参数巧合,我们换了5个随机种子、把样本区间分段重训、对超参做小范围扰动。结果排序相关度和回测收益方向保持一致,数值波动在可接受范围内。做完这些稳健性检验,我们才敢在评审会上正式写出“收益提升18%”这句话。
需要单独提醒一句的是:这里的收益全部来自历史回测区间,回测表现不代表未来收益,策略上线后仍然要靠持续监控和及时再训练来维持效果。
6. 生产环境的架构细节与踩坑实录
6.1 模型服务化与版本灰度
模型上线后,我们把预测服务封装成独立微服务,推理引擎通过导出后的模型文件加载,输入特征由上游特征服务按固定顺序推送。每次模型重训后,灰度验证包含两步:第一步用历史接口回放预测,比较新旧模型输出的统计分布;第二步在模拟盘跑两周,观察信号分布是否异常。
一个深刻的教训是:千万不要跳过灰度流程,直接把训练环境里效果最好的模型上线。我们有一次快速迭代,为了赶进度跳过了灰度,结果实盘输出分布明显偏离历史,折腾了好几天才定位到是特征顺序在推理时和训练时不一致导致的。从那以后,版本管理和灰度发布就成了强制步骤。
6.2 最容易被忽视的时点一致性
实盘运行中最常见的差异来自数据时点。线下回测是收盘后统一计算的,线上则是盘中不断有新数据进来。我们最终采用的方法是:盘中只读取已经封存的收盘特征,完全忽略盘中实时信号,只在收盘后统一更新一次信号。表面看损失了一些盘中信息,换来的却是策略行为的高度一致性,这比捕捉多一点点盘中噪音重要得多。
6.3 模型更新与监控指标
模型再训练按季度执行,每次再训练后观察三个监控指标:特征重要性漂移、IC滚动均值、输出分布分位数。三个指标任一个发生明显变化,就触发人工复核。监控面板每周输出摘要报告,负责策略的同事在这件事上花的时间比写代码还多。模型上线只是开始,持续监控才是让策略长期有效的关键。
6.4 这套架构能扩展到其他模型吗
这次方案虽然用的是随机森林,但整条架构其实可以平移到其他模型族。后期我们用梯度提升树重新训练过一版,收益接近,推理耗时更短,只因为团队对随机森林的运维经验更足,才没有整体替换。我个人更想强调的是,不要过度迷信某一个模型,把数据、特征、标签、训练切分、信号落地、回测成本这些架构层做扎实,策略才能真正长期跑下去。随机森林也好,其他模型也好,都只是这条流水线上一个可以替换的零件。