前回はモンテカルロ法で簡易的に効率的フロンティアを求めてみましたが、この方法は効率的でないので最適化問題として計算する方法を紹介します。平均分散最適化の簡単な説明については、以下の記事に記載しているので、平均分散最適化って何っていう状態の方は下の記事を読んでいただけると良いかと思います。
Contents
マーコビッツの平均分散最適化問題
計算方法
Markowitzの平均分散モデルの中でも、ポートフォリオのリスク(分散)を最小化しつつ、期待リターンが目標値を達成するための最適化問題を解いてみます。具体的には以下の方法で計算します。
目的関数
[text{Minimize} quad frac{1}{2} w^T Sigma w]
[
begin{align}
&w: 資産のウェイトのベクトル \
&Sigma: 資産の共分散行列 \
end{align}
]
制約条件
制約1: 期待リターンが目標値 (mu_p) に等しいこと
[w^T mu = mu_p]
[
begin{align}
&mu: 資産の期待リターンのベクトル \
end{align}
]
制約2: 資産のウェイトの総和が1であること
[sum w_i = 1]
制約3: ウェイトが0以上であること
[w_i geq 0 quad forall i]
実際にpythonで計算してみる
コード内の目標とするポートフォリオリターンを自分の目標リターンに書き換えて実行してください。
import yfinance as yf
import pandas as pd
import numpy as np
import cvxpy as cp
# ETFのシンボル
tickers = ['VTI', 'VEA', 'VWO', 'BND', 'HYG', 'EMB', 'IYR', 'GLDM']
# データを取得
data = yf.download(tickers, start="2010-01-01", end="2023-01-01")['Adj Close']
# 年次リターンを計算
annual_data = data.resample('Y').last() # 年次のデータに変換
returns = annual_data.pct_change().dropna()
# 平均リターンと共分散行列を計算
mu = returns.mean().values
Sigma = returns.cov().values
# 目標とするポートフォリオリターン(データに基づいて適切な値を設定)
mu_p = mu.mean() # 平均リターンに近い値を目標リターンとする
print("Mean returns:", mu)
print("Covariance matrix:n", Sigma)
print("Target return (mu_p):", mu_p)
# 変数定義
w = cp.Variable(len(tickers))
# 目的関数:リスク(分散)の最小化
objective = cp.Minimize(cp.quad_form(w, Sigma))
# 制約条件
constraints = [
cp.sum(w) == 1,
w @ mu == mu_p,
w >= 0 # ショートセール禁止
]
# 問題設定
problem = cp.Problem(objective, constraints)
# 最適化実行
result = problem.solve()
# 結果の確認
if w.value is None:
print("Optimization failed. Check the constraints and objective function.")
else:
optimal_weights = w.value
optimal_weights_percentage = optimal_weights * 100 # パーセンテージに変換
print("Optimal weights (in %):")
for ticker, weight in zip(tickers, optimal_weights_percentage):
print(f"{ticker}: {weight:.2f}%")
print("Expected portfolio return:", np.dot(optimal_weights, mu))
print("Portfolio variance:", np.dot(optimal_weights.T, np.dot(Sigma, optimal_weights)))
効率的フロンティアを最適化問題を使って計算してみる
上と同様に最適化問題として解けば、効率的フロンティアも(モンテカルロ法を使わずとも)効率よく求めることができます。
計算方法
効率的フロンティアは各リスクレベルにおいて最大のリターンをたたき出すポートフォリオのことなので、これを定式化します。
目的関数
[text{Maximize} quad w^{T}mu]
制約条件
[frac{1}{2} w^T Sigma w leq text{target_variance}]
[sum w_i = 1]
[w_i geq 0]
実際にpythonで計算する
月次のデータで計算したい場合は年次のデータに変換
になってる行のY
をM
に変えてください。
import yfinance as yf
import pandas as pd
import numpy as np
import cvxpy as cp
import matplotlib.pyplot as plt
# ETFのシンボル
tickers = ['VTI', 'VEA', 'VWO', 'BND', 'HYG', 'EMB', 'IYR', 'GLDM']
# データを取得
data = yf.download(tickers, start="2010-01-01", end="2023-01-01")['Adj Close']
# 年次リターンを計算
annual_data = data.resample('Y').last() # 年次のデータに変換
returns = annual_data.pct_change().dropna()
# 平均リターンと共分散行列を計算
mu = returns.mean().values
Sigma = returns.cov().values
# 効率的フロンティアを計算するためのリスクレベルを設定
num_portfolios = 100
target_variances = np.linspace(0, np.max(np.diag(Sigma)), num_portfolios)
# 効率的フロンティアを求める
risks = []
returns_list = []
for target_variance in target_variances:
w = cp.Variable(len(tickers))
# 目的関数:リターンの最大化
objective = cp.Maximize(w @ mu)
# 制約条件
constraints = [
cp.quad_form(w, Sigma) <= target_variance,
cp.sum(w) == 1,
w >= 0 # ショートセール禁止
]
# 問題設定
problem = cp.Problem(objective, constraints)
# 最適化実行
problem.solve()
if w.value is not None:
risk = np.sqrt(w.value.T @ Sigma @ w.value)
ret = w.value @ mu
risks.append(risk)
returns_list.append(ret)
# グラフにプロット
plt.figure(figsize=(10, 6))
plt.plot(risks, returns_list, label='Efficient Frontier')
plt.xlabel('Risk (Standard Deviation)')
plt.ylabel('Return')
plt.title('Efficient Frontier')
plt.legend()
plt.grid(True)
plt.xlim(0, max(risks) * 1.1) # x軸の範囲を設定
plt.ylim(0, max(returns_list) * 1.1) # y軸の範囲を設定
plt.savefig('graph.png')
plt.show()
Copy Code

最後に
今回は、平均分散最適化と効率的フロンティアの計算方法について、Pythonを使って解説しました。この手法はモダンポートフォリオ理論の基盤となるもので、理解することで投資判断の精度が向上するでしょう。私も学習中の身ですので、自分の学びが皆さんの学びにも役立てば幸いです。一緒に頑張って勉強していきましょう。
ポートフォリオ構築に関連する他のモデルを知りたい方は下記のリンク集をぜひご活用ください。