From e4c5d36a2da3fabd0f233df15a563d520d08b287 Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Wed, 24 Jun 2026 16:10:34 +0800
Subject: [PATCH] refactor(okx): 删除K线处理器并重命名止盈单方法为止损单

---
 src/main/java/com/xcong/excoin/modules/okxApi/OkxGridTradeService.java |    8 
 /dev/null                                                              |  234 ---------------------------------------
 src/main/java/com/xcong/excoin/modules/okxApi/OkxTradeExecutor.java    |   71 ++++++++---
 3 files changed, 55 insertions(+), 258 deletions(-)

diff --git a/src/main/java/com/xcong/excoin/modules/okxApi/OkxGridTradeService.java b/src/main/java/com/xcong/excoin/modules/okxApi/OkxGridTradeService.java
index f9e7ddd..62fdc13 100644
--- a/src/main/java/com/xcong/excoin/modules/okxApi/OkxGridTradeService.java
+++ b/src/main/java/com/xcong/excoin/modules/okxApi/OkxGridTradeService.java
@@ -641,7 +641,7 @@
                 }
                 BigDecimal triggerPrice = elem.getGridPrice();
                 int finalId = id;
-                executor.placeTakeProfit(triggerPrice, "close_short", config.getQuantity(),
+                executor.placeStopLoss(triggerPrice, "close_short", config.getQuantity(),
                         profitId -> {
                             elem.setShortStopLossOrderId(profitId);
                             GridElement.refreshIndices();
@@ -655,7 +655,7 @@
                 }
                 BigDecimal triggerPrice = elem.getGridPrice();
                 int finalId = id;
-                executor.placeTakeProfit(triggerPrice, "close_long", config.getQuantity(),
+                executor.placeStopLoss(triggerPrice, "close_long", config.getQuantity(),
                         profitId -> {
                             elem.setLongStopLossOrderId(profitId);
                             GridElement.refreshIndices();
@@ -1144,7 +1144,7 @@
             }
             BigDecimal triggerPrice = elem.getGridPrice();
             int finalSlId = newSlId;
-            executor.placeTakeProfit(triggerPrice, "close_long", config.getQuantity(),
+            executor.placeStopLoss(triggerPrice, "close_long", config.getQuantity(),
                     profitId -> {
                         elem.setLongStopLossOrderId(profitId);
                         GridElement.refreshIndices();
@@ -1172,7 +1172,7 @@
             }
             BigDecimal triggerPrice = elem.getGridPrice();
             int finalSlId = newSlId;
-            executor.placeTakeProfit(triggerPrice, "close_short", config.getQuantity(),
+            executor.placeStopLoss(triggerPrice, "close_short", config.getQuantity(),
                     profitId -> {
                         elem.setShortStopLossOrderId(profitId);
                         GridElement.refreshIndices();
diff --git a/src/main/java/com/xcong/excoin/modules/okxApi/OkxTradeExecutor.java b/src/main/java/com/xcong/excoin/modules/okxApi/OkxTradeExecutor.java
index 8c971f9..a7ee107 100644
--- a/src/main/java/com/xcong/excoin/modules/okxApi/OkxTradeExecutor.java
+++ b/src/main/java/com/xcong/excoin/modules/okxApi/OkxTradeExecutor.java
@@ -208,17 +208,12 @@
         });
     }
 
-    // ==================== 止盈条件单 ====================
+    // ==================== 止盈/止损条件单 ====================
 
     /**
-     * 异步创建止盈条件单(OKX 算法订单 — conditional 类型)。
+     * 异步创建止盈条件单(OKX 算法订单 — conditional 类型,tpTriggerPx)。
      *
-     * <p>服务器监控价格,达到触发价后自动以市价平仓。
-     * 使用 OKX 的 {@code order-algo} 接口,ordType=conditional。
-     *
-     * <h3>止盈失败兜底</h3>
-     * 止盈单创建失败时立即调用 {@link #marketClose(String, String)} 市价平仓,
-     * 确保仓位不会因止损条件未挂上而无保护。
+     * <p>止盈单创建失败时立即调用 {@link #marketClose(String, String)} 市价平仓兜底。
      *
      * @param triggerPrice 触发价格
      * @param orderType    平仓类型:"close_long" 平多 / "close_short" 平空
@@ -229,6 +224,34 @@
                                  String orderType,
                                  String size,
                                  Consumer<String> onSuccess) {
+        placeConditionalClose(triggerPrice, orderType, size, onSuccess, false);
+    }
+
+    /**
+     * 异步创建止损条件单(OKX 算法订单 — conditional 类型,slTriggerPx)。
+     *
+     * <p>止损单创建失败时立即调用 {@link #marketClose(String, String)} 市价平仓兜底。
+     *
+     * @param triggerPrice 触发价格
+     * @param orderType    平仓类型:"close_long" 平多 / "close_short" 平空
+     * @param size         平仓张数(正数,如 "15")
+     * @param onSuccess    成功回调,接收 algoId(可为 null)
+     */
+    public void placeStopLoss(BigDecimal triggerPrice,
+                               String orderType,
+                               String size,
+                               Consumer<String> onSuccess) {
+        placeConditionalClose(triggerPrice, orderType, size, onSuccess, true);
+    }
+
+    /**
+     * 通用平仓条件单:isStopLoss=true 用 slTriggerPx/slOrdPx,false 用 tpTriggerPx/tpOrdPx。
+     */
+    private void placeConditionalClose(BigDecimal triggerPrice,
+                                        String orderType,
+                                        String size,
+                                        Consumer<String> onSuccess,
+                                        boolean isStopLoss) {
         executor.execute(() -> {
             String posSide = null;
             try {
@@ -240,11 +263,11 @@
                     side = "buy";
                     posSide = "short";
                 } else {
-                    log.error("[TradeExec-OKX] 未知止盈类型: {}", orderType);
+                    log.error("[TradeExec-OKX] 未知平仓类型: {}", orderType);
                     return;
                 }
 
-                // OKX conditional 止盈止损使用 tpTriggerPx/tpOrdPx 或 slTriggerPx/slOrdPx
+                String label = isStopLoss ? "止损" : "止盈";
                 JSONObject body = new JSONObject();
                 body.put("instId", contract);
                 body.put("tdMode", "cross");
@@ -252,29 +275,35 @@
                 body.put("posSide", posSide);
                 body.put("ordType", "conditional");
                 body.put("sz", size);
-                // "close_long"=平多(止盈), "close_short"=平空(止盈)
-                body.put("tpTriggerPx", triggerPrice.stripTrailingZeros().toPlainString());
-                body.put("tpTriggerPxType", "last");
-                body.put("tpOrdPx", "-1");
+                // 止盈用 tp 系列字段,止损用 sl 系列字段
+                if (isStopLoss) {
+                    body.put("slTriggerPx", triggerPrice.stripTrailingZeros().toPlainString());
+                    body.put("slTriggerPxType", "last");
+                    body.put("slOrdPx", "-1");
+                } else {
+                    body.put("tpTriggerPx", triggerPrice.stripTrailingZeros().toPlainString());
+                    body.put("tpTriggerPxType", "last");
+                    body.put("tpOrdPx", "-1");
+                }
 
                 JSONObject resp = okPost("/api/v5/trade/order-algo", body.toJSONString());
                 String code = resp.getString("code");
                 if (!"0".equals(code)) {
-                    log.error("[TradeExec-OKX] 止盈单创建失败, code:{}, msg:{}, 立即市价止盈",
-                            code, resp.getString("msg"));
+                    log.error("[TradeExec-OKX] {}单创建失败, code:{}, msg:{}, 立即市价{}",
+                            label, code, resp.getString("msg"), label);
                     marketClose(size, posSide);
                     return;
                 }
                 JSONArray data = resp.getJSONArray("data");
                 String algoId = (data != null && !data.isEmpty())
                         ? data.getJSONObject(0).getString("algoId") : null;
-                log.info("[TradeExec-OKX] 止盈单已创建, triggerPx:{}, type:{}, sz:{}, algoId:{}",
-                        triggerPrice, orderType, size, algoId);
+                log.info("[TradeExec-OKX] {}单已创建, triggerPx:{}, type:{}, sz:{}, algoId:{}",
+                        label, triggerPrice, orderType, size, algoId);
                 if (onSuccess != null) {
                     onSuccess.accept(algoId);
                 }
             } catch (Exception e) {
-                log.error("[TradeExec-OKX] 止盈单创建失败, triggerPx:{}, sz:{}, 立即市价止盈",
+                log.error("[TradeExec-OKX] 创建失败, triggerPx:{}, sz:{}, 立即市价{}",
                         triggerPrice, size, e);
                 if (posSide != null) {
                     marketClose(size, posSide);
@@ -465,7 +494,9 @@
                             for (int i = 0; i < data.size(); i++) {
                                 JSONObject order = data.getJSONObject(i);
                                 String algoId = order.getString("algoId");
-                                if (algoId == null) continue;
+                                if (algoId == null) {
+                                    continue;
+                                }
                                 JSONObject item = new JSONObject();
                                 item.put("algoId", algoId);
                                 item.put("instId", contract);
diff --git a/src/main/java/com/xcong/excoin/modules/okxApi/wsHandler/handler/CandlestickOkxChannelHandler.java b/src/main/java/com/xcong/excoin/modules/okxApi/wsHandler/handler/CandlestickOkxChannelHandler.java
deleted file mode 100644
index 9a8d754..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxApi/wsHandler/handler/CandlestickOkxChannelHandler.java
+++ /dev/null
@@ -1,195 +0,0 @@
-package com.xcong.excoin.modules.okxApi.wsHandler.handler;
-
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
-import com.xcong.excoin.modules.okxApi.OkxGridTradeService;
-import com.xcong.excoin.modules.okxApi.wsHandler.OkxChannelHandler;
-import lombok.extern.slf4j.Slf4j;
-import org.java_websocket.client.WebSocketClient;
-
-import java.math.BigDecimal;
-
-/**
- * OKX K线频道处理器(candle1m)— 策略的唯一价格时间驱动源。
- *
- * <h3>定位</h3>
- * 这是一个<b>公开频道</b>,连接到 OKX 公开 WebSocket 端点,
- * 无需登录认证。订阅 1 分钟 K 线实时推送,每收到一根 K 线即触发
- * {@link OkxGridTradeService#onKline(BigDecimal)},由策略引擎决定是否开仓/止盈。
- *
- * <h3>订阅格式</h3>
- * <pre>
- * {"op":"subscribe","args":[{"channel":"candle1m","instId":"ETH-USDT-SWAP"}]}
- * </pre>
- *
- * <h3>数据推送格式</h3>
- * <pre>
- * {
- *   "arg": {"channel":"candle1m","instId":"ETH-USDT-SWAP"},
- *   "data": [
- *     ["timestamp","open","high","low","close","vol","volCcy","volCcyQuote","confirm"]
- *   ]
- * }
- * </pre>
- *
- * <h3>字段说明</h3>
- * <table>
- *   <tr><th>索引</th><th>字段</th><th>含义</th></tr>
- *   <tr><td>0</td><td>ts</td><td>K线起始时间(Unix ms)</td></tr>
- *   <tr><td>1</td><td>o</td><td>开盘价</td></tr>
- *   <tr><td>2</td><td>h</td><td>最高价</td></tr>
- *   <tr><td>3</td><td>l</td><td>最低价</td></tr>
- *   <tr><td>4</td><td>c</td><td><b>收盘价</b>(用于驱动策略)</td></tr>
- *   <tr><td>5</td><td>vol</td><td>成交量(张)</td></tr>
- *   <tr><td>6</td><td>volCcy</td><td>成交量(币)</td></tr>
- *   <tr><td>7</td><td>volCcyQuote</td><td>成交量(USDT)</td></tr>
- *   <tr><td>8</td><td>confirm</td><td>K线状态("0"=未完结,"1"=已完结)</td></tr>
- * </table>
- *
- * <h3>注意</h3>
- * 不判断 confirm 字段(K线是否完结)——策略需要 tick 级实时响应价格变动,
- * 而非等 1 分钟烛线完结后才行动。
- *
- * @author Administrator
- */
-@Slf4j
-public class CandlestickOkxChannelHandler implements OkxChannelHandler {
-
-    /** 频道名称 */
-    private static final String CHANNEL_NAME = "candle1m";
-
-    /** 交易对标识,如 "ETH-USDT-SWAP" */
-    private final String instId;
-
-    /** 网格交易服务,接收 K 线回调 */
-    private final OkxGridTradeService gridTradeService;
-
-    /** 订阅确认状态 */
-    private volatile boolean subscribed = false;
-
-    /**
-     * 构造 K 线频道处理器。
-     *
-     * @param instId           交易对标识(如 "ETH-USDT-SWAP")
-     * @param gridTradeService OKX 网格交易策略服务实例
-     */
-    public CandlestickOkxChannelHandler(String instId, OkxGridTradeService gridTradeService) {
-        this.instId = instId;
-        this.gridTradeService = gridTradeService;
-    }
-
-    /**
-     * @return 频道名称 "candle1m"
-     */
-    @Override
-    public String getChannelName() {
-        return CHANNEL_NAME;
-    }
-
-    /**
-     * @return 交易对标识(如 "ETH-USDT-SWAP")
-     */
-    @Override
-    public String getInstId() {
-        return instId;
-    }
-
-    /**
-     * 发送 K 线频道订阅请求(公开频道,无需签名)。
-     *
-     * <h3>订阅格式</h3>
-     * <pre>
-     * {"op":"subscribe","args":[{"channel":"candle1m","instId":"ETH-USDT-SWAP"}]}
-     * </pre>
-     *
-     * @param ws OKX 公开 WebSocket 客户端
-     */
-    @Override
-    public void subscribe(WebSocketClient ws) {
-        JSONObject msg = new JSONObject();
-        msg.put("op", "subscribe");
-        JSONArray args = new JSONArray();
-        JSONObject arg = new JSONObject();
-        arg.put("channel", CHANNEL_NAME);
-        arg.put("instId", instId);
-        args.add(arg);
-        msg.put("args", args);
-        ws.send(msg.toJSONString());
-        log.info("[OKX-WS] 订阅K线频道, instId: {}", instId);
-    }
-
-    /**
-     * 发送 K 线频道取消订阅请求。
-     *
-     * @param ws OKX 公开 WebSocket 客户端
-     */
-    @Override
-    public void unsubscribe(WebSocketClient ws) {
-        JSONObject msg = new JSONObject();
-        msg.put("op", "unsubscribe");
-        JSONArray args = new JSONArray();
-        JSONObject arg = new JSONObject();
-        arg.put("channel", CHANNEL_NAME);
-        arg.put("instId", instId);
-        args.add(arg);
-        msg.put("args", args);
-        ws.send(msg.toJSONString());
-        log.info("[OKX-WS] 取消订阅K线频道, instId: {}", instId);
-    }
-
-    /**
-     * 处理 K 线推送消息。
-     *
-     * <h3>数据提取</h3>
-     * 从 {@code data[0]} 数组中提取索引 4(收盘价 close),
-     * 传给 {@code gridTradeService.onKline(closePrice)}。
-     *
-     * <h3>OKX 数据格式</h3>
-     * data 是一个二维数组,第一层是 K 线条数(通常 1 条),
-     * 第二层是各字段值。data[0][4] = 收盘价。
-     *
-     * @param response WebSocket 推送的完整 JSON
-     * @return true 表示已处理(匹配成功)
-     */
-    @Override
-    public boolean handleMessage(JSONObject response) {
-        JSONObject arg = response.getJSONObject("arg");
-        if (arg == null || !CHANNEL_NAME.equals(arg.getString("channel"))) {
-            return false;
-        }
-        try {
-            JSONArray dataArray = response.getJSONArray("data");
-            if (dataArray == null || dataArray.isEmpty()) {
-                log.debug("[OKX-WS] candle1m 数据为空");
-                return true;
-            }
-            // data[0] 是一个数组: [ts, o, h, l, c, vol, volCcy, volCcyQuote, confirm]
-            JSONArray candle = dataArray.getJSONArray(0);
-            if (candle == null || candle.size() < 5) {
-                log.warn("[OKX-WS] candle1m 数据格式异常: {}", dataArray);
-                return true;
-            }
-            // 索引 4 = 收盘价 close
-            BigDecimal closePrice = candle.getBigDecimal(4);
-
-            if (gridTradeService != null && closePrice != null) {
-                gridTradeService.onKline(closePrice);
-            }
-        } catch (Exception e) {
-            log.error("[OKX-WS] 处理 candle1m 数据失败", e);
-        }
-        return true;
-    }
-
-    // ==================== 订阅状态 ====================
-
-    @Override
-    public boolean isSubscribed() {
-        return subscribed;
-    }
-
-    @Override
-    public void setSubscribed(boolean subscribed) {
-        this.subscribed = subscribed;
-    }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxApi/wsHandler/handler/OrderAlgoOkxChannelHandler.java b/src/main/java/com/xcong/excoin/modules/okxApi/wsHandler/handler/OrderAlgoOkxChannelHandler.java
deleted file mode 100644
index 4fff35f..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxApi/wsHandler/handler/OrderAlgoOkxChannelHandler.java
+++ /dev/null
@@ -1,234 +0,0 @@
-package com.xcong.excoin.modules.okxApi.wsHandler.handler;
-
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
-import com.xcong.excoin.modules.okxApi.OkxConfig;
-import com.xcong.excoin.modules.okxApi.OkxGridTradeService;
-import com.xcong.excoin.modules.okxApi.wsHandler.AbstractOkxPrivateChannelHandler;
-import lombok.extern.slf4j.Slf4j;
-import org.java_websocket.client.WebSocketClient;
-
-/**
- * OKX 条件订单频道处理器(orders-algo)。
- *
- * <h3>定位</h3>
- * 订阅用户条件订单(algo order)状态推送。当条件单状态变更(触发、取消等)时,
- * 获得 algoId、状态、订单类型等信息。相当于 Gate 的 {@code futures.autoorders} 频道。
- *
- * <h3>订阅格式</h3>
- * 私有频道,需要先登录认证。orders-algo 频道不支持 instId,需使用 instFamily:
- * <pre>
- * {"op":"subscribe","args":[{"channel":"orders-algo","instType":"SWAP","instFamily":"ETH-USDT"}]}
- * </pre>
- *
- * <h3>数据推送格式(条件单触发/成交)</h3>
- * <pre>
- * {
- *   "arg": {"channel":"orders-algo","instType":"SWAP","instId":"ETH-USDT-SWAP"},
- *   "data": [{
- *     "algoId": "1234567890",
- *     "state": "effective",          // 状态: "effective"=已触发, "canceled"=已取消
- *     "ordType": "conditional",       // 订单类型
- *     "actualSide": "buy",           // 实际买卖方向: "buy"/"sell"
- *     "posSide": "long",             // 持仓方向: "long"/"short"/"net"
- *     "sz": "1",                     // 委托数量
- *     "triggerPx": "3000",           // 触发价格
- *     "ordPx": "3000.5",             // 委托价格
- *     "tradeId": "9876543210"        // 关联交易ID
- *   }]
- * }
- * </pre>
- *
- * <h3>状态映射</h3>
- * <ul>
- *   <li>"effective" → status="finished"(已触发/已完成)</li>
- *   <li>"canceled" → status="cancelled"(已取消)</li>
- * </ul>
- *
- * <h3>订单类型映射(orderType)</h3>
- * 根据 posSide 和 actualSide 组合推断订单类型:
- * <ul>
- *   <li>posSide=long, actualSide=sell → "plan-close-long-position"(平多仓)</li>
- *   <li>posSide=short, actualSide=buy → "plan-close-short-position"(平空仓)</li>
- *   <li>posSide=net, actualSide=buy → "entry-long"(开多仓)</li>
- *   <li>posSide=net, actualSide=sell → "entry-short"(开空仓)</li>
- * </ul>
- *
- * @author Administrator
- */
-@Slf4j
-public class OrderAlgoOkxChannelHandler extends AbstractOkxPrivateChannelHandler {
-
-    /** OKX 条件订单频道名称 */
-    private static final String CHANNEL_NAME = "orders-algo";
-
-    /** OKX 配置 */
-    private final OkxConfig config;
-
-    /** 交易品种族(如 "ETH-USDT",从合约名 ETH-USDT-SWAP 派生) */
-    private final String instFamily;
-
-    /**
-     * 构造条件订单频道处理器。
-     *
-     * @param config           OKX 配置实例(提供合约名称等)
-     * @param gridTradeService OKX 网格交易策略服务实例
-     */
-    public OrderAlgoOkxChannelHandler(OkxConfig config, OkxGridTradeService gridTradeService) {
-        super(CHANNEL_NAME,
-                config.getApiKey(), config.getApiSecret(), config.getPassphrase(),
-                config.getContract(),
-                gridTradeService);
-        this.config = config;
-        // orders-algo 频道不支持 instId,需用 instFamily:"ETH-USDT-SWAP" → "ETH-USDT"
-        String contract = config.getContract();
-        int lastDash = contract.lastIndexOf('-');
-        this.instFamily = lastDash > 0 ? contract.substring(0, lastDash) : contract;
-    }
-
-    /**
-     * 发送订阅请求,使用 instFamily 而非 instId。
-     *
-     * @param ws 私有频道 WebSocket 客户端
-     */
-    @Override
-    public void subscribe(WebSocketClient ws) {
-        JSONObject msg = new JSONObject();
-        msg.put("op", "subscribe");
-        JSONArray args = new JSONArray();
-        JSONObject arg = new JSONObject();
-        arg.put("channel", CHANNEL_NAME);
-        arg.put("instType", "SWAP");
-        arg.put("instFamily", instFamily);   // orders-algo 频道用 instFamily,不能用 instId
-        args.add(arg);
-        msg.put("args", args);
-        ws.send(msg.toJSONString());
-        log.info("[OKX-WS] 订阅条件订单频道, instFamily: {}", instFamily);
-    }
-
-    /**
-     * 发送取消订阅请求。
-     *
-     * @param ws 私有频道 WebSocket 客户端
-     */
-    @Override
-    public void unsubscribe(WebSocketClient ws) {
-        JSONObject msg = new JSONObject();
-        msg.put("op", "unsubscribe");
-        JSONArray args = new JSONArray();
-        JSONObject arg = new JSONObject();
-        arg.put("channel", CHANNEL_NAME);
-        arg.put("instType", "SWAP");
-        arg.put("instFamily", instFamily);
-        args.add(arg);
-        msg.put("args", args);
-        ws.send(msg.toJSONString());
-        log.info("[OKX-WS] 取消订阅条件订单频道, instFamily: {}", instFamily);
-    }
-
-    /**
-     * 处理条件订单推送消息。
-     *
-     * <h3>处理流程</h3>
-     * <ol>
-     *   <li>检查 arg.channel 是否匹配 "orders-algo"</li>
-     *   <li>遍历 data 数组,提取 algoId、state、actualSide、posSide、tradeId</li>
-     *   <li>映射 state → status(effective→finished, canceled→cancelled)</li>
-     *   <li>根据 posSide + actualSide 推断 orderType</li>
-     *   <li>调用 gridTradeService.onAutoOrder(algoId, status, reason, orderType, tradeId)</li>
-     * </ol>
-     *
-     * @param response WebSocket 推送的完整 JSON
-     * @return true 表示已处理(匹配成功)
-     */
-    @Override
-    public boolean handleMessage(JSONObject response) {
-        JSONObject arg = response.getJSONObject("arg");
-        if (arg == null || !CHANNEL_NAME.equals(arg.getString("channel"))) {
-            return false;
-        }
-        try {
-            JSONArray dataArray = response.getJSONArray("data");
-            if (dataArray == null || dataArray.isEmpty()) {
-                return true;
-            }
-
-            String contract = config.getContract();
-            for (int i = 0; i < dataArray.size(); i++) {
-                JSONObject orderData = dataArray.getJSONObject(i);
-
-                // 按合约名称精确过滤(instFamily 已做粗筛,这里精筛)
-                String dataInstId = orderData.getString("instId");
-                if (dataInstId == null || !contract.equals(dataInstId)) {
-                    continue;
-                }
-
-                String algoId = orderData.getString("algoId");
-                String state = orderData.getString("state");
-                String actualSide = orderData.getString("actualSide");
-                String posSide = orderData.getString("posSide");
-                String tradeId = orderData.getString("tradeId");
-                String ordType = orderData.getString("ordType");
-
-                // 状态映射
-                String status;
-                if ("effective".equals(state)) {
-                    status = "finished";
-                } else if ("canceled".equals(state)) {
-                    status = "cancelled";
-                } else {
-                    // 其他状态(如 "pending")暂不处理
-                    log.debug("[OKX-WS] orders-algo 忽略状态: algoId:{}, state:{}", algoId, state);
-                    continue;
-                }
-
-                // 推断 orderType
-                String orderType = mapOrderType(posSide, actualSide);
-
-                log.info("[OKX-WS] orders-algo 状态变更, algoId:{}, state:{}, status:{}, orderType:{}, ordType:{}, actualSide:{}, posSide:{}, tradeId:{}",
-                        algoId, state, status, orderType, ordType, actualSide, posSide, tradeId);
-
-                if (getGridTradeService() != null) {
-                    getGridTradeService().onAutoOrder(algoId, status, state, orderType, tradeId);
-                }
-            }
-        } catch (Exception e) {
-            log.error("[OKX-WS] 处理 orders-algo 数据失败", e);
-        }
-        return true;
-    }
-
-    /**
-     * 根据 OKX 的 posSide 和 actualSide 映射到策略内部的 orderType。
-     *
-     * <h3>映射规则</h3>
-     * <table>
-     *   <tr><th>posSide</th><th>actualSide</th><th>含义</th><th>orderType</th></tr>
-     *   <tr><td>long</td><td>sell</td><td>平多仓</td><td>plan-close-long-position</td></tr>
-     *   <tr><td>short</td><td>buy</td><td>平空仓</td><td>plan-close-short-position</td></tr>
-     *   <tr><td>net</td><td>buy</td><td>开多仓</td><td>entry-long</td></tr>
-     *   <tr><td>net</td><td>sell</td><td>开空仓</td><td>entry-short</td></tr>
-     * </table>
-     *
-     * @param posSide    OKX 持仓方向("long"/"short"/"net")
-     * @param actualSide OKX 实际买卖方向("buy"/"sell")
-     * @return 策略内部的订单类型字符串
-     */
-    private String mapOrderType(String posSide, String actualSide) {
-        // 平仓方向(止盈/止损触发)
-        if ("long".equals(posSide) && "sell".equals(actualSide)) {
-            return "plan-close-long-position";
-        } else if ("short".equals(posSide) && "buy".equals(actualSide)) {
-            return "plan-close-short-position";
-        }
-        // 开仓方向 — 覆盖 long_short_mode 和 net_mode 两种模式
-        if (("long".equals(posSide) || "net".equals(posSide)) && "buy".equals(actualSide)) {
-            return "entry-long";
-        } else if (("short".equals(posSide) || "net".equals(posSide)) && "sell".equals(actualSide)) {
-            return "entry-short";
-        }
-        // 默认值
-        log.warn("[OKX-WS] 未知的 orderType 映射, posSide:{}, actualSide:{}", posSide, actualSide);
-        return "unknown";
-    }
-}

--
Gitblit v1.9.1