使用tushare采集沪深300ETF数据,并对沪深300ETF采用简单移动平均、指数移动平均、进行双均线策略,最后使用backtrader进行回测。
一、基本概念
双均线策略:运用两条不同周期的移动平均线,即短周期移动平均线和长周期移动平均线的相对大小,研判买进与卖出时机的策略。由短周期均线自下向上穿越长周期均线,所形成的交点,称为金叉。当短周期均线自上而下穿越长周期均线,所形成的交点,称为死叉。
简单移动平均法:对指定期间内的数据做算术平均,新旧数据的权重一样。
指数移动平均法:对指定期间的数据赋予不同的权重,每天数据的权重系数以指数等比形式缩小。
从tushare获取沪深300ETF数据
import pandas as pd
import numpy as np
import backtrader as bt
import datetime,time
import tushare as tsdef get_data(ts_code):#获取沪深300ETF数据pro = ts.pro_api('your token')try :df = pro.fund_daily(ts_code=ts_code)except : time.sleep(0.5)print('获取数据失败')else :print('获取数据成功')#对数据进行处理符合backtrader格式columns = ['trade_date','open','high','low','close','vol']df = df[columns]#转换日期格式df['trade_date'] = df['trade_date'].apply(lambda x: pd.to_datetime(str(x)))bt_col_dict = {'vol':'volume','trade_date':'datetime'}df = df.rename(columns = bt_col_dict)df = df.set_index('datetime')#openinterest 默认为0df['openinterest'] = 0#由于获取的数据的第一行是最新数据,需要重新排列,否则最新日期的均线数据为空df=df.sort_index()return dfdf=get_data('510300.SH')
策略准备
当前策略先计算简单移动平均,后续计算指数移动平均使用bt.indicators.EMA()
class MYstrategy(bt.Strategy):params = dict(pfast = 20, # 快周期pslow = 50) # 慢周期def __init__(self): self.dataclose = self.datas[0].close # Order变量包含持仓数据与状态self.order = None # 初始化移动平均数据 self.slow_sma = bt.indicators.SMA(self.datas[0], period = self.params.pslow) self.fast_sma = bt.indicators.SMA(self.datas[0], period = self.params.pfast)#backtrader内置函数,可以判断两线的交叉点self.crossover = bt.ind.CrossOver(self.fast_sma, self.fast_sma)#订单相关 def notify_order(self, order):if order.status in [order.Submitted, order.Accepted]:#主动买卖的订单提交或接受时 - 不触发return#验证订单是否完成if order.status in [order.Completed]:self.bar_executed = len(self) #重置订单self.order = None#next包含所有交易逻辑def next(self):# 检测是否有未完成订单if self.order:return#验证是否有持仓if not self.position:#如果没有持仓,寻找开仓信号#SMA快线突破SMA慢线if self.crossover > 0:self.order = self.buy()#SMA快线跌破SMA慢线elif self.crossover < 0:self.order = self.sell()else:# 如果已有持仓,寻找平仓信号,此地方选择10日之后平仓if len(self) >= (self.bar_executed + 10):self.order = self.close()
策略执行——backtrader回测
def execute_strategy(pfast,pslow):#初始化cerebro = bt.Cerebro(optreturn=False)#设置数据的参数data = bt.feeds.PandasDirectData(dataname=df)cerebro.adddata(data)#加载策略cerebro.addstrategy(MYstrategy,pslow=pslow,pfast=pfast) #设置初始现金cerebro.broker.set_cash(1000000.0)#设置佣金率cerebro.broker.setcommission(commission=0.0001)#设置固定的购买股数#cerebro.addsizer(bt.sizers.FixedSize, stake=100)#分析框架夏普比率和回撤cerebro.addanalyzer(bt.analyzers.SharpeRatio,_name = 'SharpeRatio')cerebro.addanalyzer(bt.analyzers.DrawDown, _name = 'DW')#初始现金start_cash = cerebro.broker.getvalue()#运行result = cerebro.run()#最终现金end_cash=cerebro.broker.getvalue()#利润计算profit = end_cash-start_cashstrategy = result[0]SR=strategy.analyzers.SharpeRatio.get_analysis()DW=strategy.analyzers.DW.get_analysis()return profit,SR['sharperatio'],DW.drawdown,DW.max.drawdown
策略优化
period = [1,5,10,20,30,60,120,250]
final_results_list = []
for i in period:for j in period:if i < j :result=execute_strategy(i,j)final_results_list.append([i,j,result[0],result[1],result[2],result[3]])pd.DataFrame(final_results_list,columns = ['pfast','pslow','profit','SR','DW','max_DW'])
策略结果
SMA运行结果
EMA运行结果
部分回测图展示
从结果上可以看到,指数移动平均法相比于移动平均法更能反映出短期的价格波动情况,其利润的波动更大,但同时最大回撤也更大。后续也可以用遍历形势求出收益最优组合。