Completed
Push — master ( 1f137d...7a6ba4 )
by Joe
01:31
created

zipline.utils.get_early_closes()   B

Complexity

Conditions 2

Size

Total Lines 33

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 2
dl 0
loc 33
rs 8.8571
1
#
2
# Copyright 2014 Quantopian, Inc.
3
#
4
# Licensed under the Apache License, Version 2.0 (the "License");
5
# you may not use this file except in compliance with the License.
6
# You may obtain a copy of the License at
7
#
8
#     http://www.apache.org/licenses/LICENSE-2.0
9
#
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15
16
17
import pandas as pd
18
import pytz
19
20
from datetime import datetime
21
from dateutil import rrule
22
from zipline.utils.tradingcalendar import end, canonicalize_datetime, \
23
    get_open_and_closes
24
25
start = pd.Timestamp('1994-01-01', tz='UTC')
26
27
28
def get_non_trading_days(start, end):
29
    non_trading_rules = []
30
31
    start = canonicalize_datetime(start)
32
    end = canonicalize_datetime(end)
33
34
    weekends = rrule.rrule(
35
        rrule.YEARLY,
36
        byweekday=(rrule.SA, rrule.SU),
37
        cache=True,
38
        dtstart=start,
39
        until=end
40
    )
41
    non_trading_rules.append(weekends)
42
43
    new_years = rrule.rrule(
44
        rrule.MONTHLY,
45
        byyearday=1,
46
        cache=True,
47
        dtstart=start,
48
        until=end
49
    )
50
    non_trading_rules.append(new_years)
51
52
    new_years_sunday = rrule.rrule(
53
        rrule.MONTHLY,
54
        byyearday=2,
55
        byweekday=rrule.MO,
56
        cache=True,
57
        dtstart=start,
58
        until=end
59
    )
60
    non_trading_rules.append(new_years_sunday)
61
62
    new_years_saturday = rrule.rrule(
63
        rrule.MONTHLY,
64
        byyearday=3,
65
        byweekday=rrule.MO,
66
        cache=True,
67
        dtstart=start,
68
        until=end
69
    )
70
    non_trading_rules.append(new_years_saturday)
71
72
    # Family day in Ontario, starting in 2008, third monday of February
73
    family_day = rrule.rrule(
74
        rrule.MONTHLY,
75
        bymonth=2,
76
        byweekday=(rrule.MO(3)),
77
        cache=True,
78
        dtstart=datetime(2008, 1, 1, tzinfo=pytz.utc),
79
        until=end
80
    )
81
    non_trading_rules.append(family_day)
82
83
    good_friday = rrule.rrule(
84
        rrule.DAILY,
85
        byeaster=-2,
86
        cache=True,
87
        dtstart=start,
88
        until=end
89
    )
90
    non_trading_rules.append(good_friday)
91
92
    # Monday prior to May 25th.
93
    victoria_day = rrule.rrule(
94
        rrule.MONTHLY,
95
        bymonth=5,
96
        byweekday=rrule.MO,
97
        bymonthday=[24, 23, 22, 21, 20, 19, 18],
98
        cache=True,
99
        dtstart=start,
100
        until=end
101
    )
102
    non_trading_rules.append(victoria_day)
103
104
    july_1st = rrule.rrule(
105
        rrule.MONTHLY,
106
        bymonth=7,
107
        bymonthday=1,
108
        cache=True,
109
        dtstart=start,
110
        until=end
111
    )
112
    non_trading_rules.append(july_1st)
113
114
    july_1st_sunday = rrule.rrule(
115
        rrule.MONTHLY,
116
        bymonth=7,
117
        bymonthday=2,
118
        byweekday=rrule.MO,
119
        cache=True,
120
        dtstart=start,
121
        until=end
122
    )
123
    non_trading_rules.append(july_1st_sunday)
124
125
    july_1st_saturday = rrule.rrule(
126
        rrule.MONTHLY,
127
        bymonth=7,
128
        bymonthday=3,
129
        byweekday=rrule.MO,
130
        cache=True,
131
        dtstart=start,
132
        until=end
133
    )
134
    non_trading_rules.append(july_1st_saturday)
135
136
    civic_holiday = rrule.rrule(
137
        rrule.MONTHLY,
138
        bymonth=8,
139
        byweekday=rrule.MO(1),
140
        cache=True,
141
        dtstart=start,
142
        until=end
143
    )
144
    non_trading_rules.append(civic_holiday)
145
146
    labor_day = rrule.rrule(
147
        rrule.MONTHLY,
148
        bymonth=9,
149
        byweekday=(rrule.MO(1)),
150
        cache=True,
151
        dtstart=start,
152
        until=end
153
    )
154
    non_trading_rules.append(labor_day)
155
156
    thanksgiving = rrule.rrule(
157
        rrule.MONTHLY,
158
        bymonth=10,
159
        byweekday=(rrule.MO(2)),
160
        cache=True,
161
        dtstart=start,
162
        until=end
163
    )
164
    non_trading_rules.append(thanksgiving)
165
166
    christmas = rrule.rrule(
167
        rrule.MONTHLY,
168
        bymonth=12,
169
        bymonthday=25,
170
        cache=True,
171
        dtstart=start,
172
        until=end
173
    )
174
    non_trading_rules.append(christmas)
175
176
    # If Christmas is a Sunday then the 26th, a Monday is observed.
177
    # (but that would be boxing day), so the 27th is also observed.
178
    christmas_sunday = rrule.rrule(
179
        rrule.MONTHLY,
180
        bymonth=12,
181
        bymonthday=27,
182
        byweekday=rrule.TU,
183
        cache=True,
184
        dtstart=start,
185
        until=end
186
    )
187
    non_trading_rules.append(christmas_sunday)
188
189
    # If Christmas is a Saturday then the 27th, a monday is observed.
190
    christmas_saturday = rrule.rrule(
191
        rrule.MONTHLY,
192
        bymonth=12,
193
        bymonthday=27,
194
        byweekday=rrule.MO,
195
        cache=True,
196
        dtstart=start,
197
        until=end
198
    )
199
    non_trading_rules.append(christmas_saturday)
200
201
    boxing_day = rrule.rrule(
202
        rrule.MONTHLY,
203
        bymonth=12,
204
        bymonthday=26,
205
        cache=True,
206
        dtstart=start,
207
        until=end
208
    )
209
    non_trading_rules.append(boxing_day)
210
211
    # if boxing day is a sunday, the Christmas was saturday.
212
    # Christmas is observed on the 27th, a month and boxing day is observed
213
    # on the 28th, a tuesday.
214
    boxing_day_sunday = rrule.rrule(
215
        rrule.MONTHLY,
216
        bymonth=12,
217
        bymonthday=28,
218
        byweekday=rrule.TU,
219
        cache=True,
220
        dtstart=start,
221
        until=end
222
    )
223
    non_trading_rules.append(boxing_day_sunday)
224
225
    # If boxing day is a Saturday then the 28th, a monday is observed.
226
    boxing_day_saturday = rrule.rrule(
227
        rrule.MONTHLY,
228
        bymonth=12,
229
        bymonthday=28,
230
        byweekday=rrule.MO,
231
        cache=True,
232
        dtstart=start,
233
        until=end
234
    )
235
    non_trading_rules.append(boxing_day_saturday)
236
237
    non_trading_ruleset = rrule.rruleset()
238
239
    for rule in non_trading_rules:
240
        non_trading_ruleset.rrule(rule)
241
242
    non_trading_days = non_trading_ruleset.between(start, end, inc=True)
243
244
    # Add September 11th closings
245
    # The TSX was open for 71 minutes on September 11, 2011.
246
    # It was closed on the 12th and reopened on the 13th.
247
    # http://www.cbc.ca/news2/interactives/map-tsx/
248
    #
249
    #    September 2001
250
    # Su Mo Tu We Th Fr Sa
251
    #                    1
252
    #  2  3  4  5  6  7  8
253
    #  9 10 11 12 13 14 15
254
    # 16 17 18 19 20 21 22
255
    # 23 24 25 26 27 28 29
256
    # 30
257
258
    non_trading_days.append(
259
        datetime(2001, 9, 12, tzinfo=pytz.utc))
260
261
    non_trading_days.sort()
262
    return pd.DatetimeIndex(non_trading_days)
263
264
non_trading_days = get_non_trading_days(start, end)
265
trading_day = pd.tseries.offsets.CDay(holidays=non_trading_days)
266
267
268
def get_trading_days(start, end, trading_day=trading_day):
269
    return pd.date_range(start=start.date(),
270
                         end=end.date(),
271
                         freq=trading_day).tz_localize('UTC')
272
273
trading_days = get_trading_days(start, end)
274
275
# Days in Environment but not in Calendar (using ^GSPTSE as bm_symbol):
276
# --------------------------------------------------------------------
277
# Used http://web.tmxmoney.com/pricehistory.php?qm_page=61468&qm_symbol=^TSX
278
# to check whether exchange was open on these days.
279
# 1994-07-01     - July 1st, Yahoo Finance has Volume = 0
280
# 1996-07-01     - July 1st, Yahoo Finance has Volume = 0
281
# 1996-08-05     - Civic Holiday, Yahoo Finance has Volume = 0
282
# 1997-07-01     - July 1st, Yahoo Finance has Volume = 0
283
# 1997-08-04     - Civic Holiday, Yahoo Finance has Volume = 0
284
# 2001-05-21     - Victoria day, Yahoo Finance has Volume = 0
285
# 2004-10-11     - Closed, Thanksgiving - Confirmed closed
286
# 2004-12-28     - Closed, Boxing Day - Confirmed closed
287
# 2012-10-08     - Closed, Thanksgiving - Confirmed closed
288
289
# Days in Calendar but not in Environment using ^GSPTSE as bm_symbol:
290
# --------------------------------------------------------------------
291
# Used http://web.tmxmoney.com/pricehistory.php?qm_page=61468&qm_symbol=^TSX
292
# to check whether exchange was open on these days.
293
# 2000-06-28     - No data this far back, can't confirm
294
# 2000-08-28     - No data this far back, can't confirm
295
# 2000-08-29     - No data this far back, can't confirm
296
# 2001-09-11     - TSE Open for 71 min.
297
# 2002-02-01     - Confirm TSE Open
298
# 2002-06-14     - Confirm TSE Open
299
# 2002-07-02     - Confirm TSE Open
300
# 2002-11-11     - TSX website has no data for 2 weeks in 2002
301
# 2003-07-07     - Confirm TSE Open
302
# 2003-12-16     - Confirm TSE Open
303
304
305
def get_early_closes(start, end):
306
    # TSX closed at 1:00 PM on december 24th.
307
308
    start = canonicalize_datetime(start)
309
    end = canonicalize_datetime(end)
310
311
    start = max(start, datetime(1993, 1, 1, tzinfo=pytz.utc))
312
    end = max(end, datetime(1993, 1, 1, tzinfo=pytz.utc))
313
314
    # Not included here are early closes prior to 1993
315
    # or unplanned early closes
316
317
    early_close_rules = []
318
319
    christmas_eve = rrule.rrule(
320
        rrule.MONTHLY,
321
        bymonth=12,
322
        bymonthday=24,
323
        byweekday=(rrule.MO, rrule.TU, rrule.WE, rrule.TH, rrule.FR),
324
        cache=True,
325
        dtstart=start,
326
        until=end
327
    )
328
    early_close_rules.append(christmas_eve)
329
330
    early_close_ruleset = rrule.rruleset()
331
332
    for rule in early_close_rules:
333
        early_close_ruleset.rrule(rule)
334
    early_closes = early_close_ruleset.between(start, end, inc=True)
335
336
    early_closes.sort()
337
    return pd.DatetimeIndex(early_closes)
338
339
early_closes = get_early_closes(start, end)
340
341
342
def get_open_and_close(day, early_closes):
343
    market_open = pd.Timestamp(
344
        datetime(
345
            year=day.year,
346
            month=day.month,
347
            day=day.day,
348
            hour=9,
349
            minute=31),
350
        tz='US/Eastern').tz_convert('UTC')
351
    # 1 PM if early close, 4 PM otherwise
352
    close_hour = 13 if day in early_closes else 16
353
    market_close = pd.Timestamp(
354
        datetime(
355
            year=day.year,
356
            month=day.month,
357
            day=day.day,
358
            hour=close_hour),
359
        tz='US/Eastern').tz_convert('UTC')
360
361
    return market_open, market_close
362
363
open_and_closes = get_open_and_closes(trading_days, early_closes,
364
                                      get_open_and_close)
365