Esc

    The API is under testing and may change before release.

    Connect a data source

    You'll plug your own market data into helixio: the market asks your source for exactly what a book needs, prices on the answer, and tells you every gap at once when the answer is incomplete.

    Before you start

    • helixio installed: see installing.
    • Access to your quotes and fixings from code: a database, a file, a service. The program uses a sheet held in memory so it runs offline.

    Steps

    1. Write a source: any object with fetch(demand, as_of) in Python, or a type implementing MarketDataSource in Rust. It returns the strips and prints the demand asks for.
    2. Give the market your source with with_data_source, and ask it demand(book) to see what the book needs: quote strips by template, and prints by series and date.
    3. Attach the market to the book. In Python, attaching fetches the market's own date; in Rust, call fetch_market_data for it. Read fetch_report() to see what was answered.
    4. Value the book as usual.

    The comments in the program match these steps.

    Rust
    use std::sync::Arc;
    
    use chrono::NaiveDate;
    use helixrs::{
        Book, DataSource, Demand, Locator, MarketContext, MarketData, MarketDataSource, SourceError,
        StripQuotes, Termination, template,
    };
    
    type Outcome<T> = Result<T, Box<dyn std::error::Error>>;
    
    /// What your source holds: the close, its quotes across the curve's segments,
    /// and the SOFR prints before the close.
    const SOFR_CLOSE_DATE: (i32, u32, u32) = (2026, 10, 1);
    const USD_SOFR_LADDER: &[(&str, &str, f64)] = &[
        ("USD-SOFR-DEPO", "1W", 0.0389),
        ("USD-SOFR-MeetingOIS", "2026-10-28", 0.0388),
        ("USD-SOFR-3M-FUT", "MAR27", 95.65),
        ("USD-SOFR-3M-FUT", "JUN27", 95.55),
        ("USD-SOFR-OIS", "2Y", 0.0459),
        ("USD-SOFR-OIS", "5Y", 0.0471),
        ("USD-SOFR-OIS", "10Y", 0.0483),
    ];
    const USD_SOFR_PRINTS: &[(&str, (i32, u32, u32), f64)] = &[
        ("USD-SOFR", (2026, 9, 28), 0.0390),
        ("USD-SOFR", (2026, 9, 29), 0.0388),
        ("USD-SOFR", (2026, 9, 30), 0.0390),
    ];
    
    fn date((year, month, day): (i32, u32, u32)) -> NaiveDate {
        NaiveDate::from_ymd_opt(year, month, day).expect("a real calendar date")
    }
    
    /// Answers every strip it holds that the market asks for, and its prints.
    struct DeskSheet {
        sheet: Vec<(&'static str, &'static str, f64)>,
    }
    
    impl MarketDataSource for DeskSheet {
        fn fetch(&self, demand: &Demand, as_of: NaiveDate) -> Result<MarketData, SourceError> {
            let unreadable = |message: String| SourceError::Source {
                source: "desk sheet".to_owned(),
                message,
            };
            let mut strips = Vec::new();
            for asked in &demand.strips {
                let mut entries = Vec::new();
                for (_, locator, quote) in self.sheet.iter().filter(|(t, _, _)| *t == asked.template) {
                    let at = Locator::parse(locator).map_err(|e| unreadable(e.to_string()))?;
                    entries.push((at, *quote));
                }
                if !entries.is_empty() {
                    strips.push(StripQuotes {
                        template: asked.template.clone(),
                        observed: as_of,
                        entries,
                    });
                }
            }
            let fixings = USD_SOFR_PRINTS
                .iter()
                .map(|(series, day, value)| ((*series).to_owned(), date(*day), *value))
                .collect();
            Ok(MarketData {
                strips,
                fixings,
                fx: Vec::new(),
            })
        }
    }
    
    fn sourced(close: NaiveDate, only: Option<&str>) -> MarketContext {
        let sheet = USD_SOFR_LADDER
            .iter()
            .copied()
            .filter(|(template, _, _)| only.is_none_or(|kept| kept == *template))
            .collect();
        MarketContext::as_of(close).with_data_source(Some(DataSource::new(Arc::new(DeskSheet { sheet }))))
    }
    
    fn main() -> Outcome<()> {
        let close = date(SOFR_CLOSE_DATE);
    
        // Ask what a book needs. A swap traded on 24 September has accrued three days
        // by the close, so the book reads three prints, and the USD recipe four strips.
        let seasoned = template("USD-SOFR-OIS")?
            .swap(date((2026, 9, 24)))?
            .with_maturity("5Y".parse::<Termination>()?)?
            .with_notional(50_000_000.0)
            .with_fixed_rate(0.0466)
            .build()?;
        let book = Book::new().add("SOFR-5Y", seasoned.clone(), 1.0);
        let market = sourced(close, None);
        let demand = market.demand(&book)?;
        for strip in &demand.strips {
            println!("strip   {}", strip.template);
        }
        for (series, day) in &demand.fixings {
            println!("fixing  {series} {day}");
        }
    
        // Attach the market and fetch its date; Rust fetches nothing until asked.
        // The report says what came back.
        let mut book = book.attach_market(market);
        book.fetch_market_data(close)?;
        let npv = book.npv(close)?;
        let rows = &book.fetch_report().rows;
        let answered = rows.iter().filter(|(_, _, status)| *status == "answered").count();
        println!("answered {answered} of {}", rows.len());
        println!("npv      {npv:.2}");
    
        // A source that holds only the swaps leaves three strips unanswered. The
        // snapshot is refused, and the refusal names every gap at once.
        let partial = sourced(close, Some("USD-SOFR-OIS"));
        let mut thin = Book::new().add("SOFR-5Y", seasoned, 1.0).attach_market(partial);
        let gaps_refused = match thin.fetch_market_data(close) {
            Ok(_) => String::new(),
            Err(refused) => refused.to_string(),
        };
        println!(
            "refused, naming 3 gaps at once: {}",
            if gaps_refused.contains("3 gap(s)") { "yes" } else { "no" }
        );
    
        Ok(())
    }
    Python
    import datetime as dt
    
    import helixio as hlx
    
    # What your source holds: the close, its quotes across the curve's segments,
    # and the SOFR prints before the close.
    SOFR_CLOSE_DATE = dt.date(2026, 10, 1)
    USD_SOFR_LADDER = {
        "USD-SOFR-DEPO": {"1W": 0.0389},
        "USD-SOFR-MeetingOIS": {"2026-10-28": 0.0388},
        "USD-SOFR-3M-FUT": {"MAR27": 95.65, "JUN27": 95.55},
        "USD-SOFR-OIS": {"2Y": 0.0459, "5Y": 0.0471, "10Y": 0.0483},
    }
    USD_SOFR_PRINTS = [
        ("USD-SOFR", dt.date(2026, 9, 28), 0.0390),
        ("USD-SOFR", dt.date(2026, 9, 29), 0.0388),
        ("USD-SOFR", dt.date(2026, 9, 30), 0.0390),
    ]
    
    class DeskSheet:
        """Answers every strip it holds that the market asks for, and its prints."""
    
        def __init__(self, sheet):
            self.sheet = sheet
    
        def fetch(self, demand, as_of):
            asked = demand.strips.to_dict()["template"]
            strips = [
                (template, as_of, list(self.sheet[template].items()))
                for template in asked
                if template in self.sheet
            ]
            return hlx.MarketData(strips=strips, fixings=list(USD_SOFR_PRINTS))
    
    # Ask what a book needs. A swap traded on 24 September has accrued three days
    # by the close, so the book reads three prints, and the USD recipe four strips.
    seasoned = (
        hlx.templates.get("USD-SOFR-OIS")
        .swap(dt.date(2026, 9, 24))
        .with_maturity("5Y")
        .with_notional(50_000_000.0)
        .with_fixed_rate(0.0466)
        .build()
    )
    book = hlx.Book().add("SOFR-5Y", seasoned, 1.0)
    market = hlx.MarketContext(SOFR_CLOSE_DATE).with_data_source(DeskSheet(USD_SOFR_LADDER))
    demand = market.demand(book)
    for template in demand.strips.to_dict()["template"]:
        print(f"strip   {template}")
    for series, day in zip(demand.fixings.to_dict()["series"], demand.fixings.to_dict()["date"]):
        print(f"fixing  {series} {day}")
    
    # Attach the market: attaching fetches the market's own date. The report says
    # what came back.
    book.attach_market(market)
    npv = book.npv(as_of=SOFR_CLOSE_DATE)
    report = book.fetch_report().to_dict()
    print(f"answered {report['status'].count('answered')} of {len(report['status'])}")
    print(f"npv      {npv:.2f}")
    
    # A source that holds only the swaps leaves three strips unanswered. The
    # snapshot is refused, and the refusal names every gap at once.
    partial = hlx.MarketContext(SOFR_CLOSE_DATE).with_data_source(
        DeskSheet({"USD-SOFR-OIS": USD_SOFR_LADDER["USD-SOFR-OIS"]})
    )
    try:
        hlx.Book().add("SOFR-5Y", seasoned, 1.0).attach_market(partial)
        gaps_refused = ""
    except hlx.SnapshotError as refused:
        gaps_refused = str(refused)
    print(f"refused, naming 3 gaps at once: {'yes' if '3 gap(s)' in gaps_refused else 'no'}")
    strip   USD-SOFR-3M-FUT
    strip   USD-SOFR-DEPO
    strip   USD-SOFR-MeetingOIS
    strip   USD-SOFR-OIS
    fixing  USD-SOFR 2026-09-28
    fixing  USD-SOFR 2026-09-29
    fixing  USD-SOFR 2026-09-30
    answered 7 of 7
    npv      -102966.25
    refused, naming 3 gaps at once: yes

    Check it worked

    • Every row of the fetch report is answered.
    • The book values.
    • If your source can't answer a strip, the fetch is refused with one message naming every gap, not just the first.
    • Data sources, the reference for the source protocol.
    • Quote sheets and recipes, for what a strip is.

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