Backtesting.py / Strategy

Backtesting.py 策略循环:init 与 next 的写法、指标接入与参数优化

策略开发是回测的核心:继承 Strategy 基类、在 init() 里注册指标、在 next() 里写买卖逻辑,再用内置优化器做参数网格搜索与热力图分析。官方 README 给的是一段完整示例,但真正卡人的是「每一步在做什么、写错会怎样」——本页把这条链路拆成可复制的步骤,并补上参数可信度判断与结构性错误排查。

Strategy:策略基类指标:BYO / talib优化:optimize + 热力图
Strategy继承基类
init()注册指标
next()买卖逻辑
optimize()参数搜索
Backtesting.py 策略开发与回测流程示意(基于官方 README)。
测试版本backtesting 0.6.6(PyPI 发布版)Python 版本本站实测 3.11.9;官方要求以 README / PyPI 为准最后核验2026-09-29(实验数据 2026-09-24 运行)数据来源新浪日线前复权(6 只 A 股 + 港股 00700)适用市场A 股 · 港股可直接复现是(策略类写法与参数网格可照抄)含交易成本是(A 股 callable 成本,与其它页口径一致)样本外验证是(样本内 2018-01-02~2022-12-30 寻优,其后区间样本外复核)
Write Strategy

Backtesting.py 策略类怎么写:五步从空文件到能跑通

Backtesting.py 的策略骨架只有两个方法:init() 算指标、next() 做决策。下面五步按顺序做,每步给出真实代码、一句话说明和可以核对的预期输出。跑不到预期结果时就停在这一步排查——报错信息往往指向最后一行,而真正的起因经常在前面几步。

  1. 导入两个类

    从包顶层取 Backtest(回测器)与 Strategy(策略基类);策略必须继承 Strategy,回测器才能调度它。这三行同时验证了包是否装在当前解释器里——「装错环境」比「策略写错」更常见。

    from backtesting import Backtest, Strategy
    from backtesting.lib import crossover
    from backtesting.test import SMA, GOOG

    预期输出:无报错。若提示 ModuleNotFoundError,说明当前解释器没装包,先回下载部署页的自检清单。

  2. 在 init() 里注册指标

    init() 只在回测开始时执行一次,负责把指标算好并注册到策略上;self.I() 的返回值之后可以在 next() 里按序列取用。窗口写成类属性(n1 / n2)而不是写死数字,是为了后面能直接做参数搜索。

    class SmaCross(Strategy):
        n1 = 10
        n2 = 20
    
        def init(self):
            price = self.data.Close
            self.ma1 = self.I(SMA, price, self.n1)
            self.ma2 = self.I(SMA, price, self.n2)

    预期输出:无输出。指标序列与数据等长;长度不匹配会在 run() 阶段才报错,所以别在这一步就以为「没问题了」。

  3. 在 next() 里写交易规则

    next() 按 bar 逐个调用,这里用 crossover() 判断两条均线谁上穿谁:快线上穿慢线买入,慢线上穿快线卖出。规则要写成「事件」(交叉那一刻),不要写成「状态比较」(快线一直在慢线上方就一直买入),否则会反复下单。

        def next(self):
            if crossover(self.ma1, self.ma2):
                self.buy()
            elif crossover(self.ma2, self.ma1):
                self.sell()

    预期输出:无输出。真正的成交记录要等 run() 之后才看得到。

  4. 处理持仓与平仓

    self.position 能读到当前持仓(is_long / is_short),self.position.close() 平掉现有仓位。多空混合的策略里,平仓条件与开仓条件要成对写,避免方向叠加或反复开平。

        def next(self):
            if self.position.is_long and crossover(self.ma2, self.ma1):
                self.position.close()

    预期输出:无输出。跑完后结果里的交易笔数应与你预期的量级一致;差得离谱通常是条件没写成对。

  5. 创建回测并读结果

    把数据、策略类、资金与成本参数交给回测器,run() 返回统计结果;exclusive_orders=True 让每个新信号先平掉反向仓。commission 一定要显式给,默认值不等于你真实市场的摩擦成本。

    bt = Backtest(GOOG, SmaCross, cash=10_000, commission=.002, exclusive_orders=True)
    stats = bt.run()
    print(stats)

    预期输出:打印一份以 Return [%]、Max. Drawdown [%]、Sharpe Ratio、# Trades 等字段开头的结果面板;字段名与口径以实际安装版本为准。

操作写法适用场景注意点
开多self.buy()趋势跟随、突破入场资金或成交量不足时会被拒单,先确认 cash 与标的最小交易单位
开空self.sell()单标的做空、对冲先确认标的是否允许做空,不然结果没有实际意义
平仓self.position.close()反向信号、固定持有期与开仓条件成对写,否则容易出现反复开平
读持仓self.position.is_long / is_short条件分支判断不要用「价格涨了」代替方向判断,两者不等价
读行情self.data.Close / self.data.Open计算信号与过滤条件数据只要求 OHLC(V) 列,其它字段要自己准备并对齐
读权益self.equity动态仓位、按权益止损逐 bar 变化,不要拿它当静态阈值
Indicators

Backtesting.py 指标库无关怎么落地:三条接入路径对照

官方 README 把「指标库无关(BYO)」写成一个特性,落地时要在三条路径里选:内置工具、第三方库、自己用 pandas / NumPy 算。三条都能跑,差别在依赖、可控性与可复现性。

路径写法示例适用场景注意点
内置 libself.I(SMA, self.data.Close, 10)快速验证均线类想法只提供基础工具,复杂指标要自己写
talibself.I(talib.RSI, self.data.Close, 14)需要成熟技术指标talib 要单独安装且依赖系统库,装不上时先查系统依赖再看 Python 侧
pandas-taself.I(ta.rsi, self.data.Close, 14)想用 pandas 生态的技术指标、又不想装系统依赖它与 TA-Lib 的同名指标由各自库实现,口径不保证一致;换库前先用同一段数据对照一次
pandas 滚动self.I(lambda c: c.rolling(20).mean(), self.data.Close)自定义指标、快速试验返回序列长度与索引要与数据对齐,否则 run() 阶段报错
NumPy 向量化自己写函数后交给 self.I()计算量大的指标返回值必须与原序列等长,长度算错会很难定位
指标参数化把窗口写成类属性 n1 / n2准备做参数搜索的策略属性名不要与基类已有属性冲突
多指标组合在 init() 里依次注册需要多个条件确认指标越多越容易过拟合,先各自单独看再组合
预热期处理前 N 根 bar 没有值所有均线类与长周期指标预热期内不产生信号,别误判成逻辑写错
说明:self.I() 会缓存指标计算结果,next() 中可按索引取用;talib 的安装方式与支持的指标集以官方文档为准。指标本身不是策略,把指标阈值直接当买卖信号前,先想清楚它在什么行情里会失效。
Optimize

Backtesting.py 参数优化怎么做?从参数网格到热力图

Backtesting.py 的内置优化器让「一组参数」变成「一片参数」,能一次看到参数的稳定区间。但同一套机制也会一次制造大量「偶然看起来很好」的组合,所以这一步的重点不是找峰值,而是判断哪片区域可信。

  1. 把要搜索的参数提成类属性

    只有写成类属性的参数才能被优化器扫描;写死在 init() 里的数字对优化器不可见。

    class SmaCross(Strategy):
        n1 = 10
        n2 = 20
    
        def init(self):
            self.ma1 = self.I(SMA, self.data.Close, self.n1)
            self.ma2 = self.I(SMA, self.data.Close, self.n2)

    预期输出:无输出。可以先手动改 n1 跑两次 run(),确认结果确实随参数变化。

  2. 调用 optimize() 做网格搜索

    把参数取值(通常是 range)、优化目标 maximize 与可选约束 constraint 传给优化器;约束用来排除无意义的组合,例如快线窗口不小于慢线。

    stats, heatmap = bt.optimize(
        n1=range(5, 30, 5),
        n2=range(10, 70, 10),
        maximize='Equity Final [$]',
        constraint=lambda p: p.n1 < p.n2)
    print(heatmap)

    预期输出:返回优化后的结果与一张热力图对象(具体返回顺序与字段以官方文档为准);打印时会看到以参数为行列表头的数值矩阵。

  3. 读热力图:看邻域,不看单点

    热力图的价值在于形态:稳定区是一片连续的高值,偶然值是一个孤立的亮点。只盯那个亮点,等于把噪声当规律。

    print(heatmap)              # 打印参数矩阵
    print(stats['# Trades'])    # 顺带看这组参数产生了多少笔交易

    打印出来的矩阵行与列分别对应两组参数,先看高值是否连续成片,再看笔数是否够多。

    预期输出:能指出一片「参数变动、表现也不差」的区域;如果全程都是棋盘状的花纹,说明这组指标在这段数据上没有稳定结构。

  4. 用未参与优化的区间复核

    把数据切成两段:一段用来搜索参数,另一段只做验证。样本内挑出来的参数在样本外经常直接失效,这一步是筛掉大部分假结论的地方。

    SmaCross.n1, SmaCross.n2 = 10, 20          # 换成你搜索得到的那组参数
    train, test = data.iloc[:-252], data.iloc[-252:]
    print(Backtest(test, SmaCross, cash=10_000, commission=.002).run()['Return [%]'])

    参数赋值方式与区间划分方法只是示意,写法与返回字段以官方文档为准。

    预期输出:样本外区间的结果明显弱于样本内是常态;如果样本外直接反向,通常说明参数在拟合噪声。

环节怎么设适用场景注意点
搜索范围n1=range(5, 30, 5)首次扫某组参数范围先粗后细,一上来就细分会把网格撑得很大
优化目标maximize='Equity Final [$]'需要单一排序指标用哪个指标优化,结果就偏向哪个指标,回撤与交易次数要另外看
约束条件constraint=lambda p: p.n1 < p.n2参数之间有大小关系约束写错会静默筛掉大量组合,看到结果稀疏先回来检查它
优化后端方法参数取值以官方文档为准网格很大或要加速不同后端的结果可能不完全一致,换后端要重跑对照
结果读取同时看结果对象与热力图任何参数搜索只看单个数字会漏掉邻域信息
成本假设显式设置 commission,必要时加滑点任何打算落地的研究不设成本会系统性高估所有组合,且高估幅度对高频策略更大
样本外复核留出未参与优化的区间所有要写进结论的参数样本外表现明显变差属正常,关键是别把它藏起来
合规与边界:参数优化只是把历史区间上的指标排了个序,不代表未来表现;任何一组参数都可能在换市场、换区间后失效。回测结果仅供研究,不构成投资建议。
Patterns

Backtesting.py 常用策略模式有哪些?参数起点参考

下面三类是单标的回测里最常见、也最容易验证的起步模式。它们的共同点是逻辑不复杂、参数少,适合先把「回测链路跑通 + 结果解释得清」这两件事做扎实,再往上加条件。

均线交叉

快慢两条均线的交叉作为买卖事件,快线上穿慢线进场、下穿离场。它的优点是逻辑可复述、参数只有窗口长度;缺点是震荡行情里交叉频繁,成本会吃掉大部分理论收益,所以成本假设必须显式设置。

RSI 超买超卖

用 RSI 的阈值(常见 30 / 70 一类的分界)判断超买超卖并反向操作。它适合区间震荡行情,但趋势行情里会持续给出反向信号;单看 RSI 容易忽略趋势过滤,建议配合均线或波动率条件一起看。

通道突破

突破最近 N 根 bar 的高低点入场,属于动量类思路。它对参数(回看窗口)与前视偏差都敏感:突破位必须用「当时已经知道」的价格区间来计算,否则回测会异常好看而无法复现。

研究场景常见做法参数起点思路注意点
趋势跟随双均线交叉先看 10 / 20 一类短中期组合,再按标的波动调整震荡期回撤集中,需同时看回撤幅度与交易次数
震荡反转RSI 或通道内反向阈值与回看窗口都要与标的波动幅度匹配趋势行情里反向信号会连续出现
突破动量N 日高低点突破窗口从较短周期起步,避免信号过稀严格检查突破位是否用了未来信息
多指标确认趋势指标 + 震荡指标同向才交易先各自单独跑,再合并条件条件越多信号越少,容易变成过拟合
控制回撤加止损或按权益比例减仓止损阈值与标的日常波动相关止损过紧会被噪声触发,反而放大成本
降低交易频率加过滤条件或拉长持有期先用较长窗口做基线频率降低会让统计样本变少,需要更长区间
Pitfalls

Backtesting.py 策略里最常见的结构性错误有哪些?

这类问题的共同点是:代码能跑、不报错,结果还很好看,但结论是错的。它们大多不是语法问题,而是「你假设了自己在某一刻知道多少信息」这件事没对齐。

现象可能成因处理方式提前规避
回测很漂亮,实盘差很多前视偏差:用了当根 bar 收盘后的信息做决策明确成交时点假设,按下一根 bar 成交或统一用收盘成交口径写策略时先写下「这条信息我在哪一刻知道」
换一段区间结论就翻转参数是在同一段数据上挑出来的切出样本外区间复核,只把稳得住的结论留下来先划分区间,再动参数
收益明显偏高没有设置手续费与滑点,或按理想价格成交按真实费率与成交价假设重跑一遍做对照把成本当默认假设写进代码,而不是最后补
交易次数异常多把「状态」当成「事件」来判断改用交叉一类的事件判断,或加确认条件先看结果里的交易笔数是否与预期量级一致
某段区间完全没有交易指标预热期没值,或数据有缺口检查前 N 根 bar 是否有值、数据是否连续把数据质检放在策略之前
做空结果与预期相反仓位方向与平仓条件没配对用持仓状态明确判断方向,再决定开平多空混合策略先用单方向跑通再加另一边
一组参数扫出来全是同款结果搜索范围过窄,或约束把组合筛得只剩几个放大范围、放宽约束,先看单参数的敏感度曲线动手前先想清楚每个参数的合理数量级
排查顺序建议:先看成交时点与成本假设,再看参数是否样本内挑的,最后才怀疑指标实现。多数「策略失效」其实发生在前面两步。
Trust Check

优化完,怎么判断这组参数能不能信

Backtesting.py 里搜索结束只代表你拿到了一批候选,不代表其中任何一个可以写进结论。下面六项是判断参数可信度时最实际的检查点,顺序也建议照这个来。

看什么怎么判断常见误读下一步
邻域稳定性看热力图上高值区域是否成片把孤立亮点当成较优解取成片区域的中心参数,而不是尖峰
样本外表现在未参与搜索的区间重跑同一组参数把样本内指标当成预期收益把样本外结果作为主要依据,样本内只做参考
交易笔数看笔数是否足以支撑统计判断认为笔数多就一定稳健结合平均持有期一起看,笔数太少就延长区间
成本敏感性提高手续费与滑点假设后结果是否崩塌忽略摩擦成本对高频策略的影响用偏高的成本假设做压力测试
时间分布收益是否集中在少数几段行情里只看总收益,不看分布分段看每段表现,判断依赖什么行情
参数可解释性参数值能否用交易含义解释清楚把随机波动当成规律优先选能复述、能在别的区间站住的参数
边界说明:这些判断标准来自常见的回测实践,不是官方结论;具体实现的字段与口径以官方 README 与文档为准。任何一组参数的历史表现都不能推导出未来收益。
Best Practice

Backtesting.py 策略开发有哪些实践要点?

把前面几节压缩成一张可勾选的清单:每次改完策略,按这六项过一遍,能挡掉大部分「结果看起来不对但找不到原因」的情况。这些建议属于经验层面的检查项,不替代官方文档。

环节建议为什么注意点
数据质量确认 OHLCV 无缺漏、复权方式一致数据口径不同会让同一策略跑出不同结论跨数据源对比前先统一复权与时间区间
成本假设显式设置 commission,必要时加滑点成本是高频策略的主要侵蚀项不要用「先跑通再看成本」的顺序做结论
成交时点明确默认成交与 trade_on_close 的差别成交时点决定你有没有用到未来信息换成交口径后结论变化大,说明策略对执行很敏感
样本外验证参数搜索与验证用不同区间样本内挑参数几乎必然乐观样本外不要反复调参,否则它也会被用成样本内
风险指标同时看收益、回撤幅度、夏普与胜率单一指标容易被局部行情诱导指标口径以实际返回为准,跨框架比较前先统一
可复现性把数据区间、参数与成本一起记录半年后要能重跑出同样的结论只记「用了双均线」是不够的
合规:回测结果仅代表历史表现,不构成投资建议;是否可投入实盘还需结合合规、资金与风险承受能力自行判断。
Family test

五类常见策略在 A 股上跑一遍,Backtesting.py 的结果如何?

把双均线、RSI 反转、MACD、布林带反转、唐奇安突破写成同样结构的策略类,在同一批标的、同一区间、同一成本下各跑一遍,看的是「这一族规则有没有稳定优势」,而不是某一个策略的单点高值。

策略族平均收益平均超额(对买入持有)跑赢买入持有平均交易笔数平均胜率
SMA(10/20) 双均线+8.11%-223.54pp0 / 611834.3%
RSI(14) 30/70 反转+71.47%-157.45pp1 / 6972.9%
MACD(12/26/9) 金叉死叉+20.25%-210.87pp0 / 615934.7%
布林带(20,2) 反转-35.75%-267.40pp0 / 610040.4%
唐奇安突破 20/10+145.37%-84.01pp1 / 64435.8%
买入持有(基准)+232.15%(6 只均值)——0—
实测口径:6 只 A 股(贵州茅台 600519、平安银行 000001、招商银行 600036、比亚迪 002594、宁德时代 300750、赛力斯 601127)2018-01-02~2026-09-18 日线(前复权),初始资金 100000,A 股成本用 callable(佣金 2.5bp 双边 + 过户费 0.1bp 双边 + 卖出印花税 5bp),默认撮合 + exclusive_orders=True。读表要点:五个策略族没有一个稳定跑赢买入持有;「笔数多」不等于更差(唐奇安突破只有 44 笔、平均超额反而最接近 0),「胜率高」也不等于赚钱(RSI 平均胜率 72.9%,仍跑输 157.45pp)。数字只代表该区间该组标的,不构成投资建议。
Stops

给 Backtesting.py 策略加上止盈止损会更好吗?

止盈止损常被当成「风险控制」的默认动作,但它同时改动了两件事:被提前截断的盈利单,和被提前截断的亏损单。实测结果是——固定比例的止盈止损让两套策略都明显变差。

标的与版本原版双均线收益加 sl=5% / tp=10% 后同期买入持有说明
贵州茅台 600519(前复权 + A 股成本)-69.16%-87.40%+107.61%止损把趋势中的回撤兑现成实际亏损
比亚迪 002594(前复权 + A 股成本)-33.15%-100.00%+318.78%高波动标的被反复止损,资金耗尽
内置 GOOG 样例(2004-08-19~2013-03-01)+469.86%未实测+607.37%本站未在这份样例上测试止盈止损
周线双均线(贵州茅台)-46.05%未实测+107.61%放大周期改善了结果,但仍远低于基准
平安银行 000001(原版双均线)-72.09%未在本轮实测+11.27%止损版本未单独跑,不给出数字
招商银行 600036(原版双均线)-74.07%未在本轮实测+77.17%同上
class SmaWithStops(Strategy):
    n1, n2, sl, tp = 10, 20, 0.05, 0.10
    def init(self):
        self.a = self.I(SMA, self.data.Close, self.n1)
        self.b = self.I(SMA, self.data.Close, self.n2)
    def next(self):
        if crossover(self.a, self.b) and not self.position:
            price = self.data.Close[-1]
            self.buy(sl=price * (1 - self.sl), tp=price * (1 + self.tp))
        elif crossover(self.b, self.a):
            self.sell()
为什么不是「止盈止损没用」:本次测的是固定百分比的止损止盈挂在均线策略上,结论只覆盖这一种组合。更合理的用法是让止损与你的持有周期、波动率(如按 ATR 自适应)匹配,并把「止损后的再入场规则」一起定义清楚——否则你只是把一个连续持仓切成了许多更差的片段。要判断止损是否真的改善风险调整后收益,请用回测审计里的成本敏感性与样本外流程复核。
Timeframes

Backtesting.py 的多时间框架怎么落地?周线实测对照

多时间框架不需要多份数据:用 resample_apply 把日线聚合成周线,再把指标喂回策略即可。要注意官方注释提醒的聚合方向——默认的 label='left' 会产生前视偏差,必须显式指定 label='right'。

from backtesting.lib import resample_apply

class SmaWeekly(Strategy):
    n1, n2 = 10, 20
    def init(self):
        self.a = resample_apply('W-FRI', SMA, self.data.Close, self.n1)
        self.b = resample_apply('W-FRI', SMA, self.data.Close, self.n2)
    def next(self):
        if crossover(self.a, self.b):
            self.buy()
        elif crossover(self.b, self.a):
            self.sell()
口径贵州茅台收益同期买入持有交易笔数注意点
日线双均线 10/20-69.16%+107.61%123信号密集,被震荡反复打脸
周线双均线 10/20-46.05%+107.61%明显减少改善但仍大幅跑输;聚合方向必须 label='right'
周线双均线(比亚迪 002594)-68.88%+318.78%明显减少换标的结论同样成立:放大周期不改变跑输
日线双均线(比亚迪 002594,对照基线)-33.15%+318.78%124供与周线版本横向比较
周线双均线(平安银行 000001)未在本轮实测+11.27%明显减少未单独跑,标为待验证
周线双均线(招商银行 600036)未在本轮实测+77.17%明显减少未单独跑,标为待验证
标的(样本内 2018-01-02~2022-12-30 寻优)样本内收益最高的参数该参数样本内收益说明
贵州茅台 60051930 / 51+342.41%不同标的表现居前的窗口各不相同
平安银行 0000013 / 69+99.13%极短与极长组合同时出现
招商银行 6000363 / 15+39.32%同一网格上的另一端
比亚迪 0025946 / 51+1388.71%样本内收益极高,属典型的「拟合好看」
宁德时代 30075015 / 27+2579.99%样本内收益顶部,需样本外复核
赛力斯 60112718 / 75+134.09%与贵州茅台的组合完全不同
实测要点:①多时间框架不改变数据量,只改变指标的聚合粒度,实现成本很低;②周线让结果从 -69.16% 改善到 -46.05%,但改善幅度远小于「换成买入持有」;③同一套代码在 6 只标的上,样本内收益最高的参数各自不同(30/51、3/69、3/15、6/51、15/27、18/75),这正是参数看起来像「拟合出来」的直接证据。参数优化后务必走样本外流程,方法与判据见回测可信度审计页。
ML boundary

Backtesting.py 能接机器学习策略吗?外部模型与自定义指标的接入边界在哪

官方 README 写明它是「指标库无关(BYO)」,所以外部模型只要能算出一条与行情等长的序列,就能接进来。难点不在接口,而在「这条序列是不是只用了当时能看到的数据」。下表按「接入方式 / 怎么写 / 适用场景 / 注意点」整理。

接入方式怎么写适用场景注意点
外部模型的预测值当指标离线算好预测序列,整理成与行情等长、同索引的数组后用 self.I() 注册想验证「模型信号」在历史上到底赚不赚钱预测值必须只用当时及更早的数据算出;一次性用全历史训练再回填整段,等于把未来信息喂进回测
pandas-ta 指标self.I(ta.rsi, self.data.Close, 14),或先用 pandas-ta 算成列再传入想用 pandas 生态的技术指标,又不想装系统依赖它与 TA-Lib 的同名指标由各自库实现,口径不保证一致;换库前先用同一段数据对照一次
TA-Lib 指标self.I(talib.RSI, self.data.Close, 14)需要成熟且有文档的技术指标需单独安装且依赖系统库,装不上时先查系统依赖再看 Python 侧
pandas / NumPy 自算self.I(lambda c: c.rolling(20).mean(), self.data.Close)自定义指标、快速试验返回值必须与输入等长、索引对齐;长度算错往往到 run() 阶段才报错
在 next() 里实时训练按滚动窗口在 next() 内重新拟合模型想把训练过程也纳入回测逐 bar 重训练非常慢,而且极易写成「用当根收盘价训练、当根决策」;重训练建议放到离线流程里
特征工程用了未来数据用整段历史算均值 / 分位数做标准化任何涉及归一化的策略这是最常见也最隐蔽的前视偏差来源:实测用全序列 z-score 比扩展窗口好看 28.70pp,且全程不会报错
把模型概率直接当仓位预测涨跌后按概率比例全仓买入想把 ML 结果直接换成仓位概率不等于仓位;还要过成本这一关——本站实测成本从 0 到 20bp 会让同一策略从 -64.44% 变 -78.22%
样本外切分训练区间与回测区间必须分开,必要时滚动重切任何 ML 策略0.6.6 不提供自动 walk-forward,也没有内建未来函数检测;切分与滚动要自己写,并写进结论
边界:它提供的是「把一条序列当成指标」的接口,不提供特征工程、模型训练、超参搜索与 walk-forward 流程;这些都要你在回测之外完成,并把「训练用了哪段数据」写进结论。官方接口能力以官方文档为准,本站不做模型效果承诺。
FAQ

策略开发常见问题

怎么接入 talib 指标?

先在环境里装好 talib,再在 init() 中用 self.I(talib.RSI, self.data.Close, 14) 这类写法注册即可。talib 需要单独安装并依赖系统库,装不上时先排查系统依赖;支持的指标集与安装方式以官方 README 与文档为准。

optimize() 会不会过拟合?

会,而且网格越大越容易。参数是在同一段历史上挑出来的,样本内表现好并不奇怪。可行的做法是把搜索与验证分成不同区间,并在热力图上优先选成片区域而不是孤立高点;具体的优化参数取值以官方文档为准。

能同时回测多个标的吗?

Backtesting.py 以单标的回测为主,不支持多资产组合与分数股交易,这一点官方 README 有明确说明。多标的要分别回测,或改用支持组合的框架;跨框架比较前先统一成本与成交口径。

buy() 是按什么价格成交的?

默认按下一根 bar 的开盘价成交,这样避免用到当时还不知道的价格;设置 trade_on_close 可改为收盘价成交。两种口径的结果会有差异,写法与默认行为以官方 README 为准。

策略里能直接用 pandas 算指标吗?

能。Backtesting.py 里把计算结果交给 self.I() 注册即可,例如用滚动窗口写成一行 lambda 或单独的函数。要注意返回序列的长度与索引必须与数据对齐,否则在 run() 阶段才会报错;细节以官方文档为准。

优化出来的参数能直接拿去实盘吗?

不建议。参数优化只说明它在某段历史上表现较好,不代表未来有效;资金规模、成交时点与滑点都不同。参数命名与返回字段以官方文档为准。较稳妥的顺序是先做样本外复核,再用小规模验证,最终是否投入实盘需自行评估风险。

本页结论五个策略族在 6 只 A 股上平均都没跑赢买入持有;固定比例止盈止损让双均线更差;周线只把 -69.16% 改善到 -46.05%。
回测局限参数搜索网格有限,样本外只做一次切分(非 walk-forward);未做多标的稳健性检验。
风险提示回测只反映历史区间的表现,不代表未来收益,也不构成投资建议;换区间、换标的或换成本假设,结论都可能改变。

下一步:继续写策略还是先看数据?

策略写好后,准备自己的 OHLCV 数据接入回测,或了解完整功能特性;两页都会给出可以照着核对的步骤。