diff --git a/docs/source/whatsnew/0.8.4.txt b/docs/source/whatsnew/0.8.4.txt index e399bc7b..692741bb 100644 --- a/docs/source/whatsnew/0.8.4.txt +++ b/docs/source/whatsnew/0.8.4.txt @@ -44,6 +44,9 @@ Bug Fixes the ``len`` of a :class:`~zipline.protocol.SIDData` object. This would cause us to think that the object was not empty even when it was (:issue:`826`). +* Fixes an error raised in calculating beta when benchmark data were sparse. + Instead `numpy.nan` is returned (:issue:`859`). + Performance ~~~~~~~~~~~ diff --git a/tests/risk/test_risk_period.py b/tests/risk/test_risk_period.py index c66212b4..37fcdce4 100644 --- a/tests/risk/test_risk_period.py +++ b/tests/risk/test_risk_period.py @@ -18,6 +18,10 @@ import datetime import calendar import numpy as np import pytz + +from itertools import chain +from six import itervalues + import zipline.finance.risk as risk from zipline.utils import factory @@ -622,3 +626,17 @@ class TestRisk(unittest.TestCase): ) self.assert_month(start_date.month, col[-1].end_date.month) self.assert_last_day(col[-1].end_date) + + def test_sparse_benchmark(self): + benchmark_returns = self.benchmark_returns_06.copy() + # Set every other day to nan. + benchmark_returns.iloc[::2] = np.nan + + report = risk.RiskReport( + self.algo_returns_06, + self.sim_params, + benchmark_returns=benchmark_returns, + env=self.env, + ) + for risk_period in chain.from_iterable(itervalues(report.to_dict())): + self.assertIsNone(risk_period['beta']) diff --git a/zipline/finance/risk/period.py b/zipline/finance/risk/period.py index 31706d2f..63c3c8cc 100644 --- a/zipline/finance/risk/period.py +++ b/zipline/finance/risk/period.py @@ -252,13 +252,19 @@ class RiskMetricsPeriod(object): http://en.wikipedia.org/wiki/Beta_(finance) """ # it doesn't make much sense to calculate beta for less than two days, - # so return none. + # so return nan. if len(self.algorithm_returns) < 2: - return 0.0, 0.0, 0.0, 0.0, [] + return np.nan, np.nan, np.nan, np.nan, [] returns_matrix = np.vstack([self.algorithm_returns, self.benchmark_returns]) C = np.cov(returns_matrix, ddof=1) + + # If there are missing benchmark values, then we can't calculate the + # beta. + if not np.isfinite(C).all(): + return np.nan, np.nan, np.nan, np.nan, [] + eigen_values = la.eigvals(C) condition_number = max(eigen_values) / min(eigen_values) algorithm_covariance = C[0][1]