Skip to main content

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_hours/trading_holidays.json
{PROJECT_DIRECTORY}/_CONFIG/trading_hours/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": {
    "open": "09:15",
    "close": "15:30",
    "timezone": "Asia/Kolkata",
    "trading_days_in_week": 5
  }
}

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

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. Configurable Market Windowminutes_before_open / minutes_after_close parameters
  5. Midnight-Crossing Sessions — Supports overnight sessions (close < open)
  6. stdlib zoneinfo — Uses Python 3.9+ zoneinfo exclusively; no pytz dependency
  7. Retry with Exponential Backoff + Jitter — Config file reads retry 3 times
  8. 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) -> 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: 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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

market_trading_hours-1.7.11.tar.gz (23.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

market_trading_hours-1.7.11-py3-none-any.whl (23.8 kB view details)

Uploaded Python 3

File details

Details for the file market_trading_hours-1.7.11.tar.gz.

File metadata

  • Download URL: market_trading_hours-1.7.11.tar.gz
  • Upload date:
  • Size: 23.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for market_trading_hours-1.7.11.tar.gz
Algorithm Hash digest
SHA256 2a79115638160130ae372ac0555705367dda7d57c4a8550d0e2942077a168c30
MD5 62de2f1b0b9c0939b594cfc067882c94
BLAKE2b-256 2201c0607be5084b2ecd73cd409ae0e6a66a193e4ad4cb60692525d375c971d1

See more details on using hashes here.

File details

Details for the file market_trading_hours-1.7.11-py3-none-any.whl.

File metadata

File hashes

Hashes for market_trading_hours-1.7.11-py3-none-any.whl
Algorithm Hash digest
SHA256 5cac8e30524867061ba5c19884100999460ecf18f61797bee0aa030113f3527f
MD5 8bf84daa598e09ec2d493fef4c57c2b2
BLAKE2b-256 21b5919975f467a2f08a7e737fddac03aa185030c85b0025d91db8338d33f7d8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page