前回はモンテカルロ法で簡易的に効率的フロンティアを求めてみましたが、この方法は効率的でないので最適化問題として計算する方法を紹介します。平均分散最適化の簡単な説明については、以下の記事に記載しているので、平均分散最適化って何っていう状態の方は下の記事を読んでいただけると良いかと思います。
>-
マーコビッツの平均分散最適化問題
計算方法
Markowitzの平均分散モデルの中でも、ポートフォリオのリスク(分散)を最小化しつつ、期待リターンが目標値を達成するための最適化問題を解いてみます。具体的には以下の方法で計算します。
目的関数
制約条件
制約1: 期待リターンが目標値 に等しいこと
制約2: 資産のウェイトの総和が1であること
制約3: ウェイトが0以上であること
実際にpythonで計算してみる
コード内の目標とするポートフォリオリターンを自分の目標リターンに書き換えて実行してください。
import yfinance as yfimport pandas as pdimport numpy as npimport 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().valuesSigma = 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)))効率的フロンティアを最適化問題を使って計算してみる
上と同様に最適化問題として解けば、効率的フロンティアも(モンテカルロ法を使わずとも)効率よく求めることができます。
計算方法
効率的フロンティアは各リスクレベルにおいて最大のリターンをたたき出すポートフォリオのことなので、これを定式化します。
目的関数
制約条件
実際にpythonで計算する
月次のデータで計算したい場合は年次のデータに変換になってる行のYをMに変えてください。
import yfinance as yfimport pandas as pdimport numpy as npimport cvxpy as cpimport 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().valuesSigma = returns.cov().values
# 効率的フロンティアを計算するためのリスクレベルを設定num_portfolios = 100target_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()
最後に
今回は、平均分散最適化と効率的フロンティアの計算方法について、Pythonを使って解説しました。この手法はモダンポートフォリオ理論の基盤となるもので、理解することで投資判断の精度が向上するでしょう。私も学習中の身ですので、自分の学びが皆さんの学びにも役立てば幸いです。一緒に頑張って勉強していきましょう。
ポートフォリオ構築に関連する他のモデルを知りたい方は下記のリンク集をぜひご活用ください。
投資ポートフォリオ構築に関するモデル記事のリンク集。資産配分・期待リターン計算・リスク管理モデルを体系的にまとめています。









