使用tushare采集沪深300ETF数据,并对沪深300ETF采用简单移动平均、指数移动平均、进行双均线策略,最后使用backtrader进行回测。
一、基本概念
双均线策略:运用两条不同周期的移动平均线,即短周期移动平均线和长周期移动平均线的相对大小,研判买进与卖出时机的策略。由短周期均线自下向上穿越长周期均线,所形成的交点,称为金叉。当短周期均线自上而下穿越长周期均线,所形成的交点,称为死叉。
简单移动平均法:对指定期间内的数据做算术平均,新旧数据的权重一样。
指数移动平均法:对指定期间的数据赋予不同的权重,每天数据的权重系数以指数等比形式缩小。
从tushare获取沪深300ETF数据
import pandas as pd
import numpy as np
import backtrader as bt
import datetime,time
import tushare as ts
def 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 默认为0
df['openinterest'] = 0
#由于获取的数据的第一行是最新数据,需要重新排列,否则最新日期的均线数据为空
df=df.sort_index()
return df
df=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_cash
strategy = 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运行结果
部分回测图展示
从结果上可以看到,指数移动平均法相比于移动平均法更能反映出短期的价格波动情况,其利润的波动更大,但同时最大回撤也更大。后续也可以用遍历形势求出收益最优组合。