The Learning Library
Contents

Module 10 Companion — Intraday Trend & Momentum Playbook

Part III · Intraday Design, Alpha & Playbooks · Companion to Module 10 Prerequisites: M7, M8, M9, M22, M23


Overview

The core module built our unified replay contract: completed bars only, close-on-t signal to open-on-t+1 fill, full spread deducted, ambiguous bars resolved adversely, and mandatory same-session flattens. This companion provides three concrete, reproducible trading experiments for commodity ETPs and BTC/ETH derivatives:

Strategy Card Trend Architecture Target Venues & Clocks Core Empirical Question
S1 — Opening-Range Breakout Fixed OR window + ATR buffer breakout Commodity ETP RTH (09:30–16:00 ET) & BTC/ETH Perp (00:00–23:50 UTC) Does initial price discovery project directional expansion that overcomes the spread?
S2 — Donchian/ATR Breakout Dynamic 20-bar rolling extremes + ATR stops Same lanes, M5 primary (M15 sensitivity) Does a rolling channel breakout with volatility exits yield positive net expectancy?
S3 — EMA Pullback Reclaim Slow drift bias (50) + fast reclaim (20) Same lanes, M5 primary Does entering shallow pullbacks beat chasing fresh breakout extensions?

💡 The Institutional Locomotive Analogy: Think of intraday breakouts as boarding an institutional freight train. You board after the train has cleared the switch (clearing the buffer), not by standing on the tracks before the whistle blows. Pullbacks are scheduled station stops where the locomotive re-absorbs minor profit-taking before accelerating.

📌 Data Contract Convention: All strategies operate strictly on OHLCV + observed spread. No tick data, order-book depth, or partial fills are assumed. Signals generated at close[t] fill at open[t+1] with full observed spread deducted. Higher-timeframe bars (H1/H4) serve purely as causal context gates joined at their release timestamps. All trades flatten before session close.


How It Works

Three strategy families share a modular decision architecture and identical execution constraints:

  1. S1 (Opening Range): Calculates fixed high/low extremes during the opening 30 minutes (09:30–10:00 ET or 00:00–00:30 UTC). An M5 close beyond the range plus an ATR buffer triggers a directional entry during the morning eligibility window.
  2. S2 (Donchian/ATR): Identifies rolling 20-bar channel extremes on completed bars quantstrategy.io on Donchian channels. Channel width acts as an automatic volatility filter, blocking trades during dead consolidation regimes LuxAlgo on Donchian channels.
  3. S3 (EMA Pullback): Evaluates trend alignment on EMA(50). When price dips below EMA(20) and subsequently closes back above it with momentum, the engine arms a trend-resumption order.

All entries must clear pre-trade spread / ATR and cost / target hurdles before execution.

Figure: Playbook execution architecture. S1 serves as the benchmark control, S2 provides rolling sensitivity, and S3 evaluates pullback timing.

flowchart TD
    data["OHLCV + spread<br/>M5 + completed H1/H4/D1 releases"] --> s1["S1 Opening Range<br/>fixed 30-min OR -> M5 break"]
    data --> s2["S2 Donchian/ATR<br/>20-bar channel -> M5 break"]
    data --> s3["S3 EMA Pullback<br/>50/20 reclaim -> M5 resume"]
    s1 -->|"frozen ATR exit & gates"| ablate["Same-engine ablation<br/>trigger vs gated<br/>vs cost-stress vs paper"]
    s2 --> ablate
    s3 --> ablate
    ablate --> decision{"Net lift +<br/>opportunity floor +<br/>ambiguous share?"}
    decision -->|"pass"| carry["Carry to paper<br/>as one-lane playbook"]
    decision -->|"revise"| narrow["Revise: narrow window<br/>swap N or EMA pair<br/>or step M15"]
    decision -->|"reject"| reject["Reject family<br/>document & stop spend"]

    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 data data
    class s1,s2,s3,ablate,narrow process
    class decision decision
    class reject risk
    class carry ok

Strategy Rules

Every strategy card adheres to the standard 9-point research template. S1 details the full baseline implementation; S2 and S3 specify structural deltas.

S1 — Opening-Range Continuation (Full Implementation)

1. Market, Timeframe, and Data Assumptions

  • Instruments: GLD / PDBC (NYSE Arca ETPs) and BTC/ETH Linear Perpetuals (Approved CEXs).
  • Timeframe: M5 primary decision bars.
  • Clock: ETP: 09:30–16:00 ET (forced flatten at 15:58 ET); Crypto: 00:00–23:50 UTC (forced flatten at 23:50 UTC).
  • Data Basis: OHLCV + observed spread per bar. Long-only default for ETPs; two-sided for crypto perps.

2. Mechanics vs. Testable Hypothesis

Mechanics literature documents intraday volatility clustering TOS Indicators on ETF hourly volatility models and opening-range formation Volatility Box on opening range breakout.

  • Hypothesis: An M5 close breaking a 30-minute opening range by $> 0.10 \times \text{ATR}$, executed between 10:00 and 11:30 ET, generates net positive expectancy after deducting spreads and fees on purged walk-forward splits.

3. Required Technical Indicators

  • Opening Range: High and Low between 09:30 and 10:00 ET; frozen thereafter.
  • Volatility: ATR(14) on M5 bars Investopedia on ATR.
  • Cost Ratios: spread / ATR and (spread + fees) / target computed on completed bars.
  • Filter: M5 ADX(14) and rolling 20-day spread 75th percentile.

4. Causal HTF Context & Trigger

  • HTF Context: Last completed H1 bar close must sit above its EMA(20) for long trades.
  • Trigger: First M5 bar closing above OR_High + (0.10 * ATR) inside the 10:00–11:30 ET window.
  • Execution: Filled at open[t+1] + half observed spread. Max 1 trade per session.

5. Exits & Trade Management

  • Stop Loss: entry_price - 1.2 * ATR_M5[t].
  • Take Profit: entry_price + 1.0 * ATR_M5[t] or 0.75 * OR_width.
  • Time Stop: 16 bars (80 minutes).
  • Session Flatten: Unconditional exit at 15:58 ET (ETPs) or 23:50 UTC (Crypto).

6. Cost Filters & Execution Assumptions

  • Pre-Trade Gate: Stand aside if spread / ATR > 0.20 or cost / target > 0.12.
  • Path Resolution: If high and low hit stop and target on the same bar, assume adverse stop hit ohlcv.io on bar resolution.

7. Position Sizing & Risk Limits

  • Fixed-fractional sizing risking $0.50%$ of equity per trade based on the ATR stop distance M23.

8. Machine Learning Meta-Filter Extension

  • Role: Meta-filter (veto model) predicting trade success.
  • Features: OR width / ATR, morning gap / ATR, M5 ADX(14), spread / ATR, and session minute.
  • Model: Logistic Regression baseline vs. shallow XGBoost classifier.
  • Validation: Purged rolling walk-forward with 20-bar embargo.

9. Failure Modes & Rejection Criteria

  • Reject if net expectancy is negative after deducting observed spreads.
  • Reject if performance collapses under 90th-percentile spread stress.
  • Reject if ambiguous bars account for $>15%$ of total trades.

S2 — Donchian/ATR Channel Breakout (Delta Card)

1–4. Core Adjustments

  • Indicators: 20-bar Donchian High/Low computed on completed bars (.shift(1)).
  • Trigger: M5 close $> \text{Donchian_High} + (0.10 \times \text{ATR})$ with $\text{ADX}(14) \ge 16$.
  • Volatility Squeeze Filter: Stand down if $\text{Donchian Channel Width} < 0.60 \times \text{ATR}$.

5–9. Risk & Validation

  • Exits: Inherits 1.2x ATR stop, 1.0x ATR target, 16-bar time stop, and hard session flatten.
  • ML Role: Classify breakout continuation probability based on channel compression metrics.

S3 — EMA Pullback Continuation (Delta Card)

1–4. Core Adjustments

  • Trend Bias: M5 close > EMA(50) and H1 close > EMA(20).
  • Pullback Condition: Bar t-1 closes below EMA(20) while remaining above EMA(50).
  • Reclaim Trigger: Bar t closes back above $\text{EMA}(20) + (0.05 \times \text{ATR})$ with close[t] > close[t-1].

5–9. Risk & Validation

  • Exits: Stop at low_of_pullback - 0.5 * ATR or standard 1.2x ATR stop; 1.0x ATR target.
  • ML Role: Meta-filter evaluating pullback depth and volume absorption.

Building It In Python

The executable script below demonstrates the complete S1 Opening-Range Breakout pipeline in Polars:

import polars as pl
import numpy as np

def run_opening_range_pipeline(
    df: pl.DataFrame,
    or_end_time: str = "10:00:00",
    max_entry_time: str = "11:30:00",
    buffer_mult: float = 0.10,
    stop_mult: float = 1.20,
    target_mult: float = 1.00,
) -> pl.DataFrame:
    """Execute causal S1 Opening Range Breakout logic with strict cost gating."""

    # 1. Compute Welles Wilder ATR(14)
    prev_close = pl.col("close").shift(1)
    tr = pl.max_horizontal([
        pl.col("high") - pl.col("low"),
        (pl.col("high") - prev_close).abs(),
        (pl.col("low") - prev_close).abs(),
    ])
    df = df.with_columns(tr.ewm_mean(span=14, adjust=False).alias("atr_14"))

    # 2. Extract and forward-fill Opening Range per session
    or_bars = df.filter(pl.col("time_str") <= or_end_time)
    or_levels = (
        or_bars.group_by("session_id")
        .agg([
            pl.col("high").max().alias("or_high"),
            pl.col("low").min().alias("or_low"),
        ])
        .with_columns((pl.col("or_high") - pl.col("or_low")).alias("or_width"))
    )
    df = df.join(or_levels, on="session_id", how="left")

    # 3. Apply Pre-Trade Cost & Volatility Gates
    df = df.with_columns([
        (pl.col("spread") / pl.col("atr_14")).alias("spread_to_atr"),
        (pl.col("spread") / (pl.col("atr_14") * target_mult)).alias("cost_to_target"),
        (pl.col("or_high") + pl.col("atr_14") * buffer_mult).alias("long_trigger_level"),
    ]).with_columns(
        (
            (pl.col("spread_to_atr") <= 0.20)
            & (pl.col("cost_to_target") <= 0.12)
            & (pl.col("time_str") > or_end_time)
            & (pl.col("time_str") <= max_entry_time)
        ).alias("gate_pass")
    )

    # 4. Generate Causal Breakout Signals
    df = df.with_columns(
        (
            pl.col("gate_pass")
            & (pl.col("close") > pl.col("long_trigger_level"))
            & (pl.col("close").shift(1) <= pl.col("long_trigger_level").shift(1))
        ).alias("signal_long")
    )

    # 5. Model Next-Open Execution and Conservative Exits
    df = df.with_columns([
        (pl.col("open").shift(-1) + 0.5 * pl.col("spread").shift(-1)).alias("entry_price"),
        (pl.col("open").shift(-1) - pl.col("atr_14") * stop_mult).alias("stop_price"),
        (pl.col("open").shift(-1) + pl.col("atr_14") * target_mult).alias("target_price"),
    ])

    return df

What This Means For Your P&L: Strategy Comparison Table

Metric S1 Opening Range (30m) S2 Donchian Breakout (20) S3 EMA Pullback Reclaim
Trade Frequency 1 trade / session max 1–3 trades / session 2–4 trades / session
Spread Sensitivity Low (wide initial target) Moderate High (tight entries demand tight spreads)
Whipsaw Rate Low (morning momentum) Moderate in consolidation High if entered without buffer
Primary Failure Mode False breakout into midday lull Range compression churn Counter-trend knives in strong selloffs
Recommended Action Deploy as primary morning engine Deploy during high-ADX regimes Deploy strictly when H1 trend is confirmed

Testing It Honestly

Honest evaluation requires strict adherence to our causal validation protocol:

  1. Next-Open Fill Execution: Signal at close[t] $\rightarrow$ fill at open[t+1]. Never evaluate entries at close[t] Saral on next-bar-open execution.
  2. Conservative Intrabar Exits: Bars touching both stop and target are booked as losses (ambiguous_stop_first) ohlcv.io on bar resolution.
  3. Mandatory Purged Embargoes: Apply a 20-bar embargo between rolling folds to eliminate feature autocorrelation leakage.
  4. Stress Testing: Re-run all strategies under 90th-percentile spread curves.
def evaluate_stress_scenarios(base_pnl: np.ndarray, spreads: np.ndarray) -> dict:
    """Compare baseline net returns against p90 spread stress."""
    p90_spread = np.percentile(spreads, 90)
    median_spread = np.median(spreads)
    spread_delta = p90_spread - median_spread

    stressed_pnl = base_pnl - spread_delta
    return {
        "base_expectancy": float(np.mean(base_pnl)),
        "stressed_expectancy": float(np.mean(stressed_pnl)),
        "stress_survival": bool(np.mean(stressed_pnl) > 0),
    }

Variants & Extensions

  • OR Duration Sweep (15m vs. 30m vs. 60m): Shorter windows capture early volatility but incur higher whipsaw rates.
  • Keltner / Bollinger Squeeze Confirmation: Require channel compression before Donchian expansion Volatility Box on Keltner vs Bollinger.
  • Dynamic Half-Kelly Scaling: Scale position size by estimated win rate and reward-to-risk ratio M23.

Hands-On Project

Deliverable: Build docs/research/m10_trend_playbook_lab.md comparing S1, S2, and S3 across GLD and BTC perps.

Implementation Checklist

  • Implement S1, S2, and S3 in Polars using strict next-open execution.
  • Enforce pre-trade cost gates (spread/ATR <= 0.20, cost/target <= 0.12).
  • Run 5-fold purged walk-forward cross-validation.
  • Evaluate performance under baseline and p90 spread stress.
  • Produce parameter sensitivity heatmaps for OR duration and Donchian lookback.

Key Takeaways

  • The Spread is Your Primary Hurdle: Intraday trend models succeed or fail based on execution friction, not technical indicator tuning.
  • Enforce Honest Timing: Signals must execute at open[t+1] with full round-trip spreads deducted.
  • Conservative Ambiguity is Mandatory: Never assume favorable intrabar execution on ambiguous bars.
  • Flatten Hard Daily: Eliminate overnight gap and unmodeled funding risks by flattening before session boundaries.

References

  1. SEC — Investor Bulletin: Exchange-Traded Fundshttps://www.sec.gov/investor/alerts/etfs.pdfMechanics. ETF market pricing, NAV drift, creation/redemption mechanisms.
  2. SSGA — SPDR Gold Shares (GLD)https://www.ssga.com/us/en/intermediary/etfs/spdr-gold-shares-gldMechanics. Physical gold trust structure and daily holdings.
  3. Pomegra — Commodity ETF Liquidity & Rollhttps://pomegra.io/learn/library/track-d-other-assets/commodities/chapter-09-commodity-etfs-and-etns/commodity-etf-liquidityMechanics. Futures pool roll mechanics and spread layers.
  4. Coinbase — Funding Rate Mechanismhttps://help.coinbase.com/en/derivatives/perpetual-style-futures/funding-rateMechanics. Perpetual funding schedules and spot tethering.
  5. Coinbase Learn — Understanding Funding Rateshttps://www.coinbase.com/learn/perpetual-futures/understanding-funding-rates-in-perpetual-futuresMechanics. Funding rates as an explicit carry friction.
  6. TOS Indicators — Hourly Volatility Models for ETFshttps://tosindicators.com/research/etf-volatility-modelsMechanics / Transfer. U-shaped intraday volatility curves.
  7. ContentWave — GLD Liquidity, Costs & Risks (2026)https://contentwave.net/article/review-spdr-gold-shares-gld-liquidity-costs-and-risks-2026Mechanics. Intraday ETP spread behavior and total ownership costs.
  8. Investopedia — Average True Range (ATR)https://www.investopedia.com/terms/a/atr.aspMechanics. Volatility yardstick for stops and cost gates.
  9. Volatility Box — Opening Range Breakouthttps://volatilitybox.com/research/opening-range-volatility-breakout/Transfer Evidence. Opening range construction and ATR filtering.
  10. QuantConnect — ORB for Stocks in Playhttps://www.quantconnect.com/research/18444/opening-range-breakout-for-stocks-in-play/Transfer Evidence. Relative-volume and ATR-filtered breakouts.
  11. MQL5 — Decoding ORB Intraday Strategieshttps://www.mql5.com/en/articles/17745Transfer Evidence. Session VWAP alignment and breakout mechanics.
  12. GitHub — Trading-ORB Engine (sam-bateman)https://github.com/sam-bateman/trading-orbMechanics Reference. Event-driven breakout execution engine.
  13. quantstrategy.io — Donchian Channel Strategyhttps://quantstrategy.io/blog/donchian-channel-strategy-identify-breakouts-and-trend-reversals/Transfer Evidence. Channel breakout design principles.
  14. LuxAlgo — Donchian Channels Breakouthttps://www.luxalgo.com/blog/donchian-channels-breakout-and-trend-following-strategy/Mechanics. Channel band calculation and volatility metrics.
  15. Deepvue — Donchian Channels Guidehttps://deepvue.com/indicators/donchian-channels-the-breakout-traders/Transfer Evidence. Channel trading and trailing stop conventions.
  16. Volatility Box — Keltner Channels vs. Bollinger Bandshttps://volatilitybox.com/research/keltner-channels-vs-bollinger-bands/Transfer Evidence. Volatility envelopes and squeeze detection.
  17. Volatility Box — Volatility-Adjusted Stop Losseshttps://volatilitybox.com/research/volatility-adjusted-stop-losses/Transfer Evidence. ATR-scaled risk controls across timeframes.
  18. ColonelBy — Breakout Momentum for Crypto Futureshttps://www.colonelby.com/breakout-momentum-strategy-crypto-futures-intraday/Transfer Evidence. Intraday crypto momentum rules.
  19. ohlcv.io — The Bar Resolution Problem (Pitfall 04)https://ohlcv.io/posts/backtesting-pitfalls/04-bar-resolution/Mechanics. Intrabar ambiguity and adverse stop modeling.
  20. Saral Money — Next Bar Open Execution Timinghttps://saral.money/blog/next-bar-open-execution-timing/Mechanics. Eliminating same-close look-ahead bias in backtests.