Microstructure Trap — The Same Signal Looks Great (Until You Model the Fill)
Writing Lab Mini-Lab: Microstructure Trap — The Same Signal Looks Great (Until You Model the Fill)
A lot of “alpha” in backtests is really just execution ambiguity.
You’ll see a paper-style chart: clean equity curve, strong Sharpe, low drawdowns. Then you ask the single question that matters:
“At what price did you actually get filled?”
This mini-lab is designed to make that question unavoidable.
Toy strategy / idea
Take an intentionally dumb signal that many researchers start with:
- Signal: if today’s close is above yesterday’s close, go long tomorrow; otherwise be flat.
It’s basically “1-day momentum.” It should not be a gold mine.
Now run the same signal under two execution assumptions:
- Fantasy fill (bad): decide using today’s close and also trade at today’s close.
- Tradable fill (better): decide using today’s close, but trade at tomorrow’s open (or tomorrow’s VWAP).
The difference between (1) and (2) is often the difference between “publishable” and “nonsense.”
Then add a microstructure cost overlay:
- Half-spread + slippage model: per trade cost in bps that depends on liquidity.
Implementability constraints
To keep this lab grounded:
- Use a liquid instrument first (SPY / ES). If you do single names, spreads and open auctions will dominate.
- Use daily OHLCV data (you don’t need ticks to learn the lesson).
- Define trading times:
- Signal computed at close of day t.
- Entry at open of day t+1.
- Exit at open of day t+2 (if holding 1 day).
If you can’t express a strategy in a trade-timestamped way, you can’t evaluate it.
Minimal pseudocode
# signal: 1-day momentum
for each day t >= 2:
sig[t] = 1 if Close[t] > Close[t-1] else 0
# execution model A (fantasy): enter at Close[t] using sig[t]
retA[t] = sig[t] * (Close[t+1]/Close[t]-1) - costs(turnover)
# execution model B (tradable): enter at Open[t+1] using sig[t]
retB[t] = sig[t] * (Open[t+2]/Open[t+1]-1) - costs(turnover)
# cost model (simple): each entry or exit pays c_bps
costs(turnover) = turnover * (c_bps / 10000)
turnover = abs(pos[t]-pos[t-1])
You can expand “costs” to include a spread proxy (e.g., higher cost on high-vol days), but start simple.
What to measure
1) The “execution gap”
Compute the difference:
Sharpe(retA) - Sharpe(retB)CAGR(retA) - CAGR(retB)
If the gap is large, your backtest was leaning on an untradable assumption.
2) Open-to-close vs close-to-open decomposition
A powerful diagnostic is to split index returns into:
- Overnight:
Open[t]/Close[t-1]-1 - Intraday:
Close[t]/Open[t]-1
Many naive daily strategies accidentally harvest overnight returns in ways you can’t actually capture after costs (or that reverse once you fix timing).
3) Sensitivity to costs
Run c_bps across a range:
- 0 bps (fantasy)
- 1–3 bps per side (SPY-like)
- 5–10 bps per side (less liquid ETFs)
Report the break-even cost level.
4) Market impact proxy (optional but enlightening)
Even with daily data, you can add a crude “impact” term:
impact_bps = k * (turnover * vol_proxy)
where vol_proxy could be ATR/price. The point isn’t precision; it’s to see whether the strategy survives once costs scale with volatility.
5) Where the PnL is coming from
- Top 20 days contribution.
- Worst 20 days contribution.
- Does performance concentrate in a single crisis regime?
A microstructure mistake often shows up as: “strategy makes money most days, then gives it all back in a few gap days.”
What could break
- Using adjusted prices incorrectly ETFs have dividends/splits; if you use adjusted close for the signal but raw open for execution, you can create artificial returns. Either use fully adjusted OHLC (if you have it) or stay consistent.
- Assuming the open is a single, clean price The open print can be noisy; liquidity is different in the opening auction. If your strategy relies on perfect open fills, it may not scale, even in liquid names.
- Ignoring borrow/financing and constraints If you extend this lab to shorting (e.g., short on down days), you must model borrow costs and short constraints. Many “market neutral” daily strategies quietly assume free shorts.
- Survivorship / symbol selection On single names, survivorship bias + corporate actions can dwarf the signal. Start with an index proxy for the microstructure lesson.
- The cost model itself A constant bps cost is a simplification. Real costs depend on:
- volatility,
- spread,
- order type,
- and participation rate. If the strategy only works under a constant 0–1 bps assumption, it’s probably not real.
Close
The goal isn’t to prove that 1-day momentum is good or bad. It’s to build the reflex:
- every backtest is also an execution model,
- and changing the fill assumption is often the fastest way to detect fake alpha.
If your results are robust to timing (close vs next open) and to reasonable costs, you’ve earned the right to keep researching. If not, you’ve saved yourself months.