Ulcer Index (UI)
The Ulcer Index is a financial metric used to assess the risk and volatility of an investment portfolio or asset. Developed by Peter Martin in the 1980s, the Ulcer Index is particularly useful for evaluating the downside risk and drawdowns associated with investments.
The Ulcer Index differs from traditional volatility measures like standard deviation or variance because it focuses on the depth and duration of drawdowns rather than the dispersion of returns.
The formula is as follows (with the default rolling=14):
\[\text{Ulcer Index} = \sqrt{\text{Σ} ((\operatorname{Wealth}(t) - \text{Highest High} (t)) / \text{Highest High} (t)) ^{2} / n}\]Where Wealth(t) compounds the daily returns within each period from a starting value of 1, Highest High(t) is the highest Wealth over the trailing 14 observations (the starting value of 1 included while fewer are available) and n is the number of daily returns in the period.
Martin measures every drawdown from the highest value so far (rolling=None); the default trailing 14-period high is the variant StockCharts popularised. While fewer than rolling observations are available, the high is taken over the observations so far, so periods shorter than the window still have a value. The result is a decimal (0.05 is a root mean squared drawdown of 5%), where Martin quotes percentages.
The Ulcer Index punishes both the depth and the length of drawdowns, so an asset that falls 10% and stays there for months scores worse than one that drops 10% and recovers within a week, which a volatility figure would not show. It is the risk measure behind the Martin ratio (excess return divided by the Ulcer Index) and is read best relative to peers over the same window, since the default 14-period lookback gives lower values than a since-inception high-water mark.
Also known as: UI, drawdown risk.
Ulcer Index (UI) in Python or via the MCP
Install the Finance Toolkit with:
pip install financetoolkit -U
Then call get_ulcer_index as shown below.
from financetoolkit import Toolkit
toolkit = Toolkit(
["BA", "AIR.PA"],
api_key="FINANCIAL_MODELING_PREP_KEY",
start_date="2021-01-01",
end_date="2025-12-31",
)
toolkit.risk.get_ulcer_index()
Or, after setting up the Finance Toolkit MCP, ask:
What was the Ulcer Index of Boeing, Airbus and the S&P 500 in each year from 2021 to 2025?
Which returns:
| BA | AIR.PA | Benchmark | |
|---|---|---|---|
| 2021 | 0.0555 | 0.0408 | 0.0129 |
| 2022 | 0.0901 | 0.0606 | 0.0477 |
| 2023 | 0.0433 | 0.0288 | 0.0194 |
| 2024 | 0.0686 | 0.0423 | 0.0162 |
| 2025 | 0.0635 | 0.0499 | 0.0267 |
Parameters
get_ulcer_index accepts the following parameters.
| Parameter | Type | Description | Example |
|---|---|---|---|
period | str | The data frequency for returns (daily, weekly, monthly, quarterly, or yearly). Defaults to “quarterly” if the Toolkit is initialised with quarterly=True, otherwise “yearly”. | "quarterly" |
rolling | int | None | The trailing lookback window used as the high-water mark reference for each day’s drawdown. Pass None for an expanding (since-inception) high-water mark instead – Martin’s “Highest High”; a fixed int window is the common trailing-high variant (e.g. a 14-day trailing high). Defaults to 14. | 20 |
rounding | int | None | The number of decimals to round the results to. Defaults to None, which uses the rounding of the Toolkit (4 unless set otherwise). | 2 |
growth | bool | Whether to calculate the growth of the UI values over time. Defaults to False. | True |
lag | int | list[int] | The lag to use for the growth calculation. Defaults to 1. | 4 |
standardize | bool | Whether to standardize (Z-Score) the result. When combined with growth=True, standardizes the growth values instead of the raw values. Defaults to False. | True |