σμ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—连续天数—

每个收益水平对应的风险

上一节为一个目标收益求出了方差最小的权重。对每个目标都这样做,以波动率为横轴、收益为纵轴作图,这些点连成一条曲线,即有效前沿。这些资产构成的任何组合都在曲线上或曲线内(右侧)。曲线内部的组合是浪费的,因为另有组合在相同收益下风险更低,或在相同风险下收益更高。马科维茨的洞见是:投资者只需要看这条曲线。

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),用的仍是之前的 a, b, c 四个数。它的最低点是全局最小方差组合,收益为 b / a:这就是曲线的顶点,也就是两节之前的最小方差组合。只有从顶点向上的部分才是有效前沿;下面的分支是它的镜像,同样风险下收益更低。

# 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))

有效点与无效点

曲线上同一个风险对应两个收益,每个分支各一个,只有较高的那个值得持有。所以一个目标收益当且仅当不低于最小方差组合的收益时才是有效的。这个检验只是一次比较,却决定了哪些目标应出现在选择图中。在前沿上寻找最佳权衡就变成选哪个点的问题,常见的答案是:每单位风险(高于无风险利率的部分)所获收益最多的那个点。

# 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 画成,所以它本身就是估计,而且是偏乐观的估计:优化器从所有组合中挑出的,恰恰是估计收益碰巧偏高、风险碰巧偏低的那些。真实结果会落在你所见曲线的右下方。对数据重新抽样,或用样本前半段画出的前沿与后半段的实际表现对比,就能看出曲线有多少是运气。请把前沿当作权衡的地图,而不是承诺。
  • 有效前沿给出每个目标收益对应的最低波动率:sqrt(w @ cov @ w),其中 w = target_return_weights(...);内部的组合是浪费的。
  • 顶点是全局最小方差组合,收益为 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 继续