6
6
7
7
EQUITY_OPEN = datetime .time (9 , 30 , 0 , tzinfo = NY_TZ )
8
8
EQUITY_CLOSE = datetime .time (16 , 0 , 0 , tzinfo = NY_TZ )
9
- EQUITY_EARLY_CLOSE = datetime .time (13 , 0 , 0 , tzinfo = NY_TZ )
10
9
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
12
13
# From https://www.nyse.com/markets/hours-calendars
13
- EQUITY_HOLIDAYS = [
14
+ NYSE_HOLIDAYS = [
14
15
datetime .datetime (2023 , 1 , 2 , tzinfo = NY_TZ ).date (),
15
16
datetime .datetime (2023 , 1 , 16 , tzinfo = NY_TZ ).date (),
16
17
datetime .datetime (2023 , 2 , 20 , tzinfo = NY_TZ ).date (),
22
23
datetime .datetime (2023 , 11 , 23 , tzinfo = NY_TZ ).date (),
23
24
datetime .datetime (2023 , 12 , 25 , tzinfo = NY_TZ ).date (),
24
25
]
25
- EQUITY_EARLY_HOLIDAYS = [
26
+ NYSE_EARLY_HOLIDAYS = [
26
27
datetime .datetime (2023 , 7 , 3 , tzinfo = NY_TZ ).date (),
27
28
datetime .datetime (2023 , 11 , 24 , tzinfo = NY_TZ ).date (),
28
29
]
36
37
datetime .datetime (2023 , 12 , 25 , tzinfo = NY_TZ ).date (),
37
38
]
38
39
40
+ RATES_OPEN = datetime .time (8 , 0 , 0 , tzinfo = NY_TZ )
41
+ RATES_CLOSE = datetime .time (17 , 0 , 0 , tzinfo = NY_TZ )
42
+
39
43
40
44
def is_market_open (asset_type : str , dt : datetime .datetime ) -> bool :
41
45
# make sure time is in NY timezone
42
46
dt = dt .astimezone (NY_TZ )
43
47
day , date , time = dt .weekday (), dt .date (), dt .time ()
44
48
45
49
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 :
47
51
if (
48
- date in EQUITY_EARLY_HOLIDAYS
52
+ date in NYSE_EARLY_HOLIDAYS
49
53
and time >= EQUITY_OPEN
50
- and time < EQUITY_EARLY_CLOSE
54
+ and time < NYSE_EARLY_CLOSE
51
55
):
52
56
return True
53
57
return False
@@ -70,6 +74,19 @@ def is_market_open(asset_type: str, dt: datetime.datetime) -> bool:
70
74
71
75
return True
72
76
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
+
73
90
# all other markets (crypto)
74
91
return True
75
92
@@ -112,6 +129,22 @@ def get_next_market_open(asset_type: str, dt: datetime.datetime) -> int:
112
129
)
113
130
while is_market_open (asset_type , next_market_open ):
114
131
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 )
115
148
else :
116
149
return None
117
150
@@ -127,11 +160,11 @@ def get_next_market_close(asset_type: str, dt: datetime.datetime) -> int:
127
160
time = dt .time ()
128
161
129
162
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 :
132
165
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 ,
135
168
second = 0 ,
136
169
microsecond = 0 ,
137
170
)
@@ -143,20 +176,35 @@ def get_next_market_close(asset_type: str, dt: datetime.datetime) -> int:
143
176
microsecond = 0 ,
144
177
)
145
178
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 = (
151
182
datetime .datetime .fromtimestamp (next_market_open )
152
183
.astimezone (NY_TZ )
153
- .replace (
154
- hour = EQUITY_CLOSE .hour ,
155
- minute = EQUITY_CLOSE .minute ,
156
- second = 0 ,
157
- microsecond = 0 ,
158
- )
184
+ .date ()
159
185
)
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
+ )
160
208
else :
161
209
next_market_close = dt .replace (
162
210
hour = EQUITY_CLOSE .hour ,
@@ -167,9 +215,9 @@ def get_next_market_close(asset_type: str, dt: datetime.datetime) -> int:
167
215
if time >= EQUITY_CLOSE :
168
216
next_market_close += datetime .timedelta (days = 1 )
169
217
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
171
219
while (
172
- next_market_close .date () in EQUITY_HOLIDAYS
220
+ next_market_close .date () in NYSE_HOLIDAYS
173
221
or next_market_close .weekday () >= 5
174
222
):
175
223
next_market_close += datetime .timedelta (days = 1 )
@@ -185,6 +233,68 @@ def get_next_market_close(asset_type: str, dt: datetime.datetime) -> int:
185
233
next_market_close += datetime .timedelta (days = 1 )
186
234
while is_market_open (asset_type , next_market_close ):
187
235
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 )
188
298
else : # crypto markets never close
189
299
return None
190
300
0 commit comments