Portfolio Performance Measurement › Performance Summary Dashboard
Michael BrenndoerferDecember 16, 20254 min read
Quantitative FinanceAll chapters Part 9 of 15Portfolio Theory and Investment Analysis
Portfolio Performance Measurement › Performance Summary DashboardOne section · Sections in this chapter6 / 11 ↓
Sections in this chapter11
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
Let's consolidate all metrics into a performance summary, which is standard practice for portfolio reporting.
In[25]:
Codedef performance_summary(
returns,
benchmark_returns=None,
risk_free_rate=0.0,
periods_per_year=252,
name="Portfolio",
):
"""
Generate a comprehensive performance summary.
Parameters:
-----------
returns : array-like
Portfolio periodic returns
benchmark_returns : array-like, optional
Benchmark periodic returns for relative metrics
risk_free_rate : float
Periodic risk-free rate
periods_per_year : int
Number of periods per year
name : str
Name for the summary
Returns:
--------
dict : Performance metrics
"""
returns = np.array(returns)
cumulative = (1 + returns).cumprod()
# Basic return metrics
total_return = cumulative[-1] - 1
n_years = len(returns) / periods_per_year
annualized_return = (1 + total_return) ** (1 / n_years) - 1
annualized_vol = np.std(returns, ddof=1) * np.sqrt(periods_per_year)
# Risk-adjusted metrics
excess_returns = returns - risk_free_rate
sharpe = (
np.mean(excess_returns)
/ np.std(excess_returns, ddof=1)
* np.sqrt(periods_per_year)
)
# Downside metrics
downside_returns = np.minimum(returns - risk_free_rate, 0)
downside_dev = np.sqrt(np.mean(downside_returns**2)) * np.sqrt(
periods_per_year
)
sortino = (
np.mean(excess_returns) * periods_per_year / downside_dev
if downside_dev > 0
else np.nan
)
# Drawdown metrics
running_max = np.maximum.accumulate(cumulative)
drawdowns = (running_max - cumulative) / running_max
max_dd = np.max(drawdowns)
calmar = annualized_return / max_dd if max_dd > 0 else np.nan
summary = {
"Name": name,
"Total Return": total_return,
"Annualized Return": annualized_return,
"Annualized Volatility": annualized_vol,
"Sharpe Ratio": sharpe,
"Sortino Ratio": sortino,
"Maximum Drawdown": max_dd,
"Calmar Ratio": calmar,
}
# Add benchmark-relative metrics if benchmark provided
if benchmark_returns is not None:
benchmark_returns = np.array(benchmark_returns)
active_returns = returns - benchmark_returns
alpha = np.mean(active_returns) * periods_per_year
tracking_error = np.std(active_returns, ddof=1) * np.sqrt(
periods_per_year
)
info_ratio = alpha / tracking_error if tracking_error > 0 else np.nan
summary["Alpha"] = alpha
summary["Tracking Error"] = tracking_error
summary["Information Ratio"] = info_ratio
return summaryIn[26]:
Code# Generate summaries
portfolio_summary = performance_summary(
df["portfolio"], df["benchmark"], daily_rf, 252, "Portfolio"
)
benchmark_summary = performance_summary(
df["benchmark"], None, daily_rf, 252, "Benchmark"
)Out[27]:
Console============================================================ PORTFOLIO PERFORMANCE SUMMARY ============================================================ Total Return : 178.78% Annualized Return : 22.76% Annualized Volatility : 16.55% Sharpe Ratio : 1.080 Sortino Ratio : 1.649 Maximum Drawdown : 19.65% Calmar Ratio : 1.158 Alpha : 4.20% Tracking Error : 4.89% Information Ratio : 0.858 ============================================================ BENCHMARK PERFORMANCE SUMMARY ============================================================ Total Return : 127.34% Annualized Return : 17.85% Annualized Volatility : 15.83% Sharpe Ratio : 0.864 Sortino Ratio : 1.292 Maximum Drawdown : 21.30% Calmar Ratio : 0.838
This summary reports both absolute and relative performance. The portfolio demonstrates modest alpha generation with controlled tracking error, resulting in a positive information ratio.
Out[28]:
Visualization