分析者
无论是回溯测试还是交易,能够分析交易系统的性能对于理解是否不仅实现了利润,而且实现利润的风险是否太大,或者与参考资产(或无风险资产)相比是否真的值得付出努力至关重要
这就是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