Determine whether a given date/time is a valid market trading day and within market hours
Project description
TRADING_HOURS
Determine whether a given date/time is a valid market trading day and whether the current moment falls within a configurable market window.
Installation
pip install TRADING_HOURS
Quick Start
Set environment variables in .env
PROJECT_DIRECTORY=/path/to/your/project
Config Files (required)
Place these two JSON files relative to PROJECT_DIRECTORY:
{PROJECT_DIRECTORY}/_CONFIG/trading_holidays.json
{PROJECT_DIRECTORY}/_CONFIG/market_timings.json
trading_holidays.json format:
{
"market_holidays": [
{
"year": 2026,
"holidays": [
{
"date": "26-Feb-2026",
"day": "Thursday",
"description": "Mahashivratri"
}
]
}
]
}
market_timings.json format:
{
"market_timings": {
"close": "20:00",
"open": "01:15",
"timezone": "UTC",
"trading_days_in_week": 5,
"weekend_days": ["Saturday", "Sunday"],
"trading_minutes": 375,
"breaks": []
}
}
Import and use
from TRADING_HOURS import (
is_trading_day,
is_today_trading_day,
was_date_trading_day,
find_next_trading_date,
get_date_n_trading_days_later,
is_market_open,
processing_market_window,
)
from datetime import date
# Check if a specific date is a trading day
is_trading_day(date(2025, 7, 4)) # → True (Friday)
is_trading_day(date(2025, 7, 5)) # → False (Saturday)
# Check today
is_today_trading_day() # → True / False
# Historical check
was_date_trading_day(date(2025, 1, 1)) # → True / False
# Find next trading date
find_next_trading_date(date(2025, 7, 4)) # → date(2025, 7, 7) (Monday)
find_next_trading_date(date(2025, 7, 4), inclusive=True) # → same if already trading day
# Settlement date calculations (T+1, T+2, etc.)
get_date_n_trading_days_later(date(2025, 7, 4), 1) # → date(2025, 7, 7)
get_date_n_trading_days_later(date(2025, 7, 4), 2) # → date(2025, 7, 8)
get_date_n_trading_days_later(date(2025, 7, 4), 0) # → date(2025, 7, 4) (T+0)
# Check if market is currently open
is_market_open() # → True / False
# Check with configurable pre/post buffers
in_window, info = processing_market_window(minutes_before_open=15, minutes_after_close=30)
Configuration
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
PROJECT_DIRECTORY |
Yes | — | Root directory for config file storage |
Config File Paths
| File | Path |
|---|---|
| Holiday calendar | {PROJECT_DIRECTORY}/_CONFIG/trading_holidays.json |
| Market timings | {PROJECT_DIRECTORY}/_CONFIG/market_timings.json |
market_timings.json Fields
| Field | Required | Default | Description |
|---|---|---|---|
open |
Yes | — | Market open time in HH:MM format |
close |
Yes | — | Market close time in HH:MM format |
timezone |
Yes | — | IANA timezone (e.g. Asia/Kolkata, America/New_York) |
trading_days_in_week |
No | 5 |
Number of trading days per week |
weekend_days |
No | ["Saturday", "Sunday"] |
List of weekday names that are non-trading days |
trading_minutes |
No | 0 |
Total active trading minutes per session (excl. breaks) |
breaks |
No | [] |
List of mid-session breaks with start/end times |
Key Features
- Pure Functions Only — No OOP, no class state; all logic in module-level functions
- Lazy Config Loading — No side effects on import; validated on first use
- Cached Holiday/Timing Data — Loaded once with thread-safe double-checked locking
- Configurable Market Window —
minutes_before_open/minutes_after_closeparameters - Midnight-Crossing Sessions — Supports overnight sessions (close < open)
- stdlib zoneinfo — Uses Python 3.9+
zoneinfoexclusively; no pytz dependency - Retry with Exponential Backoff + Jitter — Config file reads retry 3 times
- Settlement Date Calculations — T+0, T+1, T+2, etc.
API Reference
is_trading_day(check_date: date) -> bool
Check if a specific date is a trading day (not a weekend or holiday).
- Returns:
Trueif trading day,Falseotherwise
is_today_trading_day() -> bool
Check if today (UTC) is a trading day.
- Returns:
Trueif today is a trading day,Falseotherwise
was_date_trading_day(check_date: date) -> bool
Historical date check — same logic as is_trading_day with clearer intent for past dates.
- Returns:
Trueif it was a trading day,Falseotherwise
find_next_trading_date(from_date: date, inclusive: bool = False) -> date
Find the next valid trading date from a given date.
inclusive: IfTrue, returnsfrom_dateitself if already a trading day- Returns: The next trading date
get_date_n_trading_days_later(from_date: date, trading_days: int) -> date
Calculate the date N trading days from a given date (T+1, T+2 settlement).
trading_days: 0 returnsfrom_dateunchanged- Returns: The resulting date
is_market_open() -> bool
Check if the market is currently open (no buffer).
- Returns:
Trueif within market hours on a trading day,Falseotherwise
processing_market_window(minutes_before_open: int = 0, minutes_after_close: int = 0) -> tuple[bool, str]
Check if current time falls within the market window, with optional buffers.
- Returns:
(in_window: bool, debug_info: str)tuple
HolidayConfigError
Raised when the holidays JSON is missing, malformed, or has an invalid date entry.
MarketTimingError
Raised when market_timings.json is missing, malformed, or contains an invalid time string.
Retry Policy
Config file reads use @with_retry decorator:
- Max attempts: 3
- Delay: Exponential backoff (1s, 2s, 4s) with ±25% jitter
- Retryable: All exceptions during file read
- Not retried:
FileNotFoundErrorandjson.JSONDecodeError(raised immediately as config errors)
Error Handling
The library will raise:
ValueErrorifPROJECT_DIRECTORYis not set (on first use, not at import)HolidayConfigErrorif the holidays JSON file is missing or malformedMarketTimingErrorif the market timings JSON file is missing, malformed, or has missing fieldsValueErrorifminutes_before_openorminutes_after_closeare negative
Test Coverage
python3 -m pytest TRADING_HOURS.py -v
| Function | Tier | Tests | What is tested |
|---|---|---|---|
is_trading_day() |
1 | 3 | Friday true, Saturday false, Sunday false |
find_next_trading_date() |
1 | 3 | Friday→Monday, inclusive flag, Saturday→Monday |
get_date_n_trading_days_later() |
1 | 4 | T+0 same, T+1 Friday, T+2 Friday, T+1 Saturday |
parse_trading_holidays() |
2 | 4 | valid entry, malformed skipped, weekend holiday, UserWarning |
with_retry() |
2 | 4 | first-attempt success, third-attempt success, exhaustion, all exception types |
_parse_time_str() |
2 | 3 | valid HH:MM, invalid format, midnight |
is_today_trading_day() |
1 | 1 | returns bool |
was_date_trading_day() |
1 | 2 | weekend false, weekday true |
processing_market_window() |
1 | 4 | returns tuple, debug info, buffer expansion, negative raises |
is_market_open() |
1 | 1 | returns bool |
fetch_trading_holidays() |
2 | 3 | returns set, missing file, invalid JSON |
fetch_market_timings() |
2 | 3 | returns timings, missing file, missing field |
_get_holidays() |
2 | 2 | returns cached, lazy loads |
_get_timings() |
2 | 2 | returns cached, lazy loads |
| Import validation | 3 | 1 | missing PROJECT_DIRECTORY raises |
Reloading Cached Data
Holiday and timing data are cached after the first load. To force a reload:
import TRADING_HOURS
TRADING_HOURS._HOLIDAY_CACHE = None
TRADING_HOURS._TIMINGS_CACHE = None
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 market_trading_hours-7.7.7.tar.gz.
File metadata
- Download URL: market_trading_hours-7.7.7.tar.gz
- Upload date:
- Size: 23.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c072218f1f31ca54afd20813ff5f41a88d1a211b842afd78f47678bd3f7b9aeb
|
|
| MD5 |
a5c2f532e353d0517111e8f82261fccd
|
|
| BLAKE2b-256 |
87e55a421e697450ff9c7d9072ddf3eb6f455c326855ff675fd8dae1f15df2e9
|
File details
Details for the file market_trading_hours-7.7.7-py3-none-any.whl.
File metadata
- Download URL: market_trading_hours-7.7.7-py3-none-any.whl
- Upload date:
- Size: 24.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e3148a43ea11620ebcfd2688543f5e331b7d424ade1f67a94e0418b219de19b9
|
|
| MD5 |
c968895ca60413189a595b4ca6c8c71f
|
|
| BLAKE2b-256 |
3ebe42a3cce3ae3021682c314c717ca260e7e3cbf38e7ad8e6c180355b4b44b0
|