金融时间序列不能直接套用传统机器学习里的随机交叉验证。价格、成交量、波动率和新闻情绪都带着时间顺序,样本之间并不独立;更麻烦的是,量化标签往往覆盖一段时间,训练样本和测试样本的评价区间可能互相重叠。模型不是在预测未来,而是在背诵未来已经发生过的路径。
这就是金融时间序列交叉验证必须引入“清洗”和“禁运”的原因。前者处理标签重叠,后者切断测试集附近的序列相关性。少一个环节,回测就可能继续漏风。
金融数据不是普通样本:传统K折验证为什么会失效
传统K折交叉验证建立在一个前提上:样本近似独立同分布。数据被随机打乱,再切成若干份,轮流拿其中一份测试,其余部分训练。这个方法在很多静态分类问题里好用,因为一条样本和另一条样本之间没有明显的时间依赖。
金融市场不吃这一套。
今天的收益率和昨天、前天的收益率存在序列相关;同一段行情里的波动率、成交量和盘口状态,也会持续影响后续样本。股票大跌、期货逼仓、宏观数据公布后的连续波动,都不是一根K线结束后立刻清零的事件。你把这些样本随机打散,模型就能在训练集里看到测试集附近的市场状态。
这已经是第一层泄漏。
第二层泄漏来自标签。假设你使用过去的特征预测未来五个交易日收益,样本标签定义为:
- 特征观察时间:1月10日收盘;
- 标签评价时间:1月11日至1月17日;
- 标签结果:这五天的累计收益。
那么,1月11日和1月12日的样本,其标签评价区间可能仍然与1月10日样本发生重叠。它们看起来是不同样本,实际上都在描述同一段未来行情。
如果你把样本随机分配到训练集和测试集,测试集中的标签区间可能和训练集中的标签区间重叠。模型得到的不是干净的泛化检验,而是被允许提前窥视答案的考试。
标签重叠比特征重叠更隐蔽
很多交易者知道不能把未来价格直接放进特征,却忽略了标签时间跨度。
例如,你训练一个方向预测模型:
- 用过去20根日线构造特征;
- 预测未来10根日线的收益;
- 每天生成一个训练样本。
这组样本的特征窗口可以没有未来数据,但标签窗口会连续重叠。1月10日样本的标签覆盖1月11日至1月24日,1月11日样本的标签覆盖1月12日至1月25日。两者之间存在大量共同的评价区间。
如果1月10日样本在训练集,1月11日样本在测试集,训练样本的标签就已经携带了测试样本所处行情的部分信息。
这不是代码报错能提醒你的问题。程序会正常运行,模型会正常拟合,回测曲线甚至会更加漂亮。等你把策略接上实盘,滑点开始吞噬收益,连续亏损击穿止损,才发现所谓的稳定性只是交叉验证制造的幻觉。
金融交叉验证最先要防的,不是模型太复杂,而是训练集和测试集根本没有真正分开。
直接使用随机K折,会把市场结构打碎
随机K折还有一个严重问题:它把时间顺序打乱了。
在真实交易里,你只能用过去的信息预测未来。可随机K折允许模型在训练阶段同时看到测试期之前和测试期之后的数据。即便特征本身没有显式使用未来价格,市场状态、波动聚集和相邻标签也会把未来的结构带回训练集。
尤其是以下策略,泄漏风险更高:
- 使用滚动波动率、成交量异常、资金流向等持续性特征;
- 标签采用未来多日收益、最大回撤或是否触发止盈止损;
- 样本按日生成,但每个样本的评价期长于一个交易日;
- 同一资产、同一行业或高度相关的品种同时出现在训练集和测试集;
- 通过大量参数、特征和模型组合挑选最优结果。
最后一种情况尤其危险。你可能没有直接偷看测试标签,但你反复试验不同参数,再选出回测最好的那一组,实际上已经把测试结果当成了优化信号。2014年,Bailey等人发表了关于回测过拟合概率的研究,核心警告非常直接:策略试得越多,撞上漂亮回测曲线的机会越高。
传统K折会把这种问题进一步放大。它给你多个看似独立的验证结果,却没有处理时间依赖和标签重叠。数字越整齐,陷阱越容易被忽略。
清洗机制:先把与测试标签重叠的训练样本剔掉
清洗,也就是净化操作,英文通常称为“清洗”(Purging)。它的任务单一而明确:
只要训练样本的标签评价时间区间,与测试集中任意样本的标签评价区间发生重叠,就从训练集中删除。
注意,清洗不是删除测试集,也不是简单地按照样本日期切一刀。它检查的是标签的时间跨度。
用事件时间而不是样本日期做判断
给每条样本记录两个时间:
t0:特征观察时间,也可以理解为样本形成时间;t1:标签评价结束时间,也就是未来收益、止盈止损或事件结果完成的时间。
一条样本可以表示为区间 [t0, t1]。
假设测试样本的标签区间是 [T0, T1],训练样本的标签区间是 [t0, t1]。如果两个区间满足以下条件之一,就不能把训练样本留在训练集中:
- 训练标签在测试标签开始之前已经进入测试评价区间;
- 训练标签在测试标签结束之后仍然与测试区间相交;
- 两个标签区间存在任何时间重叠。
换成交易员能听懂的话:测试集在统计一段行情的结果,训练集不能拿同一段行情的结果来学习。
一个具体的标签重叠例子
假设你用期货日线构造三条样本:
| 样本 | 特征时间 | 标签评价区间 | 标签含义 |
|---|---|---|---|
| 样本A | 1月10日收盘 | 1月11日至1月17日 | 未来5个交易日收益 |
| 样本B | 1月11日收盘 | 1月12日至1月18日 | 未来5个交易日收益 |
| 样本C | 1月18日收盘 | 1月19日至1月25日 | 未来5个交易日收益 |
如果样本B被分到测试集,那么样本A不能继续作为训练样本。它的标签区间与样本B重叠了1月12日至1月17日。样本C则没有与样本B发生标签重叠,可以进入训练集,但它还要接受禁运规则的检查。
这里最容易犯的错误,是只比较特征时间:
- A的特征时间早于B;
- A不是未来样本;
- A看起来可以用于训练。
这个判断少看了一层。对于带未来窗口的标签,样本的“有效时间”不是一个点,而是一段区间。只看t0,等于把标签泄漏藏在地板下面。
清洗并不等于让样本完全独立
清洗解决的是标签评价区间重叠,不会自动消除所有序列相关性。
两个样本的标签不重叠,特征仍然可能高度相似。比如连续几天的滚动波动率、均线斜率和盘口不平衡指标,都可能在同一段趋势中保持相似状态。模型依然能够从相邻样本中识别市场环境,只是这种信息不再通过标签区间直接交叉。
所以,清洗是第一道闸门,不是整个风控系统。后面还需要禁运,把测试集附近的一段数据隔离出去。
清洗的实际流程
在工程里,清洗流程可以按下面的顺序执行:
1. 先明确标签定义。
不要只保存一个收益数值,必须保存标签开始时间和结束时间。未来持有期、止盈止损触发时间、事件确认时间,都属于标签时间的一部分。
2. 划分测试集。
测试集可以是某个连续时间段,也可以是组合净化交叉验证生成的某组分区。
3. 扫描训练样本的标签区间。
对每条训练样本,检查它是否与测试集任意标签区间相交。
4. 删除发生重叠的训练样本。
不能用填充值、降权或随机保留替代删除。既然训练样本已经接触测试期的信息,就不应继续参与拟合。
5. 记录清洗比例。
清洗后样本数量减少是正常现象。若删除数量异常庞大,说明标签持有期太长、采样过密,或者交叉验证分区设计本身不适合当前策略。
清洗比例不能简单拿一个固定数字硬套。五分钟高频策略、日线趋势策略和宏观事件策略,标签生命周期完全不同。你不能把一个适用于某类数据的经验窗口,直接扣在所有市场头上。
禁运机制:在测试集之后留出隔离带
清洗处理标签重叠,禁运处理测试集附近的序列相关性。
“禁运”(Embargoing)的做法是在测试集结束之后,立即剔除一段缓冲期内的训练样本。常见的经验设置是禁运比例为样本量的1%至2%,但这只是起点,不是万能答案。真正的窗口长度,应当结合数据的自相关结构、标签持有期和交易频率来决定。
为什么测试集结束后还要删数据
很多人会问:测试集已经结束,后面的样本不是未来数据吗?为什么还不能拿来训练?
因为交叉验证不是简单的时间切分。它往往把多个时间区间轮换成训练集和测试集。当前测试集结束后紧邻的样本,可能和测试集处于同一段市场状态,特征与标签都带有强烈的连续性。
举个更贴近实盘的例子。
你测试的是一段剧烈波动行情,测试区间从1月10日持续到1月20日。1月21日紧接着出现的样本,其波动率、成交量和价差仍可能延续测试期的状态。如果你把1月21日样本直接放进训练集,模型就会通过紧邻的序列结构间接接触测试期信息。
它未必看到了测试标签,但它看到了测试期留下的市场指纹。
对于新闻驱动、事件驱动和趋势延续策略,这种影响尤其明显。一次央行会议、财报公布、合约换月或逼仓行情,不会在一个时间戳上突然结束。数据表里虽然换了日期,市场状态却还在延续。
禁运窗口不能靠拍脑袋
事实资料中常见的禁运比例是1%至2%的样本量,例如embargo=0.01或embargo=0.02。这个范围可以作为工程初始值,但不能直接当成行业标准。
禁运窗口至少要考虑四件事:
- 标签持有期。
未来预测五个交易日的策略,不能用单日预测的隔离窗口处理。
- 样本采样频率。
五分钟一个样本和一天一个样本,即使禁运比例相同,实际隔离的时间长度也完全不同。
- 市场状态的持续性。
波动率聚集明显的品种,需要面对更长的状态延续;流动性突然收缩时,价差和成交冲击也不会在下一根K线立刻恢复。
- 特征的回看长度。
如果特征依赖过去60个交易日的滚动统计,边界附近的样本共享大量历史信息,分割设计就必须对此保持警惕。
可以把禁运窗口理解成交易所里的缓冲区。不是因为缓冲区里必然存在泄漏,而是因为你不能把正在冒烟的战场边缘直接当作干净样本。
清洗和禁运的区别
两者经常被混为一谈,但它们针对的是不同问题:
| 机制 | 主要处理的问题 | 判断依据 | 典型动作 |
|---|---|---|---|
| 清洗 | 训练标签与测试标签发生重叠 | 标签评价区间是否相交 | 删除重叠训练样本 |
| 禁运 | 测试集附近存在序列相关性 | 测试集结束后的时间距离 | 删除测试集后的缓冲样本 |
| 普通时间切分 | 保持基础时间顺序 | 样本形成时间 | 训练用过去,测试用未来 |
| 组合净化交叉验证 | 评估多条回测路径 | 分区组合与时间约束 | 先清洗,再禁运,生成多条路径 |
如果只做清洗,测试集结束后的相邻训练样本仍然可能带入市场状态信息。如果只做禁运,训练集与测试集的标签区间仍可能重叠。两个环节缺一不可。
清洗是在边界上绞杀重叠标签,禁运是在边界外再架一道火力网。只做一个,防线仍然会漏。
从普通时间切分到组合净化交叉验证
普通的向前滚动切分比随机K折更符合交易逻辑,但它不是万能药。很多人拿TimeSeriesSplit跑完,看到训练集永远早于测试集,就以为泄漏问题已经结束。
没有这么简单。
如果标签跨越多个时间点,向前滚动切分仍然可能让训练标签和测试标签重叠;如果测试集附近存在强序列相关,紧邻样本仍会传递市场状态;如果你反复调整参数并只保留最好的那条回测路径,模型依旧会被选择过程过拟合。
CPCV解决什么问题
组合净化交叉验证,英文称为“组合净化交叉验证”(Combinatorial Purged Cross-Validation,简称CPCV),是在净化K折基础上的扩展。
它把时间序列划分为多个连续分区,再从中组合训练集与测试集。在每一次组合中:
1. 选定一组分区作为测试集;
2. 对训练集执行清洗,删除标签区间重叠的样本;
3. 对测试集之后的相邻训练区间执行禁运;
4. 得到一条符合时间约束的回测路径;
5. 通过不同分区组合,生成多条回测路径。
关键变化在于:你不再只看一条从左到右的回测曲线,而是观察多条路径上的性能分布。
如果一套策略只有一条切分结果好,其他路径全部失血,那不是稳健性,而是路径依赖。它可能恰好吃到了某一段趋势、某一次波动扩张,或者某个品种的特殊行情。CPCV把时间分区重新组合,逼着模型面对更多市场顺序和状态变化。
组合数量不是越多越好看
当数据被划分为N个分区,并从中选择k个分区作为测试集,理论组合数量可以表示为C(N, k)。组合数增加,回测路径也会增加,但计算量、模型训练次数和结果解释难度会同步上升。
不要为了制造更多曲线而盲目增加分区。分区太细,单个测试区间可能缺乏足够交易事件;分区太粗,路径数量又不足以反映不同市场环境。分区设计必须服从策略的交易周期和标签生命周期。
例如:
- 日线中短线策略,需要确保每个测试区间包含足够的交易样本;
- 低频宏观策略,单个事件之间间隔长,不能把相邻事件强行当作独立样本;
- 高频策略,样本数量虽然庞大,但微观结构自相关强,禁运不应只看样本条数;
- 期货跨合约策略,还要处理换月、主力切换和不同合约流动性迁移。
CPCV能提供性能分布,但不会自动替你判断哪条路径最接近未来。它只是把单点结论拆开,让你看见策略在不同路径上的脆弱位置。
你真正该看的不是最高收益
面对CPCV结果,第一反应不要去找收益最高的路径。那是交易者最熟悉的陷阱:看到曲线抬头就追,看到回撤就解释。
更值得观察的是:
- 不同路径的收益方向是否一致;
- 回撤是否集中发生在某类市场状态;
- 交易次数变化后,收益是否完全依赖少数几笔交易;
- 换手、滑点和手续费加入后,性能分布是否整体塌陷;
- 参数轻微变化时,结果是否立刻反转;
- 单一品种的收益是否掩盖了其他品种的亏损;
- 训练窗口变化后,模型是否仍然能保持基本的风险结构。
不要把“所有路径都盈利”当作唯一标准,也不要把某一条路径的高夏普当作模型能力证明。金融数据本身存在非平稳性,合理的模型也会经历失效期。真正要防的是:回测只在一种切法、一个参数、一个市场阶段里成立。
标签定义是整套验证的地基
很多交叉验证失败,并不是验证器写错,而是标签定义从一开始就不完整。
如果你只在数据表中保存:
- 特征时间;
- 标签数值;
- 是否上涨。
却没有记录标签评价结束时间,那么清洗就没有执行基础。程序不知道标签覆盖到哪一天,只能按照样本日期机械切分。看似运行顺利,实际上已经把最关键的信息丢掉了。
常见标签类型与时间边界
不同标签必须使用不同的结束时间:
固定持有期收益
例如预测未来五个交易日收益。标签结束时间就是第五个交易日的收盘时间。如果中间遇到节假日或停牌,不能用简单的自然日加法替代交易日历。
止盈止损标签
例如使用三重障碍法,样本在止盈、止损或最长持有期中的任一条件触发时结束。标签结束时间应该是实际触发时间,而不是固定写成未来第N根K线。
波动率或风险标签
如果标签是未来一段时间的实现波动率、最大回撤或尾部损失,结束时间就是统计窗口的终点。标签虽然不是方向收益,照样存在区间重叠。
事件标签
如果模型预测财报、宏观数据或链上事件后的价格反应,标签时间要覆盖事件发生后的完整评估窗口。事件本身的发布时间、数据修订时间和市场可获得时间,也不能混在一起。
特征时间也必须遵守可得性
清洗和禁运解决不了特征本身的未来函数。
例如,你在收盘后生成交易信号,却使用了当天收盘价构造特征,再假设自己能以当天收盘价成交。这个问题与交叉验证无关,属于执行时间错误。再比如,使用财务数据时没有考虑公告发布时间,直接把季度报告的最终值回填到整个季度,也会把未来信息灌进历史。
必须把以下时间分开:
- 数据实际发生时间;
- 数据对市场公开的时间;
- 数据进入数据库的时间;
- 策略允许使用的时间;
- 下单与成交时间。
金融数据不是一张静态表,而是一条带时间戳的事件流。只要把这些时间揉成一个字段,泄漏就已经开始。
Python工程实现:验证器不是一个简单的切片器
在Python生态中,可以使用skfolio提供的CombinatorialPurgedCV,也可以参考Kaggle社区常见的PurgedGroupTimeSeriesSplit思路,自己实现带清洗和禁运的交叉验证器。
工具只是外壳,时间边界才是核心。不要因为类名里出现了“Purged”或“Embargo”,就认为数据已经被净化。你必须确认它实际使用的是哪一列时间、如何处理标签结束时间、禁运按照样本数还是时间长度计算,以及是否支持不规则交易日。
一个可执行的工程设计
建议把数据处理拆成四层,而不是把所有逻辑塞进一个训练函数。
第一层:原始事件层
保存价格、成交量、盘口、新闻、资金流和链上数据的原始时间戳。不要在这一层直接填充未来值,也不要为了方便把不同来源数据强行对齐到同一时刻。
第二层:可用数据层
按照真实发布时间和策略可见时间,生成当时可以使用的字段。任何后到数据都不能回填到过去。对于新闻和财务数据,发布日期比报告所属期间更关键。
第三层:样本与标签层
为每条样本明确记录:
- 特征形成时间;
- 标签起始时间;
- 标签结束时间;
- 资产或合约标识;
- 分组标识;
- 是否存在停牌、换月或异常成交。
这一步是清洗的入口。没有标签结束时间,后面所有净化动作都只能靠猜。
第四层:交叉验证层
交叉验证器接收样本的时间区间,生成训练索引与测试索引。每次生成训练集后,执行两步:
1. 删除与测试标签区间重叠的训练样本;
2. 删除测试集结束后的禁运窗口样本。
如果策略涉及多个品种,还要决定清洗是按单品种执行,还是按整个市场事件执行。比如股指期货和相关ETF在同一重大事件中同步波动,单独按资产切割可能无法阻断跨资产信息传播。
PurgedGroupTimeSeriesSplit的使用边界
PurgedGroupTimeSeriesSplit这类自定义验证器,常见做法是先把相邻样本划分成时间组,再在组之间进行训练测试切分。它比随机K折更接近时间序列,但“分组”不能替代标签区间检查。
如果一个组内的样本标签跨越多个时间段,组边界仍然可能不干净。你需要确认:
- 分组依据是样本形成时间,还是标签结束时间;
- 测试组前后的训练组是否被正确清洗;
- 禁运窗口是否覆盖了测试期之后的样本;
- 多个测试组之间的标签区间是否发生交叉;
- 样本权重和标准化参数是否只在训练集上计算。
尤其要注意标准化、缺失值填补、特征选择和降维。若你先用全量数据计算均值、标准差、相关系数或主成分,再进入交叉验证,数据泄漏已经发生。清洗标签不能替你挽救全量预处理。
正确的顺序应当是:每个训练折单独拟合预处理过程,再将同一组参数应用到测试折。测试集只能被变换,不能参与参数估计。
用日志把泄漏留下证据
交叉验证不是黑箱。每一折至少记录:
- 训练样本原始数量;
- 清洗删除数量;
- 禁运删除数量;
- 最终训练数量;
- 测试样本数量;
- 训练集最晚标签结束时间;
- 测试集最早与最晚标签时间;
- 禁运窗口的起止时间;
- 每个资产和每个分组的样本分布。
如果你只保存最终夏普、收益和回撤,出了问题只能凭曲线猜。把清洗与禁运日志保存下来,才能回答一个硬问题:这一折的训练集,究竟有没有碰到测试集的标签区间?
为什么普通TimeSeriesSplit仍然不够
向前滚动验证有一个明显优点:训练集在时间上早于测试集,符合交易顺序。但它只解决了“整体时间方向”问题,没有自动解决标签重叠、状态延续和复杂预处理泄漏。
假设训练集截止到3月31日,测试集从4月1日开始。若3月31日样本的标签覆盖未来五个交易日,那么它的标签直接进入了测试区间。训练集虽然在样本日期上早于测试集,标签却没有结束。
这就是为什么“训练集日期早于测试集日期”不是充分条件。
还要看特征窗口。若测试集从4月1日开始,而训练集最后一个样本使用了3月31日收盘后的信息,策略实际能否在4月1日开盘前取得这些数据?如果不能,时间切分再漂亮也没有意义。
普通时间切分更适合做基础 sanity check,而不是当作金融模型唯一的验证框架。它可以保留,但必须在标签净化、禁运和预处理隔离之后使用。
最容易把回测做坏的几种操作
把未来收益先算好,再随机切分
这是最直接的泄漏。标签先在全量数据上滚动计算本身并不一定错误,但切分时必须依据标签区间执行清洗。否则,重叠标签会被同时分配到训练集和测试集。
只删除测试区间内的训练样本
有些实现看到训练样本日期落在测试期内,就把它删除;测试期外的样本则全部保留。这种做法没有检查标签结束时间,也没有处理测试集之后的禁运窗口。
样本日期不是标签边界。两者混用,等着被回测曲线扫损。
禁运固定成一个拍脑袋的天数
不同频率的数据不能共享同一个禁运天数。五分钟数据里一天可能包含数百个样本,日线数据里一天只有一个样本。将“禁运三天”硬编码,既可能过短,也可能把训练集砍得所剩无几。
应当明确禁运按什么计算:
- 按时间长度;
- 按样本数量;
- 按交易日比例;
- 按标签持有期;
- 按自相关衰减窗口。
如果采用1%至2%的样本比例作为起点,也要把实际转换后的时间跨度记录下来。
先用全量数据做特征筛选
你用全量样本计算特征与收益的相关性,再挑选最相关的指标进入交叉验证。这等于提前让测试集参与了特征选择。即使最终模型没有直接读到测试标签,筛选过程已经根据测试期结果改变了输入变量。
特征筛选必须在每个训练折内完成。模型选型、参数搜索、阈值调整也一样。
反复试验后只展示最好的一次
这不是验证,是挑曲线。
当你尝试了大量标签窗口、止盈止损参数、入场阈值、品种组合和模型结构,最终只保留最好结果时,模型已经经历了选择偏差。CPCV能帮助你看到多条路径,但不能替你消除“试很多次再选最好”的问题。
解决方式不是把坏结果藏起来,而是记录实验空间、固定评价协议,并保留未参与调参的最终测试区间。
如何判断净化后的结果是否更可信
净化后,样本数量下降、收益回落、夏普降低,都不代表模型失败。恰恰相反,很多原本依赖泄漏的策略,会在清洗和禁运后迅速失血。
这一步像把杠杆从二十倍砍回三倍。账户曲线不再那么刺激,但你终于看见了真实风险。
可以从几个角度观察结果。
性能是否从“单点神迹”变成“分布”
净化交叉验证不应只输出一个均值。你需要查看不同路径的收益、回撤、胜率、换手和交易次数分布。性能分散很大,说明策略对时间路径敏感;收益方向相对稳定,说明模型并非只吃到某一段行情。
回撤是否符合策略逻辑
趋势策略在横盘期回撤,均值回归策略在单边行情中受伤,事件策略在事件稀疏时交易机会减少,这些都属于可解释的风险。
无法解释的回撤更危险。比如所有亏损集中在换月、夜盘、低流动性时段,说明模型可能没有处理交易执行;收益全部来自少数极端行情,说明策略对尾部样本过度依赖。
加入交易成本后是否仍有生存空间
滑点不是回测里的装饰品。期货盘口变薄时,成交价会迅速偏离信号价;高换手模型即使预测方向正确,也会被手续费、冲击成本和排队成交吞掉。
成本模型至少要覆盖:
- 手续费;
- 买卖价差;
- 滑点;
- 市场冲击;
- 换月成本;
- 夜盘与流动性差异;
- 涨跌停、熔断或无法成交的情况。
净化后本来就脆弱的策略,加入现实成本可能直接爆仓。这不是成本太保守,而是模型原先把不可交易的收益当成了利润。
轻微改动会不会击穿模型
把标签窗口从五个交易日改成四个或六个,把训练窗口前后移动,把禁运比例从1%调整到2%,结果不应当立刻从盈利变成彻底失效。
如果一丁点改动就击穿收益,模型可能在拟合某个特定边界。真正稳健的信号不会只藏在一个神奇参数里。
当然,参数稳定不代表实盘必赚。它只说明模型没有那么容易被一根边界线绞死。市场一旦换状态,仍然可能让模型失效。
一个适合实战团队的验证流程
如果你正在搭建新的量化策略,我建议把验证流程固定成下面几步,不要每次研究都临时改协议。
1. 先写清楚交易时钟。
明确特征何时生成、信号何时确认、订单何时提交、成交价如何获得。没有执行时钟,收益标签就是一笔糊涂账。
2. 为每条样本保存标签起止时间。
固定持有期、止盈止损、事件标签都必须拥有明确结束时间。
3. 冻结数据版本。
不要在模型训练期间不断替换历史数据、修正异常值或回填字段。数据版本变化会让不同实验无法比较。
4. 先做时间顺序检查。
用向前滚动切分确认最基本的训练测试方向没有反转。
5. 加入清洗。
删除训练集中所有与测试标签评价区间重叠的样本。
6. 加入禁运。
在测试集结束后留出隔离窗口,初始可以从1%至2%的样本比例开始,再结合自相关和标签持有期调整。
7. 将预处理放进训练折。
标准化、缺失值处理、特征选择、降维和模型调参,全部只使用训练集拟合。
8. 使用CPCV观察多条路径。
不要只看平均收益,保留每条路径的完整交易记录和风险指标。
9. 加入现实交易成本。
把手续费、价差、滑点和成交限制放到回测引擎,而不是在最后用一个固定比例随手扣掉。
10. 保留未触碰的最终样本。
研究阶段结束后,使用独立的时间区间做最终检验。这个区间不能拿回来反复调参,否则它也会被你吃掉。
最后的账户警告
金融时间序列交叉验证不是为了把回测曲线修饰得更好看,而是为了把虚假的好看击穿。
清洗删除标签重叠,禁运隔离测试集附近的序列相关,组合净化交叉验证把单一路径拆成性能分布。它们不能消除模型误差,不能保证实盘盈利,也不能替你处理数据质量、执行成本和市场制度变化。
但如果连这些基本防线都没有,模型的夏普、胜率和收益曲线就没有多少讨论价值。你看到的不是策略能力,而是泄漏、相关性和反复试验共同喂出来的幻觉。
回测里少赚一点,最多难受;实盘里带着泄漏上杠杆,才是真正的爆仓。先把验证做干净,再谈模型能不能赚钱。
Related reading: 量化模型中的前瞻偏差:数据泄露的隐蔽成因与检测机制 and 量化策略的终极博弈:为什么高质量数据比复杂算法更决定成败.
