Skip to content

Commit ea9a9b7

Browse files
Joe Jevnikllllllllll
authored andcommitted
TST: add test for beta where rhs is a dataframe
1 parent 0ba4d23 commit ea9a9b7

File tree

1 file changed

+52
-28
lines changed

1 file changed

+52
-28
lines changed

empyrical/tests/test_stats.py

Lines changed: 52 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
from copy import copy
44
from operator import attrgetter
5-
import random
65
from unittest import TestCase, SkipTest
76

87
from parameterized import parameterized
@@ -25,6 +24,9 @@
2524
DECIMAL_PLACES = 8
2625

2726

27+
rand = np.random.RandomState(1337)
28+
29+
2830
class BaseTestCase(TestCase):
2931
def assert_indexes_match(self, result, expected):
3032
"""
@@ -42,7 +44,6 @@ def assert_indexes_match(self, result, expected):
4244

4345

4446
class TestStats(BaseTestCase):
45-
4647
# Simple benchmark, no drawdown
4748
simple_benchmark = pd.Series(
4849
np.array([0., 1., 0., 1., 0., 1., 0., 1., 0.]) / 100,
@@ -95,12 +96,12 @@ class TestStats(BaseTestCase):
9596

9697
# Random noise
9798
noise = pd.Series(
98-
[random.gauss(0, 0.001) for i in range(1000)],
99+
rand.normal(0, 0.001, 1000),
99100
index=pd.date_range('2000-1-30', periods=1000, freq='D', tz='UTC')
100101
)
101102

102103
noise_uniform = pd.Series(
103-
[random.uniform(-0.01, 0.01) for i in range(1000)],
104+
rand.uniform(-0.01, 0.01, 1000),
104105
index=pd.date_range('2000-1-30', periods=1000, freq='D', tz='UTC')
105106
)
106107

@@ -131,11 +132,11 @@ class TestStats(BaseTestCase):
131132
)
132133

133134
# Sparse noise, same as noise but with np.nan sprinkled in
134-
replace_nan = random.sample(noise.index.tolist(), random.randint(1, 10))
135+
replace_nan = rand.choice(noise.index.tolist(), rand.randint(1, 10))
135136
sparse_noise = noise.replace(replace_nan, np.nan)
136137

137138
# Sparse flat line at 0.01
138-
replace_nan = random.sample(noise.index.tolist(), random.randint(1, 10))
139+
replace_nan = rand.choice(noise.index.tolist(), rand.randint(1, 10))
139140
sparse_flat_line_1_tz = flat_line_1_tz.replace(replace_nan, np.nan)
140141

141142
one = [-0.00171614, 0.01322056, 0.03063862, -0.01422057, -0.00489779,
@@ -432,12 +433,12 @@ def test_sharpe_translation_1(self, returns, required_return, translation):
432433
def test_sharpe_noise(self, small, large):
433434
index = pd.date_range('2000-1-30', periods=1000, freq='D')
434435
smaller_normal = pd.Series(
435-
[random.gauss(.01, small) for i in range(1000)],
436-
index=index
436+
rand.normal(.01, small, 1000),
437+
index=index,
437438
)
438439
larger_normal = pd.Series(
439-
[random.gauss(.01, large) for i in range(1000)],
440-
index=index
440+
rand.normal(.01, large, 1000),
441+
index=index,
441442
)
442443
assert self.empyrical.sharpe_ratio(smaller_normal, 0.001) > \
443444
self.empyrical.sharpe_ratio(larger_normal, 0.001)
@@ -517,12 +518,20 @@ def test_downside_risk_trans(self, returns, required_return):
517518
])
518519
def test_downside_risk_std(self, smaller_std, larger_std):
519520
less_noise = pd.Series(
520-
[random.gauss(0, smaller_std) for i in range(1000)],
521-
index=pd.date_range('2000-1-30', periods=1000, freq='D')
521+
(
522+
rand.normal(0, smaller_std, 1000)
523+
if smaller_std != 0
524+
else np.full(1000, 0)
525+
),
526+
index=pd.date_range('2000-1-30', periods=1000, freq='D'),
522527
)
523528
more_noise = pd.Series(
524-
[random.gauss(0, larger_std) for i in range(1000)],
525-
index=pd.date_range('2000-1-30', periods=1000, freq='D')
529+
(
530+
rand.normal(0, larger_std, 1000)
531+
if larger_std != 0
532+
else np.full(1000, 0)
533+
),
534+
index=pd.date_range('2000-1-30', periods=1000, freq='D'),
526535
)
527536
assert self.empyrical.downside_risk(less_noise) < \
528537
self.empyrical.downside_risk(more_noise)
@@ -580,7 +589,7 @@ def test_sortino_add_noise(self, returns, required_return):
580589
sr_1 = self.empyrical.sortino_ratio(returns, required_return)
581590
upside_values = returns[returns > required_return].index.tolist()
582591
# Add large losses at random upside locations
583-
loss_loc = random.sample(upside_values, 2)
592+
loss_loc = rand.choice(upside_values, 2)
584593
returns[loss_loc[0]] = -0.01
585594
sr_2 = self.empyrical.sortino_ratio(returns, required_return)
586595
returns[loss_loc[1]] = -0.01
@@ -600,7 +609,7 @@ def test_sortino_sub_noise(self, returns, required_return):
600609
sr_1 = self.empyrical.sortino_ratio(returns, required_return)
601610
downside_values = returns[returns < required_return].index.tolist()
602611
# Replace some values below the required return to the required return
603-
loss_loc = random.sample(downside_values, 2)
612+
loss_loc = rand.choice(downside_values, 2)
604613
returns[loss_loc[0]] = required_return
605614
sr_2 = self.empyrical.sortino_ratio(returns, required_return)
606615
returns[loss_loc[1]] = required_return
@@ -770,7 +779,7 @@ def test_alpha_beta_translation(self, mean_returns, translation):
770779
means = [mean_returns, .001]
771780
covs = [[std_returns**2, std_returns*std_bench*correlation],
772781
[std_returns*std_bench*correlation, std_bench**2]]
773-
(ret, bench) = np.random.multivariate_normal(means, covs, 1000).T
782+
(ret, bench) = rand.multivariate_normal(means, covs, 1000).T
774783
returns = pd.Series(
775784
ret,
776785
index=pd.date_range('2000-1-30', periods=1000, freq='D'))
@@ -821,15 +830,15 @@ def test_alpha_beta_correlation(self, corr_less, corr_more):
821830
means_less = [mean_returns, mean_bench]
822831
covs_less = [[std_returns**2, std_returns*std_bench*corr_less],
823832
[std_returns*std_bench*corr_less, std_bench**2]]
824-
(ret_less, bench_less) = np.random.multivariate_normal(
833+
(ret_less, bench_less) = rand.multivariate_normal(
825834
means_less, covs_less, 1000).T
826835
returns_less = pd.Series(ret_less, index=index)
827836
benchmark_less = pd.Series(bench_less, index=index)
828837
# Genereate more highly correlated returns
829838
means_more = [mean_returns, mean_bench]
830839
covs_more = [[std_returns**2, std_returns*std_bench*corr_more],
831840
[std_returns*std_bench*corr_more, std_bench**2]]
832-
(ret_more, bench_more) = np.random.multivariate_normal(
841+
(ret_more, bench_more) = rand.multivariate_normal(
833842
means_more, covs_more, 1000).T
834843
returns_more = pd.Series(ret_more, index=index)
835844
benchmark_more = pd.Series(bench_more, index=index)
@@ -865,17 +874,32 @@ def test_alpha_beta_with_nan_inputs(self, returns, benchmark):
865874
(2 * noise, noise, 2.0),
866875
(noise, inv_noise, -1.0),
867876
(2 * noise, inv_noise, -2.0),
868-
(sparse_noise*flat_line_1_tz, sparse_flat_line_1_tz, np.nan),
877+
(sparse_noise * flat_line_1_tz, sparse_flat_line_1_tz, np.nan),
878+
(
879+
simple_benchmark + rand.normal(0, 0.001, len(simple_benchmark)),
880+
pd.DataFrame({'returns': simple_benchmark}),
881+
1.0,
882+
2,
883+
),
869884
])
870-
def test_beta(self, returns, benchmark, expected):
885+
def test_beta(self,
886+
returns,
887+
benchmark,
888+
expected,
889+
decimal_places=DECIMAL_PLACES):
871890
observed = self.empyrical.beta(returns, benchmark)
872891
assert_almost_equal(
873892
observed,
874893
expected,
875-
DECIMAL_PLACES)
894+
decimal_places,
895+
)
876896

877897
if len(returns) == len(benchmark):
878898
# Compare to scipy linregress
899+
900+
if isinstance(benchmark, pd.DataFrame):
901+
benchmark = benchmark['returns']
902+
879903
returns_arr = returns.values
880904
benchmark_arr = benchmark.values
881905
mask = ~np.isnan(returns_arr) & ~np.isnan(benchmark_arr)
@@ -943,7 +967,7 @@ def test_stability_of_timeseries(self, returns, expected):
943967
(empty_returns, np.nan),
944968
(one_return, 1.0),
945969
(mixed_returns, 0.9473684210526313),
946-
(pd.Series(np.random.randn(100000)), 1.),
970+
(pd.Series(rand.randn(100000)), 1.),
947971
])
948972
def test_tail_ratio(self, returns, expected):
949973
assert_almost_equal(
@@ -1250,7 +1274,7 @@ def test_value_at_risk(self):
12501274
assert_almost_equal(value_at_risk(returns, cutoff=0.3), 81.5)
12511275

12521276
# Test a returns stream of 21 data points at different cutoffs.
1253-
returns = np.random.normal(0, 0.02, 21)
1277+
returns = rand.normal(0, 0.02, 21)
12541278
for cutoff in (0, 0.0499, 0.05, 0.20, 0.999, 1):
12551279
assert_almost_equal(
12561280
value_at_risk(returns, cutoff),
@@ -1262,7 +1286,7 @@ def test_conditional_value_at_risk(self):
12621286
conditional_value_at_risk = self.empyrical.conditional_value_at_risk
12631287

12641288
# A single-valued array will always just have a CVaR of its only value.
1265-
returns = np.random.normal(0, 0.02, 1)
1289+
returns = rand.normal(0, 0.02, 1)
12661290
expected_cvar = returns[0]
12671291
assert_almost_equal(
12681292
conditional_value_at_risk(returns, cutoff=0), expected_cvar,
@@ -1272,7 +1296,7 @@ def test_conditional_value_at_risk(self):
12721296
)
12731297

12741298
# Test a returns stream of 21 data points at different cutoffs.
1275-
returns = np.random.normal(0, 0.02, 21)
1299+
returns = rand.normal(0, 0.02, 21)
12761300

12771301
for cutoff in (0, 0.0499, 0.05, 0.20, 0.999, 1):
12781302
# Find the VaR based on our cutoff, then take the average of all
@@ -1360,11 +1384,11 @@ def setUp(self):
13601384
self.window = 12
13611385

13621386
self.returns = pd.Series(
1363-
np.random.randn(1, 120)[0]/100.,
1387+
rand.randn(1, 120)[0]/100.,
13641388
index=pd.date_range('2000-1-30', periods=120, freq='M'))
13651389

13661390
self.factor_returns = pd.Series(
1367-
np.random.randn(1, 120)[0]/100.,
1391+
rand.randn(1, 120)[0]/100.,
13681392
index=pd.date_range('2000-1-30', periods=120, freq='M'))
13691393

13701394
def test_roll_pandas(self):

0 commit comments

Comments
 (0)