Esc

    The API is under testing and may change before release.

    Value a repo and its haircut

    You'll book a three-month repo at 102% margin on a GC curve, value it, and read its margin position: the cash exposure, the collateral's value, what's required, the excess and the haircut.

    Before you start

    • helixio installed: see installing.
    • SOFR quotes for the USD curve, and the GC spread to SOFR. The example states them inline.
    • The collateral bond's terms, the repo's dates, rate, nominal, price and margin ratio.

    Steps

    1. State the close and the SOFR quotes.
    2. Build the market with with_recipe_quotes, then add general collateral as a repo spread curve over the discount curve with with_repo_spread("USD", gc, "discount(USD)").
    3. Build the collateral bond with products.fixed_rate_bond.
    4. Book the repo on the USD-GC-REPO template with .repo(..., margin_ratio=1.02). The cash follows from the nominal, the price and the margin ratio.
    5. Value it with npv, and read the margin position with bonds.repo_margin(repo, market).
    6. Work out the cash and the haircut from the terms, to compare.

    The comments in the program match these steps.

    Rust
    use chrono::NaiveDate;
    use helixrs::{
        AnyInstrument, AnyTemplate, CollateralSpec, Curve, DayCountFraction, FixedRateBond, Frequency,
        MarketContext, QuoteSheet, Repo, RepoRate, RepoSide, RepoSpread, RepoTerms, repo_margin, template,
    };
    
    type Outcome<T> = Result<T, Box<dyn std::error::Error>>;
    
    fn date(year: i32, month: u32, day: u32) -> NaiveDate {
        NaiveDate::from_ymd_opt(year, month, day).expect("a real calendar date")
    }
    
    fn main() -> Outcome<()> {
        // 1. The close and the SOFR quotes the market is built from.
        let close = date(2026, 10, 1);
        let usd_close = [
            ("USD-SOFR-OIS", "1M", 0.0391),
            ("USD-SOFR-OIS", "3M", 0.0402),
            ("USD-SOFR-OIS", "1Y", 0.0439),
            ("USD-SOFR-OIS", "2Y", 0.0459),
            ("USD-SOFR-OIS", "3Y", 0.0468),
            ("USD-SOFR-OIS", "4Y", 0.0469),
            ("USD-SOFR-OIS", "5Y", 0.0471),
            ("USD-SOFR-OIS", "7Y", 0.0475),
            ("USD-SOFR-OIS", "10Y", 0.0483),
            ("USD-SOFR-OIS", "15Y", 0.0494),
            ("USD-SOFR-OIS", "20Y", 0.0499),
            ("USD-SOFR-OIS", "30Y", 0.0492),
        ];
    
        // 2. Build the market, with general collateral 10bp under SOFR as a repo
        //    spread curve over the USD discount curve.
        let gc = RepoSpread::new(close, &[(date(2027, 10, 1), -0.0010)], DayCountFraction::Act360)?;
        let market = MarketContext::as_of(close)
            .with_recipe_quotes(&CollateralSpec::Currency("USD".parse()?), &QuoteSheet::from_rows(usd_close)?)?
            .with_repo_spread("USD", Curve::RepoSpread(gc), "discount(USD)".parse()?);
    
        // 3. The collateral: a ten-year 4.25% bond.
        let bond = FixedRateBond::bullet(
            "USD".parse()?,
            date(2025, 8, 15),
            date(2035, 8, 15),
            0.0425,
            Frequency::SemiAnnual,
            DayCountFraction::ActActIcma,
            100.0,
        )?;
    
        // 4. Book three months of repo on the USD GC template: 10 million of the bond
        //    at 99.5, at 4.00%, with a margin ratio of 1.02 (102% margin).
        let (nominal, price, margin_ratio) = (10_000_000.0, 99.5, 1.02);
        let AnyTemplate::Repo(gc_repo) = template("USD-GC-REPO")? else {
            unreachable!("a repo template")
        };
        let start = date(2026, 10, 2);
        let end = gc_repo.end_after(start, "3M".parse()?);
        let terms = RepoTerms::from_template(&gc_repo, RepoSide::Repo, start, end, RepoRate::Fixed(0.0400), margin_ratio)?;
        let repo = AnyInstrument::Repo(Box::new(Repo::from_collateral(terms, bond, nominal, price)?));
    
        // 5. Value it, and read the margin position.
        let margin = repo_margin(&repo, &market, None)?;
        println!("npv               {:.2}", repo.npv(&market)?);
        for (field, value) in [
            ("exposure", margin.exposure),
            ("collateral_value", margin.collateral_value),
            ("required", margin.required),
            ("excess", margin.excess),
            ("haircut", margin.haircut),
        ] {
            println!("{field:<17} {value:.6}");
        }
    
        // 6. Compare with the cash and the haircut worked out from the terms.
        let cash = nominal * price / 100.0 / margin_ratio;
        let haircut = 1.0 - 1.0 / margin_ratio;
        println!("cash by hand      {cash:.6}");
        println!("haircut by hand   {haircut:.6}");
    
        Ok(())
    }
    Python
    from datetime import date
    
    import helixio as hlx
    
    # 1. The close and the SOFR quotes the market is built from.
    SOFR_CLOSE_DATE = date(2026, 10, 1)
    USD_SOFR_CLOSE = {
        "USD-SOFR-OIS": {
            "1M": 0.0391, "3M": 0.0402, "1Y": 0.0439, "2Y": 0.0459, "3Y": 0.0468, "4Y": 0.0469,
            "5Y": 0.0471, "7Y": 0.0475, "10Y": 0.0483, "15Y": 0.0494, "20Y": 0.0499, "30Y": 0.0492,
        },
    }
    
    # 2. Build the market, with general collateral 10bp under SOFR as a repo spread
    #    curve over the USD discount curve.
    gc = hlx.curves.RepoSpreadCurve(SOFR_CLOSE_DATE, [(date(2027, 10, 1), -0.0010)])
    market = (
        hlx.MarketContext(SOFR_CLOSE_DATE)
        .with_recipe_quotes("USD", USD_SOFR_CLOSE)
        .with_repo_spread("USD", gc, "discount(USD)")
    )
    
    # 3. The collateral: a ten-year 4.25% bond.
    bond = hlx.products.fixed_rate_bond(
        "USD", date(2025, 8, 15), date(2035, 8, 15),
        coupon=0.0425, frequency="Semiannual", day_count="ACT/ACT.ICMA",
    )
    
    # 4. Book three months of repo on the USD GC template: 10 million of the bond at
    #    99.5, at 4.00%, with a margin ratio of 1.02 (102% margin).
    NOMINAL, PRICE, MARGIN_RATIO = 10_000_000.0, 99.5, 1.02
    repo = hlx.templates.get("USD-GC-REPO").repo(
        date(2026, 10, 2), "3M", side="repo", collateral=bond, price=PRICE,
        rate=0.0400, nominal=NOMINAL, margin_ratio=MARGIN_RATIO,
    )
    
    # 5. Value it, and read the margin position.
    margin = hlx.bonds.repo_margin(repo, market)
    print(f"npv               {repo.npv(market):.2f}")
    for field in ("exposure", "collateral_value", "required", "excess", "haircut"):
        print(f"{field:<17} {margin[field]:.6f}")
    
    # 6. Compare with the cash and the haircut worked out from the terms.
    cash = NOMINAL * PRICE / 100.0 / MARGIN_RATIO
    haircut = 1.0 - 1.0 / MARGIN_RATIO
    print(f"cash by hand      {cash:.6f}")
    print(f"haircut by hand   {haircut:.6f}")
    npv               -2099.11
    exposure          9754901.960784
    collateral_value  9647684.081304
    required          9950000.000000
    excess            -302315.918696
    haircut           0.019608
    cash by hand      9754901.960784
    haircut by hand   0.019608

    Check it worked

    • exposure equals the cash by hand: the collateral's value at the booked price, divided by the margin ratio.
    • haircut is one minus one over the margin ratio. 102% margin is a 1.96% haircut, not 2%.
    • required is the margin ratio times the exposure, and excess is collateral_value less required. A negative excess is a margin call of that size: here the bond priced off the curve is worth less than the 99.5 the cash was lent against.
    • To margin the collateral at a quoted price rather than off the curve, pass price= to repo_margin.
    • Repo, the reference for every repo argument.
    • How financing works, for why a repo is held to a margin.
    • Repo spread curves, for the GC curve.

    From the 0.0 line at revision bf7f2e2. Every printed number is that revision's own output.