Overview of Quantitative Trading Strategies › Performance Metrics for Strategy Evaluation

Michael BrenndoerferDecember 26, 20257 min read
Quantitative FinanceAll chapters
Part 11 of 15Quantitative Trading Strategies
Chapter 59Overview of Quantitative Trading Strategies › Performance Metrics for Strategy Evaluation
Reading section 5 of 9 · Performance Metrics for Strategy Evaluation · 7 minSection 5 of 9 · 7 min

Book contents

Section 5 of 9 · 7 min read

Overview of Quantitative Trading Strategies › Performance Metrics for Strategy EvaluationOne section · Sections in this chapter5 / 9 ↓
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

Evaluating quantitative strategies requires metrics that capture different dimensions of performance. As we covered in Part IV, Chapter 4, no single number tells the whole story. Each metric highlights a different aspect of risk and return, and together they show strategy quality across those dimensions.

Risk-Adjusted Return Measures

The fundamental challenge in evaluating strategies is that raw returns alone are insufficient. A strategy that returns 20% annually sounds impressive until you learn it has 40% volatility, making large losses extremely likely. Risk-adjusted metrics solve this problem by normalizing returns by the risk taken to achieve them.

The Sharpe ratio remains the most widely used metric, and for good reason: it provides a universal yardstick for comparing strategies regardless of their volatility levels. The formula is elegantly simple:

Sharpe Ratio=E[rp−rf]σp\text{Sharpe Ratio} = \frac{E[r_p - r_f]}{\sigma_p}

Each component has a clear interpretation:

  • rpr_p: portfolio return, the raw performance we observe
  • rfr_f: risk-free rate, the return available from Treasury bills or similar instruments
  • E[rp−rf]E[r_p - r_f]: expected excess return, measuring how much the strategy earns above the risk-free rate on average
  • σp\sigma_p: standard deviation (volatility) of portfolio returns, measuring the typical size of fluctuations

The ratio measures the excess return generated per unit of total risk, allowing comparison between strategies with different volatility profiles. The intuition is straightforward: we care about how much extra return we earn for each unit of uncertainty we bear. An annualized Sharpe ratio above 1.0 is generally considered good, indicating the strategy earns one percentage point of excess return for each percentage point of volatility. A Sharpe above 2.0 is exceptional and rare in practice.

The Sortino ratio addresses a fundamental limitation of the Sharpe ratio. Standard deviation treats upside volatility the same as downside volatility, but you care primarily about losses. A strategy with high volatility driven entirely by large gains is desirable, yet the Sharpe ratio would penalize it. The Sortino ratio corrects this by penalizing only downside volatility:

Sortino Ratio=E[rp−rf]σdown\text{Sortino Ratio} = \frac{E[r_p - r_f]}{\sigma_{\text{down}}}

where:

  • rpr_p: portfolio return
  • rfr_f: risk-free rate
  • σdown\sigma_{\text{down}}: downside deviation (calculated using only returns below a target, typically 0 or rfr_f)

This modification means the Sortino ratio rewards strategies with positively skewed returns, those that have occasional large gains but limited losses. For strategies with asymmetric return distributions, such as those that sell options or take concentrated positions, the Sortino ratio provides a more accurate assessment of risk-adjusted performance.

The Information ratio measures returns relative to a benchmark, making it essential for evaluating you when you are judged against a specific index:

Information Ratio=E[rp−rb]σrp−rb\text{Information Ratio} = \frac{E[r_p - r_b]}{\sigma_{r_p - r_b}}

where:

  • rpr_p: portfolio return
  • rbr_b: benchmark return
  • E[rp−rb]E[r_p - r_b]: expected active return, the average outperformance versus the benchmark
  • σrp−rb\sigma_{r_p - r_b}: tracking error (volatility of active returns), measuring how consistently the strategy beats its benchmark

This metric is central to active management, where the goal is outperforming a specific benchmark. You might have a high Sharpe ratio but a low information ratio if your returns come primarily from market exposure rather than stock selection. The information ratio isolates the value added through active decisions.

In[32]:
Code
def calculate_performance_metrics(
    returns, risk_free_rate=0.0, benchmark_returns=None
):
    """
    Calculate comprehensive performance metrics.

    Parameters:
    - returns: Series of strategy returns
    - risk_free_rate: Annual risk-free rate
    - benchmark_returns: Optional series of benchmark returns
    """
    # Convert annual risk-free to monthly
    rf_monthly = risk_free_rate / 12

    excess_returns = returns - rf_monthly

    # Basic statistics
    mean_return = returns.mean() * 12  # Annualized
    volatility = returns.std() * np.sqrt(12)  # Annualized

    # Sharpe ratio
    sharpe = (mean_return - risk_free_rate) / volatility

    # Sortino ratio
    downside_returns = returns[returns < 0]
    downside_vol = downside_returns.std() * np.sqrt(12)
    sortino = (
        (mean_return - risk_free_rate) / downside_vol
        if downside_vol > 0
        else np.inf
    )

    # Calmar ratio
    dd_stats_inner = calculate_drawdowns(returns)
    calmar = (
        mean_return / abs(dd_stats_inner["max_drawdown"])
        if dd_stats_inner["max_drawdown"] < 0
        else np.inf
    )

    # Win rate
    win_rate = (returns > 0).mean()

    # Profit factor
    gross_profit = returns[returns > 0].sum()
    gross_loss = abs(returns[returns < 0].sum())
    profit_factor = gross_profit / gross_loss if gross_loss > 0 else np.inf

    metrics = {
        "Annual Return": mean_return,
        "Annual Volatility": volatility,
        "Sharpe Ratio": sharpe,
        "Sortino Ratio": sortino,
        "Calmar Ratio": calmar,
        "Max Drawdown": dd_stats_inner["max_drawdown"],
        "Win Rate": win_rate,
        "Profit Factor": profit_factor,
    }

    # Information ratio if benchmark provided
    if benchmark_returns is not None:
        active_returns = returns - benchmark_returns
        tracking_error = active_returns.std() * np.sqrt(12)
        info_ratio = (
            active_returns.mean() * 12 / tracking_error
            if tracking_error > 0
            else np.nan
        )
        metrics["Information Ratio"] = info_ratio
        metrics["Tracking Error"] = tracking_error

    return metrics
In[33]:
Code
metrics = calculate_performance_metrics(strategy_returns, risk_free_rate=0.02)
Out[34]:
Console
Comprehensive Performance Metrics
==================================================
Annual Return: 62.44%
Annual Volatility: 4.64%
Sharpe Ratio: 13.03
Sortino Ratio: inf
Calmar Ratio: inf
Max Drawdown: 0.00%
Win Rate: 100.00%
Profit Factor: inf

The strategy delivers consistent profitability with a high win rate and a profit factor well above 1.0. The Sortino ratio exceeds the Sharpe ratio, indicating that volatility is primarily on the upside rather than downside. This asymmetry suggests the strategy captures gains more often than it experiences losses, a desirable characteristic that the Sharpe ratio alone would not reveal.

Key Parameters

The key parameters for performance evaluation are:

  • risk_free_rate: The theoretical return of an investment with zero risk. Used as the hurdle rate for Sharpe and Sortino ratios. Typically set to the yield on short-term Treasury bills.
  • benchmark_returns: Optional series of returns for a market index. Required to calculate the Information Ratio and Tracking Error. The choice of benchmark matters enormously and should reflect the strategy's investment universe.

Interpreting Multiple Metrics Together

Individual metrics can be misleading. A strategy might have a high Sharpe ratio but unacceptable maximum drawdown, or excellent returns that come with extreme tail risk. You therefore need to consider multiple dimensions simultaneously.

In[35]:
Code
# Prepare data for radar chart
categories = ["Sharpe", "Sortino", "Calmar", "Win Rate", "1-MaxDD", "Return"]
values = [
    min(metrics["Sharpe Ratio"] / 2, 1),  # Normalize Sharpe to [0,1]
    min(metrics["Sortino Ratio"] / 3, 1),  # Normalize Sortino
    min(metrics["Calmar Ratio"] / 2, 1),  # Normalize Calmar
    metrics["Win Rate"],
    1 + metrics["Max Drawdown"],  # Convert to positive
    min(metrics["Annual Return"] / 0.3, 1),  # Normalize return
]
values = [max(0, v) for v in values]  # Ensure non-negative

# Number of variables
N = len(categories)

# Compute angle for each axis
angles = [n / float(N) * 2 * np.pi for n in range(N)]
values += values[:1]  # Complete the loop
angles += angles[:1]
Out[36]:
Visualization
Six-axis radar chart for Sharpe, Sortino, Calmar, win rate, one minus maximum drawdown, and annual return, with the strategy profile lying at or near the outer value of one on every axis.
Radar chart comparing six normalized strategy metrics. Every metric reaches or nearly reaches the normalization ceiling, producing a saturated profile that highlights how clipping can conceal differences between unusually strong backtest statistics.

The radar chart can reveal performance trade-offs at a glance, but this particular profile also shows a limitation of the chosen normalization: nearly every metric is clipped at the upper bound. The resulting balanced shape says more about saturation of the scale than about the absence of trade-offs, so the underlying metric values should still be examined directly.