# Stream Reference

Prediction Markets WebSocket streams use the same protocol as the main WebSocket API. Symbols follow the prediction markets format (e.g. `GEMI-BTC05M2606011000-UP`). BTC 5-minute contracts expire every 5 minutes — the StreamTester below uses a longer-lived example; for live testing substitute an active `instrumentSymbol` from `GET /v1/prediction-markets/events`.

Use WebSocket as the preferred path for active trading and market making. REST is still useful for public event discovery and for recovery or audit snapshots after restarts, reconnects, missed messages, or settlement windows.

## Stream Matrix

| Stream | Auth | Use for | Start here |
|----|----|----|----|
| [`{symbol}@bookTicker`](#book-ticker) | Public | Best bid/ask prices and quantities | First price watcher |
| [`{symbol}@depth5`, `{symbol}@depth10`, `{symbol}@depth20`](#l2-partial-depth-streams) | Public | Periodic top-of-book snapshots | Simple dashboards |
| [`{symbol}@depth`, `{symbol}@depth@100ms`](#l2-differential-depth-streams) | Public | Maintaining a local order book | Market making |
| [`{symbol}@trade`](#trade-stream) | Public | Recent executions | Trade tape and analytics |
| [`orders@account`](#order-events) | Authenticated | Order lifecycle events | Order state tracking |
| [`balances@account`](#balance-updates) | Authenticated | Balance changes | Risk checks |
| [`positions@account`](#position-updates) | Authenticated | Low-latency position deltas | Exposure tracking |
| [`contractStatus`](#contract-status) | Public | Contract status and strike updates | Lifecycle monitors |

:::note
Authenticated streams require Gemini authentication headers during the WebSocket upgrade. Browser WebSocket clients cannot add those headers, so use a non-browser client or server-side proxy for account streams.
:::

## Book Ticker

| Schema | Frequency | Description |
|----|----|----|
| `{symbol}@bookTicker` | Real-time | Real time updates to the best bid/ask price for an order book. |

<StreamTester streamName="GEMI-BTC05M2606011000-UP@bookTicker" url="wss://ws.gemini.com" />

```json
{
  "u": 1751505576085,
  "E": 1751508438600117161,
  "s": "GEMI-BTC05M2606011000-UP",
  "b": "0.48",
  "B": "5000",
  "a": "0.52",
  "A": "3200"
}
```

| Field | Type   | Description              |
|-------|--------|--------------------------|
| `u`   | number | Update ID                |
| `E`   | number | Event time (nanoseconds) |
| `s`   | string | Symbol                   |
| `b`   | string | Best bid price           |
| `B`   | string | Best bid quantity        |
| `a`   | string | Best ask price           |
| `A`   | string | Best ask quantity        |

---

## L2 Partial Depth Streams

| Schema | Frequency | Description |
|----|----|----|
| `{symbol}@depth5` | Periodic (1s) | Periodic snapshot of the top 5 levels once per second |
| `{symbol}@depth10` | Periodic (1s) | Top 10 levels |
| `{symbol}@depth20` | Periodic (1s) | Top 20 levels |
| `{symbol}@depth5@100ms` | Periodic (100ms) | Top 5 levels every 100 milliseconds |
| `{symbol}@depth10@100ms` | Periodic (100ms) | Top 10 levels |
| `{symbol}@depth20@100ms` | Periodic (100ms) | Top 20 levels |

<StreamTester streamName="GEMI-BTC05M2606011000-UP@depth10@100ms" url="wss://ws.gemini.com" />

```json
{
  "lastUpdateId": 12345678,
  "bids": [
    ["0.26", "5000"],
    ["0.25", "2000"]
  ],
  "asks": [
    ["0.28", "3200"],
    ["0.29", "1500"]
  ]
}
```

| Field          | Type     | Description                  |
|----------------|----------|------------------------------|
| `lastUpdateId` | number   | Last update ID               |
| `bids`         | array    | Array of [price, quantity]   |
| `asks`         | array    | Array of [price, quantity]   |

:::note
Use a depth snapshot as the starting point for any local order book or dollar calculation, then apply differential depth updates to keep it current. Each level is `[price, quantity]`. Public depth for event contracts is normalized in YES space: for YES notional, compute `yesPrice * quantity`; for NO notional, compute `(1 - yesPrice) * quantity`.
:::

---

## L2 Differential Depth Streams

| Schema | Frequency | Description |
|----|----|----|
| `{symbol}@depth` | Periodic (1s) | List of all changed price levels in the last second |
| `{symbol}@depth@100ms` | Periodic (100ms) | In the last 100 milliseconds |

<StreamTester streamName="GEMI-BTC05M2606011000-UP@depth@100ms" url="wss://ws.gemini.com" />

:::note
Quantity zero indicates price level removal.
:::

To maintain a local book, request a `depth` snapshot first and store its `lastUpdateId`. Ignore any differential update whose `u` is less than or equal to that snapshot ID. Apply the first update where `U <= lastUpdateId + 1 <= u`, then continue applying updates in sequence. If a later update skips ahead of the last applied `u`, discard the local book and resync from a fresh `depth` snapshot.

```json
{
  "e": "depthUpdate",
  "E": 1751508260659505382,
  "s": "GEMI-BTC05M2606011000-UP",
  "U": 12345677,
  "u": 12345678,
  "b": [
    ["0.48", "5000"],
    ["0.47", "0.00"]
  ],
  "a": [
    ["0.52", "3200"]
  ]
}
```

| Field | Type   | Description                        |
|-------|--------|------------------------------------|
| `e`   | string | Event type ("depthUpdate")         |
| `E`   | number | Event time (nanoseconds)           |
| `s`   | string | Symbol                             |
| `U`   | number | First update ID in this event      |
| `u`   | number | Last update ID in this event       |
| `b`   | array  | Bid updates [price, quantity]      |
| `a`   | array  | Ask updates [price, quantity]      |

---

## Trade Stream

| Schema | Frequency | Description |
|----|----|----|
| `{symbol}@trade` | Real-time | Real time trade executions |

<StreamTester streamName="GEMI-BTC05M2606011000-UP@trade" url="wss://ws.gemini.com" />

```json
{
  "E": 1759873803503023900,
  "s": "GEMI-BTC05M2606011000-UP",
  "t": 2840140956529623,
  "p": "0.50",
  "q": "10",
  "m": true
}
```

| Field | Type    | Description              |
|-------|---------|--------------------------|
| `E`   | number  | Event time (nanoseconds) |
| `s`   | string  | Symbol                   |
| `t`   | number  | Trade ID                 |
| `p`   | string  | Price                    |
| `q`   | string  | Quantity                 |
| `m`   | boolean | Is buyer the maker       |

---

## Order Events

:::warning
Requires an authenticated session
:::

| Schema | Frequency | Description |
|----|----|----|
| `orders@account` | Real-time | Real time order activity for the account associated with the authenticated API key |
| `orders@session` | Real-time | Real time order activity for the authenticated API key |

Order Event - New:

```json
{
  "E": 1759291847686856569,
  "s": "GEMI-BTC05M2606011000-UP",
  "i": 73797746498585286,
  "c": "btc-5m-quote-001",
  "S": "BUY",
  "o": "LIMIT",
  "X": "NEW",
  "O": "YES",
  "p": "0.48000",
  "q": "10",
  "z": "10",
  "T": 1759291847686856569
}
```

Order Event - Canceled:

```json
{
  "E": 1759291847731455006,
  "s": "GEMI-BTC05M2606011000-UP",
  "i": 73797746498585286,
  "c": "btc-5m-quote-001",
  "X": "CANCELED",
  "T": 1759291847731455006
}
```

| Field | Type    | Description             |
|-------|---------|-------------------------|
| `E`   | number  | Event time (nanoseconds) |
| `s`   | string  | Symbol              |
| `i`   | number  | Order ID              |
| `c`   | string  | Client Order ID              |
| `S`   | string  | Side, `BUY / SELL`              |
| `o`   | string  | Type, `LIMIT / MARKET`              |
| `X`   | string  | Status, `NEW / OPEN / FILLED / PARTIALLY_FILLED / CANCELED / REJECTED / MODIFIED`              |
| `O`   | string  | Event outcome, `YES / NO` |
| `p`   | string  | Order price              |
| `q`   | string  | Original quantity              |
| `z`   | string  | Remaining quantity              |
| `Z`   | string  | Executed quantity. For `FILLED` / `PARTIALLY_FILLED` events, this is the quantity filled in the last execution. For `CANCELED` and other events, this is the cumulative quantity filled over the lifetime of the order. |
| `L`   | string  | Last execution price              |
| `t`   | number  | Trade ID              |
| `n`   | string  | Fee amount (only present in 'FILLED' events)              |
| `r`   | string  | Rejection reason              |
| `T`   | number  | Update time (nanoseconds) |

:::note
Fields with empty or zero values may be omitted from the event.
:::

#### Rejection Reasons

When an order is `REJECTED`, the `r` field contains one of:

| Reason | Description |
|--------|-------------|
| `MarketNotOpen` | Market is closed or paused |
| `InsufficientFunds` | Account lacks sufficient balance |
| `InvalidPrice` | Price must be between $0.01–$0.99 |
| `LimitPriceOffTick` | Price does not align with tick size |
| `InvalidQuantity` | Quantity below minimum or off increment |
| `InvalidTotalSpend` | Total spend calculation error |
| `DuplicateOrder` | Duplicate client order ID |
| `InsufficientLiquidity` | Not enough liquidity at price |
| `UnknownInstrument` | Trading pair does not exist |
| `TERMS_NOT_ACCEPTED` | Latest Prediction Markets terms not accepted. Use the REST terms endpoints to read, check, and accept terms before retrying. |

#### Cancellation Reasons

When an order is `CANCELED` by the system, the `r` field contains one of:

| Reason | Description |
|--------|-------------|
| `SelfCrossPrevented` | Self-trade prevention triggered |
| `FillOrKillWouldNotFill` | FOK order could not fill completely |
| `ImmediateOrCancelWouldPost` | IOC order would post to book |
| `MakerOrCancelWouldTake` | MOC order would take liquidity |
| `AuctionCancelled` | Auction-related cancellation |
| `ExceedsPriceLimits` | Price moved beyond limits |

---

## Balance Updates

:::warning
Requires an authenticated session
:::

| Schema | Frequency | Description |
|----|----|----|
| `balances@account` | Real-time | Real time balance updates for the account associated with the authenticated API key |
| `balances@account@1s` | Periodic (1s) | Periodic snapshot of all balances every second for the account associated with the authenticated API key |

The `balances@account` stream pushes updates in real time whenever a balance change occurs, and only includes the assets that changed. The `balances@account@1s` stream sends a complete snapshot of all account balances every second, regardless of whether they changed. On subscribe, `balances@account@1s` will immediately send the current balances if available.

Balance Update:

```json
{
  "e": "balanceUpdate",
  "E": 1768250434780,
  "u": 1768250421600,
  "B": [
    {
      "a": "USD",
      "f": "207.39",
      "c": "207.39"
    }
  ]
}
```

| Field | Type    | Description             |
|-------|---------|-------------------------|
| `e`   | string | Event type ("balanceUpdate")         |
| `E`   | number | Event time (nanoseconds)              |
| `u`   | number | Time of the last account update (nanoseconds) |
| `B`   | array  | Balance updates              |
| `a`   | string | Asset code              |
| `f`   | string | Available balance (amount available to trade)              |
| `c`   | string | Confirmed balance (total balance including pending)              |

---

## Position Updates

:::warning
Requires an authenticated session
:::

| Schema | Frequency | Description |
|----|----|----|
| `positions@account` | Real-time | Real time event-contract position updates for the account associated with the authenticated API key |
| `positions@account@1s` | Periodic (1s) | Periodic snapshot of all open event-contract positions every second for the account associated with the authenticated API key |

The `positions@account` stream pushes deltas in real time on fill, position open/close, and settlement-precursor events, and only includes the rows that changed. The `positions@account@1s` stream sends a complete snapshot of all open positions every second, regardless of whether they changed. On subscribe, `positions@account@1s` will immediately send the current positions if available.

Use `positions@account` for low-latency event-contract position deltas on the same WebSocket connection you use for trading. Reconcile with `POST /v1/prediction-markets/positions` after reconnects, missed messages, and settlement windows.

:::note
Connect to `wss://ws.gemini.com`. Authentication must be provided on the WebSocket upgrade — you cannot authenticate after the connection is established. Use either HMAC-signed API key headers (account-scoped only; master/group keys are rejected with HTTP 401) or an OAuth 2.0 bearer token (`Authorization: Bearer <access_token>`). See [Authentication](/prediction-markets/websocket/authentication).
:::

Subscribe after the connection opens:

```json
{
  "id": "1",
  "method": "SUBSCRIBE",
  "params": ["positions@account"]
}
```

Server acknowledgement:

```json
{
  "id": "1",
  "status": 200
}
```

Snapshot and delta frames share the same shape:

```json
{
  "e": "positionReport",
  "E": 1760000000000000000,
  "u": 1759999999000000000,
  "A": 12345,
  "P": [
    {
      "t": "ec",
      "s": "GEMI-BTC05M2606011000-UP",
      "a": [
        { "t": "position", "v": "2.5" }
      ]
    }
  ]
}
```

| Field | Type | Description |
|-------|------|-------------|
| `e` | string | Event type, always `positionReport` |
| `E` | number | Event timestamp in nanoseconds |
| `u` | number | Last account update timestamp in nanoseconds |
| `A` | number | Account ID |
| `P` | array | Position rows for this account; an empty array means no open position rows are included |
| `P[].t` | string | Product type. Event contracts use `ec` |
| `P[].s` | string | Instrument symbol |
| `P[].a` | array | Named amount array |
| `P[].a[].t` | string | Amount label. Position quantity uses `position` |
| `P[].a[].v` | string | Decimal string position size. Negative values denote short positions |
| `P[].a[].c` | string | Optional asset code. Omitted for unit-less values such as position quantity |

:::note
The first subscription for an account returns a snapshot of currently open positions. Subsequent frames are deltas carrying only rows that changed. A position close emits a row with `position` value `"0"` before that row is evicted; later snapshots omit zero-position rows. Event-contract settlement is silent on this stream after the terminal close frame. The `a` array is intentionally extensible; clients should ignore unknown amount labels instead of failing.
:::

---

## Contract Status

Prediction-market contract lifecycle events — status transitions (e.g. `Awaiting Approval` → `Approved` → `Active`) and strike-populated moments for Up/Down contracts.

| Schema | Frequency | Description |
|----|----|----|
| `contractStatus` | Real-time | Status changes and strike-price updates for prediction-market contracts |

```json
# Strike-based contract (e.g. HI78999D63)
{
  "e": "contractStatus",
  "E": 1776871540195,
  "s": "gemi-btc15m2604221545-hi78999d63",
  "k": "btc15m2604221545",
  "c": "HI78999D63",
  "i": 134794,
  "p": "78999.63",
  "o": "Awaiting Approval",
  "n": "Approved"
}

# Up/Down contract (no numeric strike — `p` omitted until populated)
{
  "e": "contractStatus",
  "E": 1776871295498,
  "s": "gemi-btc05m2604221630-up",
  "k": "btc05m2604221630",
  "c": "UP",
  "i": 134791,
  "o": "Awaiting Approval",
  "n": "Approved"
}
```

| Field | Type   | Description |
|-------|--------|-------------|
| `e`   | string | Event type (`contractStatus`) |
| `E`   | number | Event time (Unix milliseconds) |
| `s`   | string | Instrument symbol |
| `k`   | string | Event ticker |
| `c`   | string | Contract ticker (e.g. `HI78999D63`, `UP`, `DOWN`) |
| `i`   | number | Contract ID |
| `p`   | string | Strike price parsed from the contract ticker. Omitted for Up/Down contracts until the strike is set at activation |
| `o`   | string | Previous status |
| `n`   | string | New status |

:::note
For Up/Down contracts, `p` is omitted while the strike is unknown and included once it is set — subscribers can detect strike availability by the field's presence.
:::
