Calendars¶
finance-dates models three related ideas:
Concept |
Purpose |
|---|---|
Date ranges |
Inclusive |
Business days |
Dates accepted by a weekmask and not present in a holiday set |
Trading sessions |
Local open/close times converted to timezone-aware UTC datetimes |
Extended sessions |
Named pre-open and after-close windows where available |
The Python API is intentionally small. Use top-level helpers for plain
date series, then use Calendar when venue-specific holidays or trading
hours matter.
Plain date series¶
date_range(start, end, step_days=1) returns every date in an
inclusive range.
from datetime import date
from finance_dates import date_range
date_range(date(2024, 1, 1), date(2024, 1, 5))
Calendar.from_range(...).business_days() applies the standard
Monday-Friday weekmask and does not apply any holiday calendar. It is
useful for quick tests and generic weekday series.
from datetime import date
from finance_dates import Calendar
Calendar.from_range(date(2024, 1, 1), date(2024, 1, 7)).business_days()
Use Calendar.business_days() when exchange holidays matter.
Exchange calendars¶
Create calendars from exchange codes or ISO country codes:
from finance_dates import Calendar
nyse = Calendar.from_exchange("XNYS")
london = Calendar.from_exchange("XLON")
us = Calendar.from_region("US")
Calendar objects expose:
Attribute / method |
Meaning |
|---|---|
|
Calendar code used by the resolver |
|
|
|
Seven booleans, indexed Monday through Sunday |
|
IANA timezone for trading hours, or |
|
One or more local regular session templates as open/close hour/minute/offset tuples |
|
Named local extended-hours templates where available |
|
True when the date passes weekmask and holiday checks |
|
True when the date is an observed holiday |
|
Observed holidays for a year |
|
Holidays in an inclusive range |
|
Count of valid business days in an inclusive range |
|
Exchange-aware valid dates |
|
UTC open/close datetimes for regular sessions |
|
Named UTC extended-hours windows |
|
True when a UTC-aware timestamp is inside a regular session |
|
Next regular-session boundary if configured |
|
Local |
Unknown exchange or region codes raise ValueError in Python.
Market families¶
The resolver groups related MICs into calendar families. For example, US equity venues share the NYSE-style holiday set, options venues share US options hours, and CME/NYMEX futures use overnight Globex-style sessions. A few synthetic futures product-group codes are also accepted for contracts whose hours differ materially from the broad MIC family.
Representative families include:
Family |
Examples |
Notes |
|---|---|---|
US equities |
|
NYSE-style holidays, 09:30-16:00 regular hours, and 04:00-09:30 / 16:00-20:00 extended windows |
US options |
|
US options classification and close conventions |
US bonds |
|
Includes bond-market holidays such as Columbus Day and Veterans Day |
US futures |
|
Overnight and split futures sessions with market-specific timezone choices |
Major equities |
|
Venue-specific holiday rules where implemented; selected APAC venues expose split lunch sessions |
FX |
|
24x5, Sunday through Friday session family |
Crypto |
|
24x7 session family |
EXCHANGE_CODES is exported for discovery and is sourced from
finance-enums. COUNTRY_CODES and COUNTRY_CODES3 list the supported
ISO country-code inputs for Calendar.from_region(). For product-aware futures
calendars, prefer Calendar.from_asset(exchange, asset_class, subclass=...)
with finance-enums enum members, or their string values, over synthetic
exchange names.
from finance_dates import Calendar
from finance_enums import AgricultureType, EnergyType, ExchangeCode, UnderlyingAssetClass
Calendar.from_asset(ExchangeCode.XNYM, UnderlyingAssetClass.Commodity, subclass=EnergyType.NaturalGas)
Calendar.from_asset(ExchangeCode.XCBT, UnderlyingAssetClass.Agriculture, subclass=AgricultureType.Corn)
Calendar.from_asset(ExchangeCode.XNYS, UnderlyingAssetClass.Equity)
Futures product/calendar/exchange matrix¶
The table below is the current Calendar.from_exchange(...) futures mapping
for product and product-group aliases. Codes in the first column are grouped when
they share the same resolver family and session template.
Inputs |
Calendar template |
Local regular sessions |
Notes |
|---|---|---|---|
|
|
|
Baseline CME Globex overnight template; dairy currently shares this template. |
|
|
|
NYMEX/COMEX energy and metals template with the daily 16:00-17:00 CT maintenance gap. |
|
|
|
CBOT grain/oilseed split template. |
|
|
|
CME livestock daytime template. |
|
|
|
CME lumber daytime template. |
|
|
|
ICE US energy/softs broad exchange template. |
Only enum-backed exchange and generic identifiers are exported by
EXCHANGE_CODES; many product mnemonics and synthetic product-group names in
this matrix are resolver-only aliases accepted by Calendar.from_exchange().
For product-aware code, prefer Calendar.from_product(...) or
Calendar.from_asset(...) when the exchange/product vocabulary is known.
The baseline CME holiday set used by these futures templates currently captures full-closure holidays (New Year’s Day, Good Friday, Christmas). CME product holiday notices include many partial closes that remain out of scope until session-specific holiday truncation is modeled.
Valid and invalid date series¶
Common workflows:
from datetime import date
from finance_dates import Calendar
nyse = Calendar.from_exchange("XNYS")
valid_dates = nyse.business_days(date(2024, 7, 1), date(2024, 7, 5))
invalid_holidays = nyse.holidays(date(2024, 7, 1), date(2024, 9, 30))
sessions = nyse.sessions(date(2024, 7, 1), date(2024, 7, 5))
business_days() returns valid trading dates. holidays(start, end)
returns the observed holidays that explain many invalid weekdays.
Weekend invalid dates are not included in holidays(start, end); derive
those separately if you need all invalid calendar dates.
Trading hours and early closes¶
Trading hours are stored in local exchange time and converted to UTC for timestamp operations. This makes DST transitions explicit at the output boundary:
from datetime import date
from finance_dates import Calendar
nyse = Calendar.from_exchange("XNYS")
nyse.sessions(date(2024, 3, 8), date(2024, 3, 12))
Regular trading days may contain more than one session. Venues with scheduled
lunch breaks expose one regular_sessions tuple per interval, and
sessions(start, end) returns one UTC open/close pair per regular session per
business day:
tokyo = Calendar.from_exchange("XTKS")
tokyo.regular_sessions
# [(9, 0, 0, 11, 30, 0), (12, 30, 0, 15, 30, 0)]
hong_kong = Calendar.from_exchange("XHKG")
hong_kong.regular_sessions
# [(9, 30, 0, 12, 0, 0), (13, 0, 0, 16, 0, 0)]
shanghai = Calendar.from_exchange("XSHG")
shanghai.regular_sessions
# [(9, 30, 0, 11, 30, 0), (13, 0, 0, 15, 0, 0)]
The Shanghai afternoon interval includes the closing call auction through 15:00. Tokyo uses a date-effective schedule around the 2024-11-05 trading-hours extension: dates before the change close at 15:00 local, and dates on or after the change use the current 15:30 close. Most other built-in calendars still use static current-rule templates unless otherwise documented.
next_open() and next_close() return the next matching boundary inclusively.
At an exact session open, is_open() is true and next_open() returns that same
instant. At an exact session close, is_open() is false and next_close()
returns that same close instant.
Early closes shorten the final session for the affected trading day. For example, NYSE July 3, 2024 closes at 13:00 New York time:
nyse.early_close_for(date(2024, 7, 3)) # (13, 0)
US equity calendars also expose informational extended-hours windows:
nyse.regular_sessions
# [(9, 30, 0, 16, 0, 0)]
nyse.extended_hours
# [("pre_open", 4, 0, 0, 9, 30, 0), ("after_close", 16, 0, 0, 20, 0, 0)]
nyse.extended_sessions(date(2024, 7, 3), date(2024, 7, 3))
extended_sessions() returns (name, open, close) tuples with UTC
datetimes. On NYSE early-close days, after_close begins at the early
close rather than the regular 16:00 close.
Futures sessions can cross midnight. In regular_sessions, day offsets
describe the local trading-day relationship:
cme = Calendar.from_exchange("XCME")
cme.regular_sessions
# [(17, 0, -1, 16, 0, 0)]
That tuple means the session opens at 17:00 on the previous local day and closes at 16:00 on the trading day.
NYMEX energy futures use the same Chicago-time Globex template through
Calendar.from_asset(ExchangeCode.XNYM, UnderlyingAssetClass.Commodity, subclass=EnergyType.NaturalGas), Calendar.from_product("XNYM", "NaturalGas"),
and lower-level aliases such as XNYM, NYMEX_ENERGY, COMEX_METALS, CL,
MCL, QM, GC, MGC, and QO. The daily 16:00-17:00 CT maintenance
window is closed; next_open() from that gap returns the 17:00 CT open for the
next trade date.
For broader category-level parity with CME Globex filters, energy and metals
aliases also include CME_ENERGY, GLOBEX_ENERGY, CME_METALS, and
GLOBEX_METALS.
CBOT grain and oilseed futures have a more unusual split schedule. Use
Calendar.from_asset(ExchangeCode.XCBT, UnderlyingAssetClass.Agriculture, subclass=AgricultureType.Corn) when resolving from finance-enums vocabulary, or
the lower-level synthetic product-group codes CBOT_GRAINS, CME_GRAINS,
GLOBEX_GRAINS, or product mnemonics such as ZC, ZW, and ZS when that
distinction matters:
grains = Calendar.from_exchange("CBOT_GRAINS")
grains.regular_sessions
# [(19, 0, -1, 7, 45, 0), (8, 30, 0, 13, 20, 0)]
Equivalent grain/oilseed aliases include CBOT_OILSEEDS, CBOT_WHEAT,
CBOT_CORN, CBOT_SOYBEANS, ZL, ZM, ZO, KE, and HRS.
CME_LIVESTOCK / GLOBEX_LIVESTOCK and the product mnemonics LE, GF, and
HE use the daytime livestock session (08:30-13:05 CT). CME_LUMBER /
GLOBEX_LUMBER and LBR / LS use a daytime lumber session (09:00-15:05 CT).
CME_DAIRY / GLOBEX_DAIRY use the overnight dairy template (17:00 previous
day to 16:00 trade date CT).
Source-backed status and remaining placeholders¶
This is a current audit of major schedule assumptions and placeholders that are still present in the futures layer.
Area |
Current behavior |
Status |
Needed source to remove placeholder |
|---|---|---|---|
|
Routed to 08:30-13:05 CT livestock session |
Source-backed |
Optional chapter citation if you want external-link provenance in docs |
|
Routed to 17:00-16:00 CT overnight dairy session |
Source-backed |
Optional chapter citation if you want external-link provenance in docs |
|
Routed to 09:00-15:05 CT lumber session |
Source-backed |
Optional chapter citation if you want external-link provenance in docs |
|
Aliased to shared grain split template |
Partial |
Product-level confirmation for exact overnight/day split by contract group |
CME futures historical transitions |
Mostly static schedule templates |
Partial |
Official effective-date change logs for each product family (not just current hours) |
If you can share authoritative links or chapter extracts for those items, they can be promoted from placeholder/partial to source-backed templates.
Scope and limitations¶
Calendar support is designed for deterministic application logic and tests. It includes a broad set of exchange families, but it is not a replacement for venue notices or regulatory calendars.
Important boundaries:
Holiday rules are implemented per family and may use tabulated lunar holidays for markets where simple formulae are not enough.
Trading-hours templates are date-effective only where explicitly implemented, currently including Tokyo’s 2024 close-time extension. Other historical schedule changes may still use current-rule approximations.
Some special closures, ad-hoc national mourning days, weather events, or emergency interruptions may not be modeled.
holidays(start, end)returns holidays, not every invalid date.business_days()is inclusive of both endpoints.Extended-hours coverage is currently populated where the calendar has a known source-of-truth window; absent
extended_hoursmeans no extended template is configured, not that the venue never has one.Datetime inputs are interpreted as UTC when naive; prefer timezone-aware UTC datetimes for clarity.