前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >资产瞎配模型(一)

资产瞎配模型(一)

作者头像
量化小白
发布2019-01-22 15:43:32
1.9K1
发布2019-01-22 15:43:32
举报

大类资产配置是量化中一个重要的领域,本文尝试实现若干资产配置模型。全文纯属瞎配,欢迎指正!

01

理论模型

资产配置是根据投资者的收益风险偏好及不同资产特性,将资金配置于多种资产类别的一种投资策略,目的在于分散风险,是对组合收益和组合风险的权衡。

首先给出一些符号定义

接下来说明所用到的配置模型,资产配置从技术角度来说,只需要考虑三个问题:选择资产、横截面分配、时间序列分配

选择资产相对主观,最重要的原则是风险不同源,从数据角度来说,相关性不能太高,相关性过高的资产,难以通过调整权重来规避风险。横截面分配与时间序列分配实质上就是确定各种资产的权重,各种模型也都是在选定资产后,在不同的假设下给出不同的权重表达式。接下来列出文中用到的所有资产配置模型。(公式较多,可能引起不适,可以直接看下一部分,不影响悦读

几种简单粗暴的配置方式:

除了这些相对简单的配置方式外,还有很多理论上很完美的模型,基本都能看见马科维茨的影子。

风险平价跟等波动率相对比,出发点都是使每类资产的面临的风险相同,但不同之处在于,等波动率考虑的是让各个资产对应的风险值相同,风险平价考虑的是让权重变化引起风险的的比例相同

当然除了这些,还有美林时钟、Black-Litterman等模型,也应用很广,美林时钟比较定性,BL模型是MVO的基础上引入了预期收益,客观+主观,但目前没搞懂实际应用时候观点矩阵该怎么定义,等搞懂了再尝试吧。

02

回测:资产选择

资产应选择相关性较低的资产,一般都是权益、债券、商品、黄金等资产中选择。本文选择资产类型如下

回测区间:2006年1月-2018年12月

数据来源:wind

将各资产起点标准化为1000点,各资产走势如下

不得不感叹,A股真的是十年一梦啊,各资产相关性如图,没有相关性很高的资产,所以做配置模型数据上没有什么问题。

03

回测:等权重

等权方式配置,起点时刻各资产买同样权重,一直持有。

代码语言: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['nav'] = ((ret*weights).sum(axis = 1) + 1).cumprod()*1000
    return weights,result

净值曲线

权重变化

04

等资金

除了等权方式外,其他的方式配置的资产,权重都会随着时间发生变化,偏离初始的目标,需要进行“再平衡”,使资产配置重回平衡,这里分别尝试进行月度半年度年度的再平衡。

代码语言:javascript
复制
def EqualCapitalWeight(datas,period ='month'):
    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)
    if period == 'month':
        for i in range(result.shape[0]):
            if i == 0:
                weights.iloc[i,:] = (1/datas.iloc[i,:])/((1/datas.iloc[i,:]).sum())          
            elif result.m[i] != result.m[i - 1]:
                weights.iloc[i,:] = (1/datas.iloc[i,:])/((1/datas.iloc[i,:]).sum())
            else:
                weights.iloc[i,:] = weights.iloc[i-1,:]
    elif period == '6month':
        for i in range(result.shape[0]):
            if i == 0:
                weights.iloc[i,:] = (1/datas.iloc[i,:])/((1/datas.iloc[i,:]).sum())              
            elif (result.m[i] != result.m[i - 1] and  result.m[i]%6==0) :
                weights.iloc[i,:] = (1/datas.iloc[i,:])/((1/datas.iloc[i,:]).sum())
            else:
                weights.iloc[i,:] = weights.iloc[i-1,:]

    elif period == 'year':
        for i in range(result.shape[0]):
            if i == 0 :
                weights.iloc[i,:] = (1/datas.iloc[i,:])/((1/datas.iloc[i,:]).sum())             
            elif (result.m[i] != result.m[i - 1] and  result.m[i]%12==0) :
                weights.iloc[i,:] = (1/datas.iloc[i,:])/((1/datas.iloc[i,:]).sum())
            else:
                weights.iloc[i,:] = weights.iloc[i-1,:]
    else: 
        return '请输入调仓周期'
    
    result['nav'] = ((ret*weights).sum(axis = 1) + 1).cumprod()*1000
    return weights,result

只给出月度再平衡对应的权重变化,因为三个都差不多。

不过这个有问题,因为我完全没考虑到汇率

,最开始只用了国内的几个资产,后来加上了全球指数之后,忘改了,所以看看就好吧。

05

等波动率

等波动率以及后面需要用到协方差的模型都需要考虑一个问题,如何估计波动率/协方差?这里图方便我们都使用历史波动率估计量,不考虑高端方法。有两种方式计算,一种是滚动计算,每次只用过去一段时间的数据计算,另一种研报里称为递归计算,用过去所有数据计算,这里两种方法都进行尝试,对结果进行对比。

滚动计算的代码如下,递归计算代码类似。

代码语言:javascript
复制
def EqualVolWeight(datas,period ='month'):
    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)
    position = 0
    if period == 'month':
        for i in range(result.shape[0]):
            if i == 0:
                pass
            elif result.m[i] != result.m[i - 1]:
                vol = ret.iloc[position:i].std()
                position = i
                weights.iloc[i,:] = (1/vol)/((1/vol).sum()) 

            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) :
                vol = ret.iloc[position:i].std()
                position = i
                weights.iloc[i,:] = (1/vol)/((1/vol).sum())    
            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) :
                vol = ret.iloc[position:i].std()
                position = i
                weights.iloc[i,:] = (1/vol)/((1/vol).sum())    
            else:
                weights.iloc[i,:] = weights.iloc[i-1,:]

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

滚动结果

递归结果

从结果可以明显看出,滚动窗口敏感性更高,一方面能够更贴近最新的趋势,但另一方面也可能对于噪声过度反应。 相比之下, 递归窗口稳定性好得多,但不够灵敏。两种方法各有优劣。但整体来看,两种方法对应的权重是类似的,说明还是比较稳健的。

等波动率的情况下,货币的波动率太小了,导致高配货币,零配A股。很稳健,但这种结果跟直接买货币也差不多了,没什么意义。

06

GMV

首先尝试直接套用模型解析解的表达式计算权重。只给出权重计算公式,其他部分都和前面是一样的。

代码语言:javascript
复制
sigma = ret.iloc[:i].cov()
weight = np.dot(np.mat(sigma).I,np.ones([sigma.shape[1],1])) 
weights.iloc[i,:] =  np.array(weight/(weight.sum())).T[0]

递归结果

权重有负值

滚动

滚动的结果明显可以看出权重有负值,A股、债券都不能做空,所以不符合常理,因此加上卖空限制后,重新求解。

07

GMO+卖空限制

有卖空限制后,模型没有解析解,只能通过最优化方法求数值解,我们使用python的scipy库中的minmum函数进行优化求解,funs为优化目标。

代码语言: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)

res =  minimize(funs,weight, method='SLSQP',args = (sigma,),
                bounds=bnds,constraints=cons,tol=1e-8)
weights.iloc[i,:] =  res.x

滚动结果

递归结果

两种方法结果基本是差不多的,债券和货币依然占绝大比例。

将单个资产占比限定在50%以内,重新优化:

滚动结果

基本上没有什么变化。

08

Risk Parity

优化函数funsRP

代码语言:javascript
复制
def funsRP(weight,sigma):
    weight = np.array([weight]).T
    X = np.multiply(weight,np.dot(sigma.values,weight))
    result = np.square(np.dot(X,np.ones([1,X.shape[0]])) - X.T).sum()
    return(result)

滚动

递归

A股大概是怎么都不愿意配一点了,为了避免单个资产权重过高或者过低的问题,对资产权重加以限制

09

Risk Parity + w<=40%

滚动

递归

10

Risk Parity + w>=10%

滚动

递归

11

所有结果净值对比

递归-月度

递归-半年度

递归-年度

滚动-月度

滚动-半年度

滚动-年度

12

结果评价

从净值上来看,等权重是最优的,我们计算不同组合下的年化收益,波动率,夏普比,对结果进行评价。

代码语言:javascript
复制
def performance(datas):
    nav = (datas.nav[datas.shape[0]-1]/1000)**(1/12) - 1
    vol = (datas.nav.pct_change(1)).std()*np.sqrt(250)
    Sharp = nav/vol
    return nav,vol,Sharp

剔除出现做空情况的GMO和有问题的等资金,所有方法结果对比如下

滚动

递归

两种参数估计方式下,结果基本是一致的:

  • 等波动率优于RP优于GMO优于等权重。如果看收益和波动率,等波动率下的年化收益是所有方法里最低的,但波动率也是最小的,小一个数量级。因为资产中有货币这一基本没有波动的资产,导致等波动率情况下货币占了80%以上的比例。除过等波动率的情况看,风险平价要更优一些。
  • 对于半年度和年度再平衡的策略,刚开始不满六个月/十二个月的时候,我所有的权重都设置的0,导致这段时间这些策略收益一直是0,其实至少可以全配货币或者按无风险利率累积。

总之,是一篇练手的作品,做的很粗糙,后面会尝试将这些模型用于行业配置和风格配置,欢迎指正!

13

参考文献

  1. Bodnar T, Schmid W. A test for the weights of the global minimum variance portfolio in an elliptical model[J]. Metrika, 2008, 67(2):127.
  2. 20160725-华泰证券-风险平价模型实证研究:风险平价模型在大类资产配置及行业配置中的应用
  3. 20180309-华宝证券-华宝证券金融工程专题报告:资产配置的流程、框架与运用
  4. 20180928-东北证券-东北证券大类资产配臵“全解析”专题研究之一::风险平价性质探究
  5. 20181114-爱建证券-爱建证券量化资产配置系列报告:从不同维度和角度探索风险平价资产配置方法的稳定性
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-01-07,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

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