Performance & benchmark

Headline return-stream metrics and benchmark-relative statistics. These code cells are executed live by Quarto every time the docs are built.

Real return streams

We use qrt’s bundled sample datasets — AAPL as the “strategy” and SPY as the benchmark — loaded offline via q.data.datasets.load, no network dependency:

import pandas as pd

import qrt as q

aapl = q.data.datasets.load("aapl")
spy = q.data.datasets.load("spy")

returns = pd.concat(
    {
        "AAPL": aapl["close"].pct_change(),
        "SPY": spy["close"].pct_change(),
    },
    axis=1,
).dropna()
strategy = returns["AAPL"]
benchmark = returns["SPY"]

returns.tail()
AAPL SPY
datetime
2026-07-17 0.001440 -0.009897
2026-07-20 -0.021424 -0.001614
2026-07-21 0.003521 0.008341
2026-07-22 -0.005645 -0.001163
2026-07-23 -0.012980 -0.012349

Headline performance

q.stats.performance returns a pandas Series of Total Return, CAGR, Volatility, Sharpe, Sortino, Calmar, Max Drawdown, Win Rate, and Periods. The annualization frequency is inferred from the index (252 for this daily series) unless periods_per_year is given explicitly. Win Rate excludes exact-zero-return periods from both the numerator and denominator:

q.stats.performance(strategy)
Total Return     382.968908
CAGR               0.251801
Volatility         0.383015
Sharpe             0.785685
Sortino            1.122999
Calmar             0.307819
Max Drawdown      -0.818014
Win Rate           0.525658
Periods         6677.000000
Name: AAPL, dtype: float64

CAGR, Sharpe, Sortino, and Calmar all accept an optional annualized risk-free rate rf (deannualized internally and subtracted from returns before the ratio is computed). This lowers the ratios versus the rf=0.0 default above:

q.stats.performance(strategy, rf=0.02)[["CAGR", "Sharpe", "Sortino", "Calmar"]]
CAGR       0.227268
Sharpe     0.733980
Sortino    1.046929
Calmar     0.277828
Name: AAPL, dtype: float64

Benchmark-relative: alpha and beta

q.stats.beta measures sensitivity to the benchmark; q.stats.alpha is the annualized Jensen alpha unexplained by that beta exposure. q.stats.benchmark_stats combines both with active return, correlation, tracking error, and information ratio:

print(f"Beta: {q.stats.beta(strategy, benchmark):.2f}")
print(f"Alpha: {q.stats.alpha(strategy, benchmark):.2%}")

q.stats.benchmark_stats(strategy, benchmark)
Beta: 1.13
Alpha: 19.07%
Strategy Total Return      382.968908
Benchmark Total Return       7.100051
Active Return               46.403271
Beta                         1.129821
Alpha                        0.190685
Correlation                  0.569089
Tracking Error               0.315938
Information Ratio            0.643647
Periods                   6677.000000
Name: AAPL vs SPY, dtype: float64
Back to top