Skip to content

Commit fd7f08b

Browse files
authored
add rates calendar (#43)
* add rates to calendar * add get_market_hours_clause * bump * remove get_market_hours_clause
1 parent 074670b commit fd7f08b

File tree

3 files changed

+261
-29
lines changed

3 files changed

+261
-29
lines changed

pythclient/calendar.py

+134-24
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66

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

11-
# EQUITY_HOLIDAYS and EQUITY_EARLY_HOLIDAYS will need to be updated each year
10+
NYSE_EARLY_CLOSE = datetime.time(13, 0, 0, tzinfo=NY_TZ)
11+
12+
# NYSE_HOLIDAYS and NYSE_EARLY_HOLIDAYS will need to be updated each year
1213
# From https://www.nyse.com/markets/hours-calendars
13-
EQUITY_HOLIDAYS = [
14+
NYSE_HOLIDAYS = [
1415
datetime.datetime(2023, 1, 2, tzinfo=NY_TZ).date(),
1516
datetime.datetime(2023, 1, 16, tzinfo=NY_TZ).date(),
1617
datetime.datetime(2023, 2, 20, tzinfo=NY_TZ).date(),
@@ -22,7 +23,7 @@
2223
datetime.datetime(2023, 11, 23, tzinfo=NY_TZ).date(),
2324
datetime.datetime(2023, 12, 25, tzinfo=NY_TZ).date(),
2425
]
25-
EQUITY_EARLY_HOLIDAYS = [
26+
NYSE_EARLY_HOLIDAYS = [
2627
datetime.datetime(2023, 7, 3, tzinfo=NY_TZ).date(),
2728
datetime.datetime(2023, 11, 24, tzinfo=NY_TZ).date(),
2829
]
@@ -36,18 +37,21 @@
3637
datetime.datetime(2023, 12, 25, tzinfo=NY_TZ).date(),
3738
]
3839

40+
RATES_OPEN = datetime.time(8, 0, 0, tzinfo=NY_TZ)
41+
RATES_CLOSE = datetime.time(17, 0, 0, tzinfo=NY_TZ)
42+
3943

4044
def is_market_open(asset_type: str, dt: datetime.datetime) -> bool:
4145
# make sure time is in NY timezone
4246
dt = dt.astimezone(NY_TZ)
4347
day, date, time = dt.weekday(), dt.date(), dt.time()
4448

4549
if asset_type == "equity":
46-
if date in EQUITY_HOLIDAYS or date in EQUITY_EARLY_HOLIDAYS:
50+
if date in NYSE_HOLIDAYS or date in NYSE_EARLY_HOLIDAYS:
4751
if (
48-
date in EQUITY_EARLY_HOLIDAYS
52+
date in NYSE_EARLY_HOLIDAYS
4953
and time >= EQUITY_OPEN
50-
and time < EQUITY_EARLY_CLOSE
54+
and time < NYSE_EARLY_CLOSE
5155
):
5256
return True
5357
return False
@@ -70,6 +74,19 @@ def is_market_open(asset_type: str, dt: datetime.datetime) -> bool:
7074

7175
return True
7276

77+
if asset_type == "rates":
78+
if date in NYSE_HOLIDAYS or date in NYSE_EARLY_HOLIDAYS:
79+
if (
80+
date in NYSE_EARLY_HOLIDAYS
81+
and time >= RATES_OPEN
82+
and time < NYSE_EARLY_CLOSE
83+
):
84+
return True
85+
return False
86+
if day < 5 and time >= RATES_OPEN and time < RATES_CLOSE:
87+
return True
88+
return False
89+
7390
# all other markets (crypto)
7491
return True
7592

@@ -112,6 +129,22 @@ def get_next_market_open(asset_type: str, dt: datetime.datetime) -> int:
112129
)
113130
while is_market_open(asset_type, next_market_open):
114131
next_market_open += datetime.timedelta(days=1)
132+
elif asset_type == "rates":
133+
if time < RATES_OPEN:
134+
next_market_open = dt.replace(
135+
hour=RATES_OPEN.hour,
136+
minute=RATES_OPEN.minute,
137+
second=0,
138+
microsecond=0,
139+
)
140+
else:
141+
next_market_open = dt.replace(
142+
hour=RATES_OPEN.hour,
143+
minute=RATES_OPEN.minute,
144+
second=0,
145+
microsecond=0,
146+
)
147+
next_market_open += datetime.timedelta(days=1)
115148
else:
116149
return None
117150

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

129162
if asset_type == "equity":
130-
if dt.date() in EQUITY_EARLY_HOLIDAYS:
131-
if time < EQUITY_EARLY_CLOSE:
163+
if dt.date() in NYSE_EARLY_HOLIDAYS:
164+
if time < NYSE_EARLY_CLOSE:
132165
next_market_close = dt.replace(
133-
hour=EQUITY_EARLY_CLOSE.hour,
134-
minute=EQUITY_EARLY_CLOSE.minute,
166+
hour=NYSE_EARLY_CLOSE.hour,
167+
minute=NYSE_EARLY_CLOSE.minute,
135168
second=0,
136169
microsecond=0,
137170
)
@@ -143,20 +176,35 @@ def get_next_market_close(asset_type: str, dt: datetime.datetime) -> int:
143176
microsecond=0,
144177
)
145178
next_market_close += datetime.timedelta(days=1)
146-
elif dt.date() in EQUITY_HOLIDAYS:
147-
next_market_open = get_next_market_open(
148-
asset_type, dt + datetime.timedelta(days=1)
149-
)
150-
next_market_close = (
179+
elif dt.date() in NYSE_HOLIDAYS:
180+
next_market_open = get_next_market_open(asset_type, dt)
181+
next_market_open_date = (
151182
datetime.datetime.fromtimestamp(next_market_open)
152183
.astimezone(NY_TZ)
153-
.replace(
154-
hour=EQUITY_CLOSE.hour,
155-
minute=EQUITY_CLOSE.minute,
156-
second=0,
157-
microsecond=0,
158-
)
184+
.date()
159185
)
186+
if next_market_open_date in NYSE_EARLY_HOLIDAYS:
187+
next_market_close = (
188+
datetime.datetime.fromtimestamp(next_market_open)
189+
.astimezone(NY_TZ)
190+
.replace(
191+
hour=NYSE_EARLY_CLOSE.hour,
192+
minute=NYSE_EARLY_CLOSE.minute,
193+
second=0,
194+
microsecond=0,
195+
)
196+
)
197+
else:
198+
next_market_close = (
199+
datetime.datetime.fromtimestamp(next_market_open)
200+
.astimezone(NY_TZ)
201+
.replace(
202+
hour=EQUITY_CLOSE.hour,
203+
minute=EQUITY_CLOSE.minute,
204+
second=0,
205+
microsecond=0,
206+
)
207+
)
160208
else:
161209
next_market_close = dt.replace(
162210
hour=EQUITY_CLOSE.hour,
@@ -167,9 +215,9 @@ def get_next_market_close(asset_type: str, dt: datetime.datetime) -> int:
167215
if time >= EQUITY_CLOSE:
168216
next_market_close += datetime.timedelta(days=1)
169217

170-
# while next_market_close.date() is in EQUITY_HOLIDAYS or weekend, add 1 day
218+
# while next_market_close.date() is in NYSE_HOLIDAYS or weekend, add 1 day
171219
while (
172-
next_market_close.date() in EQUITY_HOLIDAYS
220+
next_market_close.date() in NYSE_HOLIDAYS
173221
or next_market_close.weekday() >= 5
174222
):
175223
next_market_close += datetime.timedelta(days=1)
@@ -185,6 +233,68 @@ def get_next_market_close(asset_type: str, dt: datetime.datetime) -> int:
185233
next_market_close += datetime.timedelta(days=1)
186234
while is_market_open(asset_type, next_market_close):
187235
next_market_close += datetime.timedelta(days=1)
236+
elif asset_type == "rates":
237+
if dt.date() in NYSE_EARLY_HOLIDAYS:
238+
if time < NYSE_EARLY_CLOSE:
239+
next_market_close = dt.replace(
240+
hour=NYSE_EARLY_CLOSE.hour,
241+
minute=NYSE_EARLY_CLOSE.minute,
242+
second=0,
243+
microsecond=0,
244+
)
245+
else:
246+
next_market_close = dt.replace(
247+
hour=RATES_CLOSE.hour,
248+
minute=RATES_CLOSE.minute,
249+
second=0,
250+
microsecond=0,
251+
)
252+
next_market_close += datetime.timedelta(days=1)
253+
elif dt.date() in NYSE_HOLIDAYS:
254+
next_market_open = get_next_market_open(asset_type, dt)
255+
next_market_open_date = (
256+
datetime.datetime.fromtimestamp(next_market_open)
257+
.astimezone(NY_TZ)
258+
.date()
259+
)
260+
if next_market_open_date in NYSE_EARLY_HOLIDAYS:
261+
next_market_close = (
262+
datetime.datetime.fromtimestamp(next_market_open)
263+
.astimezone(NY_TZ)
264+
.replace(
265+
hour=NYSE_EARLY_CLOSE.hour,
266+
minute=NYSE_EARLY_CLOSE.minute,
267+
second=0,
268+
microsecond=0,
269+
)
270+
)
271+
else:
272+
next_market_close = (
273+
datetime.datetime.fromtimestamp(next_market_open)
274+
.astimezone(NY_TZ)
275+
.replace(
276+
hour=RATES_CLOSE.hour,
277+
minute=RATES_CLOSE.minute,
278+
second=0,
279+
microsecond=0,
280+
)
281+
)
282+
else:
283+
next_market_close = dt.replace(
284+
hour=RATES_CLOSE.hour,
285+
minute=RATES_CLOSE.minute,
286+
second=0,
287+
microsecond=0,
288+
)
289+
if time >= RATES_CLOSE:
290+
next_market_close += datetime.timedelta(days=1)
291+
292+
# while next_market_close.date() is in NYSE_HOLIDAYS or weekend, add 1 day
293+
while (
294+
next_market_close.date() in NYSE_HOLIDAYS
295+
or next_market_close.weekday() >= 5
296+
):
297+
next_market_close += datetime.timedelta(days=1)
188298
else: # crypto markets never close
189299
return None
190300

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.15',
10+
version='0.1.16',
1111
packages=['pythclient'],
1212
author='Pyth Developers',
1313
author_email='[email protected]',

0 commit comments

Comments
 (0)