Skip to content

Alpaca CLOB Example

File: python/websocketmanager/alpaca_clob_example.py

Streams BTC/USD order book from Alpaca crypto WebSocket with API key authentication.

API Details

Property Value
WebSocket URL wss://stream.data.alpaca.markets/v1beta3/crypto/us
Authentication Message-based: {"action":"auth","key":"...","secret":"..."}
Subscribe {"action":"subscribe","orderbooks":["BTC/USD"]}
Order book type L3 (full depth)
Keys Paper-trading keys work on the market-data stream

Authentication Flow

Unlike Binance where the stream is public, Alpaca expects a login on every new socket, within 10 seconds. The example connects with a handshake (WebSocketManager 0.7.0+): on every socket open (the first connect, every auto_reconnect, every actor handoff to another pod) the WebSocketManager asks the example for the messages to send, by request-reply on x.examples.alpaca-clob.handshake. The example is the handler:

def handshake_reply(request: dict) -> dict:
    symbols = request.get("config", {}).get("symbols", SYMBOL).split(",")
    return {"messages": [
        # wait for Alpaca's confirmation; any Alpaca error fails the handshake (and is retried)
        {"text": json.dumps(login_message()), "expect": '"msg":"authenticated"', "fail": '"T":"error"'},
        # nothing to wait for: the full book (r=true) that follows is published
        {"text": json.dumps({"action": "subscribe", "orderbooks": symbols})},
    ]}

conn = await api.gateway.websocketmanager.Connect({
    "url": ALPACA_WS_URL,
    "auto_reconnect": True,
    "publish_topic": DEPTH_TOPIC,
    "handshake": {"topic": HANDSHAKE_TOPIC, "config": {"symbols": SYMBOL}},
})

login_message() is computed on every request, so a credential that depends on the time (a Broker API access token, which expires) is fresh on every open. With Trading API keys it is the plain {"action":"auth","key":…,"secret":…}.

The connection re-authenticates only while the example runs: the handler lives in it. For a feed that outlives the example, deploy the AlpacaHandshake worker on the handshake topic instead: it builds the same reply and gets the key as worker secrets (CreateWorkerRequest.secrets, references into the cluster's worker Secret; WorkManager 0.21+). The Alpaca notebook shows it with UseWorkerHandler = true. Either way, the reply carries the login over the platform's pub/sub (to the WebSocketManager instance's reply topic), so anyone who can subscribe there can read it: use paper-trading keys on a shared cluster.

Getting Alpaca API Keys

  1. Login to Alpaca Dashboard
  2. Navigate to Settings → API Keys
  3. Generate new key pair
  4. Set in .env: ALPACA_API_KEY and ALPACA_API_SECRET

What It Does

  1. Starts the handshake handler (subscribed to x.examples.alpaca-clob.handshake) and a listener on x.examples.alpaca-clob.orderbook, before connecting: pub/sub starts at the topic's tail
  2. Connects to Alpaca's crypto stream through WebSocketManager with auto_reconnect, the handshake and publish_topic; Connect returns once Alpaca confirmed the login
  3. Applies the full book and each update to a local order book
  4. Saves the top 20 levels and spread to State (statestore / x.examples.alpaca-clob.orderbook.BTC_USD) every 10 s
  5. Disconnects on exit

For a worker that maintains the books on the platform, with a live depth chart, see the alpaca-order-book-live.verso notebook (AlpacaOrderBook worker).

Running

source .env  # Must have ALPACA_API_KEY and ALPACA_API_SECRET
cd python
uv run python websocketmanager/alpaca_clob_example.py