Skip to content

Commit 7d39c4a

Browse files
author
Scott Sanderson
committed
ENH: Allow 2D input in cum_returns_final.
1 parent 6408f85 commit 7d39c4a

File tree

2 files changed

+29
-14
lines changed

2 files changed

+29
-14
lines changed

empyrical/stats.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -250,32 +250,34 @@ def cum_returns_final(returns, starting_value=0):
250250
251251
Parameters
252252
----------
253-
returns : pd.Series or np.ndarray
254-
Returns of the strategy as a percentage, noncumulative.
255-
- Time series with decimal returns.
256-
- Example:
257-
2015-07-16 -0.012143
258-
2015-07-17 0.045350
259-
2015-07-20 0.030957
260-
2015-07-21 0.004902.
253+
returns : pd.DataFrame, pd.Series, or np.ndarray
254+
Noncumulative simple returns of one or more timeseries.
261255
starting_value : float, optional
262256
The starting returns.
263257
264258
Returns
265259
-------
266-
total_returns : float
267-
"""
260+
total_returns : pd.Series, np.ndarray, or float
261+
If input is 1-dimensional (a Series or 1D numpy array), the result is a
262+
scalar.
268263
264+
If input is 2-dimensional (a DataFrame or 2D numpy array), the result
265+
is a 1D array containing cumulative returns for each column of input.
266+
"""
269267
if len(returns) == 0:
270268
return np.nan
271269

272-
returns = np.nanprod(returns + 1)
270+
if isinstance(returns, pd.DataFrame):
271+
result = (returns + 1).prod()
272+
else:
273+
result = np.nanprod(returns + 1, axis=0)
274+
273275
if starting_value == 0:
274-
returns -= 1
276+
result -= 1
275277
else:
276-
returns *= starting_value
278+
result *= starting_value
277279

278-
return returns
280+
return result
279281

280282

281283
def aggregate_returns(returns, convert_to):

empyrical/tests/test_stats.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1495,6 +1495,19 @@ def test_cum_returns_df(self, returns, starting_value, expected):
14951495

14961496
self.assert_indexes_match(cum_returns, returns)
14971497

1498+
@parameterized.expand([
1499+
(df_input, 0, df_0_expected.iloc[-1]),
1500+
(df_input, 100, df_100_expected.iloc[-1]),
1501+
])
1502+
def test_cum_returns_final_df(self, returns, starting_value, expected):
1503+
return_types = (pd.Series, np.ndarray)
1504+
result = self.empyrical(return_types=return_types).cum_returns_final(
1505+
returns,
1506+
starting_value=starting_value,
1507+
)
1508+
assert_almost_equal(np.array(result), expected, 5)
1509+
self.assert_indexes_match(result, expected)
1510+
14981511
@property
14991512
def empyrical(self):
15001513
"""

0 commit comments

Comments
 (0)