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)