Add your description here
Project description
Better Calendar
A unified Python library for managing business day calendars and holidays across different sources, such as exchanges, countries or risk-free rates.
- Exchanges: Trading calendars via
exchange-calendars(XPAR, XNYS, XLON, etc.) - Countries: National holidays via
workalendar(FR, US, GB, etc.) - RFR: Risk-free rate fixing calendars via
QuantLib(ESTR, SOFR, etc.)
Installation
pip install better-calendar
For development:
git clone https://github.com/yourusername/better-calendar.git
cd better-calendar
uv sync
Quick Start
from better_calendar import BetterCalendar
from datetime import date
# Initialize the hub
hub = BetterCalendar.default()
# Check if a date is a business day
calendar = hub.get_from_country("FR")
is_working = calendar.is_business_day(date(2026, 1, 1)) # False (New Year)
# Get all business days in a range
business_days = calendar.business_days(
date(2026, 1, 1),
date(2026, 12, 31)
)
# Get all holidays (including weekends)
holidays = calendar.holidays(date(2026, 1, 1), date(2026, 12, 31))
Core Features
1. Exchange Calendars
Access trading calendars for major stock exchanges:
# Euronext Paris
xpar = hub.get_from_exchange("XPAR")
is_trading = xpar.is_business_day(date(2026, 12, 25)) # False (Christmas)
# New York Stock Exchange
xnys = hub.get_from_exchange("XNYS")
holidays = xnys.holidays(date(2026, 1, 1), date(2026, 12, 31))
# London Stock Exchange
xlon = hub.get_from_exchange("XLON")
business_days = xlon.business_days(date(2026, 1, 1), date(2026, 3, 31))
2. Country Calendars
Access national holiday calendars:
# France
fr = hub.get_from_country("FR")
french_holidays = fr.holidays(date(2026, 1, 1), date(2026, 12, 31))
# United States
us = hub.get_from_country("US")
us_business_days = us.business_days(date(2026, 1, 1), date(2026, 12, 31))
# United Kingdom
gb = hub.get_from_country("GB")
is_working = gb.is_business_day(date(2026, 5, 4)) # May Day
3. RFR (Risk-Free Rate) Calendars
Access fixing calendars for reference rates:
# Euro Short-Term Rate (€STR / ESTR)
estr = hub.get_from_rfr("ESTRON Index")
is_fixing_day = estr.is_business_day(date(2026, 12, 25))
# Secured Overnight Financing Rate (SOFR)
sofr = hub.get_from_rfr("SOFRRATE Index")
fixing_days = sofr.business_days(date(2026, 1, 1), date(2026, 12, 31))
4. Unified API
Navigate business days with a single interface:
# Next business day
next_day = hub.next_business_day("country", "FR", date(2026, 1, 3))
# Previous business day
prev_day = hub.previous_business_day("exchange", "XPAR", date(2026, 1, 3))
# Offset by N business days
future_day = hub.offset_business_days("rfr", "ESTRON Index", date(2026, 1, 15), 10)
past_day = hub.offset_business_days("country", "FR", date(2026, 1, 15), -5)
Advanced Usage
Calendar Overrides
Add or remove specific holidays:
# Add exceptional holiday (e.g., strike day)
xpar_with_strike = hub.with_overrides(
"exchange", "XPAR",
add_holidays=[date(2026, 5, 15)]
)
# Remove holiday (exceptional opening)
fr_special = hub.with_overrides(
"country", "FR",
remove_holidays=[date(2026, 1, 1)]
)
# Check the modified calendar
is_open = xpar_with_strike.is_business_day(date(2026, 5, 15)) # False
Combined Calendars
Combine multiple calendars with intersection or union logic:
# Intersection: business day only if ALL calendars are open
combined_strict = hub.combine([
("country", "FR"),
("country", "US")
], mode="intersection")
# Returns True only on days when both France AND US are working
# Union: business day if AT LEAST ONE calendar is open
combined_flexible = hub.combine([
("exchange", "XPAR"),
("exchange", "XNYS")
], mode="union")
# Returns True if Paris OR New York (or both) are open
# Mix different calendar types
multi_calendar = hub.combine([
("country", "FR"),
("exchange", "XPAR"),
("rfr", "ESTRON Index")
], mode="intersection")
Visualization
Generate visual calendar heatmaps:
# Plot a calendar for 2026
calendar = hub.get_from_country("FR")
calendar.plot(year=2026)
# Custom date range
calendar.plot(
start=date(2026, 1, 1),
end=date(2026, 6, 30),
cmap="RdYlGn"
)
Command Line Interface
The package includes a CLI tool (bcal) for quick calendar visualization:
# Display French calendar for 2026
bcal -c FR 2026
# Display Euronext Paris for March 2026
bcal -e XPAR 2026 3
# Current year (defaults to today)
bcal -c US
# Combine multiple calendars (intersection)
bcal -c FR -c US 2026
# Combine with union mode
bcal -e XPAR -e XNYS --mode union 2026 1
# RFR calendar
bcal -r "ESTRON Index" 2026
# Add custom holidays
bcal -c FR --add-holiday 2026-05-15 2026
CLI Output Format
Business days are shown as regular numbers, holidays and weekends appear in brackets:
January 2026
Mo Tu We Th Fr Sa Su
[ 1][ 2][ 3][ 4]
5 6 7 8 9 [10][11]
12 13 14 15 16 [17][18]
19 20 21 22 23 [24][25]
26 27 28 29 30 [31]
CLI Options
-c, --country: Country ISO code (e.g., FR, US, GB). Can be repeated.-e, --exchange: Exchange MIC code (e.g., XPAR, XNYS, XLON). Can be repeated.-r, --rfr: RFR ticker (e.g., "ESTRON Index"). Can be repeated.-m, --mode: Combination mode:intersection(default) orunion.--add-holiday: Add custom holiday (format: YYYY-MM-DD). Can be repeated.--remove-holiday: Remove a holiday (format: YYYY-MM-DD). Can be repeated.
Real-World Examples
Financial Trading
Calculate settlement dates for cross-border transactions:
from datetime import date
hub = BetterCalendar.default()
# T+2 settlement for both Paris and New York
trade_date = date(2026, 1, 15)
calendar = hub.combine([
("exchange", "XPAR"),
("exchange", "XNYS")
], mode="intersection")
settlement_date = calendar.offset_business_days(trade_date, 2)
print(f"Settlement: {settlement_date}")
Interest Calculation
Calculate interest periods considering only business days:
start = date(2026, 1, 1)
end = date(2026, 12, 31)
calendar = hub.get_from_country("FR")
business_days = calendar.business_days(start, end)
print(f"Business days in 2026: {len(business_days)}")
Rate Fixing Availability
Check when reference rate fixings are published:
# Check ESTR fixing availability
estr = hub.get_from_rfr("ESTRON Index")
fixing_date = date(2026, 12, 25)
if estr.is_business_day(fixing_date):
print("ESTR fixing available")
else:
# Get next available fixing date
next_fixing = hub.next_business_day("rfr", "ESTRON Index", fixing_date)
print(f"Next fixing: {next_fixing}")
Supported Calendars
Exchanges (via exchange-calendars)
Common exchange codes:
XPAR: Euronext ParisXNYS: New York Stock ExchangeXLON: London Stock ExchangeXHKG: Hong Kong Stock ExchangeXTKS: Tokyo Stock ExchangeXFRA: Deutsche Börse (Frankfurt)
See exchange-calendars documentation for full list.
Countries (via workalendar)
Common country codes:
FR: FranceUS: United StatesGB: United KingdomDE: GermanyJP: JapanCN: China
See workalendar documentation for full list.
RFR Tickers (via QuantLib)
Supported reference rates:
ESTRON Index: Euro Short-Term Rate (TARGET calendar)SOFRRATE Index: Secured Overnight Financing Rate (US calendar)- Additional QuantLib calendars available
Testing
Run the test suite:
# Run all tests
uv run pytest
# Run with coverage
uv run pytest --cov=better_calendar
# Run specific test
uv run pytest tests/test_calendar_hub.py::test_combined_calendars
# Run tests matching a pattern
uv run pytest -k "intersection"
API Reference
BetterCalendar
Main hub class for accessing calendars.
Methods:
default(): Create hub with default adaptersget_from_exchange(code): Get exchange calendarget_from_country(code): Get country calendarget_from_rfr(ticker): Get RFR calendarcombine(calendars, mode): Combine multiple calendarswith_overrides(kind, code, add_holidays, remove_holidays): Create calendar with overridesnext_business_day(kind, code, date): Get next business dayprevious_business_day(kind, code, date): Get previous business dayoffset_business_days(kind, code, date, offset): Offset by N business days
CalendarAdapter
Protocol defining the calendar interface.
Methods:
is_business_day(date): Check if date is a business daybusiness_days(start, end): Get all business days in rangeholidays(start, end): Get all holidays (including weekends) in rangeplot(year, start, end, cmap, figsize): Generate visual calendar
Dependencies
- exchange-calendars (>=4.5): Trading calendars for global exchanges
- workalendar (>=17.0): National and regional holiday calendars
- QuantLib (>=1.41): Financial modeling library with calendar support
- click (>=8.1): Command-line interface creation
- matplotlib (>=3.9): Plotting library for visualizations
- pandas (>=2.3): Data manipulation for calendar operations
Contributing
Contributions are welcome. Please ensure:
- All tests pass:
uv run pytest - Code follows existing style conventions
- New features include tests
- Documentation is updated
License
MIT License
Copyright (c) 2026 Better Calendar Contributors
See LICENSE file for details.
Related Projects
- exchange-calendars: Trading calendar library
- workalendar: Worldwide holidays and working days library
- QuantLib: Quantitative finance library
- pandas_market_calendars: Alternative market calendar library
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file better_calendar-1.0.1.tar.gz.
File metadata
- Download URL: better_calendar-1.0.1.tar.gz
- Upload date:
- Size: 14.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3bc1be39bbd5bdd5d52ec1f4bf6f97d42b2ebedefb10e02ebf23cd6625a128b8
|
|
| MD5 |
81e57e630d18eefcf9fa4709256510e5
|
|
| BLAKE2b-256 |
15502809208895e2bfd4f09fa948d3dfe235d39977dfa081ab8b0db5013c1080
|
File details
Details for the file better_calendar-1.0.1-py3-none-any.whl.
File metadata
- Download URL: better_calendar-1.0.1-py3-none-any.whl
- Upload date:
- Size: 16.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
185cd265ab34b281c719e2553cd63781a85ea76242401f0751a2c2f7e9a9d9db
|
|
| MD5 |
225785927e7d61f5912d55de4e1921c0
|
|
| BLAKE2b-256 |
dcd7fb1a8d5153a5e09b1bde81e90c6705757585361dc1cb8c837d903d7d8732
|