Skip to content

Quickstart

forward_periods counts rows, not calendar time

factrix is frequency-agnostic — it only shifts row indices. forward_periods=5 on a daily panel means 5 trading days; on a weekly panel, 5 weeks. The caller is responsible for ensuring the panel is sorted per asset and has regular time spacing.

30-second smoke test

import factrix as fx
from factrix.preprocess import compute_forward_return

raw   = fx.datasets.make_cs_panel(n_assets=100, n_dates=500, ic_target=0.08, seed=2024)
panel = compute_forward_return(raw, forward_periods=5)

# Typed factory — supply the config directly (type-safe; the IDE rejects illegal combos)
cfg     = fx.AnalysisConfig.individual_continuous(metric=fx.Metric.IC, forward_periods=5)
profile = fx.evaluate(panel, cfg)

print('primary_p =', round(profile.primary_p, 4))
# → pass | primary_p = 0.0

print(profile.diagnose())
# {'identity': {'factor_id': 'factor', 'forward_periods': 5},
#  'context': {},
#  'cell': {'scope': 'individual', 'signal': 'continuous',
#           'metric': 'ic', 'mode': 'panel'},
#  'n_obs': 494, 'n_pairs': 49400, 'n_periods': 494, 'n_assets': 100,
#  'primary_p': 2.13e-40, 'primary_stat': 14.60, 'primary_stat_name': 't_nw',
#  'warnings': [], 'info_notes': [],
#  'stats': {'mean': 0.0722, 't_nw': 14.60, 'p_nw': 2.13e-40},
#  'metadata': {'t_nw': {'nw_lags': 5}, 'p_nw': {'nw_lags': 5}}}

If you are not sure which factory to use, let factrix infer it from the panel shape:

# Inferred config — factrix picks the factory from the panel shape
result  = fx.suggest_config(panel)
profile = fx.evaluate(panel, result.suggested)
# result.reasoning explains how each axis was inferred
# result.warnings flags potential risks (small N, persistence, …)

See Concepts for what each axis means.


Research question → factory

The five supported research questions and their factory calls — the AnalysisConfig.*() classmethod constructors such as individual_continuous() used above — live in Concepts § Five analysis scenarios. That page is also the SSOT for the procedure and literature behind each factory. For task-oriented help on picking the right factory (information coefficient (IC) vs FM, when to add standalone metrics), see Choosing a metric.

N = 1 (single asset / series)

Mode auto-switches to TIMESERIES. The common_continuous and *_sparse factories work as-is. individual_continuous at N=1 raises ModeAxisError with suggested_fix=common_continuous(...).


profile.diagnose() and warnings

diagnose() returns a flat dict for human inspection or AI agent triage:

{
    "identity": {"factor_id": "momentum", "forward_periods": 5},
    "context": {},
    "cell": {"scope": "individual", "signal": "continuous",
             "metric": "ic", "mode": "panel"},
    "n_obs": 500, "n_pairs": 50000, "n_periods": 500, "n_assets": 100,
    "primary_p": 0.0001,
    "primary_stat": 4.21,
    "primary_stat_name": "t_nw",
    "warnings": ["unreliable_se_short_periods"],
    "info_notes": [],
    "stats": {"mean": 0.082, "t_nw": 4.21, "p_nw": 0.0001},
    "metadata": {"t_nw": {"nw_lags": 5}, "p_nw": {"nw_lags": 5}},
}

The most common warnings:

  • UNRELIABLE_SE_SHORT_PERIODS20 ≤ T < 30; Newey-West (NW) heteroskedasticity-and-autocorrelation-consistent (HAC) SE unstable. T < 20 raises InsufficientSampleError.
  • PERSISTENT_REGRESSOR — factor augmented Dickey-Fuller (ADF) p > 0.10.
  • EVENT_WINDOW_OVERLAP — event windows overlap on the same asset.
  • SERIAL_CORRELATION_DETECTED — Ljung-Box p < 0.05 on residuals.

For the full enum and the trigger conditions for each WarningCode, InfoCode, and StatCode, see Reference § Warning / info / stat codes. For exception classes (InsufficientSampleError, ModeAxisError, UserInputError, ConfigError, MissingConfigError, ...), their TL;DR catch pattern, and recovery via suggested_fix, see Errors.

warnings does not affect primary_p — it is a risk flag. The user decides whether to filter on warnings before Benjamini-Hochberg-Yekutieli (BHY). For single-factor pre-registered analysis compare primary_p against your nominal threshold directly.

For the full field-order walk of FactorProfile — and of Survivors (after bhy) and MetricsBundle (after run_metrics) — see Reading results.


Next steps

You have one FactorProfile for one factor. The common follow-ups:

You want to… Reach for Guide
Screen N candidate factors with false discovery rate (FDR) control multi_factor.bhy(profiles) — or partial_conjunction / bhy_hierarchical for nested structure Batch screening with BHY
Compare the descriptive surface across factors run_metrics × N → compare(bundles) Standalone metrics
Rank factors after screening compare(survivors) — leaderboard with adj_q
Explore one metric across slices (sector / regime / universe / ADV bucket) by_sliceSliceResult.to_frame() Slice analysis
Test whether slices differ statistically slice_pairwise_test / slice_joint_test Slice analysis

For function semantics, the input contract, and cross-function topics (expand_over semantics, regime-analysis dispatch), see the API reference landing and Cross-function reference. For the field-order walk of FactorProfile / Survivors / MetricsBundle, see Reading results.