Library / SDK
eliasswu/AlphaPurify avatar
eliasswu/AlphaPurify

AlphaPurify: factor cleaning, quantile backtests and return attribution in one pipeline

Extremely fast quantitative factor cleaning and backtesting library, based on Polars

588 stars92 forksPythonMIT

At a glance

What is it?
AlphaPurify is a Polars-based Python library for quantitative factor research, built around four modules: FactorAnalyzer for IC and quantile backtests, AlphaPurifier for 40 plus preprocessing methods, Database for data aggregation and storage, and Exposures for factor correlation and return attribution.
Who is it for?
AlphaPurify suits a quant who wants the whole factor loop in one library, from preprocessing through a long, short or long-short quantile backtest to a report, without assembling a pipeline from a separate cleaning tool, a backtester and a reporting library. It does not suit you if you only need IC analysis, since the setup and dependency footprint is larger than a dedicated factor analysis library, and the numbers it produces are a backtest, not a live result.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 104 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Four modules covering the factor research loop

AlphaPurify is a Python library for factor construction, preprocessing, backtesting, and factor return attributions, described as helping quants rapidly validate ideas, and the repository description calls it an extremely fast quantitative factor cleaning and backtesting library based on Polars.

The library is organised as four main modules, and the division is a good summary of what factor research actually involves.

alphapurify.FactorAnalyzer is for IC and Rank IC testing and for long, short and long-short quantile backtests. That is the evaluation half.

alphapurify.AlphaPurifier is for factor preprocessing, including 40 plus Winsorization, Neutralization and Standardization methods, with the examples given being ridge regression, lasso regression and PCA decomposition. That is the cleaning half, and the reason the project is named what it is.

alphapurify.Database is for financial data aggregation, factor construction and factor storage. That is the data half.

alphapurify.Exposures is for factor correlation analysis and factor-based return attribution. That is the explanation half, asking why a factor made money rather than whether it did.

The pitch in the README is that AlphaPurify gives quants a whole factor testing pipeline with interactive reports, rather than requiring them to build custom pipelines or infrastructure. The reports are Plotly based, and the documentation points at a full set of documents and examples in the examples directory, which contains a file per module: AlphaPurifier.py, Database.py, Exposures.py and FactorAnalyzer.py.

The package is on PyPI as alphapurify, MIT licensed, requires Python 3.10 or newer, and the platform badge lists Linux, Windows and macOS.

The preprocessing half, and why cleaning comes before testing

The AlphaPurifier half is the one that does the unglamorous work, and it is where a surprising amount of the library sits.

A factor is a number computed for each asset at each point in time, and in its raw form it is almost never usable. Outliers distort any correlation you try to measure, exposure to a handful of names swamps the signal, and the scale of a factor depends on the universe you computed it over. Winsorization, neutralization and standardization are the three standard corrections, and the README lists all three as built-in, with 40 plus preprocessing methods in total supporting workflows from ultra high-frequency to low-frequency data.

The API is a chain, and the chain is the point:

python
from alphapurify import AlphaPurifier, FactorAnalyzer, PureExposures

# preprocess
df = (
    AlphaPurifier(df, factor_col="alpha_003")
    .winsorize(method="mad")
    .standardize(method="zscore")
    .to_result()
)

You construct the purifier with your frame and the name of the factor column, then call each cleaning step and finish with to_result(). The winsorize call names a method, mad here, which is the median absolute deviation rather than a standard deviation based rule, and standardize names zscore.

The design is a fluent pipeline where each step returns something you can call the next method on, and the result comes out of to_result. That is a fairly conventional choice for a data transformation library, and its practical benefit is that the cleaning steps compose in the order you decide, which matters because the order is not arbitrary.

The methods available inside each step are where the real choice lives, and the fact that there are 40 of them across three families is the argument for using a dedicated library rather than writing the cleaning yourself. The set includes regression-based neutralization, with ridge regression and lasso regression named, and PCA decomposition, which is a different way of removing common factors.

The backtest half, and the data shape it requires

FactorAnalyzer is where the factor is judged, and the constructor takes the frame plus the column names it needs:

python
FA = FactorAnalyzer(base_df=df,
                    trade_date_col='datetime',
                    symbol_col='symbol',
                    price_col='close',
                    factor_name='alpha_003')
FA.run()
FA.create_long_return_sheet()
FA.create_long_short_return_sheet()
FA.create_short_return_sheet()
FA.create_single_fac_ic_sheet()

The sheet methods are the outputs, and they tell you the shape of the analysis. There is a long-only return sheet, a short-only sheet, a long-short sheet, and a single-factor IC sheet. The long-short combination is the one most people mean when they talk about a factor backtest, since a factor that is good long and good short is a different animal from one that is only good long.

The README is explicit about the input contract, which is worth reading before you write any code against it. Your DataFrame must include a time column, an asset identifier column, a price column, and your factor column to ensure proper usage.

The example frame shows what that looks like. A datetime column with minute-level timestamps, a symbol column with values like AAPL and MSFT, a close price, a volume column, and then the factor columns, alpha_003, momentum_12_1, vol_60 and beta_252. Those last three are not the factor under test. They are there for the attribution step, and the distinction between the factor you are testing and the exposures you are explaining with is the boundary between the two halves of the library.

On the backtest fidelity claim, the README describes fully vectorized modeling of transaction fees, slippage, rebalancing, and dynamic weight drift, and calls it 1:1 production-grade backtesting. The white-box claim is the related one: empowering you to visually track weight transitions and asset returns at any timestamp, for any position direction, and within any quantile bin.

Attribution, and the performance claim that comes with it

The Exposures module answers the question the backtest raises but cannot answer on its own.

python
Ex = PureExposures(
    base_df=df,
    trade_date_col='datetime',
    symbol_col='symbol',
    price_col='close',
    factor_name='alpha_003',
    exposure_cols=['momentum_12_1', 'vol_60', 'beta_252'],
)

Ex.run()
Ex.plot_pure_exposures()
Ex.plot_pure_returns()
Ex.plot_pure_exposures_and_returns()
Ex.plot_correlations()

The exposure_cols are the other factors in the frame, and the plotting methods cover the exposures on their own, the returns on their own, both together, and the correlations between factors. A factor that outperforms is more interesting if you know which other factors it is leaning on, and that is the analysis this module exists for.

The performance claim is stated as a specific number, and it is worth quoting exactly because it is unusual. The library processes 4 million plus rows, 15 years of CSI 300, including long, short and long-short IC backtests and creates 4 interactive reports in under 25 seconds on a standard i7 CPU. The comparison table attributes this to Rust vectorization plus multiprocessing.

That is a claim about a specific dataset shape on specific hardware, and the table it appears in is a feature matrix against other libraries rather than a benchmark you can reproduce from the README. The description says the library is based on Polars, and the dependencies confirm it, with pandas, polars, duckdb, plotly, numpy, scipy, pyarrow, joblib, scikit-learn and tqdm.

The comparison table is the other thing to read carefully. It rates AlphaPurify, Qlib, Backtrader, Alphalens, QuantStats and Pyfolio across computation speed, factor preprocessing, out-of-memory protection, cross-sectional snapshot, setup complexity, IC analysis, quantile backtesting, return attribution, multi-frequency support and coupling. The prose around it is blunt about the distinction: libraries like QuantStats and Pyfolio primarily focus on analyzing return curves and portfolio performance, not backtests, while AlphaPurify goes far beyond IC analysis and simple graphs, and Qlib and Backtrader require you to build your own pipeline or infrastructure.

Packaging, the version mismatch, and the multilingual documentation

The build configuration has a small inconsistency that is worth knowing about, because it affects what you get from pip.

The pyproject.toml is the modern file and it is the more trustworthy of the two. It declares the build backend as setuptools with a requirement of setuptools 77.0 or newer, the project name as alphapurify, and the version as 1.0.6. The description is a high-performance quantitative factor analysis and purification toolkit, and the author is listed as Elias Wu.

The setup.py alongside it declares version 1.0.5, and it carries placeholder author fields reading Your Name and [email protected]. So the two packaging files disagree on the version, and the legacy one still has unfilled metadata. If you are pinning a version, pin on what pip actually resolved rather than on what either file says.

The dependency list is the same in both files, which is a good sign. pandas, polars, duckdb, plotly, numpy, scipy, pyarrow, joblib, scikit-learn and tqdm. The presence of duckdb and pyarrow alongside polars is the interesting choice, since that is a columnar query engine and an Arrow implementation, and it suggests the library does its heavy lifting in a way that does not need a pandas frame to be built in memory.

The classifiers only list Python 3.10 and 3.11 even though requires-python is 3.10 or newer, so the package is not claiming support for the newer interpreters even though it presumably runs on them.

The documentation is multilingual, which is unusual for a quant library and tells you something about where the audience is. There are English, Simplified Chinese and Traditional Chinese READMEs, and the English one is the primary. The installation is a plain pip install alphapurify, with the note that pip installs the latest stable version while the main branch is in active development, and that to test the latest scripts or functions in the main branch you should install from a clone.

Editorial conclusion

AlphaPurify suits a quant who wants the whole factor loop in one library, from preprocessing through a long, short or long-short quantile backtest to a report, without assembling a pipeline from a separate cleaning tool, a backtester and a reporting library. It does not suit you if you only need IC analysis, since the setup and dependency footprint is larger than a dedicated factor analysis library, and the numbers it produces are a backtest, not a live result. Before you rely on it, check that your DataFrame carries a time column, an asset identifier column, a price column and your factor column, since the README states that as a requirement, and note the version metadata is inconsistent across the files, with pyproject at 1.0.6 and setup.py at 1.0.5.

Frequently asked questions

What are the four modules in AlphaPurify?

FactorAnalyzer for IC and Rank IC testing and long, short and long-short quantile backtests, AlphaPurifier for factor preprocessing with 40 plus methods, Database for financial data aggregation and factor storage, and Exposures for factor correlation analysis and factor-based return attribution.

What columns does my DataFrame need for AlphaPurify?

The README states it must include a time column, an asset identifier column, a price column and your factor column. The FactorAnalyzer and PureExposures constructors take those names as trade_date_col, symbol_col, price_col and factor_name.

How do I install AlphaPurify?

Run pip install alphapurify. That installs the latest stable version, and the README notes the main branch is in active development, so to test the newest scripts or functions you should install from a clone of the repository instead.

How does AlphaPurify differ from Alphalens or QuantStats?

The README says AlphaPurify goes far beyond IC analysis and simple graphs, while libraries like QuantStats and Pyfolio focus on analysing return curves and portfolio performance rather than backtests. AlphaPurify instead provides factor cleaning, long, short and long-short rebalancing backtests, attributions and interactive Plotly reports as one pipeline.

Official sources

  1. eliasswu/AlphaPurify on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/eliasswu-alphapurify.svg)](https://hysenlabs.com/projects/eliasswu-alphapurify)

Was this useful?