Skip to content

Commit

Permalink
Merge pull request quantopian#514 from vikram-narayan/remove-perf-attrib
Browse files Browse the repository at this point in the history
MAINT: move perf attrib computations to empyrical
  • Loading branch information
Vikram Narayan authored Feb 3, 2018
2 parents 12353f8 + efb75c3 commit 0332bb1
Show file tree
Hide file tree
Showing 5 changed files with 25 additions and 27 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ install:
- source activate testenv
- pip install nose_parameterized
#- pip install --no-deps git+https://github.com/quantopian/zipline
- pip install -e .[bayesian]
- pip install -e .[bayesian] -c constraints.txt

before_script:
- "flake8 pyfolio"
Expand Down
1 change: 1 addition & 0 deletions constraints.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pymc3==3.1
41 changes: 19 additions & 22 deletions pyfolio/perf_attrib.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import matplotlib.pyplot as plt

from pyfolio.pos import get_percent_alloc

from pyfolio.txn import get_turnover
from pyfolio.utils import print_table, configure_legend

Expand All @@ -35,7 +34,18 @@ def perf_attrib(returns,
transactions=None,
pos_in_dollars=True):
"""
Does performance attribution given risk info.
Attributes the performance of a returns stream to a set of risk factors.
Preprocesses inputs, and then calls empyrical.perf_attrib. See
empyrical.perf_attrib for more info.
Performance attribution determines how much each risk factor, e.g.,
momentum, the technology sector, etc., contributed to total returns, as
well as the daily exposure to each of the risk factors. The returns that
can be attributed to one of the given risk factors are the
`common_returns`, and the returns that _cannot_ be attributed to a risk
factor are the `specific_returns`, or the alpha. The common_returns and
specific_returns summed together will always equal the total returns.
Parameters
----------
Expand Down Expand Up @@ -81,6 +91,7 @@ def perf_attrib(returns,
TLT -1.066978 0.185435
XOM -1.798401 0.761549
transactions : pd.DataFrame, optional
Executed trade volumes and fill prices. Used to check the turnover of
the algorithm. Default is None, in which case the turnover check is
Expand Down Expand Up @@ -134,28 +145,17 @@ def perf_attrib(returns,
# above, since get_turnover() expects positions in dollars.
positions = _stack_positions(positions, pos_in_dollars=pos_in_dollars)

risk_exposures_portfolio = compute_exposures(positions,
factor_loadings,
stack_positions=False)

perf_attrib_by_factor = risk_exposures_portfolio.multiply(factor_returns)

common_returns = perf_attrib_by_factor.sum(axis='columns')
specific_returns = returns - common_returns

returns_df = pd.DataFrame({'total_returns': returns,
'common_returns': common_returns,
'specific_returns': specific_returns})

return (risk_exposures_portfolio,
pd.concat([perf_attrib_by_factor, returns_df], axis='columns'))
return ep.perf_attrib(returns, positions, factor_returns, factor_loadings)


def compute_exposures(positions, factor_loadings, stack_positions=True,
pos_in_dollars=True):
"""
Compute daily risk factor exposures.
Normalizes positions (if necessary) and calls ep.compute_exposures.
See empyrical.compute_exposures for more info.
Parameters
----------
positions: pd.DataFrame or pd.Series
Expand Down Expand Up @@ -203,7 +203,7 @@ def compute_exposures(positions, factor_loadings, stack_positions=True,
Returns
-------
risk_exposures_portfolio : pd.DataFrame
df indexed by datetime, with factors as columns
df indexed by datetime, with factors as columns.
- Example:
momentum reversal
dt
Expand All @@ -213,10 +213,7 @@ def compute_exposures(positions, factor_loadings, stack_positions=True,
if stack_positions:
positions = _stack_positions(positions, pos_in_dollars=pos_in_dollars)

risk_exposures = factor_loadings.multiply(positions,
axis='rows')

return risk_exposures.groupby(level='dt').sum()
return ep.compute_exposures(positions, factor_loadings)


def create_perf_attrib_stats(perf_attrib, risk_exposures):
Expand Down
4 changes: 2 additions & 2 deletions pyfolio/tests/test_perf_attrib.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ def test_missing_stocks_and_dates(self):
level='ticker')

with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
warnings.simplefilter("always", UserWarning)

perf_attrib(returns,
positions,
Expand Down Expand Up @@ -438,7 +438,7 @@ def test_high_turnover_warning(self):
transactions = mock_transactions_from_positions(positions)

with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
warnings.simplefilter("always", UserWarning)

perf_attrib(returns,
positions,
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,14 @@
install_reqs = [
'ipython>=3.2.3' if support_ipython_6 else 'ipython>=3.2.3, <6',
'matplotlib>=1.4.0',
'numpy>=1.9.1',
'numpy>=1.11.1',
'pandas>=0.18.1',
'pytz>=2014.10',
'scipy>=0.14.0',
'scikit-learn>=0.16.1',
'seaborn>=0.7.1',
'pandas-datareader>=0.2',
'empyrical>=0.3.3'
'empyrical>=0.3.4'
]

test_reqs = ['nose>=1.3.7', 'nose-parameterized>=0.5.0', 'runipy>=0.1.3']
Expand Down

0 comments on commit 0332bb1

Please sign in to comment.