> ## Documentation Index
> Fetch the complete documentation index at: https://docs.55-tech.com/llms.txt
> Use this file to discover all available pages before exploring further.

# ABP WebSocket - 实时更新

> 连接 ABP WebSocket 获取实时订单、投注、结算、账户和系统更新。基于服务端的消息排序和可靠传递。

## 端点

```
wss://v2.55-tech.com/ws
```

## 连接流程

### 1. 连接

打开WebSocket连接。连接时无需身份验证。

### 2. 登录

在连接后 **30秒** 内发送登录消息：

```json theme={null}
{
  "type": "login",
  "apiKey": "YOUR_API_KEY",
  "channels": []
}
```

空的 `channels` 数组订阅所有可用频道。要订阅特定频道：

```json theme={null}
{
  "type": "login",
  "apiKey": "YOUR_API_KEY",
  "channels": ["orders", "bets", "settlements"]
}
```

可选启用可靠传递和消息确认：

```json theme={null}
{
  "type": "login",
  "apiKey": "YOUR_API_KEY",
  "channels": ["orders", "bets"],
  "reliableDelivery": true
}
```

### 3. 登录确认

成功后，服务器响应：

```json theme={null}
{
  "type": "login_ok",
  "clientName": "your-client",
  "channels": ["orders", "bets", "settlements"],
  "subscriptionId": 1,
  "access": {
    "clientFiltered": ["orders", "bets", "settlements", "accounts", "balance", "betslip"],
    "global": ["fixtures", "currencies", "status", "emergency"]
  },
  "reliableDelivery": false,
  "features": {
    "messageOrdering": true,
    "acknowledgments": false,
    "batchAck": false
  }
}
```

### 4. 接收更新

数据消息格式如下：

```json theme={null}
{
  "type": "data",
  "channel": "orders",
  "event": "INSERT",
  "payload": { ... },
  "ts": 1771183909789,
  "seq": 1
}
```

| 字段           | 描述                                                 |
| ------------ | -------------------------------------------------- |
| `type`       | 数据消息始终为 `"data"`                                   |
| `channel`    | 此消息所属频道                                            |
| `event`      | 事件类型：`INSERT`、`UPDATE`、`DELETE`、`SETTLED`、`STATUS` |
| `payload`    | 完整的更新对象（见[负载结构](#负载结构)）                            |
| `ts`         | 服务器时间戳（自纪元以来的毫秒数）                                  |
| `seq`        | 每订阅的单调递增序列号                                        |
| `requireAck` | 仅在启用可靠传递时存在且为 `true`                               |

各频道完整的负载结构见下文[负载结构](#负载结构)。

### 5. 保持活跃

服务器每 **30秒** 发送一次 ping：

```json theme={null}
{"type": "ping"}
```

在 **120秒** 内回复 pong，否则连接将被关闭：

```json theme={null}
{"type": "pong"}
```

您也可以从客户端发送 ping — 服务器会回复 pong。

## 频道

### 客户端过滤频道

这些频道仅传递属于您 `clientName` 的数据：

| 频道            | 事件                     | 描述                                           |
| ------------- | ---------------------- | -------------------------------------------- |
| `orders`      | INSERT, UPDATE, DELETE | 订单下达、成交、状态变更                                 |
| `bets`        | INSERT, UPDATE, DELETE | 投注下达、确认和移除                                   |
| `settlements` | SETTLED                | 结算状态更新（负载与 bets 相同）                          |
| `accounts`    | INSERT, UPDATE, DELETE | 账户创建、更新和删除                                   |
| `balance`     | UPDATE                 | 余额变动通知                                       |
| `betslip`     | UPDATE                 | 活跃投注单订阅的实时赔率更新（每次 `GET /betslip` 调用后 60 秒窗口） |

### 全局频道

所有订阅者都会收到：

| 频道           | 事件     | 描述         |
| ------------ | ------ | ---------- |
| `fixtures`   | UPDATE | 赛事元数据和比分变更 |
| `currencies` | UPDATE | 货币汇率更新     |
| `status`     | STATUS | 系统状态变更     |
| `emergency`  | STATUS | 紧急模式激活/停用  |

## 可靠投递与确认

默认情况下，消息是「发后即忘」的——快速，但断连期间丢失的消息就此消失。在登录时启用**可靠投递**，即可获得带确认与重放的至少一次投递：

```json theme={null}
{ "type": "login", "apiKey": "YOUR_API_KEY", "channels": ["orders", "bets"], "reliableDelivery": true }
```

启用后，每条 `data` 消息都携带 `requireAck: true`，你必须确认它，服务端才能将其从缓冲区释放：

```json theme={null}
{ "type": "ack", "seq": 42 }
```

或在一条消息中确认一个范围（推荐，利于吞吐）：

```json theme={null}
{ "type": "ack_batch", "upToSeq": 50 }
```

**工作原理：**

* 服务端为每个订阅缓冲最多 **100 条未确认消息**，并重发任何 **30 秒**内未被确认的消息。
* `seq` 按订阅且严格递增，因此 `seq` 出现间隙意味着你漏掉了消息。
* 要在重连后恢复漏掉的消息，从你处理的最后一个 `seq` 请求重放：

```json theme={null}
{ "type": "replay", "fromSeq": 40 }
```

<Warning>
  若你落后超过 100 条消息的缓冲，最旧的未确认消息会被丢弃。请及时确认（或使用 `ack_batch`），并将任何 `replay` 无法填补的 `seq` 间隙视为通过 REST 端点（`GET /orders`、`GET /bets`）对账的信号。
</Warning>

## 变更订阅

无需重连即可变更频道：

```json theme={null}
{ "type": "update_channels", "channels": ["orders", "bets", "settlements"] }
```

服务端以 `channels_updated` 回复。

## 赔率单订阅

`betslip` 频道为你关注的选项推送实时赔率。有两种订阅方式：

1. **通过 REST**——调用 `GET /betslip` 会注册一个 **60 秒滑动窗口**；每次调用重置计时器并立即通过 WebSocket 广播一份快照。
2. **通过 WebSocket**——发送 `subscribe_betslip` 消息并附显式 `ttl`（10–3600 秒）与博彩商列表以获得更精细的控制，用 `unsubscribe_betslip` 提前取消。

每客户端最多 **20** 个活跃赔率单订阅（REST + WS 合计）。消息结构与 `subscribed_betslip` 确认见下文[赔率单订阅消息](#赔率单订阅消息)。

## 示例：Python 客户端

```python theme={null}
import asyncio
import json
import websockets

async def connect():
    uri = "wss://v2.55-tech.com/ws"
    async with websockets.connect(uri) as ws:
        # 登录
        await ws.send(json.dumps({
            "type": "login",
            "apiKey": "YOUR_API_KEY",
            "channels": ["orders", "bets", "settlements"]
        }))

        # 等待登录确认
        login_resp = json.loads(await ws.recv())
        print(f"已登录为 {login_resp.get('clientName')}")

        # 监听更新（自动回复 ping）
        async for message in ws:
            data = json.loads(message)
            if data["type"] == "ping":
                await ws.send(json.dumps({"type": "pong"}))
            elif data["type"] == "data":
                print(f"[{data['channel']}:{data['event']}] {data['payload']}")

asyncio.run(connect())
```

## 示例：JavaScript 客户端

```javascript theme={null}
const WebSocket = require('ws');

const ws = new WebSocket('wss://v2.55-tech.com/ws');

ws.on('open', () => {
  ws.send(JSON.stringify({
    type: 'login',
    apiKey: 'YOUR_API_KEY',
    channels: ['orders', 'bets', 'settlements']
  }));
});

ws.on('message', (raw) => {
  const msg = JSON.parse(raw);

  if (msg.type === 'login_ok') {
    console.log(`已登录为 ${msg.clientName}`);
  }

  if (msg.type === 'ping') {
    ws.send(JSON.stringify({ type: 'pong' }));
  }

  if (msg.type === 'data') {
    console.log(`[${msg.channel}:${msg.event}]`, msg.payload);
  }
});
```

## 消息类型

### 客户端 → 服务端

| 类型                    | 示例                                                    | 用途                                 |
| --------------------- | ----------------------------------------------------- | ---------------------------------- |
| `login`               | `{"type": "login", "apiKey": "...", "channels": []}`  | 鉴权并订阅。可选 `reliableDelivery: true`。 |
| `update_channels`     | `{"type": "update_channels", "channels": ["orders"]}` | 登录后变更频道订阅。                         |
| `pong`                | `{"type": "pong"}`                                    | 回复服务端的 `ping`。                     |
| `ping`                | `{"type": "ping"}`                                    | 存活检查；服务端以 `pong` 回复。               |
| `ack`                 | `{"type": "ack", "seq": 42}`                          | 确认单条消息（可靠投递）。                      |
| `ack_batch`           | `{"type": "ack_batch", "upToSeq": 50}`                | 确认直到并包含 `upToSeq` 的所有消息。           |
| `replay`              | `{"type": "replay", "fromSeq": 40}`                   | 请求从 `fromSeq` 起重发缓冲消息。             |
| `subscribe_betslip`   | 见[下文](#赔率单订阅消息)                                       | 以自定义 TTL 开启赔率单价格订阅。                |
| `unsubscribe_betslip` | 见[下文](#赔率单订阅消息)                                       | 提前取消赔率单订阅。                         |

### 服务端 → 客户端

| 类型                   | 用途                                        |
| -------------------- | ----------------------------------------- |
| `login_ok`           | 登录成功；回显频道、访问范围与已启用功能。                     |
| `channels_updated`   | 确认一次 `update_channels` 变更。                |
| `data`               | 一个频道事件（见[负载结构](#负载结构)）。                   |
| `ping`               | 服务端保活；以 `pong` 回复。                        |
| `subscribed_betslip` | 确认一次 `subscribe_betslip`，附有效 `expiresAt`。 |
| `error`              | 客户端消息出错；含指向相关请求的 `ref`。                   |

## 赔率单订阅消息

在订阅前，`betslip` 频道必须在你的订阅中（通过 `login` 或 `update_channels` 添加）。

### `subscribe_betslip`

```json theme={null}
{
  "type": "subscribe_betslip",
  "fixtureId": "id1000004461512432",
  "outcomeId": 103,
  "playerId": 0,
  "bookmakers": ["pinnacle", "sharpbet"],
  "ttl": 300
}
```

* `bookmakers` 必须为非空列表。
* `ttl` 可选（秒），限制在 **10–3600 秒**；省略时默认 **60 秒**。
* 对同一选项重新订阅会刷新 TTL 并更新博彩商集合（幂等）。

服务端确认：

```json theme={null}
{
  "type": "subscribed_betslip",
  "fixtureId": "id1000004461512432",
  "outcomeId": 103,
  "playerId": 0,
  "bookmakers": ["pinnacle", "sharpbet"],
  "expiresAt": "2026-02-07T17:34:37+00:00"
}
```

你没有账户的博彩商会从订阅中剔除，并在 `skipped` 数组中返回。每客户端最多 **20** 个活跃赔率单订阅（REST + WS 合计）。

### `unsubscribe_betslip`

```json theme={null}
{
  "type": "unsubscribe_betslip",
  "fixtureId": "id1000004461512432",
  "outcomeId": 103,
  "playerId": 0
}
```

## 负载结构

数据消息将频道负载包裹在信封 `{type, channel, event, payload, ts, seq}` 中。`orders`、`bets` 与 `settlements` 频道投递**完整数据库行**（`row_to_json`）；`balance`、`emergency` 与 `betslip` 使用自定义结构。

### Orders

`orders` 表的完整行：

```json theme={null}
{
  "orderId": 327,
  "requestUuid": "eb45b192-317b-42d5-9f65-af497b9fa8c1",
  "client": "demo",
  "clientName": "demo",
  "fixtureId": "id1000004461512432",
  "outcomeId": 103,
  "playerId": 0,
  "orderPrice": 1.95,
  "orderStake": 10.0,
  "filledStake": 10.0,
  "remainingStake": 0.0,
  "orderStatus": "FILLED",
  "statusReason": null,
  "userRef": "bettor1234",
  "testOrder": false,
  "acceptBetterOdds": true,
  "acceptPartialStake": true,
  "orderCurrency": "USD",
  "back": true,
  "allowedBookmakers": "*",
  "oddsInfo": null,
  "meta": {},
  "expiresAt": "2026-02-07T17:29:37+00:00",
  "filledAt": "2026-02-07T17:29:32+00:00",
  "createdAt": "2026-02-07T17:29:32+00:00",
  "updatedAt": "2026-02-07T17:29:32+00:00"
}
```

### Bets / settlements

`bets` 表的完整行（`settlements` 频道使用相同结构）：

```json theme={null}
{
  "betId": 73,
  "orderId": 327,
  "bookmaker": "pinnacle",
  "bookmakerBetId": "3332684214",
  "betStatus": "CONFIRMED",
  "settlementStatus": "UNSETTLED",
  "placedStake": 10.0,
  "placedPrice": 1.98,
  "placedCurrency": "USD",
  "account": "pinnacle_main",
  "requestUuid": "eb45b192-317b-42d5-9f65-af497b9fa8c1",
  "userRef": "bettor1234",
  "testBet": false,
  "client": "demo",
  "clientName": "demo",
  "sentData": { "stake": 10.0, "price": 1.95 },
  "receivedData": { "betId": "3332684214", "status": "accepted", "price": 1.98 },
  "settlementAmount": null,
  "settlementReason": null,
  "settledAt": null,
  "declineReason": null,
  "betRequestId": null,
  "oddsInfo": null,
  "meta": {},
  "placedAt": "2026-02-07T17:29:32+00:00",
  "createdAt": "2026-02-07T17:29:32+00:00",
  "updatedAt": "2026-02-07T17:29:32+00:00"
}
```

<Note>
  同时订阅 `bets` 与 `settlements` 会使结算更新被投递**两次**——每个频道各一次。若想要专门的结算流，请单独使用 `settlements`。
</Note>

### Balance

```json theme={null}
{
  "clientName": "demo",
  "bookmaker": "pinnacle",
  "username": "pinnacle_main",
  "balance": 4990.0,
  "currency": "USD",
  "ts": 1771183910123
}
```

### Emergency

```json theme={null}
{
  "active": true,
  "reason": "Upstream provider maintenance",
  "ts": 1771183910123
}
```

### Betslip

在赔率单订阅活跃期间推送。对于期货，负载携带 `futureId` 与 `participantId` 而非 `fixtureId`。

**赛事：**

```json theme={null}
{
  "clientName": "demo",
  "fixtureId": "id1000004461512432",
  "outcomeId": 103,
  "playerId": 0,
  "odds": {
    "pinnacle": {
      "id1000004461512432:pinnacle:103:0": {
        "price": 1.98,
        "limit": 500.0,
        "limitMin": 1.0,
        "limitCurrency": "USD",
        "limitUsd": 500.0,
        "limitMinUsd": 1.0,
        "active": true,
        "account": "pinnacle_main",
        "currencyInfo": { "currency": "USD", "currencyValue": 1, "updatedAt": "2026-02-07T17:29:32+00:00" }
      }
    }
  }
}
```

**期货（即将推出）：**

```json theme={null}
{
  "clientName": "demo",
  "futureId": "fut_123456",
  "outcomeId": 0,
  "playerId": 0,
  "participantId": 5,
  "odds": {
    "pinnacle": {
      "fut_123456:pinnacle:0:0:5": {
        "price": 3.50,
        "limit": 1000.0,
        "limitMin": 5.0,
        "limitCurrency": "USD",
        "limitUsd": 1000.0,
        "limitMinUsd": 5.0,
        "active": true,
        "account": "pinnacle_main",
        "currencyInfo": { "currency": "USD", "currencyValue": 1, "updatedAt": "2026-02-07T17:29:32+00:00" }
      }
    }
  }
}
```

<Note>
  期货赔率单的 WebSocket 广播尚未启用。订阅基础设施已就位，将在后续版本中激活。
</Note>

## 连接限制

| 设置               | 取值       |
| ---------------- | -------- |
| 每个 API key 最大连接数 | 5        |
| 鉴权超时             | 30 秒     |
| 服务端 ping 间隔      | 30 秒     |
| Pong 超时（断开）      | 120 秒    |
| 消息缓冲（可靠投递）       | 100 条消息  |
| 每客户端输出队列         | 2000 条消息 |
| 每客户端最大赔率单订阅数     | 20       |

## 后续步骤

<Columns cols={2}>
  <Card title="币种与额度" icon="coins" href="/zh/abp-api/currency">
    各币种下数值如何计价与换算。
  </Card>

  <Card title="错误处理" icon="triangle-exclamation" href="/zh/abp-api/errors">
    处理错误响应与被拒原因。
  </Card>
</Columns>
