Skip to content
All library documents

How QuantLib Applies Compounded and Simple Bond Yield Conventions

Article Quant Q&A · Author: Kulendra 'KJ' Janaka

Summary

The discussion explains why QuantLib can return identical bond prices for Compounded and CompoundedThenSimple conventions, and likewise for Simple and SimpleThenCompounded, when settlement falls in the first coupon period. The explanation points to CashFlows::npv: it builds discounting between the settlement date and each cashflow by multiplying factors between cashflow dates. Because the day counter handles each interval separately and does not retain the full coupon-period context, the simple portion of a mixed convention may be applied to each interval in a way that makes the results coincide.

A second answer relates the conventions to U.S. Treasury market practice: yields are compounded at the coupon frequency for periods with coupons remaining, while the final period uses simple yield. The discussion does not provide a code fix or demonstrate that this convention explains every observed result. It also notes that changing the discount-factor calculation could affect other cases, including day counters such as Actual/Actual.

Key ideas

  • QuantLib’s bond pricing path calculates discounting through cashflow intervals between settlement and payment dates.
  • A day counter that treats each interval independently may not preserve its position within the full coupon period.
  • Mixed compounding conventions can therefore produce the same prices as their simpler counterparts in the described setup.
  • The discussion connects simple yield in the final period with a U.S. Treasury market convention.
  • Changing the interval calculation could affect other day-count cases, so the explanation does not establish a universal fix.

Tags

Full text
# QuantLib Compounded vs CompoundedThenSimple and Simple vs SimpleThenCompounded


# QuantLib Compounded vs CompoundedThenSimple and Simple vs SimpleThenCompounded












I was experimenting on a FixedRateBond on QuantLib python port and have a question on the use of Compounded vs. CompoundedThenSimple methods of discounting.

I am using the FixedRateBond.dirtyPrice() method to get the dirty price. But it looks like both Compounded and CompoundedThenSimple methods provide the exact same answers when the settlement date is in the first period (In my example, start date = 15-Aug-20, end date = 15-Aug-22, settlement date = 20-Aug-20). Similarly Simple and SimpleThenCompound methods provide the exact same answer.

Isn't the CompoundedThenSimple model supposed to compound until the first coupon date and then do a simple discounting till the settlement date?

My code is as follows:

```
issuedate = ql.Date(15,8,2020)
maturitydate = ql.Date(15,8,2022)
settledate = ql.Date(20,8,2020)
coupon = 0.04
period = ql.Period('6M')
daycount = ql.ActualActual()
dcadjustment = ql.Unadjusted
calendar = ql.NullCalendar()
eomrule = False
dategenmethod = ql.DateGeneration.Forward
settledelay = 0
facevalue = 100
yld = 0.045

couponvector = [coupon]
bsched = ql.Schedule(issuedate,maturitydate,period,calendar,dcadjustment,dcadjustment,dategenmethod,eomrule)

frb = ql.FixedRateBond(settledelay,facevalue,bsched,couponvector,daycount)

print('===== Compounded =====')
print(frb.dirtyPrice(yld,daycount,ql.Compounded,ql.Semiannual,settledate))
print(frb.cleanPrice(yld,daycount,ql.Compounded,ql.Semiannual,settledate))

print('===== Compounded Then Simple =====')
print(frb.dirtyPrice(yld,daycount,ql.CompoundedThenSimple,ql.Semiannual,settledate))
print(frb.cleanPrice(yld,daycount,ql.CompoundedThenSimple,ql.Semiannual,settledate))

print('===== Simple Then Compounded =====')
print(frb.dirtyPrice(yld,daycount,ql.SimpleThenCompounded,ql.Semiannual,settledate))
print(frb.cleanPrice(yld,daycount,ql.SimpleThenCompounded,ql.Semiannual,settledate))

print('===== Simple =====')
print(frb.dirtyPrice(yld,daycount,ql.Simple,ql.Semiannual,settledate))
print(frb.cleanPrice(yld,daycount,ql.Simple,ql.Semiannual,settledate))
```

## Answer by Luigi Ballabio (score 3)

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

More an explanation of what's happening than a solution, but anyway: the calls to `dirtyPrice` and `cleanPrice` ultimately end up calling this overload of the CashFlows::npv method. As you can see on this couple of lines, it calculates the discount factor between each cashflow dates and the settlement date by multiplying the discount factors between each cashflow date.

The problem in your case is that the day counter class is too simple to have any memory of what it's asked to do, and each of the separate calls to find the discount between two coupon dates considers that as the full period and applies the "simple" part in the simple-then-compounded convention.

I'm not sure what the solution can be. Modifying the code so that it calculates each whole discount between cashflow date and reference date would probably break other cases (I'm suspecting act/act would disagree with the change, for instance).

## Answer by Dimitri Vulis (score 1)

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

Looking at the code https://github.com/lballabio/QuantLib/blob/master/ql/interestrate.cpp lines 62ff, 201ff, 151ff and at this discussion https://github.com/lballabio/quantlib/issues/256 - I think this implements the market convention for U.S. treasury to use simple during the last coupon period before maturity, and to use compounding if there are any coupons left to pay before maturity.

If you get hold of Bloomberg Terminal document Bloomberg Calculation Types 1418754609 , it says:

> [Calctype] 1: Notes and Bonds, Street Convention Standard yield formula used most commonly on US Treasuries, corporate securities, and Eurobonds. Yields are calculated on a compounded basis on the same frequency as the coupon frequency for all periods except the last period. In the last period simple yield is applied. Coupon amounts for standard coupon periods are calculated as coupon / coupon frequency * face regardless of day count.

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.