-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sector Exposure #166
Closed
Closed
Sector Exposure #166
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
254cdc5
-m STY generalize naming of percent allocation function to fit use by…
a-campbell c29009e
-m ENH Map positions to sectors, calculate sector exposures, plot sec…
a-campbell e1a60c9
STY refactored sector exposure rollup
a-campbell ddcfb44
TST Added tests for sector exposure
a-campbell 15ee58e
BUG Added warning for case where no sector mapping exists for a trade…
a-campbell eb0a755
DOC doc fixes
a-campbell 456c444
STY Flake8
a-campbell a71b13d
STY Refactored test. Doc fixes. PEP8.
a-campbell c01cefa
pr fixes
a-campbell 736c469
MAINT Generalize naming of percent allocation function to fit use by …
a-campbell 8d9daaf
ENH Map positions to sectors, calculate sector exposures, plot sector…
a-campbell 479a3ef
Merge branch 'sector-rollup' of github.com:quantopian/pyfolio into se…
a-campbell 0f2d2d2
ENH added sector mapping example to zipline algo nb
a-campbell 0c0c3b2
tweaks
a-campbell 16a559c
BUG fixed state changing pop on positions
a-campbell 70070e2
BUG gracefully handle case where no sector mapping can be done
a-campbell File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,24 +15,26 @@ | |
from __future__ import division | ||
|
||
import pandas as pd | ||
import numpy as np | ||
import warnings | ||
|
||
|
||
def get_portfolio_alloc(positions): | ||
def get_percent_alloc(values): | ||
""" | ||
Determines a portfolio's allocations. | ||
|
||
Parameters | ||
---------- | ||
positions : pd.DataFrame | ||
values : pd.DataFrame | ||
Contains position values or amounts. | ||
|
||
Returns | ||
------- | ||
positions_alloc : pd.DataFrame | ||
allocations : pd.DataFrame | ||
Positions and their allocations. | ||
""" | ||
return positions.divide( | ||
positions.abs().sum(axis='columns'), | ||
return values.divide( | ||
values.abs().sum(axis='columns'), | ||
axis='rows' | ||
) | ||
|
||
|
@@ -163,3 +165,52 @@ def get_turnover(transactions, positions, period=None): | |
turnover = traded_value / 2.0 | ||
turnover_rate = turnover / portfolio_value | ||
return turnover_rate | ||
|
||
|
||
def get_sector_exposures(positions, symbol_sector_map): | ||
""" | ||
Sum position exposures by sector. | ||
|
||
Parameters | ||
---------- | ||
positions : pd.DataFrame | ||
Contains position values or amounts. | ||
- Example | ||
index 'AAPL' 'MSFT' 'CHK' cash | ||
2004-01-09 13939.380 -15012.993 -403.870 1477.483 | ||
2004-01-12 14492.630 -18624.870 142.630 3989.610 | ||
2004-01-13 -13853.280 13653.640 -100.980 100.000 | ||
symbol_sector_map : dict or pd.Series | ||
Security identifier to sector mapping. | ||
Security ids as keys/index, sectors as values. | ||
- Example: | ||
{'AAPL' : 'Technology' | ||
'MSFT' : 'Technology' | ||
'CHK' : 'Natural Resources'} | ||
Returns | ||
------- | ||
sector_exp : pd.DataFrame | ||
Sectors and their allocations. | ||
- Example: | ||
index 'Technology' 'Natural Resources' cash | ||
2004-01-09 -1073.613 -403.870 1477.4830 | ||
2004-01-12 -4132.240 142.630 3989.6100 | ||
2004-01-13 -199.640 -100.980 100.0000 | ||
""" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adding a small example self-contained example would be helpful here I think. |
||
cash = positions['cash'] | ||
positions = positions.drop('cash', axis=1) | ||
|
||
unmapped_pos = np.setdiff1d(positions.columns.values, | ||
symbol_sector_map.keys()) | ||
if len(unmapped_pos) > 0: | ||
warn_message = """Warning: Symbols {} have no sector mapping. | ||
They will not be included in sector allocations""".format( | ||
", ".join(map(str, unmapped_pos))) | ||
warnings.warn(warn_message, UserWarning) | ||
|
||
sector_exp = positions.groupby( | ||
by=symbol_sector_map, axis=1).sum() | ||
|
||
sector_exp['cash'] = cash | ||
|
||
return sector_exp |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
from unittest import TestCase | ||
from nose_parameterized import parameterized | ||
from collections import OrderedDict | ||
|
||
from pandas import ( | ||
|
@@ -15,14 +16,17 @@ | |
zeros_like, | ||
) | ||
|
||
from pyfolio.pos import (get_portfolio_alloc, | ||
from pyfolio.pos import (get_percent_alloc, | ||
extract_pos, | ||
get_turnover) | ||
get_turnover, | ||
get_sector_exposures) | ||
import warnings | ||
|
||
|
||
class PositionsTestCase(TestCase): | ||
dates = date_range(start='2015-01-01', freq='D', periods=20) | ||
|
||
def test_get_portfolio_alloc(self): | ||
def test_get_percent_alloc(self): | ||
raw_data = arange(15, dtype=float).reshape(5, 3) | ||
# Make the first column negative to test absolute magnitudes. | ||
raw_data[:, 0] *= -1 | ||
|
@@ -33,7 +37,7 @@ def test_get_portfolio_alloc(self): | |
columns=['A', 'B', 'C'] | ||
) | ||
|
||
result = get_portfolio_alloc(frame) | ||
result = get_percent_alloc(frame) | ||
expected_raw = zeros_like(raw_data) | ||
for idx, row in enumerate(raw_data): | ||
expected_raw[idx] = row / absolute(row).sum() | ||
|
@@ -121,3 +125,41 @@ def test_get_turnover(self): | |
result = get_turnover(transactions, positions, period='M') | ||
expected = Series([10.0], index=index) | ||
assert_series_equal(result, expected) | ||
|
||
@parameterized.expand([ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice! |
||
(DataFrame([[1.0, 2.0, 3.0, 10.0]]*len(dates), | ||
columns=[0, 1, 2, 'cash'], index=dates), | ||
{0: 'A', 1: 'B', 2: 'A'}, | ||
DataFrame([[4.0, 2.0, 10.0]]*len(dates), | ||
columns=['A', 'B', 'cash'], index=dates), | ||
False), | ||
(DataFrame([[1.0, 2.0, 3.0, 10.0]]*len(dates), | ||
columns=[0, 1, 2, 'cash'], index=dates), | ||
Series(index=[0, 1, 2], data=['A', 'B', 'A']), | ||
DataFrame([[4.0, 2.0, 10.0]]*len(dates), | ||
columns=['A', 'B', 'cash'], index=dates), | ||
False), | ||
(DataFrame([[1.0, 2.0, 3.0, 10.0]]*len(dates), | ||
columns=[0, 1, 2, 'cash'], index=dates), | ||
{0: 'A', 1: 'B'}, | ||
DataFrame([[1.0, 2.0, 10.0]]*len(dates), | ||
columns=['A', 'B', 'cash'], index=dates), | ||
True) | ||
]) | ||
def test_sector_exposure(self, positions, mapping, | ||
expected_sector_exposure, | ||
warning_expected): | ||
""" | ||
Tests sector exposure mapping and rollup. | ||
|
||
""" | ||
with warnings.catch_warnings(record=True) as w: | ||
result_sector_exposure = get_sector_exposures(positions, | ||
mapping) | ||
|
||
assert_frame_equal(result_sector_exposure, | ||
expected_sector_exposure) | ||
if warning_expected: | ||
assert len(w) == 1 | ||
else: | ||
assert len(w) == 0 |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not returning
ax
.