引言
前面我们提到了许多金融模型需要用到正态分布的假设,MPT就是其中的一个,接下来就简要介绍一下MPT。
投资组合优化
我们接下来探讨马科维茨的现代资产组合理论,也就是MPT。MPT的核心就是均值-方差模型,基于均值来描述资产的收益,基于方差来描述资产的风险,从而得到了特定风险水平下投资者(风险厌恶)如何构建组合来最大化期望收益。
在特定风险水平下,使得期望收益最大的投资组合为有效组合,不同风险水平下的有效组合组成的边界称之为有效前沿。不同风险偏好的投资者会在不同的风险水平下选择其投资组合。
均值-方差分析
我们仍然以前述四种资产为例进行分析。假设不允许投资者在金融工具中设立空头头寸,只允许多头头寸,这意味着100%的投资者财富必须在可用工具之间进行分配,以使所有头寸均为正且头寸加起来达到100%。 那么,容易得到资产组合的期望收益率和方差为:
μ p = E ( ∑ I w i r i ) = w T μ σ p 2 = E ( ( r − μ ) 2 ) = w T Σ w \begin{aligned}
\mu_p&=E(\sum_{I}w_i r_i) = w^T\mu\\
\sigma^2_p &= E((r-\mu)^2)= w^T\Sigma{w}
\end{aligned}
μ p σ p 2 = E ( I ∑ w i r i ) = w T μ = E ( ( r − μ ) 2 ) = w T Σ w
其中, w = [ w 1 , … , w 4 ] w=[w_1, \ldots, w_4] w = [ w 1 , … , w 4 ] 为权重向量, Σ \Sigma Σ 为四种资产收益率的协方差矩阵, ∑ I w i = 1 , w i > 0 \sum_{I}w_i=1, w_i>0 ∑ I w i = 1 , w i > 0 。
蒙特卡洛模拟
我们通过蒙特卡洛模拟不同比例的投资组合,得到其波动性(标准差)和期望收益率,在整个平面出绘制出可行域,可行域的上边界就是有效前沿。
1 2 3 4 5 rets = np.log(data / data.shift(1 )) annual_mean_rets = rets.mean() * 252 annual_cov_rets = rets.cov() * 252
1 2 3 4 5 6 7 8 9 10 11 12 13 14 def port_ret (weights ): return np.sum (annual_mean_rets*weights) def port_vol (weights ): return np.sqrt(np.dot(weights.T, np.dot(annual_cov_rets, weights))) prets = [] pvols = [] noa = len (data.columns) for p in range (100000 ): weights = np.random.random(noa) weights /= np.sum (weights) prets.append(port_ret(weights)) pvols.append(port_vol(weights)) prets = np.array(prets) pvols = np.array(pvols)
1 2 3 4 5 6 plt.figure(figsize=(15 , 9 )) plt.scatter(pvols, prets, c=prets / pvols, marker='o' , cmap='coolwarm' ) plt.xlabel('expected volatility' ) plt.ylabel('expected return' ) plt.colorbar(label='Sharpe ratio' );
从图上我们可以清晰地看到,在波动率相同的情况下,有不同的投资组合可以选择,但是我们最关心的就是上面最清晰的那条边界也就是有效前沿。
最优化投资组合
我们上面通过蒙特卡洛模拟能够将整个可行域可视化,那么如何得到“确切”的最优解呢?这就用到了我们前面提到的数值优化的方法,显然这是一个带约束的优化问题,如果我们将夏普比率最大作为优化目标:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import scipy.optimize as scodef min_func_sharpe_ratio (weights ): return -port_ret(weights) / port_vol(weights) cons = ({'type' :'eq' , 'fun' : lambda x: np.sum (x)-1 }) bnds = tuple ((0 , 1 ) for x in range (noa)) eweights = np.array(noa * [1. /noa,])
1 2 3 opts = sco.minimize(min_func_sharpe_ratio, eweights, method='SLSQP' , bounds=bnds, constraints=cons) opts
fun: -1.047072952146737
jac: array([-0.00027794, 0.00021888, 0.00056295, -0.00085148])
message: 'Optimization terminated successfully.'
nfev: 31
nit: 5
njev: 5
status: 0
success: True
x: array([0.46094944, 0.23611206, 0.23543833, 0.06750017])
而如果我们将最小化波动性最为优化目标,我们可以得到有效前沿的最左端:
1 2 3 optv = sco.minimize(port_vol, eweights, method='SLSQP' , bounds=bnds, constraints=cons) optv
fun: 0.10789487898601618
jac: array([0.11474433, 0.11216656, 0.10798594, 0.1077927 ])
message: 'Optimization terminated successfully.'
nfev: 48
nit: 8
njev: 8
status: 0
success: True
x: array([1.30104261e-18, 0.00000000e+00, 5.28790073e-01, 4.71209927e-01])
求解有效前沿
那么,只要对于不同的期望收益率,最小化其波动性(或者对于给定的波动性,最大化其收益率)就可以得到有效前沿上的点(严格来说,前者还可以求得无效边界)。
1 2 3 4 5 6 7 8 9 10 cons = ({'type' : 'eq' , 'fun' : lambda x: port_ret(x) - tret}, {'type' : 'eq' , 'fun' : lambda x: np.sum (x) - 1 }) bnds = tuple ((0 , 1 ) for x in weights) trets = np.linspace(0.05 , 0.25 , 100 ) tvols = [] for tret in trets: res = sco.minimize(port_vol, eweights, method='SLSQP' , bounds=bnds, constraints=cons) tvols.append(res['fun' ]) tvols = np.array(tvols)
1 2 3 4 5 6 7 8 9 10 11 12 plt.figure(figsize=(15 , 9 )) plt.scatter(pvols, prets, c=prets / pvols, marker='.' , alpha=0.8 , cmap='coolwarm' ) plt.plot(tvols, trets, 'b' , lw=4.0 ) plt.plot(port_vol(opts['x' ]), port_ret(opts['x' ]), 'y*' , markersize=15.0 ) plt.plot(port_vol(optv['x' ]), port_ret(optv['x' ]), 'r*' , markersize=15.0 ) plt.xlabel('expected volatility' ) plt.ylabel('expected return' ) plt.colorbar(label='Sharpe ratio' ) plt.show()
资本市场线
CAL与CML
投资者首先确定有效的风险资产组合(位于有效前沿上),然后将无风险资产添加到组合中。通过调整投资于无风险资产的投资者财富比例,可以实现无风险资产与有效风险资产组合中的任何风险回报情况。而这些组合会构成一条直线,这条直线就叫做资本配置线(capital allocation line, CAL),它的斜率等于选择的资产组合每增加一单位标准差上升的期望收益,截距为无风险利率。
对于整个市场而言,首先资本市场线(capital market line, CML)也是一条CAL。但是,其特殊性在于,在资本资产定价模型假设下,当市场达到均衡时,市场组合成为一个有效组合,而且这个均衡状态是唯一的,即CML与有效前沿相切,切点为均衡状态。容易发现,CML的斜率是所有具有可行解的CAL里面斜率最大的,意味着CML的夏普比率最大,也就是说,市场最优的投资组合就是夏普比率最大的投资组合。
求解均衡状态
如上所述,均衡状态是CML与有效前沿的切点,这就又变成了一个解方程问题。假设有效前沿和无风险利率已知,求一条过无风险利率点,且与有效前沿相切的直线。
切点处CML与有效前沿的函数值和一阶导数值相等。因此需要对有效前沿进行求导,但是我们并没有显式的有效前沿函数。为了解决这个问题,可以利用插值的方法解决:选择三次样条函数进行插值,能够满足一阶可导的需要。
1 2 3 4 5 6 7 8 9 10 11 12 13 import scipy.interpolate as sciind = np.argmin(tvols) evols = tvols[ind:] erets = trets[ind:] tck = sci.splrep(evols, erets) def f (x ): ''' Efficient frontier function (splines approximation). ''' return sci.splev(x, tck, der=0 ) def df (x ): ''' First derivative of efficient frontier function. ''' return sci.splev(x, tck, der=1 )
scipy.optimize
模块中的fsolve()
函数可以用来解方程组,我们根据切点的条件联立方程,假设无风险利率为0.01:
1 2 3 4 5 6 7 8 9 10 11 12 13 import scipy.optimize as scodef equations (p, rf=0.01 ): eq1 = rf - p[0 ] eq2 = rf + p[1 ] * p[2 ] - f(p[2 ]) eq3 = p[1 ] - df(p[2 ]) return eq1, eq2, eq3 opt = sco.fsolve(equations,[0.01 , 0.5 , 0.15 ]) opt
array([0.01 , 0.99400907, 0.19900814])
1 2 3 4 5 6 7 8 9 10 11 12 13 14 plt.figure(figsize=(15 , 9 )) plt.scatter(pvols, prets, c=(prets - 0.01 ) / pvols, marker='.' , cmap='coolwarm' ) plt.plot(evols, erets, 'b' , lw=4.0 ) cx = np.linspace(0.0 , 0.3 ) plt.plot(cx, opt[0 ] + opt[1 ] * cx, 'r' , lw=1.5 ) plt.plot(opt[2 ], f(opt[2 ]), 'y*' , markersize=15.0 ) plt.grid(True ) plt.axhline(0 , color='k' , ls='--' , lw=2.0 ) plt.axvline(0 , color='k' , ls='--' , lw=2.0 ) plt.xlabel('expected volatility' ) plt.ylabel('expected return' ) plt.colorbar(label='Sharpe ratio' ) plt.show()
我们可以看一下在最优点的资产组合情况:
1 2 3 4 5 6 7 8 cons = ({'type' : 'eq' , 'fun' : lambda x: port_ret(x) - f(opt[2 ])}, {'type' : 'eq' , 'fun' : lambda x: np.sum (x) - 1 }) res = sco.minimize(port_vol, eweights, method='SLSQP' , bounds=bnds, constraints=cons) print (f'The optimal portfolo is: {res.x} ' )print (f'The optiaml return is: {port_ret(res["x" ])} ' )print (f'The optimal voltality is: {port_vol(res["x" ])} ' )print (f'The optimal Sharpe ratio is: {port_ret(res["x" ]) / port_vol(res["x" ])} ' )
The optimal portfolo is: [0.54371066 0.28277661 0.17027672 0.00323601]
The optiaml return is: 0.20781589358674787
The optimal voltality is: 0.19900820203661537
The optimal Sharpe ratio is: 1.044257932386686