Un smile en quatre nombres
Les options de taux et de change se négocient sur de nombreux strikes, et les salles ont besoin d'un moyen lisse et conscient des arbitrages d'interpoler et d'extrapoler les volatilités cotées. SABR (Hagan, Kumar, Lesniewski, Woodward, 2002) est le standard du marché. Il modélise un forward F avec sa propre volatilité aléatoire alpha : dF = alpha F^beta dW et d alpha = nu alpha dZ, avec une corrélation rho entre les chocs. Quatre paramètres façonnent le smile d'une échéance : alpha fixe le niveau global, beta la façon dont la volatilité dépend du niveau de prix (0 normal, 1 lognormal), rho l'inclinaison (skew) et nu la courbure (de combien les ailes montent).
# The four parameters of one expiry, for a rate forward of 3%
f, t = 0.03, 1.0 # forward rate and expiry in years
alpha, beta, rho, nu = 0.035, 0.5, -0.3, 0.4
print(round(alpha / f ** (1 - beta), 4)) # 0.2021 alpha / f^(1-beta) is roughly the at-the-money volatility (before the correction)La formule de Hagan
Le développement asymptotique de Hagan donne la volatilité implicite de Black sans simulation. À la monnaie (K = F), c'est alpha / F^(1-beta) fois une correction de temps 1 + (...) * T, où la parenthèse combine trois effets : la courbure beta de l'épine dorsale, le terme de skew rho * nu et le terme de smile nu^2. Hors de la monnaie, la même correction multiplie deux facteurs de plus : un dénominateur en log-moneyness L = ln(F/K) et le rapport z / chi(z), avec z = (nu/alpha) (FK)^((1-beta)/2) L et chi(z) = ln((sqrt(1 - 2 rho z + z^2) + z - rho) / (1 - rho)). Quand z tend vers 0 le rapport tend vers 1, donc la formule rejoint en douceur la valeur à la monnaie, mais calculer directement 0 / 0 échouerait : traitez un z minuscule comme une limite.
# The pieces of Hagan's formula: the time correction, the at-the-money volatility and z / chi(z)
import math
def sabr_correction(f, k, t, alpha, beta, rho, nu):
fk = f * k
return 1 + ((1 - beta) ** 2 / 24 * alpha ** 2 / fk ** (1 - beta) + rho * beta * nu * alpha / (4 * fk ** ((1 - beta) / 2)) + (2 - 3 * rho ** 2) * nu ** 2 / 24) * t
def sabr_atm_vol(f, t, alpha, beta, rho, nu):
return alpha / f ** (1 - beta) * sabr_correction(f, f, t, alpha, beta, rho, nu)
def z_over_chi(z, rho):
if abs(z) < 1e-7:
return 1.0
return z / math.log((math.sqrt(1 - 2 * rho * z + z ** 2) + z - rho) / (1 - rho))
print(round(sabr_atm_vol(0.03, 1.0, 0.035, 0.5, -0.3, 0.4), 6)) # 0.203877 the at-the-money volatility
print(round(z_over_chi(0.5, -0.3), 6), z_over_chi(0.0, -0.3)) # 1.103683 1.0 near 1, and exactly 1.0 at z = 0Lire le smile et l'ajuster
Tracez la volatilité implicite en fonction du strike et les paramètres ont des signatures claires. Un rho négatif relève les strikes bas et abaisse les hauts (skew descendant, typique des actions et de nombreux taux) ; rho = 0 donne un smile symétrique. Un nu plus grand relève les deux ailes. beta déplace l'épine dorsale : de combien la volatilité à la monnaie change quand le forward bouge. Les praticiens fixent beta par convention ou d'après l'historique, puis calibrent le reste sur les volatilités cotées de chaque échéance (sujet suivant), en résolvant souvent alpha à partir de la cotation à la monnaie pour qu'elle colle exactement. Deux mises en garde : la formule est une approximation qui se dégrade pour les longues échéances et les très bas strikes, où elle peut impliquer des densités de probabilité négatives.
# The smile for three values of rho: a negative rho lifts the low strikes (downward skew)
import math
def sabr_vol(f, k, t, alpha, beta, rho, nu):
fk = f * k
log = math.log(f / k)
mid = fk ** ((1 - beta) / 2)
z = nu / alpha * mid * log
ratio = 1.0 if abs(z) < 1e-7 else z / math.log((math.sqrt(1 - 2 * rho * z + z ** 2) + z - rho) / (1 - rho))
denominator = mid * (1 + (1 - beta) ** 2 / 24 * log ** 2 + (1 - beta) ** 4 / 1920 * log ** 4)
bracket = 1 + ((1 - beta) ** 2 / 24 * alpha ** 2 / fk ** (1 - beta) + rho * beta * nu * alpha / (4 * mid) + (2 - 3 * rho ** 2) * nu ** 2 / 24) * t
return alpha / denominator * ratio * bracket
for rho in (-0.5, 0.0, 0.5):
print(rho, [round(sabr_vol(0.03, k, 1.0, 0.035, 0.5, rho, 0.4), 4) for k in (0.02, 0.03, 0.04)])
# one line per rho: the first value (low strike) is the highest when rho is negative and the lowest when rho is positive# A free sanity check: with beta = 1 and nu = 0 SABR is Black-Scholes, so the at-the-money volatility is alpha at every expiry
def atm(f, t, alpha, beta, rho, nu):
fk = f * f
bracket = 1 + ((1 - beta) ** 2 / 24 * alpha ** 2 / fk ** (1 - beta) + rho * beta * nu * alpha / (4 * fk ** ((1 - beta) / 2)) + (2 - 3 * rho ** 2) * nu ** 2 / 24) * t
return alpha / f ** (1 - beta) * bracket
print(atm(100.0, 0.5, 0.2, 1.0, -0.3, 0.0), atm(100.0, 5.0, 0.2, 1.0, -0.3, 0.0)) # 0.2 0.2rho strictement entre -1 et 1, nu pas énorme) et réajustez chaque échéance séparément et en douceur dans le temps. Pour des taux bas ou négatifs, utilisez la version normale (Bachelier) de la formule, qui ne s'effondre pas près de zéro.- SABR donne à chaque échéance un smile à partir de quatre paramètres : alpha (niveau), beta (épine dorsale), rho (skew) et nu (courbure).
- Formule de Hagan = préfacteur x
z / chi(z)x correction de temps ; à la monnaie elle se réduit àalpha / F^(1-beta)fois la correction, etz / chi(z)tend vers 1 quandztend vers 0. - Un rho négatif incline le smile vers le bas, un nu plus grand relève les ailes ; ajustez chaque échéance séparément, vérifiez l'arbitrage aux ailes, et utilisez la version normale près de taux nuls.
À suivre : la calibration de modèles, ajuster les paramètres d'un modèle aux prix de marché.
Lab : ce qui est noté
- Ajouter la correction de temps avec
sabr_correction25 pts - Lire le point à la monnaie avec
sabr_atm_vol25 pts - Maîtriser le 0 / 0 dans
z_over_chi25 pts - Tracer le smile avec
sabr_implied_vol25 pts