无论是回测还是交易,分析交易系统表现的关键,不仅是否仅获得利润,还是以高风险获得利润,或者与参考资产(或无风险资产)相比是否更值得努力。
这就是Analyzer对象系列的作用:提供对发生的情况或实际发生的情况分析。
接口的模型是基于Lines对象的,例如具有next方法,但有一个主要区别:
Analyzer分析器对象(如策略、观察者和数据)通过cerebro实例添加到系统中:
addanalyzer(ancls, *args, **kwargs)
但是,当在cerebro.run 期间进行操作时,对于系统中存在的每个策略,将发生以下情况:
意味着:
底线原则:分析器分析单个策略的表现,而不是整个系统的表现
某些Analyzer 对象实际上可能使用其他分析器来完成其工作。例如: SharpeRatio 使用TimeReturn 的输出进行计算。
这些sub-analyzers子分析器或slave-analyzers从属分析器也将加入到与创建它们的分析器相同的策略中。但它们对用户完全不可见。
为了执行预期的工作, Analyzer 对象提供了默认属性,这些属性会自动传递并在实例设置易于使用:
`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
Analyzer 基类创建了一个self.rets (类型为collections.OrderedDict )成员属性,做返回分析的返回值。在方法create_analysis 中完成的,如果创建自定义分析器,则可以由子类覆盖。
虽然Analyzer 对象不是Lines 对象,因此不会迭代Lines,但被设计为遵循相同的操作模式。(与处理Lines一样的操作模式)
完成常规操作周期后,分析器的附加方法提取/输出信息 。
最后:
在backtrader 平台上Analyzer对象有2种不同分析模式:
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)])
没有绘图,因为夏普比率在计算结束时是一个的值。
让我们重复说明一下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类型,外部调用方对生成的分析的访问, 返回:带有分析结果的字典。
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 。