今天这篇是backtrader文档的学习笔记。主要介绍了观察者。
官方文档链接:https://www.backtrader.com/docu/observers-and-statistics/observers-and-statistics/
backtrader内部运行的策略主要处理数据源和指标。
数据源被添加到Cerebro实例中,并最终成为策略输入的一部分(解析并作为实例的属性),而指标则由策略本身声明和管理。
到目前为止,backtrader所有的示例图表都绘制了 3 个图表,这似乎是理所当然的,因为它们没有在任何地方被声明:
【没翻译,感觉英文原文更好懂】
Cash and Value (what’s happening with the money in the broker)
Trades (aka Operations)
Buy/Sell Orders
它们都是Observers
对象并且存在于backtrader.observers
子模块中。它们之所以存在,是因为Cerebro
有一个参数支持自动把它们添加(或不添加)到策略中:
stdstats
(默认值:True)
如果遵循默认设置,Cerebro会执行以下等效的用户代码:
1 2 3 4 5 6 7 8 9 import backtrader as bt... cerebro = bt.Cerebro() cerebro.addobserver(bt.observers.Broker) cerebro.addobserver(bt.observers.Trades) cerebro.addobserver(bt.observers.BuySell)
让我们看看这 3 个默认观察者的常规图表(即使没有发出订单,没有发生交易,并且现金和投资组合价值没有变化)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from __future__ import (absolute_import, division, print_function, unicode_literals) import backtrader as btimport backtrader.feeds as btfeedsif __name__ == '__main__' : cerebro = bt.Cerebro(stdstats=False ) cerebro.addstrategy(bt.Strategy) data = bt.feeds.BacktraderCSVData(dataname='../../datas/2006-day-001.txt' ) cerebro.adddata(data) cerebro.run() cerebro.plot()
现在让我们在创建Cerebro
实例时更改stdstats
的值为False
(也可以在调用run
时完成):
1 cerebro = bt.Cerebro(stdstats=False )
现在图表不同了。
访问观察者
上面看到的观察者在默认情况下已经存在,并收集可用于统计目的的信息,这就是为什么可以通过访问策略的一个属性来访问观察者:
它只是一个占位符。如果我们还记得上面例子中添加的默认观察者之一:
1 2 3 ... cerebro.addobserver(backtrader.observers.Broker) ...
显而易见的问题是如何访问Broker
观察者。例如,这是如何通过策略中的next方法访问:
1 2 3 4 5 6 7 8 class MyStrategy (bt.Strategy): def next (self ): if self .stats.broker.value[0 ] < 1000.0 : print ('WHITE FLAG ... I LOST TOO MUCH' ) elif self .stats.broker.value[0 ] > 10000000.0 : print ('TIME FOR THE VIRGIN ISLANDS ....!!!' )
Broker
观察者就像数据、指标一样,策略本身也是一个Lines
对象。在本例中,Broker
有 2 行:
观察者的实现
其实现与指标的实现非常相似:
1 2 3 4 5 6 7 8 9 class Broker (Observer ): alias = ('CashValue' ,) lines = ('cash' , 'value' ) plotinfo = dict (plot=True , subplot=True ) def next (self ): self .lines.cash[0 ] = self ._owner.broker.getcash() self .lines.value[0 ] = value = self ._owner.broker.getvalue()
步骤:
继承Observer
类(而不是继承Indicator
类)
根据需要声明线对象和参数(Broker
有 2 个线对象但没有参数)
会有一个自动属性_owner
,它是持有观察者的策略
观察员在什么时候开始观察:
所有指标计算完毕后
执行策略next方法后
这意味着:在周期结束时……观察者观察发生了什么
在Broker
观察者例子中,它只是盲目地记录经纪人在每个时间点的现金和投资组合价值。
将观察者添加到策略中
正如上面已经指出的,Cerebro
通过stdstats
参数来决定是否添加 3 个默认观察者,从而减轻最终用户的工作量。
我们还可以添加其他观察者。
让我们采用一个简单的单均线策略,即当close
价格高于SimpleMovingAverage
时买入,否则卖出。
加上一个“补充”说明:
DrawDown
是backtrader生态系统中已经存在的观察者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 from __future__ import (absolute_import, division, print_function, unicode_literals) import argparseimport datetimeimport os.pathimport timeimport sysimport backtrader as btimport backtrader.feeds as btfeedsimport backtrader.indicators as btindclass MyStrategy (bt.Strategy): params = (('smaperiod' , 15 ),) def log (self, txt, dt=None ): ''' Logging function fot this strategy''' dt = dt or self .data.datetime[0 ] if isinstance (dt, float ): dt = bt.num2date(dt) print ('%s, %s' % (dt.isoformat(), txt)) def __init__ (self ): sma = btind.SMA(period=self .p.smaperiod) self .buysell = btind.CrossOver(self .data.close, sma, plot=True ) self .order = None def next (self ): self .log('DrawDown: %.2f' % self .stats.drawdown.drawdown[-1 ]) self .log('MaxDrawDown: %.2f' % self .stats.drawdown.maxdrawdown[-1 ]) if self .position: if self .buysell < 0 : self .log('SELL CREATE, %.2f' % self .data.close[0 ]) self .sell() elif self .buysell > 0 : self .log('BUY CREATE, %.2f' % self .data.close[0 ]) self .buy() def runstrat (): cerebro = bt.Cerebro() data = bt.feeds.BacktraderCSVData(dataname='../../datas/2006-day-001.txt' ) cerebro.adddata(data) cerebro.addobserver(bt.observers.DrawDown) cerebro.addstrategy(MyStrategy) cerebro.run() cerebro.plot() if __name__ == '__main__' : runstrat()
视觉输出显示了回撤的变化
以及部分文本输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ... 2006-12 -14 T23:59:59+00 :00,最大回撤:2.62 2006-12 -15 T23:59:59+00 :00,回撤:0.22 2006-12 -15 T23:59:59+00 :00,最大回撤:2.62 2006-12 -18 T23:59:59+00 :00,回撤:0.00 2006-12 -18 T23:59:59+00 :00,最大回撤:2.62 2006-12 -19 T23:59:59+00 :00,回撤:0.00 2006-12 -19 T23:59:59+00 :00,最大回撤:2.62 2006-12 -20 T23:59:59+00 :00,回撤:0.10 2006-12 -20 T23:59:59+00 :00,最大回撤:2.62 2006-12 -21 T23:59:59+00 :00,回撤:0.39 2006-12 -21 T23:59:59+00 :00,最大回撤:2.62 2006-12 -22 T23:59:59+00 :00,回撤:0.21 2006-12 -22 T23:59:59+00 :00,最大回撤:2.62 2006-12 -27 T23:59:59+00 :00,回撤:0.28 2006-12 -27 T23:59:59+00 :00,最大回撤:2.62 2006-12 -28 T23:59:59+00 :00,回撤:0.65 2006-12 -28 T23:59:59+00 :00,最大回撤:2.62 2006-12 -29 T23:59:59+00 :00,回撤:0.06 2006-12 -29 T23:59:59+00 :00,最大回撤:2.62
注意:从文本输出和代码中可以看出,DrawDown观察者实际上有 2 个线对象:
图中没有绘制maxdrawdown
线,但是用户还是可以在程序中使用它
实际上,maxdrawdown
的最后一个值也可以直接在名称为maxdd
的属性(不是线对象)中获取
这一篇就到这里啦。欢迎大家点赞、转发、私信。还没有关注我的朋友可以关注 江达小记