Merge pull request #1600 from quantopian/use-prev-session-for-volume-roll

BUG: Fix bounds errors in roll finder.
This commit is contained in:
Eddie Hebert
2016-11-26 21:37:21 -05:00
committed by GitHub
2 changed files with 44 additions and 36 deletions
+21 -19
View File
@@ -176,18 +176,20 @@ class ContinuousFuturesTestCase(WithCreateBarData,
vol_stop_session = sid_to_vol_stop_session[i]
m_open = tc.open_and_close_for_session(vol_stop_session)[0]
loc = dts.searchsorted(m_open)
# Add a little bit of noise to roll. So that checks for exacly
# 0 do not work, since there may be stragglers after a roll.
# Add a little bit of noise to roll. So that predicates that
# check for exactly 0 do not work, since there may be
# stragglers after a roll.
df.volume.values[loc] = 1000
df.volume.values[loc + 1:] = 0
j = i - 1
if j in sid_to_vol_stop_session:
non_primary_end = sid_to_vol_stop_session[j] - sessions.freq
non_primary_end = sid_to_vol_stop_session[j]
m_close = tc.open_and_close_for_session(non_primary_end)[1]
loc = dts.searchsorted(m_close)
# Add some volume before a roll, since a contracted may be
# entered earlier than when it is the primary.
df.volume.values[:loc] = 2000
if m_close > dts[0]:
loc = dts.get_loc(m_close)
# 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
yield i, df
def test_create_continuous_future(self):
@@ -311,14 +313,14 @@ class ContinuousFuturesTestCase(WithCreateBarData,
lambda: pd.Timestamp('2016-01-26', tz='UTC'))
contract = bar_data.current(cf_primary, 'contract')
self.assertEqual(contract.symbol, 'FOG16')
self.assertEqual(contract.symbol, 'FOF16')
bar_data = self.create_bardata(
lambda: pd.Timestamp('2016-01-26', tz='UTC'))
lambda: pd.Timestamp('2016-01-27', tz='UTC'))
contract = bar_data.current(cf_primary, 'contract')
self.assertEqual(contract.symbol, 'FOG16',
'Auto close at beginning of session. FOG16 remains '
'Auto close at beginning of session. FOG16 is now '
'the current contract.')
bar_data = self.create_bardata(
@@ -599,12 +601,12 @@ def record_current_contract(algo, data):
# Volume cuts out for FOF16 on 2016-01-25
self.assertEqual(window.loc['2016-01-26', cf],
1,
"Should be FOG16 at beginning of window.")
0,
"Should be FOF16 at beginning of window.")
self.assertEqual(window.loc['2016-01-27', cf],
1,
"Should have remained FOG16.")
"Should have rolled to FOG16.")
self.assertEqual(window.loc['2016-02-25', cf],
1,
@@ -630,24 +632,24 @@ def record_current_contract(algo, data):
self.assertEqual(window.loc['2016-02-26', cf],
2,
"Should be FOH16 on session with roll.")
"Should be FOH16 on roll session.")
self.assertEqual(window.loc['2016-02-29', cf],
2,
"Should be FOH16 on session after roll.")
"Should remain FOH16.")
self.assertEqual(window.loc['2016-03-17', cf],
2,
"Should be FOH16 on session before volume cuts out.")
self.assertEqual(window.loc['2016-03-18', cf],
3,
"Should be FOJ16 on session where the volume of "
"FOH16 cuts out.")
2,
"Should be FOH16 on session where the volume of "
"FOH16 cuts out, the roll is upcoming.")
self.assertEqual(window.loc['2016-03-24', cf],
3,
"Should have remained FOJ16.")
"Should have rolled to FOJ16.")
self.assertEqual(window.loc['2016-03-28', cf],
3,
+23 -17
View File
@@ -96,23 +96,21 @@ class RollFinder(with_metaclass(ABCMeta, object)):
i -= 1
else:
i -= 2
auto_close_date = Timestamp(oc.auto_close_dates[i], tz='UTC')
while auto_close_date > start and i > -1:
session_loc = sessions.searchsorted(auto_close_date)
curr = sessions[-1]
while curr > start and i > -1:
session_loc = sessions.searchsorted(curr)
front = oc.contract_sids[i]
back = oc.contract_sids[i + 1]
while session_loc > -1:
while session_loc > 0:
session = sessions[session_loc]
if back != self._active_contract(oc, front, back, session):
prev = sessions[session_loc - 1]
if back != self._active_contract(oc, front, back, prev):
rolls.insert(0, (oc.contract_sids[i + offset], session))
break
session_loc -= 1
roll_session = sessions[session_loc + 1]
if roll_session > start:
rolls.insert(0, (oc.contract_sids[i + offset],
roll_session))
i -= 1
auto_close_date = Timestamp(oc.auto_close_dates[i],
tz='UTC')
curr = Timestamp(oc.auto_close_dates[i],
tz='UTC')
return rolls
@@ -131,8 +129,8 @@ class CalendarRollFinder(RollFinder):
if sid == front:
break
auto_close_date = Timestamp(oc.auto_close_dates[i], tz='UTC')
before_auto_close = dt < auto_close_date
return front if before_auto_close else back
auto_closed = dt >= auto_close_date
return back if auto_closed else front
class VolumeRollFinder(RollFinder):
@@ -149,7 +147,15 @@ class VolumeRollFinder(RollFinder):
self.session_reader = session_reader
def _active_contract(self, oc, front, back, dt):
# FIXME: Possible vector for look ahead bias.
front_vol = self.session_reader.get_value(front, dt, 'volume')
back_vol = self.session_reader.get_value(back, dt, 'volume')
return back if back_vol > front_vol else front
prev = dt - self.trading_calendar.day
front_vol = self.session_reader.get_value(front, prev, 'volume')
back_vol = self.session_reader.get_value(back, prev, 'volume')
if back_vol > front_vol:
return back
else:
for i, sid in enumerate(oc.contract_sids):
if sid == front:
break
auto_close_date = Timestamp(oc.auto_close_dates[i], tz='UTC')
auto_closed = dt >= auto_close_date
return back if auto_closed else front