From e934c6aeaf1c47509a83a5e1fd8d2e0eb6672bf8 Mon Sep 17 00:00:00 2001 From: Eddie Hebert Date: Tue, 2 Aug 2016 13:19:45 -0400 Subject: [PATCH] TST: Make room for multiple calendars in tests. When adding fixtures for futures data, there will be a need for multiple calendars in the fixture ecosystem. e.g. a test that includes both equities and futures would need an overall calendar which encompasses both equities and futures; however, the test data for equities should still still be limited to the bounds set by the NYSE calendar. Make the fixtures that setup trading calendars and values dervied from the trading calendar (e.g. trading sessions) accept an iterable of calendars which need to be created, then populate those values into a dict keyed by the calendar name. Change `WithNYSETradingDays` to include sessions in the name, since we are moving to session as the name for the 'day' unit. Provide `trading_days` which is really "NYSE trading sessions` on `WithTradingSessions` for backwards compatibility. --- tests/data/test_minute_bars.py | 5 +- tests/pipeline/base.py | 7 ++- tests/pipeline/test_events.py | 4 +- tests/test_algorithm.py | 4 +- tests/test_assets.py | 4 +- tests/test_benchmark.py | 4 +- tests/test_perf_tracking.py | 5 +- tests/test_security_list.py | 4 +- zipline/testing/fixtures.py | 103 ++++++++++++++++++++++++--------- 9 files changed, 97 insertions(+), 43 deletions(-) diff --git a/tests/data/test_minute_bars.py b/tests/data/test_minute_bars.py index a1253b29..aec62cce 100644 --- a/tests/data/test_minute_bars.py +++ b/tests/data/test_minute_bars.py @@ -45,7 +45,7 @@ from zipline.data.minute_bars import ( from zipline.testing.fixtures import ( WithInstanceTmpDir, - WithTradingCalendar, + WithTradingCalendars, ZiplineTestCase, ) @@ -56,7 +56,8 @@ TEST_CALENDAR_START = Timestamp('2014-06-02', tz='UTC') TEST_CALENDAR_STOP = Timestamp('2015-12-31', tz='UTC') -class BcolzMinuteBarTestCase(WithTradingCalendar, WithInstanceTmpDir, +class BcolzMinuteBarTestCase(WithTradingCalendars, + WithInstanceTmpDir, ZiplineTestCase): @classmethod diff --git a/tests/pipeline/base.py b/tests/pipeline/base.py index 31050bec..a5c8454f 100644 --- a/tests/pipeline/base.py +++ b/tests/pipeline/base.py @@ -17,7 +17,10 @@ from zipline.testing import ( ExplodingObject, tmp_asset_finder, ) -from zipline.testing.fixtures import ZiplineTestCase, WithTradingCalendar +from zipline.testing.fixtures import ( + WithTradingCalendars, + ZiplineTestCase, +) from zipline.utils.functional import dzip_exact from zipline.utils.pandas_utils import explode @@ -50,7 +53,7 @@ def with_defaults(**default_funcs): with_default_shape = with_defaults(shape=lambda self: self.default_shape) -class BasePipelineTestCase(WithTradingCalendar, ZiplineTestCase): +class BasePipelineTestCase(WithTradingCalendars, ZiplineTestCase): @classmethod def init_class_fixtures(cls): diff --git a/tests/pipeline/test_events.py b/tests/pipeline/test_events.py index 467c8da3..3b4831ae 100644 --- a/tests/pipeline/test_events.py +++ b/tests/pipeline/test_events.py @@ -27,7 +27,7 @@ from zipline.pipeline.loaders.utils import ( from zipline.testing import check_arrays, ZiplineTestCase from zipline.testing.fixtures import ( WithAssetFinder, - WithNYSETradingDays, + WithTradingSessions, ) from zipline.testing.predicates import assert_equal from zipline.utils.numpy_utils import ( @@ -268,7 +268,7 @@ class EventIndexerTestCase(ZiplineTestCase): class EventsLoaderTestCase(WithAssetFinder, - WithNYSETradingDays, + WithTradingSessions, ZiplineTestCase): START_DATE = pd.Timestamp('2014-01-01') diff --git a/tests/test_algorithm.py b/tests/test_algorithm.py index 0968df6f..069874f9 100644 --- a/tests/test_algorithm.py +++ b/tests/test_algorithm.py @@ -98,7 +98,7 @@ from zipline.testing.fixtures import ( WithSimParams, WithTradingEnvironment, WithTmpDir, - WithTradingCalendar, + WithTradingCalendars, ZiplineTestCase, ) from zipline.test_algorithms import ( @@ -3500,7 +3500,7 @@ class TestOrderCancelation(WithDataPortal, self.assertFalse(log_catcher.has_warnings) -class TestEquityAutoClose(WithTmpDir, WithTradingCalendar, ZiplineTestCase): +class TestEquityAutoClose(WithTmpDir, WithTradingCalendars, ZiplineTestCase): """ Tests if delisted equities are properly removed from a portfolio holding positions in said equities. diff --git a/tests/test_assets.py b/tests/test_assets.py index f1ba7991..8eb6489c 100644 --- a/tests/test_assets.py +++ b/tests/test_assets.py @@ -82,7 +82,7 @@ from zipline.testing.predicates import assert_equal from zipline.testing.fixtures import ( WithAssetFinder, ZiplineTestCase, - WithTradingCalendar, + WithTradingCalendars, ) from zipline.utils.range import range @@ -404,7 +404,7 @@ class TestFuture(WithAssetFinder, ZiplineTestCase): TestFuture.asset_finder.lookup_future_symbol('XXX99') -class AssetFinderTestCase(WithTradingCalendar, ZiplineTestCase): +class AssetFinderTestCase(WithTradingCalendars, ZiplineTestCase): asset_finder_type = AssetFinder def write_assets(self, **kwargs): diff --git a/tests/test_benchmark.py b/tests/test_benchmark.py index 950c0229..7abe4604 100644 --- a/tests/test_benchmark.py +++ b/tests/test_benchmark.py @@ -30,12 +30,12 @@ from zipline.testing import ( from zipline.testing.fixtures import ( WithDataPortal, WithSimParams, - WithTradingCalendar, + WithTradingCalendars, ZiplineTestCase, ) -class TestBenchmark(WithDataPortal, WithSimParams, WithTradingCalendar, +class TestBenchmark(WithDataPortal, WithSimParams, WithTradingCalendars, ZiplineTestCase): START_DATE = pd.Timestamp('2006-01-03', tz='utc') END_DATE = pd.Timestamp('2006-12-29', tz='utc') diff --git a/tests/test_perf_tracking.py b/tests/test_perf_tracking.py index cc36b418..7e053720 100644 --- a/tests/test_perf_tracking.py +++ b/tests/test_perf_tracking.py @@ -57,7 +57,7 @@ from zipline.testing.fixtures import ( WithSimParams, WithTmpDir, WithTradingEnvironment, - WithTradingCalendar, + WithTradingCalendars, ZiplineTestCase, ) from zipline.utils.calendars import get_calendar @@ -1031,8 +1031,9 @@ class TestDividendPerformanceHolidayStyle(TestDividendPerformance): END_DATE = pd.Timestamp('2003-12-08', tz='utc') -class TestPositionPerformance(WithInstanceTmpDir, WithTradingCalendar, +class TestPositionPerformance(WithInstanceTmpDir, WithTradingCalendars, ZiplineTestCase): + def create_environment_stuff(self, num_days=4, sids=[1, 2], diff --git a/tests/test_security_list.py b/tests/test_security_list.py index 4d699ff7..f0609142 100644 --- a/tests/test_security_list.py +++ b/tests/test_security_list.py @@ -14,7 +14,7 @@ from zipline.testing import ( ) from zipline.testing.fixtures import ( WithLogger, - WithTradingCalendar, + WithTradingCalendars, ZiplineTestCase, ) from zipline.utils import factory @@ -67,7 +67,7 @@ class IterateRLAlgo(TradingAlgorithm): self.found = True -class SecurityListTestCase(WithLogger, WithTradingCalendar, ZiplineTestCase): +class SecurityListTestCase(WithLogger, WithTradingCalendars, ZiplineTestCase): @classmethod def init_class_fixtures(cls): diff --git a/zipline/testing/fixtures.py b/zipline/testing/fixtures.py index 040ace99..2a8b3294 100644 --- a/zipline/testing/fixtures.py +++ b/zipline/testing/fixtures.py @@ -3,7 +3,7 @@ from unittest import TestCase from contextlib2 import ExitStack from logbook import NullHandler, Logger -from six import with_metaclass +from six import with_metaclass, iteritems from toolz import flip import pandas as pd import responses @@ -365,28 +365,58 @@ class WithAssetFinder(WithDefaultDateBounds): cls.asset_finder = cls.make_asset_finder() -class WithTradingCalendar(object): +class WithTradingCalendars(object): """ - ZiplineTestCase mixing providing cls.trading_calendar as a class-level - fixture. + ZiplineTestCase mixin providing cls.trading_calendar, + cls.all_trading_calendars, cls.trading_calendar_for_asset_type as a + class-level fixture. - After ``init_class_fixtures`` has been called, `cls.trading_calendar` is - populated with a trading calendar. + After ``init_class_fixtures`` has been called: + - `cls.trading_calendar` is populated with a default of the nyse trading + calendar for compatibility with existing tests + - `cls.all_trading_calendars` is populated with the trading calendars + keyed by name, + - `cls.trading_calendar_for_asset_type` is populated with the trading + calendars keyed by the asset type which uses the respective calendar. Attributes ---------- - TRADING_CALENDAR_STR : str - The identifier of the calendar to use. + TRADING_CALENDAR_STRS : iterable + iterable of identifiers of the calendars to use. + TRADING_CALENDAR_FOR_ASSET_TYPE : dict + A dictionay which maps asset type names to the calendar associated + with that asset type. """ - TRADING_CALENDAR_STR = 'NYSE' + TRADING_CALENDAR_STRS = ('NYSE',) + TRADING_CALENDAR_FOR_ASSET_TYPE = {'equities': 'NYSE'} + + # For backwards compatibility, exisitng tests and fixtures refer to + # `trading_calendar` with the assumption that the value is the NYSE + # calendar. + trading_calendar = alias('nyse_calendar') @classmethod def init_class_fixtures(cls): - super(WithTradingCalendar, cls).init_class_fixtures() - cls.trading_calendar = get_calendar(cls.TRADING_CALENDAR_STR) + super(WithTradingCalendars, cls).init_class_fixtures() + + cls.trading_calendars = {} + + for cal_str in cls.TRADING_CALENDAR_STRS: + # Set name to allow aliasing. + calendar = get_calendar(cal_str) + setattr(cls, + '{0}_calendar'.format(cal_str.lower()), calendar) + cls.trading_calendars[cal_str] = calendar + for asset_type, cal_str in iteritems( + cls.TRADING_CALENDAR_FOR_ASSET_TYPE): + calendar = get_calendar(cal_str) + setattr(cls, + '{0}_calendar'.format(asset_type), + calendar) + cls.trading_calendars[asset_type] = calendar -class WithTradingEnvironment(WithAssetFinder, WithTradingCalendar): +class WithTradingEnvironment(WithAssetFinder, WithTradingCalendars): """ ZiplineTestCase mixin providing cls.env as a class-level fixture. @@ -493,15 +523,21 @@ class WithSimParams(WithTradingEnvironment): cls.sim_params = cls.make_simparams() -class WithNYSETradingDays(WithTradingCalendar): +class WithTradingSessions(WithTradingCalendars): """ - ZiplineTestCase mixin providing cls.trading_days as a class-level fixture. + ZiplineTestCase mixin providing cls.trading_days, cls.all_trading_sessions + as a class-level fixture. - After init_class_fixtures has been called, `cls.trading_days` is populated - with a DatetimeIndex containing NYSE calendar trading days ranging from: + After init_class_fixtures has been called, `cls.all_trading_sessions` + is populated with a dictionary of calendar name to the DatetimeIndex + containing the calendar trading days ranging from: (DATA_MAX_DAY - (cls.TRADING_DAY_COUNT) -> DATA_MAX_DAY) + `cls.trading_days`, for compatibility with existing tests which make the + assumption that trading days are equity only, defaults to the nyse trading + sessions. + Attributes ---------- DATA_MAX_DAY : datetime @@ -514,15 +550,27 @@ class WithNYSETradingDays(WithTradingCalendar): DATA_MIN_DAY = alias('START_DATE') DATA_MAX_DAY = alias('END_DATE') + # For backwards compatibility, exisitng tests and fixtures refer to + # `trading_days` with the assumption that the value is days of the NYSE + # calendar. + trading_days = alias('nyse_sessions') + @classmethod def init_class_fixtures(cls): - super(WithNYSETradingDays, cls).init_class_fixtures() + super(WithTradingSessions, cls).init_class_fixtures() - all_days = cls.trading_calendar.all_sessions - start_loc = all_days.get_loc(cls.DATA_MIN_DAY, 'bfill') - end_loc = all_days.get_loc(cls.DATA_MAX_DAY, 'ffill') + cls.trading_sessions = {} - cls.trading_days = all_days[start_loc:end_loc + 1] + for name, trading_calendar in iteritems(cls.trading_calendars): + all_sessions = trading_calendar.all_sessions + start_loc = all_sessions.get_loc(cls.DATA_MIN_DAY, 'bfill') + end_loc = all_sessions.get_loc(cls.DATA_MAX_DAY, 'ffill') + + sessions = all_sessions[start_loc:end_loc + 1] + # Set name for aliasing. + setattr(cls, + '{0}_sessions'.format(name.lower()), sessions) + cls.trading_sessions[name] = sessions class WithTmpDir(object): @@ -814,8 +862,9 @@ class WithEquityMinuteBarData(WithTradingEnvironment): @classmethod def make_equity_minute_bar_data(cls): + trading_calendar = cls.equities_calendar return create_minute_bar_data( - cls.trading_calendar.minutes_for_sessions_in_range( + trading_calendar.minutes_for_sessions_in_range( cls.equity_minute_bar_days[0], cls.equity_minute_bar_days[-1], ), @@ -826,19 +875,19 @@ class WithEquityMinuteBarData(WithTradingEnvironment): def init_class_fixtures(cls): super(WithEquityMinuteBarData, cls).init_class_fixtures() if cls.EQUITY_MINUTE_BAR_USE_FULL_CALENDAR: - days = cls.trading_calendar.all_execution_days + days = cls.equites_calendar.all_execution_days else: - first_session = cls.trading_calendar.minute_to_session_label( + first_session = cls.equities_calendar.minute_to_session_label( pd.Timestamp(cls.EQUITY_MINUTE_BAR_START_DATE) ) if cls.EQUITY_MINUTE_BAR_LOOKBACK_DAYS > 0: - first_session = cls.trading_calendar.sessions_window( + first_session = cls.equities_calendar.sessions_window( first_session, -1 * cls.EQUITY_MINUTE_BAR_LOOKBACK_DAYS )[0] - days = cls.trading_calendar.sessions_in_range( + days = cls.equities_calendar.sessions_in_range( first_session, cls.EQUITY_MINUTE_BAR_END_DATE ) @@ -1000,7 +1049,7 @@ class WithAdjustmentReader(WithBcolzEquityDailyBarReader): cls.adjustment_reader = SQLiteAdjustmentReader(conn) -class WithSeededRandomPipelineEngine(WithNYSETradingDays, WithAssetFinder): +class WithSeededRandomPipelineEngine(WithTradingSessions, WithAssetFinder): """ ZiplineTestCase mixin providing class-level fixtures for running pipelines against deterministically-generated random data.