Pyfolio from Alphalens not working

Hello,

i would like to create a pyfolio from alphalens results. However i'm getting an TypeError.

Is there another way to create pyfolio from alphalens? I can't find something in the documentation.

from zipline.research import run_pipeline, get_forward_returns
import pyfolio
import alphalens as al

my_pipe = make_pipeline()

pipe = run_pipeline(my_pipe, start_date="2019-04-01", end_date="2022-08-31") 
forward_returns = get_forward_returns(factor=pipe['factor'], periods=[1,5])
factor_data = al.utils.get_clean_factor(
            pipe['factor'],
            forward_returns,
            quantiles=5
)
# al.tears.create_full_tear_sheet(factor_data, group_neutral=False, by_group=False) # works


pf_returns, pf_positions, pf_benchmark = \
    al.performance.create_pyfolio_input(factor_data,
                                               period='1D',
                                               #capital=100000,
                                               #long_short=True,
                                               #group_neutral=False,
                                               #equal_weight=True,
                                               #quantiles=[1,5],
                                               #groups=None,
                                               #benchmark_period='1D'
                                              )
# TypeError: factor_weights() got an unexpected keyword argument 'group_neutral'
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Input In [48], in <cell line: 6>()
      1 import pyfolio
      2 import alphalens
      5 pf_returns, pf_positions, pf_benchmark = \
----> 6     alphalens.performance.create_pyfolio_input(factor_data,
      7                                                period='1D',
      8                                                #capital=100000,
      9                                                #long_short=True,
     10                                                #group_neutral=False,
     11                                                #equal_weight=True,
     12                                                #quantiles=[1,5],
     13                                                #groups=None,
     14                                                #benchmark_period='1D'
     15                                               )

File /opt/conda/lib/python3.9/site-packages/alphalens/performance.py:1196, in create_pyfolio_input(factor_data, period, capital, long_short, group_neutral, group_name, equal_weight, quantiles, groups, benchmark_period)
   1189 returns = cumrets.pct_change().fillna(0)
   1191 #
   1192 # Build positions. As pyfolio asks for daily position we have to resample
   1193 # the positions returned by 'factor_positions' at 1 day frequency and
   1194 # recompute the weights so that the sum of daily weights is 1.0
   1195 #
-> 1196 positions = factor_positions(
   1197     factor_data,
   1198     period=period,
   1199     long_short=long_short,
   1200     group_neutral=group_neutral,
   1201     group_name=group_name,
   1202     equal_weight=equal_weight,
   1203     quantiles=quantiles,
   1204     groups=groups)
   1205 positions = positions.resample('1D').sum().fillna(method='ffill')
   1206 positions = positions.div(positions.abs().sum(axis=1), axis=0).fillna(0)

File /opt/conda/lib/python3.9/site-packages/alphalens/performance.py:1063, in factor_positions(factor_data, period, long_short, group_neutral, group_name, equal_weight, quantiles, groups)
   1059 if groups is not None:
   1060     portfolio_data = portfolio_data[portfolio_data[group_name].isin(groups)]
   1062 weights = \
-> 1063     factor_weights(
   1064         portfolio_data,
   1065         demeaned=long_short,
   1066         group_neutral=group_neutral,
   1067         group_name=group_name,
   1068         equal_weight=equal_weight)
   1070 return positions(weights, period)

TypeError: factor_weights() got an unexpected keyword argument 'group_neutral'

I think the problem is in calling factor_weights. group_neutral needs to be group_adjust(As in Alphalens/Alphalens Reloaded).

I don't know how to change the packages :smiley: . Otherwise I would have tried it and made a git suggestion.

Thanks for reporting this and pointing out the fix. It will be fixed in the upcoming 2.10 release. In the meantime, the easiest solution to get you on your way would be to monkey-patch factor_positions with the corrected version. Just execute this code in a notebook cell to override the faulty Alphalens function, then try executing your code again.

import alphalens as al
from alphalens import utils
from alphalens.performance import factor_weights, positions

def factor_positions(factor_data,
                     period,
                     long_short=True,
                     group_neutral=False,
                     group_name="group",
                     equal_weight=False,
                     quantiles=None,
                     groups=None):
    """
    Simulate a portfolio using the factor in input and returns the assets
    positions as percentage of the total portfolio.

    Parameters
    ----------
    factor_data : pd.DataFrame - MultiIndex
        A MultiIndex DataFrame indexed by date (level 0) and asset (level 1),
        containing the values for a single alpha factor, forward returns for
        each period, the factor quantile/bin that factor value belongs to,
        and (optionally) the group the asset belongs to.
        - See full explanation in utils.get_clean_factor_and_forward_returns
    period : string
        'factor_data' column name corresponding to the 'period' returns to be
        used in the computation of porfolio returns
    long_short : bool, optional
        if True then simulates a dollar neutral long-short portfolio
        - see performance.create_pyfolio_input for more details
    group_neutral : bool, optional
        If True then simulates a group neutral portfolio
        - see performance.create_pyfolio_input for more details
    group_name : str, optional
        name of the group column in factor_data. Defaults to "group".
    equal_weight : bool, optional
        Control the assets weights:
        - see performance.create_pyfolio_input for more details.
    quantiles: sequence[int], optional
        Use only specific quantiles in the computation. By default all
        quantiles are used
    groups: sequence[string], optional
        Use only specific groups in the computation. By default all groups
        are used

    Returns
    -------
    assets positions : pd.DataFrame
        Assets positions series, datetime on index, assets on columns.
        Example:
            index                 'AAPL'         'MSFT'          cash
            2004-01-09 10:30:00   13939.3800     -14012.9930     711.5585
            2004-01-09 15:30:00       0.00       -16012.9930     411.5585
            2004-01-12 10:30:00   14492.6300     -14624.8700       0.0
            2004-01-12 15:30:00   14874.5400     -15841.2500       0.0
            2004-01-13 10:30:00   -13853.2800    13653.6400      -43.6375
    """
    fwd_ret_cols = utils.get_forward_returns_columns(factor_data.columns)

    if period not in fwd_ret_cols:
        raise ValueError("Period '%s' not found" % period)

    todrop = list(fwd_ret_cols)
    todrop.remove(period)
    portfolio_data = factor_data.drop(todrop, axis=1)

    if quantiles is not None:
        portfolio_data = portfolio_data[portfolio_data['factor_quantile'].isin(
            quantiles)]

    if groups is not None:
        portfolio_data = portfolio_data[portfolio_data[group_name].isin(groups)]

    weights = \
        factor_weights(
            portfolio_data,
            demeaned=long_short,
            group_adjust=group_neutral,
            group_name=group_name,
            equal_weight=equal_weight)

    return positions(weights, period)

# monkey-patch broken function with corrected function
al.performance.factor_positions = factor_positions
1 Like

Thanks for the quick reply! Worked as expected.

This is fixed in QuantRocket 2.10.0.

This topic was automatically closed after 3 days. New replies are no longer allowed.