0
$\begingroup$

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))
$\endgroup$

2 Answers 2

1
$\begingroup$

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.
$\endgroup$
2
  • $\begingroup$ Thanks a lot for that info! But I still get the exact same results as I step through the settlement dates. Based on that model, if I set the settlement date to be on 20-Feb-2022 (which is the last period between 15-Feb-2022 and 15-Aug-2022), I should get two different results. But I get dp = 99.81849816022718 and cp = 99.76370363967922 for both Compound and Compound Then Simple. $\endgroup$ Sep 28, 2020 at 14:33
  • $\begingroup$ As for the US Treasury method, this is exactly what I was trying to get at. But the model I mentioned seems to be the one mentioned in section 5.3.7: [ihttps://www.icmagroup.org/assets/documents/Media/Bondmarketsbook/Bond%20markets_structures%20and%20yield%20calculations.pdf] and with the last period being simple discounting. Perhaps those are two different. $\endgroup$ Sep 28, 2020 at 14:44
0
$\begingroup$

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 casse 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).

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.