I am just starting to get familiar with QuantLib (in particular, fixed rate bond pricing functions). I read a number of examples, from which I am able to calculate bond price and bond yield.

The following script uses an input yield (0.057154825761367800000) to calculate a bond price (96.9073930899788536), using bond.cleanPrice.

I then took that calculated bond price, and fed it back into a bond.bondYield calculation (other inputs unchanged), expecting to get back my original input yield.

I found the back-calculated yield was close, but not as close as I would naively expect (it matched to 8 decimal places). Did I do something wrong? Is this acceptable precision based on the numerical solver max iterations? Something else?

import QuantLib as ql

def calculate_bond_price():

    settlementDays = 0
    faceValue = 100

    issueDate = ql.Date(11, 2, 2020)
    maturityDate = ql.Date(11, 2, 2025)
    tenor = ql.Period(ql.Quarterly)
    calendar = ql.NullCalendar()
    businessConvention = ql.Following
    dateGeneration = ql.DateGeneration.Backward
    monthEnd = False
    schedule = ql.Schedule (issueDate, maturityDate, tenor, calendar, businessConvention, businessConvention, dateGeneration, monthEnd)

    coupon_rate = 0.05
    coupons = [coupon_rate]

    dayCount = ql.Thirty360()

    bond = ql.FixedRateBond(settlementDays, faceValue, schedule, coupons, dayCount)

## manually specify a yield rate to 16 decimal places
## this is the value I expect to get back from bond.bondYield calculation
    yield_rate = 0.057154825761367800000

    bond_price = bond.cleanPrice(yield_rate, dayCount, ql.Simple, ql.Quarterly)
    print(f'PRICE >> calculated={bond_price:20,.16f}')
    # OUTPUTS: PRICE >> calculated= 96.9073930899788536

# feed the calculated bond price back into a bond.bondYield calculation with exact same (dayCount, Simple, Quarterly) inputs
# expect to get back the yield_rate (16 decimal); but only match to 8 decimals
    back_calculate_bond_yield = bond.bondYield(bond_price, dayCount, ql.Simple, ql.Quarterly)
    print(f'YIELD >> calculated={back_calculate_bond_yield:20,.16f} | expected={yield_rate:20,.16f} | diff={back_calculate_bond_yield-yield_rate:20,.16f}')
    # OUTPUTS: YIELD >> calculated=  0.0571548314094543 | expected=  0.0571548257613678 | diff=  0.0000000056480865

if __name__ == '__main__':
  • $\begingroup$ IMHO, matching beyond 8 decimal places seems economically absolutely irrelevant. $\endgroup$ – skoestlmeier Feb 12 '20 at 6:58
  • 2
    $\begingroup$ @skoestlmeier - in real life, yes. But showing a "perfect" match will make it easier to convince Vetting my code is doing what it should. $\endgroup$ – Roberto Feb 12 '20 at 12:20

I would start by saying that yes, this is an acceptable precision.

However, the reason you are not getting the same result is because, by default, QuantLib has accuracy=1.0e-8 and maxEvaluations=100.

You can set these parameters like this:

bond.bondYield(bond_price, dayCount, ql.Simple, ql.Quarterly, ql.Date(), 1.0e-16, 100)

This will get you much closer...

YIELD >> calculated= 0.0571548257613679 | expected= 0.0571548257613678 | diff= 0.0000000000000001

  • $\begingroup$ Thanks, that almost certainly explains it! I will run some tests tomorrow and mark your answer. Looks like those precision parameters you mentioned are in the reference manual (quantlib.org/reference/…), but I am still finding my way around there and missed it. $\endgroup$ – Roberto Feb 12 '20 at 2:49

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.