σμ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›الوحدة 11 · أنظمة الإنتاج والنماذج المتقدمة›الموضوع 11.8·~1 س 30 دخبير

التقلب المحلي

الموضع في المسارالوحدة 11 من 11 · الموضوع 8 من 13
11.8 التقلب المحلي
—درجة المختبر— / 100إجمالي XP—التتابع—

تقلب لكل سعر تنفيذ ولكل زمن

يسعّر بلاك-شولز خيار الشراء بتقلب واحد، بينما يعلن السوق تقلبًا ضمنيًا مختلفًا لكل سعر تنفيذ وكل استحقاق. ويعيد نموذج التقلب المحلي إنتاج كل تلك الأسعار دفعة واحدة بجعل التقلب يعتمد على مستوى السعر والزمن sigma(S, t) مع مصدر عشوائية واحد. وبيّن برونو دوبير عام 1994 أن هذه الدالة ليست تخمينًا: بل تحددها سطح أسعار الخيارات C(K, T) بصيغة، فتتحول أسعار الخيارات في السوق مباشرة إلى نموذج لتسعير الخيارات الغريبة بانسجام معها.

# What "one volatility" means: a call priced with Black-Scholes at three strikes, all with the same sigma
import math

def norm_cdf(x):
    return 0.5 * (1 + math.erf(x / math.sqrt(2)))

def bs_call(s, k, t, r, q, sigma):
    d1 = (math.log(s / k) + (r - q + sigma ** 2 / 2) * t) / (sigma * math.sqrt(t))
    d2 = d1 - sigma * math.sqrt(t)
    return s * math.exp(-q * t) * norm_cdf(d1) - k * math.exp(-r * t) * norm_cdf(d2)

for k in (90, 100, 110):
    print(k, round(bs_call(100.0, k, 1.0, 0.03, 0.01, 0.2), 4))      # 90 14.6592   100 8.8273   110 4.8947   one sigma, three different prices

صيغة دوبير

تعبّر صيغة دوبير عن التباين المحلي بثلاث مشتقات لسطح أسعار الخيارات: sigma_loc^2(K, T) = (dC/dT + (r - q) * K * dC/dK + q * C) / (0.5 * K^2 * d2C/dK2). يحتوي البسط على ميل الزمن للأسعار (يجب ألا يكون سالبًا وإلا كان الخيار الأطول أرخص من الأقصر: مراجحة تقويمية) ويحتوي المقام على الفراشة d2C/dK2 المتناسبة مع كثافة احتمال السعر الضمنية في السوق وهي موجبة. ومع الأسعار الحقيقية المشوشة والمتفرقة تُحسب المشتقات بـالفروق المنتهية على سطح مُمهَّد: (f(x+h) - f(x-h)) / (2h) للمشتقة الأولى و(f(x+h) - 2 f(x) + f(x-h)) / h^2 للثانية. والسطح المشوش يعطي تقلبات محلية جامحة، فتمهيد سطح التقلب الضمني أولًا ممارسة معيارية.

# Dupire's formula recovers a constant volatility from Black-Scholes prices (finite differences on the price surface)
import math

def norm_cdf(x):
    return 0.5 * (1 + math.erf(x / math.sqrt(2)))

def bs_call(s, k, t, r, q, sigma):
    d1 = (math.log(s / k) + (r - q + sigma ** 2 / 2) * t) / (sigma * math.sqrt(t))
    return s * math.exp(-q * t) * norm_cdf(d1) - k * math.exp(-r * t) * norm_cdf(d1 - sigma * math.sqrt(t))

def local_variance(call, dc_dt, dc_dk, d2c_dk2, strike, r, q):
    return (dc_dt + (r - q) * strike * dc_dk + q * call) / (0.5 * strike ** 2 * d2c_dk2)

s, k, t, r, q, sigma = 100.0, 105.0, 1.0, 0.03, 0.01, 0.2
hk, ht = 0.5, 0.002
c = bs_call(s, k, t, r, q, sigma)
dc_dt = (bs_call(s, k, t + ht, r, q, sigma) - bs_call(s, k, t - ht, r, q, sigma)) / (2 * ht)
dc_dk = (bs_call(s, k + hk, t, r, q, sigma) - bs_call(s, k - hk, t, r, q, sigma)) / (2 * hk)
d2c_dk2 = (bs_call(s, k + hk, t, r, q, sigma) - 2 * c + bs_call(s, k - hk, t, r, q, sigma)) / hk ** 2
print(round(math.sqrt(local_variance(c, dc_dt, dc_dk, d2c_dk2, k, r, q)), 3))        # 0.2   the constant volatility comes back

استعمال السطح في المحاكاة

للمحاكاة يُجدوَل sigma_loc على شبكة أسعار (لكل شريحة زمنية) ثم يُبحث عنه عند السعر الحالي في كل خطوة بالاستيفاء الخطي بين نقاط الشبكة وإبقاء قيم الطرفين ثابتة خارجها. والخطوة حينئذ هي حركة لوغاريتم-أويلر المعتادة بذلك التقلب S * exp((mu - sigma^2/2) dt + sigma sqrt(dt) dz). وبالمقارنة مع هستون يطابق التقلب المحلي أسعار خيارات اليوم تمامًا بالبناء لكن ديناميكية ابتسامته المستقبلية غير واقعية (يتسطح الانحياز مع الزمن) وهذا مهم للمنتجات الحساسة للابتسامة المستقبلية. ولذلك تستعمل مكاتب كثيرة التقلب المحلي-العشوائي، مزيجًا منهما.

# A lookup on a strike grid (linear inside, flat outside) and one simulation step with the volatility it returns
import math

def lookup_local_vol(strikes, vols, k):
    if k <= strikes[0]:
        return vols[0]
    if k >= strikes[-1]:
        return vols[-1]
    for i in range(1, len(strikes)):
        if k <= strikes[i]:
            weight = (k - strikes[i - 1]) / (strikes[i] - strikes[i - 1])
            return vols[i - 1] + weight * (vols[i] - vols[i - 1])

strikes, vols = [90.0, 100.0, 110.0], [0.25, 0.20, 0.18]
print(lookup_local_vol(strikes, vols, 95.0), round(lookup_local_vol(strikes, vols, 105.0), 4), lookup_local_vol(strikes, vols, 200.0))      # 0.225 0.19 0.18

s = 100.0
sigma = lookup_local_vol(strikes, vols, s)
print(round(s * math.exp((0.05 - sigma ** 2 / 2) * (1 / 252) + sigma * math.sqrt(1 / 252) * 0.5), 4))       # 100.6439
# A calendar arbitrage shows up as a negative numerator: a longer option priced below a shorter one
def numerator(call, dc_dt, dc_dk, strike, r, q):
    return dc_dt + (r - q) * strike * dc_dk + q * call

print(round(numerator(10.0, 1.2, -0.45, 100.0, 0.03, 0.01), 6))           # 0.4     prices rise with maturity: a valid local variance
print(numerator(10.0, -2.0, -0.45, 100.0, 0.03, 0.01) < 0)                # True    prices fall with maturity: no model can fit this
المطابقة التامة ليست نموذجًا تامًالأن التقلب المحلي يُقرأ من الأسعار فهو يعيد إنتاجها بالبناء بما فيها ضجيجها وأي مراجحة في الأسعار المعلنة. تحقق من خلو السطح من المراجحة قبل استعمال دوبير (فراشات وفروق تقويمية غير سالبة) وضع حدًا أعلى وأدنى للتقلبات الناتجة واختبر الأسعار المحاكاة مقابل الخيارات العادية التي انطلقت منها. فنموذج يطابق اليوم تمامًا قد يتحوط سيئًا غدًا لأن تصوره لكيفية حركة الابتسامة مبسّط.
  • يعيد التقلب المحلي sigma(S, t) إنتاج سطح أسعار الخيارات كله بمصدر عشوائية واحد؛ وتعطيه صيغة دوبير مباشرة من الأسعار.
  • دوبير: (dC/dT + (r - q) K dC/dK + q C) / (0.5 K^2 d2C/dK2)؛ احسب المشتقات بالفروق المنتهية على سطح مُمهَّد خالٍ من المراجحة.
  • حاكِ بالبحث عن sigma_loc عند السعر الحالي في كل خطوة؛ والمطابقة التامة اليوم لا تضمن ديناميكية ابتسامة واقعية غدًا.

التالي: نموذج SABR، نموذج تقلب عشوائي له ابتسامة بصيغة مغلقة.

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

  • اشتق عدديًا باستخدام finite_difference25 نقطة
  • طبّق صيغة دوبير في local_variance25 نقطة
  • اقرأ السطح باستخدام lookup_local_vol25 نقطة
  • حاكِ خطوة باستخدام local_vol_step25 نقطة
المستكشف · lab-137-1
▾ lab-137-1
…
bash · داخل المتصفح
learner@quantcraft:~/lab-137-1$
لم يتم تحميل Python بعدPythonLn 1, Col 1Spaces: 4UTF-8

هل تريد إعادة ضبط هذا الموضوع؟

سيعود كل ما في lab-137-1 إلى الملفات الابتدائية وستُمسح الطرفية. ستفقد حلّك الحالي ولا يمكن التراجع عن ذلك.

سيُعاد ضبطه
  • main.py ← الشيفرة الابتدائية
  • سجل الطرفية ومخرجات الاختبارات
  • التغييرات غير المُرسلة
سيُحتفظ به
  • أفضل درجة في المختبر
  • نقاط XP والتتابع
  • تقدّم المحاضرة والملاحظات

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

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

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