量化回测:backtrader
简介
它是基于某种量化回测框架构建, 其优点在于运行速度较快, 具备支持矢量运算的能力, 还能够支持参数自动寻优运算, 并且内置了talib股票分析技术指标库, 它支持多品种、多策略以及多周期的回测和交易, 同时支持分析模块库、多因子分析模块库等, 它扩展灵活, 能够集成机器学习、神经网络分析模块, 比如和Keras等。
如果将包分解为核心组件,主要包括以下组成部分:
(1)数据加载, 也就是Data Feed,是要把交易策略的数据, 加载到回测框架里来。
(2)交易策略(): 此模块属于编程进程里最为复杂的部分, 要进行交易决策的设计, 进而得出买入或者卖出信号。
(3)设置回测框架时, 要设置初始资金, 要设置佣金, 要设置数据馈送, 要设置交易策略, 要设置交易头寸大小。
(4)运行回测:运行回测并打印出所有已执行的交易。
(5)评估性能, 其方式为凭借图形以及风险收益等指标, 针对交易策略的回测结果展开评价。
“Lines”是用于回测的数据, 它是凭借一系列的点给构成的, 一般情况下会涵盖以下类别的数据, 有Open也就是开盘价, 还有High即最高价, Low也就是最低价, Close是收盘价, 另外还有成交量, 要是没有成交量的数据那就设置为0。Data Feeds也就是数据加载, 还有技术指标这一项以及策略都会产生Lines。价格数据范畴里所有的”Open” 也就是开盘价依据时间凝聚成一条Line。所以, 一组具备上述6个类别的价格数据, 总共会有6条Lines。要是把“”(时间, 这能视作一组数据的主键)算进去, 总共有7条Lines。当去访问一条Line的数据时, 会默认朝着下标为0的数据指去。最后一个数据借助下标 -1来访问, 在 -1之后则是索引0, 用来访问当前时刻。所以,在回测过程里, 不用晓得已经处理了多少条/分钟/天/月, ”0”始终朝着当前值指向, 用下标 -1去访问最后一个值。
编写回测应用构建策略()
每一个策略都是一个类,是一个继承bt.类的父类。
策略类代码, 其中含有重要的参数, 还有用于执行策略的功能, 要定义的参数或者函数名是如下这样的。
(1)这里存在全局参数, 它具有可选特点, 能够做到更改那个交易策略里变量或者参数的值, 并且这个全局参数作用是可用于参数调优。
(2)日志, 即log, 可选的是, 记录策略的执行日志, 该日志能够打印出由该函数所提供的日期时间以及txt变量。
(3) :用于初始化交易策略的类实例的代码。
(4)选项之中, 存在这样一种情况, 即能够对交易指令(order)的状态予以跟踪。交易指令(order)呈现出多种状态, 其中包括提交状态, 还有接受状态, 另外有买入/卖出执行状态以及价格方面的相关体现, 同时也涵盖已取消/拒绝等状态。
(5)其一, 存在可供选择的情况, 即对交易的状态予以跟踪, 其二, 任何已经完成平仓操作的交易都将会去报告有关毛利以及净利润的情况。
(6)紧接着, 在必须选择的情况之下: 存在制定交易策略的函数, 它属于策略模块最为核心的那一部分。
下面拿一个简易的单均线策略当作例子, 来呈现策略的撰写进程, 也就是说只要收盘价往上突破20日均线就选择买入(做多), 在收盘价往下跌穿20日均线时就进行卖出(做空)。出于简单的缘故, 不汇报交易回测的日志, 所以log以及函数省略不计去写。
classmy_strategy1(bt.Strategy):#全局设定交易策略的参数params=( ('maperiod',20), )def__init__(self):#指定价格序列self.dataclose=self.datas[0].close#初始化交易指令、买卖价格和手续费self.order =None self.buyprice=None self.buycomm=None#添加移动均线指标,内置了talib模块self.sma =bt.indicators.SimpleMovingAverage( self.datas[0], period=self.params.maperiod)defnext(self):ifself.order:#检查是否有指令等待执行,return#检查是否持仓ifnotself.position:#没有持仓#执行买入条件判断:收盘价格上涨突破20日均线ifself.dataclose[0] >self.sma[0]:#执行买入self.order = self.buy(size=500)else:#执行卖出条件判断:收盘价格跌破20日均线ifself.dataclose[0] <self.sma[0]:#执行卖出self.order = self.sell(size=500)
self.dataclose[0]#当日的收盘价self.dataclose[-1]#昨天的收盘价self.dataclose[-2]#前天的收盘价
获取数据加载数据
#使用tushare旧版接口获取数据importtushare as tsdefget_data(code,start='2010-01-01',end='2020-03-31'): df=ts.get_k_data(code,autype='qfq',start=start,end=end) df.index=pd.to_datetime(df.date) df['openinterest']=0 df=df[['open','high','low','close','volume','openinterest']]returndf dataframe=get_data('600000')#回测期间start=datetime(2010, 3, 31) end=datetime(2020, 3, 31)#加载数据data = bt.feeds.PandasData(dataname=dataframe,fromdate=start,todate=end)
在这里要留意, 针对每一种数据来说, 有其各自的来源, 并且存在一定的标准。而这个标准呢, 就是这些列的名字分别是open, close, high, low。
Ts供我们获取数据的列有open、close、high、low以及code, 在此之上我们还需添加一列, 这里没用到, 把它设为零就行: df。
''
=0。
另外进行要求之下的, 那个index是时间, 所以要做出设置, 设置为: df.index赋值给pd这里 df.date。
最后:df=df
“打开”, “高的”, “低的”, “关闭”, “”, “”。
创建并设置模型
#回测期间start=datetime(2010, 3, 31) end=datetime(2020, 3, 31)#加载数据data = bt.feeds.PandasData(dataname=dataframe,fromdate=start,todate=end)#初始化cerebro回测系统设置cerebro =bt.Cerebro()#将数据传入回测系统cerebro.adddata(data)#将交易策略加载到回测系统中cerebro.addstrategy(my_strategy1)#设置初始资本为10,0000startcash = 100000cerebro.broker.setcash(startcash)#设置交易手续费为 0.2%cerebro.broker.setcommission(commission=0.002)
执行回测并可视化
d1=start.strftime('%Y%m%d') d2=end.strftime('%Y%m%d')print(f'初始资金: {startcash}\n回测期间:{d1}:{d2}')#运行回测系统cerebro.run()#获取回测结束后的总资金portvalue =cerebro.broker.getvalue() pnl= portvalue -startcash#打印结果print(f'总资金: {round(portvalue,2)}') cerebro.plot()
源码
#-*- coding:utf-8 -*-#正常显示画图时出现的中文和负号frompylabimportmpl mpl.rcParams['font.sans-serif']=['SimHei']importpandas as pdfromdatetimeimportdatetimeimportbacktrader as btimporttushare as ts#策略classmy_strategy1(bt.Strategy):#全局设定交易策略的参数params=( ('maperiod',20), )def__init__(self):#指定价格序列self.dataclose=self.datas[0].close#初始化交易指令、买卖价格和手续费self.order =None self.buyprice=None self.buycomm=None#添加移动均线指标,内置了talib模块self.sma =bt.indicators.SimpleMovingAverage( self.datas[0], period=self.params.maperiod)defnext(self):ifself.order:#检查是否有指令等待执行,return#检查是否持仓ifnotself.position:#没有持仓#执行买入条件判断:收盘价格上涨突破20日均线ifself.dataclose[0] >self.sma[0]:#执行买入self.order = self.buy(size=500)else:#执行卖出条件判断:收盘价格跌破20日均线ifself.dataclose[0] <self.sma[0]:#执行卖出self.order = self.sell(size=500)defget_data(code,start='2010-01-01',end='2020-03-31'): df=ts.get_k_data(code,autype='qfq',start=start,end=end)#获取的数据格式: open close high low volume code#将date变成DataFrame的indexdf.index=pd.to_datetime(df.date) df['openinterest']=0 df=df[['open','high','low','close','volume','openinterest']]returndfif__name__=="__main__":#获取数据dataframe=get_data('600000')#回测期间start=datetime(2010, 3, 31) end=datetime(2020, 3, 31)#加载数据data = bt.feeds.PandasData(dataname=dataframe,fromdate=start,todate=end)#初始化cerebro回测系统设置cerebro =bt.Cerebro()#将数据传入回测系统cerebro.adddata(data)#将交易策略加载到回测系统中cerebro.addstrategy(my_strategy1)#设置初始资本为10,000startcash = 100000cerebro.broker.setcash(startcash)#设置交易手续费为 0.2%cerebro.broker.setcommission(commission=0.002) d1=start.strftime('%Y%m%d') d2=end.strftime('%Y%m%d')print(f'初始资金: {startcash}\n回测期间:{d1}:{d2}')#运行回测系统cerebro.run()#获取回测结束后的总资金portvalue =cerebro.broker.getvalue() pnl= portvalue -startcash#打印结果print(f'总资金: {round(portvalue,2)}') cerebro.plot()
官方文档:
refer:
