Modeling Amortizing Bond Cash Flows in QuantLib
Summary
The document explains how to represent principal repayments when constructing an amortizing fixed-rate bond in QuantLib. It first builds a semiannual schedule spanning the two stated repayment dates, then supplies a notional amount for each coupon period. The notional falls after each principal repayment, so subsequent coupons are calculated on the reduced outstanding balance. The example uses an initial face amount of 1,000, a 10% coupon, and two repayments of 300, followed by repayment of the remaining principal at maturity.
The listed cash flows illustrate coupon amounts stepping down as the notional declines, alongside separate principal repayment flows. The answer also points out mismatches in the original question's face amount and schedule start date, and adjusts the illustrative schedule to include both repayment dates. It is a focused implementation example rather than a general treatment of bond conventions: dates are adjusted by the selected calendar and conventions, and users must align the notional sequence with their own coupon schedule and amortization terms.
Key ideas
- Build the bond schedule first, including the dates on which principal repayments affect outstanding notional.
- Provide a notional amount for each coupon period to reflect the remaining principal.
- Coupons decline when they are calculated on a reduced outstanding balance.
- Principal repayments appear as cash flows separate from coupon payments.
- Check that the schedule, initial face amount, and notional sequence match the bond terms.
Tags
Full text
# How to make a schedule for amortizing bonds in python quantlib?
# How to make a schedule for amortizing bonds in python quantlib?
I am trying to make a schedule for amortizing bonds in quantlib, but have no idea how to include amortization in this schedule.
I have the following bond:
```
Maturity Date:
30.04.2023
Coupon Frequency:
300 at
30.04.2018
300 at
30.04.2021
Day Count
Convention: 30Е/360
Coupon rate: 10
```
Here is the python code
```
faceValue = 100.0
ed = ql.Date(2, ql.December, 2019)
mat_d = ql.Date(30, ql.April, 2023)
coupons = [0.1]
dayCounter = ql.Thirty360(ql.Thirty360.European)
schedule = ql.Schedule(ed, mat_d, ql.Period(ql.Semiannual),
ql.Russia(),
ql.Unadjusted, ql.Unadjusted,
ql.DateGeneration.Backward, False)
```
## Answer by Luigi Ballabio (score 4, accepted)
https://quant.stackexchange.com/a/68341
Amortization is included when you build the bond.
Given that you're mentioning a payment of 300, I'll guess that your face amount is 1000, not 100 as you wrote. Also, you're mentioning a payment on 30.04.2018, but your schedule starts on 02.12.2019. For the purpose of the example, I'll start the schedule earlier so it includes both payments.
So, let's say your schedule is
```
start_d = ql.Date(30, ql.April, 2016)
mat_d = ql.Date(30, ql.April, 2023)
schedule = ql.Schedule(
start_d, mat_d, ql.Period(ql.Semiannual),
ql.Russia(),
ql.Unadjusted, ql.Unadjusted,
ql.DateGeneration.Backward, False)
```
which gives the dates:
```
[Date(30,4,2016),
Date(30,10,2016),
Date(30,4,2017),
Date(30,10,2017),
Date(30,4,2018),
Date(30,10,2018),
Date(30,4,2019),
Date(30,10,2019),
Date(30,4,2020),
Date(30,10,2020),
Date(30,4,2021),
Date(30,10,2021),
Date(30,4,2022),
Date(30,10,2022),
Date(30,4,2023)]
```
To create an amortizing bond, you'll specify the notional for each coupon. In this example, the first four coupons (before 30.04.2018) are paid on the full notional; then an amortizing payment of 300 is made, and the next six coupons are paid on the remaining notional (700); then another amortizing payment of 300 comes, and the next four coupons are paid on what remains (400). So you'll write:
```
notionals = [1000, 1000, 1000, 1000,
700, 700, 700, 700, 700, 700,
400, 400, 400, 400]
```
and you'll create the bond as
```
settlement_days = 3
coupons = [0.1]
dayCounter = ql.Thirty360(ql.Thirty360.European)
bond = ql.AmortizingFixedRateBond(settlement_days, notionals, schedule, coupons, dayCounter)
```
Now you can check its cashflows:
```
for c in bond.cashflows():
print(f"{str(c.date()):20} => {c.amount():.4}")
```
which gives
```
October 31st, 2016 => 50.0
May 2nd, 2017 => 50.0
October 30th, 2017 => 50.0
May 3rd, 2018 => 50.0
May 3rd, 2018 => 300.0
October 30th, 2018 => 35.0
April 30th, 2019 => 35.0
October 30th, 2019 => 35.0
April 30th, 2020 => 35.0
October 30th, 2020 => 35.0
April 30th, 2021 => 35.0
April 30th, 2021 => 300.0
November 1st, 2021 => 20.0
May 3rd, 2022 => 20.0
October 31st, 2022 => 20.0
May 2nd, 2023 => 20.0
May 2nd, 2023 => 400.0
```Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.