factrix.metrics.monotonicity ¶
Monotonicity test for cross-sectional panels.
Measures whether factor quantile groups exhibit monotonic return ordering. Per-date: split into n_groups by factor rank, compute mean return per group, Spearman corr between group index and return.
Notes
Pipeline. Per-date Spearman corr between quantile index and group mean return (cross-section step), then non-overlapping cross-asset t on the per-date series.
Input. DataFrame with date, asset_id, factor, forward_return.
factrix.metrics.monotonicity.monotonicity ¶
monotonicity(df: DataFrame, forward_periods: int = 5, n_groups: int = 10, factor_col: str = 'factor', return_col: str = 'forward_return', tie_policy: str = 'ordinal') -> MetricOutput
Quantile return monotonicity (Spearman correlation).
value = mean |Spearman| — magnitude of monotonicity (always ≥ 0).
t_stat = t-test on signed Spearman — whether direction is consistent.
A high value with insignificant t_stat means the factor has
strong monotonicity but the direction flips across dates.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
Panel with |
required |
n_groups
|
int
|
Number of quantile groups (default 10 for Taiwan ~2000 stocks). Use 5 for N < 1000, 3 for N < 200. |
10
|
tie_policy
|
str
|
Bucketing tie-break policy, see |
'ordinal'
|
Returns:
| Type | Description |
|---|---|
MetricOutput
|
MetricOutput with value = mean |Spearman(group_idx, group_return)|. |
Notes
Per non-overlap date t, bucket assets into n_groups by
factor rank and compute mono_t = Spearman(group_idx,
group_mean_return). value = mean_t |mono_t| (magnitude of
monotonicity, ≥ 0); t-stat = mean(mono) / (std(mono) /
sqrt(n)) on the signed series tests directional consistency.
factrix splits magnitude (value) and direction (stat)
deliberately: a high value paired with insignificant t
means the factor monotonically discriminates returns but its sign
flips across dates — useful information that a single signed
average would hide.
Examples:
>>> import factrix as fx
>>> from factrix.preprocess import compute_forward_return
>>> from factrix.metrics.monotonicity import monotonicity
>>> panel = compute_forward_return(
... fx.datasets.make_cs_panel(n_assets=200, n_dates=180, seed=0),
... forward_periods=5,
... )
>>> result = monotonicity(panel, forward_periods=5, n_groups=5)
>>> result.name
'monotonicity'
Use cases¶
-
Decile-curve direction test
Per-date Spearman correlation between quantile-group index and group mean return; cross-asset \(t\) on the signed series asks whether the bucket ordering is consistently increasing (or decreasing) across dates — a stronger requirement than a positive long-short spread.
-
Magnitude vs direction separation
valueis mean \(|\text{Spearman}|\) (magnitude, \(\geq 0\));statis a \(t\) on the signed series. Highvaluewith insignificantstatflags a factor that monotonically sorts returns but whose direction flips across dates — Patton-Timmermann (2010) territory that a single signed average would hide.
Per-bucket cardinality floor
min_assets_per_group = 50 (Patton-Timmermann 2010) — the slice-
test function downscales n_groups automatically so each bucket meets
the floor; below it, per-date bucket means are noise-dominated and
the rank statistic is unreliable. Defaults: n_groups=10 for
universes around 2000 stocks; drop to 5 for \(n_{assets} < 1000\)
and 3 for \(n_{assets} < 200\).
Worked example — Spearman direction test on quantile-bucket returns¶
monotonicity on a synthetic cross-sectional panel
import factrix as fx
from factrix.metrics.monotonicity import monotonicity
from factrix.preprocess import compute_forward_return
raw = fx.datasets.make_cs_panel(
n_assets=2000, n_dates=500, ic_target=0.08, seed=2024,
)
panel = compute_forward_return(raw, forward_periods=5)
out = monotonicity(panel, forward_periods=5, n_groups=10)
print(out.value, out.stat,
out.metadata["mean_signed"], out.metadata["p_value"])
# 0.41 5.62 0.39 2.1e-08 (approximate)
# value ≈ mean|Spearman|; mean_signed ≈ direction; stat t on signed series
See also¶
-
quantile_spread/compute_group_returns
The decile-curve chart input and the long-short headline spread over the same buckets.
-
by_slice
Axis-agnostic slice dispatcher for per-slice monotonicity summaries.
-
slice_pairwise_test/slice_joint_test
Cross-slice inference (Wald \(\chi^2\) + Holm / Romano-Wolf adjusted \(p\)).
-
Statistical methods
Cross-asset \(t\) on the per-date signed Spearman series, DDOF convention.
-
Metric applicability reference
Per-bucket sample-size floor (Patton-Timmermann 2010) and the
n_groupsdownscaling contract. -
Individual × Continuous landing
Adjacent metrics in the same cell.