From 3bcc6918c362a7837f792ef91018a1fe043096ef Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Tue, 12 May 2026 17:27:21 +0800
Subject: [PATCH] refactor(gateApi): 将条件单ID管理从单值改为集合支持批量操作

---
 src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java       |   86 ++++++++++++++++++++++++++-----------------
 src/main/java/com/xcong/excoin/modules/gateApi/GateWebSocketClientManager.java |    2 
 src/main/java/com/xcong/excoin/modules/gateApi/GateTradeExecutor.java          |   12 +++---
 3 files changed, 59 insertions(+), 41 deletions(-)

diff --git a/src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java b/src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
index b4bf382..a2ec807 100644
--- a/src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
+++ b/src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
@@ -15,6 +15,10 @@
 import java.util.Collections;
 import java.util.List;
 
+import com.xcong.excoin.modules.gateApi.wsHandler.handler.CandlestickChannelHandler;
+import com.xcong.excoin.modules.gateApi.wsHandler.handler.PositionClosesChannelHandler;
+import com.xcong.excoin.modules.gateApi.wsHandler.handler.PositionsChannelHandler;
+
 /**
  * Gate 网格交易服务 — 策略核心。
  *
@@ -108,10 +112,10 @@
     /** 空仓止盈队列,降序排列(大→小),仓位推送时消费 */
     private final List<BigDecimal> shortTakeProfitQueue = Collections.synchronizedList(new ArrayList<>());
 
-    /** 当前多仓限价单 ID,用于取消旧单 */
-    private volatile String currentLongOrderId;
-    /** 当前空仓限价单 ID,用于取消旧单 */
-    private volatile String currentShortOrderId;
+    /** 当前多仓条件单 ID 集合,用于取消旧单 */
+    private final List<String> currentLongOrderIds = Collections.synchronizedList(new ArrayList<>());
+    /** 当前空仓条件单 ID 集合,用于取消旧单 */
+    private final List<String> currentShortOrderIds = Collections.synchronizedList(new ArrayList<>());
 
     /** 基底空头入场价 */
     private BigDecimal shortBaseEntryPrice;
@@ -296,8 +300,8 @@
         longPriceQueue.clear();
         longTakeProfitQueue.clear();
         shortTakeProfitQueue.clear();
-        currentLongOrderId = null;
-        currentShortOrderId = null;
+        currentLongOrderIds.clear();
+        currentShortOrderIds.clear();
         log.info("[Gate] 网格策略已启动");
     }
 
@@ -515,11 +519,11 @@
 
             executor.placeConditionalEntryOrder(longPriceQueue.get(0),
                     FuturesPriceTrigger.RuleEnum.NUMBER_1, config.getQuantity(),
-                    orderId -> { currentLongOrderId = orderId; log.info("[Gate] 初始条件多单已挂, id:{}, trigger:{}", orderId, longPriceQueue.get(0)); },
+                    orderId -> { currentLongOrderIds.add(orderId); log.info("[Gate] 初始条件多单已挂, id:{}, trigger:{}", orderId, longPriceQueue.get(0)); },
                     null);
             executor.placeConditionalEntryOrder(shortPriceQueue.get(0),
                     FuturesPriceTrigger.RuleEnum.NUMBER_2, negate(config.getQuantity()),
-                    orderId -> { currentShortOrderId = orderId; log.info("[Gate] 初始条件空单已挂, id:{}, trigger:{}", orderId, shortPriceQueue.get(0)); },
+                    orderId -> { currentShortOrderIds.add(orderId); log.info("[Gate] 初始条件空单已挂, id:{}, trigger:{}", orderId, shortPriceQueue.get(0)); },
                     null);
 
             state = StrategyState.ACTIVE;
@@ -642,27 +646,34 @@
         if (!isMarginSafe()) {
             log.warn("[Gate] 保证金超限,跳过挂条件单");
         } else {
-            String oldLongId = currentLongOrderId;
-            executor.cancelConditionalOrder(oldLongId);
+            synchronized (currentLongOrderIds) {
+                for (String id : currentLongOrderIds) {
+                    executor.cancelConditionalOrder(id);
+                }
+                currentLongOrderIds.clear();
+            }
             executor.placeConditionalEntryOrder(newShortFirst,
                     FuturesPriceTrigger.RuleEnum.NUMBER_2, negate(config.getQuantity()),
-                    orderId -> { currentShortOrderId = orderId; log.info("[Gate] 新条件空单, id:{}, trigger:{}", orderId, newShortFirst); },
+                    orderId -> { currentShortOrderIds.add(orderId); log.info("[Gate] 新条件空单, id:{}, trigger:{}", orderId, newShortFirst); },
                     null);
             executor.placeConditionalEntryOrder(newLongFirst,
                     FuturesPriceTrigger.RuleEnum.NUMBER_1, config.getQuantity(),
-                    orderId -> { currentLongOrderId = orderId; log.info("[Gate] 新条件多单, id:{}, trigger:{}", orderId, newLongFirst); },
+                    orderId -> { currentLongOrderIds.add(orderId); log.info("[Gate] 新条件多单, id:{}, trigger:{}", orderId, newLongFirst); },
                     null);
+
+            if (newShortFirst.compareTo(shortEntryPrice) > 0
+                    && newShortFirst.compareTo(longEntryPrice) < 0) {
+                BigDecimal reverseLongTp = newShortFirst.add(step).setScale(1, RoundingMode.HALF_UP);
+                longTakeProfitQueue.add(reverseLongTp);
+                longTakeProfitQueue.sort(BigDecimal::compareTo);
+                executor.placeConditionalEntryOrder(newShortFirst,
+                        FuturesPriceTrigger.RuleEnum.NUMBER_1, config.getQuantity(),
+                        orderId -> { currentLongOrderIds.add(orderId); },
+                        null);
+                log.info("[Gate] 反向条件多单已挂, trigger:{}, size:{}, 止盈:{}", newShortFirst, config.getQuantity(), reverseLongTp);
+            }
         }
 
-        if (isMarginSafe()
-                && shortEntryPrice.compareTo(BigDecimal.ZERO) > 0
-                && longEntryPrice.compareTo(BigDecimal.ZERO) > 0
-                && longEntryPrice.compareTo(shortEntryPrice) > 0
-                && currentPrice.compareTo(shortEntryPrice) > 0
-                && currentPrice.compareTo(longEntryPrice) < 0) {
-            executor.openLong(config.getQuantity(), null, null);
-            log.info("[Gate] 额外反向开多, 当前价:{} 在[{},{}]之间", currentPrice, shortEntryPrice, longEntryPrice);
-        }
     }
 
     /**
@@ -742,27 +753,34 @@
         if (!isMarginSafe()) {
             log.warn("[Gate] 保证金超限,跳过挂条件单");
         } else {
-            String oldShortId = currentShortOrderId;
-            executor.cancelConditionalOrder(oldShortId);
+            synchronized (currentShortOrderIds) {
+                for (String id : currentShortOrderIds) {
+                    executor.cancelConditionalOrder(id);
+                }
+                currentShortOrderIds.clear();
+            }
             executor.placeConditionalEntryOrder(newLongFirst,
                     FuturesPriceTrigger.RuleEnum.NUMBER_1, config.getQuantity(),
-                    orderId -> { currentLongOrderId = orderId; log.info("[Gate] 新条件多单, id:{}, trigger:{}", orderId, newLongFirst); },
+                    orderId -> { currentLongOrderIds.add(orderId); log.info("[Gate] 新条件多单, id:{}, trigger:{}", orderId, newLongFirst); },
                     null);
             executor.placeConditionalEntryOrder(newShortFirst,
                     FuturesPriceTrigger.RuleEnum.NUMBER_2, negate(config.getQuantity()),
-                    orderId -> { currentShortOrderId = orderId; log.info("[Gate] 新条件空单, id:{}, trigger:{}", orderId, newShortFirst); },
+                    orderId -> { currentShortOrderIds.add(orderId); log.info("[Gate] 新条件空单, id:{}, trigger:{}", orderId, newShortFirst); },
                     null);
+
+            if (newLongFirst.compareTo(shortEntryPrice) > 0
+                    && newLongFirst.compareTo(longEntryPrice) < 0) {
+                BigDecimal reverseShortTp = newLongFirst.subtract(step).setScale(1, RoundingMode.HALF_UP);
+                shortTakeProfitQueue.add(reverseShortTp);
+                shortTakeProfitQueue.sort((a, b) -> b.compareTo(a));
+                executor.placeConditionalEntryOrder(newLongFirst,
+                        FuturesPriceTrigger.RuleEnum.NUMBER_2, negate(config.getQuantity()),
+                        orderId -> { currentShortOrderIds.add(orderId); },
+                        null);
+                log.info("[Gate] 反向条件空单已挂, trigger:{}, size:{}, 止盈:{}", newLongFirst, negate(config.getQuantity()), reverseShortTp);
+            }
         }
 
-        if (isMarginSafe()
-                && shortEntryPrice.compareTo(BigDecimal.ZERO) > 0
-                && longEntryPrice.compareTo(BigDecimal.ZERO) > 0
-                && longEntryPrice.compareTo(shortEntryPrice) > 0
-                && currentPrice.compareTo(shortEntryPrice) > 0
-                && currentPrice.compareTo(longEntryPrice) < 0) {
-            executor.openShort(negate(config.getQuantity()), null, null);
-            log.info("[Gate] 额外反向开空, 当前价:{} 在[{},{}]之间", currentPrice, shortEntryPrice, longEntryPrice);
-        }
     }
 
     // ---- 保证金安全阀 ----
diff --git a/src/main/java/com/xcong/excoin/modules/gateApi/GateTradeExecutor.java b/src/main/java/com/xcong/excoin/modules/gateApi/GateTradeExecutor.java
index 1752dd7..33b0e2d 100644
--- a/src/main/java/com/xcong/excoin/modules/gateApi/GateTradeExecutor.java
+++ b/src/main/java/com/xcong/excoin/modules/gateApi/GateTradeExecutor.java
@@ -260,12 +260,12 @@
     }
 
     /**
-     * 异步创建条件开仓单(价格触发开仓)。
+     * 异步创建条件开仓单(价格触发后以触发价限价开仓)。
      *
-     * <p>服务器监控价格,达到触发价后以市价 IOC 开仓。与止盈单不同,不设 order_type(默认开仓),
-     * reduce_only=false。适用于"价格到达 X 才买入 / 跌到 Y 才卖出"的场景。
+     * <p>服务器监控价格,达到触发价后以触发价挂 GTC 限价单开仓。与止盈单不同,不设 order_type(默认开仓),
+     * reduce_only=false。
      *
-     * @param triggerPrice 触发价格
+     * @param triggerPrice 触发价格(同时也是限价执行价格)
      * @param rule         触发规则(NUMBER_1: 最新价≥触发价时执行;NUMBER_2: 最新价≤触发价时执行)
      * @param size         开仓张数(正=开多,负=开空)
      * @param onSuccess    成功回调,接收 conditionOrderId
@@ -288,8 +288,8 @@
                 FuturesInitialOrder initial = new FuturesInitialOrder();
                 initial.setContract(contract);
                 initial.setSize(Long.parseLong(size));
-                initial.setPrice("0");
-                initial.setTif(FuturesInitialOrder.TifEnum.IOC);
+                initial.setPrice(triggerPrice.toString());
+                initial.setTif(FuturesInitialOrder.TifEnum.GTC);
                 initial.setReduceOnly(false);
 
                 FuturesPriceTriggeredOrder order = new FuturesPriceTriggeredOrder();
diff --git a/src/main/java/com/xcong/excoin/modules/gateApi/GateWebSocketClientManager.java b/src/main/java/com/xcong/excoin/modules/gateApi/GateWebSocketClientManager.java
index 03f018b..409d2ed 100644
--- a/src/main/java/com/xcong/excoin/modules/gateApi/GateWebSocketClientManager.java
+++ b/src/main/java/com/xcong/excoin/modules/gateApi/GateWebSocketClientManager.java
@@ -56,7 +56,7 @@
                     .leverage("100")
                     .marginMode("CROSS")
                     .positionMode("dual")
-                    .gridRate(new BigDecimal("0.005"))
+                    .gridRate(new BigDecimal("0.003"))
                     .overallTp(new BigDecimal("5"))
                     .maxLoss(new BigDecimal("15"))
                     .quantity("1")

--
Gitblit v1.9.1