# Market Data Protocol Specification

The **CryptoStruct market data protocol** uses JSON arrays for every
message: requests, responses, and events. The first element of the array
is always an integer **message type**; the rest is type-specific.

```json
[
  msgType,
  ...
]
```

> [!TIP]
> The stand-alone release of this specification ships sample data for every
> message, in both JSON and [SBE](sbe.md) encoding. Download it from
> [cryptostruct.com/download](https://cryptostruct.com/download) and test
> your parser against it rather than hand-copying the examples below.

---

## Requests

### Login

Login is required before subscribing to any instrument.

#### Request

+++ Structure

```!#
[
  13, 
  organization,
  application_name,
  application_version,
  process_id
]
```

{.compact}

| Field                 | Type    | Description                                     |
|-----------------------|---------|-------------------------------------------------|
| `msgType`             | integer | Always `13`                                     |
| `organization`        | string  | Name of your organization or company            |
| `application_name`    | string  | Name or type of your application                |
| `application_version` | string  | Version of your application                     |
| `process_id`          | string  | ID to identify the instance of your application |

+++ Example

```json !#
[
  13,
  "ACME",
  "MyApp",
  "1.2.3",
  "MyApp_Tokyo"
]
```

+++

#### Response

+++ Structure

```!#
[
  14,
  process_id,
  application_version,
  protocol,
  capabilities
]
```

{.compact}

| Field                 | Type        | Description                                    |
|-----------------------|-------------|------------------------------------------------|
| `msgType`             | integer     | Always `14`                                    |
| `process_id`          | string      | Instance ID of the market data service         |
| `application_version` | string      | Service version                                |
| `protocol`            | string      | Version of the protocol                        |
| `capabilities`        | json object | Information about available market data topics |

+++ Example

```json !#
[
  14,
  "bitmex-master-a",
  "1.23.4",
  "5",
  {
    "depthTopic": {
      "eventIdType": "ORDERED",
      "exchangeTimestampType": "UNKNOWN",
      "exchangeTimestampPrecision": "MILLIS"
    },
    "topOfBookTopic": null,
    "tradesTopic": null,
    "crossTopicBookEventId": true
  }
]
```

+++

> [!NOTE]
> There is no explicit logout. To change login data, reconnect.

---

### Subscription

+++ Structure

```!#
[
  11,
  instrument,
  options
]
```

{.compact}

| Field        | Type        | Description                                      |
|--------------|-------------|--------------------------------------------------|
| `msgType`    | integer     | Always `11`                                      |
| `instrument` | long        | Instrument ID                                    |
| `options`    | json object | (Optional) Configuration for topics and features |

**Options (defaults shown):**

```json !#
{
  "depthTopic": true,
  "tradesTopic": true,
  "topOfBookTopic": true,
  "indexPriceTopic": true,
  "markPriceTopic": true,
  "fundingRateTopic": true,
  "liquidationsTopic": false,
  "topOfBookCoalescing": false
}
```

- **Topic flags**: pick which market-data topics to subscribe to.
- **`topOfBookCoalescing`**: when enabled, the **Market Data Adapter**
  drops outdated top-of-book updates during bursts or under load and
  publishes only the latest event.

+++ Example

```json !#
[
  11,
  22,
  {
    "depthTopic": false
  }
]
```

+++

**Behaviour:**

- On a successful subscription, the first message received is an initial
  snapshot of the order book, followed by other initial data and events.
- If the subscription fails, an error state event is received for the
  instrument.
- An instrument cannot be subscribed multiple times.

---

### Unsubscription

+++ Structure

```!#
[
  12,
  instrument
]
```

{.compact}

| Field        | Type    | Description   |
|--------------|---------|---------------|
| `msgType`    | integer | Always `12`   |
| `instrument` | long    | Instrument ID |

+++ Example

```json !#
[
  12,
  22
]
```

+++

---

## Events

### Common

Every event except **Instrument State** uses the same shell:

```!# Common Event Structure
[
  msgType,
  instrument,
  prevEventId,
  eventId,
  adapterTimestamp,
  exchangeTimestamp,
  data
]
```

**Common fields:**

{.compact}

| Field               | Type    | Description                                                                |
|---------------------|---------|----------------------------------------------------------------------------|
| `msgType`           | integer | Message type (see table below)                                             |
| `instrument`        | long    | Instrument ID                                                              |
| `prevEventId`       | string  | ID of the previous event (potentially empty/null)                          |
| `eventId`           | string  | ID of this event                                                           |
| `adapterTimestamp`  | long    | Receive timestamp (ns since epoch) at MDA; not guaranteed to be increasing |
| `exchangeTimestamp` | long    | Timestamp (ns since epoch) reported by exchange; 0 if not available        |
| `data`              | json    | Event-specific data                                                        |

**Message types:**

{.compact}

| msgType | Event            |
|---------|------------------|
| 0       | Snapshot         |
| 1       | Book update      |
| 2       | Trades           |
| 5       | Instrument state |
| 6       | Top-of-Book      |
| 7       | Mark price       |
| 8       | Index price      |
| 9       | Funding rate     |
| 17      | Liquidations     |

---

### Snapshot

A **snapshot** is the full state of the order book.

The Snapshot event differs from other events in one way: the trailing
`data` slot is replaced by two top-level fields, the levels array and a
`forceReset` boolean.

+++ Structure

```!#
[
  [side, price, quantity, ordercount],
  [side, price, quantity, ordercount],
  ...
],
forceReset
```

{.compact}

| Field        | Type    | Description                                                                                            |
|--------------|---------|--------------------------------------------------------------------------------------------------------|
| `side`       | integer | Book side: `0` (BID) or `1` (ASK)                                                                      |
| `price`      | string  | Price                                                                                                  |
| `quantity`   | string  | Quantity                                                                                               |
| `ordercount` | integer | Currently not used, always `1`                                                                         |
| `forceReset` | boolean | Reset the local book unconditionally, even if `prevEventId` does not match the last accepted event ID  |

> [!NOTE]
> `forceReset` only matters when consuming multiple feeds for feed
> arbitrage. On a single feed, every snapshot should be accepted and
> reset the local book.

+++ Example

```json !#
[
  0,
  123456,
  "12345678-1",
  "12345678-2",
  1580143531008103451,
  1580143530031129024,
  [
    [
      0,
      "7098.25",
      "10",
      1
    ],
    [
      1,
      "7099.25",
      "20",
      1
    ]
  ],
  true
]
```

+++

---

### Book Update

A **book update** is a level-based change to the order book.

+++ Structure

```!#
[
  [side, price, quantity, ordercount],
  [side, price, quantity, ordercount],
  ...
]
```

{.compact}

| Field        | Type    | Description                       |
|--------------|---------|-----------------------------------|
| `side`       | integer | Book side: `0` (BID) or `1` (ASK) |
| `price`      | string  | Price                             |
| `quantity`   | string  | Quantity                          |
| `ordercount` | integer | Currently not used, always `1`    |

+++ Example

```json !#
[
  1,
  123456,
  "12345678-1",
  "12345678-2",
  1580143531008103451,
  1580143530031129024,
  [
    [
      0,
      "7098.25",
      "10",
      1
    ],
    [
      1,
      "7099.25",
      "20",
      1
    ]
  ]
]
```

+++

---

### Trades

A list of trades that occurred.

+++ Structure

```!#
[
  [side, price, quantity, tradeId, exchangeTradeTimestamp],
  ...
]
```

{.compact}

| Field                    | Type    | Description                                                                            |
|--------------------------|---------|----------------------------------------------------------------------------------------|
| `side`                   | integer | Aggressive side: `0` (BID) or `1` (ASK)                                                |
| `price`                  | string  | Trade price                                                                            |
| `quantity`               | string  | Trade quantity                                                                         |
| `tradeId`                | string  | Exchange trade ID (potentially empty/null)                                             |
| `exchangeTradeTimestamp` | integer | Timestamp (ns since epoch) of trade; can differ from the message's `exchangeTimestamp` |

+++ Example

```json !#
[
  2,
  123456,
  "12345678-2",
  "12345679-3",
  1580143531008103451,
  1580143530031129024,
  [
    [
      0,
      "7098.25",
      "10",
      "6as78d678asdf78as789fas",
      11580143530031129024
    ]
  ]
]
```

+++

---

### Top-of-Book

An array with up to two levels (the top level per side).

+++ Structure

```!#
[
  [side, price, quantity, ordercount],
  [side, price, quantity, ordercount]
]
```

{.compact}

| Field        | Type    | Description                       |
|--------------|---------|-----------------------------------|
| `side`       | integer | Book side: `0` (BID) or `1` (ASK) |
| `price`      | string  | Price                             |
| `quantity`   | string  | Quantity                          |
| `ordercount` | integer | Currently not used, always `1`    |

**Behaviour:**

- If one side is empty, the array carries one element (the non-empty
  side).
- If both sides are empty, the array is empty.
- Bid and ask order is undefined.

+++ Example

```json !#
[
  6,
  123456,
  "12345678-1",
  "12345678-2",
  1580143531008103451,
  1580143530031129024,
  [
    [
      0,
      "7098.25",
      "10",
      1
    ],
    [
      1,
      "7099.25",
      "20",
      1
    ]
  ]
]
```

+++

---

### Mark Price

Mark price for the instrument. Option instruments also publish Greeks
and implied volatilities; non-option instruments leave those fields
`null`.

+++ Structure

```!#
markPrice, delta, gamma, vega, theta, markVolatility, bidVolatility, askVolatility
```

{.compact}

| Field            | Type   | Description                                          |
|------------------|--------|------------------------------------------------------|
| `markPrice`      | string | Mark price                                           |
| `delta`          | string | Sensitivity to underlying price (`null` if N/A)      |
| `gamma`          | string | Rate of delta change (`null` if N/A)                 |
| `vega`           | string | Sensitivity to volatility (`null` if N/A)            |
| `theta`          | string | Time decay (`null` if N/A)                           |
| `markVolatility` | string | Mark implied volatility (`null` if N/A)              |
| `bidVolatility`  | string | Bid implied volatility (`null` if N/A)               |
| `askVolatility`  | string | Ask implied volatility (`null` if N/A)               |

+++ Non-option example

```json !#
[
  7,
  123456,
  "12345678-1",
  "12345678-2",
  1580143531008103451,
  1580143530031129024,
  "123.456",
  null,
  null,
  null,
  null,
  null,
  null,
  null
]
```

+++ Option example

```json !#
[
  7,
  123456,
  "12345678-1",
  "12345678-2",
  1640995200300000000,
  1640995200200000000,
  "62604.69",
  "-0.9999672034",
  "0.0000000002",
  "0.0000114332",
  "28.2649858387",
  "0.3675503331",
  "0.36",
  "0.375"
]
```

+++

---

### Index Price

Index price for the instrument.

+++ Structure

```!#
price
```

{.compact}

| Field   | Type   | Description |
|---------|--------|-------------|
| `price` | string | Index price |

+++ Example

```json !#
[
  8,
  123456,
  "12345678-1",
  "12345678-2",
  1580143531008103451,
  1580143530031129024,
  "456.789"
]
```

+++

---

### Funding Rate

Last funding rate, time of next funding, and the predicted next funding
rate.

+++ Structure

```!#
[
  lastFundingRate,
  nextFundingTime,
  nextFundingRate
]
```

{.compact}

| Field                  | Type   | Description                                |
|------------------------|--------|--------------------------------------------|
| `lastFundingRate`      | string | Last funding rate                          |
| `nextFundingTime`      | long   | Timestamp (ns since epoch) of next funding |
| `nextFundingRate`      | string | Predicted next funding rate                |

+++ Example

```json !#
[
  9,
  123456,
  "12345678-1",
  "12345678-2",
  1580143531008103451,
  1580143530031129024,
  [
    "0.123",
    1580163530031129024,
    "0.123"
  ]
]
```

+++

---

### Liquidations

A list of **liquidation orders**.

> [!NOTE]
> Some exchanges publish only a sample of liquidations rather than all
> of them.

+++ Structure

```!#
[
  [side, price, quantity, exchangeLiquidationTimestamp],
  ...
]
```

+++ Fields

{.compact}

| Field                          | Type    | Description                                                                                  |
|--------------------------------|---------|----------------------------------------------------------------------------------------------|
| `side`                         | integer | Side: `0` (BID) or `1` (ASK)                                                                 |
| `price`                        | string  | Bankruptcy price                                                                             |
| `quantity`                     | string  | Liquidation quantity                                                                         |
| `exchangeLiquidationTimestamp` | integer | Timestamp (ns since epoch) of liquidation; can differ from the message's `exchangeTimestamp` |

+++ Example

```json !#
[
  17,
  123456,
  "12345678-2",
  "12345679-3",
  1580143531008103451,
  1580143530031129024,
  [
    [
      0,
      "7098.25",
      "10",
      11580143530031129024
    ]
  ]
]
```

+++

---

### Instrument State

The state of the instrument. Moves to `ERROR` when the connection to the
exchange drops or when the subscription fails (e.g. unknown instrument).

> [!WARNING]
> The instrument state message is shaped differently from the others:
> no event IDs, no exchange timestamp.

+++ Structure

```!#
[
  msgType,
  instrument,
  adapterTimestamp,
  state,
  message
]
```

+++ Fields

{.compact}

| Field              | Type    | Description                                                 |
|--------------------|---------|-------------------------------------------------------------|
| `msgType`          | integer | Always `5`                                                  |
| `instrument`       | long    | Instrument ID                                               |
| `adapterTimestamp` | long    | System timestamp (ns since epoch) at state event occurrence |
| `state`            | string  | Market data state of the instrument: `"ERROR"` or `"READY"` |
| `message`          | string  | Error details (potentially empty)                           |

+++ Example

```json !#
[
  5,
  123456,
  1580143531008103451,
  "ERROR",
  "connection lost"
]
```

+++

---

## Market Data Capabilities

Available capabilities depend on the exchange and on the **Market Data
Adapter** version. Capabilities describe which data feeds are available
and how they behave.

### Capability structure

```json
{
  "depthTopic": {
    "eventIdType": "ORDERED",
    "exchangeTimestampType": "UNKNOWN",
    "exchangeTimestampPrecision": "MILLIS"
  },
  "topOfBookTopic": {
    "eventIdType": "ORDERED",
    "exchangeTimestampType": "UNKNOWN",
    "exchangeTimestampPrecision": "MILLIS"
  },
  "tradesTopic": {
    "eventIdType": "UNORDERED",
    "exchangeTimestampType": "MATCHING_ENGINE",
    "exchangeTimestampPrecision": "MICROS"
  },
  "fundingRateTopic": null,
  "markPriceTopic": null,
  "indexPriceTopic": null,
  "liquidationsTopic": null,
  "crossTopicBookEventId": true,
  "predictedFundingRate": false
}
```

### Topic fields

Each `*Topic` field describes a market-data topic. If the topic isn't
available, the field is `null`. Available topic fields:
`depthTopic`, `topOfBookTopic`, `tradesTopic`, `markPriceTopic`,
`indexPriceTopic`, `fundingRateTopic`, `liquidationsTopic`.

---

### Event ID Type

The nature of the event ID.

{.compact}

| Value       | Description                                    |
|-------------|------------------------------------------------|
| `ORDERED`   | Event ID is an ever-increasing sequence number |
| `UNORDERED` | Event ID is a hash                             |

---

### Exchange Timestamp Type

Where the exchange timestamp comes from.

{.compact}

| Value             | Description                                                     |
|-------------------|-----------------------------------------------------------------|
| `NONE`            | No exchange timestamp available                                 |
| `UNKNOWN`         | Exchange timestamp is present, but its origin is not documented |
| `MATCHING_ENGINE` | Timestamp is set in the matching engine                         |
| `EXCHANGE_OUT`    | Timestamp is set when an event is published to the client       |

---

### Exchange Timestamp Precision

Precision of the exchange timestamp. `null` when `exchangeTimestampType`
is `NONE`.

{.compact}

| Value    | Description           |
|----------|-----------------------|
| `MILLIS` | Millisecond precision |
| `MICROS` | Microsecond precision |
| `NANOS`  | Nanosecond precision  |

---

### Cross-Topic Book Event ID

**Field:** `crossTopicBookEventId`

- **Type:** Boolean.
- **Description:** if `true`, `depthTopic` and `topOfBookTopic` use
  ordered event IDs and share the same exchange-side sequence. The
  event ID can be used to determine whether depth data or top-of-book
  data is more recent.

---

### Predicted Funding Rate

**Field:** `predictedFundingRate`

- **Type:** Boolean.
- **Description:** if `true`, the exchange supports the predicted
  funding rate.

---

## See Also

- [Accessing Data](accessing-data.md). How to connect and subscribe.
- [SBE Encoding](sbe.md). Simple Binary Encoding for market data.
- [Binary Framing](binary-framing.md). Message framing for domain
  sockets.
