mirror of
https://github.com/wassname/catalyst.git
synced 2026-07-01 11:25:04 +08:00
ENH: Added early closes to trading environment
specifically, expected 1 PM closes since 1993
This commit is contained in:
committed by
Eddie Hebert
parent
efe50f8494
commit
75dd77ea03
@@ -182,3 +182,63 @@ If Nov has 5 Thursdays, {0} Thanksgiving is not the last week.
|
||||
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):
|
||||
early_closes = tradingcalendar.get_early_closes(
|
||||
tradingcalendar.start,
|
||||
tradingcalendar.end.replace(year=tradingcalendar.end.year + 1)
|
||||
)
|
||||
|
||||
# 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 = datetime.datetime(2012, 11, 23, tzinfo=pytz.utc)
|
||||
self.assertIn(fourth_friday, early_closes)
|
||||
|
||||
# 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 = datetime.datetime(2013, 11, 29, tzinfo=pytz.utc)
|
||||
self.assertIn(fifth_friday, early_closes)
|
||||
|
||||
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.
|
||||
"""
|
||||
early_closes = tradingcalendar.get_early_closes(
|
||||
tradingcalendar.start,
|
||||
tradingcalendar.end.replace(year=tradingcalendar.end.year + 1)
|
||||
)
|
||||
# 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 = datetime.datetime(2002, 7, 3, tzinfo=pytz.utc)
|
||||
friday_after = datetime.datetime(2002, 7, 5, tzinfo=pytz.utc)
|
||||
self.assertNotIn(wednesday_before, early_closes)
|
||||
self.assertIn(friday_after, early_closes)
|
||||
|
||||
# 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 = datetime.datetime(2013, 7, 3, tzinfo=pytz.utc)
|
||||
friday_after = datetime.datetime(2013, 7, 5, tzinfo=pytz.utc)
|
||||
self.assertIn(wednesday_before, early_closes)
|
||||
self.assertNotIn(friday_after, early_closes)
|
||||
|
||||
@@ -22,6 +22,7 @@ from delorean import Delorean
|
||||
import pandas as pd
|
||||
|
||||
from zipline.data.loader import load_market_data
|
||||
from zipline.utils.tradingcalendar import get_early_closes
|
||||
|
||||
|
||||
log = logbook.Logger('Trading')
|
||||
@@ -89,6 +90,7 @@ class TradingEnvironment(object):
|
||||
self.treasury_curves = self.treasury_curves[:max_date]
|
||||
|
||||
self.full_trading_day = datetime.timedelta(hours=6, minutes=30)
|
||||
self.early_close_trading_day = datetime.timedelta(hours=3, minutes=30)
|
||||
self.exchange_tz = exchange_tz
|
||||
|
||||
bm = None
|
||||
@@ -112,6 +114,9 @@ class TradingEnvironment(object):
|
||||
self.first_trading_day = self.trading_days[0]
|
||||
self.last_trading_day = self.trading_days[-1]
|
||||
|
||||
self.early_closes = get_early_closes(self.first_trading_day,
|
||||
self.last_trading_day)
|
||||
|
||||
def __enter__(self, *args, **kwargs):
|
||||
global environment
|
||||
self.prev_environment = environment
|
||||
@@ -203,8 +208,10 @@ Last successful date: %s" % self.last_trading_day)
|
||||
return market_open, market_close
|
||||
|
||||
def get_trading_day_duration(self, trading_day):
|
||||
# TODO: make a list of half-days and modify the
|
||||
# calculation of market close to reflect them.
|
||||
trading_day = self.normalize_date(trading_day)
|
||||
if trading_day in self.early_closes:
|
||||
return self.early_close_trading_day
|
||||
|
||||
return self.full_trading_day
|
||||
|
||||
def trading_day_distance(self, first_date, second_date):
|
||||
|
||||
@@ -247,4 +247,105 @@ def get_trading_days(start, end):
|
||||
|
||||
return business_days - non_trading_days
|
||||
|
||||
|
||||
trading_days = get_trading_days(start, end)
|
||||
|
||||
|
||||
def get_early_closes(start, end):
|
||||
# 1:00 PM close rules based on
|
||||
# http://quant.stackexchange.com/questions/4083/nyse-early-close-rules-july-4th-and-dec-25th # noqa
|
||||
# and verified against http://www.nyse.com/pdfs/closings.pdf
|
||||
|
||||
# These rules are valid starting in 1993
|
||||
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 = []
|
||||
|
||||
day_after_thanksgiving = rrule.rrule(
|
||||
rrule.MONTHLY,
|
||||
bymonth=11,
|
||||
# 4th Friday isn't correct if month starts on Friday, so restrict to
|
||||
# day range:
|
||||
byweekday=(rrule.FR),
|
||||
bymonthday=range(23, 30),
|
||||
cache=True,
|
||||
dtstart=start,
|
||||
until=end
|
||||
)
|
||||
early_close_rules.append(day_after_thanksgiving)
|
||||
|
||||
christmas_eve = rrule.rrule(
|
||||
rrule.MONTHLY,
|
||||
bymonth=12,
|
||||
bymonthday=24,
|
||||
byweekday=(rrule.MO, rrule.TU, rrule.WE, rrule.TH),
|
||||
cache=True,
|
||||
dtstart=start,
|
||||
until=end
|
||||
)
|
||||
early_close_rules.append(christmas_eve)
|
||||
|
||||
friday_after_christmas = rrule.rrule(
|
||||
rrule.MONTHLY,
|
||||
bymonth=12,
|
||||
bymonthday=26,
|
||||
byweekday=rrule.FR,
|
||||
cache=True,
|
||||
dtstart=start,
|
||||
# valid 1993-2007
|
||||
until=min(end, datetime(2007, 12, 31, tzinfo=pytz.utc))
|
||||
)
|
||||
early_close_rules.append(friday_after_christmas)
|
||||
|
||||
day_before_independence_day = rrule.rrule(
|
||||
rrule.MONTHLY,
|
||||
bymonth=7,
|
||||
bymonthday=3,
|
||||
byweekday=(rrule.MO, rrule.TU, rrule.TH),
|
||||
cache=True,
|
||||
dtstart=start,
|
||||
until=end
|
||||
)
|
||||
early_close_rules.append(day_before_independence_day)
|
||||
|
||||
day_after_independence_day = rrule.rrule(
|
||||
rrule.MONTHLY,
|
||||
bymonth=7,
|
||||
bymonthday=5,
|
||||
byweekday=rrule.FR,
|
||||
cache=True,
|
||||
dtstart=start,
|
||||
# starting in 2013: wednesday before independence day
|
||||
until=min(end, datetime(2012, 12, 31, tzinfo=pytz.utc))
|
||||
)
|
||||
early_close_rules.append(day_after_independence_day)
|
||||
|
||||
wednesday_before_independence_day = rrule.rrule(
|
||||
rrule.MONTHLY,
|
||||
bymonth=7,
|
||||
bymonthday=3,
|
||||
byweekday=rrule.WE,
|
||||
cache=True,
|
||||
# starting in 2013
|
||||
dtstart=max(start, datetime(2013, 1, 1, tzinfo=pytz.utc)),
|
||||
until=max(end, datetime(2013, 1, 1, tzinfo=pytz.utc))
|
||||
)
|
||||
early_close_rules.append(wednesday_before_independence_day)
|
||||
|
||||
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)
|
||||
|
||||
# Misc early closings from NYSE listing.
|
||||
# http://www.nyse.com/pdfs/closings.pdf
|
||||
#
|
||||
# New Year's Eve
|
||||
early_closes.append(datetime(1999, 12, 31, tzinfo=pytz.utc))
|
||||
|
||||
return pd.DatetimeIndex(sorted(early_closes))
|
||||
|
||||
Reference in New Issue
Block a user