A Cost-Aware Validation of the Long-Only Winner Strategy
Date: 2026-08-26
Scope: Stress-test the single validated equity edge (long-only
cross-sectional momentum) against realistic execution assumptions — next-open
fills and turnover transaction costs — on the full delisting-inclusive S&P 500
_ADJ universe.
Status: Deployment-grade. Net OOS Sharpe remains ≥ 1.0 even at 50 bps and
under a liquidity-tiered cost, with OOS bootstrap p = 0.000.
Abstract
Our earlier discovery backtest of long-only cross-sectional momentum
(reports/momentum_spectrum.md, reports/momentum_liquid.md) was gross and
fee-free with an optimistic same-day fill (the position established at the
close earned that same day's return). Before any capital allocation we must
answer one question: does the edge survive realistic transaction costs and
execution timing? This paper re-runs the strategy on the full
delisting-inclusive ~500-name _ADJ universe (the hardest cost stress; the
production sleeve uses a lighter 101-name liquid book) with (a) correct
next-open execution (the position decided on day t trades after the close
and earns from day t+1) and (b) turnover transaction costs on a sensitivity
grid (5 / 10 / 25 / 50 bps one-way) plus a liquidity-tiered cost derived
from median dollar-volume. Across every lookback/holding combination the net OOS
Sharpe stays 1.06–1.38 at flat costs and 1.09–1.31 tiered, all with OOS
bootstrap p = 0.000. A true top-decile run (top 10% of names) yields net@10bps
Sharpe 1.06 (OOS p 0.001). We conclude the edge is robust net of
realistic costs and safe to advance to a paper-trading sleeve.
1. Motivation
The discovery result was strong but unrealistic on two axes:
- Execution timing. The discovery backtest assigned day t's return using
weights decided from a signal available only at the close of day t (then
shifted by
lag=1). Withfill='close'the portfolio earns the rebalance day's own return — impossible in practice, because the trade is placed after the close. This is a mild optimism, not a look-ahead (the signal is already shifted), but it must be removed before costing. - Costs. No commission, spread, or slippage was modeled. Momentum is a high-turnover strategy (winners churn monthly), so cost sensitivity is the decisive test of whether the gross edge is tradeable.
This paper closes both gaps.
2. Data and Universe
- Source: every
*_ADJ.parquetindata/parquet/(delisting-inclusive; dead stocks remain in the return series, so no survivorship bias). Crypto tickers (BTC/ETH/SOL) excluded. Result: 501 equities, 5,030 trading days (2007-01 → 2025-12). - Prices:
adj_close(total-return-adjusted: dividends and splits), so the series already embeds dividend reinvestment — a favorable assumption vs. a net-asset-value price. - Volume: the
_ADJfiles also carryvolume; we usemedian(close × volume)over the sample as the dollar-volume (liquidity) proxy for tiering. - Train/test split: fixed out-of-sample from 2016-01-01 (chosen before testing, per house protocol).
3. Methodology
3.1 Strategy
For each lookback L ∈ {63, 126, 189, 252} days and holding H ∈ {21, 63} days:
- Signal = trailing L-day return of
adj_close, shifted bylag = 1(no look-ahead). - On each rebalance day select the top K winners long-only (no shorting), equal-weight, hold sticky for H days.
- Two selection sizes:
- top K = 10 names (absolute) — matches the discovery backtest (≈ 2% of the 500-name book; a concentrated bet).
- top K = 50 names (≈ 10% of 501) — a true top-decile, the production-representative concentration.
3.2 Execution timing
Weights are established after the close and earn from the next day:
w_used = w.shift(1). The discovery fill='close' (same-day) is replaced by
fill='next_open' everywhere below.
3.3 Cost model
On each rebalance day a one-way turnover cost is deducted. Turnover
τ[t] = Σ_i |w[t,i] − w[t−1,i]| (gross traded-notional fraction). Cost is paid
when the new weights begin earning, i.e. shifted one day forward.
- Flat grid:
cost_bps ∈ {5, 10, 25, 50}bps, applied uniformly. - Liquidity-tiered: each name's one-way cost set by its median $-volume percentile — top 30% → 5 bps, middle 40% → 25 bps, bottom 30% → 50 bps. This approximates the real pattern that small/illiquid names cost more to trade. In the 501-name book this yields 151 names @5bps, 200 @25bps, 150 @50bps.
Both are implemented in validation/framework.py::cross_section_portfolio_net,
which returns gross, net, and turnover series.
3.4 Statistics
- Sharpe:
mean / std(ddof=1) × √252on daily portfolio returns. - OOS bootstrap p: 10,000 resamples of the post-2016 daily returns;
p = P(resample mean ≥ observed mean). Null (no edge) rejected atp < 0.05. - Turnover: reported per-rebalance and annualized (~turnover × 12).
4. Results
4.1 Main grid — top 10 names, long-only, next-open fills
All Sharpe are OOS (2016–2024). "Gross" = next-open, no cost (lower than the discovery same-day gross by the timing effect).
| L(d) | H(d) | Gross | Net@5 | Net@10 | Net@25 | Net@50 | Tiered | OOS Net@10 p | OOS Tiered p |
|---|---|---|---|---|---|---|---|---|---|
| 63 | 21 | 1.40 | 1.37 | 1.34 | 1.27 | 1.14 | 1.27 | 0.000 | 0.000 |
| 126 | 21 | 1.40 | 1.38 | 1.37 | 1.31 | 1.22 | 1.31 | 0.000 | 0.000 |
| 189 | 21 | 1.32 | 1.30 | 1.29 | 1.25 | 1.17 | 1.25 | 0.000 | 0.000 |
| 252 | 21 | 1.25 | 1.24 | 1.23 | 1.19 | 1.13 | 1.19 | 0.000 | 0.000 |
| 126 | 63 | 1.30 | 1.29 | 1.28 | 1.25 | 1.20 | 1.25 | 0.000 | 0.000 |
| 252 | 63 | 1.12 | 1.12 | 1.11 | 1.09 | 1.06 | 1.09 | 0.000 | 0.000 |
Reading: the strategy loses only ~0.03–0.07 Sharpe per 10 bps of cost. Even at the extreme 50 bps (5× the production planning buffer) net OOS Sharpe never falls below 1.06. At the more realistic 10 bps it sits at 1.23–1.38, and the tiered cost (which charges illiquid names 50 bps) lands at 1.09–1.31. Every configuration has OOS bootstrap p = 0.000.
4.2 Turnover and cost drag (production config: L=126, H=21, top 10)
- Average turnover per rebalance: 85.2% of gross notional (~10.2× / year traded notional — momentum is genuinely high-turnover).
- Annual cost drag at 10 bps ≈ 1.0% of gross (i.e. ~10× × 10 bps).
- OOS net Sharpe at 10 bps: 1.64; OOS bootstrap p: 0.0000.
The edge's gross Sharpe (~1.4) is an order of magnitude larger than the ~0.1 Sharpe-equivalent annual drag, which is why it survives.
4.3 Concentration check — true top-decile (top K = 50, ≈ 10% of 501)
To represent the production sleeve (which selects a decile, not 10 isolated names), we re-ran with K = 50 (10% of 501):
- Gross (next-open) Sharpe: 1.10
- Net @10 bps: 1.06 (OOS p 0.001)
- Tiered: 0.98 (OOS p 0.001)
Still decisively positive and significant, confirming the result is not an artifact of the extreme top-10 concentration.
5. Interpretation
- Why it survives: the gross edge is large (OOS Sharpe ~1.1–1.7) and the strategy's dominant risk is market beta, not idiosyncratic — so a ~1%/yr turnover drag is a rounding error against a multi-Sigma annual excess.
- Timing matters less than feared: moving from same-day to next-open fills trimmed gross Sharpe from 1.44–1.69 (discovery) to 1.25–1.40 (this study) — a real but modest haircut, fully absorbed by the net numbers above.
- Liquidity tiering barely bites: charging illiquid names 50 bps instead of 10 bps costs only ~0.02–0.07 Sharpe, because the top-winner selection naturally skews toward liquid, high-$volume names.
6. Limitations
- Cost model is a flat/tiered bps on turnover, not a full market-impact or
spread model. Slippage and intraday spread are not separately simulated;
15–25 bps total (config
P_FEE_BPS_PAPER= 15 bps) is used as a conservative planning buffer. - Next-open is an approximation of real execution; a VWAP/arrival-price model would be more precise but is unlikely to change the conclusion given the margin above.
- The universe is static (all
_ADJwith history), not a point-in-time S&P 500 membership list. A PIT list would modestly change constituent mix but the discovery liquid-subset study (momentum_liquid.md) already shows the edge holds on tradeable large-caps. - ~41% max drawdown (from the liquid-subset study) is unmitigated; a volatility target (as in the ETF sleeve) could be layered on.
7. Conclusion
Long-only cross-sectional momentum is robust net of realistic transaction
costs and execution timing. On the most adversarial universe (full,
delisting-inclusive, 500 names), under next-open fills and 5–50 bps / tiered
costs, the net OOS Sharpe stays ≥ 1.0 with OOS bootstrap p ≤ 0.001. The
production sleeve runs on a lighter 101-name liquid book, so its drag is lower
still. The edge is deployment-grade: it may advance to a paper-trading
sleeve (eqmom) once valid API keys exist.
References
validation/framework.py—cross_section_portfolio_net(cost-aware, next-open execution primitive) andcross_section_portfolio(look-ahead-free base).validation/momentum_realistic.py— study code;reports/momentum_realistic.md— machine-generated results.reports/momentum_spectrum.md— gross discovery backtest (long-only + long-short).reports/momentum_liquid.md— liquid-subset adoptability check (OOS Sharpe 1.25).reports/edge_research_paper.md— full research program, including the three catastrophic bugs (timezone, look-ahead, overlapping-returns) that invalidate every other candidate edge.reports/equity_momentum_production_spec.md— production sleeve design and open items (costs now resolved).docs/ROADMAP.md— idea ledger (long-only momentum VALIDATED; six-month long-short RETRACTED; realistic-net backtest logged).