diff --git a/zipline/finance/performance.py b/zipline/finance/performance.py index 5a2aeaef..e237ec7b 100644 --- a/zipline/finance/performance.py +++ b/zipline/finance/performance.py @@ -59,11 +59,20 @@ Position Tracking +-----------------+----------------------------------------------------+ | last_sale_price | price at last sale of the security on the exchange | +-----------------+----------------------------------------------------+ + | cost_basis | the volume weighted average price paid per share | + +-----------------+----------------------------------------------------+ + Performance Period ================== +Performance Periods are updated with every trade. When calling +code needs a portfolio object that fulfills the algorithm +protocol, use the PerformancePeriod.as_portfolio method. See that +method for comments on the specific fields provided (and +omitted). + +---------------+------------------------------------------------------+ | key | value | +===============+======================================================+ @@ -190,7 +199,7 @@ class PerformanceTracker(object): ) def get_portfolio(self): - return self.cumulative_performance.to_ndict() + return self.cumulative_performance.as_portfolio() def open(self, context): if self.results_addr: @@ -247,12 +256,12 @@ class PerformanceTracker(object): self.cumulative_performance.update_last_sale(event) self.todays_performance.update_last_sale(event) - - def handle_market_close(self): #calculate performance as of last trade self.cumulative_performance.calculate_performance() self.todays_performance.calculate_performance() + def handle_market_close(self): + # add the return results from today to the list of DailyReturn objects. todays_date = self.market_close.replace(hour=0, minute=0, second=0) todays_return_obj = risk.DailyReturn( @@ -490,14 +499,7 @@ class PerformancePeriod(object): self.positions[event.sid].last_sale_price = event.price self.positions[event.sid].last_sale_date = event.dt - def to_dict(self): - """ - Creates a dictionary representing the state of this performance - period. See header comments for a detailed description. - """ - positions = self.get_positions_list() - transactions = [x.as_dict() for x in self.processed_transactions] - + def __core_dict(self): rval = { 'ending_value' : self.ending_value, 'capital_used' : self.period_capital_used, @@ -508,46 +510,69 @@ class PerformancePeriod(object): 'cumulative_capital_used' : self.cumulative_capital_used, 'max_capital_used' : self.max_capital_used, 'max_leverage' : self.max_leverage, - 'positions' : positions, 'pnl' : self.pnl, 'returns' : self.returns, - 'transactions' : transactions, 'period_open' : self.period_open, 'period_close' : self.period_close } + return rval + + + def to_dict(self): + """ + Creates a dictionary representing the state of this performance + period. See header comments for a detailed description. + """ + rval = self.__core_dict() + positions = self.get_positions_list() + rval['positions'] = positions + # we want the key to be absent, not just empty - if not self.keep_transactions: - del rval['transactions'] + if self.keep_transactions: + transactions = [x.as_dict() for x in self.processed_transactions] + rval['transactions'] = transactions return rval - def to_ndict(self): + def as_portfolio(self): """ - Creates a ndict representing the state of this perfomance period. - Properties are the same as the results of to_dict. See header comments - for a detailed description. - + The purpose of this method is to provide a portfolio + object to algorithms running inside the same trading + client. The data needed is captured raw in a + PerformancePeriod, and in this method we rename some + fields for usability and remove extraneous fields. """ - positions = self.get_positions(ndicted=True) + portfolio = self.__core_dict() + # rename: + # ending_cash -> cash + # period_open -> backtest_start + # + # remove: + # period_close, starting_value, + # cumulative_capital_used, max_leverage, max_capital_used + portfolio['cash'] = portfolio['ending_cash'] + portfolio['start_date'] = portfolio['period_open'] + portfolio['position_value'] = portfolio['ending_value'] - positions = zp.ndict(positions) + del(portfolio['ending_cash']) + del(portfolio['period_open']) + del(portfolio['period_close']) + del(portfolio['starting_value']) + del(portfolio['ending_value']) + del(portfolio['cumulative_capital_used']) + del(portfolio['max_leverage']) + del(portfolio['max_capital_used']) - return zp.ndict({ - 'ending_value' : self.ending_value, - 'capital_used' : self.period_capital_used, - 'starting_value' : self.starting_value, - 'starting_cash' : self.starting_cash, - 'ending_cash' : self.ending_cash, - 'cumulative_capital_used' : self.cumulative_capital_used, - 'max_capital_used' : self.max_capital_used, - 'max_leverage' : self.max_leverage, - 'positions' : positions, - 'transactions' : self.processed_transactions - }) + portfolio['positions'] = self.get_positions(ndicted=True) + return zp.ndict(portfolio) def get_positions(self, ndicted=False): - positions = {} + if ndicted: + positions = zp.ndict({}) + else: + positions = {} + for sid, pos in self.positions.iteritems(): cur = pos.to_dict() if ndicted: diff --git a/zipline/finance/risk.py b/zipline/finance/risk.py index f4bd736f..a63cf872 100644 --- a/zipline/finance/risk.py +++ b/zipline/finance/risk.py @@ -27,7 +27,7 @@ Risk Report | alpha | The _algorithm_ alpha to the benchmark. | +-----------------+----------------------------------------------------+ | excess_return | The excess return of the algorithm over the | - | | benchmark. | + | | treasuries. | +-----------------+----------------------------------------------------+ | max_drawdown | The largest relative peak to relative trough move | | | for the portfolio returns between self.start_date | @@ -187,6 +187,8 @@ class RiskMetrics(): return period_returns, returns def calculate_volatility(self, daily_returns): + # TODO: we should be using an annualized number for the + # square root, not the days in the period. return np.std(daily_returns, ddof=1) * math.sqrt(self.trading_days) def calculate_sharpe(self):