Young’s Calmar ratio and drawdowns#

A drawdown is the fall from the highest wealth reached so far. Terry Young (1991) proposed judging managed-futures funds by their compound annual return divided by their maximum drawdown, the Calmar ratio, because investors leave a fund after a deep drawdown long before its volatility looks alarming. Two strategies with the same mean and volatility can have very different drawdowns if their bad days cluster.

import matplotlib.pyplot as plt
import numpy as np

from financialkit.risk import calmar_ratio, drawdowns
from financialkit.risk.visualizers import plot_drawdowns

rng = np.random.default_rng(3)
n = 3 * 252
mu, sigma = 0.10 / 252, 0.15 / np.sqrt(252)

One path, dissected#

r = mu + sigma * rng.standard_normal(n)
res = drawdowns(r)
axes = plot_drawdowns(res)
axes[0].set_title(f"max drawdown {res.max_drawdown:.1%}, Calmar {calmar_ratio(r):.2f}")
print(f"peak at day {res.peak_index}, trough at day {res.trough_index}, recovered at day {res.recovery_index}")
print(f"longest time under water: {res.longest_duration} days")
max drawdown 10.0%, Calmar 2.36
peak at day 705, trough at day 754, recovered at day -1
longest time under water: 92 days

Same moments, different order#

Sorting the middle year of the same daily returns so the worst come together leaves the mean and volatility unchanged but deepens the drawdown and cuts the Calmar ratio.

clustered = r.copy()
block = slice(n // 3, 2 * n // 3)
clustered[block] = np.sort(clustered[block])
for name, x in [("original order", r), ("clustered losses", clustered)]:
    d = drawdowns(x)
    print(f"{name:16s} mean {x.mean() * 252:.3f}, vol {x.std() * np.sqrt(252):.3f}, MDD {d.max_drawdown:.1%}, Calmar {calmar_ratio(x):.2f}")
original order   mean 0.223, vol 0.149, MDD 10.0%, Calmar 2.36
clustered losses mean 0.223, vol 0.149, MDD 57.4%, Calmar 0.41

The Calmar ratio across many paths#

Over three years the maximum drawdown of a 15%-volatility strategy is itself very uncertain, so a single Calmar ratio says little.

calmars = np.array([calmar_ratio(mu + sigma * rng.standard_normal(n)) for _ in range(500)])
fig, ax = plt.subplots(figsize=(7, 3.5))
ax.hist(np.clip(calmars, -2, 6), bins=50, color="0.6")
ax.set_xlabel("three-year Calmar ratio (same true strategy)")
ax.set_ylabel("count")
fig.tight_layout()

plt.show()
plot 01 calmar drawdowns

Total running time of the script: (0 minutes 0.078 seconds)

Gallery generated by Sphinx-Gallery