Merge pull request #1610 from quantopian/allow-skips-across-contracts

BUG: Allow rolls to skip over contracts.
This commit is contained in:
Eddie Hebert
2016-12-05 22:53:36 -05:00
committed by GitHub
2 changed files with 78 additions and 6 deletions
+73 -5
View File
@@ -66,9 +66,9 @@ class ContinuousFuturesTestCase(WithCreateBarData,
@classmethod
def make_root_symbols_info(self):
return pd.DataFrame({
'root_symbol': ['FO', 'BA', 'BZ'],
'root_symbol_id': [1, 2, 3],
'exchange': ['CME', 'CME', 'CME']})
'root_symbol': ['FO', 'BA', 'BZ', 'MA'],
'root_symbol_id': [1, 2, 3, 4],
'exchange': ['CME', 'CME', 'CME', 'CME']})
@classmethod
def make_futures_info(self):
@@ -179,7 +179,33 @@ class ContinuousFuturesTestCase(WithCreateBarData,
'exchange': ['CME'] * 3,
})
return pd.concat([fo_frame, ba_frame, bz_frame])
# MA is set up to test a contract which is has no active volume.
ma_frame = DataFrame({
'symbol': ['MAG16', 'MAH16', 'MAJ16'],
'root_symbol': ['MA'] * 3,
'asset_name': ['Most Active'] * 3,
'sid': range(14, 17),
'start_date': [Timestamp('2005-01-01', tz='UTC'),
Timestamp('2005-01-21', tz='UTC'),
Timestamp('2005-01-21', tz='UTC')],
'end_date': [Timestamp('2016-08-19', tz='UTC'),
Timestamp('2016-11-21', tz='UTC'),
Timestamp('2016-10-19', tz='UTC')],
'notice_date': [Timestamp('2016-02-17', tz='UTC'),
Timestamp('2016-03-16', tz='UTC'),
Timestamp('2016-04-13', tz='UTC')],
'expiration_date': [Timestamp('2016-02-17', tz='UTC'),
Timestamp('2016-03-16', tz='UTC'),
Timestamp('2016-04-13', tz='UTC')],
'auto_close_date': [Timestamp('2016-02-17', tz='UTC'),
Timestamp('2016-03-16', tz='UTC'),
Timestamp('2016-04-13', tz='UTC')],
'tick_size': [0.001] * 3,
'multiplier': [1000.0] * 3,
'exchange': ['CME'] * 3,
})
return pd.concat([fo_frame, ba_frame, bz_frame, ma_frame])
@classmethod
def make_future_minute_bar_data(cls):
@@ -239,7 +265,7 @@ class ContinuousFuturesTestCase(WithCreateBarData,
3: Timestamp('2016-04-20', tz='UTC'),
6: Timestamp('2016-01-27', tz='UTC'),
}
for i in range(7):
for i in range(17):
df = base_df.copy()
df += i * 10000
if i in sid_to_vol_stop_session:
@@ -260,6 +286,8 @@ class ContinuousFuturesTestCase(WithCreateBarData,
# Add some volume before a roll, since a contract may be
# entered earlier than when it is the primary.
df.volume.values[:loc + 1] = 10
if i == 15: # No volume for MAH16
df.volume.values[:] = 0
yield i, df
def test_create_continuous_future(self):
@@ -852,6 +880,46 @@ def record_current_contract(algo, data):
135441.440,
err_msg="On session after roll, Should be FOJ16's 44th value.")
def test_history_close_session_skip_volume(self):
cf = self.data_portal.asset_finder.create_continuous_future(
'MA', 0, 'volume')
window = self.data_portal.get_history_window(
[cf.sid], Timestamp('2016-03-06', tz='UTC'), 30, '1d', 'close')
assert_almost_equal(
window.loc['2016-01-26', cf],
245011.440,
err_msg="At beginning of window, should be MAG16's first value.")
assert_almost_equal(
window.loc['2016-02-26', cf],
265241.440,
err_msg="Should have skipped MAH16 to MAJ16.")
assert_almost_equal(
window.loc['2016-02-29', cf],
265251.440,
err_msg="Should have remained MAJ16.")
# Advance the window a month.
window = self.data_portal.get_history_window(
[cf.sid], Timestamp('2016-04-06', tz='UTC'), 30, '1d', 'close')
assert_almost_equal(
window.loc['2016-02-24', cf],
265221.440,
err_msg="Should be MAJ16, having skipped MAH16.")
assert_almost_equal(
window.loc['2016-02-29', cf],
265251.440,
err_msg="Should be MAJ1 for rest of window.")
assert_almost_equal(
window.loc['2016-03-24', cf],
265431.440,
err_msg="Should be MAJ16 for rest of window.")
def test_history_close_session_adjusted(self):
cf = self.data_portal.asset_finder.create_continuous_future(
'FO', 0, 'calendar')
+5 -1
View File
@@ -98,9 +98,13 @@ class RollFinder(with_metaclass(ABCMeta, object)):
while session > start and curr is not None:
front = curr.contract.sid
back = curr.next.contract.sid
back = rolls[0][0]
prev_c = curr.prev
while session > start:
prev = session - freq
if prev_c is not None:
if prev < prev_c.contract.auto_close_date:
break
if back != self._active_contract(oc, front, back, prev):
rolls.insert(0, ((curr >> offset).contract.sid, session))
break