Skip to content

SterlingRatio ​

Return per unit of typical pain — mean return over the average depth of the drawdown episodes of the compounded equity curve.

Quick reference ​

FieldValue
FamilyRisk / Performance
Input typef64 (per-period returns)
Output typef64
Output rangeunbounded (negative for net-losing windows)
Default parameters(period = 12) (Python)
Warmup periodperiod
InterpretationHigher = more return per average drawdown episode.

Formula ​

equity_t  = Π_{i<=t} (1 + return_i)          (compounded curve)
peak_t    = max_{s<=t} equity_s
episode   = a stretch below the running peak, closed by a full recovery
D_j       = (peak_j − trough_j) / peak_j      (depth of episode j)
Sterling  = mean(returns) / mean(D_j)

The Sterling Ratio divides the average per-period return by the average depth of the drawdown episodes in the window (Bacon's form of Deane Sterling Jones' ratio, with every episode averaged). An episode opens when equity falls below the running peak and closes when equity regains that peak; its depth is the trough measured against that peak. An episode still open at the window's right edge is booked at its current trough. Each episode counts once, however many bars it lasts — averaging every bar's drawdown instead would be the PainIndex. Of Wickra's three drawdown-based ratios it is the gentlest on outliers: averaging episode depths means one deep crater does not dominate, unlike the BurkeRatio (root of the summed squared episode depths) or the MartinRatio (which uses the root-mean-square percentage drawdown). A window that never draws down has no episodes and reports 0.0. Source: crates/wickra-core/src/indicators/sterling_ratio.rs.

Parameters ​

NameTypeDefaultValid rangeSourceDescription
periodusize12 (Python)>= 2sterling_ratio.rs:55Window of returns (e.g. 36 months). < 2 errors with Error::InvalidPeriod.

The period getter returns the window.

Inputs / Outputs ​

From crates/wickra-core/src/indicators/sterling_ratio.rs:

rust
use wickra::{Indicator, SterlingRatio};
// SterlingRatio: Input = f64, Output = f64
const _: fn(&mut SterlingRatio, f64) -> Option<f64> = <SterlingRatio as Indicator>::update;

An f64 return in, an Option<f64> out. Python update(ret) / batch(returns) (NaN warmup); Node update(ret) / batch(returns[]) (null warmup).

Warmup ​

warmup_period() == period. The first value lands once period returns are seen (reference_value exercises the emission at index period − 1).

Edge cases ​

  • Reference value. [0.1, −0.1, 0.1] → equity 1.1, 0.99, 1.089; the peak stays 1.1, so there is one episode, still open at the window edge, booked at its trough: depth (1.1 − 0.99) / 1.1 = 0.1. Sterling = (0.1/3) / 0.1 = 1/3 (reference_value pins this).
  • Open episode. An episode that has not recovered by the window's last bar is booked at its trough so far, not dropped.
  • No drawdown. A monotonically rising window reports 0.0 (no_drawdown_is_zero pins this).
  • Losing window. A net-losing window gives a negative ratio (losing_window_is_negative pins this).
  • Non-finite input. A NaN/∞ return is skipped (ignores_non_finite_input).
  • Reset. sr.reset() clears the window (reset_clears_state pins this).

Examples ​

Rust ​

rust
use wickra::{BatchExt, Indicator, SterlingRatio};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut sr = SterlingRatio::new(3)?;
    let out = sr.batch(&[0.1, -0.1, 0.1]);
    println!("{:?}", out[2]); // Some(0.3333...)
    Ok(())
}

Output:

Some(0.3333333333333334)

Python ​

python
import numpy as np
import wickra as ta

sr = ta.SterlingRatio(36)
returns = np.random.randn(60) * 0.05
print(sr.batch(returns)[-1])

Node ​

javascript
const ta = require('wickra');
const sr = new ta.SterlingRatio(3);
console.log(sr.batch([0.1, -0.1, 0.1]).at(-1)); // ~0.3333

Streaming ​

rust
use wickra::{Indicator, SterlingRatio};

let mut sr = SterlingRatio::new(36).unwrap();
let monthly_returns: Vec<f64> = Vec::new(); // your live stream
for r in monthly_returns {
    if let Some(ratio) = sr.update(r) {
        // higher ratio -> more return per average drawdown episode
    }
}

Streaming update and batch are equivalent tick-for-tick (batch_equals_streaming pins this).

Interpretation ​

  1. Smoothness reward. The Sterling Ratio favours strategies whose equity grinds higher with shallow, frequent dips over those with rare but brutal crashes.
  2. The drawdown family. Read it next to the BurkeRatio and MartinRatio: a high Sterling but low Burke flags that the average drawdown is benign while a few are severe.
  3. Classic definition note. The original Sterling Ratio used the average of the largest annual drawdowns; this rolling form averages the depth of every drawdown episode in the window (Bacon's variant) — comparable in spirit, simpler to stream. Averaging every bar under water would be the Pain Index instead.

Common pitfalls ​

  • No-drawdown anomaly. A window that only rises reports 0.0 (undefined), not infinity.
  • Frequency. mean(returns) is per-period — annualise consistently if you quote an annualised Sterling Ratio.
  • Outlier blindness. Because it averages episode depths, a single catastrophic drawdown is under-weighted — pair with Burke/Martin to catch tail risk.
  • Episodes, not bars. A long, shallow episode and a one-bar dip of the same depth weigh the same; duration is not penalised (see the Pain Index / Ulcer Index for time-under-water measures).

References ​

Deane Sterling Jones; popularised via Kestner, L. N., Quantitative Trading Strategies (2003) — the Sterling Ratio. Bacon, C. R., Practical Portfolio Performance Measurement and Attribution (2nd ed., 2008) — the average-drawdown (episode) form.

See also ​