Esc

    The API is under testing and may change before release.

    Conventions and templates

    This page lists how you resolve a template by its code, draft a quoted swap from it by tenor, override one of its conventions, and read the conventions a swap will carry before you book it. A template is a market's standing conventions for one kind of trade, under one code: USD-SOFR-OIS holds the calendar, spot lag, frequencies, day counts, payment lag and floating index a dollar SOFR swap carries.

    Quick example

    This resolves USD-SOFR-OIS, drafts a five-year and a 2Y2Y swap from it, and prints the conventions a swap on it will carry.

    Rust
    use chrono::{Datelike, NaiveDate, Weekday};
    use helixrs::statics::templates::registry;
    use helixrs::{
        AnyInstrument, AnyTemplate, CurveError, ForwardTenor, Period, add_period,
        swap_builder_on_overnight_template, swap_builder_on_tenor,
    };
    
    fn date(year: i32, month: u32, day: u32) -> NaiveDate {
        NaiveDate::from_ymd_opt(year, month, day).expect("a real calendar date")
    }
    
    fn tenor(spelling: &str) -> ForwardTenor {
        spelling.parse().expect("a tenor spelling")
    }
    
    fn period(spelling: &str) -> Period {
        spelling.parse().expect("a period spelling")
    }
    
    /// A quoted swap on `template`, `spelling` from spot.
    fn quoted_swap(
        template: &AnyTemplate,
        valuation: NaiveDate,
        spelling: &str,
        notional: f64,
    ) -> Result<AnyInstrument, CurveError> {
        swap_builder_on_tenor(valuation, tenor(spelling), template, "USD-SOFR")?
            .with_notional(notional)
            .with_fixed_rate(0.0385)
            .build()
    }
    
    /// The first accrual start and the last accrual end of the fixed leg.
    fn accrual_span(instrument: &AnyInstrument) -> (NaiveDate, NaiveDate) {
        let legs = instrument.legs().expect("a swap has legs");
        let fixed = legs[0].coupons();
        (
            fixed[0].accrual_start().expect("an accruing coupon"),
            fixed[fixed.len() - 1].accrual_end().expect("an accruing coupon"),
        )
    }
    
    fn main() -> Result<(), Box<dyn std::error::Error>> {
        let valuation = date(2026, 9, 25); // a Friday
    
        // One code resolves to the whole convention set for a USD SOFR overnight
        // swap.
        let sofr = registry::lookup("USD-SOFR-OIS").expect("a registered template");
        let AnyTemplate::Ois(conventions) = &sofr else {
            panic!("USD-SOFR-OIS resolves to an overnight swap template");
        };
        let spot = conventions.effective_date(valuation);
        println!("code       {}", conventions.name);
        println!("family     {}", sofr.family());
        println!("spot lag   {}", conventions.spot_lag);
        println!("spot date  {spot}");
    
        // A quoted swap is asked for by tenor. "5Y" starts at spot; "2Y2Y" starts
        // two years after spot and runs two more. The notional and rate are the
        // only terms the caller states.
        let five_year = quoted_swap(&sofr, valuation, "5Y", 100_000_000.0)?;
        let forward = quoted_swap(&sofr, valuation, "2Y2Y", 100_000_000.0)?;
        let (five_start, five_end) = accrual_span(&five_year);
        let (forward_start, forward_end) = accrual_span(&forward);
        println!("5Y    {five_start} to {five_end}");
        println!("2Y2Y  {forward_start} to {forward_end}");
    
        // Every convention a swap on a template will carry, read before anything is
        // booked: the same builder that books the swap states its terms as a table.
        let rows = swap_builder_on_overnight_template(date(2026, 10, 5), date(2031, 10, 5), "USD-SOFR-OIS")?
            .conventions();
        for (field, value) in &rows {
            println!("{field:<24} {value}");
        }
    
        Ok(())
    }
    Python
    import datetime as dt
    
    import helixio as hlx
    
    VALUATION = dt.date(2026, 9, 25)  # a Friday
    
    def accrual_span(instrument: hlx.Instrument) -> tuple[dt.date, dt.date]:
        """The first accrual start and the last accrual end of the fixed leg."""
        fixed = instrument.legs()[0].coupons()
        return fixed[0].accrual_start(), fixed[-1].accrual_end()
    
    # One code resolves to the whole convention set for a USD SOFR overnight swap.
    sofr = hlx.templates.get("USD-SOFR-OIS")
    spot = sofr.spot_date(VALUATION)
    print(f"code       {sofr.code()}")
    print(f"family     {sofr.family()}")
    print(f"spot lag   {sofr.spot_lag()}")
    print(f"spot date  {spot}")
    
    # A quoted swap is asked for by tenor. "5Y" starts at spot; "2Y2Y" starts two
    # years after spot and runs two more. The notional and rate are the only terms
    # the caller states.
    five_year = sofr.vanilla_swap(VALUATION, "5Y", fixed_rate=0.0385, notional=100_000_000)
    forward = sofr.vanilla_swap(VALUATION, "2Y2Y", fixed_rate=0.0385, notional=100_000_000)
    five_start, five_end = accrual_span(five_year)
    forward_start, forward_end = accrual_span(forward)
    print(f"5Y    {five_start} to {five_end}")
    print(f"2Y2Y  {forward_start} to {forward_end}")
    
    # Every convention a swap on a template will carry, read before anything is
    # booked: the same builder that books the swap states its terms as a table.
    table = hlx.templates.swap_conventions(dt.date(2026, 10, 5), dt.date(2031, 10, 5), template="USD-SOFR-OIS")
    for field, value in zip(table.to_dict()["field"], table.to_dict()["value"]):
        print(f"{field:<24} {value}")
    code       USD-SOFR-OIS
    family     OIS
    spot lag   2D
    spot date  2026-09-29
    5Y    2026-09-29 to 2031-09-29
    2Y2Y  2028-09-29 to 2030-09-30
    family                   OvernightIndexedSwap
    currency                 USD
    index                    USD-SOFR
    effective                2026-10-05
    termination              2031-10-05
    fixed_frequency          1Y
    float_frequency          1Y
    fixed_day_count          ACT/360
    float_day_count          ACT/360
    calendar                 USNY.SOFR
    business_day_convention  MODFOLLOWING
    roll                     None
    stub                     ShortFinal
    payment_lag              2 BusinessDays FOLLOWING on USNY.SOFR
    float_payment_lag        2 BusinessDays FOLLOWING on USNY.SOFR
    accumulation             Compounded

    How it's priced

    A trade is its template's conventions CC plus its own terms θ\theta:

    trade=(C,θ),θ=(trade date or start, tenor, notional, rate, side).\text{trade} = (C, \theta), \qquad \theta = (\text{trade date or start},\ \text{tenor},\ \text{notional},\ \text{rate},\ \text{side}).

    A quoted swap starts at spot: the trade date stepped by the spot lag on the template's calendar. A forward tenor 2Y2Y starts two years after spot and runs two more. Why a market standardises these conventions is covered in how dates and conventions work.

    API

    Symbol Meaning Rust Python
    CC a template, by code template templates.get
    spot the trade date plus the spot lag AnyTemplate::spot_date Template.spot_date
    (C,θ)(C, \theta) a trade drafted on a template AnyTemplate::swap → ProductBuilder Template.swap → products.ProductBuilder
    an override a new template with one convention changed the with_* methods of each template family Template.with_payment_lag and the other Template.with_* methods
    CC as a table the conventions a swap will carry swap_builder_on_overnight_template(…).conventions() templates.swap_conventions
    Argument Type Default Meaning
    code str required templates.get(code): the registry code, such as "USD-SOFR-OIS"
    valuation date required Template.spot_date(valuation): the trade date spot is counted from
    trade_date date required Template.swap(trade_date): opens a ProductBuilder with every term defaulted, effective at spot, ten years, 10,000,000, receiving fixed
    effective date required Template.vanilla_swap: with a date termination, the first accrual date; with a tenor, the valuation date, and the swap starts at spot
    termination date or str required Template.vanilla_swap: an end date, or a tenor such as "5Y" or "2Y2Y"
    fixed_rate float required Template.vanilla_swap: the fixed rate
    notional float required Template.vanilla_swap: the notional
    pays_fixed bool False Template.vanilla_swap: pay fixed instead of receiving it
    accrual_compounding AccrualCompounding, str or None None (the template's) Template.vanilla_swap: how the floating leg compounds
    days int required with_payment_lag(days), with_spot_lag(days): the new lag in business days
    roll_convention RollConvention or str required with_roll_convention: a day of the month ("20"), "EOM" or "IMM"
    template, index str or None None templates.swap_conventions(effective, termination, *, template=, index=): name the template, or the index whose template is used

    Defaults and limits

    • A template is a value. An override returns a new template and leaves the registered one unchanged, so you can use both side by side.
    • Overrides are accepted only where a method reads them. with_payment_lag works for the overnight and vanilla swap families; with_spot_lag only where a constructor reads the spot lag.
    • A template refuses a request its family can't honour. Asking a deposit template for a swap raises ValueError naming the code and the family.
    • An unknown code raises ValueError listing the codes that do resolve. The full list is in the template registry.

    More examples

    This program overrides the payment lag and the spot lag, and shows the refusals:

    Rust
    use chrono::{Datelike, NaiveDate, Weekday};
    use helixrs::statics::templates::registry;
    use helixrs::{
        AnyInstrument, AnyTemplate, BusinessDayConvention, CurveError, ForwardTenor, Period,
        RollConvention, add_period, swap_builder_on_overnight_template, swap_builder_on_tenor,
    };
    
    fn date(year: i32, month: u32, day: u32) -> NaiveDate {
        NaiveDate::from_ymd_opt(year, month, day).expect("a real calendar date")
    }
    
    fn tenor(spelling: &str) -> ForwardTenor {
        spelling.parse().expect("a tenor spelling")
    }
    
    fn period(spelling: &str) -> Period {
        spelling.parse().expect("a period spelling")
    }
    
    /// A quoted swap on `template`, `spelling` from spot.
    fn quoted_swap(
        template: &AnyTemplate,
        valuation: NaiveDate,
        spelling: &str,
        notional: f64,
    ) -> Result<AnyInstrument, CurveError> {
        swap_builder_on_tenor(valuation, tenor(spelling), template, "USD-SOFR")?
            .with_notional(notional)
            .with_fixed_rate(0.0385)
            .build()
    }
    
    /// The first accrual start and the last accrual end of the fixed leg.
    fn accrual_span(instrument: &AnyInstrument) -> (NaiveDate, NaiveDate) {
        let legs = instrument.legs().expect("a swap has legs");
        let fixed = legs[0].coupons();
        (
            fixed[0].accrual_start().expect("an accruing coupon"),
            fixed[fixed.len() - 1].accrual_end().expect("an accruing coupon"),
        )
    }
    
    fn main() -> Result<(), Box<dyn std::error::Error>> {
        let valuation = date(2026, 9, 25); // a Friday
        let sofr = registry::lookup("USD-SOFR-OIS").expect("a registered template");
        let AnyTemplate::Ois(conventions) = &sofr else {
            panic!("USD-SOFR-OIS resolves to an overnight swap template");
        };
        let spot = conventions.effective_date(valuation);
        let (five_start, five_end) = accrual_span(&quoted_swap(&sofr, valuation, "5Y", 100_000_000.0)?);
    
        // `with_payment_lag` consumes a copy and returns the changed one, so both
        // templates can be used side by side.
        let same_day = AnyTemplate::from(conventions.clone().with_payment_lag(0));
        let lagged_swap = quoted_swap(&sofr, valuation, "5Y", 100_000_000.0)?;
        let same_day_swap = quoted_swap(&same_day, valuation, "5Y", 100_000_000.0)?;
        let lagged_pays = lagged_swap.final_payment_date().expect("a last payment");
        let same_day_pays = same_day_swap.final_payment_date().expect("a last payment");
        println!("registered template pays  {lagged_pays}");
        println!("payment lag of zero pays  {same_day_pays}");
    
        // The spot lag is overridden the same way. With no lag the quoted swap
        // starts on the trade date itself, a business day, and ends five years later.
        let same_day_start = AnyTemplate::from(conventions.clone().with_spot_lag(0));
        let start_swap = quoted_swap(&same_day_start, valuation, "5Y", 100_000_000.0)?;
        println!("spot lag of zero starts   {}", accrual_span(&start_swap).0);
    
        // A template refuses a request its family cannot honour. Asked for a swap,
        // a deposit template is refused rather than read as something it is not.
        let deposit_code = "USD-SOFR-DEPO";
        let deposit = registry::lookup(deposit_code).expect("a registered template");
        let swap_refused = quoted_swap(&deposit, valuation, "5Y", 100_000_000.0).is_err();
    
        // An unknown code resolves to nothing.
        let unknown_refused = registry::lookup("USD-SOFR-SWAP").is_none();
    
        // A swap asked for by an overnight template name refuses a vanilla code,
        // and its refusal lists exactly the codes it accepts.
        let refused = "EUR-EURIBOR-6M-IRS";
        let overnight = match swap_builder_on_overnight_template(date(2026, 9, 29), date(2031, 9, 29), refused)
        {
            Err(CurveError::UnknownIndex(unknown)) => unknown.known.clone(),
            _ => panic!("{refused} names a vanilla template and must be refused"),
        };
        let verdict = |refused: bool| if refused { "refused" } else { "accepted" };
        println!("{deposit_code} asked for a swap   {} ({} template)", verdict(swap_refused), deposit.family());
        println!("USD-SOFR-SWAP asked for         {} (not registered)", verdict(unknown_refused));
        println!("overnight swap templates        {}", overnight.len());
    
        Ok(())
    }
    Python
    import datetime as dt
    
    import helixio as hlx
    
    VALUATION = dt.date(2026, 9, 25)  # a Friday
    
    def accrual_span(instrument: hlx.Instrument) -> tuple[dt.date, dt.date]:
        """The first accrual start and the last accrual end of the fixed leg."""
        fixed = instrument.legs()[0].coupons()
        return fixed[0].accrual_start(), fixed[-1].accrual_end()
    
    sofr = hlx.templates.get("USD-SOFR-OIS")
    
    # `with_payment_lag` returns a new template, so both can be used side by side.
    same_day = sofr.with_payment_lag(0)
    lagged_swap = sofr.vanilla_swap(VALUATION, "5Y", fixed_rate=0.0385, notional=100_000_000)
    same_day_swap = same_day.vanilla_swap(
        VALUATION, "5Y", fixed_rate=0.0385, notional=100_000_000
    )
    lagged_pays = lagged_swap.final_payment_date()
    same_day_pays = same_day_swap.final_payment_date()
    print(f"registered template pays  {lagged_pays}")
    print(f"payment lag of zero pays  {same_day_pays}")
    
    # The spot lag is overridden the same way. With no lag the quoted swap starts
    # on the trade date itself, a business day, and ends five years later.
    same_day_start = sofr.with_spot_lag(0)
    start_swap = same_day_start.vanilla_swap(VALUATION, "5Y", fixed_rate=0.0385, notional=100_000_000)
    print(f"spot lag of zero starts   {accrual_span(start_swap)[0]}")
    
    # A template refuses a request its family cannot honour, and the refusal names
    # the code and the family instead of returning something unchanged.
    DEPOSIT = "USD-SOFR-DEPO"
    deposit = hlx.templates.get(DEPOSIT)
    try:
        deposit.vanilla_swap(VALUATION, "5Y", fixed_rate=0.0385, notional=100_000_000)
        swap_refused = False
    except ValueError as refusal:
        swap_refused = DEPOSIT in str(refusal) and deposit.family() in str(refusal)
    
    # An unknown code is refused at lookup, and the refusal lists the codes that
    # do resolve.
    try:
        hlx.templates.get("USD-SOFR-SWAP")
        unknown_refused = False
    except ValueError as refusal:
        listed = str(refusal).split("known: ")[1].split(", ")
        unknown_refused = "USD-SOFR-SWAP" not in listed
    
    # The registry lists every template with its family. The overnight swap
    # templates are the ones whose family is OIS.
    listed = hlx.templates.catalogue()
    overnight = [code for code, family in zip(listed["code"], listed["family"]) if family == "OIS"]
    print(f"{DEPOSIT} asked for a swap   {'refused' if swap_refused else 'accepted'} ({deposit.family()} template)")
    print(f"USD-SOFR-SWAP asked for         {'refused' if unknown_refused else 'accepted'} (not registered)")
    print(f"overnight swap templates        {len(overnight)}")
    registered template pays  2031-10-01
    payment lag of zero pays  2031-09-29
    spot lag of zero starts   2026-09-25
    USD-SOFR-DEPO asked for a swap   refused (deposit template)
    USD-SOFR-SWAP asked for         refused (not registered)
    overnight swap templates        23

    See also

    • Source: crates/helixrs/src/statics/templates/registry.rs, crates/helixrs/src/statics/templates/irs.rs, crates/helixrs/src/products.rs
    • Related pages:
      • How dates and conventions work, for why templates exist and how a swap's dates follow from them.
      • Your first trade, for a template used end to end.
      • Calendars, for the spot lag's calendar.
      • Day counts, for the day counts a template names.

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