mirror of
https://github.com/wassname/catalyst.git
synced 2026-07-13 13:40:25 +08:00
PERF: Improve risk metrics update speed.
Remove the DataFrame of headline risk metrics, in favor of a numpy array for each metric, like the underlying vectors.
This commit is contained in:
@@ -62,52 +62,59 @@ class TestRisk(unittest.TestCase):
|
||||
def test_algorithm_volatility_06(self):
|
||||
algo_vol_answers = answer_key.RISK_CUMULATIVE.volatility
|
||||
for dt, value in algo_vol_answers.iteritems():
|
||||
dt_loc = self.cumulative_metrics_06.cont_index.get_loc(dt)
|
||||
np.testing.assert_almost_equal(
|
||||
self.cumulative_metrics_06.metrics.algorithm_volatility[dt],
|
||||
self.cumulative_metrics_06.algorithm_volatility[dt_loc],
|
||||
value,
|
||||
err_msg="Mismatch at %s" % (dt,))
|
||||
|
||||
def test_sharpe_06(self):
|
||||
for dt, value in answer_key.RISK_CUMULATIVE.sharpe.iteritems():
|
||||
dt_loc = self.cumulative_metrics_06.cont_index.get_loc(dt)
|
||||
np.testing.assert_almost_equal(
|
||||
self.cumulative_metrics_06.metrics.sharpe[dt],
|
||||
self.cumulative_metrics_06.sharpe[dt_loc],
|
||||
value,
|
||||
err_msg="Mismatch at %s" % (dt,))
|
||||
|
||||
def test_downside_risk_06(self):
|
||||
for dt, value in answer_key.RISK_CUMULATIVE.downside_risk.iteritems():
|
||||
dt_loc = self.cumulative_metrics_06.cont_index.get_loc(dt)
|
||||
np.testing.assert_almost_equal(
|
||||
value,
|
||||
self.cumulative_metrics_06.metrics.downside_risk[dt],
|
||||
self.cumulative_metrics_06.downside_risk[dt_loc],
|
||||
err_msg="Mismatch at %s" % (dt,))
|
||||
|
||||
def test_sortino_06(self):
|
||||
for dt, value in answer_key.RISK_CUMULATIVE.sortino.iteritems():
|
||||
dt_loc = self.cumulative_metrics_06.cont_index.get_loc(dt)
|
||||
np.testing.assert_almost_equal(
|
||||
self.cumulative_metrics_06.metrics.sortino[dt],
|
||||
self.cumulative_metrics_06.sortino[dt_loc],
|
||||
value,
|
||||
decimal=4,
|
||||
err_msg="Mismatch at %s" % (dt,))
|
||||
|
||||
def test_information_06(self):
|
||||
for dt, value in answer_key.RISK_CUMULATIVE.information.iteritems():
|
||||
dt_loc = self.cumulative_metrics_06.cont_index.get_loc(dt)
|
||||
np.testing.assert_almost_equal(
|
||||
value,
|
||||
self.cumulative_metrics_06.metrics.information[dt],
|
||||
self.cumulative_metrics_06.information[dt_loc],
|
||||
err_msg="Mismatch at %s" % (dt,))
|
||||
|
||||
def test_alpha_06(self):
|
||||
for dt, value in answer_key.RISK_CUMULATIVE.alpha.iteritems():
|
||||
dt_loc = self.cumulative_metrics_06.cont_index.get_loc(dt)
|
||||
np.testing.assert_almost_equal(
|
||||
self.cumulative_metrics_06.metrics.alpha[dt],
|
||||
self.cumulative_metrics_06.alpha[dt_loc],
|
||||
value,
|
||||
err_msg="Mismatch at %s" % (dt,))
|
||||
|
||||
def test_beta_06(self):
|
||||
for dt, value in answer_key.RISK_CUMULATIVE.beta.iteritems():
|
||||
dt_loc = self.cumulative_metrics_06.cont_index.get_loc(dt)
|
||||
np.testing.assert_almost_equal(
|
||||
value,
|
||||
self.cumulative_metrics_06.metrics.beta[dt],
|
||||
self.cumulative_metrics_06.beta[dt_loc],
|
||||
err_msg="Mismatch at %s" % (dt,))
|
||||
|
||||
def test_max_drawdown_06(self):
|
||||
|
||||
@@ -161,7 +161,7 @@ class TestEventsThroughRisk(unittest.TestCase):
|
||||
decimal=6)
|
||||
|
||||
np.testing.assert_almost_equal(
|
||||
crm.metrics.sharpe[current_dt],
|
||||
crm.sharpe[dt_loc],
|
||||
expected_sharpe[current_dt],
|
||||
decimal=6,
|
||||
err_msg="Mismatch at %s" % (current_dt,))
|
||||
@@ -294,6 +294,7 @@ class TestEventsThroughRisk(unittest.TestCase):
|
||||
gen = algo._create_generator(sim_params)
|
||||
|
||||
crm = algo.perf_tracker.cumulative_risk_metrics
|
||||
dt_loc = crm.cont_index.get_loc(algo.datetime)
|
||||
|
||||
first_msg = next(gen)
|
||||
|
||||
@@ -309,7 +310,7 @@ class TestEventsThroughRisk(unittest.TestCase):
|
||||
|
||||
self.assertEquals(
|
||||
0,
|
||||
crm.metrics.algorithm_volatility[algo.datetime.date()],
|
||||
crm.algorithm_volatility[dt_loc],
|
||||
"On the first day algorithm volatility does not exist.")
|
||||
|
||||
second_msg = next(gen)
|
||||
|
||||
@@ -155,9 +155,14 @@ class RiskMetricsCumulative(object):
|
||||
self.latest_dt_loc = 0
|
||||
self.latest_dt = cont_index[0]
|
||||
|
||||
self.metrics = pd.DataFrame(index=cont_index,
|
||||
columns=self.METRIC_NAMES,
|
||||
dtype=float)
|
||||
self.benchmark_volatility = empty_cont.copy()
|
||||
self.algorithm_volatility = empty_cont.copy()
|
||||
self.beta = empty_cont.copy()
|
||||
self.alpha = empty_cont.copy()
|
||||
self.sharpe = empty_cont.copy()
|
||||
self.downside_risk = empty_cont.copy()
|
||||
self.sortino = empty_cont.copy()
|
||||
self.information = empty_cont.copy()
|
||||
|
||||
self.drawdowns = empty_cont.copy()
|
||||
self.max_drawdowns = empty_cont.copy()
|
||||
@@ -257,10 +262,9 @@ algorithm_returns ({algo_count}) in range {start} : {end} on {dt}"
|
||||
raise Exception(message)
|
||||
|
||||
self.update_current_max()
|
||||
metrics = self.metrics
|
||||
metrics.benchmark_volatility.iloc[dt_loc] = \
|
||||
self.benchmark_volatility[dt_loc] = \
|
||||
self.calculate_volatility(self.benchmark_returns)
|
||||
metrics.algorithm_volatility.iloc[dt_loc] = \
|
||||
self.algorithm_volatility[dt_loc] = \
|
||||
self.calculate_volatility(self.algorithm_returns)
|
||||
|
||||
# caching the treasury rates for the minutely case is a
|
||||
@@ -279,13 +283,13 @@ algorithm_returns ({algo_count}) in range {start} : {end} on {dt}"
|
||||
self.excess_returns[dt_loc] = (
|
||||
self.algorithm_cumulative_returns[dt_loc] -
|
||||
self.treasury_period_return)
|
||||
metrics.beta.iloc[dt_loc] = self.calculate_beta()
|
||||
metrics.alpha.iloc[dt_loc] = self.calculate_alpha()
|
||||
metrics.sharpe.iloc[dt_loc] = self.calculate_sharpe()
|
||||
metrics.downside_risk.iloc[dt_loc] = \
|
||||
self.beta[dt_loc] = self.calculate_beta()
|
||||
self.alpha[dt_loc] = self.calculate_alpha()
|
||||
self.sharpe[dt_loc] = self.calculate_sharpe()
|
||||
self.downside_risk[dt_loc] = \
|
||||
self.calculate_downside_risk()
|
||||
metrics.sortino.iloc[dt_loc] = self.calculate_sortino()
|
||||
metrics.information.iloc[dt_loc] = self.calculate_information()
|
||||
self.sortino[dt_loc] = self.calculate_sortino()
|
||||
self.information[dt_loc] = self.calculate_information()
|
||||
self.max_drawdown = self.calculate_max_drawdown()
|
||||
self.max_drawdowns[dt_loc] = self.max_drawdown
|
||||
self.max_leverage = self.calculate_max_leverage()
|
||||
@@ -299,13 +303,12 @@ algorithm_returns ({algo_count}) in range {start} : {end} on {dt}"
|
||||
dt = self.latest_dt
|
||||
dt_loc = self.latest_dt_loc
|
||||
period_label = dt.strftime("%Y-%m")
|
||||
metrics = self.metrics
|
||||
rval = {
|
||||
'trading_days': self.num_trading_days,
|
||||
'benchmark_volatility':
|
||||
metrics.benchmark_volatility.iloc[dt_loc],
|
||||
self.benchmark_volatility[dt_loc],
|
||||
'algo_volatility':
|
||||
metrics.algorithm_volatility.iloc[dt_loc],
|
||||
self.algorithm_volatility[dt_loc],
|
||||
'treasury_period_return': self.treasury_period_return,
|
||||
# Though the two following keys say period return,
|
||||
# they would be more accurately called the cumulative return.
|
||||
@@ -315,11 +318,11 @@ algorithm_returns ({algo_count}) in range {start} : {end} on {dt}"
|
||||
self.algorithm_cumulative_returns[dt_loc],
|
||||
'benchmark_period_return':
|
||||
self.benchmark_cumulative_returns[dt_loc],
|
||||
'beta': metrics.beta.iloc[dt_loc],
|
||||
'alpha': metrics.alpha.iloc[dt_loc],
|
||||
'sharpe': metrics.sharpe.iloc[dt_loc],
|
||||
'sortino': metrics.sortino.iloc[dt_loc],
|
||||
'information': metrics.information.iloc[dt_loc],
|
||||
'beta': self.beta[dt_loc],
|
||||
'alpha': self.alpha[dt_loc],
|
||||
'sharpe': self.sharpe[dt_loc],
|
||||
'sortino': self.sortino[dt_loc],
|
||||
'information': self.information[dt_loc],
|
||||
'excess_return': self.excess_returns[dt_loc],
|
||||
'max_drawdown': self.max_drawdown,
|
||||
'max_leverage': self.max_leverage,
|
||||
@@ -332,7 +335,7 @@ algorithm_returns ({algo_count}) in range {start} : {end} on {dt}"
|
||||
def __repr__(self):
|
||||
statements = []
|
||||
for metric in self.METRIC_NAMES:
|
||||
value = getattr(self.metrics, metric)[-1]
|
||||
value = getattr(self, metric)[-1]
|
||||
if isinstance(value, list):
|
||||
if len(value) == 0:
|
||||
value = np.nan
|
||||
@@ -390,7 +393,7 @@ algorithm_returns ({algo_count}) in range {start} : {end} on {dt}"
|
||||
http://en.wikipedia.org/wiki/Sharpe_ratio
|
||||
"""
|
||||
return sharpe_ratio(
|
||||
self.metrics.algorithm_volatility[self.latest_dt_loc],
|
||||
self.algorithm_volatility[self.latest_dt_loc],
|
||||
self.annualized_mean_returns_cont[self.latest_dt_loc],
|
||||
self.daily_treasury[self.latest_dt.date()])
|
||||
|
||||
@@ -401,14 +404,14 @@ algorithm_returns ({algo_count}) in range {start} : {end} on {dt}"
|
||||
return sortino_ratio(
|
||||
self.annualized_mean_returns_cont[self.latest_dt_loc],
|
||||
self.daily_treasury[self.latest_dt.date()],
|
||||
self.metrics.downside_risk[self.latest_dt_loc])
|
||||
self.downside_risk[self.latest_dt_loc])
|
||||
|
||||
def calculate_information(self):
|
||||
"""
|
||||
http://en.wikipedia.org/wiki/Information_ratio
|
||||
"""
|
||||
return information_ratio(
|
||||
self.metrics.algorithm_volatility[self.latest_dt_loc],
|
||||
self.algorithm_volatility[self.latest_dt_loc],
|
||||
self.annualized_mean_returns_cont[self.latest_dt_loc],
|
||||
self.annualized_mean_benchmark_returns_cont[self.latest_dt_loc])
|
||||
|
||||
@@ -420,7 +423,7 @@ algorithm_returns ({algo_count}) in range {start} : {end} on {dt}"
|
||||
self.annualized_mean_returns_cont[self.latest_dt_loc],
|
||||
self.treasury_period_return,
|
||||
self.annualized_mean_benchmark_returns_cont[self.latest_dt_loc],
|
||||
self.metrics.beta.iloc[self.latest_dt_loc])
|
||||
self.beta[self.latest_dt_loc])
|
||||
|
||||
def calculate_volatility(self, daily_returns):
|
||||
if len(daily_returns) <= 1:
|
||||
|
||||
Reference in New Issue
Block a user