失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > backtrader程序介绍-策略回测用法

backtrader程序介绍-策略回测用法

时间:2021-01-18 04:20:22

相关推荐

backtrader程序介绍-策略回测用法

backtrader的策略回测初尝

前言

backtrader作为能够在自己的python环境运行的回测程序之一,不得不说很好用。今天进行了初步的学习,稍微进行分享。

一、回测基础步骤

应用backtrader进行回测,首先需要了解backtrader的基础步骤

# 基础步骤from __future__ import print_function, absolute_importimport backtrader as btif __name__== '__main__':cerebro=bt.Cerebro() #实例化了Cerebro引擎,Cerebro引擎在后台创建了一个Broker (代理)实例cerebro.broker.setcash(100000.0) #设置broker的金额print(f'组合开始时的价值:%.2f'%cerebro.broker.getvalue()) cerebro.run() #调用 了cerebro 实例的 run 方法(里面会遍历数据)print(f'组合运行结束后的价值:%.2f'%cerebro.broker.getvalue())

组合开始时的价值:100000.00组合运行结束后的价值:100000.00

回测的基础步骤比较简单

二、加载交易数据

backtrader默认使用的雅虎财经的数据,但是国内现在好像也用不了雅虎财经数据库,但是不要紧,可以采用自己喜欢的数据集进行数据导入,步骤如下:

# 打印出每天的”收盘价”# 新建一个策略class TestStrategy(bt.Strategy):def log(self,txt,dt=None): #此策略的日志记录功能dt=dt or self.datas[0].datetime.date(0)print('%s,%s'%(dt.isoformat(),txt))def __init__(self): #保留对 data[0] 数据系列中“close”行的引用self.dataclose= self.datas[0].closedef next(self): #只需从参考记录该系列的收盘价self.log('Close,%.2f'% self.dataclose[0])

if __name__== '__main__':cerebro=bt.Cerebro()# Add a strategycerebro.addstrategy(TestStrategy)#引入数据modpath=os.path.dirname(os.path.abspath(sys.argv[0]))datapath=os.path.join(modpath,'C:/Users/gws/Desktop/OPTIONS_DATA/600519.csv')#将数据放入Data Feeds,默认的数据集是yahoo的股票数据# 载入自己的数据也是可以的,只不过你需要设定每个列的含义data=bt.feeds.GenericCSVData(dataname=datapath,datetime=0,open=1,high=3,low=4,close=2,volume=5,dtformat=('%Y%m%d'),# 使用datetime来过滤价格数据 的起止范围fromdate=datetime.datetime(,1,1), #不传递此日期之前的数值todate=datetime.datetime(,11,1) #不传递此日期之后的数值)# 将数据 Feed到Cerebrocerebro.adddata(data)#设置投资金额cerebro.broker.setcash(100000.0)print(f'组合初始价值:%.2f'%cerebro.broker.getvalue())#运行brokercerebro.run()print(f'组合期末价值:%.2f'%cerebro.broker.getvalue())

导出的数据如下所示(部分):

组合初始价值:100000.00-01-04,Close,104.59-01-05,Close,104.29-01-06,Close,102.64-01-07,Close,100.77-01-08,Close,99.71-11-01,Close,99.27-11-02,Close,101.95-11-03,Close,99.36-11-04,Close,103.73-11-05,Close,104.55-11-08,Close,105.23-11-09,Close,104.49-01-20,Close,102.12-12-01,Close,102.62-12-02,Close,101.02.......-10-27,Close,1819.00-10-28,Close,1825.49-10-29,Close,1826.08-11-01,Close,1803.00组合期末价值:100000.00

三、进行交易策略的回测

进行策略回测前,需要自定义自己的交易策略:

# 一个新的策略,如果价格三连跌的话,买入class TestStrategy(bt.Strategy):def log(self,txt,dt=None): #此策略的日志记录功能dt=dt or self.datas[0].datetime.date(0)print('%s,%s'%(dt.isoformat(),txt))def __init__(self): #保留对 data[0] 数据系列中“close”行的引用self.dataclose= self.datas[0].closedef next(self): #只需从参考记录该系列的收盘价self.log('Close,%.2f'% self.dataclose[0])if self.dataclose[0]<self.dataclose[-1]: #当前收盘价低于上一个收盘价if self.dataclose[-1]<self.dataclose[-2]: #当前收盘价低于上一个收盘价self.log('BUY CREATE,%.2f'%self.dataclose[0]) #创建买单self.buy()

进行回测:

if __name__== '__main__':cerebro=bt.Cerebro()# Add a strategycerebro.addstrategy(TestStrategy)#引入数据modpath=os.path.dirname(os.path.abspath(sys.argv[0]))datapath=os.path.join(modpath,'C:/Users/gws/Desktop/OPTIONS_DATA/600519.csv')#将数据放入Data Feeds,默认的数据集是yahoo的股票数据# 载入自己的数据也是可以的,只不过你需要设定每个列的含义data=bt.feeds.GenericCSVData(dataname=datapath,datetime=0,open=1,high=3,low=4,close=2,volume=5,dtformat=('%Y%m%d'),# 使用datetime来过滤价格数据 的起止范围fromdate=datetime.datetime(,1,1), #不传递此日期之前的数值todate=datetime.datetime(,12,1) #不传递此日期之后的数值)# 将数据 Feed到Cerebrocerebro.adddata(data)#设置投资金额cerebro.broker.setcash(100000.0)print(f'组合初始价值:%.2f'%cerebro.broker.getvalue())#运行brokercerebro.run()print(f'组合期末价值:%.2f'%cerebro.broker.getvalue())cerebro.plot()

回测文字结果:

组合初始价值:100000.00-01-04,Close,104.59-01-05,Close,104.29-01-05,BUY CREATE,104.29-01-06,Close,102.64-01-06,BUY CREATE,102.64-01-07,Close,100.77-01-07,BUY CREATE,100.77-01-08,Close,99.71-01-08,BUY CREATE,99.71-11-01,Close,99.27-11-01,BUY CREATE,99.27-11-02,Close,101.95-11-03,Close,99.36......-11-05,BUY CREATE,2062.58-11-08,Close,2043.76-11-08,BUY CREATE,2043.76-11-09,Close,1990.67-11-09,BUY CREATE,1990.67-01-20,Close,.60组合期末价值:948494.55

回测结果图

可以看出,这个策略是盈利客观的,但是策略过于简单,在后几年的大涨行情中并没有触发。

继续进行策略的回测

# 不止买入,还要卖出# Strategy 类有一个变量 position 保存当前持有的资产数量# buy() 和 sell() 会返回 被创建的订单 (还未被执行的)# 订单状态改变后将会通知 Strategy 实例的 notify() 方法# 5个柱之后(在第6个时候执行)不管涨跌都卖# 请注意,这里没有指定具体时间,而是指定的柱的数量。一个柱可能代表1分钟、1小时、1天、1星期等等,这取决于你价格数据文件里一条数据代表的周期。

# 创建一个新策略class TestStrategy(bt.Strategy):def log(self,txt,dt=None):dt=dt or self.datas[0].datetime.date(0)print('%s,%s'%(dt.isoformat(),txt))def __init__(self):self.dataclose=self.datas[0].closeself.order=None #跟踪挂单self.buyprice = Noneself.buycomm = None #加入手续费def notify_order(self,order):if order.status in [order.Submitted,order.Accepted]: #经纪商提交/接受/接受的买入/卖出订单 returnif order.status in [pleted]: ## 检查订单是否完成# 注意:如果没有足够的现金,经纪人可能会拒绝订单if order.isbuy():self.log('BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %(order.executed.price,order.executed.value,m))self.buyprice = order.executed.priceself.buycomm = melse:self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %(order.executed.price,order.executed.value,m))self.bar_executed=len(self)elif order.status in [order.Canceled,order.Margin,order.Rejected]:self.log('Order Canceled/Margin/Rejected')self.order=None #写下:无挂单def next(self):self.log('Close,%.2f'%self.dataclose[0])if self.order: ## 检查订单是否处于待处理状态...如果是,我们不能发送第二个returnif not self.position: #检查我们是否在市场上if self.dataclose[0]<self.dataclose[-1]:if self.dataclose[-1]<self.dataclose[-2]:self.log('BUY CREATE,%.2f'%self.dataclose[0])self.order=self.buy() #跟踪创建的订单以避免第二个订单else:if len(self)>=(self.bar_executed+5):self.log('SELL CREATE,%.2f'%self.dataclose[0])self.order=self.sell()

if __name__== '__main__':cerebro=bt.Cerebro()# Add a strategycerebro.addstrategy(TestStrategy)#引入数据modpath=os.path.dirname(os.path.abspath(sys.argv[0]))datapath=os.path.join(modpath,'C:/Users/gws/Desktop/OPTIONS_DATA/600519.csv')#将数据放入Data Feeds,默认的数据集是yahoo的股票数据# 载入自己的数据也是可以的,只不过你需要设定每个列的含义data=bt.feeds.GenericCSVData(dataname=datapath,datetime=0,open=1,high=3,low=4,close=2,volume=5,dtformat=('%Y%m%d'),# 使用datetime来过滤价格数据 的起止范围fromdate=datetime.datetime(,1,1), #不传递此日期之前的数值todate=datetime.datetime(,12,1) #不传递此日期之后的数值)# 将数据 Feed到Cerebrocerebro.adddata(data)#设置投资金额cerebro.broker.setcash(100000.0)#加入手续费cerebro.broker.setcommission(commission=0.001) # 0.001 即是 0.1%print(f'组合初始价值:%.2f'%cerebro.broker.getvalue())#运行brokercerebro.run()print(f'组合期末价值:%.2f'%cerebro.broker.getvalue())cerebro.plot()

回测结果

-01-08,Close,2070.51-11-01,Close,2080.14-11-02,Close,2140.74-11-03,Close,2143.82-11-04,Close,2114.09-11-05,Close,2062.58-11-05,BUY CREATE,2062.58-11-08,BUY EXECUTED, Price: 2041.84, Cost: 2041.84, Comm 2.04-11-08,Close,2043.76-11-09,Close,1990.67-01-20,Close,.60组合期末价值:100912.05

可以看出,策略盈利不多,而且没有第一种简单策略的收益高。

加入技术指标进行回测

# 添加技术指标# 收盘价高于平均价的时候,以市价买入# 持有仓位的时候,如果收盘价低于平均价,卖出# 只有一个待执行的订单

# Create a Strateyclass TestStrategy(bt.Strategy):params = (('maperiod', 15),)def log(self, txt, dt=None):''' Logging function fot this strategy'''dt = dt or self.datas[0].datetime.date(0)print('%s, %s' % (dt.isoformat(), txt))def __init__(self):# Keep a reference to the "close" line in the data[0] dataseriesself.dataclose = self.datas[0].closeself.order = Noneself.buyprice = Noneself.buycomm = None# 添加技术指标self.sma = bt.indicators.SimpleMovingAverage(self.datas[0], period=self.params.maperiod)# 绘图显示的指标bt.indicators.ExponentialMovingAverage(self.datas[0], period=25)bt.indicators.WeightedMovingAverage(self.datas[0], period=25,subplot=True)bt.indicators.StochasticSlow(self.datas[0])bt.indicators.MACDHisto(self.datas[0])rsi = bt.indicators.RSI(self.datas[0])bt.indicators.SmoothedMovingAverage(rsi, period=10)bt.indicators.ATR(self.datas[0], plot=False)def notify_order(self,order):if order.status in [order.Submitted,order.Accepted]: #经纪商提交/接受/接受的买入/卖出订单 returnif order.status in [pleted]: ## 检查订单是否完成# 注意:如果没有足够的现金,经纪人可能会拒绝订单if order.isbuy():self.log('BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %(order.executed.price,order.executed.value,m))self.buyprice = order.executed.priceself.buycomm = melse:self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %(order.executed.price,order.executed.value,m))self.bar_executed=len(self)elif order.status in [order.Canceled,order.Margin,order.Rejected]:self.log('Order Canceled/Margin/Rejected')self.order=None #写下:无挂单def notify_trade(self, trade):if not trade.isclosed:returnself.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %(trade.pnl, trade.pnlcomm))def next(self):self.log('Close,%.2f'%self.dataclose[0])if self.order: ## 检查订单是否处于待处理状态...如果是,我们不能发送第二个returnif not self.position: #检查我们是否在市场上if self.dataclose[0] > self.sma[0]:self.log('BUY CREATE,%.2f'%self.dataclose[0])self.order=self.buy() #跟踪创建的订单以避免第二个订单else:if self.dataclose[0] < self.sma[0]:self.log('SELL CREATE,%.2f'%self.dataclose[0])self.order=self.sell()

if __name__== '__main__':cerebro=bt.Cerebro()# Add a strategycerebro.addstrategy(TestStrategy)#引入数据modpath=os.path.dirname(os.path.abspath(sys.argv[0]))datapath=os.path.join(modpath,'C:/Users/gws/Desktop/OPTIONS_DATA/600519.csv')#将数据放入Data Feeds,默认的数据集是yahoo的股票数据# 载入自己的数据也是可以的,只不过你需要设定每个列的含义data=bt.feeds.GenericCSVData(dataname=datapath,datetime=0,open=1,high=3,low=4,close=2,volume=5,dtformat=('%Y%m%d'),# 使用datetime来过滤价格数据 的起止范围fromdate=datetime.datetime(,1,1), #不传递此日期之前的数值todate=datetime.datetime(,12,1) #不传递此日期之后的数值)# 将数据 Feed到Cerebrocerebro.adddata(data)#设置投资金额cerebro.broker.setcash(100000.0)#加入手续费cerebro.broker.setcommission(commission=0.001) # 0.001 即是 0.1%print(f'组合初始价值:%.2f'%cerebro.broker.getvalue())#运行brokercerebro.run()print(f'组合期末价值:%.2f'%cerebro.broker.getvalue())cerebro.plot()

回测结果

-11-05, Close,2062.58-11-08, Close,2043.76-11-09, Close,1990.67-11-09, SELL CREATE,1990.67-01-20, Close,.60组合期末价值:100998.60

回测后的收益不高。

加入手续费后,后两者策略的表现都不好,从到的收益率还不如余额宝收益率,看来后两者策略在该标的上的表现不如意。

总结

本文简单介绍了backtrader的回测用法。有兴趣的可以在官网进行深入的学习。

Backtrader 官网:/docu/plotting/plotting/

如果觉得《backtrader程序介绍-策略回测用法》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。