Skip to content

factrix.stats

Statistical tooling shared across the library.

The slice-test selection classes carry pure identity semantics (name / description, no compute); their numerics live in the slice-test dispatch path and the factrix._stats kernels. Series-mean HAC inference lives in factrix.inference. WaldNWCluster / WaldTwoWayCluster — cluster-robust Wald χ² selection-only instances for slice contrasts. DriscollKraay — Driscoll-Kraay (1998) cross-section-robust HAC SE for pooled-panel slopes; selection-only identity handle, numerics consumed by pooled_beta. multiple_testing — BHY false-discovery plus Holm and Romano-Wolf family-wise error-rate adjustments across a declared search family. bootstrap — stationary-bootstrap resampling + CI for dependent series.

DriscollKraay

Driscoll & Kraay (1998) cross-section-robust heteroskedasticity-and-autocorrelation-consistent (HAC) SE for a pooled-panel slope.

Aggregates the per-observation OLS scores cross-sectionally within each period, then runs a Bartlett-kernel HAC on the resulting time series of cross-sectional sums and sandwiches with (X'X)⁻¹. The result is robust to arbitrary contemporaneous cross-sectional correlation — the gap a one-way cluster-on-date SE leaves open, which understates SE for small, cross-sectionally correlated panels (Petersen (2009)). Numerics live in factrix._stats.hac._driscoll_kraay_cov; pooled_beta drives them directly.

Pass an instance to a function to make the inference choice explicit. Constructor takes no arguments in this release. pooled_beta resolves the Bartlett bandwidth, finite-sample covariance scale, and effective degrees of freedom from the period count and overlap horizon; an explicit-lag override lives on the metric call rather than on this identity handle.

WaldNWCluster

Cluster-robust Wald χ² with Newey-West (NW) Bartlett heteroskedasticity-and-autocorrelation-consistent (HAC) + 1-way slice cluster.

Backs the slice test on a per-date metric panel: K parallel per-slice metric series (information coefficient (IC), FM λ, etc.) are stacked and a Wald contrast tests the equality of slice means under joint NW HAC of the K-vector. Numerics live in factrix._stats.wald._wald_nw_cluster_means.

Applicability is restricted to (INDIVIDUAL, DENSE) — the PANEL inference cells whose per-date scalars feed the stacked panel. COMMON cells produce one number per date by definition of the scope and have no within-cell cross-section to slice over.

This is the sole analytic estimator the slice tests run; it is applied internally (the slice-test functions take no estimator argument). The class names that inference path for documentation and introspection.

Constructor takes no arguments in this release; the Bartlett-kernel bandwidth is resolved by _wald_nw_cluster_means (auto_bartlett(T) default, floored at overlap_periods - 1 by the slice tests to cover overlapping-return autocorrelation).

WaldTwoWayCluster

Two-way cluster Wald χ² on (date, asset) — Cameron-Gelbach-Miller (2011).

Backs the raw asset-date panel inference path (factor × slice interaction with full panel SE). Numerics live in factrix._stats.wald._wald_two_way_cluster.

Reserved interface; no function consumes it until factor_decomposition lands later.

bhy_adjust

bhy_adjust(p_values: ArrayLike, fdr: float = 0.05, *, n_tests: int | None = None) -> ndarray

Benjamini-Hochberg-Yekutieli (BHY) step-up rejection mask.

Parameters:

Name Type Description Default
p_values ArrayLike

1-D array of p-values in [0, 1]. Each must come from the same test family (e.g. all information coefficient (IC) p-values or all CAAR p-values). Public screening helpers enforce family consistency before calling this primitive.

required
fdr float

Target false discovery rate (FDR) (default 0.05).

0.05
n_tests int | None

Full candidate family size for two-stage screening. If caller already pre-filtered from a larger pool (e.g. 1000 candidates → 50 submitted), pass the pre-filter size here. Must be >= len(p_values). None (default) uses len(p_values), i.e. single-stage BHY.

None

Returns:

Type Description
ndarray

Boolean mask of length len(p_values) — True where the null

ndarray

is rejected. Order matches the input.

References
  • Benjamini & Yekutieli (2001). "The Control of the False Discovery Rate in Multiple Testing under Dependency." Annals of Statistics, 29(4), 1165–1188. The c(m) = Σ 1/i correction underlying this step-up rule.

bhy_adjusted_p

bhy_adjusted_p(p_values: ArrayLike, *, n_tests: int | None = None) -> ndarray

Per-hypothesis Benjamini-Hochberg-Yekutieli (BHY)-adjusted p-values (clipped at 1).

Formula: scale p_(k) by (m * c(m)) / k then cummin from the right to enforce monotonicity in ranked order. Gives a stable per-factor "how significant under false discovery rate (FDR) control" number.

n_tests follows the same contract as bhy_adjust — pass the pre-filter size when the submitted p's are survivors of a larger candidate family.

References
  • Benjamini & Yekutieli (2001). "The Control of the False Discovery Rate in Multiple Testing under Dependency." Annals of Statistics, 29(4), 1165–1188. The per-hypothesis adjusted-p mapping derived from the same step-up rule used by bhy_adjust.

bootstrap_mean_ci

bootstrap_mean_ci(values: ndarray, *, n_resamples: int = 999, ci: float = 0.95, block_length: float | None = None, rng: Rng = None, statistic: Callable[[ndarray], float] | None = None, method: Literal['studentized', 'percentile'] = 'studentized') -> BootstrapCI

Stationary-bootstrap confidence interval for a statistic.

Default statistic is the arithmetic mean (matches the parametric t-test's H₀ null). Pass a callable taking a 1-D array and returning a scalar to CI other statistics (e.g. Sharpe, median, skewness); those require method="percentile" (see below).

Studentized by default. The interval is the bootstrap-t

$$\left[\bar x - t^*_{1-\alpha}\,\widehat{se},\;
        \bar x - t^*_{\alpha}\,\widehat{se}
\right],\qquad
  t^*_b = \frac{\bar x^*_b - \bar x}{\widehat{se}^*_b}$$

with \(\widehat{se}\) the batch-means block SE (:func:factrix._stats.bootstrap._batch_means_se) at the resolved block length. The percentile interval is first-order accurate only (DiCiccio-Efron (1996)), and under dependence it compounds the block bootstrap's finite-sample long-run-variance shortfall — which is exactly the regime this module's docstring reaches for it.

Measured two-sided coverage at nominal 95% on an AR(1) mean (500 sims, B=999):

φ T percentile studentized
0.0 100 0.940 0.942
0.0 500 0.946 0.954
0.5 100 0.862 0.916
0.5 500 0.924 0.940
0.8 100 0.792 0.890
0.8 500 0.902 0.934

Studentizing removes roughly half the under-coverage; it does not remove it. A short, strongly persistent series still under-covers, and no resampling scheme fixes that — read the last row as the disclosed limit, not as a residual bug.

Parameters:

Name Type Description Default
values ndarray

1-D array of the original series, at least 2 finite observations.

required
n_resamples int

Resample count. Must be at least BOOTSTRAP_RESAMPLES_FLOOR; below that the interval endpoints are resampling noise. At B=1 the old implementation returned a zero-width interval that did not even contain its own point estimate.

999
ci float

Two-sided coverage, e.g. 0.95 for a 95% CI. Must be in (0, 1).

0.95
block_length float | None

See stationary_bootstrap_resamples. Validated against [1, ceil(min(3*sqrt(T), T/3))]; a length at or past T used to collapse every resample to a rotation of the input and return a zero-width interval.

None
rng Rng

An int reproduces the interval, None draws one from system entropy, and a numpy.random.Generator is used as-is and advanced by the call. Not reported back: this function returns an interval, not a metadata-carrying result.

None
statistic Callable[[ndarray], float] | None

Scalar function applied to each resample. Defaults to np.mean.

None
method Literal['studentized', 'percentile']

"studentized" (default) or "percentile". The studentized root needs a block SE of the statistic on every resample, which this module only has for the mean, so a custom statistic must pass method="percentile" explicitly. Refusing rather than silently downgrading keeps the accuracy order of the returned interval something the caller chose.

'studentized'

Returns:

Type Description
BootstrapCI

class:BootstrapCI — (low, high, estimate) where

BootstrapCI

estimate is the statistic on the original sample.

Raises:

Type Description
UserInputError

ci outside (0, 1), an unknown method, non-1-D values, fewer than 2 observations, n_resamples below the floor, or method="studentized" with a custom statistic. One exception type across every input check — UserInputError is the repo's user-facing shape.

References
  • Politis & Romano (1994). "The Stationary Bootstrap." Journal of the American Statistical Association, 89(428), 1303–1313. Underlying resampling scheme.
  • DiCiccio & Efron (1996). "Bootstrap Confidence Intervals." Statistical Science 11(3), 189–228. Accuracy ordering that makes the studentized interval the default here.
  • Götze & Künsch (1996). Second-order correctness of the blockwise bootstrap for a studentized root.

holm_adjusted_p

holm_adjusted_p(p_values: ArrayLike, *, n_tests: int | None = None) -> ndarray

Holm (1979) step-down FWER-adjusted p-values.

Holm controls the probability of at least one false rejection within a declared family under arbitrary dependence. It is appropriate when a search produces one selected winner or when every retained hypothesis must be protected from any false positive. Use BHY instead when the goal is to retain a batch while controlling the expected false-discovery proportion.

The submitted p-values are ordered from smallest to largest and scaled by m, m - 1, ..., m - n + 1 before a cumulative maximum enforces the step-down rejection order. m is n_tests when supplied and otherwise len(p_values).

Parameters:

Name Type Description Default
p_values ArrayLike

One-dimensional, finite p-values in [0, 1] from one search family.

required
n_tests int | None

Full number of hypotheses tried when p_values contains only the most-significant survivors of a larger search. Must be an integer >= len(p_values). Omitted hypotheses are assumed to rank after every submitted p-value; submit the complete family if that ordering is not guaranteed.

None

Returns:

Type Description
ndarray

Adjusted p-values in input order as a NumPy array, clipped to

ndarray

[0, 1].

References
  • Holm (1979). "A Simple Sequentially Rejective Multiple Test Procedure." Scandinavian Journal of Statistics, 6(2), 65-70.

partial_conjunction_p

partial_conjunction_p(p_values: ArrayLike, *, min_pass: int) -> float

Bonferroni-style partial conjunction p-value (Benjamini-Heller (2008)).

Tests H_0^{k/m}: at most k - 1 of the m alternatives are true, against H_1^{k/m}: at least k are true. The combined p-value is

``p_PC = min(1, (m - k + 1) * p_((k)))``

where p_((k)) is the k-th smallest of the m p-values (1-indexed). k = m reduces to max(p) (full conjunction); k = 1 reduces to Bonferroni-corrected min(p).

min_pass must be declared before the p-values are seen. The combined p-value is not monotone in k: the multiplier m - k + 1 falls as the order statistic p_((k)) rises, so demanding more passing conditions can lower p_PC — for p = [0.01, 0.02, 0.9], k = 2 gives 0.04 while k = 3 gives 0.9, and for p = [0.01, 0.4, 0.5] the ordering reverses. Choosing k after inspecting the p-values is a selection effect that no downstream step-up corrects.

Parameters:

Name Type Description Default
p_values ArrayLike

1-D array of m per-condition p-values for a single hypothesis (e.g. one factor across m universes).

required
min_pass int

k — the minimum number of conditions required to be significant. Must satisfy 1 <= min_pass <= m.

required

Returns:

Type Description
float

The PC p-value, clipped to [0, 1].

References
  • Benjamini & Heller (2008). "Screening for Partial Conjunction Hypotheses." Biometrics, 64(4), 1215–1222. The partial-conjunction test implemented here.

romano_wolf_adjusted_p

romano_wolf_adjusted_p(statistics: ArrayLike, bootstrap_statistics: ArrayLike, *, one_sided: bool = False, expected_warnings: tuple[str, ...] = ()) -> ndarray

Romano-Wolf (2005) step-down max-t adjusted p-values.

Romano-Wolf controls the family-wise error rate while using the joint bootstrap distribution to account for dependence among hypotheses. Each bootstrap row must be one joint draw across all m hypotheses. Building columns with independent resampling would destroy that dependence and invalidate the method's power advantage over Holm.

This is an expert-level adjusted-p primitive, not a resampling workflow. The caller is responsible for generating bootstrap statistics under the joint null, centring them at the null, and using the same studentization as the observed statistics. Every hypothesis tried in the search must occupy one column; unlike holm_adjusted_p(n_tests=...), this function cannot account for omitted hypotheses without their joint bootstrap statistics. The empirical p-value grid has resolution 1 / (B + 1) because the calculation uses add-one smoothing. Bootstrap values tied with the observed statistic count as exceedances (>=), a conservative tie rule for discrete bootstrap distributions.

Parameters:

Name Type Description Default
statistics ArrayLike

Finite one-dimensional observed test statistics of length m. Large positive values favour rejection; two-sided mode uses their absolute values.

required
bootstrap_statistics ArrayLike

Finite (B, m) bootstrap test-statistic matrix. Rows are joint null draws and columns align exactly with statistics.

required
one_sided bool

True for the positive-tail alternative or False (default) for a two-sided absolute-value test.

False
expected_warnings tuple[str, ...]

:class:~factrix.WarningCode values declaring a known bootstrap regime. Declared advisories remain method facts; only their UserWarning echo is suppressed.

()

Returns:

Type Description
ndarray

Adjusted p-values in input order as a NumPy array, each in [0, 1].

References
  • Romano & Wolf (2005). "Stepwise Multiple Testing as Formalized Data Snooping." Econometrica, 73(4), 1237-1282.
  • Romano & Wolf (2016). "Efficient Computation of Adjusted P-Values for Resampling-Based Stepdown Multiple Testing." Statistics & Probability Letters, 113, 38-40.

simes_p

simes_p(p_values: ArrayLike) -> float

Simes (1986) global-null p-value for a group of tests.

Formula: p_Simes = min_{k=1..m} (m / k) * p_((k)) where p_((k)) is the k-th smallest of the m p-values (1-indexed). Tests the global null "all m nulls hold" against "at least one alternative is true"; valid under independence and positive regression dependence on a subset (PRDS).

Yekutieli (2008) uses Simes as the default group representative in hierarchical false discovery rate (FDR) procedures — it dominates Bonferroni (m * min(p)) and preserves group-level FDR control when fed to an outer Benjamini-Hochberg-Yekutieli (BHY) step-up.

Parameters:

Name Type Description Default
p_values ArrayLike

1-D array of m p-values for one group. m >= 1.

required

Returns:

Type Description
float

The Simes combined p-value, clipped to [0, 1].

Raises:

Type Description
UserInputError

len(p_values) == 0 (Simes is undefined on an empty group).

References
  • Simes (1986). "An Improved Bonferroni Procedure for Multiple Tests of Significance." Biometrika, 73(3), 751–754. The combined-test formula implemented here.
  • Yekutieli (2008). "Hierarchical False Discovery Rate-controlling Methodology." Journal of the American Statistical Association, 103(481), 309–316. Uses Simes as the group representative in hierarchical FDR procedures fed to an outer BHY step-up.

stationary_bootstrap_resamples

stationary_bootstrap_resamples(values: ndarray, n_resamples: int = 999, *, block_length: float | None = None, rng: Rng = None) -> ndarray

Draw n_resamples stationary-bootstrap resamples of values.

Each resample has the same length T as the input. One-dimensional input returns (B, T); two-dimensional (T, m) input returns (B, T, m) and applies the same sampled row indices to every column. The latter preserves cross-hypothesis dependence for joint bootstrap procedures such as Romano-Wolf. Blocks have geometric lengths with mean block_length and sampling is circular.

Parameters:

Name Type Description Default
values ndarray

Finite (T,) time series or aligned (T, m) matrix. Matrix columns are always resampled jointly; do not call the function separately per column when cross-column dependence matters.

required
n_resamples int

Number of resamples to draw.

999
block_length float | None

Mean geometric block length. Defaults to the Politis-White (2004) automatic spectral plug-in (falling back to the practical 1.75 * T^(1/3) rule when the series is too short or degenerate), clamped to [1, ceil(min(3*sqrt(T), T/3))] — arch's b_max. Bounding L relative to T keeps enough effective blocks (~T/L) for the resample distribution to carry information. block_length=1 reduces to the ordinary iid bootstrap (Efron). An explicitly supplied value is validated against the same bound and raises UserInputError outside it, rather than being silently passed through: at L >= T a circular resample degenerates to a rotation of the input.

None
rng Rng

An int makes the resample reproducible, None draws one from system entropy, and a numpy.random.Generator is used as-is and advanced by the call, so consecutive calls on one generator draw different resamples. The resolved seed is not reported: this function returns draws, not an inference.

None

Returns:

Type Description
ndarray

(n_resamples, T) array for vector input or

ndarray

(n_resamples, T, m) for matrix input.

Notes

This function returns resamples, not inference. Forming a percentile interval or an empirical p directly from the returned array gives a first-order accurate interval (DiCiccio-Efron (1996)) — it forgoes the studentization :func:bootstrap_mean_ci applies by default, and under dependence it compounds the block bootstrap's finite-sample long-run-variance shortfall. The measured cost is the percentile column of the AR(1) coverage table in :func:bootstrap_mean_ci: at phi=0.8, T=100 percentile covers 0.792 against studentized 0.890 (nominal 0.95).

This is the default path for a paired custom statistic, not an edge case. Joint resampling of aligned columns is only available here (2-D input), and :func:bootstrap_mean_ci takes 1-D input and requires method="percentile" for any custom statistic — the studentized root needs a block SE of the statistic on every resample, which this module has only for the mean. So a paired, non-mean statistic reaches percentile accuracy from either entry point. Prefer :func:bootstrap_mean_ci whenever the statistic is a mean of a 1-D series; when it is not, read the resulting interval as first-order accurate and size the sample accordingly.

References
  • Politis & Romano (1994). "The Stationary Bootstrap." Journal of the American Statistical Association, 89(428), 1303–1313. Stationary block bootstrap with geometric block lengths — the resampling scheme this function implements.
  • Politis & White (2004). "Automatic Block- Length Selection for the Dependent Bootstrap." Econometric Reviews, 23(1), 53–70. Source of the spectral plug-in block_length=None resolves to.

Inference-method instances + standalone statistical helpers under factrix.stats.

Estimator catalogue

The selection-only estimator classes under factrix.stats:

Class Algorithm family Description
WaldNWCluster Cluster-Wald \(\chi^2\) (1-way cluster on slice) 1-way cluster-robust Wald contrast test
WaldTwoWayCluster Cluster-Wald \(\chi^2\) (two-way cluster on (date, asset)) 2-way cluster-robust Wald contrast test
DriscollKraay Driscoll-Kraay cross-section-robust HAC SE Selection-only base Estimator for Driscoll-Kraay robust standard errors

Multiplicity / bootstrap utilities

  • bhy_adjust(p_values, fdr=0.05, *, n_tests=None): Benjamini-Yekutieli step-up rejection mask. Returns a boolean array.
  • bhy_adjusted_p(p_values, *, n_tests=None): BHY-adjusted p-values (clipped at 1.0).
  • holm_adjusted_p(p_values, *, n_tests=None): Holm step-down FWER-adjusted p-values under arbitrary dependence. Use FWER when a search selects one winner or every retained hypothesis must avoid any false positive; use FDR when retaining a batch and controlling its expected false-discovery proportion.
  • romano_wolf_adjusted_p(statistics, bootstrap_statistics, *, one_sided=False, expected_warnings=()): Dependence-aware step-down max-t FWER-adjusted p-values. This expert primitive requires every searched hypothesis and each bootstrap row to be a joint, null-centred draw with the same studentization as the observed statistics; independently resampled or omitted columns are invalid. Its empirical p-value resolution is 1 / (B + 1). An off-centre bootstrap and fewer than 99 resamples emit the declarable romano_wolf_uncentred_bootstrap and romano_wolf_few_resamples codes. Use Holm when a valid joint bootstrap family is unavailable.
  • simes_p(p_values): Simes (1986) global-null p-value for a group of tests — min_k (m / k) * p_((k)). Valid under independence and positive regression dependence on a subset (PRDS); the default group representative in hierarchical FDR.
  • partial_conjunction_p(p_values, *, min_pass): Benjamini-Heller (2008) partial conjunction p-value for "at least min_pass of the m alternatives are true" — min(1, (m - min_pass + 1) * p_((min_pass))).
  • stationary_bootstrap_resamples(values, n_resamples, ...): Politis-Romano (1994) bootstrap resamples. An aligned (T, m) per-period statistic matrix is resampled with common row indices and returns (B, T, m), preserving cross-hypothesis dependence for Romano-Wolf; separate per-column calls do not.
  • bootstrap_mean_ci(values, *, n_resamples, ci, ...): Stationary-bootstrap CI for a statistic.

Holm and BHY adjust p-values as supplied; they neither validate nor repair producer calibration. The caller must decide whether warning-coded results are suitable for the declared family. A family may contain a documented mix of two-sided, greater, and less alternatives because the procedures do not reinterpret tails. Factrix provides no generic tail-conversion helper because that conversion depends on the producer's null distribution and contract.

romano_wolf_adjusted_p remains an expert primitive: the caller must provide the complete joint, H0-centred, correctly studentized (B, m) matrix. Factrix does not currently expose a multi_factor.romano_wolf or panel-aware bootstrap workflow; use Holm unless that joint bootstrap contract is available.