Merge pull request #1502 from quantopian/remove-future-chain

MAINT: Remove `future_chain` API method.
This commit is contained in:
Eddie Hebert
2016-09-21 11:44:57 -04:00
committed by GitHub
6 changed files with 0 additions and 650 deletions
-2
View File
@@ -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
-103
View File
@@ -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.
-267
View File
@@ -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):
-63
View File
@@ -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 (
@@ -1246,68 +1245,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
-57
View File
@@ -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
-158
View File
@@ -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]