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.
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.
| 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.
| 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.
| 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.
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
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 checksThe 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.
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)
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.
- 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.
- 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: customwith 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_illiquidityandsize_proxyfactors 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.
- A-shares (CSI 300) path via
akshareis config-driven (universe.source: akshare) and not exercised in CI. alphalens-reloadedcan be dropped in for richer tear-sheets (left commented inrequirements.txt).- Sector-neutralisation is implemented (
signals.neutralize_groups) and activates when a ticker→sector map is supplied.
MIT — research/educational use. Not investment advice.





