From f4cf30dd19e0ea33b678cab51e5f888cc5c63f2e Mon Sep 17 00:00:00 2001 From: Richard Frank Date: Wed, 18 Nov 2015 10:40:54 -0500 Subject: [PATCH 1/3] BUG: Return NaN beta when missing benchmarks instead of raising LinAlgError --- tests/risk/test_risk_period.py | 18 ++++++++++++++++++ zipline/finance/risk/period.py | 4 ++++ 2 files changed, 22 insertions(+) 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..4461de74 100644 --- a/zipline/finance/risk/period.py +++ b/zipline/finance/risk/period.py @@ -259,6 +259,10 @@ class RiskMetricsPeriod(object): returns_matrix = np.vstack([self.algorithm_returns, self.benchmark_returns]) C = np.cov(returns_matrix, ddof=1) + + 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] From 34a842c0520642a79270f395dd12ef2605a5e19f Mon Sep 17 00:00:00 2001 From: Richard Frank Date: Wed, 18 Nov 2015 13:11:55 -0500 Subject: [PATCH 2/3] MAINT: Also return nan when simulation is shorter than 2 days --- zipline/finance/risk/period.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/zipline/finance/risk/period.py b/zipline/finance/risk/period.py index 4461de74..63c3c8cc 100644 --- a/zipline/finance/risk/period.py +++ b/zipline/finance/risk/period.py @@ -252,14 +252,16 @@ 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, [] From 4237a085842da76b477e3369d61abbbd6ba187e9 Mon Sep 17 00:00:00 2001 From: Richard Frank Date: Thu, 19 Nov 2015 09:40:13 -0500 Subject: [PATCH 3/3] DOC: Updated whatsnew for missing benchmarks handling --- docs/source/whatsnew/0.8.4.txt | 3 +++ 1 file changed, 3 insertions(+) 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 ~~~~~~~~~~~