From 150f6770c84fb46e35788d87ae8125b9ded936b3 Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Tue, 07 Jul 2026 16:12:16 +0800
Subject: [PATCH] feat(gateApi): 添加首次挂单成交逻辑处理和止盈单管理功能
---
src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java | 256 ++++++++++++++++++++++++++++++++++-----------------
1 files changed, 170 insertions(+), 86 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 137d05e..0975e60 100644
--- a/src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
+++ b/src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
@@ -138,6 +138,8 @@
private volatile int accumulatedLongLossCount = 0;
/** 空头累计止损张数(加仓订单成交后归零) */
private volatile int accumulatedShortLossCount = 0;
+ /** 是否首次挂单成交(初始化后重置为 true,首次成交后置 false) */
+ private volatile boolean firstFillAfterInit = true;
private volatile BigDecimal lastKlinePrice;
private volatile BigDecimal markPrice = BigDecimal.ZERO;
@@ -307,6 +309,7 @@
shortActive = false;
accumulatedLongLossCount = 0;
accumulatedShortLossCount = 0;
+ firstFillAfterInit = true;
shortPriceQueue.clear();
longPriceQueue.clear();
totalShortPriceQueue.clear();
@@ -628,6 +631,11 @@
if (shortGridElement.isHasShortOrder() && StrUtil.isNotEmpty(tradeId) && !tradeId.equals("0") ){
int filledQty = Integer.parseInt(shortGridElement.getShortTraderParam().getQuantity());
shortEntryTraderIdParam(shortGridElement, null, false);
+ // 首次挂单成交,取消初始化阶段挂的所有止盈单
+ if (firstFillAfterInit) {
+ cancelAllInitialTakeProfits();
+ firstFillAfterInit = false;
+ }
// [Gate-需求2] 加仓后先撤空仓所有止盈+止损,再查交易所持仓后重挂
cancelAllShortTakeProfitsAndStopLosses();
// REST 查询可能因交易所延迟返回旧值,与 WS 本地缓存取最大值兜底
@@ -635,49 +643,73 @@
extendShortStopLoss(posSize, shortGridElement.getId());
log.info("[Gate] 空单成交 gridId:{}, 当前持仓:{}张", filledQty, posSize);
- // 空仓持仓超过baseQuantity时,先找多仓第一个止损位置,从该位置向下挂止盈(间隔=1)
- BigDecimal shortBaseQty = new BigDecimal(config.getBaseQuantity());
- BigDecimal shortGridQty = new BigDecimal(config.getQuantity());
- if (BigDecimal.valueOf(posSize).compareTo(shortBaseQty) > 0) {
- BigDecimal shortExcess = BigDecimal.valueOf(posSize).subtract(shortBaseQty);
- int shortExcessCount = shortExcess.divide(shortGridQty, 0, RoundingMode.DOWN).intValue();
-
- // 找多仓第一个(最近的)止损位置
- int firstLongSlId = 0;
- for (GridElement e : config.getGridElements()) {
- if (e.getLongStopLossOrderId() != null) {
- if (firstLongSlId == 0 || e.getId() > firstLongSlId) {
- firstLongSlId = e.getId();
- }
- }
- }
-
- for (int i = 0; i < shortExcessCount; i++) {
- int tpGridId;
- if (firstLongSlId != 0) {
- tpGridId = firstLongSlId - i; // 从多仓第一个止损位置开始,向下挂,间隔=1
- } else {
- tpGridId = shortGridElement.getId() - 2 * (i + 1); // 无多仓止损时回退原逻辑
- }
- GridElement tpElem = GridElement.findById(tpGridId);
- if (tpElem == null || tpElem.getShortTakeProfitOrderId() != null) {
- continue;
- }
- BigDecimal tpPrice = tpElem.getGridPrice();
- int finalTpGridId = tpGridId;
- executor.placeTakeProfit(
- tpPrice,
- FuturesPriceTrigger.RuleEnum.NUMBER_2,
- ORDER_TYPE_CLOSE_SHORT,
- config.getQuantity(),
- profitId -> {
- shortTakeProfitTraderIdParam(tpElem, profitId, true);
- log.info("[Gate] 空仓止盈挂单, gridId:{}, 触发价:{}, takeProfitId:{}",
- finalTpGridId, tpPrice, profitId);
- }
- );
- }
+ int tpGridId = 0;
+ GridElement tpElem = GridElement.findById(tpGridId);
+ // 已有止盈单先取消再重挂
+ String existingShortTpId = tpElem.getShortTakeProfitOrderId();
+ if (existingShortTpId != null) {
+ executor.cancelConditionalOrder(existingShortTpId, oid -> {
+ shortTakeProfitTraderIdParam(tpElem, null, false);
+ log.info("[Gate] 空仓止盈取消(gridId:{}),准备重挂", tpGridId);
+ });
}
+ BigDecimal tpPrice = tpElem.getGridPrice();
+ int finalTpGridId = tpGridId;
+ executor.placeTakeProfit(
+ tpPrice,
+ FuturesPriceTrigger.RuleEnum.NUMBER_2,
+ ORDER_TYPE_CLOSE_SHORT,
+ config.getQuantity(),
+ profitId -> {
+ shortTakeProfitTraderIdParam(tpElem, profitId, true);
+ log.info("[Gate] 空仓止盈挂单, gridId:{}, 触发价:{}, takeProfitId:{}",
+ finalTpGridId, tpPrice, profitId);
+ }
+ );
+
+// // 空仓持仓超过baseQuantity时,先找多仓第一个止损位置,从该位置向下挂止盈(间隔=1)
+// BigDecimal shortBaseQty = new BigDecimal(config.getBaseQuantity());
+// BigDecimal shortGridQty = new BigDecimal(config.getQuantity());
+// if (BigDecimal.valueOf(posSize).compareTo(shortBaseQty) > 0) {
+// BigDecimal shortExcess = BigDecimal.valueOf(posSize).subtract(shortBaseQty);
+// int shortExcessCount = shortExcess.divide(shortGridQty, 0, RoundingMode.DOWN).intValue();
+//
+// // 找多仓第一个(最近的)止损位置
+// int firstLongSlId = 0;
+// for (GridElement e : config.getGridElements()) {
+// if (e.getLongStopLossOrderId() != null) {
+// if (firstLongSlId == 0 || e.getId() > firstLongSlId) {
+// firstLongSlId = e.getId();
+// }
+// }
+// }
+//
+// for (int i = 0; i < shortExcessCount; i++) {
+// int tpGridId;
+// if (firstLongSlId != 0) {
+// tpGridId = firstLongSlId - i; // 从多仓第一个止损位置开始,向下挂,间隔=1
+// } else {
+// tpGridId = shortGridElement.getId() - 2 * (i + 1); // 无多仓止损时回退原逻辑
+// }
+// GridElement tpElem = GridElement.findById(tpGridId);
+// if (tpElem == null || tpElem.getShortTakeProfitOrderId() != null) {
+// continue;
+// }
+// BigDecimal tpPrice = tpElem.getGridPrice();
+// int finalTpGridId = tpGridId;
+// executor.placeTakeProfit(
+// tpPrice,
+// FuturesPriceTrigger.RuleEnum.NUMBER_2,
+// ORDER_TYPE_CLOSE_SHORT,
+// config.getQuantity(),
+// profitId -> {
+// shortTakeProfitTraderIdParam(tpElem, profitId, true);
+// log.info("[Gate] 空仓止盈挂单, gridId:{}, 触发价:{}, takeProfitId:{}",
+// finalTpGridId, tpPrice, profitId);
+// }
+// );
+// }
+// }
}
}
GridElement longGridElement = GridElement.findByLongOrderId(orderId);
@@ -686,6 +718,11 @@
int filledQty = Integer.parseInt(longGridElement.getLongTraderParam().getQuantity());
longEntryTraderIdParam(longGridElement, null, false);
+ // 首次挂单成交,取消初始化阶段挂的所有止盈单
+ if (firstFillAfterInit) {
+ cancelAllInitialTakeProfits();
+ firstFillAfterInit = false;
+ }
// [Gate-需求2] 加仓后先撤多仓所有止盈+止损,再查交易所持仓后重挂
cancelAllLongTakeProfitsAndStopLosses();
// REST 查询可能因交易所延迟返回旧值,与 WS 本地缓存取最大值兜底
@@ -693,49 +730,73 @@
extendLongStopLoss(posSize, longGridElement.getId());
log.info("[Gate] 多单成交 gridId:{}, 当前持仓:{}张", filledQty, posSize);
- // 多仓持仓超过baseQuantity时,先找空仓第一个止损位置,从该位置向上挂止盈(间隔=1)
- BigDecimal longBaseQty = new BigDecimal(config.getBaseQuantity());
- BigDecimal longGridQty = new BigDecimal(config.getQuantity());
- if (BigDecimal.valueOf(posSize).compareTo(longBaseQty) > 0) {
- BigDecimal longExcess = BigDecimal.valueOf(posSize).subtract(longBaseQty);
- int longExcessCount = longExcess.divide(longGridQty, 0, RoundingMode.DOWN).intValue();
-
- // 找空仓第一个(最近的)止损位置
- int firstShortSlId = 0;
- for (GridElement e : config.getGridElements()) {
- if (e.getShortStopLossOrderId() != null) {
- if (firstShortSlId == 0 || e.getId() < firstShortSlId) {
- firstShortSlId = e.getId();
- }
- }
- }
-
- for (int i = 0; i < longExcessCount; i++) {
- int tpGridId;
- if (firstShortSlId != 0) {
- tpGridId = firstShortSlId + i; // 从空仓第一个止损位置开始,向上挂,间隔=1
- } else {
- tpGridId = longGridElement.getId() + 2 * (i + 1); // 无空仓止损时回退原逻辑
- }
- GridElement tpElem = GridElement.findById(tpGridId);
- if (tpElem == null || tpElem.getLongTakeProfitOrderId() != null) {
- continue;
- }
- BigDecimal tpPrice = tpElem.getGridPrice();
- int finalTpGridId = tpGridId;
- executor.placeTakeProfit(
- tpPrice,
- FuturesPriceTrigger.RuleEnum.NUMBER_1,
- ORDER_TYPE_CLOSE_LONG,
- negate(config.getQuantity()),
- profitId -> {
- longTakeProfitTraderIdParam(tpElem, profitId, true);
- log.info("[Gate] 多仓止盈挂单, gridId:{}, 触发价:{}, takeProfitId:{}",
- finalTpGridId, tpPrice, profitId);
- }
- );
- }
+ int tpGridId = 0;
+ GridElement tpElem = GridElement.findById(tpGridId);
+ // 已有止盈单先取消再重挂
+ String existingLongTpId = tpElem.getLongTakeProfitOrderId();
+ if (existingLongTpId != null) {
+ executor.cancelConditionalOrder(existingLongTpId, oid -> {
+ longTakeProfitTraderIdParam(tpElem, null, false);
+ log.info("[Gate] 多仓止盈取消(gridId:{}),准备重挂", tpGridId);
+ });
}
+ BigDecimal tpPrice = tpElem.getGridPrice();
+ int finalTpGridId = tpGridId;
+ executor.placeTakeProfit(
+ tpPrice,
+ FuturesPriceTrigger.RuleEnum.NUMBER_1,
+ ORDER_TYPE_CLOSE_LONG,
+ negate(config.getQuantity()),
+ profitId -> {
+ longTakeProfitTraderIdParam(tpElem, profitId, true);
+ log.info("[Gate] 多仓止盈挂单, gridId:{}, 触发价:{}, takeProfitId:{}",
+ finalTpGridId, tpPrice, profitId);
+ }
+ );
+
+// // 多仓持仓超过baseQuantity时,先找空仓第一个止损位置,从该位置向上挂止盈(间隔=1)
+// BigDecimal longBaseQty = new BigDecimal(config.getBaseQuantity());
+// BigDecimal longGridQty = new BigDecimal(config.getQuantity());
+// if (BigDecimal.valueOf(posSize).compareTo(longBaseQty) > 0) {
+// BigDecimal longExcess = BigDecimal.valueOf(posSize).subtract(longBaseQty);
+// int longExcessCount = longExcess.divide(longGridQty, 0, RoundingMode.DOWN).intValue();
+//
+// // 找空仓第一个(最近的)止损位置
+// int firstShortSlId = 0;
+// for (GridElement e : config.getGridElements()) {
+// if (e.getShortStopLossOrderId() != null) {
+// if (firstShortSlId == 0 || e.getId() < firstShortSlId) {
+// firstShortSlId = e.getId();
+// }
+// }
+// }
+//
+// for (int i = 0; i < longExcessCount; i++) {
+// int tpGridId;
+// if (firstShortSlId != 0) {
+// tpGridId = firstShortSlId + i; // 从空仓第一个止损位置开始,向上挂,间隔=1
+// } else {
+// tpGridId = longGridElement.getId() + 2 * (i + 1); // 无空仓止损时回退原逻辑
+// }
+// GridElement tpElem = GridElement.findById(tpGridId);
+// if (tpElem == null || tpElem.getLongTakeProfitOrderId() != null) {
+// continue;
+// }
+// BigDecimal tpPrice = tpElem.getGridPrice();
+// int finalTpGridId = tpGridId;
+// executor.placeTakeProfit(
+// tpPrice,
+// FuturesPriceTrigger.RuleEnum.NUMBER_1,
+// ORDER_TYPE_CLOSE_LONG,
+// negate(config.getQuantity()),
+// profitId -> {
+// longTakeProfitTraderIdParam(tpElem, profitId, true);
+// log.info("[Gate] 多仓止盈挂单, gridId:{}, 触发价:{}, takeProfitId:{}",
+// finalTpGridId, tpPrice, profitId);
+// }
+// );
+// }
+// }
}
}
}
@@ -820,7 +881,7 @@
int tpGridIdLong = 3;
GridElement tpElemLong = GridElement.findById(tpGridIdLong);
BigDecimal triggerPriceLong = tpElemLong.getGridPrice();
- String sizeLong = config.getQuantity();
+ String sizeLong = config.getBaseQuantity();
executor.placeTakeProfit(
triggerPriceLong,
FuturesPriceTrigger.RuleEnum.NUMBER_1,
@@ -839,7 +900,7 @@
int tpGridIdShort = -3;
GridElement tpElemShort = GridElement.findById(tpGridIdShort);
BigDecimal triggerPriceShort = tpElemShort.getGridPrice();
- String sizeShort = config.getQuantity();
+ String sizeShort = config.getBaseQuantity();
executor.placeTakeProfit(
triggerPriceShort,
FuturesPriceTrigger.RuleEnum.NUMBER_2,
@@ -1600,6 +1661,29 @@
log.info("[Gate] 已提交取消所有空仓止盈+止损");
}
+ /**
+ * 首次挂单成交时,取消初始化阶段挂的所有止盈单(多+空),止损单保留不动。
+ */
+ private void cancelAllInitialTakeProfits() {
+
+
+ GridElement tpElemLong = GridElement.findById(3);
+ String longTpId = tpElemLong.getLongTakeProfitOrderId();
+ if (longTpId != null) {
+ longTakeProfitTraderIdParam(tpElemLong, null, false);
+ executor.cancelConditionalOrder(longTpId, oid -> {});
+ }
+
+ GridElement tpElemShort = GridElement.findById(-3);
+ String shortTpId = tpElemShort.getShortTakeProfitOrderId();
+ if (shortTpId != null) {
+ shortTakeProfitTraderIdParam(tpElemShort, null, false);
+ executor.cancelConditionalOrder(shortTpId, oid -> {});
+ }
+ GridElement.refreshIndices();
+ log.info("[Gate] 首次成交,取消所有初始化止盈单");
+ }
+
// ========== 止损追单 ==========
private void extendLongStopLoss(int filledQty,int gridId) {
--
Gitblit v1.9.1