From 31085bebba69a02104078ee7414ad928638b9380 Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Wed, 12 Aug 2026 10:13:51 +0800
Subject: [PATCH] feat(gate): 添加超额止盈功能配置选项
---
src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java | 68 +++++++++++++++++++++++++++++++++-
1 files changed, 66 insertions(+), 2 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 59a62f1..8c90d42 100644
--- a/src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
+++ b/src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
@@ -672,7 +672,9 @@
int posSize = Math.max(queryPositionSize(Position.ModeEnum.DUAL_SHORT), shortPositionSize.intValue());
extendShortStopLoss(posSize, shortGridElement.getId());
// [Gate] 止盈挂单:超出基础仓位的部分,挂在多仓第一止损位
-// placeExcessTakeProfit(posSize, false);
+ if (config.isPlaceExcessTakeProfit()) {
+ placeExcessTakeProfit(posSize, false);
+ }
log.info("[Gate] 空单成交 gridId:{}, 当前持仓:{}张", filledQty, posSize);
@@ -703,7 +705,9 @@
int posSize = Math.max(queryPositionSize(Position.ModeEnum.DUAL_LONG), longPositionSize.intValue());
extendLongStopLoss(posSize, longGridElement.getId());
// [Gate] 止盈挂单:超出基础仓位的部分,挂在空仓第一止损位
-// placeExcessTakeProfit(posSize, true);
+ if (config.isPlaceExcessTakeProfit()) {
+ placeExcessTakeProfit(posSize, true);
+ }
log.info("[Gate] 多单成交 gridId:{}, 当前持仓:{}张", filledQty, posSize);
}
@@ -1986,6 +1990,66 @@
}
/**
+ * 挂单成交后,将超出基础仓位的部分挂止盈单,挂在对向仓位的第一止损位上。
+ *
+ * <p>遍历所有 GridElement,找到对向仓位第一个有止损单的网格作为止盈挂单位置。
+ *
+ * <p>例:空仓成交后持仓 8 张,基础 4 张 → 超出 4 张,
+ * 找到多仓第一止损位(如 gridId=-2)→ 在该位置挂空仓止盈单。
+ *
+ * @param posSize 当前总持仓张数
+ * @param isLong true=多仓成交,false=空仓成交
+ */
+ private void placeExcessTakeProfit(int posSize, boolean isLong) {
+ int baseQty = Integer.parseInt(config.getBaseQuantity());
+ int excessQty = posSize - baseQty;
+ if (excessQty <= 0) {
+ return;
+ }
+
+ // 遍历找到对向仓位第一个有止损单的网格
+ GridElement tpElem = isLong ? findFirstShortStopLossGrid() : findFirstLongStopLossGrid();
+ if (tpElem == null) {
+ log.warn("[Gate] {}止盈挂单失败:未找到对向仓止损位", isLong ? "多仓" : "空仓");
+ return;
+ }
+ int tpGridId = tpElem.getId();
+
+ BigDecimal triggerPrice = tpElem.getGridPrice();
+ String orderType = isLong ? ORDER_TYPE_CLOSE_LONG : ORDER_TYPE_CLOSE_SHORT;
+ // 多仓止盈:价格≥触发价时平仓(NUMBER_1);空仓止盈:价格≤触发价时平仓(NUMBER_2)
+ FuturesPriceTrigger.RuleEnum rule = isLong ? FuturesPriceTrigger.RuleEnum.NUMBER_1
+ : FuturesPriceTrigger.RuleEnum.NUMBER_2;
+ String size = isLong ? negate(String.valueOf(excessQty)) : String.valueOf(excessQty);
+
+// if (isLong && tpElem.getLongTakeProfitOrderId() != null) {
+// executor.cancelConditionalOrder(tpElem.getLongTakeProfitOrderId(), oid -> {
+// longTakeProfitTraderIdParam(tpElem, null, false);
+// log.info("[Gate] 取消旧止盈, gridId:{}, orderId:{}", tpGridId, oid);
+// });
+// } else if (!isLong && tpElem.getShortTakeProfitOrderId() != null) {
+// executor.cancelConditionalOrder(tpElem.getShortTakeProfitOrderId(), oid -> {
+// shortTakeProfitTraderIdParam(tpElem, null, false);
+// log.info("[Gate] 取消旧止盈, gridId:{}, orderId:{}", tpGridId, oid);
+// });
+// }
+
+ String finalSize = size;
+ int finalTpGridId = tpGridId;
+ executor.placeTakeProfit(triggerPrice, rule, orderType, size,
+ profitId -> {
+ if (isLong) {
+ longTakeProfitTraderIdParam(tpElem, profitId, true);
+ } else {
+ shortTakeProfitTraderIdParam(tpElem, profitId, true);
+ }
+ log.info("[Gate] {}止盈挂单, gridId:{}, 触发价:{}, 数量:{}, takeProfitId:{}",
+ isLong ? "多仓" : "空仓", finalTpGridId, triggerPrice, finalSize, profitId);
+ }
+ );
+ }
+
+ /**
* 在指定网格挂一笔对手止盈单(非满仓超额止盈,挂在止损触发位的下一格)。
*/
private void placeTakeProfitAtGrid(GridElement tpElem, boolean isLong, int qty, int times) {
--
Gitblit v1.9.1