Skip to content

Commit

Permalink
Merge 361bdaf into 759fc5b
Browse files Browse the repository at this point in the history
  • Loading branch information
dmichalowicz committed Mar 27, 2017
2 parents 759fc5b + 361bdaf commit ad00917
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 65 deletions.
5 changes: 4 additions & 1 deletion tests/test_assets.py
Expand Up @@ -161,7 +161,10 @@ def build_lookup_generic_cases(asset_finder_type):
]
fof14 = finder.retrieve_asset(fof14_sid)
cf = finder.create_continuous_future(
root_symbol=fof14.root_symbol, offset=0, roll_style='volume',
root_symbol=fof14.root_symbol,
offset=0,
roll_style='volume',
adjustment=None,
)

dupe_0_start = dupe_0.start_date
Expand Down
63 changes: 33 additions & 30 deletions tests/test_continuous_futures.py
Expand Up @@ -348,7 +348,9 @@ def test_double_volume_switch(self):
close date. See `VolumeRollFinder._active_contract` for a full
explanation and example.
"""
cf = self.asset_finder.create_continuous_future('DF', 0, 'volume')
cf = self.asset_finder.create_continuous_future(
'DF', 0, 'volume', None,
)

sessions = self.trading_calendar.sessions_in_range(
'2016-02-09', '2016-02-17',
Expand Down Expand Up @@ -385,7 +387,7 @@ def test_double_volume_switch(self):

def test_create_continuous_future(self):
cf_primary = self.asset_finder.create_continuous_future(
'FO', 0, 'calendar')
'FO', 0, 'calendar', None)

self.assertEqual(cf_primary.root_symbol, 'FO')
self.assertEqual(cf_primary.offset, 0)
Expand All @@ -401,7 +403,7 @@ def test_create_continuous_future(self):
self.assertEqual(retrieved_primary, cf_primary)

cf_secondary = self.asset_finder.create_continuous_future(
'FO', 1, 'calendar')
'FO', 1, 'calendar', None)

self.assertEqual(cf_secondary.root_symbol, 'FO')
self.assertEqual(cf_secondary.offset, 1)
Expand All @@ -421,11 +423,12 @@ def test_create_continuous_future(self):
# Assert that the proper exception is raised if the given root symbol
# does not exist.
with self.assertRaises(SymbolNotFound):
self.asset_finder.create_continuous_future('NO', 0, 'calendar')
self.asset_finder.create_continuous_future(
'NO', 0, 'calendar', None)

def test_current_contract(self):
cf_primary = self.asset_finder.create_continuous_future(
'FO', 0, 'calendar')
'FO', 0, 'calendar', None)
bar_data = self.create_bardata(
lambda: pd.Timestamp('2016-01-26', tz='UTC'))
contract = bar_data.current(cf_primary, 'contract')
Expand All @@ -442,7 +445,7 @@ def test_current_contract(self):

def test_get_value_contract_daily(self):
cf_primary = self.asset_finder.create_continuous_future(
'FO', 0, 'calendar')
'FO', 0, 'calendar', None)

contract = self.data_portal.get_spot_value(
cf_primary,
Expand All @@ -466,7 +469,7 @@ def test_get_value_contract_daily(self):

def test_get_value_close_daily(self):
cf_primary = self.asset_finder.create_continuous_future(
'FO', 0, 'calendar')
'FO', 0, 'calendar', None)

value = self.data_portal.get_spot_value(
cf_primary,
Expand Down Expand Up @@ -504,7 +507,7 @@ def test_get_value_close_daily(self):

def test_current_contract_volume_roll(self):
cf_primary = self.asset_finder.create_continuous_future(
'FO', 0, 'volume')
'FO', 0, 'volume', None)
bar_data = self.create_bardata(
lambda: pd.Timestamp('2016-01-26', tz='UTC'))
contract = bar_data.current(cf_primary, 'contract')
Expand Down Expand Up @@ -535,8 +538,8 @@ def test_current_contract_in_algo(self):
)
def initialize(algo):
algo.primary_cl = continuous_future('FO', 0, 'calendar')
algo.secondary_cl = continuous_future('FO', 1, 'calendar')
algo.primary_cl = continuous_future('FO', 0, 'calendar', None)
algo.secondary_cl = continuous_future('FO', 1, 'calendar', None)
schedule_function(record_current_contract)
def record_current_contract(algo, data):
Expand Down Expand Up @@ -588,8 +591,8 @@ def test_current_chain_in_algo(self):
)
def initialize(algo):
algo.primary_cl = continuous_future('FO', 0, 'calendar')
algo.secondary_cl = continuous_future('FO', 1, 'calendar')
algo.primary_cl = continuous_future('FO', 0, 'calendar', None)
algo.secondary_cl = continuous_future('FO', 1, 'calendar', None)
schedule_function(record_current_contract)
def record_current_contract(algo, data):
Expand Down Expand Up @@ -681,7 +684,7 @@ def record_current_contract(algo, data):

def test_history_sid_session(self):
cf = self.data_portal.asset_finder.create_continuous_future(
'FO', 0, 'calendar')
'FO', 0, 'calendar', None)
window = self.data_portal.get_history_window(
[cf],
Timestamp('2016-03-04 18:01', tz='US/Eastern').tz_convert('UTC'),
Expand Down Expand Up @@ -735,7 +738,7 @@ def test_history_sid_session(self):

def test_history_sid_session_quarter_rolls(self):
cf = self.data_portal.asset_finder.create_continuous_future(
'BA', 0, 'calendar')
'BA', 0, 'calendar', None)
window = self.data_portal.get_history_window(
[cf],
Timestamp('2016-03-13 18:01', tz='US/Eastern').tz_convert('UTC'),
Expand All @@ -756,7 +759,7 @@ def test_history_sid_session_quarter_rolls(self):

def test_history_sid_session_delivery_predicate(self):
cf = self.data_portal.asset_finder.create_continuous_future(
'BZ', 0, 'calendar')
'BZ', 0, 'calendar', None)
window = self.data_portal.get_history_window(
[cf],
Timestamp('2016-01-11 18:01', tz='US/Eastern').tz_convert('UTC'),
Expand All @@ -777,7 +780,7 @@ def test_history_sid_session_delivery_predicate(self):

def test_history_sid_session_secondary(self):
cf = self.data_portal.asset_finder.create_continuous_future(
'FO', 1, 'calendar')
'FO', 1, 'calendar', None)
window = self.data_portal.get_history_window(
[cf],
Timestamp('2016-03-04 18:01', tz='US/Eastern').tz_convert('UTC'),
Expand Down Expand Up @@ -831,7 +834,7 @@ def test_history_sid_session_secondary(self):

def test_history_sid_session_volume_roll(self):
cf = self.data_portal.asset_finder.create_continuous_future(
'FO', 0, 'volume')
'FO', 0, 'volume', None)
window = self.data_portal.get_history_window(
[cf],
Timestamp('2016-03-04 18:01', tz='US/Eastern').tz_convert('UTC'),
Expand Down Expand Up @@ -895,7 +898,7 @@ def test_history_sid_session_volume_roll(self):

def test_history_sid_minute(self):
cf = self.data_portal.asset_finder.create_continuous_future(
'FO', 0, 'calendar')
'FO', 0, 'calendar', None)
window = self.data_portal.get_history_window(
[cf.sid],
Timestamp('2016-01-26 18:01', tz='US/Eastern').tz_convert('UTC'),
Expand Down Expand Up @@ -930,7 +933,7 @@ def test_history_sid_minute(self):

def test_history_close_session(self):
cf = self.data_portal.asset_finder.create_continuous_future(
'FO', 0, 'calendar')
'FO', 0, 'calendar', None)
window = self.data_portal.get_history_window(
[cf.sid], Timestamp('2016-03-06', tz='UTC'), 30, '1d', 'close')

Expand Down Expand Up @@ -980,7 +983,7 @@ def test_history_close_session(self):

def test_history_close_session_skip_volume(self):
cf = self.data_portal.asset_finder.create_continuous_future(
'MA', 0, 'volume')
'MA', 0, 'volume', None)
window = self.data_portal.get_history_window(
[cf.sid], Timestamp('2016-03-06', tz='UTC'), 30, '1d', 'close')

Expand Down Expand Up @@ -1020,11 +1023,11 @@ def test_history_close_session_skip_volume(self):

def test_history_close_session_adjusted(self):
cf = self.data_portal.asset_finder.create_continuous_future(
'FO', 0, 'calendar')
'FO', 0, 'calendar', None)
cf_mul = self.data_portal.asset_finder.create_continuous_future(
'FO', 0, 'calendar').adj('mul')
'FO', 0, 'calendar', 'mul')
cf_add = self.data_portal.asset_finder.create_continuous_future(
'FO', 0, 'calendar').adj('add')
'FO', 0, 'calendar', 'add')
window = self.data_portal.get_history_window(
[cf, cf_mul, cf_add],
Timestamp('2016-03-06', tz='UTC'), 30, '1d', 'close')
Expand Down Expand Up @@ -1148,7 +1151,7 @@ def test_history_close_session_adjusted(self):

def test_history_close_minute(self):
cf = self.data_portal.asset_finder.create_continuous_future(
'FO', 0, 'calendar')
'FO', 0, 'calendar', None)
window = self.data_portal.get_history_window(
[cf.sid],
Timestamp('2016-02-25 18:01', tz='US/Eastern').tz_convert('UTC'),
Expand Down Expand Up @@ -1183,11 +1186,11 @@ def test_history_close_minute(self):

def test_history_close_minute_adjusted(self):
cf = self.data_portal.asset_finder.create_continuous_future(
'FO', 0, 'calendar')
'FO', 0, 'calendar', None)
cf_mul = self.data_portal.asset_finder.create_continuous_future(
'FO', 0, 'calendar').adj('mul')
'FO', 0, 'calendar', 'mul')
cf_add = self.data_portal.asset_finder.create_continuous_future(
'FO', 0, 'calendar').adj('add')
'FO', 0, 'calendar', 'add')
window = self.data_portal.get_history_window(
[cf, cf_mul, cf_add],
Timestamp('2016-02-25 18:01', tz='US/Eastern').tz_convert('UTC'),
Expand Down Expand Up @@ -1247,11 +1250,11 @@ def test_history_close_minute_adjusted(self):

def test_history_close_minute_adjusted_volume_roll(self):
cf = self.data_portal.asset_finder.create_continuous_future(
'FO', 0, 'volume')
'FO', 0, 'volume', None)
cf_mul = self.data_portal.asset_finder.create_continuous_future(
'FO', 0, 'volume').adj('mul')
'FO', 0, 'volume', 'mul')
cf_add = self.data_portal.asset_finder.create_continuous_future(
'FO', 0, 'volume').adj('add')
'FO', 0, 'volume', 'add')
window = self.data_portal.get_history_window(
[cf, cf_mul, cf_add],
Timestamp('2016-02-25 18:01', tz='US/Eastern').tz_convert('UTC'),
Expand Down
11 changes: 10 additions & 1 deletion zipline/algorithm.py
Expand Up @@ -1201,7 +1201,11 @@ def symbol(self, symbol_str):

@api_method
@preprocess(root_symbol_str=ensure_upper_case)
def continuous_future(self, root_symbol_str, offset, roll):
def continuous_future(self,
root_symbol_str,
offset,
roll,
adjustment='mul'):
"""Create a specifier for a continuous contract.
Parameters
Expand All @@ -1215,6 +1219,10 @@ def continuous_future(self, root_symbol_str, offset, roll):
roll_style : str
How rolls are determined.
adjustment : str
Method for adjusting lookback prices between rolls. Options are
'mul', 'add', and None. Defaults to 'mul'.
Returns
-------
continuous_future : ContinuousFuture
Expand All @@ -1224,6 +1232,7 @@ def continuous_future(self, root_symbol_str, offset, roll):
root_symbol_str,
offset,
roll,
adjustment,
)

@api_method
Expand Down
6 changes: 5 additions & 1 deletion zipline/api.pyi
Expand Up @@ -60,7 +60,7 @@ def cancel_order(order_param):
The order_id or order object to cancel.
"""

def continuous_future(root_symbol_str, offset, roll):
def continuous_future(root_symbol_str, offset, roll, adjustment='mul'):
"""Create a specifier for a continuous contract.
Parameters
Expand All @@ -74,6 +74,10 @@ def continuous_future(root_symbol_str, offset, roll):
roll_style : str
How rolls are determined.
adjustment : str
Method for adjusting lookback prices between rolls. Options are
'mul', 'add', and None. Defaults to 'mul'.
Returns
-------
continuous_future : ContinuousFuture
Expand Down
81 changes: 49 additions & 32 deletions zipline/assets/assets.py
Expand Up @@ -16,6 +16,7 @@
import array
import binascii
from collections import deque, namedtuple
from functools import partial
from numbers import Integral
from operator import itemgetter, attrgetter
import struct
Expand Down Expand Up @@ -54,9 +55,10 @@
Asset, Equity, Future,
)
from . continuous_futures import (
OrderedContracts,
ADJUSTMENT_STYLES,
CHAIN_PREDICATES,
ContinuousFuture,
CHAIN_PREDICATES
OrderedContracts,
)
from .asset_writer import (
check_version_info,
Expand Down Expand Up @@ -1016,7 +1018,17 @@ def get_ordered_contracts(self, root_symbol):
self._ordered_contracts[root_symbol] = oc
return oc

def create_continuous_future(self, root_symbol, offset, roll_style):
def create_continuous_future(self,
root_symbol,
offset,
roll_style,
adjustment):
if adjustment not in ADJUSTMENT_STYLES:
raise ValueError(
'Invalid adjustment style {!r}. Allowed adjustment styles are '
'{}.'.format(adjustment, list(ADJUSTMENT_STYLES))
)

oc = self.get_ordered_contracts(root_symbol)
exchange = self._get_root_symbol_exchange(root_symbol)

Expand All @@ -1029,37 +1041,42 @@ def create_continuous_future(self, root_symbol, offset, roll_style):
add_sid = _encode_continuous_future_sid(root_symbol, offset,
roll_style,
'add')
mul_cf = ContinuousFuture(mul_sid,
root_symbol,
offset,
roll_style,
oc.start_date,
oc.end_date,
exchange,
'mul')
add_cf = ContinuousFuture(add_sid,
root_symbol,
offset,
roll_style,
oc.start_date,
oc.end_date,
exchange,
'add')
cf = ContinuousFuture(sid,
root_symbol,
offset,
roll_style,
oc.start_date,
oc.end_date,
exchange,
adjustment_children={
'mul': mul_cf,
'add': add_cf
})

cf_template = partial(
ContinuousFuture,
root_symbol=root_symbol,
offset=offset,
roll_style=roll_style,
start_date=oc.start_date,
end_date=oc.end_date,
exchange=exchange,
)
_no_adjustment_cf = cf_template(sid=sid)
_mul_cf = cf_template(sid=mul_sid, adjustment='mul')
_add_cf = cf_template(sid=add_sid, adjustment='add')
adjustment_children = {
None: _no_adjustment_cf,
'mul': _mul_cf,
'add': _add_cf,
}

cf = cf_template(sid=sid, adjustment_children=adjustment_children)
mul_cf = cf_template(
sid=mul_sid,
adjustment='mul',
adjustment_children=adjustment_children,
)
add_cf = cf_template(
sid=add_sid,
adjustment='add',
adjustment_children=adjustment_children,
)

self._asset_cache[cf.sid] = cf
self._asset_cache[add_cf.sid] = add_cf
self._asset_cache[mul_cf.sid] = mul_cf
return cf
self._asset_cache[add_cf.sid] = add_cf

return {None: cf, 'mul': mul_cf, 'add': add_cf}[adjustment]

def _make_sids(tblattr):
def _(self):
Expand Down

0 comments on commit ad00917

Please sign in to comment.