Skip to content
Data
Skill

/massive-websockets

Foundation skill for live streaming workflows backed by Massive WebSockets. Use whenever you need sub-second updates from stocks, options, crypto, or FX feeds. Requires a real-time tier (Stocks Advanced, Options Developer, or Crypto Developer).

From plugin
quant-garage
761 skills

Claude Code

$ npx -y skills add rgourley/quant-garage --skill massive-websockets --agent claude-code

SKILL.md

massive-websockets.SKILL.md
name: massive-websockets
description: Foundation skill for live streaming workflows backed by Massive WebSockets. Use whenever you need sub-second updates from stocks, options, crypto, or FX feeds. Requires a real-time tier (Stocks Advanced, Options Developer, or Crypto Developer).

massive-websockets

The live streaming foundation. Any skill that needs to react to market events as they happen reads this first.

Why this exists

REST snapshots are 15 minutes stale on most stocks plans, and even on real-time tiers you pay a request-per-event overhead that breaks down above a few hundred symbols. WebSockets push every update to you as it hits the exchange. One connection, thousands of symbols, no polling.

Endpoints

| Asset | URL | Requires | |---|---|---| | US stocks (real-time) | `wss://socket.polygon.io/stocks` | Stocks Advanced + signed real-time addendum | | US stocks (delayed) | `wss://delayed.polygon.io/stocks` | Stocks Starter or higher | | US stocks (Business) | `wss://business.polygon.io/stocks` | Stocks Business (FMV/AM channels) | | US options | `wss://socket.polygon.io/options` | Options Developer | | Crypto | `wss://socket.polygon.io/crypto` | Crypto Developer | | FX | `wss://socket.polygon.io/forex` | Forex Starter | | Business cluster | `wss://business.polygon.io/{asset}` | Business plan |

The `socket.polygon.io` host is the legacy real-time domain and still active when the signed real-time data agreement is in place.

**The undocumented `wss://delayed.polygon.io/{asset}` endpoint** is what the real-time host suggests in its error message when your key doesn't have the real-time entitlement: "you can connect to the delayed websocket at wss://delayed.polygon.io/stocks." It's the fallback for non-Advanced plans and isn't in the published channel matrix.

Entitlement and the `not_authorized` case

(Verified 2026-06-23 on a Stocks Business + Options Business + Currencies Business + Benzinga add-ons key.)

WebSocket entitlements diverge from the published per-channel matrix in two ways. Plan around both:

1. **Real-time stocks channels (T, Q, A) require a signed real-time data agreement** even on Stocks Business. Without the addendum the auth call succeeds but every `subscribe` to `T.{ticker}`, `Q.{ticker}`, or `A.{ticker}` returns `{ev: "status", status: "error", message: "not authorized"}`. The `socket.polygon.io/stocks` host returns a different error first: "You don't have access real-time data. If you're already subscribed to a plan that includes real-time data, you may need to visit the dashboard to sign your agreements." The fix is operator-side (sign the agreement in the Massive dashboard), not code-side.

2. **The Business cluster (`wss://business.polygon.io/stocks`) delivers `FMV.{ticker}` and `AM.{ticker}` to a Stocks Business key immediately**, no agreement needed. T/Q/A still return `not_authorized` on the same connection. The result: a Business key has a usable live stream (minute aggregates and FMV mids) for any skill that doesn't require sub-second prints.

**Pattern:** subscribe with channel-preference fallback. Send the first-preference subscribe, watch for `status: error, message: "not authorized"`, and on that message resubscribe to the next preference. The `portfolio-mark` skill uses this pattern with the order `T → AM → FMV` (or `FMV → AM → T` on a Business key).

def subscribe_with_fallback(ws, tickers, preferences):
    for channel in preferences:
        params = ",".join(f"{channel}.{t}" for t in tickers)
        ws.send(json.dumps({"action": "subscribe", "params": params}))
        ack = wait_for_status(channel, timeout=2)
        if ack == "subscribed":
            return channel
        if ack == "not_authorized":
            continue
    return None

Status errors don't include the channel or symbol they apply to. Track them at the per-subscribe-batch level, not per-symbol.

Auth flow

Connect, then send an auth message, then subscribe.

const ws = new WebSocket("wss://socket.polygon.io/stocks");

ws.on("open", () => {
  ws.send(JSON.stringify({ action: "auth", params: MASSIVE_API_KEY }));
});

ws.on("message", (data) => {
  const messages = JSON.parse(data);
  for (const msg of messages) {
    if (msg.ev === "status" && msg.status === "auth_success") {
      ws.send(JSON.stringify({ action: "subscribe", params: "T.AAPL,T.MSFT" }));
    } else if (msg.ev === "status" && msg.status === "auth_failed") {
      throw new Error(msg.message);  // bad/expired key
    } else if (msg.ev === "T") {
      handleTrade(msg);
    }
  }
});

Messages arrive as JSON arrays (multiple events per frame). Always iterate.

**Status enum reference** (verified against the official Polygon Python client's mock server, which mirrors the live protocol):

| `ev` | `status` | When | `message` example | |---|---|---|---| | `status` | `connected` | Right after TCP handshake | `"Connected Successfully"` | | `status` | `auth_success` | Auth message accepted | `"authenticated"` | | `status` | `auth_failed` | Bad / expired / wrong-tier API key | varies | | `status` | `success` | Subscribe or unsubscribe ack | `"subscribed to: T.AAPL"` | | `status` | `error` | Subscribe rejected by entitlement | `"not authorized"` |

Track `auth_failed` separately from `error` + `"not authorized"`: the former means the key itself is bad (operator must rotate), the latter means the key is fine but doesn't carry the entitlement for that channel (operator must upgrade or sign an addendum, OR your code should fall back to the next preferred channel).

Channels

| Code | What | Asset classes | |---|---|---| | `T` | Trades (tick) | stocks, options, crypto | | `Q` | NBBO quote updates | stocks | | `A` | Per-second aggregates | stocks, options, crypto | | `AM` | Per-minute aggregates | stocks, options, crypto | | `XL2` | Level 2 book | crypto only | | `FMV` | Fair Market Value stream | Business plan only |

Su

Read more
@rgourley
Ships withquant-garage

Trade like a pro. Without the terminal. View the full landing page → Quant and equity research tools that run inside Claude, or behind your own UI.

Stars on GitHub

Momentum

stars in total
7
Star history of rgourley/quant-garage on GitHub, a chart by star-history.com

Other skills on quant-garage.

8-k-scanner

8-k-scanner

Scan SEC 8-K disclosures across a single ticker or a watchlist using Massive's pre-parsed disclosure taxonomy. Groups the…

analyst-tracker

analyst-tracker

Track sell-side analyst positioning on a name via Benzinga Analyst Ratings. Pulls every rating event over the lookback window,…

backtest-data-prep

backtest-data-prep

Build a clean, point-in-time, ready-to-backtest OHLCV dataset for a US equity universe across an arbitrary date window. Emits…

commodity-cycle

commodity-cycle

Single-commodity macro read. Answers "is this commodity in a winning or losing macro setup right now" and names the macro driver…

More skills from other plugins.

context-loader
Auto-invoked

context-loader

Search memories from earlier Pi sessions in this repository. Use it when earlier work may already explain the code, error,…

mem05d69k
@mem0ai@mem0aiInstall Skill
analyzing-ai-citations

analyzing-ai-citations

Answer AI citation and answer engine optimization (AEO) questions in PostHog: whether AI answer engines (ChatGPT, Claude, and an…

posthog11d41k
@posthog@posthogInstall Skill

Questions about massive-websockets.

Which coding agents can use massive-websockets?
massive-websockets installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.