Administrator
2026-05-28 b7a2fa0ea40b15bd5b60d737cb96764884dcaaaa
src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
@@ -1,6 +1,8 @@
package com.xcong.excoin.modules.gateApi;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.xcong.excoin.utils.dingtalk.DingTalkUtils;
import io.gate.gateapi.ApiClient;
import io.gate.gateapi.ApiException;
import io.gate.gateapi.GateApiException;
@@ -426,35 +428,8 @@
                    tryGenerateQueues();
                }else {
                    longPositionSize = size;
                    //取消多仓位线以上的开空仓挂单
                    List<GridElement> allShortOrders = GridElement.findAllShortOrders(longEntryPrice);
                    if (CollUtil.isNotEmpty(allShortOrders)){
                        for (GridElement e : allShortOrders) {
                            executor.cancelConditionalOrder(
                                    e.getShortOrderId(),
                                    orderId -> {
                                        shortEntryTraderIdParam(
                                                e,
                                                null,
                                                false
                                        );
                                    }
                            );
                            if (e.getShortTakeProfitOrderId() != null){
                                executor.cancelConditionalOrder(
                                        e.getShortTakeProfitOrderId(),
                                        orderId -> {
                                            shortTakeProfitTraderIdParam(
                                                    e,
                                                    null,
                                                    false
                                            );
                                        }
                                );
                            }
                        }
                    }
                    checkShortEntryOrderToCancel();
                    checkLongEntryOrderToCancel();
                }
            } else {
                longActive = false;
@@ -472,38 +447,85 @@
                    tryGenerateQueues();
                }else {
                    shortPositionSize = size.abs();
                    //取消空仓仓位线以下的开多仓挂单
                    List<GridElement> allLongOrders = GridElement.findAllLongOrders(shortEntryPrice);
                    if (CollUtil.isNotEmpty(allLongOrders)){
                        for (GridElement e : allLongOrders) {
                            executor.cancelConditionalOrder(
                                    e.getLongOrderId(),
                                    orderId -> {
                                        longEntryTraderIdParam(
                                                e,
                                                null,
                                                false
                                        );
                                    }
                            );
                            if (e.getLongTakeProfitOrderId() != null){
                                executor.cancelConditionalOrder(
                                        e.getLongTakeProfitOrderId(),
                                        orderId -> {
                                            longTakeProfitTraderIdParam(
                                                    e,
                                                    null,
                                                    false
                                            );
                                        }
                                );
                            }
                        }
                    }
                    checkShortEntryOrderToCancel();
                    checkLongEntryOrderToCancel();
                }
            } else {
                shortActive = false;
                shortPositionSize = BigDecimal.ZERO;
            }
        }
    }
    private void checkShortEntryOrderToCancel() {
        List<GridElement> allLongOrders = GridElement.findAllShortOrders(shortEntryPrice);
        if (CollUtil.isNotEmpty(allLongOrders)){
            GridElement keep = allLongOrders.stream()
                    .min((a, b) -> a.getGridPrice().compareTo(b.getGridPrice()))
                    .orElse(null);
            for (GridElement e : allLongOrders) {
                if (e == keep) {
                    continue;
                }
                executor.cancelConditionalOrder(
                        e.getShortOrderId(),
                        orderId -> {
                            shortEntryTraderIdParam(
                                    e,
                                    null,
                                    false
                            );
                        }
                );
                if (e.getShortTakeProfitOrderId() != null){
                    executor.cancelConditionalOrder(
                            e.getShortTakeProfitOrderId(),
                            orderId -> {
                                shortTakeProfitTraderIdParam(
                                        e,
                                        null,
                                        false
                                );
                            }
                    );
                }
            }
        }
    }
    private void checkLongEntryOrderToCancel() {
        List<GridElement> allShortOrders = GridElement.findAllLongOrders(longEntryPrice);
        if (CollUtil.isNotEmpty(allShortOrders)){
            GridElement keep = allShortOrders.stream()
                    .max((a, b) -> a.getGridPrice().compareTo(b.getGridPrice()))
                    .orElse(null);
            for (GridElement e : allShortOrders) {
                if (e == keep) {
                    continue;
                }
                executor.cancelConditionalOrder(
                        e.getLongOrderId(),
                        orderId -> {
                            longEntryTraderIdParam(
                                    e,
                                    null,
                                    false
                            );
                        }
                );
                if (e.getLongTakeProfitOrderId() != null){
                    executor.cancelConditionalOrder(
                            e.getLongTakeProfitOrderId(),
                            orderId -> {
                                longTakeProfitTraderIdParam(
                                        e,
                                        null,
                                        false
                                );
                            }
                    );
                }
            }
        }
    }
@@ -526,6 +548,7 @@
            return;
        }
        cumulativePnl = cumulativePnl.add(pnl);
        updateUnrealizedPnl();
        BigDecimal totalPnl = cumulativePnl.add(unrealizedPnl);
        log.info("[Gate] 已实现:{}, 未实现:{}, 合计:{}",
                cumulativePnl, unrealizedPnl, totalPnl);
@@ -535,9 +558,13 @@
                    totalPnl, cumulativePnl, unrealizedPnl);
            state = StrategyState.STOPPED;
        } else if (totalPnl.compareTo(config.getMaxLoss().negate()) <= 0) {
            log.info("[Gate] 已达亏损上限(合计{})→已停止, 已实现:{}, 未实现:{}",
            String logMessage = StrUtil.format("[Gate] 已达亏损风险值(合计{}), 已实现:{}, 未实现:{}",
                    totalPnl, cumulativePnl, unrealizedPnl);
            state = StrategyState.STOPPED;
            log.info(logMessage);
            DingTalkUtils.getDefault().sendActionCard("风险提醒", logMessage, config.getApiKey(), "");
//            state = StrategyState.STOPPED;
        }
    }
@@ -692,22 +719,6 @@
        /**
         * 匹配止盈单止盈
         */
        GridElement byLongTakeProfitOrderId = GridElement.findByLongTakeProfitOrderId(orderId);
        if (byLongTakeProfitOrderId != null){
            longTakeProfitTraderIdParam(
                    byLongTakeProfitOrderId,
                    null,
                    false
            );
            longEntryTraderIdParam(
                    byLongTakeProfitOrderId,
                    null,
                    false
            );
            Integer downId = byLongTakeProfitOrderId.getDownId();
            GridElement shortGridElement = GridElement.findById(downId);
            onUserTradeShortEntry(shortGridElement);
        }
        GridElement byShortTakeProfitOrderId = GridElement.findByShortTakeProfitOrderId(orderId);
        if (byShortTakeProfitOrderId != null){
            shortTakeProfitTraderIdParam(
@@ -720,39 +731,27 @@
                    null,
                    false
            );
            Integer upId = byShortTakeProfitOrderId.getUpId();
            GridElement longGridElement = GridElement.findById(upId);
            onUserTradeLongEntry(longGridElement);
//            TPonUserTradeShortEntry(byShortTakeProfitOrderId);
        }
        GridElement byLongTakeProfitOrderId = GridElement.findByLongTakeProfitOrderId(orderId);
        if (byLongTakeProfitOrderId != null){
            longTakeProfitTraderIdParam(
                    byLongTakeProfitOrderId,
                    null,
                    false
            );
            longEntryTraderIdParam(
                    byLongTakeProfitOrderId,
                    null,
                    false
            );
//            TPonUserTradeLongEntry(byLongTakeProfitOrderId);
        }
        /**
         * 匹配挂单
         */
        GridElement longGridElement = GridElement.findByLongOrderId(orderId);
        if (longGridElement != null) {
            if (longGridElement.isHasLongOrder() && !tradeId.equals("0")){
                onUserTradeLongEntry(longGridElement);
                if (longGridElement.getLongTakeProfitOrderId() == null){
                    BigDecimal longTp = longGridElement.getLongTraderParam().getTakeProfitPrice();
                    if (longTp != null) {
                        executor.placeTakeProfit(longTp,
                                FuturesPriceTrigger.RuleEnum.NUMBER_1,
                                ORDER_TYPE_CLOSE_LONG,
                                negate(config.getQuantity()),
                                (profitId) -> {
                                    longTakeProfitTraderIdParam(
                                            longGridElement,
                                            profitId,
                                            true
                                    );
                                });
                        log.info("[Gate] 多单成交匹配止盈, orderId:{}, 止盈价:{}, size:{}", orderId, longTp, negate(config.getQuantity()));
                        return;
                    }
                }
            }
        }
        GridElement shortGridElement = GridElement.findByShortOrderId(orderId);
        if (shortGridElement != null) {
            if (shortGridElement.isHasShortOrder() && !tradeId.equals("0")){
@@ -777,9 +776,33 @@
                }
            }
        }
        GridElement longGridElement = GridElement.findByLongOrderId(orderId);
        if (longGridElement != null) {
            if (longGridElement.isHasLongOrder() && !tradeId.equals("0")){
                onUserTradeLongEntry(longGridElement);
                if (longGridElement.getLongTakeProfitOrderId() == null){
                    BigDecimal longTp = longGridElement.getLongTraderParam().getTakeProfitPrice();
                    if (longTp != null) {
                        executor.placeTakeProfit(longTp,
                                FuturesPriceTrigger.RuleEnum.NUMBER_1,
                                ORDER_TYPE_CLOSE_LONG,
                                negate(config.getQuantity()),
                                (profitId) -> {
                                    longTakeProfitTraderIdParam(
                                            longGridElement,
                                            profitId,
                                            true
                                    );
                                });
                        log.info("[Gate] 多单成交匹配止盈, orderId:{}, 止盈价:{}, size:{}", orderId, longTp, negate(config.getQuantity()));
                    }
                }
            }
        }
    }
    private void onUserTradeShortEntry(GridElement gridElement) {
    private void TPonUserTradeShortEntry(GridElement gridElement) {
        if (!isMarginSafe()) {
            log.warn("[Gate] 保证金超限,跳过挂条件单");
        } else {
@@ -791,6 +814,80 @@
             *      前进方向挂空仓条件单
             *      后置方向挂多空条件单
             */
            //下一个开仓位置
            BigDecimal gridPrice = gridElement.getGridPrice();
            // 判断网格是否能开空仓,如果不能则跳过
            if (gridElement != null) {
                TraderParam downShortTraderParam = gridElement.getShortTraderParam();
                if (
                        !gridElement.isHasShortOrder() &&
                                gridPrice.compareTo(longEntryPrice) <= 0 &&
                                gridPrice.compareTo(shortEntryPrice) >= 0
                ){
                    placeEntryOrderWithPreFlag(gridElement, false,
                            downShortTraderParam.getEntryPrice(),
                            FuturesPriceTrigger.RuleEnum.NUMBER_1,
                            negate(downShortTraderParam.getQuantity()));
                }
                TraderParam downLongTraderParam = gridElement.getLongTraderParam();
                if (
                        !gridElement.isHasLongOrder() &&
                                gridPrice.compareTo(longEntryPrice) <= 0
                ){
                    placeEntryOrderWithPreFlag(gridElement, true,
                            downLongTraderParam.getEntryPrice(),
                            FuturesPriceTrigger.RuleEnum.NUMBER_1,
                            downLongTraderParam.getQuantity());
                }
            }
        }
    }
    private void TPonUserTradeLongEntry(GridElement gridElement) {
        if (!isMarginSafe()) {
            log.warn("[Gate] 保证金超限,跳过挂条件单");
        } else {
            BigDecimal newLongFirst = gridElement.getGridPrice() ;
            // 判断网格是否能开多空仓,如果不能则跳过
            if (gridElement != null) {
//                TraderParam downLongTraderParam = gridElement.getLongTraderParam();
//                if (
//                        !gridElement.isHasLongOrder() &&
//                                newLongFirst.compareTo(shortEntryPrice) >= 0 &&
//                                newLongFirst.compareTo(longEntryPrice) <= 0
//                ){
//                    placeEntryOrderWithPreFlag(gridElement, true,
//                            downLongTraderParam.getEntryPrice(),
//                            FuturesPriceTrigger.RuleEnum.NUMBER_2,
//                            config.getQuantity());
//
//                }
                TraderParam shortTraderParam = gridElement.getShortTraderParam();
                if (
                        !gridElement.isHasShortOrder() &&
                                newLongFirst.compareTo(shortEntryPrice) >= 0
                ){
                    placeEntryOrderWithPreFlag(gridElement, false,
                            shortTraderParam.getEntryPrice(),
                            FuturesPriceTrigger.RuleEnum.NUMBER_2,
                            negate(config.getQuantity()));
                }
            }
        }
    }
    private void onUserTradeShortEntry(GridElement gridElement) {
        if (!isMarginSafe()) {
            log.warn("[Gate] 保证金超限,跳过挂条件单");
        } else {
            //下一个开仓位置
            GridElement UpGridElement = GridElement.findById(gridElement.getDownId());
            BigDecimal newLongFirst = UpGridElement.getGridPrice();
@@ -806,36 +903,6 @@
                            FuturesPriceTrigger.RuleEnum.NUMBER_2,
                            negate(upShortTraderParam.getQuantity()));
                }
                int i = gridElement.getUpId();
                GridElement downGridElement = GridElement.findById(i);
                if (downGridElement != null){
                    BigDecimal downGridPrice = downGridElement.getGridPrice();
                    TraderParam downShortTraderParam = downGridElement.getShortTraderParam();
                    if (
                            !downGridElement.isHasShortOrder() &&
                                    downGridPrice.compareTo(longEntryPrice) <= 0 &&
                                    downGridPrice.compareTo(shortEntryPrice) >= 0
                    ){
                        placeEntryOrderWithPreFlag(downGridElement, false,
                                downShortTraderParam.getEntryPrice(),
                                FuturesPriceTrigger.RuleEnum.NUMBER_1,
                                negate(downShortTraderParam.getQuantity()));
                    }
                    TraderParam downLongTraderParam = downGridElement.getLongTraderParam();
                    if (
                            !downGridElement.isHasLongOrder() &&
                                    downGridPrice.compareTo(longEntryPrice) <= 0
                    ){
                        placeEntryOrderWithPreFlag(downGridElement, true,
                                downLongTraderParam.getEntryPrice(),
                                FuturesPriceTrigger.RuleEnum.NUMBER_1,
                                downLongTraderParam.getQuantity());
                    }
                }
            }
        }
    }
@@ -844,14 +911,6 @@
        if (!isMarginSafe()) {
            log.warn("[Gate] 保证金超限,跳过挂条件单");
        } else {
            /**
             * 下一个开仓位置
             *      获取队列第一个元素的价格对应的网格
             *      判断网格是否能开多仓,如果不能则跳过
             *      前进方向挂多仓条件单
             *      后置方向挂多空条件单
             */
            //下一个开仓位置
            GridElement UpGridElement = GridElement.findById(gridElement.getUpId());
            BigDecimal newLongFirst = UpGridElement.getGridPrice() ;
@@ -865,38 +924,6 @@
                            upLongTraderParam.getEntryPrice(),
                            FuturesPriceTrigger.RuleEnum.NUMBER_1,
                            config.getQuantity());
                }
                int i = gridElement.getDownId();
                GridElement downGridElement = GridElement.findById(i);
                if (downGridElement != null){
                    BigDecimal downGridPrice = downGridElement.getGridPrice();
                    TraderParam downLongTraderParam = downGridElement.getLongTraderParam();
                    if (
                            !downGridElement.isHasLongOrder() &&
                                    downGridPrice.compareTo(shortEntryPrice) >= 0 &&
                                    downGridPrice.compareTo(longEntryPrice) <= 0
                    ){
                        placeEntryOrderWithPreFlag(downGridElement, true,
                                downLongTraderParam.getEntryPrice(),
                                FuturesPriceTrigger.RuleEnum.NUMBER_2,
                                config.getQuantity());
                    }
                    TraderParam shortTraderParam = downGridElement.getShortTraderParam();
                    if (
                            !downGridElement.isHasShortOrder() &&
                                    downGridPrice.compareTo(shortEntryPrice) >= 0
                    ){
                        placeEntryOrderWithPreFlag(downGridElement, false,
                                shortTraderParam.getEntryPrice(),
                                FuturesPriceTrigger.RuleEnum.NUMBER_2,
                                negate(config.getQuantity()));
                    }
                }
            }
        }
@@ -1096,8 +1123,9 @@
        int longSize = longPriceQueue.size();
        //根据精度转换成小数
        int prec = config.getPriceScale();
        BigDecimal minTick = BigDecimal.ONE.scaleByPowerOfTen(-prec);
        BigDecimal step = config.getStep().subtract(minTick);
//        BigDecimal minTick = BigDecimal.ONE.scaleByPowerOfTen(-prec);
//        BigDecimal step = config.getStep().subtract(minTick);
        BigDecimal step = config.getStep();
        String qty = config.getQuantity();
        // 空仓队列:id 从 -1 自减, shortPriceQueue[i] → id=-(i+1)
@@ -1244,32 +1272,32 @@
            // 判断网格是否能开空仓,如果不能则跳过
            if (UpGridElement != null) {
                if (!UpGridElement.isHasShortOrder() && shortEntryPrice.compareTo(newLongFirst) > 0) {
                    TraderParam upShortTraderParam = UpGridElement.getShortTraderParam();
                    placeEntryOrderWithPreFlag(UpGridElement, false,
                            upShortTraderParam.getEntryPrice(),
                            FuturesPriceTrigger.RuleEnum.NUMBER_2,
                            negate(upShortTraderParam.getQuantity()));
                }
//                if (!UpGridElement.isHasShortOrder() && shortEntryPrice.compareTo(newLongFirst) > 0) {
//
//                    TraderParam upShortTraderParam = UpGridElement.getShortTraderParam();
//                    placeEntryOrderWithPreFlag(UpGridElement, false,
//                            upShortTraderParam.getEntryPrice(),
//                            FuturesPriceTrigger.RuleEnum.NUMBER_2,
//                            negate(upShortTraderParam.getQuantity()));
//                }
                int i = UpGridElement.getId() + 2;
                GridElement downGridElement = GridElement.findById(i);
                if (downGridElement != null){
                    BigDecimal downGridPrice = downGridElement.getGridPrice();
                    TraderParam downShortTraderParam = downGridElement.getShortTraderParam();
                    if (
                            !downGridElement.isHasShortOrder() &&
                                    downGridPrice.compareTo(longEntryPrice) <= 0 &&
                                    downGridPrice.compareTo(shortEntryPrice) >= 0
                    ){
                        placeEntryOrderWithPreFlag(downGridElement, false,
                                downShortTraderParam.getEntryPrice(),
                                FuturesPriceTrigger.RuleEnum.NUMBER_1,
                                negate(downShortTraderParam.getQuantity()));
                    }
//                    TraderParam downShortTraderParam = downGridElement.getShortTraderParam();
//                    if (
//                            !downGridElement.isHasShortOrder() &&
//                                    downGridPrice.compareTo(longEntryPrice) <= 0 &&
//                                    downGridPrice.compareTo(shortEntryPrice) >= 0
//                    ){
//                        placeEntryOrderWithPreFlag(downGridElement, false,
//                                downShortTraderParam.getEntryPrice(),
//                                FuturesPriceTrigger.RuleEnum.NUMBER_1,
//                                negate(downShortTraderParam.getQuantity()));
//
//                    }
                    TraderParam downLongTraderParam = downGridElement.getLongTraderParam();
                    if (
@@ -1350,13 +1378,13 @@
            // 判断网格是否能开多仓,如果不能则跳过
            if (UpGridElement != null) {
                if (!UpGridElement.isHasLongOrder() && longEntryPrice.compareTo(newLongFirst) < 0) {
                    TraderParam upLongTraderParam = UpGridElement.getLongTraderParam();
                    placeEntryOrderWithPreFlag(UpGridElement, true,
                            upLongTraderParam.getEntryPrice(),
                            FuturesPriceTrigger.RuleEnum.NUMBER_1,
                            config.getQuantity());
                }
//                if (!UpGridElement.isHasLongOrder() && longEntryPrice.compareTo(newLongFirst) < 0) {
//                    TraderParam upLongTraderParam = UpGridElement.getLongTraderParam();
//                    placeEntryOrderWithPreFlag(UpGridElement, true,
//                            upLongTraderParam.getEntryPrice(),
//                            FuturesPriceTrigger.RuleEnum.NUMBER_1,
//                            config.getQuantity());
//                }
                int i = UpGridElement.getId() - 2;
                GridElement downGridElement = GridElement.findById(i);
@@ -1364,18 +1392,18 @@
                    BigDecimal downGridPrice = downGridElement.getGridPrice();
                    TraderParam downLongTraderParam = downGridElement.getLongTraderParam();
                    if (
                            !downGridElement.isHasLongOrder() &&
                                    downGridPrice.compareTo(shortEntryPrice) >= 0 &&
                                    downGridPrice.compareTo(longEntryPrice) <= 0
                    ){
                        placeEntryOrderWithPreFlag(downGridElement, true,
                                downLongTraderParam.getEntryPrice(),
                                FuturesPriceTrigger.RuleEnum.NUMBER_2,
                                config.getQuantity());
                    }
//                    TraderParam downLongTraderParam = downGridElement.getLongTraderParam();
//                    if (
//                            !downGridElement.isHasLongOrder() &&
//                                    downGridPrice.compareTo(shortEntryPrice) >= 0 &&
//                                    downGridPrice.compareTo(longEntryPrice) <= 0
//                    ){
//                        placeEntryOrderWithPreFlag(downGridElement, true,
//                                downLongTraderParam.getEntryPrice(),
//                                FuturesPriceTrigger.RuleEnum.NUMBER_2,
//                                config.getQuantity());
//
//                    }
                    TraderParam shortTraderParam = downGridElement.getShortTraderParam();
                    if (