以下是小哥用Dual-Thrust策略回测CTA的代码和结果,错误的地方还请大家提出来。
标的是螺纹钢的主力连续合约
#%% Dual Thrust策略
# 导入包
import pandas as pd
import matplotlib.pyplot as plt
#%% 导入和清洗数据
RBL=pd.read_excel('H:/RBL8.xlsx')
RBL.index=pd.to_datetime(RBL.iloc[:,0])
RBL=RBL['2016':'2017']
RBL=RBL.iloc[:,1:5]
RBL.columns=('Open','High','Low','Close')
#%% Dual_Thrust策略
#输入时间序列
#输出买卖信号、持仓情况
def dt(tso,tsh,tsl,tsc,N,k1,k2):
#输入
#开盘价df:tso,最高价df:tsh,最低价df:tsl,收盘价df:tsc
#参数int:N,k1,k2
#初始化
HH=pd.Series(index=tso.index[N-1:])
LC=pd.Series(index=tso.index[N-1:])
HC=pd.Series(index=tso.index[N-1:])
LL=pd.Series(index=tso.index[N-1:])
Range=pd.Series(index=tso.index[N-1:])
BuyLine=pd.Series(index=tso.index[N-1:])
SellLine=pd.Series(index=tso.index[N-1:])
Open=tso.iloc[N-1:]
Close=tsc.iloc[N-1:]
Signal=pd.Series(0,index=tso.index[N-1:]) #交易信号
Case=pd.Series(0,index=tso.index[N-1:]) #仓位情况
#计算N日内最高价HH,Close的最低价LC,Close的最高价HC,最低价LL
for i in range(len(tso.index)-N+1):
HH.iloc[i]=max(tsh.iloc[i:i+N])
LC.iloc[i]=min(tsc.iloc[i:i+N])
HC.iloc[i]=max(tsc.iloc[i:i+N])
LL.iloc[i]=min(tsl.iloc[i:i+N])
Range.iloc[i]=max(HH.iloc[i]-LC.iloc[i],HC.iloc[i]-LL.iloc[i])
#生成Range,买入线BuyLine,买出线,SellLine
BuyLine=Open+k1*Range
SellLine=Open-k2*Range
#画出区间
plt.figure('BuyLine和SellLine')
plt.plot(BuyLine)
plt.plot(SellLine)
#构建交易信号
#当日收盘价低于或高于区间时生成交易信号Signal
for i in range(len(Range)-1):
Signal.iloc[i]=0
Case.iloc[i+1]=Case.iloc[i]
if Close.iloc[i]>=BuyLine.iloc[i] and Case.iloc[i]<1:
Signal.iloc[i]=1
Case.iloc[i+1]=1
elif Close.iloc[i]<=SellLine.iloc[i] and Case.iloc[i]>-1:
Signal.iloc[i]=-1
Case.iloc[i+1]=-1
return Signal,Case
#%% 进行回测
[sig,ca]=dt(RBL['Open'],RBL['High'],RBL['Low'],RBL['Close'],20,0.1,0.1)
#%% 输出收盘价、交易信号、持仓情况
plt.figure('输出收盘价、交易信号、持仓情况')
plt.subplot(3,1,1)
plt.plot(RBL['Close'])
plt.title('收盘价')
plt.subplot(3,1,2)
plt.plot(sig)
plt.title('交易信号')
plt.subplot(3,1,3)
plt.plot(ca)
plt.title('持仓情况')
#%% 指标评价
# 累计收益、年化收益率、标的收益率、胜率、持仓时间、交易次数、最大回撤
#累计收益率
def all_re(tsc,Case):
re=tsc.diff().dropna() #以收盘价衡量的每日收益
day_profit=pd.Series(index=Case.index) #持仓后每日的收益
for i in range(len(Case)):
day_profit[i]=Case.iloc[i]*re[Case.index[i]]
#累计收益
acc_profit=day_profit.cumsum()
#保证金,以持仓初始日的保证金为本金买入一手
insur=tsc[Case.index[0]]*0.05+10000
#年化收益:日收益率的平均值做成年化
day_rate=acc_profit[-1]/(insur*len(acc_profit))
anual_rate=day_rate*250
#标的收益率
bid_re=tsc[-1]/(insur*len(tsc))
return acc_profit+insur,anual_rate,bid_re
#%% 交易次数、多头次数、空头次数
def trade_num(sig):
return len(sig[sig!=0]),len(sig[sig==1]),len(sig[sig==-1])
#%% 持仓时间
def in_case(case):
return len(case[case!=0])
#%% 回撤、最大回撤
def tradeback(tsc,Case):
tb=pd.Series(0,index=Case.index)
re=tsc.diff().dropna() #以收盘价衡量的每日收益
day_profit=pd.Series(index=Case.index) #持仓后每日的收益
for i in range(len(Case)):
day_profit[i]=Case.iloc[i]*re[Case.index[i]]
insur=tsc[Case.index[0]]*0.05+10000
#累计收益
acc_profit=day_profit.cumsum()+insur
#回撤率
for i in range(10,len(tb)-1):
if acc_profit[i]>0:
tb.iloc[i]=abs((max(acc_profit[:i+1])-acc_profit[i+1])/max(acc_profit[:i+1]))
#返回回撤率、最大回撤率
return tb,max(tb)
#%%计算胜率
def victor(tsc,Case):
ca_re=pd.Series(0,index=Case.index)
re=tsc.diff().dropna()
for i in range(len(Case)):
ca_re.iloc[i]=Case.iloc[i]*re[Case.index[i]]
vi=len(ca_re[ca_re>0])/len(Case)
return vi
#%% 输出累计收益率,回撤
plt.figure('累计收益、回撤')
plt.subplot(2,1,1)
plt.plot(all_re(RBL['Close'],ca)[0])
plt.title('累计收益')
plt.subplot(2,1,2)
plt.plot(tradeback(RBL['Close'],ca)[0]*100)
plt.title('回撤')
#%% 各项指标
print('年化收益率:',all_re(RBL['Close'],ca)[1]*100,'%')
print('\n标的收益率:',all_re(RBL['Close'],ca)[2]*100,'%')
print('\n交易次数:',trade_num(sig)[0],'\n多头次数:',trade_num(sig)[1],\
'\n空头次数:',trade_num(sig)[2])
print('\n胜率:',victor(RBL['Close'],ca))
print('\n持仓时间:',in_case(ca))
print('\n最大回撤:',tradeback(RBL['Close'],ca)[1]*100,'%')
结果为
年化收益率: 13.68464099 %
标的收益率: 0.0772052921481 %
交易次数: 93
多头次数: 47
空头次数: 46
胜率: 0.5256410256410257
持仓时间: 463
最大回撤: 5.36926264923 %