diff --git a/tests/test_execution_styles.py b/tests/test_execution_styles.py index dc1b44c5..3ae60de5 100644 --- a/tests/test_execution_styles.py +++ b/tests/test_execution_styles.py @@ -31,6 +31,10 @@ from zipline.utils.test_utils import( teardown_logger, ) +from zipline.errors import( + BadOrderParameters +) + class ExecutionStyleTestCase(TestCase): """ @@ -58,7 +62,7 @@ class ExecutionStyleTestCase(TestCase): for delta in range(1, 10) ] - INVALID_PRICES = [(-1,), (-1.0,), (0 - epsilon,)] + INVALID_PRICES = [(-1,), (-1.0,), (0 - epsilon,), (float('nan'),)] def setUp(self): setup_logger(self) @@ -72,14 +76,14 @@ class ExecutionStyleTestCase(TestCase): Test that execution styles throw appropriate exceptions upon receipt of an invalid price field. """ - with self.assertRaises(ValueError): + with self.assertRaises(BadOrderParameters): LimitOrder(price) - with self.assertRaises(ValueError): + with self.assertRaises(BadOrderParameters): StopOrder(price) for lmt, stp in [(price, 1), (1, price), (price, price)]: - with self.assertRaises(ValueError): + with self.assertRaises(BadOrderParameters): StopLimitOrder(lmt, stp) def test_market_order_prices(self): diff --git a/zipline/errors.py b/zipline/errors.py index 061dd246..4d92706d 100644 --- a/zipline/errors.py +++ b/zipline/errors.py @@ -137,6 +137,14 @@ class UnsupportedOrderParameters(ZiplineError): msg = "{msg}" +class BadOrderParameters(ZiplineError): + """ + Raised if any impossible parameters (nan, negative limit/stop) + are passed to an order call. + """ + msg = "{msg}" + + class OrderDuringInitialize(ZiplineError): """ Raised if order is called during initialize() diff --git a/zipline/finance/execution.py b/zipline/finance/execution.py index 368d7c25..23ce52bb 100644 --- a/zipline/finance/execution.py +++ b/zipline/finance/execution.py @@ -21,6 +21,10 @@ from six import with_metaclass import zipline.utils.math_utils as zp_math +from math import isnan + +from zipline.errors import BadOrderParameters + class ExecutionStyle(with_metaclass(abc.ABCMeta)): """ @@ -77,8 +81,17 @@ class LimitOrder(ExecutionStyle): """ Store the given price. """ + + if isnan(limit_price): + raise BadOrderParameters( + msg="""Attempted to place an order with a limit price + of NaN.""" + ) + if limit_price < 0: - raise ValueError("Can't place a limit with a negative price.") + raise BadOrderParameters( + msg="Can't place a limit with a negative price." + ) self.limit_price = limit_price self._exchange = exchange @@ -98,9 +111,16 @@ class StopOrder(ExecutionStyle): """ Store the given price. """ + + if isnan(stop_price): + raise BadOrderParameters( + msg="""Attempted to place an order with a stop price + of NaN.""" + ) + if stop_price < 0: - raise ValueError( - "Can't place a stop order with a negative price." + raise BadOrderParameters( + msg="Can't place a stop order with a negative price." ) self.stop_price = stop_price self._exchange = exchange @@ -121,13 +141,25 @@ class StopLimitOrder(ExecutionStyle): """ Store the given prices """ + + if isnan(limit_price): + raise BadOrderParameters( + msg="""Attempted to place an order with a limit price + of NaN.""" + ) + if isnan(stop_price): + raise BadOrderParameters( + msg="""Attempted to place an order with a stop price + of NaN.""" + ) + if limit_price < 0: - raise ValueError( - "Can't place a limit with a negative price." + raise BadOrderParameters( + msg="Can't place a limit with a negative price." ) if stop_price < 0: - raise ValueError( - "Can't place a stop order with a negative price." + raise BadOrderParameters( + msg="Can't place a stop order with a negative price." ) self.limit_price = limit_price