QuantLib Bond Yields, Price Conventions, and Schedule Dates
Summary
The document presents a QuantLib Python example that prices a fixed-rate bond from a zero curve, then solves for a yield that reproduces the bond’s net present value. It compares the model’s NPV, dirty and clean prices, accrued interest, and recalculated price, reporting a very small numerical difference between the original and yield-based valuation. The question also raises whether a coupon schedule should begin at the valuation date or the bond’s issue date.
The example highlights that yield calculations depend on consistent price and date conventions, including settlement, day count, compounding, and the date passed into the yield and price methods. It does not provide an answer to the schedule question or establish that the code works across all cases. Its unusual long settlement lag and the use of differing day-count choices in pricing and yield calculations make the example context-specific; bond schedules should be checked against the instrument’s actual coupon dates and conventions.
Key ideas
- A bond yield can be solved numerically to reproduce a stated bond price or present value.
- The example checks the yield by repricing the bond and comparing the result with its NPV.
- Yield results depend on matching settlement dates, day-count conventions, compounding, and price definitions.
- The document raises but does not resolve whether the schedule should start on the issue date.
- The reported numerical agreement does not establish correctness for other bonds or conventions.
Tags
Full text
# Schedule, Yield-to-Maturity, and NPV of Fixed Rate Bond from QuantLib Python
# Schedule, Yield-to-Maturity, and NPV of Fixed Rate Bond from QuantLib Python
I would like to price a fixed rate bond using QuantLib Python.
The pricing is fine, however I would like to understand how to extract the Yield-to-Maturity (YTM) of the fixed rate bond, that is, the yield that will sum the discounted cash flows equal to the bond's Net Present Value (NPV).
Below are my codes:
```
import QuantLib as ql
valuationDate = ql.Date(31, 7, 2020)
ql.Settings.instance().evaluationDate = valuationDate
compounding = ql.Compounded
calendar = ql.UnitedStates()
coupon = 0.05
couponFrequency = ql.Semiannual
issueDate = ql.Date(28, 9, 2019)
maturityDate = ql.Date(28, 9, 2024)
settlementDays = 100
settlementDate = calendar.advance(valuationDate, ql.Period(settlementDays, ql.Days))
businessConvention = ql.Following
schedule = ql.Schedule(valuationDate, maturityDate, ql.Period(couponFrequency), calendar, businessConvention, businessConvention, ql.DateGeneration.Forward, True)
dayCount = ql.Actual360()
faceValue = 100
redemptionValue = 100
fixedRateBond = ql.FixedRateBond(settlementDays, faceValue, schedule, [coupon], ql.ActualActual(ql.ActualActual.ISMA, schedule), businessConvention, redemptionValue)
curve = ql.ZeroCurve([ql.Date(31, 7, 2020), ql.Date(1, 1, 2027)], [0.01, 0.02], ql.Actual360(), calendar, ql.Linear(), compounding, ql.Annual)
handle = ql.YieldTermStructureHandle(curve)
bondEngine = ql.DiscountingBondEngine(handle)
fixedRateBond.setPricingEngine(bondEngine)
print('QuantLib NPV:', fixedRateBond.NPV())
print('QuantLib Dirty Price:', fixedRateBond.dirtyPrice())
print('QuantLib Clean Price:', fixedRateBond.cleanPrice())
print('QuantLib Accrued Amount:', fixedRateBond.accruedAmount(), '\n')
yield_rate = fixedRateBond.bondYield(fixedRateBond.NPV(), dayCount, ql.Compounded, ql.Annual, valuationDate, 1.0e-16, 100)
print('YTM:', yield_rate)
recalculated_NPV = fixedRateBond.dirtyPrice(yield_rate, dayCount, ql.Compounded, ql.Annual, valuationDate)
print('Recalculated NPV:', recalculated_NPV)
diff = fixedRateBond.NPV() - recalculated_NPV
print(f'Difference:{diff:20.16f}')
```
Below are my results:
> QuantLib NPV: 113.46249924298792
> QuantLib Dirty Price: 113.94963596997535
> QuantLib Clean Price: 111.94414146448085
> QuantLib Accrued Amount: 2.0054945054944984
> YTM: 0.01627100678079127
> Recalculated NPV: 113.462499242988
> Difference: -0.0000000000000853
I have managed to obtain a yield which is mathematically correct with a very small difference of `-0.0000000000000853`, however I am not sure if the coding is correct and if it will work in all circumstances.
Also, is my `schedule` correct please? In several posts, I saw that the `schedule` starts with the `issueDate`. However, when I start my `schedule` with the `issueDate`, it increases the difference obtained above.
Can someone please help?Shown 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.