From 308f2838c6dbf4d6d87a087402adcdabc4c51e41 Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Tue, 02 Jun 2026 14:47:38 +0800
Subject: [PATCH] docs(okx): 添加网格交易策略架构文档并更新钉钉配置
---
src/main/java/com/xcong/excoin/utils/dingtalk/DingTalkUtils.java | 10 +-
src/main/java/com/xcong/excoin/modules/okxNewPrice/OKX网格策略架构文档.md | 248 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 253 insertions(+), 5 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..ebd8fa0
--- /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,248 @@
+# OKX 网格交易策略 — 架构文档
+
+## 一、模块总览
+
+```
+okxNewPrice/
+├── OkxConfig.java # 全局配置(Builder模式)
+├── OkxGridTradeService.java # 核心策略引擎
+├── OkxTradeExecutor.java # REST API 异步执行器
+├── OkxGridElement.java # 网格价格层级 + 8个全局O(1)索引
+├── OkxTraderParam.java # 单笔挂单参数
+├── OkxGridWsClient.java # WebSocket 客户端(双连接)
+├── OkxWebSocketClientManager.java # Spring容器入口 + WS组装
+├── ExchangeInfoEnum.java # 账户枚举
+│
+├── gridWs/
+│ ├── OkxGridChannelHandler.java # 频道处理器接口
+│ ├── OkxKlineChannelHandler.java # candle1m → onKline()
+│ ├── OkxPositionsChannelHandler.java # positions → onPositionUpdate()
+│ ├── OkxOrdersChannelHandler.java # orders → onOrderUpdate()(替代orders-algo)
+│ └── OkxAlgoOrdersChannelHandler.java # orders-algo(测试网不可用,保留备用)
+│
+└── okxpi/config/ # OKX API 底层HTTP/签名工具
+```
+
+## 二、策略生命周期
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ init() → startGrid() → WAITING_KLINE │
+│ ↓ │
+│ onKline(首根K线) → OPENING → 市价双开基底 │
+│ ├── openLong(baseQuantity张) │
+│ └── openShort(baseQuantity张) │
+│ ↓ │
+│ onPositionUpdate() → 双基底成交 → tryGenerateQueues() │
+│ ├── generateShortQueue() 空仓队列(基准价向下步进) │
+│ ├── generateLongQueue() 多仓队列(基准价向上步进) │
+│ ├── updateGridElements() 构建OkxGridElement双向链表 │
+│ ├── 多仓止损 -2~-11: algo(sell+long, slTriggerPx, sz=qty) │
+│ └── 空仓止损 2~11: algo(buy+short, slTriggerPx, sz=qty) │
+│ ↓ │
+│ state = ACTIVE │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+### 状态机
+
+| 状态 | 含义 | 进入条件 |
+|------|------|---------|
+| `WAITING_KLINE` | 等待首根K线 | startGrid() |
+| `OPENING` | 基底开仓中 | 首根K线到达 |
+| `ACTIVE` | 策略运行中 | 网格初始化完成 |
+| `STOPPED` | 已停止(等待重启) | 盈亏达标/持仓归零 |
+
+## 三、WS连接架构
+
+```
+┌──────────────────────────────────────────────────────┐
+│ OkxWebSocketClientManager (@PostConstruct) │
+│ │
+│ ┌─ gridWsClientPublic ───────────────────────────┐ │
+│ │ URL: /ws/v5/business (无需登录,直连订阅) │ │
+│ │ ├── OkxKlineChannelHandler (candle1m) │ │
+│ └────────────────────────────────────────────────┘ │
+│ │
+│ ┌─ gridWsClientPrivate ──────────────────────────┐ │
+│ │ URL: /ws/v5/private (先登录再订阅) │ │
+│ │ ├── OkxPositionsChannelHandler (positions) │ │
+│ │ └── OkxOrdersChannelHandler (orders) │ │
+│ └────────────────────────────────────────────────┘ │
+└──────────────────────────────────────────────────────┘
+```
+
+| 连接 | URL | 频道 | 是否登录 | 回调 |
+|------|-----|------|---------|------|
+| 业务WS | `/ws/v5/business` | `candle1m` | 否 | `onKline(closePrice)` |
+| 私有WS | `/ws/v5/private` | `positions` | 是 | `onPositionUpdate(instId, posSide, pos, avgPx)` |
+| 私有WS | `/ws/v5/private` | `orders` | 是 | `onOrderUpdate(algoId, state, ordType)` |
+
+> **注意**:`orders-algo` 频道在测试网不可用(60018),已替换为 `orders` 频道。algo触发后生成普通订单,fill数据中带 `algoId` 字段可匹配回原始条件单。
+
+## 四、网格ID体系
+
+```
+价格方向: 低 ←────────────────── 基准价 ──────────────────→ 高
+ID: -N ... -3 -2 -1 0 1 2 3 ... N
+用途: 多仓止损区 基底 空仓止损区
+ (初始化: -2~-11) (初始化: 2~11)
+
+链表: ... ← -3 ← -2 ← -1 ← 0 → 1 → 2 → 3 → ...
+ (通过 upId/downId + INDEX 实现 O(1) 遍历)
+```
+
+### GridElement 字段
+
+| 类别 | 字段 | 说明 |
+|------|------|------|
+| 标识 | `id`, `gridPrice`, `upId`, `downId` | 编号、价格、双向链表指针 |
+| 多仓订单 | `hasLongOrder`, `longOrderId`, `longTraderParam` | 多仓条件单状态 |
+| 空仓订单 | `hasShortOrder`, `shortOrderId`, `shortTraderParam` | 空仓条件单状态 |
+| 止盈 | `longTakeProfitOrderId`, `shortTakeProfitOrderId` | 止盈algoId |
+| 止损 | `longStopLossOrderId`, `shortStopLossOrderId` | 止损algoId |
+
+### 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)
+```
+
+## 五、事件驱动循环(ACTIVE状态)
+
+### 5.1 K线回调 `onKline(closePrice)`
+
+```
+更新 lastKlinePrice → updateUnrealizedPnl()
+→ STOPPED: 清条件单+平仓+startGrid()
+→ WAITING_KLINE: 市价双开基底
+→ ACTIVE: checkProfitAndReset()(每根K线检查盈亏达标)
+```
+
+### 5.2 仓位推送 `onPositionUpdate(instId, posSide, posSize, avgPx)`
+
+```
+基底首次成交 → tryGenerateQueues()
+后续仓位变化 → 更新 positionSize/entryPrice
+仓位归零 → handlePositionZeroAndReset()
+```
+
+### 5.3 订单成交 `onOrderUpdate(algoId, state, ordType)`
+
+```
+state == "filled" 时:
+
+ ┌─ findByLongStopLossOrderId(algoId) → handleLongStopLossTriggered()
+ │ 清空止损ID → grid -(N-1) 挂 count×qty 张多单
+ │ → 若 N>2, 取消 grid -(N-2) 旧多单
+ │
+ ├─ findByShortStopLossOrderId(algoId) → handleShortStopLossTriggered()
+ │ 清空止损ID → grid N-1 挂 count×qty 张空单
+ │ → 若 N>2, 取消 grid N-2 旧空单
+ │
+ ├─ findByShortOrderId(algoId) → extendShortStopLoss(filledQty)
+ │ 从最远空仓止损向外扩展 stopLossCount 个网格止损
+ │
+ └─ findByLongOrderId(algoId) → extendLongStopLoss(filledQty)
+ 从最远多仓止损向外扩展 stopLossCount 个网格止损
+```
+
+### 5.4 平仓推送 `onPositionClose(side, pnl)`
+
+```
+cumulativePnl += pnl
+→ 总盈亏 ≤ -maxLoss → 钉钉告警(不停止,仅通知)
+```
+
+## 六、关键公式
+
+### 6.1 网格步长
+
+```
+step = shortBaseEntryPrice × gridRate (对齐到 priceScale 精度)
+```
+
+### 6.2 止损触发 → 追单数量
+
+```
+priceDiff = |avgEntryPrice - newEntryGridPrice|.abs()
+count = priceDiff / step (DOWN取整, 最小1)
+entryQty = count × quantity
+```
+
+### 6.3 挂单成交 → 追挂止损
+
+```
+stopLossCount = filledQty / quantity
+从最远止损ID向外扩展 stopLossCount 个网格,每格挂 1个 quantity张的止损单
+```
+
+### 6.4 未实现盈亏
+
+```
+多仓Pnl = longPositionSize × ctVal × (lastKlinePrice - longEntryPrice)
+空仓Pnl = shortPositionSize × ctVal × (shortEntryPrice - lastKlinePrice)
+unrealizedPnl = 多仓Pnl + 空仓Pnl
+```
+
+### 6.5 盈亏达标检查
+
+```
+upl + availEq > initialPrincipal + expectedProfit → 重置策略
+```
+
+## 七、REST API 映射
+
+| OkxTradeExecutor方法 | OKX API | 用途 |
+|------|---------|------|
+| `openLong(size)` / `openShort(size)` | `POST /api/v5/trade/order` (market) | 市价基底开仓 |
+| `marketClose(side, posSide, sz)` | `POST /api/v5/trade/order` (market, reduceOnly) | 市价平仓 |
+| `placeConditionalEntryOrder(tp, side, posSide, sz)` | `POST /api/v5/trade/order-algo` (triggerPx) | 条件开仓单 |
+| `placeTakeProfit(tp, side, posSide, sz)` | `POST /api/v5/trade/order-algo` (slTriggerPx) | 止损/止盈条件单 |
+| `cancelAlgoOrder(algoId)` | `POST /api/v5/trade/cancel-algos` | 取消单个条件单 |
+| `getBalance()` | `GET /api/v5/account/balance` | 查询余额 |
+| `getPositions()` | `GET /api/v5/account/positions` | 查询持仓 |
+| `setLeverage(lever)` | `POST /api/v5/account/set-leverage` | 设置杠杆 |
+
+## 八、配置参数(OkxConfig.Builder默认值)
+
+| 参数 | 默认值 | 说明 |
+|------|--------|------|
+| `instId` | `ETH-USDT-SWAP` | 合约名称 |
+| `leverage` | `100` | 杠杆倍数 |
+| `tdMode` | `cross` | 全仓保证金 |
+| `gridRate` | `0.0025` | 网格间距 0.25% |
+| `expectedProfit` | `2` | 预期收益 2 USDT |
+| `maxLoss` | `15` | 最大亏损 15 USDT |
+| `quantity` | `1` | 每格下单张数 |
+| `baseQuantity` | `10` | 基底开仓张数 |
+| `priceScale` | `2` | 价格精度 |
+| `ctVal` | `0.1` | 合约面值 |
+| `gridQueueSize` | `300` | 队列容量 |
+| `marginRatioLimit` | `0.2` | 保证金占比上限 20% |
+
+## 九、线程模型
+
+```
+WS回调线程(串行) Executor线程(单线程池)
+ │ │
+ ├─ onKline() ├─ openLong/openShort
+ ├─ onPositionUpdate() ├─ marketClose
+ ├─ onOrderUpdate() ──下单──→ ├─ placeConditionalEntryOrder
+ │ ├─ placeTakeProfit
+ │ └─ cancelAlgoOrder
+ │
+ └─ checkProfitAndReset() (REST同步查余额)
+```
+
+- WS回调在 `[ctReadThread-XX]` 中**串行**执行,避免并发问题
+- REST API 提交到 `okx-trade-worker` 单线程池异步执行,避免阻塞WS线程
+- `closeExistingPositions()` 和 `handlePositionZeroAndReset()` 在WS线程中同步调用REST,需注意耗时
+- `LostConnectionChecker` 已关闭(`setConnectionLostTimeout(0)`),由应用层 `ping`/`pong` 心跳接管
diff --git a/src/main/java/com/xcong/excoin/utils/dingtalk/DingTalkUtils.java b/src/main/java/com/xcong/excoin/utils/dingtalk/DingTalkUtils.java
index 0535436..ae08d82 100644
--- a/src/main/java/com/xcong/excoin/utils/dingtalk/DingTalkUtils.java
+++ b/src/main/java/com/xcong/excoin/utils/dingtalk/DingTalkUtils.java
@@ -49,12 +49,12 @@
if (DEFAULT == null) {
synchronized (DingTalkUtils.class) {
if (DEFAULT == null) {
-// DEFAULT = new DingTalkUtils(
-// "57a3e695f78d7547fe20fb7aef82cf35a27de1846bbc6966e0194761976d7597",
-// "SECd59a93c8939eeaef0d97b5b714639df4af95d922002d0a440bc82ad42710a89e");
DEFAULT = new DingTalkUtils(
- "e357a3417991da86a5f79ea5bc8785b529c1da8b9d27458febed3b3d10c857c4",
- "SECf2b819e930cb4b367cf599f11a30eb8a5d0f4b0b1c069a57aa15328a3feebf8c");
+ "57a3e695f78d7547fe20fb7aef82cf35a27de1846bbc6966e0194761976d7597",
+ "SECd59a93c8939eeaef0d97b5b714639df4af95d922002d0a440bc82ad42710a89e");
+// DEFAULT = new DingTalkUtils(
+// "e357a3417991da86a5f79ea5bc8785b529c1da8b9d27458febed3b3d10c857c4",
+// "SECf2b819e930cb4b367cf599f11a30eb8a5d0f4b0b1c069a57aa15328a3feebf8c");
}
}
}
--
Gitblit v1.9.1