跳到主要内容

偏差的工程防范:前视 / 幸存者 / 过拟合

直觉

模块 0.3 介绍过三大偏差。这一节把它们落到工程层:每个偏差长什么样、怎么用代码自检、怎么防范。三者的共同症状都是——回测表现好得不真实

三大偏差与防范

偏差表现工程防范
前视用了当时不可知的信息信号 shift(1);引擎内置 持仓=信号.shift(1)
幸存者只回测了「活下来」的标的用含退市标的的 PIT 数据库
过拟合参数调到完美贴合历史样本外验证、参数稀疏化(见 8.5)

可运行案例:前视泄漏让夏普「好得离谱」

构造一个偷看明天涨跌的信号( blatant 前视),与真实双均线信号对比——泄漏信号的夏普会荒唐地高,这正是前视的「红牌」。

import quant
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

df = pd.read_csv('/data/spy_daily.csv', parse_dates=['date']).set_index('date')
close = df['adj_close']
ret = close.pct_change()
cost = 2.0

# —— 泄漏信号: 用"明天"的涨跌方向(赤裸裸的前视) ——
leaky = np.sign(ret.shift(-1)).fillna(0.0)            # 1=明天涨, -1=明天跌
# —— 真实信号: 双均线(无未来信息) ——
raw = (close.rolling(20).mean() > close.rolling(60).mean()).astype(float)
safe = raw.replace(0, np.nan).ffill().fillna(0)

r_l = quant.vector_backtest(close, leaky, cost_bps=cost, freq=252)
r_s = quant.vector_backtest(close, safe, cost_bps=cost, freq=252)
print(f"泄漏信号 夏普 = {quant.sharpe(r_l['returns'], freq=252):6.2f}  ← 好得不真实(偷看未来)")
print(f"真实信号 夏普 = {quant.sharpe(r_s['returns'], freq=252):6.2f}  ← 正常水平")
print("\n→ 铁律: 任何回测夏普好得离谱, 第一反应就是查前视泄漏。")

plt.figure(figsize=(8, 3.4))
plt.plot(r_l['equity'], color='crimson', label='泄漏(前视)')
plt.plot(r_s['equity'], color='#2563eb', label='真实(双均线)')
plt.legend(); plt.title('前视泄漏 vs 真实信号'); plt.tight_layout(); plt.show()

小结

  • 前视:信号 shift(1)、引擎内置防前视;
  • 幸存者:用含退市标的的 PIT 数据;
  • 过拟合:样本外验证、少参数;
  • 回测夏普「好得不真实」≈ 一定有偏差,先排查再相信。

下一节用 Walk-Forward 把「样本外」做成可执行的验证流程。