Pythonで5資産の最適なアセットアロケーションを計算

# 1. はじめに
## 1.1 何ができるか
5資産の最適なアセットアロケーションをもとめることができる。1資産の期待収益率（期待リターン）とボラティリティ（標準偏差、リスク）は既知のものとする。欲しい期待収益率をinputし、最適なアセットアロケーション（資産配分）をoutputする。ここで、最適なアセットアロケーションとは、効率的フロンティア曲線に乗るような資産の組み合わせのことと定義する。（もう少しわかりやすく言い換えると、「最適なアセットアロケーションを計算する」とは、「欲しい期待収益率を固定したときに標準偏差が最小となるような資産配分を求める」ということである。）
# 2. プログラミング
## 2.1 複数の資産の期待収益率の求め方
投資比率が均等のときは単純に平均すればいい。投資比率が異なる場合は、その重みを考慮すればいい。5資産から成るポートフォリオの期待収益率$r_p$は、資産$i$の期待収益率$r_i$、資産$i$の投資比率$x_i$のとき、以下のように表すことができる。
$$r_p=\sum_{i=1}^5 r_ix_i$$
プログラムにすると、以下のようになる。

```python:test01.py
rp=0
for i in range(0,5):
    rp+=r[i]*x[i]
```
## 2.2 複数の資産の標準偏差の求め方
それぞれの資産の標準偏差がわかっていても、それが組み合わさったときの標準偏差は単純に平均すればいいわけではない。5資産から成るポートフォリオの標準偏差$\sigma_p$(⇔分散${\sigma_p}^2$)と、資産$i$と資産$j$の収益率の相関係数$\rho_{ij}$、資産$i$の収益率の標準偏差$\sigma_i$、資産$i$の投資比率$x_i$の関係は、以下のように表すことができる。<a href="#a1">[1]</a>
$${\sigma_p}^2=\sum_{i=1}^5\sum_{j=1}^5\rho_{ij}\sigma_i\sigma_jx_ix_j$$
添え字については<a href="#a1">[2]</a>。これをプログラムにすると、以下のようになる。

```python:test02.py
sigmap=0
for i in range(0,5):
    for j in range(0,5):
        sigmap+=rho[i][j]*sigma[i]*sigma[j]*x[i]*x[j]
```
## 2.3 投資比率の決定
### 2.3.1 手動で設定
$x$は投資比率であったから、合計すると100%にならなくてはならない。つまり、x[0]+x[1]+x[2]+x[3]+x[4]が100%になる組み合わせである必要がある。例えば、x[0]=50%, x[1]=0%, x[2]=20%, x[3]=20%, x[4]=10%とするとき、5資産の期待収益率と標準偏差は以下のように計算できる。

```python:test03.py
# -*- coding: utf-8 -*-
"""
5資産の期待収益率と標準偏差を計算
"""
import numpy as np
#-------------------------------------------------
# input data
#-------------------------------------------------
r = [1.00,4.80,3.50,5.00,9.25] # 期待収益率
rho = [[1,0.16,-0.06,-0.05,-0.29],
[0.16,1,-0.25,0.27,0.73],
[-0.06,-0.25,1,0.56,0.11],
[-0.05,0.27,0.56,1,0.91],
[-0.29,0.73,0.11,0.91,1]] # 相関係数
sigma=[5.4,22.15,13.25,19.59,26.25] # 標準偏差
x = [0.5,0,0.2,0.2,0.1] # 投資比率

#-------------------------------------------------
# 本文
#-------------------------------------------------
n=len(x) # 資産の数
#----期待収益率----
rp=0
for i in range(0,n):
    rp+=r[i]*x[i]
#----標準偏差----
sigmap=0
for i in range(0,n):
    for j in range(0,n):
        sigmap+=rho[i][j]*sigma[i]*sigma[j]*x[i]*x[j]
sigmap=np.sqrt(sigmap) # 分散から標準偏差に変換
#----output----
print('ポートフォリオの期待収益率 {:.2f}'.format(rp))
print('ポートフォリオの標準偏差 {:.2f}'.format(sigmap))
```
出力結果：ポートフォリオの期待収益率 3.12
ポートフォリオの標準偏差 7.90
ここで、資産の期待収益率と標準偏差、相関係数は以下の数字を用いた<a href="#a1">[3]</a>。

| No | 資産クラス | 期待収益率 | 標準偏差 |
|:-:|:-:|:-:|:-:|
| 資産0 | 国内債券 | 1.00% | 5.40% |
| 資産1 | 日本株式 | 4.80% | 22.15% |
| 資産2 | 先進国債券 | 3.50% | 13.25% |
| 資産3 | 先進国株式 | 5.00% | 19.29% |
| 資産4 | 新興国株式 | 9.25% | 26.25% |

| 相関係数 | 資産0 | 資産1 | 資産2 | 資産3 | 資産4 |
|:-:|:-:|:-:|:-:|:-:|:-:|
| 資産0 | 1  | 0.16 | -0.06 | -0.05 | -0.29 |
| 資産1 | 0.16 | 1 | -0.25 | 0.27 | 0.73 |
| 資産2 | -0.06 | -0.25 | 1 | 0.56 | 0.11 |
| 資産3 | -0.05 | 0.27 | 0.56 | 1 | 0.91 |
| 資産4 | -0.29 | 0.73 | 0.11 | 0.91 | 1 |
### 2.3.2 自動化
1資産の場合の数が$n$通りとすると5資産の場合の数は$n^5$となり、手動で投資比率を設定するのはきつい。そこで、for文で投資比率の組み合わせの設定を自動化する。これにとても時間がかかってしまっているのでもっといい方法があれば是非教えていただきたい。

```python:test04.py
"""
5資産の投資比率の組み合わせの設定
"""
n=5 # 資産の数
nmax=10 # 投資比率の精度
x=[0]*n # 配列の設定
for x0 in range(0,nmax):
    x[0]=x0/nmax
    for x1 in range(0,nmax):
        x[1]=x1/nmax
        for x2 in range(0,nmax):
            x[2]=x2/nmax
            for x3 in range(0,nmax):
                x[3]=x3/nmax
                if x[1]+x[2]+x[3]+x[4]>1: # 投資比率が100%を超えているとき
                    break
                else:
                    x[4]=1-(x[1]+x[2]+x[3]+x[4])
                    print(x)
```
nmaxを大きくすれば細かい投資比率を出すことができるが、計算時間が長くなる。nmax=10か20くらいならすぐだが、100にするとまあまあ時間がかかる。資産の期待収益率、標準偏差、相関係数は将来大きく変動する可能性が高いことを考えると、細かい投資比率を求めたところで意味がないので、nmax=10で十分な気はする。上記のプログラムをもう少し改善したものが以下のものになる。

```python:test05.py
# -*- coding: utf-8 -*-
"""
5資産の投資比率の組み合わせの設定
"""
n=5 # 資産の数
nmax=10 # 投資比率の精度
x=[0]*n # 配列の設定
for x0 in range(0,nmax):
    x[0]=x0/nmax
    for x1 in range(0,nmax):
        x[1]=x1/nmax
        count=x[0]+x[1]
        if count>1: # 投資比率が100%を超えているとき
            break # この内側のfor文を抜ける
        for x2 in range(0,nmax):
            x[2]=x2/nmax
            count=x[0]+x[1]+x[2]
            if count>1: # 投資比率が100%を超えているとき
                break # この内側のfor文を抜ける            
            for x3 in range(0,nmax):
                x[3]=x3/nmax
                count=x[0]+x[1]+x[2]+x[3]
                if count>1: # 投資比率が100%を超えているとき
                    break # この内側のfor文を抜ける                 
                else:
                    x[4]=1-count
                    print(x)
```
## 2.4 全体のプログラム
今までのことを統合すると、以下のプログラムになる。

```python:asset.py
# -*- coding: utf-8 -*-
"""
5資産の最適なアセットアロケーションを計算
"""
import numpy as np
#-------------------------------------------------
# input data
#-------------------------------------------------
r = [1.00,4.80,3.50,5.00,9.25] # 期待収益率
rho = [[1,0.16,-0.06,-0.05,-0.29],
[0.16,1,-0.25,0.27,0.73],
[-0.06,-0.25,1,0.56,0.11],
[-0.05,0.27,0.56,1,0.91],
[-0.29,0.73,0.11,0.91,1]] # 相関係数
sigma=[5.4,22.15,13.25,19.59,26.25] # 標準偏差

#-------------------------------------------------
# 本文
#-------------------------------------------------
n=5 # 資産の数
rmin=4 # 最低限欲しいと思う期待収益率
nmax=100 # 投資比率の精度(大きいほど精度が高くなるが計算時間は長くなる)
x=[0]*n # 配列の器の設定
xmin=[0]*n # ｘは逐次更新されてしまうので別で保存するための配列
sigmap_min=1000000 # 適当に大きい値を設定
err=1e-6 # 許容する誤差
for x0 in range(0,nmax):
    x[0]=x0/nmax
    for x1 in range(0,nmax):
        x[1]=x1/nmax
        count=x[0]+x[1]
        if count>1+err: # 投資比率が100%を超えているとき
            break # この内側のfor文を抜ける
        for x2 in range(0,nmax):
            x[2]=x2/nmax
            count=x[0]+x[1]+x[2]
            if count>1+err: # 投資比率が100%を超えているとき
                break # この内側のfor文を抜ける            
            for x3 in range(0,nmax):
                x[3]=x3/nmax
                count=x[0]+x[1]+x[2]+x[3]
                if count>1+err: # 投資比率が100%を超えているとき
                    break # この内側のfor文を抜ける                 
                x[4]=round(1-count,6) # 誤差があるので1E-6で四捨五入
                #------------------------
                # 期待収益率
                #------------------------   
                rp=0
                for i in range(0,n):
                    rp+=r[i]*x[i]
                if rp<rmin: # 最低限欲しいと思う期待収益率未満の場合
                    break
                if rp>rmin+0.1: # 計算時間短縮のため
                    break
                #------------------------
                # 標準偏差
                #------------------------
                sigmap=0
                for i in range(0,n):
                    for j in range(0,n):
                        sigmap+=rho[i][j]*sigma[i]*sigma[j]*x[i]*x[j]
                if sigmap<sigmap_min:
                    sigmap_min=sigmap # 標準偏差（ここではまだ分散）をより小さい値に更新
                    xmin[:]=x[:] # 標準偏差が最小のときのｘを別の配列に保存
                    rpt=rp # 期待収益率を別で保存
sigmap_min=np.sqrt(sigmap_min) # 分散から標準偏差に変換
print('ポートフォリオの期待収益率 {:.2f}'.format(rpt))
print('ポートフォリオの標準偏差 {:.2f}'.format(sigmap_min))
print('最適な投資比率',xmin)
```
出力結果：ポートフォリオの期待収益率 4.00
ポートフォリオの標準偏差 8.26
最適な投資比率 [0.42, 0.0, 0.31, 0.0, 0.27]

上記のプログラムはrminの値を変えることで様々な期待収益率に対する最適な投資比率をもとめることができる。この数字をツールにinputすることで、以下のように効率的フロンティア曲線に乗っていることを確認できる。<a href="#a1">[3]</a>
><div style='width:390px;background:#fff'><img src='http://chart.apis.google.com/chart?cht=p&chs=388x160&chp=4.71239&chl=%E6%97%A5%E6%9C%AC%E5%82%B5%E5%88%B8%2042%EF%BC%85%7C%E6%97%A5%E6%9C%AC%E6%A0%AA%E5%BC%8F%200%EF%BC%85%7C%E5%85%88%E9%80%B2%E5%9B%BD%E5%82%B5%E5%88%B8%2031%EF%BC%85%7C%E5%85%88%E9%80%B2%E5%9B%BD%E6%A0%AA%E5%BC%8F%200%EF%BC%85%7C%E6%96%B0%E8%88%88%E5%9B%BD%E6%A0%AA%E5%BC%8F%2027%EF%BC%85&chco=0065FF&chd=t:42,0,31,0,27&chf=bg,s,ffffff' style='padding:5px 0px;border:solid 1px #ccc;'/><a href='http://guide.fund-no-umi.com/tools/aa.html' style='font-size:10px;line-height:100%;padding:0;margin:0;color:#999;text-decoration:none;float:right'>by 長期投資予想／アセットアロケーション分析</a></div><br style='clear:both;'>
<div style='width:390px;border:solid 1px #ccc;background:#fff'><img src='http://chart.apis.google.com/chart?cht=lc&chco=CCD5E6,A3B5D4,668DC2,4774AB,33567F,4774AB,668DC2,A3B5D4,CCD5E6,ffcc00,ff5555,339933&chxt=x,y,r&chxr=0,0,30,5|1,0,600,120&chxl=2:|%E4%B8%8A%E4%BD%8D10%25%20515.0%E4%B8%87%E5%86%86|%E4%B8%8A%E4%BD%8D20%25%20%20425.4%E4%B8%87%E5%86%86|%E4%B8%8A%E4%BD%8D30%25%20%20370.9%E4%B8%87%E5%86%86|%E4%B8%8A%E4%BD%8D40%25%20%20329.7%E4%B8%87%E5%86%86|%E4%B8%8A%E4%BD%8D50%25%20%20295.4%E4%B8%87%E5%86%86|%E4%B8%8A%E4%BD%8D60%25%20%20264.6%E4%B8%87%E5%86%86|%E4%B8%8A%E4%BD%8D70%25%20%20235.2%E4%B8%87%E5%86%86|%E4%B8%8A%E4%BD%8D80%25%20%20205.1%E4%B8%87%E5%86%86|%E4%B8%8A%E4%BD%8D90%25%20%20169.4%E4%B8%87%E5%86%86&chxp=2,86,71,62,55,49,44,39,34,28&chxs=2,111111,12,-1,t,555555&chg=0,10,1,1&chds=0,600&chls=3|3|3|3|4|3|3|3|3|3,6,4|3,6,4|3,6,4&chem=y;s=bubble_text_small;d=bbbr,%E6%9C%9F%E5%BE%85%E3%83%AA%E3%82%BF%E3%83%BC%E3%83%B3,ffffcc;ds=9;dp=10|y;s=bubble_text_small;d=bbbr,%E6%9C%80%E9%A0%BB%E5%80%A4,ffdddd;ds=10;dp=8|y;s=bubble_text_small;d=bbtr,%E7%B7%8F%E6%8A%95%E8%B3%87%E9%A1%8D,ddffdd;ds=11;dp=10|&chd=e:KqNtQCSdVGX-bKerikm5rsxA27,KqM-O1QyS5VMXuagdkg9kso0tX,KqMdOBPqRdTZViX4acdRgYjznk,KqMDNXOxQTR-T0V0YBabdEf-jK,KqLrMxN-PTQvSVUEV9YCaTcyfg,KqLTMNNPOXPmQ9ScUEV2XyZ6cO,KqK7LpMeNaOdPmQ3SOTuVXXJZF,KqKgLALpMZNPOKPLQTRhS2UTV4,KqJ8KLKlLGLsMXNHN8O2P1Q6SE,KqLxM-OUPyRaTNVMXYZyccfYin,KqLfMYNVOXPeQrR-TXU3WeYNaF,KqKqKqKqKqKqKqKqKqKqKqKqKq&chs=360x200&chf=bg,s,ffffff'  style='padding:5px 0px 5px 5px;'/><img src='http://chart.apis.google.com/chart?cht=bhs&chbh=r,0,0&chco=a0d0ff&chxt=x,y&chxs=0,ffffff,0,0,_|1,ffffff,0,0,_&chd=t:0,11.83,12.40,13.01,13.64,14.30,14.99,15.72,16.48,17.27,18.10,18.96,19.87,20.81,21.80,22.82,23.90,25.02,26.18,27.39,28.66,29.97,31.34,32.75,34.23,35.75,37.33,38.97,40.66,42.41,44.22,46.07,47.99,49.96,51.97,54.04,56.16,58.32,60.51,62.75,65.01,67.30,69.61,71.92,74.24,76.55,78.84,81.10,83.31,85.47,87.55,89.54,91.42,93.17,94.77,96.21,97.45,98.47,99.25,99.76,99.99,99.90,99.48,98.70,97.54,95.98,94.02,91.64,88.83,85.60,81.97,77.93,73.53,68.79,63.76,58.49,53.05,47.51,41.96,36.47,31.14,26.07,21.33,17.00,13.16,9.84,7.06,4.84,3.13,1.90,1.06,0.53,0.24,0.09,0.03,0.01,0.00,0.00,0.00,0.00,0.00,0.00&chds=0,100&chma=0,0,0,8&chs=20x200&chf=bg,s,ffffff' style='padding:5px 5px 5px 0px;'/><p id='IDlinecap' style='font-size:12px;padding:5px 18px;margin:0;line-height:1.5'>期待リターン：4.00％　リスク：8.26％<br/>元本：100万円　総投資額：0万円　期間：30年<br/>（期待値：324.6　標準偏差：147.9　中央値：295.4　最頻値：244.6）</p><a href='http://guide.fund-no-umi.com/tools/aa.html' style='font-size:10px;line-height:100%;padding:0;margin:0;color:#999;text-decoration:none;float:right'>by 長期投資予想／アセットアロケーション分析</a></div><br style='clear:both;'>
<div style='width:390px;background:#fff'><img src='http://chart.apis.google.com/chart?chd=e:Ou64492Z0ezMxRvXuFszrhqP0eyjwovXtcsKq4pmoUnruutcrhqPo9nrmZlHkepmnrmZlHkejMijh6kejMh6hRgogAgAgAeueueFeFeFcKcKbhcKcKZmZmaPbhYUZma4Y9aPa44Uz1x6wAuFsKqPoUnClHjMhRgAeuczcKa4Y9Y9YUYUXCXCXCVwWZWZ2ZtcnrkeijfXdccKZmaPY9XCVwVwUeUeT1cza4YUXCVHT1SjgoVHTMz1x6uFsKqPoUijhReFczZmZmVHXrXCXrxRrhpmlwgAa4Y9XCWZT1UeSjT1TMY9XrUeT1TMR6R6QoXCTMR6QonCcKXrUeVHSjT1T1euVHT1R6RRRRQAQoQATMR6PXPXOFRRQAOuVwR6RRPXQoRRYUTMRRPXPXNcOFNcR6QAOuOFMzMKUeQAPXMzNcOuMKOFOFOFMzMzK4LhK4LhKPOFNcK4K4MKJmLhLhMzLhI9JmIUI9NcMKI9JmHCI9I9K4IUIUHCIUIUHCHCHrGZHrIUGZGZGZHCGZHr,Y94U1wzMwouFrho9mZj1hReu0ex6wAtcq4oUlwjMgoeFxRuusKpmnCkeijgAdcuFrho9mZj1hReucKqPoUlwjMgoeFbhnCkeh6fXcza4j1hReucKZmgoeFbhY9czaPXrZmXCWZ1wwox6vXszuusKpmrho9mZnrlHijkeh6fXhReucKZmdca4Y9aPXrXCyjszpmo9mZlHijgAbhh6fXeucKZma4YUXrijgAfXczcKZmYUjMaPY9zMwowAtcq4sKo9mZlwjMijgAbhWZVwUewotcq4pmmZjMeFfXczcKXCY9WZVHgoeFczaPXrZmXCWZeFaPXrXCq4kegodcVwaPT1TMlHeFbha4YUVwXCVHT1bhY9YUVwUeY9WZVwfXcKTMVHSjR6gAczXrY9XCVwTMSjaPXrVHWZT1TMeFaPXrXCUeR6T1RRQAXrVHSjUeR6RRSjR6Y9WZVwTMQoR6PXOuWZT1TMQoPXQAXrVHT1RRQoOFNcSjRROuOFSjQAMzLhNcMzOuMKLhKPK4JmI9Hr&cht=s&chxt=x,y,x,y&chxr=0,2.27,29.38|1,-0.24,10.49&chxl=2%3A%7C%EF%BE%98%EF%BD%BD%EF%BD%B8(%25)%7C3%3A%7C%EF%BE%98%EF%BE%80%EF%BD%B0%EF%BE%9D%7C(%25)%20%7C&chxp=2,50|3,50,44&chm=o,7F92FF,1,1,6|c,ff0000,0,0,10&chs=388x210&chf=bg,s,ffffff' style='padding:5px 0px;border:solid 1px #ccc;'/><a href='http://guide.fund-no-umi.com/tools/aa.html' style='font-size:10px;line-height:100%;padding:0;margin:0;color:#999;text-decoration:none;float:right'>by 長期投資予想／アセットアロケーション分析</a></div><br style='clear:both;'>

# Future Work
- 任意の資産の数での計算の実現。5以下の資産に限定されているが、for文をたくさん書けばごり押しで拡張できる。
- 計算時間の短縮。CやFortranで書いた方が早いのだろうなと思う。
- 対数正規分布の考慮。算術平均の期待収益率が高くとも、標準偏差が過大であれば、幾何平均の期待収益率は低下してしまう。単純に私の知識不足で実現できていない。例えば、20年後の期待収益率を求めるときに必要になる。株価が対数正規分布に従うことについては<a href="#a1">[4]</a>などを参照。

<h2 id="a1">参考文献</h2>
- [1] ポートフォリオ最適化（平均・分散モデル） - nag http://www.nag-j.co.jp/finance/mean_variance_portfolio_optimisation/fortran_example.htm
- [2] アインシュタインの縮約記法 - Wikipedia https://ja.wikipedia.org/wiki/%E3%82%A2%E3%82%A4%E3%83%B3%E3%82%B7%E3%83%A5%E3%82%BF%E3%82%A4%E3%83%B3%E3%81%AE%E7%B8%AE%E7%B4%84%E8%A8%98%E6%B3%95
- [3] 長期投資予想／アセットアロケーション分析 ～ 投資信託のガイド｜ファンドの海 http://guide.fund-no-umi.com/tools/aa.html
- [4] 後藤 允「何故，株価は対数正規分布にしたがうと仮定されるか」 https://www.econ.hokudai.ac.jp/~goto/_private/stock.pdf