Pyfolio – AttributeError: ‘Series’ object has no attribute ‘iteritems’ & AttributeError: ‘numpy.int64’ object has no attribute ‘to_pydatetime’

Recently I was following a paper and in the example they used Pyfolio which is an awesome performance and risk analysis library in Python developed by Quantopian Inc when they were still around. Given that Quantopian is no longer around nobody is maintaining this library. I ran into a few errors and figured I would outline the solutions below in case anyone has these issues. But before I dive too deep into modifying this library you may be better off just uninstalling Pyfolio and loading Pyfolio-reloaded.

Also, if you’re interested here is an article on modifying PyFolio to output charts and data to an HTML.

Pyfolio-reloaded

pip uninstall pyfolio
pip install git+https://github.com/stefan-jansen/pyfolio-reloaded.git

First Error

Traceback (most recent call last):
  File "/home/shared/algos/ml4t/pairs_trading_backtest.py", line 512, in <module>
    pf.create_full_tear_sheet(returns,
  File "/opt/anaconda3/envs/ml4t/lib/python3.10/site-packages/pyfolio/tears.py", line 201, in create_full_tear_sheet
    create_returns_tear_sheet(
  File "/opt/anaconda3/envs/ml4t/lib/python3.10/site-packages/pyfolio/plotting.py", line 52, in call_w_context
    return func(*args, **kwargs)
  File "/opt/anaconda3/envs/ml4t/lib/python3.10/site-packages/pyfolio/tears.py", line 496, in create_returns_tear_sheet
    plotting.show_perf_stats(returns, benchmark_rets,
  File "/opt/anaconda3/envs/ml4t/lib/python3.10/site-packages/pyfolio/plotting.py", line 648, in show_perf_stats
    for stat, value in perf_stats[column].iteritems():
  File "/opt/anaconda3/envs/ml4t/lib/python3.10/site-packages/pandas/core/generic.py", line 5989, in __getattr__
    return object.__getattribute__(self, name)
AttributeError: 'Series' object has no attribute 'iteritems'

Process finished with exit code 1

This is the first error I received which was generated by this line of code:


pf.create_full_tear_sheet(returns, 
                          positions=positions, 
                          transactions=transactions, 
                          benchmark_rets=benchmark.loc[returns.index], 
                          estimate_intraday=False)

This can be fixed by modifying the file, /opt/anaconda3/envs/ml4t/lib/python3.10/site-packages/pyfolio/plotting.py

The line of code I’m going to change is as follows:

Existing:
for stat, value in perf_stats[column].iteritems():

New:
for stat, value in perf_stats[column].items():

Second Error:

Traceback (most recent call last):
  File "/home/shared/algos/ml4t/pairs_trading_backtest.py", line 512, in <module>
    pf.create_full_tear_sheet(returns,
  File "/opt/anaconda3/envs/ml4t/lib/python3.10/site-packages/pyfolio/tears.py", line 201, in create_full_tear_sheet
    create_returns_tear_sheet(
  File "/opt/anaconda3/envs/ml4t/lib/python3.10/site-packages/pyfolio/plotting.py", line 52, in call_w_context
    return func(*args, **kwargs)
  File "/opt/anaconda3/envs/ml4t/lib/python3.10/site-packages/pyfolio/tears.py", line 504, in create_returns_tear_sheet
    plotting.show_worst_drawdown_periods(returns)
  File "/opt/anaconda3/envs/ml4t/lib/python3.10/site-packages/pyfolio/plotting.py", line 1664, in show_worst_drawdown_periods
    drawdown_df = timeseries.gen_drawdown_table(returns, top=top)
  File "/opt/anaconda3/envs/ml4t/lib/python3.10/site-packages/pyfolio/timeseries.py", line 1008, in gen_drawdown_table
    df_drawdowns.loc[i, 'Valley date'] = (valley.to_pydatetime()
AttributeError: 'numpy.int64' object has no attribute 'to_pydatetime'

To fix this I modified this file /opt/anaconda3/envs/ml4t/lib/python3.10/site-packages/pyfolio/timeseries.py

The issue is within the function get_max_drawdown_underwater() the code is returning the index position for valley and it needs to be the date not the index position itself. The fixed code is below


def get_max_drawdown_underwater(underwater):
    """
    Determines peak, valley, and recovery dates given an 'underwater'
    DataFrame.

    An underwater DataFrame is a DataFrame that has precomputed
    rolling drawdown.

    Parameters
    ----------
    underwater : pd.Series
       Underwater returns (rolling drawdown) of a strategy.

    Returns
    -------
    peak : datetime
        The maximum drawdown's peak.
    valley : datetime
        The maximum drawdown's valley.
    recovery : datetime
        The maximum drawdown's recovery.
    """

    valley_idx = np.argmin(underwater)  # end of the period, as an index position
    valley_date = underwater.index[valley_idx]  # convert index position to timestamp
    # Find first 0
    peak = underwater[:valley_date][underwater[:valley_date] == 0].index[-1]
    # Find last 0
    try:
        recovery = underwater[valley_date:][underwater[valley_date:] == 0].index[0]
    except IndexError:
        recovery = np.nan  # drawdown not recovered
    logging.info(f'get_max_drawdown_underwater is returning \n {peak} \n {valley_date} \n {recovery}')
    return peak, valley_date, recovery

Leave a Reply