Skip to content
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

FIX refactor annual_return to return CAGR. Removes returns_style para… #234

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions pyfolio/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -490,12 +490,12 @@ def show_perf_stats(returns, factor_returns, live_start_date=None):
returns_live = returns[returns.index > live_start_date]

perf_stats_live = np.round(timeseries.perf_stats(
returns_live, returns_style='arithmetic',
returns_live,
factor_returns=factor_returns), 2)
perf_stats_live.columns = ['Out_of_Sample']

perf_stats_all = np.round(timeseries.perf_stats(
returns, returns_style='arithmetic',
returns,
factor_returns=factor_returns), 2)
perf_stats_all.columns = ['All_History']

Expand All @@ -508,7 +508,7 @@ def show_perf_stats(returns, factor_returns, live_start_date=None):
str(int(len(returns_backtest) / APPROX_BDAYS_PER_MONTH)))

perf_stats = np.round(timeseries.perf_stats(
returns_backtest, returns_style='arithmetic',
returns_backtest,
factor_returns=factor_returns), 2)
perf_stats.columns = ['Backtest']

Expand Down Expand Up @@ -1190,7 +1190,7 @@ def plot_slippage_sensitivity(returns, transactions, positions,
for bps in range(1, 100):
adj_returns = txn.adjust_returns_for_slippage(returns, turnover, bps)
avg_returns = timeseries.annual_return(
adj_returns, style='calendar')
adj_returns)
avg_returns_given_slippage.loc[bps] = avg_returns

avg_returns_given_slippage.plot(alpha=1.0, lw=2, ax=ax)
Expand Down
34 changes: 15 additions & 19 deletions pyfolio/tests/test_timeseries.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ def test_get_max_drawdown(
# Need to use isnull because the result can be NaN, NaT, etc.
self.assertTrue(
pd.isnull(peak)) if expected_peak is None else self.assertEqual(
peak,
expected_peak)
peak,
expected_peak)
self.assertTrue(
pd.isnull(valley)) if expected_valley is None else \
self.assertEqual(
Expand Down Expand Up @@ -235,21 +235,20 @@ class TestStats(TestCase):
[10, -10, 10]) / 100. # Ends in drawdown
dt = pd.date_range('2000-1-3', periods=3, freq='D')

px_list_2 = [1.0, 1.2, 1.0, 0.8, 0.7, 0.8, 0.8, 0.8]
dt_2 = pd.date_range('2000-1-3', periods=8, freq='D')

@parameterized.expand([
(simple_rets, 'calendar', utils.DAILY, 0.10584000000000014),
(simple_rets, 'compound', utils.DAILY, 0.16317653888658334),
(simple_rets, 'calendar', utils.DAILY, 0.10584000000000014),
(simple_rets, 'compound', utils.DAILY, 0.16317653888658334),
(simple_week_rets, 'compound', utils.WEEKLY, 0.031682168889005213),
(simple_week_rets, 'calendar', utils.WEEKLY, 0.021840000000000033),
(simple_month_rets, 'compound', utils.MONTHLY, 0.0072238075842128158),
(simple_month_rets, 'calendar', utils.MONTHLY, 0.0050400000000000071)
(simple_rets, utils.DAILY, 0.15500998835658075),
(simple_week_rets, utils.WEEKLY, 0.030183329386562319),
(simple_month_rets, utils.MONTHLY, 0.006885932704891129)
])
def test_annual_ret(self, returns, style, period, expected):
def test_annual_ret(self, returns, period, expected):
self.assertEqual(
timeseries.annual_return(
returns,
style=style, period=period),
period=period
),
expected)

@parameterized.expand([
Expand Down Expand Up @@ -306,16 +305,13 @@ def test_beta(self, returns, benchmark_rets, rolling_window, expected):
expected)

@parameterized.expand([
(pd.Series(px_list,
index=dt), 'calendar', -8.3999999999999559),
(pd.Series(px_list,
index=dt), 'arithmetic', 84.000000000000014)
(pd.Series(px_list_2,
index=dt_2).pct_change().dropna(), -2.3992211554712197)
])
def test_calmar(self, returns, returns_style, expected):
def test_calmar(self, returns, expected):
self.assertEqual(
timeseries.calmar_ratio(
returns,
returns_style=returns_style),
returns),
expected)

@parameterized.expand([
Expand Down
49 changes: 14 additions & 35 deletions pyfolio/timeseries.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,20 +191,14 @@ def max_drawdown(returns):
return -1 * MDD


def annual_return(returns, style='compound', period=DAILY):
def annual_return(returns, period=DAILY):
"""Determines the annual returns of a strategy.

Parameters
----------
returns : pd.Series
Periodic returns of the strategy, noncumulative.
- See full explanation in tears.create_full_tear_sheet.
style : str, optional
- If 'compound', then return will be calculated in geometric
terms: (1+mean(all_daily_returns))^252 - 1.
- If 'calendar', then return will be calculated as
((last_value - start_value)/start_value)/num_of_years.
- Otherwise, return is simply mean(all_daily_returns)*252.
period : str, optional
- defines the periodicity of the 'returns' data for purposes of
annualizing. Can be 'monthly', 'weekly', or 'daily'
Expand All @@ -213,7 +207,7 @@ def annual_return(returns, style='compound', period=DAILY):
Returns
-------
float
Annual returns.
Annual Return as CAGR (Compounded Annual Growth Rate)

"""

Expand All @@ -230,16 +224,15 @@ def annual_return(returns, style='compound', period=DAILY):
)
)

if style == 'calendar':
num_years = len(returns) / ann_factor
df_cum_rets = cum_returns(returns, starting_value=100)
start_value = df_cum_rets[0]
end_value = df_cum_rets[-1]
return ((end_value - start_value) / start_value) / num_years
if style == 'compound':
return pow((1 + returns.mean()), ann_factor) - 1
else:
return returns.mean() * ann_factor
num_years = float(len(returns)) / ann_factor
df_cum_rets = cum_returns(returns, starting_value=100)
start_value = 100
end_value = df_cum_rets[-1]

total_return = (end_value - start_value) / start_value
annual_return = (1. + total_return) ** (1 / num_years) - 1

return annual_return


def annual_volatility(returns, period=DAILY):
Expand Down Expand Up @@ -278,7 +271,7 @@ def annual_volatility(returns, period=DAILY):
return returns.std() * np.sqrt(ann_factor)


def calmar_ratio(returns, returns_style='calendar', period=DAILY):
def calmar_ratio(returns, period=DAILY):
"""
Determines the Calmar ratio, or drawdown ratio, of a strategy.

Expand All @@ -287,8 +280,6 @@ def calmar_ratio(returns, returns_style='calendar', period=DAILY):
returns : pd.Series
Daily returns of the strategy, noncumulative.
- See full explanation in tears.create_full_tear_sheet.
returns_style : str, optional
See annual_returns' style
period : str, optional
- defines the periodicity of the 'returns' data for purposes of
annualizing. Can be 'monthly', 'weekly', or 'daily'
Expand All @@ -309,7 +300,6 @@ def calmar_ratio(returns, returns_style='calendar', period=DAILY):
if temp_max_dd < 0:
temp = annual_return(
returns=returns,
style=returns_style,
period=period
) / abs(max_drawdown(returns=returns))
else:
Expand Down Expand Up @@ -368,8 +358,6 @@ def sortino_ratio(returns, required_return=0, period=DAILY):
returns : pd.Series or pd.DataFrame
Daily returns of the strategy, noncumulative.
- See full explanation in tears.create_full_tear_sheet.
returns_style : str, optional
See annual_returns' style
required_return: float / series
minimum acceptable return
period : str, optional
Expand Down Expand Up @@ -459,8 +447,6 @@ def sharpe_ratio(returns, risk_free=0, period=DAILY):
returns : pd.Series
Daily returns of the strategy, noncumulative.
- See full explanation in tears.create_full_tear_sheet.
returns_style : str, optional
See annual_returns' style
period : str, optional
- defines the periodicity of the 'returns' data for purposes of
annualizing. Can be 'monthly', 'weekly', or 'daily'
Expand Down Expand Up @@ -728,7 +714,6 @@ def calc_alpha_beta(returns, factor_returns):

def perf_stats(
returns,
returns_style='compound',
return_as_dict=False,
factor_returns=None,
period=DAILY):
Expand All @@ -740,8 +725,6 @@ def perf_stats(
returns : pd.Series
Daily returns of the strategy, noncumulative.
- See full explanation in tears.create_full_tear_sheet.
returns_style : str, optional
See annual_returns' style
return_as_dict : boolean, optional
If True, returns the computed metrics in a dictionary.
period : str, optional
Expand All @@ -761,15 +744,11 @@ def perf_stats(
"""

all_stats = OrderedDict()
all_stats['annual_return'] = annual_return(
returns,
style=returns_style, period=period)
all_stats['annual_return'] = annual_return(returns, period=period)
all_stats['annual_volatility'] = annual_volatility(returns, period=period)
all_stats['sharpe_ratio'] = sharpe_ratio(
returns)
all_stats['calmar_ratio'] = calmar_ratio(
returns,
returns_style=returns_style, period=period)
all_stats['calmar_ratio'] = calmar_ratio(returns, period=period)
all_stats['stability'] = stability_of_timeseries(returns)
all_stats['max_drawdown'] = max_drawdown(returns)
all_stats['omega_ratio'] = omega_ratio(returns)
Expand Down