Python计算量化策略评估指标

量化评估

年化收益率

年化收益率是把当前收益率(日收益率、周收益率、月收益率)换算成年收益率来计算的,是一种理论收益率,并不是真正的已取得的收益率。因为年化收益率是变动的,所以年收益率不一定和年化收益率相同。

总收益率:R=\frac{P_T-P_t}{P_t}
其中,P_T是期末卖出时的价格,P_t是期初买入时的价格。

年化收益率:R_p=(1+R)^{\frac{m}{n}}-1
其中,R是期间总收益率,m是与n(可以是天数、周数、月数)相对应的计算周期,根据计算惯例,m=250、52、12分别指代日、周、月向年化的转换。

最大回撤

在选定周期内任一历史时点往后推,产品净值走到最低点时的收益率回撤幅度的最大值。最大回撤用来描述买入产品后可能出现的最糟糕的情况。最大回撤是一个重要的风险指标,对于对冲基金和数量化策略交易,该指标比波动率还重要。
P为某一天的净值,i为某一天,j为i后的某一天,Pi为第i天的产品净值,Pj则是Pi后面某一天的净值
则该基金的最大回撤率计算如下:
Max\_drawdown=\frac{max(P_i-P_j)}{P_i}
即通过对每一个净值进行回撤率求值,然后找出最大的。

Beta: 贝塔系数

*相当于业绩评价基准收益的总体波动性
beta=\beta_p=\frac{Cov(P_i,P_m)}{Var_m}

*衡量策略的系统性风险:
如果Beta为1,策略和市场(参照沪深300指数)同进退
如果Beta为1.1,市场上涨10%时,策略上涨11%;市场下滑10%时,策略下滑11%。
如果Beta为0.9,市场上涨10%时,策略上涨9%;市场下滑10%时,策略下滑9%。

那么问题来了, Beta值到底是大好还是小好呢?
这得具体问题具体分析,如果是牛市,股市兴兴向荣,个股、大盘狂涨,那就要选择Beta值大的策略;
如果是熊市,经济下行压力大,就应该选择Beta值小的策略,这样就可以比较好的控制风险,确保资金的安全。

Alpha:阿尔法系数

*实际收益和按照Beta系数计算的期望收益之间的差额。
*代表策略多大程度上跑赢了预期的收益率

beta和alpha的估计

这里的beta和alpha系数都来自资本资产定价模型,先来看看CAPM:
E(r_i)=r_f+\beta (E(r_m)-r_f)
E(r_i)是股票i的预期收益率,r_f是无风险利率,E(r_m)是市场指数收益率;
\beta系数是系统性风险,在评估股市波动风险与投资机会的方法中,常用来衡量结构性与系统性风险,可以简单理解为个股波动相对大盘波动的偏离程度。CAPM的计量模型可以表示为:
r_i=\alpha+\beta r_m+\varepsilon
\alpha可以理解为超额收益率,\varepsilon是随机扰动,可以理解为个体风险。

夏普比率

*代表投资人每多承担一分风险,可以拿到几分报酬;
---单位风险所获得的超额回报率
*该比率越高,策略承担单位风险得到的超额回报率越高。
所以说夏普比率是越高越好滴..

Sharpe\_ratio= \frac{R_p-R_f}{\sigma_p}
其中,R_p为年化收益率, R_f 是无风险收益率,\sigma_p为年化波动率

信息比率

Information\_ratio=\frac{R_p-R_m}{\sigma_t}
其中,R_p为年化收益率, R_f 为基准年化收益率(如沪深300指数),\sigma_t为策略与基准每日收益率差值的年化标准差

Python计算实例

使用tushare获取交易数据,考虑最简单的策略:买入持有!分别计算期间总收益率,年化收益率,最大回撤,beta、alpha系数,夏普比率和信息比率。

#先引入后面可能用到的包(package)
import pandas as pd  
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline   

#正常显示画图时出现的中文和负号
from pylab import mpl
mpl.rcParams['font.sans-serif']=['SimHei']
mpl.rcParams['axes.unicode_minus']=False
### 获取数据:tushare开源库(确认已安装好:pip install tushare)
import tushare as ts
#起始和结束日期可以自行输入,否则使用默认
def get_data(code,start_date="2009-01-01", end_date="2019-01-18"):
    df = ts.get_k_data(code, start=start_date, end=end_date)
    df.index=pd.to_datetime(df.date)
    return df.close
#返回收盘价
#以上证综指、贵州茅台、工商银行、中国平安为例
stocks={'sh':'上证综指','600519':'贵州茅台',
        '601398':'工商银行','601318':'中国平安'}
#获取上述股票(指数)的每日前复权收盘价
df=pd.DataFrame()
for code,name in stocks.items():
    df[name]=get_data(code)
df.head()
#以第一交易日2009年1月5日收盘价为基点,计算净值
df_new=df/df.iloc[0]
#将上述股票在回测期间内的净值可视化
df_new.plot(figsize=(16,7))
#图标题
plt.title('股价净值走势',fontsize=15)
#设置x轴坐标
my_ticks = pd.date_range('2008-01-01','2019-01-18',freq='Y')
plt.xticks(my_ticks,fontsize=12)
#去掉上、右图的线
ax=plt.gca()
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
plt.show()

[图片上传失败...(image-dab38d-1548119602931)]

累计收益率和年化收益率

收益率可以根据上面公式计算,或使用对数收益率,也可以根据上面的累计净值来推出累计收益率(累计净值-1)。

### 区间累计收益率(绝对收益率)
total_ret=df_new.iloc[-1]-1
TR=pd.DataFrame(total_ret.values,columns=['累计收益率'],index=total_ret.index)
TR
###年化收益率,假设一年以250交易日计算
annual_ret=pow(1+total_ret,250/len(df_new))-1
AR=pd.DataFrame(annual_ret.values,columns=['年化收益率'],index=annual_ret.index)
AR

最大回撤

实际上,numpy和pandas借助库函数均可以实现一行代码计算最大回撤。

#numpy:np.maximum.accumulate计算序列累计最大值
code='上证综指'
n_d=((np.maximum.accumulate(df[code])-df[code])/np.maximum.accumulate(df[code])).max()
#pandas使用cummax()计算序列累计最大值
p_d=((df[code].cummax()-df[code])/df[code].cummax()).max()
#打印结果
print(f'numpy方法计算结果:{round(n_d*100,2)}%')
print(f'pandas方法计算结果:{round(p_d*100,2)}%')                    
numpy方法计算结果:52.3%
pandas方法计算结果:52.3%
#定义成函数,减少重复工作
def max_drawdown(df):
    md=((df.cummax()-df)/df.cummax()).max()
    return round(md,4)
md={}
for code,name in stocks.items():
    md[name]=max_drawdown(df[name])
#最大回撤率结果:
MD=pd.DataFrame(md,index=['最大回撤']).T
MD

alpha和beta

使用回归来估计alpha和beta的值。

#计算每日收益率
#收盘价缺失值(停牌),使用前值代替
rets=(df.fillna(method='pad')).apply(lambda x:x/x.shift(1)-1)[1:]
rets.head()
#市场指数为x,个股收益率为y
from scipy import stats
x=rets.iloc[:,0].values
y=rets.iloc[:,1:].values
AB=pd.DataFrame()
alpha=[]
beta=[]
for i in range(3):
#使用scipy库中的stats.linregress线性回归
#python回归有多种实现方式,
#如statsmodels.api的OLS,sklearn库等等
    b,a,r_value,p_value,std_err=stats.linregress(x,y[:,i])
    #alpha转化为年化
    alpha.append(round(a*250,3))
    beta.append(round(b,3))
AB['alpha']=alpha
AB['beta']=beta
AB.index=rets.columns[1:]
#输出结果:
AB
#使用公式法直接计算beta值(见前文公式):
beta1=rets[['上证综指','贵州茅台']].cov().iat[0,1]/rets['上证综指'].var()
beta2=rets[['上证综指','工商银行']].cov().iat[0,1]/rets['上证综指'].var()
beta3=rets[['上证综指','中国平安']].cov().iat[0,1]/rets['上证综指'].var()
print(f'贵州茅台beta:{round(beta1,3)}')
print(f'工商银行beta:{round(beta2,3)}')
print(f'中国平安beta:{round(beta3,3)}')
贵州茅台beta:0.637
工商银行beta:0.614
中国平安beta:1.071
#使用公式法直接计算beta值(见前文公式):
#annual_ret是前文计算出来的年化收益率
alpha1=(annual_ret[1]-annual_ret[0]*beta1)
alpha2=(annual_ret[2]-annual_ret[0]*beta2)
alpha3=(annual_ret[3]-annual_ret[0]*beta3)
print(f'贵州茅台alpha:{round(alpha1,3)}')
print(f'工商银行alpha:{round(alpha2,3)}')
print(f'中国平安alpha:{round(alpha3,3)}')
贵州茅台alpha:0.244
工商银行alpha:0.077
中国平安alpha:0.138

夏普比率和信息比率

#超额收益率以无风险收益率为基准
#假设无风险收益率为年化3%
exReturn=rets-0.03/250
#计算夏普比率
sharperatio=np.sqrt(len(exReturn))*exReturn.mean()/exReturn.std()
#夏普比率的输出结果
SHR=pd.DataFrame(sharperatio,columns=['夏普比率'])
SHR
###信息比率
#超额收益率以指数收益率或其他为基准
#这里以上证综指为基准
ex_return=pd.DataFrame() 
ex_return['贵州茅台']=rets.iloc[:,1]-rets.iloc[:,0]
ex_return['工商银行']=rets.iloc[:,2]-rets.iloc[:,0]
ex_return['中国平安']=rets.iloc[:,3]-rets.iloc[:,0]
ex_return.head()
#计算信息比率
information=np.sqrt(len(ex_return))*ex_return.mean()/ex_return.std()
#信息比率的输出结果
INR=pd.DataFrame(information,columns=['信息比率'])
INR
#将上述指标合并成一张表
indicators=pd.concat([TR,AR,MD,AB,SHR,INR],axis=1,join='outer',sort='False')
#结果保留三位小数
indicators.round(3)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容

  • 这篇文章几乎回答了你对量化对冲的所有疑问 一、基础篇 1、在市场不稳定的情况下如何稳健套利? 套利,本就是很稳健的...
    胖山楂球阅读 2,773评论 1 14
  • 一、一个完整的量化策略包含哪些内容? 一个完整的策略需要包含输入、策略处理逻辑、输出;策略处理逻辑需要考虑选股、择...
    是蓝先生阅读 1,692评论 2 14
  • 摘要 BT是一家成立于2014年的私募证券投资基金,凭借产品优异的净值表现,3年左右的时间,管理规模已近40亿元。...
    冯昉中阅读 1,349评论 0 0
  • BT是一家成立于2014年的私募证券投资基金,凭借产品优异的净值表现,3年左右的时间,管理规模已近40亿元。我们希...
    冯昉中阅读 806评论 0 0
  • 犹记得儿时的雪 是两行深深的脚印 你把红红的萝卜 镶上开心的笑脸 还有你的绒线小帽 只为逗我一笑 我最爱的花围巾 ...
    画画儿阅读 192评论 0 0