Statistical analytics for return streams: performance, risk, and benchmark-relative metrics.
Use :func:returns to bind a return stream (and optional benchmark) for chained stats and plotting, e.g. q.stats.returns(returns, benchmark=spy).alpha(). Plain functions (e.g. q.stats.performance(returns)) are stateless and take the series directly. See :mod:qrt.plot for rendering return streams.
Divides the Sortino ratio by \(\sqrt{2}\) so it can be compared directly against the Sharpe ratio, which otherwise tends to run higher since it divides by an unconditional rather than downside-only deviation.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
required
return_type
ReturnType
Whether returns are "simple" or "log" returns.
'simple'
periods_per_year
int | None
Annualization frequency. Inferred from the index when not given.
None
rf
float
Annualized risk-free rate, subtracted (after deannualizing) from returns before computing the ratio. Defaults to 0.0.
0.0
smart
bool
Whether to penalize for return autocorrelation, see :func:sortino.
Calculate the annualized Jensen alpha of a return stream relative to a benchmark.
Jensen’s alpha is the annualized return left over after accounting for the return explained by beta exposure to the benchmark — the excess return generated (or lost) beyond what market risk alone would predict.
Parameters
Name
Type
Description
Default
returns
pd.Series
Strategy periodic return series.
required
benchmark
pd.Series
Benchmark periodic return series, aligned on shared dates.
required
return_type
ReturnType
Whether returns/benchmark are "simple" or "log" returns.
'simple'
periods_per_year
int | None
Annualization frequency. Inferred from the index when not given.
None
Returns
Name
Type
Description
float
Annualized alpha (excess return not explained by benchmark beta exposure).
Calculate a penalty factor for return autocorrelation.
Serially correlated returns (e.g. from stale pricing, smoothing, or illiquid assets) understate true volatility and can artificially inflate Sharpe- and Sortino-style ratios. This factor (>= 1) is meant to multiply the ratio’s denominator to correct for that; see smart=True on :func:sharpe/:func:sortino.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
required
return_type
ReturnType
Whether returns are "simple" or "log" returns.
'simple'
Returns
Name
Type
Description
float
Autocorrelation penalty factor (1.0 when lag-1 autocorrelation is zero or undefined).
Calculate relative performance, risk, and attribution versus a benchmark.
Parameters
Name
Type
Description
Default
returns
pd.Series
Strategy periodic return series.
required
benchmark
pd.Series
Benchmark periodic return series, aligned on shared dates.
required
return_type
ReturnType
Whether returns/benchmark are "simple" or "log" returns.
'simple'
periods_per_year
int | None
Annualization frequency. Inferred from the index when not given.
None
Returns
Name
Type
Description
pd.Series
Series indexed by metric name:
pd.Series
- Strategy Total Return / Benchmark Total Return: Simple compounded returns over the full period.
pd.Series
- Active Return: Geometric excess growth of the strategy over the benchmark (final value of the strategy/benchmark equity ratio, minus 1).
pd.Series
- Beta: Beta — sensitivity to benchmark movements.
pd.Series
- Alpha: Jensen’s alpha — annualized excess return not explained by beta exposure.
pd.Series
- Correlation: Correlation — Pearson correlation between strategy and benchmark returns.
pd.Series
- Tracking Error: Tracking error — annualized standard deviation of active returns (strategy minus benchmark), i.e. how consistently the strategy diverges from the benchmark.
pd.Series
- Information Ratio: Information ratio — annualized active return divided by tracking error, i.e. consistency of benchmark-beating performance.
pd.Series
- Periods: Number of aligned return observations used.
Calculate the market beta of a return stream relative to a benchmark.
Beta is the covariance of strategy and benchmark returns divided by the benchmark’s variance — a measure of sensitivity to benchmark (market) movements, i.e. systematic risk.
Parameters
Name
Type
Description
Default
returns
pd.Series
Strategy periodic return series.
required
benchmark
pd.Series
Benchmark periodic return series, aligned on shared dates.
required
return_type
ReturnType
Whether returns/benchmark are "simple" or "log" returns.
'simple'
Returns
Name
Type
Description
float
The beta coefficient (covariance with the benchmark over benchmark variance).
Calculate the Common Sense Ratio: Profit Factor x Tail Ratio.
Combines overall profitability (:func:profit_factor) with the shape of extreme returns (:func:tail_ratio), so it penalizes strategies with a poor tail-risk profile even if their average profit factor looks good.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
Calculate the historical Conditional Value at Risk (Expected Shortfall).
Expected Shortfall is the average return among periods that fell below the :func:value_at_risk threshold — the expected loss given that a tail event occurred, which VaR alone doesn’t capture.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
required
return_type
ReturnType
Whether returns are "simple" or "log" returns.
'simple'
confidence
float
Confidence level, e.g. 0.95 for a 95% CVaR. Defaults to 0.95.
0.95
Returns
Name
Type
Description
float
Conditional Value at Risk (a negative number representing an expected loss).
Return the longest streak of consecutive positive-return periods.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
required
return_type
ReturnType
Whether returns are "simple" or "log" returns.
'simple'
Returns
Name
Type
Description
int
Length of the longest winning streak.
cpc_index
stats.cpc_index(returns, *, return_type='simple')
Calculate the CPC Index: Profit Factor x Win Rate x Payoff Ratio.
A composite score combining how much is won per unit lost (:func:profit_factor), how often periods win (win rate), and the relative size of wins vs. losses (:func:payoff_ratio).
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
Break down compounded returns by period, flagging IQR outliers within each.
Compounds returns into Daily (as given), Weekly, Monthly, Quarterly, and Yearly buckets, then splits each bucket’s values into “normal” and “outlier” using the standard 1.5x interquartile-range rule.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series with a DatetimeIndex.
required
return_type
ReturnType
Whether returns are "simple" or "log" returns.
'simple'
Returns
Name
Type
Description
dict[str, dict[str, list[float]]]
Dict keyed by period name ("Daily", "Weekly", "Monthly", "Quarterly",
dict[str, dict[str, list[float]]]
"Yearly"), each mapping to {"values": [...], "outliers": [...]}.
Calculate per-period excess returns over a deannualized risk-free rate.
rf is an annualized rate; it is converted to a per-period rate before being subtracted from each period’s return (see :func:performance’s rf parameter for the same convention).
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
required
rf
float
Annualized risk-free rate. Defaults to 0.0 (returns unchanged).
0.0
return_type
ReturnType
Whether returns are "simple" or "log" returns.
'simple'
periods_per_year
int | None
Annualization frequency used to deannualize rf. Inferred from the index when not given.
Calculate the geometric-mean expected return per period, optionally per calendar period.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
required
aggregate
Literal['W', 'M', 'Q', 'Y'] | None
Optional calendar period to compound into first (see :func:aggregate_returns), e.g. "M" for the expected monthly return. None (default) uses the series’ native frequency.
Decompose each period’s excess return into per-factor contributions from a static fit.
A factor loading (:func:factor_regression) answers “how sensitive is the strategy to this factor?”; a factor contribution answers “how much return did that exposure generate?”. Fits :func:factor_regression once over the full period, then attributes every period’s excess return to Alpha (the fitted intercept), one column per factor (coefficient * factor return), and Residual (left unexplained). Columns sum, period by period, to the excess return (returns minus rf).
Note
Cumulative arithmetic sums of these columns reconstruct the cumulative excess return exactly; compounding each column separately (e.g. (1 + contribution).cumprod()) would not, since return compounding creates cross-factor interaction terms. Use arithmetic cumulative sums for additive attribution (see :func:qrt.plot.factor_contributions).
Parameters
Name
Type
Description
Default
returns
pd.Series
Strategy periodic return series.
required
factors
pd.DataFrame
DataFrame of periodic factor return columns, aligned to returns.
required
return_type
ReturnType
Whether returns is "simple" or "log" returns.
'simple'
rf
str | pd.Series | None
See :func:factor_regression.
'RF'
covariance
CovarianceType
See :func:factor_regression (only affects standard errors, not the contributions themselves, which depend only on the fitted coefficients).
'nonrobust'
Returns
Name
Type
Description
pd.DataFrame
DataFrame indexed like the aligned returns/factors data, with columns Alpha,
Fit a full-period multi-factor OLS regression (e.g. the Fama-French five-factor model).
Decomposes returns’ excess return into a constant (alpha) plus a linear exposure (beta) to each column of factors: R - Rf = alpha + beta_1 * factor_1 + ... + beta_k * factor_k + residual. This is a return-attribution regression, not a forecasting model – it explains what already happened, using every observation in the sample. See Fama-French three/five-factor model and the Kenneth French Data Library for the standard Mkt-RF/SMB/HML/RMW/CMA/RF factor set (any factor set works, not only the Fama-French five). For time-varying exposures, see :func:rolling_factor_regression.
Parameters
Name
Type
Description
Default
returns
pd.Series
Strategy periodic return series.
required
factors
pd.DataFrame
DataFrame of periodic factor return columns (e.g. Mkt-RF, SMB, HML, RMW, CMA, and optionally RF), aligned to returns on shared dates. Must be in the same decimal-return units as returns.
required
return_type
ReturnType
Whether returns is "simple" or "log" returns.
'simple'
rf
str | pd.Series | None
Risk-free rate used to convert returns into the regression’s excess-return target. Either a column name already present in factors (removed from the regressors, e.g. Ken French’s "RF") or a separate periodic risk-free return series aligned like factors. Pass None if returns is already an excess return.
'RF'
covariance
CovarianceType
Standard-error estimator: "nonrobust" (classic OLS, default) or "HC3" (heteroskedasticity-robust). Coefficients are identical either way; only standard errors, t-statistics, p-values, and confidence intervals differ.
'nonrobust'
confidence_level
float
Confidence level for CI Lower/CI Upper. Defaults to 0.95.
0.95
Returns
Name
Type
Description
pd.DataFrame
DataFrame indexed by "Alpha" followed by each factor column (in factors’
pd.DataFrame
order, excluding rf when it names a column), with columns Coefficient,
pd.DataFrame
Std Error, t-Statistic, p-Value, CI Lower, CI Upper. See
pd.DataFrame
func:factor_regression_stats for regression-level diagnostics (R², annualized
pd.DataFrame
alpha, residual volatility) and :func:factor_contributions for the per-period
Calculate regression-level diagnostics for a full-period multi-factor OLS fit.
Companion to :func:factor_regression (which returns the per-factor coefficient table); this returns the fit’s overall explanatory power and alpha, annualized.
Parameters
Name
Type
Description
Default
returns
pd.Series
Strategy periodic return series.
required
factors
pd.DataFrame
DataFrame of periodic factor return columns, aligned to returns.
required
return_type
ReturnType
Whether returns is "simple" or "log" returns.
'simple'
rf
str | pd.Series | None
See :func:factor_regression.
'RF'
covariance
CovarianceType
See :func:factor_regression (only affects Alpha’s statistical significance, not shown here – see :func:factor_regression for alpha’s standard error/t-statistic/p-value).
'nonrobust'
periods_per_year
int | None
Annualization frequency. Inferred from the index when not given.
None
Returns
Name
Type
Description
pd.Series
Series indexed by metric name:
pd.Series
- Alpha: Per-period (e.g. daily) intercept.
pd.Series
- Alpha (ann.): Simple annualization (periods_per_year * Alpha); prefer this over compounding a regression intercept, which implies more economic precision than is justified.
pd.Series
- R²: Fraction of excess-return variance explained by the factors.
pd.Series
- Adj. R²: R² penalized for the number of factors, comparable across models with a different factor count.
pd.Series
- Residual Volatility: Annualized standard deviation of the fit’s residuals – variation left unexplained by the factors.
Sum of all (excess) returns divided by the absolute sum of losing periods — a simple measure of total profit generated per unit of total loss endured, without regard to distribution shape or timing.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
required
return_type
ReturnType
Whether returns are "simple" or "log" returns.
'simple'
periods_per_year
int | None
Annualization frequency, used to deannualize rf. Inferred from the index (after any aggregate compounding) when not given.
None
rf
float
Annualized risk-free rate, subtracted (after deannualizing) from returns. Defaults to 0.0.
0.0
aggregate
Literal['W', 'M', 'Q', 'Y'] | None
Optional calendar period to compound into first (see :func:aggregate_returns), e.g. "M" for the monthly-resolution ratio.
The constant per-period return that would produce the same compounded growth as the actual return sequence (the un-annualized building block of :func:_cagr).
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
required
return_type
ReturnType
Whether returns are "simple" or "log" returns.
'simple'
Returns
Name
Type
Description
float
Geometric mean return per period.
infer_periods_per_year
stats.infer_periods_per_year(index)
Infer a conventional annualization frequency from a datetime index.
Daily and intraday data use 252 trading periods per year. Weekly, monthly, quarterly, and yearly data use 52, 12, 4, and 1 periods respectively. Non-datetime or single-observation indexes default to 252.
Parameters
Name
Type
Description
Default
index
pd.Index
Datetime index whose median spacing is used to infer frequency.
The Kelly Criterion is the recommended fraction of capital to allocate to a strategy given its win rate and :func:payoff_ratio, to maximize long-run geometric growth without risking ruin.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
required
return_type
ReturnType
Whether returns are "simple" or "log" returns.
'simple'
Returns
Name
Type
Description
float
Kelly fraction (can be negative, indicating the strategy has negative expectancy).
Kurtosis measures how fat-tailed the return distribution is relative to a normal distribution (which has an excess kurtosis of 0). Higher values mean more frequent extreme returns than a normal distribution would predict.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
Turn a return series into a portfolio equity curve.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
required
return_type
ReturnType
Whether returns are "simple" or "log" returns.
'simple'
start_balance
float
Starting portfolio balance. Defaults to 100,000.
100000.0
mode
Literal['compound', 'linear']
"compound" (default) reinvests gains/losses each period (equivalent to :func:to_prices, rebased to start_balance); "linear" applies each period’s return to the original start_balance only, without reinvestment.
Build the full quantstats-style metrics table for a return stream.
Composes the individual q.stats metric functions into one grouped DataFrame — the table behind :func:qrt.plot.metrics_table and the tearsheet’s metrics panel. Values are raw (floats, ints, timestamps), not formatted strings; rendering/formatting is left to the caller.
Parameters
Name
Type
Description
Default
returns
pd.Series
Strategy periodic return series with a DatetimeIndex.
required
benchmark
pd.Series | None
Optional benchmark periodic return series, aligned to returns on shared dates. Adds a benchmark column plus a vs. Benchmark section (Beta, Alpha, Correlation, R², Information Ratio, Tracking Error, Treynor Ratio) filled only for the strategy column.
None
mode
Literal['basic', 'full']
"full" (default) for every metric, "basic" for a compact headline subset.
'full'
return_type
ReturnType
Whether returns/benchmark are "simple" or "log" returns.
'simple'
periods_per_year
int | None
Annualization frequency. Inferred from the index when not given.
None
rf
float
Annualized risk-free rate, used by excess-return metrics and reported in the Risk-Free Rate row. Defaults to 0.0.
0.0
Returns
Name
Type
Description
pd.DataFrame
DataFrame with a (Section, Metric)MultiIndex and one column
pd.DataFrame
per series (benchmark first when given). Sections: Overview,
Simulate bootstrap-resampled return paths to gauge the range of plausible outcomes.
Each simulated path is built by resampling the historical returns with replacement (a bootstrap), then compounding them into a cumulative growth path. Useful for estimating how much of a strategy’s realized performance could plausibly be attributed to chance, and what range of terminal outcomes and drawdowns a similar return distribution could plausibly produce.
Note
This deliberately resamples with replacement rather than merely permuting (shuffling) the historical returns. A plain permutation only reorders the same fixed set of returns, and since compounding is a product, its terminal value prod(1 + r) is invariant to the order of the factors — every permuted path compounds to exactly the same terminal return as the original. That makes a permutation-only simulation’s terminal-value spread, goal_probability, degenerate (every path either meets the goal or none do) even though its path shape (and therefore Max Drawdown) still varies meaningfully. Bootstrap resampling avoids this: some returns are drawn more than once and others not at all, so the terminal value genuinely varies across simulations, making terminal_stats/goal_probability statistically meaningful.
Note
By default (block_size=None) each period is resampled independently (i.i.d.), which implicitly assumes returns have no serial dependence. Real return series routinely violate that: volatility clustering (calm/turbulent periods cluster together), short-term autocorrelation, and macro-regime persistence are all well documented. i.i.d. resampling destroys these dependencies, which tends to understate tail risk (it can’t produce a realistic run of consecutive bad days beyond what chance alone would predict). Pass block_size (e.g. 20 for roughly a trading month) to instead draw contiguous, circularly-wrapped blocks of historical returns with Geometrically-distributed lengths (mean block_size) — a stationary block bootstrap — which preserves that local structure. Larger block_size preserves longer-range dependence at the cost of fewer effectively-independent blocks (and thus less path diversity) per simulation; it should typically be set to roughly the horizon over which returns are believed to be autocorrelated (e.g. the length of a typical volatility cluster). For explicitly modeling time-varying volatility/regime shifts, a fitted parametric model (ARMA-GARCH) is a more appropriate but heavier tool than resampling; that’s out of scope here.
Note
Compounding is multiplicative, so its variance accumulates with the number of resampled periods: simulating as many periods as a long history contains (the default, periods=None) makes the terminal-value spread balloon (easily spanning multiple orders of magnitude for a volatile asset) and pushes any fixed bust/goal threshold’s probability toward 0% or 100%, since a long enough random path is nearly guaranteed to cross it at some point — both effects are real, not bugs, but make for a poorly-differentiated simulation. Pass periods to decouple the simulation horizon from the length of returns: the full history is still used as the resampling pool (so a long, multi-regime history avoids biasing what gets resampled — see the module-level guidance on avoiding a single-regime sample), while each simulated path only covers periods steps forward, matching whatever horizon bust/goal are actually meant to evaluate (e.g. periods=252 for one trading year). Must satisfy 1 <= periods <= len(returns). When given, sim_0 (the “original” reference path) is the most recent periods-length window of returns rather than the entire history.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type). Also serves as the resampling pool when periods is narrower than returns itself.
required
sims
int
Number of simulated paths, including the original (unresampled) path as the first column (sim_0). Must be at least 1.
1000
return_type
ReturnType
Whether returns are "simple" or "log" returns.
'simple'
bust
float | None
Optional Max Drawdown threshold (e.g. -0.2 for -20%). When given, bust_probability reports the fraction of simulated paths whose max drawdown breached it.
None
goal
float | None
Optional cumulative-return threshold (e.g. 1.0 for +100%). When given, goal_probability reports the fraction of simulated paths that reached it.
None
confidence
float
Confidence level for the per-period confidence_band. Defaults to 0.95 (a 95% band).
0.95
seed
int | None
Optional random seed for reproducibility.
None
block_size
float | None
Optional mean block length (in periods) for a stationary block bootstrap (see Note above). Defaults to None, resampling each period independently (i.i.d.).
None
periods
int | None
Optional simulation horizon in periods, decoupled from len(returns) (see Note above). Defaults to None (simulate as many periods as returns contains).
None
Returns
Name
Type
Description
dict[str, object]
Dict with keys:
dict[str, object]
- paths: DataFrame of cumulative simulated returns, one column per simulation, indexed like returns.
dict[str, object]
- terminal_stats: Summary statistics (mean, std, min/25%/50%/75%/max) of the final cumulative return across simulations.
dict[str, object]
- max_drawdown_stats: Same summary statistics for each simulation’s Max Drawdown.
dict[str, object]
- confidence_band: DataFrame with Lower/Upper columns holding the confidence-level percentile range of cumulative return at each period.
dict[str, object]
- bust_probability: Fraction of simulations at or below bust (None if bust wasn’t given).
dict[str, object]
- goal_probability: Fraction of simulations at or above goal (None if goal wasn’t given).
Simulate noise-perturbed return paths to gauge sensitivity to day-to-day noise/volatility.
Each simulated path keeps the exact historical sequence of returns (same order, same length, same index) but scales every period’s return by 1 + eps where eps ~ Normal(0, noise) — i.e. jitters the magnitude of each period’s move by a random relative fraction without flipping its sign. This asks “would this exact strategy still perform similarly if the day-to-day noise/volatility in prices had been slightly different, but its underlying trend/edge (the sequence of up/down calls) was unchanged?” — a different question from :func:montecarlo (which varies the order/composition of returns via bootstrap resampling) or :func:variance_test (which varies the win rate itself over a forward horizon).
Note
Unlike :func:montecarlo, noise_test never reorders or resamples returns — every simulated path is the same length as returns and stays aligned to its original index, so no separate horizon/resampling-pool decoupling is needed here.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
required
sims
int
Number of simulated paths, including the original (unperturbed) path as the first column (sim_0). Must be at least 1.
1000
return_type
ReturnType
Whether returns are "simple" or "log" returns.
'simple'
noise
float
Standard deviation of the multiplicative noise applied to each period’s return, e.g. 0.1 randomly scales each return’s magnitude by roughly ±10%. Must be non-negative.
0.1
bust
float | None
Optional Max Drawdown threshold (e.g. -0.2 for -20%). When given, bust_probability reports the fraction of simulated paths whose max drawdown breached it.
None
goal
float | None
Optional cumulative-return threshold (e.g. 1.0 for +100%). When given, goal_probability reports the fraction of simulated paths that reached it.
None
confidence
float
Confidence level for the per-period confidence_band. Defaults to 0.95 (a 95% band).
0.95
seed
int | None
Optional random seed for reproducibility.
None
Returns
Name
Type
Description
dict[str, object]
Dict with keys:
dict[str, object]
- paths: DataFrame of cumulative simulated returns, one column per simulation (sim_0 is the original, unperturbed path), indexed like returns.
dict[str, object]
- terminal_stats: Summary statistics (mean, std, min/25%/50%/75%/max) of the final cumulative return across simulations.
dict[str, object]
- max_drawdown_stats: Same summary statistics for each simulation’s Max Drawdown.
dict[str, object]
- confidence_band: DataFrame with Lower/Upper columns holding the confidence-level percentile range of cumulative return at each period.
dict[str, object]
- bust_probability: Fraction of simulations at or below bust (None if bust wasn’t given).
dict[str, object]
- goal_probability: Fraction of simulations at or above goal (None if goal wasn’t given).
The Omega ratio is the probability-weighted ratio of gains above a required-return threshold to losses below it, using the full return distribution rather than just its mean and variance (unlike Sharpe/Sortino).
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
required
return_type
ReturnType
Whether returns are "simple" or "log" returns.
'simple'
periods_per_year
int | None
Annualization frequency. Inferred from the index when not given.
None
rf
float
Annualized risk-free rate, subtracted (after deannualizing) from returns before computing the ratio. Defaults to 0.0.
0.0
required_return
float
Annualized minimum acceptable return threshold, deannualized the same way as rf. Defaults to 0.0.
Calculate the outlier loss ratio: a low quantile of returns vs. the average loss.
Ratio of the quantile (e.g. 1st percentile) return to the average losing return — how much a strategy’s worst outcomes are driven by rare, outsized losses rather than typical ones.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
required
return_type
ReturnType
Whether returns are "simple" or "log" returns.
'simple'
quantile
float
Lower quantile used for the outlier threshold. Defaults to 0.01.
Calculate the outlier win ratio: a high quantile of returns vs. the average win.
Ratio of the quantile (e.g. 99th percentile) return to the average winning return — how much a strategy’s best outcomes are driven by rare, outsized wins rather than typical ones.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
required
return_type
ReturnType
Whether returns are "simple" or "log" returns.
'simple'
quantile
float
Upper quantile used for the outlier threshold. Defaults to 0.99.
Calculate standard performance statistics for a periodic return stream.
Annualized metrics use periods_per_year when supplied; otherwise, a conventional frequency is inferred from a datetime index. rf (an annualized risk-free rate) is deannualized to match periods and subtracted from returns before computing CAGR, Sharpe, and Sortino (and, through CAGR, Calmar); Total Return, Volatility, Max Drawdown, and Win Rate always use raw returns.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
required
return_type
ReturnType
Whether returns are "simple" or "log" returns.
'simple'
periods_per_year
int | None
Annualization frequency. Inferred from the index when not given.
None
rf
float
Annualized risk-free rate, subtracted (after deannualizing) from returns before computing excess-return ratios. Defaults to 0.0.
0.0
smart
bool
Whether to penalize Sharpe/Sortino for return autocorrelation, see :func:sharpe/:func:sortino. Defaults to False.
False
Returns
Name
Type
Description
pd.Series
Series indexed by metric name:
pd.Series
- Total Return: Simple compounded return over the full period.
pd.Series
- CAGR: Compound Annual Growth Rate — the constant annual rate of return that would produce the same total growth.
pd.Series
- Volatility: Volatility — annualized standard deviation of returns, the most common measure of risk.
pd.Series
- Sharpe: Sharpe ratio — annualized excess return per unit of volatility, i.e. return earned per unit of total risk taken.
pd.Series
- Sortino: Sortino ratio — like Sharpe, but only penalizes downside volatility, since investors rarely mind upside swings.
pd.Series
- Calmar: Calmar ratio — CAGR divided by the absolute Max Drawdown, i.e. return per unit of worst-case pain endured.
pd.Series
- Max Drawdown: Drawdown — largest peak-to-trough decline in cumulative value over the period.
pd.Series
- Win Rate: Share of non-zero-return periods that were positive (periods with exactly zero return are excluded from both the count of wins and the total).
Compound returns over standard trailing and calendar windows (MTD, 3M, …, all-time).
Windows shorter than one year report the plain compounded return; multi-year windows report the annualized (CAGR) rate. Windows extending before the first observation use whatever data is available.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series with a DatetimeIndex.
required
return_type
ReturnType
Whether returns are "simple" or "log" returns.
'simple'
periods_per_year
int | None
Annualization frequency for the multi-year windows. Inferred from the index when not given.
None
Returns
Name
Type
Description
pd.Series
Series indexed by window label: MTD, 3M, 6M, YTD, 1Y,
pd.Series
3Y (ann.), 5Y (ann.), 10Y (ann.), and All-time (ann.).
Calculate the probability that a risk-adjusted ratio is truly positive.
The Probabilistic Sharpe Ratio (Bailey & Lopez de Prado, 2012) converts a point-estimate ratio into a confidence level by accounting for sample size, skew, and kurtosis, all of which affect how noisy the ratio estimate is. The same correction is applied here to the Sortino and adjusted-Sortino ratios.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
required
base
Literal['sharpe', 'sortino', 'adjusted_sortino']
Which ratio to convert: "sharpe", "sortino", or "adjusted_sortino".
'sharpe'
return_type
ReturnType
Whether returns are "simple" or "log" returns.
'simple'
periods_per_year
int | None
Annualization frequency. Inferred from the index when not given.
None
rf
float
Annualized risk-free rate, subtracted (after deannualizing) from returns before computing the base ratio. Defaults to 0.0.
0.0
smart
bool
Whether to penalize the base ratio for return autocorrelation, see :func:sharpe/:func:sortino.
False
Returns
Name
Type
Description
float
Probability (0-1) that the true ratio is greater than zero.
Calculate the profit ratio: win frequency divided by loss frequency.
Distinct from :func:payoff_ratio (which compares win/loss size) and :func:profit_factor (which compares total gains vs. total losses) — this compares how often the strategy wins vs. how often it loses.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
Calculate R² (coefficient of determination) between strategy and benchmark returns.
R² is the square of the Pearson correlation between strategy and benchmark returns — how much of the strategy’s variance is explained by benchmark movements. Closer to 1 means the strategy moves in lockstep with the benchmark; closer to 0 means it’s largely independent.
Parameters
Name
Type
Description
Default
returns
pd.Series
Strategy periodic return series.
required
benchmark
pd.Series
Benchmark periodic return series, aligned on shared dates.
required
return_type
ReturnType
Whether returns/benchmark are "simple" or "log" returns.
Calculate the Risk-Adjusted Return (CAGR divided by exposure).
Divides CAGR by :func:exposure (the fraction of periods actually invested, i.e. with non-zero returns), so a strategy that earns its return while sitting in cash most of the time scores higher than one that needed to be fully invested throughout.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
required
return_type
ReturnType
Whether returns are "simple" or "log" returns.
'simple'
periods_per_year
int | None
Annualization frequency. Inferred from the index when not given.
None
rf
float
Annualized risk-free rate, subtracted (after deannualizing) from returns before computing CAGR. Defaults to 0.0.
0.0
Returns
Name
Type
Description
float
Risk-adjusted return.
rebase
stats.rebase(prices, base=100.0)
Rescale a price series to a common starting value.
Useful for plotting or comparing price series that start on different scales (e.g. a strategy’s equity curve against a benchmark index).
Parameters
Name
Type
Description
Default
prices
pd.Series
Price series. Must contain at least one non-null, non-zero value.
Calculate the recovery factor: total return divided by Max Drawdown.
How many multiples of the worst peak-to-trough decline the strategy ultimately earned back — higher values mean faster, more complete recovery from drawdowns.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
required
return_type
ReturnType
Whether returns are "simple" or "log" returns.
'simple'
rf
float
Amount subtracted from total return before dividing. Defaults to 0.0.
The risk of ruin is the probability of losing the entire trading account, from the classic gambler’s-ruin formula applied to the strategy’s per-period win rate.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
Return annualized rolling Jensen alpha relative to a benchmark.
Jensen’s alpha is the annualized return left over after accounting for the return explained by beta exposure to the benchmark — the excess return generated (or lost) beyond what market risk alone would predict.
Parameters
Name
Type
Description
Default
returns
pd.Series
Strategy periodic return series.
required
benchmark
pd.Series
Benchmark periodic return series, aligned on shared dates.
required
window
int
Rolling window size, in periods.
63
return_type
ReturnType
Whether returns/benchmark are "simple" or "log" returns.
'simple'
periods_per_year
int | None
Annualization frequency. Inferred from the index when not given.
Return rolling beta of strategy returns relative to a benchmark.
Beta measures sensitivity to benchmark (market) movements: the covariance of strategy and benchmark returns divided by the benchmark’s variance. A beta of 1 tracks the benchmark 1:1; above 1 amplifies its moves; below 1 dampens them. See also the Capital Asset Pricing Model, which uses beta to model expected return as a function of systematic risk.
Parameters
Name
Type
Description
Default
returns
pd.Series
Strategy periodic return series.
required
benchmark
pd.Series
Benchmark periodic return series, aligned on shared dates.
required
window
int
Rolling window size, in periods.
63
return_type
ReturnType
Whether returns/benchmark are "simple" or "log" returns.
Calculate rolling multi-factor OLS betas over a moving window.
Refits :func:factor_regression’s OLS on each trailing window of aligned observations, e.g. the rolling Fama-French five-factor betas that reveal time-varying exposure a single full-period fit cannot. Following statsmodels.regression.rolling.RollingOLS’s convention, each window’s estimate is stored at the date of its last observation, so the window is a trailing observation window (not a calendar-day window) – with daily data, window=63 is roughly 3 trading months.
Parameters
Name
Type
Description
Default
returns
pd.Series
Strategy periodic return series.
required
factors
pd.DataFrame
DataFrame of periodic factor return columns, aligned to returns.
required
window
int
Trailing window size, in periods. Defaults to 63.
63
return_type
ReturnType
Whether returns is "simple" or "log" returns.
'simple'
rf
str | pd.Series | None
See :func:factor_regression.
'RF'
min_observations
int | None
Minimum aligned observations required before the first (noisier, shorter-window) estimate is produced. Defaults to window (a full window); every estimate still reports its actual observation count in N Obs.
None
Returns
Name
Type
Description
pd.DataFrame
DataFrame indexed like the aligned returns/factors data, with columns
pd.DataFrame
Alpha, one per factor (in factors’ order), R², and N Obs.
pd.DataFrame
Rows before the first min_observations aligned dates are NaN
Return annualized rolling volatility for a periodic return stream.
Volatility is the standard deviation of returns — the most common measure of risk. Unlike Sortino-style measures, it penalizes upside and downside swings equally.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series.
required
window
int
Rolling window size, in periods.
63
return_type
ReturnType
Whether returns are "simple" or "log" returns.
'simple'
periods_per_year
int | None
Annualization frequency. Inferred from the index when not given.
A composite drawdown-aware risk-adjusted return measure (KeyQuant, 2011): total excess return divided by the :func:ulcer_index scaled by a “pitfall” factor — the tail risk of drawdowns (their Conditional VaR) relative to overall return volatility. Penalizes strategies whose drawdowns are both deep/prolonged (high Ulcer Index) and concentrated in a few severe episodes (high pitfall).
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
required
return_type
ReturnType
Whether returns are "simple" or "log" returns.
'simple'
rf
float
Amount subtracted from total return before dividing. Defaults to 0.0.
Calculate the annualized Sharpe ratio of excess returns.
The Sharpe ratio is the annualized mean excess return divided by the annualized volatility of returns — return earned per unit of total risk taken.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
required
return_type
ReturnType
Whether returns are "simple" or "log" returns.
'simple'
periods_per_year
int | None
Annualization frequency. Inferred from the index when not given.
None
rf
float
Annualized risk-free rate, subtracted (after deannualizing) from returns before computing the ratio. Defaults to 0.0.
0.0
smart
bool
Whether to penalize the denominator for return autocorrelation via :func:autocorr_penalty, which otherwise inflates the ratio for serially correlated (e.g. smoothed or illiquid) return streams. Defaults to False.
Skewness measures the degree of asymmetry of the return distribution around its mean. Positive skew means a longer right tail (occasional large gains); negative skew means a longer left tail (occasional large losses).
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
Calculate the tail ratio between the right and left tails of the return distribution.
Ratio of the cutoff percentile (e.g. 95th) to the 1 - cutoff percentile (e.g. 5th) of returns — how large favorable outliers are relative to unfavorable ones.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
required
return_type
ReturnType
Whether returns are "simple" or "log" returns.
'simple'
cutoff
float
Upper percentile used for the right tail; 1 - cutoff is used for the left tail. Defaults to 0.95.
Convert a return series into a cumulative price/equity curve.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
required
return_type
ReturnType
Whether returns are "simple" or "log" returns.
'simple'
base
float
Starting value of the curve. Defaults to 1.0.
1.0
Returns
Name
Type
Description
pd.Series
Cumulative price series starting at base, same index as returns.
trade_stats
stats.trade_stats(trades, by=None)
Calculate trade-level statistics from a canonical trades-format log.
Unlike the return-stream metrics (:func:performance, :func:win_rate, …), these operate on whole round-trip trades – so Win Rate is the share of winning trades, Avg Days the mean holding period, and Exposure the fraction of calendar time (first entry to last exit) with an open position, computed from the union of actual trade spans.
Parameters
Name
Type
Description
Default
trades
pd.DataFrame
Canonical trades-format DataFrame (see :mod:qrt.data.datasets).
required
by
str | None
Optional column to group by (e.g. "exit_reason", "direction", or any feature-snapshot column). When given, the result has one column per group next to the All column.
None
Returns
Name
Type
Description
pd.DataFrame
DataFrame of metrics (rows) by group (columns):
pd.DataFrame
- Trades: Number of round-trip trades.
pd.DataFrame
- Win Rate: Share of non-zero-return trades that were profitable.
pd.DataFrame
- Total Return: Compounded return across the group’s trades.
- Avg MAE / Avg MFE: Mean max adverse/favorable excursion (NaN when the columns are absent).
pd.DataFrame
- Avg Days / Median Days: Holding period in calendar days.
pd.DataFrame
- Exposure: Fraction of the group’s calendar span with an open position.
Raises
Name
Type
Description
ValueError
If trades lacks canonical columns, or by is not a column.
trades_to_positions
stats.trades_to_positions(trades, index)
Convert a trade log into a signed position series over index.
Each trade contributes +1 (long) or -1 (short) on every bar from its entry_time through its exit_time (inclusive); overlapping trades sum. Bars with no open trade are 0 – the basis for exposure timelines and time-in-market analysis.
Parameters
Name
Type
Description
Default
trades
pd.DataFrame
Canonical trades-format DataFrame.
required
index
pd.Index
Target index (e.g. prices.index) to evaluate positions on.
required
Returns
Name
Type
Description
pd.Series
Integer position series named "position" over index.
trades_to_returns
stats.trades_to_returns(trades, prices=None)
Convert a trade log into a periodic return stream.
The bridge from qrt’s canonical trades format (one row per round-trip trade, see :mod:qrt.data.datasets) to the return-stream form every other :mod:qrt.stats/:mod:qrt.plot function consumes.
Without prices, each trade’s whole return is attributed at its exit_time (compounded when several trades exit on the same bar) – fine for aggregate stats, but intra-trade drawdown and volatility are invisible. With prices (a close series covering the trade span), trades are marked to market: each bar in a trade contributes its close-to-close return (entry bar: close vs entry_price; exit bar: exit_price vs previous close), direction-adjusted, and bars with no open trade contribute 0.0 – giving honest drawdown/volatility/ exposure statistics.
Note
For long trades, marked-to-market bar returns compound exactly to the trade’s stored return. For shorts they model a daily rebalanced short position, which compounds slightly differently than the unrebalanced trade-level return – both are correct, they answer different questions.
Parameters
Name
Type
Description
Default
trades
pd.DataFrame
Canonical trades-format DataFrame.
required
prices
pd.Series | None
Optional close series with a DatetimeIndex covering the trade span (e.g. q.data.datasets.load("spy")["close"]).
None
Returns
Name
Type
Description
pd.Series
Periodic simple-return series named "return", indexed by
pd.Series
exit_time (without prices) or by the full prices index
Calculate the annualized Treynor ratio relative to a benchmark.
The Treynor ratio is the annualized mean excess return divided by beta — like the Sharpe ratio, but measuring return earned per unit of systematic (market) risk rather than total risk.
Parameters
Name
Type
Description
Default
returns
pd.Series
Strategy periodic return series.
required
benchmark
pd.Series
Benchmark periodic return series, aligned on shared dates.
required
return_type
ReturnType
Whether returns/benchmark are "simple" or "log" returns.
'simple'
periods_per_year
int | None
Annualization frequency. Inferred from the index when not given.
None
rf
float
Annualized risk-free rate, subtracted (after deannualizing) from returns before computing the ratio. Defaults to 0.0.
The Ulcer Index is the root-mean-square of drawdowns from the running peak, capturing both the depth and duration of drawdowns rather than just their worst point (unlike Max Drawdown).
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
Calculate the Ulcer Performance Index (Martin ratio).
Annualized excess return divided by the :func:ulcer_index — like Calmar, but risk is measured by the depth and duration of drawdowns rather than just the single worst one.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
required
return_type
ReturnType
Whether returns are "simple" or "log" returns.
'simple'
periods_per_year
int | None
Annualization frequency. Inferred from the index when not given.
None
rf
float
Annualized risk-free rate, subtracted (after deannualizing) from returns before computing CAGR. Defaults to 0.0.
Calculate the parametric (variance-covariance) Value at Risk.
Value at Risk assumes normally distributed returns and estimates the maximum expected loss over one period at the given confidence level from the sample mean and standard deviation.
Parameters
Name
Type
Description
Default
returns
pd.Series
Periodic return series (simple or log, per return_type).
required
return_type
ReturnType
Whether returns are "simple" or "log" returns.
'simple'
confidence
float
Confidence level, e.g. 0.95 for a 95% VaR. Defaults to 0.95.
0.95
Returns
Name
Type
Description
float
Value at Risk (a negative number representing a loss).
Simulate forward trades with a randomly varied win rate to stress-test a strategy’s edge.
Unlike :func:montecarlo (which resamples periodic returns to explore plausible histories), this simulates forward, trade-by-trade: each simulated path draws periods future trades by (1) picking a per-simulation win rate randomly perturbed by up to win_rate_variance from the historical win rate, then (2) for each trade, flipping a coin at that win rate and sampling a return with replacement from the historical winning or losing trades accordingly. This directly stresses whether a strategy’s edge holds up if its win rate drifts, rather than just reordering/bootstrapping the exact historical return sequence.
Note
Like the rest of this package, results are expressed as compounded (multiplicative) cumulative returns rather than account-currency dollar P&L — pass per-trade returns in trades, not dollar amounts.
Note
When trades has a DatetimeIndex, paths is indexed by periods timestamps extrapolated forward from the last historical trade date (step size inferred via :func:pandas.infer_freq, falling back to the median spacing for irregular series), continuing the timeline where real_path leaves off, rather than a bare trade count — consistent with the date axes used throughout :mod:qrt.plot. Falls back to a plain 0..periods - 1 trade-count index otherwise.
Parameters
Name
Type
Description
Default
trades
pd.Series
Per-trade return series (simple or log, per return_type) — one entry per historical trade, not necessarily a periodic/calendar series, though a DatetimeIndex is used to date the simulated horizon (see Note above) when present. Must contain at least one winning and one losing (or breakeven) trade.
required
periods
int
Number of future trades to simulate per path. Must be at least 1.
required
sims
int
Number of simulated paths. Must be at least 1.
1000
return_type
ReturnType
Whether trades are "simple" or "log" returns.
'simple'
win_rate_variance
float
Amount by which each simulation’s win rate is randomly perturbed (uniformly, in both directions) from the historical win rate — e.g. 0.1 varies a 55% historical win rate anywhere from 45% to 65% per simulation. Clipped to [0, 1]. Defaults to 0.1.
0.1
ruin
float
Max Drawdown threshold (e.g. -0.9 for -90%) below which a path is considered “ruined” (having lost most or all of its capital). ruin_probability reports the fraction of paths that breached it. Defaults to -0.9.
-0.9
confidence
float
Confidence level for the per-trade confidence_band. Defaults to 0.95.
0.95
seed
int | None
Optional random seed for reproducibility.
None
Returns
Name
Type
Description
dict[str, object]
Dict with keys:
dict[str, object]
- paths: DataFrame of cumulative simulated returns, one column per simulation, indexed either by periods extrapolated future timestamps (when trades has a DatetimeIndex) or a plain 0..periods - 1 trade count otherwise — see the Note above.
dict[str, object]
- real_path: Series of the actual historical cumulative compounded return over the most recent min(periods, len(trades)) trades, for reference/comparison — not part of the simulation. Truncated to periods (rather than the full trades history) so its scale and length stay comparable to the simulated paths, and immediately precedes paths on the timeline when dated.
dict[str, object]
- win_rates: Array of the randomly perturbed win rate used by each simulation.
dict[str, object]
- terminal_stats: Summary statistics (mean, std, min/25%/50%/75%/max) of the final cumulative return across simulations.
dict[str, object]
- max_drawdown_stats: Same summary statistics for each simulation’s Max Drawdown.
dict[str, object]
- confidence_band: DataFrame with Lower/Upper columns holding the confidence-level percentile range of cumulative return at each trade.
dict[str, object]
- make_money_probability: Fraction of simulations with a positive terminal return.
dict[str, object]
- ruin_probability: Fraction of simulations whose Max Drawdown breached ruin.
dict[str, object]
- average_profit/average_drawdown: Mean terminal return / mean Max Drawdown (signed) across simulations.
dict[str, object]
- pnldd_ratio: average_profit / abs(average_drawdown) — average return earned per unit of average drawdown risked.