From 23ece6103fd890655f0eef79331d3d73921611a2 Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Wed, 07 Jan 2026 11:59:23 +0800
Subject: [PATCH] feat(trade): 优化交易系统止损逻辑和订单处理机制

---
 src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxQuantWebSocketClient.java |    7 -
 src/main/java/com/xcong/excoin/modules/okxNewPrice/celue/CaoZuoServiceImpl.java |   11 ++
 src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/PositionsWs.java       |   40 ---------
 src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/OrderInfoWs.java       |   26 +++++
 src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/TradeOrderWs.java      |   99 ++----------------------
 5 files changed, 50 insertions(+), 133 deletions(-)

diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxQuantWebSocketClient.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxQuantWebSocketClient.java
index 3a37a34..65f5676 100644
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxQuantWebSocketClient.java
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxQuantWebSocketClient.java
@@ -395,15 +395,14 @@
         // 这会导致多账号之间的数据冲突。需要进一步修改这些类的设计,让数据存储与特定账号关联
         if (OrderInfoWs.ORDERINFOWS_CHANNEL.equals(channel)) {
 
-            TradeRequestParam tradeRequestParam = OrderInfoWs.handleEvent(response, redisUtils, account.name());
-            TradeOrderWs.orderZhiYingEvent(webSocketClient, tradeRequestParam);
+            List<TradeRequestParam> tradeRequestParams = OrderInfoWs.handleEvent(response, redisUtils, account.name());
+            TradeOrderWs.orderZhiYingEvent(webSocketClient, tradeRequestParams);
         }else if (AccountWs.ACCOUNTWS_CHANNEL.equals(channel)) {
 
             AccountWs.handleEvent(response, account.name());
         } else if (PositionsWs.POSITIONSWS_CHANNEL.equals(channel)) {
 
-            List<TradeRequestParam> tradeRequestParams = PositionsWs.handleEvent(response, account.name());
-            TradeOrderWs.orderZhiSunEvent(webSocketClient, tradeRequestParams);
+            PositionsWs.handleEvent(response, account.name());
         } else if (BalanceAndPositionWs.CHANNEL_NAME.equals(channel)) {
 
             BalanceAndPositionWs.handleEvent(response);
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/celue/CaoZuoServiceImpl.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/celue/CaoZuoServiceImpl.java
index 1adb9fd..db8cd0c 100644
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/celue/CaoZuoServiceImpl.java
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/celue/CaoZuoServiceImpl.java
@@ -81,6 +81,17 @@
             // 账户预期亏损金额比这个还小时,立即止损
             if (realKuiSunAmount.compareTo(zhiSunAmount) > 0){
                 log.warn("账户冷静止损......");
+                //目前止损掉损失较大的一个方向
+                String positionAccountName = PositionsWs.initAccountName(accountName, posSide);
+                BigDecimal upl = PositionsWs.getAccountMap(positionAccountName).get("upl");
+                String posSideOther = CoinEnums.POSSIDE_LONG.getCode().equals(posSide) ? CoinEnums.POSSIDE_SHORT.getCode() : CoinEnums.POSSIDE_LONG.getCode();
+                String positionAccountOther = PositionsWs.initAccountName(accountName, posSideOther);
+                BigDecimal uplOther = PositionsWs.getAccountMap(positionAccountOther).get("upl");
+                if (upl.compareTo(uplOther) > 0){
+                    log.warn("{}的亏损{},{}的亏损{},止损{}......",posSide,upl,posSideOther,uplOther,uplOther);
+                    posSide = posSideOther;
+                }
+
                 WsMapBuild.saveStringToMap(InstrumentsWs.getAccountMap(accountName), CoinEnums.OUT.name(),  OrderParamEnums.OUT_YES.getValue());
                 tradeRequestParam.setTradeType(OrderParamEnums.TRADE_YES.getValue());
                 return caoZuoZhiSunEvent(accountName, markPx, posSide);
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/OrderInfoWs.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/OrderInfoWs.java
index 333d3b1..945081d 100644
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/OrderInfoWs.java
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/OrderInfoWs.java
@@ -16,6 +16,8 @@
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -69,7 +71,7 @@
     private static final String STATE_KEY = "state";
     private static final String FILLFEE_KEY = "fillFee";
     private static final String POSSIDE_KEY = "posSide";
-    public static TradeRequestParam handleEvent(JSONObject response, RedisUtils redisUtils, String accountName) {
+    public static List<TradeRequestParam> handleEvent(JSONObject response, RedisUtils redisUtils, String accountName) {
 
         log.info("开始执行OrderInfoWs......");
         try {
@@ -140,7 +142,7 @@
 
                     log.info("{}: 订单详情已完成: {}, 自定义编号: {}", accountName, CoinEnums.HE_YUE.getCode(), clOrdId);
 
-
+                    List<TradeRequestParam> tradeRequestParamList = new ArrayList<>();
 
                     TradeRequestParam tradeRequestParam = new TradeRequestParam();
                     tradeRequestParam.setAccountName(accountName);
@@ -176,7 +178,25 @@
                     tradeRequestParam.setSide(CoinEnums.POSSIDE_LONG.getCode().equals(posSide) ? CoinEnums.SIDE_SELL.getCode() : CoinEnums.SIDE_BUY.getCode());
                     tradeRequestParam.setClOrdId(WsParamBuild.getOrderNum(side));
                     tradeRequestParam.setSz(accFillSz);
-                    return tradeRequestParam;
+                    tradeRequestParamList.add(tradeRequestParam);
+
+                    // 1. 判断当前价格属于哪个网格去限价止损
+                    WangGeListEnum gridByPriceNew = WangGeListEnum.getGridByPrice(new BigDecimal(avgPx));
+                    if (gridByPriceNew != null) {
+                        TradeRequestParam tradeRequestParamZhiSun = new TradeRequestParam();
+                        tradeRequestParamZhiSun.setAccountName(accountName);
+                        tradeRequestParamZhiSun.setMarkPx(String.valueOf(gridByPriceNew.getZhi_sun_dian()));
+                        tradeRequestParamZhiSun.setInstId(CoinEnums.HE_YUE.getCode());
+                        tradeRequestParamZhiSun.setTdMode(CoinEnums.CROSS.getCode());
+                        tradeRequestParamZhiSun.setPosSide(posSide);
+                        tradeRequestParamZhiSun.setOrdType(CoinEnums.ORDTYPE_LIMIT.getCode());
+                        tradeRequestParamZhiSun.setTradeType(OrderParamEnums.TRADE_YES.getValue());
+                        tradeRequestParamZhiSun.setSide(CoinEnums.POSSIDE_LONG.getCode().equals(posSide) ? CoinEnums.SIDE_SELL.getCode() : CoinEnums.SIDE_BUY.getCode());
+                        tradeRequestParamZhiSun.setClOrdId(WsParamBuild.getOrderNum(side));
+                        tradeRequestParamZhiSun.setSz(accFillSz);
+                        tradeRequestParamList.add(tradeRequestParamZhiSun);
+                    }
+                    return tradeRequestParamList;
 
                 }
                 return null;
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/PositionsWs.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/PositionsWs.java
index afcf78e..7136774 100644
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/PositionsWs.java
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/PositionsWs.java
@@ -65,7 +65,7 @@
         initParam(arg, accountName,CoinEnums.POSSIDE_SHORT.getCode());
     }
 
-    public static List<TradeRequestParam> handleEvent(JSONObject response, String accountName) {
+    public static void handleEvent(JSONObject response, String accountName) {
 
 
         log.info("开始执行PositionsWs......");
@@ -73,10 +73,8 @@
             JSONArray dataArray = response.getJSONArray("data");
             if (dataArray == null || dataArray.isEmpty()) {
                 log.info("账户持仓频道数据为空,等待更新");
-                return null;
+                return;
             }
-
-            List<TradeRequestParam> tradeRequestParamList = new ArrayList<>();
 
             for (int i = 0; i < dataArray.size(); i++) {
                 JSONObject posData = dataArray.getJSONObject(i);
@@ -118,45 +116,11 @@
                     );
                     //先更新缓存
                     Map<String, BigDecimal> stringBigDecimalMap = initParam(posData, accountName, posSide);
-                    //构建止损参数
-                    if (stringBigDecimalMap.get("pos").compareTo(BigDecimal.ZERO) > 0){
-                        TradeRequestParam tradeRequestParam = new TradeRequestParam();
-                        // 1. 判断当前价格属于哪个网格
-                        WangGeListEnum gridByPriceNew = WangGeListEnum.getGridByPrice(new BigDecimal(avgPx));
-                        if (gridByPriceNew != null) {
-                            String zhiSunDian = gridByPriceNew.getZhi_sun_dian();
-                            String fangXiang = gridByPriceNew.getFang_xiang();
-                            BigDecimal fangXiangNow = stringBigDecimalMap.get("posSide");
-                            if (fangXiangNow.equals(fangXiang)){
-                                tradeRequestParam.setOrdType(CoinEnums.ORDTYPE_LIMIT.getCode());
-                                tradeRequestParam.setMarkPx(String.valueOf(zhiSunDian));
-                            }else{
-                                tradeRequestParam.setOrdType(CoinEnums.ORDTYPE_MARKET.getCode());
-                                tradeRequestParam.setMarkPx(String.valueOf(markPx));
-                            }
-                        }else{
-                            tradeRequestParam.setOrdType(CoinEnums.ORDTYPE_MARKET.getCode());
-                            tradeRequestParam.setMarkPx(String.valueOf(markPx));
-                        }
-
-
-                        tradeRequestParam.setAccountName(accountName);
-                        tradeRequestParam.setInstId(CoinEnums.HE_YUE.getCode());
-                        tradeRequestParam.setTdMode(CoinEnums.CROSS.getCode());
-                        tradeRequestParam.setPosSide(posSide);
-                        tradeRequestParam.setTradeType(OrderParamEnums.TRADE_YES.getValue());
-                        tradeRequestParam.setSide(CoinEnums.POSSIDE_LONG.getCode().equals(posSide) ? CoinEnums.SIDE_SELL.getCode() : CoinEnums.SIDE_BUY.getCode());
-                        tradeRequestParam.setClOrdId(WsParamBuild.getOrderNum(tradeRequestParam.getSide()));
-                        tradeRequestParam.setSz(String.valueOf(stringBigDecimalMap.get("pos")));
-                        tradeRequestParamList.add(tradeRequestParam);
-                    }
                 }
             }
-            return tradeRequestParamList;
         } catch (Exception e) {
             log.error("处理持仓频道推送数据失败", e);
         }
-        return null;
     }
 
     private static Map<String, BigDecimal> initParam(JSONObject posData, String accountName,String posSide) {
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/TradeOrderWs.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/TradeOrderWs.java
index 5586822..36b99e1 100644
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/TradeOrderWs.java
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/TradeOrderWs.java
@@ -130,91 +130,12 @@
         }
     }
 
-    public static void orderZhiYingEvent(WebSocketClient webSocketClient, TradeRequestParam tradeRequestParam) {
+    public static void orderZhiYingEvent(WebSocketClient webSocketClient, List<TradeRequestParam> tradeRequestParams) {
 
 
-        log.info("开始执行TradeOrderWs......");
-        if (tradeRequestParam == null){
-
-            log.warn("下单{}参数缺失,取消发送",tradeRequestParam);
-            return;
-        }
-        String accountName = tradeRequestParam.getAccountName();
-        String markPx = tradeRequestParam.getMarkPx();
-        String instId = tradeRequestParam.getInstId();
-        String tdMode = tradeRequestParam.getTdMode();
-        String posSide = tradeRequestParam.getPosSide();
-        String ordType = tradeRequestParam.getOrdType();
-
-        String tradeType = tradeRequestParam.getTradeType();
-
-        String clOrdId = tradeRequestParam.getClOrdId();
-        String side = tradeRequestParam.getSide();
-        String sz = tradeRequestParam.getSz();
-        /**
-         * 校验必要参数
-         * 验证下单参数是否存在空值
-         */
-        if (
-                StrUtil.isBlank(accountName)
-                        || StrUtil.isBlank(instId)
-                        || StrUtil.isBlank(tdMode)
-                        || StrUtil.isBlank(posSide)
-                        || StrUtil.isBlank(ordType)
-                        || StrUtil.isBlank(clOrdId)
-                        || StrUtil.isBlank(side)
-                        || StrUtil.isBlank(sz)
-                        || StrUtil.isBlank(markPx)
-
-        ){
-            log.warn("下单参数缺失,取消发送");
-            return;
-        }
-        log.info("账户:{},触发价格:{},币种:{},方向:{},买卖:{},数量:{},是否允许下单:{},编号:{},",
-                accountName, markPx, instId, posSide,side,  sz, tradeType, clOrdId);
-        //验证是否允许下单
-        if (StrUtil.isNotEmpty(tradeType) && OrderParamEnums.TRADE_NO.getValue().equals(tradeType)) {
-            log.warn("账户{}不允许下单,取消发送", accountName);
-            return;
-        }
-
-        /**
-         * 检验账户和仓位是否准备就绪
-         * 开多:买入开多(side 填写 buy; posSide 填写 long )
-         * 开空:卖出开空(side 填写 sell; posSide 填写 short ) 需要检验账户通道是否准备就绪
-         * 平多:卖出平多(side 填写 sell;posSide 填写 long )
-         * 平空:买入平空(side 填写 buy; posSide 填写 short ) 需要检验仓位通道是否准备就绪
-         */
-
-        try {
-            JSONArray argsArray = new JSONArray();
-            JSONObject args = new JSONObject();
-            args.put("instId", instId);
-            args.put("tdMode", tdMode);
-            args.put("clOrdId", clOrdId);
-            args.put("side", side);
-
-            args.put("posSide", posSide);
-            args.put("ordType", ordType);
-            args.put("sz", sz);
-            args.put("px", markPx);
-            argsArray.add(args);
-
-            String connId = WsParamBuild.getOrderNum(ORDERWS_CHANNEL);
-            JSONObject jsonObject = WsParamBuild.buildJsonObject(connId, ORDERWS_CHANNEL, argsArray);
-            webSocketClient.send(jsonObject.toJSONString());
-            log.info("发送下单频道:{},数量:{}", side, sz);
-
-        } catch (Exception e) {
-            log.error("下单构建失败", e);
-        }
-    }
-
-    public static void orderZhiSunEvent(WebSocketClient webSocketClient, List<TradeRequestParam> tradeRequestParams) {
-
-        log.info("开始止损......");
-        if (CollUtil.isEmpty(tradeRequestParams)){
-            log.error("止损下单{}参数缺失,取消发送", JSONUtil.parse(tradeRequestParams));
+        log.info("开始执行限价{}......",JSONUtil.parse(tradeRequestParams));
+        if (tradeRequestParams == null){
+            log.warn("限价下单参数缺失,取消发送");
             return;
         }
         for (TradeRequestParam tradeRequestParam : tradeRequestParams){
@@ -246,16 +167,17 @@
                             || StrUtil.isBlank(markPx)
 
             ){
-                log.error("止损下单参数缺失,取消发送");
+                log.warn("下单参数缺失,取消发送");
                 return;
             }
-            log.info("止损账户:{},触发价格:{},币种:{},方向:{},买卖:{},数量:{},是否允许下单:{},编号:{},",
+            log.info("账户:{},触发价格:{},币种:{},方向:{},买卖:{},数量:{},是否允许下单:{},编号:{},",
                     accountName, markPx, instId, posSide,side,  sz, tradeType, clOrdId);
             //验证是否允许下单
             if (StrUtil.isNotEmpty(tradeType) && OrderParamEnums.TRADE_NO.getValue().equals(tradeType)) {
-                log.error("止损账户{}不允许下单,取消发送", accountName);
+                log.warn("账户{}不允许下单,取消发送", accountName);
                 return;
             }
+
             /**
              * 检验账户和仓位是否准备就绪
              * 开多:买入开多(side 填写 buy; posSide 填写 long )
@@ -263,6 +185,7 @@
              * 平多:卖出平多(side 填写 sell;posSide 填写 long )
              * 平空:买入平空(side 填写 buy; posSide 填写 short ) 需要检验仓位通道是否准备就绪
              */
+
             try {
                 JSONArray argsArray = new JSONArray();
                 JSONObject args = new JSONObject();
@@ -280,13 +203,13 @@
                 String connId = WsParamBuild.getOrderNum(ORDERWS_CHANNEL);
                 JSONObject jsonObject = WsParamBuild.buildJsonObject(connId, ORDERWS_CHANNEL, argsArray);
                 webSocketClient.send(jsonObject.toJSONString());
-                log.info("止损已发送......");
+                log.info("发送下单频道:{},数量:{}", side, sz);
+
             } catch (Exception e) {
                 log.error("下单构建失败", e);
             }
         }
     }
-
 
 
     /**

--
Gitblit v1.9.1