0
$\begingroup$

I have used QuantLib Python to price a fixed rate bond.

My codes are as follows:

import QuantLib as ql

valuationDate = ql.Date(30, 6, 2020)
ql.Settings.instance().evaluationDate = valuationDate
compounding = ql.Continuous
calendar = ql.UnitedStates()
coupon = 0.05
couponFrequency = ql.Annual
issueDate = ql.Date(7, 5, 2016)
maturityDate = ql.Date(7, 5, 2024)
settlementDays = 2
settlementDate = calendar.advance(issueDate, ql.Period(settlementDays, ql.Days))
dayCount = ql.ActualActual(ql.ActualActual.ISMA)
fixedRateBond = ql.FixedRateBond(settlementDays, calendar, 100.0, issueDate, maturityDate, ql.Period(couponFrequency), [coupon], dayCount, ql.Unadjusted, ql.Unadjusted)
curve = ql.FlatForward(valuationDate, ql.QuoteHandle(ql.SimpleQuote(0.02)), dayCount, compounding)
handle = ql.YieldTermStructureHandle(curve)
bondEngine = ql.DiscountingBondEngine(handle)
fixedRateBond.setPricingEngine(bondEngine)
bondYield = fixedRateBond.bondYield(dayCount, compounding, couponFrequency)
cleanPrice = fixedRateBond.cleanPrice()
dirtyPrice = fixedRateBond.dirtyPrice()
print('NPV:', fixedRateBond.NPV())
print('Bond Yield:', bondYield)
print('Clean Price:', cleanPrice)
print('Dirty Price:', dirtyPrice)
print('Accrued Interest:', fixedRateBond.accruedAmount())
print('Actual Accrued Amount', dirtyPrice - cleanPrice)

The results I get are as follows:

NPV: 111.7127354483437

Bond Yield: 0.01989558171322524

Clean Price: 110.9578553230744

Dirty Price: 111.72497861074564

Accrued Interest: 0.767123287671234

Actual Accrued Amount 0.7671232876712395

However, there are some discrepancies that I have noted:

  1. As far as I know, the NPV should be equal to the dirty price, however, this is not the case here.
  2. QuantLib's accruedAmount function is not equal to the dirtyPrice minus the cleanPrice.

Can someone explain where I went wrong please?

Thanks.

$\endgroup$
4
  • $\begingroup$ I have no idea about quantlib but a difference in the 14th decimal is in my opinion most likely down to floating point math $\endgroup$
    – AKdemy
    Aug 13 at 6:52
  • $\begingroup$ The issue regarding NPV and dirty price remains though $\endgroup$ Aug 13 at 6:53
  • $\begingroup$ stackoverflow.com/q/68542560/15877695 didn't help? $\endgroup$
    – AKdemy
    Aug 13 at 7:31
  • $\begingroup$ @AKdemy unfortunately, no. I used the same day count convention throughout the codes, but the issue still remains. I thought that positing the question on here might be more appropriate. $\endgroup$ Aug 13 at 7:36
1
$\begingroup$

As @AKdemy said, the two accrued amounts are the same within the precision of floating-point math.

The NPV and dirtyPrice methods return two slightly different quantities: NPV discounts the cashflows to the reference date of the discount curve (in your case, the valuation date) while dirtyPrice discounts to the settlement date of the bond. This can also give rise to larger differences if a coupon is paid between the valuation date and the settlement date: in that case, the coupon amount will be included in the NPV but not in the price.

A note: the settlement date of the bond is not the one you defined as settlementDate. Instead, it's two days after the valuation date, not the issue date, so it should be

settlementDate = calendar.advance(valuationDate, ql.Period(settlementDays, ql.Days))

or, once you build the bond,

settlementDate = bond.settlementDate()

Another thing you might want to change: using act/act(ISMA) in a term structure is risky (see https://www.youtube.com/watch?v=dQjd3hAshj4). If you need to use it, what you should do is create first the schedule for the bond, so you can pass it to the day counter:

schedule = ql.Schedule(issueDate, maturityDate, ql.Period(couponFrequency), calendar, ql.Unadjusted, ql.Unadjusted, ql.DateGeneration.Forward, True)
dayCount = ql.ActualActual(ql.ActualActual.ISMA, schedule)
fixedRateBond = ql.FixedRateBond(settlementDays, 100.0, schedule, [coupon], dayCount)

This way, the day counter has the information about the reference periods of the coupons, which is required by the act/act calculation rules.

Once you do the two changes above, you can check the difference between NPV and dirtyPrice. If you set settlementDays to 0, the dirty price should become equal to the NPV, because in both cases the cashflows will be discounted to the valuation date; if, instead, you pass the settlementDate above to the curve, the NPV should become equal to the dirty price, because both will be discounted to the settlement date.

$\endgroup$
1
  • $\begingroup$ Thanks a lot for your help Luigi $\endgroup$ Aug 16 at 5:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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