Module 11 — Intraday Mean Reversion
Part III · Intraday Design, Alpha & Playbooks · Priority ⭐ Recommended Status: Draft v0.2 · Scope: commodity ETFs/ETPs + BTC/ETH spot & linear perps · Horizon: M1/M5 (M15 where appropriate), same-session flat Prerequisites: M5, M6, M7, M8, M9, M22, M23
Overview
You start this module with the venue and the price you can actually trade, not with an indicator.
A commodity ETP share trades during U.S. regular hours at a market price that drifts from its published NAV. Creation and redemption occur exclusively in large blocks through authorized participants SEC ETF bulletin. Physical gold trusts like GLD hold allocated bullion and publish daily holdings SPDR Gold Shares (GLD). In contrast, futures-based commodity pools roll contracts across sloped curves: contango creates structural drag, whereas backwardation yields positive roll returns Pomegra on commodity ETF liquidity and roll.
On the crypto lane, BTC and ETH linear perpetuals never expire. Instead, they exchange periodic funding payments between longs and shorts to tether perpetual prices to spot Coinbase perpetual funding and Coinbase on funding rates. Commodity ETP spreads widen predictably at the open, midday lull, and close TOS Indicators on ETF hourly volatility models. This spread is an unavoidable friction that every trade must pay ContentWave on GLD liquidity 2026.
The preregistered hypothesis for this module is specific: short-lived price deviations from an intraday anchor—Bollinger/RSI extremes, session-VWAP deviations, or failed sweeps of prior-day extremes—snap back toward fair value often enough to carry net expectancy after deducting spreads, fees, and conservative bar execution.
The economic intuition is grounded in liquidity provision:
- Impatient order flow pays for immediacy. Stretched price moves represent market participants crossing the spread aggressively to dump or acquire inventory.
- Institutional VWAP benchmark magnets. Large institutional algorithms execute relative to session VWAP. As price stretches far from volume-weighted averages, institutional passive flow pulls price back AlgoLab on VWAP mean reversion.
- Liquidity sweeps at prominent levels. Stop-loss clusters resting above yesterday’s high or below yesterday’s low are triggered, exhausting breakout momentum and allowing market makers to fade the failed expansion DayTradingToolkit on prior-day high/low.
Who sits on the other side of your fill?
- The breakout momentum trader chasing a band break at the top/bottom of the cycle.
- The institutional hedger forced to liquidate inventory regardless of local price concessions.
- The breakout buyer whose stop sits just beyond yesterday’s high.
Evidence status: hypothesis, mechanics-supported. Wrapper structures, session shapes, VWAP calculations, and liquidity sweeps are well-documented facts. However, whether any intraday reversion rule captures net alpha after fees remains an empirical question resolved only by disciplined replay.
The primary failure mode is fading a strong trend that expands further, paying the spread toll twice. Secondary pitfalls include VWAP reversion on steep trend days (where VWAP acts as a trending ramp rather than a magnet) and ambiguous-bar mirages ohlcv.io on bar resolution, exacerbated by assuming fills at the signal close Saral on next-bar-open execution.
💡 The Rubber Band & Clearance Rack: Think of intraday mean reversion as an extended rubber band or a retail clearance rack. Extreme price extensions (Bollinger/VWAP stretches) represent heavily stretched elastic. Your edge is buying the clearance item only after the markdown stops and the first buyer steps in (close-back-inside), riding the snapback to wholesale fair value (VWAP/SMA).
📌 Data Contract Convention: This module operates strictly on OHLCV + observed spread. We assume no order-book depth, tick feeds, or partial fills. Signals form at the close of bar
tand execute at the open of bart+1. Observed spreads are deducted on both entry and exit. Higher-timeframe context (H1/D1) serves purely as causal gates joined at formal release timestamps. All trades flatten before session close.
How It Works
Three reversion families share a unified execution engine and session clock. They differ only in how they identify extreme price dislocations:
- Bollinger/RSI Fade: Combines a 2-standard-deviation price extreme with RSI(2) momentum exhaustion ($< 10$ or $> 90$) TradingCompendium on RSI + Bollinger 2026. The entry triggers only when price closes back inside the band, confirming exhaustion TradingPlan on Bollinger band mean reversion.
- Session-VWAP Reversion: Anchors to the day’s volume-weighted average price AlgoLab on VWAP mean reversion. Trades trigger on a close back inside the $2\sigma$ deviation envelope, provided VWAP slope remains flat to moderate MomentumIQ on VWAP mean reversion.
- Prior-Day High/Low Sweep: Identifies liquidity pierces beyond yesterday’s high or low (PDH/PDL) DayTradingToolkit on prior-day high/low. When price pierces the level but fails to hold, closing back inside the prior session’s range, the engine fades the false breakout NetPicks on previous day high/low.
Figure: The M11 mean-reversion engine. Every setup requires a confirmed 2-step trigger (alert then close-back-inside) plus pre-trade cost and regime clearance.
flowchart TD
bars["OHLCV + spread<br/>M1/M5/M15 completed bars"] --> clock["Session clock<br/>ETF RTH vs UTC synthetic"]
bars --> calc["Technical calculations<br/>Bollinger RSI z ATR<br/>VWAP + bands prior levels"]
clock --> htf{"Completed H1/H4/D1<br/>bias & release check"}
calc --> htf
htf -->|"not yet released<br/>or regime fails"| block["Stand aside<br/>log & count"]
htf -->|"released & bias ok"| gates{"Spread-to-ATR<br/>and cost-to-target<br/>below ceiling?"}
gates -->|"no"| block
gates -->|"yes"| families["Reversion anchors<br/>Bollinger/RSI<br/>VWAP dev prior level"]
families --> extreme{"Close beyond<br/>anchor + buffer?"}
extreme -->|"no"| block
extreme -->|"yes"| trigger{"Close BACK INSIDE<br/>or sweep close-back?"}
trigger -->|"no: still extended"| block
trigger -->|"yes"| exec["Arm for next bar<br/>signal on t -> open t+1<br/>spread deducted"]
exec --> ambig{"Bar touches both<br/>stop and target?"}
ambig -->|"yes"| adverse["Conservative: stop<br/>or exclude & count"]
ambig -->|"no"| manage["Manage to target<br/>stop time-stop"]
adverse --> flatten{"Forced flatten<br/>at session boundary?"}
manage --> flatten
flatten -->|"now"| flat["Flatten same session<br/>gap at next open"]
flatten -->|"not yet"| hold["Hold to next bar<br/>time stop ticking"]
classDef data fill:#e8f0fe,stroke:#4a86e8
classDef process fill:#f3f3f3,stroke:#888
classDef decision fill:#fff4d6,stroke:#d6a300
classDef risk fill:#fde8e8,stroke:#c0392b
classDef ok fill:#e6f4ea,stroke:#2e7d32
class bars data
class calc,exec,hold,flat process
class htf,gates,extreme,trigger,ambig,flatten decision
class block,adverse risk
class families okStep-by-Step Engine Walkthrough
- Session Initialization: Define the active trading window (09:30–16:00 ET for ETPs, 00:00–23:50 UTC for crypto).
- Anchor Computation: Calculate Bollinger Bands, session VWAP, and prior-day extremes from completed bars.
- Regime & Cost Triage: Verify ranging conditions ($\text{ADX} < 20 - 25$, moderate VWAP slope) and pass
spread / ATRcost gates. - Dislocation Alert: Detect a bar
t_alertclosing beyond the outer band or level. - Close-Back Trigger: Confirm bar
t_triggercloses back inside the anchor threshold. - Next-Open Execution: Enter at
open[t_trigger+1], immediately deducting observed half-spreads. - Conservative Trade Management: Manage to target (mean/VWAP) or volatility stop, enforcing adverse stop-first resolution on ambiguous bars.
- Hard Session Flatten: Liquidate all open positions at the designated session closing bar.
Strategy Rules
All strategies inherit our strict intraday execution contract:
Replay Execution Contract
| Area | Contract Specification | Rationale for Intraday Trading |
|---|---|---|
| Price Basis & Spread | Mid vs. last defined in project card; spread column units explicitly verified | Prevents flattering backtest returns with fractional basis errors |
| Session Clock | Fixed RTH window for ETPs; synthetic UTC window for crypto with hard flatten | Eliminates overnight carry risk and uncontrolled funding decay |
| Execution Timing | Signal at close of bar t $\rightarrow$ fill at open of bar t+1 |
Completely eliminates same-bar look-ahead bias Saral on next-bar-open execution |
| Bar Ambiguity | Stop-first resolution if high and low hit both exit thresholds | Guarantees conservative, honest backtest accounting ohlcv.io on bar resolution |
| Cost Hurdles | Mandatory spread / ATR and cost / target pre-trade filters |
Rejects trades where spread friction exceeds statistical edge |
| HTF Context | As-of point-in-time joins using released bars only | Prevents look-ahead leakage from developing macro bars |
| Position Sizing | Volatility scaling based on ATR stop distance | Equalizes dollar risk across disparate asset prices M23 |
Pre-Trade Cost & Regime Gates
| Gate Metric | Mathematical Definition | Default Ceiling | Action on Breach |
|---|---|---|---|
| Spread-to-ATR | $\text{Spread}t / \text{ATR}{14, t}$ | $\le 0.15 - 0.20$ | Stand aside: bar volatility is too small to pay the spread |
| Cost-to-Target | $(\text{Spread}_t + \text{Fees}) / \text{Target Distance}$ | $\le 0.10 - 0.12$ | Stand aside: spread consumes too much expected profit |
| Trend / Regime | $\text{ADX}_{14, t}$ on M5 or completed H1 | $\le 20 - 25$ | Stand aside: market is trending strongly; reversion fails |
| VWAP Slope | $ | \Delta \text{VWAP}_{20} | / \text{ATR}_{\text{hour}}$ |
| Half-Life Alignment | Estimated Ornstein-Uhlenbeck half-life $\tau$ | $T_{\text{max}} \approx 2\tau$ | Stand aside if half-life is infinite or unstable |
Core Reversion Families
Family A: Bollinger/RSI Confluence Fade
- Band Parameters: Bollinger(20, 2) on completed M5 closes; Wilder RSI(2).
- Long Alert:
close[t_alert] < BB_Lower[t_alert]ANDRSI2[t_alert] <= 10. - Long Trigger:
close[t_trigger] > BB_Lower[t_trigger]ANDRSI2[t_trigger] > 5. - Short Trigger: Mirror logic at upper band (
RSI2 >= 90). - Stop Loss:
Low(t_alert) - 1.0 * ATR_M5. - Take Profit: Middle band (SMA 20) or fixed 1.5:1 RR.
- Time Stop & Flatten: Maximum 12 bars (60 minutes); hard session flatten.
Family B: Session-VWAP Deviation Reversion
- VWAP Anchor: Cumulative intraday volume-weighted average price reset at session open AlgoLab on VWAP mean reversion.
- Deviation Bands: $\text{VWAP} \pm 2\sigma$ calculated from rolling 20-bar residuals.
- Long Trigger:
close[t_trigger] > VWAP_Lower2following a pierce below $2\sigma$, subject to flat VWAP slope. - Take Profit: Session VWAP baseline or $1\sigma$ band.
- Risk Controls: Stop placed at $3\sigma$ extreme or 1.0x ATR beyond breach low.
Family C: Prior-Day High/Low Sweep Rejection
- Reference Levels: Prior Regular Trading Hours High (PDH) and Low (PDL) DayTradingToolkit on prior-day high/low.
- Sweep Fade (Short): M5 bar pierces above $\text{PDH} + (0.10 \times \text{ATR})$ but closes back below PDH on bar
t. - Sweep Fade (Long): M5 bar pierces below $\text{PDL} - (0.10 \times \text{ATR})$ but closes back above PDL on bar
t. - Take Profit: Session VWAP or prior-day midpoint $(\text{PDH} + \text{PDL}) / 2$.
- Stop Loss: $0.50 \times \text{ATR}$ beyond the sweep extreme wick.
Building It In Python
Below is the complete, modular Polars and NumPy implementation of the mean-reversion engine.
import polars as pl
import numpy as np
def calculate_bollinger_rsi(
frame: pl.DataFrame, bb_window: int = 20, bb_dev: float = 2.0
) -> pl.DataFrame:
"""Calculate causal Bollinger Bands and Wilder RSI(2) on completed closes."""
close = pl.col("close")
sma = close.rolling_mean(bb_window)
std = close.rolling_std(bb_window)
# Wilder RSI(2) calculation
delta = close.diff()
gain = delta.clip(lower_bound=0).ewm_mean(alpha=0.5, adjust=False)
loss = (-delta.clip(upper_bound=0)).ewm_mean(alpha=0.5, adjust=False)
rsi2 = 100.0 - (100.0 / (1.0 + (gain / (loss + 1e-9))))
return frame.with_columns([
sma.alias("bb_mid"),
(sma + bb_dev * std).alias("bb_upper"),
(sma - bb_dev * std).alias("bb_lower"),
rsi2.alias("rsi_2"),
])
def calculate_session_vwap(frame: pl.DataFrame, session_col: str = "session_id") -> pl.DataFrame:
"""Calculate cumulative session VWAP and rolling deviation bands."""
pv = pl.col("close") * pl.col("volume")
cum_pv = pv.cum_sum().over(session_col)
cum_v = pl.col("volume").cum_sum().over(session_col)
vwap = cum_pv / cum_v
resid = pl.col("close") - vwap
sigma = resid.rolling_std(20)
return frame.with_columns([
vwap.alias("vwap"),
(vwap + 2.0 * sigma).alias("vwap_upper2"),
(vwap - 2.0 * sigma).alias("vwap_lower2"),
])
def estimate_half_life(series: np.ndarray) -> float:
"""Estimate mean-reversion half-life via Ornstein-Uhlenbeck OLS regression."""
lagged = series[:-1]
delta = np.diff(series)
phi = np.linalg.lstsq(lagged[:, None], delta[:, None], rcond=None)[0].item()
if phi >= 0:
return float("inf") # Non-reverting series
return float(-np.log(2) / np.log(1 + phi))
def apply_reversion_cost_gates(
frame: pl.DataFrame,
atr_col: str = "atr_14",
target_mult: float = 1.0,
max_spread_to_atr: float = 0.20,
max_cost_to_target: float = 0.12,
) -> pl.DataFrame:
"""Filter bars where spread friction destroys mean-reversion expectancy."""
target_dist = pl.col(atr_col) * target_mult
spread_to_atr = pl.col("spread") / pl.col(atr_col)
cost_to_target = pl.col("spread") / target_dist
pass_gate = (
(spread_to_atr <= max_spread_to_atr)
& (cost_to_target <= max_cost_to_target)
)
return frame.with_columns([
spread_to_atr.alias("spread_to_atr"),
cost_to_target.alias("cost_to_target"),
pass_gate.alias("cost_gate_pass"),
])
What This Means For Your P&L: Worked Numeric Example
Let us evaluate a Bollinger/RSI fade setup across liquid and illiquid trading hours:
- Setup: Asset Price = $100.00, M5 ATR = $0.25, Target (to Mid-Band) = $0.30, Stop (beyond low) = $0.30.
- Gate Ceilings: Max
Spread / ATR= 0.20, MaxCost / Target= 0.12.
| Market Condition | Observed Spread | Spread / ATR | Cost / Target | Gate Verdict | Realized P&L Impact |
|---|---|---|---|---|---|
| RTH Mid-Morning (GLD) | $0.020 | $8.0%$ | $6.7%$ | ✅ ACCEPTED | Tight spread allows clean 1.5:1 reward-to-risk capture |
| RTH Opening Whip (GLD) | $0.060 | $24.0%$ | $20.0%$ | ❌ REJECTED | Avoids wide spreads that consume $>20%$ of mean-reversion target |
| BTC Perp (Consolidation) | $2.50 (on $50 ATR) | $5.0%$ | $4.2%$ | ✅ ACCEPTED | Optimal low-friction snapback execution |
Testing It Honestly
Honest testing requires adhering to strict simulation standards:
- Causal Data Pipeline: Compute all indicators strictly on closed bars. Join H1/D1 data using point-in-time release timestamps (
release_time = bar_close + lag). - Next-Open Execution: Fill signals at the open of bar
t+1. Never execute at the signal bar’s close Saral on next-bar-open execution. - Explicit Spread Deduction: Deduct the full observed bid-ask spread across the round trip.
- Conservative Ambiguity Resolution: If a bar touches both stop and target, record an adverse stop hit or exclude the trade and document the occurrence rate ohlcv.io on bar resolution.
- Purged Walk-Forward Splits: Validate strategies across chronologically rolling folds with embargo buffers to eliminate train-test leakage.
Variants & Extensions
| Variant Configuration | Implementation Adjustment | Primary Benefit | Operational Trade-off |
|---|---|---|---|
| Keltner vs. Bollinger | ATR envelope vs. Standard Deviation | Better accommodates fat-tailed gap moves | Slower to respond to sudden intraday volatility |
| Rolling Z-Score Anchor | Standardized $(P - \mu) / \sigma$ | Clean dimensionless metric across asset classes | Requires stationary rolling window estimation |
| Session-Mid Anchor | $(H_{\text{session}} + L_{\text{session}}) / 2$ | Computational simplicity; no volume data needed | Ignores volume-weighted institutional fair value |
| Half-Life Scaled Exits | Set time stop at $2 \times \text{Half-Life}$ | Adapts holding period to asset’s empirical speed | Unstable if underlying regime shifts to trending |
Hands-On Project
Deliverable: Build docs/research/m11_mean_reversion_lab.md and implement the complete reversion lab in notebooks/m11_mean_reversion.ipynb.
Project Card Specification
- Identity: GLD (ETP lane) and BTC-USDT Linear Perp (Crypto lane). Explicitly define price basis (mid/last) and spread column units.
- Horizon & Clock: M5 decision bars. ETP: 09:30–16:00 ET (flatten at 15:58 ET). Crypto: 00:00–23:50 UTC (flatten at 23:50 UTC).
- Execution Engine: Signal on
close[t], enter onopen[t+1]. Full observed spread deducted. Conservative stop-first ambiguous resolution. - Cost Ceilings: Spread-to-ATR $\le 0.20$; Cost-to-Target $\le 0.12$.
- Validation: 5-fold purged rolling walk-forward with 20-bar embargo.
Preregistered Rejection Triggers
- Strategy fails to produce positive net expectancy after deducting observed spreads.
- Performance collapses under 90th-percentile spread stress.
- Ambiguous bars account for $>10%$ of total trades and flip net P&L.
- Half-life analysis indicates series is non-stationary ($\phi \ge 0$).
Key Takeaways
- Reversion is Liquidity Provision: You are paid for absorbing impatient order flow, but you must pay the spread toll on both legs.
- Confluence and Triggers are Mandatory: Never fade an extreme directly. Wait for price to close back inside the anchor threshold.
- Slope Filters Save Accounts: Fading VWAP or bands during steep trend regimes results in catastrophic losses.
- Enforce Conservative Ambiguity: Always assume adverse stop execution when a single bar spans both stop and target levels.
- Flatten Hard Daily: Eliminate overnight carry risk by flattening all positions before the session close.
References
- U.S. Securities and Exchange Commission — Exchange-Traded Funds (ETFs) bulletin — https://www.sec.gov/investor/alerts/etfs.pdf — Mechanics. ETF market pricing, NAV drift, creation/redemption mechanisms.
- SSGA — SPDR Gold Shares (GLD) Prospectus — https://www.ssga.com/us/en/intermediary/etfs/spdr-gold-shares-gld — Mechanics. Physical gold trust structure and daily holdings.
- Pomegra Learn — Commodity ETF & ETN Liquidity — https://pomegra.io/learn/library/track-d-other-assets/commodities/chapter-09-commodity-etfs-and-etns/commodity-etf-liquidity — Mechanics. Futures pool roll mechanics, contango drag, and spread behaviors.
- Coinbase — US Perpetual-Style Futures Funding Rate Mechanism — https://help.coinbase.com/en/derivatives/perpetual-style-futures/funding-rate — Mechanics. Perpetual funding schedules and spot tethering.
- Coinbase Learn — Understanding Funding Rates in Perpetual Futures — https://www.coinbase.com/learn/perpetual-futures/understanding-funding-rates-in-perpetual-futures — Mechanics. Funding rates as an explicit intraday carry cost.
- TOS Indicators — Hourly Volatility Models for ETFs — https://tosindicators.com/research/etf-volatility-models — Mechanics / Transfer. U-shaped intraday volatility curves.
- ContentWave — SPDR Gold Shares (GLD) Liquidity & Costs (2026) — https://contentwave.net/article/review-spdr-gold-shares-gld-liquidity-costs-and-risks-2026 — Mechanics. Intraday ETP spread behavior and total ownership costs.
- Investopedia — Average True Range (ATR) — https://www.investopedia.com/terms/a/atr.asp — Mechanics. Standard true range and volatility calculations.
- Liberated Stock Trader — 5 Mean Reversion Trading Strategies: 120K Trades Tested — https://www.liberatedstocktrader.com/mean-reversion-trading-strategy/ — Transfer Evidence. Comprehensive multi-indicator mean-reversion testing.
- TradingCompendium — Mean Reversion: RSI + Bollinger (2026 Guide) — https://tradingcompendium.com/en/trading-strategies/mean-reversion-strategy-rsi-bollinger — Transfer Evidence. Bollinger band and RSI(2) confluence mechanics.
- TradingPlan — Bollinger Band Mean Reversion Strategy — https://www.tradingplan.io/strategies/bollinger-band-mean-reversion/ — Transfer Evidence. Close-back-inside triggers and ADX ranging filters.
- LearnCompareTrade — Intraday Mean Reversion — https://learncomparetrade.com/resources/trading-strategies/day-trading/intraday-mean-reversion/ — Transfer Evidence. Intraday VWAP and Bollinger reversion principles.
- CryptoProfitCalc — Mean Reversion Crypto Strategy Guide — https://cryptoprofitcalc.com/mean-reversion-crypto-strategy-the-complete-guide-indicators-entries-risk-backtesting/ — Transfer Evidence. Crypto intraday reversion risk controls.
- Xerogravity — Crypto Mean Reversion Strategy Guide — https://xerogravity.com/blog/crypto-mean-reversion-strategy — Transfer Evidence. Fee sensitivity and regime filter design.
- AlgoLab HK — VWAP Mean Reversion Trading Strategy Guide — https://algolabhk.com/en/blogs/vwap-mean-reversion-trading — Transfer Evidence. Volume-weighted average price band calculations.
- VolatilityBox — VWAP Mean Reversion Strategies — https://volatilitybox.com/docs/vwap-mean-reversion-strategies/ — Transfer Evidence. Session-anchored deviation bands.
- MomentumIQ — VWAP Mean Reversion (NSE) — https://www.momentumiq.in/strategies/vwap-mean-reversion — Transfer Evidence. VWAP slope filtering and risk controls.
- DayTradingToolkit — Prior-Day High and Low: Trading the Simplest Level — https://daytradingtoolkit.com/strategies/prior-day-high-low-breakout-strategy — Transfer Evidence. Intraday liquidity sweeps at prior-session extremes.
- NetPicks — Previous Day High/Low Strategy — https://www.netpicks.com/previous-day-high-low-strategy/ — Transfer Evidence. Sweep-versus-break confirmation rules.
- TOS Indicators — How to Identify Intraday Reversals — https://tosindicators.com/research/intraday-reversals — Transfer Evidence. Intraday reversal confluence models.
- ohlcv.io — The Bar Resolution Problem (Pitfall 04) — https://ohlcv.io/posts/backtesting-pitfalls/04-bar-resolution/ — Mechanics. Intrabar path ambiguity and adverse stop-first modeling.
- Saral Money — Execution Timing: Fill at Next Bar’s Open — https://saral.money/blog/next-bar-open-execution-timing/ — Mechanics. Eliminating same-close look-ahead bias in backtests.
- Next in sequence: Module 12 — Intraday Relative Value
- Strategy Playbook: Module 11 Strategies