From df818cff8343abd94068c226845ac207a72f8e70 Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Tue, 19 May 2026 14:23:14 +0800
Subject: [PATCH] ``` fix(gate): 修复网格交易中订单状态更新处理逻辑
---
src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java | 368 ++++++++++++++++++++++++++++++++--------------------
1 files changed, 228 insertions(+), 140 deletions(-)
diff --git a/src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java b/src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
index 4b4e83c..b1d7814 100644
--- a/src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
+++ b/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;
@@ -585,6 +600,125 @@
}
}
+ /**
+ * 用户私有成交回调。由 {@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);
+ if (!"finished".equals(status)) {
+ return;
+ }
+
+ /**
+ * 匹配止盈单止盈
+ */
+ GridElement byLongTakeProfitOrderId = GridElement.findByLongTakeProfitOrderId(orderId);
+ if (byLongTakeProfitOrderId != null){
+ longTakeProfitTraderIdParam(
+ byLongTakeProfitOrderId,
+ null,
+ false
+ );
+ longEntryTraderIdParam(
+ byLongTakeProfitOrderId,
+ null,
+ false
+ );
+ }
+ GridElement byShortTakeProfitOrderId = GridElement.findByShortTakeProfitOrderId(orderId);
+ if (byShortTakeProfitOrderId != null){
+ shortTakeProfitTraderIdParam(
+ byShortTakeProfitOrderId,
+ null,
+ false
+ );
+ shortEntryTraderIdParam(
+ byShortTakeProfitOrderId,
+ null,
+ false
+ );
+ }
+
+ /**
+ * 匹配挂单
+ */
+ GridElement longGridElement = GridElement.findByLongOrderId(orderId);
+ if (longGridElement != null) {
+ if (longGridElement.isHasLongOrder()){
+ 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()){
+ if (shortGridElement.getShortTakeProfitOrderId() == null){
+ BigDecimal shortTp = shortGridElement.getShortTraderParam().getTakeProfitPrice();
+ if (shortTp != null) {
+ executor.placeTakeProfit(shortTp,
+ FuturesPriceTrigger.RuleEnum.NUMBER_2,
+ ORDER_TYPE_CLOSE_SHORT,
+ config.getQuantity(),
+ (profitId) -> {
+ shortTakeProfitTraderIdParam(
+ shortGridElement,
+ profitId,
+ true
+ );
+ });
+ log.info("[Gate] 空单成交匹配止盈, orderId:{}, 止盈价:{}, size:{}", orderId, shortTp, config.getQuantity());
+ }
+ }
+ }
+ }
+ }
+
// ---- 网格队列处理 ----
/**
@@ -616,6 +750,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 +769,7 @@
//0位置的网格的空单止盈
TraderParam baseShortTraderParam = config.getBaseShortTraderParam();
baseGridElement.setShortOrderId(baseShortTraderParam.getEntryOrderId());
+ baseGridElement.setHasShortOrder(true);
BigDecimal downTakeProfitPrice = baseGridElement.getShortTraderParam().getTakeProfitPrice();
executor.placeTakeProfit(
downTakeProfitPrice,
@@ -882,36 +1018,12 @@
log.info("[Gate] 网格元素列表已构建, 共{}个元素 (空仓:{} 位置:0 多仓:{})", elements.size(), shortSize, longSize);
}
- /**
- * 空仓网格处理(当前价跌破空仓队列元素)。
- *
- * <h3>匹配规则</h3>
- * 遍历空仓队列(降序排列,大→小),收集所有大于当前价的元素为 matched。
- * 降序排列保证一旦遇到 price ≤ currentPrice 即可停止遍历。
- *
- * <h3>执行流程</h3>
- * <ol>
- * <li>匹配队列元素 → 为空则直接返回,不触发</li>
- * <li>空仓队列:移除 matched 元素,从尾部递减 step 补充等量新元素,重新降序排序</li>
- * <li>多仓队列:<b>不再更新</b>(队列转移逻辑已移除)</li>
- * <li>保证金检查 → 不安全则跳过挂单(队列照常更新),安全则继续</li>
- * <li>挂新空仓条件单(触发价 = newShortFirst,rule=NUMBER_2,止盈 = newShortFirst − step,
- * orderId → 止盈价存入 currentShortOrderIds)</li>
- * <li>多仓条件单守卫:newLongFirst = newShortFirst + step × 2,
- * 若 newLongFirst < longEntryPrice → 挂多仓条件单(止盈 = newLongFirst + step,
- * orderId → 止盈价存入 currentLongOrderIds)</li>
- * </ol>
- * 条件单成交后由 {@link #onOrderUpdate} 匹配止盈价并挂止盈条件单。
- * 反向条件单不再在此处理,改为在 {@link #onPositionUpdate} 仓位净减少时触发。
- *
- * @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;
@@ -984,91 +1096,66 @@
null
);
}
- }
+ 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
+ ){
+ executor.placeConditionalEntryOrder(
+ downShortTraderParam.getEntryPrice(),
+ FuturesPriceTrigger.RuleEnum.NUMBER_2,
+ negate(downShortTraderParam.getQuantity()),
+ orderId ->
+ {
+ shortEntryTraderIdParam(
+ downGridElement,
+ orderId,
+ true
+ );
+ },
+ null
+ );
- int i = UpGridElement.getId() + 2;
- GridElement downGridElement = GridElement.findById(i);
- if (downGridElement != null){
+ }
- TraderParam downLongTraderParam = downGridElement.getLongTraderParam();
- if (!downGridElement.isHasShortOrder()){
- executor.placeConditionalEntryOrder(
- downLongTraderParam.getEntryPrice(),
- FuturesPriceTrigger.RuleEnum.NUMBER_1,
- downLongTraderParam.getQuantity(),
- orderId ->
- {
- longEntryTraderIdParam(
- downGridElement,
- orderId,
- true
- );
- },
- null
- );
- }
-
- TraderParam downShortTraderParam = downGridElement.getShortTraderParam();
- BigDecimal downGridPrice = downGridElement.getGridPrice();
- if (
- !downGridElement.isHasShortOrder() &&
- downGridPrice.compareTo(longEntryPrice) <= 0 &&
- downGridPrice.compareTo(shortEntryPrice) >= 0
- ){
- executor.placeConditionalEntryOrder(
- downShortTraderParam.getEntryPrice(),
- FuturesPriceTrigger.RuleEnum.NUMBER_2,
- negate(downShortTraderParam.getQuantity()),
- orderId ->
- {
- shortEntryTraderIdParam(
- downGridElement,
- orderId,
- true
- );
- },
- null
- );
-
+ TraderParam downLongTraderParam = downGridElement.getLongTraderParam();
+ if (
+ !downGridElement.isHasLongOrder() &&
+ downGridPrice.compareTo(longEntryPrice) <= 0
+ ){
+ executor.placeConditionalEntryOrder(
+ downLongTraderParam.getEntryPrice(),
+ FuturesPriceTrigger.RuleEnum.NUMBER_1,
+ downLongTraderParam.getQuantity(),
+ orderId ->
+ {
+ longEntryTraderIdParam(
+ downGridElement,
+ orderId,
+ true
+ );
+ },
+ null
+ );
+ }
}
}
-
}
-
}
- /**
- * 多仓网格处理(当前价涨破多仓队列元素)。
- *
- * <h3>匹配规则</h3>
- * 遍历多仓队列(升序排列,小→大),收集所有小于当前价的元素为 matched。
- * 升序排列保证一旦遇到 price ≥ currentPrice 即可停止遍历。
- *
- * <h3>执行流程</h3>
- * <ol>
- * <li>匹配队列元素 → 为空则直接返回,不触发</li>
- * <li>多仓队列:移除 matched 元素,从尾部递增 step 补充等量新元素,重新升序排序</li>
- * <li>空仓队列:<b>不再更新</b>(队列转移逻辑已移除)</li>
- * <li>保证金检查 → 不安全则跳过挂单(队列照常更新),安全则继续</li>
- * <li>挂新多仓条件单(触发价 = newLongFirst,rule=NUMBER_1,止盈 = newLongFirst + step,
- * orderId → 止盈价存入 currentLongOrderIds)</li>
- * <li>空仓条件单守卫:newShortFirst = newLongFirst − step × 2,
- * 若 newShortFirst > shortEntryPrice → 挂空仓条件单(止盈 = newShortFirst − step,
- * orderId → 止盈价存入 currentShortOrderIds)</li>
- * </ol>
- * 条件单成交后由 {@link #onOrderUpdate} 匹配止盈价并挂止盈条件单。
- * 反向条件单不再在此处理,改为在 {@link #onPositionUpdate} 仓位净减少时触发。
- *
- * @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;
@@ -1145,59 +1232,60 @@
null
);
}
- }
+ int i = UpGridElement.getId() - 2;
+ GridElement downGridElement = GridElement.findById(i);
+ if (downGridElement != null){
+ BigDecimal downGridPrice = downGridElement.getGridPrice();
- int i = UpGridElement.getId() - 2;
- GridElement downGridElement = GridElement.findById(i);
- if (downGridElement != null){
+ TraderParam downLongTraderParam = downGridElement.getLongTraderParam();
+ if (
+ !downGridElement.isHasLongOrder() &&
+ downGridPrice.compareTo(shortEntryPrice) >= 0 &&
+ downGridPrice.compareTo(longEntryPrice) <= 0
+ ){
+ executor.placeConditionalEntryOrder(
+ downLongTraderParam.getEntryPrice(),
+ FuturesPriceTrigger.RuleEnum.NUMBER_1,
+ config.getQuantity(),
+ orderId ->
+ {
+ longEntryTraderIdParam(
+ downGridElement,
+ orderId,
+ true
+ );
+ },
+ null
+ );
- TraderParam shortTraderParam = downGridElement.getShortTraderParam();
- if (!downGridElement.isHasShortOrder()){
- executor.placeConditionalEntryOrder(
- shortTraderParam.getEntryPrice(),
- FuturesPriceTrigger.RuleEnum.NUMBER_2,
- negate(config.getQuantity()),
- orderId ->
- {
- shortEntryTraderIdParam(
- downGridElement,
- orderId,
- true
- );
- },
- null
- );
- }
+ }
- TraderParam downLongTraderParam = downGridElement.getLongTraderParam();
- BigDecimal downGridPrice = downGridElement.getGridPrice();
- if (
- !downGridElement.isHasLongOrder() &&
- downGridPrice.compareTo(longEntryPrice) <= 0 &&
- downGridPrice.compareTo(shortEntryPrice) >= 0
- ){
- executor.placeConditionalEntryOrder(
- downLongTraderParam.getEntryPrice(),
- FuturesPriceTrigger.RuleEnum.NUMBER_1,
- config.getQuantity(),
- orderId ->
- {
- longEntryTraderIdParam(
- downGridElement,
- orderId,
- true
- );
- },
- null
- );
+ TraderParam shortTraderParam = downGridElement.getShortTraderParam();
+ if (
+ !downGridElement.isHasShortOrder() &&
+ downGridPrice.compareTo(shortEntryPrice) >= 0
+ ){
+ executor.placeConditionalEntryOrder(
+ shortTraderParam.getEntryPrice(),
+ FuturesPriceTrigger.RuleEnum.NUMBER_2,
+ negate(config.getQuantity()),
+ orderId ->
+ {
+ shortEntryTraderIdParam(
+ downGridElement,
+ orderId,
+ true
+ );
+ },
+ null
+ );
+ }
}
}
-
}
-
}
// ---- 保证金安全阀 ----
--
Gitblit v1.9.1