8 votos

Matemáticas de bonos simples de QuantLib

Soy nuevo en QuantLib y estoy tratando de conseguir que reproduzca algunas matemáticas simples de bonos.

Supongamos que tenemos un bono a 5 años con pagos de cupones anuales de \$5 and face value of \$ 100, y un tipo de interés del 4%. Los cálculos clásicos arrojan que el valor actual del bono es \$104.45. When I try to do this simple example in QuantLib-Python, I get $ 104,70 a pesar de mis intentos de eliminar las convenciones del calendario.

¿Cómo puedo utilizar QuantLib para alinearme con esta simple matemática de bonos?

from QuantLib import *

# Construct yield curve
calc_date = Date(1, 1, 2017)
Settings.instance().evaluationDate = calc_date

spot_dates = [Date(1,1,2017), Date(1,1,2018), Date(1,1,2027)]
spot_rates = [0.0, 0.04, 0.04]

day_count = SimpleDayCounter()
calendar = NullCalendar()
interpolation = Linear()
compounding = Compounded
compounding_frequency = Annual
spot_curve = ZeroCurve(spot_dates, spot_rates, day_count, calendar, interpolation, compounding, compounding_frequency)

spot_curve_handle = YieldTermStructureHandle(spot_curve)

# Construct bond schedule
issue_date = Date(1, 1, 2017)
maturity_date = Date(1, 1, 2022)
tenor = Period(Semiannual)
calendar = NullCalendar()
business_convention = Unadjusted
date_generation = DateGeneration.Backward
month_end = False

schedule = Schedule(issue_date, maturity_date, tenor, calendar, business_convention, business_convention, date_generation, month_end)

# Create FixedRateBond Object

coupon_rate = 0.05
coupons = [coupon_rate]
settlement_days = 0
face_value = 100

fixed_rate_bond = FixedRateBond(settlement_days,
                                face_value,
                                schedule,
                                coupons,
                                day_count)

# Set Valuation engine
bond_engine = DiscountingBondEngine(spot_curve_handle)
fixed_rate_bond.setPricingEngine(bond_engine)

# Calculate present value
value = fixed_rate_bond.NPV()
print(value)

0 votos

No veo nada evidente. Deberías imprimir todos los flujos de caja y comprobar cada uno de ellos.

0 votos

¿Cómo lo haría? Mi corazonada es que la curva de rendimiento lo está estropeando.

1 votos

104,45 es el precio del bono con cupones anuales, pero su código fija el precio del bono asumiendo cupones semestrales. ¿No es de ahí de donde viene la diferencia?

13voto

Brad Tutterow Puntos 5628

Para empezar, como sugirió el estudiante T, puedes comprobar que los flujos de caja son los que esperas:

for c in fixed_rate_bond.cashflows():
    print '%20s %12f' % (c.date(), c.amount())

      July 1st, 2017     2.500000
   January 1st, 2018     2.500000
      July 1st, 2018     2.500000
   January 1st, 2019     2.500000
      July 1st, 2019     2.500000
   January 1st, 2020     2.500000
      July 1st, 2020     2.500000
   January 1st, 2021     2.500000
      July 1st, 2021     2.500000
   January 1st, 2022     2.500000
   January 1st, 2022   100.000000

Parecen estar bien, así que el problema debe estar en la curva de descuento. Un problema es que lo estás instanciando como:

spot_dates = [Date(1,1,2017), Date(1,1,2018), Date(1,1,2027)]
spot_rates = [0.0, 0.04, 0.04]
...

Pero eso significa que los tipos cero se interpolarán entre 0,0 y 0,04 durante el primer año y, por supuesto, eso da factores de descuento erróneos; por lo que debe utilizar

spot_dates = [Date(1,1,2017), Date(1,1,2018), Date(1,1,2027)]
spot_rates = [0.04, 0.04, 0.04]

en su lugar. Eso da un precio de \$104.67, though, which is still not what you want. The next issue is that you're initializing the curve with a 4% rate and an annual compounding frequency; this gives you discount factors equal to $ B(T) = 1/(1+R)^T$, lo que puedes comprobar:

for i, c in enumerate(fixed_rate_bond.cashflows()):
    T = day_count.yearFraction(calc_date, c.date())
    B = 1/math.pow(1.04, T)
    print '%20s %12f %12f %12f %12f' % (c.date(), c.amount(), T,
                                        B, spot_curve.discount(c.date()))

      July 1st, 2017     2.500000     0.500000     0.980581     0.980581
   January 1st, 2018     2.500000     1.000000     0.961538     0.961538
      July 1st, 2018     2.500000     1.500000     0.942866     0.942866
   January 1st, 2019     2.500000     2.000000     0.924556     0.924556
      July 1st, 2019     2.500000     2.500000     0.906602     0.906602
   January 1st, 2020     2.500000     3.000000     0.888996     0.888996
      July 1st, 2020     2.500000     3.500000     0.871733     0.871733
   January 1st, 2021     2.500000     4.000000     0.854804     0.854804
      July 1st, 2021     2.500000     4.500000     0.838204     0.838204
   January 1st, 2022     2.500000     5.000000     0.821927     0.821927
   January 1st, 2022   100.000000     5.000000     0.821927     0.821927

Dado que tiene cupones semestrales, probablemente también quiera utilizar una frecuencia de capitalización semestral. Eso le da factores de descuento $B(t) = 1/\left(1+\frac{R}{2}\right)^{2T}$ :

for i, c in enumerate(fixed_rate_bond.cashflows()):
    T = day_count.yearFraction(calc_date, c.date())
    B = 1/math.pow(1.02, 2*T)
    print '%20s %12f %12f %12f %12f' % (c.date(), c.amount(), T,
                                        B, spot_curve.discount(c.date()))

      July 1st, 2017     2.500000     0.500000     0.980392     0.980392
   January 1st, 2018     2.500000     1.000000     0.961169     0.961169
      July 1st, 2018     2.500000     1.500000     0.942322     0.942322
   January 1st, 2019     2.500000     2.000000     0.923845     0.923845
      July 1st, 2019     2.500000     2.500000     0.905731     0.905731
   January 1st, 2020     2.500000     3.000000     0.887971     0.887971
      July 1st, 2020     2.500000     3.500000     0.870560     0.870560
   January 1st, 2021     2.500000     4.000000     0.853490     0.853490
      July 1st, 2021     2.500000     4.500000     0.836755     0.836755
   January 1st, 2022     2.500000     5.000000     0.820348     0.820348
   January 1st, 2022   100.000000     5.000000     0.820348     0.820348

Con esta nueva corrección, el precio es de 104,49 dólares, lo que sigue siendo 4 puntos básicos más alto de lo esperado. Pero llegados a este punto, quizá también quieras revisar tus cálculos clásicos...

0 votos

Vaya, gracias, Luigi. Supongamos que quiero cambiar la fecha de evaluación. Si cambio calc date el precio sigue correspondiendo a Date(1,1,2017) . Si comento Settings.instance().evaluationDate = calc_date el programa no se rompe. ¿Cómo puedo encontrar el VAN del bono en Date(1,1,2018) .

0 votos

La curva de rendimiento sigue descontando a su primera fecha (1/1/17). Puede reconstruir la curva, o puede utilizar dirtyPrice en lugar de NPV . El primero tendrá en cuenta la fecha de evaluación y los días de liquidación.

0 votos

Luigi tiene razón: cpage ha calculado \$104.45 for the price of the bond with annual compounding. This answer's final result of \$ 104,49 es lo exacto para un bono con capitalización semestral.

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