Backtrader 文档学习- Analyzers
1.概述
无论是回测还是交易,分析交易系统表现的关键,不仅是否仅获得利润,还是以高风险获得利润,或者与参考资产(或无风险资产)相比是否更值得努力。
这就是Analyzer对象系列的作用:提供对发生的情况或实际发生的情况分析。
2.分析器的特点
接口的模型是基于Lines对象的,例如具有next方法,但有一个主要区别:
- Analyzers 不包括Line。
意味着它们在内存方面开销不大,因为即使在分析了数千个价格bar之后,它们在内存中只保留一个结果。
3.系统中的定位
Analyzer分析器对象(如策略、观察者和数据)通过cerebro实例添加到系统中:
addanalyzer(ancls, *args, **kwargs)
但是,当在cerebro.run 期间进行操作时,对于系统中存在的每个策略,将发生以下情况:
- 在cerebro.run 期间使用参数*args 和**kwargs 实例化ancls
- ancls 实例将结合到策略
意味着:
- 例如:如果回测运行包含3个策略,则将创建ancls 的3个实例,并且每个实例都将附加到不同的策略。策略与分析器一一对应。
底线原则:分析器分析单个策略的表现,而不是整个系统的表现
4.附属分析器
某些Analyzer 对象实际上可能使用其他分析器来完成其工作。例如: SharpeRatio 使用TimeReturn 的输出进行计算。
这些sub-analyzers子分析器或slave-analyzers从属分析器也将加入到与创建它们的分析器相同的策略中。但它们对用户完全不可见。
5.属性
为了执行预期的工作, Analyzer 对象提供了默认属性,这些属性会自动传递并在实例设置易于使用:
- self.strategy :对analyzer对象在其中运行的策略子类的引用。分析器也可以访问策略可以访问的任何内容 。
- self.datas[x] :策略中所使用数据源数组。虽然可以通过策略引用访问它,但是快捷方式使工作更加舒适。
- self.data :指向self.datas[0] 的快捷方式,更加便捷。
- self.dataX :快捷方式到不同的self.datas[x]
还提供了一些其他别名,尽管它们可能过度简化:
`self.dataX_Y` where X is a reference to `self.datas[X]` and `Y`
refers to the line, finally pointing to: `self.datas[X].lines[Y]`
等价于:
self.dataX_Y = self.datas[X].lines[Y]
如果Lines具有名称,则还可用以下内容:
`self.dataX_Name` which resolves to `self.datas[X].Name` returning
the line by name rather than by index
等价于:
self.dataX_Name = self.datas[X].Name
对于第一个数据,前两个快捷方式可用,而不需要初始的X 数字引用。例如:
`self.data_2` refers to `self.datas[0].lines[2]`
等价于:
self.data_2 = self.datas[0].lines[2]
`self.data_close` refers to `self.datas[0].close`
等价于:
self.data_close = self.datas[0].close
6.返回分析
Analyzer 基类创建了一个self.rets (类型为collections.OrderedDict )成员属性,做返回分析的返回值。在方法create_analysis 中完成的,如果创建自定义分析器,则可以由子类覆盖。
7.操作模块
虽然Analyzer 对象不是Lines 对象,因此不会迭代Lines,但被设计为遵循相同的操作模式。(与处理Lines一样的操作模式)
- 1.在系统启动之前实例化(因此调用__init__ )
- 2.从start() 发出操作开始的信号
- 3.在指标工作的最小周期之后,将调用prenext/ nextstart/ next 。
prenext 和nextstart 的默认行为是触发next,因为分析器从系统存活的第一刻开始分析。
在Lines 对象中调用len(self) 以检查实际的bar数。也适用于Analyzers ,通过返回self.strategy 的值来实现。 - 4.通过notify_order 和notify_trade 通知订单和交易,就像策略一样。
- 5.现金和价值也将像策略一样通过notify_cashvalue 方法通知
- 6.现金、价值和基金价值和基金份额也将被通知,通过notify_fund方法使用策略一样
- 7.stop 将被调用以表示操作的结束
完成常规操作周期后,分析器的附加方法提取/输出信息 。
- get_analysis :理想情况下(不强制),返回包含分析结果的dict 类似对象。
- print 使用标准的backtrader.WriterFile (除非被重写),从get_analysis 中写入分析结果。
- pprint (美化打印)使用Python pprint 模块打印get_analysis 结果。
最后:
- get_analysis 创建一个成员属性self.ret (类型为collections.OrderedDict ),分析器将分析结果写入其中。
Analyzer的子类可以重写此方法以更改此行为 。
8.分析模式
在backtrader 平台上Analyzer对象有2种不同分析模式:
- 1.通过在notify_xxx 和next 方法中收集信息,在next 中执行分析的当前信息
例如, TradeAnalyzer 仅使用notify_trade 方法生成统计信息。 - 2.收集(或不收集)上述信息,最后在stop 方法中一次性生成分析
SQN (系统质量数字)在notify_trade 期间收集交易信息,但在stop 方法中生成统计信息。
9.简单示例
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import datetime
import backtrader as bt
import backtrader.analyzers as btanalyzers
import backtrader.feeds as btfeeds
import backtrader.strategies as btstrats
cerebro = bt.Cerebro()
# data
dataname = './datas/2005-2006-day-001.txt'
data = btfeeds.BacktraderCSVData(dataname=dataname)
cerebro.adddata(data)
# strategy
cerebro.addstrategy(btstrats.SMA_CrossOver)
# Analyzer
cerebro.addanalyzer(btanalyzers.SharpeRatio, _name='mysharpe')
thestrats = cerebro.run()
thestrat = thestrats[0]
print('Sharpe Ratio:', thestrat.analyzers.mysharpe.get_analysis())
计算夏普比率,执行结果:
Sharpe Ratio: OrderedDict([('sharperatio', 11.647332609673251)])
没有绘图,因为夏普比率在计算结束时是一个的值。
10.分析器的详解
让我们重复说明一下Analyzers 不是Lines对象,但为了无缝地将它们集成到backtrader 生态系统中,遵循几个Lines对象的内部API约定(实际上是它们融合的)
注意: SharpeRatio 的代码已经发展到考虑年化收益等因素,本版本只是一个参考计算。
请检查:doc: …/analyzers-reference
此外,还有一个SharpeRatio_A ,它以年化形式直接提供值,而不考虑所查询的时间范围
作为基础的SharpeRatio 的代码(简化版本)
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import operator
from backtrader.utils.py3 import map
from backtrader import Analyzer, TimeFrame
from backtrader.mathsupport import average, standarddev
from backtrader.analyzers import AnnualReturn
class SharpeRatio(Analyzer):
params = (('timeframe', TimeFrame.Years), ('riskfreerate', 0.01),)
def __init__(self):
super(SharpeRatio, self).__init__()
self.anret = AnnualReturn()
def start(self):
# Not needed ... but could be used
pass
def next(self):
# Not needed ... but could be used
pass
def stop(self):
retfree = [self.p.riskfreerate] * len(self.anret.rets)
retavg = average(list(map(operator.sub, self.anret.rets, retfree)))
retdev = standarddev(self.anret.rets)
self.ratio = retavg / retdev
def get_analysis(self):
return dict(sharperatio=self.ratio)
代码可以分解为:
-
params 声明,虽然声明的参数没有使用(作为示例),但是Analyzers像backtrader 中的大多数其他对象一样支持参数
-
init 方法,就像Strategies在__init__ 中声明Indicators一样,分析器也使用支持对象。
在这种情况下:使用年度回报率计算SharpeRatio 。计算将是自动的,并且将可用于SharpeRatio 自行进行计算。
注意:实际的SharpeRatio 实现使用更通用和后开发的TimeReturn 分析器 -
next 方法,SharpeRatio 不需要它,但是在每次调用父策略next 后将调用此方法
-
start 方法,在回测开始之前调用。可用于额外的初始化任务。Sharperatio不需要它
-
stop 方法,在回测结束后立即调用。像SharpeRatio效果,可以用来完成/进行计算
-
get_analysis 方法,返回一个dict类型,外部调用方对生成的分析的访问, 返回:带有分析结果的字典。
11.参考
class backtrader.Analyzer()
分析器基类。所有分析器都是分析器的子类
分析器实例在策略的框架中运行,并为该策略提供分析。
属性:
-
self.strategy (提供可访问的策略,策略中任何可访问的内容)
-
self.datas[x] 系统加载的数据源,当前策略所使用的数据源
-
self.data 系统加载的默认数据源,self.datas[0]
-
self.dataX -> self.datas[X]
-
self.dataX_Y -> self.datas[X].lines[Y]
-
self.dataX_name -> self.datas[X].name
-
self.data_name -> self.datas[0].name
-
self.data_Y -> self.datas[0].lines[Y]
虽然分析器不是一个Lines对象,但是方法和操作遵循相同的设计 -
init 在实例化初始化阶段设置
-
start / stop 发出开始和结束的信号
-
prenext / nextstart / next 遵循对策略中相同方法的调用的一系列方法
-
notify_trade / notify_order / notify_cashvalue / notify_fund 以上方法收到通知与该策略的等效方法相同
操作模式是开放的,没有首选模式。因此可以在next调用时,stop操作结束时生成分析,甚至可以使用和策略一样的notify_trade方法
重要的是重写get_analysis方法,返回包含分析结果的类似dict的对象(实际格式取决于实际重新的内容)
上述与Strategy一样的方法就不赘述。
方法:
-
get_analysis()
返回一个类似dict的对象和分析结果
字典中分析结果的关键字和格式取决于重写程序。
甚至没有强制要求结果是类似dict的对象,只是习惯约定
默认实现返回由默认create_analysis方法创建的orderedDict rets返回值。 -
create_analysis()
被子类重写,提供了创建保存分析架构信息。
默认行为是创建一个OrderedDict类型的rets变量。 -
print(*args, **kwargs)
通过标准Writerfile对象打印get_analysis返回的结果,默认情况下将内容写入标准输出 -
pprint(*args, **kwargs)
使用美化打印Python模块(pprint)打印get_analysis返回的结果 -
len()
通过实际返回分析器操作的策略的当前长度,支持调用分析器上的len 。