The Learning Library
Contents

Module 13 — Sessions, Auctions & Funding Clocks

Part III · Intraday Design, Alpha & Playbooks · Priority 🎯 Core Status: Draft v0.2 · Scope: commodity ETFs/ETPs + BTC/ETH spot & linear perps · Horizon: M1/M5 primary, M15 where appropriate, same-session flat Prerequisites: M5, M6, M7, M22, M23


Overview

You start every clock decision with the venue and the price you can actually trade, not with a calendar story. A U.S.-listed commodity ETF trades in regular trading hours (RTH) from 09:30 to 16:00 ET. It opens with an auction at 09:30 and closes with an auction at 16:00. An imbalance freeze locks order books from 15:50 to 16:00 NYSE hours & calendars. Outside this window, early and late sessions trade at wide spreads that you deliberately exclude.

A BTC or ETH linear perpetual never closes. However, you impose an explicit UTC synthetic session (such as 00:00 to 23:50 UTC) with a forced flatten. This ensures every position ends the day flat and attribution stays deterministic.

The preregistered hypothesis for this module is narrow: session timing, auction proximity, and funding-clock proximity determine your cost and volatility regime before they determine directional edge.

In plain English, the hour you trade decides the invoice you pay. The price move you capture decides whether that invoice is affordable.

Who is on the other side?

  • ETP Open & Close: The imbalance between accumulated institutional auction orders.
  • ETP Midday: Thinned market-maker books during the institutional participation lull.
  • Perp Funding: Longs or shorts settling periodic holding rent because the contract lacks an expiry date.

Evidence status: hypothesis, mechanics-supported. Exchange calendars, auction freezes, and funding intervals are documented mechanisms. The fact that spreads widen at session edges is observable reality. The claim that a spread-aware session filter preserves net P&L is a testable hypothesis. You must prove it on your own asset lane, cost model, and historical fills.

The two main failure modes are:

  1. Clock averaging: Pooling 24 hours of crypto bars or a full RTH session into a single average volatility metric. This masks the expensive reality of the specific hours you actually trade.
  2. Funding blindness: Holding a perpetual through an hourly funding timestamp Coinbase funding — hourly calculation or an 8-hour settlement without booking the payment. This leaks basis points on every trade.

💡 Analogy (The Operating Schedule & Turnstile): Think of regular exchange hours as a canal lock. Continuous trading is the river, but the open and close are mandatory locks where tolls surge. Crypto 24/7 trading is an unceasing highway, but funding is periodic rent charged to whichever side holds the lease at the timestamp. A session filter acts as a bouncer at the turnstile. It does not predict where prices will go. It simply checks if the cover charge is cheap enough relative to the room’s volatility to justify walking inside.

📌 Convention: This module operates strictly on OHLCV + spread data. We assume no order book, level-2 ticks, bid/ask depth, or queue models. Execution is bar-based: a signal on the close of bar t fills at the open of bar t+1, deducting observed spread. Ambiguous intra-bar touches resolve adversely (stop-first). All positions force-flatten before the session boundary. Completed H1/H4/D1 bars act strictly as causal context at release.


How It Works

A market clock governs two mechanical variables simultaneously: bar height (volatility) and touch cost (spread & funding).

The ETP RTH Clock is U-Shaped

Volume, volatility, and bid-ask spread trace a classic U-shaped curve across a single-exchange trading day:

  • 09:30–10:00 ET (Morning Surge): Between 15% and 25% of daily volume prints in the first 30 minutes Pomegra — volume by time of day. Spreads start wide as market makers digest overnight news.
  • 11:00–13:00 ET (Midday Trough): Trading activity falls to 60%–70% of the morning pace. Spreads tighten, but average bar range (ATR) collapses.
  • 15:30–16:00 ET (Closing Surge): Volume and volatility explode as indexers and day-end rebalancing flows collide. Spreads widen aggressively into the closing bell Pomegra — commodity ETF liquidity.

On commodity ETFs, wrapper liquidity dictates your friction. Mega-funds like GLD maintain tight spreads of 0.02% to 0.05%. Narrow commodity wrappers trade at 0.3% to 1.5% spreads, blowing out past 2.0% in quiet midday hours.

ETP RTH Profile:
Vol / Spread
  ^   ***                                 ***
  |  *   *                               *   *
  | *     *                             *     *
  |*       *                           *       *
  |         ***************************
  +---------------------------------------------> Time (ET)
   09:30           11:30-13:30           16:00
   Open               Midday             Close

The RTH Auction is a Canal Lock

Continuous trading is an open river where buyers and sellers meet freely. The opening (09:30 ET) and closing (16:00 ET) auctions are canal locks. Trading halts, orders accumulate, and an algorithm determines a single clearing price that maximizes volume and minimizes order imbalance StockTitan — opening & closing auctions.

Order entry deadlines enforce this lock:

  • 15:50 ET: Market-on-Close (MOC) order cutoff.
  • 15:58 ET: Limit-on-Close (LOC) modification freeze.
  • 15:50–16:00 ET: Imbalance-only orders execute exclusively to reduce published imbalance.

Unless you maintain explicit quote-level auction execution models, gate out entries during the 15:50–16:00 freeze. Continuous bar assumptions fail during auction freezes.

The Crypto 24/7 Clock is M-Shaped

In continuous, multi-regional crypto markets, the single daily U-shape splits into an M-shaped profile driven by regional overlaps MarketsByte — intraday volatility by session:

  • Peak 1 (08:00–09:00 UTC): London market open.
  • Peak 2 (13:30–16:00 UTC): New York open overlapping European afternoon trading.

Intraday Bitcoin and Ether seasonality highlights three distinct sessions Tom Espel — BTC/ETH intraday regime seasonality:

  1. Asia (00:00–06:00 UTC): Moderate volume, occasional regional opening bursts.
  2. Europe (08:00–16:30 UTC): Broad institutional participation.
  3. Americas (13:30–20:00 UTC): Heavy volume clustering around the London 16:00 UTC and ET 16:00 fixings.

Weekends lack institutional liquidity. They display low-volume, retail-driven behavior that invalidates weekday models Securities.io — crypto day-of-week intraday. Apparent “weekly calendar anomalies” collapse under hourly analysis to a single retail re-entry spike on Sunday 23:00 to Monday 00:00 UTC MLQuants — day-of-week effects are hourly.

The Funding Clock is Periodic Lease Rent

Perpetual swaps maintain price pegging with spot markets via periodic cash transfers:

Funding is not charged across holding duration—it hits whoever holds the position at the exact settlement timestamp. An intraday trader who enters at 08:05 UTC and exits at 15:55 UTC pays zero funding. A trader holding across the 16:00 UTC mark pays full 8-hour rent.

flowchart TD
    bars["OHLCV + spread<br/>M1/M5/M15 completed bars"] --> atr["ATR + spread-to-ATR<br/>per timeframe"]
    bars --> atlas["Hour/minute atlas<br/>spread, ATR, pass rate"]
    bars --> clockETF["ETF RTH clock<br/>09:30-16:00 ET + auctions"]
    bars --> clockCrypto["Crypto UTC clock<br/>Asia/Europe/Americas sessions"]
    bars --> funding["Funding clock<br/>hourly vs 8-hour<br/>venue-specific"]
    atr --> gate{"Bar passes cheap<br/>and tall gate?"}
    atlas --> gate
    clockETF --> gate
    clockCrypto --> gate
    funding --> gate
    gate -->|"yes: inside eligible<br/>hour and outside freeze"| eval["Evaluate M1/M5/M15 trigger<br/>close[t] -> open[t+1]"]
    gate -->|"no: spread wide<br/>or in freeze/window"| skip["Skip bar<br/>stand down"]
    eval --> flatten["Forced flatten<br/>at session boundary"]

    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 atr,atlas,clockETF,clockCrypto,funding,eval,flatten process
    class gate decision
    class skip risk
flowchart TD
    pre["Pre-open queue<br/>06:30-09:30 ET"] --> open["Opening auction<br/>09:30 ET lock"]
    open --> morn["Morning RTH<br/>09:30-11:00 high vol<br/>spreads normalizing"]
    morn --> mid["Midday trough<br/>11:00-13:00 lighter<br/>lower ATR"]
    mid --> aft["Afternoon RTH<br/>13:00-15:50 drift<br/>NY/London overlap"]
    aft --> freeze["Imbalance freeze<br/>15:50-16:00 ET<br/>MOC 15:50 LOC 15:58"]
    freeze --> close["Closing auction<br/>16:00 ET lock"]
    close --> late["Late session<br/>16:00-20:00 ET excluded"]
    morn --> gate{"Gate passes<br/>spread/ATR ceiling?"}
    mid --> gate
    aft --> gate
    gate -->|"yes"| trade["Trade M5 inside<br/>eligible window"]
    gate -->|"no"| stand["Stand down<br/>ceiling breach or freeze"]

    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 pre,open,close data
    class morn,mid,aft,freeze,late process
    class gate decision
    class stand risk
    class trade ok

⚠️ Pitfall (Calendar Leakage & Overfitting): Never optimize sub-hour trading slices (such as “only trade 10:15 to 10:30”) on historical P&L. That overfits sample noise. Preregister macro-hour windows based on market mechanics (e.g., 09:45–11:30 ET). Validate these windows using dynamic cost ratios before running directional backtests.


Strategy Rules

Session, auction, and funding rules serve as compliance filters. They determine trade eligibility before any alpha logic evaluates a signal.

Eligible Calendars (Per Instrument × Venue)

Calendar Eligible Window (Project Card Default) Excluded by Declaration Source Mechanism
Commodity ETP RTH 09:30–16:00 ET. Triggers evaluated on completed bars. Force-flatten at or before 15:58 ET. Pre-open (06:30–09:30), late session (16:00–20:00), market holidays, early closes, and the 15:50–16:00 imbalance freeze. NYSE hours & calendars, StockTitan auctions
Commodity ETP Extended Only active when the project card explicitly defines extended cost and liquidity surfaces. All unmodeled auction types and periods lacking validated bid/ask spreads. Card-specific trade logs and exchange quote files.
BTC/ETH Perp UTC Synthetic 00:00–23:50 UTC. Triggers evaluated on M1/M5/M15 bars. Forced flatten at 23:50 UTC. Venue maintenance windows, settlement stand-down buffers (±15 min), and untraded illiquid hours. Tom Espel session definitions

Intraday Session Segmentation (RTH Detail)

RTH Segment Clock (ET) Cost & Volatility Profile Session Gate Posture
Open Auction 09:30 Lock Max short-term volatility, wide unhedged spreads. Stand down. Wait for the first complete M5 bar to close.
Morning Session 09:30–11:00 High volume, wide-to-normalizing spreads, peak ATR. Primary trading window. Active if spread-to-ATR clears ceilings.
Midday Trough 11:00–13:00 Compressed ATR, volume down 35%, tight spreads. Selective. Stand down on tight-target momentum models.
Afternoon Overlap 13:00–15:50 London fix flows (16:00 UTC / 11:00-12:00 ET), US institutional re-allocation. Active trading window. Strong trending behavior.
Imbalance Freeze 15:50–16:00 MOC cutoff at 15:50, LOC freeze at 15:58. Stand down. No continuous entries. Force-flatten open trades.
Close Auction 16:00 Lock Massive volume, single clearing print. Excluded. No continuous fills permitted.

Crypto UTC Session Segmentation

UTC Window Regional Session Liquidity & Market Character Gate Policy
00:00–06:00 Asia Session Moderate volume, occasional regional opening spikes. Tradable. Evaluate Asia-specific spread surfaces.
08:00–16:30 Europe Session Surging volume, London open (08:00–09:00), peak liquidity. Primary trading window.
13:30–20:00 Americas Session Highest combined volume, US ETF trading, 16:00 fixings. Primary trading window. Monitor fixing slippage.
23:00–00:00 Sunday Re-entry Retail re-positioning burst (mean 0.11% hourly return) MLQuants. Special sleeve. Isolate as a specific hourly setup.
Weekends Global Retail Thin volume, low directional persistence. Stand down. Exclude or evaluate on weekend-only tables.

Objective Bar-Based Gates (Evaluated at Close of Bar t)

  1. Spread-to-ATR Ratio: Spread[t] / ATR[t] <= 0.15 (Spread consumes <= 15% of the average bar range).
  2. Cost-to-Target Ratio: (Spread[t] + Stressed_Fee) / Target_Distance <= 0.10 (Friction consumes <= 10% of profit target).
  3. Trailing Spread Percentile: Spread[t] <= p75(Spread[Trailing_Hour_Window]) (Avoid momentary spread spikes).
  4. Funding Window Buffer: Distance to nearest settlement >= 15 minutes (Unless funding carry is explicitly booked).
  5. Auction Freeze Buffer: Time to 16:00 ET lock >= 10 minutes (No entries past 15:50 ET).

Building It In Python

Below is the complete, self-contained implementation using Polars. It builds the session atlas, evaluates multi-clock gates, and executes trades under strict data contracts.

"""
Module 13: Session, Auction, and Funding Clock Filter Engine.
Stack: Polars, NumPy. Strict causal data alignment.
"""

import polars as pl
import numpy as np

# ---------------------------------------------------------
# 1. Causal ATR & Spread Metric Construction
# ---------------------------------------------------------
def compute_session_metrics(df: pl.DataFrame, atr_period: int = 14) -> pl.DataFrame:
    """
    Computes true range, causal ATR, and cost ratios on completed bars.
    Requires columns: timestamp, open, high, low, close, spread.
    """
    prev_close = df["close"].shift(1)
    tr = pl.max_horizontal([
        df["high"] - df["low"],
        (df["high"] - prev_close).abs(),
        (df["low"] - prev_close).abs(),
    ]).alias("true_range")

    df_tr = df.with_columns(tr)
    atr = df_tr["true_range"].ewm_mean(span=atr_period, adjust=False).alias(f"atr_{atr_period}")

    return df_tr.with_columns([
        atr,
        (pl.col("spread") / atr).alias("s2atr"),
        (pl.col("spread") / (0.9 * atr)).alias("c2t_09"),
    ])

# ---------------------------------------------------------
# 2. Session Atlas Builder (RTH and UTC Clocks)
# ---------------------------------------------------------
def build_session_atlas(df: pl.DataFrame, clock_type: str = "ETP_RTH") -> pl.DataFrame:
    """
    Constructs an hourly liquidity and cost atlas.
    """
    if clock_type == "ETP_RTH":
        df_clock = df.with_columns(
            pl.col("timestamp").dt.convert_time_zone("America/New_York").dt.hour().alias("hour")
        ).filter((pl.col("hour") >= 9) & (pl.col("hour") <= 16))
    else:  # UTC Crypto
        df_clock = df.with_columns(
            pl.col("timestamp").dt.convert_time_zone("UTC").dt.hour().alias("hour")
        )

    return (
        df_clock.group_by("hour")
        .agg([
            pl.col("spread").median().alias("spread_p50"),
            pl.col("spread").quantile(0.90).alias("spread_p90"),
            pl.col("atr_14").median().alias("atr_p50"),
            pl.col("s2atr").median().alias("s2atr_p50"),
            pl.col("c2t_09").median().alias("c2t_p50"),
            (pl.col("s2atr") <= 0.15).mean().alias("pass_rate_s2atr"),
            (pl.col("c2t_09") <= 0.10).mean().alias("pass_rate_c2t"),
            pl.len().alias("sample_bars"),
        ])
        .sort("hour")
    )

# ---------------------------------------------------------
# 3. Gate Compliance Engine (Auctions, Funding, Spreads)
# ---------------------------------------------------------
def is_auction_freeze(timestamp: pl.Series) -> pl.Series:
    """Identifies NYSE closing auction freeze window: 15:50 to 16:00 ET."""
    et_time = timestamp.dt.convert_time_zone("America/New_York")
    hour = et_time.dt.hour()
    minute = et_time.dt.minute()
    return ((hour == 15) & (minute >= 50)) | (hour == 16)

def is_funding_standdown(timestamp: pl.Series, venue: str = "Coinbase", buffer_mins: int = 15) -> pl.Series:
    """
    Flags settlement buffers:
    - Coinbase: Hourly settlement (stand down within 15 mins of top-of-hour).
    - Binance/Deribit: 8-hour settlement at 00:00, 08:00, 16:00 UTC.
    """
    utc_time = timestamp.dt.convert_time_zone("UTC")
    minute = utc_time.dt.minute()
    hour = utc_time.dt.hour()

    if venue.lower() == "coinbase":
        return (minute >= (60 - buffer_mins)) | (minute <= buffer_mins)
    else:  # Standard 8-hour settlement
        is_settle_hour = hour.is_in([0, 8, 16])
        return is_settle_hour & ((minute >= (60 - buffer_mins)) | (minute <= buffer_mins))

def evaluate_session_gate(df: pl.DataFrame, venue: str = "NYSE",
                          max_s2atr: float = 0.15, max_c2t: float = 0.10) -> pl.Series:
    """
    Evaluates master bar eligibility boolean.
    """
    auction_blocked = is_auction_freeze(df["timestamp"]) if venue == "NYSE" else pl.Series([False] * len(df))
    funding_blocked = is_funding_standdown(df["timestamp"], venue) if venue != "NYSE" else pl.Series([False] * len(df))

    cost_pass = (df["s2atr"] <= max_s2atr) & (df["c2t_09"] <= max_c2t)
    timing_pass = (~auction_blocked) & (~funding_blocked)

    return cost_pass & timing_pass

# ---------------------------------------------------------
# 4. Conservative Bar-Based Execution Simulation
# ---------------------------------------------------------
def simulate_bar_execution(entry_side: str, bar_open: float, bar_high: float,
                           bar_low: float, bar_close: float, stop_price: float,
                           target_price: float) -> tuple[str, float]:
    """
    Conservative intra-bar outcome resolver.
    Adverse convention: Stop-first if both stop and target are touched.
    """
    hit_stop = bar_low <= stop_price if entry_side == "long" else bar_high >= stop_price
    hit_target = bar_high >= target_price if entry_side == "long" else bar_low <= target_price

    if hit_stop and hit_target:
        # Ambiguous resolution: enforce adverse outcome
        return "ambiguous_stop_loss", stop_price
    elif hit_stop:
        return "stop_loss", stop_price
    elif hit_target:
        return "target_hit", target_price
    else:
        return "hold", bar_close

Worked Numeric Walkthrough: Hand-Checkable Math

Let us evaluate the math across two real-world operational lanes on M5 resolution.

Assumptions:

  • Lane A (Gold Trust ETF - GLD): M5 ATR = $0.18, Observed Spread = $0.030, Fee/Slippage stress = $0.004, Target = 0.9 × ATR = $0.162.
  • Lane B (Bitcoin Perpetual - BTC-PERP): M5 ATR = $42.00, Observed Spread = $2.50, Fee/Slippage stress = $0.40, Target = 0.9 × ATR = $37.80.
Step Metric Formula Lane A (GLD ETF) Lane B (BTC Perp)
1 Spread-to-ATR Spread / ATR $0.030 / $0.18 = 16.7% $2.50 / $42.00 = 5.95%
2 Gate Threshold Check s2atr <= 15.0% FAIL (16.7% > 15.0%) PASS (5.95% <= 15.0%)
3 Cost-to-Target (Spread + Stress) / Target ($0.030 + $0.004) / $0.162 = 20.9% ($2.50 + $0.40) / $37.80 = 7.67%
4 Gate Threshold Check c2t <= 10.0% FAIL (20.9% > 10.0%) PASS (7.67% <= 10.0%)
5 Funding Cost (1 Straddle) Notional * Funding_Rate N/A ($0.00) $10,000 * 0.01% = $1.00
6 Master Eligibility Verdict Both ceilings pass? REJECT BAR (Do not trade) ACCEPT BAR (Eligible to trade)

What this means for your P&L: On Lane A, trading M5 bars costs 20.9% of your total profit target in friction. This forces negative expectancy. You must either step up to M15 bars (where ATR expands faster than spread) or restrict trading to peak morning volume. On Lane B, total friction consumes only 7.67% of your target, leaving room for a viable edge.


Testing It Honestly

An honest backtest reflects the exact execution realities of live trading.

1. Zero Lookahead in Time & Features

  • Features compute strictly on completed bars.
  • A signal generated on the close of bar t fills at the open of bar t+1.
  • Higher-timeframe context (H1/H4/D1) joins using an as_of timestamp: H1_Close + Ingest_Lag. Never reference current-hour uncompleted closes.

2. Comprehensive Friction Booking

  • Deduct the full observed spread on both entry and exit.
  • When holding a perpetual position across funding timestamps, book the actual realized venue rate.
  • Funding rate sign determines cash flow: positive rates mean longs pay shorts; negative rates mean shorts pay longs.

3. Adverse Ambiguous-Bar Resolution

When a bar’s High and Low breach both your profit target and stop loss, always book the stop loss. Alternatively, mark the trade as ambiguous and exclude it while tracking its frequency. If a strategy’s profitability relies on favorable intra-bar fills, it is unviable in production.

4. Mandatory Session Flattening

Every simulated position must force-flatten at or before the session closing boundary:

  • ETPs: 15:58 ET (before the closing auction lock).
  • Crypto: 23:50 UTC (before synthetic daily reset).

Never roll an intraday trade overnight to “let a loser recover.”

# Session-aware walk-forward split (Prevents session-boundary leakage)
from sklearn.model_selection import TimeSeriesSplit

def generate_session_splits(unique_session_dates: list, n_splits: int = 5):
    """
    Splits data by full trading sessions, applying an embargo buffer between folds.
    """
    tscv = TimeSeriesSplit(n_splits=n_splits)
    for train_idx, test_idx in tscv.split(unique_session_dates):
        train_sessions = [unique_session_dates[i] for i in train_idx]
        # Apply 1-session embargo: drop first session of test set to prevent cross-session feature leakage
        test_sessions = [unique_session_dates[i] for i in test_idx[1:]]
        yield train_sessions, test_sessions

Variants & Extensions

Evaluate one parameter change at a time, holding execution rules constant.

Variant Parameter Shift Target Market Condition Measured Trade-off
Narrowed RTH Window Trade 09:45–11:00 and 13:30–15:45 ET only. Drop midday. Commodity wrappers with severe midday liquidity evaporation. Higher net expectancy per trade, but 35% fewer total trades.
Overlap-Only Crypto Trade 08:00–16:30 and 13:30–20:00 UTC only. Drop Asia. BTC/ETH pairs where Asia volume is too thin for large clip sizes. Cleaner trends; eliminates overnight gap slippage.
Wide Funding Stand-Down Expand settlement buffer from ±15m to ±30m. Venues experiencing pre-funding spread blowouts. Eliminates funding drag; reduces tradable bars by 12.5%.
Causal HTF Trend Filter M5 entries require alignment with completed H1 20-bar EMA. Choppy, range-bound market environments. Filters false breakouts; increases average winning hold duration.
Percentile Gate Tightening Lower spread ceiling from p75 to p60 within hour. Fast-moving markets where spread leads volatility spikes. Protects against volatility traps; lowers opportunity count.
M15 Horizon Step-Out Resample M5 signals to M15 resolution. High-spread commodity wrappers (e.g., agricultural pools). Decreases spread-to-ATR ratio; lowers sample size.

Hands-On Project

Deliverable: docs/research/m13_session_filters.md and notebooks/m13_session_filters.ipynb. Build two session atlases and run a gated walk-forward backtest across both lanes. Include a one-page decision memo per lane.

Project Card — M13 Session Filters (v1.0)

Specification Area Project Requirement & Preregistered Parameter
Instrument Universe ETP Lane: GLD (Physical Trust) + PDBC (Commodity Pool).<br/>Crypto Lane: BTC-USDT and ETH-USDT spot and linear perps on approved CEX.
Execution Horizons Primary: M5. Secondary check: M1 and M15. Same-session forced flatten.
Trading Clocks ETP: RTH 09:30–16:00 ET. Imbalance freeze 15:50–16:00. Pre/Post sessions excluded.<br/>Crypto: UTC Synthetic 00:00–23:50 UTC. Mandatory flatten at 23:50 UTC.
Order Routing Model Bar-close signal t → Bar-open fill t+1. Gaps fill at open. Full observed spread deducted.
Cost Schedule Per-instrument observed spread surface + venue commission schedule.<br/>Funding rate booked per settlement timestamp on perp lanes.
Risk Thresholds Risk per trade: 0.25% to 0.50% equity. Max 3 trades per session. Daily loss limit: 1.5%. Stop-first ambiguous resolution.
Validation Architecture 5-Fold Walk-Forward with 1-session embargo. Daylight Saving Time (DST) shift regression tests.
Acceptance Criteria Cost-to-target <= 10%, Spread-to-ATR <= 15%. Gated system must beat ungated control on net P&L.

Execution Checklist:

  1. Data Ingestion: Load 1-year of M5 bars with real-time timestamped spreads. Validate data integrity per M4.
  2. Build Liquidity Atlases: Generate hourly median spread, ATR, and cost-ratio tables for both RTH ET and UTC Crypto clocks.
  3. Implement Hard Gates: Code the 5 objective gates (Spread-to-ATR, Cost-to-Target, Spread Percentile, Funding Window, Auction Freeze).
  4. Run Gated vs. Ungated Walk-Forward: Compare identical breakout signals with and without the session filter.
  5. Ablation & Stress Analysis: Run base, p50, and p90 spread stress tests. Verify DST stability across seasonal clock changes.

Key Takeaways

  • The clock sets the invoice: At M1/M5 timeframes, session timing and spread curves dictate your profitability before indicator logic ever fires.
  • RTH is U-shaped, Crypto is M-shaped: ETPs experience volume spikes at the open and close; crypto liquidity peaks around the London (08:00 UTC) and New York (13:30 UTC) overlaps.
  • Auctions are locks, not rivers: Continuous trading models fail during the 15:50–16:00 ET closing freeze. Stand down unless modeling explicit auction mechanics.
  • Funding is explicit lease rent: Never ignore perpetual settlement timestamps. Either stand down ±15 minutes around settlement or book the realized cash adjustment.
  • Crypto “day-of-week” is an hourly illusion: Weekly retail anomalies concentrate into a single Sunday 23:00 to Monday 00:00 UTC window.
  • Enforce conservative execution: Always use next-bar open fills, deduct real spreads, resolve intra-bar ambiguity adversely, and force-flatten every session.

References


Next: Module 14 — Intraday Events · Companion: M13 Strategies — Session Filters