Administrator
2026-06-02 917000befa702ed94fa12c4bf7395b940080eb71
fix(okx): 修复账户余额获取和API请求处理问题

- 修改账户余额查询逻辑,改为获取USDT合约账户余额
- 更新WebSocket连接地址为业务地址(wss://.../ws/v5/business)
- 添加pong消息处理避免心跳异常
- 修改条件单取消接口调用方式,使用原始请求体格式
- 新增sendSignedRequestRaw方法支持原始请求体签名
4 files modified
69 ■■■■■ changed files
src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxGridTradeService.java 49 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxTradeExecutor.java 8 ●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/gridWs/OkxAlgoOrdersChannelHandler.java 6 ●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/gridWs/OkxPositionsChannelHandler.java 6 ●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxGridTradeService.java
@@ -356,7 +356,7 @@
    // ---- 订单/条件单推送回调 ----
    public void onOrderUpdate(String algoId, String state, String ordType) {
        if (!"effective".equals(state) && !"canceled".equals(state)) {
        if (!"filled".equals(state) && !"canceled".equals(state)) {
            return;
        }
@@ -408,31 +408,33 @@
            baseGridElement.setShortOrderId(baseShortTp.getEntryOrderId());
            baseGridElement.setHasShortOrder(true);
            // 挂多仓止损 (id=-2 到 -11)
            // 挂多仓止损 (id=-2 到 -11),每格 quantity 张
            for (int id = -2; id >= -11; id--) {
                OkxGridElement elem = OkxGridElement.findById(id);
                if (elem == null) continue;
                BigDecimal triggerPrice = elem.getGridPrice();
                int finalId = id;
                executor.placeTakeProfit(triggerPrice.toString(), "sell", "long", "1",
                executor.placeTakeProfit(triggerPrice.toString(), "sell", "long", config.getQuantity(),
                        profitId -> {
                            elem.setLongStopLossOrderId(profitId);
                            OkxGridElement.refreshIndices();
                            log.info("[OKX] 多仓止损已挂, gridId:{}, 触发价:{}, stopLossId:{}", finalId, triggerPrice, profitId);
                            log.info("[OKX] 多仓止损已挂, gridId:{}, 触发价:{}, qty:{}, stopLossId:{}",
                                    finalId, triggerPrice, config.getQuantity(), profitId);
                        });
            }
            // 挂空仓止损 (id=2 到 11)
            // 挂空仓止损 (id=2 到 11),每格 quantity 张
            for (int id = 2; id <= 11; id++) {
                OkxGridElement elem = OkxGridElement.findById(id);
                if (elem == null) continue;
                BigDecimal triggerPrice = elem.getGridPrice();
                int finalId = id;
                executor.placeTakeProfit(triggerPrice.toString(), "buy", "short", "1",
                executor.placeTakeProfit(triggerPrice.toString(), "buy", "short", config.getQuantity(),
                        profitId -> {
                            elem.setShortStopLossOrderId(profitId);
                            OkxGridElement.refreshIndices();
                            log.info("[OKX] 空仓止损已挂, gridId:{}, 触发价:{}, stopLossId:{}", finalId, triggerPrice, profitId);
                            log.info("[OKX] 空仓止损已挂, gridId:{}, 触发价:{}, qty:{}, stopLossId:{}",
                                    finalId, triggerPrice, config.getQuantity(), profitId);
                        });
            }
@@ -561,10 +563,12 @@
        BigDecimal triggerPrice = newEntryGrid.getGridPrice();
        BigDecimal priceDiff = longEntryPrice.subtract(triggerPrice).abs();
        int entryQty = priceDiff.divide(config.getStep(), 0, RoundingMode.DOWN).intValue();
        entryQty = Math.max(1, entryQty);
        int count = priceDiff.divide(config.getStep(), 0, RoundingMode.DOWN).intValue();
        count = Math.max(1, count);
        int entryQty = count * Integer.parseInt(config.getQuantity());
        String size = String.valueOf(entryQty);
        log.info("[OKX] 多仓止损触发 gridId:{}, 在gridId:{}挂{}张多单", gridId, newEntryGridId, entryQty);
        log.info("[OKX] 多仓止损触发 gridId:{}, 在gridId:{}挂{}张多单(价差:{},步长:{},count:{},qty:{})",
                gridId, newEntryGridId, entryQty, priceDiff, config.getStep(), count, config.getQuantity());
        newEntryGrid.getLongTraderParam().setQuantity(size);
        placeEntryOrderWithPreFlag(newEntryGrid, true, triggerPrice, size);
    }
@@ -601,15 +605,20 @@
        BigDecimal triggerPrice = newEntryGrid.getGridPrice();
        BigDecimal priceDiff = shortEntryPrice.subtract(triggerPrice).abs();
        int entryQty = priceDiff.divide(config.getStep(), 0, RoundingMode.DOWN).intValue();
        entryQty = Math.max(1, entryQty);
        int count = priceDiff.divide(config.getStep(), 0, RoundingMode.DOWN).intValue();
        count = Math.max(1, count);
        int entryQty = count * Integer.parseInt(config.getQuantity());
        String size = String.valueOf(entryQty);
        log.info("[OKX] 空仓止损触发 gridId:{}, 在gridId:{}挂{}张空单", gridId, newEntryGridId, entryQty);
        log.info("[OKX] 空仓止损触发 gridId:{}, 在gridId:{}挂{}张空单(价差:{},步长:{},count:{},qty:{})",
                gridId, newEntryGridId, entryQty, priceDiff, config.getStep(), count, config.getQuantity());
        newEntryGrid.getShortTraderParam().setQuantity(size);
        placeEntryOrderWithPreFlag(newEntryGrid, false, triggerPrice, size);
    }
    private void extendLongStopLoss(int filledQty) {
        // filledQty 为本次新增止损张数 = count * quantity, 需要按 quantity 为粒度拆分为 count 个止损单
        int qty = Integer.parseInt(config.getQuantity());
        int stopLossCount = filledQty / qty;
        int furthestSlId = 0;
        for (OkxGridElement e : config.getGridElements()) {
            if (e.getLongStopLossOrderId() != null && e.getId() < furthestSlId) {
@@ -617,14 +626,14 @@
            }
        }
        if (furthestSlId == 0) furthestSlId = -11;
        log.info("[OKX] 多仓追挂止损, 当前最远止损gridId:{}, 追加{}张", furthestSlId, filledQty);
        for (int i = 0; i < filledQty; i++) {
        log.info("[OKX] 多仓追挂止损, 当前最远止损gridId:{}, 追加{}单, 每单{}张", furthestSlId, stopLossCount, qty);
        for (int i = 0; i < stopLossCount; i++) {
            int newSlId = furthestSlId - i - 1;
            OkxGridElement elem = OkxGridElement.findById(newSlId);
            if (elem == null) continue;
            BigDecimal triggerPrice = elem.getGridPrice();
            int finalSlId = newSlId;
            executor.placeTakeProfit(triggerPrice.toString(), "sell", "long", "1",
            executor.placeTakeProfit(triggerPrice.toString(), "sell", "long", config.getQuantity(),
                    profitId -> {
                        elem.setLongStopLossOrderId(profitId);
                        OkxGridElement.refreshIndices();
@@ -634,6 +643,8 @@
    }
    private void extendShortStopLoss(int filledQty) {
        int qty = Integer.parseInt(config.getQuantity());
        int stopLossCount = filledQty / qty;
        int furthestSlId = 0;
        for (OkxGridElement e : config.getGridElements()) {
            if (e.getShortStopLossOrderId() != null && e.getId() > furthestSlId) {
@@ -641,14 +652,14 @@
            }
        }
        if (furthestSlId == 0) furthestSlId = 11;
        log.info("[OKX] 空仓追挂止损, 当前最远止损gridId:{}, 追加{}张", furthestSlId, filledQty);
        for (int i = 0; i < filledQty; i++) {
        log.info("[OKX] 空仓追挂止损, 当前最远止损gridId:{}, 追加{}单, 每单{}张", furthestSlId, stopLossCount, qty);
        for (int i = 0; i < stopLossCount; i++) {
            int newSlId = furthestSlId + i + 1;
            OkxGridElement elem = OkxGridElement.findById(newSlId);
            if (elem == null) continue;
            BigDecimal triggerPrice = elem.getGridPrice();
            int finalSlId = newSlId;
            executor.placeTakeProfit(triggerPrice.toString(), "buy", "short", "1",
            executor.placeTakeProfit(triggerPrice.toString(), "buy", "short", config.getQuantity(),
                    profitId -> {
                        elem.setShortStopLossOrderId(profitId);
                        OkxGridElement.refreshIndices();
src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxTradeExecutor.java
@@ -86,7 +86,7 @@
     * @param onFailure 失败回调
     */
    public void openLong(String quantity, Consumer<String> onSuccess, Runnable onFailure) {
        submitOrder("buy", "long", quantity, "market", null, false, "tGridLong", onSuccess, onFailure);
        submitOrder("buy", "long", quantity, "market", null, false, null, onSuccess, onFailure);
    }
    /**
@@ -97,7 +97,7 @@
     * @param onFailure 失败回调
     */
    public void openShort(String quantity, Consumer<String> onSuccess, Runnable onFailure) {
        submitOrder("sell", "short", quantity, "market", null, false, "tGridShort", onSuccess, onFailure);
        submitOrder("sell", "short", quantity, "market", null, false, null, onSuccess, onFailure);
    }
    /**
@@ -233,7 +233,7 @@
    }
    /**
     * 异步取消所有未完成的 algo 订单。
     * 异步取消所有未完成的 algo 订单(best-effort,失败仅警告)。
     */
    public void cancelAllAlgoOrders() {
        executor.execute(() -> {
@@ -243,7 +243,7 @@
                        okxAccount.baseUrl, "/api/v5/trade/cancel-algos", body, HttpMethod.POST, okxAccount.isSimluate());
                log.info("[OkxExec] 已尝试清除条件单, resp:{}", resp);
            } catch (Exception e) {
                log.error("[OkxExec] 清除条件单失败", e);
                log.warn("[OkxExec] 清除条件单失败(若无挂单可忽略), msg:{}", e.getMessage());
            }
        });
    }
src/main/java/com/xcong/excoin/modules/okxNewPrice/gridWs/OkxAlgoOrdersChannelHandler.java
@@ -18,10 +18,12 @@
    private static final String CHANNEL_NAME = "orders-algo";
    private final String instId;
    private final String instFamily;
    private final OkxGridTradeService gridTradeService;
    public OkxAlgoOrdersChannelHandler(String instId, OkxGridTradeService gridTradeService) {
        this.instId = instId;
        this.instFamily = instId.contains("-") ? instId.substring(0, instId.lastIndexOf("-")) : instId;
        this.gridTradeService = gridTradeService;
    }
@@ -34,12 +36,13 @@
        JSONObject arg = new JSONObject();
        arg.put("channel", CHANNEL_NAME);
        arg.put("instType", "SWAP");
        arg.put("instFamily", instFamily);
        msg.put("op", "subscribe");
        JSONArray args = new JSONArray();
        args.add(arg);
        msg.put("args", args);
        ws.send(msg.toJSONString());
        log.info("[OKX-WS] {} 订阅成功", CHANNEL_NAME);
        log.info("[OKX-WS] {} 订阅成功, instFamily:{}", CHANNEL_NAME, instFamily);
    }
    @Override
@@ -48,6 +51,7 @@
        JSONObject arg = new JSONObject();
        arg.put("channel", CHANNEL_NAME);
        arg.put("instType", "SWAP");
        arg.put("instFamily", instFamily);
        msg.put("op", "unsubscribe");
        JSONArray args = new JSONArray();
        args.add(arg);
src/main/java/com/xcong/excoin/modules/okxNewPrice/gridWs/OkxPositionsChannelHandler.java
@@ -20,10 +20,12 @@
    private static final String CHANNEL_NAME = "positions";
    private final String instId;
    private final String instFamily;
    private final OkxGridTradeService gridTradeService;
    public OkxPositionsChannelHandler(String instId, OkxGridTradeService gridTradeService) {
        this.instId = instId;
        this.instFamily = instId.contains("-") ? instId.substring(0, instId.lastIndexOf("-")) : instId;
        this.gridTradeService = gridTradeService;
    }
@@ -36,12 +38,13 @@
        JSONObject arg = new JSONObject();
        arg.put("channel", CHANNEL_NAME);
        arg.put("instType", "SWAP");
        arg.put("instFamily", instFamily);
        msg.put("op", "subscribe");
        JSONArray args = new JSONArray();
        args.add(arg);
        msg.put("args", args);
        ws.send(msg.toJSONString());
        log.info("[OKX-WS] {} 订阅成功", CHANNEL_NAME);
        log.info("[OKX-WS] {} 订阅成功, instFamily:{}", CHANNEL_NAME, instFamily);
    }
    @Override
@@ -50,6 +53,7 @@
        JSONObject arg = new JSONObject();
        arg.put("channel", CHANNEL_NAME);
        arg.put("instType", "SWAP");
        arg.put("instFamily", instFamily);
        msg.put("op", "unsubscribe");
        JSONArray args = new JSONArray();
        args.add(arg);