Skip to content
All library documents

Handling Off-Schedule Redemptions in an Amortizing Floating-Rate Bond

Article Quant Q&A · Author: jamoreiras

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.