尊重时间的交叉验证
交叉验证会重复使用数据:把数据切成若干折,在一折上测试,在其余折上训练。对相互独立的行这样没问题,但金融标签在时间上会重叠。若第 j 行的标签是从 j 到 j+H 的收益,它的窗口就是 [j,j+H]。覆盖第 a 到 b−1 行的测试折,其标签覆盖 [a,b−1+H]。任何窗口与此范围相交的训练行(即 a−H≤j≤b−1+H)都与测试标签共享收益,保留这些行就会让模型看到部分答案。
[j,j+H]∩[a,b−1+H]=∅⟺a−H≤j≤b−1+H⟹a−H≤j≤b−1+H+e(1) 清洗去掉重叠的训练行。禁运则在测试折之后再去掉 e 行,因为收益存在自相关、波动率会聚集,所以测试块之后的头几行仍带有关于它的信息。在滚动前向方案里,训练只发生在测试之前,同样的想法变成一个间隔:用到第 a−H−1 行为止训练,间隔为 H(再加 e)。scikit-learn 的 TimeSeriesSplit 有 gap 参数就是干这个的。在月度股票面板上,当 H=3 时,滚动前向信息系数无禁运为 0.0326,禁运 3 个月为 0.0327:因为标签重叠很短,清洗带来的变化很小,但不做就无从知道。
数一数行取
n=100 行、标签期限
H=5、测试折
[40,60)、禁运
e=2。折之前,第
35 到
39 行与其标签重叠:
5 行。折之后,第
60 到
66 行(直到
b−1+H+e=66)被去掉:
7 行。该折保留
100−20−12=68 个训练行。在同一个测试折的滚动前向版本里,训练行止于第
34 行,共
35 行,这正是 scikit-learn 的 TimeSeriesSplit 在间隔为
5 时返回的结果。
- 先清洗,再禁运:去掉标签与测试标签相交的训练行,再去掉测试之后的几行。
- 滚动前向中的间隔:只用至少早于测试 H 行的数据训练。
- 即使变化很小也要做:干净的评估只损失几行,泄漏可能毁掉一个策略。
实验:评分内容
- 折之前被清洗的行10 分
- 折之后被清洗的行15 分
- 剩下的训练规模15 分
- 带间隔的滚动前向折10 分
- 哪些行要被清洗10 分
- 带清洗的划分10 分
- 配对术语15 分
- 为什么还要禁运15 分