Skip to content

factrix.metrics.ts_asymmetry

Long-side / short-side asymmetry test (issue #5).

Diagnostic for (COMMON, CONTINUOUS, *) and single-asset TIMESERIES cells. Ordinary least squares (OLS) β reports a single slope and assumes the response is symmetric around zero — β > 0 could be "rises more on positive factor" or "falls less on negative factor", and a strategy team needs to know which.

Two methods, both fit by OLS with Newey-West (NW) heteroskedasticity-and-autocorrelation-consistent (HAC) covariance and tested by Wald χ² so cross-method p-values stay comparable and the overlapping-forward-return autocorrelation is handled the same way as ts_beta_t_nw. Welch t is intentionally avoided — its iid assumption breaks under forward_periods > 1.

  • Method A (conditional means): `r = β_long·I(f>0) + β_short·I(f<0)
  • β_zero·I(f=0). H0:β_long + β_short = 0` — symmetric magnitude.
  • Method B (piecewise slopes): r = α + β_pos·max(f,0) + β_neg·min(f,0). H0: β_pos = β_neg — slope on the positive side equals slope on the negative side.

Gates (issue #5): - Gate B (mandatory for either method): factor must have both positive and negative observations. - Gate C (method B only): each side needs ≥ 2 distinct factor values to identify a slope. Below the gate, method B is skipped and metadata["method_b_skipped"] records the reason.

Standalone metric — does not enter the registry.

Notes

Pipeline. Per-date aggregation of factor and forward return to a common (_f, _r) series (cross-section step), then NW HAC OLS with sign-asymmetric slopes on the resulting time series; Wald χ² on the slope difference.

factrix.metrics.ts_asymmetry.ts_asymmetry

ts_asymmetry(df: DataFrame, *, factor_col: str = 'factor', return_col: str = 'forward_return', forward_periods: int | None = None, nw_lags: int | None = None) -> MetricOutput

Long/short asymmetry of factor → return relationship.

Reported headline:

  • value = method-A magnitude β_long + β_short (0 under perfect symmetry; positive = long side stronger)
  • stat = value / Newey-West (NW) heteroskedasticity-and-autocorrelation-consistent (HAC) SE
  • metadata["p_value"] = method-A Wald p (two-sided)

Method B (Gate C passing) populates beta_pos / beta_neg / p_wald_slopes; otherwise method_b_skipped carries the reason.

Parameters:

Name Type Description Default
df DataFrame

Long panel; aggregated to per-date (_f, _r) internally.

required
factor_col str

Column carrying the factor.

'factor'
return_col str

Column carrying the forward return.

'forward_return'
forward_periods int | None

Overlap horizon of the forward return; used to floor the NW bandwidth so the kernel is consistent with the autocorrelation it must absorb.

None
nw_lags int | None

Override for the NW lag count. None resolves to the standard rule given forward_periods and T.

None

Returns:

Type Description
MetricOutput

MetricOutput whose value is the method-A magnitude;

MetricOutput

diagnostic statistics live in metadata. Short-circuits

MetricOutput

with a reason code when input shape is insufficient (no

MetricOutput

date column, missing factor / return column, fewer

MetricOutput

than MIN_PORTFOLIO_PERIODS_HARD per-date rows, or no two-sided

MetricOutput

factor variation).

Notes

Aggregate to per-date (_f, _r) then fit two NW-HAC ordinary least squares (OLS) specifications on the resulting time series::

Method A: r_t = beta_long*I(f>0) + beta_short*I(f<0)
              + beta_zero*I(f=0)
          H0: beta_long + beta_short = 0   (Wald, two-sided)

Method B: r_t = alpha + beta_pos*max(f, 0) + beta_neg*min(f, 0)
          H0: beta_pos = beta_neg          (Wald)

value = beta_long + beta_short (method A); 0 under perfect symmetry, positive when the long side dominates in magnitude.

factrix runs both methods under NW HAC + Wald (not Welch t) because forward_periods > 1 breaks the iid assumption Welch relies on, and using one estimator family across A and B keeps cross-method p-values comparable.

References

Newey-West 1987: HAC covariance underpinning the Wald tests for both methods. Andrews 1991: Bartlett growth rate T^(1/3). 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_asymmetry import ts_asymmetry
>>> panel = compute_forward_return(
...     fx.datasets.make_cs_panel(n_assets=80, n_dates=180, seed=0),
...     forward_periods=5,
... )
>>> result = ts_asymmetry(panel)
>>> result.name
'ts_asymmetry'

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

  • Decompose \(\beta\) into long-side vs short-side response


    Ordinary least squares (OLS) \(\beta\) reports one slope and assumes a symmetric response — \(\beta > 0\) could be "rises more on positive factor" or "falls less on negative factor". ts_asymmetry runs Method A (conditional means on \(\mathrm{sign}(f)\) dummies) so the long and short legs are recoverable separately.

  • Symmetric-magnitude Wald test


    Headline is \(\beta_{\text{long}} + \beta_{\text{short}}\) — 0 under perfect symmetry, positive when the long side dominates. Newey-West (NW) heteroskedasticity-and-autocorrelation-consistent (HAC) Wald on \(H_0: \beta_{\text{long}} + \beta_{\text{short}} = 0\) handles the autocorrelation induced by overlapping forward returns; Welch \(t\) is intentionally avoided because its iid assumption breaks under forward_periods > 1.

  • Piecewise-slope check (Method B)


    When each side has \(\geq 2\) distinct factor values, Method B fits \(r = \alpha + \beta_{\text{pos}} \max(f, 0) + \beta_{\text{neg}} \min(f, 0)\) and tests \(H_0: \beta_{\text{pos}} = \beta_{\text{neg}}\). Distinguishes a magnitude asymmetry (Method A) from a slope asymmetry. Gate C below the cardinality floor records method_b_skipped and the reason; Method A already carries the full information for categorical / binary signals.

Worked example — sign-asymmetric slopes on a common-factor panel

broadcast common factor → ts_asymmetry (Methods A + B)

import factrix as fx
import polars as pl
from factrix.metrics.ts_asymmetry import ts_asymmetry
from factrix.preprocess import compute_forward_return

# Build a panel whose ``factor`` is broadcast (one value per date,
# shared across all assets) — VIX / NFP-surprise 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_asymmetry(panel, forward_periods=5)
print(out.value, out.stat, out.metadata["p_value"])
# 0.00021  1.83  0.067   (approximate; method A magnitude)
print(out.metadata["beta_long"], out.metadata["beta_short"],
      out.metadata["abs_short_over_long"])
# 0.00088  -0.00067  0.76
print(out.metadata.get("beta_pos"), out.metadata.get("beta_neg"),
      out.metadata.get("p_wald_slopes"))
# 0.00121  -0.00102  0.18   (Method B; ~null if Gate C passed)

See also

  • ts_beta


    Symmetric linear \(\beta\) on the same panel; pair when both direction and magnitude asymmetry matter.

    api/metrics/ts_beta →

  • ts_quantile_spread


    Bucketed conditional means — strictly more flexible than the two-flavour split, at the cost of \(K\) free parameters and the n_distinct(factor) >= n_groups * 2 gate.

    api/metrics/ts_quantile →

  • event_quality family


    Where the input gate redirects when the factor is single-sided (no positive or no negative observations): event_hit_rate / event_ic / profit_factor.

    api/metrics/event_quality →

  • Statistical methods


    NW HAC SE, Andrews bandwidth, Hansen-Hodrick overlap floor, and the Wald-on-linear-restriction framing for both Method A and Method B.

    reference/statistical-methods →

  • Timeseries-mode conventions


    FACTOR_ADF_P, plain stage-1 SE rationale, forward_periods vs signal_horizon bias framing.

    reference/ts-mode-conventions →

  • Metric applicability reference


    When this metric applies and the gates (Gate B: two-sided factor; Gate C: \(\geq 2\) distinct values per side for Method B).

    reference/metric-applicability →

  • Common × Continuous landing


    Adjacent macro-common metrics in the same cell.

    api/metrics/common-continuous →