Skip to main content

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/{EXCHANGE}/trading_holidays.json
{PROJECT_DIRECTORY}/_CONFIG/{EXCHANGE}/market_timings.json

trading_holidays.json format:

{
  "market_holidays": [
    {
      "year": 2026,
      "holidays": [
        {
          "date": "26-Feb-2026",
          "day": "Thursday",
          "description": "Mahashivratri"
        }
      ]
    }
  ]
}

market_timings.json — session format (multiple named sessions, required):

{
  "market_timings": {
    "timezone": "UTC",
    "trading_days_in_week": 5,
    "weekend_days": ["Saturday", "Sunday"],
    "trading_minutes": 375,
    "sessions": [
      { "name": "premarket",  "open": "07:00", "close": "09:00", "breaks": null },
      { "name": "regular",    "open": "09:00", "close": "15:00",
        "breaks": [{"start": "11:30", "end": "12:30"}] },
      { "name": "afterhours", "open": "15:00", "close": "17:00", "breaks": null }
    ]
  }
}

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, phase = processing_market_window(minutes_before_open=15, minutes_after_close=30)
# → (True, "regular")    if in regular hours
# → (True, "afterhours") if in after-hours
# → (False, "Market closed") if outside all sessions

# Check only specific sessions
in_window, phase = processing_market_window(1, 0, sessions=["premarket"])
# → (True, "premarket")  1 minute before premarket open

in_window, phase = processing_market_window(0, 0, sessions=["regular"])
# → (True, "regular")    only checks regular hours

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/{EXCHANGE}/trading_holidays.json
Market timings {PROJECT_DIRECTORY}/_CONFIG/{EXCHANGE}/market_timings.json

market_timings.json Fields

Field Required Default Description
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)
sessions Yes [] List of named sessions with own open/close/breaks

Session fields:

Field Required Default Description
name Yes - Session name (e.g., "premarket", "regular")
open Yes - Session open time in HH:MM format
close Yes - Session close time in HH:MM format
breaks No null null = no breaks, [] = explicit no breaks, [{"start":"HH:MM","end":"HH:MM"}] = session breaks

Key Features

  1. Pure Functions Only — No OOP, no class state; all logic in module-level functions
  2. Lazy Config Loading — No side effects on import; validated on first use
  3. Cached Holiday/Timing Data — Loaded once with thread-safe double-checked locking
  4. Named Sessions — Multiple named market phases (premarket, regular, afterhours, etc.)
  5. Configurable Market Windowminutes_before_open / minutes_after_close buffers per call
  6. Session Filters — Check specific sessions by name with optional sessions parameter
  7. Per-Session Breaks — Breaks defined per session only (no global fallback)
  8. Midnight-Crossing Sessions — Supports overnight sessions (close < open)
  9. stdlib zoneinfo — Uses Python 3.9+ zoneinfo exclusively; no pytz dependency
  10. Retry with Exponential Backoff + Jitter — Config file reads retry 3 times
  11. 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: True if trading day, False otherwise

is_today_trading_day() -> bool

Check if today (UTC) is a trading day.

  • Returns: True if today is a trading day, False otherwise

was_date_trading_day(check_date: date) -> bool

Historical date check — same logic as is_trading_day with clearer intent for past dates.

  • Returns: True if it was a trading day, False otherwise

find_next_trading_date(from_date: date, inclusive: bool = False) -> date

Find the next valid trading date from a given date.

  • inclusive: If True, returns from_date itself 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 returns from_date unchanged
  • Returns: The resulting date

is_market_open() -> bool

Check if the market is currently open (no buffer).

  • Returns: True if within market hours on a trading day, False otherwise

processing_market_window(minutes_before_open: int = 0, minutes_after_close: int = 0, sessions: list[str] | None = None) -> tuple[bool, str]

Check if current time falls within the market window, with optional buffers.

  • sessions: Optional list of session names to check (None = all configured sessions)
  • Returns: (in_window: bool, phase: str) — phase is session name (e.g. "premarket", "regular") or "Market closed" / "Not a trading day"

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: FileNotFoundError and json.JSONDecodeError (raised immediately as config errors)

Error Handling

The library will raise:

  • ValueError if PROJECT_DIRECTORY is not set (on first use, not at import)
  • HolidayConfigError if the holidays JSON file is missing or malformed
  • MarketTimingError if the market timings JSON file is missing, malformed, or has missing fields
  • ValueError if minutes_before_open or minutes_after_close are 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

Release files for MARKET-TRADING-HOURS 0.0.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for MARKET-TRADING-HOURS 0.0.6
File Size Uploaded
market_trading_hours-0.0.6.tar.gz 25.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for MARKET-TRADING-HOURS 0.0.6
File Interpreter ABI Platform
market_trading_hours-0.0.6-py3-none-any.whl Python 3 none any Details

Total release size:51.4 kB

Release files / market_trading_hours-0.0.6.tar.gz

Download URL market_trading_hours-0.0.6.tar.gz
Size 25.5 kB
Tags Source
SHA-256 checksum
How to use checksums
15d1f8b3a128f9490cee5076a7e33b7790dccd418db54c5ed545d60e3849e624
BLAKE2b-256 checksum
How to use checksums
7acc9535770275f47d96d19630cc1252ab3ecab3d85af093ccffcab149195209
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release files / market_trading_hours-0.0.6-py3-none-any.whl

Download URL market_trading_hours-0.0.6-py3-none-any.whl
Size 25.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
649d51fedd230b80020e05bcb0b46cfd928698b71851f8f1ed794ab835d9ee70
BLAKE2b-256 checksum
How to use checksums
96c491deca35ac110f1c9310d503eb78ac32887e659c6dc34f5081db4cfdf50a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release history Release notifications | RSS feed

This release

0.0.6 This release

2 release files

0.0.5

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page