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
- Login to Alpaca Dashboard
- Navigate to Settings → API Keys
- Generate a new key pair (paper keys work for testing)
- Set in
.env:ALPACA_API_KEYandALPACA_API_SECRET
What It Does
- Builds the
WebSocketManagerControllerDotNet worker viadotnet publish(samePackageWorkertarget as the Binance example) - Creates the worker via API Gateway and starts it
- Sends
createto open an Alpaca WebSocket connection via the WebSocketManager service - Sends
send_raw(auth),send_raw(subscribe),publish(set output topic) — same pattern the Binance example uses, with Alpaca's wire format on top - Subscribes to the output topic and prints one line per frame, with
T(kind),S(symbol), and eitherp/s(trade) orb/a(orderbook) fields - Sends
listto enumerate active connections andListWorkers()to show worker state - Cleanup: sends
disconnect, thenStopWorker+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 |