第 7 课 · Agent 辅助写策略

人机协作迭代回测的正确姿势——以及量化新手的头号死因:过拟合

预计用时:40 分钟 前置:第 2 课 · 数据底座(复权)与第 4 课(双均线策略与回测规则)

这节课你将得到什么

一个纯离线的参数扫描脚本 scripts/param_sweep.py:它把第 4 课的双均线策略跑 16 组参数(短均线 3/5/8/10 × 长均线 15/20/30/60),严格遵守第 4 课的回测规则(信号收盘产生、次日开盘成交计入成本),输出每组参数的年化收益与最大回撤。

但表格不是重点。重点是这张表教你看的"地形":参数高原孤峰的区别。看懂它,你就理解了量化交易里死人最多的那个坑——过拟合,也就明白了"让 Agent 帮你调参"的正确姿势和它的边界。

一、人机分工:四步循环

到这一课,你已经有了数据(第 2 课)、规则约束(第 1 课)、文本分析组件(第 3 课)和第一个回测(第 4 课)。现在把 Agent 正式拉进策略研发流程。正确的分工是一个循环,四步里有两步永远是人干的:

步骤谁干干什么
1. 定假设"茅台在震荡市里双均线会被反复打脸"——假设来自你对市场的理解,必须能用一句话说清"为什么它可能赚钱"
2. 写码Agent把假设翻译成策略代码和回测框架——这是 Agent 的强项,快且不知疲倦
3. 验证机器跑回测、跑参数扫描,产出数字和表格
4. 审逻辑审查:收益从哪来的?有没有未来函数?参数是高原还是孤峰?然后回到第 1 步修正假设
循环的灵魂在第 1 步和第 4 步

新手最常犯的错误是把循环压缩成"Agent 写码 → 看收益数字 → 让 Agent 继续调"。这样跑出来的不是策略,是一台过拟合制造机:Agent 会在你的数据上无限试参,直到数字好看为止——而那些数字恰恰是最不可信的。你花在第 1、4 步的判断力,才是整个流程里唯一不可外包的东西。

二、给 AI 下策略任务的 Prompt 模板

Agent 写回测代码最大的风险不是写不出来,而是写得太漂亮——它默认会给你一个"看起来对"的回测:当日信号当日成交(未来函数)、不计成本、只报一个最优收益。所以任务书里必须钉死四条约束,少一条,结果就是废纸:

我要回测一个A股双均线策略:短均线上穿长均线买入,下穿卖出,
标的贵州茅台,数据用 data/600519_qfq_2024.csv(前复权日线)。

硬性约束(违反任何一条视为错误):
1. 禁止未来函数:信号在T日收盘确认,成交只能在T+1日开盘价
2. 必须计入成本:买入0.03%,卖出0.08%(含卖出印花税0.05%)
3. 数据口径:只用前复权价,并在代码注释里写明口径
4. 输出参数敏感性:对短均线[3,5,8,10]×长均线[15,20,30,60]
   共16组参数逐一回测,输出年化收益和最大回撤表格,
   不许只报"最优参数"的结果

四条约束对应四个死因:约束 1 防回测幻觉(第 4 课亲手踩过的坑);约束 2 防收益虚高(第 1 课:双边摩擦约 0.1%,频繁交易一年磨掉本金四分之一);约束 3 防数据造假(第 2 课:不复权数据在除权日产生假跳水);约束 4 防孤峰欺骗——这正是本课的正题。

三、动手:跑参数扫描

脚本是纯离线的,只读第 2 课落地的 CSV,不碰网络(免费接口会挂的道理不用再强调了——能离线就离线)。逐行复制:

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

你会看到(我已在你的环境实测通过,数字是真实运行结果):

数据:data/600519_qfq_2024.csv,242 个交易日(前复权,除权口径正确)
回测规则:信号收盘产生、次日开盘成交;买入费 0.03%,卖出费 0.08%

表 1:年化收益率(16 组参数)
短\长 |    15 |    20 |    30 |    60
------------------------------------
   3 |-22.0%  -10.4%  -14.1%   -3.4%
   5 |-19.7%  -26.7%  -16.0%   -9.6%
   8 |-14.2%  -36.5%  -29.6%  -21.6%
  10 |-24.9%  -35.3%  -28.3%  -12.6%

表 2:最大回撤(16 组参数)
短\长 |    15 |    20 |    30 |    60
------------------------------------
   3 |-23.1%  -16.4%  -16.5%  -14.5%
   5 |-20.9%  -27.2%  -21.6%  -16.4%
   8 |-21.3%  -37.8%  -30.4%  -26.2%
  10 |-31.8%  -35.9%  -27.6%  -17.1%

对照:2024 年买入并持有茅台的总收益 = -6.2%

先别管分析,自己盯着表 1 看一分钟。你注意到三件事了吗:① 16 组参数全部亏损;② "最优"的 3/60(-3.4%)恰好蹲在表格边角上;③ 它旁边的格子(5/60、3/30)比它差一大截。这三件事就是本课的核心。

四、读表:参数高原 vs 孤峰(本课核心)

脚本最后会自动做"地形分析",输出如下(真实结果):

========================================================
地形分析:最优参数是孤峰还是高原?
========================================================
样本内最优参数:短均线 3 / 长均线 60,年化 -3.4%
其相邻参数(共 3 组)平均年化:-13.3%
最优格与邻居的差距:+9.9%
判定:孤峰——最优格显著高于周围,参数挪一格收益就塌。
这是过拟合的典型长相:它记住的是 2024 年茅台的噪音,不是规律。

把参数网格想象成一片地形,年化收益是海拔:

参数高原孤峰
长相一片连续的高地:参数在小范围内挪动,收益变化不大一根针:某个参数组合鹤立鸡群,挪一格收益就塌
含义策略可能捕捉到了某种稳定的市场结构,对参数选择不敏感策略"记住"了这段历史里的几笔偶然交易,对参数极度敏感
未来表现值得进入样本外数据继续检验(仍可能失败,但可聊)几乎可以断定未来失效——噪音不会按原样重演

我们这张表里还有第三个信号:最优参数在网格边缘。3/60 的"短均线 3"已经是你扫描范围的最小值——如果它是真规律,为什么更小的值你没扫?边缘最优通常意味着两种可能:真正的"最优"在网格外(你的假设方向就错了),或者它纯粹是噪音。两种都不是好消息。

这张表最诚实的一句话

16 组参数全亏,买入持有也才 -6.2%。说明 2024 年的茅台是震荡下行市,双均线这类趋势策略在这里没有可捕捉的结构——参数扫描救不了一个错误的前提。这就是四步循环第 4 步"人审逻辑"该下的结论:不是"把 3 改成 2 再扫一遍",而是"这个假设在这只标的上不成立,回到第 1 步"。

五、样本内最优 ≠ 未来最优

现在把逻辑钉死。你扫描的数据叫样本内(in-sample);策略未来要面对的行情叫样本外(out-of-sample)。核心事实是:只要你试的参数组合足够多,总有一组会在样本内显得"最优"——哪怕数据是纯随机数。这不是策略发现了规律,是多重检验下的必然假象。r/algotrading 社区关于过拟合的讨论里,这几乎是每个新手帖的标配剧情(见延伸阅读)。

工程上的标准做法(本课只建立概念,后续课程实操):

概念出处:Marcos López de Prado 在 《The Deflated Sharpe Ratio》中用数学证明了"回测次数越多,样本内高夏普比率越可能是假的",是这一话题的经典文献(前瞻阅读,不求全懂)。

六、Agent 是加速器,不是判断力的替代品

回到人机分工。这节课的扫描脚本,从想法到实测通过,如果用 Agent 辅助编写大约是十分钟的活——手写可能要一个下午。这就是 Agent 的真实价值:它把"写码"和"跑数"的成本压到接近零,让你可以在四步循环里转得快得多。原来一周验证一个假设,现在一天验证五个。

但请注意循环变快之后,瓶颈挪到了哪里:挪到了第 1 步的假设质量第 4 步的审查深度。Agent 不会告诉你"双均线用在震荡下行单票上前提就不成立"——它没有你的持仓、你的市场体感、你的风险承受力,它只对"把代码写对"负责。把第 1、4 步也让渡给 Agent 的人,得到的不是量化策略,是一份样本内精美的亏损说明书。对你的实盘目标来说,记住这句话:Agent 决定你迭代得多快,你决定迭代的方向对不对。

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

打开 scripts/param_sweep.py,把 SHORT_WINDOWSLONG_WINDOWS 换成你自己的猜测(比如短 [5,10,20]、长 [30,60,120]),重跑。然后回答自己两个问题:新网格里出现了高原吗?如果有"最优参数",它在网格内部还是边缘?把你观察到的情况贴给我,我们一起判断它是不是孤峰。

自测

凭记忆作答,答错会显示解析。

延伸阅读

下一课预告

第 8 课 · 执行闭环架构:策略验证通过之后怎么落地?vn.py / QMT 两条实盘路径怎么选,风控(仓位、止损、熔断)如何装进系统——从"回测好看"到"实盘敢用"的最后一公里。

有任何不清楚的地方(比如地形分析的判定逻辑、样本外怎么切、Prompt 模板怎么改),直接问我——我是你的老师。