σμ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 学习路线›模块 3 · 概率与蒙特卡洛模拟›主题 3.3·~45分钟中级

厚尾与正态性检验

路线进度模块 3/11 · 主题 3/12
3.3 厚尾与正态性检验
—实验得分— / 100总 XP—连续天数—

钟形曲线的承诺

许多风险模型假定收益率服从正态分布,因为这样只需均值和标准差就能计算一切。正态分布对极端值的说法非常明确:偏离均值超过 k 个标准差(任一方向)的概率是 erfc(k / sqrt(2)):k=1 时 31.7%,k=2 时 4.55%,k=3 时 0.27%,k=4 时 0.0063%。这样,4 西格玛的日子大约每 15800 个交易日才出现一次,约 63 年一遇,5 西格玛的日子则要数千年一遇。

import math
import numpy as np

for k in (1, 2, 3, 4, 5):
    probability = math.erfc(k / math.sqrt(2))      # P(|Z| > k) for a normal variable
    print(k, f"{probability:.2e}")
# 1 3.17e-01
# 2 4.55e-02
# 3 2.70e-03
# 4 6.33e-05
# 5 5.73e-07

print(round(1 / math.erfc(4 / math.sqrt(2))))                  # 15787   trading days between 4-sigma days, if returns were normal
print(round(1 / math.erfc(5 / math.sqrt(2)) / 252))            # 6922    years between 5-sigma days

市场有厚尾

真实市场在一个人的职业生涯中会多次出现 4 西格玛、5 西格玛的日子,比如 1987、2008、2020 年。它们的收益率分布有厚尾:极端日比钟形允许的更多(同时中间更高更窄)。一个简单的观察办法是尾部比率:观测到的超出 k 个标准差的收益率占比,除以正态分布给出的占比。比率接近 1 表示尾部正常;5 表示极端日多出五倍;日度数据在 k = 4 时可能高达几百。假定正态的风险模型恰恰低估了最重要的那些日子。

returns = np.array([0.010, 0.012, 0.008, 0.011, 0.009, 0.010, 0.012, -0.060, 0.010, 0.009])

def tail_ratio(x, k):
    observed = np.mean(np.abs(x - x.mean()) > k * x.std(ddof=1))     # share beyond k standard deviations
    return observed / math.erfc(k / math.sqrt(2))                    # divided by what a normal would give

print(round(float(tail_ratio(returns, 2)), 2))    # 2.2   the one crash is 2.2 times too frequent at 2 sigma (a tiny sample, though)

Jarque-Bera 检验

正式的检验会问:这个样本是否有可能来自正态分布?Jarque-Bera 统计量结合上一主题的两个形状度量:JB = n / 6 * (S² + K² / 4),其中 S 是偏度,K 是超额峰度。正态样本的两者都接近 0,因此 JB 很小。在正态假设下,JB 服从自由度为 2 的卡方分布,其尾部有闭式 exp(-JB / 2),这就是 p 值。若小于 0.05,则拒绝正态性。临界值是 JB = 5.99。我们那个含一次暴跌的 10 日样本 JB = 22.4,p 值为 0.00001。

x = returns
n = len(x)
deviations = x - x.mean()
m2, m3, m4 = (np.mean(deviations ** k) for k in (2, 3, 4))
skew, kurt = m3 / m2 ** 1.5, m4 / m2 ** 2 - 3

jb = n / 6 * (skew ** 2 + kurt ** 2 / 4)
p_value = math.exp(-jb / 2)
print(round(float(jb), 2), p_value < 0.05)     # 22.37 True   normality rejected
print(f"{p_value:.1e}")                         # 1.4e-05
# The p-value falls fast as the statistic grows: 5.99 is the 5% critical value
for jb in (0, 2, 5.99, 10, 22.37):
    print(jb, round(math.exp(-jb / 2), 5))
# 0 1.0
# 2 0.36788
# 5.99 0.05004
# 10 0.00674
# 22.37 1e-05
拒绝正态性并不是终点有了几百个日收益率,这个检验几乎对所有资产都会拒绝正态性,所以拒绝是意料之中的消息。关键在于尾部相差多少:用尾部比率衡量问题的大小,然后用真实的暴跌、学生 t 之类更厚尾的分布或历史模拟做压力测试,而不是相信钟形曲线。
  • 正态变量偏离超过 k 个标准差的概率是 erfc(k / sqrt(2)):k=2 时 4.55%,k=4 时 0.0063%。
  • 尾部比率(观测占比除以正态占比)显示厚尾:市场在 4 西格玛处远高于 1。
  • Jarque-Bera:n / 6 * (S**2 + K**2 / 4);p 值为 exp(-JB / 2);低于 0.05 就拒绝正态性(JB 大于 5.99)。

下一节:假设检验:t 检验,判断平均收益率是否真的不为零。

实验:评分内容

  • 用 jarque_bera 衡量样本偏离正态的程度25 分
  • 用 normality_p_value 把统计量转换为 p 值25 分
  • 用 normal_tail_probability 计算钟形曲线所预测的概率25 分
  • 用 tail_ratio 统计尾部厚了多少倍25 分
资源管理器 · lab-56-1
▾ lab-56-1
…
bash · 浏览器内
learner@quantcraft:~/lab-56-1$
Python 尚未加载PythonLn 1, Col 1Spaces: 4UTF-8

要重置此主题吗?

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

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

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

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

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