【Python量化实战 #14】回测只会手写太累?用 backtrader 接入行情跑通第一个策略 连载系列「Python量化实战」第 14 篇模块六 · 量化策略实战。#11 我们手写过双均线回测——几十行循环就能跑但手续费、仓位管理、夏普比率、最大回撤全要自己造轮子。本篇把同一个策略搬进专业回测框架 backtrader轮子它全有。backtrader 是 Python 生态最流行的开源回测框架之一事件驱动、内置指标库和绩效分析器。但新手最常卡在第一步数据怎么喂进去。官方教程用 Yahoo 的美股 CSVA 股数据要自己解决——爬虫拼的数据经常缺字段、格式对不上。本文用 mairui SDK 拉取 A 股前复权 K 线整理成 backtrader 认识的PandasData格式完整跑通数据接入 → 策略编写 → 手续费设置 → 绩效分析全流程并直面一个重要事实这次回测是亏钱的而这恰恰是回测的价值所在。本文你将得到什么把 A 股 K 线喂给 backtrader 的关键DatetimeIndex 小写 OHLCV 列名 float 类型一个 backtrader 版双均线策略金叉买入/死叉清仓含 95% 仓位管理含手续费的回测与夏普/最大回撤/胜率分析如实展示 -34.68% 的亏损案例一、环境准备本文代码使用 mairui SDK 获取股票数据、backtrader 做回测安装方法如下pipinstallmairui backtraderSDK 的完整接口文档与使用说明请查阅 GitHub 仓库https://github.com/MaiRuiApi/mairui接口的详细参数说明请查阅官网 API 文档https://mairuiapi.com/hsdata运行环境Python 3.9mairui SDK 1.0.0backtrader 1.9.78.123pandas 2.2importosimportmairui# 证书从环境变量读取注册后可在官网获取apimairui.Client(LICENCE-66D8-9F96-0C7F0FBCD073)# 演示证书演示证书读者复制即用无需替换本文数据截至 2026-07-24回测区间 2024-01-02 至 2026-07-24600519619 个交易日。二、第一步把 A 股数据喂给 backtraderbacktrader 的PandasData数据源对 DataFrame 有三个要求DatetimeIndex 索引、小写的 open/high/low/close/volume 列名、数值类型为 float。mairui 返回的 K 线字段是t/o/h/l/c/v做一次列名映射即可importpandasaspdimportbacktraderasbtdeffetch_kline_for_bt(api,code,start,end):拉前复权日K整理为 backtrader PandasData 需要的 OHLCV 格式。rowsapi.stock_history(code,d,f,ststart,etend)# d日线 f前复权kline_dfpd.DataFrame(rows).rename(columns{t:datetime,o:open,h:high,l:low,c:close,v:volume,})kline_df[datetime]pd.to_datetime(kline_df[datetime])forcolin(open,high,low,close,volume):kline_df[col]kline_df[col].astype(float)kline_dfkline_df.set_index(datetime).sort_index()returnkline_df[[open,high,low,close,volume]]关键解释回测必须用前复权数据第三个参数f否则除权缺口会被框架当成真实暴跌信号和收益全部失真st/et只认YYYYMMDD格式。这个函数是通用的——换任何 A 股代码、任何周期返回的 DataFrame 都能直接bt.feeds.PandasData(dataname...)喂给框架。三、第二步编写第一个策略backtrader 策略继承bt.Strategy两个方法最关键__init__里声明指标框架自动向量化预计算next里写每根 K 线的交易逻辑classSmaCrossStrategy(bt.Strategy):短均线上穿长均线买入下穿卖出——backtrader 版双均线策略。paramsdict(fast5,slow20)def__init__(self):sma_fastbt.ind.SMA(periodself.p.fast)# 5日均线sma_slowbt.ind.SMA(periodself.p.slow)# 20日均线self.crossoverbt.ind.CrossOver(sma_fast,sma_slow)# 1金叉, -1死叉defnext(self):ifnotself.positionandself.crossover0:# 空仓遇金叉 - 买入self.order_target_percent(target0.95)# 95%仓位elifself.positionandself.crossover0:# 持仓遇死叉 - 清仓self.close()defnotify_trade(self,trade):iftrade.isclosed:# 每笔平仓打印盈亏print(f 平仓{bt.num2date(trade.dtclose).date()}净利{trade.pnlcomm:.2f})关键解释对比 #11 手写版均线计算、金叉判定、持仓状态、成交撮合全部由框架接管bt.ind.CrossOver一行顶手写版十几行的上穿/下穿判断。order_target_percent(target0.95)表示调整到 95% 仓位留 5% 现金缓冲避免按次日开盘价成交时资金不足被拒单。四、第三步组装 Cerebro 引擎并运行Cerebro是 backtrader 的总控引擎挂数据、挂策略、设资金和手续费、挂绩效分析器然后run()cerebrobt.Cerebro()cerebro.adddata(bt.feeds.PandasData(datanamekline_df),name600519)cerebro.addstrategy(SmaCrossStrategy)cerebro.broker.setcash(100000.0)# 初始资金 ¥100,000cerebro.broker.setcommission(commission0.0003)# 佣金万3教学示例# 绩效分析器年化收益 / 夏普 / 最大回撤 / 交易统计cerebro.addanalyzer(bt.analyzers.AnnualReturn,_nameannual)cerebro.addanalyzer(bt.analyzers.SharpeRatio,_namesharpe,riskfreerate0.02,annualizeTrue)cerebro.addanalyzer(bt.analyzers.DrawDown,_namedrawdown)cerebro.addanalyzer(bt.analyzers.TradeAnalyzer,_nametrades)resultcerebro.run()[0]# 读取分析器结果sharperesult.analyzers.sharpe.get_analysis().get(sharperatio)ddresult.analyzers.drawdown.get_analysis()print(f期末市值: ¥{cerebro.broker.getvalue():,.2f})print(f夏普比率:{sharpe:.2f}, 最大回撤:{dd[max][drawdown]:.2f}%)关键解释手续费一行setcommission搞定这是手写回测最容易偷懒省略、又对结论影响最大的细节之一分析器挂上后从result.analyzers.名字.get_analysis()取结果夏普、回撤都不用自己算。五、完整回测结果真实输出600519 · 2024-01-02 至 2026-07-24 · 双均线(5,20) · 佣金万3真实运行输出节选K线条数: 619, 区间: 2024-01-02 ~ 2026-07-24 初始资金: ¥100,000.00 交易过程 平仓 2024-03-11 净利 -2756.87 平仓 2024-05-20 净利 1290.63 平仓 2024-10-29 净利 -7177.33 平仓 2025-04-03 净利 2310.94 平仓 2026-04-22 净利 -1856.71 ...共 20 笔 回测绩效 期末市值: ¥65,319.73 区间总收益: -34.68% 2024 年度收益: -26.03% 2025 年度收益: -8.14% 2026 年度收益: -3.86% 夏普比率(年化, rf2%): -1.53 最大回撤: 39.37% 交易次数: 20, 胜率: 25.0%这是一次亏损的回测而且我们原样公布。20 笔交易只有 5 笔赚钱累计 -34.68%。原因不难解释600519 这两年半整体震荡下行双均线是趋势跟随策略在无趋势的震荡下行段会被反复假金叉止损。这正是回测框架的真正价值——在投入真金白银之前用几秒钟证伪一个不适配当前行情的策略。如果一篇教程展示的回测永远在赚钱你反而应该警惕。六、避坑与进阶数据格式三要素DatetimeIndex、小写 OHLCV 列名、float 类型缺一个PandasData就报错或静默错位。均线预热期SMA(20) 前 19 根 K 线无值backtrader 自动从第 20 根开始调用next不需要手动跳过但要意识到回测实际起点晚于数据起点。夏普比率为 None交易样本太少时SharpeRatio可能返回None取值时用.get(sharperatio)并判空别直接格式化。不要在 next 里偷看未来self.data.close[0]是当前 K 线[-1]是上一根用[1]未来数据回测结果会好得离谱但毫无意义。进阶方向cerebro.optstrategy可对均线参数网格寻优注意过拟合多股票回测只需多次adddata配合cerebro.plot()可输出K线交易点位图需安装 matplotlib。七、总结与延伸本文完成了 backtrader 的最小完整闭环mairui K 线数据 →PandasData接入 → SMA 双均线策略 → 含手续费的回测 → 夏普/回撤/胜率分析并如实展示了一次亏损的回测结果。掌握这套骨架后换策略只改Strategy类换标的只改代码参数数据层稳定不动——这正是稳定数据源 专业框架的工程价值。下一篇预告【Python量化实战 #15】事件驱动策略——财报发布与公告异动的量化捕捉方案。本文为技术演示回测结果为历史数据模拟且为亏损案例不构成投资建议不代表任何未来收益。市场有风险投资需谨慎。