[color=rgba(0, 0, 0, 0.9)]前面文章[color=var(--weui-LINK)]自研股票策略回测框架分享我们分享了一个股票日频策略的回测框架,本文我们继续完善这个框架,并对绩效指标这个模块展开说明一下。
[color=rgba(0, 0, 0, 0.9)]回测引擎调用策略函数后,会得到一个目标持仓矩阵,每一行是一个交易日,每一列的一只股票,可表示策略每一天目标要持有的标的及权重。如下图:
[color=rgba(0, 0, 0, 0.9)]
[color=rgba(0, 0, 0, 0.9)]目标持仓矩阵进入到清算模块,分别计算开仓,平仓,持仓三种情况的收益率,并扣除手续费,就得到最终策略的收益率序列。
[color=rgba(0, 0, 0, 0.9)]
[color=rgba(0, 0, 0, 0.9)]收益率序列进入绩效分析模块,会逐年计算各项绩效指标,并计算所有交易日总体绩效指标。这个模块的代码如下:
self.report = pd.DataFrame(
columns=['tradeDays', 'totalRtn', 'YearlyRtn', 'YearlyVol', 'Maxdd', 'Sharp', 'Calmar',
'indexRtn', 'totalRtn_excess', 'yearlyRtn_excess', 'yearlyVol_excess', 'maxDD_excess', 'sharp_excess',
'calmar_excess', 'Turnover'])
pnl_year = self.pnl.resample('A').sum()
self.index_pnl = self.data_obj.index_daily['pct_chg']
for y in pnl_year.index:
self.report.loc[str(y.year), :] = self.computeIndicator(self.pnl[str(y.year)], self.index_pnl[str(y.year)], np.array(self.turnover[str(y.year)]))
self.report.loc['Total', :] = self.computeIndicator(self.pnl, self.index_pnl, np.array(self.turnover))
self.hold_num = self.positionDF.apply(np.sign).sum(axis=1)
self.hold_pct = self.positionDF.sum(axis=1)
self.nav = (self.pnl + 1).cumprod()
self.index_nav = (self.index_pnl + 1).cumprod()
self.excess_nav = (self.pnl - self.index_pnl + 1).cumprod()
[color=rgba(0, 0, 0, 0.9)]最终得到分年度及汇总绩效报告如下:
[color=rgba(0, 0, 0, 0.9)]
[color=rgba(0, 0, 0, 0.9)]下面分别展示主要指标的计算:
[color=rgba(0, 0, 0, 0.9)]通过策略的日收益率序列,可以得到交易天数(tradeDays),总收益率(totalRtn, 复利),年化收益率(yearlyRtn, 以每年240个交易日折算),年化波动率(yearlyVol)
tradeDays = dailyPnl.shape[0]totalRtn = (dailyPnl + 1).cumprod()[-1] - 1
yearlyRtn = np.power(1 + totalRtn, 240.0 / tradeDays) - 1
yearlyVol = dailyPnl.std() * np.sqrt(240)
[color=rgba(0, 0, 0, 0.9)]最大回撤的计算,先得到策略净值曲线,定位到回撤的起止点下标,然后计算得到最高点到回撤最低点的回撤幅度(maxDD)。
nav = (dailyPnl + 1).cumprod()tmpnav = np.maximum.accumulate(nav) - nav
idx_e = tmpnav.idxmax()
idx_s = nav[:idx_e].idxmax()
maxDD = -(nav[idx_e] - nav[idx_s]) / nav[idx_s]
[color=rgba(0, 0, 0, 0.9)]夏普(sharp)和卡玛(calmar)的计算,这里夏普没有减无风险利率rf,可以自行设置。
calmar = yearlyRtn / maxDD
[color=rgba(0, 0, 0, 0.9)]基于基准指数的日收益率数据,得到日超额收益率序列,并计算各项超额指标。
excessDaily = dailyPnl - pnl_index
excessDaily.fillna(0,inplace=True)
totalRtn_excess = (excessDaily + 1).cumprod()[-1] - 1
yearlyRtn_excess = np.power(1 + totalRtn_excess, 240.0 / tradeDays) - 1
yearlyVol_excess = excessDaily.std() * np.sqrt(240)
[color=rgba(0, 0, 0, 0.9)]再有就是用换手率,手续费数据进行统计汇总。
[color=rgba(0, 0, 0, 0.9)]至此就是一个相对完整的股日频策略的回测框架了,大家可以下载源码,逐个模块进行分析,并根据自己的需要进行调整。
我们已经将本文用到的全部源数据+源代码+Python环境打包好了,做到开箱即用,一键运行,自己多动手才是学习的最佳途径。感兴趣的朋友请关注公众号,添加下面微信获取(微信号:jsj99_01)。