With the second alternative,
pf.create_round_trip_tear_sheet(returns, positions=positions, transactions=transactions)
, you do get the error that you have mentioned.
You will need to go to the pyfolio files and make certain changes. At this point I am not sure whether I should go to pyfolio github repository and do a pull request because my changes work for what I need in the context of backtrader. I am in no way certain that making these changes will not break something else in pyfolio.
In round_trips.py, I made the following changes
Original
grouped_price = (t.groupby(('block_dir', 'block_time')).apply(vwap))
Modified to:
grouped_price = (t.groupby(['block_dir', 'block_time']).apply(vwap))
After you solve this error, as the pyfolio code is able to run further you will get a few more errors. As I understood, it is because pandas moved ahead and deprecated certain functionality. Link below:
https://pandas.pydata.org/pandas-docs/stable/whatsnew/v0.20.0.html#whatsnew-0200-api-breaking-deprecate-group-agg-dict
This functionality was used to pass dicts to
agg_all_long_short
Earlier code was:
PNL_STATS = OrderedDict([
('Total profit', lambda x: x.sum()),
('Gross profit', lambda x: x[x > 0].sum()),
('Gross loss', lambda x: x[x < 0].sum()),
('Profit factor', lambda x: x[x > 0].sum() / x[x < 0].abs().sum()
if x[x < 0].abs().sum() != 0 else np.nan),
('Avg. trade net profit', 'mean'),
('Avg. winning trade', lambda x: x[x > 0].mean()),
('Avg. losing trade', lambda x: x[x < 0].mean()),
('Ratio Avg. Win:Avg. Loss', lambda x: x[x > 0].mean() /
x[x < 0].abs().mean() if x[x < 0].abs().mean() != 0 else np.nan),
('Largest winning trade', 'max'),
('Largest losing trade', 'min'),])
Change this to:
PNL_STATS = (
(lambda x: x.sum()),
( lambda x: x[x > 0].sum()),
(lambda x: x[x < 0].sum()),
( lambda x: x[x > 0].sum() / x[x < 0].abs().sum()
if x[x < 0].abs().sum() != 0 else np.nan),
('mean'),
(lambda x: x[x > 0].mean()),
( lambda x: x[x < 0].mean()),
( lambda x: x[x > 0].mean() / x[x < 0].abs().mean() if x[x < 0].abs().mean() != 0 else np.nan),
('max'),
('min'),
)
Please make similar changes for the other dicts:
Original code
SUMMARY_STATS = OrderedDict([('Total number of round_trips', 'count'),
('Percent profitable', lambda x: len(x[x > 0]) / float(len(x))),
('Winning round_trips', lambda x: len(x[x > 0])),
('Losing round_trips', lambda x: len(x[x < 0])),
('Even round_trips', lambda x: len(x[x == 0])),
])
Modified code
SUMMARY_STATS = (
('count'),
(lambda x: len(x[x > 0]) / float(len(x))),
(lambda x: len(x[x > 0])),
(lambda x: len(x[x < 0])),
(lambda x: len(x[x == 0])),
)
And so on for even the other dicts:
RETURN_STATS
DURATION_STATS
Essentially, you now cannot pass rename logic in the dicts themselves. This also means that for you will need to create a list of the column names and pass it yourself.
In
def gen_round_trip_stats(round_trips):
this is the updated code Main changes are that we are creating a list of column names col_list before each function call to pass to the agg_all_long_short function (for reasons explained above - pandas deprecating nested renaming)
col_list = ['Total profit', 'Gross profit', 'Gross loss', 'Profit factor', 'Avg. trade net profit', 'Avg. winning trade', 'Avg. losing trade', 'Ratio Avg. Win:Avg. Loss', 'Largest winning trade', 'Largest losing trade']
stats['pnl'] = agg_all_long_short(round_trips, 'pnl', PNL_STATS, col_list)
col_list = ['Total number of round_trips','Percent profitable', 'Winning round_trips', 'Losing round_trips', 'Even round_trips']
stats['summary'] = agg_all_long_short(round_trips, 'pnl', SUMMARY_STATS, col_list)
col_list = ['Average duration', 'Median duration','Longest duration', 'Shortest duration']
stats['duration'] = agg_all_long_short(round_trips, 'duration',
DURATION_STATS, col_list)
col_list = ['Avg returns all round_trips','Avg returns winning','Avg returns losing', 'Median returns all round_trips', 'Median returns winning','Median returns losing', 'Largest winning trade', 'Largest losing trade' ]
stats['returns'] = agg_all_long_short(round_trips, 'rt_returns',
RETURN_STATS, col_list)
In function definition:
original
def agg_all_long_short(round_trips, col, stats_dict):
modified (adding col_list argument)
def agg_all_long_short(round_trips, col, stats_dict, col_list):
A few more changes in tears.py
Original code:
sns.distplot(trades.rt_returns.dropna() * 100, kde=False,
ax=ax_pnl_per_round_trip_pct)
Modified code:
sns.distplot(trades.returns.dropna() * 100, kde=False,
ax=ax_pnl_per_round_trip_pct)
With these changes the round_trip functionality should work. If any problems are faced please reply here. It will be helpful for the community.
Thanks.