Para modelar algunas sonrisas de volatilidad estoy usando el paquete de python pySABR.
Me encontré con una situación en la que tengo dos piezas de código casi idénticas para dos sonrisas de volatilidad diferentes que faltan las comillas ATM y el pySABR puede ajustar correctamente la volatilidad ATM en un caso y no puede en otro.
Este es el caso en el que todo funciona perfectamente:
import pysabr
import numpy as np
from pysabr import Hagan2002LognormalSABR as sabr
from pysabr import hagan_2002_lognormal_sabr as hagan2002
testStrikes = np.array([0.04, 0.06, 0.08, 0.10])
testVols = np.array([23.52, 16.24, 20.17, 26.19])
forward_3m_6m = (1/0.25) * (-1 + (1+0.0753*0.5) / (1+0.0747*0.25))
calibration = sabr(f = forward_3m_6m, shift = 0, t = 0.5, beta = 0.5).fit(testStrikes, testVols)
smile = []
test_smile = []
for strike in testStrikes:
smile.append(sabr(f = forward_3m_6m, shift = 0, t = 0.5, v_atm_n = 136.75/10000.00, beta = 0.5, rho = calibration[1], volvol = calibration[2]).lognormal_vol(strike) * 100.00)
test_smile.append(hagan2002.lognormal_vol(strike, forward_3m_6m, 0.5, calibration[0], beta, calibration[1], calibration[2]) * 100.00)
print(smile)
print(test_smile)
print(hagan2002.lognormal_vol(k = 0.0745136, f = forward_3m_6m, t = 0.5, alpha = calibration[0], beta = 0.5, rho = calibration[1], volvol = calibration[2]) * 100.00)
La salida es
[23.52579980722943, 16.22619971530687, 20.186954023608315, 26.176954813043512]
[23.52656681608356, 16.227190950406076, 20.188104613955648, 26.178058303454062]
18.369296025878036
La diferencia entre las listas de volatilidad primera y segunda es que la primera fue construida usando la comilla de volatilidad normal ATM en lugar del parámetro alfa.
Este es el código para el segundo caso cuando me encontré con un problema con resultados diferentes para diferentes métodos y un valor inadecuado de la volatilidad lognormal ATM resultante:
import pysabr
import numpy as np
from pysabr import Hagan2002LognormalSABR as LNsabr
from pysabr import hagan_2002_lognormal_sabr as hagan2002LN
strikes = np.array([0.05, 0.055, 0.06, 0.0650, 0.07, 0.08, 0.0850, 0.09, 0.095, 0.10])
LogNormalVols = np.array([18.90, 17.30, 16.34, 16.29, 17.19, 20.29, 21.89, 23.42, 24.84, 26.16])
vol_n_ATM = 141.01 / 10000.00
forward_3m_6m = (1 / 0.25) * (- 1 + ( 1+ 0.0756 * 0.5) / (1 + 0.0746 * 0.25))
discount_0m_6m = 0.0753
beta = 0.5
calibration_LN = LNsabr(forward_3m_6m, 0, 0.5, beta).fit(strikes, LogNormalVols)
modelVols_LN = []
test_LN = []
for strike in strikes:
modelVols_LN.append(LNsabr(forward_3m_6m, 0, 0.5, vol_n_ATM, beta, calibration_LN[1], calibration_LN[2]).lognormal_vol(strike) * 100.00)
test_LN.append(hagan2002LN.lognormal_vol(strike, f = forward_3m_6m, t = 0.5, alpha = calibration_LN[0], beta = beta, rho = calibration_LN[1], volvol = calibration_LN[2]) * 100.00)
print(modelVols_LN)
print(test_LN)
print(hagan2002LN.lognormal_vol(k = 0.0753, f = forward_3m_6m, t = 0.5, alpha = calibration_LN[0], beta = beta, rho = calibration_LN[1], volvol = calibration_LN[2]) * 100.00)
La salida es
[20.14451199912703, 18.54257849585578, 17.499371075753768, 17.1951750118971, 17.677189811525658, 20.011518064279755, 21.365538860352675, 22.691679608999, 23.95616514380161, 25.148945356594965]
[68.433853990843, 67.98546902874503, 67.8392636042873, 67.91509150013229, 68.15026017672005, 68.91958756908917, 69.39185669606091, 69.89474554951227, 70.41454229587038, 70.94145252003263]
68.64132679555016
Se puede ver fácilmente que al reproducir la sonrisa de volatilidad utilizando los cuatro parámetros calibrados lleva a volatilidades lognormales mucho más altas de lo esperado.
¿Qué estoy haciendo mal? ¿Dónde está el error en el segundo código? Cualquier ayuda será apreciada.
0 votos
¿Cuál es el resultado del ajuste? es decir, tu objeto
calibration_LN
.0 votos
@will, la salida es [0.18123299730498071, 0.5465080547109832, 0.8266797808547465], aquí el primer elemento es alpha, el segundo - rho, el tercero - volvol. Tenga en cuenta que ambos métodos que producen volatilidades lognormales utilizan los mismos parámetros de calibración.
0 votos
Lo siento, no era lo que quise decir. Normalmente, las funciones de ajuste devuelven un objeto que te indica si el ajuste fue exitoso, el número de iteraciones, evaluaciones de la función, el error de ajuste, etc.