factrix.metrics.positive_rate ¶
Positive-rate computation for any time-indexed series.
Notes
Pipeline. Time-series only, sampled non-overlapping on a 1-D
series; binomial test against p = 0.5.
Input. DataFrame with date, value or a 1-D array.
Output. Proportion of periods where the value satisfies a condition (default: value > 0).
factrix.metrics.positive_rate.positive_rate ¶
positive_rate(series: DataFrame, value_col: str = 'value', overlap_periods: int = DEFAULT_FORWARD_PERIODS, *, expected_warnings: tuple[str, ...] = ()) -> MetricResult
Positive rate = proportion of periods where value > 0.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
series
|
DataFrame
|
DataFrame with |
required |
overlap_periods
|
int
|
Sampling interval for non-overlapping dates. |
DEFAULT_FORWARD_PERIODS
|
Returns:
| Type | Description |
|---|---|
MetricResult
|
MetricResult with value = positive rate (0.0-1.0). |
Notes
rate = (#{t : value_t > 0}) / n on a non-overlapping subsample
at stride overlap_periods. Two-sided exact binomial test
against H0: p = 0.5 (:func:scipy.stats.binomtest) at every
n; stat is the hit count (stat_type="binomial_hits") —
the sufficient statistic of the test actually run — never a
Gaussian z.
Why exact rather than the normal-approximation score test: the
uncorrected z = (rate - 0.5)·√n / 0.5 is anti-conservative
(n=20, 15 hits: p=0.025 vs exact 0.041), and the
exact test is negligible in cost at any series length. Non-overlap
stride mirrors the information coefficient (IC) pipeline so
autocorrelation from overlapping forward returns does not leak in.
If the strided hit indicators remain serially persistent, the exact
binomial independence assumption is not calibrated; the result carries
serial_correlation_detected rather than leaving that regime silent.
Its warning threshold maps the project-wide persistence boundary
through the Gaussian sign-correlation identity; this is a calibration
anchor, not a latent-correlation model for arbitrary inputs.
References
Hansen-Hodrick 1980: overlapping-return autocorrelation horizon motivating the non-overlap stride.
Hurt 1973: sign correlation of a zero-mean stationary Gaussian sequence.
Examples:
Hit rate of a per-date IC series produced by
:func:~factrix.metrics.ic.compute_ic:
>>> import factrix as fx
>>> from factrix.preprocess import compute_forward_return
>>> from factrix.metrics.ic import compute_ic
>>> from factrix.metrics.positive_rate import positive_rate
>>> panel = compute_forward_return(
... fx.datasets.make_cs_panel(n_assets=80, n_dates=180, rng=0),
... forward_periods=5,
... )
>>> series = compute_ic(panel)["factor"].rename({"ic": "value"}).select("date", "value")
>>> result = positive_rate(series, overlap_periods=5)
>>> result.name == ""
True
Use cases¶
-
Sign-significance on a factor-return series
positive_rateis a standalone series diagnostic — input is a 1-D series with(date, value), not the raw panel. Typical pipes: per-date information coefficient (IC) fromcompute_ic, quantile spread fromcompute_spread_series, or any other factor-mimicking-portfolio return series. Reports the fraction of periods withvalue > 0against \(H_0: p = 0.5\). -
Exact binomial at every \(n\)
The \(p\)-value is the exact two-sided binomial test (
scipy.stats.binomtest) at every sample size;statis the hit count (stat_type="binomial_hits"), the test's sufficient statistic. An earlier version switched to the uncorrected normal-approximation \(z\) above \(n = 20\); that branch was anti-conservative (e.g. \(n=20\), 15 hits: \(p = 0.025\) vs exact \(0.041\)) and has been removed — the exact test is \(O(n)\) and free. -
Non-overlap stride matches the IC pipeline
The series is sub-sampled at stride
overlap_periodsbefore the test so the MA dependence induced by overlapping forward returns does not leak in; same convention asquantile_spreadand the other non-overlap inference paths. If the surviving hit indicators remain serially persistent, the result carriesserial_correlation_detected: the exact binomial p-value assumes independent trials and is not calibrated in that regime.
Choosing a function¶
| Goal | Function |
|---|---|
Hit-rate significance vs \(p = 0.5\) on a (date, value) series |
positive_rate |
Per-date hit indicator (value > 0 cast to float) for slice-test plumbing |
per_date_series |
Worked example — IC series fed into positive_rate¶
compute_ic → positive_rate
import factrix as fx
from factrix.metrics.ic import compute_ic
from factrix.metrics.positive_rate import positive_rate
from factrix.preprocess import compute_forward_return
raw = fx.datasets.make_cs_panel(
n_assets=100, n_dates=500, ic_target=0.08, rng=2024,
)
panel = compute_forward_return(raw, forward_periods=5)
# The series diagnostic consumes (date, value); the value column on
# the compute_ic output is named ``ic``.
ic_df = compute_ic(panel)["factor"]
out = positive_rate(ic_df, value_col="ic", overlap_periods=5)
print(out.value, out.stat, out.p_value, out.metadata["method"])
# 0.62 19 0.011 exact-binomial (approximate)
See also¶
-
compute_ic/compute_spread_series
Canonical producers of the
(date, value)series this diagnostic consumes.compute_icemits(date, ic, tie_ratio);compute_spread_seriesemits(date, spread, ...). -
trend/oos
Sibling series diagnostics on the same input shape — slope detection and IS/out-of-sample (OOS) persistence.
-
by_slice
Per-slice hit-rate summaries; uses
per_date_seriesas the slice-test capability hook. -
Statistical methods
Two-sided binomial branching, non-overlap stride discipline, and the Hansen-Hodrick autocorrelation floor that motivates it.
-
Metric applicability reference
When this metric applies and the sample-size guards that gate it (
MIN_SERIES_PERIODS_HARDfloor on the sampled series). -
Series diagnostics landing
Adjacent axis-agnostic series diagnostics.