Quickstart
forward_periods counts periods, not calendar time
factrix never reads the calendar: every horizon, window and lag is a
count of periods on the panel's own date grid. forward_periods=5 is
five periods of whatever one period represents on your grid; factrix
never infers that from the dates. Aligning the factor and price sources
onto one grid is the caller's job. See
Period grid, not calendar.
30-second smoke test¶
import factrix as fx
from factrix.preprocess import compute_forward_return
from factrix.metrics import ic
# 1. Generate synthetic panel data and compute forward returns
raw = fx.datasets.make_cs_panel(n_assets=100, n_dates=500, ic_target=0.08, rng=2024)
data = compute_forward_return(raw, forward_periods=5)
# 2. Run single-factor evaluation using the ic metric with Newey-West
results = fx.evaluate(
data,
metrics={"ic": ic(inference=fx.inference.NEWEY_WEST)},
factor_cols=["factor"],
forward_periods=5,
)
res = results["factor"]
ic_res = res.metrics["ic"]
print('ic_mean =', round(ic_res.value, 4))
# → ic_mean = 0.0722
print('p_value =', round(ic_res.p_value, 4))
# → p_value = 0.0
See Concepts for what each axis means.
Bringing your own data¶
The smoke test uses synthetic data that already carries factrix's
canonical column names (date, asset_id, price). Real-world panels
rarely do, so adapt is the first step of the pipeline — it renames
your columns to the canonical names before compute_forward_return:
import factrix as fx
from factrix.adapt import adapt
from factrix.preprocess import compute_forward_return
raw = adapt(
your_df,
date="trade_date", asset_id="ticker", price="close_adj",
)
data = compute_forward_return(raw, forward_periods=5)
results = fx.evaluate(data, metrics={"ic": fx.metrics.ic()}, factor_cols=["factor"])
So the full pipeline is adapt → compute_forward_return →
evaluate. adapt only performs schema adaptation (column renaming,
Polars Date promotion, and pandas-to-Polars conversion); it never imputes
observations. During return construction, compute_forward_return maps
non-finite prices to missing and drops rows whose required entry or exit is
unavailable. Do not forward-fill a price: that would invent a tradable level.
Repair a genuine feed error at the source under an explicit column and
staleness policy.
Choose metrics¶
Pass metric instances from factrix.metrics directly to the metrics
parameter of evaluate(); there is no central configuration object.
To learn how to choose the right metrics and configure them, see Choosing a metric and Concepts.
Read the result¶
Read a metric's value and p_value together with n_obs, metadata, and
the enclosing result's warnings. A small p-value is not sufficient evidence
when the result also reports a persistence, overlap, clustering, or thin-sample
warning.
EvaluationResult.to_dict() returns the complete result as a nested,
JSON-friendly mapping. See Reading results for
the field-by-field contract, Warning codes for
trigger conditions, and Errors for strict-mode failures.
Next steps¶
You have the evaluation results for one or more factors. The common follow-ups:
| You want to… | Reach for | Guide / Reference |
|---|---|---|
| Run the full workflow on public real-market returns | Download → evaluate(...) → BHY → IC path |
Real value-anomaly replication |
| Screen candidate factors with false discovery rate (FDR) control | multi_factor.bhy(results) — or partial_conjunction / bhy_hierarchical for nested structure |
multi_factor overview |
| Rank factors after screening | compare(results) — leaderboard with rank |
— |
| Explore one metric across slices (sector / regime / universe / ADV bucket) | by_slice → dict[str, EvaluationResult] |
Slice analysis |
| Test whether slices differ statistically | slice_pairwise_test / slice_joint_test |
Slice analysis |
For function semantics and the input contract, see the API reference landing and Reading results.