Administrator
3 hours ago 26732af73222f09a011796810e34e2dafa3a58bb
refactor(gateApi): 优化网格交易逻辑和条件单管理

- 更新文档描述多仓和空仓止盈队列的处理方式
- 修改价格处理逻辑移除条件判断直接执行长短网格处理
- 添加反向仓位逻辑支持当价格在特定范围内且仓位数量未达上限时开反向单
- 优化多仓和空仓止盈队列的更新策略确保队列按规则排序
- 移除多余的价格队列同步代码简化并发处理
- 调整条件单守卫逻辑确保只有满足条件时才更新订单状态
- 修复保证金检查逻辑确保队列和止盈队列正常更新
- 重构反向条件单挂单逻辑避免重复执行和资源浪费
2 files modified
229 ■■■■■ changed files
src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java 223 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/gateApi/gateApi-logic.md 6 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
@@ -380,11 +380,8 @@
        if (state != StrategyState.ACTIVE) {
            return;
        }
        if (!longPriceQueue.isEmpty() && closePrice.compareTo(longPriceQueue.get(0)) > 0) {
            processLongGrid(closePrice);
        } else if (!shortPriceQueue.isEmpty() && closePrice.compareTo(shortPriceQueue.get(0)) < 0) {
            processShortGrid(closePrice);
        }
        processLongGrid(closePrice);
        processShortGrid(closePrice);
    }
    // ---- 仓位推送回调 ----
@@ -445,6 +442,20 @@
                                FuturesPriceTrigger.RuleEnum.NUMBER_1, ORDER_TYPE_CLOSE_LONG, negate(config.getQuantity()));
                        log.info("[Gate] 多单止盈已设, tp:{}, size:{}", tpPrice, negate(config.getQuantity()));
                    }
                }else if(size.compareTo(longPositionSize) < 0){
                    if (entryPrice.compareTo(shortEntryPrice) > 0
                            && entryPrice.compareTo(longEntryPrice) < 0
                            && shortPositionSize.compareTo(new BigDecimal("3")) < 0) {
                        executor.openShort(negate(config.getQuantity()), () -> {
                            log.info("[Gate] 反向空单");
                        }, null);
                        BigDecimal reverseShortTp = entryPrice.subtract(config.getStep()).setScale(1, RoundingMode.HALF_UP);
                        executor.placeTakeProfit(reverseShortTp,
                                FuturesPriceTrigger.RuleEnum.NUMBER_2, ORDER_TYPE_CLOSE_SHORT, config.getQuantity());
                        log.info("[Gate] 反向条件空单已挂, trigger:{}, size:{}, 止盈:{}", entryPrice, negate(config.getQuantity()), reverseShortTp);
                    }
                } else {
                    longPositionSize = size;
                }
@@ -480,6 +491,19 @@
                        executor.placeTakeProfit(tpPrice,
                                FuturesPriceTrigger.RuleEnum.NUMBER_2, ORDER_TYPE_CLOSE_SHORT, config.getQuantity());
                        log.info("[Gate] 空单止盈已设, tp:{}, size:{}", tpPrice, config.getQuantity());
                    }
                }else if(size.abs().compareTo(shortPositionSize) < 0){
                    if (entryPrice.compareTo(shortEntryPrice) > 0
                            && entryPrice.compareTo(longEntryPrice) < 0
                            && longPositionSize.compareTo(new BigDecimal("3")) < 0) {
                        executor.openLong(config.getQuantity(), () -> {
                            log.info("[Gate] 反向多单");
                        }, null);
                        BigDecimal reverseLongTp = entryPrice.add(config.getStep()).setScale(1, RoundingMode.HALF_UP);
                        executor.placeTakeProfit(reverseLongTp,
                                FuturesPriceTrigger.RuleEnum.NUMBER_1, ORDER_TYPE_CLOSE_LONG, negate(config.getQuantity()));
                        log.info("[Gate] 反向条件多单已挂, trigger:{}, size:{}, 止盈:{}", entryPrice, negate(config.getQuantity()), reverseLongTp);
                    }
                } else {
                    shortPositionSize = size.abs();
@@ -612,13 +636,16 @@
     *   <li>空仓队列:移除 matched 元素,从尾部最小值递减 step 补充等量新元素,重新降序排序</li>
     *   <li>多仓队列:以多仓首元素(最小价)为基准递减 step,生成 matched.size() 个新元素加入,
     *       升序排序,超限截尾</li>
     *   <li>空仓止盈队列:加入新空仓首元素 − step,降序排序</li>
     *   <li>保证金检查 → 不安全则跳过挂单(队列照常更新并返回),安全则继续</li>
     *   <li>取消所有旧多仓条件单(currentLongOrderIds),清空集合</li>
     *   <li>空仓止盈队列:始终加入新空仓首元素 − step(降序排序),不受守卫限制</li>
     *   <li>保证金检查 → 不安全则跳过挂单(队列和止盈队列照常更新),安全则继续</li>
     *   <li>挂新空仓条件单(触发价 = 新空仓首元素,rule=NUMBER_2 ≤触发价时开空,size=负)</li>
     *   <li>挂新多仓条件单(触发价 = 新多仓首元素,rule=NUMBER_1 ≥触发价时开多,size=正)</li>
     *   <li>反向开多判断:新空仓首元素 > shortEntryPrice 且 < longEntryPrice 且 longPositionSize < 3
     *       → 挂反向条件多单(触发价 = 新空仓首元素),止盈价 = 首元素 + step 加入多仓止盈队列</li>
     *   <li>多仓条件单守卫:newLongFirst < longEntryPrice 时才执行
     *       → 取消所有旧多仓条件单(currentLongOrderIds) → 清空集合 →
     *       多仓止盈队列加入 newLongFirst + step →
     *       挂新多仓条件单(触发价 = newLongFirst,rule=NUMBER_1 ≥触发价时开多,size=正);
     *       不满足时保持旧多仓条件单不变,也不更新多仓止盈队列</li>
     *   <li>反向开多判断:newShortFirst > shortEntryPrice 且 < longEntryPrice 且 longPositionSize < 3
     *       → 挂反向条件多单(触发价 = newShortFirst),止盈价 = newShortFirst + step 加入多仓止盈队列</li>
     * </ol>
     *
     * @param currentPrice 当前 K 线收盘价(最新成交价)
@@ -654,21 +681,6 @@
            log.info("[Gate] 现空队列:{}", shortPriceQueue);
        }
        synchronized (longPriceQueue) {
            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);
                longPriceQueue.add(elem);
                log.info("[Gate] 多队列增加:{}", elem);
            }
            longPriceQueue.sort(BigDecimal::compareTo);
            while (longPriceQueue.size() > config.getGridQueueSize()) {
                longPriceQueue.remove(longPriceQueue.size() - 1);
            }
            log.info("[Gate] 现多队列:{}", longPriceQueue);
        }
        BigDecimal newShortFirst = shortPriceQueue.get(0);
        BigDecimal step = config.getStep();
        BigDecimal stpElem = newShortFirst.subtract(step).setScale(1, RoundingMode.HALF_UP);
@@ -676,40 +688,69 @@
        shortTakeProfitQueue.sort((a, b) -> b.compareTo(a));
        log.info("[Gate] 空止盈队列增加:{}, 现止盈队列:{}", stpElem, shortTakeProfitQueue);
//        synchronized (longPriceQueue) {
//            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);
//                longPriceQueue.add(elem);
//                log.info("[Gate] 多队列增加:{}", elem);
//            }
//            longPriceQueue.sort(BigDecimal::compareTo);
//            while (longPriceQueue.size() > config.getGridQueueSize()) {
//                longPriceQueue.remove(longPriceQueue.size() - 1);
//            }
//            log.info("[Gate] 现多队列:{}", longPriceQueue);
//        }
        if (!isMarginSafe()) {
            log.warn("[Gate] 保证金超限,跳过挂条件单");
        } else {
//            synchronized (currentShortOrderIds) {
//                for (String id : currentShortOrderIds) {
//                    executor.cancelConditionalOrder(id);
//                }
//                currentShortOrderIds.clear();
//            }
            currentShortOrderIds.clear();
            executor.placeConditionalEntryOrder(newShortFirst,
                    FuturesPriceTrigger.RuleEnum.NUMBER_2, negate(config.getQuantity()),
                    orderId -> { currentShortOrderIds.add(orderId); log.info("[Gate] 新条件空单, id:{}, trigger:{}", orderId, newShortFirst); },
                    null);
            BigDecimal newLongFirst = longPriceQueue.get(0);
            BigDecimal newLongFirst = newShortFirst.add( step.multiply(new BigDecimal("2")));
            if (newLongFirst.compareTo(longEntryPrice) < 0) {
                synchronized (currentLongOrderIds) {
                    for (String id : currentLongOrderIds) {
                        executor.cancelConditionalOrder(id);
                    }
                    currentLongOrderIds.clear();
                }
//                synchronized (currentLongOrderIds) {
//                    for (String id : currentLongOrderIds) {
//                        executor.cancelConditionalOrder(id);
//                    }
//                    currentLongOrderIds.clear();
//                }
                currentLongOrderIds.clear();
                BigDecimal ltpElem = newLongFirst.add(step).setScale(1, RoundingMode.HALF_UP);
                longTakeProfitQueue.add(ltpElem);
                longTakeProfitQueue.sort(BigDecimal::compareTo);
                log.info("[Gate] 多止盈队列增加:{}, 现止盈队列:{}", ltpElem, longTakeProfitQueue);
                executor.placeConditionalEntryOrder(newLongFirst,
                        FuturesPriceTrigger.RuleEnum.NUMBER_1, config.getQuantity(),
                        orderId -> { currentLongOrderIds.add(orderId); log.info("[Gate] 新条件多单, id:{}, trigger:{}", orderId, newLongFirst); },
                        null);
            }
            if (newShortFirst.compareTo(shortEntryPrice) > 0
                    && newShortFirst.compareTo(longEntryPrice) < 0
                    && longPositionSize.compareTo(new BigDecimal("3")) < 0) {
                BigDecimal reverseLongTp = newShortFirst.add(step).setScale(1, RoundingMode.HALF_UP);
                longTakeProfitQueue.add(reverseLongTp);
                longTakeProfitQueue.sort(BigDecimal::compareTo);
                executor.placeConditionalEntryOrder(newShortFirst,
                        FuturesPriceTrigger.RuleEnum.NUMBER_1, config.getQuantity(),
                        orderId -> { currentLongOrderIds.add(orderId); },
                        null);
                log.info("[Gate] 反向条件多单已挂, trigger:{}, size:{}, 止盈:{}", newShortFirst, config.getQuantity(), reverseLongTp);
            }
//            if (newShortFirst.compareTo(shortEntryPrice) > 0
//                    && newShortFirst.compareTo(longEntryPrice) < 0
//                    && longPositionSize.compareTo(new BigDecimal("3")) < 0) {
//                BigDecimal reverseLongTp = newShortFirst.add(step).setScale(1, RoundingMode.HALF_UP);
//                longTakeProfitQueue.add(reverseLongTp);
//                longTakeProfitQueue.sort(BigDecimal::compareTo);
//                executor.placeConditionalEntryOrder(newShortFirst,
//                        FuturesPriceTrigger.RuleEnum.NUMBER_1, config.getQuantity(),
//                        orderId -> { currentLongOrderIds.add(orderId); },
//                        null);
//                log.info("[Gate] 反向条件多单已挂, trigger:{}, size:{}, 止盈:{}", newShortFirst, config.getQuantity(), reverseLongTp);
//            }
        }
    }
@@ -727,13 +768,14 @@
     *   <li>多仓队列:移除 matched 元素,从尾部最大值递增 step 补充等量新元素,重新升序排序</li>
     *   <li>空仓队列:以空仓首元素(最高价)为基准递增 step,生成 matched.size() 个新元素加入,
     *       降序排序,超限截尾</li>
     *   <li>多仓止盈队列:加入新多仓首元素 + step,升序排序</li>
     *   <li>保证金检查 → 不安全则跳过挂单(队列照常更新并返回),安全则继续</li>
     *   <li>多仓止盈队列:始终加入新多仓首元素 + step(升序排序),不受守卫限制</li>
     *   <li>保证金检查 → 不安全则跳过挂单(队列和止盈队列照常更新),安全则继续</li>
     *   <li>挂新多仓条件单(触发价 = 新多仓首元素,rule=NUMBER_1 ≥触发价时开多,size=正)</li>
     *   <li>空仓条件单守卫:newShortFirst > shortEntryPrice 时才执行
     *       → 取消所有旧空仓条件单(currentShortOrderIds) → 清空集合 →
     *       挂新空仓条件单(触发价 = 新空仓首元素,rule=NUMBER_2 ≤触发价时开空,size=负);
     *       不满足时保持旧空仓条件单不变</li>
     *       空仓止盈队列加入 newShortFirst − step →
     *       挂新空仓条件单(触发价 = newShortFirst,rule=NUMBER_2 ≤触发价时开空,size=负);
     *       不满足时保持旧空仓条件单不变,也不更新空仓止盈队列</li>
     *   <li>反向开空判断:newLongFirst > shortEntryPrice 且 < longEntryPrice 且 shortPositionSize < 3
     *       → 挂反向条件空单(触发价 = newLongFirst),止盈价 = newLongFirst − step 加入空仓止盈队列</li>
     * </ol>
@@ -772,21 +814,6 @@
            log.info("[Gate] 现多队列:{}", longPriceQueue);
        }
        synchronized (shortPriceQueue) {
            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);
                shortPriceQueue.add(elem);
                log.info("[Gate] 空队列增加:{}", elem);
            }
            shortPriceQueue.sort((a, b) -> b.compareTo(a));
            while (shortPriceQueue.size() > config.getGridQueueSize()) {
                shortPriceQueue.remove(shortPriceQueue.size() - 1);
            }
            log.info("[Gate] 现空队列:{}", shortPriceQueue);
        }
        BigDecimal newLongFirst = longPriceQueue.get(0);
        BigDecimal step = config.getStep();
        BigDecimal ltpElem = newLongFirst.add(step).setScale(1, RoundingMode.HALF_UP);
@@ -794,41 +821,71 @@
        longTakeProfitQueue.sort(BigDecimal::compareTo);
        log.info("[Gate] 多止盈队列增加:{}, 现止盈队列:{}", ltpElem, longTakeProfitQueue);
//        synchronized (shortPriceQueue) {
//            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);
//                shortPriceQueue.add(elem);
//                log.info("[Gate] 空队列增加:{}", elem);
//            }
//            shortPriceQueue.sort((a, b) -> b.compareTo(a));
//            while (shortPriceQueue.size() > config.getGridQueueSize()) {
//                shortPriceQueue.remove(shortPriceQueue.size() - 1);
//            }
//            log.info("[Gate] 现空队列:{}", shortPriceQueue);
//        }
        if (!isMarginSafe()) {
            log.warn("[Gate] 保证金超限,跳过挂条件单");
        } else {
//            synchronized (currentLongOrderIds) {
//                for (String id : currentLongOrderIds) {
//                    executor.cancelConditionalOrder(id);
//                }
//                currentLongOrderIds.clear();
//            }
            currentLongOrderIds.clear();
            executor.placeConditionalEntryOrder(newLongFirst,
                    FuturesPriceTrigger.RuleEnum.NUMBER_1, config.getQuantity(),
                    orderId -> { currentLongOrderIds.add(orderId); log.info("[Gate] 新条件多单, id:{}, trigger:{}", orderId, newLongFirst); },
                    null);
            BigDecimal newShortFirst = shortPriceQueue.get(0);
            BigDecimal newShortFirst = newLongFirst.subtract( step.multiply(new BigDecimal("2")));
            if (newShortFirst.compareTo(shortEntryPrice) > 0){
                synchronized (currentShortOrderIds) {
                    for (String id : currentShortOrderIds) {
                        executor.cancelConditionalOrder(id);
                    }
                    currentShortOrderIds.clear();
                }
//                synchronized (currentShortOrderIds) {
//                    for (String id : currentShortOrderIds) {
//                        executor.cancelConditionalOrder(id);
//                    }
//                    currentShortOrderIds.clear();
//                }
                currentShortOrderIds.clear();
                BigDecimal stpElem = newShortFirst.subtract(step).setScale(1, RoundingMode.HALF_UP);
                shortTakeProfitQueue.add(stpElem);
                shortTakeProfitQueue.sort((a, b) -> b.compareTo(a));
                log.info("[Gate] 空止盈队列增加:{}, 现止盈队列:{}", stpElem, shortTakeProfitQueue);
                executor.placeConditionalEntryOrder(newShortFirst,
                        FuturesPriceTrigger.RuleEnum.NUMBER_2, negate(config.getQuantity()),
                        orderId -> { currentShortOrderIds.add(orderId); log.info("[Gate] 新条件空单, id:{}, trigger:{}", orderId, newShortFirst); },
                        null);
            }
            if (newLongFirst.compareTo(shortEntryPrice) > 0
                    && newLongFirst.compareTo(longEntryPrice) < 0
                    && shortPositionSize.compareTo(new BigDecimal("3")) < 0) {
                BigDecimal reverseShortTp = newLongFirst.subtract(step).setScale(1, RoundingMode.HALF_UP);
                shortTakeProfitQueue.add(reverseShortTp);
                shortTakeProfitQueue.sort((a, b) -> b.compareTo(a));
                executor.placeConditionalEntryOrder(newLongFirst,
                        FuturesPriceTrigger.RuleEnum.NUMBER_2, negate(config.getQuantity()),
                        orderId -> { currentShortOrderIds.add(orderId); },
                        null);
                log.info("[Gate] 反向条件空单已挂, trigger:{}, size:{}, 止盈:{}", newLongFirst, negate(config.getQuantity()), reverseShortTp);
            }
//            if (newLongFirst.compareTo(shortEntryPrice) > 0
//                    && newLongFirst.compareTo(longEntryPrice) < 0
//                    && shortPositionSize.compareTo(new BigDecimal("3")) < 0) {
//                BigDecimal reverseShortTp = newLongFirst.subtract(step).setScale(1, RoundingMode.HALF_UP);
//                shortTakeProfitQueue.add(reverseShortTp);
//                shortTakeProfitQueue.sort((a, b) -> b.compareTo(a));
//                executor.placeConditionalEntryOrder(newLongFirst,
//                        FuturesPriceTrigger.RuleEnum.NUMBER_2, negate(config.getQuantity()),
//                        orderId -> { currentShortOrderIds.add(orderId); },
//                        null);
//                log.info("[Gate] 反向条件空单已挂, trigger:{}, size:{}, 止盈:{}", newLongFirst, negate(config.getQuantity()), reverseShortTp);
//            }
        }
    }
src/main/java/com/xcong/excoin/modules/gateApi/gateApi-logic.md
@@ -249,8 +249,9 @@
│   │   │   │   ├─ 挂新多仓条件单(触发价=新 long[0], rule=NUMBER_1, size=+qty)
│   │   │   │   └─ 空仓条件单守卫: 新 short[0] > shortEntryPrice 时才执行
│   │   │   │       ├─ 取消所有旧空仓条件单(currentShortOrderIds 遍历取消,清空集合)
│   │   │   │       ├─ shortTakeProfitQueue.add(新 short[0] − step)
│   │   │   │       └─ 挂新空仓条件单(触发价=新 short[0], rule=NUMBER_2, size=-qty)
│   │   │   │       └─ 不满足 → 旧空仓条件单保持不动
│   │   │   │       └─ 不满足 → 旧空仓条件单+空仓止盈队列保持不动
│   │   │   └─ 超限 → 跳过挂单(队列和止盈队列照常更新)
│   └─ 反向条件单:
│       ├─ 条件: 新 long[0] > shortEntryPrice && 新 long[0] < longEntryPrice && shortPositionSize < 3
@@ -271,8 +272,9 @@
    │   │   │   ├─ 挂新空仓条件单(触发价=新 short[0], rule=NUMBER_2, size=-qty)
    │   │   │   └─ 多仓条件单守卫: 新 long[0] < longEntryPrice 时才执行
    │   │   │       ├─ 取消所有旧多仓条件单(currentLongOrderIds 遍历取消,清空集合)
    │   │   │       ├─ longTakeProfitQueue.add(新 long[0] + step)
    │   │   │       └─ 挂新多仓条件单(触发价=新 long[0], rule=NUMBER_1, size=+qty)
    │   │   │       └─ 不满足 → 旧多仓条件单保持不动
    │   │   │       └─ 不满足 → 旧多仓条件单+多仓止盈队列保持不动
    │   │   └─ 超限 → 跳过挂单(队列和止盈队列照常更新)
    └─ 反向条件单:
        ├─ 条件: 新 short[0] > shortEntryPrice && 新 short[0] < longEntryPrice && longPositionSize < 3