Pythonで効率的フロンティアを元にポートフォリオ最適化【Yahoo!Financeデータ利用】

5月 30, 2021

今回は,Pythonを用いて最適なポートフォリオを探す方法を解説します

ポートフォリオ最適化とは

ポートフォリオ最適化とは,『リスクをおさえつつリターンを最大化するようなポートフォリオを決定する』ことです.ポートフォリオとは,各金融資産の割合のことです.

ポートフォリオについての説明は以下の記事をご覧ください.

ポートフォリオ最適化のために効率的フロンティアを作成

効率的フロンティアとは

効率的フロンティアとは簡単に言うと,『ポートフォリオごとのリスクとリターンの関係』を表したマップのことです.

より詳細には,効率的フロンティアを作成した後で説明します.

準備

Pythonライブラリ『fix_yahoo_finance』を用いて株価データを取得し,それを元に効率的フロンティアを作成します.

pip install fix_yahoo_finance

でインストールしておきましょう.

もちろん,pandas datareaderで株価を取得しても問題ないです.環境によってはpandas datareaderのほうが楽かもしれません.

fix_yahoo_financeを用いたデータ取得についての記事を以下に貼っておくので,よくわからないひとは参照してください.

効率的フロンティア作成コード

効率的フロンティアを作成するコードは以下の通りです.

コード1

# import needed modules
import datetime
import fix_yahoo_finance as yf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
start = datetime.date(2019,1,1)
end = datetime.date.today()

# get prices data
selected = ['KO', 'PG', 'MSFT']
data = yf.download(selected, start=start, end=end)["Adj Close"]
data = data.reindex(columns=selected)

# calculate daily and annual returns of the stocks
returns_daily = data.pct_change()
returns_annual = returns_daily.mean() * 250

# get daily and covariance of returns of the stock
cov_daily = returns_daily.cov()
cov_annual = cov_daily * 250

# empty lists to store returns, volatility and weights of imiginary portfolios
port_returns = []
port_volatility = []
sharpe_ratio = []
stock_weights = []

# set the number of combinations for imaginary portfolios
num_assets = len(selected)
num_portfolios = 50000

#set random seed for reproduction's sake
np.random.seed(101)

# populate the empty lists with each portfolios returns,risk and weights
for single_portfolio in range(num_portfolios):
   weights = np.random.random(num_assets)
   weights /= np.sum(weights)
   returns = np.dot(weights, returns_annual)
   volatility = np.sqrt(np.dot(weights.T, np.dot(cov_annual, weights)))
   sharpe = returns / volatility
   sharpe_ratio.append(sharpe)
   port_returns.append(returns)
   port_volatility.append(volatility)
   stock_weights.append(weights)

# a dictionary for Returns and Risk values of each portfolio
portfolio = {'Returns': port_returns,
            'Volatility': port_volatility,
            'Sharpe Ratio': sharpe_ratio}

# extend original dictionary to accomodate each ticker and weight in the portfolio
for counter,symbol in enumerate(selected):
   portfolio[symbol+' Weight'] = [Weight[counter] for Weight in stock_weights]

# make a nice dataframe of the extended dictionary
df = pd.DataFrame(portfolio)

# get better labels for desired arrangement of columns
column_order = ['Returns', 'Volatility', 'Sharpe Ratio'] + [stock+' Weight' for stock in selected]

# reorder dataframe columns
df = df[column_order]

# plot frontier, max sharpe & min Volatility values with a scatterplot
plt.style.use('seaborn-dark')
df.plot.scatter(x='Volatility', y='Returns', c='Sharpe Ratio',
               cmap='RdYlGn', edgecolors='black', figsize=(10, 8), grid=True)
plt.xlabel('Volatility (Std. Deviation)')
plt.ylabel('Expected Returns')
plt.title('Efficient Frontier')
plt.show()

基本的に,皆さんが変更する箇所は以下の2つくらいだと思います.

1.データ取得期間

start = datetime.date(2019,1,1)
end = datetime.date.today()

2.銘柄

# get prices data
selected = ['KO', 'PG', 'MSFT']

ご自身に合わせて変更してください.

実行結果と効率的フロンティアの見方

上のコード1を実行すると,以下のようなグラフ(効率的フロンティア)が表示されます.

横軸はボラティリティで,リスクのようなものです(正確には,偏差です).縦軸は期待リターンです.

各点はあるポートフォリオを表しています.各点の色は緑だとシャープレシオが大きく,赤色だとシャープレシオが小さいです.

シャープレシオとは,リターンをリスクを割ったものです.シャープレシオが大きいほどリターンが大きくリスクが小さいので,シャープレシオが大きいほど良いポートフォリオということになります.

他の見方として,下の図ではリターンが0.3のところに赤い直線を引きましたが,このリターン一定の直線上にあるポートフォリオの中でも,赤い丸の方がリスクが小さいので,リスクが大きい黒い丸よりも良いポートフォリオということになります.

総じて,リスクが小さい側の縁(赤い曲線)の上にあるポートフォリオの方が良いです.

つまり,効率的フロンティアから最適なポートフォリオを選ぶときには

(1)リターンとリスクの比であるシャープレシオが大きいポートフォリオを選択するか,

(2)できるだけリスクが小さいポートフォリオを選択するか

という2つの考え方があります.

効率的フロンティアから最適ポートフォリオを選択

Pythonを使えば,簡単に最適なポートフォリオを探すことができます.

コード2を,先ほどのコード1のプロット以降と置き換えてください.つまり,

# reorder dataframe columns
df = df[column_order]

の下を全て削除して,コード2に置き換えて再度実行してください.

コード2

# find min Volatility & max sharpe values in the dataframe (df)
max_sharpe = df['Sharpe Ratio'].max()
min_volatility = df['Volatility'].min()

# use the min, max values to locate and create the two special portfolios
sharpe_portfolio = df.loc[df['Sharpe Ratio'] == max_sharpe]
min_variance_port = df.loc[df['Volatility'] == min_volatility]

# print the details of the 2 special portfolios
print(sharpe_portfolio.T)
print(min_variance_port.T)

# plot frontier, max sharpe & min Volatility values with a scatterplot
plt.style.use('seaborn-dark')
df.plot.scatter(x='Volatility', y='Returns', c='Sharpe Ratio',
               cmap='RdYlGn', edgecolors='black', figsize=(10, 8), grid=True)
plt.scatter(x=sharpe_portfolio['Volatility'], y=sharpe_portfolio['Returns'], c='red', marker='D', s=200)
plt.scatter(x=min_variance_port['Volatility'], y=min_variance_port['Returns'], c='blue', marker='D', s=200 )
plt.xlabel('Volatility (Std. Deviation)')
plt.ylabel('Expected Returns')
plt.title('Efficient Frontier')
plt.show()

実行すると,以下のような結果が出力されます.

[*********************100%***********************]  3 of 3 downloaded
                 12879
Returns       0.454600
Volatility    0.331094
Sharpe Ratio  1.373023
KO Weight     0.000831
PG Weight     0.988006
MSFT Weight   0.011163

                 38464
Returns       0.175175
Volatility    0.231835
Sharpe Ratio  0.755602
KO Weight     0.388772
PG Weight     0.076232
MSFT Weight   0.534996

シャープレシオが大きいポートフォリオを選択

実行結果の図において,赤い🔶がシャープレシオが最大のポートフォリオです.

このポートフォリオは,コード2において

max_sharpe = df['Sharpe Ratio'].max()

sharpe_portfolio = df.loc[df['Sharpe Ratio'] == max_sharpe]

で探索しています.

ポートフォリオの中身(各銘柄の割合)は

print(sharpe_portfolio.T)

で出力しています.この場合は,KO(コカ・コーラ)が0.08%,PG(P&G)が98.8%,MSFT(マイクロソフト)が1.1%がシャープレシオを最大とするポートフォリオとなっています.

PGが一番リターンとリスクのバランスが良いでしょうね.

できるだけリスクが小さいポートフォリオを選択

実行結果の図において,青い🔷がリスクが最小のポートフォリオです.

このポートフォリオは,コード2において

min_volatility = df['Volatility'].min()

min_variance_port = df.loc[df['Volatility'] == min_volatility]

で探索しています.

ポートフォリオの中身(各銘柄の割合)は

print(min_variance_port.T)

で出力しています.この場合は,KO(コカ・コーラ)が38.8%,PG(P&G)が7.6%,MSFT(マイクロソフト)が53.5%がリスクを最小とするポートフォリオとなっています.

やはり,ボラティリティが小さいKOが多めに選ばれていますね.

・参考記事

Python を用いての 効率的フロンティア と ポートフォリオの最適化 Efficient Frontier & Portfolio Optimization with Python [Part 2/2]

を参考にさせて頂きました.

投資に関する免責事項
プログラムや考え方の情報の提供・作業代行を目的としており,投資勧誘を目的とするものではありません.また,この記事で紹介するポートフォリオ最適化は将来の成績を保証するものではありません.未来は誰にも分かりませんので,どのようなポートフォリオにするのかは自分自身で判断してください.また,コードの正確性には一切責任は負いませんので,不備がある場合はご自身で修正願います.