Merge pull request #65 from quantopian/fawce_alpha1

portfolio object creation from PerformancePeriod
This commit is contained in:
fawce
2012-06-07 14:59:48 -07:00
2 changed files with 63 additions and 36 deletions
+60 -35
View File
@@ -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:
+3 -1
View File
@@ -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):