performance¶
Functions for evaluating risk-adjusted portfolio performance. These metrics measure how efficiently a portfolio converts risk into return using different definitions of risk, including total volatility, downside risk, drawdowns, and benchmark-relative performance.
performance ¶
Risk-adjusted performance ratios.
As in the rest of PortPy, rf/mar/threshold parameters are annual rates, de-annualized internally via geometric compounding.
sharpe_ratio ¶
sharpe_ratio(
returns: Series,
rf: float = DEFAULT_RISK_FREE_RATE,
annualized: bool = True,
periods_per_year: int = TRADING_DAYS_PER_YEAR,
as_result: bool = False,
) -> float | MetricResult
Mean excess return over its own standard deviation - the classic risk-adjusted return measure.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
Series
|
Periodic return series. |
required |
rf
|
float
|
Annual risk-free rate, de-annualized internally to a constant per-period rate. |
DEFAULT_RISK_FREE_RATE
|
annualized
|
bool
|
If True, scale the result by sqrt(periods_per_year). |
True
|
periods_per_year
|
int
|
Number of periods per year, used for annualization. |
TRADING_DAYS_PER_YEAR
|
as_result
|
bool
|
If True, return a MetricResult instead of a plain float. |
False
|
Returns:
| Type | Description |
|---|---|
float | MetricResult
|
The Sharpe ratio, or a MetricResult wrapping it. |
Source code in src/portpy/metrics/performance.py
sortino_ratio ¶
sortino_ratio(
returns: Series,
mar: float = DEFAULT_MAR,
annualized: bool = True,
periods_per_year: int = TRADING_DAYS_PER_YEAR,
as_result: bool = False,
) -> float | MetricResult
Like Sharpe, but penalizes only downside deviation below mar, not
total volatility.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
Series
|
Periodic return series. |
required |
mar
|
float
|
Annual minimum acceptable return, de-annualized internally. |
DEFAULT_MAR
|
annualized
|
bool
|
If True, scale the result by sqrt(periods_per_year). |
True
|
periods_per_year
|
int
|
Number of periods per year, used for annualization. |
TRADING_DAYS_PER_YEAR
|
as_result
|
bool
|
If True, return a MetricResult instead of a plain float. |
False
|
Returns:
| Type | Description |
|---|---|
float | MetricResult
|
The Sortino ratio, or a MetricResult wrapping it. |
Source code in src/portpy/metrics/performance.py
calmar_ratio ¶
calmar_ratio(
returns: Series,
periods_per_year: int = TRADING_DAYS_PER_YEAR,
as_result: bool = False,
) -> float | MetricResult
Annualized return divided by the absolute worst drawdown - reward per unit of worst-case pain.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
Series
|
Periodic return series. |
required |
periods_per_year
|
int
|
Number of periods per year, used for annualization. |
TRADING_DAYS_PER_YEAR
|
as_result
|
bool
|
If True, return a MetricResult instead of a plain float. |
False
|
Returns:
| Type | Description |
|---|---|
float | MetricResult
|
The Calmar ratio, or NaN if the series had no drawdown. |
Source code in src/portpy/metrics/performance.py
omega_ratio ¶
omega_ratio(
returns: Series,
threshold: float = 0.0,
periods_per_year: int = TRADING_DAYS_PER_YEAR,
as_result: bool = False,
) -> float | MetricResult
Ratio of the probability-weighted sum of gains to losses above/below a threshold return.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
Series
|
Periodic return series. |
required |
threshold
|
float
|
Annual required return; must be > -1. |
0.0
|
periods_per_year
|
int
|
Number of periods per year, used to de-annualize
|
TRADING_DAYS_PER_YEAR
|
as_result
|
bool
|
If True, return a MetricResult instead of a plain float. |
False
|
Returns:
| Type | Description |
|---|---|
float | MetricResult
|
The Omega ratio, or NaN if there were no losses below the threshold. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/portpy/metrics/performance.py
information_ratio ¶
information_ratio(
returns: Series,
benchmark: Series,
annualized: bool = True,
periods_per_year: int = TRADING_DAYS_PER_YEAR,
as_result: bool = False,
) -> float | MetricResult
Active return over tracking error - how consistently a strategy beats its benchmark.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
Series
|
Periodic return series. |
required |
benchmark
|
Series
|
Periodic benchmark return series, aligned to |
required |
annualized
|
bool
|
If True, scale the result by sqrt(periods_per_year). |
True
|
periods_per_year
|
int
|
Number of periods per year, used for annualization. |
TRADING_DAYS_PER_YEAR
|
as_result
|
bool
|
If True, return a MetricResult instead of a plain float. |
False
|
Returns:
| Type | Description |
|---|---|
float | MetricResult
|
The information ratio, or a MetricResult wrapping it. |
Source code in src/portpy/metrics/performance.py
treynor_ratio ¶
treynor_ratio(
returns: Series,
beta: float,
rf: float = DEFAULT_RISK_FREE_RATE,
annualized: bool = True,
periods_per_year: int = TRADING_DAYS_PER_YEAR,
as_result: bool = False,
) -> float | MetricResult
Excess return per unit of systematic (beta) risk, rather than per unit of total volatility.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
Series
|
Periodic return series. |
required |
beta
|
float
|
Pre-computed beta of |
required |
rf
|
float
|
Annual risk-free rate, de-annualized internally to a constant per-period rate. |
DEFAULT_RISK_FREE_RATE
|
annualized
|
bool
|
If True, scale the result by periods_per_year (linear, not sqrt, scaling). |
True
|
periods_per_year
|
int
|
Number of periods per year, used for annualization. |
TRADING_DAYS_PER_YEAR
|
as_result
|
bool
|
If True, return a MetricResult instead of a plain float. |
False
|
Returns:
| Type | Description |
|---|---|
float | MetricResult
|
The Treynor ratio, or a MetricResult wrapping it. |
Source code in src/portpy/metrics/performance.py
m2_measure ¶
m2_measure(
returns: Series,
benchmark: Series,
rf: float = DEFAULT_RISK_FREE_RATE,
periods_per_year: int = TRADING_DAYS_PER_YEAR,
as_result: bool = False,
) -> float | MetricResult
Modigliani risk-adjusted performance: the return the portfolio would have earned levered/delevered to match the benchmark's volatility. Expressed in the same units as an annual return, unlike Sharpe.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
Series
|
Periodic return series. |
required |
benchmark
|
Series
|
Periodic benchmark return series, aligned to |
required |
rf
|
float
|
Annual risk-free rate, used as-is (not de-annualized). |
DEFAULT_RISK_FREE_RATE
|
periods_per_year
|
int
|
Number of periods per year, used for annualization. |
TRADING_DAYS_PER_YEAR
|
as_result
|
bool
|
If True, return a MetricResult instead of a plain float. |
False
|
Returns:
| Type | Description |
|---|---|
float | MetricResult
|
The M2 measure, expressed as an annualized return. |
Source code in src/portpy/metrics/performance.py
sterling_ratio ¶
sterling_ratio(
returns: Series,
n: int = 5,
periods_per_year: int = TRADING_DAYS_PER_YEAR,
drawdown_adjustment: float = 0.0,
as_result: bool = False,
) -> float | MetricResult
Annualized return divided by the average magnitude of the n worst drawdowns.
Uses a simplified, widely-used variant (average of the n largest historical drawdowns) rather than the original definition's calendar-year drawdowns.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
Series
|
Periodic return series. |
required |
n
|
int
|
Number of worst drawdown episodes to average. |
5
|
periods_per_year
|
int
|
Number of periods per year, used for annualization. |
TRADING_DAYS_PER_YEAR
|
drawdown_adjustment
|
float
|
Added to the averaged drawdown denominator before dividing. Defaults to 0.0; the classic convention uses 0.10. |
0.0
|
as_result
|
bool
|
If True, return a MetricResult instead of a plain float. |
False
|
Returns:
| Type | Description |
|---|---|
float | MetricResult
|
The Sterling ratio, or NaN if there were no drawdown episodes. |
Source code in src/portpy/metrics/performance.py
burke_ratio ¶
burke_ratio(
returns: Series,
n: int = 5,
rf: float = DEFAULT_RISK_FREE_RATE,
periods_per_year: int = TRADING_DAYS_PER_YEAR,
as_result: bool = False,
) -> float | MetricResult
Excess annualized return over the root-sum-square of the n worst drawdowns.
Similar in spirit to sterling_ratio, but squares the drawdowns before combining them, penalizing a single deep drawdown more than several moderate ones of the same total size.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
Series
|
Periodic return series. |
required |
n
|
int
|
Number of worst drawdown episodes to include. |
5
|
rf
|
float
|
Annual risk-free rate, used as-is (not de-annualized). |
DEFAULT_RISK_FREE_RATE
|
periods_per_year
|
int
|
Number of periods per year, used for annualization. |
TRADING_DAYS_PER_YEAR
|
as_result
|
bool
|
If True, return a MetricResult instead of a plain float. |
False
|
Returns:
| Type | Description |
|---|---|
float | MetricResult
|
The Burke ratio, or NaN if there were no drawdown episodes. |
Source code in src/portpy/metrics/performance.py
gain_to_pain_ratio ¶
Sum of all returns divided by the sum of absolute losses.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
Series
|
Periodic return series. |
required |
as_result
|
bool
|
If True, return a MetricResult instead of a plain float. |
False
|
Returns:
| Type | Description |
|---|---|
float | MetricResult
|
The gain-to-pain ratio. |
Source code in src/portpy/metrics/performance.py
kappa_three_ratio ¶
kappa_three_ratio(
returns: Series,
mar: float = DEFAULT_MAR,
periods_per_year: int = TRADING_DAYS_PER_YEAR,
as_result: bool = False,
) -> float | MetricResult
Like Sortino, but penalizes shortfalls using a third-order (cubed) lower partial moment - more sensitive to a handful of severe shortfalls than Sortino's squared penalty.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
Series
|
Periodic return series. |
required |
mar
|
float
|
Annual minimum acceptable return, de-annualized internally. |
DEFAULT_MAR
|
periods_per_year
|
int
|
Number of periods per year, used to de-annualize |
TRADING_DAYS_PER_YEAR
|
as_result
|
bool
|
If True, return a MetricResult instead of a plain float. |
False
|
Returns:
| Type | Description |
|---|---|
float | MetricResult
|
The Kappa-3 ratio (unannualized, unlike sibling sortino_ratio), or a |
float | MetricResult
|
MetricResult wrapping it. |
Source code in src/portpy/metrics/performance.py
upside_potential_ratio ¶
upside_potential_ratio(
returns: Series,
mar: float = DEFAULT_MAR,
periods_per_year: int = TRADING_DAYS_PER_YEAR,
as_result: bool = False,
) -> float | MetricResult
Average upside above mar divided by downside deviation below mar -
rewards asymmetric upside.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
Series
|
Periodic return series. |
required |
mar
|
float
|
Annual minimum acceptable return, de-annualized internally. |
DEFAULT_MAR
|
periods_per_year
|
int
|
Number of periods per year, used to de-annualize |
TRADING_DAYS_PER_YEAR
|
as_result
|
bool
|
If True, return a MetricResult instead of a plain float. |
False
|
Returns:
| Type | Description |
|---|---|
float | MetricResult
|
The upside potential ratio (unannualized, unlike sibling sortino_ratio). |