Scheduling

Exchange calendars — sessions, minutes, and trading index.

Included in the base pip install mktlib — no extra dependencies.

mktlib.scheduling.get_calendar(name)[source]

Get an exchange calendar by name or alias.

Return type:

ExchangeCalendar

Parameters:

name (str)

ExchangeCalendar

Core

ExchangeCalendar.valid_days(start, end)[source]

Return a Polars Series of trading days within [start, end].

Trading dates are weekdays (Mon-Fri) minus closures. For calendars with open_offset < 0 (e.g. Globex, FX), a session’s market_open may fall on the prior calendar day (e.g. Sunday 17:00 for Monday’s session), but the session is still attributed to the weekday. Minute-level methods like is_open_on_minute handle the cross-day lookup.

Return type:

Series

Parameters:
ExchangeCalendar.schedule(start, end)[source]

Return a Polars DataFrame with columns: date, market_open, market_close.

For calendars with open_offset < 0, market_open falls on the prior calendar day (e.g. Sunday 17:00 for a Monday FX session).

Return type:

DataFrame

Parameters:
ExchangeCalendar.is_session(day)[source]

Check if a given date is a trading day.

Return type:

bool

Parameters:

day (date | str)

ExchangeCalendar.get_schedule(day)[source]

Get the schedule for a single day, or None if not a trading day.

Return type:

MarketDailySchedule | None

Parameters:

day (date | str)

Session Navigation

ExchangeCalendar.next_session(day)

First trading day strictly after day.

Return type:

date

Parameters:
  • self (_CalendarProtocol)

  • day (date | str)

ExchangeCalendar.previous_session(day)

Last trading day strictly before day.

Return type:

date

Parameters:
  • self (_CalendarProtocol)

  • day (date | str)

ExchangeCalendar.session_offset(day, n)

Offset day by n trading sessions (negative = backward).

day must be a session. n=0 returns day unchanged.

Return type:

date

Parameters:
  • self (_CalendarProtocol)

  • day (date | str)

  • n (int)

ExchangeCalendar.date_to_session(day, direction='none')

Resolve day to a session.

direction: "none" (raise if not session), "next", "previous".

Return type:

date

Parameters:
  • self (_CalendarProtocol)

  • day (date | str)

  • direction (str)

ExchangeCalendar.sessions_in_range(start, end)

Count trading sessions in [start, end].

Return type:

int

Parameters:

Minute Queries

ExchangeCalendar.is_open_on_minute(dt)

Check if the exchange is open at dt. Uses [open, close) semantics.

Return type:

bool

Parameters:
  • self (_CalendarProtocol)

  • dt (datetime)

ExchangeCalendar.next_open(dt)

Next market open strictly after dt (or today’s open if before it).

Return type:

datetime

Parameters:
  • self (_CalendarProtocol)

  • dt (datetime)

ExchangeCalendar.next_close(dt)

Next market close at or after dt (today’s close if still open).

Return type:

datetime

Parameters:
  • self (_CalendarProtocol)

  • dt (datetime)

ExchangeCalendar.previous_open(dt)

Most recent market open strictly before dt.

Return type:

datetime

Parameters:
  • self (_CalendarProtocol)

  • dt (datetime)

ExchangeCalendar.previous_close(dt)

Most recent market close strictly before dt.

Return type:

datetime

Parameters:
  • self (_CalendarProtocol)

  • dt (datetime)

ExchangeCalendar.minute_to_session(dt)

Return the session date that contains dt, or None if market is closed.

Return type:

date | None

Parameters:
  • self (_CalendarProtocol)

  • dt (datetime)

Trading Helpers

ExchangeCalendar.filter_market_hours(df, date_column='date')

Filter rows to market hours using a schedule join.

More efficient than trading_index() — joins on ~252 schedule rows per year instead of materializing all trading minutes.

Parameters:
  • df (DataFrame) – DataFrame with a Datetime column for bar timestamps.

  • date_column (str) – Name of the datetime column (default "date").

  • self (_CalendarProtocol)

Returns:

Rows within [market_open, market_close - 1min], excluding lunch breaks for break calendars.

Return type:

DataFrame

ExchangeCalendar.trading_index(start, end, period='1m', closed='left')

Generate a Polars Series of trading timestamps at the given frequency.

closed: "left" (default) = [open, close), "right" = (open, close], "both" = [open, close], "none" = (open, close).

Return type:

Series

Parameters:
  • self (_CalendarProtocol)

  • start (date | str)

  • end (date | str)

  • period (str)

  • closed (Literal['left', 'right', 'both', 'none'])

Full Class Reference

class mktlib.scheduling.ExchangeCalendar(name, *, timezone, open_time, close_time, holidays, adhoc_closures=None, early_closes=None, special_closures_fn=None, special_early_closes_fn=None, exclusions=None, open_offset=0)[source]

Bases: SessionNavigationMixin, MinuteQueryMixin, TradingHelperMixin

Polars-native exchange calendar with holiday/early-close support.

Parameters:
  • name (str)

  • timezone (str)

  • open_time (time)

  • close_time (time)

  • holidays (list[HolidayRule])

  • adhoc_closures (list[AdhocClosure] | None)

  • early_closes (list[EarlyClose] | None)

  • special_closures_fn (Callable[[date, date], list[date]] | None)

  • special_early_closes_fn (Callable[[date, date], dict[date, time]] | None)

  • exclusions (set[date] | None)

  • open_offset (int)

valid_days(start, end)[source]

Return a Polars Series of trading days within [start, end].

Trading dates are weekdays (Mon-Fri) minus closures. For calendars with open_offset < 0 (e.g. Globex, FX), a session’s market_open may fall on the prior calendar day (e.g. Sunday 17:00 for Monday’s session), but the session is still attributed to the weekday. Minute-level methods like is_open_on_minute handle the cross-day lookup.

Return type:

Series

Parameters:
schedule(start, end)[source]

Return a Polars DataFrame with columns: date, market_open, market_close.

For calendars with open_offset < 0, market_open falls on the prior calendar day (e.g. Sunday 17:00 for a Monday FX session).

Return type:

DataFrame

Parameters:
is_session(day)[source]

Check if a given date is a trading day.

Return type:

bool

Parameters:

day (date | str)

get_schedule(day)[source]

Get the schedule for a single day, or None if not a trading day.

Return type:

MarketDailySchedule | None

Parameters:

day (date | str)

ExchangeCalendarWithBreaks

ExchangeCalendarWithBreaks extends ExchangeCalendar with lunch-break support (JPX, HKEX, SSE). The BreakMixin overrides four methods:

  • schedule() adds break_start / break_end columns

  • is_open_on_minute() returns False during the break window

  • trading_index() generates separate morning and afternoon ranges

  • filter_market_hours() excludes lunch bars

class mktlib.scheduling.ExchangeCalendarWithBreaks(name, *, break_start, break_end, **kwargs)[source]

Bases: BreakMixin, ExchangeCalendar

Exchange calendar with lunch break support (e.g. JPX, HKEX).

Parameters:
  • name (str)

  • break_start (time)

  • break_end (time)

  • kwargs (Any)

Custom Calendar Example

from datetime import date, time
from mktlib.scheduling import ExchangeCalendarWithBreaks, register_exchange
from mktlib.scheduling.rules import HolidayRule

cal = ExchangeCalendarWithBreaks(
    name="XSHG",
    timezone="Asia/Shanghai",
    open_time=time(9, 30),
    close_time=time(15, 0),
    break_start=time(11, 30),
    break_end=time(13, 0),
    holidays=[
        HolidayRule("New Year's Day", month=1, day=1),
        HolidayRule("National Day", month=10, day=1),
    ],
)

register_exchange("XSHG", lambda: cal, aliases=["Shanghai", "SSE"])

Holiday Rules

class mktlib.scheduling.rules.HolidayRule(name, month, day=None, weekday=None, week=None, observance=None, start_year=None, end_year=None)[source]

A recurring holiday rule.

Either (month, day) for fixed-date holidays or (month, weekday, week) for relative holidays (e.g., 3rd Monday of January).

Parameters:
dates_in_range(start, end)[source]

Generate all observed dates for this rule within [start, end].

Checks year-1 through year+1 to catch cross-year observances (e.g., New Year’s on Saturday observed as previous Friday Dec 31).

Return type:

list[date]

Parameters:
class mktlib.scheduling.rules.AdhocClosure(name, dates=<factory>)[source]

An explicit list of closure dates (e.g., 9/11, Hurricane Sandy).

Parameters:
class mktlib.scheduling.rules.EarlyClose(name, close_time, rule=None, dates=<factory>, compute_fn=None)[source]

An early close rule — either rule-based, ad-hoc dates, or compute_fn.

Parameters:
dates_in_range(start, end)[source]

All early-close dates within [start, end].

Return type:

list[date]

Parameters: