第 1 章:第一次跑通一个完整策略
前言中那张地图——飞轮的四层(候选 / 组合 / 执行 / 归因)+ 三字要点(做 / 看 / 疑)——在这一章会被你亲手走一圈。你把“做 + 看 + 疑”三个动作都练一次,飞轮也在你手里转完第一圈。
7 个动手实验串成一条主线。先从最简单的定投起步,把动作写成规则(做),看它能不能赚钱(看)。再给它加一条判断让它“聪明”一点,看判断有没有让结果更好(再做、再看)。最后扫描一圈参数,亲眼看到量化交易最常见的失效场景——回测里看着赚钱,换一段时间就不行了(疑)。
这就是为什么本书的口号是「做要规则、看要指标、还要怀疑你的指标」 。
路线图
本章 5 节实验 + 1 节总结,串成第一次完整飞轮闭环,路线如表 1-1 所示。
表 1-1 第 1 章路线图
| 节 | 内容 | 实验 |
|---|---|---|
| 1.1 最简单的量化策略长什么样? | 从“笨定投”起步——拿到沪深 300ETF 数据,跑一次最朴素的每月固定金额买入回测 | 2 |
| 1.2 我的收益算好还是算差? | 引入“基准”——和“什么都不做的买入持有”对比,理解 Alpha 与 Beta | 1 |
| 1.3 怎么才能赢过市场? | 给定投加一条均线判断,看见“加判断”的双刃剑 | 2 |
| 1.4 找到“最优参数”就能盈利了? | 暴力扫描 24 个均线参数,找历史收益率最高的“冠军” | 1 |
| 1.5 真相时刻 | 把数据切成训练 / 测试,看冠军参数到了新数据上是否真有本事 | 1 |
| 1.6 本章总结 | 把 7 个实验串成一条策略进化路径,带走第一次飞轮闭环 | 0 |
| 拓展阅读 | 量化在金融世界的位置;钱从哪来 | — |
1.1 最简单的量化策略长什么样?
这一节我们走完飞轮的第一步——做:把策略写成规则、跑一次回测、看结果。
我们从最简单的策略开始:定投。
没错,就是你可能在银行 App 上见过的那个“定期定额投资”。每个月发了工资,拿出固定的一笔钱买入,不看涨跌、不做判断,机械地重复。这算量化吗?当然算——“量化”只要满足两个条件就够了:规则明确 + 执行可重复。定投完全够格。
不过,我们定投的不是某只个股,而是 ETF(Exchange-Traded Fund,交易所交易基金)。你可以把 ETF 理解成一份“指数套餐”。我们选择的是沪深 300ETF(代码 510300),它把 A 股市场上规模最大的 300 家公司打包在一起——银行、白酒、新能源、互联网,各行各业的龙头都在里面。你买一份 ETF,就等于同时持有了这 300 家公司的股票,不需要你自己去挑选哪家公司好、哪家公司差。
为什么选 ETF 而不是某只股票?因为选股本身就是一门极深的学问。个股可能暴涨,也可能退市归零。而 ETF 跟踪的是整个市场的平均表现——单家公司出问题,其他 299 家还在。对新手来说,一键买入整个市场,是最省心、也最不容易犯大错的起点。
所以我们的定投规则就是:每月第一个交易日,用固定金额(1000 元)买入沪深 300ETF。 没有任何判断,没有任何预测,不看新闻,不猜涨跌。就这么简单。
本章 7 个实验都写在同一个 notebook 文件 q1-strategy.ipynb 里——第一份 spec 会让 AI 帮你创建它,后面 6 份直接接着写,变量、函数、图都在同一个 notebook 里累积下来。
动手实验 1:获取历史数据
接下来是你的第一个动手实验——拿到沪深 300ETF 5 年的历史数据。我们一起把这份 spec 写出来。
起草上下文
我们刚装好环境,notebook 是空的,什么都还没做。上下文一句话说清:
上下文:学员已完成环境配置(Python 3.12 + 虚拟环境 + 依赖包已安装)。这是课程的第一个操作步骤。
📌 要点:上下文段交代“前提是什么”。AI 不知道你之前做了什么,要明示——否则它可能假设环境已有现成数据,跳过下载步骤。
起草任务
任务段要具体到一句话能说清。“获取数据”是反例——什么数据?什么时间?什么形式?q1-strategy.ipynb 是这份任务说明的交付物,这个文件就是一个 Python notebook 文件。notebook 文件是一种交互式的编程源代码,区别于非交互式的 Python 代码,我们在这份文件中可以看到策略的探索过程。
任务:在当前工作目录创建
q1-strategy.ipynb,获取沪深 300ETF(510300.SS)最近 5 年的历史日线数据并可视化。📌 要点:关键事物要点名到 ID。“沪深 300”不够——A 股有沪深 300 指数(000300.SH)也有跟踪它的 ETF(510300.SS、510310.SS、510330.SS),AI 不知道你想要哪个。spec 里所有关键名词都要具体到不能再具体。
起草要求
要求段是步骤主体——AI 自由度最低的部分。我们规定四件事:
要求:
- 导入 yfinance, pandas, numpy, matplotlib.pyplot
- 跨平台中文字体 fallback 链:
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS', 'SimHei', 'STHeiti']+plt.rcParams['axes.unicode_minus'] = False - yfinance 获取数据:标的
510300.SS、起始2021-01-01、参数auto_adjust=True, multi_level_index=False - 数据存到变量
df,索引为 DatetimeIndex,列名含 Close
📌 要点:每个非默认参数都要点名。yfinance 默认会自动调整价格、用多级列名——这两个参数不写出来,AI 可能用默认,结果跟书里不一致。不要让 AI 揣摩你的偏好。
起草结果呈现
最后是“成功长什么样”——这是 spec 最容易被忽略也最重要的部分:
结果呈现:
- 打印数据的前 5 行
- 打印数据时间范围(起始日期 到 结束日期)和总行数
- 画一条收盘价折线图,标题为「沪深 300ETF (510300) 收盘价走势」
📌 要点:结果呈现要让 AI 跑完自己能说“对了 / 错了”。“画一张图”是反例——AI 可能画 K 线、画柱状图、画散点图。要明确到具体的 X/Y 轴、标题文字、变量名。
完整 spec 在 specs/spec-01-get-data.md——把它复制到 AI 编程工具的对话框,弹窗选「允许」。spec 文件用 Markdown 写成(# 标题、- 列表、反引号代码——都是给 AI 看的格式,完整复制就好)。
AI 助手执行完毕后,你会在你的文件夹中看到多出一个叫 q1-strategy.ipynb 的文件。第一次双击打开时,TRAE 会询问你使用什么 Python 核,意思就是:这份 notebook 交给哪个 Python 环境来运行。我们在这里会选择在“开始动手之前”这一章中初始化的 Python 3.12 环境,如图 1-1 所示。

打开 notebook 文件后,源代码是一个一个单元格从上向下构成。这个界面中有几个常用按钮,如图 1-2 所示。
单步运行:每一个单独的代码单元格,可以单独运行这一步。运行结果会在这个单元格下方出现。
全部运行:整个notebook从上向下,逐一全部运行,直到出现错误为止。每个单元格的运行结果都会紧接着出现在代码单元格下方,运行错误也在下方。
重新启动:重新启动所选择的python核。通常,在初始化的python环境中安装了新的python包,或者更新了某个旧的python 包,会重新启动。
当前 python 核:显示当前 notebook 在使用哪个 python 核。

回到你的第一个成果上来,这时候你的 notebook 里应该出现了一张折线图和一些打印输出。AI 帮你做了这些事:从雅虎财经下载了沪深 300ETF(代码 510300.SS)从 2021 年 1 月至今的每日价格数据,打印了前 5 行让你看看数据“长什么样”,然后画了一张收盘价走势图,如图 1-3 所示。

读懂你的第一张图
看看这张收盘价走势图。
先看整体趋势:价格总体是在涨还是在跌?有没有明显的“大起大落”?你大概率会看到价格经历了几轮上涨和下跌的周期,不是一条直线往上走,也不是一路向下。
再看波动范围:最高点和最低点大概差多少?价格在什么区间内震荡?你会发现,波动幅度可能比你想象的大——同一只 ETF,不同时间点买入,结果可能天差地别。
这说明什么? 价格有涨有跌,波动是常态。你不可能买在最低点、卖在最高点——没有人能做到。既然择时这么难,那有没有一种方法,可以不依赖“买在最低点”也能参与市场?定投就是这个思路。
有了数据,一个自然的问题就出来了:如果从几年前开始,每月固定买入一些,到现在是赚了还是亏了? 我们来用历史数据回答这个问题。
动手实验 2:定投回测
在真正投钱之前,我们当然想先知道这个策略靠不靠谱。怎么办?用历史数据模拟一遍。
这就是 回测(Backtest) 的概念——用历史数据模拟“如果当时按某个规则操作,结果会怎样”。就像在脑海中“重播”历史,看看你的策略在过去能不能盈利。回测不能预测未来,但能帮你验证一个想法是否靠谱。
我们一起把这份回测 spec 写出来。这次重点看两件新东西:spec 之间怎么接续,以及复用代码怎么封装成函数。
起草上下文
这次的上下文不再是“什么都没有”——上一份 spec 已经留下了 df 变量。我们直接接着用:
上下文:在
q1-strategy.ipynb中已有上一步的代码:使用 yfinance 获取了沪深 300ETF(510300.SS)的日线数据,存储在变量df中,索引为 DatetimeIndex,包含 Close 列。📌 要点:spec 之间会有“接续关系”——前一份 spec 的输出是后一份的输入。上下文段把这条链条写明(变量名、形状、关键列)。这样 AI 不会重新下载数据、也不会假设一个不存在的列。
起草任务 + 要求
任务一句话:“实现简单定投回测——每月第一个交易日用 1000 元按收盘价买入”。要求段重点在两件事:
要求:
- 把回测逻辑封装为函数
backtest_dca(df, monthly_amount=1000),返回 DataFrame(后续步骤会复用此函数) - 实现:按月分组取每月第一交易日 → 每月份额 = 1000 / 收盘价 → 累计份额逐月累加 → 月末市值 = 累计份额 × 当月最后收盘价 → 累计收益率 = (市值 - 成本) / 成本
- 返回 DataFrame 含列:
total_cost, total_shares, portfolio_value, return
📌 要点:复用型代码必须封装成函数。“后续步骤会复用此函数”这一句话是给 AI 的硬约束——不要写一段一次性脚本,要写一个有清晰入参的函数。后面的实验 5 会基于它派生
backtest_dca_with_ma,函数没封装好整条 spec 链就接不上。📌 要点:返回的 DataFrame 列名要点死。这里写的是
total_cost, total_shares, portfolio_value, return——四个名字一字不差。后面好几节实验都要从result_dca.return这种路径取数,列名只要换成ret或cum_return,后面所有 spec 都得连带改。返回值的字段名是 spec 链的“API 契约”,锁死才能让后面的 spec 写起来一行不差。
起草结果呈现
结果呈现:打印投资期间 / 投资月数 / 总投入金额 / 最终市值 / 最终收益率。画两张图(上下排列,figsize 12×8):上图持仓市值 vs 累计成本(盈利绿、亏损红),下图累计收益率(零线上下绿/红填充)。
完整 spec 在 specs/spec-02-dca-backtest.md——复制给 AI,弹窗选「允许」。
AI 助手执行完毕后,你的 notebook 里会出现一组数字和两张图。AI 帮你做了这些事:按月分组找到每个月的第一个交易日作为定投日,用 1000 元除以当日收盘价算出每月能买多少份,逐月累加持有份额和投入成本,最后计算每月末的持仓市值和累计收益率。
定投的机制有一个天然的好处:价格低的时候,同样 1000 块钱能买到更多份额;价格高的时候,买到的份额更少。就像去菜市场,菜贵的时候少买点,菜便宜的时候多买点。
AI 助手还画出了上下两张图(如图 1-4 所示):
- 上图「持仓市值 vs 累计成本」:蓝色实线是你的持仓市值,它会随市场波动上下起伏;另一条线是你的累计投入成本,它稳步上升(毕竟每个月都在投钱)。两条线之间的区域用颜色标注——绿色代表盈利的时段,红色代表亏损的时段。
- 下图「累计收益率」:更直观地看到收益率随时间的变化曲线。零线以上是赚,以下是亏。

读懂你的回测结果
先看打印出来的数字:总投入是多少,最终市值是多少,收益率是正还是负?
再看两张图。你大概率会发现,收益率在正负之间来回穿越——有一段时间盈利,过一阵又亏回去,然后可能又赚回来。这不是你的策略有问题,而是市场本来就这样。定投不保证盈利,也不保证亏损。它只是一种“分散买入时点”的方法,避免了把所有钱一次性投在最高点的风险,降低了运气的成分。
不管你看到的最终结果是正收益还是负收益,有一件事比结果本身更重要:你怎么知道这个结果算好还是算差?
赚了 5%,听起来还行?但如果同期银行理财能给你 4%,那你承受了股市的波动,只多赚了 1%,值得吗?亏了 3%,听起来很糟?但如果同期大盘跌了 15%,你其实已经大幅跑赢了市场。
这就像考试拿了 80 分——你得知道全班平均分才能判断自己考得好不好。我们需要一个参照物。
1.2 我的收益算好还是算差?
上一节最后留下了一个问题:一个收益率数字本身,不能告诉你任何事。赚了 20% 是好是坏?亏了 5% 是糟糕还是其实还行?这取决于你跟谁比。
你需要一个 基准(Benchmark)。基准就是“全班平均分”——你不知道 80 分算好算差,但如果全班平均 60 分,你就知道自己考得不错;如果全班平均 95 分,那 80 分就得反思了。投资也是一样。你的收益率只有跟某个参照物放在一起看,才有意义。
跟谁比?其实有三种不同层次的基准:
- 绝对基准:你能接受的最低收益率。比如“年化至少 5%,否则我还不如买理财”。这是你的个人底线。
- 相对基准:市场指数。你的策略是赢了大盘还是输了大盘?赢了说明你有超额收益,输了说明你还不如买指数基金。
- 策略基准:同类策略中最简单的那个。择时策略要和“买入持有”比——你费心选时机,总得比什么都不做强吧?权重优化策略要和“等权重”比——你费心调配比例,总得比平均分配强吧?
在后面的章节中,我们会逐步引入这三种基准。但眼下先从最基本的开始。
那什么是最自然的基准?想象你在投资的第一天,面前有两个选择:一是按照某个策略每月定投,二是把所有钱一次性全部买入,然后什么都不做,一直拿着到最后一天。第二个选择就是 买入持有(Buy & Hold)——它代表“什么都不做”的结果。你的策略要是连“什么都不做”都赢不了,那它存在的意义是什么?
所以我们接下来要做的事很简单:算出“什么都不做”能赚多少钱,然后跟定投的结果放在一起比。
动手实验 3:和基准对比
我们一起把这份基准对比 spec 写出来。这次重点看一个新东西:结果呈现段如何写“动态结论”——不知道跑出来谁赢谁输,怎么提前定义两种结果的解读?
起草上下文 + 任务
上下文:在
q1-strategy.ipynb中已有沪深 300ETF 日线数据(变量df,含 Close 列)和简单定投回测结果(变量result_dca,含 return 列)。任务:计算「一次性买入持有」作为基准,和定投策略对比,帮助理解收益来源。
起草要求
要求:
- 基准策略(Buy & Hold):起始日全部资金一次性买入,持有到最后一天;基准收益率 = (终点 - 起点) / 起点,存到
benchmark_return - 计算基准的逐月累计收益率曲线(每月末收盘价相对起始价的涨跌幅)
- 与定投策略对比
起草结果呈现
这一段是这份 spec 最有意思的地方——我们不知道沪深 300 这 5 年是涨是跌,所以得提前写好两种结果的对应文案:
结果呈现:
- 打印起点 / 终点价格、一次性买入收益率、定投收益率、两者差异
- 根据对比结果动态选一句话:
- 如果定投跑赢:「在下跌市中,定投通过分批买入避开了部分高位」
- 如果定投跑输:「在上涨市中,越早全部投入越好」
- 无论哪种结果,都补充一句共同收束:「但两种策略都没有 Alpha——收益完全取决于市场涨跌,这就是 Beta」
- 画一张对比图(figsize 12×6):定投 vs 一次性买入两条累计收益率曲线,图例标注名称
📌 要点:spec 不能预设结果方向。“分批买入避开高位” / “越早全部投入越好”听起来像两个相反的结论,但它们其实是对同一个现象的两种表述——市场跌了 / 涨了。把“双分支动态结论”写进 spec,让 AI 跑完结果会自己讲故事,无论涨跌都不会出现“硬写但和数据不符”的尴尬。
📌 要点:双分支后面常需要一句“共同收束”。本 spec 的“两种策略都没有 Alpha”是把读者的注意力从‘谁赢’拉到‘两者本质相同’——这是教学型 spec 写作的隐藏技巧:分歧的下面藏着共识。
完整 spec 在 specs/spec-03-benchmark.md——复制给 AI,弹窗选「允许」。
AI 助手执行完毕后,你的 notebook 里会出现一组对比数字和一张双线对比图。AI 帮你做了这些事:计算了买入持有的收益率(用最后一天的价格减去第一天的价格,再除以第一天的价格),然后把它和定投的收益率放在一起对比,算出两者的差异。
两条收益率曲线画在同一张图上(如图 1-5 所示)。它们之间的距离,就是两种策略在每个月的差异。有时候定投在上面,有时候买入持有在上面——领先和落后是交替出现的。这张图的价值在于:它不只告诉你最终结果谁赢了,还让你看到整个过程中两者的“赛跑”。

读懂对比结果
先看打印出来的数字。你会发现一个有意思的现象:沪深 300ETF 的起点价格和终点价格可能非常接近。也就是说,如果你在数据起始日一次性买入,持有到现在,可能几乎没赚没亏——市场在这几年里“坐了一趟过山车”,最后又回到了起点附近。
但定投的结果呢?大概率是正收益。为什么“什么都不做”几乎没有收益,而定投反而获得了正收益?
原因不复杂。回想一下定投的机制:价格低的时候,同样 1000 块钱能买到更多份额;价格高的时候,买到的份额更少。过去几年沪深 300 经历了一轮明显的下跌,又有所回升。在下跌过程中,定投不断在低位买入,积累了大量便宜的份额。等到价格回升,这些便宜份额带来的利润弥补了高位买入的亏损。而一次性买入就没有这个“摊低成本”的机会——你在最开始买了,然后只能眼睁睁看着价格跌下去再涨回来。
再看对比图。两条线的交叉和分离,就是两种策略的攻守转换。你会发现,在市场下跌的阶段,定投那条线通常在上面(跌得少);在市场快速上涨的阶段,买入持有可能追上来甚至反超。这也符合直觉——下跌市里,分批买入天然就比一次性买入有优势,因为你避开了在最高点“全仓”的风险。
两个收益率背后的大问题
到这里,不管谁赢了,你可能会觉得:差距其实也没那么大嘛。对,你说得对。因为这两种策略做的是同一件事——买入沪深 300ETF 然后持有。区别只是“一次性全买”还是“分批买”。它们的收益来源是完全一样的:市场本身的涨跌。市场涨,两个策略都赚;市场跌,两个策略都亏。
这引出了量化交易中两个最重要的概念。
Beta(贝塔) 是你“坐电梯”获得的收益。你走进商场,坐上电梯,电梯往上走,你就到了高处。你做了什么吗?你什么都没做,只是站在电梯里。市场就是那部电梯。市场涨,你的资产就跟着涨;市场跌,你的资产就跟着跌。你承担了市场整体涨跌的风险,市场给你的回报就是 Beta。
定投和买入持有,本质上都是在“坐电梯”。收益完全取决于这部电梯——沪深 300——是往上走还是往下走。你没有做出任何“判断”,没有选择买入时机,没有选择哪只股票,没有在任何地方做出比市场更聪明的决定。赚的钱全是 Beta。
那如果有人不甘心只坐电梯呢?
Alpha(阿尔法) 是你“爬楼梯”多走的路。别人坐电梯到了 10 楼,你坐电梯到了 10 楼之后,又自己爬了 5 层到 15 楼——那多出来的 5 层就是你的 Alpha。翻译成投资语言:Alpha = 你的收益 - 市场的收益。如果市场涨了 10%,你赚了 15%,那你有 5% 的 Alpha。如果市场涨了 10%,你只赚了 8%,那你的 Alpha 是 -2%——你不仅没爬楼梯,还在电梯里摔了一跤。
Alpha 需要你做出某种“判断”,并且这个判断要比市场聪明。比如,判断什么时候该买、什么时候该卖、该买什么、不该买什么。这些判断如果正确,就能产生正的 Alpha;如果错误,Alpha 就是负的——你还不如什么都不做。
回到我们的定投策略:它有 Alpha 吗?没有。零。定投不做任何判断,不猜涨跌,不择时。它的收益百分之百来自 Beta。市场好,定投就盈利;市场差,定投就亏损。它只是一种资金分配方式,不是一种“聪明”的策略。
这个结论很重要,值得你记住:到目前为止,我们的策略没有任何 Alpha。收益完全来自 Beta——市场好就赚,市场差就亏。
既然定投没有 Alpha,怎样才能赢过市场?我们需要在策略里加入一些“自己的判断”。最简单的判断工具,是一条线。
1.3 怎么才能赢过市场?
上一节结尾说了:要产生 Alpha,就得在策略里加入“判断”。但什么叫“判断”?不是你拍脑袋觉得明天会涨就买——那叫赌博。量化的判断,必须是一条可以写成明确规则的逻辑。
最简单的判断工具,是一条线——均线(Moving Average)。
均线是什么?打个比方。你今天量体温 37.5°C,是不是发烧了?不好说,也许你刚跑完步、也许体温计不太准。但如果你连续 5 天量体温,算个平均值,发现平均体温都偏高——那大概率是真的不舒服了。均线就是这个“平均体温”。它取过去 N 天收盘价的平均值,把每天的随机波动抹平,留下更稳定的趋势方向。单看某一天的价格就像单看某一次体温,可能被噪音干扰;但看一段时间的平均值,信息就可靠多了。
那“价格在均线上方”意味着什么?意味着最近的价格比过去一段时间的平均价格高。换句话说,市场最近在涨,可能正处于上升趋势。注意我用了“可能”——均线是个滞后指标(Lagging Indicator),它看的是过去,不预测未来。就像你说“这个星期天气都不错”,不代表明天一定晴天。但作为一个参考信号,它总比什么都不看要强。
基于这个逻辑,我们给定投策略加一条规则:每月定投日,仅当收盘价高于 N 日均线时才买入,否则这个月跳过。就这么简单。不买的月份,钱留在手里,份额和成本都不变。
这就是择时(Market Timing)——不再闭着眼睛每月都买,而是先看看“天气”再决定出不出门。市场趋势向上的时候跟着买入,趋势不明朗的时候按兵不动。听上去很合理,对吧?
但这里有一个问题:N 取多少?过去 10 天的平均值?20 天?还是 60 天?不同的 N 就是不同的“回看窗口”:
- MA10(10 天,约 2 周):短期均线。反应灵敏,价格稍微涨几天它就跟着翘头,但也容易被短期波动骗到——价格随便抖一抖,信号就来回翻转。
- MA20(20 天,约 1 个月):中期均线。过滤掉了大部分日常噪音,是比较常用的参数。
- MA60(60 天,约 3 个月):长期均线。非常稳定,但反应迟钝。市场可能已经涨了一大截,它才慢悠悠地确认“嗯,确实在涨”。
到底哪个好?光想没用,我们跑一遍就知道了。但在跑回测之前,先画一张图,亲眼看看均线到底长什么样。
动手实验 4:看见均线
这份“画图” spec 比前面几份更挑细节——它要把图怎么画全部锁死到颜色、线型、标记形状。我们一起把它写出来,看看“视觉契约”要写多细才够。
起草上下文 + 任务
上下文:在
q1-strategy.ipynb中已有沪深 300ETF 日线数据(变量df,含 Close 列)。上一节引入了均线的概念——过去 N 天收盘价的平均值。任务:画出收盘价与三条均线(MA10 / MA20 / MA60)的对比图,并标记 MA20 的上穿 / 下穿点。
起草要求:视觉契约
要求段是这份 spec 的灵魂——视觉细节全部锁死:
要求:
- 截最近 1 年数据:
df_recent = df.loc[df.index[-1] - pd.DateOffset(years=1):].copy() - 三条均线:
rolling(10/20/60).mean() - 找 MA20 交叉点:上穿(前 Close ≤ MA20,当 Close > MA20)/ 下穿(前 Close ≥ MA20,当 Close < MA20)
- 画对比图(figsize 14×7):
- 收盘价(黑色实线)/ MA10(绿色虚线)/ MA20(蓝色实线)/ MA60(红色点线)
- 上穿点:绿色向上三角(
marker='^', markersize=10) - 下穿点:红色向下三角(
marker='v', markersize=10) - 标题「沪深 300ETF:收盘价与均线(最近 1 年)」+ 图例 + 网格
📌 要点:可视化 spec 必须把视觉契约写到颜色、线型、标记形状级别。“画一张对比图”是反例——AI 可能给你 default 蓝色细线、不带 marker、不带网格的图,跟书里截图完全对不上。每一个视觉决策都要进 spec:颜色用什么 hex / linestyle 用“–”还是“:”/ marker 多大 / figsize 多少。 这是【精确】原则在视觉场景的具体落点。
📌 要点:截窗口要带
.copy()。要求里的df.loc[...].copy()不是装饰——pandas 里没.copy()直接切片是“视图”,后面再往这个视图上加列(MA10/MA20/MA60)会触发SettingWithCopyWarning,有时甚至改不到原数据。spec 里写明.copy()比写一句“注意切片”更稳——AI 看到.copy()就知道你要的是独立副本。
起草结果呈现
结果呈现:价格与均线对比图(含交叉点标记)+ 打印交叉统计「上穿 N 次 / 下穿 N 次」。
完整 spec 在 specs/spec-04-ma-visual.md——复制给 AI,弹窗选「允许」。
AI 助手执行完毕后,你的 notebook 里会出现一张价格与均线的对比图,如图 1-6 所示。

读懂均线图
现在你亲眼看到了均线和交叉信号。几个值得注意的地方:
三条均线的“性格”不同。 绿色的 10 日均线紧贴价格,几乎同步波动——价格稍微涨几天它就跟着翘头,跌几天它就立刻掉头向下。蓝色的 20 日均线稍微平滑一些,过滤掉了一些日常噪音。红色的 60 日均线最“淡定”,价格上下翻飞它才慢慢跟。这正是前面说的“回看窗口”的差异:窗口越短,越灵敏也越容易被骗;窗口越长,越稳定也越迟钝。
交叉点就是信号。 绿色三角标记的是“上穿”——收盘价从 MA20 下方穿到上方,意味着短期价格超过了最近一个月的平均水平,趋势可能在转好。红色三角标记的是“下穿”——收盘价跌破了 MA20,趋势可能在转弱。
信号不少,但不是每次都“对”。 仔细看图:有些上穿之后确实涨了一段,信号是“对”的;但也有些上穿之后价格又很快跌回去——刚穿上去就掉下来,这就是“假信号”。均线越短,假信号越多;均线越长,信号越少但更迟钝。没有哪条均线能百分百准确。
有了这个直觉,我们来试试:如果真的按这些信号来决定买不买,结果会怎样?
动手实验 5:加入均线择时
这份均线择时 spec 多了一件事——把可能爆炸的边界 case 列出来。均线初期没值、累计成本为 0 时除法会爆,这些都要在 spec 里点名。我们一起把它写出来。
起草上下文 + 任务
上下文:在
q1-strategy.ipynb中已有沪深 300ETF 日线数据(df)、简单定投回测函数backtest_dca和结果(result_dca)、基准对比代码。任务:在定投基础上加入均线择时——每月第一个交易日,仅当收盘价 > N 日均线才买入。
起草要求:边界 case 显式
要求:
- 函数
backtest_dca_with_ma(df, monthly_amount=1000, ma_period=20),返回格式同result_dca,额外含 signal 列(布尔值)。后续步骤会复用此函数 - 三个边界 case 必须显式处理:
- 均线计算初期(前 N 天)没均线值,这些月份不买入
- 跳过的月份,累计成本和份额保持不变
- 累计成本为 0 时(尚未发生任何买入),收益率直接设为 0,不要用除法,避免除零警告
- 分别跑 MA10、MA20、MA60 三个参数
📌 要点:边界 case 必须显式写进 spec。“每月有买入条件”听起来简单,但隐藏着三个炸点:① 前 N 天没均线 ② 跳过月份的状态怎么传递 ③ 累计成本为 0 时除法会 NaN。只要 spec 不点名这三件事,AI 写出的代码 60% 概率会在某一个炸点上出 warning 或错值。 你不需要让 AI 自己发现边界——你列清楚,它照办。
📌 要点:派生函数的入参命名要和原函数对齐。
backtest_dca_with_ma(df, monthly_amount=1000, ma_period=20)的前两个入参和实验 2 的backtest_dca(df, monthly_amount=1000)完全一致——只在末尾加ma_period。这不是形式主义:spec 链越往后,函数会被反复调用,入参命名一致才能让“两个回测”的对比代码读起来对称。新加参数追在已有参数后面、不打乱原顺序,是 spec 链可读性的隐藏前提。
起草结果呈现
结果呈现:
- 打印每个参数:
MA10: 收益率 X.XX%, 买入 N/M 次+ 简单定投作为对照 - 画对比图(figsize 12×6):简单定投灰虚线 + MA10/20/60 三色实线,图例 + 网格 + 零线
完整 spec 在 specs/spec-05-ma-timing.md——复制给 AI,弹窗选「允许」。
AI 助手执行完毕后,你的 notebook 里会出现一组对比数据和一张多线对比图。AI 帮你做了这些事:在每个交易日计算过去 N 天收盘价的平均值(均线),然后在每月定投日比较当天收盘价和均线值——价格在均线上方就买入,在下方就跳过。和简单定投的区别就在这里:简单定投每个月都买,均线策略有些月份会选择“不出手”。AI 分别用 MA10、MA20、MA60 三个参数各跑了一遍回测,然后画了一张对比图(如图 1-7 所示):灰色虚线是简单定投作为基准,三条有色实线是不同均线参数的策略,四条线放在一起一眼就能看出谁在什么时候领先、什么时候落后。

读懂均线择时的结果
先看打印出来的数字。每一行告诉你两件事:最终收益率和买入次数。
注意买入次数的变化。简单定投是每个月都买,均线策略则会跳过一些月份。MA10 跳过得少一些,因为短期均线容易被价格快速穿越;MA60 跳过得多一些,因为长期均线更“迟钝”,价格要涨很久才能站到它上面。换句话说,均线周期越长,策略越“挑剔”——不是每个月都觉得“可以买”。
再看收益率。三个参数的收益率各不相同——有的可能比简单定投好,有的可能更差。这是第一个值得注意的现象:加了均线判断,不一定收益更高。
为什么会这样?因为均线择时是一把双刃剑。它帮你跳过了一些“价格在下跌通道”的月份,避免了在下跌中继续投钱,这是好事。但它也可能让你错过一些“刚开始反弹”的月份——价格刚从底部起来,还没站上均线,你就没买到最便宜的那几次。少买的月份如果后来涨了,你就少赚了。
现在看对比图。灰色虚线是简单定投,有色实线是三条均线策略。注意它们什么时候分开、什么时候靠近。你可能会发现:在市场大幅下跌的阶段,均线策略的曲线有时比简单定投下跌得少——因为它跳过了一些下跌月份的买入。但在市场持续上涨的阶段,均线策略可能跟不上简单定投——因为有些月份它“犹豫”了,没有买入。
这里有一个关键观察,请你记住:加入判断条件确实改变了结果,但改变的方向不确定。它不是一个“只赚不赔”的改进,而是一种取舍——用“可能错过上涨”换“可能避开下跌”。
而且三个参数的效果不一样。MA10、MA20、MA60,谁最好?在你手上这段数据里,可能 MA20 赢了,也可能 MA60 更好——这取决于市场在过去几年具体走了什么样的路径。
三个参数效果不同——MA10、MA20、MA60,到底哪个最好?为什么不干脆把所有可能的参数都试一遍呢?从 MA5 到 MA120,每隔 5 天试一个,总有一个是最优的吧?
1.4 找到“最优参数”就能盈利了?
上一节我们试了三个参数,效果各不相同。你心里可能已经在想:才三个参数就想找到最优解?太不严谨了。干脆全试一遍——从 MA5 到 MA120,每隔 5 天取一个,一共 24 个参数。跑完之后,选收益率最高的那个。听起来是不是很“科学”、很“严谨”?毕竟我们没有拍脑袋猜,而是用数据说话、穷举搜索。
为什么是 5 到 120 这个范围?5 天太短,几乎等于在看每周的波动,全是噪音;120 天约等于半年,再长的话均线几乎不动,策略基本不会发出买入信号。5 到 120 覆盖了实务中常用的所有均线周期——短期、中期、长期都包含在内。每隔 5 天取一个是为了效率:不需要逐天试,大趋势的差异在 5 天级别就能体现出来,MA15 和 MA16 的结果不会有质的不同。
不过在看结果之前,你可以先想一个问题——如果我朝墙上射了 24 支箭,总有一支是最接近靶心的。但这说明我箭术好吗?先把这个问题放在心里,我们来看看实验结果。
动手实验 6:参数扫描
这份参数扫描 spec 有点反常——它故意保持 naive:不警告“过拟合”,不分训练 / 测试集,就让你“找最优”。我们一起把它写出来,先经历一次“找到漂亮数字”的过程,下一节再回头看看那个数字到底是什么。
起草上下文 + 任务
上下文:在
q1-strategy.ipynb中已有df、result_dca、带均线条件的回测函数backtest_dca_with_ma。任务:扫描所有均线参数(MA5 到 MA120,步长 5),找到历史收益率最高的「最优参数」并可视化。
起草要求 + 结果呈现
要求:扫描范围 MA5 到 MA120 步长 5(共 24 个参数)→ 调用已有的
backtest_dca_with_ma跑一遍 → 记录每个参数的最终收益率 → 找出最高那个。结果呈现:打印最优参数 / 最优收益率 / 简单定投对照 / 提升幅度;画柱状图(figsize 12×5),正收益绿、负收益红、最优金色高亮黑边框、灰虚线标定投基线、黑实线标零线。
📌 要点:教学型 spec 有时要“刻意保持 naive”。这份 spec 的 spec-writer 是知道过拟合存在的——但 spec 不能写“小心过拟合”或者“加一个 train/test split”。如果这里就警告了,下一节“真相时刻”的冲击力就消失了。让学员先经历“找到漂亮数字”的兴奋,下一份 spec 再让那个数字在新数据上原形毕露,认知冲击才有教学价值。 学员自己写“研究探索型 spec”时也是同理——先写一个 naive 版本看看会怎样,再升级。
📌 要点:spec 的“语气”也是设计选择。本 spec 的任务段写“找到历史收益率最高的最优参数”——用了“最优”这个词。如果 spec 写“扫描参数看看分布”,学员就不会有“啊我找到秘诀了”的错觉,后面再讲过拟合就没冲击力了。spec 的措辞要服务于章节的节奏。
完整 spec 在 specs/spec-06-param-scan.md——复制给 AI,弹窗选「允许」。
AI 助手执行完毕后,你的 notebook 里会出现一组数字和一张柱状图。AI 帮你做了这些事:从 MA5 到 MA120,每隔 5 天取一个参数,逐个运行均线回测并记录收益率,一共 24 次——就像考试出了 24 套卷子,让同一个策略换不同的均线参数去做,看哪套得分最高。然后从中找出收益率最高的“冠军参数”,并画成柱状图。
参数扫描结果如图 1-8 所示。柱状图中每根柱子代表一个参数的收益率。绿色柱子 = 正收益,红色柱子 = 负收益,金色高亮那根就是“冠军”——24 个参数里收益率最高的。灰色虚线是简单定投的水平线,让你一眼看出哪些参数跑赢了简单定投,哪些跑输了。黑色实线是零线——在它下面的参数连本都没保住。

读懂参数扫描的结果
先看打印出来的数字。最优参数的收益率是多少?和简单定投比,提升了多少?你大概率会看到一个相当漂亮的数字——看起来我们真的“找到了盈利的秘诀”。
但是——请你仔细看那张柱状图的形状。
如果真的存在某个“最优均线周期”的内在规律,你会期望柱状图呈现一个“山峰”形状:最优参数附近的参数表现应该也不差,离最优越远表现越差,像一座钟形山丘,顶点就是最优值。这说明“最优参数附近就是甜蜜区”,你稍微偏一点也没关系。
但实际的柱状图是这样吗?大概率不是。你看到的更像是高高低低、毫无规律的锯齿形状。MA35 可能很好,MA40 突然变差,MA45 又回来了。相邻的两个参数——仅仅差 5 天——表现可能天差地别。金色那根“冠军”柱子的左右邻居,可能一个正收益一个负收益。
这说明什么?
说明这个“最优参数”的“好”,很可能只是碰巧。它恰好在这段特定的历史数据上表现最好,而不是因为它抓住了什么深层规律。如果真的有深层规律,相邻参数不应该差这么多——MA35 和 MA40 只差 5 天的回看窗口,它们看到的趋势信息几乎一样,凭什么一个盈利一个亏损?唯一的解释是:结果对参数极度敏感,而这种敏感性来自数据中的随机波动,不是来自某种稳定的市场规律。
现在回到开头那个问题:你朝墙上射了 24 支箭,然后走到离某支箭最近的地方画了一个靶心。“百发百中”?当然不是——你只是射得够多罢了。24 个参数里,总有一个碰巧在这段时间表现最好,这是统计学的必然,不是你发现了什么秘密。就像你抛 24 次硬币,总有一次正面连续出现最多——你不会因此断定那枚硬币有“特异功能”。
这个现象我们暂时叫它“锯齿现象”——参数和参数之间的差异不是平滑过渡的山峰,而是高低跳动的锯齿。它有一个更正式的名字,但在给它正式命名之前,我们需要一个更严格的方法去验证:这个“冠军”到底是真本事,还是只是 24 次掷骰里运气最好的那一次。
方法很简单——用这个参数从未见过的新数据来测试它。下一节就做这件事。
1.5 真相时刻
怎样才算一场“公平考试”?
你不能拿学生复习过的原题来考他——他全都见过,考满分不说明任何问题。要测出真实水平,得出一张他从没做过的卷子。投资策略也一样。上一节我们用全部 5 年的历史数据做参数扫描,找到了“冠军参数”。但这个冠军是在“开卷考”里拿的第一名——它见过所有数据,当然能挑到最好的答案。这不算本事。
要给它一场闭卷考,方法是把数据分成两段。
你手上有大约 5 年的历史数据。我们把前 60% 拿出来——大约 3 年——作为训练集(Training Set),用来“复习”,也就是做参数扫描、找最优参数。剩下的后 40%——大约 2 年——作为测试集(Test Set),这是“考试卷”。训练集里发生的事,测试集完全不知道;测试集里发生的事,训练的时候完全没看过。两段数据,互不相见。
考试的类比可以帮你记住这个逻辑:你手上有 5 年的真题。你用前 3 年的真题来复习——反复做、反复总结、找到最顺手的解题方法。然后你用后 2 年的真题来“考试”——这 2 年的题你在复习的时候完全没碰过。如果你是真的理解了规律,换一张新卷子也能考好。如果你只是背了前 3 年的答案,新卷子就会露馅。
📌 小词典:什么是训练集 / 测试集?
训练集(Training Set):用来“复习”的那段数据。
测试集(Test Set):用来“考试”的那段新数据,模型训练时完全没见过。
核心约束:两段数据互不交叉。否则就是开卷考。
为什么是 60/40?没有什么魔法数字。训练集不能太少,否则“复习”不充分,找到的参数不可靠;测试集也不能太少,否则“考试”的题量不够,结果说明不了问题。60/40 是一个常见的折中:前面大约 3 年够做一轮完整的参数扫描,后面大约 2 年够看出趋势。这不是完美的方法——真正的未来当然不可知,我们只是用“后来发生的事”来假装未来。但它是我们能做到的最公平的模拟。
接下来的实验就是这个思路:在训练集上重新做参数扫描,找到训练集的“冠军参数”。然后拿着这个冠军参数去测试集上跑一遍——看看它能不能通过“考试”。同时我们在测试集上也跑一遍简单定投作为对照。为什么要对照?因为简单定投不做任何参数优化,它就是“什么都不想”的基线。如果你精心优化的参数连“什么都不想”都赢不了,那优化的意义何在?
动手实验 7:样本外测试
这份样本外测试 spec 是本章压轴的一份——它把数据按时间切成两段,让上一节找到的“冠军参数”在没见过的新数据上重新考一次。我们一起把它写出来,重点是数据 split 的可复现写法和怎么把三个数字摆在一起做对比。
起草上下文 + 任务
上下文:在
q1-strategy.ipynb中已有df、backtest_dca、backtest_dca_with_ma、参数扫描代码(MA5 到 MA120)。任务:把数据按时间前 60% / 后 40% 分为训练集 / 测试集,验证「最优参数」在未见过的新数据上是否仍然有效。
起草要求:split 可复现 + 三栏对比
要求:
- 数据分割:按时间前 60% 为训练集,后 40% 为测试集(按时间顺序切,不打乱)
- 在训练集上扫描参数(MA5 到 MA120 步长 5),找到训练集上的最优参数
- 用该最优参数在测试集上跑回测
- 同时在测试集上跑简单定投作为对照
- 对比三个收益率:训练集最优 / 测试集该参数 / 测试集简单定投
📌 要点:数据 split 的写法直接决定可复现。“按时间前 60%”是确定的,“随机分 60/40”是不确定的——后者每次跑会得到不同 split,结果就不能 1:1 对照了。任何切分操作都要写明:是按时间还是随机?切点在哪?随机种子?锁住了你的 spec 才能跨次复现。
📌 要点:当一份 spec 要核对前一份 spec 的结论时,固定套路是把三个数字摆在一起对比——前一份给出“看似最优”,本份给出“测试集上的真实成绩”+“什么都不做的成绩”。三者一对比,前一份的“最优”到底是真本事还是巧合,一目了然。三个数字不是随便凑的:训练集最优是“你以为”,测试集该参数是“其实”,测试集简单定投是“什么都不做”——三者放一起,过拟合就藏不住了。
起草结果呈现
结果呈现:
- 打印训练集 / 测试集时间范围和条数
- 三个数字并排打印:训练集最优参数 + 收益率 / 测试集上该参数的收益率 / 测试集上简单定投的收益率
- 对比结论双分支:
- 最优参数在测试集上比简单定投还差:打印「这就是过拟合——你在历史数据上找到的是巧合,不是规律」
- 否则:打印「结果看起来还行,但仍需谨慎,样本量有限」
完整 spec 在 specs/spec-07-overfitting.md——复制给 AI,弹窗选「允许」。
AI 助手执行完毕后,你的 notebook 里会出现一组关键数字。这可能是本章最重要的一组数字——请你做好心理准备。
AI 帮你做了这些事:把数据按时间顺序在第 60% 的位置一刀切——前面是训练集(“复习用的真题”),后面是测试集(“考试当天才看到的新题”)。注意数据是按时间顺序切的,不是随机打乱的,因为在现实中你只能用过去的数据做决策,未来的数据还没发生。然后在训练集上重新做参数扫描找到冠军参数,拿着这个冠军参数去测试集上跑一遍(“正式考试”),同时在测试集上也跑一遍简单定投作为对照。
读懂样本外测试的结果
现在看看这三个数字。
第一个数字:训练集上最优参数的收益率。这个数字大概率很漂亮——毕竟它是 24 个参数里挑出来的冠军,在训练集上当然表现最好。记住,这是“开卷考”的成绩。
第二个数字:测试集上用同一个参数的收益率。这是“闭卷考”的成绩。
第三个数字:测试集上简单定投的收益率。这是“什么都不想”的成绩。
把第二个数字和第三个数字放在一起看。
大概率你会看到这样的结果:冠军参数在测试集上的收益率,比简单定投还差。
请你再读一遍这句话,确认你没有看错。你花了大量精力做参数扫描,从 MA5 到 MA120 穷举了 24 个参数,在训练集上找到了表现最好的那个。然后你拿着这个精心挑选的冠军参数去面对“未来”——它不仅没有比简单定投更好,反而更差了。
换句话说:你精心优化的策略,还不如什么都不做。
这不是你的操作有误,也不是代码有 bug。这就是过拟合的真实面目。
在训练集上,你的冠军参数看起来无懈可击——它是 24 个候选人里的第一名,收益率比第二名还高出不少。但到了测试集上,它差到几乎像换了一只策略。它在训练集上的好成绩不是因为它抓住了市场的某种深层规律,而是因为它恰好适配了那段特定数据的特定走势。换一段数据,这种适配就不存在了。
为什么会这样?我从三个角度帮你理解。
从统计的角度看。 24 个参数里挑最好的那个,就像在教室里随便拉 24 个人,选最高的那个。他身高 185cm——但这能代表这 24 个人的平均身高吗?当然不能。他只是碰巧最高的那个。你选的参数越多,“碰巧最好”的那个看起来越好——因为你给了随机波动更多的机会来制造“看起来很厉害”的假象。这种现象叫选择偏差(Selection Bias):当你从一堆结果里挑最好的那个,你挑到的往往不是“真正最好”的,而是“运气最好”的。
从市场的角度看。 前 3 年和后 2 年的市场环境不同。也许前 3 年是一轮漫长的熊市,价格一路下跌;后 2 年是触底反弹,价格逐步回升。或者反过来。一个参数在熊市中表现好——比如 MA60,因为它足够迟钝,帮你躲过了大部分下跌——但到了牛市,这种迟钝反而成了累赘:市场都涨起来了,MA60 还在犹豫要不要买入,白白错过了一大段涨幅。市场不是一成不变的。一件衣服不可能四季都合适——冬天穿着暖和的羽绒服,夏天穿就热死人。你在前 3 年找到的“最合身的衣服”,到后 2 年换了季节,哪里都不对。
从直觉的角度看。 最优参数就是为那段训练数据“量体裁衣”的产物。量体裁衣做出来的西装确实完美合身——但那是为你一个人做的。换一个身材不同的人穿,哪里都不对劲。你的冠军参数也是一样:它是为 2021 年到 2023 年某段特定行情“量身定做”的,每一个均线周期、每一次买入跳过,都恰好踩在了那段数据上。换一段数据——哪怕只是后面紧接着的两年——整套策略就不再合身。
这就是过拟合——量化交易最大的敌人。
它不是一个小 bug,不是代码写错了一行,修一修就好。它是一种系统性的思维陷阱。只要你在历史数据上做优化,过拟合的幽灵就始终在你身边。你测试的参数越多,优化的维度越多,它就越危险——因为你给了“碰巧”更多的生存空间。
每一个做量化的人,不管是刚入门的新手还是华尔街的资深研究员,都必须时刻警惕它。在历史数据上看到的任何“好结果”,在被样本外测试验证之前,都不值得相信。训练集上的冠军可能只是考试原题背得最熟的那个学生,真正的考试一来就露馅了。
你在本章走过的路——从简单定投、到加入均线判断、到参数扫描找最优、再到样本外测试看清真相——正是每一个量化新手都会经历的完整旅程。区别只在于:有些人在“找到最优参数”那一步就停下来了,兴冲冲地拿去实盘交易,然后亏了真金白银才明白过拟合是什么。而你,在投入一分钱之前,就已经看到了真相。
记住这个教训:历史数据上的“最优”,不等于未来的“可用”。 在量化交易的世界里,怀疑永远比自信更值钱。
1.6 本章总结
回头看看我们走过的路。从最简单的定投开始,到加入均线判断做择时,到参数扫描寻找“最优解”,再到样本外测试看着精挑的参数全线衰减——7 个实验,一步一步,你经历了一次完整的认知升级。这不只是学了几个策略,而是亲眼见证了“看起来有效”和“真的有效”之间的巨大鸿沟。
概念速查表
本章涉及的核心概念汇总如表 1-2 所示。
表 1-2 第 1 章核心概念速查
| 概念 | 含义 | 类比 |
|---|---|---|
| 回测(Backtest) | 用历史数据模拟策略执行 | 在脑海中“重播”历史 |
| 基准(Benchmark) | 用来比较的参照物 | 全班的平均分 |
| Beta(贝塔) | 承担市场风险获得的收益 | 坐电梯——市场涨你跟着涨 |
| Alpha(阿尔法) | 超越市场的那部分收益 | 爬楼梯——靠自己的本事往上走 |
| 均线(Moving Average) | 过去 N 天价格的平均值 | 连续几天的平均体温 |
| 过拟合(Overfitting) | 把数据中的噪音当成规律 | 背答案 vs 理解规律 |
| 训练集(Training Set) | 用来“复习”和找参数的那段数据 | 复习用的真题 |
| 测试集(Test Set) | 模型从未见过、用来检验的那段数据 | 没做过的考试卷 |
策略进化路径
第 1 章 3 个版本的策略进化与教训如表 1-3 所示。
表 1-3 第 1 章策略进化路径
| 版本 | 策略 | 教训 |
|---|---|---|
| v0.1 | 简单定投 | 纯 Beta 暴露,收益完全随市场涨跌 |
| v0.2 | 定投 + 均线择时 | 加入判断是双刃剑,不保证比无脑定投更好 |
| v0.3 | 参数扫描优化 | 历史最优不等于未来可用,过拟合的风险就在面前 |
飞轮第一圈:你刚刚走完的路
回头看:你已经把飞轮的四层都各自踩过一次。候选层——你选了沪深 300ETF;组合层——你定了“每月买一份”或“上穿均线再买”的规则;执行层——回测引擎替你按规则下单;归因层——你比了三种策略的累计收益、对照了样本内/外。
更重要的是:做、看、疑三个动作你都练了一遍。前言里那句口号——“做要规则、看要指标、还要怀疑你的指标”——你不是听别人讲,而是亲眼看见冠军参数在测试集上现原形之后才真正信的。这就是为什么本书把“疑”和“做”“看”放在同等位置:会做、会看,但不会疑,最容易在某些时候把赚到的钱加倍还给市场。
本章最重要的收获
说实话,我们并没有学会一个能稳定盈利的策略。定投跑不赢市场,加了均线也未必更好,参数优化更是一脚踩进了过拟合的坑。如果你期待的是“学完第 1 章就能盈利”,那确实要让你失望了。
但你学会了一样比任何策略都值钱的东西——怀疑。面对任何一条漂亮的回测曲线,你现在知道第一个要问的问题是:“这是规律,还是巧合?”这种本能的警觉,会让你在未来少走很多弯路,少亏很多真金白银。
带走的问题
本章带你跑通了第一个完整策略,但也留下了一些更深的问题。带着它们继续往下走:
- 只买沪深 300 够吗? 本章 7 个实验全在拿沪深 300 做样本,但真正投资时不会只买一只。下一章我们要选 3 只 ETF,组成一个真正的“投资宇宙”。——第 2 章会解答。
- 怎么区分运气和能力? 一个策略盈利了,到底是真有水平还是瞎猫碰上死耗子?——第 5 章、第 6 章会解答。
- 有没有不容易过拟合的策略? 既然参数优化这条路走不通,还有别的路吗?——第 4 章、第 6 章会解答。
- 怎么知道策略在未来还能盈利? 样本外测试只是第一步,还有更严格的检验方法吗?——第 5 章会解答。
答案都在后面。继续。
💡 提示:本章正文聚焦“亲手跑通最小闭环”。「拓展阅读」给了两段世界观铺垫——读不读都不影响主线,但读完会让后面几章吸收得更快。
💡 提示:本章所有代码的可运行版本见
notebooks/q1-strategy.ipynb。
拓展阅读
本章正文聚焦“亲手跑通最小闭环”。下面两节是动手之前可以省略的视野铺垫——读完正文后回头看,反而吸收得更快。
你做的是哪种交易?
“量化”、“程序化”、“算法”、“高频”——这四个词常被混用,其实是层层包含的关系,如图 1-9 所示。

四个概念的具体对照如表 1-4 所示。
表 1-4 量化 / 程序化 / 算法 / 高频四个概念对照
| 概念 | 干什么 |
|---|---|
| 量化交易(最外层) | 用数据和模型做决策(对面是靠经验直觉的“主观交易”) |
| 程序化交易 | 用程序自动执行——你完全可以做量化但手动执行,很多个人量化交易者就这么做 |
| 算法交易 | 解决“怎么买”——把大单拆小单降低市场冲击 |
| 高频交易(最内层) | 毫秒级速度,专用机房和光纤,门槛千万美元起 |
“投资”与“交易”也是两件事:投资买的是公司、赚的是公司成长的钱;交易买的是筹码、赚的是别人犯错的钱。本课程偏交易,不分析公司成长。
本课程做的是中低频量化交易,手动或半自动执行——持仓周期周到月级别,普通电脑就能跑。你的对手不是 Citadel 这种顶级机构,而是没有系统、靠感觉交易的散户。在这个层面上,纪律严明的量化策略已经是巨大优势。
钱从哪来,为什么大多数人赚不到
不管你买什么,赚到的钱本质上只来自两类——前言里我们用过同样的分法:资产创造(你拿到的是资产本身在帮你赚钱)和价格变化(你赚的是别人愿意付的差价)。两类的具体来源如表 1-5 所示。
表 1-5 投资收益的两大类来源
| 大类 | 具体来源 | 核心本质 | 关键特征 |
|---|---|---|---|
| 资产创造 | 分红 | 公司把利润分给股东 | 不依赖市场情绪(股票、ETF) |
| 资产创造 | 利息 | 借出资金的报酬 | 不依赖市场情绪(债券、国债) |
| 价格变化 | 价差 | 低买高卖的差额 | 赌价格变化方向(股票、ETF、期货) |
| 价格变化 | 套利 | 不同市场间的瞬时差价 | 几乎不预测方向,门槛高(跨市套利) |
一句话总结:分红和利息赚的是“现金流”,价差赚的是“共识差”,套利赚的是“空间差”。 本课程的策略主要赚价差。
短期负和、长期正和。 每笔交易都要付佣金、印花税、买卖价差——频繁交易等于把自己放进一个“整体必输”的游戏。但长期持有指数级资产能分享企业增长,蛋糕在变大、大家一起赚。这就是格雷厄姆那句“短期是投票机,长期是称重机”。本课程定位“中频”——既捕捉规律,又不被成本吃掉。
三个差距,量化的三种对抗。 虽然市场整体可以赚钱,但大多数散户没赚到——核心是三个“差”,恰好对应量化的三种优势(如表 1-6 所示):
表 1-6 三个差距与量化的对应优势
| 普通人的劣势 | 体现 | 量化的对应优势 |
|---|---|---|
| 信息差 | 你看到新闻时,机构早已先动 | 同时扫描几千标的找统计规律 |
| 情绪差 | 涨了就追买、跌了就吓得抛掉,本能在股市害人 | 规则写好机器执行,无情绪干扰 |
| 成本差 | 频繁操作累积成本,可能吃掉一半本金 | 明确规则约束,不频繁操作 |
量化不能保证赚钱,但能让你在更公平的位置上竞争。
反馈与读者群
如果你发现本章有错别字、概念不清或实验结果不一致,欢迎 提交 GitHub Issue。
课程、正式书和后续更新见 xquant.shop/courses。
添加作者微信 xquanter 进入读者群。请备注:XQuant 读者。