01学完这一课,你能做到什么
学完以后,你应该能:
- 说清楚能回答什么,不能证明什么;
- 分辨数据、策略、账户、成交和绩效分析各自负责什么;
- 用一条规则,讲清楚从数据到订单反馈的完整过程;
- 发现重复下单、未来数据和参数;
- 写出一份别人可以复核的回测实验说明。
你可以先把回测理解成“按原来的时间顺序重放一遍”。程序只能使用当时已经出现的信息,不能偷看后面的价格。
- 1历史数据按时间顺序进入
- 2策略判断是否出现信号
- 3订单处理能否成交、成交多少
- 4账户变化现金和持仓更新
- 5结果分析收益、回撤、交易记录
一句话记住回测是检查规则,不是保证未来。
02历史回放能回答什么
举个例子:你有一条规则——“短期上穿长期均线就买入,下穿就退出”。回测会把这条规则放进一段历史行情,按一天一天的顺序重放,观察当时可能出现什么信号、发出什么订单,最后账户变成什么样。
这能帮你发现三类问题:
- 规则有没有明显的逻辑漏洞;
- 某段历史行情下,风险和交易节奏是什么样;
- 不同策略或参数,在同一套假设下谁更适合继续研究。
但它不能证明未来一定会重复。比如某只股票曾经震荡下跌,另一段时间又走出趋势。去年表现最好的规则,不会自动适合今年。更稳妥的做法,是覆盖上涨、下跌、震荡等不同市场状态,并把策略成立的前提写出来。
Bar 和 Tick 有什么区别
Bar是一个时间区间的汇总记录,常见字段有、最高价、最低价、收盘价和。日线Bar只告诉你一天的汇总结果,不能还原当天先涨到最高还是先跌到最低。
Tick更接近逐笔或逐次行情更新。使用更细的数据,能研究更细的触发顺序,但数据量、成交假设和成本模型也会更复杂。回测按照哪种频率推进,必须和你的问题匹配。
一个区间的汇总
知道开高低收和成交量,但不知道区间内部的先后顺序。
更细的逐次更新
更接近逐笔变化,但需要更多数据和更细的成交假设。
一句话记住数据越粗,能还原的成交顺序越少。
03为什么要用回测框架
刚开始学习时,你可以用 Pandas 和 Python 循环,自己计算信号、现金和持仓。这样容易看懂底层逻辑。策略一多,就会遇到费用、、多个数据源、多周期和绩效统计等问题。每套程序都手写一遍,容易悄悄使用不同的假设。
回测框架的价值,是提供一套可复用的接口,把数据、策略、模拟成交和分析结果接起来。它不会因为“用了框架”就自动真实。数据错了、策略偷看未来、费用漏算,结果仍然会误导你。
以 Backtrader 的常见组件为例:
- Cerebro:调度中心,负责组织数据、策略、账户设置和分析器,并启动运行;
- Data Feed:数据入口,把 CSV、DataFrame 等输入转换成框架能读取的时间序列。DataFrame 是带行列标签的二维数据结构,不是普通列表。你要映射日期、开高低收、成交量,并检查排序、缺失、和时区;
- Strategy:策略本身,除了买卖条件,还要写指标、、退出、订单和持仓状态;
- Broker:回测里的模拟账户,维护现金、头寸和手续费,并按设定模拟、佣金、等。它不是真实券商;
- Analyzer:分析账户和交易序列,输出收益、回撤、、交易次数等。默认值、年化方式和返回格式要自己核对。
你还可以用 TA-Lib 扩展技术指标和形态指标,或组织多周期、多资产数据。但框架能读入多种数据,不等于股票、、外汇、加密资产和可以共用一套成交规则。期权还要处理到期、合约乘数和行权等机制。
Data Feed
把数据变成时间序列。
Strategy
根据当时信息决定是否发单。
Broker
模拟现金、持仓、费用和成交。
Cerebro
把组件按流程组织起来。
Analyzer
汇总收益、回撤和交易记录。
一句话记住框架统一流程,不替你验证假设。
04一轮回测怎样设置才公平
你可以按下面的顺序做,不要一上来就看收益率:
- 定义实验:写清证券范围、统计区间、热身数据、初始资金、初始持仓、基准和参数;
- 检查数据:确认代码、日期、单位、复权、缺失和排序,再映射成 Data Feed;
- 接入调度中心:创建 Cerebro,加入数据和数据频率;
- 加入策略参数:例如快均线 10、慢均线 30,并写清仓位和订单规则;
- 配置账户和交易摩擦:初始现金、佣金、最低收费、税费、滑点、交易单位,以及无法成交时怎么处理;
- 保存过程记录:至少保存交易明细、净值、回撤和关键状态;
- 运行并验收:先核对具体交易,再读汇总报告。最后保存代码环境、数据版本和参数。
setcommission 之类的设置,通常只是一个起点。比如最低收费 5 元、卖出税费和不同资产的交易规则,不能只填一个比例就说已经还原真实成本。高频或高策略里,费用可能占很大比重。有人会用“一年超过 30%”作为提醒,但这不是你的实验结果,实际费用要按每笔成交金额计算。
多策略也要分清:如果每组参数独立回测,就应该有各自的账户;如果多个策略同时操作同一个账户,它们会共享现金和风险约束。两种结果不能混成一个“并行策略收益”。
- 1先定边界区间、资金、基准
- 2再查数据代码、日期、单位、复权
- 3写规则信号、仓位、订单
- 4加摩擦佣金、税费、滑点
- 5逐笔验收交易明细和账户对账
一句话记住先写假设,再看成绩。
05双均线:从交叉信号到订单
假设你使用两条简单移动平均线(SMA):快线 10 周期,慢线 30 周期。SMA 是过去 N 个有效收盘价的简单平均。
- 快线上穿慢线,形成候选买入信号;
- 快线下穿慢线,形成候选退出信号;
- 5/20、5/60、10/60 是另外三组时间尺度不同的参数,不代表哪一组一定更好。
只有交叉条件还不够。程序至少要记住三件事:当前是否持仓、是否有未完成订单、这次允许交易多少数量。
举个常见错误:程序发出买单,但订单还没成交。下一根Bar到来时,账户仍然显示“没有持仓”。如果程序只看持仓,就可能再发一张买单。解决办法是:发单以后保存订单引用;收到订单反馈时,记录已提交、已接受、已完成、取消、拒绝或资金不足等状态;只有符合规则的终态,才清除等待标记;真正成交后,再更新现金和持仓。
下面是教学用的伪流程:
(1)每个已经可用的 Bar 到来:检查数据和指标是否有效;如果有未完成订单,就等待订单反馈;否则,如果无持仓且发生金叉,就根据资金、费用和交易单位计算数量,数量有效才提交订单,并记录订单引用;如果有可卖持仓且发生死叉,就提交退出订单并记录订单引用。
(2)订单反馈到来:记录状态、成交价、数量和费用,按终态规则清理等待状态。
这段伪代码先帮你理清责任,不是完整可运行程序。还要规定:收盘形成的信号何时执行;A 股 、停牌、等情况怎么处理。框架提供订单状态,但不会自动替你防止所有重复下单。
- 1发现交叉确认前一天和今天
- 2检查状态没有未完成订单
- 3计算数量资金、费用、交易单位
- 4提交订单保存订单引用
- 5收到反馈按状态更新账户
一句话记住信号、订单和成交,是三个不同状态。
双均线回测引擎
切换 10/30 与 9/19 两组参数,开关交易成本,观察同一份历史样本的收益、回撤、成交和交易记录。
切换参数或成本设置,观察同一份历史样本的结果怎样变化。
06事件驱动不等于可以偷看未来
在 Backtrader 策略里,__init__ 通常用来声明指标和建立依赖,next 会随着数据推进,逐步检查信号和状态。把指标声明反复放到 next 里,可能重复创建对象并拖慢运行。
更准确地说,初始化阶段建立计算关系,指标值仍然对应每个时间点的数据。不是所有值都在初始化时一次算完,也不能因为代码写在初始化里,就安全地访问未来结果。
事件驱动的意思是:每当新的数据或订单事件到来,程序按事件顺序处理。它有助于整理时间,但不能自动消除。下面这些做法都可能泄露未来信息:
- 用全样本数据提前做标准化,再回到过去交易;
- 用未来才公布的财报字段;
- 用当天最高价决定当天开盘买入;
- 索引写错,读到了尚未发生的Bar。
你要逐个检查变量:在做这个决定的时刻,它真的已经能拿到吗?如果答案不确定,就先不要把结果当成可靠结论。
先声明关系
建立指标、参数和依赖,不代表已经拥有未来数据。
随着时间判断
每个新数据点到来后,只使用当时可见的信息。
一句话记住事件按时间来,未来信息不能提前来。
07参数高分,不等于规则可靠
假设快线 9、慢线 19,刚好在某段行情里赚钱;换成 10/30,结果却不好。这个现象不能证明 9/19 更有规律,也可能只是它碰巧贴合了这段价格路径。
过拟合就是把样本里的偶然细节记得太牢,却没有学到能迁移到新数据的关系。可以把它想成:你看到一套房子前面有一棵树和一辆自行车,就把它们记成“房子价值 200 万元”。如果换一个街区,这个记忆可能完全失效。
参数搜索当然可以做,但要守住边界:
- 分开训练区间、参数选择区间和测试区间;
- 记录搜索范围和尝试过多少组合;
- 检查相邻参数是否也能工作;
- 换不同时间段和标的,看结果是否稳定;
- 检查收益是不是只来自少数几笔交易。
参数区域里只有一个特别尖的高分点,往往更需要怀疑;一片相邻参数表现接近,才值得继续检验。但“平坦”也不是未来赚钱的证明。先解释交易时间尺度和执行机制,再做参数检验,不要无限调到曲线好看。
孤立的尖峰
只有一个组合特别好,旁边都很差,可能是偶然。
相邻的一片高原
一片区域表现接近,但仍然要做样本外验证。
一句话记住别找唯一神参数,要找能解释的稳定区域。
08怎样读回测结果
以贵州茅台 2025 年的双均线示例为例,使用约 243 个交易观测,描述了 4 次完整交易中只有 1 次获利,并展示、Sharpe 和交易次数等分析结果。完整数据版本、全部参数、成本和每笔成交需要你自己核对,因此不能把缺少的收益数字补出来。
这个结果即使不理想,也有用。它可能说明:这段行情的走势、趋势规则、和信号节奏没有配合好。下一步要逐笔检查亏损来自哪里,而不是看到曲线不好就立刻删掉。
你还可以在同一数据、同一执行假设下比较:
- 均值回归:假设价格偏离后有回归倾向;
- 动量策略:假设近期相对强弱可能延续。
两种假设方向相反,可能在不同时间尺度和市场状态下各有表现。一定要写清观察窗口、进出规则和成本。
读结果时,顺序建议是:先看交易明细,再对账现金和持仓,最后看收益、回撤和 Sharpe。不要只盯着一条漂亮的资金曲线。
一句话记住先查每笔交易,再看总成绩。
09把策略做成可复用的实验
当策略越来越多,可以把每个策略放进独立的 Python 文件,由统一入口发现并运行。新增策略时,只增加一个插件,不必复制整套回测流程。
一个策略插件至少要声明:名称、参数、需要的字段、最小历史长度和策略实现。统一运行器负责数据、账户、费用和分析器;结果表记录插件版本和实验配置。自动加载本地代码时,只使用可信文件。加载失败要单独报告,不能悄悄跳过。
你可以先按下面的任务验收双均线 10/30:
- 写一份实验说明:数据区间、初始资金、初始仓位、费用、仓位规则、成交时点和股票交易约束;
- 用很短的人造价格序列,测试金叉、死叉、订单状态。人造数据要标成“测试数据”,不能放进真实绩效报告;
- 使用合法获取的历史数据运行,至少检查一笔买入、一次退出,以及一次未成交或被拒绝的情况;
- 手工对账现金和持仓;
- 把参数改成 5/20,另存一份结果,比较原因,不要只挑获利版本;
- 记录数据检查报告、交易明细、资金曲线、绩效表和未解决问题。
如果项目里已经有 data_loader,先确认它真实的输入和输出,再做适配,不要凭名字猜接口。后续也可以把 等策略做成插件,但要保留同一套实验记录。
算完成了吗? 你应该能从任意一笔成交追溯到当时的数据和规则,分清信号日与成交日,解释为什么有信号却没有成交,并说明框架运行成功不等于结果已经经过验证。
一句话记住每个结果都要能追溯到数据、规则和成交。
4 道小题
全部答对,这一课就算真正读懂了。答错了会告诉你为什么。
这一课,浓缩成一张卡
学完可以保存或分享给朋友。保存成图片的功能会在下一版加上。
先确认数据、时间顺序和成交假设,再逐笔核对信号、订单、现金和持仓。
- 历史不等于未来
- 订单状态不能漏
- 参数高分要验证
记住这 5 句话
- 01回测按时间顺序重放历史,不能证明未来一定重复。
- 02Data Feed、Strategy、Broker、Analyzer 和 Cerebro 各有明确分工。
- 03双均线信号之外,还要处理订单状态、成交时点、费用和交易制度。
- 04事件驱动不会自动消除未来函数,初始化也不等于一次算完所有值。
- 05参数要看相邻区域和样本外结果,逐笔交易比单看收益曲线更重要。
完成这一课
读完讲义、动手做一次实验、答对小题,这一课就算学完了。
- 读完讲义
- 完成一次双均线回测实验
- 通过随堂自测
实验数据AI+金融投资训练营教学样本。固定合成 K 线教学样本,实验按收盘信号、次日开盘执行,并可切换手续费与滑点。
固定教学样本只用于解释量化研究方法,不代表任何真实标的未来表现,也不构成投资建议。