Administrator
2026-05-19 2af15077fb2e66500e955ffebc44ecad42e2dd66
src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
@@ -1,5 +1,6 @@
package com.xcong.excoin.modules.gateApi;
import cn.hutool.core.collection.CollUtil;
import io.gate.gateapi.ApiClient;
import io.gate.gateapi.ApiException;
import io.gate.gateapi.GateApiException;
@@ -432,6 +433,13 @@
                    tryGenerateQueues();
                }else {
                    longPositionSize = size;
                    //取消多仓位线以上的开空仓挂单
                    List<GridElement> allShortOrders = GridElement.findAllShortOrders(longEntryPrice);
                    if (CollUtil.isNotEmpty(allShortOrders)){
                        for (GridElement e : allShortOrders) {
                            executor.cancelOrder(e.getShortOrderId());
                        }
                    }
                }
            } else {
                longActive = false;
@@ -449,6 +457,13 @@
                    tryGenerateQueues();
                }else {
                    shortPositionSize = size.abs();
                    //取消多仓位线以上的开空仓挂单
                    List<GridElement> allLongOrders = GridElement.findAllLongOrders(shortEntryPrice);
                    if (CollUtil.isNotEmpty(allLongOrders)){
                        for (GridElement e : allLongOrders) {
                            executor.cancelOrder(e.getShortOrderId());
                        }
                    }
                }
            } else {
                shortActive = false;
@@ -524,12 +539,12 @@
        GridElement byShortTakeProfitOrderId = GridElement.findByShortTakeProfitOrderId(orderId);
        if (byShortTakeProfitOrderId != null){
            shortTakeProfitTraderIdParam(
                    byLongTakeProfitOrderId,
                    byShortTakeProfitOrderId,
                    null,
                    false
            );
            shortEntryTraderIdParam(
                    byLongTakeProfitOrderId,
                    byShortTakeProfitOrderId,
                    null,
                    false
            );
@@ -585,6 +600,43 @@
        }
    }
    /**
     * 用户私有成交回调。由 {@link com.xcong.excoin.modules.gateApi.wsHandler.handler.UserTradesChannelHandler}
     * 在收到 {@code futures.usertrades} 推送时调用。
     *
     * @param contract 合约名称
     * @param orderId  订单 ID
     * @param price    成交价格
     * @param size     成交数量
     * @param role     用户角色(maker / taker)
     * @param fee      手续费
     */
    public void onUserTrade(String contract, String orderId, BigDecimal price, String size, String role, BigDecimal fee) {
        if (state == StrategyState.STOPPED) {
            return;
        }
        log.info("[Gate] 成交明细, 合约:{}, 订单ID:{}, 价格:{}, 数量:{}, 角色:{}, 手续费:{}",
                contract, orderId, price, size, role, fee);
    }
    /**
     * 自动订单(条件单)状态变更回调。
     * 由 {@link com.xcong.excoin.modules.gateApi.wsHandler.handler.AutoOrdersChannelHandler}
     * 在收到 {@code futures.autoorders} 推送时调用。
     *
     * @param orderId   条件单 ID
     * @param status    订单状态(open / finished / cancelled)
     * @param reason    变更原因
     * @param orderType 订单类型(plan-close-long-position 等)
     */
    public void onAutoOrder(String orderId, String status, String reason, String orderType) {
        if (state == StrategyState.STOPPED) {
            return;
        }
        log.info("[Gate] 条件单状态变更, id:{}, status:{}, reason:{}, order_type:{}",
                orderId, status, reason, orderType);
    }
    // ---- 网格队列处理 ----
    /**
@@ -616,6 +668,7 @@
            GridElement baseGridElement = GridElement.findById(0);
            TraderParam baseLongTraderParam = config.getBaseLongTraderParam();
            baseGridElement.setLongOrderId(baseLongTraderParam.getEntryOrderId());
            baseGridElement.setHasLongOrder(true);
            //0位置的网格的多单止盈
            BigDecimal upTakeProfitPrice = baseGridElement.getLongTraderParam().getTakeProfitPrice();
            executor.placeTakeProfit(
@@ -634,6 +687,7 @@
            //0位置的网格的空单止盈
            TraderParam baseShortTraderParam = config.getBaseShortTraderParam();
            baseGridElement.setShortOrderId(baseShortTraderParam.getEntryOrderId());
            baseGridElement.setHasShortOrder(true);
            BigDecimal downTakeProfitPrice = baseGridElement.getShortTraderParam().getTakeProfitPrice();
            executor.placeTakeProfit(
                    downTakeProfitPrice,
@@ -740,12 +794,13 @@
     */
    private void generateShortQueue() {
        shortPriceQueue.clear();
        BigDecimal step = shortBaseEntryPrice.multiply(config.getGridRate()).setScale(1, RoundingMode.HALF_UP);
        int prec = config.getPriceScale();
        BigDecimal step = shortBaseEntryPrice.multiply(config.getGridRate()).setScale(prec, RoundingMode.HALF_UP);
        config.setStep(step);
        BigDecimal elem = shortBaseEntryPrice.subtract(step).setScale(1, RoundingMode.HALF_UP);
        BigDecimal elem = shortBaseEntryPrice.subtract(step).setScale(prec, RoundingMode.HALF_UP);
            for (int i = 0; i < config.getGridQueueSize(); i++) {
                shortPriceQueue.add(elem);
                elem = elem.subtract(step).setScale(1, RoundingMode.HALF_UP);
                elem = elem.subtract(step).setScale(prec, RoundingMode.HALF_UP);
                if (elem.compareTo(BigDecimal.ZERO) <= 0) {
                    break;
                }
@@ -761,11 +816,12 @@
     */
    private void generateLongQueue() {
        longPriceQueue.clear();
        int prec = config.getPriceScale();
        BigDecimal step = config.getStep();
        BigDecimal elem = shortBaseEntryPrice.add(step).setScale(1, RoundingMode.HALF_UP);
        BigDecimal elem = shortBaseEntryPrice.add(step).setScale(prec, RoundingMode.HALF_UP);
        for (int i = 0; i < config.getGridQueueSize(); i++) {
            longPriceQueue.add(elem);
            elem = elem.add(step).setScale(1, RoundingMode.HALF_UP);
            elem = elem.add(step).setScale(prec, RoundingMode.HALF_UP);
        }
        longPriceQueue.sort(BigDecimal::compareTo);
        log.info("[Gate] 多队列:{}", longPriceQueue);
@@ -789,7 +845,10 @@
        List<GridElement> elements = new ArrayList<>();
        int shortSize = shortPriceQueue.size();
        int longSize = longPriceQueue.size();
        BigDecimal step = config.getStep().subtract(config.getContractMultiplier());
        //根据精度转换成小数
        int prec = config.getPriceScale();
        BigDecimal minTick = BigDecimal.ONE.scaleByPowerOfTen(-prec);
        BigDecimal step = config.getStep().subtract(minTick);
        String qty = config.getQuantity();
        // 空仓队列:id 从 -1 自减, shortPriceQueue[i] → id=-(i+1)
@@ -801,13 +860,13 @@
            TraderParam longParam = TraderParam.builder()
                    .direction(TraderParam.Direction.LONG)
                    .entryPrice(price)
                    .takeProfitPrice(price.add(step).setScale(1, RoundingMode.HALF_UP))
                    .takeProfitPrice(price.add(step).setScale(prec, RoundingMode.HALF_UP))
                    .quantity(qty)
                    .build();
            TraderParam shortParam = TraderParam.builder()
                    .direction(TraderParam.Direction.SHORT)
                    .entryPrice(price)
                    .takeProfitPrice(price.subtract(step).setScale(1, RoundingMode.HALF_UP))
                    .takeProfitPrice(price.subtract(step).setScale(prec, RoundingMode.HALF_UP))
                    .quantity(qty)
                    .build();
            elements.add(GridElement.builder()
@@ -826,20 +885,20 @@
            TraderParam longParam = TraderParam.builder()
                    .direction(TraderParam.Direction.LONG)
                    .entryPrice(price)
                    .takeProfitPrice(price.add(step).setScale(1, RoundingMode.HALF_UP))
                    .takeProfitPrice(price.add(step).setScale(prec, RoundingMode.HALF_UP))
                    .quantity(qty)
                    .build();
            TraderParam shortParam = TraderParam.builder()
                    .direction(TraderParam.Direction.SHORT)
                    .entryPrice(price)
                    .takeProfitPrice(price.subtract(step).setScale(1, RoundingMode.HALF_UP))
                    .takeProfitPrice(price.subtract(step).setScale(prec, RoundingMode.HALF_UP))
                    .quantity(qty)
                    .build();
            elements.add(GridElement.builder()
                    .id(0)
                    .gridPrice(price)
                    .upId(shortSize > 0 ? -1 : null)
                    .downId(longSize > 0 ? 1 : null)
                    .upId(shortSize > 0 ? 1 : null)
                    .downId(longSize > 0 ? -1 : null)
                    .longTraderParam(longParam)
                    .shortTraderParam(shortParam)
                    .build());
@@ -848,19 +907,19 @@
        // 多仓队列:id 从 1 自增, longPriceQueue[i] → id=i+1
        for (int i = 0; i < longSize; i++) {
            int id = i + 1;
            Integer upId = (i == 0) ? 0 : id - 1;
            Integer downId = (i == longSize - 1) ? null : id + 1;
            Integer downId = (i == 0) ? 0 : id - 1;
            Integer upId = (i == longSize - 1) ? null : id + 1;
            BigDecimal price = longPriceQueue.get(i);
            TraderParam longParam = TraderParam.builder()
                    .direction(TraderParam.Direction.LONG)
                    .entryPrice(price)
                    .takeProfitPrice(price.add(step).setScale(1, RoundingMode.HALF_UP))
                    .takeProfitPrice(price.add(step).setScale(prec, RoundingMode.HALF_UP))
                    .quantity(qty)
                    .build();
            TraderParam shortParam = TraderParam.builder()
                    .direction(TraderParam.Direction.SHORT)
                    .entryPrice(price)
                    .takeProfitPrice(price.subtract(step).setScale(1, RoundingMode.HALF_UP))
                    .takeProfitPrice(price.subtract(step).setScale(prec, RoundingMode.HALF_UP))
                    .quantity(qty)
                    .build();
            elements.add(GridElement.builder()
@@ -902,10 +961,11 @@
     * @param currentPrice 当前 K 线收盘价(最新成交价)
     */
    private void processShortGrid(BigDecimal currentPrice) {
        int prec = config.getPriceScale();
        List<BigDecimal> matched = new ArrayList<>();
        synchronized (shortPriceQueue) {
            for (BigDecimal p : shortPriceQueue) {
                if (p.compareTo(currentPrice) > 0) {
                if (p.compareTo(currentPrice) >= 0) {
                    matched.add(p);
                } else {
                    break;
@@ -922,7 +982,7 @@
            BigDecimal min = shortPriceQueue.isEmpty() ? matched.get(matched.size() - 1) : shortPriceQueue.get(shortPriceQueue.size() - 1);
            BigDecimal gridStep = config.getStep();
            for (int i = 0; i < matched.size(); i++) {
                min = min.subtract(gridStep).setScale(1, RoundingMode.HALF_UP);
                min = min.subtract(gridStep).setScale(prec, RoundingMode.HALF_UP);
                shortPriceQueue.add(min);
            }
            shortPriceQueue.sort((a, b) -> b.compareTo(a));
@@ -932,7 +992,7 @@
            BigDecimal first = longPriceQueue.isEmpty() ? matched.get(matched.size() - 1) : longPriceQueue.get(0);
            BigDecimal gridStep = config.getStep();
            for (int i = 1; i <= matched.size(); i++) {
                BigDecimal elem = first.subtract(gridStep.multiply(BigDecimal.valueOf(i))).setScale(1, RoundingMode.HALF_UP);
                BigDecimal elem = first.subtract(gridStep.multiply(BigDecimal.valueOf(i))).setScale(prec, RoundingMode.HALF_UP);
                longPriceQueue.add(elem);
            }
            longPriceQueue.sort(BigDecimal::compareTo);
@@ -987,7 +1047,7 @@
            if (downGridElement != null){
                TraderParam downLongTraderParam = downGridElement.getLongTraderParam();
                if (!downGridElement.isHasShortOrder()){
                if (!downGridElement.isHasLongOrder()){
                    executor.placeConditionalEntryOrder(
                            downLongTraderParam.getEntryPrice(),
                            FuturesPriceTrigger.RuleEnum.NUMBER_1,
@@ -1008,6 +1068,7 @@
                BigDecimal downGridPrice = downGridElement.getGridPrice();
                if (
                        !downGridElement.isHasShortOrder() &&
                                downGridPrice.compareTo(currentPrice) < 0 &&
                                downGridPrice.compareTo(longEntryPrice) <= 0 &&
                                downGridPrice.compareTo(shortEntryPrice) >= 0
                ){
@@ -1058,10 +1119,11 @@
     * @param currentPrice 当前 K 线收盘价(最新成交价)
     */
    private void processLongGrid(BigDecimal currentPrice) {
        int prec = config.getPriceScale();
        List<BigDecimal> matched = new ArrayList<>();
        synchronized (longPriceQueue) {
            for (BigDecimal p : longPriceQueue) {
                if (p.compareTo(currentPrice) < 0) {
                if (p.compareTo(currentPrice) <= 0) {
                    matched.add(p);
                } else {
                    break;
@@ -1084,7 +1146,7 @@
            BigDecimal max = longPriceQueue.isEmpty() ? matched.get(matched.size() - 1) : longPriceQueue.get(longPriceQueue.size() - 1);
            BigDecimal gridStep = config.getStep();
            for (int i = 0; i < matched.size(); i++) {
                max = max.add(gridStep).setScale(1, RoundingMode.HALF_UP);
                max = max.add(gridStep).setScale(prec, RoundingMode.HALF_UP);
                longPriceQueue.add(max);
            }
            longPriceQueue.sort(BigDecimal::compareTo);
@@ -1093,7 +1155,7 @@
            BigDecimal first = shortPriceQueue.isEmpty() ? matched.get(0) : shortPriceQueue.get(0);
            BigDecimal gridStep = config.getStep();
            for (int i = 1; i <= matched.size(); i++) {
                BigDecimal elem = first.add(gridStep.multiply(BigDecimal.valueOf(i))).setScale(1, RoundingMode.HALF_UP);
                BigDecimal elem = first.add(gridStep.multiply(BigDecimal.valueOf(i))).setScale(prec, RoundingMode.HALF_UP);
                shortPriceQueue.add(elem);
            }
            shortPriceQueue.sort((a, b) -> b.compareTo(a));
@@ -1168,6 +1230,7 @@
                BigDecimal downGridPrice = downGridElement.getGridPrice();
                if (
                        !downGridElement.isHasLongOrder() &&
                                downGridPrice.compareTo(currentPrice) > 0 &&
                                downGridPrice.compareTo(longEntryPrice) <= 0 &&
                                downGridPrice.compareTo(shortEntryPrice) >= 0
                ){