如果把一套交易规则放回历史,它真的能按你想的方式执行吗?

回测引擎与事件驱动

约 15 分钟1 个动手实验4 道小题

学完你能说清楚回测是按时间重放数据,逐笔检查信号、订单、资金和风险。

01学完这一课,你能做到什么

学完以后,你应该能:

  1. 说清楚能回答什么,不能证明什么;
  2. 分辨数据、策略、账户、成交和绩效分析各自负责什么;
  3. 用一条规则,讲清楚从数据到订单反馈的完整过程;
  4. 发现重复下单、未来数据和参数;
  5. 写出一份别人可以复核的回测实验说明。

你可以先把回测理解成“按原来的时间顺序重放一遍”。程序只能使用当时已经出现的信息,不能偷看后面的价格。

一轮回测要经过什么
  1. 1历史数据按时间顺序进入
  2. 2策略判断是否出现信号
  3. 3订单处理能否成交、成交多少
  4. 4账户变化现金和持仓更新
  5. 5结果分析收益、回撤、交易记录

一句话记住回测是检查规则,不是保证未来。

02历史回放能回答什么

举个例子:你有一条规则——“短期上穿长期均线就买入,下穿就退出”。回测会把这条规则放进一段历史行情,按一天一天的顺序重放,观察当时可能出现什么信号、发出什么订单,最后账户变成什么样。

这能帮你发现三类问题:

  • 规则有没有明显的逻辑漏洞;
  • 某段历史行情下,风险和交易节奏是什么样;
  • 不同策略或参数,在同一套假设下谁更适合继续研究。

但它不能证明未来一定会重复。比如某只股票曾经震荡下跌,另一段时间又走出趋势。去年表现最好的规则,不会自动适合今年。更稳妥的做法,是覆盖上涨、下跌、震荡等不同市场状态,并把策略成立的前提写出来。

Bar 和 Tick 有什么区别

Bar是一个时间区间的汇总记录,常见字段有、最高价、最低价、收盘价和。日线Bar只告诉你一天的汇总结果,不能还原当天先涨到最高还是先跌到最低。

Tick更接近逐笔或逐次行情更新。使用更细的数据,能研究更细的触发顺序,但数据量、成交假设和成本模型也会更复杂。回测按照哪种频率推进,必须和你的问题匹配。

两种历史数据记录
Bar

一个区间的汇总

知道开高低收和成交量,但不知道区间内部的先后顺序。

Tick

更细的逐次更新

更接近逐笔变化,但需要更多数据和更细的成交假设。

一句话记住数据越粗,能还原的成交顺序越少。

03为什么要用回测框架

刚开始学习时,你可以用 Pandas 和 Python 循环,自己计算信号、现金和持仓。这样容易看懂底层逻辑。策略一多,就会遇到费用、、多个数据源、多周期和绩效统计等问题。每套程序都手写一遍,容易悄悄使用不同的假设。

回测框架的价值,是提供一套可复用的接口,把数据、策略、模拟成交和分析结果接起来。它不会因为“用了框架”就自动真实。数据错了、策略偷看未来、费用漏算,结果仍然会误导你。

以 Backtrader 的常见组件为例:

  • Cerebro:调度中心,负责组织数据、策略、账户设置和分析器,并启动运行;
  • Data Feed:数据入口,把 CSV、DataFrame 等输入转换成框架能读取的时间序列。DataFrame 是带行列标签的二维数据结构,不是普通列表。你要映射日期、开高低收、成交量,并检查排序、缺失、和时区;
  • Strategy:策略本身,除了买卖条件,还要写指标、、退出、订单和持仓状态;
  • Broker:回测里的模拟账户,维护现金、头寸和手续费,并按设定模拟、佣金、等。它不是真实券商;
  • Analyzer:分析账户和交易序列,输出收益、回撤、、交易次数等。默认值、年化方式和返回格式要自己核对。

你还可以用 TA-Lib 扩展技术指标和形态指标,或组织多周期、多资产数据。但框架能读入多种数据,不等于股票、、外汇、加密资产和可以共用一套成交规则。期权还要处理到期、合约乘数和行权等机制。

回测框架里的分工
输入

Data Feed

把数据变成时间序列。

判断

Strategy

根据当时信息决定是否发单。

执行

Broker

模拟现金、持仓、费用和成交。

调度

Cerebro

把组件按流程组织起来。

统计

Analyzer

汇总收益、回撤和交易记录。

一句话记住框架统一流程,不替你验证假设。

04一轮回测怎样设置才公平

你可以按下面的顺序做,不要一上来就看收益率:

  1. 定义实验:写清证券范围、统计区间、热身数据、初始资金、初始持仓、基准和参数;
  2. 检查数据:确认代码、日期、单位、复权、缺失和排序,再映射成 Data Feed;
  3. 接入调度中心:创建 Cerebro,加入数据和数据频率;
  4. 加入策略参数:例如快均线 10、慢均线 30,并写清仓位和订单规则;
  5. 配置账户和交易摩擦:初始现金、佣金、最低收费、税费、滑点、交易单位,以及无法成交时怎么处理;
  6. 保存过程记录:至少保存交易明细、净值、回撤和关键状态;
  7. 运行并验收:先核对具体交易,再读汇总报告。最后保存代码环境、数据版本和参数。

setcommission 之类的设置,通常只是一个起点。比如最低收费 5 元、卖出税费和不同资产的交易规则,不能只填一个比例就说已经还原真实成本。高频或高策略里,费用可能占很大比重。有人会用“一年超过 30%”作为提醒,但这不是你的实验结果,实际费用要按每笔成交金额计算。

多策略也要分清:如果每组参数独立回测,就应该有各自的账户;如果多个策略同时操作同一个账户,它们会共享现金和风险约束。两种结果不能混成一个“并行策略收益”。

从实验定义到验收
  1. 1先定边界区间、资金、基准
  2. 2再查数据代码、日期、单位、复权
  3. 3写规则信号、仓位、订单
  4. 4加摩擦佣金、税费、滑点
  5. 5逐笔验收交易明细和账户对账

一句话记住先写假设,再看成绩。

05双均线:从交叉信号到订单

假设你使用两条简单移动平均线(SMA):快线 10 周期,慢线 30 周期。SMA 是过去 N 个有效收盘价的简单平均。

  • 快线上穿慢线,形成候选买入信号;
  • 快线下穿慢线,形成候选退出信号;
  • 5/20、5/60、10/60 是另外三组时间尺度不同的参数,不代表哪一组一定更好。

只有交叉条件还不够。程序至少要记住三件事:当前是否持仓、是否有未完成订单、这次允许交易多少数量。

举个常见错误:程序发出买单,但订单还没成交。下一根Bar到来时,账户仍然显示“没有持仓”。如果程序只看持仓,就可能再发一张买单。解决办法是:发单以后保存订单引用;收到订单反馈时,记录已提交、已接受、已完成、取消、拒绝或资金不足等状态;只有符合规则的终态,才清除等待标记;真正成交后,再更新现金和持仓。

下面是教学用的伪流程:

(1)每个已经可用的 Bar 到来:检查数据和指标是否有效;如果有未完成订单,就等待订单反馈;否则,如果无持仓且发生金叉,就根据资金、费用和交易单位计算数量,数量有效才提交订单,并记录订单引用;如果有可卖持仓且发生死叉,就提交退出订单并记录订单引用。

(2)订单反馈到来:记录状态、成交价、数量和费用,按终态规则清理等待状态。

这段伪代码先帮你理清责任,不是完整可运行程序。还要规定:收盘形成的信号何时执行;A 股 、停牌、等情况怎么处理。框架提供订单状态,但不会自动替你防止所有重复下单。

一个金叉怎样变成账户变化
  1. 1发现交叉确认前一天和今天
  2. 2检查状态没有未完成订单
  3. 3计算数量资金、费用、交易单位
  4. 4提交订单保存订单引用
  5. 5收到反馈按状态更新账户

一句话记住信号、订单和成交,是三个不同状态。

动手试试

双均线回测引擎

切换 10/30 与 9/19 两组参数,开关交易成本,观察同一份历史样本的收益、回撤、成交和交易记录。

固定教学样本逐根 K 线运行双均线回测
01Data Feed历史行情
02Strategy双均线规则
03Broker订单与成本
04Analyzer绩效报告
上涨 K 线下跌 K 线SMA 10SMA 30
正在载入回测图表
基准参数 10 / 30
初始资金 100,000 元100 股整数交易信号次日开盘执行手续费与滑点已开启

切换参数或成本设置,观察同一份历史样本的结果怎样变化。

运行后将看到金叉与死叉次日开盘成交手续费和滑点收益、回撤与交易记录

06事件驱动不等于可以偷看未来

在 Backtrader 策略里,__init__ 通常用来声明指标和建立依赖,next 会随着数据推进,逐步检查信号和状态。把指标声明反复放到 next 里,可能重复创建对象并拖慢运行。

更准确地说,初始化阶段建立计算关系,指标值仍然对应每个时间点的数据。不是所有值都在初始化时一次算完,也不能因为代码写在初始化里,就安全地访问未来结果。

事件驱动的意思是:每当新的数据或订单事件到来,程序按事件顺序处理。它有助于整理时间,但不能自动消除。下面这些做法都可能泄露未来信息:

  • 用全样本数据提前做标准化,再回到过去交易;
  • 用未来才公布的财报字段;
  • 用当天最高价决定当天开盘买入;
  • 索引写错,读到了尚未发生的Bar。

你要逐个检查变量:在做这个决定的时刻,它真的已经能拿到吗?如果答案不确定,就先不要把结果当成可靠结论。

初始化和逐步处理
__init__

先声明关系

建立指标、参数和依赖,不代表已经拥有未来数据。

next

随着时间判断

每个新数据点到来后,只使用当时可见的信息。

一句话记住事件按时间来,未来信息不能提前来。

07参数高分,不等于规则可靠

假设快线 9、慢线 19,刚好在某段行情里赚钱;换成 10/30,结果却不好。这个现象不能证明 9/19 更有规律,也可能只是它碰巧贴合了这段价格路径。

过拟合就是把样本里的偶然细节记得太牢,却没有学到能迁移到新数据的关系。可以把它想成:你看到一套房子前面有一棵树和一辆自行车,就把它们记成“房子价值 200 万元”。如果换一个街区,这个记忆可能完全失效。

参数搜索当然可以做,但要守住边界:

  • 分开训练区间、参数选择区间和测试区间;
  • 记录搜索范围和尝试过多少组合;
  • 检查相邻参数是否也能工作;
  • 换不同时间段和标的,看结果是否稳定;
  • 检查收益是不是只来自少数几笔交易。

参数区域里只有一个特别尖的高分点,往往更需要怀疑;一片相邻参数表现接近,才值得继续检验。但“平坦”也不是未来赚钱的证明。先解释交易时间尺度和执行机制,再做参数检验,不要无限调到曲线好看。

参数结果要看形状
需要警惕

孤立的尖峰

只有一个组合特别好,旁边都很差,可能是偶然。

更值得检验

相邻的一片高原

一片区域表现接近,但仍然要做样本外验证。

一句话记住别找唯一神参数,要找能解释的稳定区域。

08怎样读回测结果

以贵州茅台 2025 年的双均线示例为例,使用约 243 个交易观测,描述了 4 次完整交易中只有 1 次获利,并展示、Sharpe 和交易次数等分析结果。完整数据版本、全部参数、成本和每笔成交需要你自己核对,因此不能把缺少的收益数字补出来。

这个结果即使不理想,也有用。它可能说明:这段行情的走势、趋势规则、和信号节奏没有配合好。下一步要逐笔检查亏损来自哪里,而不是看到曲线不好就立刻删掉。

你还可以在同一数据、同一执行假设下比较:

  • 均值回归:假设价格偏离后有回归倾向;
  • 动量策略:假设近期相对强弱可能延续。

两种假设方向相反,可能在不同时间尺度和市场状态下各有表现。一定要写清观察窗口、进出规则和成本。

读结果时,顺序建议是:先看交易明细,再对账现金和持仓,最后看收益、回撤和 Sharpe。不要只盯着一条漂亮的资金曲线。

一句话记住先查每笔交易,再看总成绩。

09把策略做成可复用的实验

当策略越来越多,可以把每个策略放进独立的 Python 文件,由统一入口发现并运行。新增策略时,只增加一个插件,不必复制整套回测流程。

一个策略插件至少要声明:名称、参数、需要的字段、最小历史长度和策略实现。统一运行器负责数据、账户、费用和分析器;结果表记录插件版本和实验配置。自动加载本地代码时,只使用可信文件。加载失败要单独报告,不能悄悄跳过。

你可以先按下面的任务验收双均线 10/30:

  1. 写一份实验说明:数据区间、初始资金、初始仓位、费用、仓位规则、成交时点和股票交易约束;
  2. 用很短的人造价格序列,测试金叉、死叉、订单状态。人造数据要标成“测试数据”,不能放进真实绩效报告;
  3. 使用合法获取的历史数据运行,至少检查一笔买入、一次退出,以及一次未成交或被拒绝的情况;
  4. 手工对账现金和持仓;
  5. 把参数改成 5/20,另存一份结果,比较原因,不要只挑获利版本;
  6. 记录数据检查报告、交易明细、资金曲线、绩效表和未解决问题。

如果项目里已经有 data_loader,先确认它真实的输入和输出,再做适配,不要凭名字猜接口。后续也可以把 等策略做成插件,但要保留同一套实验记录。

算完成了吗? 你应该能从任意一笔成交追溯到当时的数据和规则,分清信号日与成交日,解释为什么有信号却没有成交,并说明框架运行成功不等于结果已经经过验证。

一句话记住每个结果都要能追溯到数据、规则和成交。

检验
检验一下

4 道小题

全部答对,这一课就算真正读懂了。答错了会告诉你为什么。

01回测最不能证明什么?
02买单已经发出但还没成交,下一根 Bar 到来时为什么不能只看持仓?
03盘中决定买入时,下面哪项可能使用了未来数据?
04一组参数特别高分,周围参数都很差,应该怎样理解?
知识卡

这一课,浓缩成一张卡

学完可以保存或分享给朋友。保存成图片的功能会在下一版加上。

AI+金融投资训练营 · 回测引擎与事件驱动回测时最先要检查什么?

先确认数据、时间顺序和成交假设,再逐笔核对信号、订单、现金和持仓。

  • 历史不等于未来
  • 订单状态不能漏
  • 参数高分要验证
quant.jtxaionline.com
这一课的要点

记住这 5 句话

  1. 01回测按时间顺序重放历史,不能证明未来一定重复。
  2. 02Data Feed、Strategy、Broker、Analyzer 和 Cerebro 各有明确分工。
  3. 03双均线信号之外,还要处理订单状态、成交时点、费用和交易制度。
  4. 04事件驱动不会自动消除未来函数,初始化也不等于一次算完所有值。
  5. 05参数要看相邻区域和样本外结果,逐笔交易比单看收益曲线更重要。
完成这一课

完成这一课

读完讲义、动手做一次实验、答对小题,这一课就算学完了。

  • 读完讲义
  • 完成一次双均线回测实验
  • 通过随堂自测

实验数据AI+金融投资训练营教学样本。固定合成 K 线教学样本,实验按收盘信号、次日开盘执行,并可切换手续费与滑点。

固定教学样本只用于解释量化研究方法,不代表任何真实标的未来表现,也不构成投资建议。