7 votos

Replicación de la valoración simple de swaps de tipos de interés de QuantLib

Estoy aprendiendo QuantLib-Python y tratando de replicar una valoración de Swap de Tipos de Interés en una curva personalizada construida pasando listas de fechas y factores de descuento. Vea mi código a continuación

import QuantLib as ql

today = ql.Date(31, ql.May, 2023)
ql.Settings.instance().setEvaluationDate(today)

dates = [ql.Date(31, ql.May, 2023), ql.Date(28, ql.August, 2023), ql.Date(27, ql.November, 2023), 
                 ql.Date(26, ql.February, 2024), ql.Date(27, ql.May, 2024), ql.Date(26, ql.May, 2025), ql.Date(26, ql.May, 2026)]

dfs = [1.00, 0.980626530240871, 0.961865563098355, 0.942995352345942, 0.923889577251464, 0.845638911735274, 0.770640534908645]

dayCounter = ql.ActualActual(ql.ActualActual.ISDA)
calendar = ql.UnitedStates()

curve = ql.DiscountCurve(dates, dfs, dayCounter, calendar)
curveHandle = ql.YieldTermStructureHandle(curve)

start = today
maturity = calendar.advance(start, ql.Period('1Y'))
fix_schedule = ql.MakeSchedule(start, maturity, ql.Period('1Y'))
float_schedule = ql.MakeSchedule(start, maturity, ql.Period('3M'))

customIndex = ql.IborIndex('index', ql.Period('3M'), 0, ql.USDCurrency(), ql.UnitedStates(), ql.ModifiedFollowing, True, dayCounter, curveHandle)
customIndex.addFixing(ql.Date(30, ql.May, 2023), 0.075)

notional = 100000000
swap = ql.VanillaSwap(ql.VanillaSwap.Payer, notional, fix_schedule, 0.0833, dayCounter, 
                     float_schedule, customIndex, 0, dayCounter)
swap_engine = ql.DiscountingSwapEngine(curveHandle)
swap.setPricingEngine(swap_engine)

print(swap.NPV())

Existe la posibilidad de imprimir los flujos de caja no descontados de los tramos fijo y flotante mediante

print("Net Present Value: {0}".format(swap.NPV()))
print()
print("Fixed leg cashflows:")
for i, cf in enumerate(swap.leg(0)):
    print("%2d    %-18s  %10.2f"%(i+1, cf.date(), cf.amount()))
print()
print("Floating leg cashflows:")
for i, cf in enumerate(swap.leg(1)):
    print("%2d    %-18s  %10.2f"%(i+1, cf.date(), cf.amount()))

El Valor Actual Neto de un swap debería venir dado por la diferencia de los flujos de caja descontados de los tramos flotante y fijo. He intentado reproducir los cálculos manualmente mediante

fwd1 = curve.forwardRate(ql.Date(31, 5, 2023), ql.Date(31, 8, 2023), dayCounter, ql.Continuous).rate()
df1 = curve.discount(ql.Date(31, 8, 2023))
tau1 = dayCounter.yearFraction(ql.Date(31, 5, 2023), ql.Date(31, 8, 2023))
cashflow1 = df1 * notional * fwd1 * tau1
print(cashflow1)
print()

fwd2 = curve.forwardRate(ql.Date(1, 9, 2023), ql.Date(30, 11, 2023), dayCounter, ql.Simple).rate()
df2 = curve.discount(ql.Date(30, 11, 2023))
tau2 = dayCounter.yearFraction(ql.Date(1, 9, 2023), ql.Date(30, 11, 2023))
cashflow2 = df2 * notional * fwd2 * tau2
print(cashflow2)
print()

fwd3 = curve.forwardRate(ql.Date(1, 12, 2023), ql.Date(29, 2, 2024), dayCounter, ql.Simple).rate()
df3 = curve.discount(ql.Date(29, 2, 2024))
tau3 = dayCounter.yearFraction(ql.Date(1, 12, 2023), ql.Date(29, 2, 2024))
cashflow3 = df3 * notional * fwd3 * tau3
print(cashflow3)
print()

fwd4 = curve.forwardRate(ql.Date(1, 3, 2024), ql.Date(31, 5, 2024), dayCounter, ql.Simple).rate()
df4 = curve.discount(ql.Date(31, 5, 2024))
tau4 = dayCounter.yearFraction(ql.Date(1, 3, 2024), ql.Date(31, 5, 2024))
cashflow4 = df4 * notional * fwd4 * tau4
print(cashflow4)
print()

floatCashFlow = cashflow1 + cashflow2 + cashflow3 + cashflow4
print(floatCashFlow)
print()

df = df4
fixRate = 0.0833
tau = dayCounter.yearFraction(ql.Date(31, 5, 2023), ql.Date(31, 5, 2024))
fixCashFlow = df * notional * fixRate * tau
print(fixCashFlow)
print()

valueSwap = floatCashFlow - fixCashFlow
print(valueSwap)

y obtuvimos un VAN asombrosamente diferente. ¿Puede alguien mostrarme cómo llegar con precisión a una valoración VAN incorporada utilizando métodos incorporados para llamar a los factores de descuento y a los tipos de interés a plazo? Supongo que la diferencia se esconde en algún punto de los calendarios de pagos, pero no he conseguido hacer coincidir los números jugando con las fechas.

3voto

user35980 Puntos 1

1/ el atributo cf.amount() devuelve flujos de caja no descontados, no PVd

2/ la llamada a curve.forwardRate() devuelve un tipo cero de capitalización continua, utilice customIndex(fixing date) para obtener el ibor fwd correspondiente

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