In [1]:
import numpy as np
import pandas as pd
from bokeh.io import output_notebook
from bokeh.plotting import figure, show
import scipy.optimize as sco
output_notebook()
Loading BokehJS ...

基本分散投資はマーコビッツの平均分散モデルが使われます。

  • 各資産の収益率、リスク、相関、共分散を使い、各資産にどのように投資したらどのようなリスクリターンの運用になるかを作ります。
  • 1952年に発表された古い理論です。まぁ単純で簡単に作れるから使われているのだろう。
  • 難点 リターンの予想が難しいが、そのリターン予想にポートフォリオの決定が大きく影響される。
  • Scipyで2次計画問題を解く
    https://qiita.com/ryoshi81/items/b323a363e5442a15db6d
  • 最近の理論になるとブラック・リッターマンとかリスクパリティとかあります。

ブラックリッターマン

リスクパリティ

  • 今までのやり方は結局リスクが大きい商品のポートフォリオへの影響が大きい。
  • 資産のリスクはあまり変化しない。
  • 各資産のリスクを同一にする。
  • そうすると基本債券のウェイトが高くなり、リターンが下がるがその分、レバレッジを掛ける。
  • 世界最大のヘッジファンド:ブリッジウォーターが提唱したやり方で、徐々に取り入れられ始めているのではないか?
  • しかし、現在債券バブルと言われる環境下にあるので、それほどおすすめでない。
  • Bridgewater /Research_Library/Risk_parity: https://www.bridgewater.com/research-library/risk-parity/

さて進めましょう!

  • 今回はロボアドバイザー大手のウェルスナビのモデルを使います。
    https://www.wealthnavi.com/image/WealthNavi_WhitePaper.pdf
  • リターンの推測にはブラック・リターマンが使われている。
  • 上記のPDFにウェルスナビの考えるリスクリターンと相関が掲載されています。
  • 資産クラスは米国株、欧日株、新興株、米国債券、物価連動債、金、不動産の7資産となっています。
In [2]:
rtns = np.array([0.065, 0.075, 0.085, 0.019, 0.023, 0.039, 0.058])  # 収益率(年率)
rsks = np.array([0.124, 0.148, 0.184, 0.028, 0.048, 0.179, 0.149])  # リスク(年率)
corrs = np.array([
    [1.0, 0.9, 0.8, -0.1, 0, 0, 0.7],
    [0.9, 1.0, 0.8, -0.1, 0.1, 0.1, 0.6],
    [0.8, 0.8, 1.0, 0.1, 0.2, 0.3, 0.6],
    [-0.1, -0.1, 0.1, 1.0, 0.8, 0.4, 0.4],
    [0, 0.1, 0.2, 0.8, 1.0, 0.5, 0.4],
    [0, 0.1, 0.3, 0.4, 0.4, 1.0, 0.1],
    [0.7, 0.6, 0.6, 0.4, 0.4, 0.1, 1.0]
]) # 相関
In [3]:
p = figure(width= 800, height = 500, title='各資産のリスクリターン')
p.cross(rsks, rtns, size=20)
p.xaxis.axis_label='リスク'
p.yaxis.axis_label='リターン'
show(p)
In [4]:
# 相関行列と標準偏差から共分散行列を作成
# 相関係数(x, y) = 共分散 / (標準偏差(x) * 標準偏差(y) )
# 共分散 = 相関係数(x, y) * (標準偏差(x) * 標準偏差(y))
# これを作る部分が難しい。なんか簡単な方法ないのかなぁ。

a1 = []
a2 = []
a3 = []
a4 = []
a5 = []
a6 = []
a7 = []

def testi(r = 0.065, box = a1, volls = rsks):
    for i in range(len(rsks)):
        box.append(np.dot(r ,rsks[i]))
    return np.array(box)

b1 = testi()
b2 = testi(r = rsks[1], box = a2)
b3 = testi(r = rsks[2], box = a3)
b4 = testi(r = rsks[3], box = a4)
b5 = testi(r = rsks[4], box = a5)
b6 = testi(r = rsks[5], box = a6)
b7 = testi(r = rsks[6], box = a7)

SIgma = [b1 * np.array(corrs[0]), b2 * np.array(corrs[1]), b3 * np.array(corrs[2]), \
        b4 * np.array(corrs[3]), b5 * np.array(corrs[4]), b6 * np.array(corrs[5]), b7 * np.array(corrs[6])]

以上で各資産のリスクリターン共分散を作成しました。

以下ではscipy.optimize.minimize()を解くための引数などを作ります。

  • 目的関数、初期解、ソルバー、資産配分の上下制約、制約条件が必要です。
In [5]:
# 目的関数
def min_func_var(weights):
    return np.dot(weights.T, np.dot(SIgma, weights))

# 初期解
x0 = [1.0 / len(rtns)] * len(rtns)

# 目標リターン(各資産の最高、最低で作る。レバレッジを掛けない限り採用資産のリターンの最高がポートフォリオのリターンの上限となる)

max_ret = rtns.max()
min_ret = rtns.min()
trets = np.linspace(min_ret, max_ret, 100)

# 投資比率制限(空売りを出来なくする)
bnds = [(0, None)] * len(rtns)
In [6]:
tvols = []

for tret in trets:
    cons = [{'type': 'eq', 'fun': lambda x: np.sum(x) - 1},
           {'type': 'ineq', 'fun': lambda x: np.sum(rtns * x) - tret}]
    res = sco.minimize(fun=min_func_var, x0 = x0, method = 'SLSQP', bounds = bnds, constraints = cons)
    tvols.append((np.sqrt(res['fun'])))

tvols = np.array(tvols)
In [7]:
p = figure(width = 800, height=500, title='ポートフォリオのリスクリターン')
p.line(tvols, trets, line_color='blue', line_width=3)
p.cross(rsks, rtns, size = 20)
p.xaxis.axis_label='ポートフォリオのリスク'
p.yaxis.axis_label = 'ポートフォリオのリターン' 
show(p)

資産にちょっと変更を加えてみる

  • 投資の基本として資産間の相関の低いものを入れるとポートフォリオのリターンが向上すると言うものがあります。
  • 分散投資とよく言われますが、よく似た動きの商品を沢山購入しても実は意味がありません。
  • ここではもしもの話として米株、欧日株、新興株の相関が低下したとしてのポートフォリオの見てみます。
In [8]:
corrs2 = np.array([
    [1.0, 0.2, 0.1, -0.1, 0, 0, 0.7],
    [0.2, 1.0, 0.1, -0.1, 0.1, 0.1, 0.6],
    [0.1, 0.1, 1.0, 0.1, 0.2, 0.3, 0.6],
    [-0.1, -0.1, 0.1, 1.0, 0.8, 0.4, 0.4],
    [0, 0.1, 0.2, 0.8, 1.0, 0.5, 0.4],
    [0, 0.1, 0.3, 0.4, 0.4, 1.0, 0.1],
    [0.7, 0.6, 0.6, 0.4, 0.4, 0.1, 1.0]
])

# 最初の
corrs = np.array([
    [1.0, 0.9, 0.8, -0.1, 0, 0, 0.7],
    [0.9, 1.0, 0.8, -0.1, 0.1, 0.1, 0.6],
    [0.8, 0.8, 1.0, 0.1, 0.2, 0.3, 0.6],
    [-0.1, -0.1, 0.1, 1.0, 0.8, 0.4, 0.4],
    [0, 0.1, 0.2, 0.8, 1.0, 0.5, 0.4],
    [0, 0.1, 0.3, 0.4, 0.4, 1.0, 0.1],
    [0.7, 0.6, 0.6, 0.4, 0.4, 0.1, 1.0]
]) # 相関
In [9]:
c1 = []
c2 = []
c3 = []
c4 = []
c5 = []
c6 = []
c7 = []

d1 = testi(r = rsks[0], box = c1)
d2 = testi(r = rsks[1], box = c2)
d3 = testi(r = rsks[2], box = c3)
d4 = testi(r = rsks[3], box = c4)
d5 = testi(r = rsks[4], box = c5)
d6 = testi(r = rsks[5], box = c6)
d7 = testi(r = rsks[6], box = c7)

SIgma2 = [d1*np.array(corrs2[0]), d2*np.array(corrs2[1]), d3*np.array(corrs2[2]), d4*np.array(corrs2[3]), \
         d5*np.array(corrs2[4]), d6*np.array(corrs2[5]), d7*np.array(corrs2[6])]
In [10]:
SIgma2
Out[10]:
[array([ 0.015376 ,  0.0036704,  0.0022816, -0.0003472,  0.       ,
         0.       ,  0.0129332]),
 array([ 0.0036704,  0.021904 ,  0.0027232, -0.0004144,  0.0007104,
         0.0026492,  0.0132312]),
 array([ 0.0022816,  0.0027232,  0.033856 ,  0.0005152,  0.0017664,
         0.0098808,  0.0164496]),
 array([-0.0003472, -0.0004144,  0.0005152,  0.000784 ,  0.0010752,
         0.0020048,  0.0016688]),
 array([ 0.       ,  0.0007104,  0.0017664,  0.0010752,  0.002304 ,
         0.004296 ,  0.0028608]),
 array([ 0.       ,  0.0026492,  0.0098808,  0.0020048,  0.0034368,
         0.032041 ,  0.0026671]),
 array([ 0.0129332,  0.0132312,  0.0164496,  0.0016688,  0.0028608,
         0.0026671,  0.022201 ])]
In [11]:
def min_func_var2(weights):
    return np.dot(weights.T, np.dot(SIgma2, weights))
In [12]:
tvols2 = []

for tret in trets:
    cons = [{'type': 'eq', 'fun': lambda x: np.sum(x) - 1},
           {'type': 'ineq', 'fun': lambda x: np.sum(rtns * x) - tret}]
    res = sco.minimize(fun=min_func_var2, x0 = x0, method = 'SLSQP', bounds = bnds, constraints = cons)
    tvols2.append((np.sqrt(res['fun'])))

tvols2 = np.array(tvols2)    
In [13]:
p = figure(width=800, height = 500, title='ポートフォリオのリスクリターン')
p.line(tvols, trets, line_color='blue', line_width=3, legend='高相関')
p.line(tvols2, trets, line_color='red', line_width=3, legend='低相関')
p.cross(rsks, rtns, size=20)

p.xaxis.axis_label ='ポートフォリオのリスク'
p.yaxis.axis_label = 'ポートフォリオのリターン'
p.legend.location = 'top_left'
show(p)