Backtrader中文笔记之Analyzers(二次修复)

Analyzers

分析仪

Be it backtesting or trading, being able to analyze the performance of the trading system is key to understanding if not only profit has been attained, but also if it has been achieved with too much risk or if it was really worth the effort when compared with a reference asset (or a risk-free asset)

无论是回溯测试还是交易,能够分析交易系统的表现对于理解是否不仅获得了利润,而且是在风险太大的情况下实现的,或者与参考资产(或无风险资产)相比是否真的值得付出努力的关键

That’s where the family of Analyzer objects comes in: provide an analysis of what’s happened or even of what’s actually happening.

这就是Analyzer对象系列的作用:提供所发生的事情甚至实际发生的事情的分析。

Nature of analyzers

分析仪的性质

The interface is modeled after that of Lines objects, feature for example a next method but there is a major difference:

该接口是以Lines对象的接口为模型的,具有下一个方法,但有一个主要区别:

  • Analyzers do not hold lines.

  • 分析仪不保持线。

    That means they are not expensive in terms of memory because even after having analyzed thousands of price bars they may still simply hold a single result in memory.

  • 这意味着它们在内存方面并不昂贵,因为即使在分析了数千条价格条之后,它们仍可能只是在内存中保存一个结果。

Location in the ecosystem

生态系统中的位置

Analyzer objects are (like strategies, observers and datas) added to the system through a cerebro instance:

分析对象(如策略、观察者和数据)通过大脑实例添加到系统中:

  • addanalyzer(ancls, *args, **kwargs)
  • addanalyzer是cerebro的方法,ancls是analyer的类

But when it comes to operation during cerebro.run the following will happen for each strategy present in the system

但说到操作大脑运行对于系统中存在的每个策略,将发生以下情况

  • ancls will be instantiated with *args and **kwargs during a cerebro.run

  • ancls将在cerebro.run期间使用参数*args and **kwargs实例化
  • The ancls instance will be attached to the strategy

  • ancls实例将附加到策略中

That means:

这意味着:

  • If the backtesting run contains for example 3 strategies then 3 instances of ancls will be created and each of them will be attached to a different strategy.
  • 如果回溯测试运行包含例如3个策略,那么将创建3个ancl实例,并且每个实例都将附加到不同的策略

Bottomline: an analyzer analyzes the performance of a single strategy and not the performance of an entires system

提示:分析器分析单个策略的好话,而不是整个系统的好坏

 

Additional Location

额外的位置

Some Analyzer objects may actually use other analyzers to complete its work. For example: SharpeRatio uses the output of TimeReturn for the calculations.

有些分析器对象实际上可能使用其他分析器来完成其工作。例如:SharpeRatio使用TimeReturn的输出进行计算。

These sub-analyzers or slave-analyzers will also be inserted into the same strategy as the one creating them. But they are completely invisible to the user.

这些子分析器或从属分析器也将被插入到与创建它们的策略相同的策略中。但用户完全看不见它们。

 

Attributes

取数据的方式与strategy差不多。只不过但它被添加到cerebro以后,strategy就称为他的一个属性之一

To carry out the intended work, Analyzer objects are provided with some default attributes which are automagically passed and set in the instance for ease of use:

为了执行预期的工作,Analyzer对象提供了一些默认属性,这些属性将自动传递并在实例中设置,以便于使用:

  • self.strategy: reference to the strategy subclass in which the analyzer object is operating. Anything accessible by the strategy can also be accessd by the analyzer

  • self.strategy:引用analyzer对象正在其中运行的strategy子类。策略可以访问的任何内容也可以被分析器访问
  • self.datas[x]: the array of data feeds present in the strategy. Although this could be accesed over the strategy reference, the shortcut makes work more comfortable.

  • self.datas[x]:策略中存在的数据馈送数组。虽然这可以通过策略参考来获得,但快捷方式使工作更加舒适
  • self.data: shortcut to self.datas[0] for extra comfort.

  • self.data:self.datas[0]更加的快捷方式
  • self.dataX: shortcuts to the different self.datas[x]

  • self.dataX:取不同的self.datas[x]的快捷方式.

Some other aliases are available although they are probably an overkill:

还有一些其他别名可用,尽管它们可能有点过头了:

* `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]`

 If the line has a name, the following is also available:

如果该行有名称,则以下内容也可用[返回索引]:

* `self.dataX_Name` which resolves to `self.datas[X].Name` returning
  the line by name rather than by index

 For the first data, the last two shortcuts are available without the initial X numeric reference. For example:

对于第一个的数据,最后两个快捷方式可用,但没有初始的X数字参考。例如:

* `self.data_2` refers to `self.datas[0].lines[2]`

 And

* `self.data_close` refers to `self.datas[0].close`

 

Returning the analysis

返回分析

The Analyzer base class creates a self.rets (of type collections.OrderedDict) member attribute to return the analysis. This is done in the method create_analysis which can be overriden by subclasses if creating custom analyzers.

Analyzer将创建self.rets(collections.OrderedDict类型)的属性返回。这是在create_analysis方法中完成的,如果创建自定义分析器,则可以由子类重写该方法。

 

    def create_analysis(self):
        '''Meant to be overriden by subclasses. Gives a chance to create the
        structures that hold the analysis.

        The default behaviour is to create a ``OrderedDict`` named ``rets``
        '''
        self.rets = OrderedDict()

 上面是基类创建rets用于保存分析的数据

Modus operandi

操作手法

[所有的运行的方式与逻辑与strategy都是一样的,就是因为一样,分析仪才能拿到各种数据进行统计]

Although Analyzer objects are not Lines objects and therefore do not iterate over lines, they have been designed to follow the same operation pattern.

虽然Analyzer对象不是Lines对象,因此不会迭代行,但它们的设计遵循相同的操作模式。

  1. Instantiated before the system is put into motion (therefore calling __init__)

  2. 在系统投入运行之前实例化

  3. Signaled the begin of operations with start

  4. 用start发出操作开始的信号
  5. prenext / nextstart / next will be invoked following the calculated minimum period of the strategy the indicator is working in.

  6. prenext/nextstart/next将在指标所处策略的计算出的最短周期后调用。

    The default behaviour of prenext and nextstart is to invoke next, because an analyzer may be analyzing from the very first moment the system is alive.

  7. prenext和nextstart的默认行为是调用next,因为分析器可能从系统处于活动状态的第一刻就开始分析。
  8. It may be customary to call len(self) in Lines objects to check the actual amount of bars. This also works in Analyzers by returning the value for self.strategy

  9. 通常在Lines对象中调用len(self)来检查实际的条数。这在分析器中也可以通过返回自我策略
  10. Orders and trades will be notified just like they are to the strategy via notify_order and notify_trade

  11. 订单和交易将通过“通知”订单和“通知交易”通知策略
  12. Cash and value will also be notified like it is done with the strategy over the notify_cashvalue method

  13. Cash和value也将被通知,在策略上使用notify_cashvalue方法
  14. Cash, value and fundvalue and fund shares will also be notified like it is done with the strategy over the notify_fund method

  15. 现金、价值和基金价值以及基金份额也将得到通知,在策略上使用notify_fund方法
  16. stop will be invoked to signal the end of operations

  17. 将调用stop来发出操作结束的信号

Once the regular operations cycle has been completed, the analyzers featuring additional methods for extracting/outputting information

一旦常规操作循环完成,分析仪具有提取/输出信息的附加方法

  • get_analysis: which ideally (not enforced) returnes a dict -like object containing the analysis results.

  • get_analysis:理想情况下(不是强制的)返回包含分析结果类似dict的对象。
  •     def get_analysis(self):
            '''Returns a *dict-like* object with the results of the analysis
    
            The keys and format of analysis results in the dictionary is
            implementation dependent.
    
            It is not even enforced that the result is a *dict-like object*, just
            the convention
    
            The default implementation returns the default OrderedDict ``rets``
            created by the default ``create_analysis`` method
    
            '''
            return self.rets
    

     

  • print uses a standard backtrader.WriterFile (unless overriden) to write the analysis result from get_analysis.

  • 打印使用标准backtrader.WriterFile(除非重写)从get_analysis写入分析结果。
  •     def print(self, *args, **kwargs):
            '''Prints the results returned by ``get_analysis`` via a standard
            ``Writerfile`` object, which defaults to writing things to standard
            output
            '''
            writer = bt.WriterFile(*args, **kwargs)
            writer.start()
            pdct = dict()
            pdct[self.__class__.__name__] = self.get_analysis()
            writer.writedict(pdct)
            writer.stop()
    
  • pprint (pretty print) uses the Python pprint module to print the get_analysis resutls.

  • pprint(pretty print)使用Python pprint模块打印get_analysis分析结果。
  •     def pprint(self, *args, **kwargs):
            '''Prints the results returned by ``get_analysis`` using the pretty
            print Python module (*pprint*)
            '''
            pp.pprint(self.get_analysis(), *args, **kwargs)
    

     

And finally:

最后

  • get_analysis creates a member attribute self.rets (of type collections.OrderedDict) to which analyzers write the analysis results.

  • get_analysis返回成员属性self.rets,分析程序将分析结果写入其中。

    Subclasses of Analyzer can override this method to change this behavior

  • 分析器的子类可以重写此方法来改变这种行为

   

  def get_analysis(self):
        '''Returns a *dict-like* object with the results of the analysis

        The keys and format of analysis results in the dictionary is
        implementation dependent.

        It is not even enforced that the result is a *dict-like object*, just
        the convention

        The default implementation returns the default OrderedDict ``rets``
        created by the default ``create_analysis`` method

        '''
        return self.rets

 

Analyzer Patterns

分析器模式

Development of Analyzer objects in the backtrader platform have revealed 2 different usage patterns for the generation of the analysis:

backtrader平台中Analyzer对象的开发揭示了生成分析的两种不同的使用模式:

  1. During execution by gathering information in the notify_xxx and next methods, and generating the current information of the analysis in next

  2. 在执行期间,通过在notify_xxx和next方法中收集信息,并在next中生成分析的当前信息

     

    The TradeAnalyzer, for example, uses just the notify_trade method to generate the statistics.

  3. 例如,TradeAnalyzer只使用notify_trade方法来生成统计信息。
     
     
     
  4. Gather (or not) the information as above, but generate the analysis in a single pass during the stop method

  5. 收集(或不收集)上述信息,但在stop方法期间一次性生成分析
  6.  

    The SQN (System Quality Number) gathers trade information during notify_trade but generates the statistic during the stop method

  7. SQN(系统质量编号)在notify_trade期间收集交易信息,但在stop方法期间生成统计信息

A quick example

As easy as it can be:

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())

 Executing it (having stored it in analyzer-test.py:

$ ./analyzer-test.py
Sharpe Ratio: {'sharperatio': 11.647332609673256}

 There is no plotting, because the SharpeRatio is a single value at the end of the calculation.

因为SharpeRatio在计算结束时是一个单一的值,所以没有绘图。

 

Forensic Analysis of an Analyzer

分析器的解剖

Let’s repeat that Analyzers are not Lines objects, but to seamlessly integrate them into the backtrader ecosystem, the internal API conventions of several Lines object are followed (actually a mixture of them)

让我们重复一下,分析器不是Lines对象,而是为了将它们无缝地集成到backtrader生态系统中,遵循了几个Lines对象的内部API约定(实际上是它们的混合)

Note

The code for the SharpeRatio has evolved to take for example into account annualization and the version here should only be a reference.

SharpeRatio的代码已经发展到考虑到年率化,这里的版本应该只是一个参考。

Code for SharpeRatio to serve as a basis (a simplified version)

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)

 

The code can be broken down into:

代码可分为:

  • params declaration

  • params声明

    Although the declared ones are not used (meant as an example), Analyzers like most other objects in backtrader support parameters

  • 尽管声明的对象有一个参数没有被使用(作为一个例子),分析器像backtrader中的大多数其他对象一样支持参数
  • __init__ method

    Just like Strategies declare Indicators in __init__, the same do analyzers with support objects.

  • 就像策略在__init__中声明指标一样,带有支持对象的分析器也是如此。

    In this case: the SharpeRatio is calculated using Annual Returns. The calculation will be automatic and will be available to SharpeRatio for its own calculations.

  • 在这种情况下:SharpeRatio是用年回报率计算的。计算将自动进行,SharpeRatio可自行计算。
  •  

    Note

    The actual implementation of SharpeRatio uses the more generic and later developed TimeReturn analyzer

    SharpeRatio的实际实现使用了更通用和更晚开发的TimeReturn分析器

  • next method

    SharpeRatio doesn’t need it, but this method will be called after each invocation of the parent strategy next

  • SharpeRatio不需要它,但是每次调用父策略之后都会调用这个方法
  • start method

    Called right before the backtesting starts. Can be used for extra initialization tasks. Sharperatio doesn’t need it

  • 在回溯测试开始之前调用。可用于额外的初始化任务。Sharperatio不需要它
  • stop method

    Called right after the backtesting ends. Like SharpeRatio does, it can be used to finish/make the calculation

  • 在回溯测试结束后立即调用。和SharpeRatio一样,它可以用来完成/进行计算
  • get_analysis method (returns a dictionary)

    Access for external callers to the produced analysis

  • 外部调用者访问生成的分析

    Returns: a dictionary with the analysis.

  • 返回:包含分析的字典

 

 

Reference

参考

class backtrader.Analyzer()

Analyzer base class. All analyzers are subclass of this one

分析器基类。所有的分析器都是这个的子类

An Analyzer instance operates in the frame of a strategy and provides an analysis for that strategy.

Analyzer实例在策略框架中运行,并为该策略提供分析。

Automagically set member attributes:

自动设置成员属性:

  • self.strategy (giving access to the strategy and anything accessible from it)

  • self.datas[x] giving access to the array of data feeds present in the the system, which could also be accessed via the strategy reference

  • self.data, giving access to 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]

This is not a Lines object, but the methods and operation follow the same design

这不是一个Lines对象,但是方法和操作遵循相同的设计

  • __init__ during instantiation and initial setup

  • start / stop to signal the begin and end of operations

  • prenext / nextstart / next family of methods that follow the calls made to the same methods in the strategy

  • notify_trade / notify_order / notify_cashvalue / notify_fund which receive the same notifications as the equivalent methods of the strategy

The mode of operation is open and no pattern is preferred. As such the analysis can be generated with the next calls, at the end of operations during stop and even with a single method like notify_trade

操作模式为开放式,无模式优先。因此,分析可以在next调用中生成,在stop期间的操作结束时生成,甚至可以使用notify_trade这样的单一方法生成。

The important thing is to override get_analysis to return a dict-like object containing the results of the analysis (the actual format is implementation dependent)

重要的是重写get_analysis以返回包含分析结果的dict-like对象(实际格式取决于实现)

start()

Invoked to indicate the start of operations, giving the analyzer time to setup up needed things

调用以指示操作的开始,给分析器时间设置所需的内容

stop()

Invoked to indicate the end of operations, giving the analyzer time to shut down needed things

调用以指示操作结束,给分析器时间关闭所需的东西

prenext()

Invoked for each prenext invocation of the strategy, until the minimum period of the strategy has been reached

The default behavior for an analyzer is to invoke next

为策略的每次prenext调用,直到达到策略的最短周期
分析器的默认行为是调用next

nextstart()

Invoked exactly once for the nextstart invocation of the strategy, when the minimum period has been first reached

在策略的下一次启动调用中,当第一次达到最小周期时,只调用一次

next()

Invoked for each next invocation of the strategy, once the minum preiod of the strategy has been reached

一旦达到策略的最小值前导,每次调用策略时调用

notify_cashvalue(cash, value)

Receives the cash/value notification before each next cycle

在下一个周期前收到现金/价值通知

notify_fund(cash, value, fundvalue, shares)

Receives the current cash, value, fundvalue and fund shares

接收当前现金、价值、基金价值和基金份额

notify_order(order)

Receives order notifications before each next cycle

notify_trade(trade)

Receives trade notifications before each next cycle

get_analysis()

Returns a dict-like object with the results of the analysis

The keys and format of analysis results in the dictionary is implementation dependent.

 

posted @ 2020-09-10 16:03  就是想学习  阅读(1092)  评论(0编辑  收藏  举报