Overview of Quantitative Trading Strategies › Performance Metrics for Strategy Evaluation
Quantitative FinanceAll chapters 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:
Each component has a clear interpretation:
- : portfolio return, the raw performance we observe
- : risk-free rate, the return available from Treasury bills or similar instruments
- : expected excess return, measuring how much the strategy earns above the risk-free rate on average
- : 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:
where:
- : portfolio return
- : risk-free rate
- : downside deviation (calculated using only returns below a target, typically 0 or )
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:
where:
- : portfolio return
- : benchmark return
- : expected active return, the average outperformance versus the benchmark
- : 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.
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 metricsmetrics = calculate_performance_metrics(strategy_returns, risk_free_rate=0.02)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.
# 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]
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.