From 156be81b145755eef5503a19d13ae3ec263925df Mon Sep 17 00:00:00 2001 From: jfkirk Date: Wed, 25 May 2016 11:37:19 -0400 Subject: [PATCH] ENH: Adds BMF, LSE, TSX to get_calendar --- zipline/utils/calendars/exchange_calendar.py | 20 +- zipline/utils/tradingcalendar_bmf.py | 315 ---------------- zipline/utils/tradingcalendar_lse.py | 186 ---------- zipline/utils/tradingcalendar_tse.py | 364 ------------------- 4 files changed, 19 insertions(+), 866 deletions(-) delete mode 100644 zipline/utils/tradingcalendar_bmf.py delete mode 100644 zipline/utils/tradingcalendar_lse.py delete mode 100644 zipline/utils/tradingcalendar_tse.py diff --git a/zipline/utils/calendars/exchange_calendar.py b/zipline/utils/calendars/exchange_calendar.py index 495e97fb..86907686 100644 --- a/zipline/utils/calendars/exchange_calendar.py +++ b/zipline/utils/calendars/exchange_calendar.py @@ -462,7 +462,7 @@ class ExchangeCalendar(with_metaclass(ABCMeta)): _static_calendars = {} -_lazy_calendar_names = ['NYSE', 'CME'] +_lazy_calendar_names = ['NYSE', 'CME', 'BMF', 'LSE', 'TSX'] def get_calendar(name): @@ -493,6 +493,24 @@ def get_calendar(name): cme_cal = CMEExchangeCalendar() register_calendar(cme_cal) + if name == 'BMF': + from zipline.utils.calendars.exchange_calendar_bmf \ + import BMFExchangeCalendar + bmf_cal = BMFExchangeCalendar() + register_calendar(bmf_cal) + + if name == 'LSE': + from zipline.utils.calendars.exchange_calendar_lse \ + import LSEExchangeCalendar + lse_cal = LSEExchangeCalendar() + register_calendar(lse_cal) + + if name == 'TSX': + from zipline.utils.calendars.exchange_calendar_tsx \ + import TSXExchangeCalendar + tsx_cal = TSXExchangeCalendar() + register_calendar(tsx_cal) + return _static_calendars[name] diff --git a/zipline/utils/tradingcalendar_bmf.py b/zipline/utils/tradingcalendar_bmf.py deleted file mode 100644 index c78959d6..00000000 --- a/zipline/utils/tradingcalendar_bmf.py +++ /dev/null @@ -1,315 +0,0 @@ -# -# Copyright 2014 Quantopian, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pandas as pd -import pytz - -from datetime import datetime -from dateutil import rrule -from zipline.utils.tradingcalendar import end, canonicalize_datetime, \ - get_open_and_closes - -start = pd.Timestamp('1994-01-01', tz='UTC') - - -def get_non_trading_days(start, end): - non_trading_rules = [] - - start = canonicalize_datetime(start) - end = canonicalize_datetime(end) - - weekends = rrule.rrule( - rrule.YEARLY, - byweekday=(rrule.SA, rrule.SU), - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(weekends) - - # Universal confraternization - conf_universal = rrule.rrule( - rrule.MONTHLY, - byyearday=1, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(conf_universal) - - # Sao Paulo city birthday - aniversario_sao_paulo = rrule.rrule( - rrule.MONTHLY, - bymonth=1, - bymonthday=25, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(aniversario_sao_paulo) - - # Carnival Monday - carnaval_segunda = rrule.rrule( - rrule.MONTHLY, - byeaster=-48, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(carnaval_segunda) - - # Carnival Tuesday - carnaval_terca = rrule.rrule( - rrule.MONTHLY, - byeaster=-47, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(carnaval_terca) - - # Passion of the Christ - sexta_paixao = rrule.rrule( - rrule.MONTHLY, - byeaster=-2, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(sexta_paixao) - - # Corpus Christi - corpus_christi = rrule.rrule( - rrule.MONTHLY, - byeaster=60, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(corpus_christi) - - tiradentes = rrule.rrule( - rrule.MONTHLY, - bymonth=4, - bymonthday=21, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(tiradentes) - - # Labor day - dia_trabalho = rrule.rrule( - rrule.MONTHLY, - bymonth=5, - bymonthday=1, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(dia_trabalho) - - # Constitutionalist Revolution - constitucionalista = rrule.rrule( - rrule.MONTHLY, - bymonth=7, - bymonthday=9, - cache=True, - dtstart=datetime(1997, 1, 1, tzinfo=pytz.utc), - until=end - ) - non_trading_rules.append(constitucionalista) - - # Independency day - independencia = rrule.rrule( - rrule.MONTHLY, - bymonth=9, - bymonthday=7, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(independencia) - - # Our Lady of Aparecida - aparecida = rrule.rrule( - rrule.MONTHLY, - bymonth=10, - bymonthday=12, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(aparecida) - - # All Souls' day - finados = rrule.rrule( - rrule.MONTHLY, - bymonth=11, - bymonthday=2, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(finados) - - # Proclamation of the Republic - proclamacao_republica = rrule.rrule( - rrule.MONTHLY, - bymonth=11, - bymonthday=15, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(proclamacao_republica) - - # Day of Black Awareness - consciencia_negra = rrule.rrule( - rrule.MONTHLY, - bymonth=11, - bymonthday=20, - cache=True, - dtstart=datetime(2004, 1, 1, tzinfo=pytz.utc), - until=end - ) - non_trading_rules.append(consciencia_negra) - - # Christmas Eve - vespera_natal = rrule.rrule( - rrule.MONTHLY, - bymonth=12, - bymonthday=24, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(vespera_natal) - - # Christmas - natal = rrule.rrule( - rrule.MONTHLY, - bymonth=12, - bymonthday=25, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(natal) - - # New Year Eve - ano_novo = rrule.rrule( - rrule.MONTHLY, - bymonth=12, - bymonthday=31, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(ano_novo) - - # New Year Eve on saturday - ano_novo_sab = rrule.rrule( - rrule.MONTHLY, - bymonth=12, - bymonthday=30, - byweekday=rrule.FR, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(ano_novo_sab) - - non_trading_ruleset = rrule.rruleset() - - for rule in non_trading_rules: - non_trading_ruleset.rrule(rule) - - non_trading_days = non_trading_ruleset.between(start, end, inc=True) - - # World Cup 2014 Opening - non_trading_days.append(datetime(2014, 6, 12, tzinfo=pytz.utc)) - - non_trading_days.sort() - return pd.DatetimeIndex(non_trading_days) - -non_trading_days = get_non_trading_days(start, end) -trading_day = pd.tseries.offsets.CDay(holidays=non_trading_days) - - -def get_trading_days(start, end, trading_day=trading_day): - return pd.date_range(start=start.date(), - end=end.date(), - freq=trading_day).tz_localize('UTC') - -trading_days = get_trading_days(start, end) - - -# Ash Wednesday -quarta_cinzas = rrule.rrule( - rrule.MONTHLY, - byeaster=-46, - cache=True, - dtstart=start, - until=end -) - - -def get_early_closes(start, end): - # TSX closed at 1:00 PM on december 24th. - - start = canonicalize_datetime(start) - end = canonicalize_datetime(end) - - early_close_rules = [] - - early_close_rules.append(quarta_cinzas) - - early_close_ruleset = rrule.rruleset() - - for rule in early_close_rules: - early_close_ruleset.rrule(rule) - early_closes = early_close_ruleset.between(start, end, inc=True) - - early_closes.sort() - return pd.DatetimeIndex(early_closes) - -early_closes = get_early_closes(start, end) - - -def get_open_and_close(day, early_closes): - # only "early close" event in Bovespa actually is a late start - # as the market only opens at 1pm - open_hour = 13 if day in quarta_cinzas else 10 - market_open = pd.Timestamp( - datetime( - year=day.year, - month=day.month, - day=day.day, - hour=open_hour, - minute=00), - tz='America/Sao_Paulo').tz_convert('UTC') - market_close = pd.Timestamp( - datetime( - year=day.year, - month=day.month, - day=day.day, - hour=16), - tz='America/Sao_Paulo').tz_convert('UTC') - - return market_open, market_close - -open_and_closes = get_open_and_closes(trading_days, early_closes, - get_open_and_close) diff --git a/zipline/utils/tradingcalendar_lse.py b/zipline/utils/tradingcalendar_lse.py deleted file mode 100644 index c22054b2..00000000 --- a/zipline/utils/tradingcalendar_lse.py +++ /dev/null @@ -1,186 +0,0 @@ -# -# Copyright 2013 Quantopian, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -# References: -# http://www.londonstockexchange.com -# /about-the-exchange/company-overview/business-days/business-days.htm -# http://en.wikipedia.org/wiki/Bank_holiday -# http://www.adviceguide.org.uk/england/work_e/work_time_off_work_e/ -# bank_and_public_holidays.htm - -import pytz - -import pandas as pd - -from datetime import datetime -from dateutil import rrule -from zipline.utils.tradingcalendar import end - -start = datetime(2002, 1, 1, tzinfo=pytz.utc) - -non_trading_rules = [] -# Weekends -weekends = rrule.rrule( - rrule.YEARLY, - byweekday=(rrule.SA, rrule.SU), - cache=True, - dtstart=start, - until=end -) -non_trading_rules.append(weekends) -# New Year's Day -new_year = rrule.rrule( - rrule.MONTHLY, - byyearday=1, - cache=True, - dtstart=start, - until=end -) -# If new years day is on Saturday then Monday 3rd is a holiday -# If new years day is on Sunday then Monday 2nd is a holiday -weekend_new_year = rrule.rrule( - rrule.MONTHLY, - bymonth=1, - bymonthday=[2, 3], - byweekday=(rrule.MO), - cache=True, - dtstart=start, - until=end -) -non_trading_rules.append(new_year) -non_trading_rules.append(weekend_new_year) -# Good Friday -good_friday = rrule.rrule( - rrule.DAILY, - byeaster=-2, - cache=True, - dtstart=start, - until=end -) -non_trading_rules.append(good_friday) -# Easter Monday -easter_monday = rrule.rrule( - rrule.DAILY, - byeaster=1, - cache=True, - dtstart=start, - until=end -) -non_trading_rules.append(easter_monday) -# Early May Bank Holiday (1st Monday in May) -may_bank = rrule.rrule( - rrule.MONTHLY, - bymonth=5, - byweekday=(rrule.MO(1)), - cache=True, - dtstart=start, - until=end -) -non_trading_rules.append(may_bank) -# Spring Bank Holiday (Last Monday in May) -spring_bank = rrule.rrule( - rrule.MONTHLY, - bymonth=5, - byweekday=(rrule.MO(-1)), - cache=True, - dtstart=datetime(2003, 1, 1, tzinfo=pytz.utc), - until=end -) -non_trading_rules.append(spring_bank) -# Summer Bank Holiday (Last Monday in August) -summer_bank = rrule.rrule( - rrule.MONTHLY, - bymonth=8, - byweekday=(rrule.MO(-1)), - cache=True, - dtstart=start, - until=end -) -non_trading_rules.append(summer_bank) -# Christmas Day -christmas = rrule.rrule( - rrule.MONTHLY, - bymonth=12, - bymonthday=25, - cache=True, - dtstart=start, - until=end -) -# If christmas day is Saturday Monday 27th is a holiday -# If christmas day is sunday the Tuesday 27th is a holiday -weekend_christmas = rrule.rrule( - rrule.MONTHLY, - bymonth=12, - bymonthday=27, - byweekday=(rrule.MO, rrule.TU), - cache=True, - dtstart=start, - until=end -) - -non_trading_rules.append(christmas) -non_trading_rules.append(weekend_christmas) -# Boxing Day -boxing_day = rrule.rrule( - rrule.MONTHLY, - bymonth=12, - bymonthday=26, - cache=True, - dtstart=start, - until=end -) -# If boxing day is saturday then Monday 28th is a holiday -# If boxing day is sunday then Tuesday 28th is a holiday -weekend_boxing_day = rrule.rrule( - rrule.MONTHLY, - bymonth=12, - bymonthday=28, - byweekday=(rrule.MO, rrule.TU), - cache=True, - dtstart=start, - until=end -) - -non_trading_rules.append(boxing_day) -non_trading_rules.append(weekend_boxing_day) - -non_trading_ruleset = rrule.rruleset() - -# In 2002 May bank holiday was moved to 4th June to follow the Queens -# Golden Jubilee -non_trading_ruleset.exdate(datetime(2002, 9, 27, tzinfo=pytz.utc)) -non_trading_ruleset.rdate(datetime(2002, 6, 3, tzinfo=pytz.utc)) -non_trading_ruleset.rdate(datetime(2002, 6, 4, tzinfo=pytz.utc)) -# TODO: not sure why Feb 18 2008 is not available in the yahoo data -non_trading_ruleset.rdate(datetime(2008, 2, 18, tzinfo=pytz.utc)) -# In 2011 The Friday before Mayday was the Royal Wedding -non_trading_ruleset.rdate(datetime(2011, 4, 29, tzinfo=pytz.utc)) -# In 2012 May bank holiday was moved to 4th June to preceed the Queens -# Diamond Jubilee -non_trading_ruleset.exdate(datetime(2012, 5, 28, tzinfo=pytz.utc)) -non_trading_ruleset.rdate(datetime(2012, 6, 4, tzinfo=pytz.utc)) -non_trading_ruleset.rdate(datetime(2012, 6, 5, tzinfo=pytz.utc)) - -for rule in non_trading_rules: - non_trading_ruleset.rrule(rule) - -non_trading_days = non_trading_ruleset.between(start, end, inc=True) -non_trading_day_index = pd.DatetimeIndex(sorted(non_trading_days)) - -business_days = pd.DatetimeIndex(start=start, end=end, - freq=pd.datetools.BDay()) - -trading_days = business_days.difference(non_trading_day_index) diff --git a/zipline/utils/tradingcalendar_tse.py b/zipline/utils/tradingcalendar_tse.py deleted file mode 100644 index 46c8b839..00000000 --- a/zipline/utils/tradingcalendar_tse.py +++ /dev/null @@ -1,364 +0,0 @@ -# -# Copyright 2014 Quantopian, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import pandas as pd -import pytz - -from datetime import datetime -from dateutil import rrule -from zipline.utils.tradingcalendar import end, canonicalize_datetime, \ - get_open_and_closes - -start = pd.Timestamp('1994-01-01', tz='UTC') - - -def get_non_trading_days(start, end): - non_trading_rules = [] - - start = canonicalize_datetime(start) - end = canonicalize_datetime(end) - - weekends = rrule.rrule( - rrule.YEARLY, - byweekday=(rrule.SA, rrule.SU), - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(weekends) - - new_years = rrule.rrule( - rrule.MONTHLY, - byyearday=1, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(new_years) - - new_years_sunday = rrule.rrule( - rrule.MONTHLY, - byyearday=2, - byweekday=rrule.MO, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(new_years_sunday) - - new_years_saturday = rrule.rrule( - rrule.MONTHLY, - byyearday=3, - byweekday=rrule.MO, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(new_years_saturday) - - # Family day in Ontario, starting in 2008, third monday of February - family_day = rrule.rrule( - rrule.MONTHLY, - bymonth=2, - byweekday=(rrule.MO(3)), - cache=True, - dtstart=datetime(2008, 1, 1, tzinfo=pytz.utc), - until=end - ) - non_trading_rules.append(family_day) - - good_friday = rrule.rrule( - rrule.DAILY, - byeaster=-2, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(good_friday) - - # Monday prior to May 25th. - victoria_day = rrule.rrule( - rrule.MONTHLY, - bymonth=5, - byweekday=rrule.MO, - bymonthday=[24, 23, 22, 21, 20, 19, 18], - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(victoria_day) - - july_1st = rrule.rrule( - rrule.MONTHLY, - bymonth=7, - bymonthday=1, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(july_1st) - - july_1st_sunday = rrule.rrule( - rrule.MONTHLY, - bymonth=7, - bymonthday=2, - byweekday=rrule.MO, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(july_1st_sunday) - - july_1st_saturday = rrule.rrule( - rrule.MONTHLY, - bymonth=7, - bymonthday=3, - byweekday=rrule.MO, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(july_1st_saturday) - - civic_holiday = rrule.rrule( - rrule.MONTHLY, - bymonth=8, - byweekday=rrule.MO(1), - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(civic_holiday) - - labor_day = rrule.rrule( - rrule.MONTHLY, - bymonth=9, - byweekday=(rrule.MO(1)), - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(labor_day) - - thanksgiving = rrule.rrule( - rrule.MONTHLY, - bymonth=10, - byweekday=(rrule.MO(2)), - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(thanksgiving) - - christmas = rrule.rrule( - rrule.MONTHLY, - bymonth=12, - bymonthday=25, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(christmas) - - # If Christmas is a Sunday then the 26th, a Monday is observed. - # (but that would be boxing day), so the 27th is also observed. - christmas_sunday = rrule.rrule( - rrule.MONTHLY, - bymonth=12, - bymonthday=27, - byweekday=rrule.TU, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(christmas_sunday) - - # If Christmas is a Saturday then the 27th, a monday is observed. - christmas_saturday = rrule.rrule( - rrule.MONTHLY, - bymonth=12, - bymonthday=27, - byweekday=rrule.MO, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(christmas_saturday) - - boxing_day = rrule.rrule( - rrule.MONTHLY, - bymonth=12, - bymonthday=26, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(boxing_day) - - # if boxing day is a sunday, the Christmas was saturday. - # Christmas is observed on the 27th, a month and boxing day is observed - # on the 28th, a tuesday. - boxing_day_sunday = rrule.rrule( - rrule.MONTHLY, - bymonth=12, - bymonthday=28, - byweekday=rrule.TU, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(boxing_day_sunday) - - # If boxing day is a Saturday then the 28th, a monday is observed. - boxing_day_saturday = rrule.rrule( - rrule.MONTHLY, - bymonth=12, - bymonthday=28, - byweekday=rrule.MO, - cache=True, - dtstart=start, - until=end - ) - non_trading_rules.append(boxing_day_saturday) - - non_trading_ruleset = rrule.rruleset() - - for rule in non_trading_rules: - non_trading_ruleset.rrule(rule) - - non_trading_days = non_trading_ruleset.between(start, end, inc=True) - - # Add September 11th closings - # The TSX was open for 71 minutes on September 11, 2011. - # It was closed on the 12th and reopened on the 13th. - # http://www.cbc.ca/news2/interactives/map-tsx/ - # - # September 2001 - # Su Mo Tu We Th Fr Sa - # 1 - # 2 3 4 5 6 7 8 - # 9 10 11 12 13 14 15 - # 16 17 18 19 20 21 22 - # 23 24 25 26 27 28 29 - # 30 - - non_trading_days.append( - datetime(2001, 9, 12, tzinfo=pytz.utc)) - - non_trading_days.sort() - return pd.DatetimeIndex(non_trading_days) - -non_trading_days = get_non_trading_days(start, end) -trading_day = pd.tseries.offsets.CDay(holidays=non_trading_days) - - -def get_trading_days(start, end, trading_day=trading_day): - return pd.date_range(start=start.date(), - end=end.date(), - freq=trading_day).tz_localize('UTC') - -trading_days = get_trading_days(start, end) - -# Days in Environment but not in Calendar (using ^GSPTSE as bm_symbol): -# -------------------------------------------------------------------- -# Used http://web.tmxmoney.com/pricehistory.php?qm_page=61468&qm_symbol=^TSX -# to check whether exchange was open on these days. -# 1994-07-01 - July 1st, Yahoo Finance has Volume = 0 -# 1996-07-01 - July 1st, Yahoo Finance has Volume = 0 -# 1996-08-05 - Civic Holiday, Yahoo Finance has Volume = 0 -# 1997-07-01 - July 1st, Yahoo Finance has Volume = 0 -# 1997-08-04 - Civic Holiday, Yahoo Finance has Volume = 0 -# 2001-05-21 - Victoria day, Yahoo Finance has Volume = 0 -# 2004-10-11 - Closed, Thanksgiving - Confirmed closed -# 2004-12-28 - Closed, Boxing Day - Confirmed closed -# 2012-10-08 - Closed, Thanksgiving - Confirmed closed - -# Days in Calendar but not in Environment using ^GSPTSE as bm_symbol: -# -------------------------------------------------------------------- -# Used http://web.tmxmoney.com/pricehistory.php?qm_page=61468&qm_symbol=^TSX -# to check whether exchange was open on these days. -# 2000-06-28 - No data this far back, can't confirm -# 2000-08-28 - No data this far back, can't confirm -# 2000-08-29 - No data this far back, can't confirm -# 2001-09-11 - TSE Open for 71 min. -# 2002-02-01 - Confirm TSE Open -# 2002-06-14 - Confirm TSE Open -# 2002-07-02 - Confirm TSE Open -# 2002-11-11 - TSX website has no data for 2 weeks in 2002 -# 2003-07-07 - Confirm TSE Open -# 2003-12-16 - Confirm TSE Open - - -def get_early_closes(start, end): - # TSX closed at 1:00 PM on december 24th. - - start = canonicalize_datetime(start) - end = canonicalize_datetime(end) - - start = max(start, datetime(1993, 1, 1, tzinfo=pytz.utc)) - end = max(end, datetime(1993, 1, 1, tzinfo=pytz.utc)) - - # Not included here are early closes prior to 1993 - # or unplanned early closes - - early_close_rules = [] - - christmas_eve = rrule.rrule( - rrule.MONTHLY, - bymonth=12, - bymonthday=24, - byweekday=(rrule.MO, rrule.TU, rrule.WE, rrule.TH, rrule.FR), - cache=True, - dtstart=start, - until=end - ) - early_close_rules.append(christmas_eve) - - early_close_ruleset = rrule.rruleset() - - for rule in early_close_rules: - early_close_ruleset.rrule(rule) - early_closes = early_close_ruleset.between(start, end, inc=True) - - early_closes.sort() - return pd.DatetimeIndex(early_closes) - -early_closes = get_early_closes(start, end) - - -def get_open_and_close(day, early_closes): - market_open = pd.Timestamp( - datetime( - year=day.year, - month=day.month, - day=day.day, - hour=9, - minute=31), - tz='US/Eastern').tz_convert('UTC') - # 1 PM if early close, 4 PM otherwise - close_hour = 13 if day in early_closes else 16 - market_close = pd.Timestamp( - datetime( - year=day.year, - month=day.month, - day=day.day, - hour=close_hour), - tz='US/Eastern').tz_convert('UTC') - - return market_open, market_close - -open_and_closes = get_open_and_closes(trading_days, early_closes, - get_open_and_close)