WebSockets
The client streams the channels documented under WebSockets: what each channel sends, its payloads and its limits are covered there. This page shows the SDK calls.
Connect and join
Section titled “Connect and join”import { STX } from "@stxapp/stx-typescript";
const client = new STX();const ws = await client.websocket().connect();
const ticker = await ws.ticker();console.log(ticker.reply);
await ws.close();client.websocket() uses the client’s host and key, and signs the handshake with the key. Connecting joins nothing; each join method returns a Channel. A client with no API key connects unsigned: the market data channels accept that, and the account channels refuse the join.
Receive messages
Section titled “Receive messages”Each message has topic, event and payload. Take them with a callback, one at a time, or with for await:
const ids = (await client.markets({ status: "open", limit: 5 })).items.map((m) => m.market_id!);
// A callbackawait ws.trades({ marketIds: ids, onMessage: (msg) => console.log(msg.event, msg.payload) });
// One message, with a timeout in millisecondsconst book = await ws.orderbook(ids);try { const msg = await book.next(5_000); console.log(msg.payload.market_id);} catch { console.log("nothing in 5 s");}
// Every message, until the channel closesfor await (const msg of book) { console.log(msg.payload.market_id, msg.payload.bids[0]);}Money fields are dollar strings on every channel, including the ones that send cents on the wire.
Snapshots
Section titled “Snapshots”Account channels send your current state right after the join. waitSnapshot() returns it, keyed by event name:
const positions = await ws.positions();const snap = await positions.waitSnapshot(10_000);console.log(snap.all_positions.positions);Channels
Section titled “Channels”| SDK call | Channel |
|---|---|
ws.orderbook(marketIds) |
Order book, ticker and trades |
ws.ticker({ sports, competitions }) |
Order book, ticker and trades |
ws.trades({ marketIds, eventIds }) |
Order book, ticker and trades |
ws.market(marketId) |
Market order book |
ws.markets({ ruleFilters, messageTypes }) |
Markets |
ws.marketStats(marketIds, { range }) |
Market stats |
ws.marketUpdates({ watch }) |
Market updates |
ws.orders({ marketIds, cancelOnDisconnect, pingTimeout }) |
Orders |
ws.fills({ marketIds }) |
Fills |
ws.positions({ marketIds }) |
Positions |
ws.settlements({ marketIds }) |
Settlements |
ws.balances({ accountId }) |
Balances |
ws.account({ marketIds }) |
Account |
ws.userInfo() |
User info |
Every join method also takes onMessage. To change filters after joining, call channel.selectMarketIds([...]) (and selectFilters, selectRuleFilters, selectMessageTypes); on market_updates, channel.watch([...]). ws.join(topic, payload) joins any other topic.
Live account view
Section titled “Live account view”ws.accountView() keeps your balance, open orders, fills and positions current. It joins the balances, orders, fills and positions channels, starts from their snapshots, applies every update, and starts over from fresh snapshots after a reconnect. It resolves once all four snapshots are in.
const ws = await client.websocket().connect();
const view = await ws.accountView({ onChange: (change) => render(change.view.state()), // change.kind: "balance" | "orders" | "fills" | "positions" | "payment"});
console.log(view.balance?.available_balance, view.openOrders.length, view.fills.length, view.positions.length);
// No refetch needed: the new order and the balance change arrive on the socket.await client.placeOrder(marketId, "buy", "limit", { price: "0.01", quantity: "1" });view.state() returns plain data you can send to a browser. Options: marketIds, accountId, maxFills (default 1000), keepFlatPositions, snapshotTimeoutMs (default 15000). view.close() leaves the four channels and keeps the socket open.
Reconnects
Section titled “Reconnects”The client sends the heartbeat and channel pings for you. After a drop it reconnects with backoff, signs again, and rejoins every channel with its current filters. It cannot know what you missed while offline, so read again anything you loaded with a client method (open orders, history) in onReconnect:
import { ReconnectPolicy, STX } from "@stxapp/stx-typescript";
const client = new STX();const ws = await client .websocket({ reconnectPolicy: new ReconnectPolicy({ initialBackoffMs: 500, maxBackoffMs: 30_000, maxAttempts: 20 }), onReconnect: async () => { const open = await client.orders({ status: ["open", "delayed"] }); console.log("reconnected, open orders:", open.length); }, }) .connect();reconnect: false turns reconnecting off. ws.connected and ws.reconnects report the socket’s state. After a reconnect explains what the exchange does and does not replay.
Errors
Section titled “Errors”A refused join throws STXChannelException, with the server’s reason in err.reply. No reply within joinTimeoutMs (default 10000) throws STXTimeoutException.
Keep a worker running
Section titled “Keep a worker running”runForever() resolves when you call close() or the socket gives up:
const ws = await client.websocket().connect();await ws.fills({ onMessage: (m) => console.log("fill", m.payload) });process.on("SIGINT", () => void ws.close());await ws.runForever();
