From d663147264d0fc97e9e7ddbd4aeee69e833ad025 Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Tue, 02 Jun 2026 15:14:02 +0800
Subject: [PATCH] fix(okxNewPrice): 修复合约面值配置错误并更新文档

---
 src/main/java/com/xcong/excoin/modules/okxNewPrice/OKX网格策略架构文档.md |  373 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 373 insertions(+), 0 deletions(-)

diff --git "a/src/main/java/com/xcong/excoin/modules/okxNewPrice/OKX\347\275\221\346\240\274\347\255\226\347\225\245\346\236\266\346\236\204\346\226\207\346\241\243.md" "b/src/main/java/com/xcong/excoin/modules/okxNewPrice/OKX\347\275\221\346\240\274\347\255\226\347\225\245\346\236\266\346\236\204\346\226\207\346\241\243.md"
new file mode 100644
index 0000000..e141645
--- /dev/null
+++ "b/src/main/java/com/xcong/excoin/modules/okxNewPrice/OKX\347\275\221\346\240\274\347\255\226\347\225\245\346\236\266\346\236\204\346\226\207\346\241\243.md"
@@ -0,0 +1,373 @@
+# OKX 网格交易策略 — 架构文档
+
+## 一、模块总览
+
+```
+okxNewPrice/
+├── OkxConfig.java              # 全局配置(Builder模式,策略唯一参数入口)
+├── OkxGridTradeService.java    # 核心策略引擎(状态机 + 事件驱动循环)
+├── OkxTradeExecutor.java       # REST API 异步执行器(单线程池 + 背压)
+├── OkxGridElement.java         # 网格价格层级 + 8个全局O(1)静态索引
+├── OkxTraderParam.java         # 单笔挂单参数(方向/触发价/数量/订单ID)
+├── OkxGridWsClient.java        # WebSocket 客户端(双连接:business + private)
+├── OkxWebSocketClientManager.java  # Spring容器入口 + Bean组装 + 生命周期
+├── ExchangeInfoEnum.java       # 账户枚举
+│
+├── gridWs/
+│   ├── OkxGridChannelHandler.java        # 频道处理器接口
+│   ├── OkxKlineChannelHandler.java       # candle1m → onKline(closePrice)
+│   ├── OkxPositionsChannelHandler.java   # positions → onPositionUpdate()
+│   ├── OkxOrdersChannelHandler.java      # orders → onOrderUpdate() (替代orders-algo)
+│   └── OkxAlgoOrdersChannelHandler.java  # orders-algo(测试网60018不可用,备用)
+│
+└── okxpi/config/              # OKX V5 API 底层HTTP签名/请求工具链
+    ├── Account.java / OKXAccount.java
+    ├── RequestHandler.java / ResponseHandler.java
+    └── utils/ (SignUtils, UrlBuilder, OkHttpUtils...)
+```
+
+## 二、Spring 容器入口
+
+`OkxWebSocketClientManager` 是 `@Component + @PostConstruct` 驱动的组装点。
+
+### 2.1 激活条件
+
+```yaml
+# application-test.yml
+app:
+  quant: true   # @ConditionalOnProperty 控制是否启动
+```
+
+### 2.2 初始化顺序 (@PostConstruct)
+
+```
+1. ExchangeInfoEnum 取首个账户
+2. 构建 OkxAccount(baseUrl + apiKey/secretKey/passphrase)
+3. OkxConfig.builder() 构建配置(BTC-USDT-SWAP, 100x, cross, gridRate=0.001)
+4. OkxGridTradeService.init()
+   ├── GET /api/v5/account/balance → 取 USDT details[].eq 作为初始本金
+   ├── POST /api/v5/trade/cancel-algos → 清旧条件单
+   ├── GET /api/v5/account/positions → 遍历平已有仓位
+   └── POST /api/v5/account/set-leverage → 设置杠杆
+5. 创建双 WS 客户端并注册频道处理器:
+   ├── gridWsClientPublic  → /v5/business  → candle1m (无需登录)
+   └── gridWsClientPrivate → /v5/private   → positions + orders (先登录)
+6. gridTradeService.startGrid() → WAITING_KLINE
+```
+
+### 2.3 销毁顺序 (@PreDestroy)
+
+```
+stopGrid() → cancelAllAlgoOrders + shutdown → gridWsClientPublic.destroy() → gridWsClientPrivate.destroy()
+```
+
+### 2.4 当前活跃配置
+
+| 参数 | 值 | 说明 |
+|------|-----|------|
+| `instId` | `BTC-USDT-SWAP` | 合约 |
+| `leverage` | `100` | 杠杆 |
+| `tdMode` | `cross` | 全仓 |
+| `gridRate` | `0.001` | 网格间距 0.1% |
+| `expectedProfit` | `20` USDT | 盈亏达标重置线 |
+| `maxLoss` | `30` USDT | 亏损风控告警线 |
+| `quantity` | `1` | 每格下单张数 |
+| `baseQuantity` | `10` | 基底开仓张数 |
+| `priceScale` | `2` | 价格精度 |
+| `ctVal` | `0.1` | 合约面值 |
+| `gridQueueSize` | `300` | 价格队列容量 |
+
+## 三、WS 连接架构
+
+```
+┌──────────────────────────────────────────────────────────┐
+│                 OkxWebSocketClientManager                │
+│                                                          │
+│  gridWsClientPublic (isPublic=true)                     │
+│  ├── URL: wss://wspap.okx.com:8443/ws/v5/business       │
+│  ├── 连接后立即 subscribeAllHandlers()(无需登录)       │
+│  └── OkxKlineChannelHandler(candle1m, instId)            │
+│                                                          │
+│  gridWsClientPrivate (isPublic=false)                    │
+│  ├── URL: wss://wspap.okx.com:8443/ws/v5/private        │
+│  ├── 连接后 wsLogin() → 登录成功 → subscribeAllHandlers()│
+│  ├── OkxPositionsChannelHandler(positions, instType:SWAP)│
+│  └── OkxOrdersChannelHandler(orders, instType:SWAP)     │
+└──────────────────────────────────────────────────────────┘
+```
+
+| 连接 | URL | 频道 | 订阅参数 | 回调方法 |
+|------|-----|------|---------|---------|
+| business | `/v5/business` | `candle1m` | `instId` | `onKline(closePrice)` |
+| private | `/v5/private` | `positions` | `instType:SWAP` | `onPositionUpdate(instId, posSide, pos, avgPx)` |
+| private | `/v5/private` | `orders` | `instType:SWAP` | `onOrderUpdate(algoId, state, ordType)` |
+
+> **orders 频道替代 orders-algo**:`orders-algo` 在测试网不可用(60018),改订阅 `orders` 频道。
+> algo 触发后生成普通市价单,fill 数据中 `algoId` 字段非空时可匹配回原始条件单。
+> `OkxOrdersChannelHandler` 过滤逻辑:`state=filled` AND `algoId` 非空。
+
+### 3.1 心跳机制
+
+```
+10s 无消息 → send("ping") → server reply "pong" → 重置计时器
+25s 定时检查 → 超时则 send("ping")
+LostConnectionChecker: setConnectionLostTimeout(0) 已关闭(协议级ping OKX不响应)
+```
+
+## 四、策略生命周期
+
+### 4.1 状态机
+
+| 状态 | 含义 | 进入条件 | 退出动作 |
+|------|------|---------|---------|
+| `WAITING_KLINE` | 等待首根K线 | startGrid() | 首根K线→OPENING |
+| `OPENING` | 基底开仓中 | 首根K线 | 双基底成交→ACTIVE |
+| `ACTIVE` | 策略运行 | 网格初始化完成 | 盈亏达标/持仓归零→STOPPED |
+| `STOPPED` | 已停止 | 重置/达标 | 下根K线自动 startGrid() |
+
+### 4.2 完整流程
+
+```
+┌──────────────────────────────────────────────────────────────┐
+│  init() → startGrid() → WAITING_KLINE                       │
+│    ↓                                                         │
+│  onKline(首根) → OPENING → executor.openLong/Short(10张)     │
+│    ↓                                                         │
+│  onPositionUpdate → 基底成交 → tryGenerateQueues()           │
+│    ├── generateShortQueue(): shortBasePrice - step 向下步进  │
+│    ├── generateLongQueue():  shortBasePrice + step 向上步进  │
+│    ├── updateGridElements(): 构建 601个 OkxGridElement       │
+│    │   ├── ID≤-1: 空仓区(降序)  ID=0:基底  ID≥1:多仓区(升序)│
+│    │   └── ID索引 + 价格索引 + 6个订单ID索引 (O(1))          │
+│    ├── 多仓止损 -2~-11: POST order-algo                      │
+│    │   ordType=conditional, side=sell, posSide=long,         │
+│    │   slTriggerPx=网格价, slOrdPx=-1, sz=quantity           │
+│    └── 空仓止损 +2~+11: POST order-algo                      │
+│        ordType=conditional, side=buy, posSide=short,         │
+│        slTriggerPx=网格价, slOrdPx=-1, sz=quantity           │
+│    ↓                                                         │
+│  state = ACTIVE ─────────────────────────────────────────────│
+└──────────────────────────────────────────────────────────────┘
+```
+
+## 五、事件驱动循环 (ACTIVE 状态)
+
+### 5.1 K线回调 `onKline(closePrice)`
+
+```
+lastKlinePrice = closePrice
+updateUnrealizedPnl()
+
+if STOPPED:
+    cancelAllAlgoOrders() + closeExistingPositions() + startGrid() → WAITING_KLINE
+
+if WAITING_KLINE:
+    市价双开 baseQuantity 张 (openLong + openShort) → OPENING
+
+if ACTIVE:
+    checkProfitAndReset()  // 每根K线检查盈亏达标
+```
+
+### 5.2 仓位推送 `onPositionUpdate`
+
+```
+long 有仓位:
+  首次(基底) → 记录 baseLongOpened + 均价 → tryGenerateQueues()
+  后续       → 更新 positionSize / entryPrice
+  归零       → handlePositionZeroAndReset("多仓")
+
+short 有仓位:
+  首次(基底) → 记录 baseShortOpened + 均价 → tryGenerateQueues()
+  后续       → 更新 positionSize / entryPrice
+  归零       → handlePositionZeroAndReset("空仓")
+```
+
+### 5.3 订单成交 `onOrderUpdate(algoId, state, ordType)` — 核心事件
+
+触发条件:`state == "filled"` (来自 `orders` 频道的成交推送)
+
+```
+                    ┌─ findByLongStopLossOrderId(algoId)
+                    │  → handleLongStopLossTriggered()
+                    │    止损-ID=-N → 清空止损ID
+                    │    → 在 -(N-1) 挂 count×qty 张多单
+                    │       (ordType=trigger, triggerPx, orderPx=-1)
+                    │    → N>2 时取消 -(N-2) 旧多单
+                    │
+                    ├─ findByShortStopLossOrderId(algoId)
+                    │  → handleShortStopLossTriggered()
+                    │    止损ID=N → 清空止损ID
+                    │    → 在 N-1 挂 count×qty 张空单
+                    │       (ordType=trigger, triggerPx, orderPx=-1)
+                    │    → N>2 时取消 N-2 旧空单
+algoId 匹配 ────────┤
+                    ├─ findByShortOrderId(algoId) && hasShortOrder
+                    │  → shortEntryTraderIdParam(null, false)
+                    │  → extendShortStopLoss(filledQty)
+                    │    到最远空仓止损外扩 stopLossCount 个网格
+                    │    每格挂 quantity 张止损
+                    │
+                    └─ findByLongOrderId(algoId) && hasLongOrder
+                       → longEntryTraderIdParam(null, false)
+                       → extendLongStopLoss(filledQty)
+                         到最远多仓止损外扩 stopLossCount 个网格
+                         每格挂 quantity 张止损
+```
+
+### 5.4 平仓推送 `onPositionClose`
+
+```
+cumulativePnl += pnl
+总盈亏 ≤ -maxLoss → 钉钉告警(仅通知,不停止)
+```
+
+## 六、网格ID体系
+
+```
+价格方向:  低 ←────────────── 基底价 ──────────────→ 高
+ID:        ... -3  -2  -1    0    1   2   3 ...
+用途:      多仓止损/追单区    基底    空仓止损/追单区
+           初始化止损:-2~-11        初始化止损:2~11
+
+链表: ... ← -3 ← -2 ← -1 ← 0 → 1 → 2 → 3 → ...
+      (upId/downId + INDEX → O(1) 遍历)
+```
+
+### 6.1 GridElement 字段
+
+| 类别 | 字段 | 说明 |
+|------|------|------|
+| 标识 | `id`, `gridPrice`, `upId`, `downId` | 编号、价格、双向链表指针 |
+| 多仓 | `hasLongOrder`, `longOrderId`, `longTraderParam` | 多仓挂单状态 |
+| 空仓 | `hasShortOrder`, `shortOrderId`, `shortTraderParam` | 空仓挂单状态 |
+| 止盈 | `longTakeProfitOrderId`, `shortTakeProfitOrderId` | 止盈algoId |
+| 止损 | `longStopLossOrderId`, `shortStopLossOrderId` | 止损algoId |
+
+### 6.2 8个全局O(1)索引
+
+```
+INDEX                  → findById(int)
+PRICE_INDEX            → findByPrice(BigDecimal)
+LONG_ORDER_ID_INDEX    → findByLongOrderId(String)
+SHORT_ORDER_ID_INDEX   → findByShortOrderId(String)
+LONG_TP_ORDER_ID_INDEX → findByLongTakeProfitOrderId(String)
+SHORT_TP_ORDER_ID_INDEX→ findByShortTakeProfitOrderId(String)
+LONG_SL_ORDER_ID_INDEX → findByLongStopLossOrderId(String)
+SHORT_SL_ORDER_ID_INDEX→ findByShortStopLossOrderId(String)
+```
+
+每次订单状态变更后调用 `OkxGridElement.refreshIndices()` 增量重建,同时 `logAll()` 打印全量网格数据。
+
+## 七、关键公式
+
+### 7.1 网格步长
+
+```
+step = shortBaseEntryPrice × gridRate    (priceScale 精度对齐)
+```
+
+### 7.2 止损触发 → 追单
+
+```
+priceDiff = |avgEntryPrice - newEntryGridPrice|.abs()
+count     = priceDiff / step           (RoundingMode.DOWN, 最小1)
+entryQty  = count × quantity           → 挂 ordType=trigger 条件单
+```
+
+### 7.3 挂单成交 → 追挂止损
+
+```
+stopLossCount = filledQty / quantity
+从最远止损ID向外扩展 stopLossCount 个网格
+每格挂 1 个 quantity 张止损 (ordType=conditional, slTriggerPx)
+```
+
+### 7.4 未实现盈亏
+
+```
+longPnl  = longPositionSize × ctVal × (lastKlinePrice - longEntryPrice)
+shortPnl = shortPositionSize × ctVal × (shortEntryPrice - lastKlinePrice)
+unrealizedPnl = longPnl + shortPnl
+```
+
+### 7.5 盈亏达标检查
+
+```
+GET balance → upl (未实现盈亏) + availEq (可用保证金)
+if upl + availEq > initialPrincipal + expectedProfit → STOPPED → 平仓+清条件单+startGrid()
+```
+
+## 八、REST API 映射
+
+| 方法 | OKX API | ordType | 触发参数 | 用途 |
+|------|---------|---------|---------|------|
+| `openLong(size)` | `POST /api/v5/trade/order` | `market` | — | 市价开多 |
+| `openShort(size)` | `POST /api/v5/trade/order` | `market` | — | 市价开空 |
+| `marketClose(s,p,sz)` | `POST /api/v5/trade/order` | `market` | reduceOnly | 市价平仓 |
+| `placeConditionalEntryOrder` | `POST /api/v5/trade/order-algo` | **`trigger`** | `triggerPx` | 计划委托开仓 |
+| `placeTakeProfit` | `POST /api/v5/trade/order-algo` | **`conditional`** | `slTriggerPx` | 止损/止盈平仓 |
+| `cancelAlgoOrder(id)` | `POST /api/v5/trade/cancel-algos` | — | array body | 取消单个条件单 |
+| `cancelAllAlgoOrders()` | `POST /api/v5/trade/cancel-algos` | — | array body | 清除全部条件单 |
+| `getBalance()` | `GET /api/v5/account/balance` | — | ccy=USDT | 查询余额 |
+| `getPositions()` | `GET /api/v5/account/positions` | — | instId | 查询持仓 |
+| `setLeverage(l)` | `POST /api/v5/account/set-leverage` | — | lever+mgnMode | 设置杠杆 |
+
+### 8.1 ordType 对照表
+
+| ordType | OKX含义 | 我们用途 | 触发价参数 |
+|---------|--------|---------|-----------|
+| `trigger` | 计划委托 | **开仓挂单**(等价格到位开仓) | `triggerPx` |
+| `conditional` | 单向止盈止损 | **止损单**(等价格到位平仓) | `slTriggerPx` |
+
+## 九、网格ID示例(BTC-USDT, step≈70)
+
+```
+ID=-11  price=69309.67 ← 最远多仓止损
+ID=-10  price=69379.97
+ID= -9  price=69450.27
+ID= -8  price=69520.57
+ID= -7  price=69590.87
+ID= -6  price=69661.17
+ID= -5  price=69731.47
+ID= -4  price=69801.77
+ID= -3  price=69872.07
+ID= -2  price=69942.37  ← 多仓止损起点
+ID= -1  price=70012.67
+ID=  0  price=70082.97  ← 基底 (shortBaseEntryPrice)
+ID=  1  price=70153.27
+ID=  2  price=70223.57  ← 空仓止损起点
+ID=  3  price=70293.87
+ID=  4  price=70364.17
+ID=  5  price=70434.47
+ID=  6  price=70504.77
+ID=  7  price=70575.07
+ID=  8  price=70645.37
+ID=  9  price=70715.67
+ID= 10  price=70785.97
+ID= 11  price=70856.27  ← 最远空仓止损
+```
+
+## 十、线程模型
+
+```
+[ctReadThread-XX] WS回调线程(串行)     [okx-trade-worker] Executor(单线程池)
+  │                                        │
+  ├─ onKline(closePrice)                   ├─ openLong / openShort
+  │   └─ updateUnrealizedPnl()             ├─ marketClose
+  │   └─ checkProfitAndReset()(同步REST)   ├─ placeConditionalEntryOrder (trigger)
+  │                                        ├─ placeTakeProfit (conditional)
+  ├─ onPositionUpdate(...)                 └─ cancelAlgoOrder / cancelAllAlgoOrders
+  │   └─ tryGenerateQueues() → 批量挂止损
+  │   └─ handlePositionZeroAndReset()
+  │
+  └─ onOrderUpdate(algoId, state, ordType)
+      ├─ handleLongStopLossTriggered → placeConditionalEntryOrder
+      ├─ handleShortStopLossTriggered → placeConditionalEntryOrder
+      ├─ extendLongStopLoss → placeTakeProfit
+      └─ extendShortStopLoss → placeTakeProfit
+```
+
+- WS 回调线程串行执行,天然线程安全
+- REST 提交到 `okx-trade-worker` 单线程异步执行,避免阻塞 WS
+- `closeExistingPositions()` / `handlePositionZeroAndReset()` 在 WS 线程中同步调用 REST(IOC 市价单,秒级完成)
+- `LostConnectionChecker(0)` 已禁用,由应用层 `send("ping")`/`handle "pong"` 接管

--
Gitblit v1.9.1