Skip to content

Commit de24725

Browse files
committed
Merge pull request #117 from quantopian/fix-turnover
BUG Fix turnover calculation
2 parents 48fefca + 697dc9e commit de24725

File tree

2 files changed

+26
-17
lines changed

2 files changed

+26
-17
lines changed

pyfolio/plotting.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,9 +1032,8 @@ def plot_turnover(returns, transactions, positions,
10321032
y_axis_formatter = FuncFormatter(utils.one_dec_places)
10331033
ax.yaxis.set_major_formatter(FuncFormatter(y_axis_formatter))
10341034

1035-
df_turnover = transactions.txn_volume / \
1036-
positions.abs().sum(axis='columns')
1037-
df_turnover_by_month = df_turnover.resample('1M', how='mean')
1035+
df_turnover = pos.get_turnover(transactions, positions)
1036+
df_turnover_by_month = df_turnover.resample("M")
10381037
df_turnover.plot(color='steelblue', alpha=1.0, lw=0.5, ax=ax, **kwargs)
10391038
df_turnover_by_month.plot(
10401039
color='orangered',

pyfolio/pos.py

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -135,29 +135,39 @@ def extract_pos(positions, cash):
135135
return values
136136

137137

138-
def turnover(transactions_df, backtest_data_df, period='M'):
138+
def get_turnover(transactions, positions, period=None):
139139
"""
140-
Calculates the percent absolute value portfolio turnover.
140+
Portfolio Turnover Rate:
141+
142+
Average value of purchases and sales divided
143+
by the average portfolio value for the period.
144+
145+
If no period is provided the period is one time step.
141146
142147
Parameters
143148
----------
144149
transactions_df : pd.DataFrame
145-
Contains transactional data.
146-
backtest_data_df : pd.DataFrame
147-
Contains backtest data, like positions.
150+
Contains transactions data.
151+
- See full explanation in tears.create_full_tear_sheet
152+
positions : pd.DataFrame
153+
Contains daily position values including cash
154+
- See full explanation in tears.create_full_tear_sheet
148155
period : str, optional
149156
Takes the same arguments as df.resample.
150157
151158
Returns
152159
-------
153-
turnoverpct : pd.DataFrame
154-
The number of shares traded for a period as a fraction of total shares.
160+
turnover_rate : pd.Series
161+
timeseries of portfolio turnover rates.
155162
"""
156163

157-
turnover = transactions_df.apply(
158-
lambda z: z.apply(
159-
lambda r: abs(r))).resample(period, 'sum').sum(axis=1)
160-
portfolio_value = backtest_data_df.portfolio_value.resample(period, 'mean')
161-
turnoverpct = turnover / portfolio_value
162-
turnoverpct = turnoverpct.fillna(0)
163-
return turnoverpct
164+
traded_value = transactions.txn_volume
165+
portfolio_value = positions.sum(axis=1)
166+
if period is not None:
167+
traded_value = traded_value.resample(period, how='sum')
168+
portfolio_value = portfolio_value.resample(period, how='mean')
169+
# traded_value contains the summed value from buys and sells;
170+
# this is divided by 2.0 to get the average of the two.
171+
turnover = traded_value / 2.0
172+
turnover_rate = turnover / portfolio_value
173+
return turnover_rate

0 commit comments

Comments
 (0)