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:
- 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.
- 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
tfills at the open of bart+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:
- Asia (00:00–06:00 UTC): Moderate volume, occasional regional opening bursts.
- Europe (08:00–16:30 UTC): Broad institutional participation.
- 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:
- Coinbase: Calculates funding continuously with hourly applications (75% current hour premium / 25% prior hour smoothing) Coinbase funding — hourly calculation, Coinbase — understanding funding rates.
- Binance / Deribit / OKX: Standard 8-hour settlement cycles (00:00, 08:00, 16:00 UTC), accelerating to 4-hour or 1-hour cadences when markets hit extreme premium caps FeeFlux — funding rate guide (Binance 8-hour mechanism).
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 riskflowchart 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)
- Spread-to-ATR Ratio:
Spread[t] / ATR[t] <= 0.15(Spread consumes <= 15% of the average bar range). - Cost-to-Target Ratio:
(Spread[t] + Stressed_Fee) / Target_Distance <= 0.10(Friction consumes <= 10% of profit target). - Trailing Spread Percentile:
Spread[t] <= p75(Spread[Trailing_Hour_Window])(Avoid momentary spread spikes). - Funding Window Buffer: Distance to nearest settlement
>= 15 minutes(Unless funding carry is explicitly booked). - 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
tfills at the open of bart+1. - Higher-timeframe context (H1/H4/D1) joins using an
as_oftimestamp: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:
- Data Ingestion: Load 1-year of M5 bars with real-time timestamped spreads. Validate data integrity per M4.
- Build Liquidity Atlases: Generate hourly median spread, ATR, and cost-ratio tables for both RTH ET and UTC Crypto clocks.
- Implement Hard Gates: Code the 5 objective gates (Spread-to-ATR, Cost-to-Target, Spread Percentile, Funding Window, Auction Freeze).
- Run Gated vs. Ungated Walk-Forward: Compare identical breakout signals with and without the session filter.
- 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
- NYSE — Holidays & Trading Hours — https://www.nyse.com/markets/hours-calendars — Mechanics. Core RTH rules, opening/closing auction timing, and imbalance freeze cutoffs.
- StockTitan — Opening & Closing Auctions: How Stock Market Auctions Work — https://www.stocktitan.net/articles/opening-closing-auctions-explained — Mechanics. Operational breakdown of MOC/LOC order types and auction matching logic.
- Pomegra Learn Library — Liquidity in Commodity ETFs — https://pomegra.io/learn/library/track-d-other-assets/commodities/chapter-09-commodity-etfs-and-etns/commodity-etf-liquidity — Mechanics. Comparative spread profiles of broad vs. niche commodity wrappers.
- Pomegra Wiki — Volume by Time of Day: Intraday Patterns Explained — https://pomegra.io/wiki/volume-by-time-of-day-patterns/ — Mechanics. Intraday U-shaped volume distributions across major US exchanges.
- MarketsByte — Intraday Volatility by Trading Session Explained — https://marketsbyte.com/learn/technical-analysis/intraday-volatility-by-session/ — Mechanics. M-shaped crypto volatility profiles across London and NY session overlaps.
- Tom Espel — BTC and ETH display intraday seasonality — https://tomespel.com/p1.html — Empirical Evidence. Intraday volume concentration around the 16:00 London and NY fixes.
- Securities.io — Crypto’s Day-of-the-Week Effect May Be a Mirage — https://securities.io/crypto-day-of-the-week-effects-intraday-patterns/ — Empirical Evidence. Disproves broad weekday seasonality in favor of localized hourly windows.
- MLQuants — Are Day-of-the-Week Effects in Cryptocurrencies Real? — https://mlquants.substack.com/p/are-day-of-the-week-effects-in-cryptocurrencies — Empirical Evidence. Details the Sunday 23:00 UTC US retail positioning burst.
- Coinbase Help — What is the funding rate and why does it exist? — https://help.coinbase.com/en/derivatives/perpetual-style-futures/funding-rate — Mechanics. Exact formula for Coinbase continuous hourly funding calculations.
- FeeFlux — Funding Rate: What It Costs a Position (Binance 8-hour mechanism) — https://feeflux.com/en/articles/funding-rate-cost/ — Mechanics. Mechanics of 8-hour perpetual settlement cycles.
- Investopedia — Average True Range (ATR) — https://www.investopedia.com/terms/a/atr.asp — Technical Metric. Definition and calculation of causal ATR.
Next: Module 14 — Intraday Events · Companion: M13 Strategies — Session Filters