Skip to content

Alpaca Market Data Worker

File: scripts/run_alpaca_market_data_example.py

Streams crypto market data from Alpaca's v1beta3 WebSocket feed via the WebSocketManager and a DotNet DLL worker deployed by WorkManager. Mirrors the Binance CLOB example flow but with Alpaca's inline-key auth handshake and additive subscribe format.

API Details

Property Value
WebSocket URL wss://stream.data.alpaca.markets/v1beta3/crypto
Authentication Message-based: {"action":"auth","key":...,"secret":...}
Subscribe {"action":"subscribe","orderbooks":["BTC/USD", ...]}
Order book type L3 (full depth) for orderbooks channel
Frame T types o=orderbook, d=trade, subscription, success
Environment Paper keys from app.alpaca.markets work on the real feed

Authentication Flow

Unlike Binance where the stream is public, Alpaca requires API key authentication over the WebSocket itself. The example uses the existing WebSocketManagerController DotNet worker and a new send_raw command to forward the auth and subscribe messages verbatim through the WebSocketManager service:

# Step 1: Create WS connection
{"command": "create", "url": "wss://stream.data.alpaca.markets/v1beta3/crypto", "auto_reconnect": True}
# → returns {"id": "<conn_id>"}

# Step 2: Authenticate (via send_raw → WsmServiceName.SendRaw)
{"command": "send_raw", "id": "<conn_id>", "message": "<base64 of {\"action\":\"auth\",...}>"}

# Step 3: Subscribe to symbols (additive — the server's set is the union)
{"command": "send_raw", "id": "<conn_id>", "message": "<base64 of {\"action\":\"subscribe\",\"orderbooks\":[...]}>"}

# Step 4: Stream frames to a Dapr topic
{"command": "publish", "id": "<conn_id>", "topic": "alpaca-ticks"}

The Dapr topic (alpaca-ticks by default) carries one message per server frame, with the frame's T field identifying the message kind.

Getting Alpaca API Keys

  1. Login to Alpaca Dashboard
  2. Navigate to Settings → API Keys
  3. Generate a new key pair (paper keys work for testing)
  4. Set in .env: ALPACA_API_KEY and ALPACA_API_SECRET

What It Does

  1. Builds the WebSocketManagerController DotNet worker via dotnet publish (same PackageWorker target as the Binance example)
  2. Creates the worker via API Gateway and starts it
  3. Sends create to open an Alpaca WebSocket connection via the WebSocketManager service
  4. Sends send_raw (auth), send_raw (subscribe), publish (set output topic) — same pattern the Binance example uses, with Alpaca's wire format on top
  5. Subscribes to the output topic and prints one line per frame, with T (kind), S (symbol), and either p/s (trade) or b/a (orderbook) fields
  6. Sends list to enumerate active connections and ListWorkers() to show worker state
  7. Cleanup: sends disconnect, then StopWorker + DeleteWorker

Settings

The script reads from scripts/settings.alpaca.json:

{
  "url": "wss://stream.data.alpaca.markets/v1beta3/crypto",
  "channel": "orderbooks",
  "symbols": ["BTC/USD", "ETH/USD", "SOL/USD", "DOGE/USD", "AVAX/USD"],
  "topic": "alpaca-ticks"
}

Five symbols is enough to demonstrate per-symbol parsing. Change channel to trades to subscribe to L1 trade prints instead of L3 orderbook updates.

Running

source .env  # Must have ALPACA_API_KEY and ALPACA_API_SECRET
cd virtufin-examples
python scripts/run_alpaca_market_data_example.py

Ctrl+C to stop. On exit the script disconnects the WebSocket and removes the worker.

Differences from the Binance C# Example

Aspect Binance Alpaca
Auth None (public) Inline {"action":"auth",...} message
Subscribe format {"method":"SUBSCRIBE","params":[...]} {"action":"subscribe","<channel>":[...]}
Frame format {"stream":"...","data":{...}} Array of {"T":"o/d/subscription",...}
Multi-worker Yes (5 workers, chunked pairs) No (1 worker, all symbols on 1 connection)
Output topic binance-depth-<run_id> alpaca-ticks (single topic, script filters by S)
Reconnect auto_reconnect flag on create Same — auto_reconnect flag on create