0

I used QuantLib Python to price a fixed rate bond.

I used ql.ActualActual(ql.ActualActual.ISMA, schedule) to ensure that cash flows are the same for each coupon payment period.

For discounting, I used ql.Actual360() as day count convention.

My codes are as follows:

import QuantLib as ql
valuationDate = ql.Date(30, 6, 2020)
ql.Settings.instance().evaluationDate = valuationDate
schedule = ql.Schedule(ql.Date(7, 5, 2016), ql.Date(15, 8, 2024), ql.Period(ql.Semiannual), ql.NullCalendar(), ql.Following, ql.Following, ql.DateGeneration.Forward, True)
fixedRateBond = ql.FixedRateBond(0, 100, schedule, [0.05], ql.ActualActual(ql.ActualActual.ISMA, schedule))
curve = ql.FlatForward(valuationDate, ql.QuoteHandle(ql.SimpleQuote(0.05)), ql.Actual360(), ql.Compounded)
handle = ql.YieldTermStructureHandle(curve)
bondEngine = ql.DiscountingBondEngine(handle)
fixedRateBond.setPricingEngine(bondEngine)
irr = fixedRateBond.bondYield(fixedRateBond.NPV(), ql.Actual360(), ql.Compounded, ql.Semiannual, valuationDate)
print('NPV:', fixedRateBond.NPV())
print('IRR:', irr)
print('Clean Price:', fixedRateBond.cleanPrice(irr, ql.Actual360(), ql.Compounded, ql.Semiannual, valuationDate))
print('Dirty Price:', fixedRateBond.dirtyPrice(irr, ql.Actual360(), ql.Compounded, ql.Semiannual, valuationDate))
print('Accrued Interest:', fixedRateBond.accruedAmount(ql.Date(30, 6, 2020)))

The results I get are as follows:

NPV: 100.59728065053405
IRR: 0.04743504524230957
Clean Price: 100.59728133098947
Dirty Price: 101.3309769831634
Accrued Interest: 0.7336956521739156

As far as I know, the NPV should be the same as the dirty price. However, there is a minor difference that I cannot get rid of. Grateful if someone could explain where I went wrong.

And when I subtract the clean price from the dirty price to manually obtain the accrued interest, the answer is 0.7336956521739211, which differs slightly from QuantLib's accruedAmount function. Can someone explain where I went wrong please?

Thanks.

ql.user2511
  • 369
  • 2
  • 12
  • Why are you using the irr to calculate the clean and dirty prices instead of the yield? – Darnoc Eloc Jul 27 '21 at 10:37
  • @Darnoc What do you mean by yield? The coupon rate of 5%? – ql.user2511 Jul 27 '21 at 10:53
  • My apologies, I think that the dirty price should be equal to the NPV. If you can help, grateful to provide assistance. If ever I'm wrong in what I'm saying, please feel free to correct me directly. – ql.user2511 Jul 27 '21 at 12:50
  • Yes, the IRR is less than the coupon rate that’s why the dirty price less the clean price is greater than the accrued amount function result. This is also why your NPV is likely less than your clean price? – Darnoc Eloc Jul 27 '21 at 12:52
  • "NPV should be the same as the clean price" has been changed to "NPV should be the same as the dirty price". – ql.user2511 Jul 27 '21 at 12:52
  • Therefore, I should write ```print('Clean Price:', fixedRateBond.cleanPrice(0.05 ql.Actual360(), ql.Compounded, ql.Semiannual, valuationDate))``` instead? – ql.user2511 Jul 27 '21 at 12:53
  • This is what I get if I change the rate in the ```cleanPrice``` function and ```dirtyPrice``` function from the IRR to 0.05: ```NPV: 100.59728065053405 IRR: 0.04743504524230957 Clean Price: 99.63602541352493 Dirty Price: 100.36972106569885 Accrued Interest: 0.7336956521739156``` The difference is still there – ql.user2511 Jul 27 '21 at 12:55
  • You don't need to pass parameters to the dirty price and clean price methods if you are supplying a pricing engine to your bond object. – Darnoc Eloc Jul 27 '21 at 13:01
  • Or you can pass the yield and other parameters with a null pricing engine. – Darnoc Eloc Jul 27 '21 at 13:11
  • https://quant.stackexchange.com/questions/12707/pricing-a-fixedratebond-in-quantlib-yield-vs-termstructure/12715#12715 – Darnoc Eloc Jul 27 '21 at 15:23
  • 1
    Mixing of day count conventions also seems to be an issue. – Darnoc Eloc Jul 27 '21 at 15:23
  • Since I have supplied a pricing engine, I did not pass any parameters to the ```cleanPrice``` and the ```dirtyPrice``` functions. However, the issue remains: ```NPV: 100.59947845040405 IRR: 0.04687152397836583 Clean Price: 99.86593245380611 Dirty Price: 100.64038897554524 Accrued Interest: 0.7744565217391309```. Maybe the issue lies in the day count conventions indeed – ql.user2511 Jul 29 '21 at 07:57
  • What was the logic in using different conventions? – Darnoc Eloc Jul 31 '21 at 14:06
  • ```ql.ActualActual(ql.ActualActual.ISMA, schedule)``` is used to obtain fixed cash flows for the fixed rate bond. Refer to following [link](https://quant.stackexchange.com/questions/36532/pricing-a-fixed-rate-bond-in-quantlib-python). ```ql.Actual360()``` is used to generate the coupon payment dates, given that coupon payment frequency is semiannual. – ql.user2511 Aug 03 '21 at 07:29
  • Did you review the link I posted previously? I assume the information contained there should have provided a solution. – Darnoc Eloc Aug 09 '21 at 14:57
  • 1
    I got the answer from quant.stackexchange.com https://quant.stackexchange.com/questions/66419/difference-arising-between-dirty-price-and-npv-using-quantlib-python/66426#66426 – ql.user2511 Aug 16 '21 at 05:20

0 Answers0