σμ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.10·~1 س 30 دخبير

معايرة النماذج

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

جعل النموذج يتفق مع السوق

لنموذج التسعير معاملات، وقيمها الصحيحة لا توجد في كتاب بل في سوق اليوم. والمعايرة تختار المعاملات لتكون أسعار النموذج أقرب ما يمكن إلى الأسعار المرصودة، عادةً أسعار الخيارات المعلنة لليوم. وللوصفة ثلاثة أجزاء. هدف يقيس عدم المطابقة، وهو دائمًا تقريبًا مجموع مربعات الفروق، وقد يُرجَّح لتُحسب الأسعار السائلة أكثر. وبحث يحرك المعاملات لتقليله. وفحوص لمعقولية النتيجة: مستقرة من يوم لآخر وضمن حدود مقبولة وجيدة على أسعار لم تُستعمل في المطابقة.

# The objective: how far the model's prices are from the market's, as a (weighted) sum of squares
def sum_squared_error(model, market, weights=None):
    if weights is None:
        weights = [1.0] * len(model)
    return sum(w * (m - y) ** 2 for w, m, y in zip(weights, model, market))

market = [4.20, 8.60, 14.10]
model = [4.00, 8.80, 14.20]
print(round(sum_squared_error(model, market), 4))                    # 0.09   0.2^2 + 0.2^2 + 0.1^2
print(round(sum_squared_error(model, market, [1, 4, 1]), 4))         # 0.21   the middle quote counts four times as much

أداتا بحث: إيجاد جذر وإيجاد حد أدنى

تتكرر مهمتان باستمرار. الأولى التقلب الضمني: بمعلومية سعر السوق أوجد التقلب الذي يعطيه عنده النموذج، أي جذر model(sigma) - price = 0. ولأن السعر يرتفع باطراد مع التقلب تنجح التنصيف (bisection): ابدأ بمجال يغيّر فيه الفرق إشارته وقسّمه نصفين واحتفظ بالنصف الذي ما زالت الإشارة تتغير فيه وكرّر؛ تتضاعف الدقة في كل خطوة ولا يمكن أن يفشل. والثانية الحد الأدنى: لدالة بوادٍ واحد يقلص بحث القطع الذهبي المجال بالعامل 0.618 في كل خطوة مع إعادة استعمال إحدى قيمتَي الدالة، فيحتاج تقييمًا واحدًا في الخطوة ولا يحتاج مشتقات. والمكتبات (scipy.optimize) أسرع وتعالج معاملات كثيرة لكن هاتين الأداتين تُظهران الأفكار.

# Implied volatility by bisection: the root of (model price - market price) in sigma
import math

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

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

def bisect_root(f, lo, hi, tol=1e-10):
    f_lo = f(lo)
    while hi - lo > tol:
        mid = (lo + hi) / 2
        f_mid = f(mid)
        if f_lo * f_mid <= 0:
            hi = mid
        else:
            lo, f_lo = mid, f_mid
    return (lo + hi) / 2

market_price = bs_call(100.0, 105.0, 1.0, 0.02, 0.27)           # a quote generated with a volatility of 27%
implied = bisect_root(lambda sigma: bs_call(100.0, 105.0, 1.0, 0.02, sigma) - market_price, 0.01, 2.0)
print(round(implied, 6))                                         # 0.27   recovered
# Golden-section search: one function evaluation per step, the interval shrinks by 0.618 each time
import math

def golden_section_minimize(f, lo, hi, tol=1e-8):
    ratio = (math.sqrt(5) - 1) / 2
    a, b = lo, hi
    c, d = b - ratio * (b - a), a + ratio * (b - a)
    fc, fd = f(c), f(d)
    while b - a > tol:
        if fc < fd:
            b, d, fd = d, c, fc
            c = b - ratio * (b - a)
            fc = f(c)
        else:
            a, c, fc = c, d, fd
            d = a + ratio * (b - a)
            fd = f(d)
    return (a + b) / 2

evaluations = []
def valley(x):
    evaluations.append(x)
    return (x - 1.3) ** 2 + 0.5

print(round(golden_section_minimize(valley, 0.0, 5.0), 5), len(evaluations) < 60)       # 1.3 True   about 40 evaluations for 1e-8

معايرة تقلب والقيام بذلك بمسؤولية

بجمع القطع تعني معايرة تقلب واحد على عدة أسعار خيارات تقليل مجموع مربعات الأخطاء بين price_fn(strike, sigma) وأسعار السوق على sigma مع تمرير price_fn وسيطًا ليعمل الرمز نفسه لأي نموذج. ولنموذج متعدد المعاملات (لهستون خمسة ولـ SABR ثلاثة بعد تثبيت beta) يكون البحث متعدد الأبعاد وقد يقع في حد أدنى محلي: ابدأ من عدة تقديرات أولية وقيّد المعاملات وفضّل الأخطاء النسبية أو بالتقلب الضمني على أخطاء الأسعار الخام كي لا تُهمل الخيارات الرخيصة خارج السعر. ثم تحقق من استقرار المعاملات المعايَرة من يوم لآخر ومن تسعير النموذج الجيد لخيارات لم يُعايَر عليها. والمطابقة التامة غير المستقرة فرط تعلم (overfitting).

# Calibrate one volatility to three option prices: minimise the sum of squared errors over sigma
import math

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

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

def golden_section_minimize(f, lo, hi, tol=1e-8):
    ratio = (math.sqrt(5) - 1) / 2
    a, b = lo, hi
    c, d = b - ratio * (b - a), a + ratio * (b - a)
    fc, fd = f(c), f(d)
    while b - a > tol:
        if fc < fd:
            b, d, fd = d, c, fc
            c = b - ratio * (b - a)
            fc = f(c)
        else:
            a, c, fc = c, d, fd
            d = a + ratio * (b - a)
            fd = f(d)
    return (a + b) / 2

def calibrate_sigma(strikes, market_prices, price_fn, lo, hi):
    def error(sigma):
        return sum((price_fn(k, sigma) - p) ** 2 for k, p in zip(strikes, market_prices))
    return golden_section_minimize(error, lo, hi)

strikes = [90.0, 100.0, 110.0]
quotes = [bs_call(100.0, k, 1.0, 0.02, 0.23) for k in strikes]                  # market quotes made with sigma = 23%
sigma = calibrate_sigma(strikes, quotes, lambda k, s: bs_call(100.0, k, 1.0, 0.02, s), 0.01, 1.5)
print(round(sigma, 4))                                                           # 0.23
المعايرة هي الموضع الذي تخطئ فيه النماذج بصمتالمعايرة التي تتقارب إلى قيمة عبثية (تقلب 4,000% أو ارتباط يساوي 1 تمامًا) تحذير لا نتيجة. اطبع دائمًا المعاملات المعايَرة والخطأ المتبقي وقارنها بمعاملات الأمس وارفض استعمال مطابقة خطؤها أكبر من فارق العرض والطلب للأسعار المعلنة. وأبقِ الحدود ضيقة بما يكفي كي لا يتيه المُحسِّن في مناطق لا تصح فيها الصيغة، واحتفظ ببيانات السوق التي عايرت عليها ليمكن إعادة المعايرة ومراجعتها لاحقًا.
  • تختار المعايرة معاملات النموذج التي تقلل مجموعًا (مرجّحًا) لمربعات أخطاء الأسعار؛ تحقق من الاستقرار عبر الأيام ومن الدقة على أسعار لم تُستعمل في المطابقة.
  • يجد التنصيف جذرًا (تقلبًا ضمنيًا) بتنصيف مجال فيه تغير إشارة؛ ويجد القطع الذهبي حدًّا أدنى لدالة ذات وادٍ واحد بتقييم واحد في الخطوة.
  • مرّر دالة التسعير وسيطًا وقيّد المعاملات وابدأ من عدة تقديرات في النماذج متعددة المعاملات وارفض المطابقات العبثية.

التالي: الشبكات العصبية للتنبؤ، تعلّم تنبؤ من البيانات بدل كتابة الصيغة.

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

  • قِس عدم المطابقة باستخدام sum_squared_error25 نقطة
  • حُلّ f(x) = 0 باستخدام bisect_root25 نقطة
  • اعثر على أدنى نقطة باستخدام golden_section_minimize25 نقطة
  • طابق السوق باستخدام calibrate_sigma25 نقطة
المستكشف · lab-139-1
▾ lab-139-1
…
bash · داخل المتصفح
learner@quantcraft:~/lab-139-1$
لم يتم تحميل Python بعدPythonLn 1, Col 1Spaces: 4UTF-8

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

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

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

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

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

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