← 返回目录
q1.8动手⏱ 约 10 分钟

回测实操:backtrader 跑通你的第一个回测

21 年成绩单:359% 的收益,和它背后的三盆冷水

🔎 最后验证 2026-07📚 来源:backtrader 官方文档(backtrader.com)、本节回测结果于 2026-07 实测(沪深300 2005-04-08~2026-07-29,佣金0.05%)🧰 backtrader 1.9.x、pandas
为什么学这个

规则书锁定了(q1.7),今天让机器替我们把 21 年的历史完整走一遍:每一次金叉买入、每一次死叉卖出、每一笔佣金,一秒钟跑完。

先把成绩单最亮眼的数字给你:10 万本金,21 年后变成 45.9 万,总收益 359%。

心动了?好——这正是最危险的时刻。这张成绩单上还有三个数字:45.1% 的最大回撤、35% 的胜率、以及买入持有同期的 358%。读懂这四个数字的关系,比跑通回测本身重要十倍。

💡 打个比方

回测引擎像一台驾驶模拟器:把你的「驾驶规则」放进历史路况里跑一遍,撞了多少次、油耗多少,全有记录。

模拟器的价值是便宜地暴露问题——但没人会因为模拟器里跑得好就直接上高速。记住 q0.5:模拟器用的是「已经发生过的路况」,而真实的路,永远在生成模拟器里没有的弯道。

一、把规则书翻译给 backtrader

规则书六要素,逐条变代码。新建 backtest.py:

import pandas as pd
import backtrader as bt

df = pd.read_csv("csi300.csv", parse_dates=["date"]).set_index("date").sort_index()
df = df[df.index >= "2005-04-08"]

class SmaCross(bt.Strategy):
    params = dict(fast=5, slow=20)          # 规则书:5/20均线
    def __init__(self):
        sma_f = bt.ind.SMA(period=self.p.fast)
        sma_s = bt.ind.SMA(period=self.p.slow)
        self.crossover = bt.ind.CrossOver(sma_f, sma_s)  # 1=金叉,-1=死叉
    def next(self):                          # 每根K线走完调用一次→天然「信号次日执行」
        if not self.position and self.crossover > 0:
            self.order_target_percent(target=0.95)       # 规则书:95%仓位买入
        elif self.position and self.crossover < 0:
            self.close()                                  # 规则书:死叉清仓

cerebro = bt.Cerebro()
cerebro.adddata(bt.feeds.PandasData(dataname=df))
cerebro.addstrategy(SmaCross)
cerebro.broker.setcash(100000)                            # 10万本金
cerebro.broker.setcommission(commission=0.0005)           # 规则书:单边0.05%
cerebro.addanalyzer(bt.analyzers.DrawDown, _name="dd")
cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name="ta")

result = cerebro.run()[0]
print("期末资金:", round(cerebro.broker.getvalue()))
print("最大回撤:", round(result.analyzers.dd.get_analysis()["max"]["drawdown"], 1), "%")
ta = result.analyzers.ta.get_analysis()
print("交易次数:", ta.total.closed, "| 盈利笔数:", ta.won.total, "| 亏损笔数:", ta.lost.total)
print("胜率:", round(ta.won.total / ta.total.closed * 100, 1), "%")

# 对照组:买入持有(一定要算,没有对照的收益率没有意义)
close = df["close"]
bh_return = close.iloc[-1] / close.iloc[0] - 1
bh_dd = (close / close.cummax() - 1).min()
print("--- 对照:买入持有 ---")
print("总收益:", round(bh_return * 100, 1), "% | 最大回撤:", round(bh_dd * 100, 1), "%")

next() 在每根 K 线走完后被调用,下单在下一根成交——backtrader 天然帮你守住了「信号次日执行」,这是它比手写循环省心的地方之一。

二、成绩单,和它的四堂课

2026-07 实测结果(2005-04-08 → 2026-07-29):

指标双均线策略买入持有(对照)
期末资金(10万起)45.9 万(+359.1%)45.8 万(+358.4%)
最大回撤-45.1%-72.3%
交易次数156 次1 次
胜率35.3%(55胜/101负)

四个数字,四堂课:

  1. +359% vs +358%:忙活 21 年,没多赚一分钱。156 次买卖、上百笔佣金,最后和「买了就睡觉」打平——这就是 q1.5 那个平庸信号的真实成色。任何人给你看收益率,先问对照组:跑赢「什么都不做」了吗?
  2. -45% vs -72%:它真正的价值藏在这里。买入持有在 2008 年要眼睁睁看着资产腰斩再腰斩;策略把最深的坑从 72% 减到 45%。趋势类策略的实际作用更多是「少挨跌」,不是「多赚钱」——市场大跌时它在场外,代价是震荡市里反复被小刀割(101 笔亏损就是这么来的)。
  3. 35% 胜率却没亏钱:胜率是最会骗人的单一指标。它亏 101 次、赢 55 次,但亏时割得快、赢时拿得住(趋势策略的典型形状)。反过来说:一个「胜率 90%」的策略也可能一次大亏全吐光。胜率必须和盈亏比一起看——下一批的绩效课细讲。
  4. 45% 的回撤,是规则书写的。q1.7 我们白纸黑字写了「无止损」——这就是代价。不是意外,是知情的选择。
⚠️ 避坑⚠️ 坑一:这张成绩单,还是「美颜过」的

成绩单已经不算好看,但真相是:它仍然偏乐观。回测里藏着三个理想化假设——

零滑点:假设每次都能按收盘价成交,真实市场你会买贵卖贱一点;② 随时有量:假设想买就买得到;③ 指数可直接交易:实际要通过相关基金等工具,还有管理费和跟踪误差。

156 次交易,每次「优化」一点点,累积起来足以把「打平买入持有」变成「明确跑输」。记住:回测成绩是策略的「化妆照」,实盘素颜只会更糟——化妆照都不好看的,素颜就别想了。

⚠️ 避坑⚠️ 坑二:别现在就去调参数——那扇门后面是陷阱

看到成绩平庸,你的第一冲动一定是:「把 5/20 改成别的试试?说不定 10/60 更好!」

。这个冲动无比自然,也无比危险——它就是 q0.5 讲的「试到好看为止」。剧透下一节:我们会故意放开手脚调参,在前 14 年数据上找出「最优参数」(年化 18.72%,漂亮吧?),然后拿它去后 7 年验证——结果是年化 -3.24%,由正转负。数字已经跑好了,下一节亲手复现这场车祸。

❓ 测验
策略 21 年 +359%,买入持有同期 +358%,但策略最大回撤 45% vs 买入持有 72%。最准确的评价是?
✏️ 填空
评价任何策略收益,先问对照组:跑赢「什么都不做」(买入 ___ )了吗?

📌 免责:本课为技术科普,回测基于历史数据与理想化假设,回测表现不代表实盘,更不代表未来;本策略为教学示例、统计上无超额收益;指数不可直接交易;不构成任何投资建议;投资有风险,盈亏自负。

✅ 小结

这一节你跑通了人生第一个完整回测,更重要的是学会了读成绩单:收益要有对照组(vs 买入持有)、风险看回撤(45% 是规则书自己写的)、胜率必须搭配盈亏比、以及——回测永远是化妆照

但今天的回测,我们是「守规矩」跑的。如果不守规矩呢?

下一节是整个 Q1 的灵魂课:我们故意犯规两次——先「偷看未来」(一行代码之差,收益从 724% 膨胀到 5248%),再「疯狂调参」(训练段年化 18.72% 的最优参数,测试段 -3.24% 当场翻车)。q0.5 讲过的坑,这次让它们在你自己的代码里现出原形。看完这节,你对任何回测数字的免疫力,将超过 99% 的散户。

下一节 → 防三坑实操:亲手体验回测怎么骗人
🔎 来源与核验· 3 条,点开核对
本节每个关键论断都对应一个可追溯的来源 —— 这是本课程"靠谱、不过时"的底线。
「双均线(5/20)策略回测(沪深300指数 2005-04-08~2026-07-29,95%仓位,佣金0.05%,无止损):期末+359.1%,最大回撤45.1%,156笔交易胜率35.3%;同期买入持有+358.4%,最大回撤72.3%」
📚 本节 backtrader 2026-07 实测✓ 已核验 2026-07
「backtrader 的 next() 于每根K线收盘后调用、订单于次根成交,天然规避收盘价未来函数」
📚 backtrader 官方文档✓ 已核验 2026-07
「回测默认不含滑点与冲击成本,结果系统性偏乐观」
📚 量化回测通识(承接 q0.5)✓ 已核验 2026-07
回测结果仅对所述数据、参数与假设成立,不构成任何收益暗示;历史表现不代表未来。
智图软件的赞赏码
都看到这了,打个赏呗!
接下来 · q1.9
防三坑实操:亲手体验回测怎么骗人
继续读下一节 →