mirror of
https://github.com/wassname/catalyst.git
synced 2026-09-09 11:19:23 +08:00
Merge pull request #1610 from quantopian/allow-skips-across-contracts
BUG: Allow rolls to skip over contracts.
This commit is contained in:
@@ -66,9 +66,9 @@ class ContinuousFuturesTestCase(WithCreateBarData,
|
|||||||
@classmethod
|
@classmethod
|
||||||
def make_root_symbols_info(self):
|
def make_root_symbols_info(self):
|
||||||
return pd.DataFrame({
|
return pd.DataFrame({
|
||||||
'root_symbol': ['FO', 'BA', 'BZ'],
|
'root_symbol': ['FO', 'BA', 'BZ', 'MA'],
|
||||||
'root_symbol_id': [1, 2, 3],
|
'root_symbol_id': [1, 2, 3, 4],
|
||||||
'exchange': ['CME', 'CME', 'CME']})
|
'exchange': ['CME', 'CME', 'CME', 'CME']})
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def make_futures_info(self):
|
def make_futures_info(self):
|
||||||
@@ -179,7 +179,33 @@ class ContinuousFuturesTestCase(WithCreateBarData,
|
|||||||
'exchange': ['CME'] * 3,
|
'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
|
@classmethod
|
||||||
def make_future_minute_bar_data(cls):
|
def make_future_minute_bar_data(cls):
|
||||||
@@ -239,7 +265,7 @@ class ContinuousFuturesTestCase(WithCreateBarData,
|
|||||||
3: Timestamp('2016-04-20', tz='UTC'),
|
3: Timestamp('2016-04-20', tz='UTC'),
|
||||||
6: Timestamp('2016-01-27', tz='UTC'),
|
6: Timestamp('2016-01-27', tz='UTC'),
|
||||||
}
|
}
|
||||||
for i in range(7):
|
for i in range(17):
|
||||||
df = base_df.copy()
|
df = base_df.copy()
|
||||||
df += i * 10000
|
df += i * 10000
|
||||||
if i in sid_to_vol_stop_session:
|
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
|
# Add some volume before a roll, since a contract may be
|
||||||
# entered earlier than when it is the primary.
|
# entered earlier than when it is the primary.
|
||||||
df.volume.values[:loc + 1] = 10
|
df.volume.values[:loc + 1] = 10
|
||||||
|
if i == 15: # No volume for MAH16
|
||||||
|
df.volume.values[:] = 0
|
||||||
yield i, df
|
yield i, df
|
||||||
|
|
||||||
def test_create_continuous_future(self):
|
def test_create_continuous_future(self):
|
||||||
@@ -852,6 +880,46 @@ def record_current_contract(algo, data):
|
|||||||
135441.440,
|
135441.440,
|
||||||
err_msg="On session after roll, Should be FOJ16's 44th value.")
|
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):
|
def test_history_close_session_adjusted(self):
|
||||||
cf = self.data_portal.asset_finder.create_continuous_future(
|
cf = self.data_portal.asset_finder.create_continuous_future(
|
||||||
'FO', 0, 'calendar')
|
'FO', 0, 'calendar')
|
||||||
|
|||||||
@@ -98,9 +98,13 @@ class RollFinder(with_metaclass(ABCMeta, object)):
|
|||||||
|
|
||||||
while session > start and curr is not None:
|
while session > start and curr is not None:
|
||||||
front = curr.contract.sid
|
front = curr.contract.sid
|
||||||
back = curr.next.contract.sid
|
back = rolls[0][0]
|
||||||
|
prev_c = curr.prev
|
||||||
while session > start:
|
while session > start:
|
||||||
prev = session - freq
|
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):
|
if back != self._active_contract(oc, front, back, prev):
|
||||||
rolls.insert(0, ((curr >> offset).contract.sid, session))
|
rolls.insert(0, ((curr >> offset).contract.sid, session))
|
||||||
break
|
break
|
||||||
|
|||||||
Reference in New Issue
Block a user