backtrader多股回测实战:复权因子、未来函数与真实价撮合避坑指南

发布时间:2026/9/18 23:04:47
backtrader多股回测实战:复权因子、未来函数与真实价撮合避坑指南 回测年化30%的模型放到实盘里跑三个月就开始亏钱这种剧本我在量化社群见过太多回了。大多数人的第一反应是策略过拟合、滑点太大、手续费没算够但真正的问题往往藏在一个最不起眼的地方——复权。如果你正在用 backtrader 跑多股回测或者刚把一套历史收益很漂亮的策略接到实盘我强烈建议先花十分钟看完这篇记录。前复权、后复权、不复权这三套价格体系只要有一处混用回测结果和实盘表现就能差出十万八千里甚至完全反向。我不打算写教科书式的复权定义而是用一个真实的踩坑场景切入为什么回测里能成交的价位实盘根本挂不上单为什么前复权数据会在你的策略里埋入“未来函数”以及一套我目前一直在用的“真实价成交、复权价计算”双视图方案配 backtrader 多股回测的具体落地方法。1. 复权是什么回测里为什么绕不开它1.1 三种价格形态不复权、前复权、后复权先把概念捋清楚。股票分红送转之后股价会向下跳空。最典型的例子是10送10股权登记日收盘20元第二天除权理论上参考价变成10元。如果直接看不复权K线这一天就是一个巨大的向下跳空缺口像“暴跌”一样。问题是股票并没有跌只是股本变大了、每股含金量减半了。不复权就是真实成交价有多少钱就是多少钱适合判断涨跌停、计算实际市值、做下单撮合。前复权以最新价格为基准把历史价格按照复权因子向下压缩让整条K线连起来。图形漂亮但历史价格会随着“最新价”的变化而不断重算。后复权以上市首日或某个固定基准为基准把之后的价格向上放大历史序列一旦生成就是固定的不受新数据影响。后复权的价格会高得离谱比如茅台后复权能到几万块看着吓人但它才是量化计算里最稳定的那个视图。前复权适合看图后复权适合计算不复权适合下单——这句话基本可以概括全文。1.2 除权除息与复权因子的本质任何股票数据源里复权因子adj_factor才是灵魂。它本质上是一个缩放倍数记录每次分红送转带来的价格修正。常见的数据约定是后复权价 真实价 × 复权因子举个例子某股票上市时复权因子是1.0第一次10送10之后因子变成2.0。除权前真实价20元后复权价是20×1.020元除权后真实价10元后复权价是10×2.020元。价格连续了对不对所以你最容易踩的第一个坑是拿接口直接返回的 qfq前复权K线喂给回测引擎。因为前复权K线已经把真实价格这个信息丢掉了只剩下一个被压缩过的虚拟价格。后面所有回测、信号、撮合都建立在这个虚拟价格之上和实盘的真实成交价完全脱节。更麻烦的是前复权价格还会随着行情更新不断漂移这是下一章的重点。2. 前复权数据里的隐形杀手未来函数与信号失真2.1 前复权是“会变”的历史很多人没意识到前复权的历史价格并不是一个固定事实。它采用的基准是“最新一个交易日”所以每次行情更新前复权因子就会变整段历史K线都会跟着重算。这意味着你回测时看到的2019年K线是站在2024年最新基准下重新调整过的K线2019年那一刻的交易者根本看不到这个价格。这算不算未来函数算而且是很隐蔽的一种。你并没有显式地用到未来数据但历史价格本身已经被“未来信息”改写了。对均线突破、动量这类相对价格关系影响可能不大但只要策略里涉及绝对价格水平比如“股价低于5元买入”“价格区间网格”“低价股轮动”灾难就来了。2.2 价格阈值策略被前复权悄悄改写我见过一个最典型的案例。某只股票在2020年做了一次10送20的高送转前复权之后2019年的历史价格被压缩到原来的三分之一。一只原本28元的股票在前复权K线上变成了9.3元。策略逻辑是“股价低于10元且20日均线向上”就买入于是回测在2019年触发了一大堆买入信号看似年化收益30%。但实盘那位交易者在2019年面对的是一只28元的股票条件根本不成立。这类策略在回测里越是“精准命中”实盘越是亏得惨因为回测中每一次买入都是被未来基准重写过的假信号。排查方法也很简单把同一策略分别用不复权、前复权、后复权三种数据跑一遍如果收益差异大到盈亏逆转十有八九就是绝对价格逻辑被复权坑了。2.3 涨跌停与撮合价失真涨跌停价格是按真实价格计算的不是按复权价。除权日当天真实K线上有一个向下的价格跳空而前复权K线把这个缺口补平了。问题在于很多策略会用“跌幅超过某阈值”或者“触及跌停”作为买入条件前复权之后除权日看起来就像一个“抄底机会”回测引擎会愉快地在跌停价附近给你成交。但实盘呢除权参考价、真实跌停价、集合竞价价格全部基于真实价格你看好的那个“低位”根本不存在或者就算存在真实盘口上的买单和卖单也不是回测系统里那个虚拟价格能成交的。回测里那些漂亮的低吸点位放到实盘里要么抢不到筹码要么买在了一个完全不同的位置。这是“回测稳、实盘拉胯”最常见的来源之一。3. 多股回测的复权陷阱backtrader 里最容易翻车的地方3.1 多股票组合的“基准错位”单只股票用错复权数据影响还局限在个券上多股回测里复权基准错位会被组合层放大。每只股票的除权除息日期不同、送转比例不同、累计因子不同。如果全部用前复权数据各只股票的价格水平就完全不可比了。A股票高送转多次前复权后价格被压到5块钱B股票从不分红价格一直是80块。你的组合策略如果按“价格等权”“价格区间分配资金”来配置资金分配比例会被严重扭曲。更隐蔽的是组合净值计算。很多人习惯把组合里各股票的价格直接加总或者用固定股数乘以价格求市值前复权价根本不是真实市值算出来的权重自然也是歪的。高位送转的股票实际权重被低估不分红的股票被高估最后跑出来的组合曲线完全不是你想表达的策略。3.2 backtrader 多股回测的典型错误结合我自己的使用经验backtrader 里跑多股回测最容易出现下面几类问题错误一所有股票用 tushare 的 qfq 数据直接喂给 backtrader然后按固定股数算组合市值。不同股票的复权基准不一致市值权重天然就是错的。错误二数据用了 qfq但下单价格直接取 self.data.close[0]。因为 close 已经是前复权后的虚拟价格backtrader 的 broker 就会按虚拟价格撮合和实盘价格完全对不上。错误三每次重新下载历史数据由于前复权因子随最新价漂移同一套策略前后两次回测结果对不上。我见过有人反复查代码最后发现是数据版本变了。错误四多股停牌日期不同复权因子列在停牌区间缺失有人直接前向填充导致除权日附近的因子错乱信号全部错位。3.3 为什么后复权是多股回测更稳的主数据后复权的核心优势是确定性和可复现性。只要真实价格不变、复权因子规则不变历史后复权序列就永远不变。对多股回测来说用后复权价做横截面比较、计算动量因子、构建收益率矩阵都不会被基准漂移干扰。而且后复权价有一个重要的数学性质它的日度收益率约等于含分红的真实收益率。这意味着你可以直接用后复权价格序列计算策略净值不用在每次除权日手工处理跳空。唯一的注意点是后复权价格数值大小和真实价格不是一个量级不适合做“绝对值相关的价格策略”但适合做“相对收益类”的横截面策略。记住一个公式随时切回真实价格真实价 后复权价 / 复权因子4. 解决方案双视图回测架构与回测-实盘映射4.1 数据分层原则我的做法是把数据分成四层每层各司其职互不污染。第一层是原始数据层只保存不复权的 OHLCV 和对应的复权因子 adj_factor。第二层是派生数据层需要后复权时实时计算后复权价 真实价 × adj_factor。第三层是指标计算层策略里的均线、动量、波动率、突破信号全部基于后复权价生成。第四层是撮合执行层所有下单价格、涨跌停判断、滑点模拟一律使用真实价格完成。这样设计的好处是信号计算和交易执行两个环节彻底解耦。信号告诉你要买执行层只负责按真实市场价格成交不会出现“策略在虚拟价格上开仓、实盘在真实价格上挨打”的错位。4.2 回测-实盘信号映射在 backtrader 里一个最直接的做法是在 next 回调里手动计算下单价格。假设你用的是带 factor 字段的自定义数据源class PandasDataExt(bt.feeds.PandasData): lines (factor, real_close,) params ( (factor, -1), (real_close, -1), )这里我在数据源里额外增加了 factor 列和 real_close 列。策略逻辑里信号指标用 self.data.close[0] * self.data.factor[0] 算出的后复权价而真正下买单时用真实价格real_price self.data.real_close[0] self.buy(exectypebt.Order.Limit, pricereal_price * 0.98)这样 broker 撮合时使用的是真实价格体系不会被后复权价格误导。有一个我踩过的坑要提醒除权日当天factor 的值是当天收盘后才知道的盘中下单时如果用当天的 factor 反推真实价可能出现数据错位。稳妥的处理是提前维护一份除权除息计划表或者在撮合之后加一道校验成交价不能低于当日真实最低价、不能高于当日真实最高价否则丢弃重打。4.3 股息与红利再投资后复权收益率里通常包含了分红再投资的假设但真实账户里现金分红不会自动变成股票到账有延迟还要扣红利税。为了让回测更贴近实盘比较严谨的做法是把“价格收益”和“现金分红”拆开处理用真实价格计算价格涨跌分红日单独记录一笔现金流入按除权日或实际到账日计入组合现金。backtrader 里可以通过 notify_order 或自定义 Analyzer 处理分红到账事件。如果你的策略本身不依赖分红直接用后复权收益率当然简单但心里要清楚这个收益率比真实“股价涨幅”多了一部分分红收益时间一长净值曲线的水分不小。4.4 Backtrader 多股回测实操要点多股回测我现在的标准流程是这样的。第一步下载不复权日线数据包含 ts_code、trade_date、open、high、low、close、pre_close、vol、amount再单独下载 adj_factor 表合并成一张 DataFrame。第二步每一只股票生成三列核心数据真实价、复权因子、后复权价真实价 × factor。第三步自定义 PandasDataExt 传入 backtrader。第四步使用字典管理多标的的 data 引用方便在 next 里遍历self.dnames {d._name: d for d in self.datas} for name, d in self.dnames.items(): hfq_close d.close[0] * d.factor[0] real_price d.real_close[0] # 在这里写你的信号逻辑和下单逻辑第五步回测结束后导出订单明细重点人工检查除权日附近的订单价格是否落在真实价格区间内。这一步看起来很笨但确实能抓住大部分复权问题。5. 5分钟自查清单你的回测有没有踩复权坑5.1 快速验证的五个检查点检查回测订单里有没有“买入价低于当日真实最低价、卖出价高于当日真实最高价”的成交。有就是价格视图混用了。把回测信号的触发日期和除权除息日历对齐如果信号密集出现在除权日附近基本可以判定是复权缺口被策略误读。用不复权、前复权、后复权三种数据分别跑同一套策略收益差异大到年化几个点以上数据层一定有问题。确认你的数据链路里保存了复权因子。如果你的数据只有 qfq K线没有 adj_factor那就无法做真实价映射建议立刻换数据源。实盘前做一次“除权日模拟”在股权登记日和除权日分别跑一笔模拟单看信号和成交价是否稳定。5.2 我排查过的两个真实案例案例一某个“跌停反转”策略回测年化28%一到实盘就频繁止损。我把回测订单导出来发现大量买单出现在“10送10除权日”的跌停价附近。复盘后确认回测系统用的是前复权数据除权日的价格跳空被当作“超跌”信号买入价根本不是真实盘口能成交的价格。切换成“后复权计算信号、真实价格撮合”之后年化降到12%虽然没那么惊艳但实盘终于不再乱开仓了。案例二backtrader 多股回测组合里有一只高送转股票和一只从不分红股票。用 qfq 数据直接加总市值时高送转股票前复权价被压到很低导致它的实际市值权重被严重低估。同一个策略仅仅把数据切到后复权真实市值权重组合净值的形状就完全变了。后来我固定了一套规则所有横截面权重一律用“真实收盘价×股本”计算所有因子计算一律用后复权收益率所有下单价格一律用真实价。我的个人体会是量化里“数据正确”永远排在“模型聪明”前面。现在我做任何回测数据链路里必须同时存在真实价和复权因子这两个字段。接口自带的 qfq 数据我只拿来看图从不直接喂给回测引擎。多股回测要复现先检查复权因子版本是不是一致。这套习惯改过来之后回测和实盘的偏差肉眼可见地缩小了至少“回测30%、实盘亏钱”这种乌龙已经很久没发生过了。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询