Skip to content

IC Analysis: How to Measure Whether Your Factor Actually Predicts

IC analysis is the primary tool for evaluating factor quality. Here's how to compute it, interpret it, and avoid the common mistakes.

6 of 10 ~3 min

You’ve built an alpha factor. You backtested it and the long-short portfolio returns 18% annually with 1.4 Sharpe. Before you allocate capital, you need to answer one question: is this factor actually predicting future returns, or did you just get lucky in your backtest period?

IC analysis is how you answer that question rigorously.

What Is IC?

IC (Information Coefficient) measures the correlation between your factor’s value at time t and the asset’s realized return from t to t+1.

ICt=corr(ft(X(i)),rt+1(i))IC_t = \text{corr}(f_t(X^{(i)}), r_{t+1}^{(i)})

For a factor to be useful, you want:

  • IC consistently positive across time
  • IC statistically significant (t-stat > 2.0)
  • IC stable across market regimes

Pearson IC measures linear correlation. Spearman (rank) IC measures monotone correlation. Rank IC is more robust to outliers — use it as your primary metric.

Computing IC in Python

import numpy as np
import pandas as pd
from scipy import stats

def compute_ic(factor_values, returns, method='spearman'):
    """
    Compute IC between factor values and next-period returns.

    Parameters
    ----------
    factor_values : DataFrame (date × asset)
        Factor values at each date for each asset
    returns : DataFrame (date × asset)
        Realized returns from t to t+1 for each asset
    method : 'pearson' or 'spearman'

    Returns
    -------
    ic_series : Series
        IC at each date
    mean_ic : float
        Mean IC across all dates
    t_stat : float
        t-statistic of mean IC
    """
    ic_list = []
    dates = factor_values.index

    for date in dates[:-1]:
        if date not in returns.index:
            continue
        next_date = returns.index[returns.index.get_loc(date) + 1]

        fv = factor_values.loc[date].dropna()
        ret = returns.loc[next_date].dropna()

        common = fv.index.intersection(ret.index)
        if len(common) < 10:
            continue

        if method == 'spearman':
            ic, _ = stats.spearmanr(fv[common], ret[common])
        else:
            ic, _ = stats.pearsonr(fv[common], ret[common])

        ic_list.append({'date': date, 'ic': ic})

    ic_df = pd.DataFrame(ic_list).set_index('date')
    ic_series = ic_df['ic']

    mean_ic = ic_series.mean()
    t_stat = mean_ic / (ic_series.std() / np.sqrt(len(ic_series)))

    return ic_series, mean_ic, t_stat


def compute_rolling_ic(factor_values, returns, window=60, method='spearman'):
    """
    Compute rolling IC with a trailing window.
    """
    ic_series, mean_ic, t_stat = compute_ic(factor_values, returns, method)

    rolling_ic = ic_series.rolling(window).mean()

    return rolling_ic, mean_ic, t_stat

How to Interpret IC

IC RangeInterpretation
IC < 0.01Weak — probably not economically significant
IC 0.01–0.03Moderate — can work with sufficient turnover and diversification
IC 0.03–0.05Strong — genuine predictive signal
IC > 0.05Very strong — either a great factor or look-ahead bias

Sustained IC is what matters. A factor with IC = 0.05 in one year and IC = 0.00 the next year is not reliable. You want consistent IC across multiple years.

The t-Statistic Is More Important Than IC Itself

IC = 0.03 sounds decent. But if the t-statistic is 1.2, it’s not statistically significant — it could easily be noise.

A useful rule: require t-stat > 2.0 for mean IC. This means the probability of observing this IC by chance under the null hypothesis (true IC = 0) is less than 5%.

For a factor to be practically useful, you want:

  • Mean rank IC > 0.02
  • t-stat of mean IC > 2.0
  • IC positive in > 55% of periods (more than half)
  • IC stable — no monotonic decay over time

Common Pitfalls

Only looking at average IC. A factor with mean IC = 0.04 but IC = 0.08 in 2018-2020 and IC = 0.00 in 2021-2024 is regime-dependent. Plot rolling IC to see this.

Ignoring IC volatility. A factor with IC = 0.05 but std(IC) = 0.08 is less reliable than one with IC = 0.04 and std(IC) = 0.03. The signal-to-noise ratio matters.

Using in-sample IC to justify out-of-sample allocation. If you optimized your factor on 2015-2020 data and report IC on that same period, you’re showing in-sample IC. Out-of-sample IC (computed on 2021-2024 data) is what matters for real allocation.

Confusing correlation with causation. A factor that correlates with returns doesn’t necessarily predict them causally. The correlation might be spurious or driven by a confounding variable.

IC Decay Over Time

Even good factors decay. A factor with IC = 0.05 in 2010 might have IC = 0.02 in 2020 as more quant funds trade the same signal. Factor decay is normal — the question is whether the factor remains economically viable after transaction costs.

Plot IC over rolling 6-month or 12-month windows. If you see monotonic decay over 3+ years, the factor is being arbitraged. If IC plateaus at a lower level, the factor has reached an equilibrium.

Where It Fits

IC analysis is the foundation of factor evaluation. Before adding any factor to a portfolio, you need to establish that it has statistically significant, stable IC. Factor decay studies how IC changes over different horizons. Cross-sectional stability checks whether IC is consistent across different market regimes.

References

  • López de Prado, Advances in Financial Machine Learning, Ch. 3. Wiley, 2018. ISBN: 978-1-119-48208-6. -ISLR Ch. 2 (simple linear regression foundations).
  • Ang, Asset Management, Ch. 9. Oxford University Press, 2014. ISBN: 978-0-19-995932-3.
  • Green, Hand, Zhang, “The Characteristics That Provide Independent Information,” Review of Financial Studies, 2013.