# WebSocket 共用与币安限频评估

## 一、币安合约 WS 限制（摘要）

- **Base URL**：`wss://fstream.binance.com`
- **单 stream**：`/ws/<streamName>`
- **组合 stream**：`/stream?streams=/<streamName1>/<streamName2>/...`
- **单连接最多订阅**：**1024 个 Streams**
- **订阅限速**：**每秒最多 10 条订阅消息**，超限会断连；**反复断连的 IP 可能被屏蔽**
- **连接有效期**：单连接不超过 24 小时，需断线重连
- **Ping/Pong**：服务端约 3 分钟发 ping，客户端需在 10 分钟内回复 pong（当前 aiohttp heartbeat 已满足）

---

## 二、当前系统 WS 使用方式（按进程）

当前为 **一进程一账户**（如 supervisor 按 `ATS_ACCOUNT_ID` 起多个 trading_system 进程），每个进程内：

| 用途 | 连接数/进程 | 订阅内容 | 是否公开 | 可否多账户共用 |
|------|-------------|----------|----------|----------------|
| **UserDataStream** | 1 | listenKey（订单/持仓/余额） | 否（需 listenKey） | **否**，每账户必须独立 |
| **Ticker24hStream** | 1 | `!ticker@arr`（全市场 24h ticker） | 是 | **可共用**，全市场一份即可 |
| **BookTickerStream** | 1 | `!bookTicker`（全市场最优买卖） | 是 | **可共用**，全市场一份即可 |
| **KlineStream** | 1 | `/stream` + 动态 `SUBSCRIBE`：`<symbol>@kline_<interval>` | 是 | **可共用**，按需订阅，单连接 ≤1024 streams |
| **持仓价格监控**（position_manager） | **每持仓 1 条** | 每连接 `/ws/<symbol>@ticker` | 是 | **可共用**，可合并为一条组合流 |

即：**每账户 4 条固定连接 + 持仓数个单独连接**。

---

## 三、多账户时的连接与订阅量（当前架构）

设账户数为 **N**，单账户最大持仓数为 **M**（如 10）：

- **UserDataStream**：N 条（每账户 1，**不能省**）
- **Ticker24hStream**：N 条 → 若共用为 **1 条**
- **BookTickerStream**：N 条 → 若共用为 **1 条**
- **KlineStream**：N 条，每连接动态订阅若干 `symbol@kline_interval` → 若共用为 **1 条**，总 streams 数为「所有账户用到的 (symbol, interval) 并集」
- **持仓监控**：N × M 条（每持仓一个 `/ws/<symbol>@ticker`）→ 若合并为一条组合流，为 **1 条连接**，streams 数为「所有账户持仓的 symbol 并集」，且 **≤1024**

**当前是否超限：**

- **1024 streams/连接**：单条 KlineStream 当前按「扫描/策略用到的 symbol×interval」订阅，通常几十～小几百级，远低于 1024；持仓监控若合并为一条组合流，symbol 数一般也远小于 1024。**正常使用不会超**。
- **10 条订阅/秒**：KlineStream 在扫描时可能对多个 symbol×interval 连续调用 `subscribe`，若并发高会短时间多发订阅消息，有**超过 10 条/秒**的风险，存在被断连、进而 IP 被限的风险。**需要限速**（见下文实现）。

---

## 四、可共用的流与建议

### 1. 强烈建议共用（公开、全市场一份即可）

- **Ticker24hStream**（`!ticker@arr`）  
  - 全市场 24h 行情，与账户无关。  
  - **建议**：单机多进程时，可由一个「行情进程」或主进程单独起 1 条连接，写入 Redis/共享缓存，其他进程只读缓存；或若已用 Redis 等共享缓存，仅一个进程负责拉 WS 并更新缓存。

- **BookTickerStream**（`!bookTicker`）  
  - 全市场最优买卖，与账户无关。  
  - **建议**：同上，共用 1 条连接 + 共享缓存。

### 2. 建议共用（减少连接与 stream 总数）

- **KlineStream**（`<symbol>@kline_<interval>`）  
  - 按 (symbol, interval) 动态订阅，单连接最多 1024 streams。  
  - 多账户共用 1 条连接时，订阅集合为「各账户用到的 (symbol, interval) 并集」，通常仍远小于 1024。  
  - **建议**：单机多进程时，可只在一个进程内起 KlineStream，其他进程通过 Redis/共享内存读 K 线缓存；或部署一个共享「K 线 WS 服务」供多进程使用。

- **持仓监控**（`<symbol>@ticker`）  
  - 当前实现为「每持仓 1 条 `/ws/<symbol>@ticker`」，多账户×多持仓会变成 N×M 条连接。  
  - **建议**：改为**一条组合流**：`/stream?streams=s1@ticker/s2@ticker/...`，把所有需要监控的 symbol 放在一起，总 symbol 数 <1024 即可。多账户可共用这一条连接（订阅「所有账户持仓 symbol 的并集」），每个账户只消费自己关心的 symbol 即可。

### 3. 不能共用

- **UserDataStream**（listenKey）  
  - 与账户绑定，每账户独立 listenKey、独立连接。**必须每账户 1 条连接**。

---

## 五、限频与实现建议

### 1. 订阅消息 ≤10 条/秒（必须遵守）

- **KlineStream** 在 `subscribe(symbol, interval)` 时向同一连接发送 SUBSCRIBE。  
- 若扫描阶段并发请求多个 symbol×interval，会在短时间连续发送多条订阅，容易超过 **10 条/秒**。  
- **实现**：在 KlineStream 内做**订阅限速**：例如维护「最近 1 秒内已发送的订阅次数」，若已达 10 次则 sleep 到「满 1 秒」后再发；或批量排队，按每秒最多 8～10 条发送。  
- 已在代码中为 KlineStream 增加限速逻辑（见下节）。

### 2. 单连接 1024 streams

- 当前 KlineStream 与持仓监控的 stream 数量均远低于 1024，只需在后续若「预订阅」大量 symbol×interval 或大量持仓时，确保单连接订阅数 ≤1024 即可（可做订阅数统计与上限检查）。

### 3. 连接 24 小时

- 现有各 WS 均有断线重连，满足「不超过 24 小时」的文档要求；保持即可。

---

## 六、结论与风险

| 项目 | 当前是否可能超限 | 说明 |
|------|------------------|------|
| 单连接 1024 streams | 否 | 当前 K 线与持仓监控订阅量远低于 1024 |
| 10 条订阅/秒 | **是（有风险）** | KlineStream 扫描时可能短时大量 subscribe，已加限速 |
| 连接数 | 否 | 多账户时连接数线性增长，共用后可显著下降 |
| 24h / Ping-Pong | 否 | 已重连 + heartbeat |

**已实现：**

1. **KlineStream 订阅限速**：发送 SUBSCRIBE 时控制在 ≤10 条/秒。  
2. **多进程/多账户共用 Ticker24h、BookTicker、KlineStream**：  
   - 使用 Redis 选主（`market_ws_leader`）：仅 **Leader** 进程建立上述三条 WS 连接并写入 Redis。  
   - 非 Leader 进程不建这三条连接，通过 **Redis 刷新任务**（Ticker24h/BookTicker 每 2 秒从 Redis 拉取）和 **get_klines 时读 Redis** 获取数据。  
   - 配置项 `USE_SHARED_MARKET_WS`（默认 true）：有 Redis 时启用共用；关闭则每进程独立建连接。  
3. **持仓监控**：仍为每进程按持仓建多条 `/ws/<symbol>@ticker`；后续可改为单连接组合流并共享（未实现）。
