# TypeScript SDK — WebSocket

The SDK provides real-time market data and account updates over WebSocket. Public streams require no authentication. Authenticated streams (orders, balances) require an auth strategy and run only on the server entry point.

## Public streams

Subscribe to market data without authentication:

```ts
import { createClient } from "gemini-markets/browser";

const client = createClient({ env: "sandbox" });

// Real-time trades
const trades = client.websocket.trades("BTCUSD");
trades.on("message", (trade) => {
  console.log(trade.p, trade.q, trade.m); // price, quantity, is-maker
});
await trades.ready; // wait for subscription confirmation

// Book ticker (best bid/ask)
const ticker = client.websocket.bookTicker("BTCUSD");
ticker.on("message", (tick) => console.log(tick));

// Depth updates (order book diffs)
const depthUpdates = client.websocket.depthUpdates("BTCUSD");
depthUpdates.on("message", (update) => console.log(update));

// Depth snapshots (top N levels)
const depthSnapshot = client.websocket.depth("BTCUSD", { levels: 10 });
depthSnapshot.on("message", (snapshot) => console.log(snapshot));
```

Public streams share a single underlying WebSocket connection. Opening multiple streams to different symbols reuses the same session.

### Closing a stream

```ts
await trades.close(); // sends unsubscribe, waits for acknowledgement
```

Closing one stream does not affect others on the shared session. Call `client.close()` to shut down all streams and connections.

## Live order book

The SDK maintains a self-healing L2 order book from WebSocket depth data:

```ts
const book = client.orderBook("BTCUSD");

book.on("update", (lob, delta) => {
  console.log("Best bid:", lob.bestBid());
  console.log("Best ask:", lob.bestAsk());
  console.log("Spread:", lob.spread());
  console.log("Mid:", lob.mid());
  console.log("Top 5 bids:", lob.topN("bids", 5));
  console.log("Changed levels:", delta);
});

book.on("resync", () => {
  // A gap was detected in the update stream.
  // The book is rebuilding from a fresh snapshot.
  // Treat current state as stale until the next "update" event.
  console.warn("Order book resyncing — data may be stale");
});

book.on("error", (err) => console.error(err));
```

The first `"update"` event after subscribing (or after a `"resync"`) carries the **full book** — treat it as a replacement, not an incremental diff. Subsequent updates carry only changed levels; a quantity of `"0"` means the level was removed.

`spread()` and `mid()` return floating-point values for display. Do not use them for exact execution decisions without decimal handling.

```ts
book.close(); // stop updates and release the stream
```

## Authenticated streams

Authenticated streams require the server entry point and an auth strategy. They use the `ws` package to set custom headers on the WebSocket upgrade request.

```ts
import { createClient, HmacAuth } from "gemini-markets/server";

const client = await createClient({
  env: "sandbox",
  auth: new HmacAuth({ apiKey, apiSecret }),
});

// Order updates for the current session
const orders = client.websocket.orders({ scope: "session" });
orders.on("message", (order) => {
  console.log(order.i, order.X, order.z); // orderId, status, remainingQuantity
});

// Account-wide balance updates
const balances = client.websocket.balances();
balances.on("message", (update) => {
  for (const b of update.B) {
    console.log(b.a, b.f, b.c); // asset, free, locked
  }
});

await orders.ready;
```

### Browser limitation

Browser `WebSocket` cannot set custom HTTP headers on the upgrade request. Authenticated WebSocket streams (which require HMAC headers) only work from the server entry point. Browser apps can use:
- Public streams (trades, depth, book tickers) — no auth needed
- REST endpoints via OAuth for authenticated operations

### WebSocket order operations

Place and cancel orders over WebSocket for lower latency:

```ts
// Place an order
const result = await client.websocket.placeOrder({
  symbol: "BTCUSD",
  side: "BUY",
  type: "LIMIT",
  price: "50000.00",
  quantity: "0.001",
  timeInForce: "GTC",
});
// response: { id (request correlation ID), status, result? }
// The exchange order ID is inside the result object:
const orderResult = result.result as Record<string, unknown> | undefined;
if (!orderResult?.orderId) throw new Error("Place response missing orderId");

// Cancel an order using the exchange order ID
await client.websocket.cancelOrder({ orderId: String(orderResult.orderId) });
```

Cancellation methods that affect multiple orders require explicit confirmation:

```ts
await client.websocket.cancelAllOrders({ confirm: true });
await client.websocket.cancelSessionOrders({ confirm: true });
```

## Reconnection

WebSocket connections reconnect automatically on disconnection with exponential backoff:

- Public stream subscriptions are replayed after reconnect
- Authenticated streams re-authenticate with fresh credentials (nonces and tokens are regenerated)
- **Mutating requests (order placement/cancellation) are never replayed** — they reject with an error if the connection drops mid-request

Monitor reconnection:

```ts
const stream = client.websocket.trades("BTCUSD");

stream.on("resubscribed", () => {
  console.log("Stream reconnected and resubscribed");
});

stream.on("subscriptionError", (err) => {
  console.error("Resubscription failed:", err);
});
```

### Stream state

```ts
stream.state;            // "active" | "reconnecting" | "failed" | "closed"
stream.lastError;        // the last error, if any
stream.malformedFrameCount; // count of frames that couldn't be parsed
```

## GeminiWebSocket vs WsSession

The SDK exposes two lower-level WebSocket classes if you need more control:

| Class | Use when |
| --- | --- |
| `GeminiWebSocket` | You want the full typed stream API (trades, depth, orders) but outside of a `GeminiMarkets` client |
| `WsSession` | You want raw WebSocket request/response correlation without stream abstractions |

Most applications should use `client.websocket` via `createClient()`. The lower-level classes are for advanced use cases like custom stream routing or multi-session architectures.

## What's next

- [WebSocket Reference](/tools/typescript-sdk/reference/websocket) — every stream, method, and wire-format field
- [Order Book Reconstruction](/tools/typescript-sdk/deep-dives/order-book) — snapshot + diff internals and gap recovery
- [Error handling](/tools/typescript-sdk/errors) — WebSocket-specific errors and connection failures
- [Patterns & recipes](/tools/typescript-sdk/patterns) — heartbeat, liveness checks, and advanced configuration
