使用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())