def __init__(self, start=start_default, end=end_default): # Midnight in UTC for each trading day. # In pandas 0.18.1, pandas calls into its own code here in a way that # fires a warning. The calling code in pandas tries to suppress the # warning, but does so incorrectly, causing it to bubble out here. # Actually catch and suppress the warning here: with warnings.catch_warnings(): warnings.simplefilter('ignore') _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, self.open_offset) self._closes = days_at_time( _all_days, self.close_time, self.tz, self.close_offset ) # `DatetimeIndex`s of nonstandard opens/closes _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) _overwrite_special_dates(_all_days, self._closes, _special_closes) # In pandas 0.16.1 _opens and _closes will lose their timezone # information. This looks like it has been resolved in 0.17.1. # http://pandas.pydata.org/pandas-docs/stable/whatsnew.html#datetime-with-tz # noqa self.schedule = DataFrame( index=_all_days, columns=['market_open', 'market_close'], data={ 'market_open': self._opens, 'market_close': self._closes, }, dtype='datetime64[ns]', ) # Simple cache to avoid recalculating the same minute -> session in # "next" mode. Analysis of current zipline code paths show that # `minute_to_session_label` is often called consecutively with the same # inputs. self._minute_to_session_label_cache = LRU(1) self.market_opens_nanos = self.schedule.market_open.values.\ astype(np.int64) self.market_closes_nanos = self.schedule.market_close.values.\ astype(np.int64) self._trading_minutes_nanos = self.all_minutes.values.\ astype(np.int64) self.first_trading_session = _all_days[0] self.last_trading_session = _all_days[-1] self._early_closes = pd.DatetimeIndex( _special_closes.map(self.minute_to_session_label) )
def test_current_session(self): regular_minutes = self.trading_calendar.minutes_for_sessions_in_range( self.equity_minute_bar_days[0], self.equity_minute_bar_days[-1] ) bts_minutes = days_at_time( self.equity_minute_bar_days, time(8, 45), "US/Eastern" ) # some other non-market-minute three_oh_six_am_minutes = days_at_time( self.equity_minute_bar_days, time(3, 6), "US/Eastern" ) all_minutes = [regular_minutes, bts_minutes, three_oh_six_am_minutes] for minute in list(concat(all_minutes)): bar_data = self.create_bardata(lambda: minute) self.assertEqual( self.trading_calendar.minute_to_session_label(minute), bar_data.current_session )
def __init__(self, start=start_default, end=end_default): with warnings.catch_warnings(): warnings.simplefilter('ignore') _all_days = date_range(start, end, freq=self.day, tz='UTC') self._lunch_break_starts = days_at_time(_all_days, lunch_break_start, self.tz, 0) self._lunch_break_ends = days_at_time(_all_days, lunch_break_end, self.tz, 0) TradingCalendar.__init__(self, start=start_default, end=end_default) self.schedule = pd.DataFrame( index=_all_days, columns=[ 'market_open', 'market_close', 'lunch_break_start', 'lunch_break_end' ], data={ 'market_open': self._opens, 'market_close': self._closes, 'lunch_break_start': self._lunch_break_starts, 'lunch_break_end': self._lunch_break_ends }, dtype='datetime64[ns]', )
def test_crosscheck_realtimeclock_with_minutesimulationclock(self): """Tests that RealtimeClock behaves like MinuteSimulationClock""" for minute_emission in (False, True): # MinuteSimulationClock also relies on to_datetime, shall not be # created in the patch block msc = MinuteSimulationClock( self.sessions, self.opens, self.closes, days_at_time(self.sessions, time(8, 45), "US/Eastern"), minute_emission) msc_events = list(msc) with patch('zipline.gens.realtimeclock.pd.to_datetime') as to_dt, \ patch('zipline.gens.realtimeclock.sleep') as sleep: rtc = iter( RealtimeClock( self.sessions, self.opens, self.closes, days_at_time(self.sessions, time(8, 45), "US/Eastern"), minute_emission)) self.internal_clock = \ pd.Timestamp("2017-04-20 00:00", tz='UTC') to_dt.side_effect = self.get_clock sleep.side_effect = self.advance_clock rtc_events = list(rtc) for rtc_event, msc_event in zip_longest(rtc_events, msc_events): self.assertEquals(rtc_event, msc_event) self.assertEquals(len(rtc_events), len(msc_events))
def test_midday_start(self): """Tests that RealtimeClock is able to execute if started mid-day""" msc = MinuteSimulationClock( self.sessions, self.opens, self.closes, days_at_time(self.sessions, time(8, 45), "US/Eastern"), False) msc_events = list(msc) with patch('zipline.gens.realtimeclock.pd.to_datetime') as to_dt, \ patch('zipline.gens.realtimeclock.sleep') as sleep: rtc = RealtimeClock( self.sessions, self.opens, self.closes, days_at_time(self.sessions, time(8, 45), "US/Eastern"), False) to_dt.side_effect = self.get_clock sleep.side_effect = self.advance_clock self.internal_clock = pd.Timestamp("2017-04-20 15:00", tz='UTC') rtc_events = list(rtc) # Count the mid-day position in the MinuteSimulationClock's events: # Simulation Tick: 2017-04-20 00:00:00+00:00 - 1 (SESSION_START) # Simulation Tick: 2017-04-20 12:45:00+00:00 - 4 (BEFORE_TRADING_START) # Simulation Tick: 2017-04-20 13:31:00+00:00 - 0 (BAR) msc_midday_position = 2 + 90 self.assertEquals(rtc_events[0], msc_events[0]) # Session start bar # before_trading_start is fired immediately if we're after 8:45 EDT event_time, event_type = rtc_events[1] self.assertEquals(event_time, pd.Timestamp("2017-04-20 15:00", tz='UTC')) self.assertEquals(event_type, BEFORE_TRADING_START_BAR) self.assertEquals(rtc_events[2:], msc_events[msc_midday_position:])
def __init__(self, start=start_default, end=end_default): with warnings.catch_warnings(): warnings.simplefilter('ignore') _all_days = date_range(start, end, freq=self.day, tz='UTC') self._lunch_break_starts = days_at_time(_all_days, lunch_break_start, self.tz, 0) self._lunch_break_ends = days_at_time(_all_days, lunch_break_end, self.tz, 0) TradingCalendar.__init__(self, start=start_default, end=end_default)
def test_bts_after_session(self): clock = MinuteSimulationClock( self.sessions, self.opens, self.closes, days_at_time(self.sessions, time(19, 5), "US/Eastern"), False ) all_events = list(clock) # since 19:05 Eastern is after the NYSE is closed, we don't emit # BEFORE_TRADING_START. therefore, each day has SESSION_START, # 390 BARs, and then SESSION_END def _check_session_bts_after(session_label, events): minutes = self.nyse_calendar.minutes_for_session(session_label) self.assertEqual(392, len(events)) self.assertEqual(events[0], (session_label, SESSION_START)) for i in range(1, 391): self.assertEqual(events[i], (minutes[i - 1], BAR)) self.assertEqual(events[-1], (minutes[389], SESSION_END)) for i in range(0, 2): _check_session_bts_after( self.sessions[i], all_events[(i * 392): ((i + 1) * 392)] )
def test_bts_before_session(self): clock = MinuteSimulationClock( self.sessions, self.opens, self.closes, days_at_time(self.sessions, time(6, 17), "US/Eastern"), False) all_events = list(clock) def _check_session_bts_first(session_label, events, bts_dt): minutes = self.nyse_calendar.minutes_for_session(session_label) self.assertEqual(393, len(events)) self.assertEqual(events[0], (session_label, SESSION_START)) self.assertEqual(events[1], (bts_dt, BEFORE_TRADING_START_BAR)) for i in range(2, 392): self.assertEqual(events[i], (minutes[i - 2], BAR)) self.assertEqual(events[392], (minutes[-1], SESSION_END)) _check_session_bts_first( self.sessions[0], all_events[0:393], pd.Timestamp("2016-07-15 6:17", tz='US/Eastern')) _check_session_bts_first( self.sessions[1], all_events[393:786], pd.Timestamp("2016-07-18 6:17", tz='US/Eastern')) _check_session_bts_first( self.sessions[2], all_events[786:], pd.Timestamp("2016-07-19 6:17", tz='US/Eastern'))
def test_bts_before_session(self): clock = MinuteSimulationClock( self.sessions, self.opens, self.closes, days_at_time(self.sessions, time(6, 17), "Asia/Shanghai"), False) all_events = list(clock) def _check_session_bts_first(session_label, events, bts_dt): minutes = self.xshg_calendar.minutes_for_session(session_label) self.assertEqual(243, len(events)) self.assertEqual(events[0], (session_label, SESSION_START)) self.assertEqual(events[1], (bts_dt, BEFORE_TRADING_START_BAR)) for i in range(2, 242): self.assertEqual(events[i], (minutes[i - 2], BAR)) self.assertEqual(events[242], (minutes[-1], SESSION_END)) _check_session_bts_first( self.sessions[0], all_events[0:243], pd.Timestamp("2019-12-31 6:17", tz="Asia/Shanghai")) _check_session_bts_first( self.sessions[1], all_events[243:486], pd.Timestamp("2020-01-02 6:17", tz="Asia/Shanghai")) _check_session_bts_first( self.sessions[2], all_events[486:], pd.Timestamp("2020-01-03 6:17", tz="Asia/Shanghai"))
def test_bts_after_session(self): clock = MinuteSimulationClock( self.sessions, self.opens, self.closes, days_at_time(self.sessions, time(19, 5), "US/Eastern"), False, ) all_events = list(clock) # since 19:05 Eastern is after the NYSE is closed, we don't emit # BEFORE_TRADING_START. therefore, each day has SESSION_START, # 390 BARs, and then SESSION_END def _check_session_bts_after(session_label, events): minutes = self.nyse_calendar.minutes_for_session(session_label) self.assertEqual(392, len(events)) self.assertEqual(events[0], (session_label, SESSION_START)) for i in range(1, 391): self.assertEqual(events[i], (minutes[i - 1], BAR)) self.assertEqual(events[-1], (minutes[389], SESSION_END)) for i in range(0, 2): _check_session_bts_after(self.sessions[i], all_events[(i * 392):((i + 1) * 392)])
def verify_bts_during_session(self, bts_time, bts_session_times, bts_idx): def _check_session_bts_during(session_label, events, bts_dt): minutes = self.nyse_calendar.minutes_for_session(session_label) self.assertEqual(393, len(events)) self.assertEqual(events[0], (session_label, SESSION_START)) for i in range(1, bts_idx): self.assertEqual(events[i], (minutes[i - 1], BAR)) self.assertEqual(events[bts_idx], (bts_dt, BEFORE_TRADING_START_BAR)) for i in range(bts_idx + 1, 391): self.assertEqual(events[i], (minutes[i - 2], BAR)) self.assertEqual(events[392], (minutes[-1], SESSION_END)) clock = MinuteSimulationClock( self.sessions, self.opens, self.closes, days_at_time(self.sessions, bts_time, "US/Eastern"), False) all_events = list(clock) _check_session_bts_during(self.sessions[0], all_events[0:393], bts_session_times[0]) _check_session_bts_during(self.sessions[1], all_events[393:786], bts_session_times[1]) _check_session_bts_during(self.sessions[2], all_events[786:], bts_session_times[2])
def verify_bts_during_session(self, bts_time, bts_session_times, bts_idx): def _check_session_bts_during(session_label, events, bts_dt): minutes = self.nyse_calendar.minutes_for_session(session_label) self.assertEqual(393, len(events)) self.assertEqual(events[0], (session_label, SESSION_START)) for i in range(1, bts_idx): self.assertEqual(events[i], (minutes[i - 1], BAR)) self.assertEqual( events[bts_idx], (bts_dt, BEFORE_TRADING_START_BAR) ) for i in range(bts_idx + 1, 391): self.assertEqual(events[i], (minutes[i - 2], BAR)) self.assertEqual(events[392], (minutes[-1], SESSION_END)) clock = MinuteSimulationClock( self.sessions, self.opens, self.closes, days_at_time(self.sessions, bts_time, "US/Eastern"), False ) all_events = list(clock) _check_session_bts_during( self.sessions[0], all_events[0:393], bts_session_times[0] ) _check_session_bts_during( self.sessions[1], all_events[393:786], bts_session_times[1] ) _check_session_bts_during( self.sessions[2], all_events[786:], bts_session_times[2] )
def _special_dates(self, calendars, ad_hoc_dates, start_date, end_date): """ Union an iterable of pairs of the form (time, calendar) and an iterable of pairs of the form (time, [dates]) (This is shared logic for computing special opens and special closes.) """ _dates = DatetimeIndex([], tz='UTC').union_many( [ holidays_at_time(calendar, start_date, end_date, time_, self.tz) for time_, calendar in calendars ] + [ days_at_time(datetimes, time_, self.tz) for time_, datetimes in ad_hoc_dates ] ) return _dates[(_dates >= start_date) & (_dates <= end_date)]
def _create_clock(self): """ If the clock property is not set, then create one based on frequency. """ trading_o_and_c = self.trading_calendar.schedule.ix[ self.sim_params.sessions] market_closes = trading_o_and_c['market_close'] minutely_emission = False if self.sim_params.data_frequency == 'minute': market_opens = trading_o_and_c['market_open'] minutely_emission = self.sim_params.emission_rate == "minute" # The calendar's execution times are the minutes over which we # actually want to run the clock. Typically the execution times # simply adhere to the market open and close times. In the case of # the futures calendar, for example, we only want to simulate over # a subset of the full 24 hour calendar, so the execution times # dictate a market open time of 6:31am US/Eastern and a close of # 5:00pm US/Eastern. execution_opens = \ self.trading_calendar.execution_time_from_open(market_opens) execution_closes = \ self.trading_calendar.execution_time_from_close(market_closes) else: # in daily mode, we want to have one bar per session, timestamped # as the last minute of the session. execution_closes = \ self.trading_calendar.execution_time_from_close(market_closes) execution_opens = execution_closes # FIXME generalize these values # 修改为东八市区 before_trading_start_minutes = days_at_time(self.sim_params.sessions, time(8, 45), "Asia/Shanghai") return MinuteSimulationClock( self.sim_params.sessions, execution_opens, execution_closes, before_trading_start_minutes, minute_emission=minutely_emission, )
def test_time_skew(self): """Tests that RealtimeClock's time_skew parameter behaves as expected""" for time_skew in (pd.Timedelta("2 hour"), pd.Timedelta("-120 sec")): with patch('zipline.gens.realtimeclock.pd.to_datetime') as to_dt, \ patch('zipline.gens.realtimeclock.sleep') as sleep: clock = RealtimeClock( self.sessions, self.opens, self.closes, days_at_time(self.sessions, time(11, 31), "US/Eastern"), False, time_skew) to_dt.side_effect = self.get_clock sleep.side_effect = self.advance_clock start_time = pd.Timestamp("2017-04-20 15:31", tz='UTC') self.internal_clock = start_time events = list(clock) # Event 0 is SESSION_START which always happens at 00:00. ts, event_type = events[1] self.assertEquals(ts, start_time + time_skew)
def test_bts_before_session(self): clock = MinuteSimulationClock( self.sessions, self.opens, self.closes, days_at_time(self.sessions, time(6, 17), "US/Eastern"), False ) all_events = list(clock) def _check_session_bts_first(session_label, events, bts_dt): minutes = self.nyse_calendar.minutes_for_session(session_label) self.assertEqual(393, len(events)) self.assertEqual(events[0], (session_label, SESSION_START)) self.assertEqual(events[1], (bts_dt, BEFORE_TRADING_START_BAR)) for i in range(2, 392): self.assertEqual(events[i], (minutes[i - 2], BAR)) self.assertEqual(events[392], (minutes[-1], SESSION_END)) _check_session_bts_first( self.sessions[0], all_events[0:393], pd.Timestamp("2016-07-15 6:17", tz='US/Eastern') ) _check_session_bts_first( self.sessions[1], all_events[393:786], pd.Timestamp("2016-07-18 6:17", tz='US/Eastern') ) _check_session_bts_first( self.sessions[2], all_events[786:], pd.Timestamp("2016-07-19 6:17", tz='US/Eastern') )
def test_afterhours_start(self): """Tests that RealtimeClock returns immediately if started after RTH""" with patch('zipline.gens.realtimeclock.pd.to_datetime') as to_dt, \ patch('zipline.gens.realtimeclock.sleep') as sleep: rtc = RealtimeClock( self.sessions, self.opens, self.closes, days_at_time(self.sessions, time(8, 45), "US/Eastern"), False) to_dt.side_effect = self.get_clock sleep.side_effect = self.advance_clock self.internal_clock = pd.Timestamp("2017-04-20 20:05", tz='UTC') events = list(rtc) self.assertEquals(len(events), 2) # SESSION_START & which always triggered. _, event_type = events[0] self.assertEquals(event_type, SESSION_START) event_time, event_type = events[1] self.assertEquals(event_time, pd.Timestamp("2017-04-20 20:05", tz='UTC')) self.assertEquals(event_type, BEFORE_TRADING_START_BAR)
def test_market_breaks(self): calendar = get_calendar("XTKS") sessions = calendar.sessions_in_range( pd.Timestamp("2021-06-14", tz="utc"), pd.Timestamp("2021-06-15", tz="utc")) trading_o_and_c = calendar.schedule.loc[sessions] opens = trading_o_and_c['market_open'] closes = trading_o_and_c['market_close'] break_starts = trading_o_and_c['break_start'] break_ends = trading_o_and_c['break_end'] clock = MinuteSimulationClock( sessions, opens, closes, days_at_time(sessions, time(8, 45), "Japan"), break_starts, break_ends, False) all_events = list(clock) all_events = pd.DataFrame(all_events, columns=["date", "event"]).set_index("date") bar_events = all_events[all_events.event == BAR] # XTKS is open 9am - 3pm with a 1 hour lunch break from 11:30am - 12:30pm # 2 days x 300 minutes per day self.assertEqual(len(bar_events), 600) assert_index_equal( bar_events.tz_convert("Japan").iloc[148:152].index, pd.DatetimeIndex([ '2021-06-14 11:29:00', '2021-06-14 11:30:00', '2021-06-14 12:31:00', '2021-06-14 12:32:00' ], tz="Japan", name="date"))
def holidays_at_time(calendar, start, end, time, tz): return days_at_time( calendar.holidays(start, end), time, tz=tz, )