AlphaPurify Quantitative Factor Analysis Toolkit

repository·main·Indexed 43 days ago

https://github.com/eliasswu/alphapurify

A high-performance Python library for quantitative factor research, version 1.0.6. It provides a complete pipeline for factor construction, preprocessing, and evaluation, featuring the AlphaPurifier module with 40+ cleaning methods (Winsorization, Neutralization, Standardization), a parallelized DataBase for Parquet-based financial data pipelines, a vectorized FactorAnalyzer for IC testing and backtesting, and an Exposures module for return attribution with Plotly visualizations.

Tokens
13.2K
Snippets
32
Records
49
Agent score
59%

What's inside AlphaPurify

  1. Overview of AlphaPurify core modules

    main

    AlphaPurify is a factor analysis library for quantitative researchers, providing a full pipeline from factor construction to attribution. The library is organized into four main functional modules:

    1. alphapurify.FactorAnalyzer: Used for IC/Rank IC testing and long, short, or long-short stratified backtesting.
    2. alphapurify.AlphaPurifier: Used for factor preprocessing, including over 40 methods for outlier removal (winsorization), neutralization, and standardization (e.g., Ridge regression, Lasso regression, PCA decomposition).
    3. alphapurify.Database: Used for financial data aggregation, factor construction, and database ingestion.
    4. alphapurify.Exposures: Used for factor correlation analysis and factor return attribution analysis.
  2. Overview of AlphaPurify main modules

    main

    AlphaPurify is a Python library for factor construction, preprocessing, backtesting, and factor return attributions. It consists of four primary modules:

    1. alphapurify.FactorAnalyzer: Used for IC/Rank IC testing and Long/Short/Long-Short quantile backtests.
    2. alphapurify.AlphaPurifier: Provides 40+ preprocessing methods including Winsorization, Neutralization, and Standardization (e.g., ridge regression, lasso regression, PCA decomposition).
    3. alphapurify.Database: Handles financial data aggregation, factor construction, and factor storage.
    4. alphapurify.Exposures: Used for factor correlation analysis and factor-based return attribution.
  3. Prepare your DataFrame for AlphaPurify

    main

    To use AlphaPurify, your input DataFrame must contain the following columns to ensure correct processing:

    • Time column (e.g., datetime)
    • Symbol identifier column (e.g., symbol)
    • Price column (e.g., close)
    • Factor column(s) (the alpha values you wish to test)
  4. Full factor analysis workflow: Preprocessing, Backtesting, and Attribution

    main

    A complete workflow involves preprocessing the factor using AlphaPurifier, running backtests with FactorAnalyzer, and performing attribution with Exposures (or PureExposures).

    1. Preprocessing with AlphaPurifier

    Use AlphaPurifier to clean factors. It supports method chaining for operations like winsorization and standardization.

    2. Backtesting with FactorAnalyzer

    Initialize FactorAnalyzer with your processed DataFrame and specify the column names for time, symbol, price, and the factor. Call .run() to execute the backtest, followed by specific sheet creation methods for different views (long, short, long-short, or IC).

    3. Attribution with Exposures

    Use Exposures to analyze how other factors contribute to your target factor's returns and to check correlations.

    from alphapurify import AlphaPurifier, FactorAnalyzer, PureExposures
    
    # 1. Preprocess
    df = (
        AlphaPurifier(df, factor_col="alpha_003")
        .winsorize(method="mad")
        .standardize(method="zscore")
        .to_result()
    )
    
    # 2. Backtest
    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()
    
    # 3. Attribution
    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()
  5. Run a full factor research pipeline

    main

    A typical workflow involves preprocessing the factor with AlphaPurifier, running backtests with FactorAnalyzer, and performing attribution with PureExposures.

    from alphapurify import AlphaPurifier, FactorAnalyzer, PureExposures
    
    # 1. Preprocess the factor
    df = (
        AlphaPurifier(df, factor_col="alpha_003")
        .winsorize(method="mad")
        .standardize(method="zscore")
        .to_result()
    )
    
    # 2. Run backtests and generate reports
    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()
    
    # 3. Analyze contributions of other factors
    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()
    from alphapurify import AlphaPurifier, FactorAnalyzer, PureExposures
    
    # preprocess
    df = (
        AlphaPurifier(df, factor_col="alpha_003")
        .winsorize(method="mad")
        .standardize(method="zscore")
        .to_result()
    )
    
    #backtest
    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()
    
    #contributions of other factors
    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()
  6. Install AlphaPurify via pip

    main

    Install the latest stable version of AlphaPurify using pip:

    pip install alphapurify

    Note: If you want to test the latest scripts or functions from the main branch currently under active development, you should install by cloning the repository instead of using pip.

  7. How AlphaPurifier method chaining works

    main

    AlphaPurifier uses a method-chaining design pattern. This allows you to define a standard quantitative preprocessing pipeline—typically consisting of Winsorization, Neutralization, and Standardization—in a single, readable block of code. Each transformation method (except to_result) returns the AlphaPurifier instance itself, allowing you to call the next method immediately.

    result = (
        AlphaPurifier(df, factor_name="alpha", trade_date_col="date", symbol_col="ticker")
        .winsorize("mad")
        .neutralize("multiOLS", ["size_factor", "industry_factor"])
        .standardize("zscore")
        .to_result()
    )
  8. How continuous and discrete features are aligned in DataBase

    main

    The DataBase class distinguishes between two types of features to ensure data integrity:

    1. Continuous Features: These are typically price-derived indicators (e.g., returns, volatility). They are merged directly on (datetime, symbol). To prevent look-ahead bias, these features are shifted forward in time by shift_n periods (default is 1).
    2. Discrete Features: These represent categorical or event-based data (e.g., industry classification). They are aligned using a backward join_asof strategy, which forward-fills the last known value to the current timestamp.

    The final output is a single DataFrame sorted by (datetime, symbol).

  9. Understand the Backtest Result Metrics

    main

    The backtesting engine produces a comprehensive set of performance statistics (often returned in stats_ls, stats_l, or stats_s dictionaries). Key metrics include:

    MetricDescription
    Ann. ReturnAnnualized return
    Ann. StdAnnualized volatility (standard deviation)
    Ann. SharpeAnnualized Sharpe Ratio (excess return / volatility)
    Ann. SortinoAnnualized Sortino Ratio (excess return / downside volatility)
    Ann. CalmarAnnualized Calmar Ratio (excess return / max drawdown)
    Max DrawdownThe largest peak-to-trough decline
    WinRatePercentage of positive return periods
    P/LProfit/Loss ratio (mean return / mean loss)
    PnLTotal cumulative profit/loss
    Mean TurnoverAverage turnover per period
  10. Configure overnight return handling

    main

    Both PortfolioExposures and PureExposures allow you to control how overnight returns are treated via the overnight parameter. This is useful for distinguishing between intra-day and overnight risk/return profiles.

    Options:

    • 'on': Includes all returns (default).
    • 'off': Excludes overnight returns.
    • 'only': Analyzes overnight returns only.
  11. How FactorAnalyzer handles transaction costs and rebalancing

    main

    The FactorAnalyzer implements a rigorous backtesting model to avoid common quantitative errors:

    1. Look-Ahead Bias Mitigation: Uses fac_shift to ensure signals are lagged before execution.
    2. Fractional Grouping & Drift: Instead of assuming constant weights between rebalancing dates, it uses forward_fill on weights and calculates cum_prod of returns to simulate how capital naturally drifts within a rebalancing epoch.
    3. Asymmetric Transaction Costs: It calculates turnover by comparing weights at $t$ vs $t^-$. It supports distinct cost schedules for buying and selling based on the tax_rate_direction setting ('buy', 'sell', or 'both').
    4. Vectorized Execution: Uses Polars and Apache Arrow memory-mapping to process millions of rows efficiently while bypassing Python's GIL via Joblib multi-processing.
  12. Analyze factor-sorted portfolios with PortfolioExposures

    main

    The PortfolioExposures class is used to evaluate how a factor-sorted portfolio is exposed to selected risk factors and to decompose its returns. It constructs quantile portfolios (long, short, or long-short) based on a factor ranking, calculates forward returns, and performs cross-sectional OLS regressions to attribute performance to specific risk exposures.

    Key Workflow

    1. Initialize: Pass your dataset (Pandas or Polars) and define columns for dates, symbols, prices, the target factor, and the risk exposure factors.
    2. Run: Call .run() to execute the quantile construction and regression analysis.
    3. Visualize: Use the built-in plotting methods to view exposure dynamics and return attribution.

    Parameters

    • base_df: Input dataset (Pandas or Polars DataFrame).
    • trade_date_col: Timestamp column.
    • symbol_col: Asset identifier column.
    • price_col: Column used for return calculation.
    • factor_name: The factor used for ranking assets into quantiles.
    • exposure_cols: List of risk factor columns (e.g., ['size', 'value', 'volatility']).
    • rebalance_period: Interval for rebalancing (default: 1).
    • bins: Number of quantile groups (default: 5).
    • position: Portfolio type: 'l' (long-only top quantile), 's' (short-only bottom quantile), or 'ls' (long-short spread).
    • overnight: Treatment of overnight returns: 'on', 'off', or 'only'.
    • fac_shift: Optional integer to shift factor/exposure variables forward to prevent look-ahead bias.
    pe = PortfolioExposures(
        base_df=data,
        trade_date_col="datetime",
        symbol_col="symbol",
        price_col="close",
        factor_name="momentum",
        exposure_cols=["size", "value", "volatility"],
        bins=5,
        position="ls"
    )
    
    pe.run()
    pe.plot_portfolio_exposures()
    pe.plot_portfolio_returns()