Handling Off-Schedule Redemptions in an Amortizing Floating-Rate Bond
Summary
The document concerns adding principal redemptions that fall between coupon dates to an amortizing floating-rate bond in QuantLib. Assigning a redemption directly to the bond’s redemption list did not make the cash flow appear in the reported schedule. The described workaround combines the regular schedule dates with redemption dates, sorts them, and builds a new schedule from the merged dates.
The example shows that this changes the coupon accrual periods: interest is then calculated over irregular intervals, including periods ending on redemption dates. The author reports that the resulting cash flows appear, but flags the accrued interest on those dates as incorrect for the intended bond. Thus, the procedure demonstrates how to represent dates in the schedule, but does not provide a complete solution for preserving the original coupon accrual rules while modeling separate principal payments.
Key ideas
- Redemption dates outside the bond schedule may not appear when assigned directly as redemptions.
- Merging redemption dates into the schedule makes those dates part of the generated cash flow structure.
- Adding dates changes accrual intervals and can create interest payments on irregular dates.
- The example does not resolve how to model off-schedule principal payments without distorting coupon accruals.
Tags
Full text
# how to add redemptions to amortizing floating bond in dates that are not coupon dates
# how to add redemptions to amortizing floating bond in dates that are not coupon dates
How can I in `QuantLib` add redemptions to a `AmortizingFloatingRateBond` that follow in dates outside the Bond Schedule?
```
self.schedule = ql.Schedule(ql.Date(18, 5, 2022), ql.Date(10, 12, 2026), self.frequency,
ql.UnitedStates(), ql.ModifiedFollowing, ql.ModifiedFollowing,
ql.DateGeneration.Forward, True, ql.Date(18, 5, 2022))
this is the schedule:
June 20th, 2022 53998.86
July 18th, 2022 45817.22
August 18th, 2022 50726.20
September 19th, 2022 52362.53
October 18th, 2022 64941.37
November 18th, 2022 69422.10
December 19th, 2022 69422.10
January 18th, 2023 67181.70
... ...
November 18th, 2026 67181.70
December 10th, 2026 49260.84
December 10th, 2026 15000000.01
these are the redemptions:
September 30th, 2022 159705.21
December 31st, 2022 159705.21
March 31st, 2023 227928.79
June 30th, 2023 227928.79
September 30th, 2023 227928.79
December 31st, 2023 227928.79
March 31st, 2024 254287.90
June 30th, 2024 254287.90
September 30th, 2024 254287.90
December 31st, 2024 254287.90
March 31st, 2025 293051.30
June 30th, 2025 293051.30
September 30th, 2025 293051.30
December 31st, 2025 293051.30
March 31st, 2026 345769.53
June 30th, 2026 345769.53
September 30th, 2026 345769.53
December 10th, 2026 10542209.04
```
I have tried:
```
g.bond.redemptions = (ql.Redemption(159705.21, ql.Date( 30, 9, 2022)))
```
but still do not see it in the cash flows:
```
September 19th, 2022 52362.53
October 18th, 2022 64941.37
```
## Answer by jamoreiras (score 1, accepted)
https://quant.stackexchange.com/a/72074
I have followed the guidance from Luigi posted here and joined and sorted the schedule dates with the redemption dates. The result is very satisfactory, however shows interest payment in irregular dates, which is not correct.
```
schedule = ql.Schedule(effectiveDate,
terminationDate,
frequency,
calendar,
convention,
terminationDateConvention,
rule,
endOfMonth)
dates = list(schedule)
dates.extend([ql.Date(30, 9, 2022),ql.Date(31, 12, 2022),ql.Date(31, 3, 2023),
ql.Date(30, 6, 2023),ql.Date(30, 9, 2023),ql.Date(31, 12, 2023),
ql.Date(31, 3, 2024),ql.Date(30, 6, 2024),ql.Date(30, 9, 2024),
ql.Date(31, 12, 2024),ql.Date(31, 3, 2025),ql.Date(30, 6, 2025),
ql.Date(30, 9, 2025),ql.Date(31, 12, 2025),ql.Date(31, 3, 2026),
ql.Date(30, 6, 2026),ql.Date(30, 9, 2026)])
dates.sort()
sched = ql.Schedule(dates, ql.UnitedStates(), ql.ModifiedFollowing)
notional = [15000000.01,15000000.01,15000000.01,15000000.01,15000000.01,
14840294.8, ...
```
| | accrualStartDate().to_date | accrualEndDate().to_date | accrualDays | hasOccurred | date().to_date | index | nominal | fixingDate().to_date | indexFixing | spread | rate | amount |
| 0 | 2022-05-20 | 2022-06-10 | 21 | True | 2022-06-10 | USDLibor1M Actual/360 index | 1.5e+07 | 2022-05-18 | 0.0092719 | 0.03 | 0.0392719 | 34,362.9 |
| 1 | 2022-06-10 | 2022-07-11 | 31 | True | 2022-07-11 | USDLibor1M Actual/360 index | 1.5e+07 | 2022-06-08 | 0.0092719 | 0.03 | 0.0392719 | 50,726.2 |
| 2 | 2022-07-11 | 2022-08-10 | 30 | True | 2022-08-10 | USDLibor1M Actual/360 index | 1.5e+07 | 2022-07-07 | 0.0092719 | 0.03 | 0.0392719 | 49,089.9 |
| 3 | 2022-08-10 | 2022-09-12 | 33 | False | 2022-09-12 | USDLibor1M Actual/360 index | 1.5e+07 | 2022-08-08 | 0.0092719 | 0.03 | 0.0392719 | 53,998.9 |
| 4 | 2022-09-12 | 2022-09-30 | 18 | False | 2022-09-30 | USDLibor1M Actual/360 index | 1.5e+07 | 2022-09-08 | 0.023736 | 0.03 | 0.053736 | 40,302 |
| 5 | 2022-09-30 | 2022-10-11 | 11 | False | 2022-10-11 | USDLibor1M Actual/360 index | 1.48403e+07 | 2022-09-28 | 0.0237305 | 0.03 | 0.0537305 | 24,364.3 |
| 6 | 2022-10-11 | 2022-11-10 | 30 | False | 2022-11-10 | USDLibor1M Actual/360 index | 1.48403e+07 | 2022-10-07 | 0.0237454 | 0.03 | 0.0537454 | 66,466.4 |
| 7 | 2022-11-10 | 2022-12-12 | 32 | False | 2022-12-12 | USDLibor1M Actual/360 index | 1.48403e+07 | 2022-11-08 | 0.0237469 | 0.03 | 0.0537469 | 70,899.6 |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.