分析者
无论是回溯测试还是交易,能够分析交易系统的性能对于理解是否不仅实现了利润,而且实现利润的风险是否太大,或者与参考资产(或无风险资产)相比是否真的值得付出努力至关重要
这就是Analyzer对象家族的用武之地:提供对发生了什么甚至实际发生了什么的分析。
分析仪的性质
该接口以行对象的接口建模,特征为next方法,但有一个主要区别:
-
Analyzers请勿持线。这意味着它们在内存方面并不昂贵,因为即使在分析了数千个价格条之后,它们也可能仅仅在内存中保存一个结果。
生态系统中的位置
Analyzer对象是通过cerebro实例添加到系统中的策略、观察者和数据:
addanalyzer(ancls, *args, **kwargs)
但是当在cerebro.run期间操作时,系统中存在的每个策略都会发生以下情况
-
cerebro.run期间ancls将被*args和**kwargs实例化 -
ancls实例将附加到策略
这意味着:
- 如果回溯测试运行包含例如3 个策略,则将创建
ancls的3 个实例,并且每个实例将附加到不同的策略。
底线:分析人员分析单个策略和的性能,而不是整个系统的性能
附加位置
一些Analyzer对象实际上可能使用其他分析仪来完成其工作。例如:SharpeRatio使用TimeReturn的输出进行计算。
这些子分析器或从属分析器也将插入到创建它们的策略中。但是用户完全看不见它们。
属性
为了执行预期的工作,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_Name` which resolves to `self.datas[X].Name` returning
the line by name rather than by index
对于第一个数据,最后两个快捷键不带初始X数字参考。例如:
* `self.data_2` refers to `self.datas[0].lines[2]`
和
* `self.data_close` refers to `self.datas[0].close`
返回分析
分析器基类创建self.rets(类型为collections.OrderedDict的)成员属性以返回分析。这是在方法create_analysis中完成的,如果创建自定义分析器,该方法可以被子类覆盖。
作案手法
尽管Analyzer对象不是行对象,因此不会在行上迭代,但它们被设计为遵循相同的操作模式。
-
系统启动前实例化(因此调用
__init__ -
用
start表示开始操作 -
在计算出指标工作的策略的最短期限后,将调用
prenext/nextstart/next。prenext和nextstart的默认行为是调用 next,因为分析仪可能从系统激活的第一刻就开始分析。通常在行对象中调用
len(self)来检查实际的条数。通过返回self.strategy的值,这也适用于Analyzers -
订单和交易将通过
notify_order和notify_trade通知给策略 -
现金和价值也将被通知,就像在
notify_cashvalue方法中使用策略一样 -
现金、价值和基金价值以及基金份额也将收到通知,就像在
notify_fund方法中使用策略一样 -
将调用
stop以发出操作结束的信号
一旦常规操作循环完成,分析仪具有提取/输出信息的附加方法
-
get_analysis:理想情况下(不强制)返回包含分析结果的类似dict的对象。 -
print使用标准backtrader.WriterFile(除非重写)写入get_analysis的分析结果。 -
pprint(pretty print)使用 Pythonpprint模块打印get_analysis结果。
最后:
-
get_analysis创建一个成员属性self.ret(类型为collections.OrderedDict),分析器将分析结果写入该属性。Analyzer的子类可以重写此方法来更改此行为
分析器模式
backtrader平台中分析仪对象的开发揭示了生成分析的两种不同使用模式:
-
在执行过程中,通过在
notify_xxx和next方法中收集信息,并在next中生成分析的当前信息例如,
TradeAnalyzer只使用notify_trade方法生成统计数据。 -
收集(或不收集)上述信息,但在
stop方法中一次生成分析SQN(系统质量编号)在notify_trade期间收集交易信息,但在stop期间生成统计信息
一个简单的例子
尽可能简单:
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/sample/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())
执行(已存储在analyzer-test.py中:
$ ./analyzer-test.py
Sharpe Ratio: {'sharperatio': 11.647332609673256}
没有绘图,因为SharpeRatio是计算结束时的单个值。
分析仪的法医学分析
让我们重复一下,Analyzers不是 Lines 对象,但是为了将它们无缝地集成到backtrader生态系统中,遵循了几个 Lines 对象的内部 API 约定(实际上是它们的混合体)
笔记
SharpeRatio的代码已经演变为考虑年度化,此处的版本仅作为参考。
请检查分析仪参考
另外还有一个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声明尽管未使用声明的对象(作为示例),分析器与
backtrader中的大多数其他对象一样支持参数 -
__init__方法就像
__init__中的策略声明指标一样,使用支持对象的分析器也是如此。在这种情况下,
SharpeRatio是使用年收益率计算的。该计算将自动进行,并可供SharpeRatio自行计算。笔记
SharpeRatio的实际实现使用了更通用、更晚开发的TimeReturn分析仪 -
next方法SharpeRatio不需要,但每次调用父策略next后都会调用此方法 -
start方法在回溯测试开始之前调用。可以用于额外的初始化任务。夏佩拉蒂奥不需要它
-
stop方法在回溯测试结束后立即调用。与
SharpeRatio一样,可用于完成/进行计算 -
get_analysis方法(返回字典)外部调用方对生成的分析的访问权限
返回:包含分析的字典。
参考
类 backtrader.Analyzer()
分析器基类。所有分析器都是这个分析器的子类
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]
这不是行对象,但方法和操作遵循相同的设计
-
__init__在实例化和初始设置期间 -
start/stop发出开始和结束操作的信号 -
prenext/nextstart/next方法家族,遵循对策略中相同方法的调用 -
notify_trade/notify_order/notify_cashvalue/notify_fund接收与策略等效方法相同的通知
操作模式为开放式,且无首选模式。因此,可以使用next调用、在stop期间的操作结束时生成分析,甚至可以使用notify_trade这样的单一方法生成分析
重要的是重写get_analysis以返回包含分析结果的类似dict 的对象(实际格式取决于实现)
开始()
调用以指示操作的开始,为分析器提供设置所需内容的时间
停止()
调用以指示操作的结束,为分析器提供关闭所需内容的时间
prenext()
在达到策略的最短期限之前,为策略的每次 prenext 调用调用调用
分析器的默认行为是调用next
nextstart()
在首次达到最短时间段时,为策略的下一次启动调用仅调用一次
下一个()
一旦达到策略的分钟周期,为策略的下一次调用调用调用
通知现金价值(现金、价值)
在每个下一个周期之前接收现金/价值通知
通知基金(现金、价值、基金价值、股份)
接收当前现金、价值、基金价值和基金份额
通知订单(订单)
在每个下一个周期之前接收订单通知
通知贸易部(贸易部)
在每个下一个周期之前接收交易通知
get_ 分析()
返回一个具有分析结果的类似dict 的对象
字典中分析结果的键和格式取决于实现。
甚至没有强制要求结果是一个类似dict 的对象,只是约定
默认实现返回默认create_analysis方法创建的默认 OrderedDictrets
创建 _ 分析()
意味着被子类覆盖。提供创建用于保存分析的结构的机会。
默认行为是创建一个名为rets的OrderedDict
打印(args,*kwargs)
通过标准Writerfile对象打印get_analysis返回的结果,该对象默认为将内容写入标准输出
pprint(args,*kwargs)
使用 pretty print Python 模块(pprint打印get_analysis返回的结果)
len()
支持通过实际返回分析仪操作策略的当前长度,在分析仪上调用len
