backtrader 回测入门:第一个策略
系列·回测篇 — 在真实 A 股日线上跑通双均线策略,重点不是收益,而是把回测的口径做诚实
📖 本篇是《量化实操教程》第 3 篇,前置:akshare 数据实操(本篇直接喂数据篇拉好的日线)。
01 · 为什么选 backtrader
纯 Python、事件驱动、社区成熟,个人研究者从"想验证一个想法"到"跑出结果"最快的一条路。安装无重依赖:
pip install backtrader
# 可选:绘图需要 matplotlib,且老版本兼容更好(matplotlib>=3.8 会报错),本篇聚焦指标输出,先不装02 · 喂数据:akshare 的 DataFrame 直接进 backtrader
用数据篇的清洗口径,索引是 DatetimeIndex、列小写英文,PandasData 就能直接吃:
import akshare as ak
import backtrader as bt
import pandas as pd
df = ak.stock_zh_a_hist(symbol="000001", period="daily",
start_date="20230101", end_date="20250801", adjust="qfq")
df["date"] = pd.to_datetime(df["日期"])
df = df.rename(columns={"开盘": "open", "最高": "high", "最低": "low",
"收盘": "close", "成交量": "volume"})
df = df.set_index("date")[["open", "high", "low", "close", "volume"]]03 · 第一个策略:双均线
金叉(短均线上穿长均线)买入,死叉卖出——最经典的教学策略:
class SmaCross(bt.Strategy):
params = dict(fast=5, slow=20)
def __init__(self):
ma_fast = bt.ind.SMA(self.data.close, period=self.p.fast)
ma_slow = bt.ind.SMA(self.data.close, period=self.p.slow)
self.crossover = bt.ind.CrossOver(ma_fast, ma_slow)
def next(self):
if not self.position and self.crossover > 0:
self.buy(size=100) # A股一手 = 100 股
elif self.position and self.crossover < 0:
self.close()口径说明:backtrader 默认在 next() 里下的单按下一根 bar 的开盘价成交——这正是诚实的 口径(信号在收盘后确认,下一时刻才能成交)。如果你手动改成当日 close 成交,就是在用未来数据作弊。
04 · 跑起来:A 股成本要算进去
cerebro = bt.Cerebro()
cerebro.adddata(bt.feeds.PandasData(dataname=df))
cerebro.addstrategy(SmaCross)
cerebro.broker.setcash(100000)
cerebro.broker.setcommission(commission=0.0003) # 佣金万2.5,双边简化
cerebro.broker.set_slippage_perc(0.001) # 滑点 0.1%
cerebro.addanalyzer(bt.analyzers.Returns, _name="ret")
cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name="sharpe", timeframe=bt.TimeFrame.Days)
cerebro.addanalyzer(bt.analyzers.DrawDown, _name="dd")
result = cerebro.run()
print("期末净值:", round(cerebro.broker.getvalue(), 0))
print("年化收益:", result[0].analyzers.ret.get_analysis()["rnorm"])
print("夏普:", result[0].analyzers.sharpe.get_analysis()["sharperatio"])
print("最大回撤: %.1f%%" % result[0].analyzers.dd.get_analysis()["max"]["drawdown"])成本简化声明:setcommission 不分买卖边,A 股真实口径是佣金双边 +卖出印花税(0.05%)。低频策略影响不大,但精确复算成本要在佣金类里分边实现——本教程先按简化口径。
05 · 结果怎么读
- 夏普比率 — 长期 >1 算尚可,>2 要先怀疑是不是哪里用了未来数据;负数说明还不如持有不动。
- 最大回撤 — 比收益更决定你能不能拿住;回撤 30% 的策略,大多数人会在 -15% 时手动放弃。
- 和基准比 — 同期沪深300 涨了多少?跑赢基准才算策略有增量,否则不如买指数。
06 · 四个必避的坑
- 未来函数 — 用当日
close算信号又按当日close成交=作弊;保持默认"次日开盘成交"口径。 - 忘记成本 — 不设佣金/滑点的回测,高频策略看起来全是利润,实盘全被摩擦成本吃掉。
- 过拟合 — 把 fast/slow 调到收益最大那一刻,你调的是历史噪音;正确做法:留一段数据从头到尾不参与调参,只在最后验一次。
- 复权错误 — 用不复权数据回测,除权日的假跳空会让策略产生莫名的"盈利";统一
qfq并定期全量刷新(数据篇坑 #3)。
🚀 回测跑上瘾后,你会发现"每天自动更新数据 + 每周定时回测"必须有一台不关机的机器——这正是 基础篇的用武之地:
阿里云入口 · 新客可享 85 折起 →本站入口含推广归属,你的价格不变;价格与折扣以官方活动页实时为准。本教程不构成任何投资建议;历史回测不代表未来表现。