# Order Entry Protocol

The **Order Entry Protocol** is the message-based interface a client uses
to manage orders. It carries place, amend, replace, and cancel requests
in one direction, and order updates, fills, position changes, wallet
balances, and rate-limit reports back the other.

The shape is consistent: a request specifies what to do; the
corresponding response confirms what happened (or what failed); separate
event messages report state changes that arise asynchronously.

---

## Connecting to a trading adapter

Each account is served by exactly one trading adapter. Resolve which one
through the backend, then query the adapter for its endpoints.

### Resolve the adapter for an account

```text
GET http://<backend-host>/api/accounts/{accountId}/tradingservers
```

The response always carries a single entry: the adapter's host and
port.

```json
{
  "status": 200,
  "result": "success",
  "count": 1,
  "data": [
    {
      "server_id": 1,
      "exchange_id": 42,
      "host": "10.0.0.42",
      "port": 3000,
      "ip": "10.0.0.42"
    }
  ]
}
```

### Query the adapter's endpoints

```text
GET http://{host}:{port}/api/info
```

```json
{
  "process_id": "ta-binance",
  "version": "1.0.0",
  "endpoints": [
    {
      "endpoint": "/api/v2",
      "protocol_version": "2"
    }
  ],
  "domain_socket": {
    "path": "/tmp/ta-binance.socket",
    "protocol_version": "2"
  }
}
```

`domain_socket` is only present when the request comes from `localhost`
and the adapter has domain sockets enabled.

### Connect

This page describes protocol version 2. Connect over the matching
endpoint:

- **WebSocket:** `ws://{host}:{port}{endpoint}` (for example
  `ws://10.0.0.42:3000/api/v2`).
- **Domain socket:** the `path` from `domain_socket` (for example
  `/tmp/ta-binance.socket`).

---

## Welcome

The trading adapter sends a **welcome** message immediately after the
connection is established. It carries the version of the adapter and the
version of the protocol it is serving. Read the `protocolVersion` here
and verify it before going further; the rest of this page assumes a
compatible version.

+++ Structure

```!#
{
  "msgType": "welcome",
  "processId": string,
  "version": string,
  "protocolVersion": string
}
```

{.compact}

| Field             | Type   | Description                     |
|-------------------|--------|---------------------------------|
| `msgType`         | string | Always `"welcome"`              |
| `processId`       | string | Process name of trading adapter |
| `version`         | string | Version of trading adapter      |
| `protocolVersion` | string | Version of served protocol      |

+++ Example

```json !#
{
  "msgType": "welcome",
  "processId": "trading-adapter-1",
  "version": "4.32.0",
  "protocolVersion": "2.1"
}
```

+++

> [!NOTE]
> Welcome is the first message after a connection is established.
> Verify `protocolVersion` for compatibility before sending login.

---

## Login

Establishes a session for an account and returns the current account
state.

### Request

+++ Structure

```!#
{
  "msgType": "login",
  "accountId": integer,
  "requestId": string,
  "processId": string,
  "version": string,
  "options": {
    "includeContext": boolean,
    "filterByProcessId": boolean,
    "enableCorporateActions": boolean,
    "useLegacyPositionEncoding": boolean
  }
}
```

{.compact}

| Field       | Type    | Description                            |
|-------------|---------|----------------------------------------|
| `msgType`   | string  | Always `"login"`                       |
| `accountId` | integer | Account ID to log in                   |
| `requestId` | string  | ID assigned by client for this request |
| `processId` | string  | Name of client process                 |
| `version`   | string  | Version of client process              |
| `options`   | object  | (Optional) Login options               |

**Login options:**

{.compact}

| Field                       | Type    | Description                                                                                                            |
|-----------------------------|---------|------------------------------------------------------------------------------------------------------------------------|
| `includeContext`            | boolean | If order and fill context should be included in messages from the trading adapter                                      |
| `filterByProcessId`         | boolean | If only orders and fills with this process as the owner should be published                                            |
| `enableCorporateActions`    | boolean | If corporate-action events should be published                                                                         |
| `useLegacyPositionEncoding` | boolean | Position-update encoding. `true` uses the legacy `data` field; `false` uses `positions` + `isolatedPositions`. Default `true` |

+++ Example

```json !#
{
  "msgType": "login",
  "accountId": 42,
  "requestId": "login-req-001",
  "processId": "strategy-server-1",
  "version": "1.0.0",
  "options": {
    "includeContext": false,
    "filterByProcessId": false
  }
}
```

+++

### Response

+++ Structure

```!#
{
  "msgType": "loginResponse",
  "accountId": integer,
  "requestId": string,
  "result": {
    "status": string,
    "orders": array,
    "instrumentPositions": array,
    "isolatedInstrumentPositions": array,
    "walletPositions": array,
    "maintenanceMargin": array,
    "totalBalances": array,
    "rateLimitLoads": array,
    "capabilities": object
  },
  "error": object
}
```

{.compact}

| Field       | Type    | Description                              |
|-------------|---------|------------------------------------------|
| `msgType`   | string  | Always `"loginResponse"`                 |
| `accountId` | integer | Account ID                               |
| `requestId` | string  | ID of the original request               |
| `result`    | object  | (Optional) Login result if successful    |
| `error`     | object  | (Optional) Error details if login failed |

**Result object:**

{.compact}

| Field                          | Type   | Description                                                                  |
|--------------------------------|--------|------------------------------------------------------------------------------|
| `status`                       | string | Account status (see [StatusType](enums.md#status-type))                      |
| `orders`                       | array  | List of all open orders across all instruments                               |
| `instrumentPositions`          | array  | Non-isolated positions                                                       |
| `isolatedInstrumentPositions`  | array  | Positions for instruments in dual/hedge mode (long and short coexist)        |
| `walletPositions`              | array  | Current wallet balances                                                      |
| `maintenanceMargin`            | array  | List of global maintenance margins in USD per wallet type (if available)     |
| `totalBalances`                | array  | List of total balances in USD per wallet (only for cross-margin wallets)     |
| `rateLimitLoads`               | array  | List of rate limit loads (may be empty if not available)                     |
| `capabilities`                 | object | Exchange capabilities (see [Concepts](concepts.md#capabilities))             |

**Error object:**

{.compact}

| Field     | Type   | Description                                                  |
|-----------|--------|--------------------------------------------------------------|
| `type`    | string | Error type (see [LoginErrorType](enums.md#login-error-type)) |
| `message` | string | Human-readable error description                             |
| `source`  | string | Error source: `"INTERNAL"` or `"EXCHANGE"`                   |

+++ Example

```json !#
{
  "msgType": "loginResponse",
  "accountId": 42,
  "requestId": "login-req-001",
  "result": {
    "status": "OK",
    "orders": [],
    "instrumentPositions": [],
    "walletPositions": [
      {
        "underlyingId": 1,
        "availableAmount": "10000.00",
        "amountBlockedForMargin": "0.00",
        "marginBalance": "10000.00",
        "walletType": "ACCOUNT"
      }
    ],
    "maintenanceMargin": [],
    "totalBalances": [],
    "rateLimitLoads": [
      {
        "id": "spot",
        "p": 0.0,
        "a": 0.0,
        "r": 0.0,
        "c": 0.0
      }
    ],
    "capabilities": {
      "amend": true,
      "replace": false,
      "cancelAll": true,
      "closeOnly": true,
      "reduceOnly": true,
      "displayQuantity": true,
      "orderTypes": [
        "LIMIT",
        "MARKET",
        "STOP_LIMIT"
      ],
      "timeInForces": [
        "GOOD_TILL_CANCEL",
        "IMMEDIATE_OR_CANCEL"
      ],
      "walletTypes": [
        "ACCOUNT"
      ]
    }
  }
}
```

+++

> [!NOTE]
> The login response carries the full account state: every open order,
> every position, every wallet balance, and the exchange capabilities.
> A reconnecting client uses this snapshot to sync its own state without
> any extra round trips.

---

## Logout

Ends the session for the given account. The server does not send a
response.

+++ Structure

```!#
{
  "msgType": "logout",
  "accountId": integer,
  "requestId": string
}
```

{.compact}

| Field       | Type    | Description                            |
|-------------|---------|----------------------------------------|
| `msgType`   | string  | Always `"logout"`                      |
| `accountId` | integer | Account ID to log out                  |
| `requestId` | string  | ID assigned by client for this request |

+++ Example

```json !#
{
  "msgType": "logout",
  "accountId": 42,
  "requestId": "logout-req-001"
}
```

+++

---

## Place

Submits one or more new orders to the exchange. Every order in the
request must target the same `instrumentId`.

### Request

+++ Structure

```!#
{
  "msgType": "place",
  "accountId": integer,
  "entries": [
    {
      "ownOrderId": string,
      "clientOrderId": string,
      "instrumentId": integer,
      "type": string,
      "side": string,
      "timeInForce": string,
      "price": string,
      "totalQuantity": string,
      "postOnly": boolean,
      "displayQuantity": string,
      "triggerPrice": string,
      "pegOffsetValue": string,
      "openClose": string,
      "walletType": string,
      "closeOnly": boolean,
      "reduceOnly": boolean,
      "autoBorrow": boolean,
      "autoRepay": boolean,
      "useSpare": boolean,
      "owner": object,
      "context": object
    }
  ],
  "tracing": object
}
```

{.compact}

| Field       | Type    | Description                            |
|-------------|---------|----------------------------------------|
| `msgType`   | string  | Always `"place"`                       |
| `accountId` | integer | Account to be used                     |
| `entries`   | array   | Array of orders to be placed           |
| `tracing`   | object  | (Optional) Distributed tracing context |

**Entry object:**

{.compact}

| Field             | Type    | Description                                                                  |
|-------------------|---------|------------------------------------------------------------------------------|
| `ownOrderId`      | string  | UUID assigned by client for this order                                       |
| `clientOrderId`   | string  | UUID for tracking order modifications                                        |
| `instrumentId`    | integer | Instrument ID (must be same for all orders in request)                       |
| `type`            | string  | Order type (see [OrderType](enums.md#order-type))                            |
| `side`            | string  | Order side: `"BID"` or `"ASK"`                                               |
| `timeInForce`     | string  | Time in force (see [TimeInForce](enums.md#time-in-force))                    |
| `price`           | string  | (Optional) Order price (null for MARKET_PEGGED/PRIMARY_PEGGED)               |
| `totalQuantity`   | string  | Order quantity                                                               |
| `postOnly`        | boolean | (Optional) Set to true to avoid aggressive hit                               |
| `displayQuantity` | string  | (Optional) For iceberg/hidden orders                                         |
| `triggerPrice`    | string  | (Optional) For stop orders                                                   |
| `pegOffsetValue`  | string  | (Optional) For pegged orders                                                 |
| `openClose`       | string  | (Optional) `"OPEN"` or `"CLOSE"` (null for non-isolated positions)           |
| `walletType`      | string  | (Optional) Wallet type (defaults to `"ACCOUNT"`)                             |
| `closeOnly`       | boolean | (Optional) Close-only flag                                                   |
| `reduceOnly`      | boolean | (Optional) Reduce-only flag                                                  |
| `autoBorrow`      | boolean | (Optional) Auto-borrow flag                                                  |
| `autoRepay`       | boolean | (Optional) Auto-repay flag                                                   |
| `useSpare`        | boolean | (Optional) Use spare rate limit pool                                         |
| `owner`           | object  | (Optional) Owner information (see [Concepts](concepts.md#owner-information)) |
| `context`         | object  | (Optional) Arbitrary JSON context                                            |

+++ Example

```json !#
{
  "msgType": "place",
  "accountId": 42,
  "entries": [
    {
      "ownOrderId": "123e4567-e89b-12d3-a456-426614174000",
      "clientOrderId": "123e4567-e89b-12d3-a456-426614174000",
      "instrumentId": 123,
      "type": "LIMIT",
      "side": "BID",
      "timeInForce": "GOOD_TILL_CANCEL",
      "price": "50000.00",
      "totalQuantity": "0.01",
      "postOnly": true
    }
  ]
}
```

+++

### Response

+++ Structure

```!#
{
  "msgType": "placeResponse",
  "accountId": integer,
  "adapterTimestampNs": integer,
  "results": [
    {
      "ownOrderId": string,
      "clientOrderId": string,
      "exchangeTimestampNs": integer,
      "result": object,
      "error": object
    }
  ]
}
```

{.compact}

| Field                | Type    | Description                                    |
|----------------------|---------|------------------------------------------------|
| `msgType`            | string  | Always `"placeResponse"`                       |
| `accountId`          | integer | Account ID                                     |
| `adapterTimestampNs` | integer | Adapter timestamp in nanoseconds               |
| `results`            | array   | Array of results for each order in the request |

**Result entry (success):**

{.compact}

| Field                 | Type    | Description                                |
|-----------------------|---------|--------------------------------------------|
| `ownOrderId`          | string  | UUID of the order                          |
| `clientOrderId`       | string  | Client order ID                            |
| `exchangeTimestampNs` | integer | (Optional) Exchange timestamp if available |
| `result`              | object  | (Optional) Present if placement succeeded  |

**Result entry (failure):**

{.compact}

| Field           | Type   | Description                            |
|-----------------|--------|----------------------------------------|
| `ownOrderId`    | string | UUID of the order                      |
| `clientOrderId` | string | Client order ID                        |
| `error`         | object | (Optional) Present if placement failed |

+++ Example

```json !#
{
  "msgType": "placeResponse",
  "accountId": 42,
  "adapterTimestampNs": 1677681492196000000,
  "results": [
    {
      "ownOrderId": "123e4567-e89b-12d3-a456-426614174000",
      "clientOrderId": "123e4567-e89b-12d3-a456-426614174000",
      "exchangeTimestampNs": 1677681492190000000,
      "result": {
        "exchangeOrderId": "EXCH-12345",
        "instrumentId": 123,
        "type": "LIMIT",
        "side": "BID",
        "timeInForce": "GOOD_TILL_CANCEL",
        "price": "50000.00",
        "totalQuantity": "0.01",
        "postOnly": true
      }
    }
  ]
}
```

+++

---

## Amend

Modifies existing orders **atomically**. Only available where the
exchange supports it; otherwise use [Replace](#replace).

### Request

+++ Structure

```!#
{
  "msgType": "amend",
  "accountId": integer,
  "entries": [
    {
      "ownOrderId": string,
      "currentClientOrderId": string,
      "newClientOrderId": string,
      "exchangeOrderId": string,
      "price": string,
      "totalQuantity": string,
      "triggerPrice": string,
      "pegOffsetValue": string,
      "context": object
    }
  ],
  "tracing": object
}
```

{.compact}

| Field       | Type    | Description                            |
|-------------|---------|----------------------------------------|
| `msgType`   | string  | Always `"amend"`                       |
| `accountId` | integer | Account ID                             |
| `entries`   | array   | Array of order modifications           |
| `tracing`   | object  | (Optional) Distributed tracing context |

**Entry object:**

{.compact}

| Field                  | Type   | Description                               |
|------------------------|--------|-------------------------------------------|
| `ownOrderId`           | string | UUID of the order to modify               |
| `currentClientOrderId` | string | Current client order ID                   |
| `newClientOrderId`     | string | New client order ID for this modification |
| `exchangeOrderId`      | string | (Optional) Exchange order ID              |
| `price`                | string | (Optional) New price                      |
| `totalQuantity`        | string | (Optional) New quantity                   |
| `triggerPrice`         | string | (Optional) New trigger price              |
| `pegOffsetValue`       | string | (Optional) New peg offset                 |
| `context`              | object | (Optional) Updated context                |

+++ Example

```json !#
{
  "msgType": "amend",
  "accountId": 42,
  "entries": [
    {
      "ownOrderId": "123e4567-e89b-12d3-a456-426614174000",
      "currentClientOrderId": "123e4567-e89b-12d3-a456-426614174000",
      "newClientOrderId": "123e4567-e89b-12d3-a456-426614174999",
      "price": "51000.00"
    }
  ]
}
```

+++

### Response

+++ Structure

```!#
{
  "msgType": "amendResponse",
  "accountId": integer,
  "adapterTimestampNs": integer,
  "results": [
    {
      "ownOrderId": string,
      "clientOrderId": string,
      "exchangeTimestampNs": integer,
      "result": object,
      "error": object
    }
  ]
}
```

{.compact}

| Field                | Type    | Description                         |
|----------------------|---------|-------------------------------------|
| `msgType`            | string  | Always `"amendResponse"`            |
| `accountId`          | integer | Account ID                          |
| `adapterTimestampNs` | integer | Adapter timestamp in nanoseconds    |
| `results`            | array   | Array of results for each amendment |

**Result entry:**

{.compact}

| Field                 | Type    | Description                                                          |
|-----------------------|---------|----------------------------------------------------------------------|
| `ownOrderId`          | string  | UUID of the order                                                    |
| `clientOrderId`       | string  | New client order ID                                                  |
| `exchangeTimestampNs` | integer | (Optional) Exchange timestamp if available                           |
| `result`              | object  | (Optional) Present if amendment succeeded (contains modified fields) |
| `error`               | object  | (Optional) Present if amendment failed                               |

+++ Example

```json !#
{
  "msgType": "amendResponse",
  "accountId": 42,
  "adapterTimestampNs": 1677681492196000000,
  "results": [
    {
      "ownOrderId": "123e4567-e89b-12d3-a456-426614174000",
      "clientOrderId": "123e4567-e89b-12d3-a456-426614174999",
      "exchangeTimestampNs": 1677681492190000000,
      "result": {
        "price": "51000.00"
      }
    }
  ]
}
```

+++

---

## Replace

Replaces an order by cancelling the existing one and placing a new one.
Use this where the exchange does not support amend; the trade-off is
that this is a non-atomic modify.

Replace is not a universal fallback: it needs exchange support of its own.
Check the `replace` capability in the login response before relying on it,
the same way you would check `amend`. An exchange can support neither, in
which case cancel and place the order yourself.

### Request

+++ Structure

```!#
{
  "msgType": "replace",
  "accountId": integer,
  "entries": [
    {
      "currentOwnOrderId": string,
      "newOwnOrderId": string,
      "currentClientOrderId": string,
      "newClientOrderId": string,
      "exchangeOrderId": string,
      "price": string,
      "totalQuantity": string,
      "triggerPrice": string,
      "context": object
    }
  ],
  "tracing": object
}
```

{.compact}

| Field       | Type    | Description                            |
|-------------|---------|----------------------------------------|
| `msgType`   | string  | Always `"replace"`                     |
| `accountId` | integer | Account ID                             |
| `entries`   | array   | Array of order replacements            |
| `tracing`   | object  | (Optional) Distributed tracing context |

**Entry object:**

{.compact}

| Field                  | Type   | Description                        |
|------------------------|--------|------------------------------------|
| `currentOwnOrderId`    | string | UUID of the order to replace       |
| `newOwnOrderId`        | string | New UUID for the replacement order |
| `currentClientOrderId` | string | Current client order ID            |
| `newClientOrderId`     | string | New client order ID                |
| `exchangeOrderId`      | string | (Optional) Exchange order ID       |
| `price`                | string | (Optional) New price               |
| `totalQuantity`        | string | (Optional) New quantity            |
| `triggerPrice`         | string | (Optional) New trigger price       |
| `context`              | object | (Optional) Updated context         |

+++ Example

```json !#
{
  "msgType": "replace",
  "accountId": 42,
  "entries": [
    {
      "currentOwnOrderId": "123e4567-e89b-12d3-a456-426614174000",
      "newOwnOrderId": "123e4567-e89b-12d3-a456-426614174999",
      "currentClientOrderId": "123e4567-e89b-12d3-a456-426614174000",
      "newClientOrderId": "123e4567-e89b-12d3-a456-426614174999",
      "price": "51000.00",
      "totalQuantity": "0.02"
    }
  ]
}
```

+++

### Response

+++ Structure

```!#
{
  "msgType": "replaceResponse",
  "accountId": integer,
  "adapterTimestampNs": integer,
  "results": [
    {
      "exchangeOrderId": string,
      "ownOrderId": string,
      "clientOrderId": string,
      "exchangeTimestampNs": integer,
      "result": object,
      "error": object
    }
  ]
}
```

{.compact}

| Field                | Type    | Description                           |
|----------------------|---------|---------------------------------------|
| `msgType`            | string  | Always `"replaceResponse"`            |
| `accountId`          | integer | Account ID                            |
| `adapterTimestampNs` | integer | Adapter timestamp in nanoseconds      |
| `results`            | array   | Array of results for each replacement |

+++ Example

```json !#
{
  "msgType": "replaceResponse",
  "accountId": 42,
  "adapterTimestampNs": 1677681492196000000,
  "results": [
    {
      "exchangeOrderId": "EXCH-12346",
      "ownOrderId": "123e4567-e89b-12d3-a456-426614174999",
      "clientOrderId": "123e4567-e89b-12d3-a456-426614174999",
      "exchangeTimestampNs": 1677681492190000000,
      "result": {
        "price": "51000.00",
        "totalQuantity": "0.02"
      }
    }
  ]
}
```

+++

> [!WARNING]
> **Replace is non-atomic.** The cancel can succeed while the place
> fails, leaving the order canceled with no replacement on the book.
> Where amend is available, prefer it; fall back to replace only when the
> exchange does not expose amend.

---

## Cancel

Cancels one or more existing orders.

### Request

+++ Structure

```!#
{
  "msgType": "cancel",
  "accountId": integer,
  "entries": [
    {
      "ownOrderId": string,
      "exchangeOrderId": string
    }
  ],
  "tracing": object
}
```

{.compact}

| Field       | Type    | Description                            |
|-------------|---------|----------------------------------------|
| `msgType`   | string  | Always `"cancel"`                      |
| `accountId` | integer | Account ID                             |
| `entries`   | array   | Array of orders to cancel              |
| `tracing`   | object  | (Optional) Distributed tracing context |

**Entry object:**

{.compact}

| Field             | Type   | Description                  |
|-------------------|--------|------------------------------|
| `ownOrderId`      | string | UUID of the order to cancel  |
| `exchangeOrderId` | string | (Optional) Exchange order ID |

+++ Example

```json !#
{
  "msgType": "cancel",
  "accountId": 42,
  "entries": [
    {
      "ownOrderId": "123e4567-e89b-12d3-a456-426614174000",
      "exchangeOrderId": "EXCH-12345"
    }
  ]
}
```

+++

### Response

+++ Structure

```!#
{
  "msgType": "cancelResponse",
  "accountId": integer,
  "adapterTimestampNs": integer,
  "results": [
    {
      "ownOrderId": string,
      "exchangeTimestampNs": integer,
      "result": object,
      "error": object
    }
  ]
}
```

{.compact}

| Field                | Type    | Description                            |
|----------------------|---------|----------------------------------------|
| `msgType`            | string  | Always `"cancelResponse"`              |
| `accountId`          | integer | Account ID                             |
| `adapterTimestampNs` | integer | Adapter timestamp in nanoseconds       |
| `results`            | array   | Array of results for each cancellation |

+++ Example

```json !#
{
  "msgType": "cancelResponse",
  "accountId": 42,
  "adapterTimestampNs": 1677681492196000000,
  "results": [
    {
      "ownOrderId": "123e4567-e89b-12d3-a456-426614174000",
      "exchangeTimestampNs": 1677681492190000000,
      "result": {}
    }
  ]
}
```

+++

---

## Cancel All

Cancels every open order on the account, where the exchange supports
this operation.

### Request

+++ Structure

```!#
{
  "msgType": "cancelAll",
  "accountId": integer,
  "requestId": string,
  "createTimestampNs": integer,
  "tracing": object
}
```

{.compact}

| Field               | Type    | Description                            |
|---------------------|---------|----------------------------------------|
| `msgType`           | string  | Always `"cancelAll"`                   |
| `accountId`         | integer | Account ID                             |
| `requestId`         | string  | UUID for this request                  |
| `createTimestampNs` | integer | Timestamp when request was created     |
| `tracing`           | object  | (Optional) Distributed tracing context |

+++ Example

```json !#
{
  "msgType": "cancelAll",
  "accountId": 42,
  "requestId": "e84ebdd5-1c22-43df-a85d-f28bb9d38e34",
  "createTimestampNs": 1677681492196000000
}
```

+++

### Response

+++ Structure

```!#
{
  "msgType": "cancelAllResponse",
  "accountId": integer,
  "requestId": string,
  "adapterTimestampNs": integer,
  "exchangeTimestampNs": integer,
  "result": object,
  "error": object
}
```

{.compact}

| Field                 | Type    | Description                                           |
|-----------------------|---------|-------------------------------------------------------|
| `msgType`             | string  | Always `"cancelAllResponse"`                          |
| `accountId`           | integer | Account ID                                            |
| `requestId`           | string  | UUID from the request                                 |
| `adapterTimestampNs`  | integer | Adapter timestamp in nanoseconds                      |
| `exchangeTimestampNs` | integer | (Optional) Exchange timestamp if available            |
| `result`              | object  | (Optional) Present if cancel succeeded (empty object) |
| `error`               | object  | (Optional) Present if cancel failed                   |

+++ Example

```json !#
{
  "msgType": "cancelAllResponse",
  "accountId": 42,
  "requestId": "e84ebdd5-1c22-43df-a85d-f28bb9d38e34",
  "adapterTimestampNs": 1677681492196000000,
  "exchangeTimestampNs": 1677681492190000000,
  "result": {}
}
```

+++

---

## Order Context

Updates the `context` of one or more existing orders. The context is
returned on subsequent order updates and fills, so this is the way to
attach (or change) application-side metadata after an order has already
been placed.

### Request

+++ Structure

```!#
{
  "msgType": "orderContext",
  "accountId": integer,
  "entries": [
    {
      "ownOrderId": string,
      "clientOrderId": string,
      "context": object
    }
  ]
}
```

| Field      | Type   | Description                                   |
|------------|--------|-----------------------------------------------|
| `msgType`  | string | Always `"orderContext"`                       |
| `entries`  | array  | One entry per order to update                 |

**Entry:**

| Field           | Type   | Description                              |
|-----------------|--------|------------------------------------------|
| `ownOrderId`    | string | UUID of the order                        |
| `clientOrderId` | string | Client order ID                          |
| `context`       | object | Replacement context value                |

+++

### Response

+++ Structure

```!#
{
  "msgType": "orderContextResponse",
  "accountId": integer,
  "results": [
    {
      "ownOrderId": string,
      "clientOrderId": string,
      "result": { "context": object },
      "error": object
    }
  ]
}
```

Each result entry carries exactly one of `result` (success) or `error`
(failure, with `type` from
[OrderErrorType](enums.md#order-error-type) and `source` from
[ErrorSource](enums.md#error-source)).

+++

---

## Fill Context

Updates the `context` of a fill. Same shape as Order Context, keyed by
`ownFillId`.

### Request

+++ Structure

```!#
{
  "msgType": "fillContext",
  "accountId": integer,
  "entries": [
    {
      "ownFillId": string,
      "context": object
    }
  ]
}
```

+++

### Response

+++ Structure

```!#
{
  "msgType": "fillContextResponse",
  "accountId": integer,
  "results": [
    {
      "ownFillId": string,
      "result": { "context": object },
      "error": object
    }
  ]
}
```

Per-entry errors carry `type` from
[FillErrorType](enums.md#fill-error-type).

+++

---

## Account Status

Sent when the connection to the exchange is lost or restored, or when
the account status changes for any other reason.

+++ Structure

```!#
{
  "msgType": "accountStatus",
  "accountId": integer,
  "message": string,
  "status": string,
  "orders": array,
  "instrumentPositions": array,
  "isolatedInstrumentPositions": array,
  "walletPositions": array,
  "maintenanceMargin": array,
  "totalBalances": array,
  "rateLimitLoads": array
}
```

{.compact}

| Field                          | Type    | Description                                                                         |
|--------------------------------|---------|-------------------------------------------------------------------------------------|
| `msgType`                      | string  | Always `"accountStatus"`                                                            |
| `accountId`                    | integer | Account ID                                                                          |
| `message`                      | string  | Free-text status message                                                            |
| `status`                       | string  | Status type (see [Status Type](enums.md#status-type))                               |
| `orders`                       | array   | List of all open orders                                                             |
| `instrumentPositions`          | array   | Non-isolated positions (see [Position Update](#position-update))                    |
| `isolatedInstrumentPositions`  | array   | Positions for instruments in dual/hedge mode                                        |
| `walletPositions`              | array   | Current wallet balances (see [Wallet Update](#wallet-update))                       |
| `maintenanceMargin`            | array   | Global maintenance margins per wallet type (see [Wallet Update](#wallet-update))    |
| `totalBalances`                | array   | Total balances per wallet (cross-margin only) (see [Wallet Update](#wallet-update)) |
| `rateLimitLoads`               | array   | Current rate limit loads                                                            |

+++ Example

```json !#
{
  "msgType": "accountStatus",
  "accountId": 42,
  "message": "Connected to exchange",
  "status": "OK",
  "orders": [],
  "instrumentPositions": [],
  "walletPositions": [],
  "maintenanceMargin": [],
  "totalBalances": [],
  "rateLimitLoads": []
}
```

+++

> [!WARNING]
> When status changes to `DISCONNECTED`, expect order requests to fail
> until the connection is restored and the status returns to `OK`.

---

## Order Update

Sent whenever an order is placed, modified, cancelled, filled, or
otherwise changed.

+++ Structure

```!#
{
  "msgType": "orderUpdate",
  "accountId": integer,
  "adapterTimestampNs": integer,
  "data": [
    {
      "ownOrderId": string,
      "exchangeOrderId": string,
      "clientOrderId": string,
      "exchangeTimestampNs": integer,
      "instrumentId": integer,
      "type": string,
      "side": string,
      "state": string,
      "timeInForce": string,
      "price": string,
      "totalQuantity": string,
      "executedQuantity": string,
      "postOnly": boolean,
      "closeOnly": boolean,
      "reduceOnly": boolean,
      "displayQuantity": string,
      "triggered": boolean,
      "triggerPrice": string,
      "pegOffsetValue": string,
      "openClose": string,
      "walletType": string,
      "owner": object,
      "context": object
    }
  ]
}
```

{.compact}

| Field                | Type    | Description                      |
|----------------------|---------|----------------------------------|
| `msgType`            | string  | Always `"orderUpdate"`           |
| `accountId`          | integer | Account ID                       |
| `adapterTimestampNs` | integer | Adapter timestamp in nanoseconds |
| `data`               | array   | Array of order updates           |

**Order data:**

{.compact}

| Field                 | Type    | Description                                          |
|-----------------------|---------|------------------------------------------------------|
| `ownOrderId`          | string  | UUID of the order                                    |
| `exchangeOrderId`     | string  | Exchange order ID                                    |
| `clientOrderId`       | string  | Current client order ID                              |
| `exchangeTimestampNs` | integer | (Optional) Exchange timestamp if available           |
| `instrumentId`        | integer | Instrument ID                                        |
| `type`                | string  | Order type (see [OrderType](enums.md#order-type))    |
| `side`                | string  | Order side: `"BID"` or `"ASK"`                       |
| `state`               | string  | Order state (see [OrderState](enums.md#order-state)) |
| `timeInForce`         | string  | Time in force                                        |
| `price`               | string  | (Optional) Order price                               |
| `totalQuantity`       | string  | Total order quantity                                 |
| `executedQuantity`    | string  | Quantity executed so far                             |
| `postOnly`            | boolean | Post-only flag                                       |
| `closeOnly`           | boolean | (Optional) Close-only flag                           |
| `reduceOnly`          | boolean | (Optional) Reduce-only flag                          |
| `displayQuantity`     | string  | (Optional) Display quantity for iceberg orders       |
| `triggered`           | boolean | (Optional) Whether stop order has triggered          |
| `triggerPrice`        | string  | (Optional) Trigger price for stop orders             |
| `pegOffsetValue`      | string  | (Optional) Peg offset for pegged orders              |
| `openClose`           | string  | (Optional) Open/close indicator                      |
| `walletType`          | string  | (Optional) Wallet type if not `"ACCOUNT"`            |
| `owner`               | object  | (Optional) Owner information                         |
| `context`             | object  | (Optional) Order context                             |

+++ Example

```json !#
{
  "msgType": "orderUpdate",
  "accountId": 42,
  "adapterTimestampNs": 1677681492196000000,
  "data": [
    {
      "ownOrderId": "123e4567-e89b-12d3-a456-426614174000",
      "exchangeOrderId": "EXCH-12345",
      "clientOrderId": "123e4567-e89b-12d3-a456-426614174000",
      "exchangeTimestampNs": 1677681492190000000,
      "instrumentId": 123,
      "type": "LIMIT",
      "side": "BID",
      "state": "CONFIRMED",
      "timeInForce": "GOOD_TILL_CANCEL",
      "price": "50000.00",
      "totalQuantity": "0.01",
      "executedQuantity": "0.00",
      "postOnly": true
    }
  ]
}
```

+++
---

## Fill

Sent whenever an order receives a fill, or when the data of an
already-published fill is updated.

+++ Structure

```!#
{
  "msgType": "fill",
  "accountId": integer,
  "adapterTimestampNs": integer,
  "data": [
    {
      "ownFillId": string,
      "exchangeTradeId": string,
      "exchangeTimestampNs": integer,
      "ownOrderId": string,
      "exchangeOrderId": string,
      "clientOrderId": string,
      "instrumentId": integer,
      "side": string,
      "price": string,
      "quantity": string,
      "fees": string,
      "feeUnderlyingId": integer,
      "addedLiquidity": boolean,
      "usdFees": string,
      "usdTurnover": string,
      "fxRate": string,
      "openClose": string,
      "type": string,
      "update": boolean,
      "context": object,
      "owner": object
    }
  ]
}
```

{.compact}

| Field                | Type    | Description                      |
|----------------------|---------|----------------------------------|
| `msgType`            | string  | Always `"fill"`                  |
| `accountId`          | integer | Account ID                       |
| `adapterTimestampNs` | integer | Adapter timestamp in nanoseconds |
| `data`               | array   | Array of fill updates            |

**Fill data:**

{.compact}

| Field                 | Type    | Description                                     |
|-----------------------|---------|-------------------------------------------------|
| `ownFillId`           | string  | Adapter-assigned fill ID                        |
| `exchangeTradeId`     | string  | Exchange trade ID                               |
| `exchangeTimestampNs` | integer | Exchange timestamp                              |
| `ownOrderId`          | string  | Order UUID                                      |
| `exchangeOrderId`     | string  | Exchange order ID                               |
| `clientOrderId`       | string  | Client order ID                                 |
| `instrumentId`        | integer | Instrument ID                                   |
| `side`                | string  | Side of the affected order (`"BID"` or `"ASK"`) |
| `price`               | string  | Fill price                                      |
| `quantity`            | string  | Fill quantity                                   |
| `fees`                | string  | Native fees                                     |
| `feeUnderlyingId`     | integer | Currency ID for fees                            |
| `addedLiquidity`      | boolean | Maker (`true`) or taker (`false`) trade         |
| `usdFees`             | string  | (Optional) Fees in USD                          |
| `usdTurnover`         | string  | (Optional) Turnover in USD                      |
| `fxRate`              | string  | (Optional) FX rate for USD conversion           |
| `openClose`           | string  | (Optional) Open/close indicator                 |
| `type`                | string  | (Optional) Fill type if not `"ORDER_FILL"`      |
| `update`              | boolean | Whether this is an update to existing fill data |
| `context`             | object  | (Optional) Order context                        |
| `owner`               | object  | (Optional) Owner information                    |

+++ Example

```json !#
{
  "msgType": "fill",
  "accountId": 42,
  "adapterTimestampNs": 1677681492196000000,
  "data": [
    {
      "ownFillId": "fill-001",
      "exchangeTradeId": "TRADE-12345",
      "exchangeTimestampNs": 1677681492190000000,
      "ownOrderId": "123e4567-e89b-12d3-a456-426614174000",
      "exchangeOrderId": "EXCH-12345",
      "clientOrderId": "123e4567-e89b-12d3-a456-426614174000",
      "instrumentId": 123,
      "side": "BID",
      "price": "50000.00",
      "quantity": "0.01",
      "fees": "0.50",
      "feeUnderlyingId": 1,
      "addedLiquidity": true,
      "update": false
    }
  ]
}
```

+++

> [!NOTE]
> `addedLiquidity` reports maker (`true`) or taker (`false`) on the
> trade. Most exchanges price fees differently for the two.

> [!TIP]
> When `update` is `true`, the message contains updated information for
> a fill the client already received. This typically happens when fee
> information arrives a moment after the initial fill report.

---

## Position Update

Sent whenever any position changes.

The wire shape depends on the login option `useLegacyPositionEncoding`.
The default is `true` (legacy format) for backwards compatibility; new
integrations should set it to `false` and use the new format.

In dual/hedge mode, long and short positions coexist on the same
instrument without netting; this is what the docs call *isolated
positions*. Non-isolated positions net into a single long/short pair.

+++ New format (`useLegacyPositionEncoding: false`)

```!#
{
  "msgType": "positionUpdate",
  "accountId": integer,
  "adapterTimestampNs": integer,
  "positions": [
    {
      "instrumentId": integer,
      "exchangeTimestampNs": integer,
      "shortPosition": string,
      "longPosition": string,
      "liquidationPrice": string,
      "unrealizedPnl": string,
      "averageEntryPrice": string
    }
  ],
  "isolatedPositions": [
    {
      "instrumentId": integer,
      "exchangeTimestampNs": integer,
      "long": {
        "size": string,
        "liquidationPrice": string,
        "averageEntryPrice": string,
        "unrealizedPnl": string
      },
      "short": {
        "size": string,
        "liquidationPrice": string,
        "averageEntryPrice": string,
        "unrealizedPnl": string
      }
    }
  ]
}
```

`positions` and `isolatedPositions` are always present; either array can
be empty. Inside `isolatedPositions`, the `long` and `short` sub-objects
are always both present (with `size` `0` if there is no exposure on that
side).

+++ Legacy format (`useLegacyPositionEncoding: true`, default)

```!#
{
  "msgType": "positionUpdate",
  "accountId": integer,
  "adapterTimestampNs": integer,
  "data": [
    {
      "instrumentId": integer,
      "exchangeTimestampNs": integer,
      "shortPosition": string,
      "longPosition": string,
      "liquidationPrice": string,
      "unrealizedPnl": string,
      "averageEntryPrice": string
    }
  ]
}
```

`data` is renamed to `positions` in the new format and isolated
positions can no longer be carried alongside regular positions in the
same update.

+++

{.compact}

| Field                | Type    | Description                                      |
|----------------------|---------|--------------------------------------------------|
| `msgType`            | string  | Always `"positionUpdate"`                        |
| `accountId`          | integer | Account ID                                       |
| `adapterTimestampNs` | integer | Adapter timestamp in nanoseconds                 |

**Position entry (`positions[]` and legacy `data[]`):**

{.compact}

| Field                 | Type    | Description                                                                  |
|-----------------------|---------|------------------------------------------------------------------------------|
| `instrumentId`        | integer | Instrument ID                                                                |
| `exchangeTimestampNs` | integer | (Optional) Exchange timestamp if available                                   |
| `shortPosition`       | string  | Short-side size, always `>= 0`                                               |
| `longPosition`        | string  | Long-side size, always `>= 0`                                                |
| `liquidationPrice`    | string  | (Optional) Estimated liquidation price, present if capability `liquidationPrice` is set |
| `unrealizedPnl`       | string  | (Optional) Unrealised PnL, present if capability `positionUnrealizedPnl` is set |
| `averageEntryPrice`   | string  | (Optional) Weighted average entry price, present if capability `averageEntryPrice` is set |

**Isolated position entry (`isolatedPositions[]`, new format only):**

{.compact}

| Field                 | Type    | Description                                |
|-----------------------|---------|--------------------------------------------|
| `instrumentId`        | integer | Instrument ID                              |
| `exchangeTimestampNs` | integer | (Optional) Exchange timestamp if available |
| `long.size`           | string  | Long size, always `>= 0`                   |
| `long.liquidationPrice` / `long.averageEntryPrice` / `long.unrealizedPnl` | string | (Optional) Same semantics as on a non-isolated position entry |
| `short.size`          | string  | Short size, always `>= 0`                  |
| `short.liquidationPrice` / `short.averageEntryPrice` / `short.unrealizedPnl` | string | (Optional) Same semantics as on a non-isolated position entry |

> [!NOTE]
> For instruments with non-isolated positions, only one of
> `shortPosition` or `longPosition` is greater than zero. That value is
> the net position.

---

## Wallet Update

Sent whenever any wallet balance changes.

+++ Structure

```!#
{
  "msgType": "walletUpdate",
  "accountId": integer,
  "adapterTimestampNs": integer,
  "maintenanceMargin": array,
  "totalBalances": array,
  "data": [
    {
      "type": string,
      "instrumentId": integer,
      "underlyingId": integer,
      "exchangeTimestampNs": integer,
      "availableAmount": string,
      "amountBlockedForMargin": string,
      "marginBalance": string,
      "maintenanceMargin": string,
      "borrowed": string,
      "cashBalance": string,
      "unrealizedPnl": string,
      "availableBorrow": string
    }
  ]
}
```

{.compact}

| Field                | Type    | Description                                           |
|----------------------|---------|-------------------------------------------------------|
| `msgType`            | string  | Always `"walletUpdate"`                               |
| `accountId`          | integer | Account ID                                            |
| `adapterTimestampNs` | integer | Adapter timestamp in nanoseconds                      |
| `maintenanceMargin`  | array   | Global maintenance margins per wallet type            |
| `totalBalances`      | array   | Total balances per wallet (cross-margin only)         |
| `data`               | array   | Array of all wallet entries (even if not all changed) |

**Wallet entry:**

{.compact}

| Field                    | Type    | Description                                                                |
|--------------------------|---------|----------------------------------------------------------------------------|
| `type`                   | string  | (Optional) Wallet type, only included if not `"ACCOUNT"`. See `WalletType` |
| `instrumentId`           | integer | (Optional) Instrument ID, only included for isolated-margin wallets        |
| `underlyingId`           | integer | Currency/asset ID                                                          |
| `exchangeTimestampNs`    | integer | (Optional) Exchange timestamp if available                                 |
| `availableAmount`        | string  | Amount available for trading                                               |
| `amountBlockedForMargin` | string  | Amount used as margin                                                      |
| `marginBalance`          | string  | Total margin balance                                                       |
| `maintenanceMargin`      | string  | (Optional) Minimum margin required                                         |
| `borrowed`               | string  | (Optional) Currently borrowed amount                                       |
| `cashBalance`            | string  | (Optional) Cash balance, present if capability `cashBalance` is set        |
| `unrealizedPnl`          | string  | (Optional) Unrealised PnL, present if capability `walletUnrealizedPnl` is set |
| `availableBorrow`        | string  | (Optional) Amount available to borrow, present if capability `availableBorrow` is set |

+++ Example

```json !#
{
  "msgType": "walletUpdate",
  "accountId": 42,
  "adapterTimestampNs": 1677681492196000000,
  "maintenanceMargin": [],
  "totalBalances": [],
  "data": [
    {
      "underlyingId": 1,
      "exchangeTimestampNs": 1677681492190000000,
      "availableAmount": "9500.00",
      "amountBlockedForMargin": "500.00",
      "marginBalance": "10000.00"
    }
  ]
}
```

+++

---

## Rate Limit Load Update

Sent whenever any rate-limit load changes.

+++ Structure

```!#
{
  "msgType": "rateLimitLoad",
  "accountId": integer,
  "adapterTimestampNs": integer,
  "rateLimitLoads": [
    {
      "id": string,
      "p": number,
      "a": number,
      "r": number,
      "c": number
    }
  ]
}
```

{.compact}

| Field                | Type    | Description                      |
|----------------------|---------|----------------------------------|
| `msgType`            | string  | Always `"rateLimitLoad"`         |
| `accountId`          | integer | Account ID                       |
| `adapterTimestampNs` | integer | Adapter timestamp in nanoseconds |
| `rateLimitLoads`     | array   | Array of rate limit loads        |

**Rate limit load:**

{.compact}

| Field | Type   | Description                           |
|-------|--------|---------------------------------------|
| `id`  | string | Rate limit key (exchange-specific)    |
| `p`   | number | Load for place operations (0.0-1.0)   |
| `a`   | number | Load for amend operations (0.0-1.0)   |
| `r`   | number | Load for replace operations (0.0-1.0) |
| `c`   | number | Load for cancel operations (0.0-1.0)  |

+++ Example

```json !#
{
  "msgType": "rateLimitLoad",
  "accountId": 42,
  "adapterTimestampNs": 1677681492196000000,
  "rateLimitLoads": [
    {
      "id": "spot",
      "p": 0.25,
      "a": 0.10,
      "r": 0.05,
      "c": 0.15
    }
  ]
}
```

+++

Load values range from `0.0` (no usage) to `1.0` (limit fully used).
See [Concepts](concepts.md#rate-limit-loads) for the exchange-specific
rate-limit keys.

---

## Corporate Action

Published only when the `enableCorporateActions` login option is set.
Notifies the strategy of stock splits, dividends, and similar events.

+++ Structure

```!#
{
  "msgType": "corporateAction",
  "accountId": integer,
  "adapterTimestampNs": integer,
  "instrumentId": integer,
  "split": {
    "lastPrice": string,
    "buyVolume": string,
    "sellVolume": string,
    "liquidateFractionalShares": boolean
  },
  "dividend": {
    "underlyingId": integer,
    "highPrice": string,
    "lowPrice": string
  },
  "comment": string
}
```

| Field          | Type    | Description                                                                          |
|----------------|---------|--------------------------------------------------------------------------------------|
| `msgType`      | string  | Always `"corporateAction"`                                                           |
| `instrumentId` | integer | Instrument the action applies to                                                     |
| `split`        | object  | (Optional) Stock split details                                                       |
| `dividend`     | object  | (Optional) Dividend details                                                          |
| `comment`      | string  | (Optional) Free-text description of the action                                       |

`split.liquidateFractionalShares` is `true` when fractional shares from
the split are liquidated, `false` when they are rounded.

+++

---

## Invalid Request

Sent when the server cannot parse or route a request. Carries the
offending payload so the client can correlate the failure with what it
sent.

+++ Structure

```!#
{
  "msgType": "invalidRequest",
  "message": string,
  "request": string
}
```

| Field     | Type   | Description                              |
|-----------|--------|------------------------------------------|
| `msgType` | string | Always `"invalidRequest"`                |
| `message` | string | Reason the request was rejected          |
| `request` | string | The offending request as received        |

+++

---

## Rate Limit Errors

When an internal rate limit is exceeded, the response carries retry
timing information. The same shape applies whether the error type is
`RATE_LIMIT_EXCEEDED` (the limit itself was hit) or
`RATE_LIMIT_BLOCKED_AFTER_EXCEEDED` (the adapter is in a follow-on
block after a prior exceeded limit).

**Error structure:**

```json
{
  "error": {
    "type": "RATE_LIMIT_EXCEEDED",
    "message": "rate limit exceeded",
    "source": "INTERNAL",
    "retryTimeNs": 1677681492196000000
  }
}
```

`retryTimeNs` is a nanosecond-since-epoch timestamp giving the earliest
moment at which there should be enough rate-limit budget for a retry.
The `retryTime` capability flag tells you whether the adapter populates
this field.

> [!NOTE]
> After an internal rate-limit violation, wait until `retryTimeNs`
> before retrying. Earlier retries waste both rate-limit budget and CPU.

> [!WARNING]
> `retryTimeNs` is not a reservation. It only tells you when the budget
> is *expected* to free up. If another operation has consumed the
> budget by the time you retry, the retry will still fail.

---

## See Also

- [Concepts](concepts.md). Core concepts and conventions.
- [Enums](enums.md). Enumeration type definitions.
