ENH: Track max leverage as risk

This commit is contained in:
Brian Fink
2015-03-17 16:32:19 -04:00
parent 7af9b30a99
commit 2f895bddcd
5 changed files with 64 additions and 13 deletions
+9 -5
View File
@@ -397,14 +397,16 @@ class PerformanceTracker(object):
# returns for the bench and the algo
self.intraday_risk_metrics.update(dt,
minute_returns,
self.all_benchmark_returns[dt])
self.all_benchmark_returns[dt],
self.get_account(True))
bench_since_open = \
self.intraday_risk_metrics.benchmark_cumulative_returns[dt]
self.cumulative_risk_metrics.update(todays_date,
self.todays_performance.returns,
bench_since_open)
bench_since_open,
self.get_account(True))
# if this is the close, save the returns objects for cumulative risk
# calculations and update dividends for the next day.
@@ -416,7 +418,6 @@ class PerformanceTracker(object):
Function called at market close only when emitting at minutely
frequency.
"""
# update_performance should have been called in handle_minute_close
# so it is not repeated here.
self.intraday_risk_metrics = \
@@ -438,7 +439,8 @@ class PerformanceTracker(object):
self.cumulative_risk_metrics.update(
completed_date,
self.todays_performance.returns,
self.all_benchmark_returns[completed_date])
self.all_benchmark_returns[completed_date],
self.get_account(True))
# increment the day counter before we move markers forward.
self.day_count += 1.0
@@ -481,10 +483,12 @@ class PerformanceTracker(object):
bms = self.cumulative_risk_metrics.benchmark_returns
ars = self.cumulative_risk_metrics.algorithm_returns
acl = self.cumulative_risk_metrics.algorithm_cumulative_leverages
self.risk_report = risk.RiskReport(
ars,
self.sim_params,
benchmark_returns=bms)
benchmark_returns=bms,
algorithm_leverages=acl)
risk_dict = self.risk_report.to_dict()
return risk_dict
+30 -2
View File
@@ -93,7 +93,8 @@ class RiskMetricsCumulative(object):
def __init__(self, sim_params,
returns_frequency=None,
create_first_day_stats=False):
create_first_day_stats=False,
account=None):
"""
- @returns_frequency allows for configuration of the whether
the benchmark and algorithm returns are in units of minutes or days,
@@ -143,6 +144,7 @@ class RiskMetricsCumulative(object):
self.algorithm_returns_cont = pd.Series(index=cont_index)
self.benchmark_returns_cont = pd.Series(index=cont_index)
self.algorithm_cumulative_leverages_cont = pd.Series(index=cont_index)
self.mean_returns_cont = pd.Series(index=cont_index)
self.annualized_mean_returns_cont = pd.Series(index=cont_index)
self.mean_benchmark_returns_cont = pd.Series(index=cont_index)
@@ -160,6 +162,7 @@ class RiskMetricsCumulative(object):
self.algorithm_cumulative_returns = pd.Series(index=cont_index)
self.benchmark_cumulative_returns = pd.Series(index=cont_index)
self.algorithm_cumulative_leverages = pd.Series(index=cont_index)
self.excess_returns = pd.Series(index=cont_index)
self.latest_dt = cont_index[0]
@@ -171,6 +174,8 @@ class RiskMetricsCumulative(object):
self.drawdowns = pd.Series(index=cont_index)
self.max_drawdowns = pd.Series(index=cont_index)
self.max_drawdown = 0
self.max_leverages = pd.Series(index=cont_index)
self.max_leverage = 0
self.current_max = -np.inf
self.daily_treasury = pd.Series(index=self.trading_days)
self.treasury_period_return = np.nan
@@ -195,7 +200,7 @@ class RiskMetricsCumulative(object):
def get_daily_index(self):
return self.trading_days
def update(self, dt, algorithm_returns, benchmark_returns):
def update(self, dt, algorithm_returns, benchmark_returns, account):
# Keep track of latest dt for use in to_dict and other methods
# that report current state.
self.latest_dt = dt
@@ -261,6 +266,15 @@ class RiskMetricsCumulative(object):
self.annualized_mean_benchmark_returns = \
self.annualized_mean_benchmark_returns_cont[:dt]
self.algorithm_cumulative_leverages_cont[dt] = account['leverage']
self.algorithm_cumulative_leverages = self.algorithm_cumulative_leverages_cont[:dt]
if self.create_first_day_stats:
if len(self.algorithm_cumulative_leverages) == 1:
self.algorithm_cumulative_leverages = pd.Series(
{self.day_before_start: 0.0}).append(
self.algorithm_cumulative_leverages)
if not self.algorithm_returns.index.equals(
self.benchmark_returns.index
):
@@ -305,6 +319,8 @@ algorithm_returns ({algo_count}) in range {start} : {end} on {dt}"
self.metrics.information[dt] = self.calculate_information()
self.max_drawdown = self.calculate_max_drawdown()
self.max_drawdowns[dt] = self.max_drawdown
self.max_leverage = self.calculate_max_leverage()
self.max_leverages[dt] = self.max_leverage
def to_dict(self):
"""
@@ -331,6 +347,7 @@ algorithm_returns ({algo_count}) in range {start} : {end} on {dt}"
'information': self.metrics.information[dt],
'excess_return': self.excess_returns[dt],
'max_drawdown': self.max_drawdown,
'max_leverage': self.max_leverage,
'period_label': period_label
}
@@ -383,6 +400,17 @@ algorithm_returns ({algo_count}) in range {start} : {end} on {dt}"
else:
return self.max_drawdown
def calculate_max_leverage(self):
# The leverage is defined as: the gross_exposure/net_liquidation
# gross_exposure = long_exposure + abs(short_exposure)
# net_liquidation = ending_cash + long_exposure + short_exposure
cur_leverage = self.algorithm_cumulative_leverages[self.latest_dt]
if cur_leverage is None or self.max_leverage > cur_leverage:
return self.max_leverage
else:
return cur_leverage
def calculate_sharpe(self):
"""
http://en.wikipedia.org/wiki/Sharpe_ratio
+12 -3
View File
@@ -48,7 +48,7 @@ choose_treasury = functools.partial(risk.choose_treasury,
class RiskMetricsPeriod(object):
def __init__(self, start_date, end_date, returns,
benchmark_returns=None):
benchmark_returns=None, algorithm_leverages=None):
treasury_curves = trading.environment.treasury_curves
if treasury_curves.index[-1] >= start_date:
@@ -71,6 +71,8 @@ class RiskMetricsPeriod(object):
self.algorithm_returns = self.mask_returns_to_period(returns)
self.benchmark_returns = self.mask_returns_to_period(benchmark_returns)
self.algorithm_leverages = algorithm_leverages
self.calculate_metrics()
def calculate_metrics(self):
@@ -131,6 +133,7 @@ class RiskMetricsPeriod(object):
self.excess_return = self.algorithm_period_returns - \
self.treasury_period_return
self.max_drawdown = self.calculate_max_drawdown()
self.max_leverage = self.calculate_max_leverage()
def to_dict(self):
"""
@@ -152,6 +155,7 @@ class RiskMetricsPeriod(object):
'alpha': self.alpha,
'excess_return': self.excess_return,
'max_drawdown': self.max_drawdown,
'max_leverage': self.max_leverage,
'period_label': period_label
}
@@ -175,6 +179,7 @@ class RiskMetricsPeriod(object):
"beta",
"alpha",
"max_drawdown",
"max_leverage",
"algorithm_returns",
"benchmark_returns",
"condition_number",
@@ -284,12 +289,13 @@ class RiskMetricsPeriod(object):
for r in self.algorithm_returns:
try:
cur_return += math.log(1.0 + r)
# this is a guard for a single day returning -100%
# this is a guard for a single day returning -100%, if returns are
# greater than -1.0 it will throw an error because you cannot take
# the log of a negative number
except ValueError:
log.debug("{cur} return, zeroing the returns".format(
cur=cur_return))
cur_return = 0.0
# BUG? Shouldn't this be set to log(1.0 + 0) ?
compounded_returns.append(cur_return)
cur_max = None
@@ -307,6 +313,9 @@ class RiskMetricsPeriod(object):
return 1.0 - math.exp(max_drawdown)
def calculate_max_leverage(self):
return max(self.algorithm_leverages.values)
def __getstate__(self):
state_dict = \
{k: v for k, v in iteritems(self.__dict__) if
+10 -3
View File
@@ -51,7 +51,9 @@ Risk Report
| | for the portfolio returns between self.start_date |
| | and self.end_date. |
+-----------------+----------------------------------------------------+
| max_leverage | The largest gross leverage between self.start_date |
| | and self.end_date |
+-----------------+----------------------------------------------------+
"""
@@ -70,15 +72,19 @@ log = logbook.Logger('Risk Report')
class RiskReport(object):
def __init__(self, algorithm_returns, sim_params, benchmark_returns=None):
def __init__(self, algorithm_returns, sim_params, benchmark_returns=None, algorithm_leverages=None):
"""
algorithm_returns needs to be a list of daily_return objects
sorted in date ascending order
account needs to be a list of account objects sorted in date
ascending order
"""
self.algorithm_returns = algorithm_returns
self.sim_params = sim_params
self.benchmark_returns = benchmark_returns
self.algorithm_leverages = algorithm_leverages
if len(self.algorithm_returns) == 0:
start_date = self.sim_params.period_start
@@ -136,7 +142,8 @@ class RiskReport(object):
start_date=cur_start,
end_date=cur_end,
returns=self.algorithm_returns,
benchmark_returns=self.benchmark_returns
benchmark_returns=self.benchmark_returns,
algorithm_leverages = self.algorithm_leverages,
)
ends.append(cur_period_metrics)
+3
View File
@@ -51,6 +51,9 @@ Risk Report
| | for the portfolio returns between self.start_date |
| | and self.end_date. |
+-----------------+----------------------------------------------------+
| max_leverage | The largest gross leverage between self.start_date |
| | and self.end_date |
+-----------------+----------------------------------------------------+
"""