diff --git a/tests/risk/test_risk.py b/tests/risk/test_risk.py index 9d0f3402..0526ac11 100644 --- a/tests/risk/test_risk.py +++ b/tests/risk/test_risk.py @@ -92,9 +92,9 @@ class TestRisk(unittest.TestCase): returns = factory.create_returns_from_list( [1.0, -0.5, 0.8, .17, 1.0, -0.1, -0.45], self.sim_params) #200, 100, 180, 210.6, 421.2, 379.8, 208.494 - metrics = risk.RiskMetricsBatch(returns[0].date, - returns[-1].date, - returns) + metrics = risk.RiskMetricsPeriod(returns[0].date, + returns[-1].date, + returns) self.assertEqual(metrics.max_drawdown, 0.505) def test_benchmark_returns_06(self): diff --git a/tests/risk/test_risk_compare_batch_iterative.py b/tests/risk/test_risk_compare_batch_iterative.py deleted file mode 100644 index 1b6165be..00000000 --- a/tests/risk/test_risk_compare_batch_iterative.py +++ /dev/null @@ -1,164 +0,0 @@ -# -# Copyright 2013 Quantopian, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import numbers -import unittest -import datetime -import pytz - -import numpy as np -import pandas as pd - -import zipline.finance.risk as risk -import zipline.finance.trading as trading -from zipline.finance.trading import SimulationParameters -from zipline.protocol import DailyReturn - -from test_risk import RETURNS - - -class RiskCompareIterativeToBatch(unittest.TestCase): - """ - Assert that RiskMetricsIterative and RiskMetricsBatch - behave in the same way. - """ - - def setUp(self): - self.start_date = datetime.datetime( - year=2006, - month=1, - day=1, - hour=0, - minute=0, - tzinfo=pytz.utc) - self.end_date = datetime.datetime( - year=2006, month=12, day=31, tzinfo=pytz.utc) - - def test_risk_metrics_returns(self): - trading.environment = trading.TradingEnvironment() - # Advance start date to first date in the trading calendar - if trading.environment.is_trading_day(self.start_date): - start_date = self.start_date - else: - start_date = trading.environment.next_trading_day(self.start_date) - - self.all_benchmark_returns = pd.Series({ - x.date: x.returns - for x in trading.environment.benchmark_returns - if x.date >= self.start_date - }) - - start_index = trading.environment.trading_days.searchsorted(start_date) - end_date = trading.environment.trading_days[ - start_index + len(RETURNS)] - - sim_params = SimulationParameters(start_date, end_date) - - risk_metrics_refactor = risk.RiskMetricsIterative(sim_params) - todays_date = start_date - - cur_returns = [] - for i, ret in enumerate(RETURNS): - - todays_return_obj = DailyReturn( - todays_date, - ret - ) - cur_returns.append(todays_return_obj) - - try: - risk_metrics_original = risk.RiskMetricsBatch( - start_date=start_date, - end_date=todays_date, - returns=cur_returns - ) - except Exception as e: - #assert that when original raises exception, same - #exception is raised by risk_metrics_refactor - np.testing.assert_raises( - type(e), - risk_metrics_refactor.update, - todays_date, - self.all_benchmark_returns[todays_return_obj.date] - ) - continue - - risk_metrics_refactor.update( - todays_date, - ret, - self.all_benchmark_returns[todays_return_obj.date]) - - # Move forward day counter to next trading day - todays_date = trading.environment.next_trading_day(todays_date) - - self.assertEqual( - risk_metrics_original.start_date, - risk_metrics_refactor.start_date) - self.assertEqual( - risk_metrics_original.end_date, - risk_metrics_refactor.algorithm_returns.index[-1]) - self.assertEqual( - risk_metrics_original.treasury_period_return, - risk_metrics_refactor.treasury_period_return) - np.testing.assert_allclose( - risk_metrics_original.benchmark_returns, - risk_metrics_refactor.benchmark_returns, - rtol=0.001 - ) - np.testing.assert_allclose( - risk_metrics_original.algorithm_returns, - risk_metrics_refactor.algorithm_returns, - rtol=0.001 - ) - risk_original_dict = risk_metrics_original.to_dict() - risk_refactor_dict = risk_metrics_refactor.to_dict() - self.assertEqual(set(risk_original_dict.keys()), - set(risk_refactor_dict.keys())) - - err_msg_format = """\ -"In update step {iter}: {measure} should be {truth} but is {returned}!""" - - for measure in risk_original_dict.iterkeys(): - if measure == 'max_drawdown': - np.testing.assert_almost_equal( - risk_refactor_dict[measure], - risk_original_dict[measure], - err_msg=err_msg_format.format( - iter=i, - measure=measure, - truth=risk_original_dict[measure], - returned=risk_refactor_dict[measure])) - else: - if isinstance(risk_original_dict[measure], numbers.Real): - np.testing.assert_allclose( - risk_original_dict[measure], - risk_refactor_dict[measure], - rtol=0.001, - err_msg=err_msg_format.format( - iter=i, - measure=measure, - truth=risk_original_dict[measure], - returned=risk_refactor_dict[measure]) - ) - else: - np.testing.assert_equal( - risk_original_dict[measure], - risk_refactor_dict[measure], - err_msg=err_msg_format.format( - iter=i, - measure=measure, - truth=risk_original_dict[measure], - returned=risk_refactor_dict[measure]) - ) diff --git a/tests/test_minute_risk.py b/tests/test_minute_risk.py index 9fa02a43..df568a63 100644 --- a/tests/test_minute_risk.py +++ b/tests/test_minute_risk.py @@ -43,7 +43,7 @@ class TestMinuteRisk(unittest.TestCase): def test_minute_risk(self): - risk_metrics = risk.RiskMetricsIterative(self.sim_params) + risk_metrics = risk.RiskMetricsCumulative(self.sim_params) first_dt = self.sim_params.first_open second_dt = self.sim_params.first_open + datetime.timedelta(minutes=1) diff --git a/zipline/finance/performance.py b/zipline/finance/performance.py index 1026d1d1..669ef091 100644 --- a/zipline/finance/performance.py +++ b/zipline/finance/performance.py @@ -171,17 +171,17 @@ class PerformanceTracker(object): index=trading.environment.trading_days) self.intraday_risk_metrics = None self.cumulative_risk_metrics = \ - risk.RiskMetricsIterative(self.sim_params) + risk.RiskMetricsCumulative(self.sim_params) elif self.emission_rate == 'minute': self.all_benchmark_returns = pd.Series(index=pd.date_range( self.sim_params.first_open, self.sim_params.last_close, freq='Min')) self.intraday_risk_metrics = \ - risk.RiskMetricsIterative(self.sim_params) + risk.RiskMetricsCumulative(self.sim_params) self.cumulative_risk_metrics = \ - risk.RiskMetricsIterative(self.sim_params) + risk.RiskMetricsCumulative(self.sim_params) self.cumulative_risk_metrics.initialize_daily_indices() self.minute_performance = PerformancePeriod( @@ -379,7 +379,7 @@ class PerformanceTracker(object): def handle_intraday_close(self): self.intraday_risk_metrics = \ - risk.RiskMetricsIterative(self.sim_params) + risk.RiskMetricsCumulative(self.sim_params) # increment the day counter before we move markers forward. self.day_count += 1.0 # move the market day markers forward diff --git a/zipline/finance/risk/risk.py b/zipline/finance/risk/risk.py index 0dfec3e1..47a118e2 100644 --- a/zipline/finance/risk/risk.py +++ b/zipline/finance/risk/risk.py @@ -292,7 +292,7 @@ that date doesn't exceed treasury history range." raise Exception(message) -class RiskMetricsBase(object): +class RiskMetricsPeriod(object): def __init__(self, start_date, end_date, returns, benchmark_returns=None): @@ -536,12 +536,10 @@ class RiskMetricsBase(object): return 1.0 - math.exp(max_drawdown) -class RiskMetricsIterative(RiskMetricsBase): - """Iterative version of RiskMetrics. - Should behave exaclty like RiskMetricsBatch. - +class RiskMetricsCumulative(object): + """ :Usage: - Instantiate RiskMetricsIterative once. + Instantiate RiskMetricsCumulative once. Call update() method on each dt to update the metrics. """ @@ -814,9 +812,39 @@ algorithm_returns ({algo_count}) in range {start} : {end} on {dt}" self.benchmark_period_returns[-1], self.beta[-1]) + def calculate_volatility(self, daily_returns): + return np.std(daily_returns, ddof=1) * math.sqrt(self.num_trading_days) -class RiskMetricsBatch(RiskMetricsBase): - pass + def calculate_beta(self): + """ + + .. math:: + + \\beta_a = \\frac{\mathrm{Cov}(r_a,r_p)}{\mathrm{Var}(r_p)} + + http://en.wikipedia.org/wiki/Beta_(finance) + """ + #it doesn't make much sense to calculate beta for less than two days, + #so return none. + if len(self.algorithm_returns) < 2: + return 0.0, 0.0, 0.0, 0.0, [] + + returns_matrix = np.vstack([self.algorithm_returns, + self.benchmark_returns]) + C = np.cov(returns_matrix, ddof=1) + eigen_values = la.eigvals(C) + condition_number = max(eigen_values) / min(eigen_values) + algorithm_covariance = C[0][1] + benchmark_variance = C[1][1] + beta = algorithm_covariance / benchmark_variance + + return ( + beta, + algorithm_covariance, + benchmark_variance, + condition_number, + eigen_values + ) class RiskReport(object): @@ -889,7 +917,7 @@ class RiskReport(object): cur_end = cur_start + relativedelta(months=months_per) - one_day if(cur_end > the_end): break - cur_period_metrics = RiskMetricsBatch( + cur_period_metrics = RiskMetricsPeriod( start_date=cur_start, end_date=cur_end, returns=self.algorithm_returns,