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 都触发信号。必须加入 交叉检测 或 状态锁 来避免重复开仓。
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)
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() 管理订单生命周期。信号生成应使用交叉检测或状态锁避免重复开仓。市价单、限价单、止损单分别适用于不同交易场景,理解它们的差异是构建稳健策略的基础。