Skip to content
All library documents

Pricing an Annuity from Bond Cash Flows and a Spot Curve

Article Quant Q&A · Author: Bogaso

Summary

The document shows how to value an annuity using the cash flows of a fixed-rate bond in QuantLib. The method starts with a bond built from a payment schedule and a discounting engine tied to a spot yield curve. To represent an annuity with no principal repayment at maturity, it takes the bond’s cash flows and excludes the final principal payment, then computes the net present value of the remaining coupon payments using the curve.

The response explains that the valuation date matters. The initial calculation discounts to the spot curve’s reference date, which may differ from the bond’s settlement date. For a settlement-date value, it recommends supplying that date to the cash-flow valuation and comparing with the bond’s dirty price. The example is implementation guidance rather than a general treatment of annuity conventions; it does not cover alternative payment timings, floating payments, mortality, or other contract-specific features, and the result depends on the supplied curve and schedule.

Key ideas

  • An annuity without principal repayment can be valued by discounting the bond’s coupon cash flows alone.
  • The bond cash-flow list can be reused after removing the final principal payment.
  • The valuation date must match the intended present-value date.
  • A curve-reference-date value and a settlement-date value are not interchangeable.
  • The method relies on the chosen discount curve, payment schedule, and cash-flow conventions.

Tags

Full text
# How to price an Annuity


# How to price an Annuity












When we price a fixed rate bond using `Quantlib`, we generally take below approach -

```
import QuantLib as ql
import pandas as pd

todaysDate = ql.Date(31, 8, 2019)
ql.Settings.instance().evaluationDate = todaysDate

spotDates = [ql.Date(1,9,2019), ql.Date(5,9,2019), ql.Date(7,9,2019)]
spotRates = [0.066682, 0.067199, 0.067502]

dayCount = ql.Actual365Fixed()
calendar = ql.SouthAfrica()
interpolation = ql.Linear()
compounding = ql.Compounded
compoundingFrequency = ql.Semiannual

spotCurve = ql.ZeroCurve(spotDates, spotRates, dayCount, calendar, interpolation, compounding, compoundingFrequency)
spotCurveHandle = ql.YieldTermStructureHandle(spotCurve)

issueDate = ql.Date(20, 4, 2017)
maturityDate = ql.Date(20, 4, 2019)
tenor = ql.Period(ql.Semiannual)
calendar = ql.SouthAfrica()
bussinessConvention = ql.Following
dateGeneration = ql.DateGeneration.Backward
monthEnd = False

schedule = ql.Schedule(issueDate, maturityDate, tenor, calendar, bussinessConvention, bussinessConvention, dateGeneration, monthEnd)

dayCount = ql.Actual365Fixed()
couponRate = 0.0925
coupons = [couponRate]

settlementDays = 3
faceValue = 100
fixedRateBond = ql.FixedRateBond(settlementDays, faceValue, schedule, coupons, dayCount)

bondEngine = ql.DiscountingBondEngine(spotCurveHandle)
fixedRateBond.setPricingEngine(bondEngine)

fixedRateBond.NPV()
```

However my question is - instead of a typical bond if I need to price the `Annuity` (i.e. there is no Principal payment at the maturity), how can I modify above codebase?

Any pointer will be highly appreciated.

## Answer by Luigi Ballabio (score 1)

https://quant.stackexchange.com/a/57340

The bond engine uses the `CashFlows::npv()` method internally, so you can do the same after stripping the principal payment from the bond cashflows:

```
cashflows = fixedRateBond.cashflows()
coupons = cashflows[:-1] # all except the last

includeSettlementDateFlows = False

annuity = CashFlows.npv(
    coupons,
    spotCurveHandle,
    False,
    spotCurveHandle.referenceDate()
)
```

This will be consistent to `fixedRateBond.NPV()` (and you can verify it by not stripping the last coupon, i.e., setting `coupons = cashflows`: you'll get the same result).

However, note that this (and `fixedRateBond.NPV()`) discounts to the reference date of the spot curve, i.e., to the first of the `spotDates`. If you want to discount to the settlement date of the bond, you should use instead

```
annuity = CashFlows.npv(
    coupons,
    spotCurveHandle,
    False,
    bond.settlementDate()
)
```

and you should also use `fixedRateBond.dirtyPrice()` instead of `fixedRateBond.NPV()`.

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.