金融交叉验证
随机 k 折为什么在金融里是错的
Section titled “随机 k 折为什么在金融里是错的”时间序列样本高度相关: 日的样本与 、 日几乎携带同样的信息。随机 k 折把相邻样本分进训练集与测试集,等于让训练集"看到"测试集的邻居——模型只要记住"昨天的标签",就能在测试集上表现优异。金融数据上随机 k 折的高分几乎都是泄漏,且高估幅度可以夸张到完全虚假。
两个修正:
- 清洗(purging):从训练集中剔除标签窗口与测试集重叠的样本——用三重障碍法时,标签窗口长达数十天,重叠必须显式清除;
- 禁运(embargo):测试集之后再空出一段(如标签窗口长度或 1% 样本)——特征工程若含滚动统计(波动率、均线),测试集之后的数据会通过特征泄漏给训练集。
import numpy as np
def purged_kfold_indices(T: int, label_span: int, k: int = 5, embargo: int = 0): """时间顺序 k 折 + 清洗 + 禁运。label_span: 标签窗口长度""" folds = np.array_split(np.arange(T), k) for i, test_idx in enumerate(folds): test_lo, test_hi = test_idx[0], test_idx[-1] train_idx = [] for j, f in enumerate(folds): if j == i: continue f = f[f + label_span - 1 < test_lo] # 清洗:标签窗口不伸入测试集 f = f[f > test_hi + embargo] # 禁运:测试集之后空出一段 train_idx.append(f) yield np.concatenate(train_idx), test_idxWalk-forward 与 CPCV
Section titled “Walk-forward 与 CPCV”- Walk-forward:训练在 ,测试在 ,滚动前进。最贴近实盘,但只有一条路径——一个时间轴上的一次滚动,结论对"恰好这段样本"敏感;
- 组合清洗交叉验证(CPCV):把样本切成 块,取 块做测试、其余做训练的所有组合——多条路径,能同时给出性能分布与过拟合概率,是 López de Prado 对 walk-forward 的改进。
估计过拟合概率
Section titled “估计过拟合概率”CPCV 的输出不只是平均分数,而是分数分布:
- 多条路径上的表现差异大(如夏普从 0.3 到 1.5)→ 高度过拟合;
- 把观察到的最优分数放进 CPCV 分数分布中排名,得到过拟合概率(PBO)——与第一卷 Deflated Sharpe Ratio 是同一思想的另一实现。
量化中的用法
Section titled “量化中的用法”- 所有 ML 评估用带清洗 + 禁运的时间顺序 CV,随机 k 折直接禁止;
- 最终评估用 CPCV,报告分数分布而非单点;
- walk-forward 保留为"最贴近实盘"的最后一道检验。
- 清洗:剔除与测试集标签窗口重叠的训练样本
- 禁运:测试集之后再空出一段
- Walk-forward 只有一条路径,CPCV 提供多条