4 votos

El bootstrapping de la curva de rendimientos no produce la fecha de inicio del flujo de caja esperado

Actualmente estoy trabajando en la construcción de una curva de rendimiento utilizando swaps OIS. Sin embargo, estoy encontrando un problema con la fecha de inicio del flujo de caja que estoy luchando por entender. Aquí está una versión simplificada de mi código:

Ejemplo reproducible

import QuantLib as ql

calculation_date = ql.Date().todaysDate() #When I posted this on Quant exchange the date was 25/5/2023

ql.Settings.instance().evaluationDate = calculation_date

index = ql.OvernightIndex("USD Overnight Index", 2, ql.USDCurrency(), ql.UnitedStates(ql.UnitedStates.Settlement), ql.Actual360())

swaps = {
    ql.Period("1W"): 0.05064,
    ql.Period("2W"): 0.05067,
    ql.Period("3W"): 0.05072,
    ql.Period("1M"): 0.051021000000000004,
    ql.Period("2M"): 0.051391,
    ql.Period("3M"): 0.051745,
    ql.Period("4M"): 0.05194,
    ql.Period("5M"): 0.051980000000000005,
    ql.Period("6M"): 0.051820000000000005,
    ql.Period("7M"): 0.051584000000000005,
    ql.Period("8M"): 0.05131,
    ql.Period("9M"): 0.050924,
    ql.Period("10M"): 0.050603999999999996,
    ql.Period("11M"): 0.050121,
    ql.Period("12M"): 0.049550000000000004,
    ql.Period("18M"): 0.04558500000000001,
    ql.Period("2Y"): 0.042630999999999995,
    ql.Period("3Y"): 0.038952,
    ql.Period("4Y"): 0.036976,
    ql.Period("5Y"): 0.035919,
    ql.Period("6Y"): 0.03535,
    ql.Period("7Y"): 0.034998,
    ql.Period("8Y"): 0.034808,
    ql.Period("9Y"): 0.034738000000000005,
    ql.Period("10Y"): 0.034712,
    ql.Period("12Y"): 0.034801,
    ql.Period("15Y"): 0.034923,
    ql.Period("20Y"): 0.034662,
    ql.Period("25Y"): 0.03375,
    ql.Period("30Y"): 0.032826,
    ql.Period("40Y"): 0.030834999999999998,
    ql.Period("50Y"): 0.02896
}

rate_helpers = []

for tenor, rate in swaps.items():
    helper = ql.OISRateHelper(2, tenor, ql.QuoteHandle(ql.SimpleQuote(rate)), index)
    rate_helpers.append(helper)

yts = ql.RelinkableYieldTermStructureHandle()
curve = ql.PiecewiseSplineCubicDiscount(calculation_date, rate_helpers, ql.Actual360())
yts.linkTo(curve)

index = index.clone(yts)

engine = ql.DiscountingSwapEngine(yts)

print("maturity |  market  |   model")
for tenor, rate in swaps.items():
    schedule = ql.Schedule(calculation_date, 
                        calculation_date + tenor, 
                        ql.Period('1D'), 
                        ql.UnitedStates(ql.UnitedStates.GovernmentBond), 
                        ql.ModifiedFollowing, 
                        ql.ModifiedFollowing, 
                        ql.DateGeneration.Forward, 
                        False)
    swap = ql.OvernightIndexedSwap(ql.OvernightIndexedSwap.Payer, 
                                1.0, 
                                schedule, 
                                0.01, 
                                ql.Actual360(), 
                                index)

    swap.setPricingEngine(engine)

    print(f"   {tenor}    | {rate:.6f} | {swap.fairRate():.6f}")

El problema al que me enfrento es que la fecha de inicio del flujo de caja de los swaps aparece dos días antes de la fecha de evaluación (23 de mayo de 2023 y fecha de evaluación 25 de mayo de 2023), lo que no se ajusta a mis expectativas. He establecido la fecha de evaluación como la fecha actual en mi código, por lo que la fecha de inicio del flujo de caja debería estar en la fecha de evaluación o después de ella.

  File "AAA", line 173, in build_curve_v2
    print(f"   {tenor}    | {rate:.6f} | {swap.fairRate():.6f}")
                                          ^^^^^^^^^^^^^^^
  File "AAA", line 12, in <module>
    bootstrap.Curve_Build()
RuntimeError: 2nd leg: Missing USD Overnight IndexSN Actual/360 fixing for May 23rd, 2023

Sospecho que puede haber algún error en mi método de bootstrapping o en la forma en que construyo la curva de rendimiento. Cualquier orientación o sugerencia sobre cómo resolver este problema sería muy apreciada.


Una posible explicación : Cuando un OvernightIndexedSwap se crea el calendario del tramo flotante, cuya primera fecha es T+2 días antes del inicio del swap, siendo T el inicio del swap. En mi caso, si el swap comienza en la fecha de evaluación del 25 de mayo de 2023, el calendario del tramo flotante comenzará el 23 de mayo de 2023. En consecuencia, es necesario fijar el tipo a un día el 23 de mayo de 2023.

5voto

Brad Tutterow Puntos 5628

Hay algunos problemas de coherencia.

Una es que estás pasando 2 días de fijación a la ql.OvernightIndex constructor. De esta forma, el horario comienza correctamente en la fecha de cálculo, pero el índice buscará fijaciones dos días antes. Yo usaría 0 días en su lugar.

Otra es que, cuando estás creando los ayudantes, estás pasando 2 días de liquidación. Esto significa que su horario comenzará 2 días después de la fecha de cálculo. Por coherencia, dado que desea reproducir estos tipos, necesita 0 aquí también (o podría mantener el 2, pero en este caso los calendarios de los swaps también tendrán que empezar dos días hábiles después de la fecha de cálculo).

Por último, cuando creas los swaps al final, estás pasando un tenor de 1D a la programación. Eso significaría pagos cada día. En cambio, la tasa se compone cada día, pero los pagos son anuales, por lo que necesitas pasar 1Y en su lugar.

Con estos cambios, obtengo las mismas tarifas.

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