見出し画像

現代ポートフォリオ理論:Modern Portfolio TheoryをPythonで実践してみる。



現代ポートフォリオ理論:Modern Portfolio Theoryとは?


現代ポートフォリオ理論:Modern Portfolio Theory (略:MPT) は1952年にハリー・マーコウィッツによって発表された金融資産への投資比率(ポートフォリオ)を決定する理論。

Pythonによる実装

環境はGoogle Corabです。

ライブラリの読み込み

import datetime as dt
import pandas as pd
import pandas_datareader.data as web
import yfinance as yf

yf.pdr_override()

株式銘柄の選択とヒストリカルデータの取得

stocks = [7203, 6758, 6861, 9432, 8306, 4568, 4502, 8316, 6501, 7974]

上記の銘柄はTOPIX構成銘柄から適当に選択しており、特に理由はありません。株式銘柄のヒストリカルデータが必要になるため、Yahoo Financeからデータを取得します。データの取得にはpandas_datareaderライブラリを利用します。

prices = []
for stock in stocks:
    try:
        closes = web.DataReader(str(stock) + ".T", start='2023-01-01', end='2023-12-31')["Adj Close"]
        closes_df = pd.DataFrame(closes)
        closes_df.columns = [stock]
        prices.append(closes_df)
    except:
        pass
    prices_df = pd.concat(prices, axis=1)

prices_df.sort_index(inplace=True)

ポートフォリオの期待リターンと共分散を計算

ポートフォリオの最適化に必要な計算を行うために、PyPortfolioOptというライブラリをインストールします。

PyPortfolioOptのインストールを行います。

!pip install PyPortfolioOpt

期待リターンと共分散を計算するために必要なrisk_models, expected_returnsをインポートします。

from pypfopt import risk_models, expected_returns

期待リターンを計算します。

mu = expected_returns.mean_historical_return(prices_df)
mu

期待リターンをプロットしてみます。

import plotly.express as px

fig = px.bar(x=[str(x) for x in mu.index], y=mu.values, title="期待リターン")
fig.show()

次に共分散を計算します。

S = risk_models.sample_cov(prices_df)
S
共分散

共分散行列をヒートマップにして可視化してみます。

import seaborn as sb

fig = plt.figure()
sb.heatmap(S, annot=True)
plt.show(fig)

シャープレシオの最大化

シャープレシオの最大化はmax_sharpe()で行います。
上記で計算した期待リターンと共分散を用いて計算します。

ef = EfficientFrontier(mu, S)
weights = ef.max_sharpe()
weights
最適化された投資比率が返却されている。

投資比率を端数処理するためのメソッドも用意されています。
clean_weights()はデフォルトで小数点以下5桁で四捨五入します。

cleaned_weights = ef.clean_weights()
cleaned_weights
clean_weights()適用後の投資比率

ポートフォリオのパフォーマンスを見てみる

最適化された投資比率でポートフォリオのパフォーマンスを知りたい場合は、portfolio_performance()を使って確認出来ます。

ef.portfolio_performance(verbose=True)

効率的フロンティアの可視化

最後にPyPortfolioOptのドキュメントにある効率的フロンティア可視化のコードでプロットを行ってみます。

ef = EfficientFrontier(mu, S)

fig, ax = plt.subplots()
ef_max_sharpe = ef.deepcopy()
plotting.plot_efficient_frontier(ef, ax=ax, show_assets=False)

# 接点ポートフォリオを見つける
ef_max_sharpe.max_sharpe()
ret_tangent, std_tangent, _ = ef_max_sharpe.portfolio_performance()
ax.scatter(std_tangent, ret_tangent, marker="*", s=100, c="r", label="最大シャープレシオ")

# ランダムなポートフォリオを生成
n_samples = 10000
w = np.random.dirichlet(np.ones(ef.n_assets), n_samples)
rets = w.dot(ef.expected_returns)
stds = np.sqrt(np.diag(w @ ef.cov_matrix @ w.T))
sharpes = rets / stds
ax.scatter(stds, rets, marker=".", c=sharpes, cmap="viridis_r")

# 出力
ax.set_title("ランダムポートフォリオによる効率的フロンティア")
ax.legend()
plt.tight_layout()
plt.show()