关于 walk-forward-validation
用于金融时间序列中交易策略和机器学习模型评估的验证框架与技能,涵盖按时间划分、清除与禁运、CPCV 及回测过拟合检测指标,并提供加密货币场景建议。
主要功能
- 01滚动与扩展窗口验证
- 02处理标签重叠的清除与禁运
- 03组合式清除交叉验证
- 04Deflated Sharpe ratio 与 PBO
- 05面向加密货币的验证建议
适用场景
- 01评估策略,避免随机划分导致的前视偏差
- 02测试模型应对市场状态变化的表现
- 03通过多条路径评估回测过拟合
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ agiprolabs/ claude-trading-skills --skill walk-forward-validation安装后在 Claude Code 中输入/walk-forward-validation确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/methodology.mdoverfit_detection.mdpractical_guide.mdscripts/overfit_detector.pywalk_forward.py详细介绍
滚动前向验证
用于交易策略和 ML 模型的滚动前向验证框架。标准交叉验证(k 折、随机拆分)不适用于金融时间序列,因为它会严重引入前视偏差并忽略自相关。本技能介绍适当的时间序列验证技术,包括滚动窗口和扩展窗口、清除式交叉验证、组合清除式交叉验证(CPCV)以及过拟合检测指标。
标准交叉验证为何会失败
标准 k 折 CV 假设数据点独立同分布(IID)。金融时间序列不满足这两个假设:
- 前视偏差 — 随机拆分会让模型用未来数据训练,再预测过去,导致性能被人为夸大。
- 自相关 — 相邻观测值存在相关性。随机拆分将周一放入测试集、周二放入训练集,就会造成信息泄漏。
- 市场阶段依赖 — 市场会在不同阶段之间转换。在牛市数据上训练、再在牛市数据上测试的模型,无法说明其在熊市中的表现。
- 标签重叠 — 如果标签是基于时间窗口计算的(例如 24h 前向收益),相邻的训练/测试样本会共享标签计算时段,从而造成信息泄漏。
滚动前向验证框架
滚动窗口(固定训练集大小)
训练窗口大小固定,并沿时间向前滑动。如果认为较旧的数据相关性较低(在加密货币领域很常见),则优先采用这种方式。
Window 1: [===TRAIN===][=TEST=]
Window 2: [===TRAIN===][=TEST=]
Window 3: [===TRAIN===][=TEST=]
参数:
train_size:训练窗口中的 bar/天数test_size:测试窗口中的 bar/天数step_size:各折之间的前进步长(通常等于test_size)
扩展窗口(训练集逐渐增大)
训练窗口从起点开始并向前扩展。这种方式会使用所有可用的历史数据,在数据稀缺时很有帮助。
Window 1: [==TRAIN==][=TEST=]
Window 2: [====TRAIN====][=TEST=]
Window 3: [======TRAIN======][=TEST=]
参数:
min_train_size:首折开始前所需的最少训练样本数test_size:固定的测试窗口大小step_size:各折之间的前进步长
如何选择
| 因素 | 滚动窗口 | 扩展窗口 |
|---|---|---|
| 数据时效性 | 优先使用近期数据 | 使用全部历史数据 |
| 市场阶段变化 | 更能适应新的市场阶段 | 近期市场阶段的影响可能被稀释 |
| 样本量 | 固定,可能较小 | 随时间增长 |
| 加密货币偏好 | 对 < 6mo 的期限更合适 | 更适用于市场阶段稳定的模型 |
清除与禁运期
清除
移除标签与测试集时间范围重叠的训练样本。如果标签是从时间 t 开始计算的 24h 前向收益,那么凡是 t + 24h 延伸到测试期内的训练样本都必须清除。
def purge_train_indices(
train_idx: list[int],
test_start: int,
label_horizon: int,
timestamps: list[int],
) -> list[int]:
"""Remove train samples whose label windows overlap test period."""
test_start_time = timestamps[test_start]
return [
i for i in train_idx
if timestamps[i] + label_horizon < test_start_time
]
禁运期
在训练结束与测试开始之间加入缓冲间隔,以应对仅靠清除无法消除的序列相关性。
[===TRAIN===][--EMBARGO--][=TEST=]
典型禁运期长度:
- 1 分钟 bar:60–240 个 bar(1–4 小时)
- 5 分钟 bar:12–48 个 bar(1–4 小时)
- 小时 bar:6–24 个 bar(6–24 小时)
- 日 bar:2–5 个 bar(2–5 天)
- 加密货币经验法则:禁运期 >= 标签计算期限的 2 倍
组合清除式交叉验证(CPCV)
CPCV(Lopez de Prado,2018)会从 N 个分组中生成所有可能的训练/测试组合,同时保持时间顺序。与标准滚动前向验证相比,它能生成更多测试路径,从而支持过拟合的统计检验。
主要特性:
- 将数据拆分为
N个连续分组 - 对于每种包含
k个测试组的组合,其余N-k个组构成训练集 - 在每个训练/测试边界应用清除和禁运期
- 生成
C(N, k)条回测路径(例如,N=6、k=2 时会生成 15 条路径)
完整的 CPCV 算法和公式请参见 references/methodology.md。
过拟合检测
Deflated Sharpe Ratio(DSR)
观测到的 Sharpe 比率必须根据以下因素进行调整:
- 测试的策略数量(多重检验)
- 收益的非正态性(偏度、峰度)
- 回测长度
import numpy as np
from scipy.stats import norm
def deflated_sharpe_ratio(
observed_sr: float,
num_trials: int,
backtest_length: int,
skewness: float = 0.0,
kurtosis: float = 3.0,
) -> float:
"""Compute the probability that observed SR > 0 after deflation.
Args:
observed_sr: Annualized Sharpe ratio of the selected strategy.
num_trials: Number of strategies tested (including discarded ones).
backtest_length: Number of return observations.
skewness: Skewness of returns.
kurtosis: Excess kurtosis of returns.
Returns:
p-value (probability SR is genuinely > 0).
"""
sr_std = np.sqrt(
(1 - skewness * observed_sr + (kurtosis - 1) / 4 * observed_sr**2)
/ (backtest_length - 1)
)
# Expected max SR under null (Euler-Mascheroni approximation)
euler_mascheroni = 0.5772156649
expected_max_sr = norm.ppf(1 - 1 / num_trials) * (
1 - euler_mascheroni
) + euler_mascheroni * norm.ppf(1 - 1 / (num_trials * np.e))
dsr = norm.cdf((observed_sr - expected_max_sr) / sr_std)
return dsr
DSR 低于 0.95 表明,观测到的表现很可能是测试过的多次试验造成的过拟合。
回测过拟合概率(PBO)
PBO 使用 CPCV 衡量样本内最优策略在样本外表现低于中位数的回测路径占比。PBO 高于 0.50 表示过拟合的可能性大于不过拟合。
完整推导和实现细节请参阅 references/overfit_detection.md。
加密货币特有的考量
- 更短的窗口:加密货币市场机制的变化速度快于股票市场。90 天滚动窗口可能比 252 天更合适。
- 全天候市场:无需考虑周末或节假日,但资金费率重置(永续合约每 8 小时一次)会产生微观结构效应。
- 幸存者偏差:许多代币会退市。验证必须纳入已退市的代币,或至少承认这一局限性。
- 流动性机制转变:代币的流动性状况可能发生剧烈变化(例如上线新的 CEX、流动性挖矿结束)。训练集和测试集的划分最好不要跨越重大的流动性事件。
- 数据可用性:许多代币的历史数据不足 1 年。可能需要使用较小的
min_train_size进行扩展窗口验证。
加密货币的实用窗口大小
| 策略时间范围 | 训练窗口 | 测试窗口 | 隔离期 |
|---|---|---|---|
| 剥头皮(1-5 分钟) | 3-7 天 | 1 天 | 2-4 小时 |
| 日内(15 分钟-1 小时) | 14-30 天 | 3-7 天 | 12-24 小时 |
| 波段(4 小时-日线) | 30-90 天 | 7-14 天 | 2-5 天 |
| 持仓(日线-周线) | 90-180 天 | 30 天 | 5-10 天 |
快速开始
from walk_forward import WalkForwardValidator, WalkForwardConfig
config = WalkForwardConfig(
train_size=90,
test_size=14,
step_size=14,
window_type="rolling",
embargo_size=3,
purge_horizon=1,
)
validator = WalkForwardValidator(config)
for fold in validator.split(price_data):
model.fit(fold.train_X, fold.train_y)
predictions = model.predict(fold.test_X)
fold.record_performance(predictions, fold.test_y)
results = validator.aggregate_results()
print(f"OOS Sharpe: {results.oos_sharpe:.3f}")
print(f"Train/Test Sharpe ratio: {results.sharpe_ratio_ratio:.2f}")
文件
参考资料
references/methodology.md— Walk-forward 理论、窗口类型、清除、隔离期、包含公式的 CPCV 算法references/overfit_detection.md— Deflated Sharpe 比率、回测过拟合概率、多重检验校正references/practical_guide.md— 加密货币窗口大小选择、市场机制考量、常见验证错误
脚本
scripts/walk_forward.py— Walk-forward 验证引擎,支持滚动和扩展窗口;--demo模式使用合成数据scripts/overfit_detector.py— Deflated Sharpe 比率和 PBO 计算;--demo模式使用合成回测结果