回测幻觉的最后一层——你以为能成交的价格,实盘里根本不存在
在第 4 课的回测框架上加"现实摩擦三件套":①滑点(买入 +0.1%、卖出 -0.1%);②涨跌停不可成交(当日涨跌幅 ≥ 9.8% 视为触板,涨停禁买、跌停禁卖,信号顺延);③一张对比表——第 4 课基础版 vs 加入摩擦后的现实版,看收益又被吃掉多少。
心智模型只有一句话:回测里的"价格存在"不等于实盘里的"成交存在"。第 4 课你学会了审回测的四个要素,这节课把其中"成交规则"这一格挖到实盘的深度。
到这个系列的后半程,该把"回测幻觉"整理成一个完整的分层模型了——以后任何人(包括你的 Agent 团队)交给你一份回测,你按这三层从上往下审:
| 层次 | 幻觉内容 | 哪一课治的 |
|---|---|---|
| 1. 未来函数 | 信号用到了成交时刻不可能知道的数据(当日收盘出信号、当日收盘成交) | 第 4 课:收盘出信号,次日开盘成交 |
| 2. 成本 | 佣金、印花税不算,高频来回的盈利全靠漏算它 | 第 4 课:佣金万 2.5(最低 5 元)+ 印花税 0.05% 仅卖出 |
| 3. 流动性 | 假设"有价格 = 能按这个价格成交"——忽略滑点、冲击成本、涨跌停封板 | 本课 |
第 7 课讲的过拟合属于另一个维度(研究方法的幻觉),这三层则是成交建模的幻觉:哪怕你的信号是真的、没有未来函数,只要成交价格与成交能力是编的,收益就是编的。一个有意思的规律:层次越往下,幻觉越"诚实"——未来函数是明显的错误,而流动性幻觉往往来自"懒得建模",杀伤力却一样大。
回测里写"次日开盘价成交",隐含假设是:你挂的单能恰好按开盘价成交。实盘里做不到,原因有三个:
工程上不用精确还原这三个机制,一个保守的固定滑点就够用(脚本 scripts/realistic_backtest.py 里的写法,注释是给你看的,不用敲):
SLIPPAGE = 0.001 # 滑点:买入 +0.1%,卖出 -0.1%
price = row["开盘"] * (1 + SLIPPAGE) # 买入:成交价 = 开盘价 × 1.001
price = row["开盘"] * (1 - SLIPPAGE) # 卖出:成交价 = 开盘价 × 0.999
注意方向:两个方向的滑点都对你不利——买得更贵、卖得更便宜。这是刻意的保守:回测宁可把摩擦估高一点,给策略留安全边际。如果你的策略连 0.1% 的单边滑点都扛不住,它大概率本来就活不到实盘。
复习第 1 课的硬规则(规则速查):主板涨跌幅限制 ±10%。涨停时发生了什么?价格被顶在涨停板,所有想买的人排队挂在涨停价,而持有者惜售——卖盘几乎消失。你的买单排在几万手封单后面,全天可能一股都买不到。跌停镜像对称:想卖的人排队,买盘消失,你的止损单挂出去没人接。
这就是第三层幻觉的核心:涨跌停那天,K 线上明明印着开盘价、收盘价,但那些价格对你来说不可成交。回测若照常按开盘价买入,等于假设你能插到封单队伍的最前面——这是比未来函数更隐蔽的造假。
本课的建模(阈值留 0.2% 余量,涨幅 ≥ 9.8% 即视为触板):
if pending == "buy" and row["涨跌幅"] >= LIMIT_PCT:
# 涨停:买单无法成交,信号顺延到下一个可成交日
defer_buy += 1
elif pending == "sell" and row["涨跌幅"] <= -LIMIT_PCT:
# 跌停:卖单无法成交,信号顺延
defer_sell += 1
涨到 +9.8% 时卖盘通常已经枯竭、板上封单已成形,等精确到 +10.00% 再判定会漏掉大量"接近封死"的日子。回测建模的原则和滑点一样:宁可保守,不可乐观。同理,ST 股 ±5%、创业板/科创板 ±20% 的阈值要按标的调整——本课用主板茅台,取 9.8%。
脚本完全离线,数据还是第 2 课落地的 data/600519_qfq_2024.csv。运行:
cd "$HOME/AI Project/Quant Learning"
source .venv/bin/activate
python scripts/realistic_backtest.py
你会看到(我已在你的环境实测通过,交易明细较长,中间省略):
数据:600519_qfq_2024.csv,242 个交易日(前复权,口径见第 2 课)
信号:金叉 9 次,死叉 8 次
触板日(|涨跌幅|≥9.8%):0 天(最大单日涨幅 9.29%,最大跌幅 -7.42%)
【现实版交易明细】信号次日开盘成交 + 滑点 + 涨跌停禁交易
买入 2024-02-08 成交价 1564.59 600 股 金额 938,756 费用 234.69
卖出 2024-03-11 成交价 1520.61 600 股 金额 912,365 费用 684.27 本笔盈亏 -27,310
……
买入 2024-09-30 成交价 1571.00 500 股 金额 785,500 费用 196.37
卖出 2024-10-29 成交价 1445.54 500 股 金额 722,772 费用 542.08 本笔盈亏 -63,467
……
买入 2024-12-13 成交价 1439.16 500 股 金额 719,579 费用 179.89
==================================================================
对比:同一个策略、同一份数据,逐步加入现实摩擦
==================================================================
基础版(第4课) 现实版(滑点+涨跌停) 买入持有(基准)
------------------------------------------------------------------
总收益率 -23.79% -25.39% -5.75%
年化收益 -24.64% -26.29% -5.98%
最大回撤 -25.12% -26.73% -25.17%
交易次数 8 8 1
胜率 12% 12% —
==================================================================
现实摩擦:收益从 -23.79% 降到 -25.39%,再吃掉 1.60%
其中滑点累计 14,021 元;涨跌停顺延:买 0 次、卖 0 次
注:年末仍持仓(最后一个金叉后未现死叉),浮动盈亏已计入。
把这笔账和第 4 课连起来看:幻觉版 -19.81% → 第 4 课基础版 -23.79% → 本课现实版 -25.39%。同一个策略、同一份数据,三层幻觉全部挤掉后,收益又少了 5.58 个百分点。这个策略本来就在亏钱,摩擦让它亏得更多——摩擦是乘法,不是加法:它放大亏损、侵蚀盈利,方向永远对你不利。
对比表里滑点累计 14,021 元,看最典型的一笔(9 月底追高的那次):
一年 9 买 8 卖共 17 次成交,每次被滑点咬一口,累计就是 1.4 万——比全年级数最大的一笔盈利(+8,577)还多。这就是"隐形税收":单笔看无感,全年看致命。
另一个数字同样重要:涨跌停顺延 0 次。茅台 2024 全年没有一天涨跌幅达到 9.8%(最接近的是 9 月 26 日 +9.29%)。这是大盘蓝筹的特点,不是市场的特点——换成小盘股、换成 2015 年那种极端行情,触板日会密集出现。规则必须在代码里就位,哪怕它一年都不触发一次;它就像保险丝,平时不存在,出事时保命。
真实数据上演示不了顺延机制,脚本里内置了一段教学构造的迷你行情(非真实行情):金叉次日涨停、死叉次日跌停。两个版本的分歧一目了然:
基础版(不管涨跌停,假设涨停板也能成交):
买入 2024-12-03 成交价 110.00 9000 股 金额 990,000 费用 247.50
卖出 2024-12-09 成交价 97.20 9000 股 金额 874,800 费用 656.10 本笔盈亏 -116,104
现实版(触板禁交易,信号顺延):
2024-12-03 涨停(+10.00%),买单无法成交,信号顺延
买入 2024-12-04 成交价 112.11 8900 股 金额 997,797 费用 249.45
2024-12-09 跌停(-10.00%),卖单无法成交,信号顺延
卖出 2024-12-10 成交价 93.91 8900 股 金额 835,763 费用 626.82 本笔盈亏 -162,910
基础版总收益 -11.61% vs 现实版总收益 -16.29%
两件事值得盯紧:第一,基础版那个"12 月 3 日按 110 买入"本身就是幻觉——涨停封板时你排在几万手封单后面,根本买不到;它账面上的 -11.61% 是一个你拿不到的结果。第二,顺延不是免费的,而且和 T+1 叠加成连锁反应:涨停追不进,顺延一天买到时贵了 2%(112.11 对 110);跌停卖不出,顺延一天卖出时又低了 3.4%(93.91 对 97.2),且因为 T+1,顺延买入的仓位当天还锁着不能卖。如果跌停是连续的(极端行情里常见,想想 2015 年股灾期间的千股跌停),你的止损单会一天天地顺延,风险敞口被迫持续扩大——回测里的一次"信号顺延",在实盘中就是眼睁睁看着亏损放大却动不了。
你的 Agent 盘后出信号、生成"次日操作计划"。这节课之后,计划里必须有分支逻辑:"若明日触板涨停,则买入计划取消还是改限价排队?若触板跌停,止损无法执行,风险敞口怎么算?"一个不会处理涨跌停的 Agent,在极端行情里给出的建议是不可执行的。免费数据源在极端行情里也更容易挂(第 2 课的教训),计划里还得有"数据缺失时保守处理"的兜底。
把三层幻觉都建模完之后,你的回测收益就是实盘收益的乐观上界,因为总有一些摩擦你永远建模不全:
由此得到一个极其实用的判据:回测里只赚 2~3% 的策略,实盘大概率亏钱——边际收益会被你没建模到的摩擦吃光。回测的真正用途是证伪(快速淘汰不行的策略),而不是许诺收益。这也是整个系列的基调:Agent 把"写策略 + 严谨回测"的周期压缩到一小时,意味着你一晚上能证伪十个想法——这才是它给你的真实杠杆。
凭记忆作答——答错比答对更能加固记忆。
把脚本里的SLIPPAGE = 0.001改成0.003(模拟一只流动性差的小盘股)重跑,看现实版收益变成多少。再想想:如果一个高频策略每天交易一次,0.3% 的单边滑点一年会吃掉多少收益?这就是为什么"回测年化 200% 的高频策略"在 A 股实盘里几乎都不成立。
第 15 课 · 仓位管理与凯利公式:这个策略 8 笔交易 7 笔亏,但比"买什么"更生死攸关的问题是"买多少"——满仓一把梭,一次黑天鹅就能让你出局。仓位决定你能不能活到策略生效的那天。
有任何不清楚的地方(比如滑点的方向、顺延的代码逻辑、9.8% 阈值的取舍),直接问我——我是你的老师。