第 4 课 · 策略骨架与人生第一回测

用第 2 课存的茅台数据写一个双均线策略,并亲眼看到"回测幻觉"是怎么造出来的

预计用时:40 分钟 上一课:第 3 课 · 新闻情绪打分器

这节课你将得到什么

你的第一个完整回测:5/20 双均线策略(金叉买、死叉卖)跑在茅台 2024 年的真实日线上。但重点不是策略本身,而是脚本会跑两遍:先按网上教程最常见的错误写法跑一个"幻觉版",再按 A 股真实规则跑一个"现实版"——同一份数据、同一个策略,你会亲眼看到两个版本差出多少钱。

这节课是你量化零基础补课里最关键的一节:以后任何人(包括 Agent、包括你自己)给你看一份回测结果,你都知道该审哪四个地方。

一、回测四要素:造假就藏在这四个角落里

任何回测,本质上是四样东西的组合。每个要素都有对应的"造假手法",对照着审,没有回测能骗过你:

要素本课的做法常见造假手法
1. 数据前复权日线(第 2 课已落地 CSV)用不复权数据,除权假跳水触发错误信号
2. 信号收盘价算 MA5/MA20,金叉买死叉卖信号用到当天盘中才知道的数据(未来函数)
3. 成交规则收盘出信号,次日开盘价成交"当日信号当日收盘价成交"——本课主角
4. 成本佣金万 2.5(最低 5 元)+ 印花税 0.05% 仅卖出不计成本,高频策略的盈利全靠漏算它

第 1 课学的 T+1、一手 100 股、佣金印花税,第 2 课学的前复权,这节课全部要用上——约束不是背景知识,是要写进代码的。

二、策略:5/20 双均线(10 行字就能说清)

脚本 scripts/first_backtest.py 里,信号生成只有三行 pandas(你熟):rolling(5).mean()rolling(20).mean(),再比较"昨天 MA5 还在 MA20 下方、今天到了上方"。其余代码全在建模成交与成本——这正是本课的态度:信号人人会写,成交规则和成本才见功力。

三、动手:跑起来

数据用的是第 2 课落地的 data/600519_qfq_2024.csv,全程不联网。运行:

cd "$HOME/AI Project/Quant Learning"
source .venv/bin/activate
python scripts/first_backtest.py

你会看到(我已在你的环境实测通过,交易明细较长,中间省略):

数据:600519_qfq_2024.csv,242 个交易日(前复权,口径见第 2 课)
信号:金叉 9 次,死叉 8 次

【正确版本交易明细】信号次日开盘成交,含成本
  买入 2024-02-08  开盘价  1563.03  600 股  金额 937,818  费用 234.45
  卖出 2024-03-11  开盘价  1522.13  600 股  金额 913,278  费用 684.96  本笔盈亏 -25,459
  ……
  买入 2024-09-30  开盘价  1569.43  500 股  金额 784,715  费用 196.18
  卖出 2024-10-29  开盘价  1446.99  500 股  金额 723,495  费用 542.62  本笔盈亏 -61,959
  ……
  买入 2024-12-13  开盘价  1437.72  500 股  金额 718,860  费用 179.72

==========================================================
对比:同一个策略、同一份数据,只改成交规则与成本
==========================================================
            错误版本(幻觉)  正确版本(现实)  买入持有(基准)
----------------------------------------------------------
总收益率           -19.81%         -23.79%          -5.75%
年化收益           -20.54%         -24.64%          -5.98%
最大回撤           -21.46%         -25.12%         -25.17%
交易次数                 8               8               1
胜率                   12%             12%              —
==========================================================
幻觉泡沫:错误版本比正确版本多报 3.98% 的收益
注:年末仍持仓(最后一个金叉后未现死叉),浮动盈亏已计入。

四、解剖"回测幻觉":3.98 个百分点的泡沫从哪来

先承认一个反直觉的事实:你可能期待错误版本给出一个亮眼的正收益,但茅台 2024 年的真实数据给了我们一份更好的教材——连作弊版都救不了这个策略。幻觉版的"亮眼"体现在别处:

幻觉来源错误版本的做法为什么是造假
成交价信号当日收盘价成交均线要用收盘价才能算出来——金叉这个信号在 15:00 收盘那一刻才"存在",你不可能在同一时刻已经按收盘价成交了。这是用未来的信息做过去的决定,俗称未来函数 / 前视偏差(look-ahead bias)
成本佣金、印花税全不算8 个来回 = 16 次收费。看交易明细:卖出 91 万收 684.96 元(佣金 228 + 印花税 457),一年下来成本吃掉的钱比最大一笔盈利(+12,153)还多

两个幻觉叠加,净结果是把收益虚报了 3.98 个百分点(-19.81% 对 -23.79%)。在亏损策略上它只是"少亏一点";放在一个本身赚 5% 的策略上,这 4 个点就是把"平庸"包装成"亮眼"的全部化妆品。网上大量晒回测的策略,死因就是这两行代码。

为什么 A 股的 T+1 让这种造假更不能忍

T+1 的制度精神是:今天的决策,明天才能执行出结果。你当晚复盘发现金叉,最早明天开盘买入;买入的股票最早后天才能卖。"当日信号当日成交"不仅是数学上的未来函数,在 T+1 制度下连物理上都不成立——你的回测必须体现"信号日"和"成交日"之间隔着的那个晚上。那个晚上会发生很多事(隔夜美股、突发公告、集合竞价跳空),次日开盘价就是对这种不确定性的定价。这也是为什么你的 Agent 辅助决策系统的正确形态是"盘后分析 → 次日计划",而不是盘中瞬时反应。

再看成本细节(脚本里 buy_fee / sell_fee 两个函数):

五、更难堪的真相:跑输"买入持有"18 个点

幻觉解剖完,还有个更扎心的数字:正确版本 -23.79%,而什么都不做、年初买入持有只有 -5.75%。这个策略 8 笔交易 7 亏 1 赚,胜率 12%,被买入持有甩开 18 个百分点。

回看交易明细,死因清晰:2024 年的茅台是震荡市,正是趋势策略的坟场——每次金叉都追在短期高点,每次死叉都割在短期低点("高买低卖",和你直觉中的散户行为一模一样)。最惨的一笔:9 月底政策行情启动,股价几天暴涨,但均线是滞后指标,金叉信号 9 月 27 日收盘才出现,9 月 30 日开盘 1569.43 追进去——上涨最肥的一段已经走完,随后 10 月回落,10 月 29 日 1446.99 止损,一笔亏 61,959。策略没有对错,只有适配的行情。

过拟合预警(第 7 课的钩子,先埋在这里)

看到 -23.79%,你的本能反应可能是:"那把 5/20 换成别的参数试试?3/10?10/30?总有一组能赚钱。"——停。在 242 个数据点上反复调参,直到调出正收益,这不叫研究,叫过拟合:你找到的不是规律,是这份特定历史数据里的噪音形状。换一年数据,"最优参数"立刻失效。判断一个参数是不是过拟合,需要样本外验证、参数稳定性分析等一整套方法——这是第 7 课"Agent 辅助写策略"的主题:Agent 调参的速度是人的 100 倍,制造过拟合的速度也是 100 倍,你必须比它更懂陷阱。

六、这节课在全局中的位置

回到你的目标——用 Agent 辅助实盘决策。这节课给了你两块地基:第一,回测四要素的审计清单,以后 Agent 给你写的每一个策略,你都按这四条审它的回测代码;第二,一个残酷但健康的预期:简单策略大概率跑不赢买入持有,策略研究的价值在于严谨地证伪,而不是漂亮地造假。Agent 能帮你把"写一个策略 + 严谨回测"的周期从一周压缩到一小时——前提是你要能分辨它交上来的回测是不是幻觉。

自测

凭记忆作答——答错比答对更能加固记忆。

动手挑战(可选,10 分钟)

把脚本里的 SHORT, LONG = 5, 20 改成 10, 30 重跑,看结果变好了还是更差了。然后回答自己一个问题:如果改好了,你凭什么相信这不是过拟合?如果改差了,你凭什么相信 5/20 本身不是过拟合?想不清楚就留着——这正是第 7 课要解决的。

延伸阅读

下一课预告

第 5 课 · 会查证的 Agent:工具调用循环——让 LLM 不再凭记忆回答,而是自己调用工具查行情、查新闻,把"说"和"查证"连成一个循环。

有任何不清楚的地方(比如均线信号的三行 pandas、费用计算、年化与回撤的公式),直接问我——我是你的老师。