From 50d26aad40e0e4161084043b29ddb080135ea523 Mon Sep 17 00:00:00 2001
From: Ali Behjati <bahjatia@gmail.com>
Date: Fri, 20 Dec 2024 14:35:05 +0100
Subject: [PATCH] feat!: add market schedule

This change removes the hard-coded calendar and adds MarketSchedule class to offer the same features using `schedule` metadata in the products.

feat!: add market schedule

This change removes the hard-coded calendar and adds MarketSchedule class to offer the same features using `schedule` metadata in the products.
---
 pythclient/calendar.py                |  440 --------
 pythclient/calendar_full_intervals.py | 1481 -------------------------
 pythclient/market_schedule.py         |  209 ++++
 pythclient/pythaccounts.py            |    9 +
 setup.py                              |    2 +-
 tests/test_calendar.py                |  489 --------
 tests/test_market_schedule.py         |  152 +++
 7 files changed, 371 insertions(+), 2411 deletions(-)
 delete mode 100644 pythclient/calendar.py
 delete mode 100644 pythclient/calendar_full_intervals.py
 create mode 100644 pythclient/market_schedule.py
 delete mode 100644 tests/test_calendar.py
 create mode 100644 tests/test_market_schedule.py

diff --git a/pythclient/calendar.py b/pythclient/calendar.py
deleted file mode 100644
index 3d07e6c..0000000
--- a/pythclient/calendar.py
+++ /dev/null
@@ -1,440 +0,0 @@
-import datetime
-from zoneinfo import ZoneInfo
-
-NY_TZ = ZoneInfo("America/New_York")
-UTC_TZ = ZoneInfo("UTC")
-
-EQUITY_OPEN = datetime.time(9, 30, 0, tzinfo=NY_TZ)
-EQUITY_CLOSE = datetime.time(16, 0, 0, tzinfo=NY_TZ)
-
-NYSE_EARLY_CLOSE = datetime.time(13, 0, 0, tzinfo=NY_TZ)
-
-# NYSE_HOLIDAYS and NYSE_EARLY_HOLIDAYS will need to be updated each year
-# From https://www.nyse.com/markets/hours-calendars
-NYSE_HOLIDAYS = [
-    datetime.datetime(2023, 1, 2, tzinfo=NY_TZ).date(),
-    datetime.datetime(2023, 1, 16, tzinfo=NY_TZ).date(),
-    datetime.datetime(2023, 2, 20, tzinfo=NY_TZ).date(),
-    datetime.datetime(2023, 4, 7, tzinfo=NY_TZ).date(),
-    datetime.datetime(2023, 5, 29, tzinfo=NY_TZ).date(),
-    datetime.datetime(2023, 6, 19, tzinfo=NY_TZ).date(),
-    datetime.datetime(2023, 7, 4, tzinfo=NY_TZ).date(),
-    datetime.datetime(2022, 9, 4, tzinfo=NY_TZ).date(),
-    datetime.datetime(2023, 11, 23, tzinfo=NY_TZ).date(),
-    datetime.datetime(2023, 12, 25, tzinfo=NY_TZ).date(),
-    datetime.datetime(2024, 1, 1, tzinfo=NY_TZ).date(),
-    datetime.datetime(2024, 1, 15, tzinfo=NY_TZ).date(),
-    datetime.datetime(2024, 2, 19, tzinfo=NY_TZ).date(),
-    datetime.datetime(2024, 3, 29, tzinfo=NY_TZ).date(),
-    datetime.datetime(2024, 5, 27, tzinfo=NY_TZ).date(),
-    datetime.datetime(2024, 6, 19, tzinfo=NY_TZ).date(),
-    datetime.datetime(2024, 7, 4, tzinfo=NY_TZ).date(),
-    datetime.datetime(2024, 9, 2, tzinfo=NY_TZ).date(),
-    datetime.datetime(2024, 11, 28, tzinfo=NY_TZ).date(),
-    datetime.datetime(2024, 12, 25, tzinfo=NY_TZ).date(),
-]
-NYSE_EARLY_HOLIDAYS = [
-    datetime.datetime(2023, 7, 3, tzinfo=NY_TZ).date(),
-    datetime.datetime(2023, 11, 24, tzinfo=NY_TZ).date(),
-    datetime.datetime(2024, 7, 3, tzinfo=NY_TZ).date(),
-    datetime.datetime(2024, 11, 29, tzinfo=NY_TZ).date(),
-    datetime.datetime(2024, 12, 24, tzinfo=NY_TZ).date(),
-]
-
-FX_OPEN_CLOSE_TIME = datetime.time(17, 0, 0, tzinfo=NY_TZ)
-
-# FX_METAL_HOLIDAYS will need to be updated each year
-# From https://www.cboe.com/about/hours/fx/
-FX_HOLIDAYS = [
-    datetime.datetime(2023, 1, 1, tzinfo=NY_TZ).date(),
-    datetime.datetime(2023, 12, 25, tzinfo=NY_TZ).date(),
-    datetime.datetime(2024, 1, 1, tzinfo=NY_TZ).date(),
-    datetime.datetime(2024, 12, 25, tzinfo=NY_TZ).date(),
-]
-
-METAL_OPEN_CLOSE_TIME = datetime.time(17, 0, 0, tzinfo=NY_TZ)
-
-
-# References:
-# https://www.forex.com/en-ca/help-and-support/market-trading-hours/
-METAL_EARLY_CLOSE = datetime.time(14, 30, 0, tzinfo=NY_TZ)
-
-# References:
-# https://www.ig.com/uk/help-and-support/spread-betting-and-cfds/market-details/martin-luther-king-jr-trading-hours
-# https://www.etoro.com/trading/market-hours-and-events/
-METAL_EARLY_CLOSE_OPEN = datetime.time(18, 0, 0, tzinfo=NY_TZ)
-
-# FX_METAL_HOLIDAYS will need to be updated each year
-# From https://www.cboe.com/about/hours/fx/
-METAL_HOLIDAYS = [
-    datetime.datetime(2023, 1, 1, tzinfo=NY_TZ).date(),
-    datetime.datetime(2023, 12, 25, tzinfo=NY_TZ).date(),
-    datetime.datetime(2024, 1, 1, tzinfo=NY_TZ).date(),
-    datetime.datetime(2024, 12, 25, tzinfo=NY_TZ).date(),
-]
-METAL_EARLY_HOLIDAYS = [
-    datetime.datetime(2024, 1, 15, tzinfo=NY_TZ).date(),
-    datetime.datetime(2024, 2, 19, tzinfo=NY_TZ).date(),
-]
-
-RATES_OPEN = datetime.time(8, 0, 0, tzinfo=NY_TZ)
-RATES_CLOSE = datetime.time(17, 0, 0, tzinfo=NY_TZ)
-
-
-def is_market_open(asset_type: str, dt: datetime.datetime) -> bool:
-    # make sure time is in NY timezone
-    dt = dt.astimezone(NY_TZ)
-    day, date, time = dt.weekday(), dt.date(), dt.time()
-
-    if asset_type == "equity":
-        if date in NYSE_HOLIDAYS or date in NYSE_EARLY_HOLIDAYS:
-            if (
-                date in NYSE_EARLY_HOLIDAYS
-                and time >= EQUITY_OPEN
-                and time < NYSE_EARLY_CLOSE
-            ):
-                return True
-            return False
-        if day < 5 and time >= EQUITY_OPEN and time < EQUITY_CLOSE:
-            return True
-        return False
-
-    if asset_type == "fx":
-        if date in FX_HOLIDAYS and time < FX_OPEN_CLOSE_TIME:
-            return False
-        # If the next day is a holiday, the market is closed at 5pm ET
-        if (
-            date + datetime.timedelta(days=1) in FX_HOLIDAYS
-        ) and time >= FX_OPEN_CLOSE_TIME:
-            return False
-        # On Friday the market is closed after 5pm
-        if day == 4 and time >= FX_OPEN_CLOSE_TIME:
-            return False
-        # On Saturday the market is closed all the time
-        if day == 5:
-            return False
-        # On Sunday the market is closed before 5pm
-        if day == 6 and time < FX_OPEN_CLOSE_TIME:
-            return False
-        return True
-
-    if asset_type == "metal":
-        if date in METAL_HOLIDAYS and time < METAL_OPEN_CLOSE_TIME:
-            return False
-        # If the next day is a holiday, the market is closed at 5pm ET
-        if (
-            date + datetime.timedelta(days=1) in METAL_HOLIDAYS
-        ) and time >= METAL_OPEN_CLOSE_TIME:
-            return False
-        if (
-            date in METAL_EARLY_HOLIDAYS
-            and time >= METAL_EARLY_CLOSE
-            and time < METAL_EARLY_CLOSE_OPEN
-        ):
-            return False
-        # On Friday the market is closed after 5pm
-        if day == 4 and time >= METAL_OPEN_CLOSE_TIME:
-            return False
-        # On Saturday the market is closed all the time
-        if day == 5:
-            return False
-        # On Sunday the market is closed before 5pm
-        if day == 6 and time < METAL_OPEN_CLOSE_TIME:
-            return False
-        return True
-
-    if asset_type == "rates":
-        if date in NYSE_HOLIDAYS or date in NYSE_EARLY_HOLIDAYS:
-            if (
-                date in NYSE_EARLY_HOLIDAYS
-                and time >= RATES_OPEN
-                and time < NYSE_EARLY_CLOSE
-            ):
-                return True
-            return False
-        if day < 5 and time >= RATES_OPEN and time < RATES_CLOSE:
-            return True
-        return False
-
-    # all other markets (crypto)
-    return True
-
-
-def get_next_market_open(asset_type: str, dt: datetime.datetime) -> int:
-    # make sure time is in NY timezone
-    dt = dt.astimezone(NY_TZ)
-    time = dt.time()
-
-    if asset_type == "equity":
-        if time < EQUITY_OPEN:
-            next_market_open = dt.replace(
-                hour=EQUITY_OPEN.hour,
-                minute=EQUITY_OPEN.minute,
-                second=0,
-                microsecond=0,
-            )
-        else:
-            next_market_open = dt.replace(
-                hour=EQUITY_OPEN.hour,
-                minute=EQUITY_OPEN.minute,
-                second=0,
-                microsecond=0,
-            )
-            next_market_open += datetime.timedelta(days=1)
-    elif asset_type == "fx":
-        if (dt.weekday() == 6 and time < FX_OPEN_CLOSE_TIME) or (
-            dt.date() in FX_HOLIDAYS and time < FX_OPEN_CLOSE_TIME
-        ):
-            next_market_open = dt.replace(
-                hour=FX_OPEN_CLOSE_TIME.hour,
-                minute=FX_OPEN_CLOSE_TIME.minute,
-                second=0,
-                microsecond=0,
-            )
-        else:
-            next_market_open = dt.replace(
-                hour=FX_OPEN_CLOSE_TIME.hour,
-                minute=FX_OPEN_CLOSE_TIME.minute,
-                second=0,
-                microsecond=0,
-            )
-            while is_market_open(asset_type, next_market_open):
-                next_market_open += datetime.timedelta(days=1)
-    elif asset_type == "metal":
-        if dt.date() in METAL_EARLY_HOLIDAYS and time < METAL_EARLY_CLOSE_OPEN:
-            next_market_open = dt.replace(
-                hour=METAL_EARLY_CLOSE_OPEN.hour,
-                minute=METAL_EARLY_CLOSE_OPEN.minute,
-                second=0,
-                microsecond=0,
-            )
-        elif dt.date() in METAL_EARLY_HOLIDAYS and time >= METAL_EARLY_CLOSE_OPEN:
-            next_market_open = dt.replace(
-                hour=METAL_OPEN_CLOSE_TIME.hour,
-                minute=METAL_OPEN_CLOSE_TIME.minute,
-                second=0,
-                microsecond=0,
-            )
-            next_market_open += datetime.timedelta(days=1)
-            while is_market_open(asset_type, next_market_open):
-                next_market_open += datetime.timedelta(days=1)
-        else:
-            if (dt.weekday() == 6 and time < METAL_OPEN_CLOSE_TIME) or (
-                dt.date() in METAL_HOLIDAYS and time < METAL_OPEN_CLOSE_TIME
-            ):
-                next_market_open = dt.replace(
-                    hour=METAL_OPEN_CLOSE_TIME.hour,
-                    minute=METAL_OPEN_CLOSE_TIME.minute,
-                    second=0,
-                    microsecond=0,
-                )
-            else:
-                next_market_open = dt.replace(
-                    hour=METAL_OPEN_CLOSE_TIME.hour,
-                    minute=METAL_OPEN_CLOSE_TIME.minute,
-                    second=0,
-                    microsecond=0,
-                )
-                while is_market_open(asset_type, next_market_open):
-                    next_market_open += datetime.timedelta(days=1)
-    elif asset_type == "rates":
-        if time < RATES_OPEN:
-            next_market_open = dt.replace(
-                hour=RATES_OPEN.hour,
-                minute=RATES_OPEN.minute,
-                second=0,
-                microsecond=0,
-            )
-        else:
-            next_market_open = dt.replace(
-                hour=RATES_OPEN.hour,
-                minute=RATES_OPEN.minute,
-                second=0,
-                microsecond=0,
-            )
-            next_market_open += datetime.timedelta(days=1)
-    else:
-        return None
-
-    while not is_market_open(asset_type, next_market_open):
-        next_market_open += datetime.timedelta(days=1)
-
-    return int(next_market_open.timestamp())
-
-
-def get_next_market_close(asset_type: str, dt: datetime.datetime) -> int:
-    # make sure time is in NY timezone
-    dt = dt.astimezone(NY_TZ)
-    time = dt.time()
-
-    if asset_type == "equity":
-        if dt.date() in NYSE_EARLY_HOLIDAYS:
-            if time < NYSE_EARLY_CLOSE:
-                next_market_close = dt.replace(
-                    hour=NYSE_EARLY_CLOSE.hour,
-                    minute=NYSE_EARLY_CLOSE.minute,
-                    second=0,
-                    microsecond=0,
-                )
-            else:
-                next_market_close = dt.replace(
-                    hour=EQUITY_CLOSE.hour,
-                    minute=EQUITY_CLOSE.minute,
-                    second=0,
-                    microsecond=0,
-                )
-                next_market_close += datetime.timedelta(days=1)
-        elif dt.date() in NYSE_HOLIDAYS:
-            next_market_open = get_next_market_open(asset_type, dt)
-            next_market_open_date = (
-                datetime.datetime.fromtimestamp(next_market_open)
-                .astimezone(NY_TZ)
-                .date()
-            )
-            if next_market_open_date in NYSE_EARLY_HOLIDAYS:
-                next_market_close = (
-                    datetime.datetime.fromtimestamp(next_market_open)
-                    .astimezone(NY_TZ)
-                    .replace(
-                        hour=NYSE_EARLY_CLOSE.hour,
-                        minute=NYSE_EARLY_CLOSE.minute,
-                        second=0,
-                        microsecond=0,
-                    )
-                )
-            else:
-                next_market_close = (
-                    datetime.datetime.fromtimestamp(next_market_open)
-                    .astimezone(NY_TZ)
-                    .replace(
-                        hour=EQUITY_CLOSE.hour,
-                        minute=EQUITY_CLOSE.minute,
-                        second=0,
-                        microsecond=0,
-                    )
-                )
-        else:
-            next_market_close = dt.replace(
-                hour=EQUITY_CLOSE.hour,
-                minute=EQUITY_CLOSE.minute,
-                second=0,
-                microsecond=0,
-            )
-            if time >= EQUITY_CLOSE:
-                next_market_close += datetime.timedelta(days=1)
-
-        # while next_market_close.date() is in NYSE_HOLIDAYS or weekend, add 1 day
-        while (
-            next_market_close.date() in NYSE_HOLIDAYS
-            or next_market_close.weekday() >= 5
-        ):
-            next_market_close += datetime.timedelta(days=1)
-
-    elif asset_type == "fx":
-        next_market_close = dt.replace(
-            hour=FX_OPEN_CLOSE_TIME.hour,
-            minute=FX_OPEN_CLOSE_TIME.minute,
-            second=0,
-            microsecond=0,
-        )
-        if dt.weekday() != 4:
-            while not is_market_open(asset_type, next_market_close):
-                next_market_close += datetime.timedelta(days=1)
-            while is_market_open(asset_type, next_market_close):
-                next_market_close += datetime.timedelta(days=1)
-    elif asset_type == "metal":
-        if dt.date() in METAL_EARLY_HOLIDAYS and time < METAL_EARLY_CLOSE:
-            next_market_close = dt.replace(
-                hour=METAL_EARLY_CLOSE.hour,
-                minute=METAL_EARLY_CLOSE.minute,
-                second=0,
-                microsecond=0,
-            )
-        elif dt.date() in METAL_EARLY_HOLIDAYS and time >= METAL_EARLY_CLOSE:
-            next_market_close = dt.replace(
-                hour=METAL_OPEN_CLOSE_TIME.hour,
-                minute=METAL_OPEN_CLOSE_TIME.minute,
-                second=0,
-                microsecond=0,
-            )
-            next_market_close += datetime.timedelta(days=1)
-            while is_market_open(asset_type, next_market_close):
-                next_market_close += datetime.timedelta(days=1)
-        else:
-            next_market_close = dt.replace(
-                hour=METAL_OPEN_CLOSE_TIME.hour,
-                minute=METAL_OPEN_CLOSE_TIME.minute,
-                second=0,
-                microsecond=0,
-            )
-            if dt.weekday() != 4:
-                while not is_market_open(asset_type, next_market_close):
-                    next_market_close += datetime.timedelta(days=1)
-                while is_market_open(asset_type, next_market_close):
-                    next_market_close += datetime.timedelta(days=1)
-    elif asset_type == "rates":
-        if dt.date() in NYSE_EARLY_HOLIDAYS:
-            if time < NYSE_EARLY_CLOSE:
-                next_market_close = dt.replace(
-                    hour=NYSE_EARLY_CLOSE.hour,
-                    minute=NYSE_EARLY_CLOSE.minute,
-                    second=0,
-                    microsecond=0,
-                )
-            else:
-                next_market_close = dt.replace(
-                    hour=RATES_CLOSE.hour,
-                    minute=RATES_CLOSE.minute,
-                    second=0,
-                    microsecond=0,
-                )
-                next_market_close += datetime.timedelta(days=1)
-        elif dt.date() in NYSE_HOLIDAYS:
-            next_market_open = get_next_market_open(asset_type, dt)
-            next_market_open_date = (
-                datetime.datetime.fromtimestamp(next_market_open)
-                .astimezone(NY_TZ)
-                .date()
-            )
-            if next_market_open_date in NYSE_EARLY_HOLIDAYS:
-                next_market_close = (
-                    datetime.datetime.fromtimestamp(next_market_open)
-                    .astimezone(NY_TZ)
-                    .replace(
-                        hour=NYSE_EARLY_CLOSE.hour,
-                        minute=NYSE_EARLY_CLOSE.minute,
-                        second=0,
-                        microsecond=0,
-                    )
-                )
-            else:
-                next_market_close = (
-                    datetime.datetime.fromtimestamp(next_market_open)
-                    .astimezone(NY_TZ)
-                    .replace(
-                        hour=RATES_CLOSE.hour,
-                        minute=RATES_CLOSE.minute,
-                        second=0,
-                        microsecond=0,
-                    )
-                )
-        else:
-            next_market_close = dt.replace(
-                hour=RATES_CLOSE.hour,
-                minute=RATES_CLOSE.minute,
-                second=0,
-                microsecond=0,
-            )
-            if time >= RATES_CLOSE:
-                next_market_close += datetime.timedelta(days=1)
-
-        # while next_market_close.date() is in NYSE_HOLIDAYS or weekend, add 1 day
-        while (
-            next_market_close.date() in NYSE_HOLIDAYS
-            or next_market_close.weekday() >= 5
-        ):
-            next_market_close += datetime.timedelta(days=1)
-    else:  # crypto markets never close
-        return None
-
-    return int(next_market_close.timestamp())
diff --git a/pythclient/calendar_full_intervals.py b/pythclient/calendar_full_intervals.py
deleted file mode 100644
index 0ed8aba..0000000
--- a/pythclient/calendar_full_intervals.py
+++ /dev/null
@@ -1,1481 +0,0 @@
-import datetime
-
-EQUITY_2024_INTERVALS = [
-    (datetime.date(2024, 1, 1), None),
-    (datetime.date(2024, 1, 2), "0930-1600"),
-    (datetime.date(2024, 1, 3), "0930-1600"),
-    (datetime.date(2024, 1, 4), "0930-1600"),
-    (datetime.date(2024, 1, 5), "0930-1600"),
-    (datetime.date(2024, 1, 6), None),
-    (datetime.date(2024, 1, 7), None),
-    (datetime.date(2024, 1, 8), "0930-1600"),
-    (datetime.date(2024, 1, 9), "0930-1600"),
-    (datetime.date(2024, 1, 10), "0930-1600"),
-    (datetime.date(2024, 1, 11), "0930-1600"),
-    (datetime.date(2024, 1, 12), "0930-1600"),
-    (datetime.date(2024, 1, 13), None),
-    (datetime.date(2024, 1, 14), None),
-    (datetime.date(2024, 1, 15), None),
-    (datetime.date(2024, 1, 16), "0930-1600"),
-    (datetime.date(2024, 1, 17), "0930-1600"),
-    (datetime.date(2024, 1, 18), "0930-1600"),
-    (datetime.date(2024, 1, 19), "0930-1600"),
-    (datetime.date(2024, 1, 20), None),
-    (datetime.date(2024, 1, 21), None),
-    (datetime.date(2024, 1, 22), "0930-1600"),
-    (datetime.date(2024, 1, 23), "0930-1600"),
-    (datetime.date(2024, 1, 24), "0930-1600"),
-    (datetime.date(2024, 1, 25), "0930-1600"),
-    (datetime.date(2024, 1, 26), "0930-1600"),
-    (datetime.date(2024, 1, 27), None),
-    (datetime.date(2024, 1, 28), None),
-    (datetime.date(2024, 1, 29), "0930-1600"),
-    (datetime.date(2024, 1, 30), "0930-1600"),
-    (datetime.date(2024, 1, 31), "0930-1600"),
-    (datetime.date(2024, 2, 1), "0930-1600"),
-    (datetime.date(2024, 2, 2), "0930-1600"),
-    (datetime.date(2024, 2, 3), None),
-    (datetime.date(2024, 2, 4), None),
-    (datetime.date(2024, 2, 5), "0930-1600"),
-    (datetime.date(2024, 2, 6), "0930-1600"),
-    (datetime.date(2024, 2, 7), "0930-1600"),
-    (datetime.date(2024, 2, 8), "0930-1600"),
-    (datetime.date(2024, 2, 9), "0930-1600"),
-    (datetime.date(2024, 2, 10), None),
-    (datetime.date(2024, 2, 11), None),
-    (datetime.date(2024, 2, 12), "0930-1600"),
-    (datetime.date(2024, 2, 13), "0930-1600"),
-    (datetime.date(2024, 2, 14), "0930-1600"),
-    (datetime.date(2024, 2, 15), "0930-1600"),
-    (datetime.date(2024, 2, 16), "0930-1600"),
-    (datetime.date(2024, 2, 17), None),
-    (datetime.date(2024, 2, 18), None),
-    (datetime.date(2024, 2, 19), None),
-    (datetime.date(2024, 2, 20), "0930-1600"),
-    (datetime.date(2024, 2, 21), "0930-1600"),
-    (datetime.date(2024, 2, 22), "0930-1600"),
-    (datetime.date(2024, 2, 23), "0930-1600"),
-    (datetime.date(2024, 2, 24), None),
-    (datetime.date(2024, 2, 25), None),
-    (datetime.date(2024, 2, 26), "0930-1600"),
-    (datetime.date(2024, 2, 27), "0930-1600"),
-    (datetime.date(2024, 2, 28), "0930-1600"),
-    (datetime.date(2024, 2, 29), "0930-1600"),
-    (datetime.date(2024, 3, 1), "0930-1600"),
-    (datetime.date(2024, 3, 2), None),
-    (datetime.date(2024, 3, 3), None),
-    (datetime.date(2024, 3, 4), "0930-1600"),
-    (datetime.date(2024, 3, 5), "0930-1600"),
-    (datetime.date(2024, 3, 6), "0930-1600"),
-    (datetime.date(2024, 3, 7), "0930-1600"),
-    (datetime.date(2024, 3, 8), "0930-1600"),
-    (datetime.date(2024, 3, 9), None),
-    (datetime.date(2024, 3, 10), None),
-    (datetime.date(2024, 3, 11), "0930-1600"),
-    (datetime.date(2024, 3, 12), "0930-1600"),
-    (datetime.date(2024, 3, 13), "0930-1600"),
-    (datetime.date(2024, 3, 14), "0930-1600"),
-    (datetime.date(2024, 3, 15), "0930-1600"),
-    (datetime.date(2024, 3, 16), None),
-    (datetime.date(2024, 3, 17), None),
-    (datetime.date(2024, 3, 18), "0930-1600"),
-    (datetime.date(2024, 3, 19), "0930-1600"),
-    (datetime.date(2024, 3, 20), "0930-1600"),
-    (datetime.date(2024, 3, 21), "0930-1600"),
-    (datetime.date(2024, 3, 22), "0930-1600"),
-    (datetime.date(2024, 3, 23), None),
-    (datetime.date(2024, 3, 24), None),
-    (datetime.date(2024, 3, 25), "0930-1600"),
-    (datetime.date(2024, 3, 26), "0930-1600"),
-    (datetime.date(2024, 3, 27), "0930-1600"),
-    (datetime.date(2024, 3, 28), "0930-1600"),
-    (datetime.date(2024, 3, 29), None),
-    (datetime.date(2024, 3, 30), None),
-    (datetime.date(2024, 3, 31), None),
-    (datetime.date(2024, 4, 1), "0930-1600"),
-    (datetime.date(2024, 4, 2), "0930-1600"),
-    (datetime.date(2024, 4, 3), "0930-1600"),
-    (datetime.date(2024, 4, 4), "0930-1600"),
-    (datetime.date(2024, 4, 5), "0930-1600"),
-    (datetime.date(2024, 4, 6), None),
-    (datetime.date(2024, 4, 7), None),
-    (datetime.date(2024, 4, 8), "0930-1600"),
-    (datetime.date(2024, 4, 9), "0930-1600"),
-    (datetime.date(2024, 4, 10), "0930-1600"),
-    (datetime.date(2024, 4, 11), "0930-1600"),
-    (datetime.date(2024, 4, 12), "0930-1600"),
-    (datetime.date(2024, 4, 13), None),
-    (datetime.date(2024, 4, 14), None),
-    (datetime.date(2024, 4, 15), "0930-1600"),
-    (datetime.date(2024, 4, 16), "0930-1600"),
-    (datetime.date(2024, 4, 17), "0930-1600"),
-    (datetime.date(2024, 4, 18), "0930-1600"),
-    (datetime.date(2024, 4, 19), "0930-1600"),
-    (datetime.date(2024, 4, 20), None),
-    (datetime.date(2024, 4, 21), None),
-    (datetime.date(2024, 4, 22), "0930-1600"),
-    (datetime.date(2024, 4, 23), "0930-1600"),
-    (datetime.date(2024, 4, 24), "0930-1600"),
-    (datetime.date(2024, 4, 25), "0930-1600"),
-    (datetime.date(2024, 4, 26), "0930-1600"),
-    (datetime.date(2024, 4, 27), None),
-    (datetime.date(2024, 4, 28), None),
-    (datetime.date(2024, 4, 29), "0930-1600"),
-    (datetime.date(2024, 4, 30), "0930-1600"),
-    (datetime.date(2024, 5, 1), "0930-1600"),
-    (datetime.date(2024, 5, 2), "0930-1600"),
-    (datetime.date(2024, 5, 3), "0930-1600"),
-    (datetime.date(2024, 5, 4), None),
-    (datetime.date(2024, 5, 5), None),
-    (datetime.date(2024, 5, 6), "0930-1600"),
-    (datetime.date(2024, 5, 7), "0930-1600"),
-    (datetime.date(2024, 5, 8), "0930-1600"),
-    (datetime.date(2024, 5, 9), "0930-1600"),
-    (datetime.date(2024, 5, 10), "0930-1600"),
-    (datetime.date(2024, 5, 11), None),
-    (datetime.date(2024, 5, 12), None),
-    (datetime.date(2024, 5, 13), "0930-1600"),
-    (datetime.date(2024, 5, 14), "0930-1600"),
-    (datetime.date(2024, 5, 15), "0930-1600"),
-    (datetime.date(2024, 5, 16), "0930-1600"),
-    (datetime.date(2024, 5, 17), "0930-1600"),
-    (datetime.date(2024, 5, 18), None),
-    (datetime.date(2024, 5, 19), None),
-    (datetime.date(2024, 5, 20), "0930-1600"),
-    (datetime.date(2024, 5, 21), "0930-1600"),
-    (datetime.date(2024, 5, 22), "0930-1600"),
-    (datetime.date(2024, 5, 23), "0930-1600"),
-    (datetime.date(2024, 5, 24), "0930-1600"),
-    (datetime.date(2024, 5, 25), None),
-    (datetime.date(2024, 5, 26), None),
-    (datetime.date(2024, 5, 27), None),
-    (datetime.date(2024, 5, 28), "0930-1600"),
-    (datetime.date(2024, 5, 29), "0930-1600"),
-    (datetime.date(2024, 5, 30), "0930-1600"),
-    (datetime.date(2024, 5, 31), "0930-1600"),
-    (datetime.date(2024, 6, 1), None),
-    (datetime.date(2024, 6, 2), None),
-    (datetime.date(2024, 6, 3), "0930-1600"),
-    (datetime.date(2024, 6, 4), "0930-1600"),
-    (datetime.date(2024, 6, 5), "0930-1600"),
-    (datetime.date(2024, 6, 6), "0930-1600"),
-    (datetime.date(2024, 6, 7), "0930-1600"),
-    (datetime.date(2024, 6, 8), None),
-    (datetime.date(2024, 6, 9), None),
-    (datetime.date(2024, 6, 10), "0930-1600"),
-    (datetime.date(2024, 6, 11), "0930-1600"),
-    (datetime.date(2024, 6, 12), "0930-1600"),
-    (datetime.date(2024, 6, 13), "0930-1600"),
-    (datetime.date(2024, 6, 14), "0930-1600"),
-    (datetime.date(2024, 6, 15), None),
-    (datetime.date(2024, 6, 16), None),
-    (datetime.date(2024, 6, 17), "0930-1600"),
-    (datetime.date(2024, 6, 18), "0930-1600"),
-    (datetime.date(2024, 6, 19), None),
-    (datetime.date(2024, 6, 20), "0930-1600"),
-    (datetime.date(2024, 6, 21), "0930-1600"),
-    (datetime.date(2024, 6, 22), None),
-    (datetime.date(2024, 6, 23), None),
-    (datetime.date(2024, 6, 24), "0930-1600"),
-    (datetime.date(2024, 6, 25), "0930-1600"),
-    (datetime.date(2024, 6, 26), "0930-1600"),
-    (datetime.date(2024, 6, 27), "0930-1600"),
-    (datetime.date(2024, 6, 28), "0930-1600"),
-    (datetime.date(2024, 6, 29), None),
-    (datetime.date(2024, 6, 30), None),
-    (datetime.date(2024, 7, 1), "0930-1600"),
-    (datetime.date(2024, 7, 2), "0930-1600"),
-    (datetime.date(2024, 7, 3), "0930-1300"),
-    (datetime.date(2024, 7, 4), None),
-    (datetime.date(2024, 7, 5), "0930-1600"),
-    (datetime.date(2024, 7, 6), None),
-    (datetime.date(2024, 7, 7), None),
-    (datetime.date(2024, 7, 8), "0930-1600"),
-    (datetime.date(2024, 7, 9), "0930-1600"),
-    (datetime.date(2024, 7, 10), "0930-1600"),
-    (datetime.date(2024, 7, 11), "0930-1600"),
-    (datetime.date(2024, 7, 12), "0930-1600"),
-    (datetime.date(2024, 7, 13), None),
-    (datetime.date(2024, 7, 14), None),
-    (datetime.date(2024, 7, 15), "0930-1600"),
-    (datetime.date(2024, 7, 16), "0930-1600"),
-    (datetime.date(2024, 7, 17), "0930-1600"),
-    (datetime.date(2024, 7, 18), "0930-1600"),
-    (datetime.date(2024, 7, 19), "0930-1600"),
-    (datetime.date(2024, 7, 20), None),
-    (datetime.date(2024, 7, 21), None),
-    (datetime.date(2024, 7, 22), "0930-1600"),
-    (datetime.date(2024, 7, 23), "0930-1600"),
-    (datetime.date(2024, 7, 24), "0930-1600"),
-    (datetime.date(2024, 7, 25), "0930-1600"),
-    (datetime.date(2024, 7, 26), "0930-1600"),
-    (datetime.date(2024, 7, 27), None),
-    (datetime.date(2024, 7, 28), None),
-    (datetime.date(2024, 7, 29), "0930-1600"),
-    (datetime.date(2024, 7, 30), "0930-1600"),
-    (datetime.date(2024, 7, 31), "0930-1600"),
-    (datetime.date(2024, 8, 1), "0930-1600"),
-    (datetime.date(2024, 8, 2), "0930-1600"),
-    (datetime.date(2024, 8, 3), None),
-    (datetime.date(2024, 8, 4), None),
-    (datetime.date(2024, 8, 5), "0930-1600"),
-    (datetime.date(2024, 8, 6), "0930-1600"),
-    (datetime.date(2024, 8, 7), "0930-1600"),
-    (datetime.date(2024, 8, 8), "0930-1600"),
-    (datetime.date(2024, 8, 9), "0930-1600"),
-    (datetime.date(2024, 8, 10), None),
-    (datetime.date(2024, 8, 11), None),
-    (datetime.date(2024, 8, 12), "0930-1600"),
-    (datetime.date(2024, 8, 13), "0930-1600"),
-    (datetime.date(2024, 8, 14), "0930-1600"),
-    (datetime.date(2024, 8, 15), "0930-1600"),
-    (datetime.date(2024, 8, 16), "0930-1600"),
-    (datetime.date(2024, 8, 17), None),
-    (datetime.date(2024, 8, 18), None),
-    (datetime.date(2024, 8, 19), "0930-1600"),
-    (datetime.date(2024, 8, 20), "0930-1600"),
-    (datetime.date(2024, 8, 21), "0930-1600"),
-    (datetime.date(2024, 8, 22), "0930-1600"),
-    (datetime.date(2024, 8, 23), "0930-1600"),
-    (datetime.date(2024, 8, 24), None),
-    (datetime.date(2024, 8, 25), None),
-    (datetime.date(2024, 8, 26), "0930-1600"),
-    (datetime.date(2024, 8, 27), "0930-1600"),
-    (datetime.date(2024, 8, 28), "0930-1600"),
-    (datetime.date(2024, 8, 29), "0930-1600"),
-    (datetime.date(2024, 8, 30), "0930-1600"),
-    (datetime.date(2024, 8, 31), None),
-    (datetime.date(2024, 9, 1), None),
-    (datetime.date(2024, 9, 2), None),
-    (datetime.date(2024, 9, 3), "0930-1600"),
-    (datetime.date(2024, 9, 4), "0930-1600"),
-    (datetime.date(2024, 9, 5), "0930-1600"),
-    (datetime.date(2024, 9, 6), "0930-1600"),
-    (datetime.date(2024, 9, 7), None),
-    (datetime.date(2024, 9, 8), None),
-    (datetime.date(2024, 9, 9), "0930-1600"),
-    (datetime.date(2024, 9, 10), "0930-1600"),
-    (datetime.date(2024, 9, 11), "0930-1600"),
-    (datetime.date(2024, 9, 12), "0930-1600"),
-    (datetime.date(2024, 9, 13), "0930-1600"),
-    (datetime.date(2024, 9, 14), None),
-    (datetime.date(2024, 9, 15), None),
-    (datetime.date(2024, 9, 16), "0930-1600"),
-    (datetime.date(2024, 9, 17), "0930-1600"),
-    (datetime.date(2024, 9, 18), "0930-1600"),
-    (datetime.date(2024, 9, 19), "0930-1600"),
-    (datetime.date(2024, 9, 20), "0930-1600"),
-    (datetime.date(2024, 9, 21), None),
-    (datetime.date(2024, 9, 22), None),
-    (datetime.date(2024, 9, 23), "0930-1600"),
-    (datetime.date(2024, 9, 24), "0930-1600"),
-    (datetime.date(2024, 9, 25), "0930-1600"),
-    (datetime.date(2024, 9, 26), "0930-1600"),
-    (datetime.date(2024, 9, 27), "0930-1600"),
-    (datetime.date(2024, 9, 28), None),
-    (datetime.date(2024, 9, 29), None),
-    (datetime.date(2024, 9, 30), "0930-1600"),
-    (datetime.date(2024, 10, 1), "0930-1600"),
-    (datetime.date(2024, 10, 2), "0930-1600"),
-    (datetime.date(2024, 10, 3), "0930-1600"),
-    (datetime.date(2024, 10, 4), "0930-1600"),
-    (datetime.date(2024, 10, 5), None),
-    (datetime.date(2024, 10, 6), None),
-    (datetime.date(2024, 10, 7), "0930-1600"),
-    (datetime.date(2024, 10, 8), "0930-1600"),
-    (datetime.date(2024, 10, 9), "0930-1600"),
-    (datetime.date(2024, 10, 10), "0930-1600"),
-    (datetime.date(2024, 10, 11), "0930-1600"),
-    (datetime.date(2024, 10, 12), None),
-    (datetime.date(2024, 10, 13), None),
-    (datetime.date(2024, 10, 14), "0930-1600"),
-    (datetime.date(2024, 10, 15), "0930-1600"),
-    (datetime.date(2024, 10, 16), "0930-1600"),
-    (datetime.date(2024, 10, 17), "0930-1600"),
-    (datetime.date(2024, 10, 18), "0930-1600"),
-    (datetime.date(2024, 10, 19), None),
-    (datetime.date(2024, 10, 20), None),
-    (datetime.date(2024, 10, 21), "0930-1600"),
-    (datetime.date(2024, 10, 22), "0930-1600"),
-    (datetime.date(2024, 10, 23), "0930-1600"),
-    (datetime.date(2024, 10, 24), "0930-1600"),
-    (datetime.date(2024, 10, 25), "0930-1600"),
-    (datetime.date(2024, 10, 26), None),
-    (datetime.date(2024, 10, 27), None),
-    (datetime.date(2024, 10, 28), "0930-1600"),
-    (datetime.date(2024, 10, 29), "0930-1600"),
-    (datetime.date(2024, 10, 30), "0930-1600"),
-    (datetime.date(2024, 10, 31), "0930-1600"),
-    (datetime.date(2024, 11, 1), "0930-1600"),
-    (datetime.date(2024, 11, 2), None),
-    (datetime.date(2024, 11, 3), None),
-    (datetime.date(2024, 11, 4), "0930-1600"),
-    (datetime.date(2024, 11, 5), "0930-1600"),
-    (datetime.date(2024, 11, 6), "0930-1600"),
-    (datetime.date(2024, 11, 7), "0930-1600"),
-    (datetime.date(2024, 11, 8), "0930-1600"),
-    (datetime.date(2024, 11, 9), None),
-    (datetime.date(2024, 11, 10), None),
-    (datetime.date(2024, 11, 11), "0930-1600"),
-    (datetime.date(2024, 11, 12), "0930-1600"),
-    (datetime.date(2024, 11, 13), "0930-1600"),
-    (datetime.date(2024, 11, 14), "0930-1600"),
-    (datetime.date(2024, 11, 15), "0930-1600"),
-    (datetime.date(2024, 11, 16), None),
-    (datetime.date(2024, 11, 17), None),
-    (datetime.date(2024, 11, 18), "0930-1600"),
-    (datetime.date(2024, 11, 19), "0930-1600"),
-    (datetime.date(2024, 11, 20), "0930-1600"),
-    (datetime.date(2024, 11, 21), "0930-1600"),
-    (datetime.date(2024, 11, 22), "0930-1600"),
-    (datetime.date(2024, 11, 23), None),
-    (datetime.date(2024, 11, 24), None),
-    (datetime.date(2024, 11, 25), "0930-1600"),
-    (datetime.date(2024, 11, 26), "0930-1600"),
-    (datetime.date(2024, 11, 27), "0930-1600"),
-    (datetime.date(2024, 11, 28), None),
-    (datetime.date(2024, 11, 29), "0930-1300"),
-    (datetime.date(2024, 11, 30), None),
-    (datetime.date(2024, 12, 1), None),
-    (datetime.date(2024, 12, 2), "0930-1600"),
-    (datetime.date(2024, 12, 3), "0930-1600"),
-    (datetime.date(2024, 12, 4), "0930-1600"),
-    (datetime.date(2024, 12, 5), "0930-1600"),
-    (datetime.date(2024, 12, 6), "0930-1600"),
-    (datetime.date(2024, 12, 7), None),
-    (datetime.date(2024, 12, 8), None),
-    (datetime.date(2024, 12, 9), "0930-1600"),
-    (datetime.date(2024, 12, 10), "0930-1600"),
-    (datetime.date(2024, 12, 11), "0930-1600"),
-    (datetime.date(2024, 12, 12), "0930-1600"),
-    (datetime.date(2024, 12, 13), "0930-1600"),
-    (datetime.date(2024, 12, 14), None),
-    (datetime.date(2024, 12, 15), None),
-    (datetime.date(2024, 12, 16), "0930-1600"),
-    (datetime.date(2024, 12, 17), "0930-1600"),
-    (datetime.date(2024, 12, 18), "0930-1600"),
-    (datetime.date(2024, 12, 19), "0930-1600"),
-    (datetime.date(2024, 12, 20), "0930-1600"),
-    (datetime.date(2024, 12, 21), None),
-    (datetime.date(2024, 12, 22), None),
-    (datetime.date(2024, 12, 23), "0930-1600"),
-    (datetime.date(2024, 12, 24), "0930-1300"),
-    (datetime.date(2024, 12, 25), None),
-    (datetime.date(2024, 12, 26), "0930-1600"),
-    (datetime.date(2024, 12, 27), "0930-1600"),
-    (datetime.date(2024, 12, 28), None),
-    (datetime.date(2024, 12, 29), None),
-    (datetime.date(2024, 12, 30), "0930-1600"),
-    (datetime.date(2024, 12, 31), "0930-1600"),
-]
-
-FX_2024_INTERVALS = [
-    (datetime.date(2023, 12, 31), None),
-    (datetime.date(2024, 1, 1), "1700-0000"),
-    (datetime.date(2024, 1, 2), "0000-0000"),
-    (datetime.date(2024, 1, 3), "0000-0000"),
-    (datetime.date(2024, 1, 4), "0000-0000"),
-    (datetime.date(2024, 1, 5), "0000-1700"),
-    (datetime.date(2024, 1, 6), None),
-    (datetime.date(2024, 1, 7), "1700-0000"),
-    (datetime.date(2024, 1, 8), "0000-0000"),
-    (datetime.date(2024, 1, 9), "0000-0000"),
-    (datetime.date(2024, 1, 10), "0000-0000"),
-    (datetime.date(2024, 1, 11), "0000-0000"),
-    (datetime.date(2024, 1, 12), "0000-1700"),
-    (datetime.date(2024, 1, 13), None),
-    (datetime.date(2024, 1, 14), "1700-0000"),
-    (datetime.date(2024, 1, 15), "0000-0000"),
-    (datetime.date(2024, 1, 16), "0000-0000"),
-    (datetime.date(2024, 1, 17), "0000-0000"),
-    (datetime.date(2024, 1, 18), "0000-0000"),
-    (datetime.date(2024, 1, 19), "0000-1700"),
-    (datetime.date(2024, 1, 20), None),
-    (datetime.date(2024, 1, 21), "1700-0000"),
-    (datetime.date(2024, 1, 22), "0000-0000"),
-    (datetime.date(2024, 1, 23), "0000-0000"),
-    (datetime.date(2024, 1, 24), "0000-0000"),
-    (datetime.date(2024, 1, 25), "0000-0000"),
-    (datetime.date(2024, 1, 26), "0000-1700"),
-    (datetime.date(2024, 1, 27), None),
-    (datetime.date(2024, 1, 28), "1700-0000"),
-    (datetime.date(2024, 1, 29), "0000-0000"),
-    (datetime.date(2024, 1, 30), "0000-0000"),
-    (datetime.date(2024, 1, 31), "0000-0000"),
-    (datetime.date(2024, 2, 1), "0000-0000"),
-    (datetime.date(2024, 2, 2), "0000-1700"),
-    (datetime.date(2024, 2, 3), None),
-    (datetime.date(2024, 2, 4), "1700-0000"),
-    (datetime.date(2024, 2, 5), "0000-0000"),
-    (datetime.date(2024, 2, 6), "0000-0000"),
-    (datetime.date(2024, 2, 7), "0000-0000"),
-    (datetime.date(2024, 2, 8), "0000-0000"),
-    (datetime.date(2024, 2, 9), "0000-1700"),
-    (datetime.date(2024, 2, 10), None),
-    (datetime.date(2024, 2, 11), "1700-0000"),
-    (datetime.date(2024, 2, 12), "0000-0000"),
-    (datetime.date(2024, 2, 13), "0000-0000"),
-    (datetime.date(2024, 2, 14), "0000-0000"),
-    (datetime.date(2024, 2, 15), "0000-0000"),
-    (datetime.date(2024, 2, 16), "0000-1700"),
-    (datetime.date(2024, 2, 17), None),
-    (datetime.date(2024, 2, 18), "1700-0000"),
-    (datetime.date(2024, 2, 19), "0000-0000"),
-    (datetime.date(2024, 2, 20), "0000-0000"),
-    (datetime.date(2024, 2, 21), "0000-0000"),
-    (datetime.date(2024, 2, 22), "0000-0000"),
-    (datetime.date(2024, 2, 23), "0000-1700"),
-    (datetime.date(2024, 2, 24), None),
-    (datetime.date(2024, 2, 25), "1700-0000"),
-    (datetime.date(2024, 2, 26), "0000-0000"),
-    (datetime.date(2024, 2, 27), "0000-0000"),
-    (datetime.date(2024, 2, 28), "0000-0000"),
-    (datetime.date(2024, 2, 29), "0000-0000"),
-    (datetime.date(2024, 3, 1), "0000-1700"),
-    (datetime.date(2024, 3, 2), None),
-    (datetime.date(2024, 3, 3), "1700-0000"),
-    (datetime.date(2024, 3, 4), "0000-0000"),
-    (datetime.date(2024, 3, 5), "0000-0000"),
-    (datetime.date(2024, 3, 6), "0000-0000"),
-    (datetime.date(2024, 3, 7), "0000-0000"),
-    (datetime.date(2024, 3, 8), "0000-1700"),
-    (datetime.date(2024, 3, 9), None),
-    (datetime.date(2024, 3, 10), "1700-0000"),
-    (datetime.date(2024, 3, 11), "0000-0000"),
-    (datetime.date(2024, 3, 12), "0000-0000"),
-    (datetime.date(2024, 3, 13), "0000-0000"),
-    (datetime.date(2024, 3, 14), "0000-0000"),
-    (datetime.date(2024, 3, 15), "0000-1700"),
-    (datetime.date(2024, 3, 16), None),
-    (datetime.date(2024, 3, 17), "1700-0000"),
-    (datetime.date(2024, 3, 18), "0000-0000"),
-    (datetime.date(2024, 3, 19), "0000-0000"),
-    (datetime.date(2024, 3, 20), "0000-0000"),
-    (datetime.date(2024, 3, 21), "0000-0000"),
-    (datetime.date(2024, 3, 22), "0000-1700"),
-    (datetime.date(2024, 3, 23), None),
-    (datetime.date(2024, 3, 24), "1700-0000"),
-    (datetime.date(2024, 3, 25), "0000-0000"),
-    (datetime.date(2024, 3, 26), "0000-0000"),
-    (datetime.date(2024, 3, 27), "0000-0000"),
-    (datetime.date(2024, 3, 28), "0000-0000"),
-    (datetime.date(2024, 3, 29), "0000-1700"),
-    (datetime.date(2024, 3, 30), None),
-    (datetime.date(2024, 3, 31), "1700-0000"),
-    (datetime.date(2024, 4, 1), "0000-0000"),
-    (datetime.date(2024, 4, 2), "0000-0000"),
-    (datetime.date(2024, 4, 3), "0000-0000"),
-    (datetime.date(2024, 4, 4), "0000-0000"),
-    (datetime.date(2024, 4, 5), "0000-1700"),
-    (datetime.date(2024, 4, 6), None),
-    (datetime.date(2024, 4, 7), "1700-0000"),
-    (datetime.date(2024, 4, 8), "0000-0000"),
-    (datetime.date(2024, 4, 9), "0000-0000"),
-    (datetime.date(2024, 4, 10), "0000-0000"),
-    (datetime.date(2024, 4, 11), "0000-0000"),
-    (datetime.date(2024, 4, 12), "0000-1700"),
-    (datetime.date(2024, 4, 13), None),
-    (datetime.date(2024, 4, 14), "1700-0000"),
-    (datetime.date(2024, 4, 15), "0000-0000"),
-    (datetime.date(2024, 4, 16), "0000-0000"),
-    (datetime.date(2024, 4, 17), "0000-0000"),
-    (datetime.date(2024, 4, 18), "0000-0000"),
-    (datetime.date(2024, 4, 19), "0000-1700"),
-    (datetime.date(2024, 4, 20), None),
-    (datetime.date(2024, 4, 21), "1700-0000"),
-    (datetime.date(2024, 4, 22), "0000-0000"),
-    (datetime.date(2024, 4, 23), "0000-0000"),
-    (datetime.date(2024, 4, 24), "0000-0000"),
-    (datetime.date(2024, 4, 25), "0000-0000"),
-    (datetime.date(2024, 4, 26), "0000-1700"),
-    (datetime.date(2024, 4, 27), None),
-    (datetime.date(2024, 4, 28), "1700-0000"),
-    (datetime.date(2024, 4, 29), "0000-0000"),
-    (datetime.date(2024, 4, 30), "0000-0000"),
-    (datetime.date(2024, 5, 1), "0000-0000"),
-    (datetime.date(2024, 5, 2), "0000-0000"),
-    (datetime.date(2024, 5, 3), "0000-1700"),
-    (datetime.date(2024, 5, 4), None),
-    (datetime.date(2024, 5, 5), "1700-0000"),
-    (datetime.date(2024, 5, 6), "0000-0000"),
-    (datetime.date(2024, 5, 7), "0000-0000"),
-    (datetime.date(2024, 5, 8), "0000-0000"),
-    (datetime.date(2024, 5, 9), "0000-0000"),
-    (datetime.date(2024, 5, 10), "0000-1700"),
-    (datetime.date(2024, 5, 11), None),
-    (datetime.date(2024, 5, 12), "1700-0000"),
-    (datetime.date(2024, 5, 13), "0000-0000"),
-    (datetime.date(2024, 5, 14), "0000-0000"),
-    (datetime.date(2024, 5, 15), "0000-0000"),
-    (datetime.date(2024, 5, 16), "0000-0000"),
-    (datetime.date(2024, 5, 17), "0000-1700"),
-    (datetime.date(2024, 5, 18), None),
-    (datetime.date(2024, 5, 19), "1700-0000"),
-    (datetime.date(2024, 5, 20), "0000-0000"),
-    (datetime.date(2024, 5, 21), "0000-0000"),
-    (datetime.date(2024, 5, 22), "0000-0000"),
-    (datetime.date(2024, 5, 23), "0000-0000"),
-    (datetime.date(2024, 5, 24), "0000-1700"),
-    (datetime.date(2024, 5, 25), None),
-    (datetime.date(2024, 5, 26), "1700-0000"),
-    (datetime.date(2024, 5, 27), "0000-0000"),
-    (datetime.date(2024, 5, 28), "0000-0000"),
-    (datetime.date(2024, 5, 29), "0000-0000"),
-    (datetime.date(2024, 5, 30), "0000-0000"),
-    (datetime.date(2024, 5, 31), "0000-1700"),
-    (datetime.date(2024, 6, 1), None),
-    (datetime.date(2024, 6, 2), "1700-0000"),
-    (datetime.date(2024, 6, 3), "0000-0000"),
-    (datetime.date(2024, 6, 4), "0000-0000"),
-    (datetime.date(2024, 6, 5), "0000-0000"),
-    (datetime.date(2024, 6, 6), "0000-0000"),
-    (datetime.date(2024, 6, 7), "0000-1700"),
-    (datetime.date(2024, 6, 8), None),
-    (datetime.date(2024, 6, 9), "1700-0000"),
-    (datetime.date(2024, 6, 10), "0000-0000"),
-    (datetime.date(2024, 6, 11), "0000-0000"),
-    (datetime.date(2024, 6, 12), "0000-0000"),
-    (datetime.date(2024, 6, 13), "0000-0000"),
-    (datetime.date(2024, 6, 14), "0000-1700"),
-    (datetime.date(2024, 6, 15), None),
-    (datetime.date(2024, 6, 16), "1700-0000"),
-    (datetime.date(2024, 6, 17), "0000-0000"),
-    (datetime.date(2024, 6, 18), "0000-0000"),
-    (datetime.date(2024, 6, 19), "0000-0000"),
-    (datetime.date(2024, 6, 20), "0000-0000"),
-    (datetime.date(2024, 6, 21), "0000-1700"),
-    (datetime.date(2024, 6, 22), None),
-    (datetime.date(2024, 6, 23), "1700-0000"),
-    (datetime.date(2024, 6, 24), "0000-0000"),
-    (datetime.date(2024, 6, 25), "0000-0000"),
-    (datetime.date(2024, 6, 26), "0000-0000"),
-    (datetime.date(2024, 6, 27), "0000-0000"),
-    (datetime.date(2024, 6, 28), "0000-1700"),
-    (datetime.date(2024, 6, 29), None),
-    (datetime.date(2024, 6, 30), "1700-0000"),
-    (datetime.date(2024, 7, 1), "0000-0000"),
-    (datetime.date(2024, 7, 2), "0000-0000"),
-    (datetime.date(2024, 7, 3), "0000-0000"),
-    (datetime.date(2024, 7, 4), "0000-0000"),
-    (datetime.date(2024, 7, 5), "0000-1700"),
-    (datetime.date(2024, 7, 6), None),
-    (datetime.date(2024, 7, 7), "1700-0000"),
-    (datetime.date(2024, 7, 8), "0000-0000"),
-    (datetime.date(2024, 7, 9), "0000-0000"),
-    (datetime.date(2024, 7, 10), "0000-0000"),
-    (datetime.date(2024, 7, 11), "0000-0000"),
-    (datetime.date(2024, 7, 12), "0000-1700"),
-    (datetime.date(2024, 7, 13), None),
-    (datetime.date(2024, 7, 14), "1700-0000"),
-    (datetime.date(2024, 7, 15), "0000-0000"),
-    (datetime.date(2024, 7, 16), "0000-0000"),
-    (datetime.date(2024, 7, 17), "0000-0000"),
-    (datetime.date(2024, 7, 18), "0000-0000"),
-    (datetime.date(2024, 7, 19), "0000-1700"),
-    (datetime.date(2024, 7, 20), None),
-    (datetime.date(2024, 7, 21), "1700-0000"),
-    (datetime.date(2024, 7, 22), "0000-0000"),
-    (datetime.date(2024, 7, 23), "0000-0000"),
-    (datetime.date(2024, 7, 24), "0000-0000"),
-    (datetime.date(2024, 7, 25), "0000-0000"),
-    (datetime.date(2024, 7, 26), "0000-1700"),
-    (datetime.date(2024, 7, 27), None),
-    (datetime.date(2024, 7, 28), "1700-0000"),
-    (datetime.date(2024, 7, 29), "0000-0000"),
-    (datetime.date(2024, 7, 30), "0000-0000"),
-    (datetime.date(2024, 7, 31), "0000-0000"),
-    (datetime.date(2024, 8, 1), "0000-0000"),
-    (datetime.date(2024, 8, 2), "0000-1700"),
-    (datetime.date(2024, 8, 3), None),
-    (datetime.date(2024, 8, 4), "1700-0000"),
-    (datetime.date(2024, 8, 5), "0000-0000"),
-    (datetime.date(2024, 8, 6), "0000-0000"),
-    (datetime.date(2024, 8, 7), "0000-0000"),
-    (datetime.date(2024, 8, 8), "0000-0000"),
-    (datetime.date(2024, 8, 9), "0000-1700"),
-    (datetime.date(2024, 8, 10), None),
-    (datetime.date(2024, 8, 11), "1700-0000"),
-    (datetime.date(2024, 8, 12), "0000-0000"),
-    (datetime.date(2024, 8, 13), "0000-0000"),
-    (datetime.date(2024, 8, 14), "0000-0000"),
-    (datetime.date(2024, 8, 15), "0000-0000"),
-    (datetime.date(2024, 8, 16), "0000-1700"),
-    (datetime.date(2024, 8, 17), None),
-    (datetime.date(2024, 8, 18), "1700-0000"),
-    (datetime.date(2024, 8, 19), "0000-0000"),
-    (datetime.date(2024, 8, 20), "0000-0000"),
-    (datetime.date(2024, 8, 21), "0000-0000"),
-    (datetime.date(2024, 8, 22), "0000-0000"),
-    (datetime.date(2024, 8, 23), "0000-1700"),
-    (datetime.date(2024, 8, 24), None),
-    (datetime.date(2024, 8, 25), "1700-0000"),
-    (datetime.date(2024, 8, 26), "0000-0000"),
-    (datetime.date(2024, 8, 27), "0000-0000"),
-    (datetime.date(2024, 8, 28), "0000-0000"),
-    (datetime.date(2024, 8, 29), "0000-0000"),
-    (datetime.date(2024, 8, 30), "0000-1700"),
-    (datetime.date(2024, 8, 31), None),
-    (datetime.date(2024, 9, 1), "1700-0000"),
-    (datetime.date(2024, 9, 2), "0000-0000"),
-    (datetime.date(2024, 9, 3), "0000-0000"),
-    (datetime.date(2024, 9, 4), "0000-0000"),
-    (datetime.date(2024, 9, 5), "0000-0000"),
-    (datetime.date(2024, 9, 6), "0000-1700"),
-    (datetime.date(2024, 9, 7), None),
-    (datetime.date(2024, 9, 8), "1700-0000"),
-    (datetime.date(2024, 9, 9), "0000-0000"),
-    (datetime.date(2024, 9, 10), "0000-0000"),
-    (datetime.date(2024, 9, 11), "0000-0000"),
-    (datetime.date(2024, 9, 12), "0000-0000"),
-    (datetime.date(2024, 9, 13), "0000-1700"),
-    (datetime.date(2024, 9, 14), None),
-    (datetime.date(2024, 9, 15), "1700-0000"),
-    (datetime.date(2024, 9, 16), "0000-0000"),
-    (datetime.date(2024, 9, 17), "0000-0000"),
-    (datetime.date(2024, 9, 18), "0000-0000"),
-    (datetime.date(2024, 9, 19), "0000-0000"),
-    (datetime.date(2024, 9, 20), "0000-1700"),
-    (datetime.date(2024, 9, 21), None),
-    (datetime.date(2024, 9, 22), "1700-0000"),
-    (datetime.date(2024, 9, 23), "0000-0000"),
-    (datetime.date(2024, 9, 24), "0000-0000"),
-    (datetime.date(2024, 9, 25), "0000-0000"),
-    (datetime.date(2024, 9, 26), "0000-0000"),
-    (datetime.date(2024, 9, 27), "0000-1700"),
-    (datetime.date(2024, 9, 28), None),
-    (datetime.date(2024, 9, 29), "1700-0000"),
-    (datetime.date(2024, 9, 30), "0000-0000"),
-    (datetime.date(2024, 10, 1), "0000-0000"),
-    (datetime.date(2024, 10, 2), "0000-0000"),
-    (datetime.date(2024, 10, 3), "0000-0000"),
-    (datetime.date(2024, 10, 4), "0000-1700"),
-    (datetime.date(2024, 10, 5), None),
-    (datetime.date(2024, 10, 6), "1700-0000"),
-    (datetime.date(2024, 10, 7), "0000-0000"),
-    (datetime.date(2024, 10, 8), "0000-0000"),
-    (datetime.date(2024, 10, 9), "0000-0000"),
-    (datetime.date(2024, 10, 10), "0000-0000"),
-    (datetime.date(2024, 10, 11), "0000-1700"),
-    (datetime.date(2024, 10, 12), None),
-    (datetime.date(2024, 10, 13), "1700-0000"),
-    (datetime.date(2024, 10, 14), "0000-0000"),
-    (datetime.date(2024, 10, 15), "0000-0000"),
-    (datetime.date(2024, 10, 16), "0000-0000"),
-    (datetime.date(2024, 10, 17), "0000-0000"),
-    (datetime.date(2024, 10, 18), "0000-1700"),
-    (datetime.date(2024, 10, 19), None),
-    (datetime.date(2024, 10, 20), "1700-0000"),
-    (datetime.date(2024, 10, 21), "0000-0000"),
-    (datetime.date(2024, 10, 22), "0000-0000"),
-    (datetime.date(2024, 10, 23), "0000-0000"),
-    (datetime.date(2024, 10, 24), "0000-0000"),
-    (datetime.date(2024, 10, 25), "0000-1700"),
-    (datetime.date(2024, 10, 26), None),
-    (datetime.date(2024, 10, 27), "1700-0000"),
-    (datetime.date(2024, 10, 28), "0000-0000"),
-    (datetime.date(2024, 10, 29), "0000-0000"),
-    (datetime.date(2024, 10, 30), "0000-0000"),
-    (datetime.date(2024, 10, 31), "0000-0000"),
-    (datetime.date(2024, 11, 1), "0000-1700"),
-    (datetime.date(2024, 11, 2), None),
-    (datetime.date(2024, 11, 3), "1700-0000"),
-    (datetime.date(2024, 11, 4), "0000-0000"),
-    (datetime.date(2024, 11, 5), "0000-0000"),
-    (datetime.date(2024, 11, 6), "0000-0000"),
-    (datetime.date(2024, 11, 7), "0000-0000"),
-    (datetime.date(2024, 11, 8), "0000-1700"),
-    (datetime.date(2024, 11, 9), None),
-    (datetime.date(2024, 11, 10), "1700-0000"),
-    (datetime.date(2024, 11, 11), "0000-0000"),
-    (datetime.date(2024, 11, 12), "0000-0000"),
-    (datetime.date(2024, 11, 13), "0000-0000"),
-    (datetime.date(2024, 11, 14), "0000-0000"),
-    (datetime.date(2024, 11, 15), "0000-1700"),
-    (datetime.date(2024, 11, 16), None),
-    (datetime.date(2024, 11, 17), "1700-0000"),
-    (datetime.date(2024, 11, 18), "0000-0000"),
-    (datetime.date(2024, 11, 19), "0000-0000"),
-    (datetime.date(2024, 11, 20), "0000-0000"),
-    (datetime.date(2024, 11, 21), "0000-0000"),
-    (datetime.date(2024, 11, 22), "0000-1700"),
-    (datetime.date(2024, 11, 23), None),
-    (datetime.date(2024, 11, 24), "1700-0000"),
-    (datetime.date(2024, 11, 25), "0000-0000"),
-    (datetime.date(2024, 11, 26), "0000-0000"),
-    (datetime.date(2024, 11, 27), "0000-0000"),
-    (datetime.date(2024, 11, 28), "0000-0000"),
-    (datetime.date(2024, 11, 29), "0000-1700"),
-    (datetime.date(2024, 11, 30), None),
-    (datetime.date(2024, 12, 1), "1700-0000"),
-    (datetime.date(2024, 12, 2), "0000-0000"),
-    (datetime.date(2024, 12, 3), "0000-0000"),
-    (datetime.date(2024, 12, 4), "0000-0000"),
-    (datetime.date(2024, 12, 5), "0000-0000"),
-    (datetime.date(2024, 12, 6), "0000-1700"),
-    (datetime.date(2024, 12, 7), None),
-    (datetime.date(2024, 12, 8), "1700-0000"),
-    (datetime.date(2024, 12, 9), "0000-0000"),
-    (datetime.date(2024, 12, 10), "0000-0000"),
-    (datetime.date(2024, 12, 11), "0000-0000"),
-    (datetime.date(2024, 12, 12), "0000-0000"),
-    (datetime.date(2024, 12, 13), "0000-1700"),
-    (datetime.date(2024, 12, 14), None),
-    (datetime.date(2024, 12, 15), "1700-0000"),
-    (datetime.date(2024, 12, 16), "0000-0000"),
-    (datetime.date(2024, 12, 17), "0000-0000"),
-    (datetime.date(2024, 12, 18), "0000-0000"),
-    (datetime.date(2024, 12, 19), "0000-0000"),
-    (datetime.date(2024, 12, 20), "0000-1700"),
-    (datetime.date(2024, 12, 21), None),
-    (datetime.date(2024, 12, 22), "1700-0000"),
-    (datetime.date(2024, 12, 23), "0000-0000"),
-    (datetime.date(2024, 12, 24), "0000-1700"),
-    (datetime.date(2024, 12, 25), "1700-0000"),
-    (datetime.date(2024, 12, 26), "0000-0000"),
-    (datetime.date(2024, 12, 27), "0000-1700"),
-    (datetime.date(2024, 12, 28), None),
-    (datetime.date(2024, 12, 29), "1700-0000"),
-    (datetime.date(2024, 12, 30), "0000-0000"),
-    (datetime.date(2024, 12, 31), "0000-0000"),
-]
-
-METAL_2024_INTERVALS = [
-    (datetime.date(2023, 12, 31), None),
-    (datetime.date(2024, 1, 1), "1700-0000"),
-    (datetime.date(2024, 1, 2), "0000-0000"),
-    (datetime.date(2024, 1, 3), "0000-0000"),
-    (datetime.date(2024, 1, 4), "0000-0000"),
-    (datetime.date(2024, 1, 5), "0000-1700"),
-    (datetime.date(2024, 1, 6), None),
-    (datetime.date(2024, 1, 7), "1700-0000"),
-    (datetime.date(2024, 1, 8), "0000-0000"),
-    (datetime.date(2024, 1, 9), "0000-0000"),
-    (datetime.date(2024, 1, 10), "0000-0000"),
-    (datetime.date(2024, 1, 11), "0000-0000"),
-    (datetime.date(2024, 1, 12), "0000-1700"),
-    (datetime.date(2024, 1, 13), None),
-    (datetime.date(2024, 1, 14), "1700-0000"),
-    (datetime.date(2024, 1, 15), "0000-1430"),
-    (datetime.date(2024, 1, 15), "1800-0000"),
-    (datetime.date(2024, 1, 16), "0000-0000"),
-    (datetime.date(2024, 1, 17), "0000-0000"),
-    (datetime.date(2024, 1, 18), "0000-0000"),
-    (datetime.date(2024, 1, 19), "0000-1700"),
-    (datetime.date(2024, 1, 20), None),
-    (datetime.date(2024, 1, 21), "1700-0000"),
-    (datetime.date(2024, 1, 22), "0000-0000"),
-    (datetime.date(2024, 1, 23), "0000-0000"),
-    (datetime.date(2024, 1, 24), "0000-0000"),
-    (datetime.date(2024, 1, 25), "0000-0000"),
-    (datetime.date(2024, 1, 26), "0000-1700"),
-    (datetime.date(2024, 1, 27), None),
-    (datetime.date(2024, 1, 28), "1700-0000"),
-    (datetime.date(2024, 1, 29), "0000-0000"),
-    (datetime.date(2024, 1, 30), "0000-0000"),
-    (datetime.date(2024, 1, 31), "0000-0000"),
-    (datetime.date(2024, 2, 1), "0000-0000"),
-    (datetime.date(2024, 2, 2), "0000-1700"),
-    (datetime.date(2024, 2, 3), None),
-    (datetime.date(2024, 2, 4), "1700-0000"),
-    (datetime.date(2024, 2, 5), "0000-0000"),
-    (datetime.date(2024, 2, 6), "0000-0000"),
-    (datetime.date(2024, 2, 7), "0000-0000"),
-    (datetime.date(2024, 2, 8), "0000-0000"),
-    (datetime.date(2024, 2, 9), "0000-1700"),
-    (datetime.date(2024, 2, 10), None),
-    (datetime.date(2024, 2, 11), "1700-0000"),
-    (datetime.date(2024, 2, 12), "0000-0000"),
-    (datetime.date(2024, 2, 13), "0000-0000"),
-    (datetime.date(2024, 2, 14), "0000-0000"),
-    (datetime.date(2024, 2, 15), "0000-0000"),
-    (datetime.date(2024, 2, 16), "0000-1700"),
-    (datetime.date(2024, 2, 17), None),
-    (datetime.date(2024, 2, 18), "1700-0000"),
-    (datetime.date(2024, 2, 19), "0000-1430"),
-    (datetime.date(2024, 2, 19), "1800-0000"),
-    (datetime.date(2024, 2, 20), "0000-0000"),
-    (datetime.date(2024, 2, 21), "0000-0000"),
-    (datetime.date(2024, 2, 22), "0000-0000"),
-    (datetime.date(2024, 2, 23), "0000-1700"),
-    (datetime.date(2024, 2, 24), None),
-    (datetime.date(2024, 2, 25), "1700-0000"),
-    (datetime.date(2024, 2, 26), "0000-0000"),
-    (datetime.date(2024, 2, 27), "0000-0000"),
-    (datetime.date(2024, 2, 28), "0000-0000"),
-    (datetime.date(2024, 2, 29), "0000-0000"),
-    (datetime.date(2024, 3, 1), "0000-1700"),
-    (datetime.date(2024, 3, 2), None),
-    (datetime.date(2024, 3, 3), "1700-0000"),
-    (datetime.date(2024, 3, 4), "0000-0000"),
-    (datetime.date(2024, 3, 5), "0000-0000"),
-    (datetime.date(2024, 3, 6), "0000-0000"),
-    (datetime.date(2024, 3, 7), "0000-0000"),
-    (datetime.date(2024, 3, 8), "0000-1700"),
-    (datetime.date(2024, 3, 9), None),
-    (datetime.date(2024, 3, 10), "1700-0000"),
-    (datetime.date(2024, 3, 11), "0000-0000"),
-    (datetime.date(2024, 3, 12), "0000-0000"),
-    (datetime.date(2024, 3, 13), "0000-0000"),
-    (datetime.date(2024, 3, 14), "0000-0000"),
-    (datetime.date(2024, 3, 15), "0000-1700"),
-    (datetime.date(2024, 3, 16), None),
-    (datetime.date(2024, 3, 17), "1700-0000"),
-    (datetime.date(2024, 3, 18), "0000-0000"),
-    (datetime.date(2024, 3, 19), "0000-0000"),
-    (datetime.date(2024, 3, 20), "0000-0000"),
-    (datetime.date(2024, 3, 21), "0000-0000"),
-    (datetime.date(2024, 3, 22), "0000-1700"),
-    (datetime.date(2024, 3, 23), None),
-    (datetime.date(2024, 3, 24), "1700-0000"),
-    (datetime.date(2024, 3, 25), "0000-0000"),
-    (datetime.date(2024, 3, 26), "0000-0000"),
-    (datetime.date(2024, 3, 27), "0000-0000"),
-    (datetime.date(2024, 3, 28), "0000-0000"),
-    (datetime.date(2024, 3, 29), "0000-1700"),
-    (datetime.date(2024, 3, 30), None),
-    (datetime.date(2024, 3, 31), "1700-0000"),
-    (datetime.date(2024, 4, 1), "0000-0000"),
-    (datetime.date(2024, 4, 2), "0000-0000"),
-    (datetime.date(2024, 4, 3), "0000-0000"),
-    (datetime.date(2024, 4, 4), "0000-0000"),
-    (datetime.date(2024, 4, 5), "0000-1700"),
-    (datetime.date(2024, 4, 6), None),
-    (datetime.date(2024, 4, 7), "1700-0000"),
-    (datetime.date(2024, 4, 8), "0000-0000"),
-    (datetime.date(2024, 4, 9), "0000-0000"),
-    (datetime.date(2024, 4, 10), "0000-0000"),
-    (datetime.date(2024, 4, 11), "0000-0000"),
-    (datetime.date(2024, 4, 12), "0000-1700"),
-    (datetime.date(2024, 4, 13), None),
-    (datetime.date(2024, 4, 14), "1700-0000"),
-    (datetime.date(2024, 4, 15), "0000-0000"),
-    (datetime.date(2024, 4, 16), "0000-0000"),
-    (datetime.date(2024, 4, 17), "0000-0000"),
-    (datetime.date(2024, 4, 18), "0000-0000"),
-    (datetime.date(2024, 4, 19), "0000-1700"),
-    (datetime.date(2024, 4, 20), None),
-    (datetime.date(2024, 4, 21), "1700-0000"),
-    (datetime.date(2024, 4, 22), "0000-0000"),
-    (datetime.date(2024, 4, 23), "0000-0000"),
-    (datetime.date(2024, 4, 24), "0000-0000"),
-    (datetime.date(2024, 4, 25), "0000-0000"),
-    (datetime.date(2024, 4, 26), "0000-1700"),
-    (datetime.date(2024, 4, 27), None),
-    (datetime.date(2024, 4, 28), "1700-0000"),
-    (datetime.date(2024, 4, 29), "0000-0000"),
-    (datetime.date(2024, 4, 30), "0000-0000"),
-    (datetime.date(2024, 5, 1), "0000-0000"),
-    (datetime.date(2024, 5, 2), "0000-0000"),
-    (datetime.date(2024, 5, 3), "0000-1700"),
-    (datetime.date(2024, 5, 4), None),
-    (datetime.date(2024, 5, 5), "1700-0000"),
-    (datetime.date(2024, 5, 6), "0000-0000"),
-    (datetime.date(2024, 5, 7), "0000-0000"),
-    (datetime.date(2024, 5, 8), "0000-0000"),
-    (datetime.date(2024, 5, 9), "0000-0000"),
-    (datetime.date(2024, 5, 10), "0000-1700"),
-    (datetime.date(2024, 5, 11), None),
-    (datetime.date(2024, 5, 12), "1700-0000"),
-    (datetime.date(2024, 5, 13), "0000-0000"),
-    (datetime.date(2024, 5, 14), "0000-0000"),
-    (datetime.date(2024, 5, 15), "0000-0000"),
-    (datetime.date(2024, 5, 16), "0000-0000"),
-    (datetime.date(2024, 5, 17), "0000-1700"),
-    (datetime.date(2024, 5, 18), None),
-    (datetime.date(2024, 5, 19), "1700-0000"),
-    (datetime.date(2024, 5, 20), "0000-0000"),
-    (datetime.date(2024, 5, 21), "0000-0000"),
-    (datetime.date(2024, 5, 22), "0000-0000"),
-    (datetime.date(2024, 5, 23), "0000-0000"),
-    (datetime.date(2024, 5, 24), "0000-1700"),
-    (datetime.date(2024, 5, 25), None),
-    (datetime.date(2024, 5, 26), "1700-0000"),
-    (datetime.date(2024, 5, 27), "0000-0000"),
-    (datetime.date(2024, 5, 28), "0000-0000"),
-    (datetime.date(2024, 5, 29), "0000-0000"),
-    (datetime.date(2024, 5, 30), "0000-0000"),
-    (datetime.date(2024, 5, 31), "0000-1700"),
-    (datetime.date(2024, 6, 1), None),
-    (datetime.date(2024, 6, 2), "1700-0000"),
-    (datetime.date(2024, 6, 3), "0000-0000"),
-    (datetime.date(2024, 6, 4), "0000-0000"),
-    (datetime.date(2024, 6, 5), "0000-0000"),
-    (datetime.date(2024, 6, 6), "0000-0000"),
-    (datetime.date(2024, 6, 7), "0000-1700"),
-    (datetime.date(2024, 6, 8), None),
-    (datetime.date(2024, 6, 9), "1700-0000"),
-    (datetime.date(2024, 6, 10), "0000-0000"),
-    (datetime.date(2024, 6, 11), "0000-0000"),
-    (datetime.date(2024, 6, 12), "0000-0000"),
-    (datetime.date(2024, 6, 13), "0000-0000"),
-    (datetime.date(2024, 6, 14), "0000-1700"),
-    (datetime.date(2024, 6, 15), None),
-    (datetime.date(2024, 6, 16), "1700-0000"),
-    (datetime.date(2024, 6, 17), "0000-0000"),
-    (datetime.date(2024, 6, 18), "0000-0000"),
-    (datetime.date(2024, 6, 19), "0000-0000"),
-    (datetime.date(2024, 6, 20), "0000-0000"),
-    (datetime.date(2024, 6, 21), "0000-1700"),
-    (datetime.date(2024, 6, 22), None),
-    (datetime.date(2024, 6, 23), "1700-0000"),
-    (datetime.date(2024, 6, 24), "0000-0000"),
-    (datetime.date(2024, 6, 25), "0000-0000"),
-    (datetime.date(2024, 6, 26), "0000-0000"),
-    (datetime.date(2024, 6, 27), "0000-0000"),
-    (datetime.date(2024, 6, 28), "0000-1700"),
-    (datetime.date(2024, 6, 29), None),
-    (datetime.date(2024, 6, 30), "1700-0000"),
-    (datetime.date(2024, 7, 1), "0000-0000"),
-    (datetime.date(2024, 7, 2), "0000-0000"),
-    (datetime.date(2024, 7, 3), "0000-0000"),
-    (datetime.date(2024, 7, 4), "0000-0000"),
-    (datetime.date(2024, 7, 5), "0000-1700"),
-    (datetime.date(2024, 7, 6), None),
-    (datetime.date(2024, 7, 7), "1700-0000"),
-    (datetime.date(2024, 7, 8), "0000-0000"),
-    (datetime.date(2024, 7, 9), "0000-0000"),
-    (datetime.date(2024, 7, 10), "0000-0000"),
-    (datetime.date(2024, 7, 11), "0000-0000"),
-    (datetime.date(2024, 7, 12), "0000-1700"),
-    (datetime.date(2024, 7, 13), None),
-    (datetime.date(2024, 7, 14), "1700-0000"),
-    (datetime.date(2024, 7, 15), "0000-0000"),
-    (datetime.date(2024, 7, 16), "0000-0000"),
-    (datetime.date(2024, 7, 17), "0000-0000"),
-    (datetime.date(2024, 7, 18), "0000-0000"),
-    (datetime.date(2024, 7, 19), "0000-1700"),
-    (datetime.date(2024, 7, 20), None),
-    (datetime.date(2024, 7, 21), "1700-0000"),
-    (datetime.date(2024, 7, 22), "0000-0000"),
-    (datetime.date(2024, 7, 23), "0000-0000"),
-    (datetime.date(2024, 7, 24), "0000-0000"),
-    (datetime.date(2024, 7, 25), "0000-0000"),
-    (datetime.date(2024, 7, 26), "0000-1700"),
-    (datetime.date(2024, 7, 27), None),
-    (datetime.date(2024, 7, 28), "1700-0000"),
-    (datetime.date(2024, 7, 29), "0000-0000"),
-    (datetime.date(2024, 7, 30), "0000-0000"),
-    (datetime.date(2024, 7, 31), "0000-0000"),
-    (datetime.date(2024, 8, 1), "0000-0000"),
-    (datetime.date(2024, 8, 2), "0000-1700"),
-    (datetime.date(2024, 8, 3), None),
-    (datetime.date(2024, 8, 4), "1700-0000"),
-    (datetime.date(2024, 8, 5), "0000-0000"),
-    (datetime.date(2024, 8, 6), "0000-0000"),
-    (datetime.date(2024, 8, 7), "0000-0000"),
-    (datetime.date(2024, 8, 8), "0000-0000"),
-    (datetime.date(2024, 8, 9), "0000-1700"),
-    (datetime.date(2024, 8, 10), None),
-    (datetime.date(2024, 8, 11), "1700-0000"),
-    (datetime.date(2024, 8, 12), "0000-0000"),
-    (datetime.date(2024, 8, 13), "0000-0000"),
-    (datetime.date(2024, 8, 14), "0000-0000"),
-    (datetime.date(2024, 8, 15), "0000-0000"),
-    (datetime.date(2024, 8, 16), "0000-1700"),
-    (datetime.date(2024, 8, 17), None),
-    (datetime.date(2024, 8, 18), "1700-0000"),
-    (datetime.date(2024, 8, 19), "0000-0000"),
-    (datetime.date(2024, 8, 20), "0000-0000"),
-    (datetime.date(2024, 8, 21), "0000-0000"),
-    (datetime.date(2024, 8, 22), "0000-0000"),
-    (datetime.date(2024, 8, 23), "0000-1700"),
-    (datetime.date(2024, 8, 24), None),
-    (datetime.date(2024, 8, 25), "1700-0000"),
-    (datetime.date(2024, 8, 26), "0000-0000"),
-    (datetime.date(2024, 8, 27), "0000-0000"),
-    (datetime.date(2024, 8, 28), "0000-0000"),
-    (datetime.date(2024, 8, 29), "0000-0000"),
-    (datetime.date(2024, 8, 30), "0000-1700"),
-    (datetime.date(2024, 8, 31), None),
-    (datetime.date(2024, 9, 1), "1700-0000"),
-    (datetime.date(2024, 9, 2), "0000-0000"),
-    (datetime.date(2024, 9, 3), "0000-0000"),
-    (datetime.date(2024, 9, 4), "0000-0000"),
-    (datetime.date(2024, 9, 5), "0000-0000"),
-    (datetime.date(2024, 9, 6), "0000-1700"),
-    (datetime.date(2024, 9, 7), None),
-    (datetime.date(2024, 9, 8), "1700-0000"),
-    (datetime.date(2024, 9, 9), "0000-0000"),
-    (datetime.date(2024, 9, 10), "0000-0000"),
-    (datetime.date(2024, 9, 11), "0000-0000"),
-    (datetime.date(2024, 9, 12), "0000-0000"),
-    (datetime.date(2024, 9, 13), "0000-1700"),
-    (datetime.date(2024, 9, 14), None),
-    (datetime.date(2024, 9, 15), "1700-0000"),
-    (datetime.date(2024, 9, 16), "0000-0000"),
-    (datetime.date(2024, 9, 17), "0000-0000"),
-    (datetime.date(2024, 9, 18), "0000-0000"),
-    (datetime.date(2024, 9, 19), "0000-0000"),
-    (datetime.date(2024, 9, 20), "0000-1700"),
-    (datetime.date(2024, 9, 21), None),
-    (datetime.date(2024, 9, 22), "1700-0000"),
-    (datetime.date(2024, 9, 23), "0000-0000"),
-    (datetime.date(2024, 9, 24), "0000-0000"),
-    (datetime.date(2024, 9, 25), "0000-0000"),
-    (datetime.date(2024, 9, 26), "0000-0000"),
-    (datetime.date(2024, 9, 27), "0000-1700"),
-    (datetime.date(2024, 9, 28), None),
-    (datetime.date(2024, 9, 29), "1700-0000"),
-    (datetime.date(2024, 9, 30), "0000-0000"),
-    (datetime.date(2024, 10, 1), "0000-0000"),
-    (datetime.date(2024, 10, 2), "0000-0000"),
-    (datetime.date(2024, 10, 3), "0000-0000"),
-    (datetime.date(2024, 10, 4), "0000-1700"),
-    (datetime.date(2024, 10, 5), None),
-    (datetime.date(2024, 10, 6), "1700-0000"),
-    (datetime.date(2024, 10, 7), "0000-0000"),
-    (datetime.date(2024, 10, 8), "0000-0000"),
-    (datetime.date(2024, 10, 9), "0000-0000"),
-    (datetime.date(2024, 10, 10), "0000-0000"),
-    (datetime.date(2024, 10, 11), "0000-1700"),
-    (datetime.date(2024, 10, 12), None),
-    (datetime.date(2024, 10, 13), "1700-0000"),
-    (datetime.date(2024, 10, 14), "0000-0000"),
-    (datetime.date(2024, 10, 15), "0000-0000"),
-    (datetime.date(2024, 10, 16), "0000-0000"),
-    (datetime.date(2024, 10, 17), "0000-0000"),
-    (datetime.date(2024, 10, 18), "0000-1700"),
-    (datetime.date(2024, 10, 19), None),
-    (datetime.date(2024, 10, 20), "1700-0000"),
-    (datetime.date(2024, 10, 21), "0000-0000"),
-    (datetime.date(2024, 10, 22), "0000-0000"),
-    (datetime.date(2024, 10, 23), "0000-0000"),
-    (datetime.date(2024, 10, 24), "0000-0000"),
-    (datetime.date(2024, 10, 25), "0000-1700"),
-    (datetime.date(2024, 10, 26), None),
-    (datetime.date(2024, 10, 27), "1700-0000"),
-    (datetime.date(2024, 10, 28), "0000-0000"),
-    (datetime.date(2024, 10, 29), "0000-0000"),
-    (datetime.date(2024, 10, 30), "0000-0000"),
-    (datetime.date(2024, 10, 31), "0000-0000"),
-    (datetime.date(2024, 11, 1), "0000-1700"),
-    (datetime.date(2024, 11, 2), None),
-    (datetime.date(2024, 11, 3), "1700-0000"),
-    (datetime.date(2024, 11, 4), "0000-0000"),
-    (datetime.date(2024, 11, 5), "0000-0000"),
-    (datetime.date(2024, 11, 6), "0000-0000"),
-    (datetime.date(2024, 11, 7), "0000-0000"),
-    (datetime.date(2024, 11, 8), "0000-1700"),
-    (datetime.date(2024, 11, 9), None),
-    (datetime.date(2024, 11, 10), "1700-0000"),
-    (datetime.date(2024, 11, 11), "0000-0000"),
-    (datetime.date(2024, 11, 12), "0000-0000"),
-    (datetime.date(2024, 11, 13), "0000-0000"),
-    (datetime.date(2024, 11, 14), "0000-0000"),
-    (datetime.date(2024, 11, 15), "0000-1700"),
-    (datetime.date(2024, 11, 16), None),
-    (datetime.date(2024, 11, 17), "1700-0000"),
-    (datetime.date(2024, 11, 18), "0000-0000"),
-    (datetime.date(2024, 11, 19), "0000-0000"),
-    (datetime.date(2024, 11, 20), "0000-0000"),
-    (datetime.date(2024, 11, 21), "0000-0000"),
-    (datetime.date(2024, 11, 22), "0000-1700"),
-    (datetime.date(2024, 11, 23), None),
-    (datetime.date(2024, 11, 24), "1700-0000"),
-    (datetime.date(2024, 11, 25), "0000-0000"),
-    (datetime.date(2024, 11, 26), "0000-0000"),
-    (datetime.date(2024, 11, 27), "0000-0000"),
-    (datetime.date(2024, 11, 28), "0000-0000"),
-    (datetime.date(2024, 11, 29), "0000-1700"),
-    (datetime.date(2024, 11, 30), None),
-    (datetime.date(2024, 12, 1), "1700-0000"),
-    (datetime.date(2024, 12, 2), "0000-0000"),
-    (datetime.date(2024, 12, 3), "0000-0000"),
-    (datetime.date(2024, 12, 4), "0000-0000"),
-    (datetime.date(2024, 12, 5), "0000-0000"),
-    (datetime.date(2024, 12, 6), "0000-1700"),
-    (datetime.date(2024, 12, 7), None),
-    (datetime.date(2024, 12, 8), "1700-0000"),
-    (datetime.date(2024, 12, 9), "0000-0000"),
-    (datetime.date(2024, 12, 10), "0000-0000"),
-    (datetime.date(2024, 12, 11), "0000-0000"),
-    (datetime.date(2024, 12, 12), "0000-0000"),
-    (datetime.date(2024, 12, 13), "0000-1700"),
-    (datetime.date(2024, 12, 14), None),
-    (datetime.date(2024, 12, 15), "1700-0000"),
-    (datetime.date(2024, 12, 16), "0000-0000"),
-    (datetime.date(2024, 12, 17), "0000-0000"),
-    (datetime.date(2024, 12, 18), "0000-0000"),
-    (datetime.date(2024, 12, 19), "0000-0000"),
-    (datetime.date(2024, 12, 20), "0000-1700"),
-    (datetime.date(2024, 12, 21), None),
-    (datetime.date(2024, 12, 22), "1700-0000"),
-    (datetime.date(2024, 12, 23), "0000-0000"),
-    (datetime.date(2024, 12, 24), "0000-1700"),
-    (datetime.date(2024, 12, 25), "1700-0000"),
-    (datetime.date(2024, 12, 26), "0000-0000"),
-    (datetime.date(2024, 12, 27), "0000-1700"),
-    (datetime.date(2024, 12, 28), None),
-    (datetime.date(2024, 12, 29), "1700-0000"),
-    (datetime.date(2024, 12, 30), "0000-0000"),
-    (datetime.date(2024, 12, 31), "0000-0000"),
-]
-
-RATES_2024_INTERVALS = [
-    (datetime.date(2024, 1, 1), None),
-    (datetime.date(2024, 1, 2), "0800-1700"),
-    (datetime.date(2024, 1, 3), "0800-1700"),
-    (datetime.date(2024, 1, 4), "0800-1700"),
-    (datetime.date(2024, 1, 5), "0800-1700"),
-    (datetime.date(2024, 1, 6), None),
-    (datetime.date(2024, 1, 7), None),
-    (datetime.date(2024, 1, 8), "0800-1700"),
-    (datetime.date(2024, 1, 9), "0800-1700"),
-    (datetime.date(2024, 1, 10), "0800-1700"),
-    (datetime.date(2024, 1, 11), "0800-1700"),
-    (datetime.date(2024, 1, 12), "0800-1700"),
-    (datetime.date(2024, 1, 13), None),
-    (datetime.date(2024, 1, 14), None),
-    (datetime.date(2024, 1, 15), None),
-    (datetime.date(2024, 1, 16), "0800-1700"),
-    (datetime.date(2024, 1, 17), "0800-1700"),
-    (datetime.date(2024, 1, 18), "0800-1700"),
-    (datetime.date(2024, 1, 19), "0800-1700"),
-    (datetime.date(2024, 1, 20), None),
-    (datetime.date(2024, 1, 21), None),
-    (datetime.date(2024, 1, 22), "0800-1700"),
-    (datetime.date(2024, 1, 23), "0800-1700"),
-    (datetime.date(2024, 1, 24), "0800-1700"),
-    (datetime.date(2024, 1, 25), "0800-1700"),
-    (datetime.date(2024, 1, 26), "0800-1700"),
-    (datetime.date(2024, 1, 27), None),
-    (datetime.date(2024, 1, 28), None),
-    (datetime.date(2024, 1, 29), "0800-1700"),
-    (datetime.date(2024, 1, 30), "0800-1700"),
-    (datetime.date(2024, 1, 31), "0800-1700"),
-    (datetime.date(2024, 2, 1), "0800-1700"),
-    (datetime.date(2024, 2, 2), "0800-1700"),
-    (datetime.date(2024, 2, 3), None),
-    (datetime.date(2024, 2, 4), None),
-    (datetime.date(2024, 2, 5), "0800-1700"),
-    (datetime.date(2024, 2, 6), "0800-1700"),
-    (datetime.date(2024, 2, 7), "0800-1700"),
-    (datetime.date(2024, 2, 8), "0800-1700"),
-    (datetime.date(2024, 2, 9), "0800-1700"),
-    (datetime.date(2024, 2, 10), None),
-    (datetime.date(2024, 2, 11), None),
-    (datetime.date(2024, 2, 12), "0800-1700"),
-    (datetime.date(2024, 2, 13), "0800-1700"),
-    (datetime.date(2024, 2, 14), "0800-1700"),
-    (datetime.date(2024, 2, 15), "0800-1700"),
-    (datetime.date(2024, 2, 16), "0800-1700"),
-    (datetime.date(2024, 2, 17), None),
-    (datetime.date(2024, 2, 18), None),
-    (datetime.date(2024, 2, 19), None),
-    (datetime.date(2024, 2, 20), "0800-1700"),
-    (datetime.date(2024, 2, 21), "0800-1700"),
-    (datetime.date(2024, 2, 22), "0800-1700"),
-    (datetime.date(2024, 2, 23), "0800-1700"),
-    (datetime.date(2024, 2, 24), None),
-    (datetime.date(2024, 2, 25), None),
-    (datetime.date(2024, 2, 26), "0800-1700"),
-    (datetime.date(2024, 2, 27), "0800-1700"),
-    (datetime.date(2024, 2, 28), "0800-1700"),
-    (datetime.date(2024, 2, 29), "0800-1700"),
-    (datetime.date(2024, 3, 1), "0800-1700"),
-    (datetime.date(2024, 3, 2), None),
-    (datetime.date(2024, 3, 3), None),
-    (datetime.date(2024, 3, 4), "0800-1700"),
-    (datetime.date(2024, 3, 5), "0800-1700"),
-    (datetime.date(2024, 3, 6), "0800-1700"),
-    (datetime.date(2024, 3, 7), "0800-1700"),
-    (datetime.date(2024, 3, 8), "0800-1700"),
-    (datetime.date(2024, 3, 9), None),
-    (datetime.date(2024, 3, 10), None),
-    (datetime.date(2024, 3, 11), "0800-1700"),
-    (datetime.date(2024, 3, 12), "0800-1700"),
-    (datetime.date(2024, 3, 13), "0800-1700"),
-    (datetime.date(2024, 3, 14), "0800-1700"),
-    (datetime.date(2024, 3, 15), "0800-1700"),
-    (datetime.date(2024, 3, 16), None),
-    (datetime.date(2024, 3, 17), None),
-    (datetime.date(2024, 3, 18), "0800-1700"),
-    (datetime.date(2024, 3, 19), "0800-1700"),
-    (datetime.date(2024, 3, 20), "0800-1700"),
-    (datetime.date(2024, 3, 21), "0800-1700"),
-    (datetime.date(2024, 3, 22), "0800-1700"),
-    (datetime.date(2024, 3, 23), None),
-    (datetime.date(2024, 3, 24), None),
-    (datetime.date(2024, 3, 25), "0800-1700"),
-    (datetime.date(2024, 3, 26), "0800-1700"),
-    (datetime.date(2024, 3, 27), "0800-1700"),
-    (datetime.date(2024, 3, 28), "0800-1700"),
-    (datetime.date(2024, 3, 29), None),
-    (datetime.date(2024, 3, 30), None),
-    (datetime.date(2024, 3, 31), None),
-    (datetime.date(2024, 4, 1), "0800-1700"),
-    (datetime.date(2024, 4, 2), "0800-1700"),
-    (datetime.date(2024, 4, 3), "0800-1700"),
-    (datetime.date(2024, 4, 4), "0800-1700"),
-    (datetime.date(2024, 4, 5), "0800-1700"),
-    (datetime.date(2024, 4, 6), None),
-    (datetime.date(2024, 4, 7), None),
-    (datetime.date(2024, 4, 8), "0800-1700"),
-    (datetime.date(2024, 4, 9), "0800-1700"),
-    (datetime.date(2024, 4, 10), "0800-1700"),
-    (datetime.date(2024, 4, 11), "0800-1700"),
-    (datetime.date(2024, 4, 12), "0800-1700"),
-    (datetime.date(2024, 4, 13), None),
-    (datetime.date(2024, 4, 14), None),
-    (datetime.date(2024, 4, 15), "0800-1700"),
-    (datetime.date(2024, 4, 16), "0800-1700"),
-    (datetime.date(2024, 4, 17), "0800-1700"),
-    (datetime.date(2024, 4, 18), "0800-1700"),
-    (datetime.date(2024, 4, 19), "0800-1700"),
-    (datetime.date(2024, 4, 20), None),
-    (datetime.date(2024, 4, 21), None),
-    (datetime.date(2024, 4, 22), "0800-1700"),
-    (datetime.date(2024, 4, 23), "0800-1700"),
-    (datetime.date(2024, 4, 24), "0800-1700"),
-    (datetime.date(2024, 4, 25), "0800-1700"),
-    (datetime.date(2024, 4, 26), "0800-1700"),
-    (datetime.date(2024, 4, 27), None),
-    (datetime.date(2024, 4, 28), None),
-    (datetime.date(2024, 4, 29), "0800-1700"),
-    (datetime.date(2024, 4, 30), "0800-1700"),
-    (datetime.date(2024, 5, 1), "0800-1700"),
-    (datetime.date(2024, 5, 2), "0800-1700"),
-    (datetime.date(2024, 5, 3), "0800-1700"),
-    (datetime.date(2024, 5, 4), None),
-    (datetime.date(2024, 5, 5), None),
-    (datetime.date(2024, 5, 6), "0800-1700"),
-    (datetime.date(2024, 5, 7), "0800-1700"),
-    (datetime.date(2024, 5, 8), "0800-1700"),
-    (datetime.date(2024, 5, 9), "0800-1700"),
-    (datetime.date(2024, 5, 10), "0800-1700"),
-    (datetime.date(2024, 5, 11), None),
-    (datetime.date(2024, 5, 12), None),
-    (datetime.date(2024, 5, 13), "0800-1700"),
-    (datetime.date(2024, 5, 14), "0800-1700"),
-    (datetime.date(2024, 5, 15), "0800-1700"),
-    (datetime.date(2024, 5, 16), "0800-1700"),
-    (datetime.date(2024, 5, 17), "0800-1700"),
-    (datetime.date(2024, 5, 18), None),
-    (datetime.date(2024, 5, 19), None),
-    (datetime.date(2024, 5, 20), "0800-1700"),
-    (datetime.date(2024, 5, 21), "0800-1700"),
-    (datetime.date(2024, 5, 22), "0800-1700"),
-    (datetime.date(2024, 5, 23), "0800-1700"),
-    (datetime.date(2024, 5, 24), "0800-1700"),
-    (datetime.date(2024, 5, 25), None),
-    (datetime.date(2024, 5, 26), None),
-    (datetime.date(2024, 5, 27), None),
-    (datetime.date(2024, 5, 28), "0800-1700"),
-    (datetime.date(2024, 5, 29), "0800-1700"),
-    (datetime.date(2024, 5, 30), "0800-1700"),
-    (datetime.date(2024, 5, 31), "0800-1700"),
-    (datetime.date(2024, 6, 1), None),
-    (datetime.date(2024, 6, 2), None),
-    (datetime.date(2024, 6, 3), "0800-1700"),
-    (datetime.date(2024, 6, 4), "0800-1700"),
-    (datetime.date(2024, 6, 5), "0800-1700"),
-    (datetime.date(2024, 6, 6), "0800-1700"),
-    (datetime.date(2024, 6, 7), "0800-1700"),
-    (datetime.date(2024, 6, 8), None),
-    (datetime.date(2024, 6, 9), None),
-    (datetime.date(2024, 6, 10), "0800-1700"),
-    (datetime.date(2024, 6, 11), "0800-1700"),
-    (datetime.date(2024, 6, 12), "0800-1700"),
-    (datetime.date(2024, 6, 13), "0800-1700"),
-    (datetime.date(2024, 6, 14), "0800-1700"),
-    (datetime.date(2024, 6, 15), None),
-    (datetime.date(2024, 6, 16), None),
-    (datetime.date(2024, 6, 17), "0800-1700"),
-    (datetime.date(2024, 6, 18), "0800-1700"),
-    (datetime.date(2024, 6, 19), None),
-    (datetime.date(2024, 6, 20), "0800-1700"),
-    (datetime.date(2024, 6, 21), "0800-1700"),
-    (datetime.date(2024, 6, 22), None),
-    (datetime.date(2024, 6, 23), None),
-    (datetime.date(2024, 6, 24), "0800-1700"),
-    (datetime.date(2024, 6, 25), "0800-1700"),
-    (datetime.date(2024, 6, 26), "0800-1700"),
-    (datetime.date(2024, 6, 27), "0800-1700"),
-    (datetime.date(2024, 6, 28), "0800-1700"),
-    (datetime.date(2024, 6, 29), None),
-    (datetime.date(2024, 6, 30), None),
-    (datetime.date(2024, 7, 1), "0800-1700"),
-    (datetime.date(2024, 7, 2), "0800-1700"),
-    (datetime.date(2024, 7, 3), "0800-1300"),
-    (datetime.date(2024, 7, 4), None),
-    (datetime.date(2024, 7, 5), "0800-1700"),
-    (datetime.date(2024, 7, 6), None),
-    (datetime.date(2024, 7, 7), None),
-    (datetime.date(2024, 7, 8), "0800-1700"),
-    (datetime.date(2024, 7, 9), "0800-1700"),
-    (datetime.date(2024, 7, 10), "0800-1700"),
-    (datetime.date(2024, 7, 11), "0800-1700"),
-    (datetime.date(2024, 7, 12), "0800-1700"),
-    (datetime.date(2024, 7, 13), None),
-    (datetime.date(2024, 7, 14), None),
-    (datetime.date(2024, 7, 15), "0800-1700"),
-    (datetime.date(2024, 7, 16), "0800-1700"),
-    (datetime.date(2024, 7, 17), "0800-1700"),
-    (datetime.date(2024, 7, 18), "0800-1700"),
-    (datetime.date(2024, 7, 19), "0800-1700"),
-    (datetime.date(2024, 7, 20), None),
-    (datetime.date(2024, 7, 21), None),
-    (datetime.date(2024, 7, 22), "0800-1700"),
-    (datetime.date(2024, 7, 23), "0800-1700"),
-    (datetime.date(2024, 7, 24), "0800-1700"),
-    (datetime.date(2024, 7, 25), "0800-1700"),
-    (datetime.date(2024, 7, 26), "0800-1700"),
-    (datetime.date(2024, 7, 27), None),
-    (datetime.date(2024, 7, 28), None),
-    (datetime.date(2024, 7, 29), "0800-1700"),
-    (datetime.date(2024, 7, 30), "0800-1700"),
-    (datetime.date(2024, 7, 31), "0800-1700"),
-    (datetime.date(2024, 8, 1), "0800-1700"),
-    (datetime.date(2024, 8, 2), "0800-1700"),
-    (datetime.date(2024, 8, 3), None),
-    (datetime.date(2024, 8, 4), None),
-    (datetime.date(2024, 8, 5), "0800-1700"),
-    (datetime.date(2024, 8, 6), "0800-1700"),
-    (datetime.date(2024, 8, 7), "0800-1700"),
-    (datetime.date(2024, 8, 8), "0800-1700"),
-    (datetime.date(2024, 8, 9), "0800-1700"),
-    (datetime.date(2024, 8, 10), None),
-    (datetime.date(2024, 8, 11), None),
-    (datetime.date(2024, 8, 12), "0800-1700"),
-    (datetime.date(2024, 8, 13), "0800-1700"),
-    (datetime.date(2024, 8, 14), "0800-1700"),
-    (datetime.date(2024, 8, 15), "0800-1700"),
-    (datetime.date(2024, 8, 16), "0800-1700"),
-    (datetime.date(2024, 8, 17), None),
-    (datetime.date(2024, 8, 18), None),
-    (datetime.date(2024, 8, 19), "0800-1700"),
-    (datetime.date(2024, 8, 20), "0800-1700"),
-    (datetime.date(2024, 8, 21), "0800-1700"),
-    (datetime.date(2024, 8, 22), "0800-1700"),
-    (datetime.date(2024, 8, 23), "0800-1700"),
-    (datetime.date(2024, 8, 24), None),
-    (datetime.date(2024, 8, 25), None),
-    (datetime.date(2024, 8, 26), "0800-1700"),
-    (datetime.date(2024, 8, 27), "0800-1700"),
-    (datetime.date(2024, 8, 28), "0800-1700"),
-    (datetime.date(2024, 8, 29), "0800-1700"),
-    (datetime.date(2024, 8, 30), "0800-1700"),
-    (datetime.date(2024, 8, 31), None),
-    (datetime.date(2024, 9, 1), None),
-    (datetime.date(2024, 9, 2), None),
-    (datetime.date(2024, 9, 3), "0800-1700"),
-    (datetime.date(2024, 9, 4), "0800-1700"),
-    (datetime.date(2024, 9, 5), "0800-1700"),
-    (datetime.date(2024, 9, 6), "0800-1700"),
-    (datetime.date(2024, 9, 7), None),
-    (datetime.date(2024, 9, 8), None),
-    (datetime.date(2024, 9, 9), "0800-1700"),
-    (datetime.date(2024, 9, 10), "0800-1700"),
-    (datetime.date(2024, 9, 11), "0800-1700"),
-    (datetime.date(2024, 9, 12), "0800-1700"),
-    (datetime.date(2024, 9, 13), "0800-1700"),
-    (datetime.date(2024, 9, 14), None),
-    (datetime.date(2024, 9, 15), None),
-    (datetime.date(2024, 9, 16), "0800-1700"),
-    (datetime.date(2024, 9, 17), "0800-1700"),
-    (datetime.date(2024, 9, 18), "0800-1700"),
-    (datetime.date(2024, 9, 19), "0800-1700"),
-    (datetime.date(2024, 9, 20), "0800-1700"),
-    (datetime.date(2024, 9, 21), None),
-    (datetime.date(2024, 9, 22), None),
-    (datetime.date(2024, 9, 23), "0800-1700"),
-    (datetime.date(2024, 9, 24), "0800-1700"),
-    (datetime.date(2024, 9, 25), "0800-1700"),
-    (datetime.date(2024, 9, 26), "0800-1700"),
-    (datetime.date(2024, 9, 27), "0800-1700"),
-    (datetime.date(2024, 9, 28), None),
-    (datetime.date(2024, 9, 29), None),
-    (datetime.date(2024, 9, 30), "0800-1700"),
-    (datetime.date(2024, 10, 1), "0800-1700"),
-    (datetime.date(2024, 10, 2), "0800-1700"),
-    (datetime.date(2024, 10, 3), "0800-1700"),
-    (datetime.date(2024, 10, 4), "0800-1700"),
-    (datetime.date(2024, 10, 5), None),
-    (datetime.date(2024, 10, 6), None),
-    (datetime.date(2024, 10, 7), "0800-1700"),
-    (datetime.date(2024, 10, 8), "0800-1700"),
-    (datetime.date(2024, 10, 9), "0800-1700"),
-    (datetime.date(2024, 10, 10), "0800-1700"),
-    (datetime.date(2024, 10, 11), "0800-1700"),
-    (datetime.date(2024, 10, 12), None),
-    (datetime.date(2024, 10, 13), None),
-    (datetime.date(2024, 10, 14), "0800-1700"),
-    (datetime.date(2024, 10, 15), "0800-1700"),
-    (datetime.date(2024, 10, 16), "0800-1700"),
-    (datetime.date(2024, 10, 17), "0800-1700"),
-    (datetime.date(2024, 10, 18), "0800-1700"),
-    (datetime.date(2024, 10, 19), None),
-    (datetime.date(2024, 10, 20), None),
-    (datetime.date(2024, 10, 21), "0800-1700"),
-    (datetime.date(2024, 10, 22), "0800-1700"),
-    (datetime.date(2024, 10, 23), "0800-1700"),
-    (datetime.date(2024, 10, 24), "0800-1700"),
-    (datetime.date(2024, 10, 25), "0800-1700"),
-    (datetime.date(2024, 10, 26), None),
-    (datetime.date(2024, 10, 27), None),
-    (datetime.date(2024, 10, 28), "0800-1700"),
-    (datetime.date(2024, 10, 29), "0800-1700"),
-    (datetime.date(2024, 10, 30), "0800-1700"),
-    (datetime.date(2024, 10, 31), "0800-1700"),
-    (datetime.date(2024, 11, 1), "0800-1700"),
-    (datetime.date(2024, 11, 2), None),
-    (datetime.date(2024, 11, 3), None),
-    (datetime.date(2024, 11, 4), "0800-1700"),
-    (datetime.date(2024, 11, 5), "0800-1700"),
-    (datetime.date(2024, 11, 6), "0800-1700"),
-    (datetime.date(2024, 11, 7), "0800-1700"),
-    (datetime.date(2024, 11, 8), "0800-1700"),
-    (datetime.date(2024, 11, 9), None),
-    (datetime.date(2024, 11, 10), None),
-    (datetime.date(2024, 11, 11), "0800-1700"),
-    (datetime.date(2024, 11, 12), "0800-1700"),
-    (datetime.date(2024, 11, 13), "0800-1700"),
-    (datetime.date(2024, 11, 14), "0800-1700"),
-    (datetime.date(2024, 11, 15), "0800-1700"),
-    (datetime.date(2024, 11, 16), None),
-    (datetime.date(2024, 11, 17), None),
-    (datetime.date(2024, 11, 18), "0800-1700"),
-    (datetime.date(2024, 11, 19), "0800-1700"),
-    (datetime.date(2024, 11, 20), "0800-1700"),
-    (datetime.date(2024, 11, 21), "0800-1700"),
-    (datetime.date(2024, 11, 22), "0800-1700"),
-    (datetime.date(2024, 11, 23), None),
-    (datetime.date(2024, 11, 24), None),
-    (datetime.date(2024, 11, 25), "0800-1700"),
-    (datetime.date(2024, 11, 26), "0800-1700"),
-    (datetime.date(2024, 11, 27), "0800-1700"),
-    (datetime.date(2024, 11, 28), None),
-    (datetime.date(2024, 11, 29), "0800-1300"),
-    (datetime.date(2024, 11, 30), None),
-    (datetime.date(2024, 12, 1), None),
-    (datetime.date(2024, 12, 2), "0800-1700"),
-    (datetime.date(2024, 12, 3), "0800-1700"),
-    (datetime.date(2024, 12, 4), "0800-1700"),
-    (datetime.date(2024, 12, 5), "0800-1700"),
-    (datetime.date(2024, 12, 6), "0800-1700"),
-    (datetime.date(2024, 12, 7), None),
-    (datetime.date(2024, 12, 8), None),
-    (datetime.date(2024, 12, 9), "0800-1700"),
-    (datetime.date(2024, 12, 10), "0800-1700"),
-    (datetime.date(2024, 12, 11), "0800-1700"),
-    (datetime.date(2024, 12, 12), "0800-1700"),
-    (datetime.date(2024, 12, 13), "0800-1700"),
-    (datetime.date(2024, 12, 14), None),
-    (datetime.date(2024, 12, 15), None),
-    (datetime.date(2024, 12, 16), "0800-1700"),
-    (datetime.date(2024, 12, 17), "0800-1700"),
-    (datetime.date(2024, 12, 18), "0800-1700"),
-    (datetime.date(2024, 12, 19), "0800-1700"),
-    (datetime.date(2024, 12, 20), "0800-1700"),
-    (datetime.date(2024, 12, 21), None),
-    (datetime.date(2024, 12, 22), None),
-    (datetime.date(2024, 12, 23), "0800-1700"),
-    (datetime.date(2024, 12, 24), "0800-1300"),
-    (datetime.date(2024, 12, 25), None),
-    (datetime.date(2024, 12, 26), "0800-1700"),
-    (datetime.date(2024, 12, 27), "0800-1700"),
-    (datetime.date(2024, 12, 28), None),
-    (datetime.date(2024, 12, 29), None),
-    (datetime.date(2024, 12, 30), "0800-1700"),
-    (datetime.date(2024, 12, 31), "0800-1700"),
-]
diff --git a/pythclient/market_schedule.py b/pythclient/market_schedule.py
new file mode 100644
index 0000000..cda5a16
--- /dev/null
+++ b/pythclient/market_schedule.py
@@ -0,0 +1,209 @@
+from typing import List, Dict, Optional, Tuple
+from datetime import datetime, timedelta
+from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
+
+# A time is a string of the form HHMM and 2400 is used to represent midnight
+Time = str
+
+# A time range is a tuple of two times, representing the start and end of a trading session. This
+# range is *inclusive of the start time and exclusive of the end time*.
+TimeRange = Tuple[Time, Time]
+
+# A daily schedule is a list of time ranges, representing the trading sessions for a given day
+DailySchedule = List[TimeRange]
+
+class MarketSchedule:
+    def __init__(self, schedule_str: str):
+        """
+        Parse a schedule string in Pyth format: "Timezone;WeeklySchedule;Holidays"
+        """
+
+        parts = schedule_str.split(';')
+        if len(parts) < 2:
+            raise ValueError("Schedule must contain at least timezone and weekly schedule")
+        
+        self.timezone = self._validate_timezone(parts[0])
+        # Parse and validate weekly schedule - now returns parsed time ranges
+        self.weekly_schedule = self._parse_weekly_schedule(parts[1])
+        self.holiday_schedule = self._parse_holidays(parts[2] if len(parts) > 2 else "")
+
+    def _validate_timezone(self, timezone_str: str) -> ZoneInfo:
+        try:
+            return ZoneInfo(timezone_str)
+        except ZoneInfoNotFoundError:
+            raise ValueError(f"Invalid timezone: {timezone_str}")
+
+    def _parse_weekly_schedule(self, schedule: str) -> List[DailySchedule]:
+        """Parse the weekly schedule (Mon-Sun) into daily schedules"""
+        days = schedule.split(',')
+        if len(days) != 7:
+            raise ValueError("Weekly schedule must contain exactly 7 days")
+        
+        weekly_schedule = []
+        for day_schedule in days:
+            try:
+                weekly_schedule.append(self._parse_daily_schedule(day_schedule))
+            except ValueError as e:
+                raise ValueError(f"Invalid schedule format: {day_schedule}") from e
+                
+        return weekly_schedule
+
+    def _parse_holidays(self, holidays: str) -> Dict[str, DailySchedule]:
+        """Parse holiday overrides in format MMDD/Schedule"""
+        if not holidays:
+            return {}
+        
+        holiday_dict = {}
+        for holiday in holidays.split(','):
+            if not holiday:
+                continue
+            date_str, schedule = holiday.split('/')
+            holiday_dict[date_str] = self._parse_daily_schedule(schedule)
+        return holiday_dict
+
+    def _parse_daily_schedule(self, schedule: str) -> DailySchedule:
+        """Parse time ranges in format HHMM-HHMM or HHMM-HHMM&HHMM-HHMM"""
+        if schedule == 'O':
+            return [('0000', '2400')]
+        elif schedule == 'C':
+            return []
+        
+        ranges = []
+        for time_range in schedule.split('&'):
+            start, end = time_range.split('-')
+
+            
+            # Validate time format (HHMM)
+            if not (len(start) == 4 and len(end) == 4 and
+                   start.isdigit() and end.isdigit() and
+                   0 <= int(start[:2]) <= 23 and 0 <= int(start[2:]) <= 59 and
+                   ((0 <= int(end[:2]) <= 23 and 0 <= int(end[2:]) <= 59) or end == "2400")):
+                raise ValueError(f"Invalid time format in schedule: {start}-{end}")
+                
+            ranges.append((start, end))
+        
+        # Sort ranges by start time
+        ranges.sort(key=lambda x: x[0])
+
+        return ranges
+
+    def _get_time_ranges_for_date(self, dt: datetime) -> List[TimeRange]:
+        """Helper function to get time ranges for a specific date, checking holiday schedule first"""
+        date_str = dt.strftime("%m%d")
+        if date_str in self.holiday_schedule:
+            return self.holiday_schedule[date_str]
+        return self.weekly_schedule[dt.weekday()]
+
+    def is_market_open(self, dt: datetime) -> bool:
+        """Check if the market is open at the given datetime"""
+        # Convert to market timezone
+        local_dt = dt.astimezone(self.timezone)
+        time_ranges = self._get_time_ranges_for_date(local_dt)
+        
+        if not time_ranges:
+            return False
+
+        # Check current time against ranges
+        current_time = local_dt.strftime("%H%M")
+        return any(start <= current_time < end for start, end in time_ranges)
+
+    def get_next_market_open(self, dt: datetime) -> Optional[datetime]:
+        """Get the next market open datetime after the given datetime. If the market
+        is open at the given datetime (even if just opens at the given time),
+        returns the next open datetime.
+        
+        If the market is always open, returns None. The returned datetime is in
+        the timezone of the input datetime."""
+        input_tz = dt.tzinfo
+        current = dt.astimezone(self.timezone)
+
+        # This flag is a invariant that indicates whether we're in the initial
+        # trading session of the given datetime as we move forward in time.
+        in_initial_trading_session = True
+        
+        # Look ahead up to 14 days
+        for _ in range(14):
+            time_ranges = self._get_time_ranges_for_date(current)
+            
+            current_time = current.strftime("%H%M")
+            
+            # Find the next open time after current_time
+            next_open = None
+            for start, end in time_ranges:
+                # If the end time is before the current time, skip
+                if end < current_time:
+                    continue
+
+                # If we're in the middle of a trading session, look for next session
+                # the complicated logic is to handle the distinction between
+                # a trading session that continues past midnight and one that doesn't
+                if start < current_time < end or (in_initial_trading_session and start <= current_time < end):
+                    continue
+
+                # Reaching here means we're no longer in a trading session
+                in_initial_trading_session = False
+
+                # If this start time is after current time, this is the next open
+                if current_time < start:
+                    next_open = start
+                    break
+            
+            if next_open is not None:
+                # Found next opening time today
+                hour, minute = int(next_open[:2]), int(next_open[2:])
+                result = current.replace(hour=hour, minute=minute, second=0, microsecond=0)
+                return result.astimezone(input_tz)  # Convert back to input timezone
+            
+            # Move to next day at midnight
+            current = (current + timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0)
+
+            # There is a potential edge case where the market immediately opens at midnight (when rolling over to the next day)
+            # In this case, the new current time is the open time.
+            if self.is_market_open(current) and not self.is_market_open(current - timedelta(minutes=1)):
+                return current.astimezone(input_tz)
+        
+        return None
+
+    def get_next_market_close(self, dt: datetime) -> Optional[datetime]:
+        """Get the next market close datetime after the given datetime. If the
+        market just closes at the given datetime, returns the next close datetime.
+        
+        If the market is always open, returns None. The returned datetime is in
+        the timezone of the input datetime."""
+        input_tz = dt.tzinfo
+        current = dt.astimezone(self.timezone)
+        
+        # Look ahead up to 14 days
+        for _ in range(14):
+            time_ranges = self._get_time_ranges_for_date(current)
+            
+            current_time = current.strftime("%H%M")
+            
+            # Find the next close time after current_time
+            next_close = None
+            for _, end in time_ranges:
+                # If the end time is before (or equal to) the current time, skip
+                if end <= current_time:
+                    continue
+
+                # If we're in a trading session or a new one starts and the end time
+                # doesn't roll over to the next day, this is the next close
+                if current_time < end and end < "2400":
+                    next_close = end
+                    break
+
+            if next_close is not None:
+                # Found next closing time
+                hour, minute = int(next_close[:2]), int(next_close[2:])
+                result = current.replace(hour=hour, minute=minute, second=0, microsecond=0)
+                return result.astimezone(input_tz)  # Convert back to input timezone
+            
+            # Move to next day at midnight
+            current = (current + timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0)
+
+            # There is a potential edge case where the market is not open at midnight (when rolling over to the next day)
+            # In this case, the new current time is the close time.
+            if not self.is_market_open(current) and self.is_market_open(current - timedelta(minutes=1)):
+                return current.astimezone(input_tz)
+        
+        return None
diff --git a/pythclient/pythaccounts.py b/pythclient/pythaccounts.py
index a035c43..0577f7e 100644
--- a/pythclient/pythaccounts.py
+++ b/pythclient/pythaccounts.py
@@ -7,6 +7,8 @@
 
 from loguru import logger
 
+from pythclient.market_schedule import MarketSchedule
+
 from . import exceptions
 from .solana import SolanaPublicKey, SolanaPublicKeyOrStr, SolanaClient, SolanaAccount
 
@@ -229,6 +231,13 @@ def symbol(self) -> str:
         Gets this account's symbol, or 'Unknown' if there is no 'symbol' attribute.
         """
         return self.attrs.get("symbol", "Unknown")
+    
+    @property
+    def schedule(self) -> MarketSchedule:
+        """
+        Gets the market schedule for this product. If the schedule is not set, returns an always open schedule.
+        """
+        return MarketSchedule(self.attrs.get("schedule", "America/New_York;O,O,O,O,O,O,O;"))
 
     async def get_prices(self) -> Dict[PythPriceType, PythPriceAccount]:
         """
diff --git a/setup.py b/setup.py
index 5c0e833..51f0adf 100644
--- a/setup.py
+++ b/setup.py
@@ -7,7 +7,7 @@
 
 setup(
     name='pythclient',
-    version='0.1.24',
+    version='0.2.0',
     packages=['pythclient'],
     author='Pyth Developers',
     author_email='contact@pyth.network',
diff --git a/tests/test_calendar.py b/tests/test_calendar.py
deleted file mode 100644
index 13789e8..0000000
--- a/tests/test_calendar.py
+++ /dev/null
@@ -1,489 +0,0 @@
-import datetime
-from zoneinfo import ZoneInfo
-
-from pythclient.calendar import (get_next_market_close, get_next_market_open,
-                                 is_market_open)
-from pythclient.calendar_full_intervals import (EQUITY_2024_INTERVALS,
-                                                FX_2024_INTERVALS,
-                                                METAL_2024_INTERVALS,
-                                                RATES_2024_INTERVALS)
-
-NY_TZ = ZoneInfo("America/New_York")
-UTC_TZ = ZoneInfo("UTC")
-
-# Define constants for equity market
-EQUITY_OPEN_WED_2023_6_21_12 = datetime.datetime(2023, 6, 21, 12, 0, 0, tzinfo=NY_TZ)
-EQUITY_CLOSE_WED_2023_6_21_17 = datetime.datetime(2023, 6, 21, 17, 0, 0, tzinfo=NY_TZ)
-EQUITY_CLOSE_SAT_2023_6_10_17 = datetime.datetime(2023, 6, 10, 17, 0, 0, tzinfo=NY_TZ)
-EQUITY_HOLIDAY_MON_2023_6_19 = datetime.datetime(2023, 6, 19, tzinfo=NY_TZ)
-EQUITY_HOLIDAY_NEXT_DAY_EARLY_CLOSE_OPEN_THU_2023_11_23_9_30 = datetime.datetime(2023, 11, 23, 9, 30, 0, tzinfo=NY_TZ)
-EQUITY_HOLIDAY_NEXT_DAY_EARLY_CLOSE_CLOSE_THU_2023_11_23_13 = datetime.datetime(2023, 11, 23, 13, 0, 0, tzinfo=NY_TZ)
-EQUITY_EARLY_CLOSE_OPEN_FRI_2023_11_24_11 = datetime.datetime(2023, 11, 24, 11, 0, 0, tzinfo=NY_TZ)
-EQUITY_EARLY_CLOSE_CLOSE_FRI_2023_11_24_14 = datetime.datetime(2023, 11, 24, 14, 0, 0, tzinfo=NY_TZ)
-
-# Define constants for fx & metal market
-FX_METAL_OPEN_WED_2023_6_21_21 = datetime.datetime(2023, 6, 21, 21, 0, 0, tzinfo=NY_TZ)
-FX_METAL_OPEN_WED_2023_6_21_23 = datetime.datetime(2023, 6, 21, 23, 0, 0, tzinfo=NY_TZ)
-FX_METAL_CLOSE_SUN_2023_6_18_16 = datetime.datetime(2023, 6, 18, 16, 0, 0, tzinfo=NY_TZ)
-FX_METAL_HOLIDAY_SUN_2023_1_1 = datetime.datetime(2023, 1, 1, tzinfo=NY_TZ)
-FX_METAL_HOLIDAY_SUN_2023_12_24_17 = datetime.datetime(2023, 12, 24, 17, 0, 0, tzinfo=NY_TZ)
-FX_METAL_HOLIDAY_SUN_2023_12_31_17 = datetime.datetime(2023, 12, 31, 17, 0, 0, tzinfo=NY_TZ)
-
-METAL_EARLY_HOLIDAY_MON_2024_1_15_13 = datetime.datetime(2024, 1, 15, 13, 0, 0, tzinfo=NY_TZ)
-METAL_EARLY_HOLIDAY_MON_2024_1_15_17 = datetime.datetime(2024, 1, 15, 17, 0, 0, tzinfo=NY_TZ)
-METAL_EARLY_HOLIDAY_MON_2024_1_15_18 = datetime.datetime(2024, 1, 15, 18, 0, 0, tzinfo=NY_TZ)
-
-# Define constants for rates market
-RATES_OPEN_WED_2023_6_21_12 = datetime.datetime(2023, 6, 21, 8, 0, 0, tzinfo=NY_TZ)
-RATES_CLOSE_WED_2023_6_21_17 = datetime.datetime(2023, 6, 21, 17, 0, 0, tzinfo=NY_TZ)
-RATES_CLOSE_SAT_2023_6_10_17 = datetime.datetime(2023, 6, 10, 17, 0, 0, tzinfo=NY_TZ)
-RATES_HOLIDAY_MON_2023_6_19 = datetime.datetime(2023, 6, 19, tzinfo=NY_TZ)
-RATES_HOLIDAY_NEXT_DAY_EARLY_CLOSE_OPEN_THU_2023_11_23_8 = datetime.datetime(2023, 11, 23, 8, 0, 0, tzinfo=NY_TZ)
-RATES_HOLIDAY_NEXT_DAY_EARLY_CLOSE_CLOSE_THU_2023_11_23_13 = datetime.datetime(2023, 11, 23, 13, 0, 0, tzinfo=NY_TZ)
-RATES_EARLY_CLOSE_OPEN_FRI_2023_11_24_11 = datetime.datetime(2023, 11, 24, 11, 0, 0, tzinfo=NY_TZ)
-RATES_EARLY_CLOSE_CLOSE_FRI_2023_11_24_14 = datetime.datetime(2023, 11, 24, 14, 0, 0, tzinfo=NY_TZ)
-
-# Define constants for cryptocurrency market
-CRYPTO_OPEN_WED_2023_6_21_12 = datetime.datetime(2023, 6, 21, 12, 0, 0, tzinfo=NY_TZ)
-CRYPTO_OPEN_SUN_2023_6_18_12 = datetime.datetime(2023, 6, 18, 12, 0, 0, tzinfo=NY_TZ)
-
-
-def format_datetime_to_unix_timestamp(dt: datetime.datetime):
-    # Convert the datetime object to a Unix timestamp in UTC
-    timestamp = dt.astimezone(UTC_TZ).timestamp()
-    unix_timestamp_utc = int(timestamp)
-    return unix_timestamp_utc
-
-
-def test_is_market_open():
-    # equity
-    # weekday, within equity market hours
-    assert is_market_open("equity", EQUITY_OPEN_WED_2023_6_21_12) == True
-
-    # weekday, out of equity market hours
-    assert is_market_open("equity", EQUITY_CLOSE_WED_2023_6_21_17) == False
-
-    # weekend, out of equity market hours
-    assert is_market_open("equity", EQUITY_CLOSE_SAT_2023_6_10_17) == False
-
-    # weekday, NYSE holiday
-    assert is_market_open("equity", EQUITY_HOLIDAY_MON_2023_6_19) == False
-
-    # weekday, NYSE early close holiday
-    assert is_market_open("equity", EQUITY_EARLY_CLOSE_OPEN_FRI_2023_11_24_11) == True
-    assert is_market_open("equity", EQUITY_EARLY_CLOSE_CLOSE_FRI_2023_11_24_14) == False
-
-    # fx & metal
-    # weekday, within fx & metal market hours
-    assert is_market_open("fx", FX_METAL_OPEN_WED_2023_6_21_21) == True
-    assert is_market_open("metal", FX_METAL_OPEN_WED_2023_6_21_21) == True
-
-    # weekday, out of fx & metal market hours
-    assert is_market_open("fx", FX_METAL_CLOSE_SUN_2023_6_18_16) == False
-    assert is_market_open("metal", FX_METAL_CLOSE_SUN_2023_6_18_16) == False
-
-    # fx & metal holiday
-    assert is_market_open("fx", FX_METAL_HOLIDAY_SUN_2023_1_1) == False
-    assert is_market_open("metal", FX_METAL_HOLIDAY_SUN_2023_1_1) == False
-
-    # metal early holiday
-    assert is_market_open("metal", METAL_EARLY_HOLIDAY_MON_2024_1_15_13) == True
-    assert is_market_open("metal", METAL_EARLY_HOLIDAY_MON_2024_1_15_17) == False
-    assert is_market_open("metal", METAL_EARLY_HOLIDAY_MON_2024_1_15_18) == True
-  
-
-    # fx & metal out of market hours on Sunday Dec 24 2023 after 10pm UTC
-    assert is_market_open("fx", FX_METAL_HOLIDAY_SUN_2023_12_24_17) == False
-
-    # fx & metal out of market hours on Sunday Dec 31 2023 after 10pm UTC
-    assert is_market_open("fx", FX_METAL_HOLIDAY_SUN_2023_12_31_17) == False
-
-    # rates
-    # weekday, within rates market hours
-    assert is_market_open("rates", RATES_OPEN_WED_2023_6_21_12) == True
-
-    # weekday, out of rates market hours
-    assert is_market_open("rates", RATES_CLOSE_WED_2023_6_21_17) == False
-
-    # weekend, out of rates market hours
-    assert is_market_open("rates", RATES_CLOSE_SAT_2023_6_10_17) == False
-
-    # weekday, NYSE holiday
-    assert is_market_open("rates", RATES_HOLIDAY_MON_2023_6_19) == False
-
-    # weekday, NYSE early close holiday
-    assert is_market_open("rates", RATES_EARLY_CLOSE_OPEN_FRI_2023_11_24_11) == True
-    assert is_market_open("rates", RATES_EARLY_CLOSE_CLOSE_FRI_2023_11_24_14) == False
-
-    # crypto
-    assert is_market_open("crypto", CRYPTO_OPEN_WED_2023_6_21_12) == True
-    assert is_market_open("crypto", CRYPTO_OPEN_SUN_2023_6_18_12) == True
-
-
-def test_get_next_market_open():
-    # equity within market hours
-    assert (
-        get_next_market_open("equity", EQUITY_OPEN_WED_2023_6_21_12)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 22, 9, 30, 0, tzinfo=NY_TZ))
-    )
-
-    # equity out of market hours
-    assert (
-        get_next_market_open("equity", EQUITY_CLOSE_WED_2023_6_21_17)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 22, 9, 30, 0, tzinfo=NY_TZ))
-    )
-
-    # equity weekend
-    assert (
-        get_next_market_open("equity", EQUITY_CLOSE_SAT_2023_6_10_17)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 12, 9, 30, 0, tzinfo=NY_TZ))
-    )
-
-    # equity holiday
-    assert (
-        get_next_market_open("equity", EQUITY_HOLIDAY_MON_2023_6_19)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 20, 9, 30, 0, tzinfo=NY_TZ))
-    )
-
-    # equity holiday next day early close holiday
-    assert (
-        get_next_market_open("equity", EQUITY_HOLIDAY_NEXT_DAY_EARLY_CLOSE_OPEN_THU_2023_11_23_9_30)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 11, 24, 9, 30, 0, tzinfo=NY_TZ))
-    )
-
-    # equity early close holiday
-    assert (
-        get_next_market_open("equity", EQUITY_EARLY_CLOSE_OPEN_FRI_2023_11_24_11)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 11, 27, 9, 30, 0, tzinfo=NY_TZ))
-    )
-    assert (
-        get_next_market_open("equity", EQUITY_EARLY_CLOSE_CLOSE_FRI_2023_11_24_14)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 11, 27, 9, 30, 0, tzinfo=NY_TZ))
-    )
-
-    # fx & metal within market hours (before 10pm UTC)
-    assert (
-        get_next_market_open("fx", FX_METAL_OPEN_WED_2023_6_21_21)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 25, 17, 0, 0, tzinfo=NY_TZ))
-    )
-    assert (
-        get_next_market_open("metal", FX_METAL_OPEN_WED_2023_6_21_21)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 25, 17, 0, 0, tzinfo=NY_TZ))
-    )
-    # fx & metal within market hours (after 10pm UTC)
-    assert (
-        get_next_market_open("fx", FX_METAL_OPEN_WED_2023_6_21_23)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 25, 17, 0, 0, tzinfo=NY_TZ))
-    )
-    assert (
-        get_next_market_open("metal", FX_METAL_OPEN_WED_2023_6_21_23)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 25, 17, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # fx & metal out of market hours
-    assert (
-        get_next_market_open("fx", FX_METAL_CLOSE_SUN_2023_6_18_16)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 18, 17, 0, 0, tzinfo=NY_TZ))
-    )
-    assert (
-        get_next_market_open("metal", FX_METAL_CLOSE_SUN_2023_6_18_16)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 18, 17, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # fx & metal out of market hours on Sunday Dec 24 2023 after 5pm ET
-    assert (
-        get_next_market_open("fx", FX_METAL_HOLIDAY_SUN_2023_12_24_17)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 12, 25, 17, 0, 0, tzinfo=NY_TZ))
-    )
-    assert (
-        get_next_market_open("metal", FX_METAL_HOLIDAY_SUN_2023_12_24_17)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 12, 25, 17, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # fx & metal out of market hours on holiday Dec 25 2023 before 5pm ET
-    assert (
-        get_next_market_open("fx", datetime.datetime(2023, 12, 25, 8, 15, 0, tzinfo=NY_TZ))
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 12, 25, 17, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # fx & metal holiday
-    assert (
-        get_next_market_open("fx", FX_METAL_HOLIDAY_SUN_2023_1_1)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 1, 1, 17, 0, 0, tzinfo=NY_TZ))
-    )
-    assert (
-        get_next_market_open("metal", FX_METAL_HOLIDAY_SUN_2023_1_1)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 1, 1, 17, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # metal early holiday
-    assert (
-        get_next_market_open("metal", METAL_EARLY_HOLIDAY_MON_2024_1_15_13)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2024, 1, 15, 18, 0, 0, tzinfo=NY_TZ))
-    )
-    assert (
-        get_next_market_open("metal", METAL_EARLY_HOLIDAY_MON_2024_1_15_17)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2024, 1, 15, 18, 0, 0, tzinfo=NY_TZ))
-    )
-    assert (
-        get_next_market_open("metal", METAL_EARLY_HOLIDAY_MON_2024_1_15_18)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2024, 1, 21, 17, 0, 0, tzinfo=NY_TZ))
-    )
-    
-
-    # rates within market hours
-    assert (
-        get_next_market_open("rates", RATES_OPEN_WED_2023_6_21_12)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 22, 8, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # rates out of market hours
-    assert (
-        get_next_market_open("rates", RATES_CLOSE_WED_2023_6_21_17)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 22, 8, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # rates weekend
-    assert (
-        get_next_market_open("rates", RATES_CLOSE_SAT_2023_6_10_17)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 12, 8, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # rates holiday
-    assert (
-        get_next_market_open("rates", RATES_HOLIDAY_MON_2023_6_19)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 20, 8, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # rates holiday next day early close holiday
-    assert (
-        get_next_market_open("rates", RATES_HOLIDAY_NEXT_DAY_EARLY_CLOSE_OPEN_THU_2023_11_23_8)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 11, 24, 8, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # rates early close holiday
-    assert (
-        get_next_market_open("rates", RATES_EARLY_CLOSE_OPEN_FRI_2023_11_24_11)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 11, 27, 8, 0, 0, tzinfo=NY_TZ))
-    )
-    assert (
-        get_next_market_open("rates", RATES_EARLY_CLOSE_CLOSE_FRI_2023_11_24_14)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 11, 27, 8, 0, 0, tzinfo=NY_TZ))
-    )
-
-
-    # crypto
-    assert get_next_market_open("crypto", CRYPTO_OPEN_WED_2023_6_21_12) == None
-    assert get_next_market_open("crypto", CRYPTO_OPEN_SUN_2023_6_18_12) == None
-
-
-def test_get_next_market_close():
-    # equity within market hours
-    assert (
-        get_next_market_close("equity", EQUITY_OPEN_WED_2023_6_21_12)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 21, 16, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # equity out of market hours
-    assert (
-        get_next_market_close("equity", EQUITY_CLOSE_WED_2023_6_21_17)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 22, 16, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # equity weekend
-    assert (
-        get_next_market_close("equity", EQUITY_CLOSE_SAT_2023_6_10_17)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 12, 16, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # equity holiday
-    assert (
-        get_next_market_close("equity", EQUITY_HOLIDAY_MON_2023_6_19)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 20, 16, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # equity holiday next day early close holiday
-    assert (
-        get_next_market_close("equity", EQUITY_HOLIDAY_NEXT_DAY_EARLY_CLOSE_CLOSE_THU_2023_11_23_13)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 11, 24, 13, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # equity early close holiday
-    assert (
-        get_next_market_close("equity", EQUITY_EARLY_CLOSE_OPEN_FRI_2023_11_24_11)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 11, 24, 13, 0, 0, tzinfo=NY_TZ))
-    )
-    assert (
-        get_next_market_close("equity", EQUITY_EARLY_CLOSE_CLOSE_FRI_2023_11_24_14)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 11, 27, 16, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # fx & metal within market hours (before 10pm UTC)
-    assert (
-        get_next_market_close("fx", FX_METAL_OPEN_WED_2023_6_21_21)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 23, 17, 0, 0, tzinfo=NY_TZ))
-    )
-    assert (
-        get_next_market_close("metal", FX_METAL_OPEN_WED_2023_6_21_21)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 23, 17, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # fx & metal within market hours (after 10pm UTC)
-    assert (
-        get_next_market_close("fx", FX_METAL_OPEN_WED_2023_6_21_23)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 23, 17, 0, 0, tzinfo=NY_TZ))
-    )
-    assert (
-        get_next_market_close("metal", FX_METAL_OPEN_WED_2023_6_21_23)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 23, 17, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # fx & metal within market hours on a friday (before 10pm UTC)
-    assert (
-        get_next_market_close("fx", datetime.datetime(2023, 11, 10, 7, 0, 0, tzinfo=NY_TZ))
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 11, 10, 17, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # fx & metal out of market hours
-    assert (
-        get_next_market_close("fx", FX_METAL_CLOSE_SUN_2023_6_18_16)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 23, 17, 0, 0, tzinfo=NY_TZ))
-    )
-    assert (
-        get_next_market_close("metal", FX_METAL_CLOSE_SUN_2023_6_18_16)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 23, 17, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # fx & metal holiday
-    assert (
-        get_next_market_close("fx", FX_METAL_HOLIDAY_SUN_2023_1_1)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 1, 6, 17, 0, 0, tzinfo=NY_TZ))
-    )
-    assert (
-        get_next_market_close("metal", FX_METAL_HOLIDAY_SUN_2023_1_1)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 1, 6, 17, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # metal early holiday
-    assert (
-        get_next_market_close("metal", METAL_EARLY_HOLIDAY_MON_2024_1_15_13)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2024, 1, 15, 14, 30, 0, tzinfo=NY_TZ))
-    )
-    assert (
-        get_next_market_close("metal", METAL_EARLY_HOLIDAY_MON_2024_1_15_17)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2024, 1, 19, 17, 0, 0, tzinfo=NY_TZ))
-    )
-    assert (
-        get_next_market_close("metal", METAL_EARLY_HOLIDAY_MON_2024_1_15_18)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2024, 1, 19, 17, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # rates within market hours
-    assert (
-        get_next_market_close("rates", RATES_OPEN_WED_2023_6_21_12)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 21, 17, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # rates out of market hours
-    assert (
-        get_next_market_close("rates", RATES_CLOSE_WED_2023_6_21_17)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 22, 17, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # rates weekend
-    assert (
-        get_next_market_close("rates", RATES_CLOSE_SAT_2023_6_10_17)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 12, 17, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # rates holiday
-    assert (
-        get_next_market_close("rates", RATES_HOLIDAY_MON_2023_6_19)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 6, 20, 17, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # rates holiday next day early close holiday
-    assert (
-        get_next_market_close("rates", RATES_HOLIDAY_NEXT_DAY_EARLY_CLOSE_CLOSE_THU_2023_11_23_13)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 11, 24, 13, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # rates early close holiday
-    assert (
-        get_next_market_close("rates", RATES_EARLY_CLOSE_OPEN_FRI_2023_11_24_11)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 11, 24, 13, 0, 0, tzinfo=NY_TZ))
-    )
-    assert (
-        get_next_market_close("rates", RATES_EARLY_CLOSE_CLOSE_FRI_2023_11_24_14)
-        == format_datetime_to_unix_timestamp(datetime.datetime(2023, 11, 27, 17, 0, 0, tzinfo=NY_TZ))
-    )
-
-    # crypto
-    assert get_next_market_close("crypto", CRYPTO_OPEN_WED_2023_6_21_12) == None
-    assert get_next_market_close("crypto", CRYPTO_OPEN_SUN_2023_6_18_12) == None
-
-
-def test_is_market_open_full():
-    start_date = datetime.datetime(2024, 1, 1, tzinfo=NY_TZ)
-    end_date = datetime.datetime(2025, 1, 1, tzinfo=NY_TZ)
-    asset_types = ["equity", "fx", "metal", "rates", "crypto"]
-
-    all_intervals = {
-        "equity": {},
-        "fx": {},
-        "metal": {},
-        "rates": {},
-    }
-
-    data_sources = {
-        "equity": EQUITY_2024_INTERVALS,
-        "fx": FX_2024_INTERVALS,
-        "metal": METAL_2024_INTERVALS,
-        "rates": RATES_2024_INTERVALS,
-    }
-
-    for asset_type, data in data_sources.items():
-        for date, interval in data:
-            if date not in all_intervals[asset_type]:
-                all_intervals[asset_type][date] = []
-            if interval != None:
-                all_intervals[asset_type][date].append(interval)
-
-    current_date = start_date
-    while current_date < end_date:
-        for at in asset_types:
-            if at == "crypto":
-                continue
-            # Get the interval for the date
-            intervals = all_intervals[at].get(current_date.date())
-
-            if not intervals:
-                should_be_open = False
-            else:
-                should_be_open = is_time_in_intervals(current_date.time(), intervals)
-                pass
-
-            # Check if the market is open
-            is_open = is_market_open(at, current_date)
-
-            # Assert that the market is open if and only if it should be open
-            assert (
-                is_open == should_be_open
-            ), f"Failed for asset type: {at}, date: {current_date}"
-
-        # Add one minute to the current date
-        current_date += datetime.timedelta(minutes=1)
-
-
-def is_time_in_intervals(current_time, intervals):
-    for interval in intervals:
-        start_time, end_time = [
-            datetime.datetime.strptime(t, "%H%M").time() for t in interval.split("-")
-        ]
-        if start_time < end_time:
-            if start_time <= current_time < end_time:
-                return True
-        else:  # Over midnight
-            if start_time <= current_time or current_time < end_time:
-                return True
-    return False
diff --git a/tests/test_market_schedule.py b/tests/test_market_schedule.py
new file mode 100644
index 0000000..888b481
--- /dev/null
+++ b/tests/test_market_schedule.py
@@ -0,0 +1,152 @@
+import pytest
+from datetime import datetime, timedelta
+from zoneinfo import ZoneInfo
+from pythclient.market_schedule import MarketSchedule
+
+# This fixtures are based on the schedule from the metadata of the assets taken at 2024-12-20
+FIXTURES = {
+    "amazonusd": "America/New_York;0930-1600,0930-1600,0930-1600,0930-1600,0930-1600,C,C;1224/0930-1300,1225/C,0101/C,0120/C,0217/C,0418/C,0526/C,0619/C",
+    "brent1musd": "America/New_York;0000-1800&2000-2400,0000-1800&2000-2400,0000-1800&2000-2400,0000-1800&2000-2400,0000-1800,C,1800-2400;1224/0000-1400&2000-2400,1225/C,0101/C,0418/C",
+    "btcusd": "America/New_York;O,O,O,O,O,O,O;",
+    "eurusd": "America/New_York;O,O,O,O,0000-1700,C,1700-2400;1224/0000-1700,1225/1700-2400,1231/0000-1700,0101/1700-2400",
+}
+
+def test_market_open():
+    # Test Amazon trading hours (regular NYSE hours)
+    schedule = MarketSchedule(FIXTURES["amazonusd"])
+
+    # Close at 9:29 ET
+    assert not schedule.is_market_open(datetime(2024, 3, 1, 9, 29, tzinfo=ZoneInfo("UTC"))) # 9:29 ET
+    # Open at 9:30 ET
+    assert schedule.is_market_open(datetime(2024, 3, 1, 14, 30, tzinfo=ZoneInfo("UTC"))) # 9:30 ET
+
+    # Regular trading day
+    assert schedule.is_market_open(datetime(2024, 3, 1, 19, 30, tzinfo=ZoneInfo("UTC"))) # 14:30 ET
+
+    # Open at 15:59 ET
+    assert schedule.is_market_open(datetime(2024, 3, 1, 20, 59, tzinfo=ZoneInfo("UTC"))) # 15:59 ET
+    # Close at 16:00 ET
+    assert not schedule.is_market_open(datetime(2024, 3, 1, 21, 0, tzinfo=ZoneInfo("UTC"))) # 16:00 ET
+    
+    # Weekend
+    assert not schedule.is_market_open(datetime(2024, 3, 2, 14, 30, tzinfo=ZoneInfo("UTC"))) # Saturday 9:30 ET
+    
+    # Holiday (Christmas) - market is closed
+    assert not schedule.is_market_open(datetime(2024, 12, 25, 14, 30, tzinfo=ZoneInfo("UTC"))) # Christmas 25/12/2024 9:30 ET
+
+    # Holiday (Christmas Eve) - market has early close
+    assert not schedule.is_market_open(datetime(2024, 12, 24, 14, 29, tzinfo=ZoneInfo("UTC"))) # Christmas Eve 24/12/2024 9:29 ET
+    assert schedule.is_market_open(datetime(2024, 12, 24, 14, 30, tzinfo=ZoneInfo("UTC"))) # Christmas Eve 24/12/2024 9:30 ET
+    assert schedule.is_market_open(datetime(2024, 12, 24, 17, 59, tzinfo=ZoneInfo("UTC"))) # Christmas Eve 24/12/2024 12:59 ET
+    assert not schedule.is_market_open(datetime(2024, 12, 24, 18, 0, tzinfo=ZoneInfo("UTC"))) # Christmas Eve 24/12/2024 13:00 ET
+
+def test_next_market_open():
+    schedule = MarketSchedule(FIXTURES["amazonusd"])
+    
+    # Test next open from weekend
+    dt = datetime(2024, 3, 2, 12, 0, tzinfo=ZoneInfo("UTC")) # Saturday
+    next_open = schedule.get_next_market_open(dt)
+    assert next_open.strftime("%Y-%m-%d %H:%M") == "2024-03-04 14:30" # Monday 9:30 ET
+    
+    # Test next open from current open trading session (right at the start of the session)
+    dt = datetime(2024, 3, 1, 14, 30, tzinfo=ZoneInfo("UTC")) # Friday 9:30 ET
+    next_open = schedule.get_next_market_open(dt)
+    assert next_open.strftime("%Y-%m-%d %H:%M") == "2024-03-04 14:30" # Monday 9:30 ET
+
+def test_next_market_close():
+    schedule = MarketSchedule(FIXTURES["amazonusd"])
+    
+    # Test next close during trading hours
+    dt = datetime(2024, 3, 1, 14, 30, tzinfo=ZoneInfo("UTC")) # Friday 9:30 ET
+    next_close = schedule.get_next_market_close(dt)
+    assert next_close == datetime(2024, 3, 1, 21, 0, tzinfo=ZoneInfo("UTC"))
+
+    # Test next close from the end of the trading session
+    dt = datetime(2024, 3, 1, 21, 0, tzinfo=ZoneInfo("UTC")) # Friday 16:00 ET
+    next_close = schedule.get_next_market_close(dt)
+    assert next_close == datetime(2024, 3, 4, 21, 0, tzinfo=ZoneInfo("UTC"))
+    
+    # Test next close from weekend
+    dt = datetime(2024, 3, 2, 12, 0, tzinfo=ZoneInfo("UTC")) # Saturday 8:00 ET
+    next_close = schedule.get_next_market_close(dt)
+    assert next_close == datetime(2024, 3, 4, 21, 0, tzinfo=ZoneInfo("UTC"))
+
+def test_complex_schedule_brent1musd():
+    """Test Brent oil futures with multiple sessions per day"""
+
+    schedule = MarketSchedule(FIXTURES["brent1musd"])
+    
+    # Test during first session, 4 Dec 2024 (Wednesday)
+    assert schedule.is_market_open(datetime(2024, 12, 4, 0, 0, tzinfo=ZoneInfo("America/New_York")))
+    assert schedule.is_market_open(datetime(2024, 12, 4, 12, 0, tzinfo=ZoneInfo("America/New_York")))
+    
+    # Test during gap between sessions
+    assert not schedule.is_market_open(datetime(2024, 12, 4, 18, 0, tzinfo=ZoneInfo("America/New_York")))
+    assert not schedule.is_market_open(datetime(2024, 12, 4, 19, 0, tzinfo=ZoneInfo("America/New_York")))
+    
+    # Test during second session
+    assert schedule.is_market_open(datetime(2024, 12, 4, 20, 0, tzinfo=ZoneInfo("America/New_York")))
+    assert schedule.is_market_open(datetime(2024, 12, 4, 23, 59, tzinfo=ZoneInfo("America/New_York")))
+
+    # Test next market close
+    next_close = schedule.get_next_market_close(datetime(2024, 12, 4, 23, 59, tzinfo=ZoneInfo("America/New_York")))
+    assert next_close == datetime(2024, 12, 5, 18, 0, tzinfo=ZoneInfo("America/New_York"))
+
+    # Test next market open
+    next_open = schedule.get_next_market_open(datetime(2024, 12, 4, 23, 59, tzinfo=ZoneInfo("America/New_York")))
+    assert next_open == datetime(2024, 12, 5, 20, 0, tzinfo=ZoneInfo("America/New_York"))
+
+def test_always_open_schedule():
+    """Test a schedule that is always open"""
+
+    schedule = MarketSchedule(FIXTURES["btcusd"])
+    assert schedule.is_market_open(datetime(2024, 12, 3, 23, 59, tzinfo=ZoneInfo("America/New_York")))
+
+    # Make sure next market open and next market close are None
+    assert schedule.get_next_market_open(datetime(2024, 12, 3, 23, 59, tzinfo=ZoneInfo("America/New_York"))) is None
+    assert schedule.get_next_market_close(datetime(2024, 12, 3, 23, 59, tzinfo=ZoneInfo("America/New_York"))) is None
+
+def test_invalid_schedules():
+    # Test invalid timezone
+    with pytest.raises(ValueError):
+        MarketSchedule("Invalid/Timezone;0930-1600,0930-1600,0930-1600,0930-1600,0930-1600,C,C")
+    
+    # Test invalid number of days
+    with pytest.raises(ValueError):
+        MarketSchedule("America/New_York;0930-1600,0930-1600,0930-1600,0930-1600,0930-1600,C")
+    
+    # Test invalid time format
+    with pytest.raises(ValueError):
+        MarketSchedule("America/New_York;1600-2500,0930-1600,0930-1600,0930-1600,0930-1600,C,C")
+    
+    # Test invalid schedule format
+    with pytest.raises(ValueError):
+        MarketSchedule("America/New_York;0930-1600-1700,0930-1600,0930-1600,0930-1600,0930-1600,C,C")
+    
+    # Test invalid holiday schedule
+    with pytest.raises(ValueError):
+        MarketSchedule("America/New_York;0930-1600,0930-1600,0930-1600,0930-1600,0930-1600,C,C;1224/0930-2500")
+
+@pytest.mark.parametrize("asset", ["amazonusd", "brent1musd", "eurusd"])
+def test_walk_backwards_through_schedule(asset):
+    """Test walking backwards through the schedule to test the next market open and next market close"""
+    schedule = MarketSchedule(FIXTURES[asset])
+
+    current_time = datetime(2025, 6, 6, 0, 0, tzinfo=ZoneInfo("America/New_York"))
+    expected_next_market_open = None
+    expected_next_market_close = None
+
+    while datetime(2024, 6, 6, 0, 0, tzinfo=ZoneInfo("America/New_York")) < current_time:
+        # update the expected next market open and next market close
+        if not schedule.is_market_open(current_time) and schedule.is_market_open(current_time + timedelta(minutes=1)):
+            expected_next_market_open = current_time + timedelta(minutes=1)
+        if schedule.is_market_open(current_time) and not schedule.is_market_open(current_time + timedelta(minutes=1)):
+            expected_next_market_close = current_time + timedelta(minutes=1)
+
+        next_market_open = schedule.get_next_market_open(current_time)
+        next_market_close = schedule.get_next_market_close(current_time)
+
+        assert not expected_next_market_open or next_market_open == expected_next_market_open
+        assert not expected_next_market_close or next_market_close == expected_next_market_close
+
+        current_time -= timedelta(minutes=1)