ポートフォリオ理論の基礎として、平均分散アプローチを紹介しましたが、これを活用するためには、期待リターンとリスク、各資産間の相関が必要になります。
しかし、これまでの記事でも確認したように期待リターンをどのように推定するかという問題は非常に難しい問題です。
それに比べて、リスクや相関は過去の時系列からの乖離が比較的少なく、推定しやすいです。
その性質を利用して、リスクのみに着目したアロケーション手法が提案されています。
その代表的な手法である、最小分散ポートフォリオとリスクパリティ―ポートフォリオについて、これからの数回で確認していきたいと思います。
最小分散ポートフォリオとは
以前の記事でも紹介したように、最小分散ポートフォリオとは、効率的フロンティアの中で、最もリスクの小さいポートフォリオのことを示しています。
CAPMに基づくと、最小分散ポートフォリオは無リスク資産が存在する状況で、最適なポートフォリオではありませんが、実務においては、時価総額加重平均のポートフォリオよりも、パフォーマンスがいいといった研究結果もあります。
その他に最小分散ポートフォリオが好まれる理由としては、推定の難しい期待リターン情報が必要ないからということが挙げられます。一般的に、リターンの推定誤差よりも分散の推定リスクの方が小さいため、安定した結果が得られやすいのです。
最小分散ポートフォリオの導出
2資産の場合
ポートフォリオの分散は
となります。
ウェイトの合計は1になるので、
を使って、
という最適化問題を解くことになります。
$w$で微分して0になるwが求めたいウェイトになるので、
を解くことになります。
これに先ほどの標準偏差を求める式を代入すると、
となるので、$=0$として$w$について解くと、
となり解を得ます。
この$w$は$w_{1}$にあたるので、$w_{2}$は
となります。
多資産の場合
多資産の場合も2資産の場合と同様にポートフォリオの標準偏差を最小化するという最適化問題を解きます。
という最適化問題を解くことになります。
この問題はPythonなどを使えば簡単に最適化問題として解を得ることができます。
最小分散ポートフォリオの実装
ここまでの議論をもとに実際にPythonで最小分散ポートフォリオのウェイトを求めてみます。
最適化にはPythonのscipyを使います。
import scipy.optimize as op
import pandas as pd
import numpy as np
def cal_min_vol(Sigma):
#目的関数の設定
def min_vol(w):
var = np.dot(w.T, np.dot(Sigma, w))
return var
n = Sigma.shape[0]
#制約条件
cons = [{'type': 'eq', 'fun': lambda x: np.sum(x) - 1}]
bnds = [(0, None)] * n
#初期値
x0 = [1./n] * n
#最適化実行
opts = op.minimize(fun=min_vol, x0=x0, method='SLSQP', bounds=bnds, constraints=cons)
return opts['x']
この関数を、例えば、リスクが6%と13%相関が-0.2の2資産に適用した場合を見てみます。
s1 = 0.13
s2 = 0.06
rho = -0.2
Sigma = np.array([[s1*s1, rho*s1*s2],[rho*s1*s2, s2*s2]])
cal_min_vol(Sigma)
array([0.21845893, 0.78154107])
この結果を、解析的に解いて求めた2資産の場合のウェイト式に当てはめた場合と比較すると、
w1 = (s2**2-rho*s1*s2)/(s1**2 + s2**2 - 2*rho*s1*s2)
print(w1)
w2 = (s1**2-rho*s1*s2)/(s1**2 + s2**2 - 2*rho*s1*s2)
print(w2)
0.21845893310753595
0.7815410668924639
このように概ね結果が一致していることが確認できます。
多資産の場合でも、この関数を使って、各資産の最適ウェイトを求めることができます。
#4資産の場合
s1 = 0.13
s2 = 0.06
s3 = 0.03
s4 = 0.08
rho12 = 0.5
rho13 = 0.2
rho14 = -0.3
rho23 = 0.4
rho24 = -0.1
rho34 = 0.2
Sigma = np.array([
[s1**2, rho12*s1*s2, rho13*s1*s3, rho14*s1*s4],
[rho12*s1*s2, s2**2, rho23*s2*s3, rho24*s2*s4],
[rho13*s1*s3, rho23*s2*s3, s3*s3, rho34*s3*s4],
[rho14*s1*s4, rho24*s2*s4, rho34*s3*s4, s4*s4]
])
w = cal_min_vol(Sigma)
w
array([0.00936281, 0.06587514, 0.84607625, 0.0786858 ])