Esc

    The API is under testing and may change before release.

    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

    1. Describe the bond with products.fixed_rate_bond.
    2. Add the accrued at settlement to the clean quote: that's the cash per 100.
    3. Read the quote as a yield with yield_, and turn a yield into a clean price with price.
    4. 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.
    • 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.