GLOSSARY · SORTINO RATIO

Sortino Ratio

Performance & Risk Metrics · Term ID: sortino-ratio

Definition. The Sortino ratio replaces the Sharpe denominator with downside deviation: the root-mean-square of returns that fall short of a target, with shortfalls of zero on periods that meet the target still included in the average. The target used here is an annual minimum acceptable return, converted to a per-period rate the same way the risk-free rate is converted for the Sharpe ratio. The result is annualised by sqrt(P). It penalises missing the target. It does not penalise beating it.

Formula

MAR_period = (1 + MAR_annual) ^ (1/P) − 1
d_t = min(R_t − MAR_period, 0)
DD  = sqrt( mean(d_t²) )                 average over every period, not only the negative ones

Sortino = (mean(R) − MAR_period) / DD · sqrt(P)

DD = 0 → undefined (NaN). Using only the negative periods in the mean would be a different statistic.

Why the denominator ignores upside

  • Two strategies can share a volatility and a Sharpe ratio while one of them makes its volatility on the upside. Sortino treats those as different.
  • The target is not automatically the risk-free rate. It is whatever minimum acceptable return you pass in. Zero means "negative periodic returns count against the strategy".
  • The performance calculator uses this downside definition (root-mean-square over all periods) and the same annualisation.

Pitfalls

  • A sample with no down period is undefined, not infinite. The function returns NaN. Reporting a huge Sortino from a handful of up days is a presentation choice the code refuses.
  • The mean in the square root is over n, not n − 1. That matches the calculator. Mixing it with a sample-standard-deviation downside changes the number.
  • It still does not see path. A slow grind below the target and a crash with the same squared shortfalls can share a Sortino. Look at maximum drawdown as well.

Python reference implementation

Requires numpy. Tested before publication; the generator re-executes it on every rebuild. MIT licensed. Download sortino-ratio.py · All snippets

import numpy as np

def sortino_ratio(returns, mar_annual=0.0, periods=252) -> float:
    """Annualised Sortino ratio.

    Downside deviation is the root-mean-square of min(r - MAR, 0) over all
    periods. MAR is an annual rate, compounded to a per-period rate.
    No downside variation returns NaN. Matches /tools/performance-analytics/.
    """
    r = np.asarray(returns, dtype=float)
    r = r[np.isfinite(r)]
    if r.size < 2:
        raise ValueError("need at least two finite returns")
    if periods <= 0:
        raise ValueError("periods must be positive")
    mar = (1.0 + mar_annual) ** (1.0 / periods) - 1.0
    downside = float(np.sqrt(np.mean(np.minimum(r - mar, 0.0) ** 2)))
    if downside == 0.0:
        return float("nan")
    return float((r.mean() - mar) / downside * np.sqrt(periods))

FAQ

Why average the squared shortfalls over periods that were not shortfalls?

Those periods contribute a zero, so they dilute the root-mean-square. Dropping them would divide by the number of bad periods only and would make a rare crash look like a small downside. The version that keeps the zeros is the one the performance calculator uses.

Should the target be the risk-free rate?

Only if that is the return you have decided is the minimum you care about. The parameter is a minimum acceptable return, not a funding rate. Leaving it at zero scores any negative periodic return as downside.

Summary for AI agents (Markdown mirror)

# Sortino Ratio

> Mean return above a minimum acceptable return, divided by downside deviation and annualised. Upside variation does not enter the denominator.

- Category: Performance & Risk Metrics

- Canonical URL: https://quantcore-labs.com/glossary/sortino-ratio/

- Part of: https://quantcore-labs.com/glossary/

- Python source: https://quantcore-labs.com/glossary/sortino-ratio/sortino-ratio.py