En toda brevedad
- ¿Cuál es la condición de terminación utilizada en el "bootstrapping" de la curva de QuantLib?
- ¿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
- ¿hay algo mal en la configuración de mis instrumentos?
- ¿Qué condición terminal aplica la metodología de "bootstrap" de QuantLib aquí, y
- ¿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.