backtrader如何加載股票因子數據?以換手率、市盈率為例進行回測


 

原創 Python金融量化 2020-05-08 17:18:09

1引言

 

關於backtrader,公眾號已連續發布了三篇推文:《【手把手教你】入門量化回測最強神器backtrader(一)》、《【手把手教你】入門量化回測最強神器backtrader(二)》和《【手把手教你】入門量化回測最強神器backtrader(三)》,分別介紹了backtrader整個框架的組成部分、回測系統的運行、策略模塊交易日志的編寫和策略參數的尋優,以及Analyzers模塊的用法,並對策略的業績評價指標進行可視化分析。之前在回測中使用的數據僅限於系統默認的價格和成交量,那么如何加載其它數據或因子呢,如換手率、市盈率(PE)市凈率(PB)和其他財務指標等?其實前面一直強調backtrader由於采用元編程,具有很強的擴展性,本文為大家展示如何擴展feeds模塊中的數據加載,使系統能添加換手率、市盈率等數據,並以市盈率和換手率為指標構建交易策略進行回測。

 

2數據擴展實例


01數據准備

下面使用tushare pro獲取個股交易數據,包含日期(datetime)、價格(open、high、low、close)和成交量(volume)、換手率(turnover_rate)、市盈率(pe)、市凈率(pb)等數據。

import pandas as pd
import tushare as ts
#tushare pro需到官網注冊並獲取token才能用
token='輸入你的token'
pro=ts.pro_api(token)
def get_data(code,date='20200101'):
    data1=ts.pro_bar(ts_code=code, adj='qfq', start_date=date)
    data1=data1[['trade_date','open','high','low','close','vol']]
    data2=pro.daily_basic(ts_code=code,fields='trade_date,turnover_rate,pe,pb')
    data=pd.merge(data1,data2,on='trade_date')
    data.index=pd.to_datetime(data.trade_date)
    data=data.sort_index()
    data['volume']=data.vol
    data['openinterest']=0
    data['datetime']=pd.to_datetime(data.trade_date)
    data=data[['datetime','open','high','low','close',\
               'volume','openinterest','turnover_rate','pe','pb']]
    data=data.fillna(0)
    return data

 

查看數據並保存csv格式到本地,文件名為“test.csv”。

#數據保存到本地
get_data('300002.SZ').to_csv('test.csv',index=False)
get_data('300002.SZ').head()


02擴展feeds中的數據加載

對backtrader相關模塊進行擴展,首先要先研究一下原生代碼的構成,找到安裝文件夾,我裝的是anaconda,所以backtrader所在文件夾路徑為:

C:\Anaconda3\Lib\site-packages\backtrader\,進入該路徑找到feeds文件夾,看到里面有很多py文件,說明backtrader支持加載的數據或類型,在線數據支持quandl和yahoo適合做美股分析。我們關注的是如何加載A股數據,目前只能通過pandas或csv格式導入,於是找到csvgeneric.py和pandafeed.py這兩個文件,用軟件Notepad++(可以打開大部分格式的文本文件)打開看看,以pandafeed.py為例,加載數據的類為class PandasData(feed.DataBase),默認要輸入的數據只有七列,即之前提到的價格和成交量數據,如下圖所示。

 

backtrader如何加載股票因子數據?以換手率、市盈率為例進行回測

 

 

對於元編程,要擴展相應參數,不需要修改源代碼,只需要寫一個class類,然后繼承原來的類,加入新的參數即可,下面加入'turnover_rate','pe','pb',這些指標在將要加載的數據表中分別在第7、8、9列。

 

擴展PandasData類,加載更多列數據

#pandas的數據格式
from backtrader.feeds import PandasData
class Addmoredata(PandasData):
    lines = ('turnover_rate','pe','pb',)
    params = (('turnover_rate',7),('pe',8),('pb',9),)

擴展GenericCSVData加載csv格式數據#直接讀取本地csv格式數據

from backtrader.feeds import GenericCSVData
class AddCsvData(GenericCSVData):
    lines = ('turnover_rate','pe','pb',)
    params = (('turnover_rate',7),('pe',8),('pb',9),)

03測試數據是否加載成功

寫一個簡單策略,直接打印新加入的三列數據,加載數據的時候可以是單只股票,也可以是多只股票。多只股票這個很重要,以后可以用來全市場選股並進行回測。

import backtrader as bt
from datetime import datetime
class TestStrategy1(bt.Strategy):
    def log(self, txt, dt=None):
        dt = dt or self.datas[0].datetime.date(0)
        print('%s, %s' % (dt.isoformat(), txt))
    def next(self):
        self.log(f"換手率:{self.datas[0].turnover_rate[0]},\
          市凈率:{self.datas[0].pb[0]},市盈率:{self.datas[0].pe[0]}")

單只股票數據加載運行測試:

cerebro = bt.Cerebro()
cerebro.addstrategy(TestStrategy1)
feed = Addmoredata(dataname = get_data('300002.SZ','20200420'))
#如果是讀取csv數據使用下式
#feed = AddCsvData(dataname = 'test.csv',dtformat=('%Y-%m-%d'))
cerebro.adddata(feed)
cerebro.run()
輸出結果:
2020-04-20, 換手率:20.8743,市凈率:3.3256,市盈率:158.3584
2020-04-21, 換手率:16.503,市凈率:2.992,市盈率:142.4736
2020-04-22, 換手率:18.2413,市凈率:3.2897,市盈率:156.6477
2020-04-23, 換手率:21.3831,市凈率:3.0793,市盈率:146.6281
2020-04-24, 換手率:16.1957,市凈率:3.1203,市盈率:148.5832
2020-04-27, 換手率:13.0385,市凈率:2.874,市盈率:136.8529
2020-04-28, 換手率:10.3652,市凈率:2.9355,市盈率:0.0
2020-04-29, 換手率:8.3977,市凈率:2.797,市盈率:0.0
2020-04-30, 換手率:8.3719,市凈率:2.8967,市盈率:0.0
2020-05-06, 換手率:9.4114,市凈率:3.0462,市盈率:0.0
2020-05-07, 換手率:9.1606,市凈率:3.013,市盈率:0.0 


多只股票數據加載測試:

class TestStrategy2(bt.Strategy):
    def log(self, txt, dt=None):
        dt = dt or self.datas[0].datetime.date(0)
        print('%s, %s' % (dt.isoformat(), txt))
    def next(self):
        for data in self.datas:
            print(data._name)
            self.log(f"換手率:{data.turnover_rate[0]},\
            市凈率:{data.pb[0]},市盈率:{data.pe[0]}")

 

運行回測,三只股票的指標數據都能加載進來。

cerebro = bt.Cerebro()
cerebro.addstrategy(TestStrategy2)
codes=['600862.SH','300326.SZ','300394.SZ']
#加載最近兩日交易數據
for code in codes:
    feed = Addmoredata(dataname = get_data(code,'20200506'),name=code)
    cerebro.adddata(feed)
cerebro.run()
#輸出結果:
600862.SH
2020-05-06, 換手率:3.3376,市凈率:4.9646,市盈率:39.247
300326.SZ
2020-05-06, 換手率:1.8874,市凈率:6.4762,市盈率:60.2625
300394.SZ
2020-05-06, 換手率:2.833,市凈率:8.6431,市盈率:63.1617
600862.SH
2020-05-07, 換手率:1.8531,市凈率:4.9486,市盈率:39.1208
300326.SZ
2020-05-07, 換手率:2.1378,市凈率:6.5019,市盈率:60.5016
300394.SZ
2020-05-07, 換手率:2.7572,市凈率:8.367,市盈率:61.1439

3換手率、市盈率指標的交易策略實例

下面以加載的換手率和市盈率數據構建交易策略並進行回測。這里舉例的個股為神州泰岳,考慮到其回測期間換手率均值為3%,75%分位數為3.98%,市盈率均值為52,最高166,負值設置為0。基於這些指標的統計規律,將交易策略簡單設置為:當換手率小於3%且市盈率小於50倍時買入,當換手率大於10%或市盈率大於80倍時賣出。

 

class MyStrategy(bt.Strategy):
    def next(self):
        if not self.position: # 沒有持倉
            if self.datas[0].turnover_rate[0]<3 and 0<self.datas[0].pe[0]<50:
                # 得到當前的賬戶價值
                total_value = self.broker.getvalue()
                #1手=100股,滿倉買入
                ss=int((total_value/100)/self.datas[0].close[0])*100
                self.order=self.buy(size=ss)
        else:#持倉,滿足條件全部賣出
            if self.datas[0].turnover_rate[0]>10 or self.datas[0].pe[0]>80 :
                self.close(self.datas[0])

 

運行回測:

cerebro = bt.Cerebro()  
cerebro.addstrategy(MyStrategy)
feed = Addmoredata(dataname = get_data('300002.SZ','20050101'))
cerebro.adddata(feed)
startcash = 100000
cerebro.broker.setcash(startcash) 
cerebro.broker.setcommission(commission=0.001) 
cerebro.run()
portvalue = cerebro.broker.getvalue()
pnl = portvalue - startcash
#打印結果
print(f'期初總資金: {round(startcash,2)}')
print(f'期末總資金: {round(portvalue,2)}')
print(f'凈收益: {round(pnl,2)}')
期初總資金: 100000
期末總資金: 303371.37
凈收益: 203371.37

 

得到回測過程的原生圖:

%matplotlib inline 
cerebro.plot(style='candlestick')
backtrader如何加載股票因子數據?以換手率、市盈率為例進行回測

 

獲取回測的量化評價指標:注意,out_result是自己寫的腳本zjy_plot.py里輸出策略評價指標的函數,由於代碼較長,此處省略,完整代碼分享在“金融量化”知識星球上。

 

#addmoredata是在PandasData上的擴展
ddf=get_data('300002.SZ','20050101')
data = Addmoredata(dataname = ddf)
df00,df0,df1,df2,df3,df4=bt.out_result(MyStrategy,\data,startcash = 100000,commission=0.001)
backtrader如何加載股票因子數據?以換手率、市盈率為例進行回測

 

 

對評價指標進行可視化:

下面pyecharts用的是0.5.11版本,1.0以上版本用法基本上不同。

from pyecharts import*
def plot_result_py(data,v,title,plot_type='line',zoom=False):
    att=data.index
    try:
        attr=att.strftime('%Y%m%d')
    except:
        attr=att
    if plot_type=='line':
        p=Line(title)
        p.add('',attr,list(data[v].round(2)),
         is_symbol_show=False,line_width=2,
        is_datazoom_show=zoom,is_splitline_show=True)
    else:
        p=Bar(title)
        p.add('',attr,[int(i*1000)/10 for i in list(data[v])],
              is_label_show=True,
        is_datazoom_show=zoom,is_splitline_show=True)
    return p

 

賬戶價值plot_result_py(df0,'total_value','賬戶價值')

 

backtrader如何加載股票因子數據?以換手率、市盈率為例進行回測

 

持倉市值

plot_result_py(df4,'total_position_value','持倉市值')

 

backtrader如何加載股票因子數據?以換手率、市盈率為例進行回測

 

 

年化收益率

plot_result_py(df3,'year_rate','年化收益%',plot_type='bar')
backtrader如何加載股票因子數據?以換手率、市盈率為例進行回測

 

策略評價指標

df00

 

backtrader如何加載股票因子數據?以換手率、市盈率為例進行回測

 

 

4結語

 

本文着重介紹了如何在backtrader上通過擴展類編程,加載除價格和成交量外的其他因子數據,並構建交易策略進行回測。文中利用換手率和市盈率指標構建的交易策略僅作為示例,並沒有對相關參數進行優化,而且不同標的參數閾值設置可能存在較大差異,從回測結果的評價指標來看,該策略並不是很理想,盡管總收益率達到3倍,但最大回撤高達68%,夏普比率只有0.34。當然,本文的目的不是兜售某“成功”交易策略,而是介紹量化策略的構建與回測過程,希望能達到“授人以漁”和“拋磚引玉”的作用。

 

 


免責聲明!

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



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