Implied move
A straddle-based estimate of how far the stock could swing by the nearest options expiry. We pull the next available expiry from yfinance (expiries[0] — the nearest weekly or monthly), find the strike closest to the current price (the at-the-money, or ATM, strike), and look at the ATM call and ATM put. The correct calculation ADDS the two premiums (the cost of buying both = a straddle) and divides by the spot price: (call + put) / spot, shown as ±N%. Two code paths compute this differently. The live path (data_provider.py:742-743) does it right: it sums the call and put mid-prices (bid/ask midpoint, falling back to last trade) and divides by spot. The daily-batch path (options_snapshot_service.py:83-85) does NOT sum — it AVERAGES the two legs ((call.lastPrice + put.lastPrice) / 2) and divides that by spot, producing roughly half the true straddle move, and it uses last-trade prices rather than the midpoint.
A high implied move tells you the options market is paying up for protection or leverage into the nearest expiry — common ahead of earnings, a regulatory date, or a macro print, or when a name is in play. Set it against the stock's recent realized moves: if implied is running well above the stock's historical swings, the market is pricing in something specific; if it's below, the market sees calm. You can use it to gauge whether a thesis price target is within reach inside the expiry window, or whether the options market is at odds with a quiet consensus. One caution before drawing any of those conclusions: the number you see on screen comes from the batch path, which currently halves the straddle (see The catch), so compare it against the bands below rather than against textbook implied-move figures.
This metric’s live data pipeline is under review — the method above describes how it is intended to be calculated, but the current displayed value may not yet reflect that formula. Treat it as in progress, not a finished number.
For a correctly-computed straddle move: theoretical floor is 0% (options worth nothing — never seen in practice). Typical large-cap non-event range: ±2-6%. Pre-earnings: ±5-15%. Small/micro-cap or high-fear names: ±15-40%. Distress or near-expiry thin chains can exceed ±50% but lose precision there. Caveat on the shipped number: because the preferred batch source averages the two legs instead of summing them, the displayed ±N% lands at roughly half these bands. The two code paths can also disagree for the same ticker — the live path (sum) reports about 2x the batch path (average) — so which number you see depends on which snapshot is fresher.
The number displayed today is mathematically wrong by a factor of about 2. The on-screen value prefers the daily-batch snapshot (options_snapshot_service.py), and that path AVERAGES the ATM call and put ((call + put)/2) instead of summing them — a straddle is the sum, not the average. So the displayed implied move is roughly HALF the true straddle move. The batch path also uses last-trade prices, which on thinly traded strikes can be stale on top of the halving. The live-fetch path (data_provider.py) computes it correctly — it sums the two legs and prefers the bid/ask midpoint — but the display layer (ticker_service.py:529-535) prefers the batch value when it exists. Net: for a given ticker the batch and live numbers can be about 2x apart, and the one you usually see is the halved, less accurate one.