Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rolling alpha added in timeseries and plotting #617

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 55 additions & 1 deletion pyfolio/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ def customize(func):
"""
Decorator to set plotting context and axes style during function call.
"""

@wraps(func)
def call_w_context(*args, **kwargs):
set_context = kwargs.pop('set_context', True)
Expand All @@ -52,6 +53,7 @@ def call_w_context(*args, **kwargs):
return func(*args, **kwargs)
else:
return func(*args, **kwargs)

return call_w_context


Expand Down Expand Up @@ -436,7 +438,7 @@ def plot_drawdown_periods(returns, top=10, ax=None, **kwargs):
lim = ax.get_ylim()
colors = sns.cubehelix_palette(len(df_drawdowns))[::-1]
for i, (peak, recovery) in df_drawdowns[
['Peak date', 'Recovery date']].iterrows():
['Peak date', 'Recovery date']].iterrows():
if pd.isnull(recovery):
recovery = returns.index[-1]
ax.fill_between((peak, recovery),
Expand Down Expand Up @@ -888,6 +890,58 @@ def plot_rolling_beta(returns, factor_returns, legend_loc='best',
return ax


def plot_rolling_alpha(returns, factor_returns, legend_loc='best',
ax=None, **kwargs):
"""
Plots the rolling 6-month and 12-month alpha versus date.

Parameters
----------
returns : pd.Series
Daily returns of the strategy, noncumulative.
- See full explanation in tears.create_full_tear_sheet.
factor_returns : pd.Series
Daily noncumulative returns of the benchmark factor to which alpha are
computed. Usually a benchmark such as market returns.
- This is in the same style as returns.
legend_loc : matplotlib.loc, optional
The location of the legend on the plot.
ax : matplotlib.Axes, optional
Axes upon which to plot.
**kwargs, optional
Passed to plotting function.

Returns
-------
ax : matplotlib.Axes
The axes that were plotted on.
"""

if ax is None:
ax = plt.gca()

y_axis_formatter = FuncFormatter(utils.two_dec_places)
ax.yaxis.set_major_formatter(FuncFormatter(y_axis_formatter))

ax.set_title("Rolling portfolio alpha to " + str(factor_returns.name))
ax.set_ylabel('Alpha')
rb_1 = timeseries.rolling_alpha(
returns, factor_returns, rolling_window=APPROX_BDAYS_PER_MONTH * 6)
rb_1.plot(color='steelblue', lw=3, alpha=0.6, ax=ax, **kwargs)
rb_2 = timeseries.rolling_alpha(
returns, factor_returns, rolling_window=APPROX_BDAYS_PER_MONTH * 12)
rb_2.plot(color='grey', lw=3, alpha=0.4, ax=ax, **kwargs)
ax.axhline(rb_1.mean(), color='steelblue', linestyle='--', lw=3)
ax.axhline(0.0, color='black', linestyle='-', lw=2)

ax.set_xlabel('')
ax.legend(['6-mo',
'12-mo'],
loc=legend_loc, frameon=True, framealpha=0.5)
ax.set_ylim((-1.0, 1.0))
return ax


def plot_rolling_volatility(returns, factor_returns=None,
rolling_window=APPROX_BDAYS_PER_MONTH * 6,
legend_loc='best', ax=None, **kwargs):
Expand Down
44 changes: 44 additions & 0 deletions pyfolio/timeseries.py
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,50 @@ def rolling_beta(returns, factor_returns,
return out


def rolling_alpha(returns, factor_returns,
rolling_window=APPROX_BDAYS_PER_MONTH * 6):
"""
Determines the rolling alpha of a strategy.

Parameters
----------
returns : pd.Series
Daily returns of the strategy, noncumulative.
- See full explanation in tears.create_full_tear_sheet.
factor_returns : pd.Series or pd.DataFrame
Daily noncumulative returns of the benchmark factor to which alpha are
computed. Usually a benchmark such as market returns.
- If DataFrame is passed, computes rolling alpha for each column.
- This is in the same style as returns.
rolling_window : int, optional
The size of the rolling window, in days, over which to compute
alpha (default 6 months).

Returns
-------
pd.Series
Rolling alpha.

Note
-----
See https://en.wikipedia.org/wiki/Alpha_(finance) for more details.
"""

if factor_returns.ndim > 1:
# Apply column-wise
return factor_returns.apply(partial(rolling_alpha, returns),
rolling_window=rolling_window)
else:
out = pd.Series(index=returns.index)
for beg, end in zip(returns.index[0:-rolling_window],
returns.index[rolling_window:]):
out.loc[end] = ep.alpha(
returns.loc[beg:end],
factor_returns.loc[beg:end])

return out


def rolling_regression(returns, factor_returns,
rolling_window=APPROX_BDAYS_PER_MONTH * 6,
nan_threshold=0.1):
Expand Down