@@ -47,19 +47,19 @@ def is_market_open(asset_type: str, dt: datetime.datetime) -> bool:
4747 if (
4848 date in EQUITY_EARLY_HOLIDAYS
4949 and time >= EQUITY_OPEN
50- and time <= EQUITY_EARLY_CLOSE
50+ and time < EQUITY_EARLY_CLOSE
5151 ):
5252 return True
5353 return False
54- if day < 5 and time >= EQUITY_OPEN and time <= EQUITY_CLOSE :
54+ if day < 5 and time >= EQUITY_OPEN and time < EQUITY_CLOSE :
5555 return True
5656 return False
5757
5858 if asset_type in ["fx" , "metal" ]:
5959 if date in FX_METAL_HOLIDAYS :
6060 return False
6161 # On Friday the market is closed after 5pm
62- if day == 4 and time > FX_METAL_OPEN_CLOSE_TIME :
62+ if day == 4 and time >= FX_METAL_OPEN_CLOSE_TIME :
6363 return False
6464 # On Saturday the market is closed all the time
6565 if day == 5 :
@@ -79,9 +79,6 @@ def get_next_market_open(asset_type: str, dt: datetime.datetime) -> str:
7979 dt = dt .astimezone (NY_TZ )
8080 time = dt .time ()
8181
82- if is_market_open (asset_type , dt ):
83- return dt .astimezone (UTC_TZ ).strftime ("%Y-%m-%dT%H:%M:%S" ) + "Z"
84-
8582 if asset_type == "equity" :
8683 if time < EQUITY_OPEN :
8784 next_market_open = dt .replace (
@@ -113,30 +110,53 @@ def get_next_market_open(asset_type: str, dt: datetime.datetime) -> str:
113110 second = 0 ,
114111 microsecond = 0 ,
115112 )
116- next_market_open += datetime .timedelta (days = 1 )
113+ while is_market_open (asset_type , next_market_open ):
114+ next_market_open += datetime .timedelta (days = 1 )
115+
117116 else :
118- next_market_open = dt .replace (hour = 0 , minute = 0 , second = 0 , microsecond = 0 )
119- next_market_open += datetime .timedelta (days = 1 )
117+ return None
120118
121119 while not is_market_open (asset_type , next_market_open ):
122120 next_market_open += datetime .timedelta (days = 1 )
123121
124122 return next_market_open .astimezone (UTC_TZ ).strftime ("%Y-%m-%dT%H:%M:%S" ) + "Z"
125123
124+
126125def get_next_market_close (asset_type : str , dt : datetime .datetime ) -> str :
127126 # make sure time is in NY timezone
128127 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"
128+ time = dt .time ()
131129
132130 if asset_type == "equity" :
133131 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 ,
132+ if time < EQUITY_EARLY_CLOSE :
133+ next_market_close = dt .replace (
134+ hour = EQUITY_EARLY_CLOSE .hour ,
135+ minute = EQUITY_EARLY_CLOSE .minute ,
136+ second = 0 ,
137+ microsecond = 0 ,
138+ )
139+ else :
140+ next_market_close = dt .replace (
141+ hour = EQUITY_CLOSE .hour ,
142+ minute = EQUITY_CLOSE .minute ,
143+ second = 0 ,
144+ microsecond = 0 ,
145+ )
146+ next_market_close += datetime .timedelta (days = 1 )
147+ elif dt .date () in EQUITY_HOLIDAYS :
148+ next_market_open = get_next_market_open (
149+ asset_type , dt + datetime .timedelta (days = 1 )
150+ )
151+ next_market_close = (
152+ datetime .datetime .fromisoformat (next_market_open .replace ("Z" , "+00:00" ))
153+ .astimezone (NY_TZ )
154+ .replace (
155+ hour = EQUITY_CLOSE .hour ,
156+ minute = EQUITY_CLOSE .minute ,
157+ second = 0 ,
158+ microsecond = 0 ,
159+ )
140160 )
141161 else :
142162 next_market_close = dt .replace (
@@ -145,14 +165,28 @@ def get_next_market_close(asset_type: str, dt: datetime.datetime) -> str:
145165 second = 0 ,
146166 microsecond = 0 ,
147167 )
168+ if time >= EQUITY_CLOSE :
169+ next_market_close += datetime .timedelta (days = 1 )
170+
171+ # while next_market_close.date() is in EQUITY_HOLIDAYS or weekend, add 1 day
172+ while (
173+ next_market_close .date () in EQUITY_HOLIDAYS
174+ or next_market_close .weekday () >= 5
175+ ):
176+ next_market_close += datetime .timedelta (days = 1 )
177+
148178 elif asset_type in ["fx" , "metal" ]:
149179 next_market_close = dt .replace (
150180 hour = FX_METAL_OPEN_CLOSE_TIME .hour ,
151181 minute = FX_METAL_OPEN_CLOSE_TIME .minute ,
152182 second = 0 ,
153183 microsecond = 0 ,
154184 )
155- else : # crypto markets never close
185+ while not is_market_open (asset_type , next_market_close ):
186+ next_market_close += datetime .timedelta (days = 1 )
187+ while is_market_open (asset_type , next_market_close ):
188+ next_market_close += datetime .timedelta (days = 1 )
189+ else : # crypto markets never close
156190 return None
157191
158192 return next_market_close .astimezone (UTC_TZ ).strftime ("%Y-%m-%dT%H:%M:%S" ) + "Z"
0 commit comments