头条 机器学习策略入门最齐全教程

-- 次阅读

作者: 窥市寻真

机器学习策略入门最齐全教程

从因子挖掘到实盘落地的十个关键问题


我在量化交易这个方向折腾了六年,期间踩过的机器学习策略的坑,够写一本书咯。每次和同行聊起来,总有人问同样的问题:特征因子怎么选?标签怎么打?训练集选多长?为什么回测曲线漂亮得像假钞,一到实盘就原形毕露?

这篇文章是我把六年来反复试验、踩坑、推倒重来的经验,按逻辑顺序梳理出来的完整框架。不是什么论文综述,也不是调包教程,是从零开始搭建一套可用的机器学习策略时,你需要面对的最核心的十个问题。每个问题我都会告诉你我们在实战中具体怎么做的、犯了什么错、后来怎么改的。通篇不涉及任何具体投资建议,纯粹讨论方法论。

为了让你快速定位,这十个问题的顺序就是入门到精通的自然路径:先搞懂ML策略是什么、为什么区别于传统规则;然后学会选因子、打标签这两个最影响上限的决策;接着是训练集、验证集、测试集的三段式数据架构;再来是风控和突发事件应对;最后是迭代机制和实盘落地细节。整篇文章读下来,不管你是刚入门的新手还是已经在跑策略的老兵,应该都能找到立刻能用上的东西。如果你时间有限,至少把前六个问题仔细看完,它们是策略设计阶段最容易犯错的环节。后四个更偏工程和运维,可以边实践边对照。

ok,大致介绍结束,全文16000字左右,阅读差不多要半个小时,请自行备好咖啡或者清茶。咱们现在开始!

一、机器学习策略到底是什么

先说清楚一个根本问题:机器学习策略和传统规则策略的区别到底在哪。

传统规则策略,本质上是人类把经验写成如果那么,也就是if-else。比如"市盈率低于10且ROE大于15%",这是一条显式规则。规则从哪里来?从人的认知里来。问题在于,人的认知能同时处理多少个变量?五个?十个?再多就脑力不够用了。而且不同变量之间的非线性交互,人类直觉几乎捕捉不到。举个例子:市盈率和换手率之间的关系是线性的吗?不是。在某些市场环境下它们正相关,在另一些环境下负相关,切换的临界点在哪里人根本看不出来。传统的多因子线性模型本质上就是在做"给每个因子赋一个权重然后求和"这件事,它永远发现不了两两因子交互构成的新模式。

机器学习策略的核心差异就在这里:它不需要你显式地写出规则。你只需要提供特征X和标签Y,模型自己去学X到Y之间的映射关系。这个映射可以是非线性的,可以包含几十上百个特征的复杂交互,可以在高维空间中捕捉人类直觉发现不了的模式。我发现一个很有意思的现象:在我们团队的策略里,单因子IC最高的几个因子,在ML模型里的特征重要性排名反而不是最高。这说明ML模型学到了因子之间的互补关系,而不是简单地挑最强的用。这个能力是传统方法不具备的。

常见的ML模型选择中,XGBoost和LightGBM是A股各种量化社区中最主流的两个框架,原因很简单:对表格型数据表现极好、训练快、天然处理缺失值、有成熟的特征重要性输出。我对比过XGBoost、LightGBM和CatBoost在同样数据下的表现,前两者的IC几乎一致,CatBoost在类别特征多的场景下略有优势。深度神经网络在一些团队有尝试,但我个人经验是实盘中稳定性普遍不如GBDT系列,主要是因为金融数据的信噪比太低,深网络更容易抓到噪声而不是信号。

一个完整的机器学习策略工作流是这样的:每天收盘后,从数据库中拉出所有股票的几十个特征值,市盈率、动量、波动率、换手率等,形成特征矩阵X。然后定义这些股票未来五个交易日的收益率排名作为标签Y。把X和Y喂给模型,模型自动学到什么样的特征组合对应着较高的未来收益。次日开盘前,用最新特征值跑一遍模型,得到每只股票的预测得分,按得分排序,买前10%到20%,卖后10%到20%,循环往复。这个流程每个环节都有大量细节:数据清洗中的缺失值处理、特征工程中的截面标准化、模型选择时的验证策略、参数调优时的搜索空间设计、回测评估时的交易成本建模。每一个环节的偏差,在后面的环节中都会被放大。这些环节串在一起的能力,就是ML策略工程师的核心价值。

打个比喻:传统规则策略像老中医,望闻问切后凭经验开方子。机器学习策略则像是拿到了几十万份病历和对应的疗效数据,用算法找出什么样的症状组合对应什么样的治疗方案最有效。老中医的经验可能偏颇,统计规律不会。但统计规律也有自己的问题:它永远假设未来和历史一样,而市场里这个假设经常不成立。

这里有个关键陷阱需要说清楚:机器学习不是魔法。它学到的映射关系永远基于历史数据。如果未来市场结构和历史发生了根本性的变化,流动性分布变了、投资者结构变了、甚至交易规则变了,模型学到的规律可能一夜失效。这不是模型的错,是所有基于历史统计的方法都避不开的死穴。(所以像我每天都会看一下策略汇总,哪些个策略回撤超过最大限值了,超过了就要做好策略失效的预案)另外ML策略和传统策略还有一个本质区别——可解释性。规则策略中你知道为什么买卖,出了问题可以回溯到具体规则。ML策略中模型给你一个0.73的得分,你不知道这个得分到底来自低估值还是动量。这种黑箱特性在实盘中会带来额外的心理负担,尤其在策略表现不佳的时候。我们团队的补救措施是用SHAP值做事后归因,虽然不能完全替代可解释性,但至少在复盘时能大致知道模型的判断依据是什么。入门ML策略之前,需要先接受一个事实:你放弃了一部分可解释性,换取了处理高维非线性特征的能力。A股的经验是:这个取舍值得,但必须有配套的风控和监控来兜底。

二、特征因子怎么选

这个问题排在第一位,因为它决定了策略天花板有多高。选错特征,后面标签打得多巧妙、模型调得多精细,都是在错误的方向上努力。

一个我从2019年记到现在的教训:特征不是越多越好。当年我天真地以为把所有能找到的因子全塞进去让模型自己挑就行了。结果训练出来的模型对历史数据拟合得像教科书插图,样本外直接腰斩。复盘发现,一百多个特征里真正有信息量的不超过二十个,其余全是噪声。特征数量多不等于信息量大,没有IC支撑的特征是模型最大的敌人。

特征选择我总结了三步法:分类覆盖、IC筛选、共线性剔除。

**第一步:分类覆盖。**把特征按大类划好,确保每个重要维度都有代表因子。六大分类如下。价量类:多周期收益率(5日、10日、20日)、波动率、换手率、量比、日内振幅、买卖盘不平衡度。这类是使用人数最多、alpha衰减最快的类别。估值类:市盈率的静态、动态、TTM三个版本、市净率、市销率、股息率、企业价值倍数。估值因子在A股的有效性有所下降,但风格切换期仍然不能缺。基本面类:营收增速、净利润增速、ROE、毛利率、现金流比率、资产负债率、存货周转率。这类因子的信息半衰期很长,不像价量因子快速衰减,但更新频率低(季报才有更新)。资金流类:主力资金净流入、北向资金持仓变化、融资余额变化、大宗交易折溢价。这类因子在A股有特殊价值,因为A股的资金流向结构和其他市场差异显著。情绪类:涨停家数占比、融资买入占比、期权隐含波动率、偏度指数,在极端行情下尤其重要。另类数据:分析师预期修正、新闻舆情得分、研报覆盖变化、一致预期分歧度。这是alpha最不易衰减的方向但数据成本最高。这六类至少各选一到两个代表因子。很多新手只堆价量类因子,因为好算。但纯价量因子的alpha衰减极快,加入基本面和另类数据才是拉开差距的地方。在我的策略里,将近一半的因子来自基本面和资金流两个方向,这两个方向的失效速度明显慢于纯价量因子。

选完大类代表因子之后,我通常会让数据在仓库里沉淀一周,再做IC筛选。这一周的间隔不是浪费时间,而是给自己一个冷却期,避免带着"这个因子一定好用"的偏见去做IC分析。主观偏见是做因子筛选时最隐蔽的陷阱——你越是投入时间构建了一个因子,你越倾向于看到它有效的数据、忽略它无效的数据。冷却一周后重新审视每个因子的IC图表,你会发现自己的判断明显更客观。

**第二步:IC筛选。**每个因子和未来收益算Rank IC,推荐用Spearman秩相关系数而非Pearson,因为金融数据很少满足正态分布。取滚动三年以上的IC均值,绝对值低于0.02直接淘汰。同时要看IC的稳定性和方向一致性。IC标准差越小越好,IR(IC均值除以IC标准差)大于0.3保留,低于0.15丢弃。另统计IC为正的月份占比,如果超过60%的月份IC为正,说明有稳定预测方向。除了定量指标,我还会把因子的IC序列画成时序图,肉眼判断是否在系统性地下滑。如果是,哪怕定量指标还过得去,也列入观察名单。此外还要分析IC的衰减曲线:看因子在不同预测窗口下的IC变化。一个好因子通常在短窗口上IC最高,随窗口拉长逐渐衰减。如果某个因子在长窗口上的IC反而比短窗口高,那可能是数据泄露或者未来函数造成的伪IC。

**第三步:共线性剔除。**两个因子高度相关,相关系数绝对值超过0.7,只保留IC更强的那个。对树模型来说多重共线性不像线性回归那样直接破坏模型,但会导致特征重要性被稀释。两个高度相关的因子本质上提供重复信息,多一个没多信息反而增加过拟合风险。剔除后还要用训练好的模型输出特征重要性,对排名垫底的几个因子做人工审核,确认不是因为数据质量问题导致的低重要性。最终保留的特征数量控制在15到30个之间,太多则噪声主导。做完之后对所有因子做截面Z-score标准化,消除量纲差异,保证后续IC分析和异常值检测在统一尺度上。我记得有一次,我们团队发现一个估值因子的Z-score突然飙到了6以上,排查后发现是数据商把某些股票的每股收益搞成了负值,导致市盈率计算异常。如果没有截面标准化,这个异常值可能在一个月后才被发现。

三、标签到底怎么打

标签定义是ML策略中最被低估的环节。很多初学者把80%的精力放在调模型上,但标签如果定义错了,后面全是白费。标签就是你想要模型学到的东西,你教它什么它就学什么。

常见的标签定义有三种,各有优劣,理解它们的适用场景是标签设计的第一步。

**第一种:回归标签。**直接用未来T日收益率作为标签,比如T+5日的对数收益率。模型做回归任务,输出连续收益预测值。优点是简单直接、保留全部信息、不需要人为设阈值。缺点是噪声太大,日级别收益率信号非常稀薄,信噪比通常低于0.05。直接用原始收益率做标签,模型R方很难超过1%,对实盘决策来说精度太低。回归标签在低频策略(预测窗口长于一个月)中更有价值,因为长期收益的信号比短期强,且市场microstructure的噪声在长窗口上会被自然平均掉。我在做月度调仓策略时会切换到回归标签,效果明显好于日频场景。

**第二种:分类标签。**把未来收益离散化。在每天截面上按未来收益率排序,前20%标为买入(类别1),后20%标为卖出(类别0),中间60%丢弃不用。优点是滤掉了噪声:模型不需要精确预测收益率数字,只需学会区分好股票和差股票的相对顺序。三分类天然适配多空组合思路。缺点是需要设定阈值。我测试过10%、15%、20%、25%、30%五个阈值,在A股中证800池子上20%的IR最稳定。对自己数据集不确定的话,最简单的验证就是用验证集上的ICIR做比较,选ICIR最高的那个阈值。注意阈值优化本身也会引入一定的过拟合,所以建议把阈值选择也纳入验证集的调参流程中,而不是在训练集上确定。

**第三种:排序标签。**截面上按未来收益排序,用排位百分比作为标签,排名前1%为0.99,最末为0.01。等价于RankNet的学习目标,天然适配截面多空策略。优点是连阈值都不用设,保留了全排序信息。缺点是对极值非常敏感,一个异常涨跌停可能扭曲整个截面的排序,所以使用排序标签时前端数据清洗尤其关键。

新手建议从分类标签入手,效果稳定、概念直观、与传统多因子策略衔接最顺畅。预测窗口选T+5,和日频策略调仓节奏匹配。如果做周频策略,窗口放大到T+15至T+30。标签设计上还有三个必须做的前置清洗:剔除涨跌停样本、剔除ST股和上市不满60天的新股、对原始收益率做行业中性化后再排序打标签。这三个清洗步骤写死在代码前置流程里,从不跳过。不清洗的代价是回测IC虚高,实盘落差大得让人怀疑人生。另外标签窗口的起始点也要注意。如果标签窗口从T+1开始,相当于你假设在获得信号到执行交易之间只有隔夜的延迟。但实际上可能还有数据处理的延迟。我一般把标签窗口设为T+2到T+7,跳过最近一个交易日,给数据处理留出缓冲。

标签设计的另一个重要维度是样本权重的分配。不是所有训练样本都同等重要。市值越大的股票,流动性越好,信号噪声越低,我通常会按市值的平方根给样本赋权重。另外时间维度上也有权重差异:最近一年的样本权重要高于三年前的样本。样本权重和标签定义的配合非常关键。举个例子,如果标签用的是分类标签,而样本权重用的是等权,那么小盘股的噪声会拉低整个模型的预测精度。正确的做法是让样本权重和标签类型保持一致:分类标签配合市值权重,回归标签配合时间衰减权重。这个小细节在文献中很少被提及,但在实盘中对IC的提升是可测量的。

还有一个容易被忽略的标签设计问题:标签是否需要平滑。直接用排序分位作为标签的话,排名靠前的几只股票之间的标签值差距可能和排名靠后的几千只股票之间的差距一样大,这显然不合理。我采用的做法是对排序标签做一次非线性变换,用logit函数把均匀分布的分位值映射到一个更符合实际收益分布的空间。具体来说,排序百分比p做变换后的标签值为log(p/(1-p)),这个变换后标签的分布更接近正态,对回归模型和分类模型都更友好。变换虽然简单,但在验证集上的IC提升通常是稳定且显著的。

最后提醒一个常见错误:不要在标签中使用任何未来信息。听起来是废话,但在实践中很容易踩坑。比如你在计算"未来五日收益率"时用了未来五日的均价而非最后一日的收盘价,那你实际上在标签中混入了比预测窗口更远的信息。财务数据尤其容易出这类问题,因为季报发布时间和报告期之间有一个天然的时间差。如果要使用基本面数据作为因子,必须严格按财报的实际发布日期来对齐时间轴,不能用报告期的截止日期。关于这一点我踩过的坑多到自己都不好意思说。

四、训练集怎么选

训练集的选择,本质上在两个互相矛盾的目标之间找平衡。一方面训练集越长越好,越长覆盖的市场场景越多,牛市、熊市、震荡市、急涨急跌、风格切换,模型看到的案例越丰富,泛化能力越强。另一方面训练集越长,早期的数据分布和当前市场差异越大。用十年前的规律来预测现在,本质上是在用不同的游戏规则积累的经验来玩当前这局游戏。

十年前A股什么样子?注册制没推、两融规模远不如现在、量化交易占比几乎为零、投资者结构和微观流动性与今天完全不同。让模型学2015年杠杆牛的经验来指导2025年交易,大概率南辕北辙。很多团队因此只取最近两年数据,这又走到另一个极端:训练集里或许根本没有熊市样本,遇到下行行情模型一定会失灵。我的结论:训练集至少覆盖一个完整的牛熊周期,但权重上给近期数据更高。

按交易日算,三年750个交易日是建议的最低长度。五年1250个交易日是理想选择。超过八年的数据通过样本权重打折扣:最近一年权重1.0,一到三年前0.8,三到五年前0.5,五年前0.3。模型会优先拟合最近规律,同时保留对历史极端行情的记忆。操作层面,三年下来大约三百万条样本,XGBoost或LightGBM在这个规模上跑得最舒服。如果池子更大(全A加中证1000加北交所),按市值分五层做随机采样,每层保证足够代表比例。还有一件必须要做的事:给训练集做市场状态标注。单边上行、单边下行、宽幅震荡、窄幅波动四个标签,在验证时逐状态看IC。只有所有状态下IC都稳定为正,才能说策略有真正的泛化能力,不只是靠beta吃饭。我上个月刚做过一次复盘,发现我们的策略在单边下行市中的IC和牛市中的IC差距达到了近40%,这个信息直接驱动了最新的因子调整决策。

训练集里必须包含极端行情。如果训练数据全是牛市和震荡市,模型根本没见过暴跌。这个坑我身边至少三个团队踩过——训练集始于2020年,完美避开了2018年熊市,结果2023年下半年市场调整时策略直接崩了。另一个小技巧:如果某些特征只有近期才有(比如北向资金数据),不要因此缩短训练集。对该特征缺失的部分用截面均值填充,同时建一个缺失标记特征,让模型自己学会"缺失"本身也可能是信息。

训练集构建中还有一个重要的技术决策:要不要做数据增强。在图像识别中数据增强是标配,在金融ML中这个问题有争议。我的看法是:金融数据不适合做数据增强,但适合做场景重采样。什么意思?如果你的训练集中牛市样本占70%、熊市占15%、震荡市占15%,你可以对熊市和震荡市的样本做上采样(bootstrap resample),让三个市场状态的样本量接近平衡。这不叫数据增强,叫样本重平衡。这个操作能让模型对极端行情的敏感度明显提高。

另一个训练集的实操问题是特徵缺失值的处理。由于各因子的起始时间不同(比如某些另类数据从2022年才有),回补历史数据时会出现大片缺失值。常见的做法是用截面均值或0填充,但这会引入偏差。我更推荐的做法是:对早期缺失的特征,用一个简单的线性模型根据同期其他可用特征来"猜测"该特征的近似值,然后给这些近似值贴上一个较小的样本权重。这比直接用均值填充更可靠,也不会引入偏见。当然如果某个特征缺失超过整个训练集40%的区间,说明这个特征可能就不应该用。我宁愿砍掉一个不完整的特征,也不愿意让它污染整个训练集。

还有一点关于训练集的时间切片方式。大多数团队按自然日划分训练集,每天的数据是一个独立截面。但可以做得更细:在同一天的截面上按行业分组采样,确保每个行业在每天的训练batch中都有代表。LightGBM和XGBoost不支持原生的分组采样,但你可以通过在每棵树的子采样中手动控制来实现近似的效果。我自己写了一个行业分层采样器包裹在LightGBM外面,代码只有三十几行,但对行业中性化的效果比事后做中性化更好。

五、验证集怎么定

先区分两个概念:验证集(Validation Set)用来调超参数和做模型选择;测试集(Test Set)用来最终检验策略效果,在策略定型之前不能碰。很多人混为一谈,用测试集来调参然后汇报结果,这在方法论上是严重的错误。验证集是你开发过程中的"方向盘",测试集是你交卷时的"成绩单",两个角色完全不一样。

金融时序数据做验证,最大的坑是不能用标准K折交叉验证。因为金融数据有时序结构,今天的数据和昨天高度相关。标准K折把数据随机打乱分到不同折里,模型可能在训练集中学到2024年的模式,然后在验证集里"预测"2021年,等于考试前看答案。正确的做法是用时序验证,永远用过去的数据训练、用未来的数据验证。

**扩张窗口法:**训练窗口从最早时点不断向后扩展。第一轮2019到2020训练、2021验证;第二轮2019到2021训练、2022验证;第三轮2019到2022训练、2023验证。每轮验证集都是训练集时间轴之后的独立样本。这个方法评估模型在整个历史区间上的平均表现,适合看长期稳定性。

**滚动窗口法:**固定训练窗口长度比如三年,滚动移动。第一轮2019到2021训练、2022验证;第二轮2020到2022训练、2023验证;第三轮2021到2023训练、2024验证。更贴近实际场景,因为你拿到新数据时总是用最近N年重新训练。

我的做法是两种结合:先用扩张窗口看长期IC趋势,再用滚动窗口看近期稳定性。如果两种方法下模型IC排序基本一致,说明策略稳定性不错。差距很大的话,比如扩张窗口平均IC是0.04而滚动窗口最后一期降到0.01,近期市场结构可能发生了显著变化,需要排查。我遇到过的情况是某次验证发现差距很大,排查后发现是某个基本面因子在膨胀窗口中被历史数据的高IC拉高了均值,实际上这个因子从2023年下半年就已经失效了。验证集上还有一个铁律:最多调三轮超参数,三轮后不论表现如何定稿不再改。超过三轮还能提升的话,大概率只是拟合了验证集上的噪声。在验证集上过度优化的策略,在测试集上一定会现原形。

验证阶段还有一个关键但常常被省略的检查:验证集的IC是否和训练集的IC在量级上一致。如果训练集IC是0.06,验证集IC只有0.02,说明模型严重过拟合了。健康的比例是验证集IC达到训练集IC的60%到80%。低于50%基本可以断定过拟合。这个检查极其简单但极其有效,我在每次验证必做。

还有一种更严格的验证方法叫Purged Cross-Validation,专门为金融时序数据设计。它的核心思想是在训练和验证之间插入一段"空白期"(purge period),排除掉那些因为时序相关性而可能泄露信息的样本。具体来说,如果训练集的最后一天是2023年12月31日,验证集不从2024年1月1日开始,而是从2024年1月11日开始,中间空出10个交易日。这10天的数据既不用来训练也不用来验证,纯粹作为缓冲。这个做法的逻辑是:训练集最后一天的样本中使用的因子(比如10日动量),其信息会延续到未来至少10个交易日。如果你不做purge,验证集里靠近训练集边界的那些样本其实是"半泄露"的。我对比过有purge和没有purge的验证结果,不做purge会让验证集IC虚高大约5%到10%。比例不大但方向一致,日积月累就会导致系统性高估模型表现。

验证集上还需要特别注意行业轮动的影响。假设你的验证集恰好是一段"科技股暴涨、银行股横盘"的行情,而你做的行业中性化只是简单的截面回归,那么模型在验证集上的高IC可能还是部分来自行业beta。一个更保险的做法是在验证集上跑一遍Fama-French三因子归因(或加上行业因子的扩展版),看纯alpha在验证集上的表现是否依然稳健。如果alpha项的表现明显弱于总信号,那你的策略还是押注了风格。

验证阶段还可以引入一个实用的诊断工具:学习曲线。所谓学习曲线,就是以训练集大小为横轴、验证集表现为纵轴画的一条曲线。当训练集从10%逐步增加到100%,验证集的表现应该稳步上升并最终收敛。如果验证集表现很早就收敛甚至开始下降,说明模型容量不足以从更多数据中获益;如果验证集表现一直在上升且收敛趋势不明显,说明模型还能从更多数据中获益,可以考虑扩充训练集。学习曲线在深度学习领域很常见,但在表格型ML中很少有人用。我把它引入到因子模型中之后,发现它能有效地判断模型复杂度和数据量的匹配程度,避免了多次过度复杂的模型选择错误。

还有一个验证集的实用技巧:追踪每个因子在不同验证窗口上的IC变化。如果你把验证集按半年切片,观察每个因子在每一段上的IC,你可以很直观地看到哪些因子稳定、哪些因子在衰减。这个分析在表格型数据中做起来很快,但信息量很大。我每个季度复盘时都会做一份"因子IC热力图",横轴是时间窗口,纵轴是因子名称,颜色是IC值。热力图中如果出现一整片蓝色(IC下滑),那就知道策略的某些因子在退役了。

六、测试集怎么划分

测试集的地位是神圣不可侵犯的。它必须是在模型开发全流程中从未被触碰过的数据。一旦你在测试集上看过一次结果并据此修改了模型,测试集就降级为验证集了,你永远失去了一段真正独立的样本外评估。

我见过最荒唐的事:一个团队做了三年ML策略,每次都在同一个测试集上跑,每次看到不好的结果就回头改因子、改参数再跑。三年后测试集已经变成训练集的一部分了,最终实盘表现和最后那版回测之间差了将近40%。这不是策略的问题,是评估方法完整崩塌。

测试集的划分原则有三条。第一,时间上严格在训练集和验证集之后,写代码时在时间处理模块加断言:测试集最早日期必须大于训练集和验证集的最晚日期。第二,长度至少六到十二个月,且最好包含一次市场级别的较大波动。如果测试集恰好是一段单边上涨行情,IC好看也别得意,很可能只是跟着beta涨的。第三,测试指标不能只看整体,必须按月拆开分析。看每个月的IC、负IC月份占比、IC月度标准差。一个月IC崩了是正常波动,连续两个月IC为负就要警惕了。除了逐月分析,我还会看IC的滚动标准差。如果IC均值稳定但标准差在持续扩大,说明模型预测的一致性在下降,这也是退化的前兆。

另外,测试集不应该只跑一次。我建议每季度用新产生的样本外数据追加测试,形成一条随时间延伸的IC曲线。这条曲线的趋势比任何一个截面的单点IC都有价值。如果IC曲线是平稳的,模型健康;如果持续下行,模型正在退化;如果突然跳水,市场结构可能发生了突变。三种形态对应完全不同的行动方案。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 测试集逐月拆分分析
test_results['month'] = test_results['date'].dt.to_period('M')
monthly_ic = test_results.groupby('month').apply(
    lambda g: g['pred'].corr(g['label'], method='spearman')
)
print('月度IC均值:', monthly_ic.mean(), '±', monthly_ic.std())
print('负IC月数:', (monthly_ic < 0).sum(), '/', len(monthly_ic))

# 滚动IC趋势 - 看趋势比看单点重要得多
rolling_ic = monthly_ic.rolling(3).mean()
print('最近3个月滚动IC:', rolling_ic.iloc[-1])

测试集还需要做的一件事是对预测残差做自相关检验。如果模型在测试集上的预测残差存在显著的一阶或高阶自相关,说明模型没有完全捕捉到时间序列中的可预测部分,还留下了结构化的信息没有被利用。这种情况下,可以考虑加入滞后特征或改用时序模型(LSTM、Transformer等)来捕获残差中的剩余结构。另外测试集也要和纸面交易(Paper Trading)配合使用。纸面交易是在真实市场环境中用模拟资金跟踪信号表现,它解决的是回测中无法完全模拟的微观结构问题。测试集的统计检验 + 纸面交易的流程检验,两者结合才能对策略的真实表现形成完整的判断。

还有一个关于测试集的进阶话题:对抗样本测试。在传统ML中对抗样本是指对输入特征做微小扰动,看模型输出是否剧烈变化。在金融ML中可以做类似的测试:对每个特征单独加一个适度扰动(比如加0.1个标准差),看模型预测排名是否剧烈变化。如果某个特征的微小扰动导致预测排名大幅波动,说明模型对这个特征过度敏感,这通常意味着过拟合。对抗测试本来是用在图像识别上检测模型鲁棒性的,我把它移植到金融ML中,效果出乎意料地好。它帮我发现了至少三个过度依赖单一因子的模型版本。

测试集的最终交付物不应该只是一个"IC=0.045"的数字。我建议把测试结果整理成一份结构化的测试报告,至少包含以下内容:整体IC和ICIR的时序曲线、逐月IC的分布直方图、分层IC(按行业、按市值、按波动率分组)、换手率曲线、最大回撤曲线、不同信号强度分位下的分组收益表现。这份报告的价值在于它不只告诉你策略"好不好",而是告诉你策略在什么情况下好、什么情况下不好。这个信息对实盘中的仓位管理和心理预期管理都非常重要。

七、择时与风控

这是ML策略中最容易被偷懒的环节。一个迷思是模型输出足够准了,风控就是多余的。这个想法逻辑上站不住,再准的模型也有置信区间,尾部风险不会因为历史胜率高就放过你。我记得很清楚,有一次我们的策略连续八个月表现优异,于是我放松了波动率控制的阈值。结果第九个月市场突然剧烈震荡,单月回撤超过了过去八个月累计收益的一半。从那以后我再也不敢省风控。

择时风控我分成信号层面和组合层面,两者互补。

**信号层面第一招:分位数筛选。**模型输出得分后只做极端值。5000只股票只买前10%、只卖后10%。逻辑是模型对极端值的判断比中间值更可靠,中间得分接近中位数的股票,模型对它们没有强判断,强行参与只增加噪声。这里的核心参数是筛选比例。太窄(比如5%)会让组合过于集中,太宽(比如30%)会让噪声侵入。我的经验是在资金容量允许的前提下尽量窄,取10%在多数场景下是合理的起点。

**信号层面第二招:信号强度门限。**每天计算预测得分的截面标准差。如果某天标准差特别低,模型对各股票区分度很差,市场可能缺乏alpha机会。量化做法:用过去60天信号截面标准差做滚动Z-score,当天Z-score低于负1.5触发降仓。这个规则不追加新数据就能显著提升夏普比率。

**组合层面第一招:行业中性化。**对信号做行业分组回归,取残差作为纯alpha信号。如果不去行业beta,模型可能只是学会了买某个行业的股票,而这是beta不是alpha。验证方法很简单:回测中看组合在每个行业上的平均偏离,如果有行业长期正偏离,行业beta占比肯定高。第二招市值中性化同理,避免大小盘风格轮动对信号的干扰。

**组合层面第三招:波动率控制。**设目标波动率比如年化15%,每天计算组合实际波动率,超出目标按比例缩仓。公式:目标仓位比例等于目标波动率除以当前滚动20日实际波动率,裁剪到0到1。此外还需要一个实时信号质量监控面板,跟踪IC、换手率、行业暴露。IC连续五天为负降半仓;换手率从20%飙升到50%触发检查。这些自动化风控规则虽然简单粗暴,但在对抗尾部风险方面是最靠谱的手段。此外,我还监控信号对常见风险因子(市场beta、规模因子、价值因子)的暴露度。如果暴露在时间上有漂移,说明策略风险特征在变化,需要做归因分析。风险因子暴露监测的价值体现在它能提前预警策略风格的漂移。

风控体系中还有一个重要的概念叫"熔断后的恢复机制"。当风控规则触发降仓或停仓后,什么时候恢复正常仓位是一个需要设计的问题。最简单的做法是设定固定的冷却期,比如降仓后至少等三个交易日再恢复。更好的做法是设计一个基于信号质量的恢复条件:只有当信号IC连续两天为正、且波动率回落到目标范围以下时,才逐步恢复仓位。恢复也不应该是一次性的,而是分批恢复,比如第一天恢复到50%,第二天恢复到75%,第三天恢复到100%。分批恢复的好处是在恢复过程中依然有缓冲,万一触发降仓的因素还没有消退,不至于一恢复就又被打回去。

另外关于行业中性化和市值中性化的执行顺序应该怎么排。我的做法是先做行业中性化,再做市值中性化。原因是市值在A股是一个横跨行业的全局因子,如果先做市值中性化再去做行业中性化,行业中性化的回归中会包含一部分已经被市值中性化处理过的信息,导致双重扣除。具体实现上,我用的是两步回归法:第一步信号对行业哑变量做回归取残差,第二步残差对市值做回归再取残差。这个顺序在数学上是有讲究的,但大部分量化框架默认的顺序是错的。如果你用的是现成的量化框架,建议检查一下中性化的默认顺序。

八、突发情况的处理方案

行情不会按你设定的剧本走。ML策略尤其脆弱,因为它依赖统计规律,而突发事件往往打破这些规律。下面四类突发情况,每一类背后都有真实的事故,也是我这些年踩坑踩出来的血泪经验。

**第一类:流动性枯竭。**2024年1月那波小微盘流动性危机最具代表性。大量策略一天回撤超过全年收益,因为小盘股的流动性突然蒸发,模型还在输出买入信号。根本问题在于:流动性是ML策略中几乎所有模型都忽视的一个隐蔽风险维度。处理方案分三层。第一层设置流动性硬过滤器,实时成交额相对历史均值降到50%以下,当天不交易。第二层市值因子加负向惩罚,小市值暴露不超过组合20%。第三层组合加权平均日均成交额不低于绝对阈值。我还额外加了一条规则:如果某只股票从入选到执行之间经历了流动性突变(比如日均成交额从2亿暴跌到3000万),系统自动取消对该股的交易并发出告警。

**第二类:黑天鹅。**政策突变、国际冲突、监管重拳。ML模型从历史频率分布中学习规律,对极端尾部几乎没有预测能力。你无法在训练数据里告诉模型"突然加征关税"会发生什么。处理方案:设置宏观风险警戒线,监控VIX或隐含波动率,超过过去三年均值两倍自动触发半仓。这不是精密科学,而是经验性的应急预案。极度不确定面前,少犯错比多赚钱重要得多。我还备了一份"极端行情checklist"贴在屏幕旁边,里面列了十项必须立即执行的动作,包括检查所有持仓的流动性、确认止损单状态、通知团队进入紧急状态。这份checklist在2024年1月和2025年4月各被激活了一次,每次都帮了大忙。

**第三类:模型退化。**最隐蔽的威胁。不是突然失效,而是慢慢地IC从0.05跌到0.04再到0.03,等你发现可能已经持续两个月。初期症状通常很轻微:整体IC还可以,但细分到某些行业或市值段的IC已经开始下滑。解决方案:按月跟踪滚动IC的12个月均值;同时做分层IC分析,把股票按行业和市值分组,看每一组的IC是否都在健康范围内。连续三个月下滑触发重新训练。生产环境永远保留最近三个模型版本,新版本上线IC下降可以快速回滚。退化的常见伴随现象是换手率异常升高,模型在不确定性增加时往往输出更极端的得分,导致频繁换仓。换手率是模型退化的先行指标,比IC更灵敏。

**第四类:数据源故障。**某天数据商接口挂了,基本面因子全变NaN。模型直接吃进NaN,预测完全跑偏。处理方案:为每个特征设缺失值占比监控,某天缺失超过10%触发数据回补,同时暂停该特征使用直到恢复正常。上线前写好数据完整性校验脚本,每天生成数据前确认所有特征列的缺失率和异常值比例在正常范围内。

核心心法:不要用ML模型解决ML模型自身的问题。突发事件本质是训练集从未覆盖的市场条件,不要指望模型给出正确预测。应该切换到规则层面的应急方案:硬止损、流动性阈值、波动率上限。机器擅长找规律,人也擅长找规律,但人还擅长一种机器做不到的事:承认有些时候规律不存在。在那种时刻,把控制权从模型手里拿回来,是唯一正确的选择。

九、策略怎么迭代升级

ML策略的迭代边际成本天然比规则策略低,但也因此带来一个危险:高频迭代陷阱。你看到策略最近一个月表现不好,马上加两个新因子、调超参数、甚至换模型架构,回测变好了就上线。如此往复,你在持续地过拟合最近一个月的行情波动。这个方法在震荡市能撑三五个月,但一到风格切换或极端行情就会全面崩塌,因为整个策略优化的不是alpha,而是对近期噪声的记忆。我曾经犯过这个错误:在2022年连续对同一个策略做了五次迭代,每次都因为当月表现不如意。回头来看,那五次迭代去掉任何三次,最终的实盘表现反而会更好。从那之后我严格锁定了迭代频率。

我定下的迭代节奏是每季度做一次完整复盘,包含四个标准动作。

**第一步因子更新:**把最近一个季度新增数据回补到因子表中,重新计算所有因子的滚动IC和IR。标记IC显著下滑的因子,连续两个季度IC低于0.01考虑替换。同时从新数据源试一两个新因子探路,不批量引入,每次最多加三个,避免因子集合膨胀失控。同时更新因子协方差矩阵,如果协方差结构发生大变化,说明市场因子定价机制可能在改变。

**第二步模型重训:**在扩增后的训练集上重新训练。不能偷懒只重跑代码,必须对比新旧两个模型在样本外数据上的预测差异。预测排名相关系数低于0.6说明模型发生了根本变化,要逐因子排查。很多时候会发现某个因子突然失效了,而只看新模型IC汇总根本注意不到。

**第三步回测验证:**用最新一个季度新产生的样本外数据验证。永远用你还没看过的数据来检验,这是所有ML应用场景的铁律。**第四步灰度切换:**新旧两个模型各跑一个观察窗口,小仓位跟踪两边信号。至少观察两到三周,确认差异模式稳定后再决定是否切换。慢但安全。

迭代不要只盯alpha,也要盯风险指标的连续变化。换手率持续上升、行业暴露集中度增加、最大单票权重趋势性增大,这些信号比IC下降早两到四周。风险指标是巡警,IC是法医。除了季度复盘,建议每月做轻量评审:只看五个核心风险指标,没触发二级告警就不动策略。这样既不增加不必要的迭代噪音,也不会错过真正的风险信号。一个好的迭代习惯是每次复盘后写一份迭代记录,写明改了什么、基于什么决策、预期效果是什么、实际效果怎么样。这份记录三个月后回头看,你会发现自己当时的判断有多少是理性的、有多少是被近期行情情绪驱动的。

迭代流程中还有一个容易被忽视的环节:因子的"退役"仪式。很多人只关注加新因子,忘了需要定期清理失效的旧因子。我的做法是每个季度复盘时,把连续三个季度IC低于0.01的因子正式标记为"退役",从后续的特征列表中移除。退役的因子不会立刻删除,而是归档到一个单独的因子库中,以防未来市场环境变化导致它再次生效。这个做法解决了一个隐形问题:因子集合随着时间推移越来越臃肿,噪声因子占比越来越高,模型的有效自由度被不断稀释。

另外有一个关于迭代频率的补充观点:不是所有策略都需要同一频率的迭代。高频策略(日均换手率超过50%)可能需要每一个月到两个月做一次因子IC扫描,因为高频alpha的衰减速度极快。低频策略(月度调仓)可以每季度甚至每半年做一次复盘。迭代频率应该匹配alpha衰减速度,而不是机械地跟着日历走。这个原则在实盘中帮我省了大量不必要的迭代工作。

迭代的另一个陷阱是"过度工程化"。随着时间推移,你不断给策略加新因子、新过滤条件、新风控规则,整个系统的复杂度越来越高。到了某个临界点,新增复杂度的边际收益变为负值。我的经验是:当一个策略的因子数超过40个、风控规则超过10条的时候,停下来做一次系统性的简化。删掉那些贡献不到1%IC的因子,合并那些逻辑相似的规则。好策略的标志不是复杂,而是精炼。越简单的策略越容易维护、越不容易出未知bug、越容易在压力下保持一致的表现。

十、实盘落地时的注意事项

从回测到实盘,这最后一步才是真正考验ML策略工程化能力的地方。回测做得再漂亮,不处理好实盘特有的摩擦,一切都是纸上富贵。以下六个实操要点,每一个都是从真实的实盘事故中总结的。

**第一,滑点成本必须建模。**回测里按收盘价成交,实盘里成交价一定偏离收盘价。A股大盘股买卖价差1到2个基点,小盘股可能达到几十个基点。我的做法:每笔订单按市值分组估计滑点,订单量超过该股日均成交额1%的部分额外加5到10基点冲击成本。滑点不仅要看均值还要看分布,如果某些月份滑点特别大,可能是流动性出了问题,应该单独标记。回测中提前打折扣,回测曲线才能接近实盘。

**第二,成交概率不是100%。**涨停买不到跌停卖不掉。回测引擎中对涨跌停板设成交概率为零,信号排序时直接剔除涨跌停股票,不让其进入候选池。小盘股流动性和涨跌停风险往往叠加在一起,同一个股票既是小盘又封涨跌停的时候,风险敞口是倍增的。

**第三,信号延迟。**收盘后跑模型,下个交易日开盘下单,中间有隔夜跳空。解决方向:要么拉长预测窗口到T+5或T+10,降低单日执行精度要求;要么把信号生成前置,比如用下午1点半数据预测收盘信号。我倾向于前者,因为后者引入了盘中数据和收盘信号之间的信息不一致问题。

**第四,交易成本。**A股印花税对高频策略是极大负担。日均换手率30%,年化双向换手约150次,印花税0.05%等于每年7.5%的摩擦成本。如果超额年化收益15%,印花税吃掉一半。降换手率的收益空间往往大于继续压榨IC。回测中单独加印花税成本项,比较不同参数时才能准确衡量真实收益。融券成本也是重要变量,A股融券成本波动大且常有券源不足的问题,回测中保守估计一个较高费率。

**第五,数据质量是地基。**回测数据是洗过的,实盘数据是实时流入的,质量不在一个量级。特征因数据商接口超时变NaN怎么办?基本面是三十天前静态数据但价量是实时的怎么办?容错逻辑必须上线前写好测好。我的原则:任何特征缺失的股票当天不给信号,宁愿少做也不错做。这不是保守,是生存代价。

另外补充一个容易被忽略的实盘细节:交易所的撮合规则。A股采用价格优先、时间优先的连续竞价撮合机制,但实际成交中还有一个隐形成本——订单簿的厚度。表面上你有2000万要买,但卖一价格上可能只有80万的挂单。你的单子会先吃掉卖一的80万,然后卖二价格上的单子比你预期的买入价高了两分钱。这个跨价位的成本在回测中几乎总是被低估。小资金可能不在乎这两分钱,但一旦资金体量上来了,跨价位冲击成本会变成一个不可忽略的数字。在策略设计阶段就应该注意持仓分散度,避免在单一股票上的订单量超过其日均成交额的3%。

**第六,监控体系比策略本身更重要。**最可怕的不是模型不准,而是你不知道它什么时候开始不准。我的每日自动监控包含五个指标:每日信号IC、每日信号换手率、每日持仓行业偏离、每日最大单票权重、每日滚动20日组合波动率。任一指标触发偏离阈值,系统自动告警并在下个交易日执行风控动作。另外建议加定时完整性检查:每天开盘前确认所有数据管道正常、所有特征值在合理范围、所有模型文件加载成功。花一个小时写好这个检查脚本,未来可能省下你一整天的问题排查时间。

实盘运行中最容易被忽视的一个环节是文档和日志。听起来很枯燥,但在故障排查时价值不可估量。我要求团队对每次交易记录下详细的执行日志:时间戳、信号值、因子值、成交价、滑点、下单失败原因。当策略表现出现异常时,翻看日志能帮你在一小时内定位到是数据问题、模型问题还是执行问题。没有日志的话,你可能花三天也找不到原因。不要省这个功夫。一个完整的日志体系至少包含三级:交易级日志(每笔下单的执行情况)、日级日志(每天的信号、持仓、风险指标汇总)、月级日志(每月的策略评估报告)。每一级有不同的受众和不同的用途,但缺一不可。

最后想说一下心态问题。ML策略从开始研发到稳定盈利,中间的时间通常是以年为单位计算的。你可能花三个月做出一个看起来IC很好的策略,花六个月调试所有工程细节,再花三个月跑纸面交易发现一堆新问题。这个过程中最大的敌人不是市场,是自己的耐心。我见过太多人在第三个月就放弃了,因为回测和实盘的落差太打击人。但真正跑出来的那些人,都是在坚持到第十二个月之后才看到拐点的。ML策略不是一个短期项目,它是一个需要持续投入和迭代的系统工程。如果你刚开始做,给自己至少一年的时间来犯错和学习。这一年里你可能做不出盈利的策略,但你会积累起别人花多少钱都买不到的经验。

结语

这篇文章到这里超过了一万五千字。如果你从头看到了这里,我想你已经get到了我最想说的东西:ML策略的本质不是炫技,不是调一个深度网络然后等着数钱。它是一套严密的工程体系。特征因子是原材料,标签定义是教学目标,训练集验证集测试集是三道防火墙,择时风控是安全带,迭代机制是发动机,实盘监控是仪表盘。

任何一个环节偷懒,链条就会在未来某个时间点断掉。而交易中最残酷的地方在于:你永远不知道链条在哪里会断,直到它断了。

我和我的团队做了六年,踩过几乎所有能踩的坑,也做出过连续十二个月正信号IC的策略。这篇文章里的每一个建议,背后都有一次真实的事故作为代价。希望新入这个方向的人至少能用更低的代价学到这些经验。如果你在实践中遇到了文章没覆盖到的问题,欢迎留言讨论。策略是活的,好的讨论是让它变好的唯一方式。

最后留一句话:机器学习策略做得好坏,很多时候不在于你选了多先进的模型,而在于你把前面提到的工程细节做到了哪个程度。细节的颗粒度,就是超额收益的颗粒度。这也是六年来我反复被市场教育之后,留下的最深刻的一条经验。

写完这十个问题,我突然想起六年前刚开始做ML策略的时候,林总跟我说过一句话:做量化的人分两种,一种把ML当黑箱用,一种把ML当工程系统做。第一种人运气好的时候能跑出来,但总有一天会被市场教育回原点。第二种人走得慢,但走得远。希望读完这篇文章的你,至少有了做第二种人的方法和勇气。

机器学习不会告诉你市场的真相,它只会告诉你历史上最接近的答案。而你要做的,是在它犯错之前准备好Plan B。

【声明】本文为量化策略研究方法论讨论,不构成任何形式的投资建议。文中提及的方法、框架、参数均为作者个人研究与经验总结,不代表任何机构观点。机器学习策略存在模型风险、过拟合风险及市场环境变化导致的策略失效风险,读者应充分认知相关风险并独立判断。市场有风险,决策需谨慎。


来源: 微信公众号

目录

×