σμ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 · بحث ألفا والمخاطر›الموضوع 7.6·~50 دمتقدم

العجز المتوقع

الموضع في المسارالوحدة 7 من 11 · الموضوع 6 من 9
7.6 العجز المتوقع
—درجة المختبر— / 100إجمالي XP—التتابع—

ما لا تقوله القيمة المعرضة للخطر

قيمة معرضة للخطر بنسبة 99% قدرها 3% تعني أنك تخسر أقل من 3% في 99 يومًا من كل 100. ولا تقول شيئًا عن اليوم المئة: قد تكون الخسارة 3.1% أو 30%. والعجز المتوقع (ES، CVaR) يسدّ الثغرة. فهو متوسط الخسارة في الأيام التي تُتجاوز فيها القيمة، فيصف شكل الذيل. ولأنه متوسط يتغير بسلاسة، وخلافًا للقيمة يكافئ التنويع دائمًا، ولهذا انتقلت بازل من القيمة إلى العجز لمخاطر السوق.

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 تبين مدى ثقل الذيل. وللتوزيع الطبيعي نحو 1.2 عند 95%، وتُظهر البيانات ذات الذيول السميكة عادة نسبة أكبر.

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 الكمّي المستخدم للقيمة وpdf كثافة التوزيع الطبيعي المعياري عند z. فيختصر الحساب في دالة من سطرين ولا يكون دقيقًا إلا إذا كانت البيانات طبيعية، ومقارنته بالعجز التاريخي اختبار سريع لمدى ابتعاد البيانات عن الطبيعية.

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
العجز المتوقع أصعب في الاختبار الرجعي أيضًاتُفحص القيمة بعدّ التجاوزات. أما العجز فيعتمد على المشاهدات القليلة في الذيل: عند 99% على 250 يومًا أي عائدان أو ثلاثة. وتقديره أكثر اضطرابًا ويحتاج بيانات أكثر أو نموذجًا. وعمليًا يُبلَّغ الاثنان معًا: القيمة كعتبة والعجز كشدة بعدها.
  • العجز المتوقع هو متوسط الخسارة في الذيل بعد القيمة: سالب متوسط العوائد عند الكمّي (1 - confidence) أو دونه.
  • لا تقل أبدًا عن القيمة؛ وES / VaR يقيس ثقل الذيل، والصيغة الطبيعية sigma * pdf(z) / (1 - confidence) - mu.
  • أبلغ عن القيمة كعتبة والعجز كشدة؛ والعجز أكثر اضطرابًا لاعتماده على مشاهدات ذيل قليلة.

التالي: اختبارات الضغط، السؤال عمّا ستخسره المحفظة في سيناريو محدد متطرف.

المختبر: ما الذي يُقيَّم

  • اعزل أسوأ الأيام باستخدام tail_returns25 نقطة
  • احسب متوسط الذيل باستخدام historical_es25 نقطة
  • احسبها بصيغة باستخدام normal_es25 نقطة
  • قارن الذيل والعتبة باستخدام es_var_ratio25 نقطة
المستكشف · 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 والتتابع
  • تقدّم المحاضرة والملاحظات

سجّل الدخول لتشغيل الكود الخاص بك.

قراءة كل المحاضرات مجانية. أنشئ حسابًا أو سجّل الدخول لتشغيل الكود وتنفيذ الاختبارات وتسليم هذا المختبر.

  • بيئة تطوير داخل المتصفح مع طرفية bash — لا حاجة لتثبيت أي شيء
  • تصحيح فوري ونقاط خبرة ومكانك في المسار
إنشاء حسابتسجيل الدخول
أو
المتابعة عبر GitHub