From 6cf81a3f1c540a3d2754969a00a3ec793a18f6b5 Mon Sep 17 00:00:00 2001 From: Jean Bredeche Date: Mon, 27 Mar 2017 20:44:12 -0400 Subject: [PATCH] ENH: Allow override of order amount rounding. (#1722) * ENH: Use regular rounding to calculate order amounts. We previously tried to prevent accidental over-ordering by truncating orders down unless they were within 1e-4 of the next higher integer. Unfortunately, this makes it easy for a sell order to be one share short of the desired position. Using regular rounding treats both buys and sells in the same way. * ENH keep non-rounding behavior consistent, but leave code structured to make easier to override * DOC make round_order public and describe behavior in docstring --- tests/test_algorithm.py | 21 +++++++++++++++++++++ zipline/algorithm.py | 19 ++++++++++++++----- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/tests/test_algorithm.py b/tests/test_algorithm.py index 64f760d7..847bf485 100644 --- a/tests/test_algorithm.py +++ b/tests/test_algorithm.py @@ -907,6 +907,27 @@ def before_trading_start(context, data): ) self.assertEqual(algo.sim_params.data_frequency, 'minute') + def test_order_rounding(self): + answer_key = [ + (0, 0), + (10, 10), + (1.1, 1), + (1.5, 1), + (1.9998, 1), + (1.99991, 2), + ] + + for input, answer in answer_key: + self.assertEqual( + answer, + TradingAlgorithm.round_order(input) + ) + + self.assertEqual( + -1 * answer, + TradingAlgorithm.round_order(-1 * input) + ) + @parameterized.expand([ ('order', TestOrderAlgorithm,), ('order_value', TestOrderValueAlgorithm,), diff --git a/zipline/algorithm.py b/zipline/algorithm.py index b645e4a9..ee324174 100644 --- a/zipline/algorithm.py +++ b/zipline/algorithm.py @@ -131,7 +131,7 @@ from zipline.utils.events import ( from zipline.utils.factory import create_simulation_parameters from zipline.utils.math_utils import ( tolerant_equals, - round_if_near_integer + round_if_near_integer, ) from zipline.utils.pandas_utils import clear_dataframe_indexer_caches from zipline.utils.preprocess import preprocess @@ -1422,10 +1422,7 @@ class TradingAlgorithm(object): def _calculate_order(self, asset, amount, limit_price=None, stop_price=None, style=None): - # Truncate to the integer share count that's either within .0001 of - # amount or closer to zero. - # E.g. 3.9999 -> 4.0; 5.5 -> 5.0; -5.5 -> -5.0 - amount = int(round_if_near_integer(amount)) + amount = self.round_order(amount) # Raises a ZiplineError if invalid parameters are detected. self.validate_order_params(asset, @@ -1441,6 +1438,18 @@ class TradingAlgorithm(object): style) return amount, style + @staticmethod + def round_order(amount): + """ + Convert number of shares to an integer. + + By default, truncates to the integer share count that's either within + .0001 of amount or closer to zero. + + E.g. 3.9999 -> 4.0; 5.5 -> 5.0; -5.5 -> -5.0 + """ + return int(round_if_near_integer(amount)) + def validate_order_params(self, asset, amount,