From b779f4884d55f8bcb24c0d13ca34795d1003c296 Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Fri, 05 Jun 2026 13:16:00 +0800
Subject: [PATCH] fix(okxNewPrice): 解决网格交易价格精度计算问题

---
 src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxGridTradeService.java |   39 +++++++++++++++++++++++++++++----------
 1 files changed, 29 insertions(+), 10 deletions(-)

diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxGridTradeService.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxGridTradeService.java
index d0bc3b9..aa9f09b 100644
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxGridTradeService.java
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxGridTradeService.java
@@ -267,10 +267,12 @@
             executor.openLong(config.getBaseQuantity(), (orderId) -> {
                 OkxTraderParam baseLongTp = OkxTraderParam.builder().entryOrderId(orderId).build();
                 config.setBaseLongTraderParam(baseLongTp);
+                tryGenerateQueues(); // 异步回调到达后重试,防止 WS 推送先到导致 NPE
             }, null);
             executor.openShort(config.getBaseQuantity(), (orderId) -> {
                 OkxTraderParam baseShortTp = OkxTraderParam.builder().entryOrderId(orderId).build();
                 config.setBaseShortTraderParam(baseShortTp);
+                tryGenerateQueues(); // 异步回调到达后重试,防止 WS 推送先到导致 NPE
             }, null);
             return;
         }
@@ -300,9 +302,10 @@
                     baseLongOpened = true;
                     log.info("[OKX] 基底多成交价: {}", longBaseEntryPrice);
                     tryGenerateQueues();
-                } else {
-                    longPositionSize = posSize;
-                }
+            } else {
+                longPositionSize = posSize;
+                tryGenerateQueues(); // 后续 WS 推送触发重试,兜底此前 NPE 失败的情况
+            }
             } else {
                 if (longActive && state == StrategyState.ACTIVE) {
                     log.info("[OKX] 多仓持仓归零,重置策略");
@@ -321,9 +324,10 @@
                     baseShortOpened = true;
                     log.info("[OKX] 基底空成交价: {}", shortBaseEntryPrice);
                     tryGenerateQueues();
-                } else {
-                    shortPositionSize = posSize;
-                }
+            } else {
+                shortPositionSize = posSize;
+                tryGenerateQueues(); // 后续 WS 推送触发重试,兜底此前 NPE 失败的情况
+            }
             } else {
                 if (shortActive && state == StrategyState.ACTIVE) {
                     log.info("[OKX] 空仓持仓归零,重置策略");
@@ -394,17 +398,28 @@
     // ---- 网格队列处理 ----
 
     private void tryGenerateQueues() {
+        // 防止重复执行:一旦已进入 ACTIVE 状态不再重复初始化
+        if (state == StrategyState.ACTIVE) {
+            return;
+        }
         if (baseLongOpened && baseShortOpened) {
+            // 防御异步竞态:openLong/openShort 的回调可能还未设置 trader param
+            OkxTraderParam baseLongTp = config.getBaseLongTraderParam();
+            OkxTraderParam baseShortTp = config.getBaseShortTraderParam();
+            if (baseLongTp == null || baseShortTp == null) {
+                log.warn("[OKX] tryGenerateQueues 等待异步回调: longTp={}, shortTp={}",
+                        baseLongTp != null, baseShortTp != null);
+                return;
+            }
+
             generateShortQueue();
             generateLongQueue();
             updateGridElements();
 
             // 标记基座挂单
             OkxGridElement baseGridElement = OkxGridElement.findById(0);
-            OkxTraderParam baseLongTp = config.getBaseLongTraderParam();
             baseGridElement.setLongOrderId(baseLongTp.getEntryOrderId());
             baseGridElement.setHasLongOrder(true);
-            OkxTraderParam baseShortTp = config.getBaseShortTraderParam();
             baseGridElement.setShortOrderId(baseShortTp.getEntryOrderId());
             baseGridElement.setHasShortOrder(true);
 
@@ -563,7 +578,9 @@
 
         BigDecimal triggerPrice = newEntryGrid.getGridPrice();
         BigDecimal priceDiff = longEntryPrice.subtract(triggerPrice).abs();
-        int count = priceDiff.divide(config.getStep(), 0, RoundingMode.DOWN).intValue();
+        // 精度补偿:步长被setScale截断,priceDiff/step可能产生1.99998→Down截断为1的问题
+        BigDecimal epsilon = new BigDecimal("0.00000001");
+        int count = priceDiff.add(epsilon).divide(config.getStep(), 0, RoundingMode.DOWN).intValue();
         count = Math.max(1, count);
         int entryQty = count * Integer.parseInt(config.getQuantity());
         String size = String.valueOf(entryQty);
@@ -605,7 +622,9 @@
 
         BigDecimal triggerPrice = newEntryGrid.getGridPrice();
         BigDecimal priceDiff = shortEntryPrice.subtract(triggerPrice).abs();
-        int count = priceDiff.divide(config.getStep(), 0, RoundingMode.DOWN).intValue();
+        // 精度补偿:步长被setScale截断,priceDiff/step可能产生1.99998→Down截断为1的问题
+        BigDecimal epsilon = new BigDecimal("0.00000001");
+        int count = priceDiff.add(epsilon).divide(config.getStep(), 0, RoundingMode.DOWN).intValue();
         count = Math.max(1, count);
         int entryQty = count * Integer.parseInt(config.getQuantity());
         String size = String.valueOf(entryQty);

--
Gitblit v1.9.1