SD book

8. Direction: how far can SD honestly go?

I'm new here · 4/6 I'm working on SD itself · 4/6 I'm working on the backtester · 4/6 I want to rerun the evidence · 3/5

5 min read

Chapter 2 found that SD's squeeze and momentum readings call direction no better than a coin flip once the market's upward drift is removed. A fair reply is: maybe you didn't look hard enough. This chapter looks as hard as we know how, and reports the ceiling.

The short version: SD now calls direction right 56.85% of the time on 2019–2026 data (classic SD: 50.93%), which matches the ~56% estimate. But that 56.85% is the market's drift, not foresight. "Always up" scores exactly the same, and nothing beat it.

#The search, spread across time

A search run once on one train/test split can get lucky: one era happens to suit one idea. So sd_direction spreads the search across nineteen test years:

  1. For each test year Y from 2008 to 2026, every candidate model trains only on rows whose outcome was known before 1 January Y (an expanding window, purged at the boundary).
  2. It is graded on year Y.
  3. A candidate is chosen for year Y by its average record over the five years before Y. That chosen candidate's grade on Y is the honest score. You only ever trade the model you would have picked at the time.

Why choose by the previous five years? Choosing by the full-history table would quietly use the answers. The walk-forward choice is what a careful person could actually have done each January.

#What we threw at it

src/direction.rs builds 41 measurements in 10 families. Each one is there because the finance literature says it carries some direction information:

FamilyIdea
sdSD's own reading: blend, agreement, fire × decay, tier, energy, stretch
mom5/21/63/126-bar returns, 12-minus-1-month momentum
trenddistance from 50/200-bar averages, drawdown from the 1-year high
volvolatility ratio and level ("calm markets drift up")
rel63-bar return minus SPY's (relative strength)
mktSPY's trend, momentum, volatility ratio, 5/21-bar returns
vixVIX vs its 1-year average, VIX level, VIX minus realized vol
seasonNovember–April flag, month of year
sdw, sdmSD read on weekly and monthly bars (the Extended SD idea)

The candidates are 37 linear models (logistic regression at two penalty strengths) plus three families of small gradient-boosted trees, which can find regimes like "falling market and fear spiking".

#The results

Hit rate pooled over every test year from 2008 to 2026, at four horizons:

Target"Always up"Best candidate in hindsightWalk-forward choiceBoosted trees
5-bar direction54.63%54.95% (season)55.18% (vs 54.92%)52.1–52.8%
21-bar direction57.88%57.88% (drift)57.71% (vs 58.16%)54.5–55.1%
63-bar direction62.21%62.21% (drift)62.76% (vs 62.75%)58.2–59.7%
21-bar beat SPY?50.55%51.16% (mom+trend+vol)50.62% (vs 50.93%)50.0–50.8%

Reports: reports/direction_{5d,21d,63d,excess_21d,excess_5d}.txt.

Three things to notice:

  • Models that dare to say "down" lose. Their down calls are right 35–45% of the time. Bear markets are rare and different from one another, so a model trained on the last one misreads the next.
  • The rank skill is real but tiny. The best per-year AUC (the chance a random up row scores above a random down row) is about 0.52. Higher P(up) does mean slightly more ups, but not enough to flip a call against drift.
  • Weekly and monthly SD add nothing to direction (AUC 0.50–0.505). This answers chapter 6's open question about Extended SD.

#What shipped

SD's side of 0.5 now comes from the best call the search could support: P(up) from drift plus the market's weather (SPY trend, momentum, volatility ratio, 5- and 21-bar returns). It is a five-input logistic model, stored as side in src/sd2/sd2_model.json and fitted by sd_direction --ship. How far SD sits from 0.5 still measures the expected move, scaled by time-decay strength:

SD = 0.5 + 0.5 · side · strength · intensity

sd_backtest verify on 2019–2026 data:

SD v2 (shipped)Classic SD
Hit rate of SD's side56.85%50.93%
"Always up" on the same rows56.85%56.85%
ρ(|SD − 0.5|, |21-bar move|)+0.187 (was +0.171)+0.003

Taking momentum's coin-flip lean out of the distance from 0.5 made the expected-move signal cleaner as well.

What this means for you. SD above 0.5 now mostly means "the market drifts up, and here is how big the move will be." Read the distance, not the side. If you want direction you can trade, chapter 11 shows the evidence: no SD direction strategy beat simply owning the stocks.

#Recipe: try your own direction idea

  1. Add a column to direction_frame in src/direction.rs, and add its name to a group in GROUPS (or to a new group).
  2. Add a candidate in candidates() in src/bin/sd_direction.rs.
  3. cargo run --release --bin sd_direction -- --h 21 (and --h 5, --h 63, --excess).
  4. Believe it only if the walk-forward choice row beats "drift only". The hindsight table is there to explain, not to pick from.