From f41c37a606e40b1c966b7de2ea28b1c3bf049d76 Mon Sep 17 00:00:00 2001 From: Scott Sanderson Date: Fri, 25 Apr 2014 19:02:57 -0400 Subject: [PATCH] ENH: Add the ability to specify an exchange on an ExecutionStyle. Adds the exchange property the interface for ExecutionStyle and adds an exchange parameter to the interface of all the existing ExceutionStyles. Subclasses wishing to support the ability to specify an exchange should set the _exchange attribute in __init__. --- zipline/finance/execution.py | 226 ++++++++++++++++++----------------- 1 file changed, 119 insertions(+), 107 deletions(-) diff --git a/zipline/finance/execution.py b/zipline/finance/execution.py index 04226e7b..368d7c25 100644 --- a/zipline/finance/execution.py +++ b/zipline/finance/execution.py @@ -22,6 +22,125 @@ from six import with_metaclass import zipline.utils.math_utils as zp_math +class ExecutionStyle(with_metaclass(abc.ABCMeta)): + """ + Abstract base class representing a modification to a standard order. + """ + + _exchange = None + + @abc.abstractmethod + def get_limit_price(self, is_buy): + """ + Get the limit price for this order. + Returns either None or a numerical value >= 0. + """ + raise NotImplemented + + @abc.abstractmethod + def get_stop_price(self, is_buy): + """ + Get the stop price for this order. + Returns either None or a numerical value >= 0. + """ + raise NotImplemented + + @property + def exchange(self): + """ + The exchange to which this order should be routed. + """ + return self._exchange + + +class MarketOrder(ExecutionStyle): + """ + Class encapsulating an order to be placed at the current market price. + """ + + def __init__(self, exchange=None): + self._exchange = exchange + + def get_limit_price(self, _is_buy): + return None + + def get_stop_price(self, _is_buy): + return None + + +class LimitOrder(ExecutionStyle): + """ + Execution style representing an order to be executed at a price equal to or + better than a specified limit price. + """ + def __init__(self, limit_price, exchange=None): + """ + Store the given price. + """ + if limit_price < 0: + raise ValueError("Can't place a limit with a negative price.") + self.limit_price = limit_price + self._exchange = exchange + + def get_limit_price(self, is_buy): + return asymmetric_round_price_to_penny(self.limit_price, is_buy) + + def get_stop_price(self, _is_buy): + return None + + +class StopOrder(ExecutionStyle): + """ + Execution style representing an order to be placed once the market price + reaches a specified stop price. + """ + def __init__(self, stop_price, exchange=None): + """ + Store the given price. + """ + if stop_price < 0: + raise ValueError( + "Can't place a stop order with a negative price." + ) + self.stop_price = stop_price + self._exchange = exchange + + def get_limit_price(self, _is_buy): + return None + + def get_stop_price(self, is_buy): + return asymmetric_round_price_to_penny(self.stop_price, not is_buy) + + +class StopLimitOrder(ExecutionStyle): + """ + Execution style representing a limit order to be placed with a specified + limit price once the market reaches a specified stop price. + """ + def __init__(self, limit_price, stop_price, exchange=None): + """ + Store the given prices + """ + if limit_price < 0: + raise ValueError( + "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." + ) + + self.limit_price = limit_price + self.stop_price = stop_price + self._exchange = exchange + + def get_limit_price(self, is_buy): + return asymmetric_round_price_to_penny(self.limit_price, is_buy) + + def get_stop_price(self, is_buy): + return asymmetric_round_price_to_penny(self.stop_price, not is_buy) + + def asymmetric_round_price_to_penny(price, prefer_round_down, diff=(0.0095 - .005)): """ @@ -50,110 +169,3 @@ def asymmetric_round_price_to_penny(price, prefer_round_down, if zp_math.tolerant_equals(rounded, 0.0): return 0.0 return rounded - - -class ExecutionStyle(with_metaclass(abc.ABCMeta)): - """ - Abstract base class representing a modification to a standard order. - """ - - @abc.abstractmethod - def get_limit_price(self, is_buy): - """ - Get the limit price for this order. - Returns either None or a numerical value >= 0. - """ - raise NotImplemented - - @abc.abstractmethod - def get_stop_price(self, is_buy): - """ - Get the stop price for this order. - Returns either None or a numerical value >= 0. - """ - raise NotImplemented - - -class MarketOrder(ExecutionStyle): - """ - Class encapsulating an order to be placed at the current market price. - """ - - def __init__(self): - pass - - def get_limit_price(self, _is_buy): - return None - - def get_stop_price(self, _is_buy): - return None - - -class LimitOrder(ExecutionStyle): - """ - Execution style representing an order to be executed at a price equal to or - better than a specified limit price. - """ - def __init__(self, limit_price): - """ - Store the given price. - """ - if limit_price < 0: - raise ValueError("Can't place a limit with a negative price.") - self.limit_price = limit_price - - def get_limit_price(self, is_buy): - return asymmetric_round_price_to_penny(self.limit_price, is_buy) - - def get_stop_price(self, _is_buy): - return None - - -class StopOrder(ExecutionStyle): - """ - Execution style representing an order to be placed once the market price - reaches a specified stop price. - """ - def __init__(self, stop_price): - """ - Store the given price. - """ - if stop_price < 0: - raise ValueError( - "Can't place a stop order with a negative price." - ) - self.stop_price = stop_price - - def get_limit_price(self, _is_buy): - return None - - def get_stop_price(self, is_buy): - return asymmetric_round_price_to_penny(self.stop_price, not is_buy) - - -class StopLimitOrder(ExecutionStyle): - """ - Execution style representing a limit order to be placed with a specified - limit price once the market reaches a specified stop price. - """ - def __init__(self, limit_price, stop_price): - """ - Store the given prices - """ - if limit_price < 0: - raise ValueError( - "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." - ) - - self.limit_price = limit_price - self.stop_price = stop_price - - def get_limit_price(self, is_buy): - return asymmetric_round_price_to_penny(self.limit_price, is_buy) - - def get_stop_price(self, is_buy): - return asymmetric_round_price_to_penny(self.stop_price, not is_buy)