Skip to content

Scorecard omits tail risk — no CVaR, tail ratio, skew/kurtosis, or drawdown recovery time #12

Description

@algotester123

Problem

summarize() reports CAGR, Sharpe, Sortino, max drawdown, Calmar, annualised vol, and worst month. Every one of those is either a mean-variance summary or a single worst-point statistic.

Nothing describes the shape of the loss tail: no CVaR/expected shortfall, no tail ratio, no skew or kurtosis, no time-to-recover.

Why it matters

Two strategies can share a Sharpe and a max drawdown while having completely different risk of ruin. The classic failure mode — small steady gains punctuated by rare severe losses — is invisible to every metric currently reported, and it is exactly the profile a naive backtest makes look attractive.

There's a concrete internal reason too: deflated_sharpe.py already computes skewness and kurtosis (_moments()), because the PSR/DSR formulas need them — negative skew and fat tails make a given Sharpe less trustworthy. Those moments are calculated and then discarded rather than surfaced to the user. The framework is already telling itself the distribution is non-normal but not telling the reader.

Max drawdown also says nothing about duration: a −20% drawdown recovered in two months and one that takes four years are not the same risk.

Proposed fix

Add to metrics.py and include in summarize():

  • cvar(returns, alpha=0.05) — mean of the worst alpha tail (expected shortfall)
  • tail_ratio(returns) — |95th percentile| / |5th percentile|; below ~1 means losses outweigh gains at the extremes
  • skewness / kurtosis — reuse the moment code already in deflated_sharpe._moments
  • max_drawdown_duration(returns) — longest peak-to-recovery span in days
  • avg_recovery_time(returns)

Acceptance criteria

  • Hand-computed values verified on small fixtures
  • A symmetric-normal synthetic yields skew ≈ 0, tail ratio ≈ 1
  • A negatively-skewed synthetic is correctly flagged
  • Duration metrics handle a series that never recovers (return inf or the censored length, documented)
  • Moment code shared with deflated_sharpe, not duplicated

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions