Esc

    The API is under testing and may change before release.

    Build a leg from a schedule

    You'll get a fixed leg and a floating leg from a confirmation's terms: the schedule's periods, a payment lag, the coupons over each period, and the side each leg is on. Nothing here needs a market; you value the legs later against whatever market you have.

    Before you start

    • helixio installed, in Rust or Python: see installing.
    • The terms from the confirmation: effective and termination dates, frequency, calendar, payment lag, notional, and each leg's rate or index and day count.
    • If the term isn't a whole number of periods, which stub rule the confirmation elects for the floating leg. The rules are listed on from a schedule to a leg.

    Steps

    1. Generate the schedule with dates.schedule(effective, termination, frequency, calendar). It measures periods back from the termination date, so a short period lands at the front. In Rust, Schedule::generate takes the roll convention and the stub position as arguments.
    2. Get one payment date per period with schedule.paying(lag, calendar). In Rust the lag is a PaymentRule::business_days(lag, calendar) inside LegTerms.
    3. Build each strip over the schedule: legs.fixed_rate_leg with the fixed rate, and legs.ibor_leg with the index and the stub rule as stub=.
    4. Wrap each strip in a Leg with its side, "Pay" or "Receive", and the key it's discounted on. In Rust the side is in LegTerms, and the strip builders return the Leg.
    5. Read the coupons back to check the dates and amounts against the confirmation.

    The comments in the program match these steps.

    Rust
    use chrono::NaiveDate;
    use helixrs::{
        AnyIndex, Direction, LegTerms, PaymentRule, Schedule, StubRule, calendar, fixed_rate_leg,
        ibor_leg_with_stubs, index,
    };
    
    type Outcome<T> = Result<T, Box<dyn std::error::Error>>;
    
    fn date((year, month, day): (i32, u32, u32)) -> NaiveDate {
        NaiveDate::from_ymd_opt(year, month, day).expect("a real calendar date")
    }
    
    fn main() -> Outcome<()> {
        let (effective, termination) = (date((2026, 11, 20)), date((2029, 8, 20)));
        let (notional, fixed_rate) = (5_000_000.0, 0.0275);
    
        // Semiannual periods on the TARGET calendar, measured back from the end, so the
        // first period is the short one.
        let target = calendar("EUTA")?;
        let schedule = Schedule::generate(
            effective,
            termination,
            "6M".parse()?,
            target.as_ref(),
            "ModFollowing".parse()?,
            "None".parse()?,
            "ShortInitial".parse()?,
            false,
            None,
            None,
        )?;
        let paying = PaymentRule::business_days(2, target.clone());
    
        // The fixed strip pays 2.75% on 30E/360; the floating strip reads EURIBOR 6M on
        // ACT/360, its short first period at the index's own six-month window.
        let terms = |day_count: &str, direction: Direction| -> Outcome<LegTerms> {
            Ok(LegTerms {
                notional,
                day_count: day_count.parse()?,
                currency: "EUR".parse()?,
                direction,
                payment: paying.clone(),
            })
        };
        let Some(AnyIndex::Ibor(euribor)) = index("EUR-EURIBOR-6M") else {
            panic!("a registered term index");
        };
        let fixed = fixed_rate_leg(&schedule, &terms("30E/360", Direction::Pay)?, fixed_rate);
        let floating = ibor_leg_with_stubs(
            &schedule,
            &terms("ACT/360", Direction::Receive)?,
            &euribor,
            1.0,
            0.0,
            &StubRule::IndexTenor,
        )?;
    
        // Each fixed coupon: its accrual, its payment date and its amount.
        for coupon in fixed.coupons() {
            println!(
                "{}  {}  pays {}  {:.6}  {:10.2}",
                coupon.accrual_start().expect("an accrual start"),
                coupon.accrual_end().expect("an accrual end"),
                coupon.payment_date(),
                coupon.year_fraction(),
                coupon.amount().expect("a fixed amount")
            );
        }
        println!("fixed     {} coupons, {:?}", fixed.coupons().len(), fixed.direction());
        println!("floating  {} coupons, {:?}", floating.coupons().len(), floating.direction());
    
        Ok(())
    }
    Python
    import datetime as dt
    
    import helixio as hlx
    
    EFFECTIVE, TERMINATION = dt.date(2026, 11, 20), dt.date(2029, 8, 20)
    NOTIONAL, FIXED_RATE = 5_000_000.0, 0.0275
    
    # Semiannual periods on the TARGET calendar, measured back from the end, so the
    # first period is the short one.
    target = hlx.Calendar("EUTA")
    schedule = hlx.dates.schedule(EFFECTIVE, TERMINATION, "6M", target)
    paying = schedule.paying(2, target)
    
    # The fixed strip pays 2.75% on 30E/360; the floating strip reads EURIBOR 6M on
    # ACT/360, its short first period at the index's own six-month window.
    fixed = hlx.Leg(
        hlx.legs.fixed_rate_leg(
            schedule, paying, "EUR", notional=NOTIONAL, fixed_rate=FIXED_RATE, day_count="30E/360"
        ),
        "Pay",
        "EUR",
    )
    floating = hlx.Leg(
        hlx.legs.ibor_leg(
            schedule, paying, "EUR", notional=NOTIONAL, index="EUR-EURIBOR-6M", day_count="ACT/360",
            stub="index_tenor",
        ),
        "Receive",
        "EUR",
    )
    
    # Each fixed coupon: its accrual, its payment date and its amount.
    for coupon in fixed.coupons():
        print(
            f"{coupon.accrual_start()}  {coupon.accrual_end()}  pays {coupon.payment_date()}"
            f"  {coupon.year_fraction():.6f}  {coupon.amount():10.2f}"
        )
    print(f"fixed     {len(fixed.coupons())} coupons, {fixed.direction()}")
    print(f"floating  {len(floating.coupons())} coupons, {floating.direction()}")
    2026-11-20  2027-02-22  pays 2027-02-24  0.255556    35138.89
    2027-02-22  2027-08-20  pays 2027-08-24  0.494444    67986.11
    2027-08-20  2028-02-21  pays 2028-02-23  0.502778    69131.94
    2028-02-21  2028-08-21  pays 2028-08-23  0.500000    68750.00
    2028-08-21  2029-02-20  pays 2029-02-22  0.497222    68368.06
    2029-02-20  2029-08-20  pays 2029-08-22  0.500000    68750.00
    fixed     6 coupons, Pay
    floating  6 coupons, Receive

    Check it worked

    • The first period is the short one, and its accrual fraction is under a half year.
    • Each payment date is the period's end plus the lag in business days: here 20 February 2027 is a Saturday, so that period ends on Monday 22 February and pays on Wednesday 24 February.
    • Each fixed amount is notional × rate × accrual fraction.
    • Both legs have one coupon per period, on the side you gave them.
    • To value the legs, call leg.npv(market); a market missing a curve a leg needs refuses with the curve named.
    • From a schedule to a leg, the reference for periods, payment lags and stub rules.
    • Legs and coupons, for every strip and coupon argument.
    • Schedules, for the schedule generator itself.

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