Quote a bond from its yield
You'll take a bond's clean quote and work out the cash a buyer pays, the yield the quote implies, the price a yield implies, and the rate risk on your position.
Before you start
- helixio installed: see installing.
- The bond's terms (currency, issue and maturity dates, coupon, frequency and day count), the settlement date, the clean quote, and your face amount.
- The yield convention the market quotes in. The bond's own frequency,
"default", is the usual one; bond conventions lists each market's.
Steps
- Describe the bond with
products.fixed_rate_bond. - Add the accrued at settlement to the clean quote: that's the cash per 100.
- Read the quote as a yield with
yield_, and turn a yield into a clean price withprice. - Read the DV01 per 100 at that yield with
dv01, and scale it to your face.
The comments in the program match these steps.
Rust
use chrono::NaiveDate;
use helixrs::{Currency, DayCountFraction, FixedRateBond, Frequency, PriceBasis, YieldQuoteConvention, yield_moments};
fn date(year: i32, month: u32, day: u32) -> NaiveDate {
NaiveDate::from_ymd_opt(year, month, day).expect("a real calendar date")
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. Describe the bond: a EUR 2.75% annual bond on ACT/ACT ICMA, issued on
// 15 September 2024 and maturing on 15 September 2034.
let bond = FixedRateBond::bullet(
Currency::new("EUR").expect("a registered currency"),
date(2024, 9, 15),
date(2034, 9, 15),
0.0275,
Frequency::Annual,
DayCountFraction::ActActIcma,
100.0,
)?;
let settlement = date(2026, 11, 3);
let clean = 97.85;
let face = 25_000_000.0;
// 2. Turn the clean quote into the cash a buyer pays: add the accrued.
let accrued = bond.accrued(settlement);
let dirty = clean + accrued;
println!("accrued per 100 {accrued:.6}");
println!("cash per 100 {dirty:.6}");
println!("cash on the face {:.2}", dirty * face / 100.0);
// 3. Read the quote as a yield in the bond's own (annual) convention, and
// turn a yield back into a clean price.
let own = YieldQuoteConvention::Default;
let y = bond
.yield_(clean, settlement, PriceBasis::Clean, own, None)
.expect("a bracketed solve");
let at_three = bond.price(0.0300, settlement, PriceBasis::Clean, own, None);
println!("yield of 97.85 {:.6}%", y * 100.0);
println!("clean at 3.00% {at_three:.6}");
// 4. Size the rate risk: DV01 per 100, then on the position.
let basis = own.basis(bond.frequency).expect("a compounding convention");
let dv01 = yield_moments(&bond, y, settlement, basis).dv01();
println!("DV01 per 100 {dv01:.6}");
println!("DV01 on the face {:.2}", dv01 * face / 100.0);
Ok(())
}Python
from datetime import date
import helixio as hlx
# 1. Describe the bond: a EUR 2.75% annual bond on ACT/ACT ICMA, issued on
# 15 September 2024 and maturing on 15 September 2034.
bond = hlx.products.fixed_rate_bond(
"EUR", date(2024, 9, 15), date(2034, 9, 15), coupon=0.0275,
frequency=hlx.dates.Frequency.Annual, day_count=hlx.dates.DayCountFraction.ActActIcma,
)
settlement = date(2026, 11, 3)
clean = 97.85
face = 25_000_000.0
# 2. Turn the clean quote into the cash a buyer pays: add the accrued.
accrued = bond.accrued(settlement)
dirty = clean + accrued
print(f"accrued per 100 {accrued:.6f}")
print(f"cash per 100 {dirty:.6f}")
print(f"cash on the face {dirty * face / 100.0:.2f}")
# 3. Read the quote as a yield in the bond's own (annual) convention, and turn
# a yield back into a clean price.
y = bond.yield_(clean, settlement, hlx.bonds.PriceBasis.Clean, "default")
at_three = bond.price(0.0300, settlement, hlx.bonds.PriceBasis.Clean, "default")
print(f"yield of 97.85 {y * 100:.6f}%")
print(f"clean at 3.00% {at_three:.6f}")
# 4. Size the rate risk: DV01 per 100, then on the position.
dv01 = bond.dv01(y, settlement, "default")
print(f"DV01 per 100 {dv01:.6f}")
print(f"DV01 on the face {dv01 * face / 100.0:.2f}")accrued per 100 0.369178
cash per 100 98.219178
cash on the face 24554794.52
yield of 97.85 3.061009%
clean at 3.00% 98.266526
DV01 per 100 0.068100
DV01 on the face 17025.02
Check it worked
- The cash per 100 is the clean quote plus the accrued.
- The bond trades below par, so its yield is above its 2.75% coupon.
- At 3.00%, below the quote's yield, the clean price is higher than 97.85: price and yield move opposite ways.
- The DV01 is per 100 face for one basis point; multiply by face / 100 for the position.
Related
- Fixed-rate bond, the reference for every argument and convention.
- How bonds work, for why quotes are clean and yields need a convention.
- Measure a bond's spread to a curve, for the next step: comparing the bond with a curve.
From the 0.0 line at revision bf7f2e2. Every printed number is that revision's own output.