200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > 【基金量化研究系列】大类资产配置研究(四)——基于马科维茨模型的资产配置研究

【基金量化研究系列】大类资产配置研究(四)——基于马科维茨模型的资产配置研究

时间:2018-09-03 00:01:10

相关推荐

【基金量化研究系列】大类资产配置研究(四)——基于马科维茨模型的资产配置研究

文章目录

1. 引言1.1 前文回顾1.2 数据收集与标的资产选取1.3 基本假设2. 马科维茨模型简介2.1 模型的建立2.2 模型求解3. 策略实现伪代码4. 基于python的模型实现4.1 python代码4.2 实证结果5. 结论5.1 模型策略结果5.2 模型可能存在的问题5.3 留给读者的一些悬念6. 参考文献免责声明写在最后

1. 引言

1.1 前文回顾

在上一篇文章中,我们简单介绍了基于股票、债券、黄金和现金四类资产的均衡配置策略。(详情请见【基金量化研究系列】大类资产配置研究(三)——多资产均衡配置策略)。到目前为止,前三篇文章中涉及的所有配置策略的思想均比较简单直观,而策略表现也还算不错,因此笔者认为这些策略对那些经验不足的小白投资者,或者资产管理时间有限的非专业投资者来说具有一定的参考意义。

然而,这些“简单”的资产配置策略却不免显得有些“粗糙”。例如在四资产均衡配置策略中,我们对各类资产的投资比例均为25%,但有读者可能会质疑:能不能将权重分配改为30%-30%-30%-10%呢?配置为35.2%-41.3%-13.5%-10%会不会更好呢?

因此,为了避免在资产配置过程中存在类似这样“拍脑门”的嫌疑,以进一步提升策略表现和科学性,我们需要借助更多的工具和模型。当然,这也就要求资管人有更多的量化手段和更高的量化水平。

在这篇文章中,我们就来简要探讨一下经典的马科维茨模型在中国市场资产配置中的应用吧~

1.2 数据收集与标的资产选取

本文以开源的Choice数据库作为本文的数据来源。

对于标的资产的选择,我们仍以“易方达沪深300ETF(510310)”与“国泰上证5年国债ETF(511010)”作为股票市场与债券市场的基准可投标的,并以“华安黄金易ETF(518880)”和“七日深圳国债逆回购R-007(131801)”作为商品和无风险资产的基准可投资标的。这些标的均可以在券商账户中进行交易。

1.3 基本假设

本文仍延续前文的基本假设,规定:

(1)市场中不允许融资做多与融券做空;

(2)建仓调仓过程中不允许借款、参与国债回购;

(3)每日仅在收盘时间以收盘价格进行交易;

(4)未特别规定,忽略调、建仓手续费。

2. 马科维茨模型简介

2.1 模型的建立

简单来说,所谓马科维茨模型(Markowitz Model),是以期望收益-标准差作为衡量各资产的两个指标,并认为所有的投资组合都可以在这一框架之中找到相对应的坐标位置。而最优的投资策略则是投资者效用达到最大值的那一只组合。

:囿于篇幅限制,本文不对该模型的具体细节进行展开,感兴趣的读者可以关注我的后续文章。在此预留一个接口:某篇后续文章

在加入无风险资产的前提下,可以将效用函数设定为夏普比率(Sharpe Ratio)。则可构造优化问题:

maxrp−rfσp=ωTr−(1−ωf)rf(ωTΣω)12\text{max} \frac{r_p - r_f}{σ_p} = \frac{\omega^Tr - (1 - \omega_f) r_f} { (\omega^T \Sigma \omega)^{\frac{1}{2}}} maxσp​rp​−rf​​=(ωTΣω)21​ωTr−(1−ωf​)rf​​

s.t.

ωT1=1−ωfωi≥0,i=1,...,N,f\omega^T\bold{1} = 1 - \omega_f \\ \omega_i ≥ 0,i= 1, ..., N,f ωT1=1−ωf​ωi​≥0,i=1,...,N,f

其中,ωi表示第i类风险资产的权重;

ω表示n类风险资产的权重向量;

ωf表示无风险资产的权重;

ri表示第i类风险资产的预期收益率;

r表示n类风险资产的预期收益向量;

rf表示无风险利率;

rp表示资产组合的预期收益率;

σp表示资产组合的波动率;

Σ 表示n类风险资产的协方差矩阵。

​或者写成如下的标准形式:

min−ωTr−(1−ωf)rf(ωTΣω)12\text{min} - \frac{\omega^Tr - (1 - \omega_f) r_f} { (\omega^T \Sigma \omega)^{\frac{1}{2}}} min−(ωTΣω)21​ωTr−(1−ωf​)rf​​

s.t.

ωT1+ωf−1=0ωi≥0,i=1,...,N,f\omega^T\bold{1} + \omega_f - 1 =0 \\ \omega_i ≥ 0,i= 1, ..., N,f ωT1+ωf​−1=0ωi​≥0,i=1,...,N,f

可以发现,上述的标准约束优化问题共有N+1 个自变量,其限制条件分别为1 个等式约束条件和N+1 个不等式约束条件,且均为线性约束条件。

2.2 模型求解

对于上述线性规划问题,可以使用拉格朗日乘子法(Lagrange Lagrange multiplier Model)进行求解。通过构建拉格朗日函数:

L(x,λ,θ)=−ωTr−(1−ωf)rf(ωTΣω)12+λ(ωT1+ωf−1)−ωTθ−θfωfL(x, λ, \theta) = - \frac{\omega^Tr - (1 - \omega_f) r_f} { (\omega^T \Sigma \omega)^{\frac{1}{2}}} + λ( \omega^T\bold{1} + \omega_f - 1) -\omega^T\theta- \theta_f \omega_f L(x,λ,θ)=−(ωTΣω)21​ωTr−(1−ωf​)rf​​+λ(ωT1+ωf​−1)−ωTθ−θf​ωf​

可以根据其对应的一阶KKT条件( First Order Karush-Kuhn-Tucker Conditions)进行求解。

注1:关于一阶KKT条件相关介绍可以参考博客[1][最优化]不等式约束的优化问题求解;

注2:关于优化方法的相关内容笔者将会另起文章进行探讨。

此外,我们还可以使用数值算法求解上述优化问题,例如经典的牛顿迭代法(Newton Iteration Method)。许多编程软件都有线性\非线性约束优化问题的数值求解器,本文使用Python的Scipy包中的minimize函数进行上述优化问题的求解。对于R语言使用者,可以使用alabama包中的constrOptim.nl函数进行求解。

3. 策略实现伪代码

通过对马科维茨模型的求解,我们可以得到每一调仓周期的最优资产配置 (w,wf)。我们只需要在每一个调仓周期的起始点将仓位调整至最优配置比率即可。

策略的伪代码如下:

Step 0:始化参数。规定调仓频率(本文以T= 30 即30个交易日作为调仓周期进行说明),设定每一周期模型的优化目标函数(本文设定目标函数为夏普比率)。规定 t = 0,进入Step 1;

Step 1:在当前调仓日,根据前一周期的日收盘价格计算该周期内除无风险资产以外的各类资产日收益率ri,t与日波动率σi,t,并带入马科维茨模型中,生成各类资产目标权重wi,t;

Step 2:以当日收盘价格将各类资产比例调整至目标权重;

Step 3: 令t=t+ 1,重复Step 1直至回测结束。

4. 基于python的模型实现

4.1 python代码

首先,搭建好均值-方差优化求解框架模块(Mean_Variance)

# -*- coding: utf-8 -*-# author: Mikey_Sun time: 5/19/# all copyright belongs to the author. NO COPY ALLOWED.import mathfrom scipy.optimize import minimizeimport numpy as npimport pandas as pd########################## Module 1 MV框架模块 ##########################def M_V(R, rf, Sigma, output_type = 'Dict', print_out = False):''':param R: list格式,输入各收益序列:param rf: num格式或list格式,输入无风险利率:param Sigma: two-d list格式,输入各收益序列协方差矩阵:param output_type: Bool类型,True为打印结果,Flase为不打印结果:param : num格式或list格式:return: dataframe'''# Step 1: 初始化收入变量格式N = len(R) # 获取资产类别数R = np.array(R).reshape(N) # 格式化收益率序列rf = np.array(rf) # 格式化无风险利率Sigma = np.array(Sigma).reshape(N, N) # 格式化协方差矩阵# Step 2: 将无风险利率嵌入到收益率序列与协方差矩阵中(增广阵)R = np.r_[R, rf] # 增广收益率序列Sigma = np.c_[Sigma, np.zeros(N).reshape(N, 1)] # 增广协方差矩阵列Sigma = np.r_[Sigma, np.zeros(N+1).reshape(1, N+1)] # 增广协方差矩阵行# Step 3: 利用minimize函数进行模型求解:eps = 1e-10 # 找到一个非常接近0的值作为误差限w0 = np.ones(N+1) / (N+1) # 各类别资产权重参数初始化#fun = lambda w: 0.5 * math.log(np.dot(np.dot(w, Sigma), np.transpose(w))) - math.log(# np.dot(w, R - rf)) # 约束函数:最大化对数sharpe-ratiofun = lambda w: - np.dot(w, R - rf) / np.dot(np.dot(w, Sigma), np.transpose(w))**0.5 # 约束函数:最大化对数sharpe-ratiocons = ({'type': 'eq', 'fun': lambda w: np.sum(w) - 1}, # 限制条件一:全部投资{'type': 'ineq', 'fun': lambda w: w - eps}, # 限制条件二:不可做空)res = minimize(fun, w0, method='SLSQP', constraints=cons)# Step 4: 计算各指标w_op = res.x # 获取权重数据r_op = np.dot(res.x, R) # 计算收益率sigma_op = np.dot(np.dot(w_op, Sigma), np.transpose(w_op)) # 计算组合波动率SR = (r_op - rf) / sigma_op # 计算夏普比率# Step 5: 是否打印测试if print_out:print('''最优权重配比:{0:}最优收益率:{1:.2f}最优风险:{2:.2f}最优夏普比率:{3:.2f} '''.format(w_op, r_op, sigma_op, SR))# Step 6: 打印模块if str(output_type).upper() == 'DATAFRAME':return pd.DataFrame({'Weights':[w_op], 'Return':r_op, 'Sigma':sigma_op, 'SR':SR})elif str(output_type).upper() == 'DICT':return {'Weights':w_op, 'Return':r_op, 'Sigma':sigma_op, 'SR':SR}elif str(output_type).upper() == 'LIST':return [w_op, r_op, sigma_op, SR]else:return {'Weights':w_op, 'Return':r_op, 'Sigma':sigma_op, 'SR':SR}

下面是主函数模块代码块。其中引用的“Risk_Factor_Calculation”模块来自于本人自己开发的量化策略回测框架中的一部分,请参考:【基于python的量化策略回测框架搭建】策略表现衡量指标模块。

# -*- coding: utf-8 -*-# author: Mikey_Sun time: 5/26/# all copyright belongs to the author. NO COPY ALLOWED.import Risk_Factor_Calculation as RFimport Mean_Variance as MVfrom EmQuantAPI import * #使用chioce数据库。详细使用说明请查阅百度import numpy as npimport pandas as pdimport matplotlib.pyplot as plt################################### Module 1: 下载数据,并存储到本地 ###################################def download_data_from_choice():loginresult = c.start()codes = "510310.SH,511010.SH,518880.SH,131801.SZ" # 所需数据的代码。510310为沪深300ETF,511010为五年国债ETFindicators = "open,high,low,close" # 需要下载的数据指标startdate = "0729" # 回测起始时间,可自行修改enddate = "" # 回测结束时间,可自行修改data = c.csd(codes=codes, indicators=indicators, startdate=startdate, enddate=enddate,options=("Ispandas,1")) # 导入数据,以pd.DataFrame类型格式存储path = '你的数据保存地址' # 将数据保存到你的本地地址(需自行修改)data.to_excel(path, index=True, header=True)download_data_from_choice()path = '你的数据保存地址' #需要修改data = pd.read_excel(path) #读取数据data.set_index(data["DATES"], inplace=True) #设置数据索引为交易日tickers = ["510310.SH","511010.SH","518880.SH",'131801.SZ']HS300ETF = pd.DataFrame(data[data["CODES"] == '510310.SH']["CLOSE"].astype(float)) #沪深300ETF专属数据结构。仅保留收盘价格GZ5yETF = pd.DataFrame(data[data["CODES"] == '511010.SH']["CLOSE"].astype(float)) #国债ETF专属数据结构。仅保留收盘价格GoldETF = pd.DataFrame(data[data["CODES"] == '518880.SH']["CLOSE"].astype(float)) #黄金ETF专属数据结构。仅保留收盘价格R_001 = pd.DataFrame(data[data["CODES"] == '131801.SZ']["CLOSE"].astype(float)) #黄金ETF专属数据结构。仅保留收盘价格td_dates = HS300ETF.index #记录交易日信息,方便以后进行查找n = len(td_dates)######################### Module2: 计算收益率 #########################pd.set_option('mode.chained_assignment', None)# 创建收益率序列HS300ETF['RETURN'] = 0 #创建收益率序列GZ5yETF['RETURN'] = 0 #创建收益率序列GoldETF['RETURN'] = 0 #创建收益率序列R_001['RETURN'] = 0HS300ETF['RETURN'].astype(float)#设定格式为浮点GZ5yETF['RETURN'].astype(float)#设定格式为浮点GoldETF['RETURN'].astype(float)#设定格式为浮点R_001['RETURN'].astype(float)#设定格式为浮点# 计算收益率HS300ETF["RETURN"][td_dates[1:]] = np.array(HS300ETF["CLOSE"][td_dates[1:]]) / np.array(HS300ETF["CLOSE"][td_dates[:-1]]) - 1 #计算HS300ETF日收益率GZ5yETF["RETURN"][td_dates[1:]] = np.array(GZ5yETF["CLOSE"][td_dates[1:]]) / np.array(GZ5yETF["CLOSE"][td_dates[:-1]]) - 1 #计算GZ5yETF日收益率GoldETF["RETURN"][td_dates[1:]] = np.array(GoldETF["CLOSE"][td_dates[1:]]) / np.array(GoldETF["CLOSE"][td_dates[:-1]]) - 1 #计算GZ5yETF日收益率R_001["RETURN"][td_dates[1:]] = np.array(R_001["CLOSE"][td_dates[1:]])/100/365# 将收益率序列单独存储至returnsreturns = pd.DataFrame({'510310.SH':HS300ETF["RETURN"], '511010.SH':GZ5yETF["RETURN"], '518880.SH':GoldETF["RETURN"], '131801.SZ':R_001["RETURN"]},index= td_dates)################################### Module 3: 计算策略权重 #######################################def weights_calculate_MV(T):weights_strategy_MV = []for t in range(T, n, T):R = []for i in range(3):R.append(np.mean(returns[tickers[i]][td_dates[t - T:t]]) * T)rf = np.mean(returns['131801.SZ'][td_dates[t - T:t]]) * TSigma = np.cov(returns[tickers[0:3]][t - T:t] * T, rowvar=False)outcome = MV.M_V(R=R, rf=rf, Sigma=Sigma)for i in range(T):weights_strategy_MV.append(outcome['Weights'])return weights_strategy_MV################################### Module 4: 各策略净值与收益率计算 ###################################nv_HS300ETF = [1] #记录全仓股票策略净值nv_GZ5yETF = [1] #记录全仓债券策略净值nv_GoldETF = [1]nv_Cash = [1]r_HS300ETF = []r_GZ5yETF = []r_GoldETF = []r_Cash = []nv_strategy_MV = [1]r_strategy_MV = []T = 30 #设置调仓/回看窗口期(以30天为例)weights_strategy_MV = weights_calculate_MV(T)for i in range(T, n):nv_HS300ETF.append( nv_HS300ETF[i-T] *(1 + returns['510310.SH'][td_dates[i]]))nv_GZ5yETF.append( nv_GZ5yETF[i-T] *(1 + returns['511010.SH'][td_dates[i]]))nv_GoldETF.append( nv_GoldETF[i-T] *(1 + returns['518880.SH'][td_dates[i]]))nv_Cash.append( nv_Cash[i-T] *(1 + returns['131801.SZ'][td_dates[i]]))r_HS300ETF.append(returns['510310.SH'][td_dates[i]])r_GZ5yETF.append(returns['511010.SH'][td_dates[i]])r_GoldETF.append(returns['518880.SH'][td_dates[i]])r_Cash.append(returns['131801.SZ'][td_dates[i]])if i >= T:r_strategy_MV.append(weights_strategy_MV[i - T][0] * returns[tickers[0]][td_dates[i]] +weights_strategy_MV[i - T][1] * returns[tickers[1]][td_dates[i]] +weights_strategy_MV[i - T][2] * returns[tickers[2]][td_dates[i]] +weights_strategy_MV[i - T][3] * returns[tickers[3]][td_dates[i]])else:r_strategy_MV.append(0)nv_strategy_MV.append(nv_strategy_MV[i-T] * (1 + r_strategy_MV[i-T]))################################################### Module 5: 汇总各策略净值信息,并计算其风险及收益情况 ###################################################r_series = [r_HS300ETF, r_GZ5yETF, r_GoldETF, r_Cash, r_strategy_MV]nv_series = [nv_HS300ETF, nv_GZ5yETF, nv_GoldETF, nv_Cash, nv_strategy_MV]Period = 'D'Indicators = 'ALL'alpha = 0.05output_type = 'pd.DataFrame'strategy_names = ['沪深300', '债券ETF', '黄金ETF', '国债逆回购', '马科维茨策略']# 进入风险值计算回测系统框架(该框架为自主创建,需要读取该文件)Risks = RF.Risk_Indicators(r_series, nv_series, Period = Period, Indicators= Indicators, alpha=alpha, output_type=output_type)Risks = pd.DataFrame(Risks.values.tolist(), index = strategy_names, columns = Risks.columns)#打印风险值情况print(Risks[Risks.columns[:4]])print(Risks[Risks.columns[4:]])################################## Module 6: 绘制各策略净值走势图 ##################################plt.plot(nv_HS300ETF, label = "nv_HS300ETF", color = "black")plt.plot(nv_GZ5yETF, label = "nv_GZ5yETF", color = "red")plt.plot(nv_GoldETF, label = "nv_GoldETF", color = "blue")plt.plot(nv_Cash, label="nv_Cash", color="purple")plt.plot(nv_strategy_MV, label="nv_strategy_MV", color="green")plt.show()

4.2 实证结果

代码运行结果如下所示:

ReturnSigma R/S MDD沪深300 0.121751 0.245480 0.495972 1.046858债券ETF 0.037404 0.028362 1.318794 0.056963黄金ETF 0.058866 0.126968 0.463625 0.216147国债逆回购 0.018584 0.000557 33.345747 0.000000马科维茨策略 0.061792 0.106153 0.582107 0.338244MDD_R MDD_P VaRCVaR沪深300 0.456784 155.0 0.099732 0.099732债券ETF 0.049023 39.0 0.016768 0.016768黄金ETF 0.213716 461.0 0.046573 0.046573国债逆回购 0.000000 0.0 0.000000 0.000000马科维茨策略 0.258140 17.0 0.065919 0.065919

*数据来源:Choice数据库

图1 /09/09 - /5/15 期间各策略净值走势图(以9月8日为基日,定义净值为1。黑线为全股策略,红线为全债策略,蓝线为纯黄金策略,紫线为纯现金策略,绿线为马科维茨模型策略)

读者也可以将调仓频率更改为每半年一次(设置T= 120)或者每季度一次(设置T= 60)。由于策略的表现大同小异,笔者在此不做进一步展示。

5. 结论

5.1 模型策略结果

从策略表现上来看,该策略在近8年的回测中,年化收益率略与年化波动率高于纯债和纯黄金策略,而远低于纯股策略,夏普比率却没有明显高于上述策略;从尾部风险角度来看,该策略的最大回撤比率达到了25%,而95%置信度下的在险价值则高达6.6%,远远高于纯债和纯黄金组合,也远高于前文中的均衡配置策略。

综上,基于马科维茨模型的资产配置策略相比于前文的均衡配置策略并没有明显的优势。

5.2 模型可能存在的问题

这一策略并没有给我们带来预期效果。导致模型没有达到预期效果的原因可能有三:

(1)参数估计量的有偏性。模型的预期收益率和协方差阵均是通过历史的样本均值和样本协方差来模近似的。这些估计量可能在中长期或震荡市是相对无偏的;然而,在市场的反转信号已经明朗的情况下,估计量存在较大偏差,而模型并不能做出及时的更正,从而导致资产配置存在滞后性。

(2)模型的高灵敏性。模型对参数的灵敏度较高,或者说模型严重依赖于参数输入的准确性。这一特点是由模型算法的特性决定的。有些时候,参数极其细微的误差就会导致模型的解存在一个数量级的差距。正可谓“差之毫厘,谬以千里”。

(3)模型严苛的假设前提。模型假设市场是有效的,且所有资产的收益序列特征可以通过均值-方差这两个指标进行完全描述(这假设暗含收益率服从正态分布)。然而,有诸多的研究结果表明中国的金融市场并不是有效的。而且根据笔者对中国市场中股票、债券、黄金等标的收益率的实证研究发现,上述各资产收益率序列的经验分布存在严重的尖峰肥尾特征。不过,也有相关研究表明:收益率序列满足稳态分布(Stable Distribution)的前提下,均值-方差的配置框架依然有效(不必一定是正态分布)。相关的研究文献请自行百度。

此外,马科维茨模型还会导致仓位调整幅度过大。在每一个调仓周期,由于模型的高灵敏性,每一类资产的权重将会发生较大的变化,而过大的调仓幅度可能会带来高昂的成本。这对于高净值投资者来说很难接受。

5.3 留给读者的一些悬念

虽然马科维茨模型在应用层面上存在比较严重的问题,但是笔者仍然认为该模型是一个非常具有参考价值的模型。而对于模型中存在的“硬伤”,笔者则有一些改进方向,在此分享给喜好研究的读者,有兴趣的读者可以自行验证,也希望有相关成果的读者与笔者分享、交流心得~:

(1)在模型中关于预期收益率和协方差阵的估计过程中,使用指数移动加权平均方法(EWMA)是否可以相比与本文的简单平均法(SMA)具有更好的效果?

(2)除了上述的历史模拟法以外,能否分别对收益率序列和波动率序列进行建模估计(例如使用GARCH模型估计波动率,或者建立Ornstein-Uhlenbeck过程描述收益率与波动率序列)?

(3)是否可以将马科维茨的均值-方差框架进行高维延拓(例如,增加偏度(Skewness)、峰度(Kurtosis)等高阶矩指标),并用高维效用函数作为资产配置的目标函数?

(4)能不能将一些专业分析师的主流观点融入到均值-方差框架中呢?

6. 参考文献

【1】[最优化]不等式约束的优化问题求解

免责声明

免责声明:本文内容及观点仅供参考,不构成任何投资建议,投资者据此操作,风险自担。一切有关本文涉及的数据均来自于第三方数据机构,使用时请查阅第三方数据平台的使用说明,本文不负有数据准确性的直接负责。本文所述策略表现均基于历史数据,不代表对未来的预期。投资有风险,入市需谨慎。

写在最后

若想查阅本系列全部文章,请参见目录页:系列文章目录索引。

欢迎感兴趣的小伙伴来跟作者一起挑刺儿~ 包括但不限于语言上的、排版上的和内容上的不足和疏漏~ 一起进步呀!

有任何问题,欢迎在本文下方留言,或者将问题发送至勘误邮箱: mikeysun_bugfix@

谢谢大家!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。