Contract Expiry Week Roll Cycle Fade
Abstract: This strategy trades the mean reversion created by concentrated institutional roll flows during the final 5-10 trading days before futures contract expiration. The idea is that portfolio liquidations and calendar spread executions push the expiring contract away from fair value during known time windows, creating a temporary dislocation that reverts as the roll completes.
Why this might work:
Futures contracts have fixed expiration dates. As each contract approaches expiry, market participants who hold positions, hedge funds, corporate treasurers, pension funds, must decide whether to close them or roll into the next contract. This creates predictable flows. CME Group contract specifications document that open interest migrates systematically from the front month to the back month in the final 1-2 weeks before expiration [1]. Volume concentrates in time windows aligned with institutional trading infrastructure; practitioner convention holds that most calendar rolls execute between 10:00-11:30 AM ET in equity index futures, when risk management teams process batch roll orders across large portfolios [conventional observation].
This forced migration differs from organic trading. A pension fund managing $1 billion cannot trade 10,000 ES contracts without moving the market; the order flow itself distorts price temporarily. Academic research on commodity futures has documented that contracts approaching expiry exhibit elevated volatility and predictable basis changes relative to the back month [2]. The empirical pattern is most pronounced in the 3-5 days immediately before expiry, when the penalty for failing to roll, assignment to physical delivery, cash settlement, or default to spot price, becomes imminent [3].
The mechanical advantage is that these flows are temporary. Once the bulk of the roll is complete and open interest has migrated to the next month, price pressure reverses. A strategy that fades extreme moves during the peak flow window and closes by end of session can capture this reversion without taking overnight roll risk [established mechanic].
The rules:
- Instrument: ES (E-mini S&P 500) quarterly futures, or any contract with clearly known expiration dates
- Timeframe: Daily bars; entries triggered only during a specific 60-minute intraday window (10:00-11:00 AM ET)
- Entry condition: The strategy enters only on days 1-5 of the contract's expiry week. Within that window, it enters only if the current price has deviated more than 1.5 times the 14-day Average True Range (ATR) away from its 10-day simple moving average (MA), AND the current bar timestamp falls within 10:00-11:00 AM ET.
- Entry direction: If price is below the 10-day MA at signal time, enter long (fading the downward move). If price is above the MA, enter short (fading the upward move).
- Initial stop: Long entries stop 2.0 * ATR(14) below entry price; short entries stop 2.0 * ATR(14) above entry price.
- Profit target: Long entries target 1.5 * ATR(14) above entry price; short entries target 1.5 * ATR(14) below entry price.
- Exit time: Force-close all open positions at 4:00 PM ET (end of regular trading hours), regardless of profit or loss. This prevents overnight roll chaos and gapping.
- Position size: 1 contract per signal for testing. Scale to risk tolerance per account.
- Expected trade frequency: Approximately 4-10 trades per year (one expiry per quarter, 1-2 setups per expiry week).
The code:
//@version=6
strategy("Contract Roll Cycle Fade", overlay=true, default_qty_type=strategy.fixed, default_qty_value=1,
commission_type=strategy.commission.percent, commission_value=0.002, slippage=1)
// Input parameters
atr_period = input(14, "ATR Period")
ma_period = input(10, "MA Period")
entry_atr_mult = input(1.5, "Entry ATR Multiple")
stop_atr_mult = input(2.0, "Stop ATR Multiple")
profit_atr_mult = input(1.5, "Profit Target Multiple")
roll_hour_start = input(10, "Roll Window Start Hour (ET)")
roll_hour_end = input(11, "Roll Window End Hour (ET)")
expiry_dom_start = input(13, "Expiry Month Day Start")
expiry_dom_end = input(22, "Expiry Month Day End")
// Indicators
atr_val = ta.atr(atr_period)
sma_val = ta.sma(close, ma_period)
price_dev = math.abs(close - sma_val)
// Timing conditions
is_expiry_period = dayofmonth(time) >= expiry_dom_start and dayofmonth(time) <= expiry_dom_end
is_roll_window = hour >= roll_hour_start and hour < roll_hour_end
is_signal_bar = price_dev > atr_val * entry_atr_mult and is_expiry_period and is_roll_window
// Entry signals (fade the move back to MA)
long_entry = is_signal_bar and close < sma_val
short_entry = is_signal_bar and close > sma_val
// Execute long trades
if long_entry and strategy.position_size == 0
stop_price = close - atr_val * stop_atr_mult
target_price = close + atr_val * profit_atr_mult
strategy.entry("Long", strategy.long)
strategy.exit("Long Exit", "Long", stop=stop_price, limit=target_price)
// Execute short trades
if short_entry and strategy.position_size == 0
stop_price = close + atr_val * stop_atr_mult
target_price = close - atr_val * profit_atr_mult
strategy.entry("Short", strategy.short)
strategy.exit("Short Exit", "Short", stop=stop_price, limit=target_price)
// Force exit all positions at end of RTH session
if hour == 16 and minute == 0
strategy.close_all()
// Plot MA and entry bands
plot(sma_val, "MA", color.blue, 2)
plot(sma_val + atr_val * entry_atr_mult, "UpperBand", color.green, 1)
plot(sma_val - atr_val * entry_atr_mult, "LowerBand", color.red, 1)
How the code works:
Lines 6-14 define all tunable parameters, allowing easy testing of different expiry windows, ATR periods, and trading hours.
Lines 17-19 compute the 14-period ATR, the 10-period simple moving average, and the absolute deviation of current price from the MA.
Lines 22-24 check three timing conditions: whether the current day falls within the expiry month window (e.g., between the 13th and 22nd of the month), whether the current hour is within 10:00-11:00 AM ET, and whether price deviation exceeds 1.5 ATR. All three must be true to generate a signal.
Lines 27-28 determine entry direction: if the signal fires and close is below the MA, flag a long entry; if close is above the MA, flag a short entry.
Lines 31-37 execute long entry: when a long signal fires and no position is open, enter long, set a stop 2 ATR below entry, and set a profit target 1.5 ATR above entry. The strategy.exit() call ensures those levels are active for this trade.
Lines 40-46 execute short entry: when a short signal fires and no position is open, enter short, set a stop 2 ATR above entry, and set a profit target 1.5 ATR below entry.
Line 49-50 close all open positions at 16:00 (4:00 PM ET), eliminating overnight roll risk regardless of current P&L.
Lines 53-55 plot the moving average and entry bands so the trader can visually verify signal conditions on the chart.
Testing it honestly:
Sample size is critical. This strategy generates only 4-10 trades per year, meaning a 5-year backtest yields 20-50 total trades. At this sample size, win rate is nearly meaningless: a 60% observed win rate could easily reflect 50% true probability plus noise. A single large loss on any expiry can erase several small wins.
Run backtests on 10+ calendar years of ES daily data. Split the data 70/30: optimize parameters on the first 7 years (in-sample), then test without re-optimization on the most recent 3 years (out-of-sample). If out-of-sample results diverge sharply from in-sample (e.g., 55% win rate in-sample but 45% out-of-sample), suspect parameter overfitting.
Set realistic costs: ES typically has a 0.5-1.0 tick bid-ask spread (1-2 ticks round-trip). The code assumes 0.2% commission (about 1 tick for ES) plus 1 tick slippage. If actual execution costs are wider, especially during expiry chaos, the strategy's edge disappears.
Verify expiry date handling carefully. ES contracts expire on the third Friday of March, June, September, and December; other contract families have different schedules. The input parameters expiry_dom_start and expiry_dom_end must be set correctly for each contract. Testing across different months will show whether the strategy works consistently or depends on specific calendar effects (e.g., December expirations may have different behavior due to year-end flows).
Confirm that stops and targets actually execute at the specified prices during backtesting. TradingView's backtester slips orders by the specified amount, but during volatile periods, actual slippage may exceed assumptions.
Limitations:
The sample size issue is fundamental. Forty trades across five years is insufficient to distinguish skill from luck at any useful confidence level. A trader would need 30+ years of clean data to generate 150-200+ trades and establish statistical significance.
The expiry date approximation using day-of-month is crude. Real expiry dates shift each year (the third Friday is never the same calendar day). The code must be manually adjusted per year, or a trader must use a more sophisticated calendar check (e.g., a data source that explicitly marks expiry dates).
Intraday timing is assumed to be the same every year (10-11 AM ET). In reality, roll timing varies with market conditions. During periods of high market stress or unusual volatility, institutional roll flows may shift to different hours, or be spread across multiple days. The strategy provides no adaptation to these shifts.
Open interest data is not directly accessible in Pine Script; the code assumes that a deviation from the moving average is correlated with roll pressure. This is a heuristic, not a direct measurement. A trader testing this strategy should manually verify that volume and volatility do indeed spike during expiry weeks on the specific exchange and platform used.
Liquidity deteriorates as expiry approaches. On the final 1-2 trading days before expiration, bid-ask spreads widen and order-fill latency increases. The strategy's assumed slippage (1 tick) may be optimistic in these final days, turning breakeven or marginal trades into losses.
The strategy assumes mean reversion: that extreme moves in expiry week revert by end of day. This fails when expiry weeks coincide with major macroeconomic events (e.g., Fed announcements, jobs reports) that create genuine directional bias. No filter is included to detect or avoid these regime changes.
Cost sensitivity is high. At a 55% win rate with 1.5:2.0 risk-reward ratio, the strategy generates an edge of roughly (0.55 * 1.5), (0.45 * 2.0) = 0.825, 0.9 = -0.075 in ratio terms (a small loss). This means even small increases in commission or slippage eliminate all edge.
No evidence is provided that the specific parameters (1.5 ATR entry, 2.0 ATR stop, 1.5 ATR target, 10-day MA) are optimal. These are illustrative. Any trader must conduct their own parameter grid search, which introduces severe overfitting risk: testing 100 parameter combinations on a dataset with only 40-80 trades will inevitably surface a set of parameters that worked in the past but will not generalize to future data.
Finally, this strategy is untested. No backtest results, win rates, or profit targets are provided. It is presented as a mechanical framework for a trader to evaluate independently.
Key definitions
Average True Range (ATR): A volatility measure that calculates the average of the true range (the greatest of: high minus low, high minus prior close, or prior close minus low) over a specified number of periods, typically 14. ATR is used to set stop and target distances that scale with market volatility.
Calendar roll: The process by which traders close a position in an expiring futures contract and open an equivalent position in the next contract month, maintaining the same underlying exposure while avoiding delivery or cash settlement.
Expiry week: The week in which a futures contract reaches its final trading day and expires. For quarterly equity index contracts, this is the week containing the third Friday of March, June, September, or December.
Mean reversion: The statistical tendency for prices that move far from their average to drift back toward that average over time, the basis for fade or countertrend strategies.
Open interest: The total number of outstanding long and short futures contracts that have not been closed or settled. Open interest typically declines as a contract approaches expiry as traders exit or roll positions.
Stop loss: A predetermined price level at which an open position is automatically closed to limit downside risk. A long stop is below entry price; a short stop is above entry price.
Regular Trading Hours (RTH): The official session for a futures exchange, typically 9:30 AM to 4:00 PM ET for equity index futures. Positions opened during RTH can be held overnight; this strategy closes before RTH ends to avoid overnight roll gap risk.
Figures
References
-
CME Group, "E-mini S&P 500 Futures Contract Specifications", CME Globex. Https://www.cmegroup.com/markets/equities/sp-500/sp-500-emini.contractSpecs.html
-
Geman, H., "Commodities and Commodity Derivatives: Modeling and Pricing for Agriculals, Metals and Energy", John Wiley & Sons (2005). SSRN: https://ssrn.com/abstract=706040
-
Routledge, B. R., Seppi, D. J., and Spatt, C. S., "Equilibrium Forward Curves for Commodities", Journal of Finance Vol. 55, No. 3 (2000). Https://doi.org/10.1111/0022-1082.00246
-
Bessembinder, H. And Seguin, P. J., "Futures-Trading Activity and Stock Price Volatility", Journal of Finance Vol. 47, No. 5 (1992). Https://doi.org/10.2307/2329061
-
SEC, "Investor Alert: Options Expiration Weeks", U.S. Securities and Exchange Commission. Https://www.sec.gov/investor/alerts/optionexpiration.pdf
Educational research on historical data only. Not investment advice, not a signal, and never a performance promise. Past results do not predict future performance. Every reference is link-verified before publication and every paper is re-audited weekly against the library's editorial standard.
Last reviewed by the PropLedger research pipeline: 2026-09-13. Educational research on historical data, not financial advice.
Keep reading
Failed Breakout Fade
A complete, testable trading strategy: a failed-breakout fade strategy: defining the failure objectively, the trap mechanics behind it, and why the entry is easier to describe than to time. Exact rules, full Pine Script code, and an honest reading of the evidence.
RSI Divergence: Mechanical Definition and Null-Hypothesis Testing
A complete, testable trading strategy: an RSI divergence strategy subjected to an honest test: defining divergence mechanically, then measuring it against a matched null. Exact rules, full Pine Script code, and an honest reading of the evidence.
Prior-Day Breakout with Stop Clustering
A complete, testable trading strategy: a prior-day high and low breakout strategy: why levels where stops cluster behave differently from arbitrary recent extremes. Exact rules, full Pine Script code, and an honest reading of the evidence.