7 votos

Utilizar QuantLib Python para calcular el roll-down de un swap

Me gustaría utilizar QuantLib Python para calcular el roll-down a 6 meses de un swap a 5 años.

Creo que el cálculo que tengo que hacer es el siguiente:

$Rolldown=r_{0,5Y}-r_{0,4.5Y}$

Dónde $r_{0,5Y}$ es el tipo de interés al contado a 5 años y $r_{0,4.5Y}$ es el tipo de interés al contado de 4 años.

Puede encontrar la definición de "roll-down" en los siguientes enlaces:

https://e-markets.nordea.com/api/research/attachment/2796

http://www.gioa.us/presentations/2012/4-Rajappa.pdf

Por favor, dígame si mi ecuación es correcta.

Asumiendo que mi ecuación es correcta, lo siguiente es cómo intenté calcular el roll-down del swap usando QuantLib Python:

from QuantLib import *

# global data
calendar = TARGET()
todaysDate = Date(6,November,2001);
Settings.instance().evaluationDate = todaysDate
settlementDate = Date(8,November,2001);

# market quotes
deposits = { (1,Weeks): 0.0382,
             (1,Months): 0.0372,
             (3,Months): 0.0363,
             (6,Months): 0.0353,
             (9,Months): 0.0348,
             (1,Years): 0.0345 }

swaps = { (2,Years): 0.037125,
          (3,Years): 0.0398,
          (5,Years): 0.0443,
          (10,Years): 0.05165,
          (15,Years): 0.055175 }

# convert them to Quote objects
for n,unit in deposits.keys():
    deposits[(n,unit)] = SimpleQuote(deposits[(n,unit)])
for n,unit in swaps.keys():
    swaps[(n,unit)] = SimpleQuote(swaps[(n,unit)])

# build rate helpers

dayCounter = Actual360()
settlementDays = 2
depositHelpers = [ DepositRateHelper(QuoteHandle(deposits[(n,unit)]),
                                     Period(n,unit), settlementDays,
                                     calendar, ModifiedFollowing,
                                     False, dayCounter)
                   for n, unit in [(1,Weeks),(1,Months),(3,Months),
                                   (6,Months),(9,Months),(1,Years)] ]

fixedLegFrequency = Annual
fixedLegTenor = Period(1,Years)
fixedLegAdjustment = Unadjusted
fixedLegDayCounter = Thirty360()
floatingLegFrequency = Semiannual
floatingLegTenor = Period(6,Months)
floatingLegAdjustment = ModifiedFollowing
swapHelpers = [ SwapRateHelper(QuoteHandle(swaps[(n,unit)]),
                               Period(n,unit), calendar,
                               fixedLegFrequency, fixedLegAdjustment,
                               fixedLegDayCounter, Euribor6M())
                for n, unit in swaps.keys() ]

# term structure handles

discountTermStructure = RelinkableYieldTermStructureHandle()
forecastTermStructure = RelinkableYieldTermStructureHandle()

# term-structure construction

helpers = depositHelpers + swapHelpers
depoSwapCurve = PiecewiseFlatForward(settlementDate, helpers, Actual360())

swapEngine = DiscountingSwapEngine(discountTermStructure)

# 5Y Swap 

nominal = 1000000
maturity1 = calendar.advance(settlementDate,5,Years)

fixedLegFrequency = Annual
fixedLegAdjustment = Unadjusted
fixedLegDayCounter = Thirty360()
fixedRate = 0.04

floatingLegFrequency = Semiannual
spread = 0.0
fixingDays = 2
index = Euribor6M(forecastTermStructure)
floatingLegAdjustment = ModifiedFollowing
floatingLegDayCounter = index.dayCounter()

fixedSchedule1 = Schedule(settlementDate, maturity1,
                         fixedLegTenor, calendar,
                         fixedLegAdjustment, fixedLegAdjustment,
                         DateGeneration.Forward, False)
floatingSchedule1 = Schedule(settlementDate, maturity1,
                            floatingLegTenor, calendar,
                            floatingLegAdjustment, floatingLegAdjustment,
                            DateGeneration.Forward, False)

spot1 = VanillaSwap(VanillaSwap.Receiver, nominal,
                   fixedSchedule1, fixedRate, fixedLegDayCounter,
                   floatingSchedule1, index, spread,
                   floatingLegDayCounter)
spot1.setPricingEngine(swapEngine)

# 4.5Y Swap 

rolldown_period = Period(6, Months)

maturity2 = calendar.advance(maturity1, -rolldown_period)

fixedSchedule2 = Schedule(settlementDate, maturity2,
                         fixedLegTenor, calendar,
                         fixedLegAdjustment, fixedLegAdjustment,
                         DateGeneration.Forward, False)
floatingSchedule2 = Schedule(settlementDate, maturity2,
                            floatingLegTenor, calendar,
                            floatingLegAdjustment, floatingLegAdjustment,
                            DateGeneration.Forward, False)

spot2 = VanillaSwap(VanillaSwap.Receiver, nominal,
                   fixedSchedule2, fixedRate, fixedLegDayCounter,
                   floatingSchedule2, index, spread,
                   floatingLegDayCounter)
spot2.setPricingEngine(swapEngine)

# price on two different evaluation dates

discountTermStructure.linkTo(depoSwapCurve)
forecastTermStructure.linkTo(depoSwapCurve)

spot5Y = spot1.fairRate()
spot4Y6M = spot2.fairRate()

print('5Y spot rate')
print(spot5Y)
print('4.5Y spot rate')
print(spot4Y6M)
print('6 month roll down')
print(spot5Y - spot4Y6M)

¿Estoy en lo cierto en cuanto a cómo utilizar QuantLib Python para calcular el roll-down a 6 meses de un swap a 5 años? ¿Es necesario crear 2 objetos de swap como he hecho para calcular el roll-down del swap?

5voto

Brad Tutterow Puntos 5628

No, creo que te equivocas de figura. La idea general puede funcionar, pero hay que cambiar la construcción del segundo intercambio. (O se puede utilizar sólo un intercambio; Voy a llegar a eso más adelante).

Según he leído en la documentación que has enlazado, la idea del segundo canje sería modelar el primer canje una vez transcurridos 6 meses. Sin embargo, vamos a ver los dos swaps. Definiré una función de ayuda para ver sus cupones:

def show_cashflows(leg):
    for c in leg:
        print '%20s | %s | %.4f%%' % (c.date(), c.amount(),
                                      as_coupon(c).rate()*100)

Esto es lo que paga el tramo fijo del swap a 5 años...

show_cashflows(spot1.fixedLeg())

 November 8th, 2002 | 40000.0 | 4.0000%
November 10th, 2003 | 40000.0 | 4.0000%
 November 8th, 2004 | 40000.0 | 4.0000%
 November 8th, 2005 | 40000.0 | 4.0000%
 November 8th, 2006 | 40000.0 | 4.0000%

...y aquí está la pierna flotante:

show_cashflows(spot1.floatingLeg())

      May 8th, 2002 | 17748.0555555 | 3.5300%
 November 8th, 2002 | 16930.6254304 | 3.3125%
      May 8th, 2003 | 19219.7194265 | 3.8227%
November 10th, 2003 | 19755.8616671 | 3.8237%
     May 10th, 2004 | 22498.6599297 | 4.4503%
 November 8th, 2004 | 22498.6599297 | 4.4503%
      May 9th, 2005 | 25550.9745581 | 5.0540%
 November 8th, 2005 | 25693.1528497 | 5.0544%
      May 8th, 2006 | 25408.8159749 | 5.0537%
 November 8th, 2006 | 25835.3508522 | 5.0547%

El segundo canje debería dar los mismos cupones, vistos desde una fecha de evaluación 6 meses después. El tramo flotante tiene el mismo aspecto, salvo que un cupón ha desaparecido:

show_cashflows(spot2.floatingLeg())

      May 8th, 2002 | 17748.0555555 | 3.5300%
 November 8th, 2002 | 16930.6254304 | 3.3125%
      May 8th, 2003 | 19219.7194265 | 3.8227%
November 10th, 2003 | 19755.8616671 | 3.8237%
     May 10th, 2004 | 22498.6599297 | 4.4503%
 November 8th, 2004 | 22498.6599297 | 4.4503%
      May 9th, 2005 | 25550.9745581 | 5.0540%
 November 8th, 2005 | 25693.1528497 | 5.0544%
      May 8th, 2006 | 25408.8159749 | 5.0537%

(Parece que el último cupón ha desaparecido, no el primero; pero eso es porque estás asumiendo que la curva se mantiene igual y se desplaza en el tiempo, y por tanto la fijación del segundo cupón dentro de seis meses será igual a la fijación del primer cupón ahora).

Sin embargo, este es el aspecto de la pata fija:

show_cashflows(spot2.fixedLeg())

 November 8th, 2002 | 40000.0 | 4.0000%
November 10th, 2003 | 40000.0 | 4.0000%
 November 8th, 2004 | 40000.0 | 4.0000%
 November 8th, 2005 | 40000.0 | 4.0000%
      May 8th, 2006 | 20000.0 | 4.0000%

Como has creado un calendario de 4,5 años, el último cupón es más corto y le falta la mitad del importe. Esto, por supuesto, hace que el tipo de interés justo se desvíe.

print spot2.fairRate()

0.0434536470094

Tienes un par de maneras de solucionar esto.

En primer lugar, en lugar de crear un swap a 4,5 años, puede crear un swap a 5 años comenzando 6 meses antes:

issue2 = calendar.advance(settlementDate, -rolldown_period)
maturity2 = calendar.advance(issue2, 5, Years)

fixedSchedule2 = Schedule(issue2, maturity2,
                         fixedLegTenor, calendar,
                         fixedLegAdjustment, fixedLegAdjustment,
                         DateGeneration.Forward, False)
floatingSchedule2 = Schedule(issue2, maturity2,
                            floatingLegTenor, calendar,
                            floatingLegAdjustment, floatingLegAdjustment,
                            DateGeneration.Forward, False)

swap3 = VanillaSwap(VanillaSwap.Receiver, nominal,
                    fixedSchedule2, fixedRate, fixedLegDayCounter,
                    floatingSchedule2, index, spread,
                    floatingLegDayCounter)
swap3.setPricingEngine(swapEngine)

Ahora el tramo fijo paga las cantidades correctas...

show_cashflows(swap3.fixedLeg())

     May 8th, 2002 | 40000.0 | 4.0000%
     May 8th, 2003 | 40000.0 | 4.0000%
    May 10th, 2004 | 40000.0 | 4.0000%
     May 9th, 2005 | 40000.0 | 4.0000%
     May 8th, 2006 | 40000.0 | 4.0000%

...y la tasa justa cambia en consecuencia.

print swap3.fairRate()

0.0387677146311

La segunda forma de solucionarlo es, como has sugerido, utilizar un único swap.

Puede construir la estructura de plazos para que se mueva con la fecha de evaluación; en lugar de pasar la fecha de referencia explícitamente, indique a la curva que la fecha de referencia debe ser dos días hábiles después de la fecha de evaluación.

depoSwapCurve = PiecewiseFlatForward(2, TARGET(), helpers, Actual360())

El resto de la inicialización se mantiene igual. Con esta configuración, simplemente se puede pedir la tasa justa del swap spot1 en dos fechas de evaluación diferentes:

Settings.instance().evaluationDate = todaysDate
print spot1.fairRate()

0.0443

Settings.instance().evaluationDate = calendar.advance(todaysDate, 6, Months)
print spot1.fairRate()

0.038847797035

(La tasa justa después de 6 meses es similar, pero no exactamente igual a la calculada de la primera manera. Probablemente se deba a que la curva es un poco diferente debido a que los plazos de los depósitos utilizados para el bootstrapping pueden equivaler a un número diferente de días según la fecha de contado).

4voto

dotnetcoder Puntos 1262

No utilices la pieza de Nordea: no es correcta. Déjenme explicarles por qué;

Si el rolldown es la suposición de que la curva del IRS permanece estática a través del tiempo, entonces para un IRS de 5 años tienes dos componentes; la primera fijación, es decir, la parte del 0,5Y que es conocida, y el IRS fwd del 0,5Y4,5Y, que es flotante y aún no se ha fijado.

Si, a través del rolldown, se supone que el IRS 0,5Y4,5Y convergerá al IRS 0Y4,5Y actual, entonces eso es lo que hay que restar para obtener el número de puntos básicos que se aplican a la parte de riesgo que existe para la extensión del swap 0,5Y4,5Y.

No sé por qué el carry y el rolldown se entienden tan mal y universalmente en las finanzas, pero muchos trabajos de investigación de los bancos son erróneos o, si no son erróneos, están mal concebidos y son demasiado complicados. Si quieres leer detalles precisos sobre los IRS, considera "pricing and trading interest rate derivatives: a pratical guide to swaps" de JHM Darbyshire, y también echa un vistazo a estas otras respuestas...

Cálculo del carry en un swap de tipos de interés

Pregunta sobre el carry puro para dos bonos

pregunta sobre el transporte y el rodaje de una fianza

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