From 3521a11ed43e5f18ce4c14d737f9c7f057031021 Mon Sep 17 00:00:00 2001 From: Delaney Granizo-Mackenzie Date: Tue, 8 Jul 2014 10:58:31 -0400 Subject: [PATCH] ENH: Added informative message for calling order in init. Previously, calling order() in initalize resulted in a weird stack trace. It now returns a well formulated error that is readable to the user through the API. Adding a slippage kwarg to test_algorithm and simfactor was necessary because slippage can only be called during init. Previously initaliazed was never set to true and calls to init-only function were sprinkled around the code in non-init sections. Code changes were to enforce init-only rules. --- tests/test_algorithm.py | 17 ++++++++++++++++- zipline/algorithm.py | 9 ++++++++- zipline/errors.py | 7 +++++++ zipline/test_algorithms.py | 23 +++++++++++++++++++++-- zipline/utils/simfactory.py | 10 ++-------- 5 files changed, 54 insertions(+), 12 deletions(-) diff --git a/tests/test_algorithm.py b/tests/test_algorithm.py index a806c5fd..e6b458b6 100644 --- a/tests/test_algorithm.py +++ b/tests/test_algorithm.py @@ -29,6 +29,7 @@ import zipline.utils.factory as factory import zipline.utils.simfactory as simfactory from zipline.errors import ( + OrderDuringInitialize, RegisterTradingControlPostInit, TradingControlViolation, ) @@ -53,6 +54,7 @@ from zipline.test_algorithms import ( api_algo, api_symbol_algo, call_all_order_methods, + call_order_in_init, handle_data_api, handle_data_noop, initialize_api, @@ -585,7 +587,8 @@ def handle_data(context, data): self._algo_record_float_magic_should_pass('nan') def test_order_methods(self): - """Only test that order methods can be called without error. + """ + Only test that order methods can be called without error. Correct filling of orders is tested in zipline. """ test_algo = TradingAlgorithm( @@ -602,6 +605,18 @@ def handle_data(context, data): output, _ = drain_zipline(self, zipline) + def test_order_in_init(self): + """ + Test that calling order in initialize + will return an error + """ + with self.assertRaises(OrderDuringInitialize): + test_algo = TradingAlgorithm( + script=call_order_in_init, + sim_params=self.sim_params, + ) + set_algo_instance(test_algo) + class TestHistory(TestCase): def test_history(self): diff --git a/zipline/algorithm.py b/zipline/algorithm.py index 1949763d..a10645ee 100644 --- a/zipline/algorithm.py +++ b/zipline/algorithm.py @@ -27,6 +27,7 @@ from six import iteritems, exec_ from operator import attrgetter from zipline.errors import ( + OrderDuringInitialize, OverrideCommissionPostInit, OverrideSlippagePostInit, RegisterTradingControlPostInit, @@ -76,7 +77,6 @@ DEFAULT_CAPITAL_BASE = float("1.0e5") class TradingAlgorithm(object): - """ Base class for trading algorithms. Inherit and overload initialize() and handle_data(data). @@ -531,6 +531,13 @@ class TradingAlgorithm(object): Raises an UnsupportedOrderParameters if invalid arguments are found. """ + + # Make sure we're not in init before we order. + if not self.initialized: + raise OrderDuringInitialize( + msg="order() can only be called from within handle_data()" + ) + if style: if limit_price: raise UnsupportedOrderParameters( diff --git a/zipline/errors.py b/zipline/errors.py index 3c34763e..061dd246 100644 --- a/zipline/errors.py +++ b/zipline/errors.py @@ -137,6 +137,13 @@ class UnsupportedOrderParameters(ZiplineError): msg = "{msg}" +class OrderDuringInitialize(ZiplineError): + """ + Raised if order is called during initialize() + """ + msg = "{msg}" + + class TradingControlViolation(ZiplineError): """ Raised if an order would violate a constraint set by a TradingControl. diff --git a/zipline/test_algorithms.py b/zipline/test_algorithms.py index ef99f597..31e20274 100644 --- a/zipline/test_algorithms.py +++ b/zipline/test_algorithms.py @@ -97,7 +97,12 @@ class TestAlgorithm(TradingAlgorithm): at the close of a simulation. """ - def initialize(self, sid, amount, order_count, sid_filter=None): + def initialize(self, + sid, + amount, + order_count, + sid_filter=None, + slippage=None): self.count = order_count self.sid = sid self.amount = amount @@ -108,6 +113,9 @@ class TestAlgorithm(TradingAlgorithm): else: self.sid_filter = [self.sid] + if slippage is not None: + self.set_slippage(slippage) + def handle_data(self, data): # place an order for amount shares of sid if self.incr < self.count: @@ -241,7 +249,6 @@ class RecordAlgorithm(TradingAlgorithm): class TestOrderAlgorithm(TradingAlgorithm): def initialize(self): self.incr = 0 - self.sale_price = None def handle_data(self, data): if self.incr == 0: @@ -633,6 +640,7 @@ class BatchTransformAlgorithm(TradingAlgorithm): self.iter = 0 self.set_slippage(FixedSlippage()) + self.initialized = True def handle_data(self, data): self.history_return_price_class.append( @@ -943,6 +951,17 @@ def handle_data(context, data): order(symbol(0), 1) """ +call_order_in_init = """ +from zipline.api import (order) + +def initialize(context): + order(0, 10) + pass + +def handle_data(context, data): + pass +""" + call_all_order_methods = """ from zipline.api import (order, order_value, diff --git a/zipline/utils/simfactory.py b/zipline/utils/simfactory.py index 7250162c..70fbcc9c 100644 --- a/zipline/utils/simfactory.py +++ b/zipline/utils/simfactory.py @@ -62,7 +62,8 @@ def create_test_zipline(**config): order_amount, order_count, sim_params=config.get('sim_params', - factory.create_simulation_parameters()) + factory.create_simulation_parameters()), + slippage=config.get('slippage'), ) # ------------------- @@ -94,13 +95,6 @@ def create_test_zipline(**config): if transforms is not None: test_algo.set_transforms(transforms) - # ------------------- - # Slippage - # ------------------ - slippage = config.get('slippage', None) - if slippage is not None: - test_algo.set_slippage(slippage) - # ------------------ # generator/simulator sim = test_algo.get_generator()