Skip to content

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.

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.

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 callback
await ws.trades({ marketIds: ids, onMessage: (msg) => console.log(msg.event, msg.payload) });
// One message, with a timeout in milliseconds
const 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 closes
for 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.

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

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.

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.

A refused join throws STXChannelException, with the server’s reason in err.reply. No reply within joinTimeoutMs (default 10000) throws STXTimeoutException.

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();
v1.5.9Changelogllms.txtllms-full.txt