天天看點

量化回測架構BackTrader【7】-開發名額0,序1,BackTrader名額特性2,名額的繪制3,名額開發4,耦合不同時間機關的資料

目錄

0,序

1,BackTrader名額特性

2,名額的繪制

3,名額開發

4,耦合不同時間機關的資料

0,序

名額可以說是對原始資料的預處理,從原始資料無法發現的特征,現象;往往通過對資料進行一定的處理産生一些特别的名額才能顯現出來。比如移動均值,MACD,RSI,布林帶等等。絕大部分的政策都會用到一些特定的名額,特别是一些核心名額對于政策來說是至關重要。BackTrader有一套特有的名額開發方式,開發起來很友善,運作效率也很高。

1,BackTrader名額特性

由于名額是服務于政策的,是以名額一般都是聲明在政策内部。

名額主要存在于BackTrader政策的兩個函數内,__init__()和next()。

在__init__()内,名額會被預計算成line,并且所有對名額的計算都會産生一條新的line。

在next()内,名額以line的形式被通路使用,并且對名額的計算不會産生line,而是産生普通的數值。

hilo_diff = self.data.high - self.data.low
           
sma = bt.SimpleMovingAverage(self.data.close)
close_sma_diff = self.data.close - sma
           
close_over_sma = self.data.close > sma
           

以上三個例子都運作在__init__()中,hilo_diff,close_sma_diff,close_over_sma都是名額,并且是line形式的

相應地,在next()中運作如下代碼

close_over_sma = self.data.close > self.sma
           

close_over_sma就隻是一個布爾數值

這麼設計的一個好處是,節省了next運作期間的運算壓力。可以看如下的例子

class MyStrategy(bt.Strategy):

    def __init__(self):

        sma1 = btind.SimpleMovingAverage(self.data)
        ema1 = btind.ExponentialMovingAverage()

        close_over_sma = self.data.close > sma1
        close_over_ema = self.data.close > ema1
        sma_ema_diff = sma1 - ema1

        buy_sig = bt.And(close_over_sma, close_over_ema, sma_ema_diff > 0)

    def next(self):

        if buy_sig:
            self.buy()
           

購買的信号也在__init__()中預計算完成,在next()隻做判斷。這樣顯著提高了整個回測的運作效率。

2,名額的繪制

預設的情況下BackTrader會把名額顯示在cerebro.plot ()輸出圖像上,除了布爾型的名額。如果希望顯示布爾型的名額,可以如下操作

close_over_sma = self.data.close > self.sma
LinePlotterIndicator(close_over_sma, name='Close_over_SMA')
           

當通過繼承Indicator開發名額子類時,可以聲明plotinfo用于控制名額圖像。plotinfo接受元祖的元祖或字典的方式傳參。

class MyIndicator(bt.Indicator):

    ....
    plotinfo = dict(subplot=False)
    ....
           
“subplot ”可以通過如下方式進行設定
           
myind = MyIndicator(self.data, someparam=value)
myind.plotinfo.subplot = True
           
myind = MyIndicator(self.data, someparams=value, subplot=True)
           

plotinfo支援以下參數:

  • plot (default: True):名額是否需要繪制
  • subplot (default: True):是否在其他視窗中繪制名額。
  • plotname (default: ''):設定要在繪圖上顯示的繪圖名稱。空值表示将使用名額的規範名稱(class .__ name__)。
  • plotabove (default: False):名額通常在其操作的資料下方繪制(帶有subplot = True的名額)。該設定為True将使名額繪制在資料上方。
  • plotlinelabels (default: False):标簽顯示生成該名額的源名額。
  • plotymargin(預設值:0.0):頂部和底部的留白
  • plotyticks (default: []):y軸刻度
  • plothlines (default: []):繪制水準線
  • plotyhlines (default: []):同時控制plotyticks和plothlines

3,名額開發

開發一個自己的名額基本流程如下:

  1. 從Indicator基類(或它的子類)進行繼承
  2. 定義需要的lines
  3. 一個名額類至少包含一條line,如果被繼承的名額已經有line則可省略
  4. (可選)定義可修改的參數
  5. (可選)定義一些參數修改名額的預設繪制
  6. 在__init__()中提供一個操作,并綁定到名額的line,或者提供next()或once()方法

下面就第6點的三種方式,舉例說明:

class DummyInd(bt.Indicator):
    lines = ('dummyline',)

    params = (('value', 5),)

    def __init__(self):
        self.lines.dummyline = bt.Max(0.0, self.params.value)
           

名額資料的生成在__init__函數内

class DummyInd(bt.Indicator):
    lines = ('dummyline',)

    params = (('value', 5),)

    def next(self):
        self.lines.dummyline[0] = max(0.0, self.params.value)
           

這個例子的效果跟上面那個一樣,隻是資料的生成放在了next()函數内,每次對line的0下标(即目前值)進行指派

class DummyInd(bt.Indicator):
    lines = ('dummyline',)

    params = (('value', 5),)

    def next(self):
        self.lines.dummyline[0] = max(0.0, self.params.value)

    def once(self, start, end):
       dummy_array = self.lines.dummyline.array

       for i in xrange(start, end):
           dummy_array[i] = max(0.0, self.params.value)
           

額外的once()可以優化計算。

4,耦合不同時間機關的資料

當名額需要操作兩個不同時間機關的資料,比如日線和月線,如果直接按照通常的方法操作的話會報錯。

pivotpoint = btind.PivotPoint(self.data1)
sellsignal = self.data0.close < pivotpoint.s1
           

其中“data1”是月線資料,“data0”是日線資料,運作時會報錯

return self.array[self.idx + ago]
IndexError: array index out of range      

BackTrader提供了一個解決方案來應對這種需求,就是在“s1”後面添加()

pivotpoint = btind.PivotPoint(self.data1)
sellsignal = self.data0.close < pivotpoint.s1()
           

pivotpoint.s1()會傳回一個内部LinesCoupler對象,它會用最近的s1月線值來進行填充空缺的日線值。

如果要使這個方法生效,必須在建立cerebro時,添加一個參數“runonce=False”,類似這樣

cerebro = bt.Cerebro(runonce=False)
           

cerebro.run(runonce=False)
           

差別于隻對s1生效,另一種寫法是這樣

pp1 = pp()
self.sellsignal = self.data0.close < pp1.s1
           

如果pp含有多條lines,那個所有的lines都會進行耦合。

一個完整的示例:

import backtrader as bt
import backtrader.indicators as btind
import inspect

class St(bt.Strategy):
    params = dict(multi=True)

    def __init__(self):
        self.pp = pp = btind.PivotPoint(self.data1)
        pp.plotinfo.plot = False  # deactivate plotting

        if self.p.multi:
            pp1 = pp()  # couple the entire indicators
            self.sellsignal = self.data0.close < pp1.s1
        else:
            self.sellsignal = self.data0.close < pp.s1()

    def next(self):
        txt = ','.join(
            ['%04d' % len(self),
             '%04d' % len(self.data0),
             '%04d' % len(self.data1),
             self.data.datetime.date(0).isoformat(),
             '%.2f' % self.data0.close[0],
             '%.2f' % self.pp.s1[0],
             '%.2f' % self.sellsignal[0]])

        print(txt)

cerebro = bt.Cerebro()
data = bt.feeds.GenericCSVData(
    dataname='CU1811.csv',
    nullvalue=0.0,
    dtformat=('%Y%m%d'),
    datetime=1,
    open=4,
    high=5,
    low=6,    
    close=7,
    volume=11,
    openinterest=-1
)

cerebro.adddata(data)
cerebro.resampledata(data, timeframe=bt.TimeFrame.Months)
cerebro.broker.set_cash(1000000)
cerebro.addstrategy(St, multi=False)

cerebro.run()
cerebro.plot(style='bar',iplot=False)
           
0012,0012,0001,2017-12-01,54460.00,53516.67,0.00
0013,0013,0001,2017-12-04,55060.00,53516.67,0.00
0014,0014,0001,2017-12-05,53910.00,53516.67,0.00
0015,0015,0001,2017-12-06,52800.00,53516.67,1.00
0016,0016,0001,2017-12-07,52630.00,53516.67,1.00
0017,0017,0001,2017-12-08,53240.00,53516.67,1.00
0018,0018,0001,2017-12-11,52680.00,53516.67,1.00
0019,0019,0001,2017-12-12,53460.00,53516.67,1.00
0020,0020,0001,2017-12-13,53630.00,53516.67,0.00
0021,0021,0001,2017-12-14,53700.00,53516.67,0.00
0022,0022,0001,2017-12-15,54320.00,53516.67,0.00
0023,0023,0001,2017-12-18,54910.00,53516.67,0.00
0024,0024,0001,2017-12-19,54800.00,53516.67,0.00
0025,0025,0001,2017-12-20,55150.00,53516.67,0.00
0026,0026,0001,2017-12-21,55700.00,53516.67,0.00
0027,0027,0001,2017-12-22,55940.00,53516.67,0.00
0028,0028,0001,2017-12-25,56090.00,53516.67,0.00
0029,0029,0001,2017-12-26,56680.00,53516.67,0.00
0030,0030,0001,2017-12-27,56340.00,53516.67,0.00
0031,0031,0001,2017-12-28,57230.00,53516.67,0.00
0032,0032,0001,2017-12-29,56820.00,53516.67,0.00
0033,0033,0002,2018-01-02,56610.00,53890.00,0.00
0034,0034,0002,2018-01-03,56200.00,53890.00,0.00
0035,0035,0002,2018-01-04,56650.00,53890.00,0.00      

從輸出可以看出s1值一個月才會變化。

量化回測架構BackTrader【7】-開發名額0,序1,BackTrader名額特性2,名額的繪制3,名額開發4,耦合不同時間機關的資料