見出し画像

初心者でもわかるマーコウィッツのポートフォリオ最適化:投資を劇的に変える理論と実践法

「リスクを抑えつつ、最大限のリターンを狙いたい!」そんな投資家の永遠の悩みを、たった一つの理論が解決に導きました。その理論こそが、1952年にハリー・マーコウィッツが発表した「ポートフォリオ最適化理論」です。

マーコウィッツは「人はリスクを嫌う」というシンプルな人間心理に着目し、リターンだけでなくリスクも数学的に分析し、最適なバランスを見つける画期的な方法を考案しました。この理論はノーベル経済学賞を受賞し、現在も多くの投資家に愛用されています。


マーコウィッツ理論の「キモ」を簡単に解説

マーコウィッツ理論では、リターンは単なる資産ごとの平均ではなく、資産間の関係性を重視します。資産のリスク(価格変動)も、それぞれがどれだけ関連して動くのか(相関性)を考慮して計算されます。その結果、最適な資産配分の組み合わせを図示すると、「効率的フロンティア」と呼ばれる曲線が現れます。この曲線上にある組み合わせが、理想的なリスクとリターンのバランスを示しているのです。

お金の広場引用画像

Pythonで実装してみよう!ポートフォリオ最適化

実践に移すためには、まず過去の株価データを取得し、リターンとリスクを計算します。Pythonを使って実現させてみましょう。具体的には、個人的に私が保有している銘柄の一部を検証してみます。

6銘柄、直近3年データを使用しリスクフリーレートをゼロと仮定した上で、シャープレシオ(リスクあたりのリターン)を最大化するポートフォリオを「最適」として選んでいます。また、リスクを最小限に抑えつつ最大限のリターンを得られるポートフォリオも合わせて見つけていきます。

  • U-NEXT(9418)

  • パルグループHLDGS(2726)

  • INPEX(1605)

  • 三菱重工業(7011)

  • 大成建設(1801)

  • 三井住友ファイナンシャルG(8316)

1. 環境設定

下の記事の環境設定部分を同じですので実践する場合は参照してください。

2. 複数の銘柄データ取得

今回使用するライブラリをインポート

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline

銘柄コードと取得期間を指定してする

# 銘柄コードと取得期間の設定
codes = ["94180", "27260", "16050", "70110", "18010", "83160"]
start_date_str = "2022-03-24"
end_date_str = "2025-03-25"

pf_data = pd.DataFrame()

2-1 銘柄の取得

取得期間中に株式分割も行われているので、取得データを調整後終値(AdjustmentClose)に指定してそれぞれの銘柄を取得する

# 各銘柄のデータを取得して、調整後終値(AdjustmentClose)をDataFrameに格納
for code in codes:
    print(f"--- 実行銘柄コード --- {code}")
    params = {
        "code": code,
        "from": start_date_str,
        "to": end_date_str,
    }
    r = requests.get("https://api.jquants.com/v1/prices/daily_quotes", params=params, headers=headers)
    daily_quotes = r.json()["daily_quotes"]
    df = pd.DataFrame(daily_quotes)
    # Date列をdatetime型に変換してインデックスに設定
    df["Date"] = pd.to_datetime(df["Date"])
    df.set_index("Date", inplace=True)
    # 調整後終値(AdjustmentClose)を取得
    pf_data[code] = df["AdjustmentClose"]

取得データを確認したい場合は以下のコードを実行してください。

pf_data.tail()

2-2 ここまで得た情報の株価推移を表示して確認してみましょう。

DataFrame「pf_data」に含まれる各銘柄の値を、最初の時点の値で割って100倍することで、各銘柄の初期値を100として正規化し、その推移をプロットしています。

(pf_data / pf_data.iloc[0] * 100).plot(figsize=(10,5))
各銘柄の株価推移

三菱重工業(7011)がここ3年で7倍になっていることがわかりますね、3年前に買っていれば…

3. ポートフォリオ最適化実装

3-1 最適化手順

  1. ランダムポートフォリオの生成と評価
    ・重みの生成と正規化:
    ループ内で、各資産(銘柄)の重みをランダムに生成し、全体の重みの合計が1になるように正規化しています。
    ・年間リターンの計算:各ポートフォリオについて、対数利益率の平均に250(日数)を乗じることで、年間の期待リターンを求めています。
    ・年間ボラティリティの計算:対数利益率の共分散行列(250倍して年率換算)とランダムに生成した重みを用い、ポートフォリオ全体のリスク(ボラティリティ)を計算しています。

  2. ポートフォリオ評価指標の算出
    ・DataFrameへの格納:
    ループで求めたリターン、ボラティリティ、重みをまとめ、1万個のポートフォリオデータとしてDataFrameに格納しています。
    ・シャープレシオの計算:リスクフリーレートを0と仮定し、各ポートフォリオのシャープレシオを「期待リターン ÷ ボラティリティ」で計算し、DataFrameに追加しています。

  3. 最適なポートフォリオの抽出
    ・シャープレシオ最大のポートフォリオ(リスクを取れる投資家向け):
    idxmax() を使い、生成したポートフォリオの中で最も高いシャープレシオを持つポートフォリオを抽出します。
    ・ボラティリティ最小のポートフォリオ(リスク抑制型の投資家向け):idxmin() を使い、ボラティリティが最も低いポートフォリオを抽出しています。

  4. 結果の出力
    ・各ポートフォリオの評価結果の表示:
    抽出した「シャープレシオ最大のポートフォリオ」と「ボラティリティ最小のポートフォリオ」について、それぞれの期待リターン、ボラティリティ、シャープレシオ、そして各銘柄の保有比率を出力しています。

  5. 効率的フロンティアのプロット
    ・散布図の作成:
    全ての生成したポートフォリオのボラティリティと期待リターンを散布図としてプロットし、シャープレシオに応じたカラーマッピング(cmap="viridis")を適用しています。
    ・最適ポートフォリオの強調表示:シャープレシオ最大のポートフォリオを赤い星マーク(リスクをとれる投資家向け)で、ボラティリティ最小のポートフォリオを青い四角マーク(リスク抑制型)でプロット上に重ね、視覚的に区別できるようにしています。

# ランダムに重みを設定して、その時のリスクとリターンを計算する
pfolio_returns = []
pfolio_volatility = []
pfolio_weights = []

# 銘柄データの数をカウント
num_assets = len(codes)

for _ in range(10000):
    # 重みの加重平均
    weights = np.random.random(num_assets)  # 銘柄の数だけランダムな値を生成
    weights /= np.sum(weights)              # 合計が1になるように正規化
    pfolio_weights.append(weights)
    
    # 年間のリターン計算(対数利益率の平均×250日)
    port_return = np.sum(weights * log_returns.mean()) * 250
    pfolio_returns.append(port_return)
    
    # 年間のボラティリティ計算
    port_vol = np.sqrt(np.dot(weights.T, np.dot(log_returns.cov() * 250, weights)))
    pfolio_volatility.append(port_vol)

# DataFrameにまとめる(各ポートフォリオの重みも保存)
portfolios = pd.DataFrame({
    "Returns": pfolio_returns,
    "Volatility": pfolio_volatility,
    "Weights": pfolio_weights
})

# シャープレシオ(リスクフリーレート0と仮定)の計算
portfolios["SharpeRatio"] = portfolios["Returns"] / portfolios["Volatility"]

# 最適なポートフォリオ(シャープレシオが最大)の抽出
optimal_portfolio = portfolios.iloc[portfolios["SharpeRatio"].idxmax()]

# リスクを抑えたい投資家向けに、最小ボラティリティのポートフォリオを抽出
min_vol_portfolio = portfolios.iloc[portfolios["Volatility"].idxmin()]

# 結果の出力(最適ポートフォリオ:シャープレシオ最大)
print("=== 最適なポートフォリオ(シャープレシオ最大) ===")
print("期待リターン:", optimal_portfolio["Returns"])
print("ボラティリティ:", optimal_portfolio["Volatility"])
print("シャープレシオ:", optimal_portfolio["SharpeRatio"])
print("保有比率(Weights):")
for code, weight in zip(codes, optimal_portfolio["Weights"]):
    print(f"  {code}: {weight:.2%}")

# 結果の出力(リスクを抑えたい投資家向け:最小ボラティリティのポートフォリオ)
print("\n=== 最小ボラティリティのポートフォリオ(リスク抑制型) ===")
print("期待リターン:", min_vol_portfolio["Returns"])
print("ボラティリティ:", min_vol_portfolio["Volatility"])
print("シャープレシオ:", min_vol_portfolio["SharpeRatio"])
print("保有比率(Weights):")
for code, weight in zip(codes, min_vol_portfolio["Weights"]):
    print(f"  {code}: {weight:.2%}")

# 散布図のプロット
plt.figure(figsize=(10,10))
plt.scatter(portfolios["Volatility"], portfolios["Returns"], c=portfolios["SharpeRatio"], cmap="viridis")
plt.xlabel("Volatility")
plt.ylabel("Returns")
plt.title("Efficient Frontier with Random Portfolios")
plt.colorbar(label="Sharpe Ratio")

# シャープレシオ最大のポートフォリオを赤い星マークで表示(リスクを取れる人向け)
plt.scatter(optimal_portfolio["Volatility"], optimal_portfolio["Returns"], color="red", s=100, marker="*", label="Max Sharpe Portfolio")

# 最小ボラティリティのポートフォリオを青い四角で表示(リスク抑制型)
plt.scatter(min_vol_portfolio["Volatility"], min_vol_portfolio["Returns"], color="blue", s=100, marker="s", label="Min Volatility Portfolio")

plt.legend()
plt.show()

3-2 出力結果を確認

=== 最適なポートフォリオ(シャープレシオ最大) ===
期待リターン: 0.46323684110754443
ボラティリティ: 0.23403650203757626
シャープレシオ: 1.979335860323055
保有比率(Weights):
94180: 8.66%
27260: 37.04%
16050: 0.48%
70110: 23.90%
18010: 2.72%
83160: 27.20%

=== 最小ボラティリティのポートフォリオ(リスク抑制型) ===
期待リターン: 0.3052681349225337
ボラティリティ: 0.2059639426568018
シャープレシオ: 1.482143578068918
保有比率(Weights):
94180: 14.98%
27260: 20.13%
16050: 14.85%
70110: 0.96%
18010: 21.29%
83160: 27.80%

Google Colabの出力結果
出力結果散布図

3-3 結果の解説

今回のシミュレーションでは、10000通りのランダムなポートフォリオから2つのポートフォリオの割合を出力。1つは「シャープレシオ最大のポートフォリオ」、もう1つは「ボラティリティ最小のポートフォリオ」です。

【シャープレシオ最大のポートフォリオ】

  • 期待リターン:46.32%

  • ボラティリティ:23.40%

  • シャープレシオ:1.98

  • 保有比率:銘柄U-NEXT(9418)が8.66%、パルグループHLDGS(2726)が37.04%、INPEX(1605)が0.48%、三菱重工業(7011)が23.90%、大成建設(1801)が2.72%、三井住友ファイナンシャルG(8316)が27.20%

このポートフォリオは、リスク(ボラティリティ)に対して非常に効率的にリターンを生み出しているため、リスクをある程度許容できる投資家にとって魅力的な選択肢となります。

【ボラティリティ最小のポートフォリオ】

  • 期待リターン:30.53%

  • ボラティリティ:20.60%

  • シャープレシオ:1.48

  • 保有比率:銘柄U-NEXT(9418)が14.98%、パルグループHLDGS(2726)が20.13%、INPEX(1605)が14.85%、三菱重工業(7011)が0.96%、大成建設(1801)が21.29%、三井住友ファイナンシャルG(8316)が27.80%

こちらは、リスクをできるだけ抑えたい投資家向けのポートフォリオで、ボラティリティが低い割には十分なリターンを期待できます。

両者を比較すると、シャープレシオ最大のポートフォリオはより高いリターン(約46%)を狙いつつ、リスクも若干上乗せされています。一方、リスク抑制型のポートフォリオは期待リターンが低め(約30%)ですが、リスクが抑えられており、より安定した運用を目指す投資家に適しています。

シャープレシオ最大のポートフォリオではINPEX(1605)が0.48%ということで今回のポートフォリオでは省いても良さそうな銘柄になりますね、また、ボラティリティ最小のポートフォリオでは3%低めに抑えられていますがリターンは16%も下がるので3%のリスクをとってシャープレシオ最大のポートフォリオにするべきと考察できます。内訳も三菱重工業(7011)が0.96%とリスクが高い銘柄が極端に低くなっているのこともわかります。

応用として、期待リターンをこちら側で設定した時の最適なポートフォリオバランスも提示してくれるものも作成できるはずです。興味がある方はコードを改良して試してみてください。

理論を理解し、実践へ

マーコウィッツ理論は単なる理論にとどまらず、現代の投資戦略の中心的な考え方となっています。デジタル技術の発展で、今や個人投資家でもプロのような高度なポートフォリオ管理が可能な時代になりました。

理論をきちんと理解し、自分に合った方法で活用して、着実な資産形成を目指しましょう。今こそマーコウィッツ理論を実践する絶好のチャンスです!

まとめ

マーコウィッツのポートフォリオ最適化理論は、投資の「リスクとリターンのバランス」を数値化し、投資判断を客観的かつ明確にしました。この理論を理解し、最新のツールを活用して実践することで、より賢く、効果的な資産運用を行うことができます。投資初心者から上級者まで、この基本を押さえた投資戦略で、着実に資産を増やしていきましょう!
ここまで読んでくださった方ありがとうございました!また次のnoteでお会いしましょう!

いいなと思ったら応援しよう!

この記事は noteマネー にピックアップされました

noteマネーのバナー