QuantLib Bond Yield Accuracy and Solver Tolerance
Summary
The document investigates why a fixed-rate bond yield calculated in QuantLib does not exactly match the input yield after the bond price is computed and then used to recover the yield. The example prices a bond from a specified yield, then calls the yield-solving function with the same day-count and compounding conventions. The returned yield matches to about eight decimal places, with a small residual difference.
The accepted answer attributes this to the solver’s default accuracy tolerance of 1e-8, rather than inconsistent pricing inputs. It explains that tighter accuracy and a larger evaluation limit can be supplied to the yield calculation; the example then recovers the input yield to near machine precision. This illustrates that round-trip numerical results depend on solver settings. The tighter tolerance is an option, not a general requirement: acceptable precision depends on the use case and numerical stability.
Key ideas
- A yield recovered from a bond price may differ slightly from the yield used to produce that price.
- The document attributes the observed discrepancy to QuantLib’s default solver accuracy setting.
- The yield calculation accepts an accuracy tolerance and a maximum evaluation count.
- Tightening the tolerance makes the round-trip result closer to the input yield.
- Choose solver precision according to the application’s needs rather than assuming every small difference indicates an error.
Tags
Full text
# QuantLib returns slightly different bondYield when backtested
# QuantLib returns slightly different bondYield when backtested
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__':
calculate_bond_price()
```
## Answer by David Duarte (score 6, accepted)
https://quant.stackexchange.com/a/51110
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.0000000000000001Shown 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.