Calculating Forward Bond Yields from Prices and Discount Curves
Summary
The document explains why a bond forward’s impliedYield method does not directly return the yield of a forward-starting bond. In the example, applying it to the spot bond price and clean forward price produces a curve zero rate, because the forward price reflects discounting of the spot value. The answer illustrates this with a QuantLib curve and bond example, then constructs cash flows for a bond beginning at the forward date and derives its yield from the resulting price.
It also describes using the curve’s forwardRate method when the desired quantity is an annually compounded forward rate, and gives a discount-factor formula for a simplified coupon bond. The distinction between a forward bond yield and a forward rate depends on the bond’s cash flows and coupon assumptions. The example assumes a forward-starting bond without intermediate cash flows before its start date, and its numerical outputs are specific to the illustrative curve and conventions.
Key ideas
- The bond forward impliedYield calculation shown returns a zero rate rather than the forward-starting bond yield.
- A forward-starting bond yield can be calculated by modeling its cash flows from the forward date and solving from its price.
- A curve forward rate is a separate measure and can be obtained directly from the yield term structure.
- The discount-factor expression provides an approximate coupon-based forward rate under the example’s assumptions.
- Coupon structure and cash-flow timing determine which yield or rate calculation is appropriate.
Tags
Full text
# Forward bond yield with QuantLib
# Forward bond yield with QuantLib
I'm looking into way to calculate forward bond yield using QuantLib. In Python QuantLib book I see an example for bond futures, where
```
futures = ql.FixedRateBondForward(calc_date, futures_maturity_date,
ql.Position.Long, 0.0, settlement_days, day_count, calendar, business_convention,
ctd_bond, yield_curve_handle, yield_curve_handle)
implied_yield = futures.impliedYield(ctd_price/ctd_cf,
futures_price, calc_date, ql.Compounded, day_count).rate()
```
Is it correct to do something like this?
```
fwd= ql.FixedRateBondForward(calc_date, fwd_date, ql.Position.Long, 0.0,
settlement_days, day_count, calendar, business_convention, bond,
yield_curve_handle, yield_curve_handle)
fwd_price = fwd.cleanForwardPrice()
fwd_yield = fwd.impliedYield(bond_spot_price, fwd_price,
calc_date, ql.Compounded, day_count).rate()
```
```
## Answer by David Duarte (score 5, accepted)
https://quant.stackexchange.com/a/58982
The direct answer to your question is actually no, and here are some other ways to get a forward bond yield if all you want is the yield of a forward starting bond (I'm assuming it's a forward starting bond you want, ie, no intermediate cashflows)
```
import QuantLib as ql
today = ql.Date().todaysDate()
calendar = ql.NullCalendar()
dayCounter = ql.ActualActual()
dates = [today, ql.Date(28,10,2021), ql.Date(28,10,2022), ql.Date(28,10,2025)]
zeros = [0.01, 0.02, 0.03, 0.04]
crv = ql.LogLinearZeroCurve(dates, zeros, dayCounter, calendar)
yts = ql.YieldTermStructureHandle(crv)
engine = ql.DiscountingBondEngine(yts)
```
Defining a simple foward starting bond, you can get the bond yield from it's price (npv).
```
issueDate = today + ql.Period('2Y')
maturityDate = issueDate + ql.Period('2Y')
bond = ql.FixedRateBond(0, calendar, 100.0, issueDate, maturityDate, ql.Period('1Y'), [0.05], dayCounter)
bond.setPricingEngine(engine)
bondPrice = bond.NPV()
print(f"Bond Price: {bondPrice:.5f}")
bondYield = bond.bondYield(bondPrice, dayCounter, ql.Compounded, ql.Annual)
print(f"Bond Yield: {bondYield:.3%}")
```
Bond Price: 95.32379 Bond Yield: 3.689%
However, this will be the yield starting now and not the forward yield.
The approach you used:
```
fwd = ql.FixedRateBondForward(today, issueDate, ql.Position.Long, 100, 2, dayCounter, ql.TARGET(), ql.Following, bond, yts, yts)
fwdPrice = fwd.cleanForwardPrice()
fwdYield = fwd.impliedYield(bondPrice, fwdPrice, today, ql.Compounded, dayCounter).rate()
print(f"Fwd Yield: {fwdYield:.3%}")
```
Fwd Yield: 3.045%
Will also not give you the forward yield. According to QuantLib documentation, the impliedYield method gives:
"Simple yield calculation based on underlying spot and forward values, taking into account underlying income. When t>0, call with: underlyingSpotValue=spotValue(t), forwardValue=strikePrice, to get current yield. For a repo, if t=0, impliedYield should reproduce the spot repo rate. For FRA's, this should reproduce the relevant zero rate at the FRA's maturityDate"
So if you are feeding it the bondPrice and the forward bond price, you will basically get the zero rate. And in fact, since the forward bond price is just the compounded bond price:
```
print(fwdPrice)
print(bondPrice * crv.discount(issueDate)**-1)
```
101.21680137389713 101.21680137389713:
```
zeroRate = crv.zeroRate(issueDate, dayCounter, ql.Compounded).rate()
print(f"Zero Rate: {zeroRate:.3%}")
```
Zero Rate: 3.045%
What you could do is build the cashflows of a forward bond:
```
cfs = ql.Leg([ql.AmortizingPayment(-100, issueDate)] + [*bond.cashflows()][:-1])
bond2 = ql.Bond(2, calendar, today, cfs)
bond2.setPricingEngine(engine)
for cf in bond2.cashflows():
print(cf.date().ISO(), cf.amount())
```
2022-10-28 -100.0 2023-10-28 5.000000000000004 2024-10-28 5.002432816827618 2024-10-28 100.0
And get it's yield:
```
fwdYield = bond2.bondYield(bond2.NPV(), dayCounter, ql.Compounded, ql.Annual)
print(f"Fwd Yield: {fwdYield:.3%}")
```
Fwd Yield: 4.336%
If you don't know the coupon, you could just get the annually compounded forward from the curve:
```
fwdRate = crv.forwardRate(issueDate, maturityDate, dayCounter, ql.Compounded, ql.Annual).rate()
print(f"Fwd Rate: {fwdRate:.3%}")
```
Fwd Rate: 4.361%
Which would be more or less:
$$ fwd = \frac{DF_0 - DF_T}{\sum^T_{i=1} DF_i}$$
where i are cashflow dates and T is maturity date
```
dates = ql.MakeSchedule(issueDate, maturityDate, ql.Period('1Y'), )
dfs = [crv.discount(date) for date in dates]
fwdRate2 = (dfs[0]-dfs[-1])/ sum(dfs[1:])
print(f"Fwd Rate: {fwdRate2:.3%}")
```
Fwd Rate: 4.354%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.