A shot at Pyfolio


#1

I gave a shot at pyfolio. As some people pointed out on Discord, you have to convert your results from minute frequency to daily frequency. You can achieve this with the function convert_results_to_daily included below.

In code:

import pyfolio as pf
# Constructs the daily results from the minute results "results"
results_daily = convert_results_to_daily(results)
# Uses pyfolio function to construct the return, positions and transactions dataframes
returns, positions, transactions = pf.utils.extract_rets_pos_txn_from_zipline(results_daily)
# You have to set a benchmark. I set it to zero for simplicity. You have to name the dataframe to avoid a bug.
benchmark_rets = pd.DataFrame([[0]], index = results_daily.index)
benchmark_rets.name = 'BTC'
# Construct the full tearsheet
pf.create_full_tear_sheet(returns, positions=positions, transactions=transactions, round_trips=True, benchmark_rets=benchmark_rets)

You’ll see that it does work for a bit, but then errors appear. I managed to fix the first one by replacing

factor_returns[cum_rets.index], 1.0)

to

factor_returns.loc[cum_rets.index], 1.0)

on line 857 of https://github.com/quantopian/pyfolio/blob/869badec948c9ecefcc6e22eaccccb44b27aa679/pyfolio/plotting.py

Then other errors appear and I have to go to bed. If anybody wants to help debugging, it would be great!

Samuel

P.S: Here is the function to convert the results from minute to daily:

def convert_results_to_daily(results):
    """
    A function that converts the results dataframe from daily frequency to minute frequency, to be fed in 
    pyfolio.
    """
    # Computes the resampled returns
    res_pf_val = results.portfolio_value.resample('D', convention='start').last()
    res_ret = np.divide(res_pf_val, res_pf_val.shift()).add(-1)
    res_ret.iloc[0] = 0.0

    # Computes the resampled positions
    res_pos = results.positions.resample('D').last()

    # Computes the resampled ending cash
    res_ec = results.ending_cash.resample('D').last()

    # Computes the resampled transactions
    res_trans = results.transactions.resample('D').agg('sum')

    # Creates the results dataframe
    results_daily = pd.DataFrame(index = res_ret.index)
    results_daily.returns = res_ret
    results_daily.positions = res_pos
    results_daily.ending_cash = res_ec
    results_daily.transactions = res_trans

    return results_daily

#2

Ok it works, once you get rid of the plots that don’t make sense for crypto (rolling beta, Fama-French factors, etc…).


#3

It seems that pyfolio ignores the commission in several places, which yields quite different results compared to catalyst results, specifically portfolio_value.


#4

The examples you mention in the issue involve only the round trip computations to extract statistics on the trades and positions, right?

The equity curve and the associated statistics are deduced from the returns, and unless I’m mistaken, those already include the transaction costs.


#5

Everything calculated by pyfolio and not by catalyst does not include commission.
I guess it makes sense that this is limited only to the transactions data, which is round trips calculations.

I haven’t checked specifically the statistics you mentioned.