分析器#
无论是回测还是交易,能够分析交易系统的表现是理解是否仅获得了利润的关键,还是是否以过高的风险实现了利润,或者与参考资产(或无风险资产)相比是否真的值得付出努力。
这就是 Analyzer
对象系列发挥作用的地方:提供已经发生甚至正在发生的分析。
分析器的特点#
该接口模仿了 Lines 对象的接口,例如具有 next
方法,但有一个主要区别:
Analyzers
不持有行。这意味着它们在内存方面不会很昂贵,因为即使分析了成千上万个价格柱,它们仍然可以只在内存中存储单个结果。
生态系统中的位置#
通过 cerebro
实例将 Analyzer
对象(与 策略、* 观察者 和*数据 一样)添加到系统中:
addanalyzer(ancls, *args, **kwargs)
但是在cerebro.run
操作期间,对于系统中的每个 * strategy*,将发生以下情况:
- strategy 中的 ancls
将在 cerebro.run
期间使用 *args
和 **kwargs
实例化
- ancls
实例将被附加到 strategy
这意味着:
- 如果回测运行包含例如 3 个策略 ,那么将创建 3 个 ``ancls`` 实例 ,并且每个实例将附加到不同的策略。
最重要的是: 分析器可以分析单个策略的性能 ,而不是整个系统的性能。
附加位置#
有些 Analyzer
对象实际上可能使用其他分析器来完成其工作。例如: SharpeRatio
使用 TimeReturn
的输出进行计算。
这些 子分析器 或*从属分析器*也将插入到与创建它们的策略相同的策略中。但是它们对用户来说是完全看不见的。
属性 ==========为了执行所需的工作,“Analyzer”对象提供了一些默认属性,这些属性会自动传递并在实例中设置,以便于使用:
self.strategy
:指向策略子类的引用,分析器对象正在其上操作。任何可由 strategy 访问的内容也可以由 analyzer 访问。
self.datas[x]
:策略中存在的数据源数组。尽管可以通过 strategy 引用访问,但这个快捷方式使工作更加舒适。
self.data
:对self.datas[0]
的快捷方式,提供额外的便利。
self.dataX
:对不同的self.datas[x]
的快捷方式。还有一些别名可用,尽管它们可能有些多余:
self.dataX_Y
,其中X是对self.datas[X]
的引用,而Y是对隐射到self.datas[X].lines[Y]
的行的引用。如果行有一个名称,还可以使用以下方式:
self.dataX_Name
解析为self.datas[X].Name
,返回按名称而不是按索引查询的行。对于第一个数据,可以在没有初始的“X”数字引用的情况下使用最后两个快捷方式。例如:- “self.data_2” 指的是 “self.datas[0].lines[2]”
和
“self.data_close” 指的是 “self.datas[0].close”
返回分析#
Analyzer 基类创建了一个 self.rets (类型为 collections.OrderedDict )成员属性来返回分析结果。这是在方法 create_analysis 中完成的,如果需要创建定制的分析器,则可以在子类中重写该方法。
操作方式#
尽管 Analyzer 对象不是 Lines 对象,因此不能迭代行,但它们已经被设计为遵循相同的操作模式。
在系统开始运行之前初始化(因此调用 __init__ )
用 “start” 标记操作的开始
在指标工作的最小时间段后,将调用 “prenext” / “nextstart” / “next” 来进行下一轮操作。
prenext
和nextstart
的默认行为是调用next,因为分析器可能从系统一开始就进行分析。
在 Lines 对象中,通常可以调用 len(self)
来检查实际的bar数量。这也适用于 Analyzers
,通过返回 self.strategy
的值来实现。
订单和交易将通过
notify_order
和notify_trade
类似于策略一样进行通知。现金和价值也将通过
notify_cashvalue
方法进行通知,就像在策略中一样。现金、价值、基金净值和基金份额还将通过
notify_fund
方法进行通知,就像在策略中一样。
stop
将被调用以表示操作的结束。
完成常规操作周期后, 分析器 具有用于提取/输出信息的附加方法。
get_analysis
:理想情况下(非强制性),返回包含分析结果的类似于dict
的对象。
backtrader.WriterFile
(除非被覆盖)来从get_analysis
中写入分析结果。
pprint
( pretty print )使用Python的pprint
模块打印get_analysis
的结果。最后:
get_analysis
方法创建了一个成员属性self.ret
(类型为collections.OrderedDict
),分析器将分析结果写入该属性。Analyzer 的子类可以重写此方法以更改此行为。
分析器模式#
在 backtrader
平台中开发 Analyzer 对象时,已经发现了两种不同的用法模式来生成分析结果:
在执行期间通过在
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()
# 数据 dataname = ‘../datas/sample/2005-2006-day-001.txt’ data = btfeeds.BacktraderCSVData(dataname=dataname)
cerebro.adddata(data)
# 策略 cerebro.addstrategy(btstrats.SMA_CrossOver)
# 分析器 cerebro.addanalyzer(btanalyzers.SharpeRatio, _name=’mysharpe’)
thestrats = cerebro.run() thestrat = thestrats[0]
``` 打印(‘夏普比率:’, thestrat.analyzers.mysharpe.get_analysis())
执行它(将其存储在 analyzer-test.py
中):
$ ./analyzer-test.py 夏普比率: {‘sharpe比率’: 11.647332609673256}
由于 SharpeRatio
在计算结束时只有一个单一值,所以没有绘图。
分析器的法医学分析#
让我们重申一下, 分析器
并不是 Lines
对象,但为了无缝地将它们整合到 backtrader
生态系统中,
遵循了几个 Lines
对象的内部API约定(实际上是它们的 混合 )。
SharpeRatio
的代码作为基础(简化版本)该代码可以分为以下部分:
params 声明
虽然声明的变量没有被使用(仅作为示例),但是像大多数 backtrader 中的其他对象一样, Analyzers 也支持参数。
__init__ 方法
就像 Strategies 在 __init__ 中声明 Indicators 一样, Analyzers 也可以声明需要的支持对象。
在这个例子中:
SharpeRatio
是使用 年收益率 计算的。计算将会自动执行,并且SharpeRatio
将会使用这个计算结果进行进一步的计算。备注
实际上,
SharpeRatio
的实现使用了更通用且后续开发的TimeReturn
analyzernext 方法
SharpeRatio
不需要这个方法,但是这个方法会在父策略的next
方法调用后被调用。-start
方法在回测开始之前调用。可以用于额外的初始化任务。Sharpe比率不需要它。
stop
方法在回测结束后立即调用。与
SharpeRatio
一样,可用于结束/完成计算。get_analysis
方法(返回一个字典)用于外部调用者访问生成的分析结果。
返回值:包含分析数据的字典。