← all research
REJECTED

"Production Spec — Equity Momentum Sleeve (`eqmom`)"

2026-08-26

Status: BUILT & TESTED (observe-only by default; not yet trading — requires valid paper keys + EQMOM_TRADE_ENABLED=1).

Source edge: reports/momentum_spectrum.md + reports/momentum_liquid.md. The single edge that survived the 2026-08 three-bug audit (timezone / look-ahead / overlapping-returns). Long-only cross-sectional momentum, OOS 2016–2024 Sharpe 1.44–1.69 (full universe) / 1.25 (liquid subset), bootstrap p < 0.001.


1. Strategy

2. Timing & idempotency (house convention)

3. Safety (fail-closed)

Inherited from production/etf_trend_runner.py and the codebase's paper-only design:

4. Deploy

# observe-only smoke test (no orders, no keys needed for logic; needs valid
# keys only to read account/positions from Alpaca)
python -m production.equity_momentum_runner --once --force

# register / launch with the bot manager (uses .env; observe until enabled)
python start_bots.py --status      # confirms 'eqmom' is registered
python start_bots.py               # starts eqmom (observe) among the live set

To enable paper trading (after regenerating the dead main .env keys per docs/ROADMAP.md incident 2026-08-22):

  1. Set P_TRADE_MODE=PAPER_TRADE in .env.
  2. Set EQMOM_TRADE_ENABLED=1 (e.g. in the eqmom entry of start_bots.py, or export it).
  3. Verify a 32 s startup (python start_bots.py --status) shows [ OK ].

5. Env knobs

VarDefaultMeaning
EQMOM_EQUITY_FRACTION0.95fraction of account equity used as gross long
EQMOM_TRADE_ENABLEDunset"1" allows orders when global mode is PAPER_TRADE
EQMOM_LOOKBACK126trailing-return window (trading days)
EQMOM_TOP_DECILE0.10fraction of universe held (top decile)
EQMOM_REBALANCE_DAYS21calendar days between rebalances

6. Artifacts

7. Tests

tests/test_equity_momentum_runner.py (4 cases) verifies:

8. Open items before live trading

  1. Keys: main .env Alpaca keys are dead (401, ~Aug 19) — regenerate.
  2. Costs (RESOLVED in research): the discovery backtest was gross/fee-free with same-day fills, but validation/momentum_realistic.py now re-runs the long-only top-N on the full delisting-inclusive ~500-name universe with correct next-open execution timing AND turnover costs on a grid (5/10/25/50 bps) + a liquidity-tiered cost (top 30% $-volume → 5 bps, mid 40% → 25 bps, bottom 30% → 50 bps). Result: even at 50 bps or tiered, net OOS Sharpe stays ≥ 1.0 (full universe Net@10 ≈ 1.23–1.38, Tiered ≈ 1.19–1.31), OOS p = 0.000. Turnover is heavy (~85% of gross per monthly rebalance ≈ 10×/yr notional) but the edge absorbs it. The 101-name liquid production universe drags less. Use config.P_FEE_BPS_PAPER (15 bps) as a conservative floor in sizing; for modeling use 10 bps flat as the base case and 25 bps as stress. No separate slippage model yet — 15–25 bps total is a safe planning buffer.
  3. Universe: promote to a PIT S&P 500 list for a cleaner, larger book.
  4. Risk: confirm the ~41% maxDD from research is acceptable for the account size; consider a volatility target like the ETF sleeve if not.