> 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/3.5-qu-xiao-ding-yue.md).

# 🔕 3.5 取消订阅

客户端可以通过取消订阅指令，停止接收指定产品或市场的实时行情。

支持：

* 取消单个产品。
* 一次取消多个产品。
* 取消单个市场。
* 一次取消多个市场。
* 在保持 WebSocket 连接的情况下调整订阅范围。

### 使用前提

只能取消当前连接中已经成功订阅的产品或市场。

建议流程：

```
发送订阅指令
        ↓
确认订阅成功
        ↓
等待至少5秒
        ↓
发送取消订阅指令
        ↓
检查取消结果
        ↓
更新客户端订阅状态
```

订阅和取消订阅属于两条独立业务指令，建议至少间隔5秒。

### 3.5.1 取消单个产品

指令格式：

```
/unsymbol/交易所:产品代码
```

取消 JM 市场的 BTCUSDT：

```
/unsymbol/JM:BTCUSDT
```

格式说明：

| 内容   | 示例           | 说明         |
| ---- | ------------ | ---------- |
| 指令类型 | `/unsymbol/` | 表示取消产品订阅   |
| 交易所  | `JM`         | 产品所属市场或交易所 |
| 分隔符  | `:`          | 分隔交易所与产品代码 |
| 产品代码 | `BTCUSDT`    | 需要取消的产品    |

> 必须使用 `/unsymbol/`，不要使用旧文档中的错误拼写 `/unsymol/`。

### 产品取消成功

服务端返回：

```
{
  "msg": [
    {
      "res": true,
      "code": "JM:BTCUSDT",
      "message": "Unsubscription symbol successful.",
      "cancelledCount": 1
    }
  ],
  "type": "UnSub"
}
```

字段说明：

| 字段                     | 类型      | 说明                       |
| ---------------------- | ------- | ------------------------ |
| `type`                 | String  | 产品取消结果，值为 `UnSub`        |
| `msg`                  | Array   | 本次取消指令中每个产品的处理结果         |
| `msg[].res`            | Boolean | `true` 表示成功，`false` 表示失败 |
| `msg[].code`           | String  | 被取消的交易所和产品代码             |
| `msg[].message`        | String  | 取消结果说明                   |
| `msg[].cancelledCount` | Integer | 本次取消匹配到的订阅数量             |

### 3.5.2 一次取消多个产品

多个产品使用英文逗号分隔：

```
/unsymbol/JM:BTCUSDT,JM:ETHUSDT
```

每个产品都必须包含交易所代码：

```
交易所:产品代码
```

正确：

```
/unsymbol/JM:BTCUSDT,JM:ETHUSDT
```

错误：

```
/unsymbol/BTCUSDT,ETHUSDT
```

错误：

```
/unsymbol/JM:BTCUSDT, JM:ETHUSDT
```

### 多产品取消成功

服务端对每个产品分别返回结果：

```
{
  "msg": [
    {
      "res": true,
      "code": "JM:BTCUSDT",
      "message": "Unsubscription symbol successful.",
      "cancelledCount": 1
    },
    {
      "res": true,
      "code": "JM:ETHUSDT",
      "message": "Unsubscription symbol successful.",
      "cancelledCount": 1
    }
  ],
  "type": "UnSub"
}
```

客户端必须遍历 `msg` 数组，逐项检查 `res`。

不要因为其中一个产品取消失败，就忽略其他产品的成功结果。

### 3.5.3 取消单个市场

指令格式：

```
/unexchange/市场代码
```

取消 JM 市场订阅：

```
/unexchange/JM
```

取消成功后，服务端返回：

```
{
  "msg": [
    {
      "res": true,
      "code": "JM",
      "message": "Unsubscribe Symbols successful.",
      "cancelledCount": 0
    }
  ],
  "type": "UnSubmkt"
}
```

市场取消结果的消息类型为：

```
UnSubmkt
```

### 3.5.4 一次取消多个市场

多个市场代码使用英文逗号分隔：

```
/unexchange/JM,NASDAQ
```

正确：

```
/unexchange/JM,NASDAQ
```

不推荐：

```
/unexchange/JM, NASDAQ
```

客户端应逐项检查返回结果，并更新本地市场订阅列表。

### 产品取消与市场取消

| 取消对象 | 指令                 | 返回类型       |
| ---- | ------------------ | ---------- |
| 产品   | `/unsymbol/交易所:产品` | `UnSub`    |
| 市场   | `/unexchange/市场`   | `UnSubmkt` |

### 成功判断

客户端应以 `res` 判断取消结果：

```
{
  "res": true
}
```

不要只根据 `cancelledCount` 判断是否成功。

实际验证中，市场取消成功时可能返回：

```
{
  "res": true,
  "cancelledCount": 0
}
```

因此：

* `res: true`：取消成功。
* `res: false`：取消失败。
* `cancelledCount`：作为补充信息使用。

### 取消未订阅的产品

取消当前连接中未订阅的产品：

```
/unsymbol/JM:INVALID_SYMBOL
```

服务端返回：

```
{
  "msg": [
    {
      "res": false,
      "code": "JM:INVALID_SYMBOL",
      "message": "Cannot unsubscribe if not subscribed.",
      "cancelledCount": 0
    }
  ],
  "type": "UnSub"
}
```

该错误不会导致 WebSocket 连接断开。

### 取消未订阅的市场

取消当前连接中未订阅的市场：

```
/unexchange/INVALID_MARKET
```

服务端返回：

```
{
  "msg": [
    {
      "res": false,
      "code": "INVALID_MARKET",
      "message": "Cannot unsubscribe if not subscribed.",
      "cancelledCount": 0
    }
  ],
  "type": "UnSubmkt"
}
```

客户端应从本地订阅状态中检查目标是否已订阅，避免发送无效的取消指令。

### 在途行情

取消订阅成功后，客户端仍可能短暂收到少量行情数据。

原因是部分消息可能已经：

* 由服务端生成。
* 进入发送队列。
* 在网络中传输。
* 到达客户端接收缓冲区。

因此，取消成功并不表示所有行情会在同一时刻立即停止。

客户端可以使用本地订阅状态过滤已经取消的行情：

```
收到行情
        ↓
检查产品或市场是否仍在活动订阅列表
        ↓
是：继续处理
否：忽略该行情
```

### JavaScript示例

以下示例假设 WebSocket 已经连接成功。

#### 取消单个产品

```
socket.send(
  "/unsymbol/JM:BTCUSDT"
);
```

#### 取消多个产品

```
socket.send(
  "/unsymbol/JM:BTCUSDT,JM:ETHUSDT"
);
```

#### 取消单个市场

```
socket.send(
  "/unexchange/JM"
);
```

#### 取消多个市场

```
socket.send(
  "/unexchange/JM,NASDAQ"
);
```

### JavaScript结果处理

```
function handleUnsubscribe(data) {
  for (const result of data.msg) {
    if (result.res) {
      console.log(
        "取消成功：",
        result.code
      );

      activeSubscriptions.delete(
        result.code
      );
    } else {
      console.error(
        "取消失败：",
        result.code,
        result.message
      );
    }
  }
}
```

处理产品和市场取消结果：

```
socket.addEventListener("message", (event) => {
  const data = JSON.parse(event.data);

  if (
    !Array.isArray(data) &&
    (
      data.type === "UnSub" ||
      data.type === "UnSubmkt"
    )
  ) {
    handleUnsubscribe(data);
    return;
  }

  if (Array.isArray(data)) {
    handleMarketData(data);
  }
});
```

### Python示例

#### 取消单个产品

```
ws.send(
    "/unsymbol/JM:BTCUSDT"
)
```

#### 取消多个产品

```
ws.send(
    "/unsymbol/JM:BTCUSDT,JM:ETHUSDT"
)
```

#### 取消单个市场

```
ws.send(
    "/unexchange/JM"
)
```

#### 取消多个市场

```
ws.send(
    "/unexchange/JM,NASDAQ"
)
```

### Python结果处理

```
def handle_unsubscribe(data):
    for result in data["msg"]:
        code = result["code"]

        if result["res"]:
            print(
                "取消成功：",
                code,
            )

            active_subscriptions.discard(code)
        else:
            print(
                "取消失败：",
                code,
                result["message"],
            )
```

处理消息：

```
import json


def on_message(ws, message):
    data = json.loads(message)

    if (
        isinstance(data, dict)
        and data.get("type")
        in ("UnSub", "UnSubmkt")
    ):
        handle_unsubscribe(data)
        return

    if isinstance(data, list):
        handle_market_data(data)
```

### 本地状态管理

客户端应维护活动订阅列表：

```
activeSymbols
activeExchanges
```

取消成功后：

* 产品取消成功：从 `activeSymbols` 中移除。
* 市场取消成功：从 `activeExchanges` 中移除。
* 取消失败：保留原状态并记录错误。
* 连接断开：清空本次连接的活动订阅状态。

### 使用建议

* 只取消当前连接中已经成功订阅的目标。
* 产品取消使用 `/unsymbol/`。
* 市场取消使用 `/unexchange/`。
* 产品必须使用完整的 `交易所:产品代码`。
* 多个取消项使用英文逗号分隔。
* 订阅和取消指令之间至少间隔5秒。
* 批量取消时逐项检查 `res`。
* 以 `res` 判断成功，不要只判断 `cancelledCount`。
* 允许取消成功后短暂收到在途行情。
* 取消成功后及时更新客户端本地订阅状态。
* 避免频繁订阅和取消，减少不必要的服务端压力。
