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 drawdown behavior #297

Merged
merged 3 commits into from
May 11, 2016
Merged
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
65 changes: 65 additions & 0 deletions pyfolio/tests/test_timeseries.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,71 @@


class TestDrawdown(TestCase):
drawdown_list = np.array(
[100, 90, 75]
) / 10.
dt = pd.date_range('2000-1-3', periods=3, freq='D')

drawdown_serie = pd.Series(drawdown_list, index=dt)

@parameterized.expand([
(drawdown_serie,)
])
def test_get_max_drawdown_begins_first_day(self, px):
rets = px.pct_change()
drawdowns = timeseries.gen_drawdown_table(rets, top=1)
self.assertEqual(drawdowns.loc[0, 'net drawdown in %'], 25)

drawdown_list = np.array(
[100, 110, 120, 150, 180, 200, 100, 120,
160, 180, 200, 300, 400, 500, 600, 800,
900, 1000, 650, 600]
) / 10.
dt = pd.date_range('2000-1-3', periods=20, freq='D')

drawdown_serie = pd.Series(drawdown_list, index=dt)

@parameterized.expand([
(drawdown_serie,
pd.Timestamp('2000-01-08'),
pd.Timestamp('2000-01-09'),
pd.Timestamp('2000-01-13'),
50,
pd.Timestamp('2000-01-20'),
pd.Timestamp('2000-01-22'),
None,
40
)
])
def test_gen_drawdown_table_relative(
self, px,
first_expected_peak, first_expected_valley,
first_expected_recovery, first_net_drawdown,
second_expected_peak, second_expected_valley,
second_expected_recovery, second_net_drawdown
):

rets = px.pct_change()

drawdowns = timeseries.gen_drawdown_table(rets, top=2)

self.assertEqual(np.round(drawdowns.loc[0, 'net drawdown in %']),
first_net_drawdown)
self.assertEqual(drawdowns.loc[0, 'peak date'],
first_expected_peak)
self.assertEqual(drawdowns.loc[0, 'valley date'],
first_expected_valley)
self.assertEqual(drawdowns.loc[0, 'recovery date'],
first_expected_recovery)

self.assertEqual(np.round(drawdowns.loc[1, 'net drawdown in %']),
second_net_drawdown)
self.assertEqual(drawdowns.loc[1, 'peak date'],
second_expected_peak)
self.assertEqual(drawdowns.loc[1, 'valley date'],
second_expected_valley)
self.assertTrue(pd.isnull(drawdowns.loc[1, 'recovery date']))

px_list_1 = np.array(
[100, 120, 100, 80, 70, 110, 180, 150]) / 100. # Simple
px_list_2 = np.array(
Expand Down
17 changes: 6 additions & 11 deletions pyfolio/timeseries.py
Original file line number Diff line number Diff line change
Expand Up @@ -938,7 +938,7 @@ def get_max_drawdown_underwater(underwater):

"""

valley = np.argmax(underwater) # end of the period
valley = np.argmin(underwater) # end of the period
# Find first 0
peak = underwater[:valley][underwater[:valley] == 0].index[-1]
# Find last 0
Expand Down Expand Up @@ -976,7 +976,7 @@ def get_max_drawdown(returns):
returns = returns.copy()
df_cum = cum_returns(returns, 1.0)
running_max = np.maximum.accumulate(df_cum)
underwater = (running_max - df_cum) / running_max
underwater = df_cum / running_max - 1
return get_max_drawdown_underwater(underwater)


Expand All @@ -1001,7 +1001,7 @@ def get_top_drawdowns(returns, top=10):
returns = returns.copy()
df_cum = cum_returns(returns, 1.0)
running_max = np.maximum.accumulate(df_cum)
underwater = running_max - df_cum
underwater = df_cum / running_max - 1

drawdowns = []
for t in range(top):
Expand Down Expand Up @@ -1067,15 +1067,10 @@ def gen_drawdown_table(returns, top=10):
df_drawdowns.loc[i, 'net drawdown in %'] = (
(df_cum.loc[peak] - df_cum.loc[valley]) / df_cum.loc[peak]) * 100

df_drawdowns['peak date'] = pd.to_datetime(
df_drawdowns['peak date'],
unit='D')
df_drawdowns['valley date'] = pd.to_datetime(
df_drawdowns['valley date'],
unit='D')
df_drawdowns['peak date'] = pd.to_datetime(df_drawdowns['peak date'])
df_drawdowns['valley date'] = pd.to_datetime(df_drawdowns['valley date'])
df_drawdowns['recovery date'] = pd.to_datetime(
df_drawdowns['recovery date'],
unit='D')
df_drawdowns['recovery date'])

return df_drawdowns

Expand Down