Lab Research ポートフォリオ理論の基礎——分散投資でリスクを減らす数学的仕組み
目次

「卵を一つのカゴに盛るな」——これは投資の世界で広く信じられている教訓だ。しかし、なぜ分散投資はリスクを減らすのか。数学的に理解している投資家は少ない。本稿では現代ポートフォリオ理論(MPT)の基礎を、Python 実装とともに解説する。

1. 個別資産のリターンとリスク

1.1 期待リターンの計算

期待リターンは過去のリターンの平均、または将来シナリオの確率加重平均で推定する。

import numpy as np

# 株式 A の過去 5 年の年間リターン(%)
returns_A = np.array([8.5, -2.3, 12.1, 5.7, -4.0])

# 期待リターン(平均リターン)
expected_return_A = np.mean(returns_A)
print(f"株式 A の期待リターン:{expected_return_A:.2f}%")  # 4.00%

1.2 リスクの計算——標準偏差

リスクはリターンのばらつき(標準偏差)で測定する。

# リスク(標準偏差)
risk_A = np.std(returns_A, ddof=1)  # 不偏標準偏差
print(f"株式 A のリスク(標準偏差):{risk_A:.2f}%")  # 6.22%

標準偏差の意味:

  • 標準偏差 6.22% は、約 68% の確率でリターンが「平均±6.22%」の範囲に入ることを意味する
  • 95% の確率なら「平均±2×標準偏差」の範囲(信頼区間)

2. 2 資産ポートフォリオ——分散投資の効果

2.1 ポートフォリオの期待リターン

ポートフォリオの期待リターンは個別資産の期待リターンの加重平均だ。

E[Rp] = w₁ × E[R₁] + w₂ × E[R₂]

ここで:
w₁, w₂ = 各資産の組成比率(w₁ + w₂ = 1)
E[R₁], E[R₂] = 各資産の期待リターン
# 株式 A と株式 B
returns_B = np.array([6.2, 3.1, -1.5, 8.9, 4.3])

expected_return_B = np.mean(returns_B)
print(f"株式 B の期待リターン:{expected_return_B:.2f}%")  # 4.20%

# ポートフォリオ(50%ずつ)
w_A, w_B = 0.5, 0.5
portfolio_expected_return = w_A * expected_return_A + w_B * expected_return_B
print(f"ポートフォリオの期待リターン:{portfolio_expected_return:.2f}%")  # 4.10%

2.2 ポートフォリオのリスク——共分散が鍵

ポートフォリオのリスクは、個別リスクの単純平均ではない

σp² = w₁²σ₁² + w₂²σ₂² + 2w₁w₂Cov(R₁,R₂)

ここで:
σp² = ポートフォリオの分散
σ₁², σ₂² = 個別資産の分散
Cov(R₁,R₂) = 共分散(2 資産のリターンの連動度)

共分散:2 つの資産が一緒に動く傾向

  • 正の共分散:同じ方向に動く
  • 負の共分散:逆の方向に動く
  • 0:無関係
# 共分散の計算
cov_AB = np.cov(returns_A, returns_B, ddof=1)[0, 1]
print(f"株式 A と B の共分散:{cov_AB:.4f}")

# 相関係数(-1 〜 +1 の標準化された指標)
corr_AB = np.corrcoef(returns_A, returns_B)[0, 1]
print(f"株式 A と B の相関係数:{corr_AB:.4f}")

# ポートフォリオの分散
var_A = np.var(returns_A, ddof=1)
var_B = np.var(returns_B, ddof=1)

portfolio_var = w_A**2 * var_A + w_B**2 * var_B + 2 * w_A * w_B * cov_AB
portfolio_risk = np.sqrt(portfolio_var)

print(f"ポートフォリオのリスク(標準偏差):{portfolio_risk:.2f}%")

2.3 分散投資の効果

# 個別資産のリスクの単純平均
simple_avg_risk = (risk_A + np.std(returns_B, ddof=1)) / 2

print(f"個別リスクの平均:{simple_avg_risk:.2f}%")
print(f"ポートフォリオリスク:{portfolio_risk:.2f}%")
print(f"リスク低減効果:{simple_avg_risk - portfolio_risk:.2f}%")

なぜリスクが減少するか:

  • 2 資産が完全に連動しない限り(相関<1)、一部のリスクが相殺される
  • 一方が下落しても、他方が下落しない場合、損失が緩和される

2.4 相関係数と分散効果

相関係数 関係 分散効果
+1.0 完全な正の相関 なし(リスク低減なし)
+0.5 正の相関 ややあり
0 無相関 あり
-0.5 負の相関 大きい
-1.0 完全な負の相関 最大(理論上リスク 0 も可能)
# 相関係数を変えたポートフォリオリスクの計算
def portfolio_risk_with_correlation(risk1, risk2, w1, correlation):
    """相関係数からポートフォリオリスクを計算"""
    cov = correlation * risk1 * risk2  # 共分散 = 相関 × σ1 × σ2
    var1, var2 = risk1**2, risk2**2
    w2 = 1 - w1
    port_var = w1**2 * var1 + w2**2 * var2 + 2 * w1 * w2 * cov
    return np.sqrt(port_var)

# 株式 A と B のリスク
risk_A = np.std(returns_A, ddof=1)
risk_B = np.std(returns_B, ddof=1)

for corr in [1.0, 0.5, 0.0, -0.5, -1.0]:
    pr = portfolio_risk_with_correlation(risk_A, risk_B, 0.5, corr)
    print(f"相関係数 {corr:+.1f}: ポートフォリオリスク = {pr:.2f}%")

出力例:

相関係数 +1.0: ポートフォリオリスク = 6.10%
相関係数 +0.5: ポートフォリオリスク = 5.30%
相関係数 +0.0: ポートフォリオリスク = 4.37%
相関係数 -0.5: ポートフォリオリスク = 3.23%
相関係数 -1.0: ポートフォリオリスク = 1.54%

3. 多資産ポートフォリオ——n 資産への拡張

3.1 期待リターンと分散の行列表現

E[Rp] = w^T × μ
σp² = w^T × Σ × w

ここで:
w = 重みベクトル
μ = 期待リターンベクトル
Σ = 分散共分散行列
# 5 資産のポートフォリオ
np.random.seed(42)
n_assets = 5
n_years = 20

# 模擬リターンデータ(正規分布から生成)
returns = np.random.normal(loc=0.05, scale=0.15, size=(n_years, n_assets))

# 各資産の期待リターンとリスク
expected_returns = np.mean(returns, axis=0)
risks = np.std(returns, axis=0, ddof=1)

# 分散共分散行列
cov_matrix = np.cov(returns.T, ddof=1)

print("期待リターン:", expected_returns)
print("リスク(標準偏差):", risks)
print("分散共分散行列:\n", cov_matrix)

3.2 等ウェイトポートフォリオ

# 等ウェイト(1/n で分散)
weights_equal = np.array([1/n_assets] * n_assets)

# ポートフォリオの期待リターン
port_return_equal = np.dot(weights_equal, expected_returns)

# ポートフォリオのリスク
port_var_equal = np.dot(weights_equal.T, np.dot(cov_matrix, weights_equal))
port_risk_equal = np.sqrt(port_var_equal)

print(f"等ウェイトポートフォリオ")
print(f"期待リターン:{port_return_equal*100:.2f}%")
print(f"リスク:{port_risk_equal*100:.2f}%")

4. 効率的フロンティア——最適ポートフォリオの集合

4.1 効率的フロンティアとは

効率的フロンティアは**「同じリスクで最大の期待リターン」または「同じリターンで最小リスク」**のポートフォリオの集合だ。

import matplotlib.pyplot as plt

def generate_efficient_frontier(expected_returns, cov_matrix, n_points=100):
    """効率的フロンティアを生成(簡易版)"""
    n_assets = len(expected_returns)

    # ランダムなポートフォリオを 10000 個生成
    n_portfolios = 10000
    results = np.zeros((3, n_portfolios))
    weights_record = []

    for i in range(n_portfolios):
        # ランダムな重み
        weights = np.random.random(n_assets)
        weights /= np.sum(weights)  # 合計を 1 に正規化
        weights_record.append(weights)

        # ポートフォリオのリターン・リスク・シャープレシオ
        port_return = np.dot(weights, expected_returns)
        port_var = np.dot(weights.T, np.dot(cov_matrix, weights))
        port_std = np.sqrt(port_var)

        results[0, i] = port_std  # リスク
        results[1, i] = port_return  # リターン
        results[2, i] = port_return / port_std  # シャープレシオ(リスクフリーレート 0 と仮定)

    return results, weights_record

# 効率的フロンティアの生成
results, weights = generate_efficient_frontier(expected_returns, cov_matrix)

# プロット
plt.figure(figsize=(10, 6))
plt.scatter(results[0, :], results[1, :], c=results[2, :], marker='o', cmap='viridis', alpha=0.5)
plt.colorbar(label='シャープレシオ')
plt.xlabel('リスク(標準偏差)')
plt.ylabel('期待リターン')
plt.title('効率的フロンティア')
plt.grid(True, alpha=0.3)
plt.show()

4.2 最小分散ポートフォリオ

最小分散ポートフォリオはリスクが最小のポートフォリオだ。

from scipy.optimize import minimize

def portfolio_variance(weights, cov_matrix):
    """ポートフォリオ分散の計算"""
    return np.dot(weights.T, np.dot(cov_matrix, weights))

def find_minimum_variance_portfolio(cov_matrix, n_assets):
    """最小分散ポートフォリオの重みを求める"""
    # 初期値(等ウェイト)
    init_guess = np.array([1/n_assets] * n_assets)

    # 制約:重みの合計 = 1
    constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})

    # 境界:各重みは 0 以上 1 以下
    bounds = tuple((0, 1) for _ in range(n_assets))

    # 最適化
    result = minimize(
        portfolio_variance,
        init_guess,
        args=(cov_matrix,),
        method='SLSQP',
        bounds=bounds,
        constraints=constraints
    )

    return result.x

# 最小分散ポートフォリオ
min_var_weights = find_minimum_variance_portfolio(cov_matrix, n_assets)
min_var_return = np.dot(min_var_weights, expected_returns)
min_var_risk = np.sqrt(portfolio_variance(min_var_weights, cov_matrix))

print(f"最小分散ポートフォリオ")
print(f"重み:{min_var_weights}")
print(f"期待リターン:{min_var_return*100:.2f}%")
print(f"リスク:{min_var_risk*100:.2f}%")

4.3 最大シャープレシオポートフォリオ

シャープレシオはリスク 1 単位あたりの超過リターンを表す。

シャープレシオ = (E[Rp] - Rf) / σp

ここで:
Rf = リスクフリーレート(国債利回り等)
def negative_sharpe_ratio(weights, expected_returns, cov_matrix, risk_free_rate):
    """負のシャープレシオ(最小化問題にするため)"""
    port_return = np.dot(weights, expected_returns)
    port_var = np.dot(weights.T, np.dot(cov_matrix, weights))
    port_std = np.sqrt(port_var)
    return -(port_return - risk_free_rate) / port_std

def find_max_sharpe_portfolio(expected_returns, cov_matrix, risk_free_rate=0.01):
    """最大シャープレシオポートフォリオ"""
    n_assets = len(expected_returns)
    init_guess = np.array([1/n_assets] * n_assets)

    constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
    bounds = tuple((0, 1) for _ in range(n_assets))

    result = minimize(
        negative_sharpe_ratio,
        init_guess,
        args=(expected_returns, cov_matrix, risk_free_rate),
        method='SLSQP',
        bounds=bounds,
        constraints=constraints
    )

    return result.x

# 最大シャープレシオポートフォリオ
max_sharpe_weights = find_max_sharpe_portfolio(expected_returns, cov_matrix)
max_sharpe_return = np.dot(max_sharpe_weights, expected_returns)
max_sharpe_risk = np.sqrt(portfolio_variance(max_sharpe_weights, cov_matrix))
max_sharpe = (max_sharpe_return - 0.01) / max_sharpe_risk

print(f"最大シャープレシオポートフォリオ")
print(f"重み:{max_sharpe_weights}")
print(f"期待リターン:{max_sharpe_return*100:.2f}%")
print(f"リスク:{max_sharpe_risk*100:.2f}%")
print(f"シャープレシオ:{max_sharpe:.3f}")

5. 実践的な分散投資

5.1 資産クラス間の相関

資産ペア 典型的な相関係数
先進国株式 ↔ 先進国債券 -0.2 〜 +0.2
先進国株式 ↔ 新興国株式 +0.5 〜 +0.8
株式 ↔ 商品(金) -0.1 〜 +0.3
株式 ↔ REIT +0.5 〜 +0.7
先進国債券 ↔ 新興国債券 +0.3 〜 +0.6

分散投資のポイント:

  • 相関係数が低い資産を組み合わせるほど、分散効果は大きい
  • 株式と債券の組み合わせが古典的だが有効

5.2 ナシオの分散——銘柄数とリスク低減

def diversification_effect(n_stocks, idiosyncratic_risk=0.25):
    """銘柄数による非系統リスクの低減"""
    # 非系統リスク = 個別リスク / √n
    remaining_risk = idiosyncratic_risk / np.sqrt(n_stocks)
    return remaining_risk

# 銘柄数によるリスク低減
for n in [1, 5, 10, 20, 50, 100]:
    risk = diversification_effect(n)
    reduction = (1 - risk / 0.25) * 100
    print(f"{n}銘柄:残余リスク {risk*100:.1f}%({reduction:.0f}%低減)")

出力:

1 銘柄:残余リスク 25.0%(0% 低減)
5 銘柄:残余リスク 11.2%(55% 低減)
10 銘柄:残余リスク 7.9%(68% 低減)
20 銘柄:残余リスク 5.6%(78% 低減)
50 銘柄:残余リスク 3.5%(86% 低減)
100 銘柄:残余リスク 2.5%(90% 低減)

重要な知見:

  • 30 銘柄程度で非系統リスクの約 90% が消える
  • これ以上増やしても分散効果は限定的(限界効用逓減)

5.3 グローバル分散

# 地域別株式インデックスの相関(例)
correlation_matrix = np.array([
    [1.00, 0.72, 0.68, 0.55],   # 日本
    [0.72, 1.00, 0.80, 0.70],   # 米国
    [0.68, 0.80, 1.00, 0.75],   # 欧州
    [0.55, 0.70, 0.75, 1.00],   # 新興国
])

# 日本株は他地域と比較的低い相関(特に新興国)
# 米国・欧州は高い相関だが、完全な 1 ではない

グローバル分散の効果:

  • 一国の経済リスクに依存しない
  • 為替リスクも分散(円高時に外貨資産が評価増)

6. ポートフォリオ理論の限界

6.1 前提仮定の現実性

仮定 現実
リターンは正規分布 ファットテール(極端な事象)が実際には発生
相関は一定 危機時に相関が 1 に収束(2008 年金融危機)
投資家は合理的 行動経済学:実際は感情的・バイアスあり
取引コスト・税金なし 実際には売買手数料・税金がかかる

6.2 ブラック・スワン事象

2008 年金融危機や 2020 年コロナショックのような稀で極端な事象は、正規分布ベースのモデルでは予測できない。

対策:

  • リスクパリティ(リスク貢献度を均等化)
  • タールリスクヘッジ(プットオプションなど)
  • 流動性リスクの考慮

まとめ

ポートフォリオ理論の核心は以下の通り:

  1. 分散投資は数学的にリスク低減効果がある(相関<1 なら必ず効果)
  2. 効率的フロンティアは最適ポートフォリオの軌跡
  3. 最小分散ポートフォリオはリスク最小、最大シャープレシオはコストパフォーマンス最適
  4. 30 銘柄程度で非系統リスクの 90% が消える
  5. 理論には限界(ファットテール、相関変動)もあるため、盲信は禁物

分散投資は「免费のランチ(free lunch)」と呼ばれる。リターンを犠牲にせずリスクだけを低減できる、ほぼ唯一の方法だ。

免責事項 — 当記事は情報提供を目的としており、特定の金融商品の売買を推奨するものではありません。投資判断はご自身の責任で行ってください。