Metrics Reference¶
Return, risk, drawdown, trade, and factor evaluation metrics.
Portfolio and factor metrics.
autocorrelation ¶
autocorrelation(factor: Series, lag: int = 1, nan_policy: NanPolicy = 'drop') -> pd.Series
Compute lagged factor rank autocorrelation by date.
Parameters¶
factor : pandas.Series
Factor values indexed by (date, symbol).
lag : int, default 1
Number of date observations to lag.
nan_policy : {"drop", "zero", "propagate", "raise"}, default "drop"
How missing rows are handled.
Returns¶
pandas.Series Cross-sectional rank autocorrelation by date.
Examples¶
import pandas as pd idx = pd.MultiIndex.from_product( ... [pd.to_datetime(["2024-01-01", "2024-01-02"]), ["A", "B"]], ... names=["date", "symbol"], ... ) autocorrelation(pd.Series([1, 2, 1, 2], idx)).round(4).tolist() [1.0]
factor_returns ¶
factor_returns(factor: Series, prices: Series, quantiles: int = 5, nan_policy: NanPolicy = 'drop') -> pd.DataFrame
Compute quantile forward returns from prices.
Parameters¶
factor : pandas.Series
Factor values indexed by (date, symbol).
prices : pandas.Series
Prices indexed by (date, symbol).
quantiles : int, default 5
Number of date-level factor quantiles.
nan_policy : {"drop", "zero", "propagate", "raise"}, default "drop"
How missing aligned rows are handled.
Returns¶
pandas.DataFrame Mean next-period returns by date and factor quantile.
Examples¶
import pandas as pd idx = pd.MultiIndex.from_product( ... [pd.to_datetime(["2024-01-01", "2024-01-02"]), ["A", "B"]], ... names=["date", "symbol"], ... ) out = factor_returns(pd.Series([1, 2], idx[:2]), pd.Series([100, 100, 110, 90], idx), 2) out.round(4).to_dict("list")
ic ¶
ic(factor: Series, forward_returns: Series, nan_policy: NanPolicy = 'drop', groupby: Series | None = None, by_group: bool = False) -> pd.Series
Compute per-date Pearson information coefficient.
Parameters¶
factor : pandas.Series
Factor values indexed by (date, symbol).
forward_returns : pandas.Series
Forward returns indexed by (date, symbol).
nan_policy : {"drop", "zero", "propagate", "raise"}, default "drop"
How missing aligned rows are handled.
Returns¶
pandas.Series Pearson correlation by date.
Examples¶
import pandas as pd idx = pd.MultiIndex.from_product( ... [pd.to_datetime(["2024-01-01"]), ["A", "B", "C"]], ... names=["date", "symbol"], ... ) ic(pd.Series([1, 2, 3], idx), pd.Series([0.1, 0.2, 0.3], idx)).tolist() [1.0]
ic_ir ¶
ic_ir(ic_series: Series, periods: int, nan_policy: NanPolicy = 'drop') -> float
Annualized information coefficient information ratio.
Parameters¶
ic_series : pandas.Series Information coefficient values through time. periods : int Periods per year. nan_policy : {"drop", "zero", "propagate", "raise"}, default "drop" How missing IC values are handled.
Returns¶
float Annualized IC mean divided by sample standard deviation.
Examples¶
import pandas as pd round(ic_ir(pd.Series([0.10, 0.20, -0.05, 0.15]), periods=12), 4) 3.2071
quantile_returns ¶
quantile_returns(factor: Series, forward_returns: Series, quantiles: int = 5, nan_policy: NanPolicy = 'drop', groupby: Series | None = None, group_neutral: bool = False) -> pd.DataFrame
Compute mean forward returns by factor quantile.
Parameters¶
factor : pandas.Series
Factor values indexed by (date, symbol).
forward_returns : pandas.Series
Forward returns indexed by (date, symbol).
quantiles : int, default 5
Number of date-level factor quantiles.
nan_policy : {"drop", "zero", "propagate", "raise"}, default "drop"
How missing aligned rows are handled.
Returns¶
pandas.DataFrame Mean forward return by date and quantile. Quantiles are 1-based.
Examples¶
import pandas as pd idx = pd.MultiIndex.from_product( ... [pd.to_datetime(["2024-01-01"]), ["A", "B"]], ... names=["date", "symbol"], ... ) out = quantile_returns(pd.Series([1, 2], idx), pd.Series([0.1, -0.1], idx), 2) out.to_dict("list")
rank_ic ¶
rank_ic(factor: Series, forward_returns: Series, nan_policy: NanPolicy = 'drop', groupby: Series | None = None, by_group: bool = False) -> pd.Series
Compute per-date Spearman rank information coefficient.
Parameters¶
factor : pandas.Series
Factor values indexed by (date, symbol).
forward_returns : pandas.Series
Forward returns indexed by (date, symbol).
nan_policy : {"drop", "zero", "propagate", "raise"}, default "drop"
How missing aligned rows are handled.
Returns¶
pandas.Series Spearman rank correlation by date.
Examples¶
import pandas as pd idx = pd.MultiIndex.from_product( ... [pd.to_datetime(["2024-01-01"]), ["A", "B", "C"]], ... names=["date", "symbol"], ... ) rank_ic(pd.Series([1, 2, 3], idx), pd.Series([0.3, 0.2, 0.1], idx)).tolist() [-1.0]
turnover ¶
turnover(factor: Series, lag: int = 1, nan_policy: NanPolicy = 'drop') -> pd.Series
Compute factor rank turnover.
Parameters¶
factor : pandas.Series
Factor values indexed by (date, symbol).
lag : int, default 1
Date lag used for rank autocorrelation.
nan_policy : {"drop", "zero", "propagate", "raise"}, default "drop"
How missing rows are handled.
Returns¶
pandas.Series
1 - autocorrelation by date.
Examples¶
import pandas as pd idx = pd.MultiIndex.from_product( ... [pd.to_datetime(["2024-01-01", "2024-01-02"]), ["A", "B"]], ... names=["date", "symbol"], ... ) turnover(pd.Series([1, 2, 2, 1], idx)).tolist() [2.0]
annual_return ¶
annual_return(returns: Series | DataFrame, periods: int, nan_policy: NanPolicy = 'drop') -> float | pd.Series
Compute compound annual growth rate from periodic simple returns.
Parameters¶
returns : pandas.Series or pandas.DataFrame Periodic simple returns. periods : int Periods per year. For example, 252 for daily stock returns. nan_policy : {"drop", "zero", "propagate", "raise"}, default "drop" How missing returns are handled before calculation.
Returns¶
float or pandas.Series Annualized compound return.
Examples¶
import pandas as pd round(annual_return(pd.Series([0.01, 0.02, -0.005, 0.004]), periods=252), 4) 5.1113
cum_returns ¶
cum_returns(returns: Series | DataFrame, starting_value: float = 0.0, nan_policy: NanPolicy = 'zero') -> pd.Series | pd.DataFrame
Compute cumulative returns from simple returns.
Parameters¶
returns : pandas.Series or pandas.DataFrame Periodic simple returns. starting_value : float, default 0.0 If zero, return cumulative return. Otherwise return an equity curve starting from this value. nan_policy : {"drop", "zero", "propagate", "raise"}, default "zero" How missing returns are handled before compounding.
Returns¶
pandas.Series or pandas.DataFrame Cumulative returns or equity curve.
Examples¶
import pandas as pd cum_returns(pd.Series([0.10, -0.05, 0.02])).round(4).tolist() [0.1, 0.045, 0.0659]
excess_returns ¶
excess_returns(returns: Series | DataFrame, rf: float, periods: int) -> pd.Series | pd.DataFrame
Subtract per-period risk-free return from simple returns.
Parameters¶
returns : pandas.Series or pandas.DataFrame Periodic simple returns. rf : float Annualized risk-free rate. periods : int Periods per year.
Returns¶
pandas.Series or pandas.DataFrame Excess returns.
Examples¶
import pandas as pd excess_returns(pd.Series([0.01, 0.02]), rf=0.024, periods=12).round(4).tolist() [0.008, 0.018]
log_to_simple ¶
log_to_simple(log_returns: Series | DataFrame) -> pd.Series | pd.DataFrame
simple_returns ¶
simple_returns(prices: Series | DataFrame) -> pd.Series | pd.DataFrame
Compute simple returns from prices.
Parameters¶
prices : pandas.Series or pandas.DataFrame Price series or wide price frame.
Returns¶
pandas.Series or pandas.DataFrame Periodic simple returns with the first missing period removed.
Examples¶
import pandas as pd simple_returns(pd.Series([100.0, 105.0, 102.9])).round(4).tolist() [0.05, -0.02]
alpha ¶
alpha(returns: Series, benchmark: Series, periods: int, rf: float = 0.0, nan_policy: NanPolicy = 'drop') -> float
Annualized alpha to a benchmark.
Parameters¶
returns : pandas.Series Asset or strategy returns. benchmark : pandas.Series Benchmark returns. periods : int Periods per year. rf : float, default 0.0 Annualized risk-free rate. nan_policy : {"drop", "zero", "propagate", "raise"}, default "drop" How missing aligned rows are handled.
Returns¶
float Annualized intercept.
Examples¶
import pandas as pd round(alpha(pd.Series([0.01, 0.02, 0.03]), pd.Series([0.02, 0.03, 0.04]), 252), 4) -0.9206
beta ¶
beta(returns: Series, benchmark: Series, nan_policy: NanPolicy = 'drop') -> float
Beta to a benchmark.
Parameters¶
returns : pandas.Series Asset or strategy returns. benchmark : pandas.Series Benchmark returns. nan_policy : {"drop", "zero", "propagate", "raise"}, default "drop" How missing aligned rows are handled.
Returns¶
float OLS slope using sample covariance and variance.
Examples¶
import pandas as pd round(beta(pd.Series([0.01, 0.02, 0.03]), pd.Series([0.02, 0.03, 0.04])), 4) 1.0
calmar ¶
calmar(returns: Series, periods: int, nan_policy: NanPolicy = 'drop') -> float
Calmar ratio.
Parameters¶
returns : pandas.Series Periodic simple returns. periods : int Periods per year. nan_policy : {"drop", "zero", "propagate", "raise"}, default "drop" How missing returns are handled.
Returns¶
float Annual return divided by absolute maximum drawdown.
Examples¶
import pandas as pd round(calmar(pd.Series([0.10, -0.20, 0.05, -0.10]), periods=12), 4) -1.7414
cvar ¶
cvar(returns: Series, cutoff: float = 0.05, nan_policy: NanPolicy = 'drop') -> float
Conditional value at risk.
Parameters¶
returns : pandas.Series Periodic simple returns. cutoff : float, default 0.05 Lower-tail percentile expressed as a fraction. nan_policy : {"drop", "zero", "propagate", "raise"}, default "drop" How missing returns are handled.
Returns¶
float Mean return at or below the historical VaR threshold.
Examples¶
import pandas as pd round(cvar(pd.Series([-0.10, -0.04, -0.01, 0.02, 0.05, 0.09])), 4) -0.1
downside_risk ¶
downside_risk(returns: Series, periods: int, required: float = 0.0, nan_policy: NanPolicy = 'drop') -> float
Annualized downside risk.
Parameters¶
returns : pandas.Series Periodic simple returns. periods : int Periods per year. required : float, default 0.0 Per-period required return threshold. nan_policy : {"drop", "zero", "propagate", "raise"}, default "drop" How missing returns are handled.
Returns¶
float Annualized downside deviation.
Examples¶
import pandas as pd round(downside_risk(pd.Series([0.02, -0.01, -0.03, 0.01]), periods=12), 4) 0.0548
drawdown_series ¶
drawdown_series(returns: Series, nan_policy: NanPolicy = 'zero') -> pd.Series
Compute drawdown at each point in a return series.
Parameters¶
returns : pandas.Series Periodic simple returns. nan_policy : {"drop", "zero", "propagate", "raise"}, default "zero" How missing returns are handled.
Returns¶
pandas.Series Drawdown series, where zero means no drawdown.
Examples¶
import pandas as pd drawdown_series(pd.Series([0.10, -0.20, 0.05])).round(2).tolist() [0.0, -0.2, -0.16]
information_ratio ¶
information_ratio(returns: Series, benchmark: Series, periods: int, nan_policy: NanPolicy = 'drop') -> float
Annualized information ratio.
Parameters¶
returns : pandas.Series Asset or strategy returns. benchmark : pandas.Series Benchmark returns. periods : int Periods per year. nan_policy : {"drop", "zero", "propagate", "raise"}, default "drop" How missing aligned rows are handled.
Returns¶
float Active return divided by tracking error.
Examples¶
import pandas as pd r = pd.Series([0.01, 0.03, 0.02]) b = pd.Series([0.02, 0.01, 0.02]) round(information_ratio(r, b, 252), 4) 3.4641
max_drawdown ¶
max_drawdown(returns: Series, nan_policy: NanPolicy = 'zero') -> float
Maximum drawdown.
Parameters¶
returns : pandas.Series Periodic simple returns. nan_policy : {"drop", "zero", "propagate", "raise"}, default "zero" How missing returns are handled.
Returns¶
float Most negative drawdown.
Examples¶
import pandas as pd round(max_drawdown(pd.Series([0.10, -0.20, 0.05, -0.10])), 4) -0.244
omega ¶
omega(returns: Series, threshold: float = 0.0, periods: int = 252, nan_policy: NanPolicy = 'drop') -> float
Omega gain/loss ratio.
Parameters¶
returns : pandas.Series Periodic simple returns. threshold : float, default 0.0 Annualized return threshold. periods : int, default 252 Periods per year. nan_policy : {"drop", "zero", "propagate", "raise"}, default "drop" How missing returns are handled.
Returns¶
float Sum of gains divided by absolute sum of losses.
Examples¶
import pandas as pd round(omega(pd.Series([0.03, 0.01, -0.02, -0.01]), threshold=0.12, periods=12), 4) 0.4
sharpe ¶
sharpe(returns: Series, periods: int, rf: float = 0.0, nan_policy: NanPolicy = 'drop') -> float
Annualized Sharpe ratio.
Parameters¶
returns : pandas.Series Periodic simple returns. periods : int Periods per year. rf : float, default 0.0 Annualized risk-free rate. nan_policy : {"drop", "zero", "propagate", "raise"}, default "drop" How missing returns are handled.
Returns¶
float Annualized Sharpe ratio.
Examples¶
import pandas as pd round(sharpe(pd.Series([0.01, 0.02, -0.01, 0.0]), periods=252), 4) 6.1482
sortino ¶
sortino(returns: Series, periods: int, rf: float = 0.0, required: float = 0.0, nan_policy: NanPolicy = 'drop') -> float
Annualized Sortino ratio.
Parameters¶
returns : pandas.Series Periodic simple returns. periods : int Periods per year. rf : float, default 0.0 Annualized risk-free rate. required : float, default 0.0 Per-period downside threshold. nan_policy : {"drop", "zero", "propagate", "raise"}, default "drop" How missing returns are handled.
Returns¶
float Annualized Sortino ratio.
Examples¶
import pandas as pd round(sortino(pd.Series([0.02, -0.01, -0.03, 0.01]), periods=12), 4) -0.5477
tail_ratio ¶
tail_ratio(returns: Series, cutoff: float = 0.05, nan_policy: NanPolicy = 'drop') -> float
Right-tail to left-tail ratio.
Parameters¶
returns : pandas.Series Periodic simple returns. cutoff : float, default 0.05 Tail percentile expressed as a fraction. nan_policy : {"drop", "zero", "propagate", "raise"}, default "drop" How missing returns are handled.
Returns¶
float Absolute ratio of upper and lower percentiles.
Examples¶
import pandas as pd round(tail_ratio(pd.Series([-0.10, -0.04, -0.01, 0.02, 0.05, 0.09])), 4) 0.9412
var ¶
var(returns: Series, cutoff: float = 0.05, nan_policy: NanPolicy = 'drop') -> float
Historical value at risk percentile.
Parameters¶
returns : pandas.Series Periodic simple returns. cutoff : float, default 0.05 Lower-tail percentile expressed as a fraction. nan_policy : {"drop", "zero", "propagate", "raise"}, default "drop" How missing returns are handled.
Returns¶
float Historical lower-tail percentile.
Examples¶
import pandas as pd round(var(pd.Series([-0.10, -0.04, -0.01, 0.02, 0.05, 0.09])), 4) -0.085
volatility ¶
volatility(returns: Series, periods: int, nan_policy: NanPolicy = 'drop') -> float
Annualized volatility.
Parameters¶
returns : pandas.Series Periodic simple returns. periods : int Periods per year. nan_policy : {"drop", "zero", "propagate", "raise"}, default "drop" How missing returns are handled.
Returns¶
float
Sample standard deviation annualized by sqrt(periods).
Examples¶
import pandas as pd round(volatility(pd.Series([0.01, 0.02, -0.01, 0.0]), periods=252), 4) 0.2049
avg_loss ¶
avg_loss(trades: Series | DataFrame) -> float
avg_win ¶
avg_win(trades: Series | DataFrame) -> float
expectancy ¶
expectancy(trades: Series | DataFrame) -> float
max_consecutive_losses ¶
max_consecutive_losses(trades: Series | DataFrame) -> int
max_consecutive_wins ¶
max_consecutive_wins(trades: Series | DataFrame) -> int
Compute the longest run of profitable trades.
Parameters¶
trades : pandas.Series or pandas.DataFrame
Trade PnL values, or a frame containing a pnl column.
Returns¶
int Longest consecutive positive-PnL streak.
Examples¶
import pandas as pd max_consecutive_wins(pd.Series([100.0, 25.0, -10.0, 50.0])) 2
profit_factor ¶
profit_factor(trades: Series | DataFrame) -> float
Compute gross profit divided by absolute gross loss.
Parameters¶
trades : pandas.Series or pandas.DataFrame
Trade PnL values, or a frame containing a pnl column.
Returns¶
float Sum of wins divided by the absolute sum of losses.
Examples¶
import pandas as pd round(profit_factor(pd.Series([100.0, -50.0, -25.0, 25.0])), 4) 1.6667
win_rate ¶
win_rate(trades: Series | DataFrame) -> float