mirror of
https://github.com/wassname/catalyst.git
synced 2026-06-28 17:00:51 +08:00
Merge pull request #1335 from quantopian/more-calendars
ENH: ICE calendar, more calendar tests, cleaned up CME calendar
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
from unittest import TestCase
|
||||
import pandas as pd
|
||||
|
||||
from tests.calendars.test_trading_calendar import ExchangeCalendarTestBase
|
||||
from zipline.utils.calendars.exchange_calendar_cfe import CFEExchangeCalendar
|
||||
|
||||
|
||||
class CFECalendarTestCase(ExchangeCalendarTestBase, TestCase):
|
||||
answer_key_filename = "cfe"
|
||||
calendar_class = CFEExchangeCalendar
|
||||
|
||||
MAX_SESSION_HOURS = 8
|
||||
|
||||
def test_2016_holidays(self):
|
||||
# new years: jan 1
|
||||
# mlk: jan 18
|
||||
# presidents: feb 15
|
||||
# good friday: mar 25
|
||||
# mem day: may 30
|
||||
# independence day: july 4
|
||||
# labor day: sep 5
|
||||
# thanksgiving day: nov 24
|
||||
# christmas (observed): dec 26
|
||||
# new years (observed): jan 2 2017
|
||||
for day in ["2016-01-01", "2016-01-18", "2016-02-15", "2016-03-25",
|
||||
"2016-05-30", "2016-07-04", "2016-09-05", "2016-11-24",
|
||||
"2016-12-26", "2017-01-02"]:
|
||||
self.assertFalse(
|
||||
self.calendar.is_session(pd.Timestamp(day, tz='UTC'))
|
||||
)
|
||||
|
||||
def test_2016_early_closes(self):
|
||||
# only early close is day after thanksgiving: nov 25
|
||||
dt = pd.Timestamp("2016-11-25", tz='UTC')
|
||||
self.assertTrue(dt in self.calendar.early_closes)
|
||||
|
||||
market_close = self.calendar.schedule.loc[dt].market_close
|
||||
market_close = market_close.tz_localize("UTC").tz_convert(
|
||||
self.calendar.tz
|
||||
)
|
||||
self.assertEqual(12, market_close.hour)
|
||||
self.assertEqual(15, market_close.minute)
|
||||
@@ -0,0 +1,41 @@
|
||||
from unittest import TestCase
|
||||
import pandas as pd
|
||||
|
||||
from .test_trading_calendar import ExchangeCalendarTestBase
|
||||
from zipline.utils.calendars.exchange_calendar_cme import CMEExchangeCalendar
|
||||
|
||||
|
||||
class CMECalendarTestCase(ExchangeCalendarTestBase, TestCase):
|
||||
answer_key_filename = "cme"
|
||||
calendar_class = CMEExchangeCalendar
|
||||
GAPS_BETWEEN_SESSIONS = False
|
||||
MAX_SESSION_HOURS = 24
|
||||
|
||||
def test_2016_holidays(self):
|
||||
# good friday: 2016-03-25
|
||||
# christmas (observed)_: 2016-12-26
|
||||
# new years (observed): 2016-01-02
|
||||
for date in ["2016-03-25", "2016-12-26", "2016-01-02"]:
|
||||
self.assertFalse(
|
||||
self.calendar.is_session(pd.Timestamp(date, tz='UTC'))
|
||||
)
|
||||
|
||||
def test_2016_early_closes(self):
|
||||
# mlk day: 2016-01-18
|
||||
# presidents: 2016-02-15
|
||||
# mem day: 2016-05-30
|
||||
# july 4: 2016-07-04
|
||||
# labor day: 2016-09-05
|
||||
# thankgiving: 2016-11-24
|
||||
for date in ["2016-01-18", "2016-02-15", "2016-05-30", "2016-07-04",
|
||||
"2016-09-05", "2016-11-24"]:
|
||||
dt = pd.Timestamp(date, tz='UTC')
|
||||
self.assertTrue(dt in self.calendar.early_closes)
|
||||
|
||||
market_close = self.calendar.schedule.loc[dt].market_close
|
||||
self.assertEqual(
|
||||
12,
|
||||
market_close.tz_localize('UTC').tz_convert(
|
||||
self.calendar.tz
|
||||
).hour
|
||||
)
|
||||
@@ -0,0 +1,54 @@
|
||||
from unittest import TestCase
|
||||
import pandas as pd
|
||||
|
||||
from .test_trading_calendar import ExchangeCalendarTestBase
|
||||
from zipline.utils.calendars.exchange_calendar_ice import ICEExchangeCalendar
|
||||
|
||||
|
||||
class ICECalendarTestCase(ExchangeCalendarTestBase, TestCase):
|
||||
|
||||
answer_key_filename = 'ice'
|
||||
calendar_class = ICEExchangeCalendar
|
||||
MAX_SESSION_HOURS = 22
|
||||
|
||||
def test_hurricane_sandy_one_day(self):
|
||||
self.assertFalse(
|
||||
self.calendar.is_session(pd.Timestamp("2012-10-29", tz='UTC'))
|
||||
)
|
||||
|
||||
# ICE wasn't closed on day 2 of hurricane sandy
|
||||
self.assertTrue(
|
||||
self.calendar.is_session(pd.Timestamp("2012-10-30", tz='UTC'))
|
||||
)
|
||||
|
||||
def test_2016_holidays(self):
|
||||
# 2016 holidays:
|
||||
# new years: 2016-01-01
|
||||
# good friday: 2016-03-25
|
||||
# christmas (observed): 2016-12-26
|
||||
|
||||
for date in ["2016-01-01", "2016-03-25", "2016-12-26"]:
|
||||
self.assertFalse(
|
||||
self.calendar.is_session(pd.Timestamp(date, tz='UTC'))
|
||||
)
|
||||
|
||||
def test_2016_early_closes(self):
|
||||
# 2016 early closes
|
||||
# mlk: 2016-01-18
|
||||
# presidents: 2016-02-15
|
||||
# mem day: 2016-05-30
|
||||
# independence day: 2016-07-04
|
||||
# labor: 2016-09-05
|
||||
# thanksgiving: 2016-11-24
|
||||
for date in ["2016-01-18", "2016-02-15", "2016-05-30", "2016-07-04",
|
||||
"2016-09-05", "2016-11-24"]:
|
||||
dt = pd.Timestamp(date, tz='UTC')
|
||||
self.assertTrue(dt in self.calendar.early_closes)
|
||||
|
||||
market_close = self.calendar.schedule.loc[dt].market_close
|
||||
self.assertEqual(
|
||||
13, # all ICE early closes are 1 pm local
|
||||
market_close.tz_localize("UTC").tz_convert(
|
||||
self.calendar.tz
|
||||
).hour
|
||||
)
|
||||
@@ -0,0 +1,228 @@
|
||||
from unittest import TestCase
|
||||
import pandas as pd
|
||||
|
||||
from tests.calendars.test_trading_calendar import ExchangeCalendarTestBase
|
||||
from zipline.utils.calendars.exchange_calendar_nyse import NYSEExchangeCalendar
|
||||
|
||||
|
||||
class NYSECalendarTestCase(ExchangeCalendarTestBase, TestCase):
|
||||
|
||||
answer_key_filename = 'nyse'
|
||||
calendar_class = NYSEExchangeCalendar
|
||||
|
||||
MAX_SESSION_HOURS = 6.5
|
||||
|
||||
def test_2012(self):
|
||||
# holidays we expect:
|
||||
holidays_2012 = [
|
||||
pd.Timestamp("2012-01-02", tz='UTC'),
|
||||
pd.Timestamp("2012-01-16", tz='UTC'),
|
||||
pd.Timestamp("2012-02-20", tz='UTC'),
|
||||
pd.Timestamp("2012-04-06", tz='UTC'),
|
||||
pd.Timestamp("2012-05-28", tz='UTC'),
|
||||
pd.Timestamp("2012-07-04", tz='UTC'),
|
||||
pd.Timestamp("2012-09-03", tz='UTC'),
|
||||
pd.Timestamp("2012-11-22", tz='UTC'),
|
||||
pd.Timestamp("2012-12-25", tz='UTC')
|
||||
]
|
||||
|
||||
for session_label in holidays_2012:
|
||||
self.assertNotIn(session_label, self.calendar.all_sessions)
|
||||
|
||||
# early closes we expect:
|
||||
early_closes_2012 = [
|
||||
pd.Timestamp("2012-07-03", tz='UTC'),
|
||||
pd.Timestamp("2012-11-23", tz='UTC'),
|
||||
pd.Timestamp("2012-12-24", tz='UTC')
|
||||
]
|
||||
|
||||
for early_close_session_label in early_closes_2012:
|
||||
self.assertIn(early_close_session_label,
|
||||
self.calendar.early_closes)
|
||||
|
||||
def test_special_holidays(self):
|
||||
# 9/11
|
||||
# Sept 11, 12, 13, 14 2001
|
||||
self.assertNotIn(pd.Period("9/11/2001"), self.calendar.all_sessions)
|
||||
self.assertNotIn(pd.Period("9/12/2001"), self.calendar.all_sessions)
|
||||
self.assertNotIn(pd.Period("9/13/2001"), self.calendar.all_sessions)
|
||||
self.assertNotIn(pd.Period("9/14/2001"), self.calendar.all_sessions)
|
||||
|
||||
# Hurricane Sandy
|
||||
# Oct 29, 30 2012
|
||||
self.assertNotIn(pd.Period("10/29/2012"), self.calendar.all_sessions)
|
||||
self.assertNotIn(pd.Period("10/30/2012"), self.calendar.all_sessions)
|
||||
|
||||
# various national days of mourning
|
||||
# Gerald Ford - 1/2/2007
|
||||
self.assertNotIn(pd.Period("1/2/2007"), self.calendar.all_sessions)
|
||||
|
||||
# Ronald Reagan - 6/11/2004
|
||||
self.assertNotIn(pd.Period("6/11/2004"), self.calendar.all_sessions)
|
||||
|
||||
# Richard Nixon - 4/27/1994
|
||||
self.assertNotIn(pd.Period("4/27/1994"), self.calendar.all_sessions)
|
||||
|
||||
def test_new_years(self):
|
||||
"""
|
||||
Check whether the TradingCalendar contains certain dates.
|
||||
"""
|
||||
# January 2012
|
||||
# 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 31
|
||||
|
||||
start_session = pd.Timestamp("2012-01-02", tz='UTC')
|
||||
end_session = pd.Timestamp("2013-12-31", tz='UTC')
|
||||
sessions = self.calendar.sessions_in_range(start_session, end_session)
|
||||
|
||||
day_after_new_years_sunday = pd.Timestamp("2012-01-02",
|
||||
tz='UTC')
|
||||
self.assertNotIn(day_after_new_years_sunday, sessions,
|
||||
"""
|
||||
If NYE falls on a weekend, {0} the Monday after is a holiday.
|
||||
""".strip().format(day_after_new_years_sunday)
|
||||
)
|
||||
|
||||
first_trading_day_after_new_years_sunday = pd.Timestamp("2012-01-03",
|
||||
tz='UTC')
|
||||
self.assertIn(first_trading_day_after_new_years_sunday, sessions,
|
||||
"""
|
||||
If NYE falls on a weekend, {0} the Tuesday after is the first trading day.
|
||||
""".strip().format(first_trading_day_after_new_years_sunday)
|
||||
)
|
||||
|
||||
# January 2013
|
||||
# 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 31
|
||||
|
||||
new_years_day = pd.Timestamp("2013-01-01", tz='UTC')
|
||||
self.assertNotIn(new_years_day, sessions,
|
||||
"""
|
||||
If NYE falls during the week, e.g. {0}, it is a holiday.
|
||||
""".strip().format(new_years_day)
|
||||
)
|
||||
|
||||
first_trading_day_after_new_years = pd.Timestamp("2013-01-02",
|
||||
tz='UTC')
|
||||
self.assertIn(first_trading_day_after_new_years, sessions,
|
||||
"""
|
||||
If the day after NYE falls during the week, {0} \
|
||||
is the first trading day.
|
||||
""".strip().format(first_trading_day_after_new_years)
|
||||
)
|
||||
|
||||
def test_thanksgiving(self):
|
||||
"""
|
||||
Check TradingCalendar Thanksgiving dates.
|
||||
"""
|
||||
# November 2005
|
||||
# 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
|
||||
|
||||
start_session_label = pd.Timestamp('2005-01-01', tz='UTC')
|
||||
end_session_label = pd.Timestamp('2012-12-31', tz='UTC')
|
||||
sessions = self.calendar.sessions_in_range(start_session_label,
|
||||
end_session_label)
|
||||
|
||||
thanksgiving_with_four_weeks = pd.Timestamp("2005-11-24", tz='UTC')
|
||||
|
||||
self.assertNotIn(thanksgiving_with_four_weeks, sessions,
|
||||
"""
|
||||
If Nov has 4 Thursdays, {0} Thanksgiving is the last Thursday.
|
||||
""".strip().format(thanksgiving_with_four_weeks)
|
||||
)
|
||||
|
||||
# November 2006
|
||||
# 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
|
||||
thanksgiving_with_five_weeks = pd.Timestamp("2006-11-23", tz='UTC')
|
||||
|
||||
self.assertNotIn(thanksgiving_with_five_weeks, sessions,
|
||||
"""
|
||||
If Nov has 5 Thursdays, {0} Thanksgiving is not the last week.
|
||||
""".strip().format(thanksgiving_with_five_weeks)
|
||||
)
|
||||
|
||||
first_trading_day_after_new_years_sunday = pd.Timestamp("2012-01-03",
|
||||
tz='UTC')
|
||||
|
||||
self.assertIn(first_trading_day_after_new_years_sunday, sessions,
|
||||
"""
|
||||
If NYE falls on a weekend, {0} the Tuesday after is the first trading day.
|
||||
""".strip().format(first_trading_day_after_new_years_sunday)
|
||||
)
|
||||
|
||||
def test_day_after_thanksgiving(self):
|
||||
# November 2012
|
||||
# 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
|
||||
fourth_friday_open = pd.Timestamp('11/23/2012 11:00AM', tz='EST')
|
||||
fourth_friday = pd.Timestamp('11/23/2012 3:00PM', tz='EST')
|
||||
self.assertTrue(self.calendar.is_open_on_minute(fourth_friday_open))
|
||||
self.assertFalse(self.calendar.is_open_on_minute(fourth_friday))
|
||||
|
||||
# November 2013
|
||||
# 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
|
||||
fifth_friday_open = pd.Timestamp('11/29/2013 11:00AM', tz='EST')
|
||||
fifth_friday = pd.Timestamp('11/29/2013 3:00PM', tz='EST')
|
||||
self.assertTrue(self.calendar.is_open_on_minute(fifth_friday_open))
|
||||
self.assertFalse(self.calendar.is_open_on_minute(fifth_friday))
|
||||
|
||||
def test_early_close_independence_day_thursday(self):
|
||||
"""
|
||||
Until 2013, the market closed early the Friday after an
|
||||
Independence Day on Thursday. Since then, the early close is on
|
||||
Wednesday.
|
||||
"""
|
||||
# July 2002
|
||||
# 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 31
|
||||
wednesday_before = pd.Timestamp('7/3/2002 3:00PM', tz='EST')
|
||||
friday_after_open = pd.Timestamp('7/5/2002 11:00AM', tz='EST')
|
||||
friday_after = pd.Timestamp('7/5/2002 3:00PM', tz='EST')
|
||||
self.assertTrue(self.calendar.is_open_on_minute(wednesday_before))
|
||||
self.assertTrue(self.calendar.is_open_on_minute(friday_after_open))
|
||||
self.assertFalse(self.calendar.is_open_on_minute(friday_after))
|
||||
|
||||
# July 2013
|
||||
# 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 31
|
||||
wednesday_before = pd.Timestamp('7/3/2013 3:00PM', tz='EST')
|
||||
friday_after_open = pd.Timestamp('7/5/2013 11:00AM', tz='EST')
|
||||
friday_after = pd.Timestamp('7/5/2013 3:00PM', tz='EST')
|
||||
self.assertFalse(self.calendar.is_open_on_minute(wednesday_before))
|
||||
self.assertTrue(self.calendar.is_open_on_minute(friday_after_open))
|
||||
self.assertTrue(self.calendar.is_open_on_minute(friday_after))
|
||||
@@ -12,7 +12,6 @@
|
||||
# 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.
|
||||
|
||||
from datetime import time
|
||||
from os.path import (
|
||||
abspath,
|
||||
@@ -25,24 +24,23 @@ from collections import namedtuple
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from nose_parameterized import parameterized
|
||||
from pandas import (
|
||||
read_csv,
|
||||
Timestamp,
|
||||
)
|
||||
from pandas import read_csv
|
||||
from pandas.tslib import Timedelta
|
||||
from pandas.util.testing import assert_index_equal
|
||||
from pytz import timezone
|
||||
|
||||
from zipline.errors import (
|
||||
CalendarNameCollision,
|
||||
InvalidCalendarName,
|
||||
)
|
||||
from zipline.utils.calendars.exchange_calendar_nyse import NYSEExchangeCalendar
|
||||
from zipline.utils.calendars.trading_calendar import days_at_time
|
||||
|
||||
from zipline.utils.calendars import(
|
||||
register_calendar,
|
||||
deregister_calendar,
|
||||
get_calendar,
|
||||
clear_calendars,
|
||||
)
|
||||
from zipline.utils.calendars.trading_calendar import days_at_time
|
||||
|
||||
|
||||
class CalendarRegistrationTestCase(TestCase):
|
||||
@@ -121,6 +119,10 @@ class ExchangeCalendarTestBase(object):
|
||||
answer_key_filename = None
|
||||
calendar_class = None
|
||||
|
||||
GAPS_BETWEEN_SESSIONS = True
|
||||
|
||||
MAX_SESSION_HOURS = 0
|
||||
|
||||
@staticmethod
|
||||
def load_answer_key(filename):
|
||||
"""
|
||||
@@ -128,7 +130,7 @@ class ExchangeCalendarTestBase(object):
|
||||
"""
|
||||
fullpath = join(
|
||||
dirname(abspath(__file__)),
|
||||
'resources',
|
||||
'../resources',
|
||||
'calendars',
|
||||
filename + '.csv',
|
||||
)
|
||||
@@ -154,6 +156,13 @@ class ExchangeCalendarTestBase(object):
|
||||
cls.one_minute = pd.Timedelta(minutes=1)
|
||||
cls.one_hour = pd.Timedelta(hours=1)
|
||||
|
||||
def test_sanity_check_session_lengths(self):
|
||||
# make sure that no session is longer than self.MAX_SESSION_HOURS hours
|
||||
for session in self.calendar.all_sessions:
|
||||
o, c = self.calendar.open_and_close_for_session(session)
|
||||
delta = c - o
|
||||
self.assertTrue((delta.seconds / 3600) <= self.MAX_SESSION_HOURS)
|
||||
|
||||
def test_calculated_against_csv(self):
|
||||
assert_index_equal(self.calendar.schedule.index, self.answers.index)
|
||||
|
||||
@@ -165,18 +174,21 @@ class ExchangeCalendarTestBase(object):
|
||||
# The exchange should be classified as open on its first minute
|
||||
self.assertTrue(self.calendar.is_open_on_minute(market_minute_utc))
|
||||
|
||||
# Decrement minute by one, to minute where the market was not open
|
||||
pre_market = market_minute_utc - one_minute
|
||||
self.assertFalse(self.calendar.is_open_on_minute(pre_market))
|
||||
if self.GAPS_BETWEEN_SESSIONS:
|
||||
# Decrement minute by one, to minute where the market was not
|
||||
# open
|
||||
pre_market = market_minute_utc - one_minute
|
||||
self.assertFalse(self.calendar.is_open_on_minute(pre_market))
|
||||
|
||||
for market_minute in self.answers.market_close:
|
||||
close_minute_utc = market_minute
|
||||
# should be open on its last minute
|
||||
self.assertTrue(self.calendar.is_open_on_minute(close_minute_utc))
|
||||
|
||||
# increment minute by one minute, should be closed
|
||||
post_market = close_minute_utc + one_minute
|
||||
self.assertFalse(self.calendar.is_open_on_minute(post_market))
|
||||
if self.GAPS_BETWEEN_SESSIONS:
|
||||
# increment minute by one minute, should be closed
|
||||
post_market = close_minute_utc + one_minute
|
||||
self.assertFalse(self.calendar.is_open_on_minute(post_market))
|
||||
|
||||
def _verify_minute(self, calendar, minute,
|
||||
next_open_answer, prev_open_answer,
|
||||
@@ -203,12 +215,12 @@ class ExchangeCalendarTestBase(object):
|
||||
|
||||
def test_next_prev_open_close(self):
|
||||
# for each session, check:
|
||||
# - the minute before the open
|
||||
# - the minute before the open (if gaps exist between sessions)
|
||||
# - the first minute of the session
|
||||
# - the second minute of the session
|
||||
# - the minute before the close
|
||||
# - the last minute of the session
|
||||
# - the first minute after the close
|
||||
# - the first minute after the close (if gaps exist between sessions)
|
||||
answers_to_use = self.answers[1:-2]
|
||||
|
||||
for idx, info in enumerate(answers_to_use.iterrows()):
|
||||
@@ -227,10 +239,11 @@ class ExchangeCalendarTestBase(object):
|
||||
next_close = self.answers.iloc[idx + 2].market_close
|
||||
|
||||
# minute before open
|
||||
self._verify_minute(
|
||||
self.calendar, minute_before_open, open_minute, previous_open,
|
||||
close_minute, previous_close
|
||||
)
|
||||
if self.GAPS_BETWEEN_SESSIONS:
|
||||
self._verify_minute(
|
||||
self.calendar, minute_before_open, open_minute,
|
||||
previous_open, close_minute, previous_close
|
||||
)
|
||||
|
||||
# open minute
|
||||
self._verify_minute(
|
||||
@@ -257,10 +270,11 @@ class ExchangeCalendarTestBase(object):
|
||||
)
|
||||
|
||||
# minute after the close
|
||||
self._verify_minute(
|
||||
self.calendar, close_minute + self.one_minute, next_open,
|
||||
open_minute, next_close, close_minute
|
||||
)
|
||||
if self.GAPS_BETWEEN_SESSIONS:
|
||||
self._verify_minute(
|
||||
self.calendar, close_minute + self.one_minute, next_open,
|
||||
open_minute, next_close, close_minute
|
||||
)
|
||||
|
||||
def test_next_prev_minute(self):
|
||||
all_minutes = self.calendar.all_minutes
|
||||
@@ -278,19 +292,20 @@ class ExchangeCalendarTestBase(object):
|
||||
)
|
||||
|
||||
# test a couple of non-market minutes
|
||||
for open_minute in self.answers.market_open[1:]:
|
||||
hour_before_open = open_minute - self.one_hour
|
||||
self.assertEqual(
|
||||
open_minute,
|
||||
self.calendar.next_minute(hour_before_open)
|
||||
)
|
||||
if self.GAPS_BETWEEN_SESSIONS:
|
||||
for open_minute in self.answers.market_open[1:]:
|
||||
hour_before_open = open_minute - self.one_hour
|
||||
self.assertEqual(
|
||||
open_minute,
|
||||
self.calendar.next_minute(hour_before_open)
|
||||
)
|
||||
|
||||
for close_minute in self.answers.market_close[1:]:
|
||||
hour_after_close = close_minute + self.one_hour
|
||||
self.assertEqual(
|
||||
close_minute,
|
||||
self.calendar.previous_minute(hour_after_close)
|
||||
)
|
||||
for close_minute in self.answers.market_close[1:]:
|
||||
hour_after_close = close_minute + self.one_hour
|
||||
self.assertEqual(
|
||||
close_minute,
|
||||
self.calendar.previous_minute(hour_after_close)
|
||||
)
|
||||
|
||||
def test_minute_to_session_label(self):
|
||||
for idx, info in enumerate(self.answers[1:-2].iterrows()):
|
||||
@@ -328,18 +343,29 @@ class ExchangeCalendarTestBase(object):
|
||||
direction="previous"),
|
||||
self.calendar.minute_to_session_label(close_minute,
|
||||
direction="none"),
|
||||
self.calendar.minute_to_session_label(minute_before_session),
|
||||
self.calendar.minute_to_session_label(
|
||||
minute_before_session,
|
||||
direction="next"
|
||||
),
|
||||
self.calendar.minute_to_session_label(
|
||||
minute_after_session,
|
||||
direction="previous"
|
||||
),
|
||||
session_label
|
||||
]
|
||||
|
||||
if self.GAPS_BETWEEN_SESSIONS:
|
||||
minutes_that_resolve_to_this_session.append(
|
||||
self.calendar.minute_to_session_label(
|
||||
minute_before_session
|
||||
)
|
||||
)
|
||||
minutes_that_resolve_to_this_session.append(
|
||||
self.calendar.minute_to_session_label(
|
||||
minute_before_session,
|
||||
direction="next"
|
||||
)
|
||||
)
|
||||
|
||||
minutes_that_resolve_to_this_session.append(
|
||||
self.calendar.minute_to_session_label(
|
||||
minute_after_session,
|
||||
direction="previous"
|
||||
)
|
||||
)
|
||||
|
||||
self.assertTrue(all(x == minutes_that_resolve_to_this_session[0]
|
||||
for x in minutes_that_resolve_to_this_session))
|
||||
|
||||
@@ -363,9 +389,12 @@ class ExchangeCalendarTestBase(object):
|
||||
with self.assertRaises(ValueError):
|
||||
self.calendar.minute_to_session_label(open_minute, "asdf")
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
self.calendar.minute_to_session_label(minute_before_session,
|
||||
direction="none")
|
||||
if self.GAPS_BETWEEN_SESSIONS:
|
||||
with self.assertRaises(ValueError):
|
||||
self.calendar.minute_to_session_label(
|
||||
minute_before_session,
|
||||
direction="none"
|
||||
)
|
||||
|
||||
def test_next_prev_session(self):
|
||||
session_labels = self.answers.index[1:-2]
|
||||
@@ -494,7 +523,11 @@ class ExchangeCalendarTestBase(object):
|
||||
minute_after_last_close
|
||||
)
|
||||
|
||||
np.testing.assert_array_equal(minutes1, minutes2)
|
||||
if self.GAPS_BETWEEN_SESSIONS:
|
||||
np.testing.assert_array_equal(minutes1, minutes2)
|
||||
else:
|
||||
# if no gaps, then minutes2 should have 2 extra minutes
|
||||
np.testing.assert_array_equal(minutes1, minutes2[1:-1])
|
||||
|
||||
# manually construct the minutes
|
||||
all_minutes = np.concatenate([
|
||||
@@ -574,223 +607,34 @@ class ExchangeCalendarTestBase(object):
|
||||
self.assertEqual(open_answer, found_open)
|
||||
self.assertEqual(close_answer, found_close)
|
||||
|
||||
def test_daylight_savings(self):
|
||||
# 2004 daylight savings switches:
|
||||
# Sunday 2004-04-04 and Sunday 2004-10-31
|
||||
|
||||
class NYSECalendarTestCase(ExchangeCalendarTestBase, TestCase):
|
||||
# make sure there's no weirdness around calculating the next day's
|
||||
# session's open time.
|
||||
|
||||
answer_key_filename = 'nyse'
|
||||
calendar_class = NYSEExchangeCalendar
|
||||
for date in ["2004-04-05", "2004-11-01"]:
|
||||
next_day = pd.Timestamp(date, tz='UTC')
|
||||
open_date = next_day + Timedelta(days=self.calendar.open_offset)
|
||||
|
||||
def test_2012(self):
|
||||
# holidays we expect:
|
||||
holidays_2012 = [
|
||||
pd.Timestamp("2012-01-02", tz='UTC'),
|
||||
pd.Timestamp("2012-01-16", tz='UTC'),
|
||||
pd.Timestamp("2012-02-20", tz='UTC'),
|
||||
pd.Timestamp("2012-04-06", tz='UTC'),
|
||||
pd.Timestamp("2012-05-28", tz='UTC'),
|
||||
pd.Timestamp("2012-07-04", tz='UTC'),
|
||||
pd.Timestamp("2012-09-03", tz='UTC'),
|
||||
pd.Timestamp("2012-11-22", tz='UTC'),
|
||||
pd.Timestamp("2012-12-25", tz='UTC')
|
||||
]
|
||||
the_open = self.calendar.schedule.loc[next_day].market_open
|
||||
|
||||
for session_label in holidays_2012:
|
||||
self.assertNotIn(session_label, self.calendar.all_sessions)
|
||||
localized_open = the_open.tz_localize("UTC").tz_convert(
|
||||
self.calendar.tz
|
||||
)
|
||||
|
||||
# early closes we expect:
|
||||
early_closes_2012 = [
|
||||
pd.Timestamp("2012-07-03", tz='UTC'),
|
||||
pd.Timestamp("2012-11-23", tz='UTC'),
|
||||
pd.Timestamp("2012-12-24", tz='UTC')
|
||||
]
|
||||
self.assertEqual(
|
||||
(open_date.year, open_date.month, open_date.day),
|
||||
(localized_open.year, localized_open.month, localized_open.day)
|
||||
)
|
||||
|
||||
for early_close_session_label in early_closes_2012:
|
||||
self.assertIn(early_close_session_label,
|
||||
self.calendar.early_closes)
|
||||
self.assertEqual(
|
||||
self.calendar.open_time.hour,
|
||||
localized_open.hour
|
||||
)
|
||||
|
||||
def test_special_holidays(self):
|
||||
# 9/11
|
||||
# Sept 11, 12, 13, 14 2001
|
||||
self.assertNotIn(pd.Period("9/11/2001"), self.calendar.all_sessions)
|
||||
self.assertNotIn(pd.Period("9/12/2001"), self.calendar.all_sessions)
|
||||
self.assertNotIn(pd.Period("9/13/2001"), self.calendar.all_sessions)
|
||||
self.assertNotIn(pd.Period("9/14/2001"), self.calendar.all_sessions)
|
||||
|
||||
# Hurricane Sandy
|
||||
# Oct 29, 30 2012
|
||||
self.assertNotIn(pd.Period("10/29/2012"), self.calendar.all_sessions)
|
||||
self.assertNotIn(pd.Period("10/30/2012"), self.calendar.all_sessions)
|
||||
|
||||
# various national days of mourning
|
||||
# Gerald Ford - 1/2/2007
|
||||
self.assertNotIn(pd.Period("1/2/2007"), self.calendar.all_sessions)
|
||||
|
||||
# Ronald Reagan - 6/11/2004
|
||||
self.assertNotIn(pd.Period("6/11/2004"), self.calendar.all_sessions)
|
||||
|
||||
# Richard Nixon - 4/27/1994
|
||||
self.assertNotIn(pd.Period("4/27/1994"), self.calendar.all_sessions)
|
||||
|
||||
def test_new_years(self):
|
||||
"""
|
||||
Check whether the TradingCalendar contains certain dates.
|
||||
"""
|
||||
# January 2012
|
||||
# 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 31
|
||||
|
||||
start_session = pd.Timestamp("2012-01-02", tz='UTC')
|
||||
end_session = pd.Timestamp("2013-12-31", tz='UTC')
|
||||
sessions = self.calendar.sessions_in_range(start_session, end_session)
|
||||
|
||||
day_after_new_years_sunday = pd.Timestamp("2012-01-02",
|
||||
tz='UTC')
|
||||
self.assertNotIn(day_after_new_years_sunday, sessions,
|
||||
"""
|
||||
If NYE falls on a weekend, {0} the Monday after is a holiday.
|
||||
""".strip().format(day_after_new_years_sunday)
|
||||
)
|
||||
|
||||
first_trading_day_after_new_years_sunday = pd.Timestamp("2012-01-03",
|
||||
tz='UTC')
|
||||
self.assertIn(first_trading_day_after_new_years_sunday, sessions,
|
||||
"""
|
||||
If NYE falls on a weekend, {0} the Tuesday after is the first trading day.
|
||||
""".strip().format(first_trading_day_after_new_years_sunday)
|
||||
)
|
||||
|
||||
# January 2013
|
||||
# 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 31
|
||||
|
||||
new_years_day = pd.Timestamp("2013-01-01", tz='UTC')
|
||||
self.assertNotIn(new_years_day, sessions,
|
||||
"""
|
||||
If NYE falls during the week, e.g. {0}, it is a holiday.
|
||||
""".strip().format(new_years_day)
|
||||
)
|
||||
|
||||
first_trading_day_after_new_years = pd.Timestamp("2013-01-02",
|
||||
tz='UTC')
|
||||
self.assertIn(first_trading_day_after_new_years, sessions,
|
||||
"""
|
||||
If the day after NYE falls during the week, {0} \
|
||||
is the first trading day.
|
||||
""".strip().format(first_trading_day_after_new_years)
|
||||
)
|
||||
|
||||
def test_thanksgiving(self):
|
||||
"""
|
||||
Check TradingCalendar Thanksgiving dates.
|
||||
"""
|
||||
# November 2005
|
||||
# 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
|
||||
|
||||
start_session_label = pd.Timestamp('2005-01-01', tz='UTC')
|
||||
end_session_label = pd.Timestamp('2012-12-31', tz='UTC')
|
||||
sessions = self.calendar.sessions_in_range(start_session_label,
|
||||
end_session_label)
|
||||
|
||||
thanksgiving_with_four_weeks = pd.Timestamp("2005-11-24", tz='UTC')
|
||||
|
||||
self.assertNotIn(thanksgiving_with_four_weeks, sessions,
|
||||
"""
|
||||
If Nov has 4 Thursdays, {0} Thanksgiving is the last Thursday.
|
||||
""".strip().format(thanksgiving_with_four_weeks)
|
||||
)
|
||||
|
||||
# November 2006
|
||||
# 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
|
||||
thanksgiving_with_five_weeks = pd.Timestamp("2006-11-23", tz='UTC')
|
||||
|
||||
self.assertNotIn(thanksgiving_with_five_weeks, sessions,
|
||||
"""
|
||||
If Nov has 5 Thursdays, {0} Thanksgiving is not the last week.
|
||||
""".strip().format(thanksgiving_with_five_weeks)
|
||||
)
|
||||
|
||||
first_trading_day_after_new_years_sunday = pd.Timestamp("2012-01-03",
|
||||
tz='UTC')
|
||||
|
||||
self.assertIn(first_trading_day_after_new_years_sunday, sessions,
|
||||
"""
|
||||
If NYE falls on a weekend, {0} the Tuesday after is the first trading day.
|
||||
""".strip().format(first_trading_day_after_new_years_sunday)
|
||||
)
|
||||
|
||||
def test_day_after_thanksgiving(self):
|
||||
# November 2012
|
||||
# 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
|
||||
fourth_friday_open = Timestamp('11/23/2012 11:00AM', tz='EST')
|
||||
fourth_friday = Timestamp('11/23/2012 3:00PM', tz='EST')
|
||||
self.assertTrue(self.calendar.is_open_on_minute(fourth_friday_open))
|
||||
self.assertFalse(self.calendar.is_open_on_minute(fourth_friday))
|
||||
|
||||
# November 2013
|
||||
# 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
|
||||
fifth_friday_open = Timestamp('11/29/2013 11:00AM', tz='EST')
|
||||
fifth_friday = Timestamp('11/29/2013 3:00PM', tz='EST')
|
||||
self.assertTrue(self.calendar.is_open_on_minute(fifth_friday_open))
|
||||
self.assertFalse(self.calendar.is_open_on_minute(fifth_friday))
|
||||
|
||||
def test_early_close_independence_day_thursday(self):
|
||||
"""
|
||||
Until 2013, the market closed early the Friday after an
|
||||
Independence Day on Thursday. Since then, the early close is on
|
||||
Wednesday.
|
||||
"""
|
||||
# July 2002
|
||||
# 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 31
|
||||
wednesday_before = Timestamp('7/3/2002 3:00PM', tz='EST')
|
||||
friday_after_open = Timestamp('7/5/2002 11:00AM', tz='EST')
|
||||
friday_after = Timestamp('7/5/2002 3:00PM', tz='EST')
|
||||
self.assertTrue(self.calendar.is_open_on_minute(wednesday_before))
|
||||
self.assertTrue(self.calendar.is_open_on_minute(friday_after_open))
|
||||
self.assertFalse(self.calendar.is_open_on_minute(friday_after))
|
||||
|
||||
# July 2013
|
||||
# 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 31
|
||||
wednesday_before = Timestamp('7/3/2013 3:00PM', tz='EST')
|
||||
friday_after_open = Timestamp('7/5/2013 11:00AM', tz='EST')
|
||||
friday_after = Timestamp('7/5/2013 3:00PM', tz='EST')
|
||||
self.assertFalse(self.calendar.is_open_on_minute(wednesday_before))
|
||||
self.assertTrue(self.calendar.is_open_on_minute(friday_after_open))
|
||||
self.assertTrue(self.calendar.is_open_on_minute(friday_after))
|
||||
self.assertEqual(
|
||||
self.calendar.open_time.minute,
|
||||
localized_open.minute
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,8 @@ from zipline.errors import (
|
||||
InvalidCalendarName,
|
||||
CalendarNameCollision,
|
||||
)
|
||||
|
||||
from zipline.utils.calendars.exchange_calendar_cfe import CFEExchangeCalendar
|
||||
from zipline.utils.calendars.exchange_calendar_ice import ICEExchangeCalendar
|
||||
from zipline.utils.calendars.exchange_calendar_nyse import NYSEExchangeCalendar
|
||||
from zipline.utils.calendars.exchange_calendar_cme import CMEExchangeCalendar
|
||||
from zipline.utils.calendars.exchange_calendar_bmf import BMFExchangeCalendar
|
||||
@@ -37,6 +38,10 @@ def get_calendar(name):
|
||||
cal = LSEExchangeCalendar()
|
||||
elif name == 'TSX':
|
||||
cal = TSXExchangeCalendar()
|
||||
elif name == "ICE":
|
||||
cal = ICEExchangeCalendar()
|
||||
elif name == "CFE":
|
||||
cal = CFEExchangeCalendar()
|
||||
else:
|
||||
raise InvalidCalendarName(calendar_name=name)
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ from pytz import timezone
|
||||
|
||||
from .trading_calendar import (
|
||||
TradingCalendar,
|
||||
FRIDAY
|
||||
)
|
||||
FRIDAY,
|
||||
HolidayCalendar)
|
||||
|
||||
# Universal Confraternization (new years day)
|
||||
ConfUniversal = Holiday(
|
||||
@@ -196,23 +196,47 @@ class BMFExchangeCalendar(TradingCalendar):
|
||||
- New Year's Eve (December 31)
|
||||
"""
|
||||
|
||||
name = "BMF"
|
||||
tz = timezone('America/Sao_Paulo')
|
||||
open_time = time(10, 1)
|
||||
close_time = time(17)
|
||||
@property
|
||||
def name(self):
|
||||
return "BMF"
|
||||
|
||||
# Does the market open or close on a different calendar day, compared to
|
||||
# the calendar day assigned by the exchange to this session?
|
||||
open_offset = 0
|
||||
close_offset = 0
|
||||
@property
|
||||
def tz(self):
|
||||
return timezone("America/Sao_Paolo")
|
||||
|
||||
holidays_calendar = BMFHolidayCalendar()
|
||||
special_opens_calendars = [
|
||||
(time(13, 1), BMFLateOpenCalendar()),
|
||||
]
|
||||
special_closes_calendars = ()
|
||||
@property
|
||||
def open_time(self):
|
||||
return time(10, 1)
|
||||
|
||||
holidays_adhoc = ()
|
||||
@property
|
||||
def close_time(self):
|
||||
return time(16)
|
||||
|
||||
special_opens_adhoc = ()
|
||||
special_closes_adhoc = ()
|
||||
@property
|
||||
def regular_holidays(self):
|
||||
return HolidayCalendar([
|
||||
ConfUniversal,
|
||||
AniversarioSaoPaulo,
|
||||
CarnavalSegunda,
|
||||
CarnavalTerca,
|
||||
SextaPaixao,
|
||||
CorpusChristi,
|
||||
Tiradentes,
|
||||
DiaTrabalho,
|
||||
Constitucionalista,
|
||||
Independencia,
|
||||
Aparecida,
|
||||
Finados,
|
||||
ProclamacaoRepublica,
|
||||
ConscienciaNegra,
|
||||
VesperaNatal,
|
||||
Natal,
|
||||
AnoNovo,
|
||||
AnoNovoSabado,
|
||||
])
|
||||
|
||||
@property
|
||||
def special_opens(self):
|
||||
return [
|
||||
(time(13, 1), HolidayCalendar([QuartaCinzas]))
|
||||
]
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
from datetime import time
|
||||
|
||||
from pandas.tseries.holiday import (
|
||||
USPresidentsDay,
|
||||
USLaborDay,
|
||||
USThanksgivingDay,
|
||||
GoodFriday
|
||||
)
|
||||
from pytz import timezone
|
||||
|
||||
from zipline.utils.calendars import TradingCalendar
|
||||
from zipline.utils.calendars.trading_calendar import HolidayCalendar
|
||||
from zipline.utils.calendars.us_holidays import (
|
||||
USMartinLutherKingJrAfter1998,
|
||||
USMemorialDay,
|
||||
USBlackFridayInOrAfter1993,
|
||||
USNewYearsDay,
|
||||
USIndependenceDay,
|
||||
Christmas
|
||||
)
|
||||
|
||||
|
||||
class CFEExchangeCalendar(TradingCalendar):
|
||||
"""
|
||||
Exchange calendar for the CBOE Futures Exchange (CFE).
|
||||
|
||||
http://cfe.cboe.com/aboutcfe/expirationcalendar.aspx
|
||||
|
||||
Open Time: 8:30am, America/Chicago
|
||||
Close Time: 3:15pm, America/Chicago
|
||||
|
||||
(We are ignoring extended trading hours for now)
|
||||
"""
|
||||
@property
|
||||
def name(self):
|
||||
return "CFE"
|
||||
|
||||
@property
|
||||
def tz(self):
|
||||
return timezone("America/Chicago")
|
||||
|
||||
@property
|
||||
def open_time(self):
|
||||
return time(8, 31)
|
||||
|
||||
@property
|
||||
def close_time(self):
|
||||
return time(15, 15)
|
||||
|
||||
@property
|
||||
def regular_holidays(self):
|
||||
return HolidayCalendar([
|
||||
USNewYearsDay,
|
||||
USMartinLutherKingJrAfter1998,
|
||||
USPresidentsDay,
|
||||
GoodFriday,
|
||||
USIndependenceDay,
|
||||
USMemorialDay,
|
||||
USLaborDay,
|
||||
USThanksgivingDay,
|
||||
Christmas
|
||||
])
|
||||
|
||||
@property
|
||||
def special_closes(self):
|
||||
return [(
|
||||
time(12, 15),
|
||||
HolidayCalendar([
|
||||
USBlackFridayInOrAfter1993,
|
||||
])
|
||||
)]
|
||||
@@ -14,71 +14,29 @@
|
||||
# limitations under the License.
|
||||
|
||||
from datetime import time
|
||||
from itertools import chain
|
||||
|
||||
from pandas.tseries.holiday import AbstractHolidayCalendar
|
||||
from pandas.tseries.holiday import (
|
||||
USPresidentsDay,
|
||||
USLaborDay,
|
||||
USThanksgivingDay,
|
||||
GoodFriday
|
||||
)
|
||||
from pytz import timezone
|
||||
|
||||
# Useful resources for making changes to this file:
|
||||
# http://www.cmegroup.com/tools-information/holiday-calendar.html
|
||||
|
||||
from .trading_calendar import TradingCalendar
|
||||
|
||||
from .trading_calendar import TradingCalendar, HolidayCalendar
|
||||
from .us_holidays import (
|
||||
USNewYearsDay,
|
||||
Christmas,
|
||||
ChristmasEveBefore1993,
|
||||
ChristmasEveInOrAfter1993,
|
||||
FridayAfterIndependenceDayExcept2013,
|
||||
MonTuesThursBeforeIndependenceDay,
|
||||
USBlackFridayInOrAfter1993,
|
||||
September11Closings,
|
||||
USNationalDaysofMourning
|
||||
)
|
||||
|
||||
US_CENTRAL = timezone('America/Chicago')
|
||||
CME_OPEN = time(17)
|
||||
CME_CLOSE = time(16)
|
||||
|
||||
# The CME seems to have different holiday rules depending on the type
|
||||
# of instrument. For example, http://www.cmegroup.com/tools-information/holiday-calendar/files/2016-4th-of-july-holiday-schedule.pdf # noqa
|
||||
# shows that Equity, Interest Rate, FX, Energy, Metals & DME Products close at
|
||||
# 1200 CT on July 4, 2016, while Grain, Oilseed & MGEX Products and Livestock,
|
||||
# Dairy & Lumber products are completely closed.
|
||||
|
||||
# For now, we will treat the CME as having a single calendar, and just go with
|
||||
# the most conservative hours - and treat July 4 as an early close at noon.
|
||||
CME_STANDARD_EARLY_CLOSE = time(12)
|
||||
|
||||
# Does the market open or close on a different calendar day, compared to the
|
||||
# calendar day assigned by the exchange to this session?
|
||||
CME_OPEN_OFFSET = -1
|
||||
CME_CLOSE_OFFSET = -0
|
||||
|
||||
|
||||
class CMEHolidayCalendar(AbstractHolidayCalendar):
|
||||
"""
|
||||
Non-trading days for the CME.
|
||||
|
||||
See CMEExchangeCalendar for full description.
|
||||
"""
|
||||
rules = [
|
||||
USNewYearsDay,
|
||||
Christmas,
|
||||
]
|
||||
|
||||
|
||||
class CMEEarlyCloseCalendar(AbstractHolidayCalendar):
|
||||
"""
|
||||
Regular early close calendar for NYSE
|
||||
"""
|
||||
rules = [
|
||||
MonTuesThursBeforeIndependenceDay,
|
||||
FridayAfterIndependenceDayExcept2013,
|
||||
USBlackFridayInOrAfter1993,
|
||||
ChristmasEveBefore1993,
|
||||
ChristmasEveInOrAfter1993,
|
||||
]
|
||||
USNationalDaysofMourning,
|
||||
USMartinLutherKingJrAfter1998,
|
||||
USMemorialDay,
|
||||
USIndependenceDay)
|
||||
|
||||
|
||||
class CMEExchangeCalendar(TradingCalendar):
|
||||
@@ -89,58 +47,65 @@ class CMEExchangeCalendar(TradingCalendar):
|
||||
Close Time: 5:00 PM, America/Chicago
|
||||
|
||||
Regularly-Observed Holidays:
|
||||
- New Years Day (observed on monday when Jan 1 is a Sunday)
|
||||
- Martin Luther King Jr. Day (3rd Monday in January, only after 1998)
|
||||
- Washington's Birthday (aka President's Day, 3rd Monday in February)
|
||||
- Good Friday (two days before Easter Sunday)
|
||||
- Memorial Day (last Monday in May)
|
||||
- Independence Day (observed on the nearest weekday to July 4th)
|
||||
- Labor Day (first Monday in September)
|
||||
- Thanksgiving (fourth Thursday in November)
|
||||
- Christmas (observed on nearest weekday to December 25)
|
||||
|
||||
NOTE: For the following US Federal Holidays, part of the CME is closed
|
||||
(Foreign Exchange, Interest Rates) but Commodities, GSCI, Weather & Real
|
||||
Estate is open. Thus, we don't treat these as holidays.
|
||||
- Columbus Day
|
||||
- Veterans Day
|
||||
|
||||
Regularly-Observed Early Closes:
|
||||
- Christmas Eve (except on Fridays, when the exchange is closed entirely)
|
||||
- Day After Thanksgiving (aka Black Friday, observed from 1992 onward)
|
||||
|
||||
Additional Irregularities:
|
||||
- Closed from 9/11/2001 to 9/16/2001 due to terrorist attacks in NYC.
|
||||
- Closed on 10/29/2012 and 10/30/2012 due to Hurricane Sandy.
|
||||
- Closed on 4/27/1994 due to Richard Nixon's death.
|
||||
- Closed on 6/11/2004 due to Ronald Reagan's death.
|
||||
- Closed on 1/2/2007 due to Gerald Ford's death.
|
||||
- Closed at 1:00 PM on Wednesday, July 3rd, 2013
|
||||
- Closed at 1:00 PM on Friday, December 31, 1999
|
||||
- Closed at 1:00 PM on Friday, December 26, 1997
|
||||
- Closed at 1:00 PM on Friday, December 26, 2003
|
||||
|
||||
NOTE: The exchange was **not** closed early on Friday December 26, 2008,
|
||||
nor was it closed on Friday December 26, 2014. The next Thursday Christmas
|
||||
will be in 2025. If someone is still maintaining this code in 2025, then
|
||||
we've done alright...and we should check if it's a half day.
|
||||
- New Years Day
|
||||
- Good Friday
|
||||
- Christmas
|
||||
"""
|
||||
@property
|
||||
def name(self):
|
||||
return "CME"
|
||||
|
||||
name = "CME"
|
||||
tz = US_CENTRAL
|
||||
open_time = CME_OPEN
|
||||
close_time = CME_CLOSE
|
||||
open_offset = CME_OPEN_OFFSET
|
||||
close_offset = CME_CLOSE_OFFSET
|
||||
@property
|
||||
def tz(self):
|
||||
return timezone('America/Chicago')
|
||||
|
||||
holidays_calendar = CMEHolidayCalendar()
|
||||
special_opens_calendars = ()
|
||||
special_closes_calendars = []
|
||||
@property
|
||||
def open_time(self):
|
||||
return time(17, 1)
|
||||
|
||||
holidays_adhoc = list(chain(
|
||||
September11Closings,
|
||||
USNationalDaysofMourning,
|
||||
))
|
||||
@property
|
||||
def close_time(self):
|
||||
return time(17)
|
||||
|
||||
special_opens_adhoc = ()
|
||||
special_closes_adhoc = []
|
||||
@property
|
||||
def open_offset(self):
|
||||
return -1
|
||||
|
||||
@property
|
||||
def regular_holidays(self):
|
||||
# The CME has different holiday rules depending on the type of
|
||||
# instrument. For example, http://www.cmegroup.com/tools-information/holiday-calendar/files/2016-4th-of-july-holiday-schedule.pdf # noqa
|
||||
# shows that Equity, Interest Rate, FX, Energy, Metals & DME Products
|
||||
# close at 1200 CT on July 4, 2016, while Grain, Oilseed & MGEX
|
||||
# Products and Livestock, Dairy & Lumber products are completely
|
||||
# closed.
|
||||
|
||||
# For now, we will treat the CME as having a single calendar, and just
|
||||
# go with the most conservative hours - and treat July 4 as an early
|
||||
# close at noon.
|
||||
return HolidayCalendar([
|
||||
USNewYearsDay,
|
||||
GoodFriday,
|
||||
Christmas,
|
||||
])
|
||||
|
||||
@property
|
||||
def adhoc_holidays(self):
|
||||
return USNationalDaysofMourning
|
||||
|
||||
@property
|
||||
def special_closes(self):
|
||||
return [(
|
||||
time(12),
|
||||
HolidayCalendar([
|
||||
USMartinLutherKingJrAfter1998,
|
||||
USPresidentsDay,
|
||||
USMemorialDay,
|
||||
USLaborDay,
|
||||
USIndependenceDay,
|
||||
USThanksgivingDay,
|
||||
USBlackFridayInOrAfter1993,
|
||||
ChristmasEveBefore1993,
|
||||
ChristmasEveInOrAfter1993,
|
||||
])
|
||||
)]
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
from datetime import time
|
||||
from itertools import chain
|
||||
|
||||
from pandas.tseries.holiday import (
|
||||
GoodFriday,
|
||||
USPresidentsDay,
|
||||
USLaborDay,
|
||||
USThanksgivingDay
|
||||
)
|
||||
from pandas.tslib import Timestamp
|
||||
from pytz import timezone
|
||||
|
||||
from zipline.utils.calendars import TradingCalendar
|
||||
from zipline.utils.calendars.trading_calendar import HolidayCalendar
|
||||
from zipline.utils.calendars.us_holidays import (
|
||||
USNewYearsDay,
|
||||
Christmas,
|
||||
USMartinLutherKingJrAfter1998,
|
||||
USMemorialDay,
|
||||
USIndependenceDay,
|
||||
USNationalDaysofMourning)
|
||||
|
||||
|
||||
class ICEExchangeCalendar(TradingCalendar):
|
||||
"""
|
||||
Exchange calendar for ICE US.
|
||||
|
||||
Open Time: 8pm, US/Eastern
|
||||
Close Time: 6pm, US/Eastern
|
||||
|
||||
https://www.theice.com/publicdocs/futures_us/ICE_Futures_US_Regular_Trading_Hours.pdf # noqa
|
||||
"""
|
||||
@property
|
||||
def name(self):
|
||||
return "ICE"
|
||||
|
||||
@property
|
||||
def tz(self):
|
||||
return timezone("US/Eastern")
|
||||
|
||||
@property
|
||||
def open_time(self):
|
||||
return time(20, 1)
|
||||
|
||||
@property
|
||||
def close_time(self):
|
||||
return time(18)
|
||||
|
||||
@property
|
||||
def open_offset(self):
|
||||
return -1
|
||||
|
||||
@property
|
||||
def special_closes(self):
|
||||
return [
|
||||
(time(13), HolidayCalendar([
|
||||
USMartinLutherKingJrAfter1998,
|
||||
USPresidentsDay,
|
||||
USMemorialDay,
|
||||
USIndependenceDay,
|
||||
USLaborDay,
|
||||
USThanksgivingDay
|
||||
]))
|
||||
]
|
||||
|
||||
@property
|
||||
def adhoc_holidays(self):
|
||||
return list(chain(
|
||||
USNationalDaysofMourning,
|
||||
# ICE was only closed on the first day of the Hurricane Sandy
|
||||
# closings (was not closed on 2012-10-30)
|
||||
[Timestamp('2012-10-29', tz='UTC')]
|
||||
))
|
||||
|
||||
@property
|
||||
def regular_holidays(self):
|
||||
# https://www.theice.com/publicdocs/futures_us/exchange_notices/NewExNot2016Holidays.pdf # noqa
|
||||
return HolidayCalendar([
|
||||
USNewYearsDay,
|
||||
GoodFriday,
|
||||
Christmas
|
||||
])
|
||||
@@ -1,6 +1,5 @@
|
||||
from datetime import time
|
||||
from pandas.tseries.holiday import(
|
||||
AbstractHolidayCalendar,
|
||||
Holiday,
|
||||
DateOffset,
|
||||
MO,
|
||||
@@ -14,7 +13,7 @@ from .trading_calendar import (
|
||||
TradingCalendar,
|
||||
MONDAY,
|
||||
TUESDAY,
|
||||
)
|
||||
HolidayCalendar)
|
||||
|
||||
# New Year's Day
|
||||
LSENewYearsDay = Holiday(
|
||||
@@ -73,26 +72,6 @@ WeekendBoxingDay = Holiday(
|
||||
)
|
||||
|
||||
|
||||
class LSEHolidayCalendar(AbstractHolidayCalendar):
|
||||
"""
|
||||
Non-trading days for the LSE.
|
||||
|
||||
See NYSEExchangeCalendar for full description.
|
||||
"""
|
||||
rules = [
|
||||
LSENewYearsDay,
|
||||
GoodFriday,
|
||||
EasterMonday,
|
||||
MayBank,
|
||||
SpringBank,
|
||||
SummerBank,
|
||||
Christmas,
|
||||
WeekendChristmas,
|
||||
BoxingDay,
|
||||
WeekendBoxingDay,
|
||||
]
|
||||
|
||||
|
||||
class LSEExchangeCalendar(TradingCalendar):
|
||||
"""
|
||||
Exchange calendar for the London Stock Exchange
|
||||
@@ -113,18 +92,33 @@ class LSEExchangeCalendar(TradingCalendar):
|
||||
- Dec. 28th (if Boxing Day is on a weekend)
|
||||
"""
|
||||
|
||||
name = 'LSE'
|
||||
tz = timezone('Europe/London')
|
||||
open_time = time(8, 1)
|
||||
close_time = time(16, 30)
|
||||
open_offset = 0
|
||||
close_offset = 0
|
||||
@property
|
||||
def name(self):
|
||||
return "LSE"
|
||||
|
||||
holidays_calendar = LSEHolidayCalendar()
|
||||
special_opens_calendars = ()
|
||||
special_closes_calendars = ()
|
||||
@property
|
||||
def tz(self):
|
||||
return timezone('Europe/London')
|
||||
|
||||
holidays_adhoc = ()
|
||||
@property
|
||||
def open_time(self):
|
||||
return time(8, 1)
|
||||
|
||||
special_opens_adhoc = ()
|
||||
special_closes_adhoc = ()
|
||||
@property
|
||||
def close_time(self):
|
||||
return time(16, 30)
|
||||
|
||||
@property
|
||||
def regular_holidays(self):
|
||||
return HolidayCalendar([
|
||||
LSENewYearsDay,
|
||||
GoodFriday,
|
||||
EasterMonday,
|
||||
MayBank,
|
||||
SpringBank,
|
||||
SummerBank,
|
||||
Christmas,
|
||||
WeekendChristmas,
|
||||
BoxingDay,
|
||||
WeekendBoxingDay
|
||||
])
|
||||
|
||||
@@ -17,7 +17,6 @@ from datetime import time
|
||||
from itertools import chain
|
||||
|
||||
from pandas.tseries.holiday import(
|
||||
AbstractHolidayCalendar,
|
||||
GoodFriday,
|
||||
USLaborDay,
|
||||
USPresidentsDay,
|
||||
@@ -25,8 +24,7 @@ from pandas.tseries.holiday import(
|
||||
)
|
||||
from pytz import timezone
|
||||
|
||||
from .trading_calendar import TradingCalendar
|
||||
|
||||
from .trading_calendar import TradingCalendar, HolidayCalendar
|
||||
from .us_holidays import (
|
||||
USNewYearsDay,
|
||||
USMartinLutherKingJrAfter1998,
|
||||
@@ -48,58 +46,6 @@ from .us_holidays import (
|
||||
# http://www.nyse.com/pdfs/closings.pdf
|
||||
# http://www.stevemorse.org/jcal/whendid.html
|
||||
|
||||
US_EASTERN = timezone('US/Eastern')
|
||||
NYSE_OPEN = time(9, 31)
|
||||
NYSE_CLOSE = time(16)
|
||||
NYSE_STANDARD_EARLY_CLOSE = time(13)
|
||||
|
||||
# Whether market opens or closes on a different calendar day, compared to the
|
||||
# calendar day assigned by the exchange to this session.
|
||||
NYSE_OPEN_OFFSET = 0
|
||||
NYSE_CLOSE_OFFSET = 0
|
||||
|
||||
|
||||
class NYSEHolidayCalendar(AbstractHolidayCalendar):
|
||||
"""
|
||||
Non-trading days for the NYSE.
|
||||
|
||||
See NYSEExchangeCalendar for full description.
|
||||
"""
|
||||
rules = [
|
||||
USNewYearsDay,
|
||||
USMartinLutherKingJrAfter1998,
|
||||
USPresidentsDay,
|
||||
GoodFriday,
|
||||
USMemorialDay,
|
||||
USIndependenceDay,
|
||||
USLaborDay,
|
||||
USThanksgivingDay,
|
||||
USIndependenceDay,
|
||||
Christmas,
|
||||
]
|
||||
|
||||
|
||||
class NYSE2PMCloseCalendar(AbstractHolidayCalendar):
|
||||
"""
|
||||
Holiday Calendar for 2PM closes for NYSE
|
||||
"""
|
||||
rules = [
|
||||
ChristmasEveBefore1993,
|
||||
USBlackFridayBefore1993,
|
||||
]
|
||||
|
||||
|
||||
class NYSEEarlyCloseCalendar(AbstractHolidayCalendar):
|
||||
"""
|
||||
Regular early close calendar for NYSE
|
||||
"""
|
||||
rules = [
|
||||
MonTuesThursBeforeIndependenceDay,
|
||||
FridayAfterIndependenceDayExcept2013,
|
||||
USBlackFridayInOrAfter1993,
|
||||
ChristmasEveInOrAfter1993,
|
||||
]
|
||||
|
||||
|
||||
class NYSEExchangeCalendar(TradingCalendar):
|
||||
"""
|
||||
@@ -149,30 +95,68 @@ class NYSEExchangeCalendar(TradingCalendar):
|
||||
we've done alright...and we should check if it's a half day.
|
||||
"""
|
||||
|
||||
name = "NYSE"
|
||||
tz = US_EASTERN
|
||||
open_time = NYSE_OPEN
|
||||
close_time = NYSE_CLOSE
|
||||
open_offset = NYSE_OPEN_OFFSET
|
||||
close_offset = NYSE_CLOSE_OFFSET
|
||||
regular_early_close = time(13)
|
||||
|
||||
holidays_calendar = NYSEHolidayCalendar()
|
||||
special_opens_calendars = ()
|
||||
special_closes_calendars = [
|
||||
(NYSE_STANDARD_EARLY_CLOSE, NYSEEarlyCloseCalendar()),
|
||||
(time(14), NYSE2PMCloseCalendar()),
|
||||
]
|
||||
@property
|
||||
def name(self):
|
||||
return "NYSE"
|
||||
|
||||
holidays_adhoc = list(chain(
|
||||
September11Closings,
|
||||
HurricaneSandyClosings,
|
||||
USNationalDaysofMourning,
|
||||
))
|
||||
@property
|
||||
def tz(self):
|
||||
return timezone('US/Eastern')
|
||||
|
||||
special_opens_adhoc = ()
|
||||
special_closes_adhoc = [
|
||||
(NYSE_STANDARD_EARLY_CLOSE, ('1997-12-26',
|
||||
'1999-12-31',
|
||||
'2003-12-26',
|
||||
'2013-07-03')),
|
||||
]
|
||||
@property
|
||||
def open_time(self):
|
||||
return time(9, 31)
|
||||
|
||||
@property
|
||||
def close_time(self):
|
||||
return time(16)
|
||||
|
||||
@property
|
||||
def regular_holidays(self):
|
||||
return HolidayCalendar([
|
||||
USNewYearsDay,
|
||||
USMartinLutherKingJrAfter1998,
|
||||
USPresidentsDay,
|
||||
GoodFriday,
|
||||
USMemorialDay,
|
||||
USIndependenceDay,
|
||||
USLaborDay,
|
||||
USThanksgivingDay,
|
||||
Christmas,
|
||||
])
|
||||
|
||||
@property
|
||||
def adhoc_holidays(self):
|
||||
return list(chain(
|
||||
September11Closings,
|
||||
HurricaneSandyClosings,
|
||||
USNationalDaysofMourning,
|
||||
))
|
||||
|
||||
@property
|
||||
def special_closes(self):
|
||||
return [
|
||||
(self.regular_early_close, HolidayCalendar([
|
||||
MonTuesThursBeforeIndependenceDay,
|
||||
FridayAfterIndependenceDayExcept2013,
|
||||
USBlackFridayInOrAfter1993,
|
||||
ChristmasEveInOrAfter1993
|
||||
])),
|
||||
(time(14), HolidayCalendar([
|
||||
ChristmasEveBefore1993,
|
||||
USBlackFridayBefore1993,
|
||||
])),
|
||||
]
|
||||
|
||||
@property
|
||||
def special_closes_adhoc(self):
|
||||
return [
|
||||
(self.regular_early_close, [
|
||||
'1997-12-26',
|
||||
'1999-12-31',
|
||||
'2003-12-26',
|
||||
'2013-07-03'
|
||||
])
|
||||
]
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
from datetime import time
|
||||
from pandas.tseries.holiday import(
|
||||
AbstractHolidayCalendar,
|
||||
Holiday,
|
||||
DateOffset,
|
||||
MO,
|
||||
weekend_to_monday,
|
||||
GoodFriday,
|
||||
GoodFriday
|
||||
)
|
||||
from pytz import timezone
|
||||
|
||||
from zipline.utils.calendars.trading_calendar import TradingCalendar
|
||||
from zipline.utils.calendars.trading_calendar import TradingCalendar, \
|
||||
HolidayCalendar
|
||||
from zipline.utils.calendars.us_holidays import Christmas
|
||||
from zipline.utils.calendars.exchange_calendar_lse import (
|
||||
WeekendChristmas,
|
||||
BoxingDay,
|
||||
WeekendBoxingDay,
|
||||
WeekendBoxingDay
|
||||
)
|
||||
|
||||
# New Year's Day
|
||||
@@ -69,28 +69,6 @@ Thanksgiving = Holiday(
|
||||
)
|
||||
|
||||
|
||||
class TSXHolidayCalendar(AbstractHolidayCalendar):
|
||||
"""
|
||||
Non-trading days for the TSX.
|
||||
|
||||
See NYSEExchangeCalendar for full description.
|
||||
"""
|
||||
rules = [
|
||||
TSXNewYearsDay,
|
||||
FamilyDay,
|
||||
GoodFriday,
|
||||
VictoriaDay,
|
||||
CanadaDay,
|
||||
CivicHoliday,
|
||||
LaborDay,
|
||||
Thanksgiving,
|
||||
Christmas,
|
||||
WeekendChristmas,
|
||||
BoxingDay,
|
||||
WeekendBoxingDay,
|
||||
]
|
||||
|
||||
|
||||
class TSXExchangeCalendar(TradingCalendar):
|
||||
"""
|
||||
Exchange calendar for the Toronto Stock Exchange
|
||||
@@ -113,18 +91,35 @@ class TSXExchangeCalendar(TradingCalendar):
|
||||
- Dec. 28th (if Boxing Day is on a weekend)
|
||||
"""
|
||||
|
||||
name = 'TSX'
|
||||
tz = timezone('Canada/Atlantic')
|
||||
open_time = time(9, 31)
|
||||
close_time = time(16)
|
||||
open_offset = 0
|
||||
close_offset = 0
|
||||
@property
|
||||
def name(self):
|
||||
return "TSX"
|
||||
|
||||
holidays_calendar = TSXHolidayCalendar()
|
||||
special_opens_calendars = ()
|
||||
special_closes_calendars = ()
|
||||
@property
|
||||
def tz(self):
|
||||
return timezone('Canada/Atlantic')
|
||||
|
||||
holidays_adhoc = ()
|
||||
@property
|
||||
def open_time(self):
|
||||
return time(9, 31)
|
||||
|
||||
special_opens_adhoc = ()
|
||||
special_closes_adhoc = ()
|
||||
@property
|
||||
def close_time(self):
|
||||
return time(16)
|
||||
|
||||
@property
|
||||
def regular_holidays(self):
|
||||
return HolidayCalendar([
|
||||
TSXNewYearsDay,
|
||||
FamilyDay,
|
||||
GoodFriday,
|
||||
VictoriaDay,
|
||||
CanadaDay,
|
||||
CivicHoliday,
|
||||
LaborDay,
|
||||
Thanksgiving,
|
||||
Christmas,
|
||||
WeekendChristmas,
|
||||
BoxingDay,
|
||||
WeekendBoxingDay
|
||||
])
|
||||
|
||||
@@ -12,7 +12,9 @@
|
||||
# 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.
|
||||
from abc import ABCMeta
|
||||
from abc import ABCMeta, abstractproperty
|
||||
|
||||
from pandas.tseries.holiday import AbstractHolidayCalendar
|
||||
from six import with_metaclass
|
||||
from numpy import searchsorted
|
||||
import numpy as np
|
||||
@@ -24,13 +26,12 @@ from pandas import (
|
||||
DateOffset
|
||||
)
|
||||
from pandas.tseries.offsets import CustomBusinessDay
|
||||
|
||||
from zipline.utils.calendars._calendar_helpers import (
|
||||
next_divider_idx,
|
||||
previous_divider_idx,
|
||||
is_open
|
||||
)
|
||||
from zipline.utils.memoize import remember_last
|
||||
from zipline.utils.memoize import remember_last, lazyval
|
||||
|
||||
start_default = pd.Timestamp('1990-01-01', tz='UTC')
|
||||
end_base = pd.Timestamp('today', tz='UTC')
|
||||
@@ -57,28 +58,19 @@ class TradingCalendar(with_metaclass(ABCMeta)):
|
||||
For each session, we store the open and close time in UTC time.
|
||||
"""
|
||||
def __init__(self, start=start_default, end=end_default):
|
||||
open_offset = self.open_offset
|
||||
close_offset = self.close_offset
|
||||
|
||||
# Define those days on which the exchange is usually open.
|
||||
self.day = CustomBusinessDay(
|
||||
holidays=self.holidays_adhoc,
|
||||
calendar=self.holidays_calendar,
|
||||
)
|
||||
|
||||
# Midnight in UTC for each trading day.
|
||||
_all_days = date_range(start, end, freq=self.day, tz='UTC')
|
||||
|
||||
# `DatetimeIndex`s of standard opens/closes for each day.
|
||||
self._opens = days_at_time(_all_days, self.open_time, self.tz,
|
||||
open_offset)
|
||||
self.open_offset)
|
||||
self._closes = days_at_time(
|
||||
_all_days, self.close_time, self.tz, close_offset
|
||||
_all_days, self.close_time, self.tz, self.close_offset
|
||||
)
|
||||
|
||||
# `DatetimeIndex`s of nonstandard opens/closes
|
||||
_special_opens = self._special_opens(start, end)
|
||||
_special_closes = self._special_closes(start, end)
|
||||
_special_opens = self._calculate_special_opens(start, end)
|
||||
_special_closes = self._calculate_special_closes(start, end)
|
||||
|
||||
# Overwrite the special opens and closes on top of the standard ones.
|
||||
_overwrite_special_dates(_all_days, self._opens, _special_opens)
|
||||
@@ -113,7 +105,95 @@ class TradingCalendar(with_metaclass(ABCMeta)):
|
||||
_special_closes.map(self.minute_to_session_label)
|
||||
)
|
||||
|
||||
@lazyval
|
||||
def day(self):
|
||||
return CustomBusinessDay(
|
||||
holidays=self.adhoc_holidays,
|
||||
calendar=self.regular_holidays,
|
||||
)
|
||||
|
||||
@abstractproperty
|
||||
def name(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
@abstractproperty
|
||||
def tz(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
@abstractproperty
|
||||
def open_time(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
@abstractproperty
|
||||
def close_time(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
@property
|
||||
def open_offset(self):
|
||||
return 0
|
||||
|
||||
@property
|
||||
def close_offset(self):
|
||||
return 0
|
||||
|
||||
@property
|
||||
def regular_holidays(self):
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
pd.AbstractHolidayCalendar: a calendar containing the regular holidays
|
||||
for this calendar
|
||||
"""
|
||||
return None
|
||||
|
||||
@property
|
||||
def adhoc_holidays(self):
|
||||
return []
|
||||
|
||||
@property
|
||||
def special_opens(self):
|
||||
"""
|
||||
A list of special open times and corresponding HolidayCalendars.
|
||||
|
||||
Returns
|
||||
-------
|
||||
list: List of (time, AbstractHolidayCalendar) tuples
|
||||
"""
|
||||
return []
|
||||
|
||||
@property
|
||||
def special_opens_adhoc(self):
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
list: List of (time, DatetimeIndex) tuples that represent special
|
||||
closes that cannot be codified into rules.
|
||||
"""
|
||||
return []
|
||||
|
||||
@property
|
||||
def special_closes(self):
|
||||
"""
|
||||
A list of special close times and corresponding HolidayCalendars.
|
||||
|
||||
Returns
|
||||
-------
|
||||
list: List of (time, AbstractHolidayCalendar) tuples
|
||||
"""
|
||||
return []
|
||||
|
||||
@property
|
||||
def special_closes_adhoc(self):
|
||||
"""
|
||||
Returns
|
||||
-------
|
||||
list: List of (time, DatetimeIndex) tuples that represent special
|
||||
closes that cannot be codified into rules.
|
||||
"""
|
||||
return []
|
||||
|
||||
# -----
|
||||
|
||||
def opens(self):
|
||||
return self.schedule.market_open
|
||||
|
||||
@@ -176,7 +256,7 @@ class TradingCalendar(with_metaclass(ABCMeta)):
|
||||
The UTC timestamp of the next open.
|
||||
"""
|
||||
idx = next_divider_idx(self.market_opens_nanos, dt.value)
|
||||
return self.schedule.market_open[idx].tz_localize('UTC')
|
||||
return pd.Timestamp(self.market_opens_nanos[idx], tz='UTC')
|
||||
|
||||
def next_close(self, dt):
|
||||
"""
|
||||
@@ -193,7 +273,7 @@ class TradingCalendar(with_metaclass(ABCMeta)):
|
||||
The UTC timestamp of the next close.
|
||||
"""
|
||||
idx = next_divider_idx(self.market_closes_nanos, dt.value)
|
||||
return self.schedule.market_close[idx].tz_localize('UTC')
|
||||
return pd.Timestamp(self.market_closes_nanos[idx], tz='UTC')
|
||||
|
||||
def previous_open(self, dt):
|
||||
"""
|
||||
@@ -210,7 +290,7 @@ class TradingCalendar(with_metaclass(ABCMeta)):
|
||||
The UTC imestamp of the previous open.
|
||||
"""
|
||||
idx = previous_divider_idx(self.market_opens_nanos, dt.value)
|
||||
return self.schedule.market_open[idx].tz_localize('UTC')
|
||||
return pd.Timestamp(self.market_opens_nanos[idx], tz='UTC')
|
||||
|
||||
def previous_close(self, dt):
|
||||
"""
|
||||
@@ -227,7 +307,7 @@ class TradingCalendar(with_metaclass(ABCMeta)):
|
||||
The UTC timestamp of the previous close.
|
||||
"""
|
||||
idx = previous_divider_idx(self.market_closes_nanos, dt.value)
|
||||
return self.schedule.market_close[idx].tz_localize('UTC')
|
||||
return pd.Timestamp(self.market_closes_nanos[idx], tz='UTC')
|
||||
|
||||
def next_minute(self, dt):
|
||||
"""
|
||||
@@ -643,17 +723,17 @@ class TradingCalendar(with_metaclass(ABCMeta)):
|
||||
)
|
||||
return _dates[(_dates >= start_date) & (_dates <= end_date)]
|
||||
|
||||
def _special_opens(self, start, end):
|
||||
def _calculate_special_opens(self, start, end):
|
||||
return self._special_dates(
|
||||
self.special_opens_calendars,
|
||||
self.special_opens,
|
||||
self.special_opens_adhoc,
|
||||
start,
|
||||
end,
|
||||
)
|
||||
|
||||
def _special_closes(self, start, end):
|
||||
def _calculate_special_closes(self, start, end):
|
||||
return self._special_dates(
|
||||
self.special_closes_calendars,
|
||||
self.special_closes,
|
||||
self.special_closes_adhoc,
|
||||
start,
|
||||
end,
|
||||
@@ -677,7 +757,6 @@ def days_at_time(days, t, tz, day_offset=0):
|
||||
day_offset : int
|
||||
The number of days we want to offset @days by
|
||||
"""
|
||||
|
||||
# Offset days without tz to avoid timezone issues.
|
||||
days = DatetimeIndex(days).tz_localize(None)
|
||||
days_offset = days + DateOffset(days=day_offset)
|
||||
@@ -735,3 +814,8 @@ def _overwrite_special_dates(midnight_utcs,
|
||||
# maintaining sorting, this should be ok, but this is a good place to
|
||||
# sanity check if things start going haywire with calendar computations.
|
||||
opens_or_closes.values[indexer] = special_opens_or_closes.values
|
||||
|
||||
|
||||
class HolidayCalendar(AbstractHolidayCalendar):
|
||||
def __init__(self, rules):
|
||||
super(HolidayCalendar, self).__init__(self, rules=rules)
|
||||
|
||||
Reference in New Issue
Block a user