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_gamma_profile |
Where are the gamma walls, and which side of the flip point is price on? | 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, gamma profile = the levels dealers defend, 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 from gex/{ticker}: gex_call, gex_put, gex_net, gex_ratio, gex_net_change, plus the delta legs 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_gamma_profile — the gamma walls and the flip pointThe full chain aggregated per strike, from the native gex/{ticker}/strike endpoint (fields strike, call_gex, put_gex, call_delta, put_delta, single latest session; puts already signed negative). Returns spot, net_gex, call_walls / put_walls (top three strikes by magnitude), flip_point, and by_strike (~25 strikes centred on spot).
flip_point is the zero-gamma level: cumulative net GEX summed low→high, linearly interpolated at the last sign change. The last, not the first — far-OTM dust strikes flip the running total repeatedly on noise.
Trading use: this is the strike-level version of the single gex_net scalar. Walls are soft support/resistance while dealers are long gamma; spot above the flip point means suppressed volatility and pinning (fade extensions), below it means amplified moves (widen stops, favour continuation). gex/{ticker}/expiry exists too and is not wired up.
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, gamma_profile, and computed oi_changes (net call/put OI delta + biggest strikes moving). This is the "what is smart money doing" panel. The gamma profile renders as a "Dealer Gamma" section — per-strike bars (puts left, calls right) with the spot and flip-point markers drawn between the strikes they actually fall on. _slim_for_swing carries only the walls, spot, flip point and net GEX into the report snapshot; by_strike is dropped so 25 rows of detail no text agent reads don't eat the budget.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,ext_get_net_premium,ext_get_gamma_profile (_SWING_TOOLS, line ~717) — 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.)
Resolved (2026-08-09):
- ~~No GEX in the swing engine.~~ Done. ext_get_net_premium and ext_get_gamma_profile are both in the swing analyst's toolset (agents/config.json, _SWING_TOOLS), and the skill's Institutional Positioning section explains how to read the regime. Two bugs fell out of the same audit: _query_net_premium was fetching a full year of gex/{ticker} rows to use one, and was keeping only the delta legs while call_gex/put_gex sat unread in the same row — so the product's only dealer-gamma number was the gex_ratio scalar.
- ~~Greek exposure by strike.~~ Done for gamma. gex/{ticker}/strike turned out to exist natively (no need to reconstruct from top_chains_by_volume), and backs the new ext_get_gamma_profile. DEX/charm/vanna curves are still untouched; gex/{ticker} carries call_charm/call_vanna per day if wanted.
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 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.
3. 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.
- Delta/charm/vanna exposure by strike — gamma is now covered by ext_get_gamma_profile; the other greek curves are not. gex/{ticker} already returns call_charm/call_vanna/call_delta per day, and gex/{ticker}/expiry gives the term structure.
- 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: a freshness/health indicator (open item 3) — a silently-empty UW feed currently corrupts signals with no warning, and the gamma profile makes that worse, since a blank Dealer Gamma section is indistinguishable from a genuinely flat chain.