From bc95c3a62ea29a3643dc68f6dabc6621a27f2511 Mon Sep 17 00:00:00 2001 From: fawce Date: Wed, 17 Apr 2013 22:47:00 -0400 Subject: [PATCH] BUG: Fix emission of order updates. The emission of order updates from the blotter were incorrect, and subsequently, performance. Previously, only the first action of the order was emitted, fix so that all status updates are emitted. --- zipline/finance/performance.py | 3 +- zipline/finance/slippage.py | 29 ++++++---------- zipline/gens/tradesimulation.py | 59 +++++++++++++++++++++------------ 3 files changed, 50 insertions(+), 41 deletions(-) diff --git a/zipline/finance/performance.py b/zipline/finance/performance.py index 630cf9b8..88566461 100644 --- a/zipline/finance/performance.py +++ b/zipline/finance/performance.py @@ -551,8 +551,7 @@ class PerformancePeriod(object): self.period_cash_flow = 0.0 self.pnl = 0.0 self.processed_transactions = [] - self.placed_orders = \ - [order for order in self.placed_orders if order.open] + self.placed_orders = [] self.cumulative_capital_used = 0.0 self.max_capital_used = 0.0 self.max_leverage = 0.0 diff --git a/zipline/finance/slippage.py b/zipline/finance/slippage.py index 59090257..c3ccb2d5 100644 --- a/zipline/finance/slippage.py +++ b/zipline/finance/slippage.py @@ -144,9 +144,6 @@ class VolumeShareSlippage(object): if zp_math.tolerant_equals(open_amount, 0): continue - # check price limits, continue if the - # order isn't triggered yet - order.check_triggers(event) if not order.triggered: continue @@ -175,19 +172,18 @@ class VolumeShareSlippage(object): simulated_impact = (volume_share) ** 2 \ * self.price_impact * order.direction * event.price - txn = create_transaction( - event.sid, - cur_amount, - # In the future, we may want to change the next line - # for limit pricing - event.price + simulated_impact, - dt.replace(tzinfo=pytz.utc), - order.id - ) + if cur_amount > 0: + txn = create_transaction( + event.sid, + cur_amount, + # In the future, we may want to change the next line + # for limit pricing + event.price + simulated_impact, + dt.replace(tzinfo=pytz.utc), + order.id + ) - # mark the last_modified date of the order to match - order.last_modified_dt = event.dt - txns.append(txn) + txns.append(txn) return txns @@ -210,9 +206,6 @@ class FixedSlippage(object): # and one for 100 shares short # such as in a hedging scenario? - # check price limits, continue if the - # order isn't triggered yet - order.check_triggers(event) if not order.triggered: continue diff --git a/zipline/gens/tradesimulation.py b/zipline/gens/tradesimulation.py index 063e23de..3327f543 100644 --- a/zipline/gens/tradesimulation.py +++ b/zipline/gens/tradesimulation.py @@ -51,9 +51,6 @@ class Blotter(object): self.open_orders = defaultdict(list) # keep a dict of orders by their own id self.orders = {} - # track transactions by sid and by order - self.txns_by_sid = defaultdict(list) - self.txns_by_order = defaultdict(list) # holding orders that have come in since the last # event. self.new_orders = [] @@ -72,14 +69,32 @@ class Blotter(object): for date, snapshot in stream_in: # relay any orders placed in prior snapshot # handling and reset the internal holding pen - results = self.new_orders - self.new_orders = [] + if self.new_orders: + yield date, self.new_orders + self.new_orders = [] + results = [] + for event in snapshot: results.append(event) # We only fill transactions on trade events. if event.type == DATASOURCE_TYPE.TRADE: txns = self.process_trade(event) results.extend(txns) + + modified_orders = [order for order + in self.open_orders[event.sid] + if order.last_modified_dt == date] + results.extend(modified_orders) + + # update the open orders for the trade_event's sid + self.open_orders[event.sid] = [order for order + in self.open_orders[event.sid] + if order.open] + + for order in modified_orders: + if not order.open: + del self.orders[order.id] + yield date, results def process_trade(self, trade_event): @@ -98,22 +113,15 @@ class Blotter(object): else: return [] + for order in current_orders: + # check price limits, continue if the + # order isn't triggered yet + order.check_triggers(trade_event) txns = self.transact(trade_event, current_orders) for txn in txns: - self.txns_by_order[txn.order_id].append(txn) - self.txns_by_sid[txn.sid].append(txn) self.orders[txn.order_id].filled += txn.amount - - # update the open orders for the trade_event's sid - self.open_orders[trade_event.sid] = \ - [order for order in orders if order.open] - - # drop any filled orders. - filled = \ - [order.id for order in orders if not order.open] - - for order_id in filled: - del self.orders[order_id] + # mark the last_modified date of the order to match + self.orders[txn.order_id].last_modified_dt = txn.dt return txns @@ -129,7 +137,7 @@ class Order(object): @filled - how many shares of the order have been filled so far """ # get a string representation of the uuid. - self.id = uuid.uuid4().get_hex() + self.id = self.make_id() self.dt = dt self.last_modified_dt = dt self.sid = sid @@ -143,6 +151,9 @@ class Order(object): self.direction = math.copysign(1, self.amount) self.type = DATASOURCE_TYPE.ORDER + def make_id(self): + return uuid.uuid4().get_hex() + def to_dict(self): py = copy(self.__dict__) for field in ['type', 'direction']: @@ -154,9 +165,13 @@ class Order(object): Update internal state based on price triggers and the trade event's price. """ - self.last_modified_dt = event.dt - self.stop_reached, self.limit_reached = \ + stop_reached, limit_reached = \ check_order_triggers(self, event) + if (stop_reached, limit_reached) \ + != (self.stop_reached, self.limit_reached): + self.last_modified_dt = event.dt + self.stop_reached = stop_reached + self.limit_reached = limit_reached @property def open(self): @@ -377,6 +392,8 @@ class AlgorithmSimulator(object): # receives its next message. self.blotter.place_order(order) + return order.id + def transform(self, stream_in): """ Main generator work loop.