Skip to content

Commit

Permalink
BUG: compute_forward_returns fails detecting the correct period length
Browse files Browse the repository at this point in the history
Since Alphalens is not aware of the public holidays the computation of the
periods goes wrong in certain scenarios.

issue #292
  • Loading branch information
luca-s committed Apr 23, 2018
1 parent e444e8d commit 417621b
Showing 1 changed file with 27 additions and 15 deletions.
42 changes: 27 additions & 15 deletions alphalens/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,18 +165,30 @@ def infer_trading_calendar(factor_idx, prices_idx):
"""
full_idx = factor_idx.union(prices_idx)

# drop days of the week that are not used
days_to_keep = []
traded_weekdays = []
holidays = []

days_of_the_week = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
for day, day_str in enumerate(days_of_the_week):
if (full_idx.dayofweek == day).any():
days_to_keep.append(day_str)

days_to_keep = ' '.join(days_to_keep)
weekday_mask = (full_idx.dayofweek == day)

# drop days of the week that are not traded at all
if not weekday_mask.any():
continue
traded_weekdays.append(day_str)

# look for holidays
used_weekdays = full_idx[weekday_mask].normalize()
all_weekdays = pd.date_range(full_idx.min(), full_idx.max(),
freq=CustomBusinessDay(weekmask=day_str)
).normalize()
_holidays = all_weekdays.difference(used_weekdays)
_holidays = [timestamp.date() for timestamp in _holidays]
holidays.extend(_holidays)

# we currently don't infer holidays, but CustomBusinessDay class supports
# custom holidays. So holidays could be inferred too eventually
return CustomBusinessDay(weekmask=days_to_keep)
traded_weekdays = ' '.join(traded_weekdays)
return CustomBusinessDay(weekmask=traded_weekdays, holidays=holidays)


def compute_forward_returns(factor,
Expand Down Expand Up @@ -256,18 +268,18 @@ def compute_forward_returns(factor,
fwdret[mask] = np.nan

#
# Find the period length, which will be the column name
# Becase the calendar inferred from factor and prices doesn't take
# into consideration holidays yet, there could be some non-trading days
# in between the trades so we'll test several entries to find out the
# correct period length
# Find the period length, which will be the column name. We'll test
# several entries in order to find out the correct period length as
# there could be non-trading days which would make the computation
# wrong if made only one test
#
entries_to_test = min(10, len(fwdret.index), len(prices.index)-period)
entries_to_test = min(30, len(fwdret.index),
len(prices.index) - period)
days_diffs = []
for i in range(entries_to_test):
p_idx = prices.index.get_loc(fwdret.index[i])
start = prices.index[p_idx]
end = prices.index[p_idx+period]
end = prices.index[p_idx + period]
period_len = diff_custom_calendar_timedeltas(start, end, freq)
days_diffs.append(period_len.components.days)

Expand Down

0 comments on commit 417621b

Please sign in to comment.