openquant.backtest_stats
Compiled from Rust (crates/pyopenquant). Signatures and types are from the shipped .pyi stubs; descriptions are the functions’ own docstrings, as help() shows them. All Python modules.
Functions
all_bets_concentration
all_bets_concentration(
timestamps: Sequence[str],
returns: Sequence[float],
) -> tuple[float | None, float | None, float | None]Concentration of positive returns, negative returns, and bets over time.
AFML Snippet 14.3. Each component is bets_concentration over: the non-negative returns, the negative returns, and the number of bets per calendar day from the first to the last date (days without bets count as zero). AFML and mlfinlab group the time component by month, so the time index here is higher on data with gaps; compare it only with itself.
Parameters
timestamps: list[str]Bet timestamps as
"%Y-%m-%d %H:%M:%S"(an optional fractional second is accepted), in increasing order; the first and last define the day range.returns: list[float]Return of each bet.
Returns
- tuple[float | None, float | None, float | None]
(positive, negative, time)concentrations; each is None whenbets_concentrationhas too few values or they sum to zero.
Raises
- ValueError
If
timestampsandreturnsdiffer in length, or a timestamp does not parse.
average_holding_period
average_holding_period(
timestamps: Sequence[str],
positions: Sequence[float],
) -> float | NoneAverage holding period of a position series, in days.
AFML Snippet 14.2. Tracks a size-weighted average entry time as the position grows, and records a holding time each time it shrinks or flips, weighted by the size exited. Returns the weighted mean of those holding times in days (86,400 seconds; whole seconds only).
Parameters
timestamps: list[str]Bar timestamps as
"%Y-%m-%d %H:%M:%S"(an optional fractional second is accepted), in increasing order.positions: list[float]Target position at each timestamp.
Returns
- float | None
The average holding period in days, or None if the input is empty or the position is never reduced.
Raises
- ValueError
If
timestampsandpositionsdiffer in length, or a timestamp does not parse.
bets_concentration
bets_concentration(returns: Sequence[float]) -> float | NoneNormalised Herfindahl-Hirschman concentration of a set of returns.
AFML Snippet 14.3. Each return's share is r_i / sum(r) and the index is (HHI - 1/n) / (1 - 1/n): 0 when every bet contributed equally, 1 when one bet made everything. Mixing signs makes the shares meaningless; pass one sign at a time, as all_bets_concentration does.
Parameters
returns: list[float]Returns of the bets, all of one sign.
Returns
- float | None
The concentration, or None for two or fewer returns or when they sum to zero.
deflated_sharpe_ratio
deflated_sharpe_ratio(
observed_sr: float,
sr_estimates: Sequence[float],
number_of_returns: int,
skewness: float,
kurtosis: float,
estimates_param: bool = False,
benchmark_out: bool = False,
) -> floatDeflated Sharpe ratio: the PSR against the best Sharpe ratio expected from skill-less trials.
AFML 14.7.3 (Bailey and Lopez de Prado, 2014). The benchmark is SR_0 = sigma_SR * ((1 - g) Z^-1(1 - 1/N) + g Z^-1(1 - 1/(N e))), with g the Euler-Mascheroni constant, and the result is probabilistic_sharpe_ratio(observed_sr, SR_0, number_of_returns, skewness, kurtosis). Units follow probabilistic_sharpe_ratio: per-period Sharpe ratios, raw kurtosis. The formula assumes independent trials; correlated variations of one idea overstate N.
Parameters
observed_sr: floatObserved per-period Sharpe ratio of the selected strategy.
sr_estimates: list[float]With
estimates_param=False, every trial's per-period Sharpe ratio (sigma_SRis their population standard deviation,Ntheir count). Withestimates_param=True,[sigma_SR, N].number_of_returns: intNumber of returns
observed_srwas estimated from.skewness: floatSkewness of the returns.
kurtosis: floatRaw kurtosis of the returns (3 for a normal distribution).
estimates_param: bool, default FalseWhether
sr_estimatesis[sigma_SR, N]rather than the trials' Sharpe ratios.benchmark_out: bool, default FalseReturn the benchmark
SR_0instead of the deflated Sharpe ratio.
Returns
- float
The deflated Sharpe ratio (a probability in
[0, 1]), orSR_0whenbenchmark_outis True.
Raises
- ValueError
If
sr_estimateshas fewer than two values, orestimates_paramis True and the number of trialssr_estimates[1]is NaN or not above 1.
drawdown_and_time_under_water
drawdown_and_time_under_water(
timestamps: Sequence[str],
returns: Sequence[float],
dollars: bool = False,
) -> tuple[list[float], list[float]]Drawdowns and time under water of a cumulative series.
AFML Snippet 14.4, reproducing mlfinlab. Despite the parameter name, returns must be a cumulative series (equity, NAV or cumulative PnL). For every high-water mark followed by a dip, reports the drawdown (1 - trough / peak for a positive series, or peak - trough with dollars=True) and the time under water in years (365.25 days) from that high-water mark to the next one that itself had a drawdown, or to the end of the series. That is not the time to recovery: new highs without a dip after them extend it.
Parameters
timestamps: list[str]Timestamps as
"%Y-%m-%d %H:%M:%S"(an optional fractional second is accepted), in increasing order.returns: list[float]Cumulative series value at each timestamp.
dollars: bool, default FalseReport drawdowns as
peak - troughinstead of1 - trough / peak.
Returns
- tuple[list[float], list[float]]
(drawdowns, time_under_water_years), one entry per high-water mark that was followed by a dip (both empty for empty input).
Raises
- ValueError
If
timestampsandreturnsdiffer in length, or a timestamp does not parse.
information_ratio
information_ratio(
returns: Sequence[float],
benchmark: float,
entries_per_year: float,
) -> floatAnnualised information ratio against a constant per-period benchmark return.
Equivalent to sharpe_ratio(returns - benchmark, entries_per_year, 0.0): the mean excess return over its sample standard deviation (ddof=1), times sqrt(entries_per_year). Nothing is validated; fewer than two returns give NaN.
Parameters
returns: list[float]Per-period returns.
benchmark: floatPer-period benchmark return, in the same units as
returns.entries_per_year: floatNumber of return periods per year (252 for daily).
Returns
- float
The annualised information ratio.
minimum_track_record_length
minimum_track_record_length(
observed_sr: float,
benchmark_sr: float,
skewness: float,
kurtosis: float,
alpha: float,
) -> floatMinimum track record length: the returns needed for the PSR to reach 1 - alpha.
AFML 14.7.2 (Bailey and Lopez de Prado, 2012). Returns 1 + (1 - skew SR + (kurt - 1) / 4 SR^2) (Z^-1(1 - alpha) / (SR - SR*))^2, a number of return periods. Units follow probabilistic_sharpe_ratio: per-period Sharpe ratios, raw kurtosis. Only meaningful when observed_sr > benchmark_sr: the difference is squared, so an underperforming strategy gets a finite positive answer and an equal one gets infinity.
Parameters
observed_sr: floatObserved per-period Sharpe ratio.
benchmark_sr: floatPer-period Sharpe ratio to beat.
skewness: floatSkewness of the returns.
kurtosis: floatRaw kurtosis of the returns (3 for a normal distribution).
alpha: floatSignificance level in
[0, 1](e.g. 0.05).
Returns
- float
The minimum number of return periods.
Raises
- ValueError
If
alphais outside[0, 1].
probabilistic_sharpe_ratio
probabilistic_sharpe_ratio(
observed_sr: float,
benchmark_sr: float,
number_of_returns: int,
skewness: float,
kurtosis: float,
) -> floatProbabilistic Sharpe ratio: the probability that the true Sharpe ratio exceeds a benchmark.
AFML 14.7.2 (Bailey and Lopez de Prado, 2012). Returns Phi((SR - SR*) sqrt(n - 1) / sqrt(1 - skew SR + (kurt - 1) / 4 SR^2)). The Sharpe ratios must be per-period (not annualised; passing an annualised ratio silently overstates the confidence), and kurtosis is raw kurtosis (3 for normal returns), not excess kurtosis.
Parameters
observed_sr: floatObserved per-period Sharpe ratio.
benchmark_sr: floatPer-period Sharpe ratio to beat.
number_of_returns: intNumber of returns the Sharpe ratio was estimated from.
skewness: floatSkewness of the returns.
kurtosis: floatRaw kurtosis of the returns (3 for a normal distribution).
Returns
- float
A probability in
[0, 1].
sharpe_ratio
sharpe_ratio(
returns: Sequence[float],
entries_per_year: float,
risk_free_rate: float,
) -> floatAnnualised Sharpe ratio of per-period returns.
Computes (mean - risk_free_rate) / std * sqrt(entries_per_year) with the sample standard deviation (ddof=1), AFML 14.7.1. Nothing is validated: an empty list or a single return gives NaN, and constant returns give an infinite ratio.
Parameters
returns: list[float]Per-period returns.
entries_per_year: floatNumber of return periods per year (252 for daily).
risk_free_rate: floatPer-period risk-free rate, in the same units as
returns(not annualised).
Returns
- float
The annualised Sharpe ratio.
timing_of_flattening_and_flips
timing_of_flattening_and_flips(
timestamps: Sequence[str],
positions: Sequence[float],
) -> list[str]Timestamps at which a position was closed or reversed.
AFML Snippet 14.1. A flattening is a bar where the position goes from non-zero to zero; a flip is a bar where the position changes sign. The result is sorted, deduplicated and always ends with the last timestamp of the input, so an open position is treated as closed there.
Parameters
timestamps: list[str]Bar timestamps as
"%Y-%m-%d %H:%M:%S"(an optional fractional second is accepted), in increasing order.positions: list[float]Target position at each timestamp.
Returns
- list[str]
Timestamps of flattenings and flips plus the last timestamp (empty for empty input).
Raises
- ValueError
If
timestampsandpositionsdiffer in length, or a timestamp does not parse.