Skip to content
All library documents

Equalizing Bond Coupons with a Schedule-Based Day Count

Article Quant Q&A · Author: egor_zhev

Summary

The document addresses unequal coupon amounts generated by QuantLib’s amortizing fixed-rate bond constructor when monthly payments start on the last day of a month. The example shows February’s short length shifting interest between adjacent payments, even though the other monthly amounts match. Changing the business-day adjustment and day-count convention alone did not resolve the behavior in the reported setup.

The proposed remedy is to build an explicit backward schedule from issue date to maturity, preserving end-of-month treatment, then use an Actual/Actual ISMA day-count convention tied to that schedule. The bond is constructed from the schedule, coupon rate, and day-count convention rather than relying on the simpler period-based constructor. The answer also points out that a genuinely amortizing bond generally needs a sequence of changing notionals; with a single nominal amount, the instrument is effectively a plain fixed-rate bond. The document offers a practical QuantLib configuration, but does not provide a rerun showing the resulting payment amounts or discuss other schedule conventions.

Key ideas

  • An end-of-month start can produce uneven monthly coupon accrual around February.
  • The suggested setup uses an explicit schedule with end-of-month handling.
  • Actual/Actual ISMA should be constructed with the schedule for this example.
  • An amortizing bond generally requires multiple nominal amounts to represent principal reductions.

Tags

Full text
# quantlib: make AmortizingFixedRateBond coupon payments equal


# quantlib: make AmortizingFixedRateBond coupon payments equal












I found some unexpected result when trying to call AmortizingFixedRateBond wiht daily coupon payments, but starting in the last day of the month.

```
settlementDays = 0
calendar = ql.Russia()
nominal = 1000
startDate = ql.Date(31, 8, 2021) #ql.Date.todaysDate() 
duration = ql.Period('12m')
frequency = ql.Monthly
interest = 0.07
daytimeConvention = ql.Thirty360()
businessAdjustment = ql.ModifiedFollowing

annuityAsBond = ql.AmortizingFixedRateBond(settlementDays, calendar, nominal, startDate, duration, frequency, interest, daytimeConvention, businessAdjustment, ql.Date())

leg = ql.Leg([*annuityAsBond.cashflows()])        
df = pd.DataFrame({'Date': [c.date().to_date() for c in leg],
                           'Payment': [c.amount() for c in leg]})
df = df.groupby('Date').sum()
```

Output:

```
             Payment
Date                
2021-09-30  86.526746
2021-10-29  86.526746
2021-11-30  86.526746
2021-12-31  86.526746
2022-01-31  86.526746
2022-02-28  **86.296602**
2022-03-31  **86.823502**
2022-04-29  86.526746
2022-05-31  86.526746
2022-06-30  86.526746
2022-07-29  86.526746
2022-08-31  86.526746
```

As you see, the annuity calculator breaks a little bit (basically it transfers a single day interest from one month to another due to shortness of the February). At the same time, it is totally working as expected if `startDate` is not the end of the month. I tried different combinations of the convention and businessAdjustment, but this end of month trick is persistent. Is there a way around it to obtain correct coupons?

## Answer by ql.user2511 (score 1)

https://quant.stackexchange.com/a/67732

You should use the following day convention, but first construct your schedule using the issue date and the maturity date of the bond.

```
schedule = ql.Schedule(issueDate, maturityDate, ql.Period(frequency), calendar, businessAdjustment, businessAdjustment, ql.DateGeneration.Backward, True)
dayConvention = ql.ActualActual(ql.ActualActual.ISMA, schedule)
```

Moreover, change your `annuityAsBond` to the following:

```
annuityAsBond = ql.AmortizingFixedRateBond(settlementDays, [nominal], schedule, [interest], dayConvention, businessAdjustment)
```

Also, if it is an amortizing fixed rate bond, shouldn't there be more than one nominal? Else, it becomes a simple fixed rate bond.

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.