Minimum Drawdown
Calculate the weights of the portfolio with the lowest drawdown risk over the historical returns: the lowest Conditional Drawdown at Risk (CDaR, the average of the worst drawdowns), the lowest maximum drawdown or the lowest average drawdown. Drawdowns are what investors feel and what ends strategies, and unlike volatility or CVaR they account for losses that build up over consecutive periods.
The problem is a linear programme over the uncompounded cumulative returns y_t = Σ_(s≤t) r_s’w, with the running peak u_t ≥ y_t, u_t ≥ u_(t-1) from u_0 = 0 and the drawdown u_t - y_t. It minimizes ζ + Σ max(0, u_t - y_t - ζ) / ((1 - α)T) for the CDaR with α the confidence level and T the number of periods, the largest drawdown for the maximum and the average over all periods for the average.
For more information, see the following paper:
- Chekhlov, A., Uryasev, S. and Zabarankin, M. (2005). “Drawdown Measure in Portfolio Optimization.” International Journal of Theoretical and Applied Finance, 8(1), 13-58.
Also known as: minimum CDaR, minimum maximum drawdown, minimum average drawdown, drawdown optimization.
Minimum Drawdown in Python or via the MCP
Install the Finance Toolkit with:
pip install financetoolkit -U
Then call get_minimum_drawdown_weights as shown below.
from financetoolkit import Toolkit
toolkit = Toolkit(
["AAPL", "MSFT", "JNJ", "XOM"],
api_key="FINANCIAL_MODELING_PREP_KEY",
start_date="2021-01-01",
end_date="2025-12-31",
)
toolkit.portfolio.get_minimum_drawdown_weights()
Or, after setting up the Finance Toolkit MCP, ask:
Which weights in Apple, Microsoft, Johnson & Johnson and ExxonMobil minimized the conditional drawdown at risk, based on 2021 to 2025?
Which returns:
| Weight | Expected Return | Volatility | Sharpe Ratio | Risk Contribution | CDaR | |
|---|---|---|---|---|---|---|
| AAPL | 0.152 | 0.1932 | 0.2786 | 0.5725 | 0.2007 | 0.2553 |
| MSFT | 0.1761 | 0.2017 | 0.2571 | 0.6534 | 0.1887 | 0.3173 |
| JNJ | 0.4312 | 0.0987 | 0.1673 | 0.3884 | 0.3284 | 0.1573 |
| XOM | 0.2407 | 0.2911 | 0.2702 | 0.9524 | 0.2822 | 0.178 |
| Portfolio | 1 | 0.1775 | 0.1412 | 1.0185 | 1 | 0.0769 |
Parameters
get_minimum_drawdown_weights accepts the following parameters.
| Parameter | Type | Description | Example |
|---|---|---|---|
measure | str | The drawdown measure to minimize: “cdar”, “maximum” or “average”. Defaults to “cdar”. | "cdar" |
confidence_level | float | The confidence level of the CDaR, e.g. 0.95 for the average of the worst 5% of drawdowns. Defaults to 0.95. | 0.95 |
period | str | The frequency of the returns (daily, weekly, monthly, quarterly or yearly). Defaults to “daily”. | "weekly" |
minimum_weight | float | dict[str, float] | The lowest weight of every asset, or per asset, e.g. {“AAPL”: 0.05} (the others keep 0); a negative value allows short positions. Defaults to 0. | 0 |
maximum_weight | float | dict[str, float] | The highest weight of every asset, or per asset, e.g. {“AAPL”: 0.3} (the others keep 1). Defaults to 1. | 1 |
sector_bounds | dict[str, tuple[float, float]] | None | The minimum and maximum total weight per sector, e.g. {“Technology”: (0, 0.4)}, with the sector of each asset from its company profile. Defaults to None. | None |
country_bounds | dict[str, tuple[float, float]] | None | The minimum and maximum total weight per country, named in full or by its two-letter code, e.g. {“United States”: (0, 0.6)}, with the country of each asset from its company profile. Defaults to None. | None |
net_exposure | float | The sum of the weights: 1 for a fully invested portfolio. A net exposure of 0 is refused, as holding nothing has no drawdown. Defaults to 1. | 1 |
gross_exposure | float | None | The highest sum of the absolute weights, which limits the leverage of a long-short portfolio, e.g. 1.6 for a 130/30 portfolio. Defaults to None, no limit. | None, no limit |
current_weights | dict[str, float] | None | The weights held now, e.g. {“AAPL”: 0.5, “MSFT”: 0.5} (an asset left out is not held), which the turnover is measured from; they add a Turnover column, |w_i - w0_i| per asset and its sum for the portfolio. Defaults to None, which uses the latest weights of the Portfolio the Toolkit came from when a maximum turnover needs them. | None, which uses the latest weights of the Portfolio the Toolkit came from when a maximum turnover needs them |
maximum_turnover | float | None | The highest turnover Σ |w_i - w0_i| from the current weights, e.g. 0.2 to trade at most 10% of the portfolio each way. Defaults to None, no limit. | None, no limit |
include_benchmark | bool | Whether to include the benchmark as an asset. Defaults to False. | True |
rounding | int | None | The number of decimals to round the results to. Defaults to None, which uses the Toolkit’s rounding. | 2 |