本文基于backtrader的官方文档,对Strategy类的内容进行梳理。
在backtrader中,Strategy类是用户制定回测策略的核心。
我们可以用Strategy的各个方法来表示它的整个生命周期:孕育期——出生——儿童期——成年期——繁殖期——死亡。
当实例化Strategy时,__init__方法将被调用。技术指标和一些其他的属性需要在这时候创建。例如:
def __init__(self):
self.sma = btind.SimpleMovingAverage(period=15)
上面的代码创建了一个15日简单移动均线的技术指标。
backtrader的核心cerebro会告诉strategy是时候开始行动了。这里默认会创建一个空的start方法。在出生阶段,strategy的创建可能被中断,并抛出StrategySkipError异常。
在孕育期声明的技术指标,有的需要经过某个周期长度才能得到有效数值,这个周期称为最小周期(minimum period)。
例如,在__init__方法中,我们创建了一个15日均线的指标,这个指标就需要经过15根K线才能计算出有效的数值,前14根K线无法得到15日均线的数值。backtrader将在访问到15根K线以后,才会调用prenext方法。prenext方法默认不进行任何操作。
当backtrader经过了minimum period(在上面的例子中,就是访问到第15根K线时),并且有足够的空间来存储将要计算得到的数据时,strategy就开始执行next来进行具体的买卖操作。
这里实际上调用且仅调用一次nextstart方法,来作为从prenext转到next标志。在nextstart的默认实现中,仅仅是调用了一次next。
strategy是不会做复制的。但是当我们使用不同的参数来优化策略时,backtrader就会多次实例化strategy,在这种情况下,也可以看作是对strategy进行了复制。
backtrader告知strategy是时候重置把所有事情安排妥当了。backtrader提供一个默认为空的stop方法。
通常情况下,我们会像下面这样使用Strategy:
class MyStrategy(bt.Strategy):
def __init__(self):
self.sma = btind.SimpleMovingAverage(period=15)
def next(self):
if self.sma > self.data.close:
# Do something
pass
elif self.sma < self.data.close:
# Do something else
pass
在上面的代码中:
在__init__中,将一个技术指标赋值给一个属性
没有重写默认为空的start方法
没有重写prenext和nextstart方法
在next方法中,我们使用技术指标来和收盘价做比较,可以根据比较结果来进行后续操作
没有重写默认为空的stop方法
像现实生活中的交易者会收到订单成交通知一样,当有交易订单状态更新等事件发生时,Strategy也会收到通知,并且在每个next周期内都会被通知一次,这些通知包括:
通过notify_order(order)发出订单状态变化的通知
通过notify_trade(trade)发出交易开始/更新/关闭的通知
通过notify_cashvalue(cash, value)发出代理手中当前现金和资产的通知
通过notify_fund(cash, value, fundvalue, shares)发出代理手中当前现金和资产,以及正在交易的资金和股票的通知
通过notify_store(msg, *args, **kwargs)发出的事件通知(需要单独实现)
在next方法中,Strategy可以通过以下操作来尝试盈利:
使用buy方法,来做多或者减少/关闭空头交易
使用sell方法,来做空或者减少/关闭多头交易
使用close方法,来关闭一个现有的交易
使用cancel方法,来取消一个尚未执行的订单
当调用buy和sell方法后,都会生成订单,并且返回一个order(或者order子类)的实例,每个实例都包含有一个唯一的ref标识符,用于区分不同的order。
为了便于相互交流学习,新建了微信群,感兴趣的读者请加微信。