Skip to content

Commit

Permalink
Improvements to AssetFinder future lookups
Browse files Browse the repository at this point in the history
Contracts must have been trading at the as_of_date to be considered valid, and a contract's position in the chain is now zero-indexed.
  • Loading branch information
Andrew Daniels committed Jun 26, 2015
1 parent cc77a52 commit fe9bbad
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 17 deletions.
42 changes: 30 additions & 12 deletions tests/test_assets.py
Expand Up @@ -581,19 +581,22 @@ def test_lookup_future_in_chain(self):
'symbol': 'ADN15',
'root_symbol': 'AD',
'asset_type': 'future',
'expiration_date': pd.Timestamp('2015-06-15', tz='UTC')
'expiration_date': pd.Timestamp('2015-06-15', tz='UTC'),
'start_date': pd.Timestamp('2015-01-01', tz='UTC')
},
1: {
'symbol': 'ADV15',
'root_symbol': 'AD',
'asset_type': 'future',
'expiration_date': pd.Timestamp('2015-09-14', tz='UTC')
'expiration_date': pd.Timestamp('2015-09-14', tz='UTC'),
'start_date': pd.Timestamp('2015-01-01', tz='UTC')
},
0: {
'symbol': 'ADF16',
'root_symbol': 'AD',
'asset_type': 'future',
'expiration_date': pd.Timestamp('2015-12-14', tz='UTC')
'expiration_date': pd.Timestamp('2015-12-14', tz='UTC'),
'start_date': pd.Timestamp('2015-01-01', tz='UTC')
},

}
Expand All @@ -602,13 +605,12 @@ def test_lookup_future_in_chain(self):
dt = pd.Timestamp('2015-06-19', tz='UTC')

# Check that the primary and secondary contracts are as expected
primary = finder.lookup_future_in_chain('AD', dt, 1)
secondary = finder.lookup_future_in_chain('AD', dt, 2)
primary = finder.lookup_future_in_chain('AD', dt, 0)
secondary = finder.lookup_future_in_chain('AD', dt, 1)
self.assertEqual(primary.sid, 1)
self.assertEqual(secondary.sid, 0)

# Check that we get None for an invalid contract num
self.assertIsNone(finder.lookup_future_in_chain('AD', dt, 0))
self.assertIsNone(finder.lookup_future_in_chain('AD', dt, -10))
self.assertIsNone(finder.lookup_future_in_chain('AD', dt, 10))
self.assertIsNone(finder.lookup_future_in_chain('CL', dt, 1))
Expand All @@ -619,19 +621,32 @@ def test_lookup_future_chain(self):
'symbol': 'ADN15',
'root_symbol': 'AD',
'asset_type': 'future',
'expiration_date': pd.Timestamp('2015-06-15', tz='UTC')
'expiration_date': pd.Timestamp('2015-06-15', tz='UTC'),
'start_date': pd.Timestamp('2015-01-01', tz='UTC')
},
1: {
'symbol': 'ADV15',
'root_symbol': 'AD',
'asset_type': 'future',
'expiration_date': pd.Timestamp('2015-09-14', tz='UTC')
'expiration_date': pd.Timestamp('2015-09-14', tz='UTC'),
'start_date': pd.Timestamp('2015-01-01', tz='UTC')
},
# Starts trading today, so should be valid.
0: {
'symbol': 'ADF16',
'root_symbol': 'AD',
'asset_type': 'future',
'expiration_date': pd.Timestamp('2015-12-14', tz='UTC')
'expiration_date': pd.Timestamp('2015-12-14', tz='UTC'),
'start_date': pd.Timestamp('2015-06-19', tz='UTC')
},
# Copy of the above future, but starts trading in August,
# so it isn't valid.
3: {
'symbol': 'ADF16',
'root_symbol': 'AD',
'asset_type': 'future',
'expiration_date': pd.Timestamp('2015-12-14', tz='UTC'),
'start_date': pd.Timestamp('2015-08-01', tz='UTC')
},

}
Expand All @@ -652,19 +667,22 @@ def test_lookup_future_by_expiration(self):
'symbol': 'ADN15',
'root_symbol': 'AD',
'asset_type': 'future',
'expiration_date': pd.Timestamp('2015-06-15', tz='UTC')
'expiration_date': pd.Timestamp('2015-06-15', tz='UTC'),
'start_date': pd.Timestamp('2015-01-01', tz='UTC')
},
1: {
'symbol': 'ADV15',
'root_symbol': 'AD',
'asset_type': 'future',
'expiration_date': pd.Timestamp('2015-09-14', tz='UTC')
'expiration_date': pd.Timestamp('2015-09-14', tz='UTC'),
'start_date': pd.Timestamp('2015-01-01', tz='UTC')
},
0: {
'symbol': 'ADF16',
'root_symbol': 'AD',
'asset_type': 'future',
'expiration_date': pd.Timestamp('2015-12-14', tz='UTC')
'expiration_date': pd.Timestamp('2015-12-14', tz='UTC'),
'start_date': pd.Timestamp('2015-01-01', tz='UTC')
},

}
Expand Down
10 changes: 5 additions & 5 deletions zipline/assets/assets.py
Expand Up @@ -247,7 +247,8 @@ def _valid_contracts(self, root_symbol, as_of_date):
"""
try:
return [c for c in self.future_chains_cache[root_symbol]
if c.expiration_date > as_of_date]
if c.expiration_date and (c.expiration_date > as_of_date)
and c.start_date and (c.start_date <= as_of_date)]
except KeyError:
return None

Expand All @@ -269,7 +270,7 @@ def lookup_future_chain(self, root_symbol, as_of_date):
as_of_date = normalize_date(as_of_date)
return self._valid_contracts(root_symbol, as_of_date)

def lookup_future_in_chain(self, root_symbol, as_of_date, contract_num=1):
def lookup_future_in_chain(self, root_symbol, as_of_date, contract_num=0):
""" Find a specific contract in the futures chain for a given
root symbol.
Expand All @@ -293,11 +294,10 @@ def lookup_future_in_chain(self, root_symbol, as_of_date, contract_num=1):
as_of_date = normalize_date(as_of_date)

valid_contracts = self._valid_contracts(root_symbol, as_of_date)
contract_index = contract_num - 1

if valid_contracts and contract_index >= 0:
if valid_contracts and contract_num >= 0:
try:
return valid_contracts[contract_index]
return valid_contracts[contract_num]
except IndexError:
pass

Expand Down

0 comments on commit fe9bbad

Please sign in to comment.