Skip to main content

Polars-native factor signal validation toolkit for quantitative finance

Project description

factrix

Tests one factor. Screens a thousand.

Where factrix fits

Does this factor possess predictive edge?

factrix is the first Polars-native Python toolkit that picks the right statistical test for each factor type. Cross-sectional, event, common factor — each gets the tests that fit its data-generating process.

factor construction  →  factrix (inference)  →  strategy construction  →  backtest  →  live trading
                            ▲ you are here

For each candidate factor factrix answers — is the predictive power real? — and corrects for multiple testing when you screen at scale. Kill fakes before they cost you a backtest.

Why factrix?

  • Type-routed evaluation — Information Coefficient + Fama-MacBeth for cross-sectional factors; Cumulative Average Abnormal Return for events.
  • Financial statistics built in — autocorrelation-robust standard errors (Newey-West), overlapping-forward-return correction, persistent-predictor flagging (Stambaugh bias), and false-discovery-rate control across batches (Benjamini-Hochberg-Yekutieli).
  • Polars-native — modern Polars alternative to the pandas-based alphalens.

factrix stops at the inference — primary test plus diagnostic battery. It does not size positions, model slippage, optimise weights, or compose alphas; those belong to the later stages of the pipeline.

Is factrix the right tool?

You want to… Use this
Inference on a factor (cross-sectional / event / common factor) factrix
Screen many factors with multiple-testing correction factrix
Backtest with positions / slippage / margin zipline-reloaded, backtrader, bt, vectorbt, nautilus_trader
Optimise portfolio weights skfolio, riskfolio-lib
Returns-level tear-sheet (P&L diagnostics) pyfolio-reloaded, QuantStats
Familiar cross-sectional tear-sheet alphalens-reloaded
End-to-end machine-learning pipeline qlib
Deflated / probabilistic Sharpe today (commercial) mlfinlab

Installation

pip install factrix
# or
uv add factrix

See the installation guide for version pinning and development setup.

Typical usage

Single factor — IC evaluation

import factrix as fx
from factrix.metrics import ic

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

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))
print('p_value =', round(ic_res.p_value, 4))

Multi-factor BHY screening

# A panel can carry many candidate factors — pass every column to factor_cols.
raw_mf  = fx.datasets.make_multi_factor_panel(n_factors=3, n_assets=100, n_dates=500, seed=2024)
data_mf = fx.preprocess.compute_forward_return(raw_mf, forward_periods=5)

results = fx.evaluate(
    data_mf,
    metrics={"ic": ic(inference=fx.inference.NEWEY_WEST)},
    factor_cols=["factor_0000", "factor_0001", "factor_0002"],
    forward_periods=5,
)

# evaluate returns dict[str, EvaluationResult] keyed by factor column;
# bhy screens the list of results as one hypothesis family.
fdr_results = fx.multi_factor.bhy(list(results.values()), metrics=["ic"], q=0.05)
bhy_ic = fdr_results["ic"]
print("survivors =", [r.factor for r in bhy_ic.survivors])

Multi-horizon sweep and BHY screening

# evaluate_horizons sweeps the factors across forward periods and returns a
# flat list[EvaluationResult] — one per (factor, forward_periods) — so it feeds
# bhy directly (no list(...values()) needed). expand_over=("forward_periods",)
# runs an independent BHY step-up per horizon, the correct cross-horizon screen
# (each horizon bucket is its own family of the swept factors).
results_sweep = fx.evaluate_horizons(
    raw_mf,  # raw multi-factor panel — no forward_return attached
    metrics={"ic": ic(inference=fx.inference.NEWEY_WEST)},
    factor_cols=["factor_0000", "factor_0001", "factor_0002"],
    forward_periods=[1, 5, 10],
)

fdr_results = fx.multi_factor.bhy(
    results_sweep,
    metrics=["ic"],
    expand_over=("forward_periods",),
    q=0.05,
)
bhy_ic = fdr_results["ic"]
print("survivors =", [(r.factor, r.forward_periods) for r in bhy_ic.survivors])

Single-asset (timeseries) evaluation

import numpy as np
import polars as pl
from datetime import datetime, timedelta
from factrix.metrics import directional_hit_rate

# Build a one-asset panel by hand (the cross-section generators need N >= 2).
rng    = np.random.default_rng(7)
dates  = [datetime(2020, 1, 1) + timedelta(days=i) for i in range(250)]
factor = rng.standard_normal(250)
ret    = 0.05 * factor + rng.standard_normal(250)        # factor leads next return
single_asset_data = pl.DataFrame({
    "date": dates,
    "asset_id": "SPX",
    "price": 100 * np.exp(np.cumsum(ret) / 100),
    "macro_factor": factor,
})
data = fx.preprocess.compute_forward_return(single_asset_data, forward_periods=5)

# A single-asset panel auto-resolves the structure axis to
# DataStructure.TIMESERIES (N == 1); a structure-agnostic metric such as
# directional_hit_rate runs unchanged through the same entry point.
results = fx.evaluate(
    data,
    metrics={"dir_hit": directional_hit_rate()},
    factor_cols=["macro_factor"],
    forward_periods=5,
)
print(results["macro_factor"].metrics["dir_hit"].value)

Documentation

  • Get Started — install, quickstart, where factrix fits
  • User Guide — concepts (three-axis design, architecture), how-to (PANEL vs TIMESERIES, BHY screening, slice analysis), examples
  • API Reference — entry points, results, lookup tables, per-metric pages
  • Development — contributing, design notes
  • Release Notes — changelog

License

Released under the Apache License 2.0.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

factrix-0.14.0.tar.gz (365.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

factrix-0.14.0-py3-none-any.whl (311.7 kB view details)

Uploaded Python 3

File details

Details for the file factrix-0.14.0.tar.gz.

File metadata

  • Download URL: factrix-0.14.0.tar.gz
  • Upload date:
  • Size: 365.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for factrix-0.14.0.tar.gz
Algorithm Hash digest
SHA256 d9116f5360e77a38bb3badbaeb4b912639ab24640ce5d55010190b32e265f9d5
MD5 76cb3c2c7f578975ae420a1d58e3e297
BLAKE2b-256 b778d2e338dc743ce7e98f9c97f16aa1fd5828f761d9384b03d773ee1c0cd354

See more details on using hashes here.

Provenance

The following attestation bundles were made for factrix-0.14.0.tar.gz:

Publisher: release.yml on awwesomeman/factrix

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file factrix-0.14.0-py3-none-any.whl.

File metadata

  • Download URL: factrix-0.14.0-py3-none-any.whl
  • Upload date:
  • Size: 311.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for factrix-0.14.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ed8aa0128b7e2d721e0b6154f68e443f6e19be4859ef6776a962927527c8a331
MD5 7a64edfb1291d4787776daa3f03eec69
BLAKE2b-256 40953bcc18623ef67884173904dd34cf2c0709d7a7edbeb768d8c37361d6995e

See more details on using hashes here.

Provenance

The following attestation bundles were made for factrix-0.14.0-py3-none-any.whl:

Publisher: release.yml on awwesomeman/factrix

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page