Skip to content

Commit 0cf6974

Browse files
authored
Merge pull request #601 from zorionarrillaga/docs/max-drawdown-return-convention
docs: get_max_drawdown returns a positive fraction, not a negative percent
2 parents 75dfc95 + 9679223 commit 0cf6974

2 files changed

Lines changed: 34 additions & 3 deletions

File tree

investing_algorithm_framework/services/metrics/drawdown.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,18 +56,20 @@ def get_drawdown_series(snapshots: List[PortfolioSnapshot]) -> List[Tuple[float,
5656

5757
def get_max_drawdown(snapshots: List[PortfolioSnapshot]) -> float:
5858
"""
59-
Calculate the maximum drawdown of the portfolio as a percentage from the peak.
59+
Calculate the maximum drawdown of the portfolio as a fraction of the peak.
6060
6161
Max Drawdown is the maximum observed loss from a peak to a
6262
trough before a new peak is achieved.
6363
64-
It is expressed here as a negative percentage.
64+
It is expressed here as a positive fraction, matching
65+
:func:`get_twr_max_drawdown`.
6566
6667
Args:
6768
snapshots (List[PortfolioSnapshot]): List of portfolio snapshots
6869
6970
Returns:
70-
float: The maximum drawdown as a negative percentage (e.g., -12.5 for a 12.5% drawdown).
71+
float: The maximum drawdown as a positive fraction
72+
(e.g., ``0.125`` for a 12.5% peak-to-trough decline).
7173
"""
7274
equity_curve = get_equity_curve(snapshots)
7375

tests/services/metrics/test_drawdowns.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,3 +387,32 @@ def test_drawdown_series_timestamps_match_snapshots(self):
387387

388388
for (_, ts), expected_ts in zip(drawdown_series, timestamps):
389389
self.assertEqual(ts, expected_ts)
390+
391+
392+
class TestMaxDrawdownReturnConvention(unittest.TestCase):
393+
"""Pin the documented return convention of ``get_max_drawdown``.
394+
395+
The docstring's own worked example is the thing that drifted: it promised
396+
``-12.5`` for a 12.5% drawdown while the function returns ``abs()`` of a
397+
``(equity - peak) / peak`` fraction. These assertions fail if either the
398+
sign or the scale changes again.
399+
"""
400+
401+
def test_returns_a_positive_fraction_not_a_negative_percent(self):
402+
snapshots = _make_snapshots(
403+
[datetime(2024, 1, 1), datetime(2024, 1, 2)], [100.0, 87.5]
404+
)
405+
self.assertAlmostEqual(get_max_drawdown(snapshots), 0.125)
406+
407+
def test_scale_matches_get_twr_max_drawdown(self):
408+
# Both are documented as fractions; a 30% decline is 0.3, not 30.0.
409+
snapshots = _make_snapshots(
410+
[datetime(2024, 1, 1), datetime(2024, 1, 2)], [200.0, 140.0]
411+
)
412+
self.assertAlmostEqual(get_max_drawdown(snapshots), 0.3)
413+
414+
def test_no_drawdown_is_zero(self):
415+
snapshots = _make_snapshots(
416+
[datetime(2024, 1, 1), datetime(2024, 1, 2)], [100.0, 110.0]
417+
)
418+
self.assertEqual(get_max_drawdown(snapshots), 0.0)

0 commit comments

Comments
 (0)