> For the complete documentation index, see [llms.txt](https://docscn.jkidata.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docscn.jkidata.com/websocket-xie-yi-shuo-ming.md).

# WebSocket 协议说明

JKiData WebSocket API 用于建立客户端与服务端之间的实时通信连接，持续接收市场行情和 K 线更新。

它需要与rest api进行联动，比如说获取到符号进行订阅

### 主要能力

WebSocket API 支持：

* 按产品代码订阅实时行情。
* 同时订阅多个产品。
* 按市场订阅行情。
* 同时订阅多个市场。
* 在同一个连接中持续接收行情更新。
* 一次推送多条行情数据。
* 返回订阅成功或失败的详细结果。
* 取消已有订阅。
* 使用心跳机制维持连接。
* 客户端主动断开连接。
* WebSocket 协议层压缩。

### 文档结构

WebSocket 文档按照实际接入流程分为以下页面。

| 页面             | 说明                     |
| -------------- | ---------------------- |
| WebSocket 协议说明 | 介绍协议结构、消息类型和开发规则       |
| 连接             | 介绍连接地址、API Key 认证和连接结果 |
| 订阅行情           | 介绍市场订阅、产品订阅和批量订阅       |
| 取消订阅           | 介绍如何停止接收指定市场或产品的数据     |
| 心跳机制           | 介绍心跳指令、超时处理和连接保活       |
| 断开连接           | 介绍客户端正常关闭及异常断线处理       |

### 标准接入流程

```
建立 WebSocket 连接
        ↓
等待连接成功消息
        ↓
连接成功后等待至少 5 秒
        ↓
发送市场或产品订阅指令
        ↓
检查每一项订阅结果
        ↓
持续接收实时行情
        ↓
定时执行心跳机制
        ↓
取消订阅或断开连接
```

### 连接与指令间隔

客户端建立连接后，应先等待服务端返回连接成功消息。

```
{
  "type": "connect",
  "msg": "ok"
}
```

收到连接成功消息后，必须等待至少 5 秒，再发送订阅或其他业务指令。

```
收到 connect: ok
        ↓
等待至少 5 秒
        ↓
发送第一条业务指令
```

不要在 WebSocket 刚刚打开时立即发送订阅指令。

如果需要分批发送多条订阅指令，也应控制发送频率，避免在短时间内连续发送大量命令。

### 上行与下行

#### 上行消息

上行消息是客户端发送给服务端的指令，包括：

* 市场订阅。
* 产品订阅。
* 取消市场订阅。
* 取消产品订阅。
* 心跳指令。
* 断开连接指令。

具体指令格式在对应子页面中说明。

#### 下行消息

下行消息是服务端向客户端返回的数据，包括：

* 连接状态。
* 订阅结果。
* 取消订阅结果。
* 心跳结果。
* 实时报价。
* K 线更新。
* 错误信息。

### 消息类型

服务端消息主要通过 `type` 字段区分。

| `type`      | 说明             |
| ----------- | -------------- |
| `connect`   | WebSocket 连接状态 |
| `bind`      | 订阅或绑定结果        |
| `quote`     | 实时报价数据         |
| `bars`      | K 线更新数据        |
| `heartbeat` | 心跳相关消息         |

不同消息类型的数据结构不同，客户端应先识别 `type`，再解析对应的 `msg`。

### 连接状态消息

连接成功后，服务端返回：

```
{
  "type": "connect",
  "msg": "ok"
}
```

只有收到该消息后，才能认为 API Key 验证和 WebSocket 连接已经成功。

未收到连接成功消息时，不应发送订阅指令。

### 订阅结果消息

发送市场或产品订阅指令后，服务端返回 `bind` 消息。

```
{
  "type": "bind",
  "msg": [
    {
      "type": "symbol",
      "res": true,
      "message": "Subscription success."
    }
  ]
}
```

字段说明：

| 字段              | 类型      | 说明                       |
| --------------- | ------- | ------------------------ |
| `type`          | String  | 消息类型，订阅结果为 `bind`        |
| `msg`           | Array   | 本次指令中每一个订阅项的处理结果         |
| `msg[].type`    | String  | 订阅类型，例如产品或市场             |
| `msg[].res`     | Boolean | `true` 表示成功，`false` 表示失败 |
| `msg[].message` | String  | 订阅结果的文字说明                |

批量订阅时，`msg` 数组中可能同时包含成功和失败结果。

客户端必须逐项检查 `res`，不能只检查外层 `type`。

### 多条行情推送

行情下行消息使用 JSON 数组。

一个 WebSocket 文本消息中可以同时包含多个行情对象：

```
[
  {
    "type": "quote",
    "msg": {
      "e": "JM",
      "s": "BTCUSDT",
      "p": "77727.8"
    }
  },
  {
    "type": "quote",
    "msg": {
      "e": "JM",
      "s": "ETHUSDT",
      "p": "2436.49"
    }
  }
]
```

因此，客户端不能将每次收到的数据直接当作单个行情对象处理。

正确处理流程：

```
收到 WebSocket 文本消息
        ↓
解析为 JSON
        ↓
判断是否为数组
        ↓
遍历数组中的每个元素
        ↓
根据每个元素的 type 分别处理
```

### 混合消息

同一个行情数组中可能同时包含不同类型的数据，例如：

```
[
  {
    "type": "bars",
    "msg": {}
  },
  {
    "type": "quote",
    "msg": {}
  }
]
```

客户端应逐条检查 `type`，不要假设一个数组中的全部元素都是同一种消息。

### 产品与市场订阅

WebSocket 支持两种订阅方式。

#### 产品订阅

适用于只需要关注指定产品的场景。

支持：

* 单个产品。
* 多个产品。
* 同一个市场下的多个产品。
* 不同市场下的多个产品。

#### 市场订阅

适用于需要接收某个市场全部行情的场景。

支持：

* 单个市场。
* 多个市场同时订阅。

市场订阅可能产生大量实时数据，客户端应具备足够的消息处理能力。

具体订阅指令请查看“订阅行情”页面。

### 建议订阅数量

服务端允许批量提交多个产品，但为避免客户端一次发送过多命令，建议：

* 单次产品订阅不超过 50 个符号。
* 超过 50 个产品时分批发送。
* 每批指令之间保留合理间隔。
* 如果需要某个市场的大量产品，优先考虑市场订阅。
* 不要将“建议50个”理解为服务端的固定技术边界。

### 错误处理原则

WebSocket 业务错误不一定会断开连接。

市场或产品不存在时，服务端可能保持连接，并通过订阅结果返回：

```
{
  "type": "bind",
  "msg": [
    {
      "type": "symbol",
      "res": false,
      "message": "The symbol does not exist."
    }
  ]
}
```

客户端应：

1. 检查外层消息类型。
2. 遍历 `msg` 数组。
3. 检查每一项的 `res`。
4. 记录失败的市场或产品。
5. 不要反复订阅已经确认无效的目标。
6. 保持其他有效订阅继续运行。

### API Key 错误

API Key 通过 WebSocket 连接地址进行认证。

当 API Key 无效时，连接可能在未返回 `connect: ok` 的情况下被关闭。

实际验证中，错误 Key 的连接表现为：

```
WebSocket 已打开
        ↓
未收到 connect: ok
        ↓
连接异常关闭
        ↓
关闭代码 1006
```

客户端应设置连接确认超时。如果在规定时间内没有收到 `connect: ok`，应将连接判定为失败。

不要在认证失败后立即无限重连。

### 行情字段兼容

不同市场、产品类型和数据源返回的字段可能不同。

客户端应遵循以下规则：

* 不依赖固定字段顺序。
* 不要求所有市场返回完全相同的字段。
* 允许部分字段缺失或为空。
* 数字字段可能以字符串形式返回。
* 未识别字段应忽略或保留，不应导致程序崩溃。
* 使用 `e` 和 `s` 识别交易所与产品。
* 根据消息的 `type` 选择对应解析方式。

完整字段含义请查看“实时报价字段说明”。

### 异步处理建议

WebSocket 行情可能高频推送，并且每次消息可能包含多条数据。

建议：

* 使用异步方式接收和处理消息。
* 接收线程不要执行耗时业务。
* 将原始数据投递到队列后再进行计算。
* 不要在接收回调中执行同步数据库写入。
* 不要在接收回调中执行同步日志写入。
* 对高频日志进行采样或批量处理。
* 监控消息积压、解析失败和重连次数。

### 数据压缩

WebSocket 支持：

```
permessage-deflate
```

该压缩方式属于 WebSocket 协议层压缩，通常在建立连接时自动协商。

客户端一般会自动完成解压，不需要手动处理压缩内容。解压后的数据仍按照普通 JSON 解析。

### 连接状态管理

客户端建议维护以下状态：

```
DISCONNECTED
CONNECTING
CONNECTED
SUBSCRIBING
SUBSCRIBED
RECONNECTING
CLOSING
```

应用程序应同时记录：

* 当前连接状态。
* 已发送的订阅指令。
* 订阅成功的市场和产品。
* 订阅失败的市场和产品。
* 最近一次心跳时间。
* 最近一次收到数据的时间。
* 当前重连次数。

### 断线重连

连接异常断开后，可以执行自动重连，但应采用递增等待时间。

例如：

```
第1次重连：等待 5 秒
第2次重连：等待 10 秒
第3次重连：等待 20 秒
```

重连成功并收到 `connect: ok` 后：

1. 等待至少 5 秒。
2. 恢复之前已经确认成功的订阅。
3. 不自动恢复已经确认失败的订阅。
4. 避免短时间内重复发送相同指令。

### 安全建议

* API Key 应保存在服务端环境变量或密钥管理系统中。
* 不要将正式 API Key 写入前端代码。
* 不要在公开仓库中提交 API Key。
* 不要在公开日志中记录完整的 WebSocket 连接地址。
* 测试环境和生产环境应使用不同的 API Key。
* 怀疑密钥泄露时，应及时更换密钥。
