On the wire · 13:54:55 UTC Live 72 · Prematch 8,062 · Markets 32,473 Vol. I · No. 1 · Monday 9 November 2026 wss://pinnfeed.io/v1/stream
PinnFeed
Guide · Steam · Dropping odds

Detect dropping odds and steam moves on Pinnacle in real time

Updated · PinnFeed · 3 min read

A steam move is a fast, sharp price drop at a market-making book like Pinnacle, usually because sharp money hit one side. Soft bookmakers often lag by seconds or minutes, which is why steam alerts are among the most useful "picks" you can generate from a live feed. This guide builds a detector on the PinnFeed WebSocket.

Step 1 — Define a steam move#

A practical definition you can tune:

  • the decimal price of an outcome shortens by at least 5% from its highest point in the last 2 minutes;
  • the market's max stake is at least 500, because Pinnacle raises limits as it gets more confident in a price, so moves in thin markets are noisier;
  • you alert once per outcome per window, not on every tick.

Step 2 — Stream the book#

Use the Python client from the Python and Node guide. Save it as pinnfeed_stream.py next to the script below; it calls on_market(old, new, event) every time a market changes.

Step 3 — Track a rolling high per outcome#

Keep a short deque of (timestamp, price) per market and outcome, drop entries older than the window, and compare the current price with the window's high:

# steam.py: flag Pinnacle steam moves (fast, sharp price drops) from the live WebSocket.
# Uses pinnfeed_stream.py from the Python/Node guide. pip install websockets
import asyncio, collections, os, time
from pinnfeed_stream import stream, SIDES

WINDOW_S = int(os.environ.get("WINDOW_S", 120))        # look-back window
MIN_DROP = float(os.environ.get("MIN_DROP", 0.05))  # alert when the price shortens 5% or more inside the window
MIN_STAKE = float(os.environ.get("MIN_STAKE", 500))  # ignore thin markets: Pinnacle's max stake is a confidence signal

history = collections.defaultdict(collections.deque)  # (market id, outcome) -> deque[(ts, decimal)]
alerted = {}


def on_market(old, new, event):
    if not event or (new.get("maxStake") or 0) < MIN_STAKE:
        return
    now = time.time()
    for o in new["outcomes"]:
        if o["name"] not in SIDES:
            continue
        h = history[(new["id"], o["name"])]
        h.append((now, o["decimal"]))
        while h and now - h[0][0] > WINDOW_S:
            h.popleft()
        high = max(p for _, p in h)
        drop = 1 - o["decimal"] / high
        key = (new["id"], o["name"])
        if drop >= MIN_DROP and now - alerted.get(key, 0) > WINDOW_S:
            alerted[key] = now
            pts = o.get("points")
            line = "" if pts is None else f" {pts:g}" if new["type"] == "total" else f" {pts:+g}"
            print(f'STEAM {event["sport"]} | {event["home"]} v {event["away"]} | {new["type"]}{line} {o["name"]}: '
                  f'{high:.3f} -> {o["decimal"]:.3f} (-{drop:.1%} in {WINDOW_S}s) fair {o["fair"]:.3f} max stake {new["maxStake"]}')


asyncio.run(stream({"sports": ["soccer", "tennis", "basketball"], "markets": ["moneyline", "spread", "total"],
                    "main": True, "specials": False}, on_market))

Run it with python steam.py. Thresholds are environment variables, for example MIN_DROP=0.03 WINDOW_S=300 python steam.py for 3% over five minutes. A typical line looks like this:

STEAM tennis | Sebastian Baez v Daniil Glinka | moneyline away: 2.260 -> 2.050 (-9.3% in 120s) fair 2.143 max stake 975

Step 4 — Or ask the REST endpoint#

If you only need a periodic list, GET /v1/drops returns the biggest drops in a rolling window, computed on the server:

curl -H "x-api-key: $PINNFEED_KEY" "https://pinnfeed.io/v1/drops?sport=soccer&live=true&minutes=10&min_pct=5"

Each row has the event, market, line, outcome, from, to, dropPct and movedAt. It works on every plan, including Free with its 30-second delay.

Step 5 — Turn alerts into picks#

  • Compare with soft books. A steam move is only actionable where the soft price has not followed yet. The value betting guide shows how to price that edge with Pinnacle's fair odds.
  • Prefer prematch for soft-book lag; in-play moves are often score-driven rather than sharp money.
  • Measure yourself with closing line value: if your steam picks beat the close on average, the signal is real.
  • Publish them: the Telegram picks bot posts these alerts to a channel.

Questions people ask#

What is the difference between dropping odds and steam?#

Dropping odds is any price shortening. Steam is a fast, significant drop at a sharp book, usually driven by informed money, which tends to spread to other books afterwards.

Why use Pinnacle for steam detection?#

Pinnacle takes high limits and moves its prices on sharp action, so its moves are the market's best signal. Soft books copy or lag behind them.

Can I do this without the WebSocket?#

Yes, by polling /v1/drops, which works on every plan. The WebSocket sees each move as it happens; polling only sees it on your next request.

The WebSocket plan: Builder, €99/mo.

Real-time Pinnacle odds pushed over one WebSocket, all 13 sports, every market, no-vig fair odds and closing lines. 3 days free, cancel in one click.

Form K-1 · Free API key

Get your free API key.

No password. We email you a link; click it and your key is on screen.

One free key per email. Link valid for 20 minutes.