使用backtrader數據進行回測,數據源來自于交易所爬取。
效果還行,我相信各位通過這個的框架學習,會對backtrader的應用有更深的領悟。包括數據的連接,新指標的加入。
導入框架:
__future__ import (absolute_import, division, print_function, unicode_literals)
import pymysql
from sqlalchemy import create_engine
import pandas as pd
import numpy as np
import pymssql
import datetime
import os.path
import sys
import backtrader as bt
import pandas as pd
import akshare as ak
import matplotlib
import matplotlib.pyplot as plt
from backtrader.feeds import PandasData
import datetime
from backtrader.feeds import PandasData
starttime = datetime.datetime.now()
print(starttime)
導入數據
df_read = pd.read_csv('option1.csv')
#df_read = df_read[~df_read['ts_code'].isin([0])]date_value_list = []
創建策略
class BollStrategy(bt.Strategy):# 可配置策略參數params = dict(poneplot=False, # 是否打印到同一張圖pstake=1, # 單筆交易股票數據)def __init__(self):self.order = None# for i, d in enumerate(self.datas):# #跳過第一只股票data,第一只股票data作為主圖數據# if i == 0:# if self.p.poneplot:# d.plotinfo.plotmaster = self.datas[0]def prenext(self):# for i,d in enumerate(self.datas):# print(d._name)self.next()# 策略核心,根據條件執行買賣交易指令(必選)def next(self):# 獲取當天日期date = self.datas[0].datetime.date(0)# 獲取當天valuevalue = self.broker.getvalue()# 存入列表date_value_list.append((date, value))for i,d in enumerate(self.datas):#self.log(f'收盤價,{self.datas[i].close[0]}')dt,dn = self.datetime.date(), d._name #獲取時間和股票代碼print(self.datas[i].rt[0])if self.order:returnpos = self.getposition(d).sizeif not pos:if self.datas[i].close[0] > 0:self.log(d._name, 'SELL Create, %2f' %self.datas[i].close[0])self.order = self.sell(d , size=self.p.pstake)elif self.datas[i].close[0] == 0 or self.datas[i].close[0] > pos.price * 2 or self.datas[i].rt[0] == 0 :self.log(d._name, 'CLOSE Create, %2f' % self.datas[i].close[0])self.order = self.close(d , size= self.p.pstake)#pass# print(self.datetime.date())# for i, d in enumerate(self.datas):# pos = self.getposition(d)# if not len(pos):# if d.close[0]>0:# self.sell(d,size= self.p.pstake)# elif d.close[0] ==0 or d.close[0] > pos.price * 2:# self.close(d,size= self.p.pstake)# for d in self.datas:# if len(d) == 0:# continue# else:# # 獲取當天日期# date = self.datetime.date()# print(self.datas[0].datetime[0])# #print(date)# #查看持倉盈利情況# for i,d in enumerate(self.datas):# pos = self.getposition(d)# # if self.datas[i].close[0] < 0.05:# # self.close(d,size = self.params.pstake)# if len(pos):# # print('{}, 持倉:{}, 成本價:{}, 當前價:{}, 盈虧:{:.2f}'.format(# # d._name, pos.size, pos.price, pos.adjbase, pos.size * (pos.adjbase - pos.price)),# # file=self.log_file)# # print('{}, 持倉:{}, 成本價:{}, 當前價:{}, 盈虧:{:.2f}'.format(# # d._name, pos.size, pos.price, pos.adjbase, pos.size * (pos.adjbase - pos.price)),# # )# if self.datas[i].close[0] == 0:# self.close(d,size=self.params.pstake)# # 獲取當天value# value = self.broker.getvalue()## # 存入列表# date_value_list.append((date, value))## for i,d in enumerate(self.datas):# #if self.datas[i].close[0] > 0.01:# try:# if self.datas[i].close[0] > 0:# #print('buy',d.close[0])# self.sell(data = d, size=self.params.pstake)# #print(d._name)# #order.addinfo(ticker=d._name)# #print(d._name)# # else:# # #print('sell',d.close[0])# # self.close(data = d, size=self.params.pstake)# # #order.addinfo(ticker=d._name)# # #print(d._name)# except:# pass#交易記錄日志def log(self,txt,dt = None,doprint=False):dt = dt or self.datas[0].datetime.date(0)f = open("log.txt",'a')date = self.datas[0].datetime.date(0)f.write(f'{date},{txt}')f.write("\n")f.close()print(f'{txt}')# 記錄交易收益情況(可省略,默認不輸出結果)def notify_trade(self, trade):if not trade.isclosed:returnself.log(f'策略收益:\n毛收益 {trade.pnl:.2f}, 凈收益 {trade.pnlcomm:.2f}')# 訂單狀態變化時引擎會調用notify_order# 記錄交易執行情況(可省略,默認不輸出結果)def notify_order(self, order):#print(order.getstatusname(order.status))if order.status in [order.Submitted, order.Accepted]:return# 如果交易已經完成,顯示成交信息if order.status in [order.Completed]:if order.isbuy() or order.issell():self.log(f'買入:\n價格:{order.executed.price},\成本:{order.executed.value},\手續費:{order.executed.comm}')self.buyprice = order.executed.priceself.buycomm = order.executed.commelse:self.log(f'賣出:\n價格:{order.executed.price},\成本: {order.executed.value},\手續費{order.executed.comm}')self.bar_executed = len(self)elif order.status in [order.Canceled, order.Margin, order.Rejected]:self.log('交易失敗')self.order = None
#回測結束后輸出結果(可省略,默認輸出結果)# def stop(self):# self.log('(MA均線: %2d日) 期末總資金 %.2f' %# (self.params.maperiod, self.broker.getvalue()), doprint=True)def stop(self):self.log('期末總資金 %.2f' %(self.broker.getvalue()), doprint=True)
添加參數
#添加參數
class ETFOptionPandasData(PandasData):# 新增兩條數據線lines = ('rt',)# 新增數據在dataframe中的位置,分別是第6列和第7列params = (('rt', 6),)
調取數據,放入cerebro
cerebro = bt.Cerebro()
# 建立期權池
stk_pools = df_read['ts_code'].unique().tolist()
# 獲取期權數據
for stk_code in stk_pools:df = df_read[df_read['ts_code'] == stk_code]df.index = pd.to_datetime(df['datetime'])#print(df.head())#data = ETFOptionPandasData(dataname=df)df = df[['ts_code', 'open', 'high', 'low', 'close', 'volume','rt']]#print(df.head())data = ETFOptionPandasData(dataname = df,datetime = -1)cerebro.adddata(data, name = str(stk_code))
運行策略,添加交易參數
cerebro.broker.setcash(10000000.0)
cerebro.broker.setcommission(commission=1.62,margin = 2000,mult= 10000 )
cerebro.addstrategy(BollStrategy)
cerebro.broker.set_coc(True)#設置以當天收盤價成交
cerebro.run() #減少內存。此設置會自動禁止數據預加載(preload)和指標預計算(runonce),也禁止繪圖plot,因為內存中數據不足以繪圖了。
for d in cerebro.datas:d.plotinfo.plot =False
print('cash',cerebro.broker.getvalue())