IV Rank Scanner: Find the Highest Implied Volatility Stocks Today
Build an IV rank scanner in Python to find the highest implied volatility stocks today. Scan 50+ tickers, filter by IV rank, and automate daily alerts to Slack or Discord — all using the FlashAlpha API.
- #IVRank #ImpliedVolatility #OptionsScreener #Python #PremiumSelling
Why IV Rank Matters More Than Raw IV
AAPL at 30% IV and TSLA at 30% IV are completely different situations. AAPL's 52-week IV range might be 15%-35%, putting 30% near the top. TSLA's range might be 25%-80%, putting 30% near the bottom. Same raw number, opposite conclusions.
IV rank solves this by normalizing: it tells you where current IV sits as a percentage of the annual range (0-100). IV percentile goes further — it tells you what percentage of trading days had lower IV than today. Both give you the context that raw IV lacks.
Selling premium when IV rank is elevated is the most basic statistical edge in options. You're selling insurance when insurance is expensive. The data backs this up — IV tends to mean-revert, so high IV rank today implies IV is likely to contract, benefiting premium sellers.
IV rank > 50 means current IV is in the upper half of its annual range. Many premium sellers use this as their minimum threshold for entering short vol positions.
Build a Scanner in 20 Lines of Python
This scanner pulls volatility data for a 50-ticker watchlist, filters for elevated IV, and sorts by the highest readings. The Volatility Analysis endpoint returns ATM IV, realized vol, and the IV-RV spread for each ticker.
from flashalpha import FlashAlphaClient
import pandas as pd
client = FlashAlphaClient(api_key="your_api_key")
# Define your watchlist — mega caps, popular options names
watchlist = [
"SPY", "QQQ", "IWM", "TSLA", "NVDA", "AAPL", "MSFT", "AMZN", "GOOGL", "META",
"AMD", "NFLX", "AVGO", "CRM", "ORCL", "ADBE", "INTC", "MU", "QCOM", "NOW",
"JPM", "GS", "BAC", "WFC", "V", "MA", "PYPL", "COIN", "SOFI", "PLTR",
"XOM", "CVX", "COP", "OXY", "SLB", "JNJ", "UNH", "PFE", "LLY", "ABBV",
"COST", "WMT", "HD", "NKE", "SBUX", "BA", "CAT", "DIS", "UBER", "ABNB"
]
results = []
for ticker in watchlist:
try:
vol = client.get_volatility(ticker)
results.append({
"ticker": ticker,
"atm_iv": vol["atm_iv"],
"rv_20d": vol["realized_vol"]["rv_20d"],
"vrp": vol["iv_rv_spreads"]["vrp_20d"],
"assessment": vol["iv_rv_spreads"]["assessment"]
})
except Exception:
continue # skip if data unavailable
df = pd.DataFrame(results)
df = df.sort_values("atm_iv", ascending=False)
# Show top 20 by ATM IV
print("\n=== Highest IV Stocks Today ===\n")
print(df.head(20).to_string(index=False))
Sample output:
=== Highest IV Stocks Today ===
ticker atm_iv rv_20d vrp assessment
TSLA 52.30 45.80 6.50 moderate_premium
COIN 48.70 41.20 7.50 moderate_premium
PLTR 45.20 38.90 6.30 moderate_premium
NVDA 42.80 39.10 3.70 fair_premium
AMD 39.50 35.20 4.30 fair_premium
SOFI 38.90 33.40 5.50 moderate_premium
UBER 36.20 31.80 4.40 fair_premium
ABNB 35.80 30.50 5.30 moderate_premium
MU 34.50 29.80 4.70 fair_premium
OXY 33.90 28.60 5.30 moderate_premium
NFLX 32.40 27.90 4.50 fair_premium
NKE 31.80 26.40 5.40 moderate_premium
BA 31.20 28.70 2.50 fair_premium
CRM 30.50 25.80 4.70 fair_premium
INTC 29.80 26.30 3.50 fair_premium
QCOM 28.90 24.70 4.20 fair_premium
PYPL 28.40 23.60 4.80 fair_premium
DIS 27.60 22.90 4.70 fair_premium
GS 26.80 22.10 4.70 fair_premium
BAC 25.50 21.30 4.20 fair_premium
The vrp column is the volatility risk premium — the spread between implied and realized vol. Positive VRP means options are pricing in more volatility than is actually occurring. When VRP is high and IV is elevated, that's the sweet spot for premium selling.
Filter for Premium Selling Opportunities
Not every high-IV stock is a good short vol trade. You want elevated IV and positive VRP — confirmation that options are overpriced relative to actual movement:
# Filter: ATM IV > 30% AND positive VRP > 3%
opportunities = df[(df["atm_iv"] > 30) & (df["vrp"] > 3.0)]
print(f"\n=== Premium Selling Candidates ({len(opportunities)} found) ===\n")
print(opportunities.to_string(index=False))
This filters your scan down to the actionable names — stocks where IV is elevated and the market is overpricing vol relative to what's being realized.
How to Use This for Trading
Selling Premium
High IV + positive VRP is the classic setup for credit spreads, iron condors, and strangles. You're selling expensive insurance — collecting premium inflated by fear or uncertainty, betting that actual movement will be less than what's priced in.
The scanner identifies where premium is richest. Your job is to decide how to structure the trade — defined risk (credit spreads, iron condors) or undefined risk (strangles, straddles) — based on your risk tolerance and account size.
Avoiding IV Crush
If you're buying options, the scanner serves as a warning system. Buying calls on a stock with ATM IV at 50% when its normal range is 25-35% means you're paying a massive premium that will erode even if the stock moves your direction. Check IV context before entering any long options position.
This is especially critical around earnings — see our IV Crush guide for how to track and exploit earnings vol collapse.
Relative Value
Compare IV across correlated names. If AMD's IV is at a historic high while NVDA's is at a historic low — same sector, same macro exposure — there may be a relative value opportunity. Sell AMD vol, buy NVDA vol. The scanner helps you spot these divergences.
Automate It: Daily IV Alert to Discord
Run the scanner automatically every morning and push results to a Discord channel. Set it up once, never manually scan again:
import requests
from flashalpha import FlashAlphaClient
DISCORD_WEBHOOK = "https://discord.com/api/webhooks/your_webhook_url"
client = FlashAlphaClient(api_key="your_api_key")
watchlist = [
"SPY", "QQQ", "TSLA", "NVDA", "AAPL", "MSFT", "AMZN", "META", "AMD", "NFLX",
"COIN", "PLTR", "SOFI", "UBER", "ABNB", "BA", "NKE", "CRM", "MU", "GS",
"JPM", "XOM", "OXY", "LLY", "JNJ", "GOOGL", "AVGO", "INTC", "PYPL", "DIS"
]
results = []
for ticker in watchlist:
try:
vol = client.get_volatility(ticker)
if vol["atm_iv"] > 30 and vol["iv_rv_spreads"]["vrp_20d"] > 3.0:
results.append(f"**{ticker}** — IV: {vol['atm_iv']}% | VRP: {vol['iv_rv_spreads']['vrp_20d']}%")
except Exception:
continue
if results:
message = "## Daily IV Scanner — Premium Selling Candidates\n" + "\n".join(results[:10])
requests.post(DISCORD_WEBHOOK, json={"content": message})
print(f"Sent {len(results)} results to Discord")
else:
requests.post(DISCORD_WEBHOOK, json={"content": "## Daily IV Scanner\nNo elevated IV stocks found today."})
print("No candidates today")
Schedule this with cron (Linux) or Task Scheduler (Windows) to run at 9:15am ET every trading day. 30 API calls — well within the Growth plan's 2,500/day limit.
Tip: Add Slack support by replacing the Discord webhook with a Slack incoming webhook URL. The payload format is nearly identical — just change "content" to "text".
Get the Data
The Volatility Analysis endpoint requires the Growth plan ($299/mo, 2,500 requests/day). For a 50-ticker watchlist scanned once daily, that's 50 calls — 2% of your daily quota.
Start Scanning for High IV
Build your own IV scanner and find the richest premium every morning.
Get API Key → Try the PlaygroundRelated Reading
- Implied Volatility API — pull raw per-strike IV data
- IV Crush Explained — track earnings vol collapse
- IV Rank vs IV Percentile — understand the difference and when each matters
- Build a Volatility Surface — visualize IV in 3D