Skip to content

Commit

Permalink
MAINT: add NOT_PASSED_SENTINEL (#543)
Browse files Browse the repository at this point in the history
* MAINT: add not passed sentinel

* STY: pep8

* BUG: change benchmark_rets back to None after warning

* BUG: added extra check to avoid invalid type comp

* BUG: bufixes

* MAINT: refactored vertical sections

* BUG: fixed plotting order
  • Loading branch information
eigenfoo authored and twiecki committed Aug 1, 2018
1 parent 1ee6eab commit 7d37dc5
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 16 deletions.
70 changes: 54 additions & 16 deletions pyfolio/tears.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@
'technology']
}

BENCHMARK_RETS_WARNING = ('The latest version of pyfolio requires users to '
'supply benchmark returns. Your current tearsheets '
'will not include plots and analyses that require a '
'benchmark. In the future, please pass '
'benchmark_rets, or pass None to silence this '
'warning.')


def timer(msg_body, previous_time):
current_time = time()
Expand All @@ -68,7 +75,7 @@ def create_full_tear_sheet(returns,
positions=None,
transactions=None,
market_data=None,
benchmark_rets=None,
benchmark_rets=utils.NOT_PASSED_SENTINEL,
slippage=None,
live_start_date=None,
sector_mappings=None,
Expand Down Expand Up @@ -188,6 +195,10 @@ def create_full_tear_sheet(returns,
factor returns and risk exposures plots
- See create_perf_attrib_tear_sheet().
"""
if (isinstance(benchmark_rets, str)
and benchmark_rets == utils.NOT_PASSED_SENTINEL):
warnings.warn(BENCHMARK_RETS_WARNING)
benchmark_rets = None

if (unadjusted_returns is None) and (slippage is not None) and\
(transactions is not None):
Expand Down Expand Up @@ -262,7 +273,7 @@ def create_full_tear_sheet(returns,
def create_simple_tear_sheet(returns,
positions=None,
transactions=None,
benchmark_rets=None,
benchmark_rets=utils.NOT_PASSED_SENTINEL,
slippage=None,
estimate_intraday='infer',
live_start_date=None,
Expand Down Expand Up @@ -335,6 +346,11 @@ def create_simple_tear_sheet(returns,
positions = utils.check_intraday(estimate_intraday, returns,
positions, transactions)

if (isinstance(benchmark_rets, str)
and benchmark_rets == utils.NOT_PASSED_SENTINEL):
warnings.warn(BENCHMARK_RETS_WARNING)
benchmark_rets = None

if (slippage is not None) and (transactions is not None):
returns = txn.adjust_returns_for_slippage(returns, positions,
transactions, slippage)
Expand Down Expand Up @@ -443,7 +459,7 @@ def create_returns_tear_sheet(returns, positions=None,
transactions=None,
live_start_date=None,
cone_std=(1.0, 1.5, 2.0),
benchmark_rets=None,
benchmark_rets=utils.NOT_PASSED_SENTINEL,
bootstrap=False,
turnover_denom='AGB',
header_rows=None,
Expand Down Expand Up @@ -493,6 +509,11 @@ def create_returns_tear_sheet(returns, positions=None,
If True, returns the figure that was plotted on.
"""

if (isinstance(benchmark_rets, str)
and benchmark_rets == utils.NOT_PASSED_SENTINEL):
warnings.warn(BENCHMARK_RETS_WARNING)
benchmark_rets = None

if benchmark_rets is not None:
returns = utils.clip_returns_to_benchmark(returns, benchmark_rets)

Expand All @@ -506,18 +527,21 @@ def create_returns_tear_sheet(returns, positions=None,

plotting.show_worst_drawdown_periods(returns)

vertical_sections = 11
always_sections = 11
live_start_date_sections = 1 if (live_start_date is not None) else 0
benchmark_sections = 1 if (benchmark_rets is not None) else 0
bootstrap_sections = 1 if bootstrap else 0

vertical_sections = sum([
always_sections,
live_start_date_sections,
benchmark_sections,
bootstrap_sections
])

if live_start_date is not None:
vertical_sections += 1
live_start_date = ep.utils.get_utc_timestamp(live_start_date)

if benchmark_rets is not None:
vertical_sections += 1

if bootstrap:
vertical_sections += 1

fig = plt.figure(figsize=(14, vertical_sections * 6))
gs = gridspec.GridSpec(vertical_sections, 3, wspace=0.5, hspace=0.5)
ax_rolling_returns = plt.subplot(gs[:2, :])
Expand All @@ -531,10 +555,10 @@ def create_returns_tear_sheet(returns, positions=None,
i += 1
ax_returns = plt.subplot(gs[i, :],
sharex=ax_rolling_returns)
i += 1
if benchmark_rets is not None:
ax_rolling_beta = plt.subplot(gs[i, :], sharex=ax_rolling_returns)
i += 1
ax_rolling_beta = plt.subplot(gs[i, :], sharex=ax_rolling_returns)
i += 1
ax_rolling_volatility = plt.subplot(gs[i, :], sharex=ax_rolling_returns)
i += 1
ax_rolling_sharpe = plt.subplot(gs[i, :], sharex=ax_rolling_returns)
Expand Down Expand Up @@ -614,7 +638,8 @@ def create_returns_tear_sheet(returns, positions=None,
live_start_date=live_start_date,
ax=ax_return_quantiles)

if bootstrap and benchmark_rets is not None:
if ((bootstrap is not None)
and (benchmark_rets is not None)):
ax_bootstrap = plt.subplot(gs[i, :])
plotting.plot_perf_stats(returns, benchmark_rets,
ax=ax_bootstrap)
Expand Down Expand Up @@ -907,7 +932,10 @@ def create_round_trip_tear_sheet(returns, positions, transactions,

@plotting.customize
def create_interesting_times_tear_sheet(
returns, benchmark_rets=None, legend_loc='best', return_fig=False):
returns,
benchmark_rets=utils.NOT_PASSED_SENTINEL,
legend_loc='best',
return_fig=False):
"""
Generate a number of returns plots around interesting points in time,
like the flash crash and 9/11.
Expand All @@ -934,6 +962,11 @@ def create_interesting_times_tear_sheet(
If True, returns the figure that was plotted on.
"""

if (isinstance(benchmark_rets, str)
and benchmark_rets == utils.NOT_PASSED_SENTINEL):
warnings.warn(BENCHMARK_RETS_WARNING)
benchmark_rets = None

rets_interesting = timeseries.extract_interesting_date_ranges(returns)

if not rets_interesting:
Expand Down Expand Up @@ -1094,7 +1127,8 @@ def create_capacity_tear_sheet(returns, positions, transactions,


@plotting.customize
def create_bayesian_tear_sheet(returns, benchmark_rets=None,
def create_bayesian_tear_sheet(returns,
benchmark_rets=utils.NOT_PASSED_SENTINEL,
live_start_date=None, samples=2000,
return_fig=False, stoch_vol=False,
progressbar=True):
Expand Down Expand Up @@ -1126,6 +1160,10 @@ def create_bayesian_tear_sheet(returns, benchmark_rets=None,
progressbar : boolean, optional
If True, show a progress bar
"""
if (isinstance(benchmark_rets, str)
and benchmark_rets == utils.NOT_PASSED_SENTINEL):
warnings.warn(BENCHMARK_RETS_WARNING)
benchmark_rets = None

if not have_bayesian:
raise NotImplementedError(
Expand Down
8 changes: 8 additions & 0 deletions pyfolio/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

from . import pos
from . import txn
from .deprecate import deprecated

APPROX_BDAYS_PER_MONTH = 21
APPROX_BDAYS_PER_YEAR = 252
Expand All @@ -53,6 +54,11 @@
'#008080', '#e6beff', '#aa6e28', '#800000', '#aaffc3',
'#808000', '#ffd8b1', '#000080', '#808080']

DEPRECATION_WARNING = ('register_return_func and get_symbol_rets are '
'deprecated and will be removed in a future version.')

NOT_PASSED_SENTINEL = '__not_passed_by_user'


def one_dec_places(x, pos):
"""
Expand Down Expand Up @@ -436,6 +442,7 @@ def to_series(df):
}


@deprecated(msg=DEPRECATION_WARNING)
def register_return_func(func):
"""
Registers the 'returns_func' that will be called for
Expand All @@ -459,6 +466,7 @@ def register_return_func(func):
SETTINGS['returns_func'] = func


@deprecated(msg=DEPRECATION_WARNING)
def get_symbol_rets(symbol, start=None, end=None):
"""
Calls the currently registered 'returns_func'
Expand Down

0 comments on commit 7d37dc5

Please sign in to comment.