1. 程式人生 > 實用技巧 >Backtrader中文筆記之Analyzers

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)

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

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方法中完成的,如果建立自定義分析器,則可以由子類重寫該方法。

Modus operandi

操作手法

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將在指標所處策略的計算出的最短週期後呼叫。
  7. 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.

  8. prenext和nextstart的預設行為是呼叫next,因為分析器可能從系統處於活動狀態的第一刻就開始分析。
  9. 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

  10. 通常在Lines物件中呼叫len(self)來檢查實際的條數。這在分析器中也可以通過返回自我策略
  11. Orders and trades will be notified just like they are to the strategy via notify_order and notify_trade

  12. 訂單和交易將通過“通知”訂單和“通知交易”通知策略
  13. Cash and value will also be notified like it is done with the strategy over the notify_cashvalue method

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

  16. 現金、價值和基金價值以及基金份額也將得到通知,在策略上使用notify_fund方法
  17. stop will be invoked to signal the end of operations

  18. 將呼叫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的物件。
  • print uses a standard backtrader.WriterFile (unless overriden) to write the analysis result from get_analysis.

  • 列印使用標準backtrader.WriterFile(除非重寫)從get_analysis寫入分析結果。
  • pprint (pretty print) uses the Python pprint module to print the get_analysis resutls.

  • pprint(pretty print)使用Python pprint模組列印get_analysis分析結果。

And finally:

最後

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

  • get_analysis建立成員屬性self.ret,分析程式將分析結果寫入其中。

    Subclasses of Analyzer can override this method to change this behavior

  • 分析器的子類可以重寫此方法來改變這種行為

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

操作模式為開放式,無模式優先。因此,分析可以在下一個呼叫中生成,在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

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.