简体   繁体   中英

Pricing an Amortizing Floating Rate Bond using QuantLib and Python

I am trying to price an amortizing floating rate bond using QuantLib Python.

Below is my code:

notional = [3640875000, 3640875000, 3640875000, 3640875000, 3640875000, 3640875000, 3640875000, 3640875000, 3640875000, 3380812500, 3120750000, 2860687500, 2600625000, 2340562500, 2080500000, 1820437500, 1560375000, 1300312500, 1040250000, 780187500, 520125000, 260062500]
tenor = ql.Semiannual
calendar = ql.UnitedStates()
valuationDate = ql.Date(30, 6, 2020)
ql.Settings.instance().evaluationDate = valuationDate
issueDate = ql.Date(1, 2, 2020)
maturityDate = ql.Date(1, 2, 2031)
spread = 0.0008
schedule = ql.Schedule(issueDate, maturityDate, ql.Period('6M'), ql.UnitedStates(), ql.ModifiedFollowing, ql.ModifiedFollowing, ql.DateGeneration.Forward, False)
myDates = [ql.Date(30,6,2020), ql.Date(3,8,2020), ql.Date(1,2,2021), ql.Date(2,8,2021), ql.Date(1,2,2022), ql.Date(1,8,2022), ql.Date(1,2,2023), ql.Date(1,8,2023), ql.Date(1,2,2024), ql.Date(1,8,2024), ql.Date(3,2,2025), ql.Date(1,8,2025), ql.Date(2,2,2026), ql.Date(3,8,2026), ql.Date(1,2,2027), ql.Date(2,8,2027), ql.Date(1,2,2028), ql.Date(1,8,2028), ql.Date(1,2,2029), ql.Date(1,8,2029), ql.Date(1,2,2030), ql.Date(1,8,2030), ql.Date(3,2,2031)]
fwds = [0.030299999999999997, 0.025, 0.01354543171228029, 0.019589368178700814, 0.01958777464424011, 0.019589368178700814, 0.02057467941338146, 0.025178974367120032, 0.025177220897067684, 0.02517897436712047, 0.02509806907076861, 0.02476222313506464, 0.024759679154385567, 0.02476222313506464, 0.024759679154385567, 0.024762223135064203, 0.024760527109263717, 0.02476222313506464, 0.024759679154385567, 0.024762223135064203, 0.024903682036959026, 0.025577044856498768, 0.025574331077211018]
fwdcurve = ql.ForwardCurve(myDates, fwds, ql.Actual360(), ql.UnitedStates())
fwdhandle = ql.YieldTermStructureHandle(fwdcurve)
index = ql.IborIndex('MyIndex', ql.Period('6M'), 0, ql.USDCurrency(), ql.UnitedStates(), ql.ModifiedFollowing, True, ql.Actual360(), fwdhandle)
index.addFixing(ql.Date(30, 1, 2020), 2.98/100)
amortizingfloatbond = ql.AmortizingFloatingRateBond(0, notional, schedule, index, ql.Actual360(), ql.ModifiedFollowing, 2, [spread])
dates = [ql.Date(30, 6, 2020), ql.Date(30, 12, 2020), ql.Date(30, 6, 2021), ql.Date(30, 6, 2023), ql.Date(30, 6, 2025), ql.Date(30, 6, 2030), ql.Date(30, 6, 2035), ql.Date(30, 6, 2040)] 
zeros = [0.0091, 0.0090, 0.0104, 0.0194, 0.0135, 0.0425, 0.0379, 0.0317]
curve = ql.ZeroCurve(dates, zeros, ql.Actual360(), ql.UnitedStates())
discount_handle = ql.YieldTermStructureHandle(curve)
discount = ql.DiscountingBondEngine(discount_handle)
amortizingfloatbond.setPricingEngine(discount)
for i, cf in enumerate(amortizingfloatbond.cashflows()):
    print((i + 1), cf.date(), cf.amount())`

I constructed my zero curve and forward curve from existing data and used the forward rates to construct an ibor index and the zero rates to discount my cash flows.

The following cash flows are obtained from QuantLib Python:

[1840664583.3333333, 1840664583.3333333, 1840664583.3333333, 1850778125.0, 1830551041.6666667, 1860891666.6666665, 1830551041.6666667, 1860891666.6666665, 1840664583.3333333, 260062500.0, 1746753124.9999998, 260062500.0, 1551706250.0, 260062500.0, 1470075520.8333333, 260062500.0, 1314760416.6666665, 260062500.0, 1183284375.0, 260062500.0, 1051808333.3333333, 260062500.0, 925389062.5, 260062500.0, 788856250.0, 260062500.0, 664604166.6666666, 260062500.0, 523014583.3333333, 260062500.0, 398762499.99999994, 260062500.0, 261507291.66666666, 260062500.0, 134365624.99999997, 260062500.0]

as at the following dates:

[Date(3,8,2020), Date(1,2,2021), Date(2,8,2021), Date(1,2,2022), Date(1,8,2022), Date(1,2,2023), Date(1,8,2023), Date(1,2,2024), Date(1,8,2024), Date(1,8,2024), Date(3,2,2025), Date(3,2,2025), Date(1,8,2025), Date(1,8,2025), Date(2,2,2026), Date(2,2,2026), Date(3,8,2026), Date(3,8,2026), Date(1,2,2027), Date(1,2,2027), Date(2,8,2027), Date(2,8,2027), Date(1,2,2028), Date(1,2,2028), Date(1,8,2028), Date(1,8,2028), Date(1,2,2029), Date(1,2,2029), Date(1,8,2029), Date(1,8,2029), Date(1,2,2030), Date(1,2,2030), Date(1,8,2030), Date(1,8,2030), Date(3,2,2031), Date(3,2,2031)]

The correct cash flow as at 3 August 2020 should be as follows: ((0.025 * (34/360)) + 0.0008) * 3640875000 = 37723510.42

What is wrong with my code and how should I proceed to compute the correct cash flows for an amortizing floating rate bond using QuantLib and Python?

Also, please advice how to display the rates from my ibor index.

Any help from LB or GB would be highly appreciated!!

Thank you.

AA

It's a combination of a problem in the QuantLib wrappers and a wrong call in your code.

In your code, you have

amortizingfloatbond = ql.AmortizingFloatingRateBond(0, notional, schedule, index, ql.Actual360(), ql.ModifiedFollowing, 2, [spread])

The last argument you pass is in the position of the gearings, not the spreads; so the bond is paying 0.0008 * Libor + the default spread. Unfortunately, the default spread is 1, not 0 (probably a copy-paste error from gearings, for which 1 is the correct default). In the end, your bond turns out paying 0.0008 * Libor + 100%, giving an absurd cash flow.

To bypass the problem and initialize the bond correctly, you can use the call

amortizingfloatbond = ql.AmortizingFloatingRateBond(
    0, notional, schedule, index, ql.Actual360(), ql.ModifiedFollowing, 2, spreads=[spread])

that sets the spreads correctly.

At that point, the amount of the first coupon is 56324336.25, which is correct; your formula for the expected value should be modified. First, the fixing for the LIBOR is 0.0298, which you specified with

index.addFixing(ql.Date(30, 1, 2020), 2.98/100)

Second, it's not ((0.0298 * T) + 0.0008); it's (0.0298 + 0.0008) * T, where the spread is also multiplied by the accrual time. Finally, the time is not 34/360; there are 34 days between the valuation date and the payment date of the coupon, but the cash flow is based on the length of the coupon, which is 6 months, or more precisely in this case, 182 days. So: (0.0298 + 0.0008) * (182/360) * 3640875000 = 56324336.25 which is the returned amount.

Finally: to display the rates, use

for i, cf in enumerate(amortizingfloatbond.cashflows()):
    c = ql.as_floating_rate_coupon(cf)
    if c:
       print((i + 1), c.rate(), c.indexFixing(), c.fixingDate())

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM