From c846827b1024f3b6f5a33663f219827c67489d21 Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Fri, 22 May 2026 21:31:35 +0800
Subject: [PATCH] refactor(gateApi): 优化条件单挂单逻辑并解决竞态问题
---
src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java | 282 +++++++++++++++++++++++++++++++++++++-------------------
1 files changed, 187 insertions(+), 95 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 e66ecef..9ac5617 100644
--- a/src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
+++ b/src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
@@ -14,7 +14,6 @@
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -726,6 +725,8 @@
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) {
@@ -749,6 +750,8 @@
GridElement shortGridElement = GridElement.findByShortOrderId(orderId);
if (shortGridElement != null) {
if (shortGridElement.isHasShortOrder() && !tradeId.equals("0")){
+
+ onUserTradeShortEntry(shortGridElement);
if (shortGridElement.getShortTakeProfitOrderId() == null){
BigDecimal shortTp = shortGridElement.getShortTraderParam().getTakeProfitPrice();
if (shortTp != null) {
@@ -764,6 +767,129 @@
);
});
log.info("[Gate] 空单成交匹配止盈, orderId:{}, 止盈价:{}, size:{}", orderId, shortTp, config.getQuantity());
+ }
+ }
+ }
+ }
+ }
+
+ private void onUserTradeShortEntry(GridElement gridElement) {
+ if (!isMarginSafe()) {
+ log.warn("[Gate] 保证金超限,跳过挂条件单");
+ } else {
+
+ /**
+ * 下一个开仓位置
+ * 获取队列第一个元素的价格对应的网格
+ * 判断网格是否能开空仓,如果不能则跳过
+ * 前进方向挂空仓条件单
+ * 后置方向挂多空条件单
+ */
+ //下一个开仓位置
+ GridElement UpGridElement = GridElement.findById(gridElement.getDownId());
+ BigDecimal newLongFirst = UpGridElement.getGridPrice();
+
+ // 判断网格是否能开空仓,如果不能则跳过
+ 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()));
+ }
+ 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());
+ }
+ }
+ }
+ }
+ }
+
+ private void onUserTradeLongEntry(GridElement gridElement) {
+ if (!isMarginSafe()) {
+ log.warn("[Gate] 保证金超限,跳过挂条件单");
+ } else {
+
+ /**
+ * 下一个开仓位置
+ * 获取队列第一个元素的价格对应的网格
+ * 判断网格是否能开多仓,如果不能则跳过
+ * 前进方向挂多仓条件单
+ * 后置方向挂多空条件单
+ */
+ //下一个开仓位置
+ GridElement UpGridElement = GridElement.findById(gridElement.getUpId());
+ BigDecimal newLongFirst = UpGridElement.getGridPrice() ;
+
+ // 判断网格是否能开多仓,如果不能则跳过
+ 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());
+ }
+
+ 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()));
}
}
}
@@ -843,33 +969,17 @@
Integer upId = baseGridElement.getUpId();
GridElement upGridElementOne = GridElement.findById(upId);
BigDecimal longTp = upGridElementOne.getGridPrice();
- executor.placeConditionalEntryOrder(
+ placeEntryOrderWithPreFlag(upGridElementOne, true,
longTp,
FuturesPriceTrigger.RuleEnum.NUMBER_1,
- config.getQuantity(),
- orderId -> {
- longEntryTraderIdParam(
- upGridElementOne,
- orderId,
- true
- );
- },
- null);
+ config.getQuantity());
Integer downId = baseGridElement.getDownId();
GridElement downGridElementOne = GridElement.findById(downId);
BigDecimal shortTp = downGridElementOne.getGridPrice();
- executor.placeConditionalEntryOrder(
+ placeEntryOrderWithPreFlag(downGridElementOne, false,
shortTp,
FuturesPriceTrigger.RuleEnum.NUMBER_2,
- negate(config.getQuantity()),
- orderId -> {
- shortEntryTraderIdParam(
- downGridElementOne,
- orderId,
- true
- );
- },
- null);
+ negate(config.getQuantity()));
state = StrategyState.ACTIVE;
}
@@ -1130,22 +1240,11 @@
if (!UpGridElement.isHasShortOrder() && shortEntryPrice.compareTo(newLongFirst) > 0) {
- //挂空仓条件单
TraderParam upShortTraderParam = UpGridElement.getShortTraderParam();
- executor.placeConditionalEntryOrder(
+ placeEntryOrderWithPreFlag(UpGridElement, false,
upShortTraderParam.getEntryPrice(),
FuturesPriceTrigger.RuleEnum.NUMBER_2,
- negate(upShortTraderParam.getQuantity()),
- orderId ->
- {
- shortEntryTraderIdParam(
- UpGridElement,
- orderId,
- true
- );
- },
- null
- );
+ negate(upShortTraderParam.getQuantity()));
}
int i = UpGridElement.getId() + 2;
GridElement downGridElement = GridElement.findById(i);
@@ -1159,20 +1258,10 @@
downGridPrice.compareTo(longEntryPrice) <= 0 &&
downGridPrice.compareTo(shortEntryPrice) >= 0
){
- executor.placeConditionalEntryOrder(
+ placeEntryOrderWithPreFlag(downGridElement, false,
downShortTraderParam.getEntryPrice(),
FuturesPriceTrigger.RuleEnum.NUMBER_1,
- negate(downShortTraderParam.getQuantity()),
- orderId ->
- {
- shortEntryTraderIdParam(
- downGridElement,
- orderId,
- true
- );
- },
- null
- );
+ negate(downShortTraderParam.getQuantity()));
}
@@ -1181,20 +1270,10 @@
!downGridElement.isHasLongOrder() &&
downGridPrice.compareTo(longEntryPrice) <= 0
){
- executor.placeConditionalEntryOrder(
+ placeEntryOrderWithPreFlag(downGridElement, true,
downLongTraderParam.getEntryPrice(),
FuturesPriceTrigger.RuleEnum.NUMBER_1,
- downLongTraderParam.getQuantity(),
- orderId ->
- {
- longEntryTraderIdParam(
- downGridElement,
- orderId,
- true
- );
- },
- null
- );
+ downLongTraderParam.getQuantity());
}
}
}
@@ -1266,22 +1345,11 @@
if (UpGridElement != null) {
if (!UpGridElement.isHasLongOrder() && longEntryPrice.compareTo(newLongFirst) < 0) {
- //挂多仓条件单
TraderParam upLongTraderParam = UpGridElement.getLongTraderParam();
- executor.placeConditionalEntryOrder(
+ placeEntryOrderWithPreFlag(UpGridElement, true,
upLongTraderParam.getEntryPrice(),
FuturesPriceTrigger.RuleEnum.NUMBER_1,
- config.getQuantity(),
- orderId ->
- {
- longEntryTraderIdParam(
- UpGridElement,
- orderId,
- true
- );
- },
- null
- );
+ config.getQuantity());
}
int i = UpGridElement.getId() - 2;
@@ -1296,20 +1364,10 @@
downGridPrice.compareTo(shortEntryPrice) >= 0 &&
downGridPrice.compareTo(longEntryPrice) <= 0
){
- executor.placeConditionalEntryOrder(
+ placeEntryOrderWithPreFlag(downGridElement, true,
downLongTraderParam.getEntryPrice(),
FuturesPriceTrigger.RuleEnum.NUMBER_2,
- config.getQuantity(),
- orderId ->
- {
- longEntryTraderIdParam(
- downGridElement,
- orderId,
- true
- );
- },
- null
- );
+ config.getQuantity());
}
@@ -1319,20 +1377,10 @@
downGridPrice.compareTo(shortEntryPrice) >= 0
){
- executor.placeConditionalEntryOrder(
+ placeEntryOrderWithPreFlag(downGridElement, false,
shortTraderParam.getEntryPrice(),
FuturesPriceTrigger.RuleEnum.NUMBER_2,
- negate(config.getQuantity()),
- orderId ->
- {
- shortEntryTraderIdParam(
- downGridElement,
- orderId,
- true
- );
- },
- null
- );
+ negate(config.getQuantity()));
}
}
}
@@ -1375,6 +1423,50 @@
}
/**
+ * 预设标志位后提交条件开仓单,防止异步回调导致的竞态重复挂单。
+ *
+ * <p>在调用 {@link GateTradeExecutor#placeConditionalEntryOrder} 之前同步设置
+ * {@code isHasLongOrder / isHasShortOrder},关闭 WS 线程与 Executor 线程之间的
+ * 检查-下单时间窗口。API 失败时自动回滚标志位。
+ *
+ * @param gridElement 目标网格元素
+ * @param isLong true=多仓下单,false=空仓下单
+ * @param triggerPrice 触发价
+ * @param rule 触发规则
+ * @param size 开仓张数
+ */
+ private void placeEntryOrderWithPreFlag(GridElement gridElement, boolean isLong,
+ BigDecimal triggerPrice,
+ FuturesPriceTrigger.RuleEnum rule,
+ String size) {
+ if (isLong) {
+ gridElement.setHasLongOrder(true);
+ } else {
+ gridElement.setHasShortOrder(true);
+ }
+ executor.placeConditionalEntryOrder(triggerPrice, rule, size,
+ orderId -> {
+ if (isLong) {
+ longEntryTraderIdParam(gridElement, orderId, true);
+ } else {
+ shortEntryTraderIdParam(gridElement, orderId, true);
+ }
+ },
+ () -> {
+ if (isLong) {
+ gridElement.setHasLongOrder(false);
+ gridElement.setLongOrderId(null);
+ } else {
+ gridElement.setHasShortOrder(false);
+ gridElement.setShortOrderId(null);
+ }
+ GridElement.refreshIndices();
+ log.warn("[Gate] 条件单创建失败,回滚标志位 gridId:{}, isLong:{}", gridElement.getId(), isLong);
+ }
+ );
+ }
+
+ /**
* 根据持仓和当前价格计算未实现盈亏。
*
* <h3>正向合约公式</h3>
--
Gitblit v1.9.1