Skip to content

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 date, asset_id, factor, forward_return.

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 _assign_quantile_groups.

'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


    value is mean \(|\text{Spearman}|\) (magnitude, \(\geq 0\)); stat is a \(t\) on the signed series. High value with insignificant stat flags 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