无论是回溯测试还是交易,能够分析交易系统的性能是了解是否不仅获得了利润,而且是否在风险太大的情况下实现了利润,或者与参考资产(或无风险资产)相比,是否真的值得付出努力的关键。
这就是对象家族的用武之Analyzer
地:提供对所发生事件甚至实际发生的事情的分析。
analyzers的性质
该接口是根据 Lines 对象(例如next
方法)的接口建模的,但有一个主要区别:
-
Analyzers
不要握住 lines。这意味着它们在内存方面并不昂贵,因为即使在分析了数千个价格柱之后,它们仍然可能只是在内存中保存单个结果。
在生态系统中的位置
Analyzer
对象(如策略、 observers 和数据)通过 cerebro
实例添加到系统中:
addanalyzer(ancls, *args, **kwargs)
但是,当涉及到操作期间cerebro.run
,系统中存在的每个策略将发生以下情况
-
ancls
将在*args
**kwargs
cerebro.run
-
实例
ancls
将附加到策略
这意味着:
- 例如,如果回溯测试运行包含 3 个策略,则将创建 3 个实例,每个实例
ancls
将附加到不同的策略。
结论:分析器分析单个策略的性能,而不是整个系统的性能
其他位置
某些Analyzer
对象实际上可能使用其他 analyzers 来完成其工作。例如: SharpeRatio
使用的 TimeReturn
输出进行计算。
这些子analyzers 或从analyzers 也将插入到与创建它们的策略相同的策略中。但它们对用户来说是完全不可见的。
属性
为了运行预期的工作,Analyzer
对象提供了一些缺省属性,这些属性会自动传递并在实例中设置,以便于使用:
-
self.strategy
:引用分析器对象在其中操作的策略子类。策略可访问的任何内容也可以由分析器访问 -
self.datas[x]
:策略中存在的 data feeds 数组。尽管可以通过策略参考访问这一点,但快捷方式使工作更加舒适。 -
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]`
如果 line 具有名称,则以下选项也可用:
* `self.dataX_Name` which resolves to `self.datas[X].Name` returning the line by name rather than by index
对于第一个数据, last 两个快捷方式在没有初始X
数字参考的情况下可用。例如:
* `self.data_2` refers to `self.datas[0].lines[2]`
和
* `self.data_close` refers to `self.datas[0].close`
返回分析
Analyzer 基类创建一个self.rets
(类型collections.OrderedDict
为 )成员属性以返回分析。这是在方法中完成的,如果创建自定义analyzers,则该方法create_analysis
可由子类覆盖。
做法
虽然Analyzer
对象不是 Lines 对象,因此不会反复运算 lines,但它们被设计为遵循相同的操作模式。
-
在系统启动之前实例化(因此调用
__init__
) -
发出开始操作的信号
start
-
prenext
/nextstart
/next
将在指针工作的策略的计算最短周期之后被调用。的缺省
nextstart
行为prenext
是调用 next,因为分析器可能从系统处于活动状态的第一刻起就开始分析。习惯上调用
len(self)
Lines对象来检查实际的柱数。这也通过返回值来工作Analyzers
self.strategy
-
订单和交易将像
notify_order
通过和notify_trade
-
现金和价值也将被通知,就像策略完成的方法一
notify_cashvalue
样 -
现金,价值和基金价值以及基金份额也将被通知,就像使用方法的策略
notify_fund
一样 -
stop
将被调用以指示操作结束
一旦常规操作周期完成, analyzers 具有提取/输出信息的其他方法
-
get_analysis
:理想情况下(不强制)返回包含dict
分析结果的类似对象。 -
print
使用标准backtrader.WriterFile
(除非被覆盖)从get_analysis
写入分析结果。 -
pprint
(漂亮的打印)使用Pythonpprint
模块来打印get_analysis
resutls。
最后:
-
get_analysis
创建一个成员属性self.ret
(类型collections.OrderedDict
), analyzers 向该属性写入分析结果。Analyzer 的子类可以重写此方法以更改此行为
分析器模式
平台中 Analyzer 对象的backtrader
开发揭示了生成分析的 2 种不同使用模式:
-
在运行过程中,通过在 和
next
方法中notify_xxx
收集信息,并在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
代码已经发展到例如考虑年化,这里的版本应该只是一个参考。
请查看 Analyzers 参考
另外还有一个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__
方法就像策略声明指针一
__init__
样,支持对象 analyzers 也是如此。在这种情况下:使用
SharpeRatio
年度申报表计算。计算将是自动的,并SharpeRatio
可用于自己的计算。注意
实际实现
SharpeRatio
使用更通用和后来开发的TimeReturn
分析仪 -
next
方法SharpeRatio
不需要它,但此方法将在每次调用父策略后调用next
-
start
方法在回溯测试开始之前调用。可用于额外的初始化任务。夏普不需要它
-
stop
方法在回溯测试结束后立即调用。像
SharpeRatio
这样,它可以用来完成/进行计算 -
get_analysis
方法(返回字典)外部调用者可以访问生成的分析
返回:带有分析的字典。
参考
backtrader类 .分析仪()
分析器基类。所有 analyzers 都是这个的子类
Analyzer 实例在策略的框架中运行,并为该策略提供分析。
自动设置成员属性:
-
self.strategy
(允许访问策略以及从中访问的任何内容) -
self.datas[x]
允许访问系统中存在的 data feeds 数字,也可以通过策略参考访问 -
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
接收与策略的等效方法相同的通知
操作模式 open ,不首选任何模式。因此,分析可以通过next
调用生成,在操作期间 stop
甚至使用单个方法,例如 notify_trade
重要的是重写get_analysis
以返回包含分析结果的类似字典的对象(实际格式取决于实现)
开始()
调用以指示操作的开始,使分析器有时间设置所需的内容
停止()
调用以指示操作结束,使分析器有时间关闭所需的内容
prenext()
针对策略的每次前置点调用调用,直到达到策略的最小周期
分析器的缺省行为是调用next
nextstart()
在策略的下一个启动调用时仅调用一次,此时首次达到最小周期
下一步()
在策略的每次下一次调用时调用,一旦达到策略的最小值
notify_cashvalue(现金,价值)
在每个下一个周期之前收到现金/价值通知
notify_fund(现金、价值、基金价值、股份)
接收当前现金、价值、基金价值和基金份额
notify_order(订货)
在每个下一个周期之前接收订单通知
notify_trade(贸易)
在每个下一个周期之前接收交易通知
get_analysis()
返回类似 dict 的对象以及分析结果
字典中分析结果的键和格式取决于实现。
甚至没有强制要求结果是一个类似字典的对象,只是约定
默认实现返回由缺省方法创建的缺省 create_analysis
OrderedDictrets
create_analysis()
意味着被子类覆盖。提供创建保存分析的结构的机会。
默认行为是创建一个OrderedDict
named rets
print(*args, **kwargs)
打印通过标准Writerfile
对象返回get_analysis
的结果,该对象缺省将内容写入标准输出
pprint(*args, **kwargs)
打印使用漂亮的打印 Python 模块 (pprint) 传回get_analysis
的结果
伦()
len
通过实际返回分析器操作的策略的当前长度来支持调用analyzers