> For the complete documentation index, see [llms.txt](https://docs.flash.trade/flash-trade/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.flash.trade/flash-trade/flash-trade-protocol/build-on-flash/flash-trade-api/flash-trade-v2/websocketstreaming.md).

# WebSocketStreaming

Flash Trade **v2** streams a wallet's entire v2 trading state — positions and orders — as a single **basket snapshot**. Unlike v1 (which streams separate `positions` and `orders` arrays), v2 ships the raw basket bytes plus precomputed per-market metrics, and the client decodes the bytes with the v2 client SDK.

There are two ways to read the snapshot:

* **One-shot HTTP:** `GET /v2/owner/{owner}` — for initial loads or environments that can't hold a WebSocket.
* **Live WebSocket:** `GET /v2/owner/{owner}/ws` — the live source of truth.

## `GET /v2/owner/{owner}/ws`

Upgrades to a WebSocket that streams the owner's basket state.

| Parameter          | In    | Type   | Required | Description                                                               |
| ------------------ | ----- | ------ | -------- | ------------------------------------------------------------------------- |
| `owner`            | path  | string | yes      | Owner wallet public key (base58)                                          |
| `updateIntervalMs` | query | number | no       | Metrics recompute interval in ms (default `1000`, min `100`, max `10000`) |

**Connection example:**

```
wss://$FLASH_API_URL/v2/owner/bRXn4PPnUCLKP3kdHbX6FFQFpw2AhFzBVMgo4ZaWvtfb/ws?updateIntervalMs=1000
```

## Message Protocol

The server sends two JSON message types.

### `basket` — full snapshot

Sent once on connect, then again on every basket account update (driven by gRPC). Contains the raw basket bytes plus all per-market metrics.

```json
{
  "type": "basket",
  "data": {
    "owner": "bRXn4PPnUCLKP3kdHbX6FFQFpw2AhFzBVMgo4ZaWvtfb",
    "basketPubkey": "…",
    "basketData": "base64-encoded-raw-basket-bytes…",
    "positionMetrics": { "<marketPubkey>": { /* MbPositionMetricsDto */ } },
    "orderMetrics": { "<marketPubkey>": { /* MbOrderMetricsDto */ } }
  }
}
```

### `metrics` — metrics-only tick

Sent on every oracle tick (at `updateIntervalMs`) so PnL / leverage / liquidation refresh without re-shipping the basket bytes or order metrics.

```json
{
  "type": "metrics",
  "data": { "<marketPubkey>": { /* MbPositionMetricsDto */ } }
}
```

> **Decoding the basket.** `basketData` is the raw on-chain basket account (base64). Decode it with the v2 client SDK to get native `PositionMeta[]` / `OrderMeta[]`. The `positionMetrics` / `orderMetrics` maps give you UI-ready strings so you don't have to recompute PnL, leverage, or price formatting.

## Snapshot Payload (`MbBasketSnapshotDto`)

| Field             | Type                        | Description                                                                 |
| ----------------- | --------------------------- | --------------------------------------------------------------------------- |
| `owner`           | string                      | Owner wallet pubkey                                                         |
| `basketPubkey`    | string?                     | Basket PDA (derived from owner)                                             |
| `basketData`      | string?                     | Base64 raw basket bytes. `null` if the basket hasn't been ingested yet      |
| `positionMetrics` | map\<marketPubkey, metrics> | Per-position derived metrics; only active-pool positions with non-zero size |
| `orderMetrics`    | map\<marketPubkey, orders>  | Per-market order metrics; only markets with a non-empty limit/TP/SL slot    |

### `MbPositionMetricsDto` (per position)

UI-ready display strings plus raw native-unit values.

| Field                                         | Type    | Description                                        |
| --------------------------------------------- | ------- | -------------------------------------------------- |
| `marketSymbol`                                | string  | Market token symbol                                |
| `collateralSymbol`                            | string  | Collateral token symbol                            |
| `sideUi`                                      | string  | `"Long"` / `"Short"`                               |
| `entryPriceUi`                                | string  | Entry price, UI-formatted                          |
| `sizeAmountUi`                                | string  | Position size in target token                      |
| `sizeAmountUiKmb`                             | string? | Size abbreviated (e.g. `1.2K`)                     |
| `sizeUsdUi`                                   | string  | Position size in USD                               |
| `collateralAmountUi`                          | string  | Collateral in collateral token                     |
| `collateralUsdUi`                             | string  | Collateral in USD                                  |
| `pnlWithFeeUsdUi`                             | string  | Signed PnL net of fees (e.g. `"-0.02"`, `"12.50"`) |
| `pnlPercentageWithFee`                        | string  | Signed PnL % of collateral                         |
| `pnlWithoutFeeUsdUi`                          | string  | Signed PnL before fees                             |
| `pnlPercentageWithoutFee`                     | string  | Signed PnL % before fees                           |
| `liquidationPriceUi`                          | string  | Liquidation price, UI-formatted                    |
| `leverageUi`                                  | string  | Leverage (e.g. `"5.00"`) or `"Infinity"`           |
| `profitUsd` / `lossUsd`                       | string  | Raw USD profit / loss                              |
| `exitFeeUsd` / `borrowFeeUsd` / `totalFeeUsd` | string  | Raw fee components                                 |
| `marginUsd`                                   | string  | Margin in USD                                      |
| `leverage`                                    | string  | Raw leverage in BPS (u128; divide by BPS\_POWER)   |
| `liquidationPrice`                            | object  | Liquidation price as `{ price, exponent, … }`      |
| `exitPrice`                                   | object  | Current oracle price used for the PnL calc         |

### `MbOrderMetricsDto` (per market)

| Field              | Type   | Description                  |
| ------------------ | ------ | ---------------------------- |
| `marketSymbol`     | string | Market token symbol          |
| `sideUi`           | string | `"Long"` / `"Short"`         |
| `limitOrders`      | array  | `MbLimitOrderMetricsDto[]`   |
| `takeProfitOrders` | array  | `MbTriggerOrderMetricsDto[]` |
| `stopLossOrders`   | array  | `MbTriggerOrderMetricsDto[]` |

Each limit order includes reserve/size/collateral amounts (UI + USD), entry & liquidation prices, leverage, and attached TP/SL prices. Each trigger order includes `orderId`, `type` (`"TP"`/`"SL"`), `triggerPriceUi`, size, and leverage.

## Connection Behavior

* On connect, the server waits for the first computed snapshot, then sends one full `basket` message — guaranteeing the client has basket bytes before any `metrics`-only ticks arrive.
* `basket` messages are re-sent whenever the basket account changes on-chain (event-driven via gRPC).
* `metrics` messages are sent on the oracle-tick cadence (`updateIntervalMs`) so PnL/leverage/liq keep refreshing cheaply.
* The server sends WebSocket **Ping** frames every 30s; if no **Pong** is received within 10s, the connection is closed.
* The client doesn't need to send any messages after connecting.

## Connection Limits

Shared with the v1 API.

| Limit                 | Value  |
| --------------------- | ------ |
| Global connections    | 10,000 |
| Per-owner connections | 5      |

| Status | Condition                                            |
| ------ | ---------------------------------------------------- |
| `429`  | Per-owner connection limit reached (5 connections)   |
| `503`  | Global connection limit reached (10,000 connections) |

## Client Example (TypeScript)

```typescript
const ws = new WebSocket(
  `wss://flashapi.trade/v2/owner/${owner}/ws?updateIntervalMs=1000`
);

let latestBasketBytes: Uint8Array | null = null;

ws.onmessage = (ev) => {
  const msg = JSON.parse(ev.data);
  if (msg.type === "basket") {
    latestBasketBytes = Buffer.from(msg.data.basketData, "base64");
    // Decode with the v2 client SDK → PositionMeta[] / OrderMeta[]
    renderPositions(msg.data.positionMetrics, msg.data.orderMetrics);
  } else if (msg.type === "metrics") {
    // Refresh PnL/leverage/liq without re-decoding the basket
    updateMetrics(msg.data);
  }
};
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.flash.trade/flash-trade/flash-trade-protocol/build-on-flash/flash-trade-api/flash-trade-v2/websocketstreaming.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
