factrix.metrics.ts_quantile ¶
Time-series quantile bucketing + monotonicity test (issue #5).
Diagnostic for (COMMON, CONTINUOUS, *) and single-asset TIMESERIES
cells: bucket factor history into quantiles and check the conditional
mean forward return per bucket. Catches U-shape / inverted-U /
extreme-only signals that ordinary least squares (OLS) β assumes away (linear) and reports
pass / fail on as a single slope.
Standalone metric — does not enter the registry. See
ARCHITECTURE.md §"Registry procedure vs standalone metric" for the
distinction. SPARSE / binary signals are out of scope; the input gate
redirects to event_quality helpers.
Notes
Pipeline. Per-date aggregation to a common (_f, _r) series
(cross-section step), then quantile-bucketed Newey-West (NW) heteroskedasticity-and-autocorrelation-consistent (HAC) OLS on that
time series; Wald χ² on the top-bottom bucket spread.
factrix.metrics.ts_quantile.ts_quantile_spread ¶
ts_quantile_spread(df: DataFrame, *, factor_col: str = 'factor', return_col: str = 'forward_return', n_groups: int = 5, forward_periods: int | None = None, nw_lags: int | None = None) -> MetricOutput
Bucket time-series factor by historical quantiles, test conditional means.
Reported:
value= top-bottom spread (β_{K-1} - β_0)stat= Wald onH0: β_{K-1} = β_0→ two-sided p in metadatametadata["spearman_rho"]/spearman_p= small-sample monotonicity diagnostic across the K bucket meansmetadata["buckets"]= per-bucket{idx, mean_return, n}
Gate (issue #5): n_unique(factor) >= n_groups * 2. Below the
gate the factor cannot sustain quantile cuts — short-circuits with
a redirect to event_quality.* for binary / sparse signals.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
Long panel; aggregated to per-date |
required |
factor_col
|
str
|
Column carrying the factor. |
'factor'
|
return_col
|
str
|
Column carrying the forward return. |
'forward_return'
|
n_groups
|
int
|
Number of quantile buckets |
5
|
forward_periods
|
int | None
|
Overlap horizon of the forward return; floors the Newey-West (NW) bandwidth. |
None
|
nw_lags
|
int | None
|
Override for the NW lag count. |
None
|
Returns:
| Type | Description |
|---|---|
MetricOutput
|
|
MetricOutput
|
spread; bucket detail and the Spearman monotonicity diagnostic |
MetricOutput
|
live in |
MetricOutput
|
input shape is insufficient (no |
MetricOutput
|
column, fewer than |
MetricOutput
|
variation below |
Notes
Aggregate the panel to per-date (_f, _r), ordinal-rank into
K = n_groups buckets by historical _f quantile, run
r_t = sum_k beta_k * I(bucket_t = k) + eps with NW heteroskedasticity-and-autocorrelation-consistent (HAC)
covariance, and form the spread value = beta_{K-1} - beta_0
with Wald p-value on H0: beta_{K-1} = beta_0. A
Spearman(0..K-1, beta) rank-monotonicity diagnostic across
buckets is reported alongside.
factrix uses NW HAC + Wald rather than Welch t for cross-method
comparability with ts_asymmetry / ts_beta_t_nw and
because forward_periods > 1 breaks the iid assumption Welch
relies on.
References
Newey-West 1987: HAC covariance under-pinning
the Wald test.
Andrews 1991: Bartlett growth rate T^(1/3)
used for the default lag.
Hansen-Hodrick 1980: forward_periods - 1
floor for overlapping returns.
Examples:
>>> import factrix as fx
>>> from factrix.preprocess import compute_forward_return
>>> from factrix.metrics.ts_quantile import ts_quantile_spread
>>> panel = compute_forward_return(
... fx.datasets.make_cs_panel(n_assets=80, n_dates=180, seed=0),
... forward_periods=5,
... )
>>> result = ts_quantile_spread(panel, n_groups=5)
>>> result.name
'ts_quantile_spread'
Timeseries-mode conventions
FACTOR_ADF_P persistence diagnostic, plain stage-1 SE rationale,
and the forward_periods vs signal_horizon bias framing apply
here as for the rest of the TS-mode family. See
Timeseries-mode conventions.
Use cases¶
-
Detect non-linear factor → return shape
Linear ordinary least squares (OLS) \(\beta\) reports a single slope and fails on U-shape / inverted-U / extreme-only signals.
ts_quantile_spreadaggregates the panel to a per-date \((\_f, \_r)\) series, buckets_finto \(K\) historical quantiles, and reads the conditional mean return per bucket — preserves whatever shape the relationship has. -
Top-bottom spread significance with Newey-West (NW) heteroskedasticity-and-autocorrelation-consistent (HAC)
Headline is \(\beta_{K-1} - \beta_0\) — the conditional mean difference between the top and bottom quantile of factor history. Inference is a Wald \(\chi^2\) on \(H_0: \beta_{K-1} = \beta_0\) with Newey-West HAC covariance; kernel choice is consistent with
ts_asymmetryso cross-method \(p\)-values stay comparable under overlapping forward returns. -
Rank-monotonicity diagnostic across buckets
metadata["spearman_rho"]/spearman_pgive a non-parametric Spearman rank check on the bucket-mean sequence \((\beta_0, \ldots, \beta_{K-1})\). Catches a monotone shape that the top-bottom Wald would conflate with a wider U.
Worked example — quantile-bucketed conditional means on a common-factor panel¶
broadcast common factor → ts_quantile_spread
import factrix as fx
import polars as pl
from factrix.metrics.ts_quantile import ts_quantile_spread
from factrix.preprocess import compute_forward_return
# Build a panel whose ``factor`` is broadcast (one value per date,
# shared across all assets) — VIX / USD-index style.
raw = fx.datasets.make_cs_panel(
n_assets=50, n_dates=1000, ic_target=0.08, seed=2024,
)
common = raw.group_by("date").agg(pl.col("factor").mean().alias("factor"))
panel = raw.drop("factor").join(common, on="date")
panel = compute_forward_return(panel, forward_periods=5)
out = ts_quantile_spread(panel, n_groups=5, forward_periods=5)
print(out.value, out.stat, out.metadata["p_value"])
# 0.0018 3.20 0.0014 (approximate)
print(out.metadata["spearman_rho"], out.metadata["spearman_p"])
# 0.90 0.037 (approximate; positive ⇒ monotone shape)
for b in out.metadata["buckets"]:
print(b)
# {"idx": 0, "mean_return": -0.00091, "n": 199}
# ...
# {"idx": 4, "mean_return": 0.00091, "n": 199}
See also¶
-
ts_beta/ts_asymmetry
Linear \(\beta\) and signed-slope asymmetry diagnostics on the same broadcast-factor panel. Use
ts_quantile_spreadwhen a Spearman rank check or U-shape detection matters; the others assume a monotone or piecewise-linear response. -
event_qualityfamily
Where the input gate redirects when the factor cannot sustain quantile cuts (binary / sparse signals):
event_hit_rate/event_ic/profit_factor/event_skewness. -
by_slice/slice_pairwise_test
Axis-agnostic slice dispatcher and pairwise Wald (Holm / Romano-Wolf adjusted \(p\)) for per-slice spread comparisons.
-
Statistical methods
NW HAC SE, Andrews bandwidth, Hansen-Hodrick overlap floor, and the Wald-on-linear-restriction framing.
-
Timeseries-mode conventions
FACTOR_ADF_P, plain stage-1 SE rationale,forward_periodsvssignal_horizonbias framing. -
Metric applicability reference
When this metric applies and the sample-size guards that gate it (
MIN_PORTFOLIO_PERIODS_HARD,n_distinct(factor) >= n_groups * 2). -
Common × Continuous landing
Adjacent macro-common metrics in the same cell.