Last reviewed: 2026-07-27. A plain-English overview of the Unusual Whales (UW) options data the platform pulls, what each feed tells us, how it feeds the dashboard and the swing-trading engine, and what still needs improving. Code lives in
tools/data_ingestion_external_data.py(short nameuw), authenticated viatools/uw_session.py.
Everything comes from one login to unusualwhales.com. There's no email/password API, so uw_session.py logs in once with a headless browser, harvests the Bearer token from localStorage, caches it for 12h, and reuses it for plain HTTP calls to phx.unusualwhales.com/api/.... No offline fallback — if the token is missing or a call fails, each tool returns empty and the rest of the report/dashboard degrades gracefully.
| Tool | What it answers | Needs ticker? |
|---|---|---|
ext_get_options_screener |
Is the options market net bullish or bearish on this stock, and how strongly? | No (omit → market-wide top movers) |
ext_get_options_flow |
What large, aggressive individual bets are being placed right now? | Yes |
ext_get_oi_changes |
Where are traders building positions over time (which strikes)? | Yes |
ext_get_net_premium |
Compact directional + dealer-hedging (GEX) summary | Yes |
ext_get_hot_chains |
Which specific strikes/expiries are seeing the heaviest volume? | Yes |
ext_get_dark_pool |
Where are institutions quietly trading the stock off-exchange? | No (omit → volume leaders) |
The mental model: flow = what's happening now, OI = what's built up over time, screener/net-premium = the scoreboard, hot chains = the hot strikes, dark pool = stock-side institutional footprint.
Options data provides an extra layer of information about expectations, risk, and positioning in the underlying stock. It is most useful as a confirmation and risk-management tool — not a standalone buy/sell signal.
| Options measure | Possible stock-trading use |
|---|---|
| Implied volatility (IV) | Estimates expected future movement. High IV → larger expected swings → smaller positions or wider stops. |
| IV term structure | Shows when risk is concentrated. A sharp rise around one expiry usually flags earnings or another scheduled catalyst. |
| Put/call skew | Expensive puts → demand for downside protection; expensive calls → upside speculation or short-squeeze concern. |
| Options volume | Unusual activity may reveal new interest before it shows up in the stock's volume. |
| Open interest | Highlights strikes with large positions that can influence price near expiration. |
| Changes in IV | Rising IV alongside a price move strengthens the signal; falling IV suggests uncertainty is resolving. |
| Option-implied expected move | A market-based range for planning entries, targets, and stops. |
| Dealer gamma (GEX) | Anticipates whether dealer hedging will suppress volatility (positive gamma / pinning) or amplify moves (negative gamma). |
1. Size positions to expected movement. If a $100 stock's options imply a ±$8 one-week move, a $2 stop is too tight. Use the implied move to pick a stop consistent with normal expected movement. Rough approximation:
expected move ≈ price × IV × √(days / 365)
2. Judge whether a breakout is credible. A breakout with rising call activity, rising IV, and rising stock volume has stronger participation than one where IV and options activity are falling. Confirmation, not proof — the calls could be hedges or one leg of a spread.
3. Detect event risk. When near-term options suddenly get much more expensive than later expiries, the market expects a specific catalyst. Even a pure stock trader can use that to avoid an oversized position into the event.
4. Identify important price levels. Large OI at a strike can act like a magnet or temporary support/resistance near expiration, as holders and dealers adjust hedges. Conditional — mostly relevant in liquid names close to expiry.
5. Compare market expectations with your own. If options imply a 10% move but you expect 3%, the stock is probably too event-sensitive for a plain directional trade. If the market expects little movement but you see a catalyst, the discrepancy can support a stock position — provided your thesis is genuinely differentiated.
Do not read "lots of calls" as automatically bullish or "lots of puts" as bearish. Options may be hedges, covered positions, spreads, volatility trades, or closing transactions. The more useful question is: what risk is the options market pricing, and does that agree with what the stock itself is doing? This is why the size_greater_oi flag (opening vs closing) and cross-confirmation with price/volume matter more than raw flow direction.
ext_get_options_screener — the sentiment scorecardOne row of options metrics per stock, straight from UW's ticker_day_states. With a ticker, all fields for that stock; without one, the top N bullish and top N bearish tickers by premium flow.
Key fields: put_call_ratio, bullish_premium / bearish_premium, net_call_premium / net_put_premium, call_volume / put_volume vs avg_30_day_call_volume / avg_30_day_put_volume, iv_rank, iv30d (+ 1d/1w/1m history), implied_move_perc, total/call/put_open_interest, next_earnings_date, plus sector / marketcap.
Trading use: the quickest read on net premium lean and how expensive options are (IV rank) — i.e. whether it's a good time to be the buyer. Net premium is a balance of pressure, not a verdict; pair it with price action before drawing a directional conclusion.
ext_get_options_flow — the big individual betsIndividual large trades from the live option_trades_v2 feed, sorted by premium. Filters: is_call, is_put, is_otm, min_premium, size_greater_oi (size > open interest = a genuinely new position, not a close). Cancelled prints dropped.
Key fields: strike, option_type, expiry, underlying_price, price, size, premium, open_interest, implied_volatility, delta, nbbo_bid/nbbo_ask.
Trading use: catching real-time conviction — a whale spending $2M on OTM calls expiring Friday may be a directional bet, but it could equally be a hedge or a spread leg (see "The biggest trap" above). size_greater_oi + is_otm + min_premium isolate the aggressive, opening bets most likely to be directional; treat even those as confirmation, not proof.
ext_get_oi_changes — where positions are accumulatingPer-contract open-interest change (curr_oi − last_oi) for one ticker, sorted by absolute change. Rising OI = traders stacking positions at a strike, not just day-trading it.
Trading use: finds the price levels the market is anchoring to. In the dashboard we net the call-OI vs put-OI changes and flag when call OI is building above the current price — often bullish accumulation, but only if the OI is opening (not closing) and price action agrees.
ext_get_net_premium — directional + dealer pressureA compact summary combining the screener day-state (net_call_premium, net_put_premium, put_call_ratio, iv_rank, implied_move_perc) with the latest gamma exposure (GEX) reading (gex_ratio, gex_net_change, gex_call_delta, gex_put_delta).
Trading use: GEX tells you how dealers are positioned — negative gamma means dealers amplify moves (more volatility), positive gamma means they dampen them (pinning). Useful for timing entries and setting expectations about how the stock will behave into an event.
ext_get_hot_chains — the busiest strikesMost-active option chains for a ticker by volume, with per-contract volume, open_interest, total_premium, IV, and greeks (delta, gamma).
Trading use: confirms where the flow is concentrated — pairs with oi_changes to distinguish "high volume, no new OI" (churn) from "high volume + rising OI" (real positioning).
ext_get_dark_pool — off-exchange stock printsIndividual off-exchange trades for a ticker (size, price, premium, executed_at), sorted by size. This is stock, not options — where institutions accumulate/distribute away from the lit market.
Trading use: large dark-pool prints near a technical level corroborate that institutions are quietly building or unloading a position.
routes/dashboard.py)Two cards consume UW data via cached calls (_cached(_ext_run, ...)):
_fetch_technical, ~line 1358): derives simple boolean checks from the screener + OI feed — call_vol_2x (call volume > 2× 30-day avg), ivr_under_40 (IV rank favourable for buying), iv_contracting, and oi_calls_above_price (net call OI building above spot). These are confirmation signals layered on top of the price/technical setup._fetch_whale_insider, ~line 1524): surfaces raw dark_pool, options_flow, net_premium, hot_chains, and computed oi_changes (net call/put OI delta + biggest strikes moving). This is the "what is smart money doing" panel.These snapshots are written to snapshots/dashboard_<symbol>.md and injected into the report pipeline as the # Symbol Snapshots block, so every analysis/critic/report agent sees the options picture without re-fetching.
routes/trades.py)The swing_trading_analyst gets a subset — uw:ext_get_options_flow,ext_get_oi_changes,ext_get_options_screener (line ~675) — as live tools in the propose flow. It uses them as evidence when justifying a trade idea: e.g. confirming a long candidate with heavy bullish call flow + rising call OI + a favourable IV rank before proposing an actual bracket order.
agents/config.json)The market_data agent has uw in its toolset, mainly for cross-ticker discovery via the tickerless screener (market-wide bullish/bearish leaders).
Resolved (2026-07-27):
- ~~Fragile OI call/put parsing.~~ Done. Both dashboard blocks now use the module-level _osi_is_call() helper, which regex-matches the type char at its true OSI position (\d{6}([CP])\d{8}$) and returns None on any non-standard symbol instead of silently mislabeling. (File: routes/dashboard.py.)
- ~~stock_price field mismatch.~~ Done. The Options Flow Confirmation block read r.get("stock_price"), which _query_oi_changes never returns — so oi_calls_above_price was always False. Now uses the card's own price as spot and gates the check on oi and spot so it emits absent rather than a misleading False. (File: routes/dashboard.py ~1390.)
Still open:
1. Carry type from the tool. Even with _osi_is_call(), the route still re-derives call/put from a string. Cleaner would be to add an explicit type field in _query_oi_changes so the route consumes structured data. (Lower priority now that parsing is hardened.)
2. No GEX in the swing engine. ext_get_net_premium (with GEX) isn't in the swing analyst's toolset — it only gets flow/OI/screener. Dealer-gamma context would meaningfully improve entry timing and volatility expectations.
3. No historical/backtest angle. Everything is a today snapshot. We never store yesterday's flow/OI to detect trend (e.g. "call OI has climbed 3 days running"). A lightweight daily persist of the screener row per watchlist symbol would unlock multi-day positioning trends.
4. Silent-empty degradation is invisible. When the UW token expires, tools return [] and cards just go blank — no signal in the UI or logs that the data is missing vs. genuinely quiet. A freshness/health indicator would prevent trading on stale-but-silent data.
UW's API exposes a lot we don't touch yet:
- Options alerts / "flow alerts" — UW's own pre-scored unusual-activity feed (sweep vs block, opening vs closing) — higher signal than raw option_trades_v2 we filter ourselves.
- Greek exposure by strike (GEX/DEX/charm/vanna profiles) — full curves, not just the single net GEX reading. Key for identifying gamma walls / pinning levels and dealer hedging flip points.
- Volatility term structure & skew — we get iv30d; UW has the full surface. Skew steepening is an early warning signal.
- Historical flow per ticker — enables the multi-day trend analysis above without us building our own store.
- Insider transactions & congressional trades from UW — cross-check against the FMP versions we already show.
- Max pain / expected move by expiry — useful for framing weekly options-driven levels.
Priority if picking one thing: the two correctness bugs are now fixed. Next-highest value is a freshness/health indicator (open item 4) — a silently-empty UW feed currently corrupts signals with no warning — followed by GEX in the swing engine.