Portfolio Performance Measurement › Performance Summary Dashboard

Michael BrenndoerferDecember 16, 20254 min read
Quantitative FinanceAll chapters
Part 9 of 15Portfolio Theory and Investment Analysis
Chapter 49Portfolio Performance Measurement › Performance Summary Dashboard
Reading section 6 of 11 · Performance Summary Dashboard · 4 minSection 6 of 11 · 4 min

Book contents

Section 6 of 11 · 4 min read

Portfolio Performance Measurement › Performance Summary DashboardOne section · Sections in this chapter6 / 11 ↓
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]:
Code
def 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 summary
In[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
Grouped bar chart comparing six metrics (annualized return, annualized volatility, Sharpe ratio, Sortino ratio, maximum drawdown, and Calmar ratio) side by side for the portfolio and benchmark. Numeric labels appear above each bar.
Comparison of key performance metrics for the portfolio and benchmark. The portfolio demonstrates superior risk-adjusted returns (higher Sharpe and Sortino ratios) despite having slightly higher maximum drawdown, illustrating the trade-off between alpha generation and tail risk.