σμ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 AcademyParcours
Ctrl K
AO
+1,2 k apprenants
ConnexionRejoindre ↗
Parcours
AO
+1,2 k apprenants
Connexion
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Parcours Python›Module 11 · Systèmes de production et modèles de pointe›Sujet 11.10·~1 h 30 minExpert

Calibrage de modèles

Position dans le parcoursModule 11 sur 11 · Sujet 10 sur 13
11.10 Calibrage de modèles
—Score du lab— / 100XP total—Série—

Faire concorder le modèle avec le marché

Un modèle de valorisation a des paramètres, et les bonnes valeurs ne se trouvent pas dans un manuel mais dans le marché d'aujourd'hui. La calibration choisit les paramètres pour que les prix du modèle soient aussi proches que possible des prix observés, en général les cotations d'options du jour. La recette a trois parties. Un objectif qui mesure l'écart, presque toujours une somme de carrés des différences, éventuellement pondérée pour que les cotations liquides comptent plus. Une recherche qui déplace les paramètres pour le réduire. Et des vérifications que le résultat est sensé : stable d'un jour à l'autre, dans des bornes plausibles et bon sur des cotations non utilisées dans l'ajustement.

# 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

Deux outils de recherche : une racine et un minimum

Deux tâches reviennent constamment. D'abord la volatilité implicite : étant donné un prix de marché, trouver la volatilité pour laquelle le modèle le donne, c'est-à-dire une racine de model(sigma) - price = 0. Comme le prix croît régulièrement avec la volatilité, la bissection fonctionne : partez d'un intervalle où la différence change de signe, coupez-le en deux, gardez la moitié où le signe change encore, et recommencez ; chaque pas double la précision et ne peut pas échouer. Ensuite un minimum : pour une fonction à une seule vallée, la recherche par section dorée réduit l'intervalle du facteur 0,618 à chaque pas en réutilisant l'une des deux valeurs de fonction, donc une évaluation par pas, et sans dérivées. Les bibliothèques (scipy.optimize) sont plus rapides et gèrent de nombreux paramètres, mais ces deux outils montrent les idées.

# 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

Calibrer une volatilité, et le faire sérieusement

En assemblant les pièces, calibrer une volatilité unique sur plusieurs prix d'options revient à minimiser la somme des carrés des écarts entre price_fn(strike, sigma) et les prix de marché selon sigma, avec price_fn passée en argument pour que le même code serve à n'importe quel modèle. Pour un modèle à plusieurs paramètres (Heston en a cinq, SABR trois une fois beta fixé) la recherche est multidimensionnelle et peut tomber dans un minimum local : partez de plusieurs valeurs initiales, bornez les paramètres, et préférez des erreurs relatives ou en volatilité implicite aux erreurs brutes de prix pour que les options bon marché hors de la monnaie ne soient pas ignorées. Vérifiez ensuite que les paramètres ajustés sont stables d'un jour à l'autre et que le modèle valorise bien des options sur lesquelles il n'a pas été ajusté. Un ajustement parfait mais instable est du surapprentissage.

# 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
La calibration est l'endroit où les modèles dérapent discrètementUne calibration qui converge vers une valeur absurde (une volatilité de 4 000 %, une corrélation exactement égale à 1) est un avertissement, pas un résultat. Affichez toujours les paramètres ajustés et l'erreur résiduelle, comparez-les à ceux d'hier, et refusez d'utiliser un ajustement dont l'erreur dépasse la fourchette bid-ask des cotations. Gardez des bornes assez serrées pour que l'optimiseur ne s'égare pas dans des zones où la formule n'est pas valide, et conservez les données de marché utilisées, afin que la calibration puisse être reproduite et auditée plus tard.
  • La calibration choisit les paramètres du modèle qui minimisent une somme (pondérée) de carrés d'erreurs de prix ; vérifiez la stabilité d'un jour à l'autre et la précision sur des cotations non utilisées.
  • La bissection trouve une racine (une volatilité implicite) en coupant en deux un intervalle avec changement de signe ; la section dorée trouve le minimum d'une fonction à une vallée avec une évaluation par pas.
  • Passez la fonction de valorisation en argument, bornez les paramètres, partez de plusieurs valeurs initiales pour les modèles multi-paramètres et rejetez les ajustements absurdes.

À suivre : les réseaux de neurones pour la prévision, apprendre une prédiction à partir des données au lieu d'écrire la formule.

Lab : ce qui est noté

  • Mesurer l'écart avec sum_squared_error25 pts
  • Résoudre f(x) = 0 avec bisect_root25 pts
  • Trouver le point le plus bas avec golden_section_minimize25 pts
  • Ajuster le marché avec calibrate_sigma25 pts
Explorateur · lab-139-1
▾ lab-139-1
…
bash · dans le navigateur
learner@quantcraft:~/lab-139-1$
Python pas encore chargéPythonLn 1, Col 1Spaces: 4UTF-8

Réinitialiser ce sujet ?

Tout le contenu de lab-139-1 revient aux fichiers de départ et le terminal est vidé. Votre solution actuelle sera perdue : cette action est irréversible.

Sera réinitialisé
  • main.py → code de départ
  • Historique du terminal et sortie des tests
  • Modifications non soumises
Sera conservé
  • Meilleur score du lab
  • Votre XP et votre série
  • Progression du cours et notes

Connectez-vous pour exécuter votre code.

Tous les cours sont gratuits à lire. Créez un compte ou connectez-vous pour exécuter du code, lancer les tests et soumettre ce lab.

  • IDE dans le navigateur avec terminal bash — rien à installer
  • Correction instantanée, XP et votre place dans le parcours
Créer un compteSe connecter
OU
Continuer avec GitHub