策略如下:
- 回测区间为2016年10月10日至2017年10月13日,选择沪深300进行回测。
- 记录所有当天5日滑动平均价格高于20日滑动平均价格的股票
- 将总资金额的一半n/2用于买入股票,每一支股票按照等额购入
- 买入后的第二天清仓
回测过程:
数据获取(获取股票收盘价stock_price、大盘收盘价benchmark_price以及公司名称stocks):
def getPrice():
stocks_name='沪深300'
start_date='2016-10-01'
end_date='2017-10-13'
fields=['ClosingPx']
#选择沪深300里面所有的股票代码
stocks=index_components(stocks_name)
#正式获取股票价格
stock_price=get_price(stocks,start_date=start_date,end_date=end_date,fields=fields)
#获得沪深300指数
benchmark_name='399300.XSHE'
benchmark_price=get_price(benchmark_name,start_date=start_date,end_date=end_date,fields=fields)
return stock_price,benchmark_price,stocks
这里对沪深300指数解释一下:对样本空间内股票在最近一年(新股为上市以来)的日均成交金额由高到低进行排名,剔除排名在后50%的股票,然后对剩余股票按照日均总市值由高到低进行排名,选取排名在前300名的股票作为指数样本。
数据处理——计算滑动平均数(长短期):
def getRolling(data,shortPeriod,longPeriod):
for i in range(len(data.ix[1])):
col=data.ix[:,i]
name=data.columns[i]
data[name+'_'+str(shortPeriod)+'days']=col.rolling(window=shortPeriod).mean()
data[name+'_'+str(longPeriod)+'days']=col.rolling(window=longPeriod).mean()
return data
主体计算过程:由于每次仅用总资金额的一半即n/2,而且购买后第二天卖出,因此每次购买时资金额都是充足。而且每一支股票投入相同资金,因此直接将所有股票当天收益率取平均作为当天整体的收益率。循环过程从20天滑动平均收盘价开始,第i天作为得到信息的时间,第i+1天购入,第i+2天卖出。
def calculate(data,benchmark_price,stocks):
#初始化累积收益率等
IRR=1
benchmark_revenue=1
victories=0
profits=[]
benchmark_profits=[]
#计算交易的日数
length=0
for i in range(len(data.index)-2):
portfolio=[]
print(data.index[i])
for j,company in enumerate(stocks):
if(data.ix[i,company+'_5days']>data.ix[i,company+'_20days']):
portfolio.append(company)
#如果当天存在出现该信号的公司,则购买
if(portfolio):
length=length+1
#计算策略当日收益率
new=data.ix[i+2,portfolio]/data.ix[i+1,portfolio]
#计算大盘当日收益率
benchmark_new=benchmark_price.ix[i+2]/benchmark_price.ix[i+1]
benchmark_revenue=benchmark_revenue*benchmark_new
data.ix[i+2,'benchmark_profits']=benchmark_revenue
#计算胜率
if(new.mean()>(benchmark_new)):
victories=victories+1
#固定印花说0.1%,手续费0.08%和滑点0.246%
IRR=IRR*(new.mean()-(0.001+0.0008+0.00246))
data.ix[i+2,'profits']=IRR
#如果当天不出现信号,策略的累计收益率不变,大盘的依然要更新
else:
data.ix[i+2,'profits']=data.ix[i+1,'profits']
benchmark_new=benchmark_price.ix[i+2]/benchmark_price.ix[i+1]
benchmark_revenue=benchmark_revenue*benchmark_new
data.ix[i+2,'benchmark_profits']=benchmark_revenue
#计算最大回撤
Max_drawdown=max_drawdown(data['profits'])
#为了便于画图,我先将策略累计收益率和大盘结合
plotData=pd.concat([data['profits'],data['benchmark_profits']],axis=1)
plotData.plot()
#计算夏普率
Sharpo_Ratio=Sharpo(data['profits'],data['benchmark_profits'])
return [IRR,victories/length,Sharpo_Ratio,Max_drawdown]
最大回撤计算:
def max_drawdown(timeseries):
max=-100
for i in np.arange(0,len(timeseries)-1,1):
for j in np.arange(i,len(timeseries),1):
if((timeseries.iloc[i]-timeseries.iloc[j])/timeseries.iloc[j])>max:
max=(timeseries.iloc[i]-timeseries.iloc[j])/timeseries.iloc[j]
return max
夏普率:
def Sharpo(strategy_profits,benchmark_profits):
Sharpo_ratio=(np.mean(strategy_profits)-np.mean(benchmark_profits))/np.std(strategy_profits)
return Sharpo_ratio*np.sqrt(252)
胜率计算:VictoryRatio=日收益率大于大盘的日数\策略真正交易的日数
讲解结束,全部代码如下:
import pandas as pd
import numpy as np
def max_drawdown(timeseries):
max=-100
for i in np.arange(0,len(timeseries)-1,1):
for j in np.arange(i,len(timeseries),1):
if((timeseries.iloc[i]-timeseries.iloc[j])/timeseries.iloc[j])>max:
max=(timeseries.iloc[i]-timeseries.iloc[j])/timeseries.iloc[j]
return max
def Sharpo(strategy_profits,benchmark_profits):
Sharpo_ratio=(np.mean(strategy_profits)-np.mean(benchmark_profits))/np.std(strategy_profits)
return Sharpo_ratio
def getPrice():
stocks_name='沪深300'
start_date='2016-10-01'
end_date='2017-10-13'
fields=['ClosingPx']
#选择沪深300里面所有的股票代码
stocks=index_components(stocks_name)
#正式获取股票价格(不过不知道为什么只能从2016-10-10开始)
stock_price=get_price(stocks,start_date=start_date,end_date=end_date,fields=fields)
#获得沪深300指数(对样本空间内股票在最近一年(新股为上市以来)的日均成交金额由高到低进行排名,剔除排名在后50%的股票,然后对剩余股票按照日均总市值由高到低进行排名,选取排名在前300名的股票作为指数样本。)
benchmark_name='399300.XSHE'
benchmark_price=get_price(benchmark_name,start_date=start_date,end_date=end_date,fields=fields)
return stock_price,benchmark_price,stocks
def getRolling(data,shortPeriod,longPeriod):
for i in range(len(data.ix[1])):
col=data.ix[:,i]
name=data.columns[i]
data[name+'_'+str(shortPeriod)+'days']=col.rolling(window=shortPeriod).mean()
data[name+'_'+str(longPeriod)+'days']=col.rolling(window=longPeriod).mean()
return data
def calculate(data,benchmark_price,stocks):
#初始化累积收益率等
IRR=1
benchmark_revenue=1
victories=0
profits=[]
benchmark_profits=[]
#计算交易的日数
length=0
for i in range(len(data.index)-2):
portfolio=[]
print(data.index[i])
for j,company in enumerate(stocks):
if(data.ix[i,company+'_5days']>data.ix[i,company+'_20days']):
portfolio.append(company)
#如果当天存在出现该信号的公司,则购买
if(portfolio):
length=length+1
#计算策略当日收益率
new=data.ix[i+2,portfolio]/data.ix[i+1,portfolio]
#计算大盘当日收益率
benchmark_new=benchmark_price.ix[i+2]/benchmark_price.ix[i+1]
benchmark_revenue=benchmark_revenue*benchmark_new
data.ix[i+2,'benchmark_profits']=benchmark_revenue
#计算胜率
if(new.mean()>(benchmark_new)):
victories=victories+1
#固定印花说0.1%,手续费0.08%和滑点0.246%
IRR=IRR*(new.mean()-(0.001+0.0008+0.00246))
data.ix[i+2,'profits']=IRR
#如果当天不出现信号,策略的累计收益率不变,大盘的依然要更新
else:
data.ix[i+2,'profits']=data.ix[i+1,'profits']
benchmark_new=benchmark_price.ix[i+2]/benchmark_price.ix[i+1]
benchmark_revenue=benchmark_revenue*benchmark_new
data.ix[i+2,'benchmark_profits']=benchmark_revenue
#计算最大回撤
Max_drawdown=max_drawdown(data['profits'])
#为了便于画图,我先将策略累计收益率和大盘结合
plotData=pd.concat([data['profits'],data['benchmark_profits']],axis=1)
plotData.plot()
#计算夏普率
Sharpo_Ratio=Sharpo(data['profits'],data['benchmark_profits'])
return [IRR,victories/length,Sharpo_Ratio,Max_drawdown]
stock_price,benchmark_price,stocks=getPrice()
stock_price_rolling=getRolling(stock_price,5,20)
IRR,victories,Sharpo_Ratio,Max_drawdown=calculate(stock_price_rolling[19:],benchmark_price[19:],stocks)
print([IRR,victories,Sharpo_Ratio*np.sqrt(252),Max_drawdown])
输出结果为:[0.42318161427509665, 0.5043859649122807, -37.242237755917365, 1.395223166619767]
最后计算得到年化收益率为-57.7%,胜率为50.4%,夏普率为-37.2,最大回撤为139.5%
策略和大盘的累计收益图片:
忽略了交易成本,输出结果为:[1.1195674560662801, 0.5043859649122807, -8.8431527706262312, 0.092890344378694187]
最后计算得到年化收益率为11.9%,胜率为50.4%,夏普率为-8.84,最大回撤为9.2%
总结:从不计交易成本的输出结果我们可以看出,最终策略的累积收益率还是跑不赢大盘,策略本身对于累积收益率提升的贡献并不大。同时由于策略交易过于频繁,持有时间过于短暂(一天),每次交易的获益基本都会被交易成本吸收掉,导致累积收益率一路走低。