Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AlphaForge — Cross-Sectional Equity Alpha Research & Backtesting Engine

AlphaForge is a compact, reproducible systematic equity alpha-research workflow. It researches ~10 classic cross-sectional factors on a liquid US universe (current S&P 500), evaluates each with information-coefficient (IC) analysis and decile long-short portfolios, accounts for turnover and transaction costs, and combines the factors into a walk-forward composite — all under strict point-in-time / no-lookahead discipline.

Philosophy: correctness and no-lookahead beat impressive numbers. We test many signals, report all of them, never cherry-pick, and include a deflated-Sharpe multiple-testing check. Modest ICs (0.02–0.05 monthly rank-IC) and a composite whose edge largely vanishes after honest deflation are exactly what we expect — and exactly what we show.


Results

Universe: 500 current S&P 500 names, daily adjusted OHLCV 2010-01-04 → 2026-06-26 (4,145 trading days), monthly rebalance, 21-day holding horizon, 10 bps round-trip cost per unit turnover, decile dollar-neutral long-short. Regenerate everything with the three scripts below.

Full IC table (headline horizon = 21d, all signals, sorted by ICIR)

signal mean IC IC std ICIR t-stat p-value IC hit-rate n
amihud_illiquidity 0.0270 0.1020 0.264 3.70 0.0003 0.59 196
size_proxy 0.0178 0.0982 0.181 2.53 0.012 0.58 196
downside_beta 0.0336 0.2566 0.131 1.81 0.072 0.57 191
momentum_12_1 0.0079 0.1908 0.041 0.56 0.576 0.54 184
reversal_1m 0.0033 0.1612 0.020 0.28 0.778 0.50 195
relative_turnover -0.0004 0.0798 -0.006 -0.08 0.939 0.48 191
high_52w -0.0153 0.2136 -0.072 -0.99 0.323 0.51 191
low_volatility -0.0275 0.2447 -0.113 -1.57 0.119 0.45 194
idiosyncratic_vol -0.0201 0.1737 -0.116 -1.61 0.108 0.45 194
max_reversal -0.0222 0.1891 -0.117 -1.64 0.102 0.46 196

Only amihud_illiquidity and size_proxy clear a conventional significance bar — and within an all-large-cap universe these two almost certainly proxy the same relative-size effect (note their similar IC profile). Several textbook factors (low-vol, momentum) are flat or negative here, a reminder that factor performance is universe- and regime-dependent.

Long-short backtest, net of 10 bps cost (monthly rebalance)

strategy Sharpe (net) Sharpe (gross) ann. return (net) ann. vol max DD avg turnover
amihud_illiquidity 0.94 1.01 11.3% 11.3% -17.5% 0.64
downside_beta 0.65 0.67 18.1% 25.8% -48.1% 0.57
size_proxy 0.51 0.58 6.0% 11.4% -45.7% 0.69
momentum_12_1 0.19 0.25 4.3% 22.1% -61.3% 1.04
composite_ic_weighted 0.16 0.25 3.9% 23.8% -58.2% 1.79
composite_equal_weight -0.47 -0.32 -7.0% 15.6% -81.4% 1.96
benchmark_equal_weight_market 1.03 20.2% 18.0% -38.3%
reversal_1m / low_vol / idio_vol / high_52w / max_reversal / rel_turnover all < 0

(Full machine-readable table: reports/backtest_metrics.csv.)

Honest reading:

  • The equal-weight long-only market benchmark (Sharpe ≈ 1.0) beats every dollar-neutral long-short strategy here. That is expected: 2010–2026 was a strong equity bull market, and a market-neutral book deliberately strips out that beta. The right comparison for a long-short alpha is risk-adjusted, market-neutral performance — not the raw bull-market return.
  • IC-weighting clearly beats naive equal-weighting (Sharpe +0.16 vs −0.47): tilting toward factors that have recently worked (using only past ICs) avoids being dragged down by the many dead factors. But it is no free lunch.
  • Deflated Sharpe ratio of the composite ≈ 0.00 (P(true SR > 0) given 12 trials). After accounting for how many signals/composites we tried, the composite's Sharpe is not distinguishable from luck — in fact it sits below the expected maximum Sharpe under the null, because one factor (amihud) dominates the rest. This is the single most important honesty point in the whole study.

Sensitivity (IC-weighted composite, net)

rebalance 0 bps 5 bps 10 bps 20 bps avg turnover
Monthly 0.25 0.21 0.16 0.07 1.79
Weekly -0.18 -0.28 -0.37 -0.56 0.93

Sharpe degrades monotonically with cost and weekly rebalancing destroys the edge — turnover matters more than the raw signal. Robustness, not a single lucky configuration, is the point.

Figures (reports/figures/)

IC bars IC decay
Decile monotonicity — amihud Composite equity vs market
Composite drawdown Walk-forward IC weights

Install & run

python -m venv .venv && source .venv/bin/activate     # Python 3.11+
pip install -r requirements.txt

python scripts/1_build_data.py        # download + cache S&P 500 OHLCV -> data/raw/*.parquet
python scripts/2_run_ic_study.py      # full IC table, IC decay, decile plots -> reports/
python scripts/3_backtest_composite.py# single-factor + composite backtests, sensitivity, DSR

pytest                                # 29 tests incl. explicit no-lookahead checks

The first script needs internet (yfinance + a Wikipedia scrape of current constituents); the others run entirely off the local parquet cache. Everything is driven by config.yaml and a fixed seed, so a run is reproducible from config + pinned requirements.txt. For a fast smoke run set universe.max_names: 60 in the config.


How it works

src/alphaforge/
  config.py      typed view over config.yaml
  data.py        universe resolution + yfinance download + parquet cache
  signals.py     the ~10 point-in-time factor functions + cross-sectional preprocessing
  evaluation.py  IC / ICIR / IC-decay, decile portfolios, portfolio stats, deflated Sharpe
  backtest.py    walk-forward dollar-neutral long-short engine (lag, turnover, costs)
  combine.py     equal-weight + walk-forward IC-weighted composite
  plotting.py    all figures
scripts/         1_build_data · 2_run_ic_study · 3_backtest_composite
tests/           unit tests, including test_no_lookahead.py
notebooks/       analysis.ipynb (narrative with figures)

Signals (each oriented so higher score → higher predicted return)

12-1 momentum · 1-month reversal · low volatility (60d) · idiosyncratic volatility (residual vs a market factor) · Amihud illiquidity · relative turnover / abnormal volume · 52-week-high proximity · MAX daily-return reversal · downside beta · size proxy (trailing dollar volume). Each is winsorised and cross-sectionally z-scored (or ranked) every rebalance date.

Quant rigor (what makes it credible, not just pretty)

  • Strict point-in-time. A signal at date t uses only data up to t (all trailing rolling windows, no negative shifts). Trades execute at t+1 (execution_lag ≥ 1). Forward returns are used only as evaluation targets, never as model inputs. This is enforced mechanically in tests/test_no_lookahead.py: we recompute every signal, every IC-weight, and backtest P&L on data whose future has been replaced with noise and assert nothing on/before the cutoff changes.
  • Walk-forward for anything fitted. The IC-weighted composite weights at date t use only realised ICs whose forward-return window already closed by t — no peeking at returns that haven't happened.
  • Costs in the headline. Net-of-cost is the headline; gross is shown alongside for transparency.
  • Multiple-testing honesty. We report the full IC table for all signals and compute a deflated Sharpe ratio (Bailey & López de Prado, 2014) for the composite using the dispersion of all trial Sharpes.
  • Sensitivity. Rebalance frequency (weekly/monthly) and cost (0–20 bps) are varied to show robustness rather than a single tuned point.

Limitations (read this)

  • Survivorship bias. The universe is the current S&P 500. Firms dropped from the index (often after poor performance or distress) are absent, which biases results optimistically. To fix this properly you need a point-in-time constituent list; the code supports supplying one via universe.source: custom with a dated membership file, but the bundled run does not have one.
  • Universe is all large-cap. Genuine size/illiquidity premia live in small caps. Within the S&P 500 the amihud_illiquidity and size_proxy factors measure only relative size among large caps and are mutually correlated — do not read them as two independent sources of alpha.
  • Multiple testing. ~10 signals plus 2 composites were evaluated. The composite's deflated Sharpe ≈ 0: treat the headline long-short numbers as research output, not a deployable edge.
  • No corporate-action / point-in-time fundamentals. Size is proxied by dollar volume because free, reliably point-in-time shares-outstanding data is not available; true market-cap size and fundamental factors (value, quality) are out of scope.
  • Simplified execution. Fixed-weight holding between rebalances, linear turnover cost, no borrow/financing/slippage/market-impact model, no capacity analysis. Trades assume next-bar close fills at the adjusted price.
  • Equal-weight cross-sectional market proxy is used for beta/idiosyncratic-vol regressions rather than a true value-weighted market.

Optional / extensions

  • A-shares (CSI 300) path via akshare is config-driven (universe.source: akshare) and not exercised in CI.
  • alphalens-reloaded can be dropped in for richer tear-sheets (left commented in requirements.txt).
  • Sector-neutralisation is implemented (signals.neutralize_groups) and activates when a ticker→sector map is supplied.

License

MIT — research/educational use. Not investment advice.

About

Cross-sectional equity alpha research & backtesting engine: ~10 point-in-time factors on the S&P 500 with IC/ICIR analysis, decile long-short portfolios, walk-forward IC-weighted composite, transaction costs, and strict no-lookahead tests.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages