0DTE Trading Strategies — 5 Data-Driven Approaches With Live Examples
Five 0DTE strategies using real-time dealer positioning: gamma regime scalps, pin risk fades, expected move sells, and more. See when to enter, when to exit, and when to sit out.
Five 0DTE strategies using real-time dealer positioning: gamma regime scalps, pin risk fades, expected move sells, and more. See when to enter, when to exit, and when to sit out.
A 30DTE option and a 0DTE option share the same ticker but live in completely different universes. With hours - not weeks - until expiration, the Greeks behave differently:
This isn't theory - it creates real order flow. When dealers are short 50,000 SPY contracts at the 590 strike and SPY drops $1, they must sell hundreds of thousands of shares to stay hedged. That selling is not discretionary. It's mechanical. And it moves the tape.
The question for every 0DTE trader is: are dealers helping you or hurting you right now? That's what gamma regime, pin risk, and hedging estimates tell you.
Each strategy below uses specific fields from the Zero-DTE API endpoint. Here's a quick reference for the key signals:
| Signal | What It Tells You | Where to Find It |
|---|---|---|
| Gamma regime | Are dealers dampening moves (positive) or amplifying them (negative)? | regime.label |
| Gamma flip | The price where the regime changes - the key intraday pivot | regime.gamma_flip |
| Pin score | How likely is price to pin to a specific strike near close? (0–100) | pin_risk.pin_score |
| Expected move | How far the market expects price to travel by close | expected_move.remaining_1sd_dollars |
| Call wall / put wall | Intraday resistance and support from dealer gamma | levels.call_wall, levels.put_wall |
| Dealer hedging | How many shares dealers must trade for a given move | hedging.spot_up_1pct, etc. |
| Theta per hour | How fast premium is decaying right now | decay.theta_per_hour_remaining |
| 0DTE % of total GEX | Does 0DTE positioning dominate intraday, or is the full chain in charge? | exposures.pct_of_total_gex |
import requests
resp = requests.get(
"https://lab.flashalpha.com/v1/exposure/zero-dte/SPY",
headers={"X-Api-Key": "YOUR_API_KEY"}
)
d = resp.json()
print(f"Regime: {d['regime']['label']}")
print(f"Gamma flip: ${d['regime']['gamma_flip']}")
print(f"Pin score: {d['pin_risk']['pin_score']}/100")
print(f"Expected move: ±${d['expected_move']['remaining_1sd_dollars']:.2f}")
print(f"Walls: ${d['levels']['put_wall']} support - ${d['levels']['call_wall']} resistance")
print(f"0DTE dominance: {d['exposures']['pct_of_total_gex']:.0f}% of total GEX")
Condition: regime.label == "positive_gamma" and spot is between put wall and call wall.
When dealers are long gamma, they buy dips and sell rallies to stay delta-neutral. This creates a natural mean-reverting environment - every move toward the call wall gets sold, every dip toward the put wall gets bought. Price oscillates between the two walls.
The trade: Fade moves toward either wall. Buy SPY at the put wall, sell at the call wall - or use options: sell call spreads when price touches the call wall, sell put spreads when price touches the put wall.
regime = d["regime"]
levels = d["levels"]
price = d["underlying_price"]
if regime["label"] == "positive_gamma":
put_wall = levels["put_wall"]
call_wall = levels["call_wall"]
range_width = call_wall - put_wall
dist_to_put = (price - put_wall) / range_width
dist_to_call = (call_wall - price) / range_width
if dist_to_put < 0.2:
print(f"FADE: Near put wall ${put_wall} - dealers buying. Go long, target mid-range.")
elif dist_to_call < 0.2:
print(f"FADE: Near call wall ${call_wall} - dealers selling. Go short, target mid-range.")
else:
print(f"Range: ${put_wall}–${call_wall}. Wait for approach to a wall.")
The gamma fade works best in the first half of the session (9:45 AM – 1 PM ET) when the walls are well-established and gamma hasn't yet accelerated to extreme levels. By 2 PM, gamma acceleration can make the walls "softer" - dealers still defend them, but the hedging force relative to the move size weakens.
negative_gamma, exit regardless of P&L - the thesis is invalidated.SPY opens at $590.40. The API shows positive gamma regime with put wall at $588 and call wall at $593. At 10:30 AM, SPY dips to $588.20 - approaching the put wall. You buy SPY (or a $588 call) targeting $590.50 (mid-range). Dealers are buying at the put wall, providing natural support. SPY bounces to $590.80 by 11:15 AM. You take profit at $590.50 - $1.80/contract, defined risk, and the mechanics were in your favor the entire time.
vol_context.iv_ratio_0dte_7dte - if it's above 1.0, the market is pricing an event and the walls are less reliable. Also check the full-chain GEX levels - if the full-chain put wall is at $585 but the 0DTE put wall is at $588, the 0DTE wall has less structural support.
See these levels live for any symbol
Call wall, put wall, gamma flip, dealer hedging estimates - updated in real-time. Free account gets you started.
Condition: pin_risk.pin_score > 70 and time_to_close_hours < 2.
When massive OI is concentrated in a few strikes and expiration is approaching, price tends to get "pinned" to the magnet strike. Dealers continuously hedge around it, and max pain convergence pulls price toward the point of minimum option holder intrinsic value.
The trade: Sell an ATM straddle or tight iron butterfly at the magnet strike. You profit if price stays near the pin. The expected move narrows as close approaches - your risk shrinks with time.
pin = d["pin_risk"]
hours_left = d["time_to_close_hours"]
em = d["expected_move"]
if pin["pin_score"] > 70 and hours_left < 2:
magnet = pin["magnet_strike"]
distance = pin["distance_to_magnet_pct"]
print(f"PIN SETUP: Magnet at ${magnet} (pin score: {pin['pin_score']}/100)")
print(f"Spot is {distance:.2f}% from magnet")
print(f"Remaining expected move: ±${em['remaining_1sd_dollars']:.2f}")
print(f"OI concentration (top 3): {pin['oi_concentration_top3_pct']}%")
print(f"→ Sell butterfly/straddle at ${magnet}. Target pin into close.")
else:
print(f"Pin score: {pin['pin_score']}/100 - not high enough for pin play")
OI Concentration (30%) - how much OI sits in the top 3 strikes. Magnet Proximity (25%) - how close spot is to the highest-GEX strike. Time Remaining (25%) - pin strengthens as expiry approaches. Gamma Magnitude (20%) - higher gamma = stronger hedging force toward the pin.
Not all high pin scores lead to successful pins. The best setups have these characteristics:
pin_risk.max_pain equals pin_risk.magnet_strike, both the hedging force and the intrinsic value minimization point converge on the same level.The purest pin play is a 0DTE butterfly centered at the magnet strike. Buy the magnet strike straddle, sell the wings (magnet ± expected move). Max profit at the pin, defined risk on both sides. The expected move field gives you natural wing strikes - use upper_bound and lower_bound as your short strikes.
Alternatively, a short straddle at the magnet works but carries undefined risk. Only appropriate if you're comfortable managing gamma risk near expiry. The remaining_1sd_dollars field tells you the breakeven range - if it's $1.50 and the straddle collects $2.00, you have a cushion.
Condition: regime.label == "negative_gamma" and exposures.pct_of_total_gex > 50.
Negative gamma is the opposite of Strategy 1. Dealers are short gamma - they must sell into drops and buy into rallies, amplifying moves in both directions. The market trends, doesn't mean-revert. Fading is dangerous here.
The trade: Trade breakouts. Buy momentum in the direction of the move. Use the gamma flip as your trigger - a break below the flip accelerates downside as dealers switch from buyers to sellers. Use the hedging object to gauge how much fuel is behind the move.
regime = d["regime"]
exp = d["exposures"]
if regime["label"] == "negative_gamma" and exp["pct_of_total_gex"] > 50:
flip = regime["gamma_flip"]
price = d["underlying_price"]
print(f"⚠ NEGATIVE GAMMA - dealers amplify moves")
print(f"Gamma flip: ${flip}")
print(f"Spot: ${price} ({'below' if price < flip else 'above'} flip)")
# How much selling on a 1% drop?
hedge_down = d["hedging"]["spot_down_1pct"]
print(f"If SPY drops 1%: dealers {hedge_down['direction']} {abs(hedge_down['dealer_shares_to_trade']):,} shares")
print(f" Notional: ${abs(hedge_down['notional_usd']):,.0f}")
print(f"→ Trade breakouts, not mean reversion. Watch the flip.")
The hedging estimates tell you the size of the flow. If a 1% down move forces dealers to sell 300,000+ shares ($180M+ notional), that's significant order flow - enough to move the tape further. This is the feedback loop that creates trending 0DTE days.
The most powerful negative gamma signal is a break through the gamma flip. Above the flip, dealers are net long gamma (dampening). Below it, they're net short (amplifying). The transition point is where the regime changes - and it often creates a cascade:
Cross-reference the 0DTE gamma flip with the full-chain key levels endpoint - if the 0DTE flip is at $589 and the full-chain put wall is at $585, a break through $589 has $4 of runway before structural support.
dealer_shares_to_trade starts shrinking (dealers have already hedged most of their exposure), the move is losing fuel. Take remaining profit.Track dealer gamma positioning in real-time
Know whether dealers are amplifying or dampening the move - before you enter. Sign up free.
Condition: regime.label == "positive_gamma", decay.gamma_acceleration > 2, and time_to_close_hours > 2.
0DTE theta decay is non-linear. The optimal window for premium selling is when gamma acceleration has kicked in (premium is decaying fast) but there's still enough time for mean reversion to protect you if price moves against. Typically 1–3 PM ET.
The trade: Sell an iron condor with short strikes at the call wall and put wall. The positive gamma regime means dealers are actively pushing price back toward center. Time decay accelerates in your favor. Your defined risk caps losses if you're wrong.
decay = d["decay"]
regime = d["regime"]
hours_left = d["time_to_close_hours"]
if regime["label"] == "positive_gamma" and decay["gamma_acceleration"] > 2 and hours_left > 2:
levels = d["levels"]
em = d["expected_move"]
print(f"THETA HARVEST window open")
print(f"Gamma acceleration: {decay['gamma_acceleration']}x vs 7DTE")
print(f"Theta/hour: ${abs(decay['theta_per_hour_remaining']):,.0f}")
print(f"Time remaining: {hours_left:.1f}h")
print(f"")
print(f"Iron condor strikes:")
print(f" Short put: ${levels['put_wall']} (put wall)")
print(f" Short call: ${levels['call_wall']} (call wall)")
print(f" Expected range: ${em['lower_bound']:.2f}–${em['upper_bound']:.2f}")
print(f" Charm: {decay['charm_description']}")
0DTE theta follows a characteristic intraday curve. Understanding the shape helps you time entries:
| Time (ET) | Theta/Hour (approx.) | Gamma Accel. | Notes |
|---|---|---|---|
| 9:30 AM | 1× | 1.5–2× | Slow bleed. Gamma risk not worth the theta. |
| 11:00 AM | 1.3× | 2–2.5× | Starting to accelerate. Still early. |
| 1:00 PM | 2× | 2.5–3.5× | Sweet spot begins. Decay accelerating, gamma manageable. |
| 2:30 PM | 3.5× | 4–5× | Peak theta harvest zone. Take entries here if not already in. |
| 3:30 PM | 5–8× | 6–10× | Massive theta but gamma risk extreme. Closing, not entering. |
| 3:55 PM | 15×+ | 20×+ | Last 5 minutes. Do not enter. Close everything. |
The charm_description field tells you the secondary effect: whether time decay is pushing dealers to buy or sell. "time_decay_dealers_buy" is ideal for theta harvesting - it means charm is supportive, reducing the chance of a late-day selloff from dealer de-hedging.
Use the API's levels and expected_move together:
put_wall. Dealers are buying here - natural support.call_wall. Dealers are selling here - natural resistance.expected_move bounds. If the expected move extends beyond your short strikes, you're selling inside the expected range - that's a losing proposition.Condition: vol_context.iv_ratio_0dte_7dte > 1.0 and regime is positive gamma.
When 0DTE IV exceeds 7DTE IV, the market is pricing a same-day event - FOMC, CPI, unexpected news. The vol premium is elevated in the front expiration specifically. If the event passes without a large move (or the event hasn't happened yet and vol is bid purely on fear), that premium collapses.
The trade: Sell 0DTE premium after the event (or when the vol spike is clearly exhausted). The vanna_interpretation field tells you what happens when vol drops: if it says vol_down_dealers_buy, a vol crush triggers dealer buying - supportive for longs.
vc = d["vol_context"]
ratio = vc["iv_ratio_0dte_7dte"]
if ratio > 1.0 and d["regime"]["label"] == "positive_gamma":
print(f"VOL SPIKE - 0DTE IV ({vc['zero_dte_atm_iv']}%) > 7DTE IV ({vc['seven_dte_atm_iv']}%)")
print(f"Ratio: {ratio:.2f}x - event premium in front expiry")
print(f"Vanna: {vc['vanna_interpretation']}")
if "vol_down_dealers_buy" in vc.get("vanna_interpretation", ""):
print(f"→ If vol collapses, dealers BUY - supportive for longs")
print(f"→ Sell 0DTE straddle or strangle after event passes")
Cross-reference with IV Crush mechanics - the same principle applies to 0DTE event premium as it does to earnings. The vol premium doesn't survive the event.
| IV Ratio (0DTE / 7DTE) | Interpretation | Action |
|---|---|---|
| < 0.85 | 0DTE is cheap relative to term structure. Normal conditions, no event premium. | No vol edge - use other strategies (gamma fade, theta harvest). |
| 0.85 – 1.0 | Slightly elevated front-end. Could be elevated intraday realized vol. | Monitor but don't act on vol alone. |
| 1.0 – 1.15 | Event premium. Market pricing a same-day catalyst (FOMC minutes, Fed speaker, data release). | Wait for event to pass, then sell premium if regime is positive gamma. |
| > 1.15 | Significant event premium or intraday stress. Elevated premium but also elevated risk. | Sell after the event only. Do not sell ahead of the catalyst. |
| > 1.3 | Extreme. Major uncertainty (FOMC decision, CPI surprise, geopolitical shock). | Sit out. Risk/reward for premium selling is unfavorable. |
The vanna_interpretation field tells you what happens to dealer hedging when vol itself changes. This matters because after an event, vol usually drops:
vol_down_dealers_buy - if vol drops, dealers need to buy stock to stay hedged. This is supportive for longs after the event passes.vol_up_dealers_sell - if vol spikes, dealers sell stock. This amplifies the downside during the stress event itself.For a deeper understanding of how vanna and charm drive intraday dealer flows, read the Vanna & Charm second-order Greeks guide.
Negative aggregate vanna (the common case for 0DTE) means a vol drop pushes delta higher - dealers must buy shares to hedge. This creates a tailwind for longs after vol events resolve.
Explore real-time Greeks for any option chain
Delta, gamma, vanna, charm - per strike, per expiry. Try it in the interactive playground.
Standard Greeks textbooks assume weeks or months to expiry. On 0DTE, the same variables behave differently because \(T\) is measured in hours, not days. Here's what changes:
| Greek | Normal (30DTE) | 0DTE Behavior | Why It Matters |
|---|---|---|---|
| Gamma | Moderate, stable | 3-10x higher, spikes near close | Every $1 move forces massive dealer hedging. This IS the 0DTE edge. |
| Theta | Slow, linear-ish | Non-linear: 1x at open, 5x by 3 PM, 15x by 3:55 PM | Premium sellers get paid the most in the last 2 hours - but gamma risk peaks too. |
| Vanna | Small, ignorable | Significant intraday - vol moves change delta meaningfully | A VIX spike during 0DTE forces dealer re-hedging. Vanna tells you which direction. |
| Charm | Tiny daily effect | Accelerates hourly - delta decays visibly between 2-4 PM | OTM options lose delta fast. Dealers must unwind hedges, creating directional flow. |
The key insight: on 0DTE, second-order Greeks (vanna, charm) have intraday effects that normally take days to play out. A FlashAlpha stock dashboard shows you all four exposure metrics in real-time so you can see regime shifts as they happen.
Market makers don't have opinions. They have inventory. When a retail trader buys a 0DTE call, the market maker sells it and immediately delta-hedges by buying shares. As the underlying moves, they must continuously adjust - and on 0DTE, the adjustments are enormous because gamma is extreme.
This creates mechanical levels that have nothing to do with technical analysis:
These levels shift throughout the day as new contracts trade and OI changes. The FlashAlpha API updates them in real-time - check the SPY page during market hours to see the current gamma flip, call wall, and put wall.
Track dealer positioning live for SPY, QQQ, TSLA & 6,000+ symbols
Gamma flip, call/put walls, hedging estimates, regime detection - all from one API call.
0DTE isn't always the right play. Different expirations suit different market conditions and strategies:
| Attribute | 0DTE | Weekly (5-7 DTE) | Monthly (30+ DTE) |
|---|---|---|---|
| Gamma | Extreme (3-10x) | Moderate | Low |
| Theta decay | Non-linear, accelerates | Faster than monthly | Slow, predictable |
| Best for | Intraday scalping, gamma fades, pin plays | Swing trades, earnings plays | Portfolio hedging, income strategies |
| Dealer impact | Dominant - drives intraday levels | Moderate - blended with full chain | Background - sets structural levels |
| Risk profile | High gamma risk, time-critical | Balanced | Vega-dominant, time-forgiving |
| Data needed | 0DTE endpoint + levels | GEX per strike + levels | Volatility analytics + term structure |
Use the volatility analytics endpoint to compare IV term structure across expirations - when 0DTE IV is cheap relative to weekly (IV ratio < 0.85), there's no edge in selling 0DTE premium. Switch to weekly or monthly strategies instead.
Not every day is a 0DTE day. The API tells you when conditions are unfavorable:
no_zero_dte: true - no 0DTE expiry today (SPY only trades 0DTE Mon/Wed/Fri)pct_of_total_gex < 30 - 0DTE isn't driving intraday price action; the full chain dominates and 0DTE levels are unreliablepin_score < 30 and regime == "undetermined" - no clear signal; OI is diffuse, no dominant strike, no clear regimeiv_ratio_0dte_7dte > 1.3 - extreme event premium; the market expects a large move and your risk/reward on premium selling is unfavorabledef should_trade_0dte(data):
if data.get("no_zero_dte"):
return False, "No 0DTE expiry today"
if data["exposures"]["pct_of_total_gex"] < 30:
return False, "0DTE isn't driving intraday - full chain dominates"
if data["pin_risk"]["pin_score"] < 30 and data["regime"]["label"] == "undetermined":
return False, "No clear signal - sit out"
if data["vol_context"]["iv_ratio_0dte_7dte"] > 1.3:
return False, "Extreme event premium - too risky for premium selling"
return True, data["regime"]["label"]
tradeable, reason = should_trade_0dte(d)
print(f"Trade 0DTE? {'Yes' if tradeable else 'No'} - {reason}")
Here's how the five strategies map to market conditions:
Decision tree: gamma regime determines the framework, then pin risk, vol context, and dealer levels select the specific strategy.
The flow object provides volume and OI context that complements the strategy signals. Here's how to read it:
| Metric | Reading | Implication |
|---|---|---|
pc_ratio_volume < 0.5 | Heavy call buying | Bullish flow. If dealers are short these calls, it increases call-side gamma and can push the call wall higher. |
pc_ratio_volume > 1.0 | Heavy put buying | Bearish or protective flow. Increases put-side gamma and can lower the put wall. |
volume_to_oi_ratio > 1.0 | Intraday volume exceeds overnight OI | Active day-trading. The 0DTE levels are being established by today's flow, not yesterday's positioning. More volatile. |
volume_to_oi_ratio < 0.3 | Low intraday activity | Quiet day. Positions are from overnight. Walls and pin risk are more stable but moves are less likely. |
flow = d["flow"]
print(f"Volume: {flow['total_volume']:,} ({flow['call_volume']:,} calls / {flow['put_volume']:,} puts)")
print(f"P/C ratio (vol): {flow['pc_ratio_volume']:.3f}")
print(f"Volume/OI ratio: {flow['volume_to_oi_ratio']:.3f}")
if flow["pc_ratio_volume"] < 0.5:
print("→ Heavy call flow - watch for call wall to shift higher")
elif flow["pc_ratio_volume"] > 1.0:
print("→ Heavy put flow - defensive positioning, put wall may shift lower")
The strikes array gives you per-strike granularity: GEX, DEX, OI, volume, IV, and Greeks for each 0DTE strike. This is the raw data behind the call wall, put wall, and magnet strike - you can build your own level analysis from it.
import pandas as pd
strikes_df = pd.DataFrame(d["strikes"])
strikes_df["net_oi"] = strikes_df["call_oi"] + strikes_df["put_oi"]
# Find the strikes with the most gamma impact
top_gex = strikes_df.nlargest(5, "net_gex")[["strike", "net_gex", "call_oi", "put_oi"]]
print("Top 5 GEX Strikes:")
print(top_gex.to_string(index=False))
# GEX by strike for charting
print("\nGEX Profile:")
for _, row in strikes_df.iterrows():
bar = "+" * int(abs(row["net_gex"]) / 50_000_000)
sign = "+" if row["net_gex"] > 0 else "-"
print(f" ${row['strike']:>6.0f} {sign}{bar} ({row['net_gex']:>+12,.0f})")
The per-strike data also lets you identify gamma clusters - adjacent strikes where both calls and puts have heavy OI. These clusters create zones of support/resistance that are more robust than single-strike walls.
0DTE strategies have compressed timelines but the same capacity for loss. Rules that matter:
pin_score, regime, and expected_move all update in real-time as conditions change.The Zero-DTE endpoint is available on the Growth plan ($299/mo or $2,868/yr billed annually, 2,500 requests/day) and Alpha plan ($1,499/mo or $14,388/yr billed annually, unlimited requests). Every field referenced in this guide comes from a single API call.
Get API Access Zero-DTE API Docs Try It in the Playground
by Tomasz Dobrowolski
by Tomasz Dobrowolski
by Tomasz Dobrowolski
Get tick-by-tick visibility into market shifts with full-chain analytics streaming in real time.
Screen millions of option pairs per second using your custom EV rules, filters, and setups.
Instantly send structured orders to Interactive Brokers right from your scan results.