σμ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 学习路线›模块 7 · Alpha 研究与风险›主题 7.6·~50分钟高级

预期损失

路线进度模块 7/11 · 主题 6/9
7.6 预期损失
—实验得分— / 100总 XP—连续天数—

VaR 没有说的话

99% 的 VaR 为 3%,意思是 100 天里有 99 天亏损少于 3%。它对第 100 天一无所说:可能是 3.1%,也可能是 30%。预期损失(ES,CVaR)填补了这个缺口:它是 VaR 被突破的那些天的平均亏损,因而描述了尾部的形状。由于是平均值,它变化平滑,而且与 VaR 不同,它总是奖励分散化,这也是巴塞尔在市场风险上从 VaR 转向 ES 的原因。

import numpy as np

rng = np.random.default_rng(5)
returns = rng.standard_t(4, size=1000) * 0.01

cutoff = np.quantile(returns, 0.05)                   # the 95% VaR is minus this
tail = np.sort(returns[returns <= cutoff])            # the worst 5% of the days, lowest first
print(len(tail), round(float(-cutoff), 5))            # 50 days; the VaR
print(np.round(tail[:4], 4))                          # the four worst days

对尾部求平均

历史法下 ES 很简单:选出不高于 VaR 阈值的收益率,求平均,再取负号。结果绝不会小于 VaR,因为尾部的每个收益率都至少和阈值一样差。比值 ES / VaR 表明尾部有多厚。正态分布在 95% 时约为 1.2,厚尾数据通常显示更大的比值。

import numpy as np

def tail_returns(returns, confidence):
    cutoff = np.quantile(returns, 1 - confidence)
    return np.sort(returns[returns <= cutoff])

def historical_es(returns, confidence):
    return float(-tail_returns(returns, confidence).mean())

rng = np.random.default_rng(5)
returns = rng.standard_t(4, size=1000) * 0.01
for confidence in (0.95, 0.99):
    var = -np.quantile(returns, 1 - confidence)
    es = historical_es(returns, confidence)
    print(confidence, round(float(var), 5), round(es, 5), round(es / float(var), 3))

正态情形的公式

对于正态收益,尾部平均值有闭式:ES = sigma * pdf(z) / (1 - confidence) - mu,其中 z 是计算 VaR 所用的分位数,pdf 是标准正态密度在 z 处的值。计算只需两行,且只有数据服从正态时才精确,所以与历史 ES 比较是检验数据偏离正态程度的快速办法。

from statistics import NormalDist

def normal_es(mu, sigma, confidence):
    z = NormalDist().inv_cdf(confidence)
    return sigma * NormalDist().pdf(z) / (1 - confidence) - mu

for confidence in (0.95, 0.975, 0.99):
    var = NormalDist().inv_cdf(confidence)                        # in standard deviations, mu = 0
    es = normal_es(0.0, 1.0, confidence)
    print(confidence, round(var, 3), round(es, 3), round(es / var, 3))       # ES/VaR: 1.254, 1.193, 1.146
# The formula against a simulated tail
import numpy as np
from statistics import NormalDist

rng = np.random.default_rng(0)
sample = rng.normal(0.0, 0.01, size=1_000_000)
tail = sample[sample <= np.quantile(sample, 0.01)]
simulated = -tail.mean()
z = NormalDist().inv_cdf(0.99)
formula = 0.01 * NormalDist().pdf(z) / 0.01
print(round(float(simulated), 5), round(formula, 5))         # both close to 0.02665
ES 也更难回测VaR 可以通过统计突破次数来检验。ES 依赖尾部的少数观测:99% 水平、250 天时只有两三个收益率。它的估计噪声更大,需要更多数据或模型。实践中两者一并报告:VaR 作为阈值,ES 说明越过阈值后的严重程度。
  • ES 是超过 VaR 的尾部平均亏损:不高于 (1 - confidence) 分位数的收益率均值取负。
  • 它永远不低于 VaR;ES / VaR 衡量尾部的厚重,正态公式为 sigma * pdf(z) / (1 - confidence) - mu。
  • VaR 作为阈值、ES 作为严重程度一并报告;ES 依赖的尾部观测少,噪声更大。

下一节:压力测试,问一个组合在某个具体的极端情景下会亏多少。

实验:评分内容

  • 用 tail_returns 取出最糟的日子25 分
  • 用 historical_es 对尾部求平均25 分
  • 用 normal_es 由公式得出25 分
  • 用 es_var_ratio 比较尾部与阈值25 分
资源管理器 · lab-98-1
▾ lab-98-1
…
bash · 浏览器内
learner@quantcraft:~/lab-98-1$
Python 尚未加载PythonLn 1, Col 1Spaces: 4UTF-8

要重置此主题吗?

lab-98-1 中的所有内容将恢复为初始文件,终端也会被清空。你当前的解答将丢失,且无法撤销。

将被重置
  • main.py → 初始代码
  • 终端历史与测试输出
  • 未提交的更改
将被保留
  • 实验最高得分
  • 你的 XP 与连续天数
  • 讲解进度与笔记

登录后即可运行你的代码。

所有讲解均可免费阅读。创建账户或登录后,即可运行代码、运行测试并提交此实验。

  • 浏览器内 IDE 与 bash 终端,无需安装
  • 即时评分、XP 以及你在学习路线中的位置
创建账户登录
或
使用 GitHub 继续