Esc

    The API is under testing and may change before release.

    Value a book and collect what failed

    You'll value every trade in a book that can be valued, and get a list of the ones that couldn't, each with the market input it was missing, instead of stopping at the first failure.

    Before you start

    • helixio installed: see installing.
    • A market and the trades you want to value.

    Steps

    1. Build the market once.
    2. Value each trade in a book of its own, so one trade's failure can't stop the others.
    3. Catch the missing-input error (MissingMarketInputError in Python; in Rust, a pricing error whose missing_input() is set) and keep the trade id with what was missing. Let any other error stop the run: it means something other than missing data is wrong.

    The comments in the program match these steps.

    Rust
    use chrono::NaiveDate;
    use helixrs::{AnyInstrument, Book, CollateralSpec, MarketContext, QuoteSheet, Termination, template};
    
    /// The close you value at, and the desk's SOFR swap quotes at it.
    const SOFR_CLOSE_DATE: (i32, u32, u32) = (2026, 10, 1);
    const USD_SOFR_CLOSE: &[(&str, &str, f64)] = &[
        ("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),
    ];
    
    fn date((year, month, day): (i32, u32, u32)) -> NaiveDate {
        NaiveDate::from_ymd_opt(year, month, day).expect("a real calendar date")
    }
    
    fn swap(code: &str, traded: NaiveDate, rate: f64) -> Result<AnyInstrument, Box<dyn std::error::Error>> {
        Ok(template(code)?
            .swap(traded)?
            .with_maturity("5Y".parse::<Termination>()?)?
            .with_notional(1e6)
            .with_fixed_rate(rate)
            .build()?)
    }
    
    fn main() -> Result<(), Box<dyn std::error::Error>> {
        let close = date(SOFR_CLOSE_DATE);
    
        // Three trades: a fresh SOFR swap, a EUR swap, and a SOFR swap traded a week
        // before the close, which has already reset.
        let sheet = QuoteSheet::from_rows(USD_SOFR_CLOSE.iter().copied())?;
        let market = MarketContext::as_of(close).with_recipe_quotes(&CollateralSpec::Currency("USD".parse()?), &sheet)?;
        let trades = [
            ("SOFR-5Y", swap("USD-SOFR-OIS", close, 0.0466)?),
            ("ESTR-5Y", swap("EUR-ESTR-OIS", close, 0.0200)?),
            ("SEASONED", swap("USD-SOFR-OIS", date((2026, 9, 24)), 0.0466)?),
        ];
    
        // Value each trade in its own book. A missing input is kept; any other
        // error still stops the run, because it means something else is wrong.
        let mut valued = Vec::new();
        let mut refused = Vec::new();
        for (trade_id, instrument) in &trades {
            let book = Book::new().add(*trade_id, instrument.clone(), 1.0).attach_market(market.clone());
            match book.npv(close) {
                Ok(value) => valued.push((*trade_id, value)),
                Err(error) => match error.missing_input() {
                    Some((kind, key)) => refused.push((*trade_id, format!("{kind}({key})"))),
                    None => return Err(error.into()),
                },
            }
        }
        let valued_trades = valued.len();
        println!("valued    {valued_trades} of {} trades", trades.len());
        for (trade_id, what) in &refused {
            println!("refused   {trade_id:<9} missing {what}");
        }
    
        Ok(())
    }
    Python
    import datetime as dt
    
    import helixio as hlx
    
    # The close you value at, and the desk's SOFR swap quotes at it.
    SOFR_CLOSE_DATE = dt.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,
        },
    }
    
    # Three trades: a fresh SOFR swap, a EUR swap, and a SOFR swap traded a week
    # before the close, which has already reset.
    market = hlx.MarketContext(SOFR_CLOSE_DATE).with_recipe_quotes("USD", USD_SOFR_CLOSE)
    sofr, estr = hlx.templates.get("USD-SOFR-OIS"), hlx.templates.get("EUR-ESTR-OIS")
    trades = [
        ("SOFR-5Y", sofr.swap(SOFR_CLOSE_DATE).with_maturity("5Y").with_notional(1e6).with_fixed_rate(0.0466).build()),
        ("ESTR-5Y", estr.swap(SOFR_CLOSE_DATE).with_maturity("5Y").with_notional(1e6).with_fixed_rate(0.0200).build()),
        ("SEASONED", sofr.swap(dt.date(2026, 9, 24)).with_maturity("5Y").with_notional(1e6).with_fixed_rate(0.0466).build()),
    ]
    
    # Value each trade in its own book. A missing input is caught and kept; any
    # other error still stops the run, because it means something else is wrong.
    valued, refused = [], []
    for trade_id, instrument in trades:
        book = hlx.Book().add(trade_id, instrument, 1.0)
        book.attach_market(market)
        try:
            valued.append((trade_id, book.npv(as_of=SOFR_CLOSE_DATE)))
        except hlx.MissingMarketInputError as error:
            refused.append((trade_id, f"{error.kind}({error.key})"))
    valued_trades = len(valued)
    print(f"valued    {valued_trades} of {len(trades)} trades")
    for trade_id, what in refused:
        print(f"refused   {trade_id:<9} missing {what}")
    valued    1 of 3 trades
    refused   ESTR-5Y   missing discount(EUR)
    refused   SEASONED  missing fixing(USD-SOFR@2026-09-28)

    Check it worked

    • The trades the market covers are valued.
    • Each refused trade names its missing input in the market's own spelling, such as discount(EUR) for a curve or fixing(USD-SOFR@2026-09-28) for a print.
    • Add the missing curve or print and run again; the trade moves from refused to valued.
    • Errors, the reference for every error class and what it names.
    • Books carry their market, for valuing a whole book at once.

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