From a4f1171f1f24c23fbd5f60ac35b57ea3fa6d4109 Mon Sep 17 00:00:00 2001 From: Andrew Daniels Date: Tue, 2 May 2017 13:33:42 -0400 Subject: [PATCH] TST: Adds MinuteEquityHistoryFuturesCalendarTestCase Added as a subclass of MinuteEquityHistoryTestCase, where the primary calendar is 'us_futures'. Notes on modifications to MinuteEquityHistoryTestCase: - To work on generic calendars, many tests now use set minutes for window start and end, and check the values on active equity minutes. - test_minute_regular should test against active equity minutes - Adapts test_minute_midnight to work with futures calendar - Use a method of getting the last open minute that works with calendars that are open at midnight - Test against Sunday at midnight, since the real intention of this test is to check that given a non-open minute, we fall back to the last open minute. --- tests/test_history.py | 363 ++++++++++++++++++++++++++++++------------ 1 file changed, 264 insertions(+), 99 deletions(-) diff --git a/tests/test_history.py b/tests/test_history.py index c4ed5130..e1f197ce 100644 --- a/tests/test_history.py +++ b/tests/test_history.py @@ -22,7 +22,7 @@ from six import iteritems from zipline import TradingAlgorithm from zipline._protocol import handle_non_market_minutes, BarData -from zipline.assets import Asset +from zipline.assets import Asset, Equity from zipline.errors import ( HistoryInInitialize, HistoryWindowStartsBeforeData, @@ -537,19 +537,21 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase): @classmethod def make_equity_minute_bar_data(cls): + equities_cal = cls.trading_calendars[Equity] + data = {} sids = {2, 5, cls.SHORT_ASSET_SID, cls.HALF_DAY_TEST_ASSET_SID} for sid in sids: asset = cls.asset_finder.retrieve_asset(sid) data[sid] = create_minute_df_for_asset( - cls.trading_calendar, + equities_cal, asset.start_date, asset.end_date, start_val=2, ) data[1] = create_minute_df_for_asset( - cls.trading_calendar, + equities_cal, pd.Timestamp('2014-01-03', tz='utc'), pd.Timestamp('2016-01-29', tz='utc'), start_val=2, @@ -557,9 +559,9 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase): asset2 = cls.asset_finder.retrieve_asset(2) data[asset2.sid] = create_minute_df_for_asset( - cls.trading_calendar, + equities_cal, asset2.start_date, - cls.trading_calendar.previous_session_label(asset2.end_date), + equities_cal.previous_session_label(asset2.end_date), start_val=2, minute_blacklist=[ pd.Timestamp('2015-01-08 14:31', tz='UTC'), @@ -574,29 +576,29 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase): # the thousands place. data[cls.MERGER_ASSET_SID] = data[cls.SPLIT_ASSET_SID] = pd.concat(( create_minute_df_for_asset( - cls.trading_calendar, + equities_cal, pd.Timestamp('2015-01-05', tz='UTC'), pd.Timestamp('2015-01-05', tz='UTC'), start_val=8000), create_minute_df_for_asset( - cls.trading_calendar, + equities_cal, pd.Timestamp('2015-01-06', tz='UTC'), pd.Timestamp('2015-01-06', tz='UTC'), start_val=2000), create_minute_df_for_asset( - cls.trading_calendar, + equities_cal, pd.Timestamp('2015-01-07', tz='UTC'), pd.Timestamp('2015-01-07', tz='UTC'), start_val=1000), create_minute_df_for_asset( - cls.trading_calendar, + equities_cal, pd.Timestamp('2015-01-08', tz='UTC'), pd.Timestamp('2015-01-08', tz='UTC'), start_val=1000) )) asset3 = cls.asset_finder.retrieve_asset(3) data[3] = create_minute_df_for_asset( - cls.trading_calendar, + equities_cal, asset3.start_date, asset3.end_date, start_val=2, @@ -817,7 +819,8 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase): # 10 minutes asset = self.env.asset_finder.retrieve_asset(sid) - minutes = self.trading_calendar.minutes_for_session( + # Check the first hour of equities trading. + minutes = self.trading_calendars[Equity].minutes_for_session( pd.Timestamp('2015-01-05', tz='UTC') )[0:60] @@ -826,33 +829,37 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase): assets=[asset], fields=[field]) - def test_minute_midnight(self): - midnight = pd.Timestamp('2015-01-06', tz='UTC') - last_minute = self.trading_calendar.open_and_close_for_session( - self.trading_calendar.minute_to_session_label( - midnight, - direction="previous" - ) - )[1] + def test_minute_sunday_midnight(self): + # Most trading calendars aren't open at midnight on Sunday. + sunday_midnight = pd.Timestamp('2015-01-09', tz='UTC') - midnight_bar_data = self.create_bardata( - lambda: midnight, - ) - yesterday_bar_data = self.create_bardata( - lambda: last_minute - ) + # Find the closest prior minute when the trading calendar was + # open (note that if the calendar is open at `sunday_midnight`, + # this will be `sunday_midnight`). + trading_minutes = self.trading_calendar.all_minutes + last_minute = trading_minutes[trading_minutes <= sunday_midnight][-1] - with handle_non_market_minutes(midnight_bar_data): + sunday_midnight_bar_data = self.create_bardata(lambda: sunday_midnight) + last_minute_bar_data = self.create_bardata(lambda: last_minute) + + # Ensure that we get the same results at midnight on Sunday as + # the last open minute. + with handle_non_market_minutes(sunday_midnight_bar_data): for field in ALL_FIELDS: np.testing.assert_array_equal( - midnight_bar_data.history(self.ASSET2, field, 30, '1m'), - yesterday_bar_data.history(self.ASSET2, field, 30, '1m') + sunday_midnight_bar_data.history( + self.ASSET2, + field, + 30, + '1m', + ), + last_minute_bar_data.history(self.ASSET2, field, 30, '1m') ) def test_minute_after_asset_stopped(self): # SHORT_ASSET's last day was 2015-01-06 # get some history windows that straddle the end - minutes = self.trading_calendar.minutes_for_session( + minutes = self.trading_calendars[Equity].minutes_for_session( pd.Timestamp('2015-01-07', tz='UTC') )[0:60] @@ -867,15 +874,6 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase): # Reset data portal because it has advanced past next test date. data_portal = self.make_data_portal() - # choose a window that contains the last minute of the asset - bar_data = BarData( - data_portal=data_portal, - simulation_dt_func=lambda: minutes[15], - data_frequency='minute', - restrictions=NoRestrictions(), - trading_calendar=self.trading_calendar, - ) - # close high low open price volume # 2015-01-06 20:47:00+00:00 768 770 767 769 768 76800 # 2015-01-06 20:48:00+00:00 769 771 768 770 769 76900 @@ -908,9 +906,29 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase): # 2015-01-07 14:45:00+00:00 NaN NaN NaN NaN NaN 0 # 2015-01-07 14:46:00+00:00 NaN NaN NaN NaN NaN 0 - window = bar_data.history(self.SHORT_ASSET, ALL_FIELDS, 30, '1m') + # choose a window that contains the last minute of the asset + window_start = pd.Timestamp('2015-01-06 20:47', tz='UTC') + window_end = pd.Timestamp('2015-01-07 14:46', tz='UTC') - # there should be 14 values and 16 NaNs/0s + bar_data = BarData( + data_portal=data_portal, + simulation_dt_func=lambda: minutes[15], + data_frequency='minute', + restrictions=NoRestrictions(), + trading_calendar=self.trading_calendar, + ) + + bar_count = len( + self.trading_calendar.minutes_in_range(window_start, window_end) + ) + window = bar_data.history( + self.SHORT_ASSET, + ALL_FIELDS, + bar_count, + '1m', + ) + + # Window should start with 14 values and end with 16 NaNs/0s. for field in ALL_FIELDS: if field == 'volume': np.testing.assert_array_equal( @@ -953,9 +971,10 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase): for asset in [self.SPLIT_ASSET, self.MERGER_ASSET]: # before any of the adjustments, last 10 minutes of jan 5 + equity_cal = self.trading_calendars[Equity] window1 = self.data_portal.get_history_window( [asset], - self.trading_calendar.open_and_close_for_session(jan5)[1], + equity_cal.open_and_close_for_session(jan5)[1], 10, '1m', 'close' @@ -964,11 +983,19 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase): np.testing.assert_array_equal( np.array(range(8380, 8390)), window1) - # straddling the first event + # straddling the first event - begins with the last 5 equity + # minutes on 2015-01-05, ends with the first 5 on + # 2015-01-06. + window2_start = pd.Timestamp('2015-01-05 20:56', tz='UTC') + window2_end = pd.Timestamp('2015-01-06 14:35', tz='UTC') + window2_count = len(self.trading_calendar.minutes_in_range( + window2_start, + window2_end, + )) window2 = self.data_portal.get_history_window( [asset], pd.Timestamp('2015-01-06 14:35', tz='UTC'), - 10, + window2_count, '1m', 'close' )[asset] @@ -979,22 +1006,33 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase): 2096.5, 2096.75, 2097, - 2097.25, - # Split occurs. The value of the thousands place should - # match. - 2000, + 2097.25], + window2[:5], + ) + # Split occurs. The value of the thousands place should + # match. + np.testing.assert_array_equal( + [2000, 2001, 2002, 2003, 2004], - window2 + window2[-5:], ) - # straddling both events! + # straddling both events! on the equities calendar this is 5 + # minutes of 1/7, 390 of 1/6, and 5 minutes of 1/5. + window3_start = pd.Timestamp('2015-01-05 20:56', tz='UTC') + window3_end = pd.Timestamp('2015-01-07 14:35', tz='UTC') + window3_minutes = self.trading_calendar.minutes_in_range( + window3_start, + window3_end, + ) + window3_count = len(window3_minutes) window3 = self.data_portal.get_history_window( [asset], pd.Timestamp('2015-01-07 14:35', tz='UTC'), - 400, # 5 minutes of 1/7, 390 of 1/6, and 5 minutes of 1/5 + window3_count, '1m', 'close' )[asset] @@ -1005,14 +1043,21 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase): window3[0:5] ) - # next 390 minutes should be 2000-2390, but halved + # next 390 minutes (the 2015-01-06 session) should be + # 2000-2390, but halved + middle_day_open_i = window3_minutes.searchsorted( + pd.Timestamp('2015-01-06 14:31', tz='UTC') + ) + middle_day_close_i = window3_minutes.searchsorted( + pd.Timestamp('2015-01-06 21:00', tz='UTC') + ) np.testing.assert_array_equal( np.array(range(2000, 2390), dtype='float64') / 2, - window3[5:395] + window3[middle_day_open_i:middle_day_close_i + 1] ) # final 5 minutes should be 1000-1004 - np.testing.assert_array_equal(range(1000, 1005), window3[395:]) + np.testing.assert_array_equal(range(1000, 1005), window3[-5:]) # after last event window4 = self.data_portal.get_history_window( @@ -1040,11 +1085,16 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase): np.testing.assert_array_equal(np.array(range(382, 392)), window1) - # straddling the first dividend + # straddling the first dividend (10 active equity minutes) + window2_start = pd.Timestamp('2015-01-05 20:56', tz='UTC') + window2_end = pd.Timestamp('2015-01-06 14:35', tz='UTC') + window2_count = len( + self.trading_calendar.minutes_in_range(window2_start, window2_end) + ) window2 = self.data_portal.get_history_window( [self.DIVIDEND_ASSET], - pd.Timestamp('2015-01-06 14:35', tz='UTC'), - 10, + window2_end, + window2_count, '1m', 'close' )[self.DIVIDEND_ASSET] @@ -1057,13 +1107,21 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase): ) # second half of window is unadjusted - np.testing.assert_array_equal(range(392, 397), window2[5:]) + np.testing.assert_array_equal(range(392, 397), window2[-5:]) - # straddling both dividends + # straddling both dividends (on the equities calendar, this is + # 5 minutes of 1/7, 390 of 1/6, and 5 minutes of 1/5). + window3_start = pd.Timestamp('2015-01-05 20:56', tz='UTC') + window3_end = pd.Timestamp('2015-01-07 14:35', tz='UTC') + window3_minutes = self.trading_calendar.minutes_in_range( + window3_start, + window3_end, + ) + window3_count = len(window3_minutes) window3 = self.data_portal.get_history_window( [self.DIVIDEND_ASSET], - pd.Timestamp('2015-01-07 14:35', tz='UTC'), - 400, # 5 minutes of 1/7, 390 of 1/6, and 5 minutes of 1/5 + window3_end, + window3_count, '1m', 'close' )[self.DIVIDEND_ASSET] @@ -1074,14 +1132,21 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase): window3[0:5] ) - # next 390 minutes should be hit by 0.96 (second dividend) + # next 390 minutes (the 2015-01-06 session) should be hit by 0.96 + # (second dividend) + middle_day_open_i = window3_minutes.searchsorted( + pd.Timestamp('2015-01-06 14:31', tz='UTC') + ) + middle_day_close_i = window3_minutes.searchsorted( + pd.Timestamp('2015-01-06 21:00', tz='UTC') + ) np.testing.assert_array_almost_equal( np.array(range(392, 782), dtype='float64') * 0.96, - window3[5:395] + window3[middle_day_open_i:middle_day_close_i + 1] ) # last 5 minutes should not be adjusted - np.testing.assert_array_equal(np.array(range(782, 787)), window3[395:]) + np.testing.assert_array_equal(np.array(range(782, 787)), window3[-5:]) def test_passing_iterable_to_history_regular_hours(self): # regular hours @@ -1129,50 +1194,70 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase): 'price': np.arange(382, 392) / 2.0, } + # Use a window looking back to 3:51pm from 8:45am the following day. + # This contains the last ten minutes of the equity session for + # 2015-01-05. + window_start = pd.Timestamp('2015-01-05 20:51', tz='UTC') + window_end = pd.Timestamp('2015-01-06 13:44', tz='UTC') + window_length = len( + self.trading_calendar.minutes_in_range(window_start, window_end) + ) + with handle_non_market_minutes(bar_data): # Single field, single asset for field in ALL_FIELDS: - values = bar_data.history(self.SPLIT_ASSET, field, 10, '1m') - np.testing.assert_array_equal(values.values, + values = bar_data.history( + self.SPLIT_ASSET, + field, + window_length, + '1m', + ) + + # The first 10 bars the `values` correspond to the last + # 10 minutes in the 2015-01-05 session. + np.testing.assert_array_equal(values.values[:10], adj_expected[field], err_msg=field) # Multi field, single asset values = bar_data.history( - self.SPLIT_ASSET, ['open', 'volume'], 10, '1m' + self.SPLIT_ASSET, ['open', 'volume'], window_length, '1m' ) - np.testing.assert_array_equal(values.open.values, + np.testing.assert_array_equal(values.open.values[:10], adj_expected['open']) - np.testing.assert_array_equal(values.volume.values, + np.testing.assert_array_equal(values.volume.values[:10], adj_expected['volume']) # Single field, multi asset values = bar_data.history( - [self.SPLIT_ASSET, self.ASSET2], 'open', 10, '1m' + [self.SPLIT_ASSET, self.ASSET2], 'open', window_length, '1m' ) - np.testing.assert_array_equal(values[self.SPLIT_ASSET].values, + np.testing.assert_array_equal(values[self.SPLIT_ASSET].values[:10], adj_expected['open']) - np.testing.assert_array_equal(values[self.ASSET2].values, + np.testing.assert_array_equal(values[self.ASSET2].values[:10], expected['open'] * 2) # Multi field, multi asset values = bar_data.history( - [self.SPLIT_ASSET, self.ASSET2], ['open', 'volume'], 10, '1m' + [self.SPLIT_ASSET, self.ASSET2], + ['open', 'volume'], + window_length, + '1m', ) np.testing.assert_array_equal( - values.open[self.SPLIT_ASSET].values, + values.open[self.SPLIT_ASSET].values[:10], adj_expected['open'] ) np.testing.assert_array_equal( - values.volume[self.SPLIT_ASSET].values, + values.volume[self.SPLIT_ASSET].values[:10], adj_expected['volume'] ) np.testing.assert_array_equal( - values.open[self.ASSET2].values, + values.open[self.ASSET2].values[:10], expected['open'] * 2 ) np.testing.assert_array_equal( - values.volume[self.ASSET2].values, + values.volume[self.ASSET2].values[:10], expected['volume'] / 2 ) @@ -1182,12 +1267,16 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase): # # five minutes into the day after the early close, get 20 1m bars - dt = pd.Timestamp('2014-07-07 13:35:00', tz='UTC') + cal = self.trading_calendar + + window_start = pd.Timestamp('2014-07-03 16:46:00', tz='UTC') + window_end = pd.Timestamp('2014-07-07 13:35:00', tz='UTC') + bar_count = len(cal.minutes_in_range(window_start, window_end)) window = self.data_portal.get_history_window( [self.HALF_DAY_TEST_ASSET], - dt, - 20, + window_end, + bar_count, '1m', 'close' )[self.HALF_DAY_TEST_ASSET] @@ -1196,10 +1285,20 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase): # first minute of 7/7 is the 600th trading minute for this asset # this asset's first minute had a close value of 2, so every value is # 2 + (minute index) - np.testing.assert_array_equal(range(587, 607), window) + expected = range(587, 607) + + # First 15 bars on occur at the end of 2014-07-03. + np.testing.assert_array_equal(window[:15], expected[:15]) + # Interim bars (only on other calendars) should all be nan. + np.testing.assert_array_equal( + window[15:-5], + np.full(len(window) - 20, np.nan), + ) + # Last 5 bars occur at the start of 2014-07-07. + np.testing.assert_array_equal(window[-5:], expected[-5:]) self.assertEqual( - window.index[-6], + window.index[14], pd.Timestamp('2014-07-03 17:00', tz='UTC') ) @@ -1209,34 +1308,58 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase): ) def test_minute_different_lifetimes(self): + cal = self.trading_calendar + equity_cal = self.trading_calendars[Equity] + # at trading start, only asset1 existed day = self.trading_calendar.next_session_label(self.TRADING_START_DT) - asset1_minutes = \ - self.trading_calendar.minutes_for_sessions_in_range( - self.ASSET1.start_date, - self.ASSET1.end_date - ) + # Range containing 100 equity minutes, possibly more on other + # calendars (i.e. futures). + window_start = pd.Timestamp('2014-01-03 19:22', tz='UTC') + window_end = pd.Timestamp('2014-01-06 14:31', tz='UTC') + bar_count = len(cal.minutes_in_range(window_start, window_end)) - asset1_idx = asset1_minutes.searchsorted( - self.trading_calendar.open_and_close_for_session(day)[0] + equity_cal = self.trading_calendars[Equity] + first_equity_open, _ = equity_cal.open_and_close_for_session(day) + + asset1_minutes = equity_cal.minutes_for_sessions_in_range( + self.ASSET1.start_date, + self.ASSET1.end_date ) + asset1_idx = asset1_minutes.searchsorted(first_equity_open) window = self.data_portal.get_history_window( [self.ASSET1, self.ASSET2], - self.trading_calendar.open_and_close_for_session(day)[0], - 100, + first_equity_open, + bar_count, '1m', 'close' ) + expected = range(asset1_idx - 97, asset1_idx + 3) + + # First 99 bars occur on the previous day, np.testing.assert_array_equal( - range(asset1_idx - 97, asset1_idx + 3), - window[self.ASSET1] + window[self.ASSET1][:99], + expected[:99], + ) + # Any interim bars are not active equity minutes, so should all + # be nan. + np.testing.assert_array_equal( + window[self.ASSET1][99:-1], + np.full(len(window) - 100, np.nan), + ) + # Final bar in the window is the first equity bar of `day`. + np.testing.assert_array_equal( + window[self.ASSET1][-1:], + expected[-1:], ) + # All NaNs for ASSET2, since it hasn't started yet. np.testing.assert_array_equal( - np.full(100, np.nan), window[self.ASSET2] + window[self.ASSET2], + np.full(len(window), np.nan), ) def test_history_window_before_first_trading_day(self): @@ -1264,11 +1387,17 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase): day = pd.Timestamp('2015-01-07', tz='UTC') minutes = self.trading_calendar.minutes_for_session(day) + equity_cal = self.trading_calendars[Equity] + equity_minutes = equity_cal.minutes_for_session(day) + equity_open, equity_close = equity_minutes[0], equity_minutes[-1] + # minute data, baseline: # Jan 5: 2 to 391 # Jan 6: 392 to 781 # Jan 7: 782 to 1172 - for idx, minute in enumerate(minutes): + for minute in minutes: + idx = equity_minutes.searchsorted(min(minute, equity_close)) + for field in ALL_FIELDS: window = self.data_portal.get_history_window( @@ -1299,7 +1428,20 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase): last_val = -1 - if field == 'open': + # XXX + if minute == day: + continue + + if minute < equity_open: + # If before the equity calendar open, we don't yet + # have data (but price is forward-filled). + if field == 'volume': + last_val = 0 + elif field == 'price': + last_val = window[1] + else: + last_val = nan + elif field == 'open': last_val = 783 elif field == 'high': # since we increase monotonically, it's just the last @@ -1317,7 +1459,7 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase): last_val = sum(np.array(range(782, 782 + idx + 1)) * 100) - self.assertEqual(window[-1], last_val) + np.testing.assert_equal(window[-1], last_val) @parameterized.expand(ALL_FIELDS) def test_daily_history_blended_gaps(self, field): @@ -1328,11 +1470,16 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase): day = pd.Timestamp('2015-01-08', tz='UTC') minutes = self.trading_calendar.minutes_for_session(day) + equity_cal = self.trading_calendars[Equity] + equity_minutes = equity_cal.minutes_for_session(day) + equity_open, equity_close = equity_minutes[0], equity_minutes[-1] + # minute data, baseline: # Jan 5: 2 to 391 # Jan 6: 392 to 781 # Jan 7: 782 to 1172 - for idx, minute in enumerate(minutes): + for minute in minutes: + idx = equity_minutes.searchsorted(min(minute, equity_close)) window = self.data_portal.get_history_window( [self.ASSET2], @@ -1363,9 +1510,22 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase): self.assertEqual(window[0], 22873500) self.assertEqual(window[1], 38083500) + # XXX + if minute == day: + continue + last_val = -1 - if field == 'open': + if minute < equity_open: + # If before the equity calendar open, we don't yet + # have data (but price is forward-filled). + if field == 'volume': + last_val = 0 + elif field == 'price': + last_val = window[1] + else: + last_val = nan + elif field == 'open': if idx == 0: last_val = np.nan else: @@ -1902,6 +2062,11 @@ class NoPrefetchDailyEquityHistoryTestCase(DailyEquityHistoryTestCase): DATA_PORTAL_DAILY_HISTORY_PREFETCH = 0 -class DailyEquityHistoryOnFuturesCalendarTestCase(DailyEquityHistoryTestCase): +class MinuteEquityHistoryFuturesCalendarTestCase(MinuteEquityHistoryTestCase): + TRADING_CALENDAR_STRS = ('NYSE', 'us_futures') + TRADING_CALENDAR_PRIMARY_CAL = 'us_futures' + + +class DailyEquityHistoryFuturesCalendarTestCase(DailyEquityHistoryTestCase): TRADING_CALENDAR_STRS = ('NYSE', 'us_futures') TRADING_CALENDAR_PRIMARY_CAL = 'us_futures'