文章来自量化小白的评分,作者量化小白h
看完前面的铺垫,写点可以实践的东西。本文给出了编写时序策略回测的详细步骤,并用代码展示了整个过程。代码是用python写的,后台“择时”检索数据和代码,可以自己测试。
择时策略
根据百度百科的解释,择时交易是指运用某种方法判断大势,无论是上涨还是下跌,还是盘整。如果判断是上涨,买入持有;如果判断为下跌,卖出清仓;如果是盘整,可以高抛低吸。
从定量的角度来说,择时就是通过资产的数据构造一个买卖信号,然后根据这个买卖信号进行交易。回测就是实现全过程。
本文以最简单的双均线策略为例进行回测。具体规则如下:
短均线下穿长均线(金叉),目前没有仓位:买入;短均线下穿长均线(死叉),目前持有卖出;其他情况下,保持之前的位置;可以考虑控制回撤,单笔亏损超过一定范围就平仓。
回测评价
对于策略的评价,一般分为收益和风险两个方面,或者将两者结合起来,列出一些常用的评价指标。
年化收益
回测从起点到终点的累计收益可以年化,可以计算复利或单利。复利假设策略的利润也会用于投资,所以复利计算结果会更好看。
夏普比
夏普比率=(战略预期收益率-无风险收益率)/战略波动率
Sharpie是应用最广泛的指数,综合衡量收益和风险。
胜率
要统计中签率,首先要统计交易笔数,然后计算盈利笔数在所有交易中所占的比例。
最大回撤率
回撤是策略从前期最高点到当前点的损失,最大回撤是所有回撤的最大值,反映了策略可能的最大损失。
单次最大亏损
所有单笔交易中最大的损失
策略阶段性表现
划分战略时间段,统计上述指标在各个时间段的变化。本文以年为单位进行划分,统计年收益率和相对于基准的超额收益率。
其他
此外,还有各种指标,如波动性、下行风险、交易比率等。python里有专门的模块,可以计算各种指标。这里我们自己算算各种指标,供参考。
另外,还要测试策略的稳定性,扰动策略中的参数,测试策略的灵敏度。一个好的策略应该对参数不敏感。
回测说明
回测标的:沪深300指数
回测区间: 2010年1月至2019年3月
代码说明:后面的测试代码分为两部分,一部分是策略函数(Strategy),另一部分是评估函数(http://www . Sina . com/)。策略函数通过指数收盘价构造信号,计算策略净值,统计策略每笔交易的情况。评价函数根据策略的净值和策略每笔交易的情况计算策略的上述指标。
Performance
策略代码
def策略(pdatas,win_long,win_short,loss ratio=999): # pdatas=datas . copy();win _ long=12win _ short=6;lossratio=999pma:计算均线的价格序列。win:窗口宽度损失率:止损率。默认值为0 ' ' pdatas=pdatas . copy()pdatas[' LMA ']=pdatas . close . rolling(win _ long,Min _ periods=0)。mean()pdata s[' SMA ']=pdata s . close . rolling(win _ short,min _ periods=0)。Mean () pdatas ['position']=0 #记录位置pdatas['flag']=0 #记录
_in = 1 for i in range(max(1,win_long),pdatas.shape[0] - 1): # 当前无仓位,短均线上穿长均线,做多 if (pdatas.sma[i-1] < pdatas.lma[i-1]) & (pdatas.sma[i] > pdatas.lma[i]) & (pdatas.position[i]==0): pdatas.loc[i,'flag'] = 1 pdatas.loc[i + 1,'position'] = 1 date_in = pdatas.DateTime[i] price_in = pdatas.loc[i,'CLOSE'] pricein.append([date_in,price_in]) # 当前持仓,下跌超出止损率,止损 elif (pdatas.position[i] == 1) & (pdatas.CLOSE[i]/price_in - 1 < -lossratio): pdatas.loc[i,'flag'] = -1 pdatas.loc[i + 1,'position'] = 0 priceout.append([pdatas.DateTime[i],pdatas.loc[i,'CLOSE']]) # 当前持仓,死叉,平仓 elif (pdatas.sma[i-1] > pdatas.lma[i-1]) & (pdatas.sma[i] < pdatas.lma[i]) &(pdatas.position[i]==1): pdatas.loc[i,'flag'] = -1 pdatas.loc[i+1 ,'position'] = 0 priceout.append([pdatas.DateTime[i],pdatas.loc[i,'CLOSE']]) # 其他情况,保持之前仓位不变 else: pdatas.loc[i+1,'position'] = pdatas.loc[i,'position'] p1 = pd.DataFrame(pricein,columns = ['datebuy','pricebuy']) p2 = pd.DataFrame(priceout,columns = ['datesell','pricesell']) transactions = pd.concat([p1,p2],axis = 1) pdatas = pdatas.loc[max(0,win_long):,:].reset_index(drop = True) pdatas['ret'] = pdatas.CLOSE.pct_change(1).fillna(0) pdatas['nav'] = (1 + pdatas.ret*pdatas.position).cumprod() pdatas['benchmark'] = pdatas.CLOSE/pdatas.CLOSE[0] stats,result_peryear = performace(transactions,pdatas) return stats,result_peryear,transactions,pdatas说明
评价函数
def performace(transactions,strategy): # strategy = pdatas.copy(); N = 250 # 年化收益率 rety = strategy.nav[strategy.shape[0] - 1]**(N/strategy.shape[0]) - 1 # 夏普比 Sharp = (strategy.ret*strategy.position).mean()/(strategy.ret*strategy.position).std()*np.sqrt(N) # 胜率 VictoryRatio = ((transactions.pricesell - transactions.pricebuy)>0).mean() DD = 1 - strategy.nav/strategy.nav.cummax() MDD = max(DD) # 策略逐年表现 strategy['year'] = strategy.DateTime.apply(lambda x:x[:4]) nav_peryear = strategy.nav.groupby(strategy.year).last()/strategy.nav.groupby(strategy.year).first() - 1 benchmark_peryear = strategy.benchmark.groupby(strategy.year).last()/strategy.benchmark.groupby(strategy.year).first() - 1 excess_ret = nav_peryear - benchmark_peryear result_peryear = pd.concat([nav_peryear,benchmark_peryear,excess_ret],axis = 1) result_peryear.columns = ['strategy_ret','bench_ret','excess_ret'] result_peryear = result_peryear.T # 作图 xtick = np.round(np.linspace(0,strategy.shape[0] - 1,7),0) xticklabel = strategy.DateTime[xtick] plt.figure(figsize = (9,4)) ax1 = plt.axes() plt.plot(np.arange(strategy.shape[0]),strategy.benchmark,'black',label = 'benchmark',linewidth = 2) plt.plot(np.arange(strategy.shape[0]),strategy.nav,'red',label = 'nav',linewidth = 2) plt.plot(np.arange(strategy.shape[0]),strategy.nav/strategy.benchmark,'orange',label = 'RS',linewidth = 2) plt.legend() ax1.set_xticks(xtick) ax1.set_xticklabels(xticklabel) maxloss = min(transactions.pricesell/transactions.pricebuy - 1) print('------------------------------') print('夏普比为:',round(Sharp,2)) print('年化收益率为:{}%'.format(round(rety*100,2))) print('胜率为:{}%'.format(round(VictoryRatio*100,2))) print('最大回撤率为:{}%'.format(round(MDD*100,2))) print('单次最大亏损为:{}%'.format(round(-maxloss*100,2))) print('月均交易次数为:{}(买卖合计)'.format(round(strategy.flag.abs().sum()/strategy.shape[0]*20,2))) result = {'Sharp':Sharp, 'RetYearly':rety, 'WinRate':VictoryRatio, 'MDD':MDD, 'maxlossOnce':-maxloss, 'num':round(strategy.flag.abs().sum()/strategy.shape[0],1)} result = pd.DataFrame.from_dict(result,orient='index').T return result,result_peryear
说明
回测结果
nav为策略净值,benchmark为基准净值,RS为相对强弱曲线,可以看出,策略表现并不稳定。
transcation中记录每笔交易的买卖时点和价格
result_peryear中是策略的逐年表现情况,也并不会比基准好多少
综上,是一个完整的策略回测和评价过程,当然实际操作中还有许多需要细化的地方,仅供参考,欢迎指正!
关于Python金融量化
专注于分享Python在金融量化领域的应用。加入知识星球,可以免费获取量化投资视频资料、量化金融相关PDF资料、公众号文章Python完整源码、量化投资前沿分析框架,与博主直接交流、结识圈内朋友等。