5 votos

Bootstrap con QuantLib: Swap justo o NPV cero

En toda brevedad

  1. ¿Cuál es la condición de terminación utilizada en el "bootstrapping" de la curva de QuantLib?
  2. ¿Puedo modificar esta configuración según mis necesidades, por ejemplo, puedo ajustarla para obtener una mayor precisión?

Antecedentes

Cuando se "bootstrapean" curvas de descuento y proyección, una condición necesaria es que las curvas implícitas valoren los instrumentos de referencia al precio del mercado, es decir, valorar los instrumentos que fueron suministrados originalmente al algoritmo de construcción de la curva lo más cerca posible de sus precios de entrada. Con una curva que utiliza un mecanismo de interpolación local ("bootstrap" iterativo), yo asumiría que todos los instrumentos de referencia se vuelven a evaluar a precios de mercado con un error mínimo, es decir,

Suponga un mecanismo de curva/"bootstrap" que utiliza interpolación local, por ejemplo, interpolación lineal de factores de descuento logarítmicos. Dada una comilla de mercado, y dada la curva construida hasta ahora, el mecanismo de "bootstrap" selecciona el instrumento de referencia "siguiente" y actualiza la curva desplazando una tasa en un punto de nodo hasta que el instrumento de referencia se valora al precio de mercado.

Mi observación y pregunta

Revalué mis instrumentos de referencia en la curva que acabo de "bootstrapear" y descubro que estos no se valoran tan cerca de cero como hubiera asumido, vea el ejemplo mínimo a continuación:

import QuantLib as ql

today = ql.Date(23,ql.June,2020)
ql.Settings.instance().evaluationDate = today
eonia = ql.Eonia()

Suponiendo comillas "flat" de OIS en el mercado al 1% por periodo y recolectando los ayudantes de construcción de la curva:

quotes = {str(k)+'Y' : ql.SimpleQuote(0.01) for k in range(1,21)}
ois_helpers = []    
for k,v in quotes.items():
    ois_helpers.append(ql.OISRateHelper(
        settlementDays = 2, 
        tenor = ql.Period(k), 
        rate = ql.QuoteHandle(v), 
        index = eonia, 
        telescopicValueDates =True))

eonia_curve = ql.PiecewiseLinearZero(2,ql.TARGET(),ois_helpers,ql.Actual365Fixed())
val_curve   = ql.YieldTermStructureHandle(eonia_curve)

Creando otro eonia_index, esta vez con una curva de proyección adjunta; y un motor de valoración:

eonia_index = ql.Eonia(val_curve)
swap_engine = ql.DiscountingSwapEngine(val_curve)

Ahora estoy configurando los instrumentos de referencia como instrumentos "reales" y obteniendo sus VP. Tenga en cuenta que asumo un nominal de 1 millón de moneda:

print('PLAZO \t VP \t tasa justa% \t tasa justa% + spread justo%')
for p in quotes.keys():
    schedule = ql.MakeSchedule(today, today + ql.Period(p), ql.Period('1d'), calendar=ql.TARGET())
    fixedRate = quotes[p].value()
    ois_swap = ql.OvernightIndexedSwap(
        ql.OvernightIndexedSwap.Receiver, 
        1E6, 
        schedule, 
        fixedRate, 
        ql.Actual360(),
        eonia_index)
    ois_swap.setPricingEngine(swap_engine)
    print(p + "\t" + 
        str(round(ois_swap.NPV(),2)) + " \t " + 
        str(round(ois_swap.fairRate()*100,4)) + "\t\t" +
        str(100*(ois_swap.fairRate()+ois_swap.fairSpread())))

Resultando en

PLAZO    VP     tasa justa%   tasa justa% + spread justo%
1Y  50.25    0.995      1.0
2Y  100.55   0.995      1.0
3Y  149.95   0.995      1.0
4Y  199.23   0.995      1.0
5Y  247.63   0.995      1.0
6Y  295.67   0.995      1.0
7Y  343.23   0.995      1.0
8Y  390.7    0.995      1.0
9Y  437.44   0.995      1.0
10Y 483.46   0.995      1    
11Y 529.01   0.995      1.0
12Y 574.48   0.995      1.0
13Y 619.49   0.995      1.0
14Y 663.69   0.995      1.0
15Y 707.68   0.995      1.0
16Y 751.11   0.995      1.0
17Y 794.1    0.995      1.0
18Y 836.66   0.995      1.0
19Y 879.03   0.995      1.0
20Y 920.98   0.995      1.0

Claramente, la tasa justa implícita no es exactamente del 1%, pero la tasa justa implícita más el spread implícito da el 1%. Además, el VP de cada swap es cercano, pero no 'muy' cercano a cero.

Me pregunto, si

  1. ¿hay algo mal en la configuración de mis instrumentos?
  2. ¿Qué condición terminal aplica la metodología de "bootstrap" de QuantLib aquí, y
  3. ¿Puedo establecer límites más estrictos para ese mecanismo?

Gracias por cualquier aporte / pensamiento / orientación.

Mi configuración

He construido la curva de descuento siguiendo el libro de cocina de QuantLib Python de Luigi y Goutham a partir del 1 de junio de 2019; estoy utilizando el SWIG de QuantLib Python; versión 1.19.

6voto

Chris Mc Puntos 31

El problema es que no estás cotizando lo mismo, y por dos razones:

  1. Los instrumentos de vainilla que estás cotizando deberían comenzar en la fecha spot y tener una madurez con esa fecha como referencia
  2. La frecuencia de la pata fija en el swap OIS debería ser anual.

Si cambias tu código a:

print('TENOR \t PV \t tasa_justa% \t tasa_justa% + spread_justo%')
calendario = ql.TARGET()
for p in quotes.keys():
    start = calendario.advance(today, 2, ql.Days)
    calendario = ql.MakeSchedule(start, calendario.advance(start, ql.Period(p)), ql.Period('1Y'), calendar=calendario)
    tasa_fija = quotes[p].value()
    swap_ois = ql.OvernightIndexedSwap(
        ql.OvernightIndexedSwap.Receiver, 
        1E6, 
        calendario, 
        tasa_fija, 
        ql.Actual360(),
        eonia_index)

    swap_ois.setPricingEngine(swap_engine)
    print(p + "\t" + 
        str(round(swap_ois.NPV(),2)) + " \t \t" + 
        str(round(swap_ois.fairRate()*100,4)) + "\t\t" +
        str(100*(swap_ois.fairRate()+swap_ois.fairSpread())))

Obtendrás la salida correcta.

Esto también se puede verificar extrayendo los instrumentos de los ayudantes:

print('TENOR \t PV \t tasa_justa% \t tasa_justa% + spread_justo%')
for idx, p in enumerate(quotes.keys()):
    swap_ois = ois_helpers[idx].swap()
    swap_ois.setPricingEngine(swap_engine)
    print(p + "\t" + 
        str(round(swap_ois.NPV(),2)) + " \t \t" + 
        str(round(swap_ois.fairRate()*100,4)) + "\t\t" +
        str(100*(swap_ois.fairRate()+swap_ois.fairSpread())))

0 votos

¡Muchas gracias, David!

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