Skip to content

Commit 944a24b

Browse files
authored
next close (#32)
* add next market close * bump * make sure args in ny tz * assert dt in is_market_open
1 parent b97aad1 commit 944a24b

File tree

2 files changed

+62
-24
lines changed

2 files changed

+62
-24
lines changed

pythclient/calendar.py

+61-23
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,45 @@
11
import datetime
2+
from zoneinfo import ZoneInfo
23

3-
import pytz
4+
NY_TZ = ZoneInfo("America/New_York")
5+
UTC_TZ = ZoneInfo("UTC")
46

5-
TZ = pytz.timezone("America/New_York")
6-
7-
EQUITY_OPEN = datetime.time(9, 30, 0, tzinfo=TZ)
8-
EQUITY_CLOSE = datetime.time(16, 0, 0, tzinfo=TZ)
9-
EQUITY_EARLY_CLOSE = datetime.time(13, 0, 0, tzinfo=TZ)
7+
EQUITY_OPEN = datetime.time(9, 30, 0, tzinfo=NY_TZ)
8+
EQUITY_CLOSE = datetime.time(16, 0, 0, tzinfo=NY_TZ)
9+
EQUITY_EARLY_CLOSE = datetime.time(13, 0, 0, tzinfo=NY_TZ)
1010

1111
# EQUITY_HOLIDAYS and EQUITY_EARLY_HOLIDAYS will need to be updated each year
1212
# From https://www.nyse.com/markets/hours-calendars
1313
EQUITY_HOLIDAYS = [
14-
datetime.datetime(2023, 1, 2, tzinfo=TZ).date(),
15-
datetime.datetime(2023, 1, 16, tzinfo=TZ).date(),
16-
datetime.datetime(2023, 2, 20, tzinfo=TZ).date(),
17-
datetime.datetime(2023, 4, 7, tzinfo=TZ).date(),
18-
datetime.datetime(2023, 5, 29, tzinfo=TZ).date(),
19-
datetime.datetime(2023, 6, 19, tzinfo=TZ).date(),
20-
datetime.datetime(2023, 7, 4, tzinfo=TZ).date(),
21-
datetime.datetime(2022, 9, 4, tzinfo=TZ).date(),
22-
datetime.datetime(2023, 11, 23, tzinfo=TZ).date(),
23-
datetime.datetime(2023, 12, 25, tzinfo=TZ).date(),
14+
datetime.datetime(2023, 1, 2, tzinfo=NY_TZ).date(),
15+
datetime.datetime(2023, 1, 16, tzinfo=NY_TZ).date(),
16+
datetime.datetime(2023, 2, 20, tzinfo=NY_TZ).date(),
17+
datetime.datetime(2023, 4, 7, tzinfo=NY_TZ).date(),
18+
datetime.datetime(2023, 5, 29, tzinfo=NY_TZ).date(),
19+
datetime.datetime(2023, 6, 19, tzinfo=NY_TZ).date(),
20+
datetime.datetime(2023, 7, 4, tzinfo=NY_TZ).date(),
21+
datetime.datetime(2022, 9, 4, tzinfo=NY_TZ).date(),
22+
datetime.datetime(2023, 11, 23, tzinfo=NY_TZ).date(),
23+
datetime.datetime(2023, 12, 25, tzinfo=NY_TZ).date(),
2424
]
2525
EQUITY_EARLY_HOLIDAYS = [
26-
datetime.datetime(2023, 7, 3, tzinfo=TZ).date(),
27-
datetime.datetime(2023, 11, 24, tzinfo=TZ).date(),
26+
datetime.datetime(2023, 7, 3, tzinfo=NY_TZ).date(),
27+
datetime.datetime(2023, 11, 24, tzinfo=NY_TZ).date(),
2828
]
2929

30-
FX_METAL_OPEN_CLOSE_TIME = datetime.time(17, 0, 0, tzinfo=TZ)
30+
FX_METAL_OPEN_CLOSE_TIME = datetime.time(17, 0, 0, tzinfo=NY_TZ)
3131

3232
# FX_METAL_HOLIDAYS will need to be updated each year
3333
# From https://www.cboe.com/about/hours/fx/
3434
FX_METAL_HOLIDAYS = [
35-
datetime.datetime(2023, 1, 1, tzinfo=TZ).date(),
36-
datetime.datetime(2023, 12, 25, tzinfo=TZ).date(),
35+
datetime.datetime(2023, 1, 1, tzinfo=NY_TZ).date(),
36+
datetime.datetime(2023, 12, 25, tzinfo=NY_TZ).date(),
3737
]
3838

3939

4040
def is_market_open(asset_type: str, dt: datetime.datetime) -> bool:
41+
# make sure time is in NY timezone
42+
dt = dt.astimezone(NY_TZ)
4143
day, date, time = dt.weekday(), dt.date(), dt.time()
4244

4345
if asset_type == "equity":
@@ -73,10 +75,12 @@ def is_market_open(asset_type: str, dt: datetime.datetime) -> bool:
7375

7476

7577
def get_next_market_open(asset_type: str, dt: datetime.datetime) -> str:
78+
# make sure time is in NY timezone
79+
dt = dt.astimezone(NY_TZ)
7680
time = dt.time()
7781

7882
if is_market_open(asset_type, dt):
79-
return dt.astimezone(pytz.UTC).strftime("%Y-%m-%dT%H:%M:%S") + "Z"
83+
return dt.astimezone(UTC_TZ).strftime("%Y-%m-%dT%H:%M:%S") + "Z"
8084

8185
if asset_type == "equity":
8286
if time < EQUITY_OPEN:
@@ -117,4 +121,38 @@ def get_next_market_open(asset_type: str, dt: datetime.datetime) -> str:
117121
while not is_market_open(asset_type, next_market_open):
118122
next_market_open += datetime.timedelta(days=1)
119123

120-
return next_market_open.astimezone(pytz.UTC).strftime("%Y-%m-%dT%H:%M:%S") + "Z"
124+
return next_market_open.astimezone(UTC_TZ).strftime("%Y-%m-%dT%H:%M:%S") + "Z"
125+
126+
def get_next_market_close(asset_type: str, dt: datetime.datetime) -> str:
127+
# make sure time is in NY timezone
128+
dt = dt.astimezone(NY_TZ)
129+
if not is_market_open(asset_type, dt):
130+
return dt.astimezone(UTC_TZ).strftime("%Y-%m-%dT%H:%M:%S") + "Z"
131+
132+
if asset_type == "equity":
133+
if dt.date() in EQUITY_EARLY_HOLIDAYS:
134+
135+
next_market_close = dt.replace(
136+
hour=EQUITY_EARLY_CLOSE.hour,
137+
minute=EQUITY_EARLY_CLOSE.minute,
138+
second=0,
139+
microsecond=0,
140+
)
141+
else:
142+
next_market_close = dt.replace(
143+
hour=EQUITY_CLOSE.hour,
144+
minute=EQUITY_CLOSE.minute,
145+
second=0,
146+
microsecond=0,
147+
)
148+
elif asset_type in ["fx", "metal"]:
149+
next_market_close = dt.replace(
150+
hour=FX_METAL_OPEN_CLOSE_TIME.hour,
151+
minute=FX_METAL_OPEN_CLOSE_TIME.minute,
152+
second=0,
153+
microsecond=0,
154+
)
155+
else: # crypto markets never close
156+
return None
157+
158+
return next_market_close.astimezone(UTC_TZ).strftime("%Y-%m-%dT%H:%M:%S") + "Z"

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
setup(
99
name='pythclient',
10-
version='0.1.5',
10+
version='0.1.6',
1111
packages=['pythclient'],
1212
author='Pyth Developers',
1313
author_email='[email protected]',

0 commit comments

Comments
 (0)