diff --git a/tests/test_data_portal.py b/tests/test_data_portal.py index b69c1979..345e0fc7 100644 --- a/tests/test_data_portal.py +++ b/tests/test_data_portal.py @@ -23,13 +23,14 @@ from zipline.assets import Equity from zipline.testing.fixtures import ( ZiplineTestCase, WithTradingSessions, - WithDataPortal + WithDataPortal, + alias, ) -class TestDataPortal(WithDataPortal, - WithTradingSessions, - ZiplineTestCase): +class DataPortalTestBase(WithDataPortal, + WithTradingSessions, + ZiplineTestCase): ASSET_FINDER_EQUITY_SIDS = (1,) START_DATE = pd.Timestamp('2016-08-01') @@ -374,3 +375,13 @@ class TestDataPortal(WithDataPortal, self.assertEqual(minutes[4], result, "Asset 10000 had a trade on fourth minute, so should " "return that as the last trade on the fifth.") + + +class TestDataPortal(DataPortalTestBase): + DATA_PORTAL_LAST_AVAILABLE_SESSION = None + DATA_PORTAL_LAST_AVAILABLE_MINUTE = None + + +class TestDataPortalExplicitLastAvailable(DataPortalTestBase): + DATA_PORTAL_LAST_AVAILABLE_SESSION = alias('START_DATE') + DATA_PORTAL_LAST_AVAILABLE_MINUTE = alias('END_DATE') diff --git a/zipline/data/data_portal.py b/zipline/data/data_portal.py index 8e9ca304..956fe61a 100644 --- a/zipline/data/data_portal.py +++ b/zipline/data/data_portal.py @@ -104,6 +104,10 @@ class DataPortal(object): adjustment_reader : SQLiteAdjustmentWriter, optional The adjustment reader. This is used to apply splits, dividends, and other adjustment data to the raw data from the readers. + last_available_session : pd.Timestamp, optional + The last session to make available in session-level data. + last_available_minute : pd.Timestamp, optional + The last minute to make available in minute-level data. """ def __init__(self, asset_finder, @@ -113,7 +117,9 @@ class DataPortal(object): equity_minute_reader=None, future_daily_reader=None, future_minute_reader=None, - adjustment_reader=None): + adjustment_reader=None, + last_available_session=None, + last_available_minute=None): self.trading_calendar = trading_calendar self.asset_finder = asset_finder @@ -169,12 +175,14 @@ class DataPortal(object): self.trading_calendar, self.asset_finder, aligned_minute_readers, + last_available_minute, ) _dispatch_session_reader = AssetDispatchSessionBarReader( self.trading_calendar, self.asset_finder, aligned_session_readers, + last_available_session, ) self._pricing_readers = { diff --git a/zipline/data/dispatch_bar_reader.py b/zipline/data/dispatch_bar_reader.py index a80e66b6..dab38439 100644 --- a/zipline/data/dispatch_bar_reader.py +++ b/zipline/data/dispatch_bar_reader.py @@ -35,11 +35,21 @@ class AssetDispatchBarReader(with_metaclass(ABCMeta)): - readers : dict A dict mapping Asset type to the corresponding [Minute|Session]BarReader + - last_available_dt : pd.Timestamp or None, optional + If not provided, infers it by using the min of the + last_available_dt values of the underlying readers. """ - def __init__(self, trading_calendar, asset_finder, readers): + def __init__( + self, + trading_calendar, + asset_finder, + readers, + last_available_dt=None, + ): self._trading_calendar = trading_calendar self._asset_finder = asset_finder self._readers = readers + self._last_available_dt = last_available_dt for t, r in iteritems(self._readers): assert trading_calendar == r.trading_calendar, \ @@ -72,7 +82,10 @@ class AssetDispatchBarReader(with_metaclass(ABCMeta)): @lazyval def last_available_dt(self): - return min(r.last_available_dt for r in self._readers.values()) + if self._last_available_dt is not None: + return self._last_available_dt + else: + return min(r.last_available_dt for r in self._readers.values()) @lazyval def first_trading_day(self): diff --git a/zipline/testing/fixtures.py b/zipline/testing/fixtures.py index 3570d255..98709ab8 100644 --- a/zipline/testing/fixtures.py +++ b/zipline/testing/fixtures.py @@ -1265,6 +1265,9 @@ class WithDataPortal(WithAdjustmentReader, DATA_PORTAL_FIRST_TRADING_DAY = None + DATA_PORTAL_LAST_AVAILABLE_SESSION = None + DATA_PORTAL_LAST_AVAILABLE_MINUTE = None + def make_data_portal(self): if self.DATA_PORTAL_FIRST_TRADING_DAY is None: if self.DATA_PORTAL_USE_MINUTE_DATA: @@ -1300,6 +1303,8 @@ class WithDataPortal(WithAdjustmentReader, if self.DATA_PORTAL_USE_MINUTE_DATA else None ), + last_available_session=self.DATA_PORTAL_LAST_AVAILABLE_SESSION, + last_available_minute=self.DATA_PORTAL_LAST_AVAILABLE_MINUTE, ) def init_instance_fixtures(self):