Skip to content

Commit

Permalink
add rates calendar (#43)
Browse files Browse the repository at this point in the history
* add rates to calendar

* add get_market_hours_clause

* bump

* remove get_market_hours_clause
  • Loading branch information
cctdaniel authored Nov 7, 2023
1 parent 074670b commit fd7f08b
Show file tree
Hide file tree
Showing 3 changed files with 261 additions and 29 deletions.
158 changes: 134 additions & 24 deletions pythclient/calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@

EQUITY_OPEN = datetime.time(9, 30, 0, tzinfo=NY_TZ)
EQUITY_CLOSE = datetime.time(16, 0, 0, tzinfo=NY_TZ)
EQUITY_EARLY_CLOSE = datetime.time(13, 0, 0, tzinfo=NY_TZ)

# EQUITY_HOLIDAYS and EQUITY_EARLY_HOLIDAYS will need to be updated each year
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
EQUITY_HOLIDAYS = [
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(),
Expand All @@ -22,7 +23,7 @@
datetime.datetime(2023, 11, 23, tzinfo=NY_TZ).date(),
datetime.datetime(2023, 12, 25, tzinfo=NY_TZ).date(),
]
EQUITY_EARLY_HOLIDAYS = [
NYSE_EARLY_HOLIDAYS = [
datetime.datetime(2023, 7, 3, tzinfo=NY_TZ).date(),
datetime.datetime(2023, 11, 24, tzinfo=NY_TZ).date(),
]
Expand All @@ -36,18 +37,21 @@
datetime.datetime(2023, 12, 25, 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 EQUITY_HOLIDAYS or date in EQUITY_EARLY_HOLIDAYS:
if date in NYSE_HOLIDAYS or date in NYSE_EARLY_HOLIDAYS:
if (
date in EQUITY_EARLY_HOLIDAYS
date in NYSE_EARLY_HOLIDAYS
and time >= EQUITY_OPEN
and time < EQUITY_EARLY_CLOSE
and time < NYSE_EARLY_CLOSE
):
return True
return False
Expand All @@ -70,6 +74,19 @@ def is_market_open(asset_type: str, dt: datetime.datetime) -> bool:

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

Expand Down Expand Up @@ -112,6 +129,22 @@ def get_next_market_open(asset_type: str, dt: datetime.datetime) -> int:
)
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

Expand All @@ -127,11 +160,11 @@ def get_next_market_close(asset_type: str, dt: datetime.datetime) -> int:
time = dt.time()

if asset_type == "equity":
if dt.date() in EQUITY_EARLY_HOLIDAYS:
if time < EQUITY_EARLY_CLOSE:
if dt.date() in NYSE_EARLY_HOLIDAYS:
if time < NYSE_EARLY_CLOSE:
next_market_close = dt.replace(
hour=EQUITY_EARLY_CLOSE.hour,
minute=EQUITY_EARLY_CLOSE.minute,
hour=NYSE_EARLY_CLOSE.hour,
minute=NYSE_EARLY_CLOSE.minute,
second=0,
microsecond=0,
)
Expand All @@ -143,20 +176,35 @@ def get_next_market_close(asset_type: str, dt: datetime.datetime) -> int:
microsecond=0,
)
next_market_close += datetime.timedelta(days=1)
elif dt.date() in EQUITY_HOLIDAYS:
next_market_open = get_next_market_open(
asset_type, dt + datetime.timedelta(days=1)
)
next_market_close = (
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)
.replace(
hour=EQUITY_CLOSE.hour,
minute=EQUITY_CLOSE.minute,
second=0,
microsecond=0,
)
.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,
Expand All @@ -167,9 +215,9 @@ def get_next_market_close(asset_type: str, dt: datetime.datetime) -> int:
if time >= EQUITY_CLOSE:
next_market_close += datetime.timedelta(days=1)

# while next_market_close.date() is in EQUITY_HOLIDAYS or weekend, add 1 day
# while next_market_close.date() is in NYSE_HOLIDAYS or weekend, add 1 day
while (
next_market_close.date() in EQUITY_HOLIDAYS
next_market_close.date() in NYSE_HOLIDAYS
or next_market_close.weekday() >= 5
):
next_market_close += datetime.timedelta(days=1)
Expand All @@ -185,6 +233,68 @@ def get_next_market_close(asset_type: str, dt: datetime.datetime) -> int:
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

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

setup(
name='pythclient',
version='0.1.15',
version='0.1.16',
packages=['pythclient'],
author='Pyth Developers',
author_email='[email protected]',
Expand Down
Loading

0 comments on commit fd7f08b

Please sign in to comment.