Skip to content

Commit c3b06cd

Browse files
twieckiluca-s
authored andcommitted
ENH: add ability to get daily multi-period forward returns rather than only cumulative
1 parent 957d5cc commit c3b06cd

File tree

2 files changed

+37
-4
lines changed

2 files changed

+37
-4
lines changed

alphalens/tests/test_utils.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,23 @@ def test_compute_forward_returns(self):
8181

8282
assert_frame_equal(fp, expected)
8383

84+
def test_compute_forward_returns_non_cum(self):
85+
dr = date_range(start='2015-1-1', end='2015-1-3')
86+
prices = DataFrame(index=dr, columns=['A', 'B'],
87+
data=[[1, 1], [1, 2], [2, 1]])
88+
factor = prices.stack()
89+
90+
fp = compute_forward_returns(factor, prices, periods=[1, 2],
91+
cumulative_returns=False)
92+
93+
ix = MultiIndex.from_product([dr, ['A', 'B']],
94+
names=['date', 'asset'])
95+
expected = DataFrame(index=ix, columns=['1D', '2D'])
96+
expected['1D'] = [0., 1., 1., -0.5, nan, nan]
97+
expected['2D'] = [1., -0.5, nan, nan, nan, nan]
98+
99+
assert_frame_equal(fp, expected)
100+
84101
@parameterized.expand([(factor_data, 4, None, False, False,
85102
[1, 2, 3, 4, 4, 3, 2, 1]),
86103
(factor_data, 2, None, False, False,

alphalens/utils.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,8 @@ def infer_trading_calendar(factor_idx, prices_idx):
215215
def compute_forward_returns(factor,
216216
prices,
217217
periods=(1, 5, 10),
218-
filter_zscore=None):
218+
filter_zscore=None,
219+
cumulative_returns=True):
219220
"""
220221
Finds the N period forward returns (as percent change) for each asset
221222
provided.
@@ -240,6 +241,10 @@ def compute_forward_returns(factor,
240241
Sets forward returns greater than X standard deviations
241242
from the the mean to nan. Set it to 'None' to avoid filtering.
242243
Caution: this outlier filtering incorporates lookahead bias.
244+
cumulative_returns : bool, optional
245+
If True, forward returns columns will contain cumulative returns.
246+
Setting this to False is useful if you want to analyze how predictive
247+
a factor is for a single forward day.
243248
244249
Returns
245250
-------
@@ -278,8 +283,13 @@ def compute_forward_returns(factor,
278283
column_list = []
279284

280285
for period in sorted(periods):
286+
if cumulative_returns:
287+
returns = prices.pct_change(period)
288+
else:
289+
returns = prices.pct_change()
290+
281291
forward_returns = \
282-
prices.pct_change(period).shift(-period).reindex(factor_dateindex)
292+
returns.shift(-period).reindex(factor_dateindex)
283293

284294
if filter_zscore is not None:
285295
mask = abs(
@@ -631,7 +641,8 @@ def get_clean_factor_and_forward_returns(factor,
631641
filter_zscore=20,
632642
groupby_labels=None,
633643
max_loss=0.35,
634-
zero_aware=False):
644+
zero_aware=False,
645+
cumulative_returns=True):
635646
"""
636647
Formats the factor data, pricing data, and group mappings into a DataFrame
637648
that contains aligned MultiIndex indices of timestamp and asset. The
@@ -741,6 +752,10 @@ def get_clean_factor_and_forward_returns(factor,
741752
If True, compute quantile buckets separately for positive and negative
742753
signal values. This is useful if your signal is centered and zero is
743754
the separation between long and short signals, respectively.
755+
cumulative_returns : bool, optional
756+
If True, forward returns columns will contain cumulative returns.
757+
Setting this to False is useful if you want to analyze how predictive
758+
a factor is for a single forward day.
744759
745760
Returns
746761
-------
@@ -774,7 +789,8 @@ def get_clean_factor_and_forward_returns(factor,
774789
"""
775790

776791
forward_returns = compute_forward_returns(factor, prices, periods,
777-
filter_zscore)
792+
filter_zscore,
793+
cumulative_returns)
778794

779795
factor_data = get_clean_factor(factor, forward_returns, groupby=groupby,
780796
groupby_labels=groupby_labels,

0 commit comments

Comments
 (0)