量化回測:backtrader


 

backtrader簡介

backtrader是基於Python的量化回測框架,優點是運行速度快,支持pandas的矢量運算;支持參數自動尋優運算,內置了talib股票分析技術指標庫;支持多品種、多策略、多周期的回測和交易;支持pyflio、empyrica分析模塊庫、alphalens多因子分析模塊庫等;擴展靈活,可以集成TensorFlow、PyTorch和Keras等機器學習、神經網絡分析模塊。

如果將backtrader包分解為核心組件,主要包括以下組成部分:
(1)數據加載(Data Feed):將交易策略的數據加載到回測框架中。

(2)交易策略(Strategy):該模塊是編程過程中最復雜的部分,需要設計交易決策,得出買入/賣出信號。
(3)回測框架設置( Cerebro):需要設置(i)初始資金(ii)佣金(iii)數據饋送(iv)交易策略(v)交易頭寸大小。
(4)運行回測:運行Cerebro回測並打印出所有已執行的交易。
(5)評估性能(Analyzers):以圖形和風險收益等指標對交易策略的回測結果進行評價。

“Lines”是backtrader回測的數據,由一系列的點組成,通常包括以下類別的數據:Open(開盤價), High(最高價), Low(最低價), Close(收盤價), Volume(成交量), OpenInterest(無的話設置為0)。Data Feeds(數據加載)、Indicators(技術指標)和Strategies(策略)都會生成 Lines。價格數據中的所有”Open” (開盤價)按時間組成一條 Line。所以,一組含有以上6個類別的價格數據,共有6條 Lines。如果算上“DateTime”(時間,可以看作是一組數據的主鍵),一共有7條 Lines。當訪問一條 Line 的數據時,會默認指向下標為 0 的數據。最后一個數據通過下標 -1 來訪問,在-1之后是索引0,用於訪問當前時刻。因此,在回測過程中,無需知道已經處理了多少條/分鍾/天/月,”0”一直指向當前值,下標 -1 來訪問最后一個值。

編寫回測應用

構建策略(Strategy)

每一個策略都是一個類,是一個繼承bt.Strategy類的父類。

策略類代碼包含重要的參數和用於執行策略的功能,要定義的參數或函數名如下:

(1)params-全局參數,可選:更改交易策略中變量/參數的值,可用於參數調優。

(2)log:日志,可選:記錄策略的執行日志,可以打印出該函數提供的日期時間和txt變量。

(3) __init__:用於初始化交易策略的類實例的代碼。

(4)notify_order,可選:跟蹤交易指令(order)的狀態。order具有提交,接受,買入/賣出執行和價格,已取消/拒絕等狀態。

(5)notify_trade,可選:跟蹤交易的狀態,任何已平倉的交易都將報告毛利和凈利潤。

(6)next,必選:制定交易策略的函數,策略模塊最核心的部分。

下面以一個簡單的單均線策略為例,展示backtrader策略的編寫過程,即當收盤價上漲突破20日均線買入(做多),當收盤價下跌跌穿20日均線賣出(做空)。為簡單起見,不報告交易回測的日志,因此log、notify_order和notify_trade函數省略不寫。

class my_strategy1(bt.Strategy):
    #全局設定交易策略的參數
    params=(
        ('maperiod',20),
           )

    def __init__(self):
        #指定價格序列
        self.dataclose=self.datas[0].close
        # 初始化交易指令、買賣價格和手續費
        self.order = None
        self.buyprice = None
        self.buycomm = None

        #添加移動均線指標,內置了talib模塊
        self.sma = bt.indicators.SimpleMovingAverage(
                      self.datas[0], period=self.params.maperiod)
    def next(self):
        if self.order: # 檢查是否有指令等待執行, 
            return
        # 檢查是否持倉   
        if not self.position: # 沒有持倉
            #執行買入條件判斷:收盤價格上漲突破20日均線
            if self.dataclose[0] > self.sma[0]:
                #執行買入
                self.order = self.buy(size=500)         
        else:
            #執行賣出條件判斷:收盤價格跌破20日均線
            if self.dataclose[0] < self.sma[0]:
                #執行賣出
                self.order = self.sell(size=500)
self.dataclose[0] # 當日的收盤價
self.dataclose[-1] # 昨天的收盤價
self.dataclose[-2] # 前天的收盤價

獲取數據加載數據

#使用tushare舊版接口獲取數據
import tushare as ts 
def get_data(code,start='2010-01-01',end='2020-03-31'):
    df=ts.get_k_data(code,autype='qfq',start=start,end=end)
    df.index=pd.to_datetime(df.date)
    df['openinterest']=0
    df=df[['open','high','low','close','volume','openinterest']]
    return df
dataframe=get_data('600000')

#回測期間
start=datetime(2010, 3, 31)
end=datetime(2020, 3, 31)
# 加載數據
data = bt.feeds.PandasData(dataname=dataframe,fromdate=start,todate=end)

這里需要注意backtrader對每種數據的來源都有一定的標准。backtrader對於pandas的標准就是這些列的名字是open,close,high,low,volume,openinterest。

我們通過ts獲取數據的列為:open close high low volume code,我們還需要在此基礎上添加一列openinterest,我們這里沒有用到openinterest,所以把它設置為零就可以了:df['openinterest']=0。

另外backtrader要求pandas下的DataFeed、DataFrame的index是時間,所以設置:df.index=pd.to_datetime(df.date)

最后:df=df[['open','high','low','close','volume','openinterest']]

創建並設置模型

    #回測期間
    start=datetime(2010, 3, 31)
    end=datetime(2020, 3, 31)
    # 加載數據
    data = bt.feeds.PandasData(dataname=dataframe,fromdate=start,todate=end)
    # 初始化cerebro回測系統設置
    cerebro = bt.Cerebro()
    #將數據傳入回測系統
    cerebro.adddata(data)
    # 將交易策略加載到回測系統中
    cerebro.addstrategy(my_strategy1)
    # 設置初始資本為10,0000
    startcash = 100000
    cerebro.broker.setcash(startcash)
    # 設置交易手續費為 0.2%
    cerebro.broker.setcommission(commission=0.002)

執行回測並可視化

d1=start.strftime('%Y%m%d')
    d2=end.strftime('%Y%m%d')
    print(f'初始資金: {startcash}\n回測期間:{d1}:{d2}')

    #運行回測系統
    cerebro.run()
    #獲取回測結束后的總資金
    portvalue = cerebro.broker.getvalue()
    pnl = portvalue - startcash
    #打印結果
    print(f'總資金: {round(portvalue,2)}')

    cerebro.plot()

源碼

# -*- coding:utf-8 -*-


#正常顯示畫圖時出現的中文和負號
from pylab import mpl
mpl.rcParams['font.sans-serif']=['SimHei']
import pandas as pd
from datetime import datetime
import backtrader as bt
import tushare as ts

#策略
class my_strategy1(bt.Strategy):
    #全局設定交易策略的參數
    params=(
        ('maperiod',20),
           )

    def __init__(self):
        #指定價格序列
        self.dataclose=self.datas[0].close
        # 初始化交易指令、買賣價格和手續費
        self.order = None
        self.buyprice = None
        self.buycomm = None

        #添加移動均線指標,內置了talib模塊
        self.sma = bt.indicators.SimpleMovingAverage(
                      self.datas[0], period=self.params.maperiod)
    def next(self):
        if self.order: # 檢查是否有指令等待執行,
            return
        # 檢查是否持倉
        if not self.position: # 沒有持倉
            #執行買入條件判斷:收盤價格上漲突破20日均線
            if self.dataclose[0] > self.sma[0]:
                #執行買入
                self.order = self.buy(size=500)
        else:
            #執行賣出條件判斷:收盤價格跌破20日均線
            if self.dataclose[0] < self.sma[0]:
                #執行賣出
                self.order = self.sell(size=500)


def get_data(code,start='2010-01-01',end='2020-03-31'):
    df=ts.get_k_data(code,autype='qfq',start=start,end=end)
    #獲取的數據格式: open   close    high     low      volume    code
    #將date變成DataFrame的index
    df.index=pd.to_datetime(df.date)

    df['openinterest']=0
    df=df[['open','high','low','close','volume','openinterest']]
    return df


if __name__ == "__main__":
    #獲取數據
    dataframe=get_data('600000')

    #回測期間
    start=datetime(2010, 3, 31)
    end=datetime(2020, 3, 31)

    # 加載數據
    data = bt.feeds.PandasData(dataname=dataframe,fromdate=start,todate=end)

    # 初始化cerebro回測系統設置
    cerebro = bt.Cerebro()

    #將數據傳入回測系統
    cerebro.adddata(data)

    # 將交易策略加載到回測系統中
    cerebro.addstrategy(my_strategy1)

    # 設置初始資本為10,000
    startcash = 100000
    cerebro.broker.setcash(startcash)

    # 設置交易手續費為 0.2%
    cerebro.broker.setcommission(commission=0.002)

    d1=start.strftime('%Y%m%d')
    d2=end.strftime('%Y%m%d')
    print(f'初始資金: {startcash}\n回測期間:{d1}:{d2}')

    #運行回測系統
    cerebro.run()
    #獲取回測結束后的總資金
    portvalue = cerebro.broker.getvalue()
    pnl = portvalue - startcash
    #打印結果
    print(f'總資金: {round(portvalue,2)}')

    cerebro.plot()

backtrader官方文檔:

 

refer:

https://zhuanlan.zhihu.com/p/122183963


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM