σμ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 AcademySkills
Ctrl K
AO
+1,2 Tsd. Lernende
AnmeldenBeitreten ↗
Skills
AO
+1,2 Tsd. Lernende
Anmelden
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Python-Skill›Modul 11 · Produktivsysteme und Spitzenmodelle›Thema 11.10·~1 Std. 30 Min.Experte

Modellkalibrierung

Position in der SkillModul 11 von 11 · Thema 10 von 13
11.10 Modellkalibrierung
—Lab-Punkte— / 100XP gesamt—Serie—

Das Modell mit dem Markt in Einklang bringen

Ein Bewertungsmodell hat Parameter, und die richtigen Werte stehen nicht im Lehrbuch, sondern im heutigen Markt. Kalibrierung wählt die Parameter so, dass die Modellpreise den beobachteten möglichst nahekommen, meist den Optionsquotes des Tages. Das Rezept hat drei Teile. Ein Ziel, das die Abweichung misst, fast immer eine Summe quadrierter Differenzen, womöglich gewichtet, damit liquide Quotes mehr zählen. Eine Suche, die die Parameter verschiebt, um sie zu verkleinern. Und Prüfungen, ob das Ergebnis sinnvoll ist: von Tag zu Tag stabil, in plausiblen Grenzen und gut bei Quotes, die nicht in die Anpassung eingingen.

# 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

Zwei Suchwerkzeuge: eine Nullstelle und ein Minimum

Zwei Aufgaben tauchen ständig auf. Erstens die implizite Volatilität: Zu einem Marktpreis die Volatilität finden, bei der das Modell ihn liefert, also eine Nullstelle von model(sigma) - price = 0. Da der Preis stetig mit der Volatilität steigt, funktioniert die Bisektion: Man beginnt mit einem Intervall, in dem die Differenz das Vorzeichen wechselt, halbiert es, behält die Hälfte, in der das Vorzeichen noch wechselt, und wiederholt; jeder Schritt verdoppelt die Genauigkeit und kann nicht scheitern. Zweitens ein Minimum: Für eine Funktion mit einem einzigen Tal verkleinert die Goldene-Schnitt-Suche das Intervall pro Schritt um den Faktor 0,618 und verwendet einen der beiden Funktionswerte wieder, braucht also eine Auswertung pro Schritt und keine Ableitungen. Bibliotheken (scipy.optimize) sind schneller und bewältigen viele Parameter, aber diese beiden zeigen die Ideen.

# 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

Eine Volatilität kalibrieren und das verantwortungsvoll tun

Setzt man die Teile zusammen, heißt eine einzelne Volatilität an mehrere Optionspreise zu kalibrieren, die Summe der quadrierten Fehler zwischen price_fn(strike, sigma) und den Marktpreisen über sigma zu minimieren, wobei price_fn übergeben wird, damit derselbe Code für jedes Modell funktioniert. Bei einem Modell mit mehreren Parametern (Heston hat fünf, SABR drei nach Festlegung von beta) ist die Suche mehrdimensional und kann in einem lokalen Minimum landen: Starten Sie von mehreren Anfangswerten, begrenzen Sie die Parameter und bevorzugen Sie relative oder Implied-Volatility-Fehler gegenüber rohen Preisfehlern, damit billige Out-of-the-money-Optionen nicht ignoriert werden. Prüfen Sie dann, dass die angepassten Parameter von Tag zu Tag stabil sind und das Modell Optionen gut bewertet, an die es nicht angepasst wurde. Ein perfekter, aber instabiler Fit ist 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
In der Kalibrierung geraten Modelle leise auf AbwegeEine Kalibrierung, die gegen einen unsinnigen Wert konvergiert (eine Volatilität von 4.000 %, eine Korrelation von genau 1), ist eine Warnung, kein Ergebnis. Geben Sie stets die angepassten Parameter und den Restfehler aus, vergleichen Sie sie mit gestern und verwenden Sie keinen Fit, dessen Fehler größer ist als die Geld-Brief-Spanne der Quotes. Halten Sie die Grenzen eng genug, dass der Optimierer nicht in Bereiche gerät, in denen die Formel ungültig ist, und bewahren Sie die angepassten Marktdaten auf, damit sich die Kalibrierung später reproduzieren und prüfen lässt.
  • Die Kalibrierung wählt Modellparameter, die eine (gewichtete) Summe quadrierter Preisfehler minimieren; prüfen Sie Stabilität über Tage und Genauigkeit bei nicht verwendeten Quotes.
  • Die Bisektion findet eine Nullstelle (eine implizite Volatilität), indem sie ein Intervall mit Vorzeichenwechsel halbiert; der Goldene Schnitt findet das Minimum einer Ein-Tal-Funktion mit einer Auswertung pro Schritt.
  • Übergeben Sie die Bewertungsfunktion, begrenzen Sie die Parameter, starten Sie bei Mehrparametermodellen von mehreren Schätzungen und verwerfen Sie unsinnige Fits.

Als Nächstes: neuronale Netze für Prognosen, eine Vorhersage aus Daten lernen, statt die Formel zu schreiben.

Lab: Was bewertet wird

  • Die Abweichung mit sum_squared_error messen25 Pkt.
  • f(x) = 0 mit bisect_root lösen25 Pkt.
  • Den tiefsten Punkt mit golden_section_minimize finden25 Pkt.
  • Den Markt mit calibrate_sigma anpassen25 Pkt.
Explorer · lab-139-1
▾ lab-139-1
…
bash · im Browser
learner@quantcraft:~/lab-139-1$
Python noch nicht geladenPythonLn 1, Col 1Spaces: 4UTF-8

Dieses Thema zurücksetzen?

Alles in lab-139-1 wird auf die Startdateien zurückgesetzt und das Terminal geleert. Ihre aktuelle Lösung geht verloren – das lässt sich nicht rückgängig machen.

Wird zurückgesetzt
  • main.py → Startcode
  • Terminal-Verlauf und Testausgabe
  • Nicht eingereichte Änderungen
Bleibt erhalten
  • Beste Lab-Punktzahl
  • Ihre XP und Ihre Serie
  • Vorlesungsfortschritt und Notizen

Melde dich an, um deinen Code auszuführen.

Alle Lektionen kannst du kostenlos lesen. Erstelle ein Konto oder melde dich an, um Code auszuführen, Tests zu starten und dieses Lab abzugeben.

  • IDE im Browser mit Bash-Terminal — nichts zu installieren
  • Sofortige Bewertung, XP und dein Platz auf der Skill
Konto erstellenAnmelden
ODER
Weiter mit GitHub