From bb272e1e2c0d4637d6d9ebfefb635662b804b459 Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Wed, 13 May 2026 17:52:48 +0800
Subject: [PATCH] refactor(okxNewPrice): 账户配置

---
 src/main/java/com/xcong/excoin/modules/okxApi/okxApi-logic.md |  935 ++++++++++++++++------------------------------------------
 1 files changed, 262 insertions(+), 673 deletions(-)

diff --git a/src/main/java/com/xcong/excoin/modules/okxApi/okxApi-logic.md b/src/main/java/com/xcong/excoin/modules/okxApi/okxApi-logic.md
index 9e67176..2a0a49c 100644
--- a/src/main/java/com/xcong/excoin/modules/okxApi/okxApi-logic.md
+++ b/src/main/java/com/xcong/excoin/modules/okxApi/okxApi-logic.md
@@ -1,724 +1,313 @@
-# OKX API 网格交易策略 — 逻辑文档
+# OKX 永续合约双边网格策略 — 逻辑文档
+
+> 最后更新: 2026-05-13
 
 ---
 
-## 目录
+## 一、模块架构
 
-1. [整体架构](#1-整体架构)
-2. [配置层:OkxConfig](#2-配置层okxconfig)
-3. [基础设施层](#3-基础设施层)
-   - [3.1 OkxEnums — 常量定义](#31-okxenums--常量定义)
-   - [3.2 OkxWsUtil — WebSocket 工具类](#32-okxwsutil--websocket-工具类)
-   - [3.3 TradeRequestParam — 下单参数](#33-traderequestparam--下单参数)
-4. [WebSocket 通信层](#4-websocket-通信层)
-   - [4.1 OkxWebSocketClientManager — 入口管理器](#41-okxwebsocketclientmanager--入口管理器)
-   - [4.2 OkxKlineWebSocketClient — WS 连接客户端](#42-okxklinewebsocketclient--ws-连接客户端)
-5. [频道处理器层](#5-频道处理器层)
-   - [5.1 OkxChannelHandler — 处理器接口](#51-okxchannelhandler--处理器接口)
-   - [5.2 OkxCandlestickChannelHandler — K线频道](#52-okxcandlestickchannelhandler--k线频道)
-   - [5.3 OkxPositionsChannelHandler — 持仓频道](#53-okxpositionschannelhandler--持仓频道)
-   - [5.4 OkxAccountChannelHandler — 账户频道](#54-okxaccountchannelhandler--账户频道)
-   - [5.5 OkxOrderInfoChannelHandler — 订单成交频道](#55-okxorderinfochannelhandler--订单成交频道)
-6. [策略执行层](#6-策略执行层)
-   - [6.1 OkxTradeExecutor — 异步下单执行器](#61-okxtradeexecutor--异步下单执行器)
-   - [6.2 OkxGridTradeService — 网格策略核心](#62-okxgridtradeservice--网格策略核心)
-7. [独立启动类:OkxWebSocketClientMain](#7-独立启动类okxwebsocketclientmain)
-8. [完整调用链](#8-完整调用链)
-9. [与 Gate API 的差异对比](#9-与-gate-api-的差异对比)
+```
+OkxWebSocketClientMain                     ← 入口,持有主运行线程
+  └── OkxWebSocketClientManager            ← 管理 WS 连接(公共 + 私有)
+        ├── OkxKlineWebSocketClient (公共)  ← K线推送
+        └── OkxKlineWebSocketClient (私有)  ← 私有频道(login → 订阅)
+              ├── OkxCandlestickChannelHandler  → onKline()
+              ├── OkxOrderInfoChannelHandler    → onOrderFilled()
+              ├── OkxPositionsChannelHandler    → onPositionUpdate()
+              └── OkxAccountChannelHandler      → 仅日志,供后续扩展
+  └── OkxGridTradeService                  ← 核心策略引擎(状态机)
+        ├── OkxConfig                       ← 策略配置(Builder 模式)
+        └── OkxTradeExecutor               ← REST 下单执行器
+```
+
+### WS 连接层职责
+
+| 类 | 职责 |
+|----|------|
+| `OkxKlineWebSocketClient` | TCP 连接管理、心跳(10s ping)、指数退避重连(最多3次)、消息路由(login/subscribe/pong → handler.handleMessage) |
+| `OkxWebSocketClientManager` | 双重连接(公共/私有)的初始化、销毁、共享线程池 |
+| `OkxWsUtil` | SSL 配置、HMAC-SHA256 签名、订单 ID 生成、ISO8601 时间戳 |
 
 ---
 
-## 1. 整体架构
+## 二、核心配置 `OkxConfig`
 
-```
-┌──────────────────────────────────────────────────────────────┐
-│  OkxWebSocketClientManager (Spring @Component)               │
-│    · 读取配置 · 组装所有组件 · 启动 WS 连接 · 生命周期管理    │
-└──────────────────────┬───────────────────────────────────────┘
-                       │
-         ┌─────────────┼─────────────┐
-         ▼             ▼             ▼
-   OkxConfig      OkxGridTradeService   OkxKlineWebSocketClient
-   (Builder配置)    (策略核心)            (WS连接客户端)
-                       │                      │
-                       │              ┌────────┴────────┐
-                       │              │  4 个频道处理器   │
-                       │              ├─────────────────┤
-                       │              │ K线 | 持仓       │
-                       │              │ 账户 | 订单成交   │
-                       │              └────────┬────────┘
-                       │                       │
-                       ▼                       │
-              OkxTradeExecutor                 │
-              (异步下单线程池) ◄────────────────┘
-                       │
-                       ▼
-              通过 WS 发送下单 JSON
-```
-
-**核心数据流**:
-
-```
-K线推送 → OkxGridTradeService.onKline() → 匹配网格队列 → OkxTradeExecutor 异步下单
-持仓推送 → OkxGridTradeService.onPositionUpdate() → 识别基底成交 → 设置止盈单 → 队列就绪 → 激活策略
-订单推送 → OkxGridTradeService.onOrderFilled() → 累计盈亏跟踪 → 达标/超限自动停止
-```
-
-**设计原则**:
-- **包自包含**:`okxApi` 包不依赖任何其他业务包(`okxNewPrice`、`gateApi`、`newPrice`、`blackchain` 等)
-- **WS 回调不阻塞**:所有下单操作通过 `OkxTradeExecutor` 单线程池异步执行
-- **状态机驱动**:策略状态(`WAITING_KLINE → OPENING → ACTIVE → STOPPED`)严格控制执行流程
+| 参数 | 默认值 | 说明 |
+|------|--------|------|
+| `contract` | `BTC-USDT-SWAP` | 合约 ID |
+| `gridRate` | `0.003` (0.3%) | 网格步长比率 |
+| `overallTp` | `100 USDT` | 整体止盈线 |
+| `maxLoss` | `100 USDT` | 最大亏损线 |
+| `quantity` | `"1"` | 单次开仓张数(字符串,1 张 = 100 合约) |
+| `gridQueueSize` | `20` | 多/空队列各 20 个元素 |
+| `marginRatioLimit` | `0.05` (5%) | 保证金安全阀 |
+| `contractMultiplier` | `0.01` | 合约乘数(BTC 0.01,ETH 0.1) |
+| `unrealizedPnlPriceMode` | `LAST_PRICE` | 未实现盈亏计算基准价(LAST_PRICE / MARK_PRICE) |
+| `maxPosSize` | `10` 张 | **新** 单方向最大持仓张数 |
+| `step` | 运行时计算 | **新** 网格步长 = `shortBaseEntryPrice × gridRate`,队列生成时写入 |
 
 ---
 
-## 2. 配置层:OkxConfig
+## 三、策略状态机
 
 ```
-文件:OkxConfig.java
+INIT ──startGrid()──▶ WAITING_KLINE ──首根K线稳定──▶ OPENING ──双基底成交──▶ ACTIVE ──止盈/止损──▶ STOPPED
+                         ▲                                                          │
+                         │                       K线重连 / 重订阅                    │
+                         └──────────────────────────────────────────────────────────┘
 ```
 
-使用 **Builder 模式**构造配置对象,不可变设计,所有字段 `private final`。
+| 状态 | 说明 | 触发条件 |
+|------|------|----------|
+| `INIT` | 初始态 | `startGrid()` |
+| `WAITING_KLINE` | 等待 K 线推送稳定(标记价在基底持仓价 ± 步长范围内) | 基底开仓成功 |
+| `OPENING` | 等待双基底订单成交 | K线稳定后开仓 |
+| `ACTIVE` | 网格运行中 | 双基底成交,队列生成完成 |
+| `STOPPED` | 策略停止 | `cumulativePnl >= overallTp` 或 `<= -maxLoss` |
 
-### 配置字段
+---
 
-| 分组 | 字段 | 说明 |
-|------|------|------|
-| **API 密钥** | `apiKey` | OKX API Key |
-| | `secretKey` | OKX Secret Key |
-| | `passphrase` | OKX Passphrase |
-| **合约参数** | `contract` | 合约品种(如 `BTC-USDT-SWAP`) |
-| | `marginMode` | 保证金模式(`cross` 全仓 / `isolated` 逐仓) |
-| | `tickSz` | 价格精度 |
-| | `contractMultiplier` | 合约乘数(用于盈亏计算) |
-| | `leverage` | 杠杆倍数 |
-| **策略参数** | `quantity` | 每次开仓张数 |
-| | `gridRate` | 网格间距比例(如 0.01 = 1%) |
-| | `gridQueueSize` | 网格队列长度 |
-| | `marginRatioLimit` | 保证金占用比例上限 |
-| | `overallTp` | 全局止盈目标(累计盈亏 ≥ 此值停止) |
-| | `maxLoss` | 最大亏损限制(累计盈亏 ≤ 此值停止) |
-| **环境** | `isProduction` | 是否生产环境(决定 WS URL 域名) |
+## 四、完整策略时序图
 
-### Builder 方法链
+```mermaid
+sequenceDiagram
+    participant M as Main
+    participant S as OkxGridTradeService
+    participant C as OkxConfig
+    participant K as K线Handler
+    participant O as 订单Handler
+    participant P as 仓位Handler
+    participant E as OkxTradeExecutor
+    participant OKX as OKX交易所
 
-```java
-OkxConfig config = OkxConfig.builder()
-    .apiKey("xxx")
-    .secretKey("xxx")
-    .passphrase("xxx")
-    .contract("BTC-USDT-SWAP")
-    .marginMode("cross")
-    .leverage(1)
-    .quantity("1")
-    .gridRate(new BigDecimal("0.01"))
-    .gridQueueSize(10)
-    .overallTp(new BigDecimal("100"))
-    .isProduction(false)
-    .build();
+    Note over M,OKX: ═══ 阶段①: 初始化 ═══
+
+    M->>S: startGrid()
+    S->>S: state = INIT
+    S->>E: setPositionMode("long_short_mode")
+    E->>OKX: POST /set-position-mode
+    S->>E: setLeverage(instId, lever, "isolated")
+    E->>OKX: POST /set-leverage
+    S->>E: openShort(quantity, null, null)  基底空单
+    E->>OKX: 市价做空
+    S->>E: openLong(quantity, null, null)   基底多单
+    E->>OKX: 市价做多
+    S->>S: state = WAITING_KLINE
+    S->>S: 重置所有字段(step=null, baseOpened=false, ...)
+
+    Note over M,OKX: ═══ 阶段②: 等待K线稳定 ═══
+
+    K-->>S: onKline(price)
+    Note over S: WAITING_KLINE 状态 <br/> 检查标记价是否在基底价±步长内
+    S->>S: step = shortBaseEntryPrice × gridRate
+    S->>S: state = OPENING
+    S->>E: openLong(quantity, baseStepLongPrice, null)
+    E->>OKX: 限价做多
+    S->>E: openShort(quantity, baseStepShortPrice, null)
+    E->>OKX: 限价做空
+
+    Note over M,OKX: ═══ 阶段③: 订单成交 → 基底识别 + 队列生成 ═══
+
+    O-->>S: onOrderFilled(posSide=long, avgPx, fillSz, pnl)
+    Note over S: !baseLongOpened → 首次多基底
+    S->>S: longBaseEntryPrice = avgPx
+    S->>S: baseLongOpened = true
+    S->>S: tryGenerateQueues()
+
+    O-->>S: onOrderFilled(posSide=short, avgPx, fillSz, pnl)
+    Note over S: !baseShortOpened → 首次空基底
+    S->>S: shortBaseEntryPrice = avgPx
+    S->>S: baseShortOpened = true
+    S->>S: tryGenerateQueues()
+
+    Note over S: 双基底就绪 ✓
+    S->>C: setStep(shortBaseEntryPrice × gridRate)
+    S->>S: generateShortQueue() → [s0-s, s0-2s, ..., s0-Ns]
+    S->>S: generateLongQueue() → [l0+s, l0+2s, ..., l0+Ns]
+    S->>S: state = ACTIVE
+
+    Note over M,OKX: ═══ 阶段④: 网格运行 (ACTIVE) ═══
+
+    loop 每根K线
+        K-->>S: onKline(price)
+        Note over S: 止盈止损检查<br/>队列匹配
+        alt 空仓队列触发 (price <= queue[0])
+            S->>S: processShortGrid()
+            Note over S: 收集所有 price<=queue[i] 的元素<br/>check maxPosSize<br/>开空 + 填充队列 + 转移到多队列
+        else 多仓队列触发 (price >= queue[0])
+            S->>S: processLongGrid()
+            Note over S: 收集所有 price>=queue[i] 的元素<br/>check maxPosSize<br/>开多 + 填充队列 + 转移到空队列
+        end
+    end
+
+    Note over M,OKX: ═══ 阶段⑤: 加仓成交 → 挂止盈 ═══
+
+    O-->>S: onOrderFilled(posSide, avgPx, fillSz, pnl)
+    Note over S: baseOpened && fillSz > 0 → 加仓
+    S->>S: tpPrice = queue.get(0)
+    S->>E: placeTakeProfit(tpPrice, CLOSE_LONG, quantity)
+    E->>OKX: 挂止盈限价单
+
+    Note over M,OKX: ═══ 阶段⑥: 仓位更新 → 累计盈亏 → 停止判断 ═══
+
+    loop 每次仓位变化
+        P-->>S: onPositionUpdate(posSide, size, avgPx, realizedPnl)
+        S->>S: 更新 longRealizedPnl / shortRealizedPnl
+        S->>S: cumulativePnl = longRealizedPnl + shortRealizedPnl
+        alt cumulativePnl >= overallTp
+            S->>S: state = STOPPED
+            Note over S: 🏁 盈利目标达成
+        else cumulativePnl <= -maxLoss
+            S->>S: state = STOPPED
+            Note over S: 🏁 亏损上限触发
+        end
+    end
 ```
 
 ---
 
-## 3. 基础设施层
+## 五、详细逻辑说明
 
-### 3.1 OkxEnums — 常量定义
+### 5.1 K线处理 `onKline()` — 策略核心驱动
 
 ```
-文件:enums/OkxEnums.java
+WAITING_KLINE:
+  └── 标记价在 [shortBasePrice - step, shortBasePrice + step] 范围 → OPENING
+       └── 开基多限价单(baseStepLongPrice) + 基空限价单(baseStepShortPrice)
+
+ACTIVE:
+  ├── 止盈止损检查(累计已实现盈亏 vs overallTp / maxLoss)
+  ├── 保证金安全阀检查(isMarginSafe)
+  ├── processShortGrid(): 当前价 <= 空队列首 → 触发空仓网格
+  └── processLongGrid(): 当前价 >= 多队列首 → 触发多仓网格
 ```
 
-集中管理所有 OKX API 相关字符串常量,替代外部 `CoinEnums` 依赖。
+### 5.2 订单成交处理 `onOrderFilled()` — 基底识别 + 挂止盈
 
-| 常量 | 值 | 用途 |
-|------|-----|------|
-| `INSTTYPE_SPOT` | `SPOT` | 现货 |
-| `INSTTYPE_SWAP` | `SWAP` | 永续合约 |
-| `POSSIDE_LONG` | `long` | 多仓方向 |
-| `POSSIDE_SHORT` | `short` | 空仓方向 |
-| `SIDE_BUY` | `buy` | 买入 |
-| `SIDE_SELL` | `sell` | 卖出 |
-| `ORDTYPE_MARKET` | `market` | 市价单 |
-| `ORDTYPE_LIMIT` | `limit` | 限价单 |
-| `CHANNEL_POSITIONS` | `positions` | 持仓频道 |
-| `CHANNEL_CANDLE` | `candle` + 周期 | K线频道 |
-| `CHANNEL_ACCOUNT` | `account` | 账户频道 |
-| `CHANNEL_ORDERS` | `orders` | 订单频道 |
-| `CHANNEL_ORDERS_ALGO` | `orders-algo` | 策略委托频道 |
+**数据来源**:orders 频道 `state=filled`,取 `avgPx`(成交量加权均价)。
+
+```
+!baseLongOpened → 基底多成交 → longBaseEntryPrice = avgPx → tryGenerateQueues()
+!baseShortOpened → 基底空成交 → shortBaseEntryPrice = avgPx → tryGenerateQueues()
+baseOpened → 加仓成交 → queue[0] = 止盈价 → placeTakeProfit()
+```
+
+> `tryGenerateQueues()`:双基底都成交后,计算 step,生成多空队列,状态 → ACTIVE。
+
+### 5.3 队列生成逻辑
+
+```
+step = shortBaseEntryPrice × config.gridRate  (统一用空基价)
+
+空队列 (shortPriceQueue):  [shortBase - s, shortBase - 2s, ...]  → 降序(最近的在前)
+多队列 (longPriceQueue):   [longBase + s, longBase + 2s, ...]   → 升序(最近的在前)
+```
+
+**步长统一来源**:`config.step`,由 `tryGenerateQueues()` 计算并写入。
+
+### 5.4 网格触发 `processShortGrid()` / `processLongGrid()`
+
+```
+processShortGrid():
+  1. 收集所有 price <= queue[i] 的元素
+  2. 开空(shortPositionSize < maxPosSize 限制)
+  3. 填补自身队列(从队尾追加 step 递减)
+  4. 转移到多仓队列(在 longBaseEntryPrice - step 下方插入)
+  5. 额外开多条件: 价格在多/空持仓价之间 && 多>空 && 远离多均价 && longPositionSize < maxPosSize
+
+processLongGrid():
+  1. 收集所有 price >= queue[i] 的元素
+  2. 开多(longPositionSize < maxPosSize 限制)
+  3. 填补自身队列(从队尾追加 step 递增)
+  4. 转移到空仓队列(在 shortBaseEntryPrice + step 上方插入)
+  5. 额外开空条件: 价格在多/空持仓价之间 && 多>空 && 远离空均价 && shortPositionSize < maxPosSize
+```
+
+### 5.5 止盈止损 `onPositionUpdate()` — 累计已实现盈亏
+
+**数据来源**:positions 频道 `realizedPnl` 字段(含手续费、资金费的综合已实现盈亏)。
+
+```
+cumulativePnl = longRealizedPnl + shortRealizedPnl
+≥ overallTp → STOPPED (盈利)
+≤ -maxLoss → STOPPED (亏损)
+```
+
+> 仓位 size=0 时该方向的 `realizedPnl` 置为 0,避免重复累加。
 
 ---
 
-### 3.2 OkxWsUtil — WebSocket 工具类
+## 六、策略评估
 
-```
-文件:OkxWsUtil.java
-```
+### ✅ 好的方面
 
-替代外部 `SSLConfig`、`SignUtils`、`WsParamBuild`、`DateUtil` 等依赖,提供以下静态方法:
+| # | 方面 | 说明 |
+|---|------|------|
+| 1 | **双边网格 + 队列转换** | 多空队列互为补仓,价格在区间内震荡时持续收割网格利润。止盈触发后队列元素转移到对方队列,形成"无限网格"循环 |
+| 2 | **WS 推送驱动,非轮询** | orders + positions + kline 三频道覆盖,低延迟,不浪费 API 额度 |
+| 3 | **用 `avgPx` 而非 `fillPx`** | 均价比单笔成交价更能反映真实持仓成本,避免分笔成交造成的偏差 |
+| 4 | **用 `realizedPnl` 而非手动累加** | positions 频道的 realizedPnl 包含手续费 + 资金费,比 orders 频道的逐笔 pnl 累加更完整 |
+| 5 | **步长统一存储** | `config.step` 全局唯一来源,避免多空各自计算不一致 |
+| 6 | **maxPosSize 持仓上限** | 防止单方向无限加仓,控制风险敞口 |
+| 7 | **保证金安全阀** | `isMarginSafe()` 检查,防止爆仓 |
+| 8 | **状态机清晰** | INIT → WAITING_KLINE → OPENING → ACTIVE → STOPPED,每个状态职责明确 |
+| 9 | **连接层健壮** | 心跳保活、指数退避重连、订阅恢复 |
 
-| 方法 | 用途 |
-|------|------|
-| `configureSSL(wsClient)` | 为 `WebSocketClient` 配置 SSL(跳过证书验证,仅测试环境) |
-| `generateSignature(timestamp, method, requestPath, body, secretKey)` | OKX 签名算法:HMAC-SHA256 + Base64 |
-| `getOrderNum(side)` | 生成唯一订单 ID(时间戳 + 随机数 + side) |
-| `timestampToDateTime(timestamp)` | 毫秒时间戳 → `yyyy/MM/dd HH:mm:ss` 格式 |
-| `timestampToDateToString(timestamp)` | 毫秒时间戳 → `yyyy/MM/dd` 格式 |
-| `buildJsonObject(connId, channel, args)` | 构建 WS 请求 JSON 对象 |
-| `buildLoginParam(okxConfig)` | 构建登录认证参数(sign + timestamp) |
+### ⚠️ 不好的方面 / 潜在风险
 
-**签名算法**:
-```
-sign = Base64(HMAC-SHA256(timestamp + "GET" + requestPath + body, secretKey))
-```
+| # | 问题 | 风险等级 | 说明 |
+|---|------|----------|------|
+| 1 | **无订单状态追踪** | 🔴 高 | `onOrderFilled` 触发了止盈挂单,但没有记录止盈单的 `ordId`。如果止盈单一直未成交但策略已标记 STOPPED,可能出现"策略已停止但仍有挂单"的幽灵状态 |
+| 2 | **止盈挂单无超时/撤销机制** | 🔴 高 | `placeTakeProfit` 挂出的限价单没有定时检查和撤销逻辑。市场反向走时止盈单长期挂单,占用保证金且可能过期(OKX 限价单默认 30 天) |
+| 3 | **基底开仓用市价单** | 🟡 中 | `startGrid()` 中 `openShort/openLong` 传 null 即市价,滑点风险 |
+| 4 | **队列队列无限增长风险** | 🟡 中 | `replenishOwnQueue` 从队尾追加新元素,没有队列长度上限检查。如果价格长时间单边运动,队列可能增长到远超 `gridQueueSize` 的规模 |
+| 5 | **基底价更新与队列不同步** | 🟡 中 | `onPositionUpdate` 更新 `longEntryPrice/shortEntryPrice`(加权均价),但队列仍用初始 `longBaseEntryPrice/shortBaseEntryPrice` 计算。加仓后均价变化,队列位置可能不再合理 |
+| 6 | **`realizedPnl` 归零逻辑有漏洞** | 🟡 中 | 仓位 size=0 时 `realizedPnl` 置为 0,但如果仓位先平仓再重新开仓,累计盈亏会丢失。正确的做法是记录到另一个持久累加字段 |
+| 7 | **步长只依赖空基价** | 🟡 中 | 多空各自用 `空基价 × gridRate`,如果多空基底价相差较大,多的队列步长偏大/偏小 |
+| 8 | **无断路器(熔断)** | 🟡 中 | 极端行情下没有暂停交易的机制,可能连续触发网格大量开仓 |
+| 9 | **多线程安全依赖 volatile** | 🟢 低 | 状态字段用 `volatile`,但对于复合操作(判断-修改)没有锁保护。实际风险较低,因为 WS 回调是单线程 |
+| 10 | **无订单幂等保护** | 🟢 低 | 如果 OKX 重复推送同一订单(小概率),会重复挂止盈单 |
+| 11 | **K线处理无防重** | 🟢 低 | 同一根 K 线可能被 onKline 和 processGrid 多次触发(WS 推送更新),不过 markprice 变更才会进入处理,影响有限 |
+| 12 | **账户频道未使用** | 🟢 低 | `OkxAccountChannelHandler` 只输出日志,`isMarginSafe()` 用的是交易量/可用余额估算,不如直接用 account 频道的 `availBal` |
 
 ---
 
-### 3.3 TradeRequestParam — 下单参数
+## 七、优化方向(按优先级排序)
 
-```
-文件:param/TradeRequestParam.java
-```
+### 🔴 P0 — 紧急
 
-纯 POJO,替代外部 `TradeRequestParam` 依赖。
+1. **止盈挂单生命周期管理**
+   - 挂单后记录 `tpOrdId`,用 orders 频道监控成交
+   - 止盈单未成交超过 N 根 K 线 → 撤销重挂
+   - 策略 STOPPED 时撤销所有未成交挂单
 
-| 字段 | 说明 |
-|------|------|
-| `accountName` | 账户标识 |
-| `instId` | 合约 ID |
-| `tdMode` | 保证金模式(cross/isolated) |
-| `posSide` | 持仓方向(long/short) |
-| `ordType` | 订单类型(market/limit) |
-| `side` | 买卖方向(buy/sell) |
-| `clOrdId` | 客户端订单 ID(唯一) |
-| `sz` | 下单数量 |
-| `markPx` | 标记价格(限价单用) |
-| `tradeType` | 交易类型(1=开仓,3=平仓) |
+2. **队列长度上限**
+   - `replenishOwnQueue` 中限制 `queue.size() <= gridQueueSize * 2`
+   - 超过上限时清理队尾(远离当前价的元素)
 
----
+### 🟡 P1 — 重要
 
-## 4. WebSocket 通信层
+3. **`realizedPnl` 累加到独立字段**
+   - 当前用 volatile `longRealizedPnl/shortRealizedPnl` 直接赋值会被归零覆盖
+   - 新增 `totalRealizedPnl` 累加字段,`onPositionUpdate` 中 delta 累加而非覆盖
 
-### 4.1 OkxWebSocketClientManager — 入口管理器
+4. **基底价与队列同步更新**
+   - 当 `longEntryPrice` 偏离 `longBaseEntryPrice` 超过 N 个 step 时,重新生成队列
 
-```
-文件:OkxWebSocketClientManager.java
-```
+5. **爆仓熔断**
+   - 每分钟开仓次数超过阈值 → 暂停策略 + 告警
+   - 未实现亏损超过保证金 80% → 暂停
 
-**Spring `@Component`**,管理完整的 WS 生命周期。
+6. **账户频道接入保证金计算**
+   - `isMarginSafe()` 改用 account 频道的 `availBal`,更精确
 
-#### 职责
+### 🟢 P2 — 优化
 
-1. **组件组装**:创建 `OkxConfig` → `OkxGridTradeService` → `OkxTradeExecutor`(注入 WS Client)→ 4 个频道处理器 → `OkxKlineWebSocketClient`
-2. **生命周期管理**:
-   - `@PostConstruct init()`:初始化和连接(生产环境)或注册 MBean(测试环境)
-   - `@PreDestroy close()`:优雅关闭(停止策略 → 取消条件单 → 关闭 WS)
+7. **订单幂等**
+   - 用 `Set<clOrdId>` 缓存已处理订单 ID,防止重复挂单
 
-#### 初始化流程
+8. **步长优化**
+   - 考虑用 `(longBase + shortBase) / 2 × gridRate` 作为统一基准,而非只用空基价
 
-```
-init()
-  ├── configMap — 从本地缓存读取账户配置
-  ├── OkxGridTradeService.startGrid()
-  ├── OkxTradeExecutor.setWebSocketClient(wsClient)
-  ├── 创建 4 个频道处理器
-  ├── OkxKlineWebSocketClient.connect()
-  │     ├── 连接 OSKL 公开 WS(K线频道不需要登录)
-  │     ├── 登录私有 WS → 订阅 持仓/账户/订单/策略委托频道
-  │     └── 启动心跳定时器(30s ping/pong)
-  └── isProduction ? 直接启动 : MBean 注册(JMX 手动控制)
-```
-
----
-
-### 4.2 OkxKlineWebSocketClient — WS 连接客户端
-
-```
-文件:OkxKlineWebSocketClient.java
-```
-
-封装 `java-websocket` 客户端,管理物理连接。
-
-#### 连接架构
-
-- **公开频道 WS**(`wss://ws.okx.com:8443/ws/v5/public` 或模拟盘域名):K线推送不需要登录
-- **私有频道 WS**(`wss://ws.okx.com:8443/ws/v5/private` 或模拟盘域名):需要登录认证,订阅持仓/账户/订单/策略委托频道
-
-#### 连接流程
-
-```
-connect()
-  ├── 1. 创建公开 WS Client → connect()
-  │      └── onOpen → subscribePublicChannels() → 订阅 K线频道
-  ├── 2. 创建私有 WS Client → connect()
-  │      └── onOpen → login() → onLoginSuccess → subscribePrivateChannels()
-  │             ├── 订阅 positions 频道
-  │             ├── 订阅 account 频道
-  │             ├── 订阅 orders 频道
-  │             └── 订阅 orders-algo 频道
-  └── 3. 启动心跳定时器(30s 间隔 ping/pong,60s 超时检测)
-```
-
-#### 断线重连机制
-
-- 最多重连 `MAX_RECONNECT_ATTEMPTS` 次(默认 30)
-- 重连延迟 `RECONNECT_DELAY_MS`(默认 5000ms)
-- 重连成功后重新执行登录 + 订阅流程
-- 兜底机制:重连失败后尝试通过 MBean 重启整个 WS 客户端
-
-#### 消息路由
-
-`onMessage` → 遍历所有 `OkxChannelHandler` → 调用 `handleMessage(response)` 分发到具体处理器
-
----
-
-## 5. 频道处理器层
-
-### 5.1 OkxChannelHandler — 处理器接口
-
-```
-文件:wsHandler/OkxChannelHandler.java
-```
-
-统一接口,所有频道处理器实现此接口:
-
-```java
-public interface OkxChannelHandler {
-    String getChannelName();                      // 频道名称
-    void subscribe(WebSocketClient ws);           // 订阅
-    void unsubscribe(WebSocketClient ws);         // 取消订阅
-    boolean handleMessage(JSONObject response);   // 处理推送消息
-}
-```
-
-### 5.2 OkxCandlestickChannelHandler — K线频道
-
-```
-文件:wsHandler/handler/OkxCandlestickChannelHandler.java
-```
-
-#### 订阅参数
-
-| 参数 | 值 |
-|------|-----|
-| `channel` | `candle{period}`(如 `candle1H`) |
-| `instId` | 合约 ID(如 `BTC-USDT-SWAP`) |
-
-#### 数据处理
-
-- 解析 `data[0]` → `[ts, o, h, l, c, vol, volCcy, ...]`
-- 提取**收盘价** `c` → 调用 `gridTradeService.onKline(closePrice)`
-- K线为 `candle1H` 时打印整点日志
-
-#### 调用链
-
-```
-onKline(closePrice)
-  ├── WAITING_KLINE → 进入 OPENING 状态,开基底多+空
-  ├── ACTIVE → processShortGrid(closePrice) + processLongGrid(closePrice)
-  └── STOPPED → 仅更新未实现盈亏
-```
-
-### 5.3 OkxPositionsChannelHandler — 持仓频道
-
-```
-文件:wsHandler/handler/OkxPositionsChannelHandler.java
-```
-
-#### 订阅参数
-
-| 参数 | 值 |
-|------|-----|
-| `channel` | `positions` |
-| `instType` | `SWAP` |
-| `instId` | 合约 ID |
-
-#### 数据处理
-
-解析 `data[]` 数组 → 提取 `posSide`、`pos`(数量)、`avgPx`(均价)→ 调用 `gridTradeService.onPositionUpdate(posSide, size, avgPx)`
-
-#### 在策略中的作用
-
-```
-onPositionUpdate() → 区分 3 种场景:
-  1. 仓位从无到有(基底开仓成交)→ 标记 baseOpened → 双基底都成后生成网格队列
-  2. 仓位量增加(网格触发开仓成交)→ 取队列首元素做止盈价 → 设止盈条件单
-  3. 仓位归零(止盈平仓完成)→ 标记 active=false
-```
-
-### 5.4 OkxAccountChannelHandler — 账户频道
-
-```
-文件:wsHandler/handler/OkxAccountChannelHandler.java
-```
-
-#### 订阅参数
-
-| 参数 | 值 |
-|------|-----|
-| `channel` | `account` |
-
-#### 数据处理
-
-解析 `data[]` → 提取 `availBal`(可用余额)、`cashBal`(现金余额)、`eq`(权益)、`upl`(未实现盈亏)、`imr`(保证金占用)
-
-**当前版本**:仅做日志输出,不做业务判断。后续可扩展保证金安全阀功能。
-
-### 5.5 OkxOrderInfoChannelHandler — 订单成交频道
-
-```
-文件:wsHandler/handler/OkxOrderInfoChannelHandler.java
-```
-
-#### 订阅参数
-
-| 参数 | 值 |
-|------|-----|
-| `channel` | `orders` |
-| `instType` | `SWAP` |
-| `instId` | 合约 ID |
-
-#### 数据处理
-
-- 过滤 `state=filled` 且 `accFillSz>0` 的订单
-- 提取 `posSide`、`accFillSz`(成交数量)、`fillPnl`(已实现盈亏)
-- 调用 `gridTradeService.onOrderFilled(posSide, accFillSz, fillPnl)`
-
-#### 在策略中的作用
-
-```
-onOrderFilled()
-  ├── 累计盈亏 += fillPnl
-  ├── 累计盈亏 ≥ overallTp → 策略停止(止盈达标)
-  └── 累计盈亏 ≤ -maxLoss → 策略停止(亏损超限)
-```
-
----
-
-## 6. 策略执行层
-
-### 6.1 OkxTradeExecutor — 异步下单执行器
-
-```
-文件:OkxTradeExecutor.java
-```
-
-#### 设计目的
-
-WS 消息在回调线程处理,下单操作提交到**独立线程池异步执行**,避免阻塞 WS 回调线程。
-
-#### 线程模型
-
-| 参数 | 值 |
-|------|-----|
-| 核心线程 | 1 |
-| 最大线程 | 1 |
-| 空闲超时 | 60s(`allowCoreThreadTimeOut`) |
-| 队列类型 | `LinkedBlockingQueue` |
-| 队列容量 | 64 |
-| 拒绝策略 | `CallerRunsPolicy`(队列满时由提交线程直接同步执行,形成自然背压) |
-| 守护线程 | 是 |
-
-**单线程的作用**:保证下单顺序(开多 → 开空 → 止盈单),避免并发竞争。
-
-#### 公开方法
-
-| 方法 | 说明 |
-|------|------|
-| `openLong(quantity, onSuccess, onFailure)` | 异步市价开多 |
-| `openShort(quantity, onSuccess, onFailure)` | 异步市价开空 |
-| `placeTakeProfit(triggerPrice, orderType, size)` | 异步创建止盈条件单(通过 `batch-orders` 发送 algo 委托) |
-| `cancelAllPriceTriggeredOrders()` | 撤销所有条件单(`cancel-algos`) |
-| `setWebSocketClient(wsClient)` | 注入 WS 客户端引用 |
-| `shutdown()` | 优雅关闭(等待 10s,超时强制中断) |
-
-#### 止盈兜底机制
-
-```
-placeTakeProfit()
-  ├── 成功 → 发送 batch-orders(algo 条件单)
-  └── 失败 → marketClose() 立即市价平仓兜底
-```
-
-#### 下单方式
-
-所有下单通过 **WebSocket** 发送 JSON(非 REST API),`sendOrder` 构建如下消息:
-
-```json
-{
-  "id": "order_1712345678901_a1b2c3",
-  "op": "order",
-  "args": [{
-    "instId": "BTC-USDT-SWAP",
-    "tdMode": "cross",
-    "clOrdId": "buy_1712345678901_d4e5f6",
-    "side": "buy",
-    "posSide": "long",
-    "ordType": "market",
-    "sz": "1"
-  }]
-}
-```
-
-#### 调用链
-
-```
-OkxGridTradeService.onKline
-  └── executor.openLong() / openShort()     ← 基底双开 + 网格触发
-
-OkxGridTradeService.onPositionUpdate
-  └── executor.placeTakeProfit()             ← 仓位成交后设止盈
-
-OkxGridTradeService.stopGrid
-  └── executor.cancelAllPriceTriggeredOrders()  + shutdown()
-```
-
----
-
-### 6.2 OkxGridTradeService — 网格策略核心
-
-```
-文件:OkxGridTradeService.java
-```
-
-这是整个包的核心,实现了**多空双开网格交易策略**的所有状态机和网格队列逻辑。
-
-#### 策略状态机
-
-```
-WAITING_KLINE ──(首根K线到达)──▶ OPENING ──(双基底成交)──▶ ACTIVE ──(止盈/止损达标)──▶ STOPPED
-                        ▲                                                         │
-                        └──────────────────(startGrid() 重新启动)──────────────────┘
-```
-
-| 状态 | 含义 |
-|------|------|
-| `WAITING_KLINE` | 等待首根 K 线到达 |
-| `OPENING` | 已收到 K 线,正在开基底仓位(多+空各一单) |
-| `ACTIVE` | 双基底已成交,网格队列已生成,正常交易中 |
-| `STOPPED` | 已停止(止盈达标 / 亏损超限 / 手动停止) |
-
-#### 数据结构
-
-| 字段 | 类型 | 说明 |
-|------|------|------|
-| `shortPriceQueue` | `List<BigDecimal>` | 空仓价格队列(**降序**) |
-| `longPriceQueue` | `List<BigDecimal>` | 多仓价格队列(**升序**) |
-| `shortBaseEntryPrice` | `BigDecimal` | 空仓基底成交价 |
-| `longBaseEntryPrice` | `BigDecimal` | 多仓基底成交价 |
-| `baseLongOpened` | `boolean` | 多仓基底是否已开 |
-| `baseShortOpened` | `boolean` | 空仓基底是否已开 |
-| `cumulativePnl` | `BigDecimal` | 累计已实现盈亏 |
-
-#### 策略完整生命周期
-
-```
-1. startGrid()
-   └── 重置所有状态 → WAITING_KLINE
-
-2. onKline(closePrice) → WAITING_KLINE
-   └── 转为 OPENING → 发送基底多单 + 基底空单
-
-3. onPositionUpdate(posSide, size, entryPrice)
-   ├── 仓位从无到有
-   │   ├── 标记 baseLongOpened / baseShortOpened
-   │   ├── 记录 entryPrice
-   │   ├── 双基底都成交 → tryGenerateQueues() → ACTIVE
-   │   └── 生成网格队列:
-   │       · 空仓队列:entryPrice × (1 - gridRate×1), (1 - gridRate×2), ... 降序排列
-   │       · 多仓队列:entryPrice × (1 + gridRate×1), (1 + gridRate×2), ... 升序排列
-   ├── 仓位量增加(网格触发开仓成交)
-   │   └── 检查队列非空 → 取队列首元素做止盈价 → executor.placeTakeProfit()
-   └── 仓位归零
-       └── 标记 active=false
-
-4. onKline(closePrice) → ACTIVE
-   ├── processShortGrid(closePrice)  ← 空仓网格处理
-   │   ├── 匹配队列中 > closePrice 的元素
-   │   ├── 移除已匹配 → 尾部补充新元素(递减)
-   │   ├── 多仓队列转移(以对方队列首元素为种子生成递减元素)
-   │   ├── 贴近持仓均价过滤(skip)
-   │   ├── 保证金安全检查
-   │   ├── 开空一次
-   │   └── 额外反向开多(价格夹在多/空均价之间且多>空倒挂时)
-   └── processLongGrid(closePrice)   ← 多仓网格处理
-       └── (对称逻辑,方向反转)
-
-5. onOrderFilled(posSide, fillSz, pnl)
-   ├── cumulativePnl += pnl
-   ├── cumulativePnl ≥ overallTp → STOPPED
-   └── cumulativePnl ≤ -maxLoss → STOPPED
-
-6. stopGrid()
-   ├── 状态 → STOPPED
-   ├── cancelAllPriceTriggeredOrders()
-   └── executor.shutdown()
-```
-
-#### 空仓网格处理 `processShortGrid(currentPrice)` 详解
-
-```
-1. 匹配队列元素(空仓队列降序遍历,收集 > currentPrice 的元素)
-   └── 为空 → 直接返回
-
-2. 空仓队列更新
-   ├── 移除 matched 元素
-   └── 尾部补充新元素(尾价 × (1 - gridRate) 循环递减)→ 降序排序
-
-3. 多仓队列转移
-   ├── 以多仓队列首元素(最小价)为种子
-   ├── 生成 matched.size() 个递减元素加入多仓队列
-   ├── 贴近持仓均价过滤:元素与多仓均价差距 < gridRate → skip
-   └── 升序排序,超长截断
-
-4. 保证金安全检查
-   └── 超限 → warn 跳过开仓
-
-5. 开空一次 → executor.openShort()
-
-6. 额外反向开多条件(同时满足):
-   ├── longEntryPrice > shortEntryPrice(多>空倒挂)
-   ├── currentPrice > shortEntryPrice(当前价在空仓均价上方)
-   └── currentPrice < longEntryPrice × (1 - gridRate)(远离多仓均价)
-   └── 满足 → executor.openLong() 额外开多一次
-```
-
-#### 多仓网格处理 `processLongGrid(currentPrice)` 详解
-
-对称逻辑,方向反转。
-
----
-
-## 7. 独立启动类:OkxWebSocketClientMain
-
-```
-文件:OkxWebSocketClientMain.java
-```
-
-**纯 main 方法启动**,不依赖 Spring 容器。
-
-### 用途
-
-用于**本地测试和调试**,无需启动整个 Spring 应用。
-
-### 启动流程
-
-```java
-public static void main(String[] args) {
-    OkxConfig config = OkxConfig.builder()
-        .apiKey("xxx")
-        .secretKey("xxx")
-        .passphrase("xxx")
-        .contract("BTC-USDT-SWAP")
-        .marginMode("cross")
-        .leverage(1)
-        .quantity("1")
-        .gridRate(new BigDecimal("0.01"))
-        .gridQueueSize(10)
-        .overallTp(new BigDecimal("100"))
-        .isProduction(false)
-        .build();
-
-    OkxGridTradeService service = new OkxGridTradeService(config, "test-account");
-    service.startGrid();
-
-    OkxKlineWebSocketClient wsClient = new OkxKlineWebSocketClient(config, service, ...);
-    wsClient.connect();
-
-    // 注册 JVM 关闭钩子
-    Runtime.getRuntime().addShutdownHook(new Thread(() -> {
-        service.stopGrid();
-        wsClient.close();
-    }));
-}
-```
-
----
-
-## 8. 完整调用链
-
-```
-┌─────────────────────────────────────────────────────────────┐
-│ 1. 启动阶段                                                   │
-├─────────────────────────────────────────────────────────────┤
-│ OkxWebSocketClientManager.init()                             │
-│   ├── new OkxConfig.Builder()...build()                      │
-│   ├── new OkxGridTradeService(config, accountName)           │
-│   │     └── new OkxTradeExecutor(contract, marginMode, name) │
-│   ├── gridTradeService.startGrid() → WAITING_KLINE           │
-│   ├── new OkxCandlestickChannelHandler(instId, candlePeriod, │
-│   │        gridTradeService, config)                          │
-│   ├── new OkxPositionsChannelHandler(instId, gridTradeService)│
-│   ├── new OkxAccountChannelHandler()                         │
-│   ├── new OkxOrderInfoChannelHandler(instId, gridTradeService,│
-│   │        config)                                            │
-│   ├── new OkxKlineWebSocketClient(config, handlers, ...)     │
-│   ├── wsClient.connect()                                     │
-│   │     ├── 连接公开 WS → 订阅 K线频道                        │
-│   │     ├── 连接私有 WS → 登录 → 订阅 持仓/账户/订单/策略委托  │
-│   │     └── 启动心跳定时器                                    │
-│   └── executor.setWebSocketClient(privateWsClient)           │
-└─────────────────────────────────────────────────────────────┘
-
-┌─────────────────────────────────────────────────────────────┐
-│ 2. 运行时数据流                                               │
-├─────────────────────────────────────────────────────────────┤
-│ [K线推送]                                                    │
-│ OkxCandlestickChannelHandler.handleMessage()                 │
-│   └── gridTradeService.onKline(closePrice)                   │
-│         ├── WAITING_KLINE → OPENING                          │
-│         │     ├── executor.openLong(quantity, onSuccess,     │
-│         │     │        onFailure)                             │
-│         │     └── executor.openShort(quantity, onSuccess,    │
-│         │              onFailure)                             │
-│         └── ACTIVE                                           │
-│               ├── processShortGrid(closePrice)               │
-│               │     ├── 匹配队列 → 更新队列 → 转移对方队列    │
-│               │     └── executor.openShort()                  │
-│               └── processLongGrid(closePrice)                │
-│                     └──(对称逻辑)                            │
-│                                                              │
-│ [持仓推送]                                                    │
-│ OkxPositionsChannelHandler.handleMessage()                   │
-│   └── gridTradeService.onPositionUpdate(posSide, size, avgPx)│
-│         ├── 基底成交 → 标记 baseOpened → tryGenerateQueues() │
-│         └── 增量成交 → executor.placeTakeProfit(tp, type, sz)│
-│                                                              │
-│ [订单成交推送]                                                │
-│ OkxOrderInfoChannelHandler.handleMessage()                   │
-│   └── gridTradeService.onOrderFilled(posSide, fillSz, pnl)   │
-│         └── cumulativePnl 累加 → 达标则停止                   │
-└─────────────────────────────────────────────────────────────┘
-
-┌─────────────────────────────────────────────────────────────┐
-│ 3. 停止阶段                                                   │
-├─────────────────────────────────────────────────────────────┤
-│ OkxWebSocketClientManager.close()                            │
-│   ├── gridTradeService.stopGrid()                            │
-│   │     ├── state = STOPPED                                  │
-│   │     ├── executor.cancelAllPriceTriggeredOrders()         │
-│   │     │     └── wsClient.send(cancel-algos)               │
-│   │     └── executor.shutdown()                              │
-│   └── wsClient.close()                                       │
-│         └── 关闭公开WS + 私有WS                               │
-└─────────────────────────────────────────────────────────────┘
-```
-
----
-
-## 9. 与 Gate API 的差异对比
-
-| 方面 | Gate API (gateApi) | OKX API (okxApi) |
-|------|-------------------|-----------------|
-| **下单方式** | REST API (`FuturesApi.createFuturesOrder`) | WebSocket JSON 消息 (`op: "order"`) |
-| **止盈单** | REST API (`createPriceTriggeredOrder`),`plan-close-*-position` | WS 消息 (`op: "batch-orders"`),`ordType: limit` + `px` 触发价 |
-| **仓位方向** | 正数=开多、负数=开空(size 带符号) | `posSide: long/short` 显式区分,`sz` 始终正数 |
-| **保证金模式** | 无(Gate API 隐含) | `tdMode: cross/isolated` 显式指定 |
-| **客户端订单 ID** | 自动生成(Gate API 隐式处理) | `clOrdId` 显式生成和传入 |
-| **取消条件单** | REST API (`cancelPriceTriggeredOrderList`) | WS 消息 (`op: "cancel-algos"`) |
-| **止损失败兜底** | REST `createFuturesOrder` IOC 市价平仓 | WS 消息 `marketClose()`(`tradeType: "3"`) |
-| **成交识别** | 通过 WS `FuturesOrderBookTicker` 或 REST 查询 | WS `orders` 频道推送 `state=filled` |
-| **API 认证** | HMAC-SHA256 请求头签名(Gate SDK 封装) | HMAC-SHA256 + Base64 WS 登录消息 |
-| **WS 连接** | 单一连接,频道订阅混合 | 双连接:公开 WS(K线)+ 私有 WS(持仓/账户/订单) |
-| **`placeTakeProfit` 签名** | `(triggerPrice, rule, orderType, size)` 多一个 `rule` 参数 | `(triggerPrice, orderType, size)` (OKX 无 rule 概念) |
-| **止盈单下单** | 单条 `FuturesPriceTriggeredOrder` | `batch-orders` 包装(List 格式,但实际传 1 条) |
-| **心跳机制** | 应用层 ping/pong JSON 消息 | `java-websocket` 自带 ping/pong + 60s 超时重连 |
-| **包依赖** | 依赖 Gate SDK (`io.gate.gateapi`) | **完全自包含**,仅依赖 `java-websocket` + `fastjson` + `lombok` |
+9. **基底开仓改用限价单**
+   - `startGrid()` 中基底开仓用限价单(取盘口卖一/买一价),减少滑点

--
Gitblit v1.9.1