一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

如何用backtrader對(duì)股票組合進(jìn)行量化回測(cè)?

 老三的休閑書屋 2020-05-29

01 引言

backtrader是功能非常強(qiáng)大的量化回測(cè)框架之一,得到歐洲很多銀行、基金等金融機(jī)構(gòu)的青睞,并應(yīng)用于實(shí)盤交易中。公眾號(hào)Python金融量化針對(duì)backtrader的入門和應(yīng)用已連續(xù)發(fā)布了四篇推文:《【手把手教你】入門量化回測(cè)最強(qiáng)神器backtrader(一)》、《【手把手教你】入門量化回測(cè)最強(qiáng)神器backtrader(二)》、《【手把手教你】入門量化回測(cè)最強(qiáng)神器backtrader(三)》和《backtrader如何加載股票因子數(shù)據(jù)?以換手率、市盈率為例進(jìn)行回測(cè)【附Python代碼】》,分別介紹了backtrader整個(gè)框架的組成部分、回測(cè)系統(tǒng)的運(yùn)行,策略模塊交易日志的編寫、策略參數(shù)的尋優(yōu),Analyzers模塊的分析、策略業(yè)績(jī)?cè)u(píng)價(jià)指標(biāo)可視化分析以及擴(kuò)張數(shù)據(jù)加載模塊對(duì)市盈率因子進(jìn)行回測(cè)等。上述文章有一個(gè)共同點(diǎn)是回測(cè)實(shí)例均以個(gè)股為交易標(biāo)的,那么如何對(duì)股票組合進(jìn)行回測(cè)呢?本文將重點(diǎn)介紹如何加載多只股票數(shù)據(jù),并構(gòu)建交易組合進(jìn)行量化回測(cè)。

02 股票組合回測(cè)實(shí)例

數(shù)據(jù)獲取

第一步是數(shù)據(jù)獲取和加載,A股數(shù)據(jù)個(gè)人一般使用tushare來獲取,由于對(duì)多只股頻繁獲取容易出現(xiàn)接口報(bào)錯(cuò),因此個(gè)人在本地搭建了一個(gè)股票數(shù)據(jù)庫(關(guān)于數(shù)據(jù)庫搭建請(qǐng)參照:《【手把手教你】Python面向?qū)ο缶幊倘腴T及股票數(shù)據(jù)管理應(yīng)用實(shí)例》)。注意,下面導(dǎo)入的update_sql和base是自己寫的本地腳本,使用自己數(shù)據(jù)運(yùn)行時(shí)請(qǐng)將其注釋掉。

import backtrader as btimport pandas as pd#以下引入腳本是個(gè)人的數(shù)據(jù)庫文件,導(dǎo)入其他數(shù)據(jù)請(qǐng)注釋掉from update_sql import update_sql#更新數(shù)據(jù)庫update_sql(table_name='daily_data')
from base import sql_engine,ts_propro=ts_pro()engine = sql_engine()def get_data(code):    sql=f'select * from daily_data where trade_date>'20150201' and ts_code='{code}''    data=pd.read_sql(sql,engine)    data=data.sort_values('trade_date')    #前復(fù)權(quán)    data['adjclose']=(data.close*data.adj_factor/data.adj_factor.iloc[-1]).values    data['adjvol']=(data.vol*data.adj_factor/data.adj_factor.iloc[-1]).values    data['adjopen']=(data.open*data.adj_factor/data.adj_factor.iloc[-1]).values    data['adjhigh']=(data.high*data.adj_factor/data.adj_factor.iloc[-1]).values    data['adjlow']=(data.low*data.adj_factor/data.adj_factor.iloc[-1]).values    data=data[['trade_date','adjopen','adjhigh','adjlow','adjclose','adjvol']]    n1=['open','high','low','close','volume']    n2=['adjopen','adjhigh','adjlow','adjclose','adjvol']    data.rename(columns=dict(zip(n2,n1)),inplace=True)    data.index=pd.to_datetime(data.trade_date)    data=data.sort_index()    data['openinterest']=0    data['datetime']=data.index    data=data[['datetime','open','high','low','close','volume']]    return data如果不會(huì)搭建數(shù)據(jù)庫,也可以使用tushare pro直接在線獲取數(shù)據(jù),并轉(zhuǎn)化為backtrader能接受的數(shù)據(jù)格式。
import tushare as ts#tushare pro需到官網(wǎng)注冊(cè)并獲取token才能用token='輸入你的token'pro=ts.pro_api(token)def get_data2(code,date='20150101'):    data=ts.pro_bar(ts_code=code, adj='qfq', start_date=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']]    data=data.fillna(0)    return data由于A股全市場(chǎng)有三千多只股票,如果對(duì)所有股票進(jìn)行遍歷構(gòu)建交易策略,Python循環(huán)起來會(huì)非常慢。為了節(jié)省時(shí)間,下面先對(duì)市場(chǎng)個(gè)股進(jìn)行一次篩選,根據(jù)個(gè)人偏好,通過條件設(shè)置過濾掉大部分股票。
def get_code_list(date='20150202'):    #默認(rèn)2010年開始回測(cè)    dd=pro.daily_basic(trade_date=date)    x1=dd.close<100    #流通市值低于300億大于50億    x2=dd.circ_mv>500000    x3=dd.circ_mv<3000000    #市盈率低于80    x4=dd.pe_ttm<80    #股息率大于2%    x5=dd.dv_ttm>3    x=x1&x2&x3&x4&x5    stock_list=dd[x].ts_code.values    return stock_list通過價(jià)格、市值、市盈率和股息率指標(biāo)的設(shè)置,選擇了24只個(gè)股進(jìn)行量化回測(cè)。
len(get_code_list())
24


策略編寫

下面以一個(gè)簡(jiǎn)單的“動(dòng)量+趨勢(shì)跟蹤”策略作為示例。策略思路為:計(jì)算24只股票過去30日的收益率并進(jìn)行排序,選擇前10只股票加入選股池(動(dòng)量),逐日滾動(dòng)計(jì)算和判斷:如果選股池中某只個(gè)股滿足股價(jià)位于20均線以上且沒有持倉時(shí)買入(以20日均線為生命線跟蹤趨勢(shì));如果某只個(gè)股已持倉但判斷不在選股池中或股價(jià)位于20均線以下則賣出。每次交易根據(jù)十只個(gè)股平均持倉(注意:最多交易10只個(gè)股)。

class MyStrategy(bt.Strategy):    # 策略參數(shù)    params = dict(        period=20,  # 均線周期        look_back_days=30,        printlog=False    )    def __init__(self):        self.mas = dict()        #遍歷所有股票,計(jì)算20日均線        for data in self.datas:            self.mas[data._name] = bt.ind.SMA(data.close, period=self.p.period)     def next(self):        #計(jì)算截面收益率        rate_list=[]        for data in self.datas:            if len(data)>self.p.look_back_days:                p0=data.close[0]                pn=data.close[-self.p.look_back_days]                rate=(p0-pn)/pn                rate_list.append([data._name,rate])        #股票池           long_list=[]        sorted_rate=sorted(rate_list,key=lambda x:x[1],reverse=True)        long_list=[i[0] for i in sorted_rate[:10]]        # 得到當(dāng)前的賬戶價(jià)值        total_value = self.broker.getvalue()        p_value = total_value*0.9/10        for data in self.datas:            #獲取倉位            pos = self.getposition(data).size            if not pos and data._name in long_list and \              self.mas[data._name][0]>data.close[0]:                size=int(p_value/100/data.close[0])*100                self.buy(data = data, size = size)             if pos!=0 and data._name not in long_list or \              self.mas[data._name][0]<data.close[0]:                self.close(data = data)                            def log(self, txt, dt=None,doprint=False):        if self.params.printlog or doprint:            dt = dt or self.datas[0].datetime.date(0)            print(f'{dt.isoformat()},{txt}')    #記錄交易執(zhí)行情況(可省略,默認(rèn)不輸出結(jié)果)    def notify_order(self, order):        # 如果order為submitted/accepted,返回空        if order.status in [order.Submitted, order.Accepted]:            return        # 如果order為buy/sell executed,報(bào)告價(jià)格結(jié)果        if order.status in [order.Completed]:             if order.isbuy():                self.log(f'買入:\n價(jià)格:{order.executed.price:.2f},\                成本:{order.executed.value:.2f},\                手續(xù)費(fèi):{order.executed.comm:.2f}')                self.buyprice = order.executed.price                self.buycomm = order.executed.comm            else:                self.log(f'賣出:\n價(jià)格:{order.executed.price:.2f},\                成本: {order.executed.value:.2f},\                手續(xù)費(fèi){order.executed.comm:.2f}')            self.bar_executed = len(self)         # 如果指令取消/交易失敗, 報(bào)告結(jié)果        elif order.status in [order.Canceled, order.Margin, order.Rejected]:            self.log('交易失敗')        self.order = None    #記錄交易收益情況(可省略,默認(rèn)不輸出結(jié)果)    def notify_trade(self,trade):        if not trade.isclosed:            return        self.log(f'策略收益:\n毛收益 {trade.pnl:.2f}, 凈收益 {trade.pnlcomm:.2f}')

數(shù)據(jù)加載和回測(cè)系統(tǒng)設(shè)置

寫一個(gè)循環(huán)遍歷24只個(gè)股數(shù)據(jù)并加載到回測(cè)系統(tǒng)中,將初始本金設(shè)置為10萬元,手續(xù)費(fèi)為千分之一,回測(cè)結(jié)束打印出交易日記。

#加載數(shù)據(jù)

cerebro = bt.Cerebro()
for s in get_code_list():
feed = bt.feeds.PandasData(dataname = get_data(s))
cerebro.adddata(feed, name = s)
#回測(cè)設(shè)置
startcash=100000.0
cerebro.broker.setcash(startcash)
# 設(shè)置傭金為千分之一
cerebro.broker.setcommission(commission=0.001)
# 添加策略
cerebro.addstrategy(MyStrategy,printlog=True)
cerebro.run()
#獲取回測(cè)結(jié)束后的總資金
portvalue = cerebro.broker.getvalue()
pnl = portvalue - startcash
#打印結(jié)果
print(f'總資金: {round(portvalue,2)}')
print(f'凈收益: {round(pnl,2)}')

輸出結(jié)果:

2015-04-27,買入:價(jià)格:14.54,成本:8724.53, 手續(xù)費(fèi):8.722015-04-27,買入:價(jià)格:22.34,成本:8934.14, 手續(xù)費(fèi):8.932015-04-28,賣出:價(jià)格:23.06,成本: 8934.14, 手續(xù)費(fèi)9.222015-04-28,策略收益:毛收益 287.97, 凈收益 269.82......2020-05-26,策略收益:毛收益 624.00, 凈收益 591.712020-05-26,策略收益:毛收益 570.00, 凈收益 537.052020-05-26,策略收益:毛收益 40.00, 凈收益 7.372020-05-26,策略收益:毛收益 561.00, 凈收益 527.872020-05-27,買入:價(jià)格:20.00,成本:16000.00,手續(xù)費(fèi):16.00總資金: 182914.68凈收益: 82914.68

策略回測(cè)結(jié)果可視化

cerebro = bt.Cerebro()

for s in get_code_list():
feed = bt.feeds.PandasData(dataname = get_data(s))
cerebro.adddata(feed, name = s)
#回測(cè)設(shè)置
startcash=100000.0
cerebro.broker.setcash(startcash)
# 設(shè)置傭金為千分之一
cerebro.broker.setcommission(commission=0.001)
# 添加策略
cerebro.addstrategy(MyStrategy)

注意,plot_result是自己寫的對(duì)回測(cè)結(jié)果進(jìn)行可視化的腳本文件,代碼比較長(zhǎng),此處省略,完整代碼分享在“金融量化知識(shí)星球中(文末)。

bt.plot_result(cerebro)

如何用backtrader對(duì)股票組合進(jìn)行量化回測(cè)?

如何用backtrader對(duì)股票組合進(jìn)行量化回測(cè)?

如何用backtrader對(duì)股票組合進(jìn)行量化回測(cè)?

03 結(jié)語

本文著重介紹了如何使用backtrader進(jìn)行股票組合量化回測(cè),回測(cè)實(shí)例僅供參考,不構(gòu)成任何交易建議。文中構(gòu)建的“動(dòng)量+趨勢(shì)跟蹤”策略,并沒有對(duì)相關(guān)參數(shù)進(jìn)行優(yōu)化,而且股票組合的選股范圍較?。ùx股票只有24只,而每次交易組合不超過10只),不同交易周期、不同標(biāo)的參數(shù)閾值設(shè)置可能存在較大差異。從回測(cè)結(jié)果的評(píng)價(jià)指標(biāo)來看,該策略并不是很理想,年化收益只有11%,最大回撤21%,夏普比率只有0.95。當(dāng)然,本文的目的不在于結(jié)果而是過程。總之,兄弟我先拋塊磚,有玉的盡管砸過來

參考資料:

1. backtrader官方文檔和安裝包原生代碼

https://www./docu/

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    亚洲午夜精品视频观看| 国产精品一区二区丝袜| 久久天堂夜夜一本婷婷| 大香伊蕉欧美一区二区三区| 欧美日韩一级黄片免费观看| 中文字幕亚洲精品在线播放| 国产精品二区三区免费播放心| 久久精品中文字幕人妻中文| 好骚国产99在线中文| 一区二区三区日韩中文| 日韩少妇人妻中文字幕| 欧美色婷婷综合狠狠爱| 五月婷婷综合缴情六月| 国产日韩精品激情在线观看| 中文字幕一区二区久久综合| 久久免费精品拍拍一区二区| 亚洲女同一区二区另类| 中文字幕亚洲在线一区| 国产av一二三区在线观看| 91插插插外国一区二区| 少妇高潮呻吟浪语91| 国产又猛又大又长又粗| 日韩欧美三级中文字幕| 微拍一区二区三区福利| 国产又粗又猛又爽色噜噜| 熟女乱一区二区三区四区| 欧美午夜一区二区福利视频| 国产又粗又长又大高潮视频| 亚洲精品美女三级完整版视频| 国产亚洲成av人在线观看| 亚洲夫妻性生活免费视频| 97人妻精品一区二区三区免| 国产精品一区二区成人在线| 国产精品亚洲一区二区| 中文字幕在线区中文色| 日韩中文字幕欧美亚洲| 欧美三级精品在线观看| 欧美日韩精品人妻二区三区| 精品国产日韩一区三区| 日本午夜乱色视频在线观看| 日韩人妻少妇一区二区|