# Streaming

> The per-account WebSocket protocol. Full sync, incremental updates, market data, sequence numbers and resume.

Source: https://tickwire.io/docs/streaming/

There are two ways to stream:

- **A socket.io protocol** for existing clients that already speak it, alongside the
  `/users/current/…` REST API. See [Switching from another provider](https://tickwire.io/docs/switching/).
- **Tickwire's plain WebSocket protocol** (this page): JSON frames, one socket per account, easy to
  use from any language without an SDK.

## Connect

```
wss://api.tickwire.io/users/current/accounts/{accountId}/ws?auth-token=<token>
```

Or, better, leave the key out of the URL and send it as the `auth-token` (or `X-API-Key`, or
`Authorization: Bearer`) header on the upgrade, if your client can set headers. Browsers can't, so
they use the query parameter; give a browser a short-lived [scoped token](https://tickwire.io/docs/authentication/#token-kinds),
not a full key. See [WebSockets: header or query parameter](https://tickwire.io/docs/authentication/#websockets-header-or-query-parameter).

## Subscribe

```json
{
  "type": "subscribe",
  "sync": { "startingHistoryTime": "2026-09-01T00:00:00Z", "lastSeq": null },
  "marketData": [
    { "symbol": "EURUSD", "subscriptions": [
      { "type": "quotes", "intervalInMilliseconds": 500 },
      { "type": "candles", "timeframe": "1m" },
      { "type": "ticks" },
      { "type": "marketDepth" }
    ] }
  ]
}
```

- `sync.startingHistoryTime`: send history (deals and history orders) from this time. `null` skips
  history.
- `sync.lastSeq`: resume after a disconnect (below).
- `marketData`: optional. `ticks` and `marketDepth` are MT5 only.

Send `{"type": "unsubscribe", "marketData": [...]}` to drop market data, and `{"type": "ping"}`
whenever you like; you'll get `pong`.

## Frames

Every server frame has `type`, `seq` (increasing by one per frame) and `time` (UTC).

**Full synchronization**, in this order:

| Frame | Carries |
|---|---|
| `synchronizationStarted` | — |
| `accountInformation` | `accountInformation` |
| `positions` | the full list (replace yours) |
| `orders` | the full list of pending orders (replace yours) |
| `historyOrders` | a batch; may repeat |
| `deals` | a batch; may repeat |
| `positionsSynchronized`, `ordersSynchronized`, `historyOrdersSynchronized`, `dealsSynchronized` | — |
| `synchronized` | Your state is complete |

**Incremental updates** after that: `accountInformationUpdated`, `positionUpdated`,
`positionRemoved`, `orderUpdated`, `orderCompleted`, `historyOrderAdded`, `dealAdded`.

**Market data:** `prices` (with the account's `equity`, `margin`, `freeMargin` and `marginLevel`),
`candles`, `ticks` and `book`.

**Status:** `health` (our [health snapshot](https://tickwire.io/docs/health/)) whenever it changes, and
`brokerConnectionStatus` with `connected: true|false`.

**Warnings:** `warning`, for example when you read too slowly (below).

```json
{ "type": "positionUpdated", "seq": 1842, "time": "2026-09-23T11:02:14.120Z",
  "position": { "id": "46870472", "symbol": "EURUSD", "type": "POSITION_TYPE_BUY", "volume": 0.01,
                "openPrice": 1.07182, "profit": 0.42, "brokerTime": "2026-09-23 14:02:14.120" } }
```

## Resume after a disconnect

Remember the last `seq` you processed. Reconnect and subscribe with `"lastSeq": <that seq>`:

- If we still hold the frames after it, we replay them and carry on. No gap, no full sync.
- If not, we run a full synchronization. Replace your state when `synchronized` arrives.

## Slow consumers

If you don't read fast enough, we don't buffer forever. We drop frames and send:

```json
{ "type": "warning", "seq": 2210, "code": "slow_consumer", "firstMissingSeq": 2011 }
```

Reconnect with `lastSeq: 2010` to get the missing frames back, or accept a fresh sync.

## Heartbeats

We send a frame at least every 15 seconds, a `health` frame if nothing else is happening. If you
hear nothing for 60 seconds, reconnect.
