RankAlpha
Built the ML, then beat it. A cross-sectional equity ranker that learns to order the S&P 500 — then a pre-registered duel showed plain 12-1 momentum wins the long-only product book (after-cost Sharpe 1.79 vs 1.41), so momentum ships and the ML stays research. The model's ordering skill is real, but it only looks monetisable long/short. Built on a SEC point-in-time fundamentals gate, a 380+ test suite, and published negative results.
- Python
- LightGBM · LambdaMART
- Learning-to-rank
- Walk-forward + embargo
- Pre-registered duel
- SEC EDGAR point-in-time
- SHAP
- After-cost Sharpe — momentum vs ML
- 1.79 vs 1.41
- ML mean Rank IC (vs momentum)
- 0.0505 vs 0.0414 (t=1.64)
- Audit machinery
- 380+ tests · SEC point-in-time
What it is
RankAlpha ranks the S&P 500 every month, trades the top and bottom of that ranking as a market-neutral book, and turns the result into a risk-managed, self-explaining portfolio. The key word is ranking: it doesn't predict prices, it predicts relative order — which stocks will outperform their peers — because that's the only thing the strategy actually uses.
The trap I escaped first
The first version did the obvious thing: predict a single stock's direction from its own technical features. It posted a Sharpe of 0.72 — until I audited it. Most of that edge was look-ahead leakage: training labels that quietly peeked into the test window. After purging the leakage and charging realistic costs, accuracy fell to ~0.50 — a coin flip.
That dead end was the most useful result in the project. Single-stock direction is dominated by market-wide noise nobody can predict. The honest failure is what forced the real design.
The cross-sectional pivot
Instead of "will this stock go up?" the question became "will it outperform the other 499?". You rank all the stocks each month, go long the top decile and short the bottom, and size the two legs equally so the market's move cancels out. What's left is the part that's actually about the individual stock — relative strength — which is where systematic equity edge tends to live.
The model
The ranker is LightGBM with a learning-to-rank objective (LambdaMART) — it optimises the ordering of stocks directly, not each stock's return. Seven price-based factors (momentum, volatility, size, reversal, liquidity), normalised cross-sectionally within each day. It's validated with walk-forward retraining plus a 21-day embargo, so no training label can overlap the test window — the same leakage discipline the first version failed.
The bar it had to clear was a dumb baseline: plain 12-month momentum, no learning. It cleared it — modestly and honestly.
A modest, honestly-measured edge of +0.32 Sharpe over a plain-momentum baseline, after 10 bps/side costs. Not yet statistically significant (Rank IC t = 1.64) and untested through a momentum crash — the believability is the point, not the headline.
What it actually learned — and a mistake I caught
Feature importance said volatility dominated, and I first wrote that up as "the model discovered the low-volatility anomaly." That was wrong, and checking the SHAP direction caught it: the long picks have high volatility with positive contribution — it tilts toward higher-vol names paired with momentum, the opposite of low-vol.
The lesson is worth more than the result: importance tells you which feature matters, never which direction. You confirm the sign before you name the effect.
Volatility dominates by importance — but SHAP direction shows the long book tilts toward higher-volatility names paired with momentum, not the low-volatility anomaly. Importance tells you which feature matters, never which direction.
Does the ranking hold?
The real test isn't the headline Sharpe — it's whether stocks the model ranks high actually outperform. Sort by the model's score and look at what happened next month: the returns climb cleanly through the top deciles. The top decile is the strongest; the bottom (short) decile is the weak spot, so the edge leans long.
Sorted by the model's score (known in advance), then measured against realised forward returns. The clean rise from decile 5 → 9 is the real signal; the short leg (decile 0) still sits slightly high, so the edge is concentrated on the long side.
The duel — I built the ML, then beat it
Ranking skill is not the same thing as a tradeable edge, so I pre-registered a decision rule before running the test: the ML earns its keep only if it beats plain 12-1 momentum on after-cost Sharpe over the full window AND wins Rank IC in more than half the calendar years. Ties or momentum wins ⇒ trade momentum, keep the ML as research. The rule is applied by code as arithmetic, so no judgement is exercised after seeing the numbers.
Both books run through the identical harness — same universe, same dates, same long-only top-N, same inverse-vol sizing, same weight cap, same vol target, same 10 bps/side cost. Only the score differs.
Over 47 monthly rebalances, momentum won:
- —Momentum (12-1) — +23.93% annualised, Sharpe 1.792, Sortino 3.32, max drawdown −12.03%, mean Rank IC 0.0414 (t = 1.58).
- —Frozen ML — +20.62% annualised, Sharpe 1.407, Sortino 2.44, max drawdown −15.20%, mean Rank IC 0.0505 (t = 1.64).
The split is the interesting part. The ML has the better Rank IC — its ordering skill is real, and slightly better than momentum's. It still loses the book, because the long-only top-decile product only harvests one tail of that ordering, and momentum's tail happens to be fatter and cheaper to trade. The ML's advantage lives across the whole cross-section, which is a long/short shape, not a long-only one.
So the product ships momentum, and the ML stays research with a specific open question rather than a vague hope.
The duel harness is lab/signal_duel.py, its generated report is figures/lab/signal_duel.md, and the whole system is documented in ARCHITECTURE.md.
From signal to portfolio
The score becomes an actual portfolio: long-only top picks, weighted inverse to volatility, capped per name, and scaled to a target volatility with the remainder held as cash. Every holding carries the factor reasons that put it there, and a local LLM turns those reasons into plain English — fed only the model's real numbers, so it explains rather than invents. It's framed throughout as an educational simulation, never investment advice, and it quotes no return probabilities.
Forward paper-trading
To test it honestly, I froze the model and traded it forward — no refitting with hindsight — for 23 months. Against an equal-weight benchmark it landed at Sharpe 1.25 vs 1.21, with realised volatility right on the 14% target, so the risk control is well-calibrated even where the edge is thin.