From daf29c2d39d9b6229f1345f464141f0ac93f8513 Mon Sep 17 00:00:00 2001 From: Thomas Wiecki Date: Thu, 20 Dec 2012 12:49:24 -0500 Subject: [PATCH 1/5] ENH: Add granularity and annualizer arguments to TradingAlgorithm. Accompanying doc string and unittest. --- tests/test_algorithm.py | 14 ++++++++++++++ zipline/algorithm.py | 36 ++++++++++++++++++++++++++++++++---- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/tests/test_algorithm.py b/tests/test_algorithm.py index d757c04a..6485ccce 100644 --- a/tests/test_algorithm.py +++ b/tests/test_algorithm.py @@ -89,3 +89,17 @@ class TestTransformAlgorithm(TestCase): assert algo.registered_transforms['mavg']['kwargs'] == \ {'window_length': 2, 'market_aware': True} assert algo.registered_transforms['mavg']['class'] is MovingAverage + + def test_granularity_setting(self): + algo = TestRegisterTransformAlgorithm(granularity='daily') + self.assertEqual(algo.granularity, 'daily') + self.assertEqual(algo.annualizer, 250) + + algo = TestRegisterTransformAlgorithm(granularity='minutely') + self.assertEqual(algo.granularity, 'minutely') + self.assertEqual(algo.annualizer, 250 * 6 * 60) + + algo = TestRegisterTransformAlgorithm(granularity='minutely', + annualizer=10) + self.assertEqual(algo.granularity, 'minutely') + self.assertEqual(algo.annualizer, 10) diff --git a/zipline/algorithm.py b/zipline/algorithm.py index dda384fb..7622c665 100644 --- a/zipline/algorithm.py +++ b/zipline/algorithm.py @@ -65,8 +65,16 @@ class TradingAlgorithm(object): """ def __init__(self, *args, **kwargs): - """ - Initialize sids and other state variables. + """Initialize sids and other state variables. + + :Arguments: + granularity : str (daily, hourly or minutely) + The duration of the bars. + annualizer : int + Which constant to use for annualizing risk metrics. + If not provided, will extract from granularity. + capital_base : float + How much capital to start with. """ self.done = False self.order = None @@ -84,16 +92,36 @@ class TradingAlgorithm(object): self.slippage = VolumeShareSlippage() self.commission = PerShare() + self.granularity = kwargs.get('granularity', 'daily') + # annualizer is used for e.g. risk calculations + self.annualizer = kwargs.get('annualizer', None) + # set the capital base self.capital_base = kwargs.get('capital_base', DEFAULT_CAPITAL_BASE) - # an algorithm subclass needs to set initialized to True - # when it is fully initialized. + # an algorithm subclass needs to set initialized to True when + # it is fully initialized. self.initialized = False # call to user-defined constructor method self.initialize(*args, **kwargs) + # set annualizer according to granularity + # this is happening after initialize because granularity + # could be set in there. + if self.annualizer is None: + if self.granularity == 'daily': + self.annualizer = 250 + elif self.granularity == 'hourly': + # trading days * hours + self.annualizer = 250 * 6 + elif self.granularity == 'minutely': + # trading days * hours * minutes + self.annualizer = 250 * 6 * 60 + else: + raise NotImplementedError('{g} is not implemented.\ + '.format(g=self.granularity)) + def _create_generator(self, environment): """ Create a basic generator setup using the sources and From 3ca8029766966fd7bc963722e00599e2e6d8d7ce Mon Sep 17 00:00:00 2001 From: Thomas Wiecki Date: Thu, 20 Dec 2012 14:27:40 -0500 Subject: [PATCH 2/5] MIN: Replaced annualizer with a dictionary. Added set_granularity method. --- zipline/algorithm.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/zipline/algorithm.py b/zipline/algorithm.py index 7622c665..6e61ccdd 100644 --- a/zipline/algorithm.py +++ b/zipline/algorithm.py @@ -43,6 +43,10 @@ from zipline import MESSAGES DEFAULT_CAPITAL_BASE = float("1.0e5") +ANNUALIZER = {'daily': 250, + 'hourly': 250 * 6, + 'minutely': 250 * 6 * 60} + class TradingAlgorithm(object): """Base class for trading algorithms. Inherit and overload @@ -110,17 +114,7 @@ class TradingAlgorithm(object): # this is happening after initialize because granularity # could be set in there. if self.annualizer is None: - if self.granularity == 'daily': - self.annualizer = 250 - elif self.granularity == 'hourly': - # trading days * hours - self.annualizer = 250 * 6 - elif self.granularity == 'minutely': - # trading days * hours * minutes - self.annualizer = 250 * 6 * 60 - else: - raise NotImplementedError('{g} is not implemented.\ - '.format(g=self.granularity)) + self.annualizer = ANNUALIZER[self.granularity] def _create_generator(self, environment): """ @@ -323,3 +317,7 @@ class TradingAlgorithm(object): def set_transforms(self, transforms): assert isinstance(transforms, list) self.transforms = transforms + + def set_granuliarity(self, granularity): + assert granularity in ('daily', 'minute') + self.granularity = granularity From 8346155f0e2f97a3571d2492a48fcb3ffad0a96c Mon Sep 17 00:00:00 2001 From: Thomas Wiecki Date: Mon, 24 Dec 2012 12:55:52 -0500 Subject: [PATCH 3/5] MIN: Override annualizer if set. New handling of granularity (no default arg anymore). Renamed minutely to minute. --- tests/test_algorithm.py | 8 ++++---- zipline/algorithm.py | 22 +++++++++++----------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/test_algorithm.py b/tests/test_algorithm.py index 6485ccce..b22151ff 100644 --- a/tests/test_algorithm.py +++ b/tests/test_algorithm.py @@ -95,11 +95,11 @@ class TestTransformAlgorithm(TestCase): self.assertEqual(algo.granularity, 'daily') self.assertEqual(algo.annualizer, 250) - algo = TestRegisterTransformAlgorithm(granularity='minutely') - self.assertEqual(algo.granularity, 'minutely') + algo = TestRegisterTransformAlgorithm(granularity='minute') + self.assertEqual(algo.granularity, 'minute') self.assertEqual(algo.annualizer, 250 * 6 * 60) - algo = TestRegisterTransformAlgorithm(granularity='minutely', + algo = TestRegisterTransformAlgorithm(granularity='minute', annualizer=10) - self.assertEqual(algo.granularity, 'minutely') + self.assertEqual(algo.granularity, 'minute') self.assertEqual(algo.annualizer, 10) diff --git a/zipline/algorithm.py b/zipline/algorithm.py index 6e61ccdd..f3e8ce1d 100644 --- a/zipline/algorithm.py +++ b/zipline/algorithm.py @@ -45,7 +45,7 @@ DEFAULT_CAPITAL_BASE = float("1.0e5") ANNUALIZER = {'daily': 250, 'hourly': 250 * 6, - 'minutely': 250 * 6 * 60} + 'minute': 250 * 6 * 60} class TradingAlgorithm(object): @@ -96,9 +96,14 @@ class TradingAlgorithm(object): self.slippage = VolumeShareSlippage() self.commission = PerShare() - self.granularity = kwargs.get('granularity', 'daily') - # annualizer is used for e.g. risk calculations - self.annualizer = kwargs.get('annualizer', None) + if 'granularity' in kwargs: + self.set_granularity(kwargs.pop('granularity')) + else: + self.granularity = None + + # Override annualizer if set + if 'annualizer' in kwargs: + self.annualizer = kwargs['annualizer'] # set the capital base self.capital_base = kwargs.get('capital_base', DEFAULT_CAPITAL_BASE) @@ -110,12 +115,6 @@ class TradingAlgorithm(object): # call to user-defined constructor method self.initialize(*args, **kwargs) - # set annualizer according to granularity - # this is happening after initialize because granularity - # could be set in there. - if self.annualizer is None: - self.annualizer = ANNUALIZER[self.granularity] - def _create_generator(self, environment): """ Create a basic generator setup using the sources and @@ -318,6 +317,7 @@ class TradingAlgorithm(object): assert isinstance(transforms, list) self.transforms = transforms - def set_granuliarity(self, granularity): + def set_granularity(self, granularity): assert granularity in ('daily', 'minute') self.granularity = granularity + self.annualizer = ANNUALIZER[self.granularity] From 4615b29713caf68e5cc7ab34a4b2462cdf079fac Mon Sep 17 00:00:00 2001 From: Thomas Wiecki Date: Sun, 30 Dec 2012 11:50:46 -0500 Subject: [PATCH 4/5] REF: Renamed granularity to data_frequency. --- tests/test_algorithm.py | 14 +++++++------- zipline/algorithm.py | 18 +++++++++--------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/test_algorithm.py b/tests/test_algorithm.py index b22151ff..78b1bea8 100644 --- a/tests/test_algorithm.py +++ b/tests/test_algorithm.py @@ -90,16 +90,16 @@ class TestTransformAlgorithm(TestCase): {'window_length': 2, 'market_aware': True} assert algo.registered_transforms['mavg']['class'] is MovingAverage - def test_granularity_setting(self): - algo = TestRegisterTransformAlgorithm(granularity='daily') - self.assertEqual(algo.granularity, 'daily') + def test_data_frequency_setting(self): + algo = TestRegisterTransformAlgorithm(data_frequency='daily') + self.assertEqual(algo.data_frequency, 'daily') self.assertEqual(algo.annualizer, 250) - algo = TestRegisterTransformAlgorithm(granularity='minute') - self.assertEqual(algo.granularity, 'minute') + algo = TestRegisterTransformAlgorithm(data_frequency='minute') + self.assertEqual(algo.data_frequency, 'minute') self.assertEqual(algo.annualizer, 250 * 6 * 60) - algo = TestRegisterTransformAlgorithm(granularity='minute', + algo = TestRegisterTransformAlgorithm(data_frequency='minute', annualizer=10) - self.assertEqual(algo.granularity, 'minute') + self.assertEqual(algo.data_frequency, 'minute') self.assertEqual(algo.annualizer, 10) diff --git a/zipline/algorithm.py b/zipline/algorithm.py index f3e8ce1d..63801cf5 100644 --- a/zipline/algorithm.py +++ b/zipline/algorithm.py @@ -72,11 +72,11 @@ class TradingAlgorithm(object): """Initialize sids and other state variables. :Arguments: - granularity : str (daily, hourly or minutely) + data_frequency : str (daily, hourly or minutely) The duration of the bars. annualizer : int Which constant to use for annualizing risk metrics. - If not provided, will extract from granularity. + If not provided, will extract from data_frequency. capital_base : float How much capital to start with. """ @@ -96,10 +96,10 @@ class TradingAlgorithm(object): self.slippage = VolumeShareSlippage() self.commission = PerShare() - if 'granularity' in kwargs: - self.set_granularity(kwargs.pop('granularity')) + if 'data_frequency' in kwargs: + self.set_data_frequency(kwargs.pop('data_frequency')) else: - self.granularity = None + self.data_frequency = None # Override annualizer if set if 'annualizer' in kwargs: @@ -317,7 +317,7 @@ class TradingAlgorithm(object): assert isinstance(transforms, list) self.transforms = transforms - def set_granularity(self, granularity): - assert granularity in ('daily', 'minute') - self.granularity = granularity - self.annualizer = ANNUALIZER[self.granularity] + def set_data_frequency(self, data_frequency): + assert data_frequency in ('daily', 'minute') + self.data_frequency = data_frequency + self.annualizer = ANNUALIZER[self.data_frequency] From fccc5e8006394d01209b7a9d23fe2b930c2cb7b6 Mon Sep 17 00:00:00 2001 From: Thomas Wiecki Date: Sun, 30 Dec 2012 12:02:38 -0500 Subject: [PATCH 5/5] ENH: Added constants.py which contains financial constants. --- zipline/algorithm.py | 6 +----- zipline/finance/constants.py | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 zipline/finance/constants.py diff --git a/zipline/algorithm.py b/zipline/algorithm.py index 63801cf5..f0ae7b8b 100644 --- a/zipline/algorithm.py +++ b/zipline/algorithm.py @@ -32,7 +32,7 @@ from zipline.finance.slippage import ( transact_partial ) from zipline.finance.commission import PerShare, PerTrade - +from zipline.finance.constants import ANNUALIZER from zipline.gens.composites import ( date_sorted_sources, @@ -43,10 +43,6 @@ from zipline import MESSAGES DEFAULT_CAPITAL_BASE = float("1.0e5") -ANNUALIZER = {'daily': 250, - 'hourly': 250 * 6, - 'minute': 250 * 6 * 60} - class TradingAlgorithm(object): """Base class for trading algorithms. Inherit and overload diff --git a/zipline/finance/constants.py b/zipline/finance/constants.py new file mode 100644 index 00000000..ce7ec8f6 --- /dev/null +++ b/zipline/finance/constants.py @@ -0,0 +1,23 @@ +# +# Copyright 2012 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. + +TRADING_DAYS_IN_YEAR = 250 +TRADING_HOURS_IN_DAY = 6 +MINUTES_IN_HOUR = 60 + +ANNUALIZER = {'daily': TRADING_DAYS_IN_YEAR, + 'hourly': TRADING_DAYS_IN_YEAR * TRADING_HOURS_IN_DAY, + 'minute': TRADING_DAYS_IN_YEAR * TRADING_HOURS_IN_DAY * + MINUTES_IN_HOUR}