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_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,
    "active_days_in_week": 5,
    "weekend_days": ["Saturday", "Sunday"],
    "trading_minutes": 375,
    "active_minutes": 365,
    "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
active_days_in_week No 5 Active days per week (may differ from trading days)
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)
active_minutes No 365 Active minutes per session (may differ from trading minutes)
breaks No [] List of mid-session breaks with start/end times

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-60.60.68.tar.gz (24.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-60.60.68-py3-none-any.whl (24.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: market_trading_hours-60.60.68.tar.gz
  • Upload date:
  • Size: 24.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-60.60.68.tar.gz
Algorithm Hash digest
SHA256 7778ce8833bb33144f9f4caa9e4dff42cef41760805f1eb20673df8b99cc8d92
MD5 56399d1884bc62bbfb80f8404c2092cd
BLAKE2b-256 88ac9e40021856c46647e1c408224b9fa54d2766361db3d12ea871ef84836c91

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for market_trading_hours-60.60.68-py3-none-any.whl
Algorithm Hash digest
SHA256 539dd639f6d4d11c6506082dc26e5075aac85ca7a3f39d541e890d2054e21071
MD5 2990fa8f87a4d9d71386ecaf7df1e17c
BLAKE2b-256 a9d8319ee2e4ba74eca52a6f4d0af9dd78f6e8ebfceb00aee1515ec9f81527b2

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