From a2b1b1b71e46fc5400cec1c4b699be806dcb062a Mon Sep 17 00:00:00 2001 From: Eddie Hebert Date: Mon, 14 Jan 2013 21:27:27 -0500 Subject: [PATCH] Uses vdot and numpy arrays for position totals. Gets almost 100x speed up over iterating over the values and summing up the values in Python. Farms out the work to numpy and atlas by using the vector dot product of the amounts and last sale prices. Adds some wiring of keeping track of an index into the numpy arrays for each position, so that value can be overwritten as events update those amounts and sale prices. --- zipline/finance/performance.py | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/zipline/finance/performance.py b/zipline/finance/performance.py index 3d1f4641..ed1e2822 100644 --- a/zipline/finance/performance.py +++ b/zipline/finance/performance.py @@ -136,6 +136,8 @@ import datetime import pytz import math +import numpy as np + import zipline.protocol as zp import zipline.finance.risk as risk @@ -420,6 +422,12 @@ class PerformancePeriod(object): self.max_capital_used = 0.0 self.max_leverage = 0.0 + # Maps position to following array indexes + self._position_index_map = {} + # Arrays for quick calculations of positions value + self._position_amounts = np.array([]) + self._position_last_sale_prices = np.array([]) + self.calculate_performance() # An object to recycle via assigning new values @@ -438,6 +446,17 @@ class PerformancePeriod(object): self.max_capital_used = 0.0 self.max_leverage = 0.0 + def index_for_position(self, sid): + try: + index = self._position_index_map[sid] + except KeyError: + index = len(self._position_index_map) + self._position_index_map[sid] = index + self._position_amounts = np.append(self._position_amounts, [0]) + self._position_last_sale_prices = np.append( + self._position_last_sale_prices, [0]) + return index + def calculate_performance(self): self.ending_value = self.calculate_positions_value() @@ -454,7 +473,11 @@ class PerformancePeriod(object): def execute_transaction(self, txn): # Update Position # ---------------- - self.positions[txn.sid].update(txn) + position = self.positions[txn.sid] + position.update(txn) + index = self.index_for_position(txn.sid) + self._position_amounts[index] = position.amount + self.period_capital_used += -1 * txn.price * txn.amount # Max Leverage @@ -485,15 +508,15 @@ class PerformancePeriod(object): return int(base * round(float(x) / base)) def calculate_positions_value(self): - mktValue = 0.0 - for key, pos in self.positions.iteritems(): - mktValue += pos.currentValue() - return mktValue + return np.vdot(self._position_amounts, self._position_last_sale_prices) def update_last_sale(self, event): is_trade = event.type == zp.DATASOURCE_TYPE.TRADE if event.sid in self.positions and is_trade: self.positions[event.sid].last_sale_price = event.price + index = self.index_for_position(event.sid) + self._position_last_sale_prices[index] = event.price + self.positions[event.sid].last_sale_date = event.dt def __core_dict(self):