4. 策略编写基础:Strategy 类结构、next() 方法、买入卖出信号生成、订单类型

在 Backtrader 中,策略(Strategy)是回测引擎的核心。所有交易逻辑、信号判断、资金管理都封装在继承自 bt.Strategy 的类中。本章将系统讲解策略骨架、核心回调方法、信号生成逻辑以及三种基础订单类型,帮助你搭建第一个可运行的量化策略。

4.1 Strategy 类结构

一个标准的 Backtrader 策略类包含以下生命周期方法,按执行顺序排列:

方法 调用时机 典型用途
__init__() 策略初始化时(仅一次) 定义指标、保存参数、初始化变量
start() 回测开始前 日志、全局设置
next() 每个 bar 触发一次 核心逻辑:判断信号、下单
stop() 回测结束后 统计、最终输出
关键原则: 所有交易决策必须放在 next() 中。不要在 __init__() 里直接下单,因为此时数据尚未就绪。

下面是一个完整的策略骨架:

import backtrader as bt

class MyFirstStrategy(bt.Strategy):
    # 可调参数
    params = (
        ('ma_period', 20),
        ('rsi_period', 14),
    )

    def __init__(self):
        # 在初始化时计算指标
        self.sma = bt.indicators.SimpleMovingAverage(
            self.data.close, period=self.params.ma_period
        )
        self.rsi = bt.indicators.RelativeStrengthIndex(
            self.data.close, period=self.params.rsi_period
        )
        # 记录订单状态
        self.order = None

    def next(self):
        # 每次新 bar 到来时执行
        if self.order:  # 有未完成订单时跳过
            return

        if not self.position:  # 空仓
            if self.data.close[0] > self.sma[0] and self.rsi[0] < 30:
                self.buy()
        else:  # 持仓
            if self.data.close[0] < self.sma[0] or self.rsi[0] > 70:
                self.sell()

    def notify_order(self, order):
        # 订单状态变化时回调
        if order.status in [order.Completed, order.Canceled, order.Margin]:
            self.order = None

    def stop(self):
        print(f'最终资金: {self.broker.getvalue():.2f}')

4.2 next() 方法——策略的心脏

next() 是策略中最频繁执行的方法,每个 bar 都会触发一次。理解它的执行上下文至关重要:

  • 数据索引: self.data.close[0] 表示当前 bar 的收盘价,[-1] 表示前一个 bar,以此类推。
  • 位置判断: self.position 返回当前持仓量,self.position.size 获取具体股数。
  • 订单保护: 每次 next() 开始前检查 self.order 是否为空,避免重复下单。
  • 多数据支持: 可通过 self.datas[0]、self.datas[1] 访问不同品种。
性能提示: next() 中避免做耗时计算。指标计算应放在 __init__() 中,next() 只做逻辑判断和下单。

4.3 买入卖出信号生成

信号生成是策略的核心。常见模式包括:

信号类型 典型条件 示例代码
均线交叉 快线上穿慢线 self.fast_sma[0] > self.slow_sma[0] and self.fast_sma[-1] <= self.slow_sma[-1]
超买超卖 RSI 低于 30 买入,高于 70 卖出 self.rsi[0] < 30
价格突破 收盘价突破 N 日高点 self.data.close[0] > max(self.data.close[-20:])
组合条件 多个指标同时满足 cond1 and cond2 and cond3

信号生成的最佳实践:

  • 使用 布尔变量 命名条件,提高可读性:buy_signal = self.sma_cross > 0
  • 避免在 next() 中重复计算相同指标值
  • 使用 self.position 区分开仓和平仓逻辑
常见陷阱: 不要使用 self.data.close[0] > self.sma[0] 作为唯一条件,这会在每个 bar 都触发信号。必须加入 交叉检测 或 状态锁 来避免重复开仓。
策略信号生成与执行流程 数据流(Bar 序列) 指标计算(__init__) 条件判断(next 中) 买入信号 → self.buy() 卖出信号 → self.sell() 无信号 → 等待下一 bar 关键约束 • 每个 bar 只执行一次 • 检查订单状态避免重复 • 使用交叉检测防震荡

4.4 订单类型详解

Backtrader 支持三种基础订单类型,理解它们的差异是实盘模拟的关键:

订单类型 下单函数 成交条件 适用场景
市价单(Market) self.buy() / self.sell() 立即以当前 bar 的收盘价成交 流动性好、追求快速成交
限价单(Limit) self.buy(exectype=bt.Order.Limit, price=p) 价格达到或优于指定价格时成交 控制买入成本、挂单交易
止损单(Stop) self.sell(exectype=bt.Order.Stop, price=p) 价格触及止损价时触发市价单 风险控制、趋势跟踪
重要区别: 限价单保证价格不差于指定价,但不保证成交;止损单保证成交,但不保证成交价格(可能滑点)。市价单在回测中默认以 bar 收盘价成交,实盘中可能产生滑点。

订单参数扩展示例:

# 市价买入 100 股
self.buy(size=100)

# 限价买入:价格不高于 50.0
self.buy(exectype=bt.Order.Limit, price=50.0, size=200)

# 止损卖出:价格跌破 45.0 时触发
self.sell(exectype=bt.Order.Stop, price=45.0, size=100)

# 带止损的限价单(StopLimit):价格触及 48.0 后,以不低于 47.5 成交
self.buy(exectype=bt.Order.StopLimit, price=48.0, plimit=47.5)
三种基础订单类型对比 市价单 Market 立即以当前价成交 ✅ 优点:保证成交 ❌ 缺点:价格不可控 📌 回测:收盘价成交 ⚡ 滑点:需额外设置 限价单 Limit 达到指定价或更优时成交 ✅ 优点:价格可控 ❌ 缺点:可能不成交 📌 回测:精确模拟 ⚡ 适合:挂单策略 止损单 Stop 价格触及止损价后触发市价单 ✅ 优点:自动风控 ❌ 缺点:可能滑点 📌 回测:触发价成交 ⚡ 适合:趋势跟踪 选择建议 • 回测初期:使用市价单简化逻辑,快速验证策略方向 • 实盘模拟:切换为限价单 + 止损单,模拟真实交易环境 • 高级用法:组合使用 StopLimit 实现“止损限价”,兼顾价格与成交

4.5 订单状态管理与回调

订单提交后并非立即成交,Backtrader 通过 notify_order() 回调通知状态变化:

def notify_order(self, order):
    if order.status == order.Pending:      # 待处理
        pass
    elif order.status == order.Accepted:   # 已接受
        pass
    elif order.status == order.Completed:  # 已完成
        self.log(f'订单完成: {order.executed.price:.2f}')
        self.order = None
    elif order.status == order.Canceled:   # 已取消
        self.order = None
    elif order.status == order.Margin:     # 保证金不足
        self.order = None
    elif order.status == order.Rejected:   # 被拒绝
        self.order = None
最佳实践: 始终在 notify_order() 中将 self.order 置为 None,否则 next() 中的订单保护会永久阻塞策略。

4.6 完整策略示例:双均线交叉

将以上知识点整合为一个完整的双均线交叉策略:

class SmaCross(bt.Strategy):
    params = (('fast', 10), ('slow', 30))

    def __init__(self):
        self.fast_sma = bt.indicators.SMA(self.data.close, period=self.params.fast)
        self.slow_sma = bt.indicators.SMA(self.data.close, period=self.params.slow)
        self.crossover = bt.indicators.CrossOver(self.fast_sma, self.slow_sma)
        self.order = None

    def next(self):
        if self.order:
            return

        if not self.position:
            if self.crossover > 0:  # 金叉
                self.buy()
        else:
            if self.crossover < 0:  # 死叉
                self.sell()

    def notify_order(self, order):
        if order.status in [order.Completed, order.Canceled, order.Margin]:
            self.order = None

这个策略展示了:

  • 在 __init__() 中预计算指标
  • 使用 CrossOver 内置指标检测交叉,避免手动比较
  • 通过 self.order 防止重复下单
  • 在 notify_order() 中清理订单状态
本章小结: Strategy 类通过 __init__() 预计算指标、next() 执行交易逻辑、notify_order() 管理订单生命周期。信号生成应使用交叉检测或状态锁避免重复开仓。市价单、限价单、止损单分别适用于不同交易场景,理解它们的差异是构建稳健策略的基础。