Skip to content

Commit

Permalink
Merge ec6f298 into eba02da
Browse files Browse the repository at this point in the history
  • Loading branch information
ehebert committed Oct 7, 2016
2 parents eba02da + ec6f298 commit 9546dea
Show file tree
Hide file tree
Showing 9 changed files with 697 additions and 5 deletions.
2 changes: 2 additions & 0 deletions setup.py
Expand Up @@ -80,6 +80,8 @@ def build_extensions(self):

ext_modules = [
Extension('zipline.assets._assets', ['zipline/assets/_assets.pyx']),
Extension('zipline.assets.continuous_futures',
['zipline/assets/continuous_futures.pyx']),
Extension('zipline.lib.adjustment', ['zipline/lib/adjustment.pyx']),
Extension('zipline.lib._factorize', ['zipline/lib/_factorize.pyx']),
Extension(
Expand Down
11 changes: 9 additions & 2 deletions tests/test_algorithm.py
Expand Up @@ -38,6 +38,7 @@
from zipline import TradingAlgorithm
from zipline.api import FixedSlippage
from zipline.assets import Equity, Future, Asset
from zipline.assets.continuous_futures import ContinuousFuture
from zipline.assets.synthetic import (
make_jagged_equity_info,
make_simple_equity_info,
Expand Down Expand Up @@ -1431,8 +1432,12 @@ class TestAlgoScript(WithLogger,
STRING_TYPE_NAMES = [s.__name__ for s in string_types]
STRING_TYPE_NAMES_STRING = ', '.join(STRING_TYPE_NAMES)
ASSET_TYPE_NAME = Asset.__name__
CONTINUOUS_FUTURE_NAME = ContinuousFuture.__name__
ASSET_OR_STRING_TYPE_NAMES = ', '.join([ASSET_TYPE_NAME] +
STRING_TYPE_NAMES)
ASSET_OR_STRING_OR_CF_TYPE_NAMES = ', '.join([ASSET_TYPE_NAME,
CONTINUOUS_FUTURE_NAME] +
STRING_TYPE_NAMES)
ARG_TYPE_TEST_CASES = (
('history__assets', (bad_type_history_assets,
ASSET_OR_STRING_TYPE_NAMES,
Expand All @@ -1445,7 +1450,7 @@ class TestAlgoScript(WithLogger,
STRING_TYPE_NAMES_STRING,
False)),
('current__assets', (bad_type_current_assets,
ASSET_OR_STRING_TYPE_NAMES,
ASSET_OR_STRING_OR_CF_TYPE_NAMES,
True)),
('current__fields', (bad_type_current_fields,
STRING_TYPE_NAMES_STRING,
Expand All @@ -1465,7 +1470,9 @@ class TestAlgoScript(WithLogger,
('history_kwarg__frequency',
(bad_type_history_frequency_kwarg, STRING_TYPE_NAMES_STRING, False)),
('current_kwarg__assets',
(bad_type_current_assets_kwarg, ASSET_OR_STRING_TYPE_NAMES, True)),
(bad_type_current_assets_kwarg,
ASSET_OR_STRING_OR_CF_TYPE_NAMES,
True)),
('current_kwarg__fields',
(bad_type_current_fields_kwarg, STRING_TYPE_NAMES_STRING, True)),
)
Expand Down
172 changes: 172 additions & 0 deletions tests/test_continuous_futures.py
@@ -0,0 +1,172 @@
#
# Copyright 2016 Quantopian, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from textwrap import dedent

import pandas as pd
from pandas import Timestamp, DataFrame

from zipline import TradingAlgorithm
from zipline.testing.fixtures import (
WithCreateBarData,
WithSimParams,
ZiplineTestCase,
)


class ContinuousFuturesTestCase(WithCreateBarData,
WithSimParams,
ZiplineTestCase):

START_DATE = pd.Timestamp('2015-01-05', tz='UTC')
END_DATE = pd.Timestamp('2016-10-19', tz='UTC')

SIM_PARAMS_START = pd.Timestamp('2016-01-25', tz='UTC')
SIM_PARAMS_END = pd.Timestamp('2016-01-27', tz='UTC')
SIM_PARAMS_DATA_FREQUENCY = 'minute'
TRADING_CALENDAR_STRS = ('us_futures',)
TRADING_CALENDAR_PRIMARY_CAL = 'us_futures'

@classmethod
def make_root_symbols_info(self):
return pd.DataFrame({
'root_symbol': ['FO'],
'root_symbol_id': [1],
'exchange': ['CME']})

@classmethod
def make_futures_info(self):
return DataFrame({
'symbol': ['FOF', 'FOG', 'FOH'],
'root_symbol': ['FO', 'FO', 'FO'],
'asset_name': ['Foo'] * 3,
'start_date': [Timestamp('2015-01-05', tz='UTC'),
Timestamp('2015-02-05', tz='UTC'),
Timestamp('2015-03-05', tz='UTC')],
'end_date': [Timestamp('2016-08-19', tz='UTC'),
Timestamp('2016-09-19', tz='UTC'),
Timestamp('2016-10-19', tz='UTC')],
'notice_date': [Timestamp('2016-01-26', tz='UTC'),
Timestamp('2016-02-26', tz='UTC'),
Timestamp('2016-03-26', tz='UTC')],
'expiration_date': [Timestamp('2016-01-26', tz='UTC'),
Timestamp('2016-02-26', tz='UTC'),
Timestamp('2016-03-26', tz='UTC')],
'auto_close_date': [Timestamp('2016-01-26', tz='UTC'),
Timestamp('2016-02-26', tz='UTC'),
Timestamp('2016-03-26', tz='UTC')],
'tick_size': [0.001] * 3,
'multiplier': [1000.0] * 3,
'exchange': ['CME'] * 3,
})

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

self.assertEqual(cf_primary.root_symbol, 'FO')
self.assertEqual(cf_primary.offset, 0)
self.assertEqual(cf_primary.roll_style, 'calendar')

retrieved_primary = self.asset_finder.retrieve_asset(
cf_primary.sid)

self.assertEqual(retrieved_primary, cf_primary)

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

self.assertEqual(cf_secondary.root_symbol, 'FO')
self.assertEqual(cf_secondary.offset, 1)
self.assertEqual(cf_secondary.roll_style, 'calendar')

retrieved = self.asset_finder.retrieve_asset(
cf_secondary.sid)

self.assertEqual(retrieved, cf_secondary)

self.assertNotEqual(cf_primary, cf_secondary)

def test_current_contract(self):
cf_primary = self.asset_finder.create_continuous_future(
'FO', 0, 'calendar')
bar_data = self.create_bardata(
lambda: pd.Timestamp('2016-01-25', tz='UTC'))
contract = bar_data.current(cf_primary, 'contract')

self.assertEqual(contract.symbol, 'FOF')

bar_data = self.create_bardata(
lambda: pd.Timestamp('2016-01-26', tz='UTC'))
contract = bar_data.current(cf_primary, 'contract')

self.assertEqual(contract.symbol, 'FOG',
'Auto close at beginning of session so FOG is now '
'the current contract.')

bar_data = self.create_bardata(
lambda: pd.Timestamp('2016-01-27', tz='UTC'))
contract = bar_data.current(cf_primary, 'contract')
self.assertEqual(contract.symbol, 'FOG')

def test_current_contract_in_algo(self):
code = dedent("""
from zipline.api import (
record,
continuous_future,
schedule_function,
get_datetime,
)
def initialize(algo):
algo.primary_cl = continuous_future('FO', 0, 'calendar')
algo.secondary_cl = continuous_future('FO', 1, 'calendar')
schedule_function(record_current_contract)
def record_current_contract(algo, data):
record(datetime=get_datetime())
record(primary=data.current(algo.primary_cl, 'contract'))
record(secondary=data.current(algo.secondary_cl, 'contract'))
""")
algo = TradingAlgorithm(script=code,
sim_params=self.sim_params,
trading_calendar=self.trading_calendar,
env=self.env)
results = algo.run(self.data_portal)

self.assertEqual(results.iloc[0].primary.symbol,
'FOF',
'Primary should be FOF on first session.')
self.assertEqual(results.iloc[0].secondary.symbol,
'FOG',
'Secondary should be FOG on first session.')

# Second day, primary should switch to FOG
self.assertEqual(results.iloc[1].primary.symbol,
'FOG',
'Primary should be FOG on second session, auto close '
'is at beginning of the session.')
self.assertEqual(results.iloc[1].secondary.symbol,
'FOH',
'Secondary should be FOH on second session, auto '
'close is at beginning of the session.')

# Second day, primary should switch to FOG
self.assertEqual(results.iloc[2].primary.symbol,
'FOG',
'Primary should remain as FOG on third session.')
self.assertEqual(results.iloc[2].secondary.symbol,
'FOH',
'Secondary should remain as FOG on third session.')
3 changes: 2 additions & 1 deletion zipline/_protocol.pyx
Expand Up @@ -25,6 +25,7 @@ from cpython cimport bool
from collections import Iterable

from zipline.assets import Asset, Future
from zipline.assets.continuous_futures import ContinuousFuture
from zipline.zipline_warnings import ZiplineDeprecationWarning


Expand Down Expand Up @@ -254,7 +255,7 @@ cdef class BarData:
return dt

@check_parameters(('assets', 'fields'),
((Asset,) + string_types, string_types))
((Asset, ContinuousFuture) + string_types, string_types))
def current(self, assets, fields):
"""
Returns the current value of the given assets for the given fields
Expand Down
27 changes: 27 additions & 0 deletions zipline/algorithm.py
Expand Up @@ -1185,6 +1185,33 @@ def symbol(self, symbol_str):
as_of_date=_lookup_date,
)

@api_method
@preprocess(root_symbol_str=ensure_upper_case)
def continuous_future(self, root_symbol_str, offset, roll):
"""Create a specifier for a continuous contract.
Parameters
----------
root_symbol_str : str
The root symbol for the future chain.
offset : int
The distance from the primary contract.
roll_style : str
How rolls are determined.
Returns
-------
continuous_future : ContinuousFuture
The continuous future specifier.
"""
return self.asset_finder.create_continuous_future(
root_symbol_str,
offset,
roll,
)

@api_method
def symbols(self, *args):
"""Lookup multuple Equities as a list.
Expand Down

0 comments on commit 9546dea

Please sign in to comment.