← Quant 101 总览
第 03 关 · 策略

第一个策略:双均线交叉Your First Strategy & Look-Ahead

终于要写一个会“自己买卖”的策略了。但更重要的是:这一关你会亲眼看到量化最大的坑——未来函数——是怎么把回测做得假漂亮的。

1双均线交叉:规则很简单

快线(20 日均线)上穿慢线(60 日均线)= 金叉 → 买入持有;下穿 = 死叉 → 空仓。直觉是“短期势头转强就上车,转弱就下车”。

ma_fast = px.rolling(20).mean()
ma_slow = px.rolling(60).mean()
signal  = (ma_fast > ma_slow).astype(int)   # 想持有=1, 空仓=0

2关键:信号和执行的“时间差”

今天收盘才算得出信号,所以最早明天(T+1)才能成交。代码上就是把信号往后挪一天(shift(1))。这一步看着不起眼,却是防未来函数的命门:

pos   = signal.shift(1).fillna(0)          # T+1 才持有(防未来函数!)
trade = pos.diff().abs()                   # 仓位变化=发生买卖
ret_strat = pos*ret - trade*0.001          # 持仓吃收益, 调仓扣单边千1成本

3亲眼看“未来函数”造假

我们故意做两个“偷看未来”的版本对比:

策略年化夏普回撤
买入持有26.8%0.94-47%
双均线·正确(T+1)14.1%0.73-37%
双均线·不延迟(轻微偷看)15.4%0.78-36%
偷看明天(作弊)530%9.590%

“偷看明天”= 只在会涨的日子持有(ret.where(ret>0,0)),用了当天才知道的真实涨跌——年化 530%、夏普 9.59、零回撤。现实绝不可能。

★ 最该记住的一条铁律

如果你的回测夏普 > 3、几乎不回撤、曲线笔直向上——99% 是 bug(代码里混进了未来信息),不是你发现了圣杯。第一反应永远是“我哪里偷看了未来”,而不是“我要发财了”。

4一个反直觉的事实

“双均线·正确版”(14.1%)反而跑不赢躺着不动的买入持有(26.8%)。在茅台这种大牛票上,择来择去会错过大涨、还交手续费。说明择时是有代价的,简单技术指标不是印钞机。但注意它把回撤从 -47% 降到 -37%——牺牲收益换了平稳。这又回到第 02 关:收益和风险要一起看。

✍ 动手做

1. 改参数(5/20、10/30…),看结果随之大变——这其实埋了“过拟合”的雷(下一关讲)。
2. 故意把 signal.shift(1) 的 shift 去掉(制造未来函数),看夏普怎么虚高,再改回来。亲手造一遍、修一遍,记得最牢。

小结

信号只能用截至T日的信息、成交最早T+1;漂亮到离谱的回测基本是未来函数bug;简单择时常跑不赢躺平,但能降回撤。

上=价格+均线+买卖点(▲买▼卖);下=策略净值 vs 买入持有。
← 上一关
风险与收益的度量
下一关 →
回测的三大陷阱