Module 9 Companion — Causal Multi-Timeframe Context for Intraday Entries
Part III · Intraday Design, Alpha & Playbooks · Companion to Module 9 Prerequisites: M9, M8, M5, M22, M23
Overview
The core module established the causal multi-timeframe framework: higher-timeframe features release only after their source bars close, forward-filling never leaks forming data, all positions force flatten before session close, and every slower layer must beat an identical ungated baseline on purged out-of-sample net return.
This companion stocks the lab with three structured experiments designed to answer one central question: Does adding higher-timeframe context improve net session profitability after accounting for reduced trade frequency and transaction friction?
None of these experiments claims to prove alpha. Each is a preregistered ablation audit with explicit cost ceilings, metrics, and revise/reject criteria.
| Experiment | Core Question | Target Lane & Clock | Primary Slower Context |
|---|---|---|---|
| E1 — Completed H1 Trend Gate | Does aligning M5 pullback entries with completed H1 20 EMA trend improve net session P&L? | Commodity ETP (RTH) + Crypto perp (UTC synthetic) | Completed H1 20 EMA slope + H1 ADX(14) ≥ 18 |
| E2 — Prior-Session Level Map | Does scoring breakouts against prior-day levels (PDH/PDL) outperform raw intraday channel breakouts? | Same two lanes on M5 | Prior Day High/Low (PDH/PDL) + Prior H4 levels with buffer |
| E3 — Volatility Gate & Cost Routing | Does filtering trades by H1 ATR percentile (20%–85%) protect capital from quiet chop and explosive stops? | Same two lanes on winning timeframe | Completed H1 ATR percentile band (20%–85%) + Spread-to-ATR ceiling |
💡 The Division of Labor Analogy: A gate is a bouncer, not an athlete. It cannot create winning moves; it can only refuse trades that enter unfavorable market regimes. The true test of a gate is not win rate—it is net return per eligible session after accounting for every missed opportunity.
📌 Data Contract: All experiments operate strictly on OHLCV + spread data. Signals evaluate on the close of bar
tand fill at the open of bart+1. Spreads are deducted on entry and exit. Ambiguous same-bar exits resolve stop-first. All positions force flatten before session close. Completed H1/H4/D1 bars serve as causal context only.
How It Works
The three experiments follow a coordinated evaluation pipeline.
E1 — The Trend Gate Ablation
We compute 20 EMA slope and ADX(14) on completed H1 bars. After releasing features strictly at the H1 bar close, we gate M5 pullback entries: longs are permitted only when H1 trend is bullish (EMA slope > 0) and trend strength is confirmed (ADX ≥ 18). We ablate the gated strategy directly against an ungated M5 baseline under identical execution rules.
E2 — The Level Map Ablation
We construct Prior Day High (PDH) and Low (PDL) from completed daily bars, and prior H4 extremes from completed H4 bars. M5 price action is scored as an objective structural event: a breakout occurs when price closes beyond PDH + (0.10 × ATR). We test whether conditioning on prior-session levels produces superior risk-adjusted returns compared to unanchored M5 channel breaks.
E3 — The Volatility Gate & Routing Audit
We rank completed H1 ATR against its trailing 100-bar history. Entries are permitted only when ATR percentile sits within the tradable 20% to 85% band. We evaluate whether this filter successfully eliminates low-wage quiet chop and high-risk volatility expansions.
Figure: The Module 9 experimental workflow. Slower context feeds into gated ablations to determine whether higher-timeframe features earn their complexity.
flowchart TD
data["OHLCV + spread<br/>M5 + completed H1/H4/D1 releases"] --> e1["E1 Trend Gate<br/>H1 EMA/ADX/Donchian -> M5"]
data --> e2["E2 Level Map<br/>PDH/PDL + prior H4 -> M5 events"]
data --> e3["E3 Volatility & Cost Gate<br/>ATR percentile + spread/ATR"]
e1 -->|"frozen M5 trigger + gates"| ablate["Gated vs ungated ablation<br/>same engine, same session"]
e2 --> ablate
e3 --> ablate
ablate --> decision{"Net lift +<br/>opportunity floor?"}
decision -->|"pass"| carry["Carry gates + clock<br/>to M10/M11 as baseline"]
decision -->|"revise"| narrow["Revise: narrow clock<br/>swap H1 for H4 or buffer"]
decision -->|"reject"| reject["Reject gate<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 e1,e2,e3,ablate,narrow process
class decision decision
class reject risk
class carry okStrategy Rules
Each experiment follows our standardized nine-point protocol.
E1 — Completed H1 Trend Alignment Gate (M5 Pullback Control)
1. Market, Timeframe, and Data Assumptions
- Lanes: One commodity ETP (GLD or PDBC) on RTH and one BTC/ETH linear perpetual on a UTC synthetic session (00:00–23:50 UTC).
- Timeframes: M5 execution; H1 completed-bar trend context.
- Data: Clean OHLCV + timestamped spread per bar.
2. Mechanics vs. Testable Hypothesis
- Mechanics: Higher-timeframe flow represents institutional liquidity. Aligning intraday trades with slower trends filters out counter-trend distribution MQL5 on higher-timeframe alignment.
- Hypothesis: Requiring M5 pullback longs to align with completed H1 20 EMA bullish slope and
H1 ADX ≥ 18yields higher net session return than an ungated baseline under purged walk-forward testing.
3. Required Indicators & Calculations
- Completed H1 20 EMA slope and 14-period Wilder ADX.
- M5 ATR(14) and feasibility ratios (
Spread ÷ ATR ≤ 15%). - M5 pullback-resume trigger:
close[t] > close[t-1]following an intraday dip below 5 EMA.
4. Causal HTF Context & Synchronization
- An H1 bar closing at 10:00 is joined to M5 bars starting at the 10:05 open. The forming 10:00–11:00 bar is invisible.
5. Entry, Stop, Target, Time Stop, and Forced Flatten
- Long Entry:
bias_long[H1]andcost_passandpullback_resume[M5]→ fill atopen[t+1]paying half-spread. - Stop Loss:
entry_price - (1.2 × ATR_M5). - Profit Target:
entry_price + (1.0 × ATR_M5). - Time Stop: 16 M5 bars (80 minutes).
- Forced Flatten: Mandatory exit at session close.
6. Cost Filters & Execution
- Spread-to-ATR ≤ 15%; Cost-to-Target ≤ 10%. Stop-first ambiguous resolution.
7. Sizing & Risk Limits
- Standard ATR-based risk sizing (e.g., 0.5% equity risk per trade).
8. ML Extension (Walk-Forward Meta-Filter)
- Train a Logistic Regression classifier on chronological folds to predict trade follow-through using H1 ADX, M5 pullback depth, and spread percentiles.
9. Failure Modes & Rejection Criteria
- Opportunity Starvation: Gate raises win rate by 3% but cuts trades by 85%, lowering total P&L. Verdict: Reject gate.
- Rejection Trigger: Reject if gated net session return is lower than ungated baseline on out-of-sample folds.
E2 — Prior-Session Level Map (PDH / PDL Breakout Control)
1. Market, Timeframe, and Data Assumptions
- Same lanes and session clocks as E1. Primary execution on M5.
- Daily PDH and PDL computed from completed daily bars.
2. Mechanics vs. Testable Hypothesis
- Mechanics: Prior-day high and low are objective structural benchmarks tracked by institutional algorithms BuildTradingStrategies on previous-day levels.
- Hypothesis: Trading M5 breakouts beyond
PDH + (0.10 × ATR)with H1 trend alignment produces positive net expectancy after spreads and outperforms unanchored channel breakouts.
3. Required Indicators & Calculations
- Completed Daily High (PDH), Low (PDL), and Close (PDC).
- Completed H4 High/Low extremes.
- M5 ATR(14) for dynamic buffer sizing (
0.10 × ATR).
4. Causal Synchronization
- Daily levels are fixed before session open. Prior H4 levels update strictly at H4 bar closes.
5. Entry, Stop, Target, Time Stop, and Forced Flatten
- Breakout Long:
close[t] > PDH + bufferandbias_long[H1]→ fill atopen[t+1]. - Stop Loss:
entry_price - (1.0 × ATR_M5)or below breakout bar low. - Profit Target:
entry_price + (0.50 × Prior_Day_Range)or1.2 × ATR_M5. - Forced Flatten: Mandatory session close.
6. Cost Filters & Execution
- Standard double cost ceiling and stop-first ambiguous resolution.
7. Sizing & Risk
- Volatility-scaled risk sizing per M23.
8. ML Extension (Breakout vs. Sweep Classifier)
- Train a shallow XGBoost or Tree model to classify level tests as true breakouts vs. false sweeps using opening volume and prior-day range width.
9. Failure Modes & Rejection Criteria
- Microstructure Churn: Tight buffers cause single-tick wicks to trigger false breakouts. Fix: Enforce
0.10 × ATRbuffer. - Rejection Trigger: Reject if level breakouts fail to achieve positive net return after fees under walk-forward testing.
E3 — Volatility-Percentile Gate & Cost Routing
1. Market, Timeframe, and Data Assumptions
- Same dual-lane scope on M5.
- Completed H1 ATR(14) ranked against trailing 100 completed H1 bars.
2. Mechanics vs. Testable Hypothesis
- Mechanics: ATR percentile normalizes volatility across assets DesireToTrade on ATR percentile.
- Hypothesis: Restricting M5 trading to regimes where H1 ATR percentile is between 20% and 85% eliminates unprofitable chop and explosive drawdowns, raising net session Sharpe.
3. Required Indicators & Calculations
- 14-period ATR on completed H1 bars.
- Rolling 100-period percentile rank:
(ATR < current_ATR).mean() * 100.
4. Causal Synchronization
- H1 ATR percentile updates at each H1 bar close and applies to subsequent M5 bars.
5. Entry & Flatten Contract
- Evaluates frozen M5 breakout or reversion baseline; armed only when
20% ≤ ATR_pct ≤ 85%. Mandatory session flatten.
6. Cost Filters & Execution
- Strict enforcement of Spread-to-ATR ≤ 15%.
7. Sizing & Risk
- Standard risk limits. Optionally scale down size by 50% when ATR percentile is in the 20%–35% band.
8. ML Extension (Regime Router)
- Fit a 3-state Gaussian Mixture Model (GMM) on standardized ATR and spread to dynamically route between Breakout, Reversion, and Stand-Aside sleeves.
9. Failure Modes & Rejection Criteria
- Band Over-Tightening: Restricting the band to 40%–60% starves trade frequency.
- Rejection Trigger: Reject the volatility gate if it fails to improve net session return on out-of-sample test folds.
Building It In Python
Below is the complete, vectorized Polars and pandas implementation for E1, E2, and E3.
1. Intuition: E1 Causal H1 Trend Gate
We resample H1 bars causally, compute 20 EMA trend, and align features backward to M5.
# E1: Causal H1 trend gate aligned to M5 bars
# Q: How do we synchronize H1 trend features without lookahead?
import pandas as pd
import numpy as np
def build_e1_causal_trend_dataset(m5_df: pd.DataFrame) -> pd.DataFrame:
# 1. Resample to completed H1 bars (Right-labeled)
h1 = m5_df["close"].resample("1h", label="right", closed="right").last().to_frame()
# 2. Compute H1 20 EMA and Trend Slope
h1["ema20"] = h1["close"].ewm(span=20, min_periods=20).mean()
h1["ema_slope"] = h1["ema20"].diff()
h1["bias_long"] = h1["ema_slope"] > 0
# 3. Backward As-Of Alignment to M5
m5_df["h1_bias_long"] = h1["bias_long"].reindex(m5_df.index, method="ffill").fillna(False)
# 4. M5 ATR and Feasibility Ratios
prev_close = m5_df["close"].shift(1)
tr = np.maximum(
m5_df["high"] - m5_df["low"],
np.maximum((m5_df["high"] - prev_close).abs(), (m5_df["low"] - prev_close).abs())
)
m5_df["atr14"] = tr.ewm(span=14, adjust=False).mean()
m5_df["spread_to_atr"] = m5_df["spread"] / m5_df["atr14"]
cost_pass = m5_df["spread_to_atr"] <= 0.15
# 5. M5 Pullback-Resume Trigger
pullback = m5_df["close"].shift(1) < m5_df["close"].shift(1).rolling(5).mean()
resume = m5_df["close"] > m5_df["close"].shift(1)
m5_df["sig_ungated"] = pullback & resume & cost_pass
m5_df["sig_gated"] = m5_df["sig_ungated"] & m5_df["h1_bias_long"]
return m5_df
2. Intuition: E2 Prior-Day Level Map
We construct PDH/PDL from completed daily bars and score M5 breakout events.
# E2: Prior-Day Level Map and M5 Breakout Trigger
def build_e2_level_map(m5_df: pd.DataFrame) -> pd.DataFrame:
# Daily Levels from completed days (Right-labeled)
d1_high = m5_df["high"].resample("1D", label="right", closed="right").max()
d1_low = m5_df["low"].resample("1D", label="right", closed="right").min()
m5_df["pdh"] = d1_high.reindex(m5_df.index, method="ffill")
m5_df["pdl"] = d1_low.reindex(m5_df.index, method="ffill")
# Breakout Trigger beyond PDH + 0.10 ATR buffer
buffer = 0.10 * m5_df["atr14"]
m5_df["sig_pdh_breakout"] = (
(m5_df["close"] > m5_df["pdh"] + buffer) &
m5_df["h1_bias_long"] &
(m5_df["spread_to_atr"] <= 0.15)
)
return m5_df
3. Intuition: E3 Volatility Percentile Filter
We compute completed H1 ATR percentile and gate trades within the 20%–85% band.
# E3: H1 ATR Percentile Gate
def build_e3_volatility_gate(m5_df: pd.DataFrame) -> pd.DataFrame:
# Compute H1 ATR on completed bars
h1 = m5_df[["high", "low", "close"]].resample("1h", label="right", closed="right").agg({
"high": "max", "low": "min", "close": "last"
})
prev_c = h1["close"].shift(1)
h1_tr = np.maximum(h1["high"] - h1["low"], np.maximum((h1["high"] - prev_c).abs(), (h1["low"] - prev_c).abs()))
h1["atr14"] = h1_tr.ewm(span=14, adjust=False).mean()
# 100-period ATR Percentile
h1["atr_pct"] = h1["atr14"].rolling(100, min_periods=50).apply(
lambda w: (w < w.iloc[-1]).mean() * 100, raw=False
)
h1["vol_gate_pass"] = h1["atr_pct"].between(20, 85)
# Align to M5
m5_df["vol_gate_pass"] = h1["vol_gate_pass"].reindex(m5_df.index, method="ffill").fillna(False)
m5_df["sig_vol_gated"] = m5_df["sig_gated"] & m5_df["vol_gate_pass"]
return m5_df
4. Intuition: Conservative Bar-Replay Ablation Engine
We simulate both gated and ungated variants through our conservative execution engine.
# Conservative Replay Engine for Gated vs. Ungated Ablation
def run_ablation_replay(m5_df: pd.DataFrame, signal_col: str) -> dict:
records = m5_df.to_dicts() if hasattr(m5_df, "to_dicts") else m5_df.to_dict("records")
trades = []
in_pos = False
entry_p = 0.0
stop_p = 0.0
target_p = 0.0
for i in range(len(records) - 1):
curr = records[i]
nxt = records[i+1]
# Enter at next bar open
if not in_pos and curr[signal_col] and not curr.get("is_final_bar", False):
in_pos = True
entry_p = nxt["open"] + 0.5 * nxt["spread"]
atr = curr["atr14"]
stop_p = entry_p - (1.2 * atr)
target_p = entry_p + (1.0 * atr)
continue
if in_pos:
hit_stop = curr["low"] <= stop_p
hit_target = curr["high"] >= target_p
# Stop-First Ambiguous Resolution
if hit_stop and hit_target:
trades.append({"pnl": stop_p - entry_p - curr["spread"], "type": "ambig_stop"})
in_pos = False
elif hit_stop:
trades.append({"pnl": stop_p - entry_p - curr["spread"], "type": "stop"})
in_pos = False
elif hit_target:
trades.append({"pnl": target_p - entry_p - curr["spread"], "type": "target"})
in_pos = False
elif curr.get("is_final_bar", False):
trades.append({"pnl": curr["close"] - entry_p - curr["spread"], "type": "flatten"})
in_pos = False
pnl_series = [t["pnl"] for t in trades]
return {
"trade_count": len(trades),
"total_pnl": sum(pnl_series),
"win_rate": np.mean([p > 0 for p in pnl_series]) if trades else 0.0,
}
Testing It Honestly
Honest testing requires comparing gated strategies against an identical ungated baseline:
- Gated vs. Ungated Ablation: Evaluate the strategy with and without the HTF gate. The gate is justified only if it improves net return per session after accounting for reduced trade count.
- Next-Bar-Open Execution: Every fill occurs at the open of bar
t+1. Never fill at the signal bar’s close. - Stop-First Ambiguous Resolution: When both stop and target are touched in a single bar, book the stop loss.
- Mandatory Session Flatten: Every position is closed at or before the session boundary.
- Purged Walk-Forward Splits: Test folds are session-aligned with embargo periods to prevent information leakage.
Variants & Extensions
| Variant | Modification | Intended Benefit | Key Metric to Report |
|---|---|---|---|
| H1 vs. H4 Trend Source | Use H4 bars for trend permission | Smoother, less noisy trend direction | Trade count vs. net session P&L |
| Prior H4 Level Mapping | Use H4 high/low instead of PDH/PDL | Nearer intraday structural references | Breakout frequency and buffer sensitivity |
| ATR Percentile Band Tuning | Test bands: 20–85 vs. 25–75 vs. 15–90 | Optimizes volatility filtering tightness | Session P&L across volatility regimes |
| Directional Sizing Modifier | Trade counter-trend setups at 50% size | Retains trade opportunity with reduced risk | Total P&L vs. stand-aside rule |
Hands-On Project
Deliverable: docs/research/m09_causal_mtf_experiments.md and runnable notebook notebooks/m09_causal_mtf_experiments.ipynb.
Project Card — M9 Multi-Timeframe Experiments (v1.0)
| Specification Area | Project Requirement |
|---|---|
| Assets & Lanes | One commodity ETP (GLD or PDBC) and one crypto perpetual (BTC or ETH) on an approved venue. |
| Experiments Evaluated | E1 Trend Gate, E2 Level Map, and E3 Volatility Gate on M5. |
| Execution Rules | Signal on close of bar t → fill at open of bar t+1; deduct timestamped spread; stop-first ambiguous resolution. |
| Session Calendars | ETP: RTH 09:30–16:00 ET; Crypto: UTC 00:00–23:50 synthetic session (forced flatten at 23:50 UTC). |
| Validation Design | 5-fold chronological purged walk-forward with embargo; separate untouched holdout period. |
| Acceptance Criteria | Gated net expectancy exceeds ungated baseline on purged OOS; session trade floor met; ambiguous share < 5%. |
Step-by-Step Instructions
- Prepare clean M5 data. Construct right-labeled H1 EMA trend, ATR percentile, and daily PDH/PDL levels.
- Verify causal alignment by printing boundary rows around H1 bar closes.
- Replay the ungated M5 baseline and record total session P&L.
- Replay E1 (Trend Gate), E2 (Level Map), and E3 (Volatility Gate) under identical execution rules.
- Run side-by-side walk-forward ablations comparing net return per session across all variants.
- Publish the final research memo documenting whether each gate earned its complexity.
Key Takeaways
- HTF context is publish/subscribe: Slower bars publish only at their close. Lower timeframes subscribe causally.
- Ablation is non-negotiable: An HTF gate must beat an identical ungated baseline on net session P&L, not just win rate.
- Prior-day levels provide structural anchors: PDH and PDL turn arbitrary intraday wicks into objective breakout events.
- Volatility gates filter out unviable tapes: ATR percentile ensures you only trade when market movement comfortably exceeds spread friction.
- Enforce conservative execution: Fill at next-bar open, deduct full spreads, resolve ambiguous bars stop-first, and force flatten at session end.
References
- U.S. Securities and Exchange Commission — Exchange-Traded Funds Investor Bulletin — https://www.sec.gov/investor/alerts/etfs.pdf — Mechanics. ETF share trading and NAV mechanics.
- Fidelity — Commodity ETFs: Contango and Backwardation — https://www.fidelity.com/learning-center/investment-products/etf/commodity-etfs-contango-backwardation — Mechanics. Roll yield mechanics in futures-based funds.
- SSGA — SPDR Gold Shares (GLD) — https://www.ssga.com/us/en/intermediary/etfs/spdr-gold-shares-gld — Mechanics. Physical trust wrapper disclosures.
- Coinbase — Funding Rates on Perpetual Futures — https://help.coinbase.com/en/derivatives/perpetual-style-futures/funding-rate — Mechanics. Perpetual funding cash-flow mechanism.
- LuxAlgo — Prior Period Levels Concept — https://www.luxalgo.com/library/concept/prior-period-levels/ — Mechanics. Objective horizontal level mapping.
- FXGlory — ATR and ADX Strategy Guide — https://fxglory.com/learn/forex-strategies/atr-and-adx-strategy/ — Mechanics / Transfer. ATR and ADX indicator pairing.
- DesireToTrade — Volatility Filter: ATR Percentile — https://www.desiretotrade.com/docs/volatility-filter-indicator-atr-percentile-volatility/ — Mechanics. Normalized volatility ranking.
- Supa.is — Fix Pine Script v6 Request Security Lookahead — https://supa.is/article/pine-script-v6-request-security-lookahead-fix-2026 — Mechanics. Causal MTF synchronization in code.
- Jayadevrana — Non-Repainting Multi-Timeframe Patterns — https://jayadevrana.com/pine-script-request-security-lookahead/ — Mechanics. Offset discipline for completed HTF bars.
- Tradepilot — Multi-Timeframe Scripting Without Repainting — https://tradepilot.co.in/blog/multi-timeframe-pine-script-request-security — Mechanics. Anti-lookahead MTF design.
- TradingView — Other Timeframes and Data Documentation — https://www.tradingview.com/pine-script-docs/concepts/other-timeframes-and-data/ — Mechanics. Official
request.securitymechanics.
Next: Module 10 — Intraday Trend-Following & Momentum Playbooks · Core: Module 9 — Causal Multi-Timeframe Context