Skip to content

Average Drawdown

Mean drawdown over the rolling window — the typical drawdown depth during the period. Scans the trailing window, tracks the running peak inside the window, and reports the mean of all bar-by-bar drawdowns. Equivalent to the Pain Index (see PainIndex).

Quick reference

ItemValue
FamilyRisk / Performance
Input typef64 — one equity-curve sample per update
Output typef64
Output range[0, 1] (fraction of peak)
Default parametersperiod required
Warmup periodperiod
InterpretationAverage drawdown depth over window — "typical pain"

Formula

peak_t   = running max over window up to t
dd_t     = (peak_t - equity_t) / peak_t       (0 if no drawdown)
AvgDD    = mean(dd_t over window)

See crates/wickra-core/src/indicators/average_drawdown.rs.

Parameters

NameTypeDefaultConstraintDescription
periodusizenone> 0Rolling window length.

Inputs / Outputs

Indicator<Input = f64, Output = f64>.

Warmup

warmup_period() == period.

Edge cases

  • Same metric as PainIndex. This indicator and PainIndex emit identical values — they exist as separate types for naming convenience.
  • Monotonically rising equity. AvgDD = 0 throughout.
  • Reset. Clears the rolling window.

Examples

Rust

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

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let equity = vec![100.0, 110.0, 100.0, 95.0, 88.0, 90.0, 92.0, 95.0, 100.0, 105.0];
    let mut adv = AverageDrawdown::new(10)?;
    println!("{:?}", adv.batch(&equity).last());
    Ok(())
}

Python

python
import numpy as np
import wickra as ta

equity = np.array([100, 110, 100, 95, 88, 90, 92, 95, 100, 105], dtype=float)
adv = ta.AverageDrawdown(10)
print(adv.batch(equity)[-1])

Node

javascript
const wickra = require('wickra');
const adv = new wickra.AverageDrawdown(10);
console.log(adv.batch([100, 110, 100, 95, 88, 90, 92, 95, 100, 105]));

Streaming

rust
use wickra::{AverageDrawdown, Indicator};

let mut adv = AverageDrawdown::new(252).unwrap();
let equity_stream: Vec<f64> = Vec::new(); // your equity-curve feed
for equity in equity_stream {
    if let Some(v) = adv.update(equity) {
        // v is the typical drawdown depth over last year
    }
}

Interpretation

  • Typical pain. Where MaxDrawdown reports worst case, AvgDD reports the typical experience. A strategy can have low MaxDD but high AvgDD if it spends a lot of time under water.
  • Vs UlcerIndex. UlcerIndex squares drawdowns before averaging — punishes deep drawdowns disproportionately. AvgDD = PainIndex weights all drawdowns equally.
  • Reporting. Often paired with MaxDD: "worst 20% / typical 5%" tells a more complete risk story than either alone.

Common pitfalls

  • Confusing with MaxDrawdown. Average ≠ maximum. Two strategies with the same MaxDD can have very different AvgDDs.
  • Period choice. Same as MaxDrawdown — short windows forget past drawdowns.

References

  • Same as PainIndex — Thomas Becker's pain measure.

See also