From 28f86bc14e55aaae86323f1abde539af005ac094 Mon Sep 17 00:00:00 2001 From: Peter Cawthron Date: Wed, 16 Oct 2013 15:14:58 +0100 Subject: [PATCH] BUG: Fix handling of STOP, LIMIT and STOP LIMIT Orders Includes specific handling of Buy Stop, Sell Stop, Buy Limit, Sell Limit, Buy Stop Limit and Sell Stop Limit orders. --- zipline/finance/blotter.py | 6 +++-- zipline/finance/slippage.py | 50 ++++++++++++++++++++++++++----------- 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/zipline/finance/blotter.py b/zipline/finance/blotter.py index 3af95fd5..cab04045 100644 --- a/zipline/finance/blotter.py +++ b/zipline/finance/blotter.py @@ -282,13 +282,16 @@ class Order(object): Update internal state based on price triggers and the trade event's price. """ - stop_reached, limit_reached = \ + stop_reached, limit_reached, sl_stop_reached = \ check_order_triggers(self, event) if (stop_reached, limit_reached) \ != (self.stop_reached, self.limit_reached): self.dt = event.dt self.stop_reached = stop_reached self.limit_reached = limit_reached + if sl_stop_reached: + # Change the STOP LIMIT order into a LIMIT order + self.stop = None def handle_split(self, split_event): ratio = split_event.ratio @@ -329,7 +332,6 @@ class Order(object): For a market order, True. For a stop order, True IFF stop_reached. For a limit order, True IFF limit_reached. - For a stop-limit order, True IFF (stop_reached AND limit_reached) """ if self.stop and not self.stop_reached: return False diff --git a/zipline/finance/slippage.py b/zipline/finance/slippage.py index b4c2895a..4ff0226f 100644 --- a/zipline/finance/slippage.py +++ b/zipline/finance/slippage.py @@ -31,30 +31,52 @@ def check_order_triggers(order, event): For market orders, will return (False, False). For stop orders, limit_reached will always be False. For limit orders, stop_reached will always be False. + For stop limit orders a Boolean is returned to flag + that the stop has been reached. Orders that have been triggered already (price targets reached), the order's current values are returned. """ if order.triggered: - return (order.stop_reached, order.limit_reached) + return (order.stop_reached, order.limit_reached, False) stop_reached = False limit_reached = False - # if the stop price is reached, simply set stop_reached + sl_stop_reached = False if order.stop is not None: - if (order.direction * (event.price - order.stop) <= 0): - # convert stop -> limit or market - stop_reached = True + if order.limit is not None: + if order.amount > 0: + # This is a BUY STOP LIMIT order + if event.price >= order.stop: + sl_stop_reached = True + if event.price <= order.limit: + limit_reached = True + else: + # This is a SELL STOP LIMIT order + if event.price <= order.stop: + sl_stop_reached = True + if event.price >= order.limit: + limit_reached = True + else: + if order.amount > 0: + # This is a BUY STOP order + if event.price >= order.stop: + stop_reached = True + else: + # This is a SELL STOP order + if event.price <= order.stop: + stop_reached = True + else: + if order.amount > 0: + # This is BUY LIMIT order + if event.price <= order.limit: + limit_reached = True + else: + # This is a SELL LIMIT order + if event.price >= order.limit: + limit_reached = True - # if the limit price is reached, we execute this order at - # (event.price + simulated_impact) - # we skip this order with a continue when the limit is not reached - if order.limit is not None: - # if limit conditions not met, then continue - if (order.direction * (event.price - order.limit) <= 0): - limit_reached = True - - return (stop_reached, limit_reached) + return (stop_reached, limit_reached, sl_stop_reached) def transact_stub(slippage, commission, event, open_orders):