From 9fd8ddda530786ef315ca0e43ef53729961bc57d Mon Sep 17 00:00:00 2001 From: Eddie Hebert Date: Tue, 20 Sep 2016 16:24:18 -0400 Subject: [PATCH] MAINT: Remove `future_chain` API method. `future_chain` will be replaced by the as yet to be implemented method, `data.current_chain` Also removing `FutureChain` which will be replaced by another version which only supports indexing and iteration. --- docs/source/appendix.rst | 2 - tests/test_algorithm.py | 103 --------------- tests/test_assets.py | 267 -------------------------------------- zipline/algorithm.py | 63 --------- zipline/assets/assets.py | 57 -------- zipline/assets/futures.py | 158 ---------------------- 6 files changed, 650 deletions(-) diff --git a/docs/source/appendix.rst b/docs/source/appendix.rst index 25142ba1..2da8f1b2 100644 --- a/docs/source/appendix.rst +++ b/docs/source/appendix.rst @@ -87,8 +87,6 @@ Assets .. autofunction:: zipline.api.future_symbol -.. autofunction:: zipline.api.future_chain - .. autofunction:: zipline.api.set_symbol_lookup_date .. autofunction:: zipline.api.sid diff --git a/tests/test_algorithm.py b/tests/test_algorithm.py index 8ec6a0f8..39403fd2 100644 --- a/tests/test_algorithm.py +++ b/tests/test_algorithm.py @@ -58,7 +58,6 @@ from zipline.errors import ( TradingControlViolation, AccountControlViolation, SymbolNotFound, - RootSymbolNotFound, UnsupportedDatetimeFormat, CannotOrderDelistedAsset, SetCancelPolicyPostInit, @@ -726,108 +725,6 @@ def log_nyse_close(context, data): with self.assertRaises(TypeError): algo.future_symbol({'foo': 'bar'}) - def test_future_chain_offset(self): - # November 2006 December 2006 - # Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa - # 1 2 3 4 1 2 - # 5 6 7 8 9 10 11 3 4 5 6 7 8 9 - # 12 13 14 15 16 17 18 10 11 12 13 14 15 16 - # 19 20 21 22 23 24 25 17 18 19 20 21 22 23 - # 26 27 28 29 30 24 25 26 27 28 29 30 - # 31 - - algo = TradingAlgorithm(env=self.env) - algo.datetime = pd.Timestamp('2006-12-01', tz='UTC') - - self.assertEqual( - algo.future_chain('CL', offset=1).as_of_date, - pd.Timestamp("2006-12-04", tz='UTC') - ) - - self.assertEqual( - algo.future_chain("CL", offset=5).as_of_date, - pd.Timestamp("2006-12-08", tz='UTC') - ) - - self.assertEqual( - algo.future_chain("CL", offset=-10).as_of_date, - pd.Timestamp("2006-11-16", tz='UTC') - ) - - # September 2016 - # Su Mo Tu We Th Fr Sa - # 1 2 3 - # 4 5 6 7 8 9 10 - # 11 12 13 14 15 16 17 - # 18 19 20 21 22 23 24 - # 25 26 27 28 29 30 - self.assertEqual( - algo.future_chain( - "CL", - as_of_date=pd.Timestamp("2016-08-31", tz='UTC'), - offset=10 - ).as_of_date, - pd.Timestamp("2016-09-15", tz='UTC') - ) - - def test_future_chain(self): - algo = TradingAlgorithm(env=self.env) - algo.datetime = pd.Timestamp('2006-12-01', tz='UTC') - - # Check that the fields of the FutureChain object are set correctly - cl = algo.future_chain('CL') - self.assertEqual(cl.root_symbol, 'CL') - self.assertEqual(cl.as_of_date, algo.datetime) - - # Check the fields are set correctly if an as_of_date is supplied - as_of_date = pd.Timestamp('1952-08-11', tz='UTC') - - cl = algo.future_chain('CL', as_of_date=as_of_date) - self.assertEqual(cl.root_symbol, 'CL') - self.assertEqual(cl.as_of_date, as_of_date) - - cl = algo.future_chain('CL', as_of_date='1952-08-11') - self.assertEqual(cl.root_symbol, 'CL') - self.assertEqual(cl.as_of_date, as_of_date) - - # Check that weird capitalization is corrected - cl = algo.future_chain('cL') - self.assertEqual(cl.root_symbol, 'CL') - - cl = algo.future_chain('cl') - self.assertEqual(cl.root_symbol, 'CL') - - # Check that invalid root symbols raise RootSymbolNotFound - with self.assertRaises(RootSymbolNotFound): - algo.future_chain('CLZ') - - with self.assertRaises(RootSymbolNotFound): - algo.future_chain('') - - # Check that invalid dates raise UnsupportedDatetimeFormat - with self.assertRaises(UnsupportedDatetimeFormat): - algo.future_chain('CL', 'my_finger_slipped') - - with self.assertRaises(UnsupportedDatetimeFormat): - algo.future_chain('CL', '2015-09-') - - # Supplying a non-string argument to future_chain() - # should result in a TypeError. - with self.assertRaises(TypeError): - algo.future_chain(1) - - with self.assertRaises(TypeError): - algo.future_chain((1,)) - - with self.assertRaises(TypeError): - algo.future_chain({1}) - - with self.assertRaises(TypeError): - algo.future_chain([1]) - - with self.assertRaises(TypeError): - algo.future_chain({'foo': 'bar'}) - def test_set_symbol_lookup_date(self): """ Test the set_symbol_lookup_date API method. diff --git a/tests/test_assets.py b/tests/test_assets.py index a4cdfa3c..68c0e3d5 100644 --- a/tests/test_assets.py +++ b/tests/test_assets.py @@ -48,11 +48,6 @@ from zipline.assets.synthetic import ( from six import itervalues, integer_types from toolz import valmap -from zipline.assets.futures import ( - cme_code_to_month, - FutureChain, - month_to_cme_code -) from zipline.assets.asset_writer import ( check_version_info, write_version_info, @@ -67,7 +62,6 @@ from zipline.errors import ( EquitiesNotFound, FutureContractsNotFound, MultipleSymbolsFound, - RootSymbolNotFound, AssetDBVersionError, SidsNotFound, SymbolNotFound, @@ -883,94 +877,6 @@ class AssetFinderTestCase(WithTradingCalendars, ZiplineTestCase): self.assertTrue(issubclass(warning.category, DeprecationWarning)) - def test_lookup_future_chain(self): - metadata = pd.DataFrame.from_records([ - # Notice day is today, so should be valid. - { - 'symbol': 'ADN15', - 'root_symbol': 'AD', - 'notice_date': pd.Timestamp('2015-06-14', tz='UTC'), - 'expiration_date': pd.Timestamp('2015-08-14', tz='UTC'), - 'start_date': pd.Timestamp('2015-01-01', tz='UTC'), - 'exchange': "TEST", - }, - { - 'symbol': 'ADV15', - 'root_symbol': 'AD', - 'notice_date': pd.Timestamp('2015-05-14', tz='UTC'), - 'expiration_date': pd.Timestamp('2015-09-14', tz='UTC'), - 'start_date': pd.Timestamp('2015-01-01', tz='UTC'), - 'exchange': "TEST", - }, - # Starts trading today, so should be valid. - { - 'symbol': 'ADF16', - 'root_symbol': 'AD', - 'notice_date': pd.Timestamp('2015-11-16', tz='UTC'), - 'expiration_date': pd.Timestamp('2015-12-16', tz='UTC'), - 'start_date': pd.Timestamp('2015-05-14', tz='UTC'), - 'exchange': "TEST", - }, - # Starts trading in August, so not valid. - { - 'symbol': 'ADX16', - 'root_symbol': 'AD', - 'notice_date': pd.Timestamp('2015-11-16', tz='UTC'), - 'expiration_date': pd.Timestamp('2015-12-16', tz='UTC'), - 'start_date': pd.Timestamp('2015-08-01', tz='UTC'), - 'exchange': "TEST", - }, - # Notice date comes after expiration - { - 'symbol': 'ADZ16', - 'root_symbol': 'AD', - 'notice_date': pd.Timestamp('2016-11-25', tz='UTC'), - 'expiration_date': pd.Timestamp('2016-11-16', tz='UTC'), - 'start_date': pd.Timestamp('2015-08-01', tz='UTC'), - 'exchange': "TEST", - }, - # This contract has no start date and also this contract should be - # last in all chains - { - 'symbol': 'ADZ20', - 'root_symbol': 'AD', - 'notice_date': pd.Timestamp('2020-11-25', tz='UTC'), - 'expiration_date': pd.Timestamp('2020-11-16', tz='UTC'), - 'exchange': "TEST", - }, - ]) - self.write_assets(futures=metadata) - finder = self.asset_finder - dt = pd.Timestamp('2015-05-14', tz='UTC') - dt_2 = pd.Timestamp('2015-10-14', tz='UTC') - dt_3 = pd.Timestamp('2016-11-17', tz='UTC') - - # Check that we get the expected number of contracts, in the - # right order - ad_contracts = finder.lookup_future_chain('AD', dt) - self.assertEqual(len(ad_contracts), 6) - self.assertEqual(ad_contracts[0].sid, 1) - self.assertEqual(ad_contracts[1].sid, 0) - self.assertEqual(ad_contracts[5].sid, 5) - - # Check that, when some contracts have expired, the chain has advanced - # properly to the next contracts - ad_contracts = finder.lookup_future_chain('AD', dt_2) - self.assertEqual(len(ad_contracts), 4) - self.assertEqual(ad_contracts[0].sid, 2) - self.assertEqual(ad_contracts[3].sid, 5) - - # Check that when the expiration_date has passed but the - # notice_date hasn't, contract is still considered invalid. - ad_contracts = finder.lookup_future_chain('AD', dt_3) - self.assertEqual(len(ad_contracts), 1) - self.assertEqual(ad_contracts[0].sid, 5) - - # Check that pd.NaT for as_of_date gives the whole chain - ad_contracts = finder.lookup_future_chain('AD', pd.NaT) - self.assertEqual(len(ad_contracts), 6) - self.assertEqual(ad_contracts[5].sid, 5) - def test_map_identifier_index_to_sids(self): # Build an empty finder and some Assets dt = pd.Timestamp('2014-01-01', tz='UTC') @@ -1218,179 +1124,6 @@ class AssetFinderTestCase(WithTradingCalendars, ZiplineTestCase): ) -class TestFutureChain(WithAssetFinder, ZiplineTestCase): - @classmethod - def make_futures_info(cls): - return pd.DataFrame.from_records([ - { - 'symbol': 'CLG06', - 'root_symbol': 'CL', - 'start_date': pd.Timestamp('2005-12-01', tz='UTC'), - 'notice_date': pd.Timestamp('2005-12-20', tz='UTC'), - 'expiration_date': pd.Timestamp('2006-01-20', tz='UTC'), - 'exchange': "TEST", - }, - { - 'root_symbol': 'CL', - 'symbol': 'CLK06', - 'start_date': pd.Timestamp('2005-12-01', tz='UTC'), - 'notice_date': pd.Timestamp('2006-03-20', tz='UTC'), - 'expiration_date': pd.Timestamp('2006-04-20', tz='UTC'), - 'exchange': "TEST", - }, - { - 'symbol': 'CLQ06', - 'root_symbol': 'CL', - 'start_date': pd.Timestamp('2005-12-01', tz='UTC'), - 'notice_date': pd.Timestamp('2006-06-20', tz='UTC'), - 'expiration_date': pd.Timestamp('2006-07-20', tz='UTC'), - 'exchange': "TEST", - }, - { - 'symbol': 'CLX06', - 'root_symbol': 'CL', - 'start_date': pd.Timestamp('2006-02-01', tz='UTC'), - 'notice_date': pd.Timestamp('2006-09-20', tz='UTC'), - 'expiration_date': pd.Timestamp('2006-10-20', tz='UTC'), - 'exchange': "TEST", - } - ]) - - def _get_future_chain(self, date_str, symbol): - dt = pd.Timestamp(date_str, tz='UTC') - - return FutureChain( - symbol, - dt, - self.asset_finder.lookup_future_chain(symbol, dt) - ) - - def test_len(self): - """ Test the __len__ method of FutureChain. - """ - # Sids 0, 1, & 2 have started, 3 has not yet started, but all are in - # the chain - cl = self._get_future_chain('2005-12-01', 'CL') - self.assertEqual(len(cl), 4) - - # Sid 0 is still valid on its notice date. - cl = self._get_future_chain('2005-12-20', 'CL') - self.assertEqual(len(cl), 4) - - # Sid 0 is now invalid, leaving Sids 1 & 2 valid (and 3 not started). - cl = self._get_future_chain('2005-12-21', 'CL') - self.assertEqual(len(cl), 3) - - # Sid 3 has started, so 1, 2, & 3 are now valid. - cl = self._get_future_chain('2006-02-01', 'CL') - self.assertEqual(len(cl), 3) - - # All contracts are no longer valid. - cl = self._get_future_chain('2006-09-21', 'CL') - self.assertEqual(len(cl), 0) - - def test_getitem(self): - """ Test the __getitem__ method of FutureChain. - """ - cl = self._get_future_chain('2005-12-01', 'CL') - self.assertEqual(cl[0], 0) - self.assertEqual(cl[1], 1) - self.assertEqual(cl[2], 2) - - cl = self._get_future_chain('2005-12-20', 'CL') - self.assertEqual(cl[0], 0) - - cl = self._get_future_chain('2005-12-21', 'CL') - self.assertEqual(cl[0], 1) - - cl = self._get_future_chain('2006-02-01', 'CL') - self.assertEqual(cl[-1], 3) - - def test_iter(self): - """ Test the __iter__ method of FutureChain. - """ - cl = self._get_future_chain('2005-12-01', 'CL') - for i, contract in enumerate(cl): - self.assertEqual(contract, i) - - # First contract is now invalid, so sids will be offset by one - cl = self._get_future_chain('2005-12-21', 'CL') - for i, contract in enumerate(cl): - self.assertEqual(contract, i + 1) - - def test_root_symbols(self): - """ Test that different variations on root symbols are handled - as expected. - """ - # Make sure this successfully gets the chain for CL. - cl = self._get_future_chain('2005-12-01', 'CL') - self.assertEqual(cl.root_symbol, 'CL') - - # These root symbols don't exist, so RootSymbolNotFound should - # be raised immediately. - with self.assertRaises(RootSymbolNotFound): - self._get_future_chain('2005-12-01', 'CLZ') - - with self.assertRaises(RootSymbolNotFound): - self._get_future_chain('2005-12-01', '') - - def test_repr(self): - """ Test the __repr__ method of FutureChain. - """ - cl = self._get_future_chain('2005-12-01', 'CL') - - self.assertEqual( - repr(cl), - "FutureChain('CL', '2005-12-01')" - ) - - def test_contracts_returns_a_copy(self): - cl = self._get_future_chain('2005-12-01', 'CL') - self.assertEqual(len(cl), 4) - - contracts = cl.contracts - contracts.pop(0) - - self.assertEqual(len(contracts), 3) - self.assertEqual(len(cl), 4) - - def test_cme_code_to_month(self): - codes = { - 'F': 1, # January - 'G': 2, # February - 'H': 3, # March - 'J': 4, # April - 'K': 5, # May - 'M': 6, # June - 'N': 7, # July - 'Q': 8, # August - 'U': 9, # September - 'V': 10, # October - 'X': 11, # November - 'Z': 12 # December - } - for key in codes: - self.assertEqual(codes[key], cme_code_to_month(key)) - - def test_month_to_cme_code(self): - codes = { - 1: 'F', # January - 2: 'G', # February - 3: 'H', # March - 4: 'J', # April - 5: 'K', # May - 6: 'M', # June - 7: 'N', # July - 8: 'Q', # August - 9: 'U', # September - 10: 'V', # October - 11: 'X', # November - 12: 'Z', # December - } - for key in codes: - self.assertEqual(codes[key], month_to_cme_code(key)) - - class TestAssetDBVersioning(ZiplineTestCase): def init_instance_fixtures(self): diff --git a/zipline/algorithm.py b/zipline/algorithm.py index 3b94ca3c..35de317c 100644 --- a/zipline/algorithm.py +++ b/zipline/algorithm.py @@ -82,7 +82,6 @@ from zipline.finance.slippage import ( ) from zipline.finance.cancel_policy import NeverCancel, CancelPolicy from zipline.assets import Asset, Future -from zipline.assets.futures import FutureChain from zipline.gens.tradesimulation import AlgorithmSimulator from zipline.pipeline import Pipeline from zipline.pipeline.engine import ( @@ -1245,68 +1244,6 @@ class TradingAlgorithm(object): """ return self.asset_finder.lookup_future_symbol(symbol) - @api_method - @preprocess(root_symbol=ensure_upper_case) - def future_chain(self, root_symbol, as_of_date=None, offset=0): - """ - Look up a future chain. - - Parameters - ---------- - root_symbol : str - The root symbol of a future chain. - as_of_date : datetime.datetime or pandas.Timestamp or str, optional - Date at which the chain determination is rooted. If this date is - not passed in, the current simulation session (not minute) is used. - offset: int - Number of sessions to shift `as_of_date`. Positive values shift - forward in time. Negative values shift backward in time. - - Returns - ------- - chain : FutureChain - The future chain matching the specified parameters. - - Raises - ------ - RootSymbolNotFound - If a future chain could not be found for the given root symbol. - """ - if as_of_date: - try: - as_of_date = pd.Timestamp(as_of_date, tz='UTC') - except ValueError: - raise UnsupportedDatetimeFormat( - input=as_of_date, - method='future_chain' - ) - else: - as_of_date = self.trading_calendar.minute_to_session_label( - self.get_datetime() - ) - - if offset != 0: - # move as_of_date by offset sessions - session_window = self.trading_calendar.sessions_window( - as_of_date, offset - ) - - if offset > 0: - as_of_date = session_window[-1] - else: - as_of_date = session_window[0] - - chain_of_contracts = self.asset_finder.lookup_future_chain( - root_symbol, - as_of_date - ) - - return FutureChain( - root_symbol=root_symbol, - as_of_date=as_of_date, - contracts=chain_of_contracts - ) - def _calculate_order_value_amount(self, asset, value): """ Calculates how many shares/contracts to order based on the type of diff --git a/zipline/assets/assets.py b/zipline/assets/assets.py index 000a0fc7..411afd09 100644 --- a/zipline/assets/assets.py +++ b/zipline/assets/assets.py @@ -40,7 +40,6 @@ from zipline.errors import ( FutureContractsNotFound, MapAssetIdentifierIndexError, MultipleSymbolsFound, - RootSymbolNotFound, SidsNotFound, SymbolNotFound, ) @@ -741,62 +740,6 @@ class AssetFinder(object): raise SymbolNotFound(symbol=symbol) return self.retrieve_asset(data['sid']) - def lookup_future_chain(self, root_symbol, as_of_date): - """ Return the futures chain for a given root symbol. - - Parameters - ---------- - root_symbol : str - Root symbol of the desired future. - - as_of_date : pd.Timestamp or pd.NaT - Date at which the chain determination is rooted. I.e. the - existing contract whose notice date/expiration date is first - after this date is the primary contract, etc. If NaT is - given, the chain is unbounded, and all contracts for this - root symbol are returned. - - Returns - ------- - list - A list of Future objects, the chain for the given - parameters. - - Raises - ------ - RootSymbolNotFound - Raised when a future chain could not be found for the given - root symbol. - """ - fc_cols = self.futures_contracts.c - - if as_of_date is pd.NaT: - # If the as_of_date is NaT, get all contracts for this - # root symbol. - sids = list(map( - itemgetter('sid'), - sa.select((fc_cols.sid,)).where( - (fc_cols.root_symbol == root_symbol), - ).order_by( - fc_cols.notice_date.asc(), - ).execute().fetchall())) - else: - sids = self._get_future_sids_for_root_symbol( - root_symbol, - as_of_date.value - ) - - if not sids: - # Check if root symbol exists. - count = sa.select((sa.func.count(fc_cols.sid),)).where( - fc_cols.root_symbol == root_symbol, - ).scalar() - if count == 0: - raise RootSymbolNotFound(root_symbol=root_symbol) - - contracts = self.retrieve_futures_contracts(sids) - return [contracts[sid] for sid in sids] - @weak_lru_cache(100) def _get_future_sids_for_root_symbol(self, root_symbol, as_of_date_ns): fc_cols = self.futures_contracts.c diff --git a/zipline/assets/futures.py b/zipline/assets/futures.py index 9dcaff63..fa43c5c9 100644 --- a/zipline/assets/futures.py +++ b/zipline/assets/futures.py @@ -13,164 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pandas import Timestamp - -from zipline.utils.input_validation import expect_types - - -class FutureChain(object): - """ - Allows users to look up future contracts. - - Parameters - ---------- - root_symbol : str - The root symbol of a future chain. - as_of_date : pandas.Timestamp - Date at which the chain determination is rooted. I.e. the - existing contract whose notice date is first after this date is - the primary contract, etc. - chain: list - List of assets that represent the chain of contracts for the given - root symbol at the given as_of_date. - - Attributes - ---------- - root_symbol : str - The root symbol of the future chain. - as_of_date: Timestamp - The current as-of date of this future chain. - """ - @expect_types(root_symbol=str, as_of_date=Timestamp) - def __init__(self, root_symbol, as_of_date, contracts): - self._root_symbol = root_symbol - self._as_of_date = as_of_date - self._contracts = contracts - - def __repr__(self): - return "FutureChain('%s', '%s')" % ( - self.root_symbol, self.as_of_date.strftime('%Y-%m-%d')) - - @property - def root_symbol(self): - """ - The root symbol for this future chain. - - Returns - ------- - root_symbol: str - The root symbol for this chain. - """ - return self._root_symbol - - @property - def as_of_date(self): - """ - The as-of date of this future chain. - - Returns - ------- - as_of_date: pd.Timestamp - The as_of date for this chain. - """ - return self._as_of_date - - @property - def contracts(self): - """ - Returns - ------- - contracts: list - The contracts wrapped by this chain. - """ - return list(self._contracts) - - def __getitem__(self, key): - return self._contracts[key] - - def __len__(self): - return len(self._contracts) - - def __iter__(self): - return iter(self._contracts) - - # http://www.cmegroup.com/product-codes-listing/month-codes.html CME_CODE_TO_MONTH = dict(zip('FGHJKMNQUVXZ', range(1, 13))) MONTH_TO_CME_CODE = dict(zip(range(1, 13), 'FGHJKMNQUVXZ')) - - -def cme_code_to_month(code): - """ - Convert a CME month code to a month index. - - The month codes are as follows: - - 'F' -> 1 (January) - 'G' -> 2 (February) - 'H' -> 3 (March) - 'J' -> 4 (April) - 'K' -> 5 (May) - 'M' -> 6 (June) - 'N' -> 7 (July) - 'Q' -> 8 (August) - 'U' -> 9 (September) - 'V' -> 10 (October) - 'X' -> 11 (November) - 'Z' -> 12 (December) - - Parameters - ---------- - code : str - The month code to look up. - - Returns - ------- - month : int - The month number (starting at 1 for January) corresponding to the - requested code. - - See Also - -------- - month_to_cme_code - Inverse of this function. - """ - return CME_CODE_TO_MONTH[code] - - -def month_to_cme_code(month): - """ - Convert a month to a CME code. - - The month codes are as follows: - - 1 (January) -> 'F' - 2 (February) -> 'G' - 3 (March) -> 'H' - 4 (April) -> 'J' - 5 (May) -> 'K' - 6 (June) -> 'M' - 7 (July) -> 'N' - 8 (August) -> 'Q' - 9 (September) -> 'U' - 10 (October) -> 'V' - 11 (November) -> 'X' - 12 (December) -> 'Z' - - Parameters - ---------- - month : int - The month number (starting at 1 for January) corresponding to the - requested code. - - Returns - ------- - code : str - The month code to look up. - - See Also - -------- - cme_code_to_month - Inverse of this function. - """ - return MONTH_TO_CME_CODE[month]