From c7cb31dcafe3046f925f17e3d05604b35938199e Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Fri, 08 May 2026 12:57:05 +0800
Subject: [PATCH] refactor(gateApi): 重构 WebSocket 客户端架构并优化网格交易逻辑

---
 src/main/java/com/xcong/excoin/modules/gateApi/gateApi-logic.md |  222 +++++++++++++++++++++++++++++++++++-------------------
 1 files changed, 143 insertions(+), 79 deletions(-)

diff --git a/src/main/java/com/xcong/excoin/modules/gateApi/gateApi-logic.md b/src/main/java/com/xcong/excoin/modules/gateApi/gateApi-logic.md
index 57e30a5..1290e32 100644
--- a/src/main/java/com/xcong/excoin/modules/gateApi/gateApi-logic.md
+++ b/src/main/java/com/xcong/excoin/modules/gateApi/gateApi-logic.md
@@ -4,32 +4,47 @@
 
 | 文件 | 类型 | 说明 |
 |------|------|------|
-| [GateWebSocketClientManager](#gatewebsocketclientmanager) | `@Component` | 启动入口,生命周期管理 |
-| [GateKlineWebSocketClient](#gateklinewebsocketclient) | WebSocket 客户端 | K 线 + 仓位数据监听 |
+| [GateWebSocketClientManager](#gatewebsocketclientmanager) | `@Component` | Spring 启动入口,生命周期管理 |
+| [GateKlineWebSocketClient](#gateklinewebsocketclient) | WebSocket 连接管理 | 连接/心跳/重连/消息路由 |
 | [GateGridTradeService](#gategridtradeservice) | 交易服务 | 网格策略 + REST 下单 |
 | [GateWebSocketClientMain](#gatewebsocketclientmain) | main 入口 | 独立测试启动 |
 | [Example.java](#examplejava) | 示例 | Gate SDK 用法参考 |
+
+### wsHandler 子包
+
+| 文件 | 类型 | 说明 |
+|------|------|------|
+| `wsHandler/GateChannelHandler.java` | **接口** | `subscribe/unsubscribe/handleMessage/getChannelName` |
+| `wsHandler/AbstractPrivateChannelHandler.java` | **抽象类** | 私有频道基类:HMAC-SHA512 签名 + 认证请求 |
+| `wsHandler/handler/CandlestickChannelHandler.java` | 公开频道处理器 | K 线订阅/解析 → `onKline()` |
+| `wsHandler/handler/PositionsChannelHandler.java` | 私有频道处理器 | 仓位推送解析 → `onPositionUpdate()` |
+| `wsHandler/handler/PositionClosesChannelHandler.java` | 私有频道处理器 | 平仓推送解析 → `onPositionClose()` |
 
 ---
 
 ## 架构总览
 
 ```
-┌────────────────────────────────────────────────────────────┐
-│                  GateWebSocketClientManager                │
-│                    (Spring @Component)                     │
-│                                                            │
-│  @PostConstruct init():                                    │
-│    ┌──────────────────────┐   ┌─────────────────────────┐  │
-│    │ GateGridTradeService │   │ GateKlineWebSocketClient │  │
-│    │  ▶ init()            │   │  ▶ init()                │  │
-│    │  ▶ startGrid()       │←──│  ▶ connect()             │  │
-│    └──────┬───────────────┘   └───────────┬─────────────┘  │
-│           │ REST API                      │ WebSocket       │
-│           ▼                               ▼                │
-│     Gate Testnet API           Gate Testnet WS             │
-│  (https://api-testnet...)   (wss://ws-testnet.gate.com)   │
-└────────────────────────────────────────────────────────────┘
+┌──────────────────────────────────────────────────────────────────┐
+│                    GateWebSocketClientManager                    │
+│                      (Spring @Component)                         │
+│                                                                  │
+│  @PostConstruct init():                                          │
+│    ┌──────────────────────┐   ┌───────────────────────────────┐  │
+│    │ GateGridTradeService │   │ GateKlineWebSocketClient       │  │
+│    │  ▶ init()            │   │  ▶ addChannelHandler(x3)       │  │
+│    │  ▶ startGrid()       │   │  ▶ init()                      │  │
+│    └──────┬───────────────┘   └──────────┬────────────────────┘  │
+│           │ REST API                     │ WebSocket (委托路由)   │
+│           ▼                              ▼                        │
+│     Gate Testnet API          Gate Testnet WS                     │
+│  (https://api-testnet...)    (wss://ws-testnet.gate.com)         │
+│                              ┌──────────────────────┐            │
+│                              │ CandlestickHandler    │  → onKline │
+│                              │ PositionsHandler      │  → onPos   │
+│                              │ PositionClosesHandler │  → onClose │
+│                              └──────────────────────┘            │
+└──────────────────────────────────────────────────────────────────┘
 ```
 
 ---
@@ -39,22 +54,27 @@
 ```
 WebSocket 推送
 │
-├─ futures.candlesticks (update)
-│   └─ GateKlineWebSocketClient.processPushDataV2()
+├─ futures.candlesticks (update) [公开]
+│   └─ CandlestickChannelHandler.handleMessage()
 │       ├─ 解析: o/h/l/c/v/a/t/w
 │       ├─ 打印 K 线日志
 │       └─ GateGridTradeService.onKline(closePx)
 │           ├─ 首次 → dualOpenPositions() → 开多 + 开空 + TP 单
 │           └─ 后续 → 仅缓存 lastKlinePrice
 │
-├─ futures.positions (update) [需 HMAC-SHA512 签名]
-│   └─ GateKlineWebSocketClient.processPositionData()
-│       ├─ 解析: contract/mode/size/entry_price/history_pnl/realised_pnl
+├─ futures.positions (update) [私有,HMAC-SHA512]
+│   └─ PositionsChannelHandler.handleMessage()
+│       ├─ 解析: contract/mode/size/entry_price
 │       └─ GateGridTradeService.onPositionUpdate(...)
 │           ├─ size=0 && longActive → reopenLongPosition()
 │           ├─ size=0 && shortActive → reopenShortPosition()
-│           ├─ size>0 → 确认仓位活跃
-│           └─ checkStopConditions(history_pnl)
+│           └─ size>0 → 确认仓位活跃
+│
+├─ futures.position_closes (update) [私有,HMAC-SHA512]
+│   └─ PositionClosesChannelHandler.handleMessage()
+│       ├─ 解析: contract/side/pnl
+│       └─ GateGridTradeService.onPositionClose(pnl)
+│           └─ cumulativePnl += pnl → checkStopConditions()
 │
 ├─ futures.pong
 │   └─ cancelPongTimeout()
@@ -73,12 +93,15 @@
 Spring 启动
   → GateWebSocketClientManager.init()
     → GateGridTradeService.init()
+      → REST: 查用户ID
+      → REST: 查账户 → 如需要切换持仓模式 (dual)
+      → REST: 清除旧止盈止损条件单
       → REST: 设杠杆 30x cross
-      → REST: 查账户余额
-      → REST: 切双向持仓 dual
-    → GateKlineWebSocketClient.init()
-      → WebSocket: connect()
-        → onOpen: subscribe candlesticks + positions + ping
+      → REST: 打印账户余额
+    → GateKlineWebSocketClient
+      → addChannelHandler: Candlestick + Positions + PositionCloses
+      → init() → connect()
+        → onOpen: 遍历 handlers.subscribe() + sendPing()
     → GateGridTradeService.startGrid()
       → strategyActive = true
 ```
@@ -86,37 +109,46 @@
 ### 阶段 2:首次开仓
 
 ```
-K 线推送 closePrice=80000
-  → onKline(80000)
+K 线推送 closePrice=4711
+  → onKline(4711)
     → dualOpened = true
     → dualOpenPositions()
-      → REST: 市价开多 10 张 → longEntryPrice=80000
-      → REST: 创建多头止盈单 TP=80000×1.0035=80280
-      → REST: 市价开空 10 张 → shortEntryPrice=80000
-      → REST: 创建空头止盈单 TP=80000×0.9965=79720
+      → REST: 市价开多 10 张 → longEntryPrice=4711
+      → REST: 创建多头止盈单 TP=4711×1.0035=4727 (close-long-position)
+      → REST: 市价开空 10 张 → shortEntryPrice=4711
+      → REST: 创建空头止盈单 TP=4711×0.9965=4694 (close-short-position)
+      → longActive=true, shortActive=true
 ```
 
 ### 阶段 3:止盈触发 → 补仓
 
 ```
-仓位推送: mode=dual_long, size=0  (多头被止盈平掉了)
+仓位推送: mode=dual_long, size=0  (多头被服务器止盈平掉了)
   → longActive=true && size=0
-    → longActive=false
     → reopenLongPosition()
-      → REST: 市价开多 10 张 (用最新 K 线价)
+      → REST: 市价补开多 10 张
       → REST: 创建新的多头止盈单
 
-仓位推送: history_pnl=0.12  (未达阈值,继续)
+仓位推送: mode=dual_short, size=0  (空头被服务器止盈平掉了)
+  → shortActive=true && size=0
+    → reopenShortPosition()
+      → REST: 市价补开空 10 张
+      → REST: 创建新的空头止盈单
 ```
 
-### 阶段 4:停止
+> 注意:只补被平掉的方向(另一方向不受影响),不双方向重新开。补仓时检查 `longActive` / `shortActive` 防重复开仓。
+
+### 阶段 4:停止条件
 
 ```
-仓位推送: history_pnl ≥ 0.5 (累计盈利达标)
-  → strategyActive = false  → 不再补仓
+平仓推送: pnl=+0.2
+  → cumulativePnl = 0.2, 未达阈值,继续
 
-仓位推送: history_pnl ≤ -7.5 (亏损超限)
-  → strategyActive = false  → 不再补仓
+平仓推送: pnl=+0.4
+  → cumulativePnl = 0.6 ≥ 0.5 → strategyActive=false
+
+平仓推送: pnl=-8
+  → cumulativePnl = -8 ≤ -7.5 → strategyActive=false
 ```
 
 ---
@@ -126,14 +158,14 @@
 **角色**: Spring Bean 入口,组装并管理所有 Gate 模块组件。
 
 **关键方法**:
-- `init()`: 创建 `GateGridTradeService` → 初始化 → 创建 `GateKlineWebSocketClient` → 启动策略
+- `init()`: 创建 `GateGridTradeService` → 初始化 → 创建 `GateKlineWebSocketClient` + 注册 3 个 Handler → 启动策略
 - `destroy()`: 停止策略 → 关闭 WebSocket
 
 **当前参数**(硬编码在 `init()` 中):
 
 | 参数 | 值 | 说明 |
 |------|-----|------|
-| 合约 | XAU_USDT | 黄金 |
+| 合约 | XAUT_USDT | 黄金(测试网) |
 | 杠杆 | 30x | 全仓模式 |
 | 持仓模式 | dual | 双向持仓 |
 | 网格间距 | 0.0035 | 0.35% |
@@ -145,29 +177,17 @@
 
 ## GateKlineWebSocketClient
 
-**角色**: WebSocket 客户端,订阅 Gate 的行情和仓位频道。
+**角色**: WebSocket 连接管理器。负责建立连接、心跳检测、指数退避重连。频道逻辑委托给 `GateChannelHandler` 实现类。
 
-**订阅频道**:
+**Handler 路由**:
 
-| 频道 | 类型 | 认证 | 用途 |
-|------|------|------|------|
-| `futures.candlesticks` | 公开 | 否 | 1m K 线数据 |
-| `futures.positions` | 私有 | HMAC-SHA512 | 用户仓位更新 |
-| `futures.ping` | 公开 | 否 | 应用层心跳 |
+| 频道 | Handler | 认证 | 回调 |
+|------|---------|------|------|
+| `futures.candlesticks` | `CandlestickChannelHandler` | 否 | `onKline(closePx)` |
+| `futures.positions` | `PositionsChannelHandler` | HMAC-SHA512 | `onPositionUpdate(mode, size, entryPrice)` |
+| `futures.position_closes` | `PositionClosesChannelHandler` | HMAC-SHA512 | `onPositionClose(side, pnl)` |
 
-**签名算法**(`futures.positions` 订阅时使用):
-
-```
-message = "channel=futures.positions&event=subscribe&time={秒级时间戳}"
-SIGN = Hex(HmacSHA512(apiSecret, message))
-```
-
-**连接管理**:
-- 心跳: 10 秒超时,每 25 秒检查,超时发 ping
-- 重连: 指数退避,最多 3 次,初始 5 秒
-- 关闭: 先取消订阅 → 等 500ms → 关闭连接 → 关闭线程池
-
-**消息路由** (`handleWebSocketMessage`):
+**消息路由** (`handleMessage`):
 
 | channel | event | 处理 |
 |---------|-------|------|
@@ -175,8 +195,38 @@
 | — | `subscribe` | 打日志 |
 | — | `unsubscribe` | 打日志 |
 | — | `error` | 打错误日志 |
-| `futures.candlesticks` | `update`/`all` | `processPushDataV2()` |
-| `futures.positions` | `update`/`all` | `processPositionData()` |
+| 任意 | `update`/`all` | 遍历 channelHandlers → `handler.handleMessage()` |
+
+**签名算法**:
+
+```
+message = "channel={channel}&event=subscribe&time={秒级时间戳}"
+SIGN = Hex(HmacSHA512(apiSecret.getBytes(UTF-8), message.getBytes(UTF-8)))
+```
+
+**连接管理**:
+- 心跳: 10 秒超时,每 25 秒检查,超时发 ping
+- 重连: 指数退避,最多 3 次,初始 5 秒
+- 关闭: 遍历 `handler.unsubscribe()` → 等 500ms → `closeBlocking()` → `sharedExecutor.shutdown()`
+
+---
+
+## GateChannelHandler 接口体系
+
+```
+GateChannelHandler (接口)
+  ├── CandlestickChannelHandler                (公开频道)
+  └── AbstractPrivateChannelHandler (抽象类)   (私有频道基类)
+        ├── PositionsChannelHandler
+        └── PositionClosesChannelHandler
+```
+
+| 方法 | 说明 |
+|------|------|
+| `getChannelName()` | 返回频道名(如 `"futures.candlesticks"`) |
+| `subscribe(ws)` | 发送订阅请求。私有频道自动附加 HMAC-SHA512 签名 |
+| `unsubscribe(ws)` | 发送取消订阅请求 |
+| `handleMessage(response)` | 解析推送数据并回调 `GateGridTradeService`,返回 `true` 表示已处理 |
 
 ---
 
@@ -199,17 +249,20 @@
                          longActive=true         shortActive=true
                               │                       │
                     仓位推送 size=0             仓位推送 size=0
+                   (止盈触发平仓)             (止盈触发平仓)
                               │                       │
                               ▼                       ▼
                        reopenLong()             reopenShort()
                               │                       │
                               └───────────┬───────────┘
                                           │
+                              平仓推送 pnl (futures.position_closes)
+                                          │
                                     checkStopConditions()
                                           │
                               ┌───────────┴───────────┐
                               ▼                       ▼
-                         history_pnl≥0.5       history_pnl≤-7.5
+                         cumulativePnl≥0.5   cumulativePnl≤-7.5
                          strategyActive=false  strategyActive=false
 ```
 
@@ -217,18 +270,29 @@
 
 | 方向 | 公式 | 触发条件 | order_type | auto_size |
 |------|------|----------|------------|-----------|
-| 多头 TP | entryPrice × 1.0035 | 最新价 ≥ 触发价 | `close-long-position` | `close_long` |
-| 空头 TP | entryPrice × 0.9965 | 最新价 ≤ 触发价 | `close-short-position` | `close_short` |
+| 多头 TP | entryPrice × (1 + gridRate) | 最新价 ≥ 触发价 | `close-long-position` | `close_long` |
+| 空头 TP | entryPrice × (1 - gridRate) | 最新价 ≤ 触发价 | `close-short-position` | `close_short` |
 
 **REST API 调用**:
 
-| 操作 | API |
-|------|-----|
-| 设杠杆 | `POST /futures/usdt/positions/{contract}/leverage` |
-| 查账户 | `GET /futures/usdt/accounts` |
-| 设持仓模式 | `POST /futures/usdt/set_position_mode` |
-| 市价下单 | `POST /futures/usdt/orders` (price=0, tif=IOC) |
-| 止盈条件单 | `POST /futures/usdt/price_orders` |
+| 操作 | API | 方法 |
+|------|-----|------|
+| 获取用户 ID | `GET /account/detail` | `AccountApi.getAccountDetail()` |
+| 切持仓模式 | `POST /futures/{settle}/set_position_mode` | `FuturesApi.setPositionMode()` |
+| 设杠杆 | `POST /futures/{settle}/positions/{contract}/leverage` | `FuturesApi.updateContractPositionLeverageCall()` |
+| 查账户 | `GET /futures/{settle}/accounts` | `FuturesApi.listFuturesAccounts()` |
+| 清除条件单 | `DELETE /futures/{settle}/price_orders` | `FuturesApi.cancelPriceTriggeredOrderList()` |
+| 市价下单 | `POST /futures/{settle}/orders` (price=0, tif=IOC) | `FuturesApi.createFuturesOrder()` |
+| 止盈条件单 | `POST /futures/{settle}/price_orders` | `FuturesApi.createPriceTriggeredOrder()` |
+
+**初始化顺序** (`init()`):
+```
+1. 获取用户 ID
+2. 查账户 → 如需要切持仓模式
+3. 清除旧的止盈止损条件单
+4. 设杠杆
+5. 打印账户余额
+```
 
 ---
 
@@ -240,4 +304,4 @@
 
 ## Example.java
 
-Gate SDK 使用示例,展示 `FuturesApi` 的基本用法:获取账户、设置仓位模式、设置杠杆。仅作参考,不参与实际策略运行。
+Gate SDK 使用示例,展示 `FuturesApi` 的基本用法。仅作参考,不参与实际策略运行。

--
Gitblit v1.9.1