Edges
Volume vs Open Interest: Powering an Options Alert Scanner
Volume vs open interest is the first thing a serious options trader checks, because together they answer one question: is the action at this strike fresh money or just noise? Volume is how many contracts traded today; open interest is how many contracts are still open and outstanding. This guide explains the difference, shows why their ratio is the real unusual-activity signal, and then builds a complete scoring engine — the kind that powers an "unusual whales"-style scanner — that ranks any contract into a STRONG, WATCH, or INTERESTING alert tier.
Volume vs open interest: the core difference
These two numbers sit side by side in every chain, and beginners constantly confuse them. The distinction is simple once you anchor it:
| Volume | Open Interest | |
|---|---|---|
| Measures | Contracts traded TODAY | Contracts still OPEN |
| Resets | Every day at the open | Never — it's cumulative |
| Tells you | Today's activity / interest | Total existing positions |
| Direction | Can rise all day | Updated once overnight |
Volume is a speedometer — it reads today's pace and zeros out tomorrow. Open interest (OI) is an odometer — it accumulates every position that has been opened and not yet closed. A strike can have huge open interest (lots of standing positions) but tiny volume today (nobody trading), or the reverse.
The volume/OI ratio is the real signal
Neither number means much alone. The signal is the ratio:
This single number tells you whether today's trading is routine churn or something new arriving:
When volume exceeds open interest (ratio > 1), more contracts changed hands today than existed at the open — impossible unless brand-new positions are being created. When the ratio crosses 3, it is a loud signal: roughly three times the standing base traded in a single session. That is the footprint of large, directional, intentional buying. Plug in real numbers below and watch the verdict flip across the thresholds.
Fresh positioning vs liquidation
A high ratio tells you something happened — it does not tell you what. This is the nuance most guides skip. A spike in volume relative to OI can mean two opposite things:
- Fresh positioning — new buyers opening contracts. Tomorrow's OI will rise to absorb today's volume. This is the bullish-for-calls / bearish-for-puts signal scanners hunt.
- Liquidation — existing holders closing out. Tomorrow's OI will fall. Same volume spike, opposite meaning.
You cannot tell them apart from the ratio alone in real time. That is exactly why a good scanner never trades the ratio by itself — it stacks the ratio with price direction and premium movement to confirm new money is flowing in the same direction. Unusual volume that lines up with a rising stock and a rising premium is the smart-money flow signal worth acting on.
From signal to score: the alert detector
So how do professionals turn these scattered clues into one decision? They score every contract. Instead of one make-or-break rule, each bullish clue adds points and each red flag subtracts them. A contract that stacks many confirmations rises to the top; a contract with one good trait but a fatal flaw (a wide, illiquid spread) gets filtered out.
The engine runs in five stages: pull the raw contract data, compute derived metrics (ratios, percentage changes, velocity), score each factor, confirm direction, then map the total to an alert tier. Here is the complete, commented engine:
# =====================================================================
# EXPERT OPTION ALERT DETECTOR
# Scores every contract in a chain for unusual activity and assigns
# an alert tier. Higher score = more confirmations stacked together.
# =====================================================================
for contract in option_chain:
# -----------------------------------------------------------------
# 1. Raw contract data (straight from the feed)
# -----------------------------------------------------------------
symbol = contract.symbol
type = contract.type # "CALL" or "PUT"
bid = contract.bid
ask = contract.ask
volume = contract.volume # contracts traded TODAY
oi = max(contract.open_interest, 1) # avoid divide-by-zero
delta = abs(contract.delta) # use magnitude (puts are negative)
gamma = contract.gamma
theta = contract.theta
stock_now = underlying.price_now
stock_5m_ago = underlying.price_5m_ago
premium_now = (bid + ask) / 2 # mid price
premium_5m_ago = contract.premium_5m_ago
# -----------------------------------------------------------------
# 2. Derived metrics (the numbers that actually carry signal)
# -----------------------------------------------------------------
spread_pct = (ask - bid) / premium_now * 100 # liquidity cost
volume_oi_ratio = volume / oi # fresh-money gauge
premium_change_pct = (premium_now - premium_5m_ago) / premium_5m_ago * 100
stock_change_pct = (stock_now - stock_5m_ago) / stock_5m_ago * 100
premium_velocity = premium_change_pct / 5 # %% move per minute
# -----------------------------------------------------------------
# 3. Direction confirmation
# A real alert needs price AND premium moving the same way.
# -----------------------------------------------------------------
bullish_call = (type == "CALL"
and stock_change_pct > 0
and premium_change_pct > 0)
bearish_put = (type == "PUT"
and stock_change_pct < 0 # stock falling
and premium_change_pct > 0) # put gaining value
direction_confirmed = bullish_call or bearish_put
# -----------------------------------------------------------------
# 4. Expert scoring — each factor adds or subtracts points
# -----------------------------------------------------------------
score = 0
# --- Unusual activity (the volume/OI core) ---
if volume > 1000: score += 10 # liquid enough to matter
if volume_oi_ratio > 1: score += 15 # volume tops open interest
if volume_oi_ratio > 3: score += 25 # fresh positioning (stacks)
# --- Premium movement (is the option itself running?) ---
if premium_change_pct > 10: score += 10
if premium_change_pct > 20: score += 20 # stacks on top of the +10
if premium_velocity > 3: score += 15 # moving FAST, not drifting
# --- Liquidity (reward tight, punish wide) ---
if spread_pct < 5: score += 15 # easy in / easy out
elif spread_pct > 10: score -= 20 # spread tax — avoid
# --- Day-trading delta zone ---
if 0.30 <= delta <= 0.70: score += 15 # responsive but not too deep
# --- Gamma accelerator (delta picks up speed near the money) ---
if gamma > 0.03: score += 10
if gamma > 0.05: score += 15 # stacks on top of the +10
# --- Direction confirmation bonus ---
if direction_confirmed: score += 20
# --- Theta risk penalty (decay eating the premium) ---
if abs(theta) > premium_now * 0.25: score -= 10
# -----------------------------------------------------------------
# 5. Alert tier — map the total score to a label
# -----------------------------------------------------------------
if score >= 85: alert_level = "🔥 STRONG ALERT"
elif score >= 70: alert_level = "⚠️ WATCH ALERT"
elif score >= 55: alert_level = "👀 INTERESTING"
else: continue # below threshold — skip silently
# -----------------------------------------------------------------
# 6. Send the alert
# -----------------------------------------------------------------
send_alert({
"symbol": symbol,
"type": type,
"strike": contract.strike,
"expiry": contract.expiry,
"score": score,
"alert_level": alert_level,
"premium_now": premium_now,
"premium_change_pct": premium_change_pct,
"volume": volume,
"open_interest": oi,
"volume_oi_ratio": volume_oi_ratio,
"spread_pct": spread_pct,
"delta": delta,
"gamma": gamma,
"stock_change_pct": stock_change_pct,
})
The next sections unpack why each block earns the points it does.
Scoring unusual activity
The volume and OI block is the heart of the engine. Note how the volume/OI rules stack: a ratio above 3 fires both the > 1 rule (+15) and the > 3 rule (+25) for +40 total. That is intentional — a ratio of 3.5 is far more meaningful than 1.1, so it should score far higher.
The bare volume > 1000 check is a liquidity floor — it stops the scanner from flagging a thinly-traded strike where five contracts created a misleading ratio.
Scoring premium movement
Volume tells you contracts are trading; premium change tells you traders are paying up. A rising premium confirms demand is real, not passive.
Velocity is the subtle one. premium_velocity = premium_change_pct / 5 converts a 5-minute change into a per-minute pace. A premium up 16% over five minutes (velocity 3.2) is a far stronger signal than the same 16% bled out over an hour — it means buyers are aggressive right now.
Scoring liquidity and the spread tax
Liquidity is the only factor that can go negative on its own, and for good reason. A wide bid/ask spread is a hidden tax you pay on entry and exit:
A contract can ace every other test and still be untradeable if the spread is 12% wide — you would lose a chunk the instant you enter. The −20 penalty deliberately drags those contracts below the alert threshold no matter how unusual the volume looks.
The Greek filters: delta, gamma, theta
The same Greeks you decoded in how to read an option chain become quality filters here:
- Delta 0.30–0.70 (+15) — the day-trading sweet spot. Responsive to the underlying, but not so deep ITM that it costs a fortune and stops accelerating.
- Gamma > 0.03 (+10), > 0.05 (+15, stacks) — rewards contracts whose delta will accelerate as the move develops. High gamma is the near-the-money kicker.
- Theta penalty (−10) — fires when
abs(theta) > premium_now × 0.25, i.e. decay is burning more than a quarter of the premium per day. This protects you from buying a fast-bleeding contract right before expiration.
The theta rule is what keeps the scanner honest near expiry: a 0-DTE contract can look explosive on volume and premium, but if it is shedding 30% of its value per day, the engine docks it.
Alert tiers: STRONG, WATCH, INTERESTING
The final step maps the total to a tier. Anything under 55 is dropped silently — no alert, no noise.
Because the bullish rules stack, a textbook setup — unusual ratio, fast premium, tight spread, ATM delta, high gamma, confirmed direction — easily clears 85 and several points beyond. That headroom is fine; the tiers are floors, not caps. Run a live contract through the full engine below and watch each rule light up:
Developer notes: the data you actually need
To run this engine you do not need an expensive platform — you need a feed that delivers, per contract:
| Required | Plus a short history | |
|---|---|---|
| Volume | Live, per contract | — |
| Open interest | Daily snapshot | — |
| Bid / Ask | For spread + mid | — |
| Greeks | delta, gamma, theta | — |
| Premium 5m ago | — | rolling buffer you store |
| Stock 5m ago | — | underlying lookback |
| IV | — | optional refinement |
The only piece a raw snapshot does not hand you is the 5-minutes-ago values — you maintain those yourself with a small rolling buffer, sampling each contract's mid price and the underlying every minute. Everything else (Greeks, OI, volume, quotes) comes straight from a standard options feed. That is enough to build a scanner that rivals the paid tools.
Confirmed Alert — Price and Premium Rising Together
Key takeaway
Read volume and open interest together, never apart: volume/OI ratio first to spot fresh money, then stack confirmations — premium movement, tight spread, the right delta and gamma, and price direction — before you call it an alert. The scoring engine's whole purpose is to stop you acting on a single clue. One unusual-volume print is a question; a contract that scores 85+ across six independent factors is an answer.