σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft Academyロードマップ
Ctrl K
AO
+1.2k 人の学習者
ログイン参加する ↗
ロードマップ
AO
+1.2k 人の学習者
ログイン
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Python ロードマップ›モジュール 5 · ポートフォリオ最適化›トピック 5.5·~55 分中級

効率的フロンティア

ロードマップ上の位置モジュール 5/11 · トピック 5/5
5.5 効率的フロンティア
—ラボスコア— / 100合計XP—連続日数—

リターンの水準ごとのリスク

前のトピックでは、1つの目標リターンに対して分散が最小のウェイトを求めました。これをすべての目標について行い、横軸にボラティリティ、縦軸にリターンを取ると、点は曲線を描きます。これが効率的フロンティアです。これらの資産で作れるどのポートフォリオも、その曲線上か、右側の内部にあります。曲線の内側にあるポートフォリオは無駄が多く、同じリターンでリスクが小さい、あるいは同じリスクでリターンが大きい別のポートフォリオが存在します。マーコウィッツの洞察は、投資家は曲線だけを見ればよいというものでした。

import numpy as np

mu = np.array([0.08, 0.05, 0.10])
cov = np.array([[0.0400, 0.0060, 0.0100],
                [0.0060, 0.0100, 0.0020],
                [0.0100, 0.0020, 0.0225]])

def target_return_weights(mu, cov, target):
    ones = np.ones(len(mu))
    s1, sm = np.linalg.solve(cov, ones), np.linalg.solve(cov, mu)
    a, b, c = ones @ s1, ones @ sm, mu @ sm
    d = a * c - b ** 2
    return ((c - b * target) * s1 + (a * target - b) * sm) / d

for target in (0.05, 0.06, 0.07, 0.08, 0.09, 0.10):
    w = target_return_weights(mu, cov, target)
    print(target, round(float(np.sqrt(w @ cov @ w)), 4))

その形と先端

フロンティアは(リターン、分散)の平面では放物線で、ボラティリティを横軸に取れば双曲線です。目標リターン t での分散は (a * t**2 - 2 * b * t + c) / (a * c - b**2) で、前回と同じ4つの数 a, b, c から決まります。最低点が全体の最小分散ポートフォリオで、リターンは b / a です。これが曲線の先端で、2つ前のトピックの最小分散ポートフォリオです。先端から上の部分だけが効率的フロンティアで、下の枝は同じリスクでリターンが小さい鏡像です。

# The tip of the curve: the minimum-variance portfolio and its return
import numpy as np

mu = np.array([0.08, 0.05, 0.10])
cov = np.array([[0.0400, 0.0060, 0.0100],
                [0.0060, 0.0100, 0.0020],
                [0.0100, 0.0020, 0.0225]])
ones = np.ones(3)
x = np.linalg.solve(cov, ones)
w = x / x.sum()
print(np.round(w, 4), round(float(w @ mu), 4), round(float(np.sqrt(w @ cov @ w)), 4))     # the tip: return, volatility

def closed_form_vol(target):
    s1, sm = x, np.linalg.solve(cov, mu)
    a, b, c = ones @ s1, ones @ sm, mu @ sm
    return float(np.sqrt((a * target ** 2 - 2 * b * target + c) / (a * c - b ** 2)))

for target in (0.05, 0.0642, 0.07):
    print(target, round(closed_form_vol(target), 4))

効率的な点と非効率な点

曲線上には同じリスクで2つのリターンが現れ、枝ごとに1つずつありますが、持つ価値があるのは高いほうだけです。したがって目標が効率的であるのは、最小分散ポートフォリオのリターン以上のときに限られます。判定は1回の比較ですが、選択肢のグラフにどの目標が属するかを決めます。フロンティア上で最良のバランスを探すことは、どの点を選ぶかの問題になり、よくある答えは無リスク金利を超えるリターンをリスク1単位あたり最も多く得られる点です。

# The point with the highest Sharpe ratio among candidate targets
import numpy as np

mu = np.array([0.08, 0.05, 0.10])
cov = np.array([[0.0400, 0.0060, 0.0100],
                [0.0060, 0.0100, 0.0020],
                [0.0100, 0.0020, 0.0225]])
rf = 0.02
ones = np.ones(3)
s1, sm = np.linalg.solve(cov, ones), np.linalg.solve(cov, mu)
a, b, c = ones @ s1, ones @ sm, mu @ sm
d = a * c - b ** 2

targets = np.linspace(0.05, 0.10, 11)
vols = np.sqrt((a * targets ** 2 - 2 * b * targets + c) / d)
sharpe = (targets - rf) / vols
for t, v, s in zip(targets, vols, sharpe):
    print(round(float(t), 3), round(float(v), 4), round(float(s), 4))
print("best target:", round(float(targets[np.argmax(sharpe)]), 3))
# Efficient or not: compare a target with the return of the minimum-variance portfolio
import numpy as np

mu = np.array([0.08, 0.05, 0.10])
cov = np.array([[0.0400, 0.0060, 0.0100],
                [0.0060, 0.0100, 0.0020],
                [0.0100, 0.0020, 0.0225]])
x = np.linalg.solve(cov, np.ones(3))
tip = float((x / x.sum()) @ mu)
for target in (0.05, 0.06, 0.07, 0.08):
    print(target, "efficient" if target >= tip else "inefficient")
推定したフロンティアはぼやけた帯曲線は mu と cov から描かれ、どちらも推定値なので、曲線自体も推定であり、しかも見栄えのよい推定です。オプティマイザは全ての組み合わせから、たまたま推定リターンが高くリスクが低く出たものを選んだからです。実際の結果は、見えている線の右下に落ちます。データを再標本化したり、標本の前半で描いたフロンティアを後半の成績と比べたりすれば、曲線のどれだけが運だったか分かります。フロンティアは約束ではなく、トレードオフの地図として扱いましょう。
  • 効率的フロンティアは、各目標リターンに対する最小のボラティリティ(w = target_return_weights(...) での sqrt(w @ cov @ w))を与える。内側のポートフォリオは無駄
  • 先端は全体の最小分散ポートフォリオ(リターン b / a)。そのリターン以上の目標だけが効率的
  • 点の選択は (target - rf) / volatility が最大のシャープ・レシオなどの基準で行う。曲線は推定から作られ、楽観的であることを忘れない

次はベクトル化バックテスターです。過去の価格で売買ルールを検証します。

ラボ:採点される内容

  • frontier_vols でフロンティアをたどる25点
  • min_variance_return で曲線の先端を見つける25点
  • is_efficient で良い側と悪い側を見分ける25点
  • max_sharpe_target で最良の点を選ぶ25点
エクスプローラー · lab-86-1
▾ lab-86-1
…
bash · ブラウザ内
learner@quantcraft:~/lab-86-1$
Pythonは未読み込みPythonLn 1, Col 1Spaces: 4UTF-8

このトピックをリセットしますか?

lab-86-1 の内容はすべてスターターファイルに戻り、ターミナルも消去されます。現在の解答は失われ、元に戻せません。

リセットされるもの
  • main.py → スターターコード
  • ターミナル履歴とテスト出力
  • 未提出の変更
保持されるもの
  • ラボの最高スコア
  • XPと連続日数
  • 講義の進捗とメモ

コードを実行するにはログインしてください。

レクチャーはすべて無料で読めます。コードの実行、テスト、ラボの提出にはアカウントの作成かログインが必要です。

  • ブラウザ内IDEとbashターミナル。インストール不要
  • 即時採点、XP、ロードマップでの現在地
アカウントを作成ログイン
または
GitHubで続ける