Skip to content

12.5 交叉验证与泄露:金融里最容易踩的雷

课前导读

本章目标

适合读者:已经理解机器学习基础、训练集、测试集、过拟合和回测概念的 A 股投资者。

本节重点:你将学会判断普通 K 折为什么可能误导金融模型,理解时间顺序、purge、embargo 的直觉,并用泄露清单审查自己的策略研究。

学习难度:advanced

预计用时:约 40 分钟

在量化研究里,模型复杂不一定危险。

真正危险的是:你以为模型在预测未来,其实它已经偷偷看过未来。

这类问题叫“泄露”。

它像考试前有人把答案夹在草稿纸里。

考出来的分数再高,也不能说明学生真正会了。

金融机器学习的验证,首先不是问“模型多聪明”。

而是问“它有没有在公平考场里答题”。


知识正文

1. 类比:金融验证像给未来考试封卷

先想一个生活场景。

老师要检验学生是否真正掌握知识。

如果模拟题里混入了正式考试的原题,学生成绩会很好看。

但这不是能力,而是信息提前泄露。

股票预测也是类似。

我们训练模型时,输入是今天或过去的信息。

目标通常是未来一段时间的收益、涨跌或排名。

如果验证时把未来信息混入训练过程,模型分数就会虚高。

A 股里这种混入非常常见。

它不一定来自明显错误。

更多时候来自细节:复权价格、指数成分、因子标准化、财报发布时间、停牌样本处理、未来区间重叠。

所以,金融交叉验证的第一原则是:不要让未来的影子落到过去。

2. 术语:普通 K 折为什么容易失真

普通 K 折交叉验证会把样本随机分成 K 份。

每次拿其中一份验证,其余几份训练。

在图片识别、文本分类等任务里,这通常很有效。

因为一张猫图和另一张猫图之间没有明确的时间前后关系。

但金融数据不是这样。

金融数据是一条时间河流。

今天的成交、明天的涨跌、下周的波动,彼此相连。

如果把这条河流随机打散,模型可能在训练集中看到验证集之后的信息。

这就像让考生先看下周新闻,再预测今天股价。

普通 K 折在金融里主要有四类问题。

第一,时间顺序被破坏。

随机抽样可能用 2024 年数据训练,再验证 2021 年样本。

但真实投资永远只能用过去面对未来。

第二,标签区间可能重叠。

假设标签是“未来 5 个交易日收益”。

1 月 2 日的标签会用到 1 月 3 日到 1 月 9 日。

1 月 5 日的标签也会用到其中几天。

如果一个样本在训练集,一个相邻样本在验证集,答案区域就部分重叠。

第三,横截面样本会共享同一天市场环境。

同一天所有股票都受同一政策、指数波动、行业风格影响。

如果按“股票-日期”随机切分,模型可能在训练集中见过同一天别的股票。

验证结果会变得过于乐观。

第四,预处理过程也可能泄露。

用全样本均值做标准化、用全样本排序做分组、用未来成分股名单回填历史,都可能让验证集信息进入训练。

风险提示

交叉验证分数高,不等于策略可靠。 如果切分方式破坏了时间顺序,分数越高,越可能是在奖励泄露。

3. 更适合金融的直觉:按时间走,留出安全距离

金融验证更像按时间进行分段演练。

先用一段历史训练。

再用之后的一段历史验证。

然后继续往前滚动。

这种思路常被称为时间序列验证、滚动验证或前推验证。

名称不重要。

重要的是它尊重“先过去,后未来”。

下面是一个更稳妥的验证流程直觉。

mermaid
graph TD
    A[整理原始数据] --> B[按真实发布时间对齐]
    B --> C[确定标签观察期]
    C --> D[按日期切分训练区间]
    D --> E[留出安全间隔]
    E --> F[验证未来区间]
    F --> G[记录样本外表现]
    G --> H[滚动到下一段]

这里的“安全间隔”就是 purge 和 embargo 想解决的核心问题。

purge 可以理解为“把边界附近可能共用答案的样本清掉”。

embargo 可以理解为“验证期之后再隔一小段,不马上拿来训练”。

不必先记英文名。

先记住直觉:当标签会看未来几天,训练和验证之间就要隔开一点。

如果目标是未来 5 日收益,训练结束日和验证开始日之间通常不能无缝相接。

因为训练区间末尾样本的标签已经用到了验证期附近的数据。

可用一句话概括:

+

这不是精确公式,而是检查思路。

你的标签看多远,数据发布慢多久,验证边界就要更谨慎。

4. A 股案例:预测未来 5 日收益的泄露陷阱

假设你做一个 A 股多因子模型。

输入特征包括最近 20 日涨跌幅、成交额变化、换手率、波动率和行业相对强弱。

标签是未来 5 个交易日收益率。

你希望模型每天给所有股票打分。

然后买入排名靠前的一组。

如果你直接把所有“股票-日期”样本随机做 5 折,问题就来了。

2023 年 6 月 1 日的某股票样本可能进入验证集。

2023 年 6 月 2 日同一只股票样本可能进入训练集。

两者的未来 5 日收益区间高度重叠。

模型并没有真正预测未知未来。

它只是从邻近样本里看到了答案轮廓。

再看横截面问题。

2022 年 4 月某天全市场大幅波动。

如果同一天一部分股票在训练集,另一部分股票在验证集,模型可能学到“这一天整体市场情绪很差”。

这在真实交易中并不一定违规。

但如果你宣称验证集是完全陌生的未来,就会高估泛化能力。

A 股还有一些特别需要注意的地方。

涨跌停会改变可成交性。

停牌会造成样本缺口。

ST 与退市风险会让股票池发生变化。

指数成分股会定期调整。

财报数据有公告日,不是报告期结束日当天就能用。

如果这些时间点处理不严,模型会借到未来信息。

5. 泄露清单:先审数据,再审模型

检查项常见错误更稳妥的做法
价格数据使用未来复权结果直接训练明确回测时点可见的价格口径
财报数据按报告期结束日使用按公告日或可获得日使用
指数成分用当前成分股回看历史使用当时真实成分或可投资股票池
标准化全样本均值方差每个训练窗口内单独拟合
因子分位用全历史排序只在当日或训练期内排序
标签未来区间重叠却随机切分按时间切分并留安全间隔
停牌样本简单删除导致幸存者偏差记录停牌、退市、不可交易状态
涨跌停假设信号日一定成交加入可买可卖限制
调参反复看测试集验证集调参,最终测试集少看
代码缓存特征缓存混入全样本每个窗口重新生成或严格标记时间

这张表比模型参数更重要。

因为只要泄露存在,复杂模型会更快地把泄露放大。

线性模型可能只是虚高一点。

树模型、集成模型、深度模型可能把泄露学得非常彻底。

最后你得到的是一台“答案记忆机”,不是预测机。

关键心得

金融机器学习里,验证设计本身就是策略的一部分。 一个普通模型配上干净验证,通常比一个复杂模型配上泄露验证更值得信任。

6. 一个简化的时间验证伪代码

下面示例不依赖真实数据。

它只展示验证边界应该怎样思考。

python
# 目标:用过去训练,验证未来,并在边界留出安全间隔
label_days = 5
report_delay_days = 2
safe_gap = label_days + report_delay_days

for split in time_splits:
    train_end = split["train_end"]
    valid_start = train_end + safe_gap
    valid_end = split["valid_end"]

    train_data = data[data["date"] <= train_end]
    valid_data = data[(data["date"] >= valid_start) & (data["date"] <= valid_end)]

    scaler.fit(train_data[features])
    model.fit(scaler.transform(train_data[features]), train_data["label"])
    score = model.evaluate(scaler.transform(valid_data[features]), valid_data["label"])

注意两个细节。

标准化工具只在训练集上拟合。

验证集只是使用训练集学到的转换方式。

验证开始日不是训练结束日的下一天。

中间留出安全间隔,避免标签区间重叠和数据发布时间错配。

真实项目会更复杂。

但这段伪代码已经抓住了核心:所有步骤都必须站在当时能知道什么的角度。

7. 可执行练习:给你的研究做一次泄露体检

请拿一个你熟悉的策略想法做纸面审查。

不需要真实数据。

只需要把验证流程写清楚。

第一步,写出你的预测目标。

例如“用今天收盘后可见的数据,预测未来 5 个交易日收益排名”。

第二步,列出每个特征的可见时间。

行情特征通常较快可见。

财报、公告、研报类数据要特别标注发布时间。

第三步,画出训练期、间隔期、验证期。

如果标签看未来 5 天,至少说明你为什么需要或不需要安全间隔。

第四步,检查预处理是否只使用训练窗口。

包括缺失值填充、标准化、行业中性化、异常值处理和特征选择。

第五步,写下一个最容易被你忽略的泄露点。

比如“当前指数成分回填历史”或“用最终退市名单过滤样本”。

完成这五步,你已经比只追求模型分数的研究更接近真实投资。


交互实验室

纸面实验:普通随机 K 折 vs 时间前推验证

请在纸上画 12 个连续月份,标记为 M1 到 M12。

设定标签是“未来 2 个月收益”。

方案 A:随机抽取 M3、M8、M11 做验证,其余月份训练。

方案 B:M1-M6 训练,M8-M9 验证,中间 M7 作为安全间隔。

请回答下面四个问题。

  1. 方案 A 有没有可能让训练样本和验证样本共享未来收益区间?
  2. 方案 A 有没有可能使用比验证月份更晚的数据训练?
  3. 方案 B 的安全间隔在防什么?
  4. 如果财报数据平均晚 1 个月可见,方案 B 是否还需要调整?

建议答案不是背概念,而是画时间轴。

只要你能指出“训练时点能不能看到这些信息”,你就掌握了本节核心。

泄露审计模板

步骤你的填写是否通过
预测时点今天收盘后还是盘中待检查
标签长度未来几天或几周待检查
数据可见时间行情、财报、公告分别何时可见待检查
切分方式是否按时间前推待检查
安全间隔是否覆盖标签期与延迟待检查
预处理是否只在训练期拟合待检查
股票池是否存在幸存者偏差待检查
成交假设是否考虑涨跌停、停牌、T+1待检查

把这张表填完,再去看模型分数。

不要反过来。


核心复盘

知识清单

  • [ ] 普通 K 折会随机打散时间顺序,因此不一定适合金融时间序列。
  • [ ] 当标签使用未来多日收益时,训练和验证样本可能共享答案区间。
  • [ ] purge 的直觉是清掉边界附近可能互相污染的样本。
  • [ ] embargo 的直觉是在验证期附近留出不立即使用的安全距离。
  • [ ] 任何标准化、填充、选股池构建和特征选择都必须尊重可见时间。
  • [ ] A 股研究要特别注意涨跌停、停牌、退市、ST、指数成分和财报公告日。
  • [ ] 干净的验证流程通常比复杂模型更能保护投资者。

金句总结

金融机器学习最怕的不是模型不够聪明,而是验证考场不够干净。

本文档仅供学习交流用途,不构成任何投资建议。股市有风险,入市需谨慎。