2 votos

QuantLib - Calibración del factor único de Hull White sobre los tipos de interés negativos

Llevo unos días trabajando con el paquete QuantLib Python. Actualmente, estoy trabajando en la calibración de un modelo de un factor de Hull White para los tipos cortos. Estoy calibrando el modelo en la curva de rendimiento y en las volatilidades de los swaptions.

Dadas las circunstancias actuales del mercado, gran parte de la curva de rendimiento es negativa. Esto provoca algunos problemas a la hora de calibrar el modelo de un factor de Hull White, ya que la distribución lognormal no puede ser negativa. Después de buscar un poco encontré la posibilidad de especificar un desplazamiento. En el código siguiente se muestra la inicialización de mi SwaptionHelper incluyendo el desplazamiento.

Tenga en cuenta que actualmente estoy utilizando una curva de rendimiento, sin embargo, cuando se utiliza un flatforward de -0,00478 obtengo resultados similares.

Sin embargo, el uso del desplazamiento da lugar a un resultado poco realista para a (a=0,0000389, sigma=0,0222).

He estado buscando mucho una respuesta, pero no puedo encontrar información decente para situaciones con una tasa negativa. Si alguien pudiera explicar el uso de un desplazamiento o hacer una comprobación rápida en mi código para ver si algo está mal sería tan útil.

helper = ql.SwaptionHelper(
        ql.Period(int(maturity), ql.Years),
        ql.Period(int(tenor), ql.Years),
        volatility,
        index,
        fixedLegTenor,
        fixedLegDayCounter,
        floatingLegDayCounter,
        term_structure,
        ql.BlackCalibrationHelper.RelativePriceError,
        ql.nullDouble(),
        1.0,
        ql.ShiftedLognormal,
        0.05 #shift to make rates non-negative
    )

Código completo:

import csv
from QuantLib.QuantLib import SwaptionHelper
import matplotlib.pyplot as plt
import pandas as pd
import QuantLib as ql

def load_csv_input():
    zero_curve = pd.read_csv('ZeroCurve.csv', delimiter=';')
    dates = []
    rates = []

    for i in range(0,len(zero_curve)):
        rates.append(float(zero_curve['Rate'][i]))
        dates.append(ql.Date(int(zero_curve['Mat_Day'][i]),int(zero_curve['Mat_Month'][i]),int(zero_curve['Mat_Year'][i])))

    return dates, rates

# Read the swaption volatilities from the csv file.
swaption_vols = pd.read_csv('SwaptionVol.csv', delimiter=';', index_col=0)
dates, rates = load_csv_input()

curve = ql.ZeroCurve(dates, rates, ql.Actual365Fixed())
term_structure = ql.YieldTermStructureHandle(curve)

model = ql.HullWhite(term_structure)
#engine = ql.TreeSwaptionEngine(model, 25)
engine = ql.JamshidianSwaptionEngine(model)
#engine = ql.G2SwaptionEngine(model, 10, 400)

index = ql.Euribor1Y(term_structure)
fixedLegTenor = ql.Period('1Y')
fixedLegDayCounter = ql.Actual360()
floatingLegDayCounter = ql.Actual360()

swaptions = []
ql.Settings.instance().evaluationDate = ql.Date(1, 10, 2020)
for maturity in swaption_vols.index:
    for tenor in swaption_vols.columns:
        volatility = ql.QuoteHandle(ql.SimpleQuote(swaption_vols.at[maturity,tenor]))
        helper = ql.SwaptionHelper(
            ql.Period(int(maturity), ql.Years),
            ql.Period(int(tenor), ql.Years),
            volatility,
            index,
            fixedLegTenor,
            fixedLegDayCounter,
            floatingLegDayCounter,
            term_structure,
            ql.BlackCalibrationHelper.RelativePriceError,
            ql.nullDouble(),
            1.0,
            ql.ShiftedLognormal,
            0.2 #shift to make rates non-negative
        )
        helper.setPricingEngine(engine)
        swaptions.append(helper)

optimization_method = ql.LevenbergMarquardt(1.0e-8,1.0e-8,1.0e-8)
end_criteria = ql.EndCriteria(500000, 1000, 1e-6, 1e-8, 1e-8)
model.calibrate(swaptions, optimization_method, end_criteria)
params = model.params()
print(params)

Actualización

He cambiado el tipo de volatilidad de lognormal a normal, ya que los datos contienen volatilidades normales. Así que esto debería ser capaz de trabajar con los valores negativos.

Ahora me encuentro con un problema en el que la calibración sólo funciona si utilizo una pequeña selección de volatilidades. Mi matriz de volatilidad completa es de 10x10 (1, 2, 3, 4, 5, 10, 15, 20, 25, y 30Y vencimiento y tenores). Sin embargo, si utilizo más de la primera 5x5 obtengo un error de "root sin corchetes".

0 votos

No entiendo tu comentario "ya que la distribución lognormal no puede ser negativa". La tasa corta en el modelo HW es normal y puede ser negativa. ¿Lognormal son los precios de los bonos y estos son positivos?

0 votos

Tienes razón, el modelo HW es efectivamente normal. Sin embargo, por lo que sé, los cálculos de las swaptions incluyen una distribución lognormal.

0 votos

Supongo que primero comprobaré la plausibilidad de los parámetros calculados de Hull White construyendo la superficie de volatilidad implícita

3voto

Chris Mc Puntos 31

Cuando se construye un SwaptionHelper, hay que decirle a QuantLib qué tipo de volatilidad se está introduciendo. Hay tres opciones: Black Vol, Shifted Black Vol y Normal Vol.

Dado que no tiene vol negro para la mayor parte de la superficie de swaption (EUR) debido a los forwards negativos, puede utilizar vol negro desplazado o vol normal.

¡En el ejemplo estás usando vol negro desplazado pero tienes un desplazamiento del 20%! Sus vols desplazados deberían tener un valor de desplazamiento respectivo, pero dudo que sea del 20%. ICAP, por ejemplo, cotiza vols negros desplazados con un desplazamiento del 2% para los tipos de euros.

Sin hacer nada de código, supongo que si corriges tu desplazamiento al valor adecuado obtendrás mejores resultados. Puedes comparar los valores del modelo con los del mercado para tus ayudantes.

0 votos

Hola David, muchas gracias por tu ayuda. La verdad es que he conseguido comprobar que mis datos están formados por volatilidades normales. Corrígeme si me equivoco pero creo que eso debería significar que puedo eliminar el desplazamiento ya que la normal puede manejar valores negativos. He actualizado mi script para mostrar las diferencias entre los valores del modelo y del mercado.

0 votos

Correcto, sólo use ql.Normal en lugar de ql.ShiftedLognormal como el tipo de volatilidad y no introduzca ningún desplazamiento...

0 votos

¡Gracias David! Acabo de escribir una breve actualización de mi pregunta. Sigo recibiendo un error de "root no corchetes" cuando se utiliza la superficie de volatilidad completa (10x10). Sólo para superficies más pequeñas (5x5) la calibración da un resultado.

Finanhelp.com

FinanHelp es una comunidad para personas con conocimientos de economía y finanzas, o quiere aprender. Puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X