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
Install
$ npx -y skills add rgourley/quant-garage --skill massive-websockets --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/massive-websockets

Context preview

The summary Claude sees to decide when to auto-load this skill.

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).

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
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.

Get the whole plugin
Stats
7
Stars
0
Forks
Active
Maintenance
Python
Language
7d ago
Last commit
2mo ago
Created

Repo: rgourley/quant-garage

Other skills on quant-garage.