Esc

    The API is under testing and may change before release.

    Use your own calendar

    You'll get a business-day calendar the shipped data doesn't carry: a shipped one with an extra closure, one you build outright with its own weekend, or one you ingest under a code so every later lookup reads it.

    Before you start

    • helixio installed, in Rust or Python: see installing.
    • The closures you need, as dates. For a calendar you build outright, its weekend days too.

    Steps

    1. To add a closure to a shipped calendar, call amended on it with the closing dates. You get a new calendar; the shipped one is unchanged.
    2. To build a calendar outright, give Calendar.from_holidays a name, the closures and the weekend days (in Rust, DataCalendar::new with a weekend mask indexed from Monday).
    3. To make a code answer everywhere in the process, ingest it with ingest_calendars. From then on Calendar("XDSK") and joint expressions such as "XDSK,USNY" read it. Ingest before you draft trades: a trade drafted earlier keeps its dates.

    The comments in the program match these steps.

    Rust
    use std::collections::HashSet;
    
    use chrono::{Datelike, Duration, NaiveDate, Weekday};
    use helixrs::{Calendar, CalendarSpec, DataCalendar, amended, calendar, ingest_calendars, ingested_calendars};
    
    fn date(year: i32, month: u32, day: u32) -> NaiveDate {
        NaiveDate::from_ymd_opt(year, month, day).expect("a real calendar date")
    }
    
    fn main() -> Result<(), Box<dyn std::error::Error>> {
        let new_york = calendar("USNY")?;
        let year_start = date(2031, 1, 1);
        let year_end = date(2032, 1, 1);
        let closure = date(2031, 7, 3);
    
        // Amend a shipped calendar with a closure it does not carry. Calendars are
        // never edited: you get a new one, and New York itself is unchanged.
        let derived = amended(new_york.as_ref(), &[closure], &[])?;
        let shipped_days = new_york.business_days_between(year_start, year_end);
        let amended_days = derived.business_days_between(year_start, year_end);
        let open = |c: &dyn Calendar| if c.is_business_day(closure) { "open" } else { "closed" };
        println!("{closure} in {:<14} {}", derived.name(), open(derived.as_ref()));
        println!("{closure} in {:<14} {}", new_york.name(), open(new_york.as_ref()));
        println!("open days in 2031: {amended_days} amended, {shipped_days} shipped");
    
        // Build a calendar outright, here with a Friday-Saturday weekend and one
        // closure. The weekend mask is indexed from Monday.
        let mut weekmask = [false; 7];
        weekmask[Weekday::Fri.num_days_from_monday() as usize] = true;
        weekmask[Weekday::Sat.num_days_from_monday() as usize] = true;
        let desk = DataCalendar::new("DESK", HashSet::from([date(2031, 3, 20)]), weekmask);
        let desk_next = desk.next_business_day(date(2031, 3, 20));
        println!(
            "DESK: after its 2031-03-20 closure, next open day {desk_next} ({})",
            desk_next.format("%a")
        );
    
        // Ingest a calendar under a code. From then on every lookup of that code in
        // the process reads it, alone or joined with a shipped centre.
        ingest_calendars(&[CalendarSpec::new("XDSK", vec![date(2031, 3, 20)])])?;
        let ingested_days = calendar("XDSK")?.business_days_between(year_start, year_end);
        let joined_days = calendar("XDSK,USNY")?.business_days_between(year_start, year_end);
        println!("XDSK       open days in 2031  {ingested_days}");
        println!("XDSK,USNY  open days in 2031  {joined_days}");
        println!("ingested codes  {}", ingested_calendars().join(", "));
    
        Ok(())
    }
    Python
    import datetime as dt
    
    import helixio as hlx
    
    NEW_YORK = hlx.Calendar("USNY")
    YEAR_START = dt.date(2031, 1, 1)
    YEAR_END = dt.date(2032, 1, 1)
    CLOSURE = dt.date(2031, 7, 3)
    
    # Amend a shipped calendar with a closure it does not carry. Calendars are
    # never edited: you get a new one, and New York itself is unchanged.
    amended = NEW_YORK.amended(closing=[CLOSURE])
    shipped_days = NEW_YORK.business_days_between(YEAR_START, YEAR_END)
    amended_days = amended.business_days_between(YEAR_START, YEAR_END)
    print(f"{CLOSURE} in {amended.name():<14} {'open' if amended.is_business_day(CLOSURE) else 'closed'}")
    print(f"{CLOSURE} in {NEW_YORK.name():<14} {'open' if NEW_YORK.is_business_day(CLOSURE) else 'closed'}")
    print(f"open days in 2031: {amended_days} amended, {shipped_days} shipped")
    
    # Build a calendar outright, here with a Friday-Saturday weekend and one closure.
    desk = hlx.Calendar.from_holidays("DESK", [dt.date(2031, 3, 20)], ["Friday", "Saturday"])
    desk_next = desk.next_business_day(dt.date(2031, 3, 20))
    print(f"DESK: after its 2031-03-20 closure, next open day {desk_next} ({desk_next.strftime('%a')})")
    
    # Ingest a calendar under a code. From then on every lookup of that code in the
    # process reads it, alone or joined with a shipped centre.
    hlx.dates.ingest_calendars([hlx.dates.CalendarSpec("XDSK", [dt.date(2031, 3, 20)])])
    ingested_days = hlx.Calendar("XDSK").business_days_between(YEAR_START, YEAR_END)
    joined_days = hlx.Calendar("XDSK,USNY").business_days_between(YEAR_START, YEAR_END)
    print(f"XDSK       open days in 2031  {ingested_days}")
    print(f"XDSK,USNY  open days in 2031  {joined_days}")
    print(f"ingested codes  {', '.join(hlx.dates.ingested_calendars())}")
    2031-07-03 in <USNY amended> closed
    2031-07-03 in USNY           open
    open days in 2031: 249 amended, 250 shipped
    DESK: after its 2031-03-20 closure, next open day 2031-03-23 (Sun)
    XDSK       open days in 2031  260
    XDSK,USNY  open days in 2031  249
    ingested codes  XDSK

    Check it worked

    • The amended calendar is closed on the day you named, and the shipped one is still open.
    • The year loses exactly one open day per closure you added.
    • The built calendar treats its own weekend days as closed, and the next open day after its closure respects that weekend.
    • ingested_calendars() lists your code.
    • Calendars, the reference for every calendar method.
    • Dates and periods, for advancing by tenors on a calendar.

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