stefan-jansen / alphalens-reloaded

Performance analysis of predictive (alpha) stock factors
https://alphalens.ml4trading.io
Apache License 2.0
304 stars 68 forks source link

Date index inconsistency when creating input for pyfolio #9

Closed gcmsrc closed 5 months ago

gcmsrc commented 2 years ago

Date index inconsistency when creating input for pyfolio

In a nutshell

alphalens-generated input for pyfolio (e.g., returns series) is indexed using a 1D resampled index. This implies that non-trading days would be present in the portfolio returns series (e.g., December 25th would be there).

Example

Using your example notebook, we get the following:

As you can see, January 10th, 2015 (a Saturday) is not in factor_data but it is present in pf_returns.

Implications

While this is not an issue per se, inconsistencies emerge when running, for example, pyfolio.timeseries.perf_stats (link). In the case of empyrical.stats.annual_return (link) - for example - annual return would calculate # of years using the default annualization factor for trading calendars (i.e., 252 days) instead of 365 days as per a full calendar year. This results in a inconsistent calculation.

In particular, in empyrical.stats.annual_return, # of years is calculated as:

num_years = len(returns) / ann_factor

where len(returns) - if returns is portfolio return coming from alpha lens.performance.create_pyfolio_input - will be inflated by including non-trading days. To calculate # years, as of current implementation, we will divide number of days (including non trading days) by 252. For example, # years for a portfolio returns series starting for 2002/01/01 to 2003/01/01 will be equal to 365 / 252 instead of 365 / 365.

Feedback

@stefan-jansen - am I missing something here? As of now, I am taking the output of alphalens.performance.create_pyfolio_input and filter it by keeping only rows for trading days. An alternative option would be that of letting the user specify the annualization argument of empyrical.stats.annual_return in alphalens.performance.create_pyfolio_input (and propagate the change downstream to all dependent methods). What do you think?

stefan-jansen commented 1 year ago

Sorry for the late reply. Sounds like this could indeed be a problem. PRs are welcome in case you want to come up with a solution?

stefan-jansen commented 5 months ago

Closing due to lack of activity; feel free to reopen if the issue persists, or submit a PR.