前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >用python绘制有效前沿

用python绘制有效前沿

作者头像
量化小白
发布2019-03-06 10:30:04
7.4K8
发布2019-03-06 10:30:04
举报
文章被收录于专栏:量化小白上分记

投资中最关心的两个问题是预期收益与风险,当对多个资产进行投资时,如何测定组合的风险与收益,如何根据这两项指标进行资产权重配置? 马科维茨理论给出了解决这一问题的框架,被认为是现代金融学的开端。本文首先给出马科维茨均值方差模型的理论说明,随后用股票指数数据绘制组合的有效前沿,最后给出一种应用方法,获取代码和数据请在后台回复“代码”。

01

均值方差模型

马科维茨理论的几个假设:

1、投资者在考虑每一次投资选择时,其依据是某一持仓时间内的证券收益的概率分布。

2、投资者是根据证券的期望收益率的方差或标准差估测组合的风险。

3、投资者的决定仅仅是依据证券的风险和收益。

4、在一定的风险水平上,投资者期望收益最大;在一定的收益水平上,投资者希望风险最小。

根据以上假设,马科维茨均值方差模型可以表述为:

最优组合为上述模型的解。在这一框架下,不同的期望收益对应不同的最优组合,从而得到一条最优组合曲线,对于给定的收益率,这条曲线上的投资组合有最小的风险,这条曲线称为有效前沿。

一种极端的情况是,投资者完全不考虑期望收益,只希望最小化风险,这样就可以忽略预期收益约束,得到最小化方差组合。

接下来通过python绘制资产组合的有效前沿。

02

两资产

首先考虑最简单的两资产情形

假设资产A,B收益率分别为5%,10%,波动率分别为15%,20%,对相关系数取值从-1到1,资产A权重从0到1,看不同投资组合的收益与风险情况。

图中每一条曲线的上半部分都是一个有效前沿(同样风险水平下,上半部分的收益显然高于下半部分),可以看到,当两资产完全正相关/负相关时,有效前沿退化成直线/双直线。并且随着两资产相关性的提高,同样的期望收益下,最优组合的风险越来越高,对应的,同样的风险水平下,期望收益越来越小。

代码

代码语言:javascript
复制
def portfolio1(ra,rb,sa,sb,rho,wa):
    wb = 1 - wa
    rp = wa*ra + wb*rb
    sp = ((wa**2)*(sa**2) + (wb**2)*(sb**2) + 2*wa*wb*sa*sb*rho)**0.5
    return rp,sp

ra = 0.05;rb = 0.1;sa = 0.15;sb = 0.2
rho = np.arange(-10,11,2)/10
wa = np.arange(101)/100

result = pd.DataFrame(columns = ['rho','wa','rp','sp'])
for rhos in rho:
    for was in wa:
        rp,sp = portfolio1(ra,rb,sa,sb,rhos,was)
        x = pd.DataFrame([rhos,was,rp,sp]).T
        x.columns = ['rho','wa','rp','sp']
        result = result.append(x)
result = result.reset_index(drop = True)

plt.figure(figsize=(20,5))
for i in rho:
    datas = result.loc[result.rho ==i]
    plt.scatter(datas.sp,datas.rp,marker = 'o',label = 'rho = '+ str(i))
plt.legend()
plt.xlabel('sigma',fontsize = 20)
plt.ylabel('r',fontsize = 20)
plt.show()

03

多资产

多个资产情形下,由于股票会出现停牌等问题导致数据不连续。我们选用申万一级行业指数中的食品饮料(801120.SI)、银行(801780.SI)、通信(801770.SI)、国防军工(801740.SI)、医药生物(801150.SI)作为基础资产。

以2010年1月4日为起点将指数归一化到1000点后,各指数走势如下

用组合2017年的数据计算收益和方差,年化

我们首先用蒙特卡洛方法模拟出各种组合权重,计算各组合的收益风险水平并进行作图,根据组合的夏普比率进行着色,共模拟20000次。

代码

代码语言:javascript
复制
def weights(n):
    w = np.random.random(n)
    return w/w.sum()

def performance(price,w):
    rf = 0.04
    ret = price.pct_change(1).dropna()
    r_mean = ret.mean()*252
    p_mean = np.sum(r_mean*w)
    r_cov = ret.cov()*252
    p_var = np.dot(w.T,np.dot(r_cov,w))
    p_std = np.sqrt(p_var)
    p_sharpe = (p_mean-rf)/p_std
    return p_mean,p_std,p_sharpe

np.random.seed(1)
p_mean,p_std,p_sharpe = np.column_stack([performance(datax,weights(5)) for i in range(20000)]) #产生随机组合

plt.figure(figsize = (10,5))# YlGnBu
plt.scatter(p_std, p_mean, c=p_sharpe, marker = 'o',cmap = 'YlGnBu',s = 15)
plt.xlabel('std')
plt.ylabel('mean')
plt.colorbar(label = 'Sharpe')
plt.title('Mean and Std of Returns')
plt.show()

接下来绘制组合的有效前沿,首先考虑之前提到最极端的情况:最小方差组合,使用scipy.minimize进行优化。

以最小方差下的收益为区间起点,收益的6倍为区间终点,期望收益等步长遍历区间,计算各收益下的最优组合,得到有效前沿。

代码

代码语言:javascript
复制
r_min = result[0]
r_mean_list = np.linspace(r_min,r_min*6,51)
v_list = []
for r in r_mean_list:
    cons1 = ({'type':'eq','fun':lambda w:performance(datax,w)[0]-r},{'type':'eq','fun':lambda w:np.sum(w)-1})
    opt_var_1 = minimize(min_variance,x0 = weights(5),args = (datax,),bounds=((0,1),(0,1),(0,1),(0,1),(0,1)),constraints = cons1)
    #最小方差下的收益率
    v_min = performance(datax,opt_var_1.x)[1]    
    v_list.append(v_min)    

plt.figure(figsize = (10,5))
plt.scatter(p_std, p_mean, c=p_sharpe,  marker = 'o',cmap = 'YlGnBu',s = 10)
plt.xlabel('std')
plt.ylabel('mean')
plt.colorbar(label = 'Sharpe')
plt.scatter(v_list,r_mean_list,marker = '*',color = 'orange',label = '有效前沿')
plt.scatter(result[1],result[0],marker = '*',color = 'red',s = 100,label = '最小方差组合')
plt.legend(prop=font)
plt.title('Mean and Std of Returns')
plt.show()

03

应用

以上文5种资产为例,2010年1月-2018年12月的数据,对比等权重配置和最小方差权重下的组合表现,对比从2011年开始,2010年数据用于优化,半年度调仓。

等权重配置

代码语言:javascript
复制
def EqualWeight(datas):
    ret = datas.pct_change(1).fillna(0)
    data_norm = datas/datas.iloc[0,]*1000
    result = data_norm.copy()

    result['ym'] = result.index
    result['ym'] = result.ym.apply(lambda x:x.year*100 + x.month)
    weights = pd.DataFrame(columns = datas.columns,index = datas.index).fillna(1/datas.shape[1])
    result = result.loc[datetime.date(2011,1,4):]
    result['nav'] = ((ret*weights).sum(axis = 1) + 1).cumprod()*1000
    return weights,result

最小方差配置

代码语言:javascript
复制
def funs(weight,sigma):
    weight = np.array([weight]).T
    result = np.dot(np.dot(weight.T,np.mat(sigma)),weight)[0,0]
    return(result)

def ConstraintGMOWeight(datas,period ='month',alpha = 1):
    ret = datas.pct_change(1).fillna(0)
    data_norm = datas/datas.iloc[0,]*1000
    result = data_norm.copy()    
    result['m'] = result.index
    result['m'] = result.m.apply(lambda x:x.month)
    weights = pd.DataFrame(columns = datas.columns,index = datas.index).fillna(0)

    # 约束
    cons = ({'type': 'eq', 'fun': lambda x:  1 - sum(x)})
    # 边界
    bnds = ((0, alpha),(0, alpha),(0, alpha),(0, alpha),(0, alpha))    

    
    if period == 'month':
        for i in range(result.shape[0]):
            if i == 0:
                pass
            elif result.m[i] != result.m[i - 1]:
                sigma = ret.iloc[:i].cov()                
                weight = [0 for i in range(datas.shape[1])]
                res =  minimize(funs,weight, method='SLSQP',args = (sigma,),
                bounds=bnds,constraints=cons,tol=1e-8)
                weights.iloc[i,:] =  res.x

            else:
                weights.iloc[i,:] = weights.iloc[i-1,:]

    elif period == '6month':
        for i in range(result.shape[0]):
            if i == 0:
                pass
            elif (result.m[i] != result.m[i - 1] and  result.m[i]%6==0) :
                sigma = ret.iloc[:i].cov()                
                weight = [0 for i in range(datas.shape[1])]
                res =  minimize(funs,weight, method='SLSQP',args = (sigma,),
                bounds=bnds,constraints=cons,tol=1e-8)
                weights.iloc[i,:] =  res.x
            else:
                weights.iloc[i,:] = weights.iloc[i-1,:]

    elif period == 'year':
        for i in range(result.shape[0]):
            if i == 0:
                pass
            elif (result.m[i] != result.m[i - 1] and  result.m[i]%12==0) :
                sigma = ret.iloc[:i].cov()               
                weight = [0 for i in range(datas.shape[1])]
                res =  minimize(funs,weight, method='SLSQP',args = (sigma,),
                bounds=bnds,constraints=cons,tol=1e-8)
                weights.iloc[i,:] =  res.x
            else:
                weights.iloc[i,:] = weights.iloc[i-1,:]

    else: 
        return '请输入调仓周期'
    result = result.loc[datetime.date(2011,1,4):]
    result['nav'] = ((ret*weights).sum(axis = 1) + 1).cumprod()*1000    
    return weights,result

结果如下

可以看出,最小方差组合的效果略优于等权重组合。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-02-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 量化小白躺平记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档