| 2026-09-30 | Administrator | ![]() |
| 2026-09-30 | Administrator | ![]() |
| 2026-09-30 | Administrator | ![]() |
src/main/java/com/xcong/excoin/modules/gateApi/GateConfig.java
@@ -140,6 +140,10 @@ private final boolean tpTriggerOppositeAddEnabled; /** 对向加仓挂单数量:止盈成交后在对向挂出的开仓张数,0=不启用。默认 0 */ private final int tpTriggerOppositeAddQuantity; /** 单格止盈张数:基础仓位止盈挂单时每个网格挂的止盈张数,支持小数。默认 0 */ private final BigDecimal baseTakeProfitPerGrid; /** 止盈网格数量:基础仓位止盈挂单的网格数量。默认 0 */ private final int baseTakeProfitGridCount; /** 网格绝对步长(shortBaseEntryPrice × gridRate),运行时由队列生成逻辑设置 */ private BigDecimal step; /** 网格元素列表,由队列初始化时同步填充,包含完整的多空仓挂单状态 */ @@ -186,6 +190,8 @@ this.shortStopLossPrice = builder.shortStopLossPrice; this.tpTriggerOppositeAddEnabled = builder.tpTriggerOppositeAddEnabled; this.tpTriggerOppositeAddQuantity = builder.tpTriggerOppositeAddQuantity; this.baseTakeProfitPerGrid = builder.baseTakeProfitPerGrid; this.baseTakeProfitGridCount = builder.baseTakeProfitGridCount; } // ==================== REST/WS 地址 ==================== @@ -303,6 +309,10 @@ public boolean isTpTriggerOppositeAddEnabled() { return tpTriggerOppositeAddEnabled; } /** @return 对向加仓挂单数量:止盈成交后在对向挂出的开仓张数 */ public int getTpTriggerOppositeAddQuantity() { return tpTriggerOppositeAddQuantity; } /** @return 单格止盈张数:基础仓位止盈挂单时每个网格挂的止盈张数 */ public BigDecimal getBaseTakeProfitPerGrid() { return baseTakeProfitPerGrid; } /** @return 止盈网格数量:基础仓位止盈挂单的网格数量 */ public int getBaseTakeProfitGridCount() { return baseTakeProfitGridCount; } // ==================== 运行时参数 ==================== @@ -425,6 +435,10 @@ private boolean tpTriggerOppositeAddEnabled = false; /** 对向加仓挂单数量:止盈成交后在对向挂出的开仓张数,默认 0 */ private int tpTriggerOppositeAddQuantity = 0; /** 单格止盈张数:基础仓位止盈挂单时每个网格挂的止盈张数,默认 0 */ private BigDecimal baseTakeProfitPerGrid = BigDecimal.ZERO; /** 止盈网格数量:基础仓位止盈挂单的网格数量,默认 0 */ private int baseTakeProfitGridCount = 0; /** 设置 API Key */ public Builder apiKey(String apiKey) { this.apiKey = apiKey; return this; } @@ -498,6 +512,10 @@ public Builder tpTriggerOppositeAddEnabled(boolean tpTriggerOppositeAddEnabled) { this.tpTriggerOppositeAddEnabled = tpTriggerOppositeAddEnabled; return this; } /** 设置对向加仓挂单数量:止盈成交后在对向挂出的开仓张数 */ public Builder tpTriggerOppositeAddQuantity(int tpTriggerOppositeAddQuantity) { this.tpTriggerOppositeAddQuantity = tpTriggerOppositeAddQuantity; return this; } /** 设置单格止盈张数:基础仓位止盈挂单时每个网格挂的止盈张数 */ public Builder baseTakeProfitPerGrid(BigDecimal baseTakeProfitPerGrid) { this.baseTakeProfitPerGrid = baseTakeProfitPerGrid; return this; } /** 设置止盈网格数量:基础仓位止盈挂单的网格数量 */ public Builder baseTakeProfitGridCount(int baseTakeProfitGridCount) { this.baseTakeProfitGridCount = baseTakeProfitGridCount; return this; } public GateConfig build() { return new GateConfig(this); src/main/java/com/xcong/excoin/modules/gateApi/GateConfigDTO.java
@@ -81,6 +81,10 @@ private Boolean tpTriggerOppositeAddEnabled; /** 对向加仓挂单数量:止盈成交后在对向挂出的开仓张数,0=不启用 */ private Integer tpTriggerOppositeAddQuantity; /** 单格止盈张数:基础仓位止盈挂单时每个网格挂的止盈张数,支持小数,0=不启用 */ private BigDecimal baseTakeProfitPerGrid; /** 止盈网格数量:基础仓位止盈挂单的网格数量,0=不启用 */ private Integer baseTakeProfitGridCount; /** * 从 GateConfig 构建 DTO(不暴露 apiSecret)。 @@ -119,6 +123,8 @@ .shortStopLossPrice(config.getShortStopLossPrice()) .tpTriggerOppositeAddEnabled(config.isTpTriggerOppositeAddEnabled()) .tpTriggerOppositeAddQuantity(config.getTpTriggerOppositeAddQuantity()) .baseTakeProfitPerGrid(config.getBaseTakeProfitPerGrid()) .baseTakeProfitGridCount(config.getBaseTakeProfitGridCount()) .build(); } @@ -151,6 +157,8 @@ m.put("shortStopLossPrice", shortStopLossPrice); m.put("tpTriggerOppositeAddEnabled", tpTriggerOppositeAddEnabled); m.put("tpTriggerOppositeAddQuantity", tpTriggerOppositeAddQuantity); m.put("baseTakeProfitPerGrid", baseTakeProfitPerGrid); m.put("baseTakeProfitGridCount", baseTakeProfitGridCount); return m; } @@ -190,6 +198,8 @@ .shortStopLossPrice(null) .tpTriggerOppositeAddEnabled(false) .tpTriggerOppositeAddQuantity(0) .baseTakeProfitPerGrid(BigDecimal.ZERO) .baseTakeProfitGridCount(0) .build(); } } src/main/java/com/xcong/excoin/modules/gateApi/GateConfigPersistenceService.java
@@ -117,6 +117,8 @@ .shortStopLossPrice(dto.getShortStopLossPrice()) .tpTriggerOppositeAddEnabled(nvl(dto.getTpTriggerOppositeAddEnabled(), false)) .tpTriggerOppositeAddQuantity(dto.getTpTriggerOppositeAddQuantity() != null && dto.getTpTriggerOppositeAddQuantity() > 0 ? dto.getTpTriggerOppositeAddQuantity() : 0) .baseTakeProfitPerGrid(dto.getBaseTakeProfitPerGrid() != null && dto.getBaseTakeProfitPerGrid().compareTo(BigDecimal.ZERO) > 0 ? dto.getBaseTakeProfitPerGrid() : BigDecimal.ZERO) .baseTakeProfitGridCount(dto.getBaseTakeProfitGridCount() != null && dto.getBaseTakeProfitGridCount() > 0 ? dto.getBaseTakeProfitGridCount() : 0) .build(); } src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
@@ -836,8 +836,9 @@ lastShortStopLossGridId = Integer.MAX_VALUE; // [Gate-需求2] 加仓后先撤空仓所有止盈+止损,再查交易所持仓后重挂 cancelAllShortTakeProfitsAndStopLosses(); // REST 查询可能因交易所延迟返回旧值,与 WS 本地缓存取最大值兜底 int posSize = Math.max(queryPositionSize(Position.ModeEnum.DUAL_SHORT), shortPositionSize.intValue()); // REST/WS 持仓可能因交易所延迟返回旧值(仓位归零后重开甚至为 0), // 与本次成交张数 filledQty 取最大值兜底,避免 posSize=0 导致止盈止损全部跳过 int posSize = Math.max(Math.max(queryPositionSize(Position.ModeEnum.DUAL_SHORT), shortPositionSize.intValue()), filledQty); extendShortStopLoss(posSize, shortGridElement.getId()); // [Gate] 止盈挂单:超出基础仓位的部分,挂在多仓第一止损位 if (GateConfig.EXCESS_TP_CONCENTRATED.equals(config.getPlaceExcessTakeProfit())) { @@ -845,6 +846,8 @@ } else if (GateConfig.EXCESS_TP_DISPERSED.equals(config.getPlaceExcessTakeProfit())) { placeDispersedTakeProfit(posSize, false, shortGridElement.getId()); } // [Gate] 基础仓位止盈挂单:与超额止盈并行,独立于止盈模式 placeBaseTakeProfit(posSize, false, shortGridElement.getId()); log.info("[Gate] 空单成交 gridId:{}, 当前持仓:{}张", filledQty, posSize); @@ -884,8 +887,9 @@ lastLongStopLossGridId = Integer.MAX_VALUE; // [Gate-需求2] 加仓后先撤多仓所有止盈+止损,再查交易所持仓后重挂 cancelAllLongTakeProfitsAndStopLosses(); // REST 查询可能因交易所延迟返回旧值,与 WS 本地缓存取最大值兜底 int posSize = Math.max(queryPositionSize(Position.ModeEnum.DUAL_LONG), longPositionSize.intValue()); // REST/WS 持仓可能因交易所延迟返回旧值(仓位归零后重开甚至为 0), // 与本次成交张数 filledQty 取最大值兜底,避免 posSize=0 导致止盈止损全部跳过 int posSize = Math.max(Math.max(queryPositionSize(Position.ModeEnum.DUAL_LONG), longPositionSize.intValue()), filledQty); extendLongStopLoss(posSize, longGridElement.getId()); // [Gate] 止盈挂单:超出基础仓位的部分,挂在空仓第一止损位 if (GateConfig.EXCESS_TP_CONCENTRATED.equals(config.getPlaceExcessTakeProfit())) { @@ -893,6 +897,8 @@ } else if (GateConfig.EXCESS_TP_DISPERSED.equals(config.getPlaceExcessTakeProfit())) { placeDispersedTakeProfit(posSize, true, longGridElement.getId()); } // [Gate] 基础仓位止盈挂单:与超额止盈并行,独立于止盈模式 placeBaseTakeProfit(posSize, true, longGridElement.getId()); log.info("[Gate] 多单成交 gridId:{}, 当前持仓:{}张", filledQty, posSize); } @@ -909,8 +915,8 @@ private void handleLongTakeProfitTriggered(GridElement tpElem, String orderId) { // 清理前判断:当前是否为该方向最后一个止盈单(最后一个不触发对向加仓) boolean isLastTp = GridElement.getLongTakeProfitCount() <= 1; // 清理止盈单状态(已成交) longTakeProfitTraderIdParam(tpElem, null, false); // 清理止盈单状态(移除已成交的这笔止盈单) longTakeProfitTraderIdParam(tpElem, orderId, false); log.info("[Gate] 多仓止盈触发 gridId:{}, orderId:{}, 剩余止盈:{}", tpElem.getId(), orderId, GridElement.getLongTakeProfitCount()); @@ -947,8 +953,8 @@ private void handleShortTakeProfitTriggered(GridElement tpElem, String orderId) { // 清理前判断:当前是否为该方向最后一个止盈单(最后一个不触发对向加仓) boolean isLastTp = GridElement.getShortTakeProfitCount() <= 1; // 清理止盈单状态(已成交) shortTakeProfitTraderIdParam(tpElem, null, false); // 清理止盈单状态(移除已成交的这笔止盈单) shortTakeProfitTraderIdParam(tpElem, orderId, false); log.info("[Gate] 空仓止盈触发 gridId:{}, orderId:{}, 剩余止盈:{}", tpElem.getId(), orderId, GridElement.getShortTakeProfitCount()); @@ -1465,18 +1471,48 @@ GridElement baseElement,String profitId, boolean flag ) { TraderParam tp = baseElement.getLongTraderParam(); tp.setTakeProfitOrderId(profitId); tp.setTakeProfitPlaced(flag); baseElement.setLongTakeProfitOrderId(profitId); if (flag && profitId != null) { // 挂单成功:追加到止盈单列表(支持基础止盈 + 超额止盈并行挂单) tp.setTakeProfitOrderId(profitId); tp.setTakeProfitPlaced(true); baseElement.addLongTakeProfitOrderId(profitId); } else { // 清理:profitId 非空移除指定止盈单,为空清空该网格全部止盈单 tp.setTakeProfitPlaced(false); if (profitId != null) { baseElement.removeLongTakeProfitOrderId(profitId); if (!baseElement.hasLongTakeProfitOrders()) { tp.setTakeProfitOrderId(null); } } else { baseElement.clearLongTakeProfitOrderIds(); tp.setTakeProfitOrderId(null); } } GridElement.refreshIndices(); } private void shortTakeProfitTraderIdParam( GridElement baseElement,String profitId, boolean flag ) { TraderParam tp = baseElement.getShortTraderParam(); tp.setTakeProfitOrderId(profitId); tp.setTakeProfitPlaced(flag); baseElement.setShortTakeProfitOrderId(profitId); if (flag && profitId != null) { // 挂单成功:追加到止盈单列表(支持基础止盈 + 超额止盈并行挂单) tp.setTakeProfitOrderId(profitId); tp.setTakeProfitPlaced(true); baseElement.addShortTakeProfitOrderId(profitId); } else { // 清理:profitId 非空移除指定止盈单,为空清空该网格全部止盈单 tp.setTakeProfitPlaced(false); if (profitId != null) { baseElement.removeShortTakeProfitOrderId(profitId); if (!baseElement.hasShortTakeProfitOrders()) { tp.setTakeProfitOrderId(null); } } else { baseElement.clearShortTakeProfitOrderIds(); tp.setTakeProfitOrderId(null); } } GridElement.refreshIndices(); } @@ -2202,11 +2238,13 @@ */ private void cancelAllLongTakeProfitsAndStopLosses() { for (GridElement e : config.getGridElements()) { String tpId = e.getLongTakeProfitOrderId(); if (tpId != null) { e.getLongTraderParam().setTakeProfitPlaced(false); e.setLongTakeProfitOrderId(null); for (String tpId : new ArrayList<>(e.getLongTakeProfitOrderIds())) { executor.cancelConditionalOrder(tpId, oid -> {}); } e.clearLongTakeProfitOrderIds(); if (e.getLongTraderParam() != null) { e.getLongTraderParam().setTakeProfitPlaced(false); e.getLongTraderParam().setTakeProfitOrderId(null); } for (String slId : new ArrayList<>(e.getLongStopLossOrderIds())) { executor.cancelConditionalOrder(slId, oid -> {}); @@ -2222,11 +2260,13 @@ */ private void cancelAllShortTakeProfitsAndStopLosses() { for (GridElement e : config.getGridElements()) { String tpId = e.getShortTakeProfitOrderId(); if (tpId != null) { e.getShortTraderParam().setTakeProfitPlaced(false); e.setShortTakeProfitOrderId(null); for (String tpId : new ArrayList<>(e.getShortTakeProfitOrderIds())) { executor.cancelConditionalOrder(tpId, oid -> {}); } e.clearShortTakeProfitOrderIds(); if (e.getShortTraderParam() != null) { e.getShortTraderParam().setTakeProfitPlaced(false); e.getShortTraderParam().setTakeProfitOrderId(null); } for (String slId : new ArrayList<>(e.getShortStopLossOrderIds())) { executor.cancelConditionalOrder(slId, oid -> {}); @@ -2241,20 +2281,28 @@ * 首次挂单成交时,取消初始化阶段挂的所有止盈单(多+空),止损单保留不动。 */ private void cancelAllInitialTakeProfits() { GridElement tpElemLong = GridElement.findById(3); String longTpId = tpElemLong.getLongTakeProfitOrderId(); if (longTpId != null) { longTakeProfitTraderIdParam(tpElemLong, null, false); executor.cancelConditionalOrder(longTpId, oid -> {}); if (tpElemLong != null) { for (String tpId : new ArrayList<>(tpElemLong.getLongTakeProfitOrderIds())) { executor.cancelConditionalOrder(tpId, oid -> {}); } tpElemLong.clearLongTakeProfitOrderIds(); if (tpElemLong.getLongTraderParam() != null) { tpElemLong.getLongTraderParam().setTakeProfitPlaced(false); tpElemLong.getLongTraderParam().setTakeProfitOrderId(null); } } GridElement tpElemShort = GridElement.findById(-3); String shortTpId = tpElemShort.getShortTakeProfitOrderId(); if (shortTpId != null) { shortTakeProfitTraderIdParam(tpElemShort, null, false); executor.cancelConditionalOrder(shortTpId, oid -> {}); if (tpElemShort != null) { for (String tpId : new ArrayList<>(tpElemShort.getShortTakeProfitOrderIds())) { executor.cancelConditionalOrder(tpId, oid -> {}); } tpElemShort.clearShortTakeProfitOrderIds(); if (tpElemShort.getShortTraderParam() != null) { tpElemShort.getShortTraderParam().setTakeProfitPlaced(false); tpElemShort.getShortTraderParam().setTakeProfitOrderId(null); } } GridElement.refreshIndices(); log.info("[Gate] 首次成交,取消所有初始化止盈单"); @@ -2642,6 +2690,7 @@ } else if (GateConfig.EXCESS_TP_DISPERSED.equals(config.getPlaceExcessTakeProfit())) { placeDispersedTakeProfit(posSize, true, 0); } placeBaseTakeProfit(posSize, true, 0); log.info("[Gate] 生成多仓止盈止损完成, 仓位:{}张", posSize); }); } @@ -2664,6 +2713,7 @@ } else if (GateConfig.EXCESS_TP_DISPERSED.equals(config.getPlaceExcessTakeProfit())) { placeDispersedTakeProfit(posSize, false, 0); } placeBaseTakeProfit(posSize, false, 0); log.info("[Gate] 生成空仓止盈止损完成, 仓位:{}张", posSize); }); } @@ -2992,6 +3042,83 @@ } /** * 基础止盈:对基础仓位(baseQuantity)也挂止盈单,与超额止盈并行。 * * <p>规则:从入场网格下一格(entryGridId ± 1)开始,每格 baseTakeProfitPerGrid 张, * 最多挂 baseTakeProfitGridCount 格,挂到止损位的前一格截止(与分散止盈同起点/同终点)。 * * <p>边界:累计张数到 min(baseQuantity, 当前持仓) 即停(张数不足则最后一格挂剩余 = "挂几个算几个"); * 网格不足时挂到停止位置结束。 * * @param posSize 当前总持仓张数 * @param isLong true=多仓止盈,false=空仓止盈 * @param entryGridId 入场网格 ID */ private void placeBaseTakeProfit(int posSize, boolean isLong, int entryGridId) { BigDecimal perGrid = config.getBaseTakeProfitPerGrid(); int gridCount = config.getBaseTakeProfitGridCount(); if (perGrid == null || perGrid.compareTo(BigDecimal.ZERO) <= 0 || gridCount <= 0) { return; } int baseQty = Integer.parseInt(config.getBaseQuantity()); if (baseQty <= 0) { return; } // 张数硬上限 = min(基础张数, 当前持仓),避免持仓不足 baseQuantity 时超挂 int maxQty = Math.min(baseQty, posSize); if (maxQty <= 0) { return; } GridElement endElem = isLong ? findFirstShortStopLossGrid() : findFirstLongStopLossGrid(); if (endElem == null) { log.warn("[Gate] {}基础止盈失败:未找到对手盘止损位", isLong ? "多仓" : "空仓"); return; } int dir = isLong ? 1 : -1; int startId = entryGridId + dir; // 分配范围到止损位的前一格截止(与分散止盈一致) int endId = endElem.getId() - dir; BigDecimal remaining = BigDecimal.valueOf(maxQty); int placed = 0; int id = startId; while (placed < gridCount && remaining.compareTo(BigDecimal.ZERO) > 0) { if (isLong ? id > endId : id < endId) { break; } GridElement elem = GridElement.findById(id); if (elem == null) { break; } BigDecimal qty = perGrid.min(remaining); final GridElement fe = elem; final int gid = id; final BigDecimal triggerPrice = elem.getGridPrice(); final String size = isLong ? negate(qty.toPlainString()) : qty.toPlainString(); final String orderType = isLong ? ORDER_TYPE_CLOSE_LONG : ORDER_TYPE_CLOSE_SHORT; final FuturesPriceTrigger.RuleEnum rule = isLong ? FuturesPriceTrigger.RuleEnum.NUMBER_1 : FuturesPriceTrigger.RuleEnum.NUMBER_2; executor.placeTakeProfit(triggerPrice, rule, orderType, size, profitId -> { if (isLong) { longTakeProfitTraderIdParam(fe, profitId, true); } else { shortTakeProfitTraderIdParam(fe, profitId, true); } log.info("[Gate] 基础止盈 {} gridId:{}, 触发价:{}, 数量:{}, tpId:{}", isLong ? "多仓" : "空仓", gid, triggerPrice, size, profitId); } ); remaining = remaining.subtract(qty); placed++; id += dir; } log.info("[Gate] 基础止盈 {} 挂单完成, 入场gridId:{}, 挂{}格, 剩余{}张", isLong ? "多仓" : "空仓", entryGridId, placed, remaining); } /** * 在指定网格挂一笔对手止盈单(非满仓超额止盈,挂在止损触发位的下一格)。 */ private void placeTakeProfitAtGrid(GridElement tpElem, boolean isLong, int qty, int times) { src/main/java/com/xcong/excoin/modules/gateApi/GridElement.java
@@ -30,7 +30,7 @@ * <tr><td>标识</td><td>id, gridPrice, upId, downId</td><td>编号、价格、双向链表指针</td></tr> * <tr><td>多仓订单</td><td>hasLongOrder, longOrderId, longTraderParam</td><td>是否有挂单、订单ID、完整参数</td></tr> * <tr><td>空仓订单</td><td>hasShortOrder, shortOrderId, shortTraderParam</td><td>是否有挂单、订单ID、完整参数</td></tr> * <tr><td>止盈</td><td>longTakeProfitOrderId, shortTakeProfitOrderId</td><td>止盈条件单ID</td></tr> * <tr><td>止盈</td><td>longTakeProfitOrderIds, shortTakeProfitOrderIds</td><td>止盈条件单ID列表</td></tr> * </table> * * <h3>6 个全局 O(1) 索引</h3> @@ -83,10 +83,10 @@ private List<String> longOrderIds = new ArrayList<>(); /** 空仓挂单订单 ID 列表(支持同一网格多个挂单,由止损追单产生) */ private List<String> shortOrderIds = new ArrayList<>(); /** 多仓止盈订单 ID */ private String longTakeProfitOrderId; /** 空仓止盈订单 ID */ private String shortTakeProfitOrderId; /** 多仓止盈订单 ID 列表(支持一网格多笔止盈单:基础止盈 + 超额止盈并行挂单) */ private List<String> longTakeProfitOrderIds = new ArrayList<>(); /** 空仓止盈订单 ID 列表(支持一网格多笔止盈单:基础止盈 + 超额止盈并行挂单) */ private List<String> shortTakeProfitOrderIds = new ArrayList<>(); /** 多仓止损订单 ID 列表(支持一个网格多个止损单) */ private List<String> longStopLossOrderIds = new ArrayList<>(); /** 空仓止损订单 ID 列表(支持一个网格多个止损单) */ @@ -263,7 +263,8 @@ StringBuilder sb = new StringBuilder("\n========== 网格数据 ==========\n"); for (GridElement e : sorted) { if (e.isHasLongOrder() || e.isHasShortOrder() || e.hasLongStopLossOrders() || e.hasShortStopLossOrders()){ || e.hasLongStopLossOrders() || e.hasShortStopLossOrders() || e.hasLongTakeProfitOrders() || e.hasShortTakeProfitOrders()){ sb.append(String.format( " ID=%4d 价格=%s up=%s down=%s 多仓=%s(%s) 空仓=%s(%s) 多止盈=%s 空止盈=%s 多止损=%s 空止损=%s\n", e.getId(), @@ -274,8 +275,8 @@ e.hasLongOrderIds() ? String.join(",", e.getLongOrderIds()) : "-", e.isHasShortOrder() ? "有" : "无", e.hasShortOrderIds() ? String.join(",", e.getShortOrderIds()) : "-", e.getLongTakeProfitOrderId() != null ? e.getLongTakeProfitOrderId() : "-", e.getShortTakeProfitOrderId() != null ? e.getShortTakeProfitOrderId() : "-", e.hasLongTakeProfitOrders() ? String.join(",", e.getLongTakeProfitOrderIds()) : "-", e.hasShortTakeProfitOrders() ? String.join(",", e.getShortTakeProfitOrderIds()) : "-", e.hasLongStopLossOrders() ? String.join(",", e.getLongStopLossOrderIds()) : "-", e.hasShortStopLossOrders() ? String.join(",", e.getShortStopLossOrderIds()) : "-" )); @@ -310,7 +311,7 @@ public static List<GridElement> findAllShortOrders(BigDecimal currentPrice) { List<GridElement> result = new ArrayList<>(); for (GridElement e : INDEX.values()) { if (e.isHasShortOrder() && e.getGridPrice().compareTo(currentPrice) < 0 && e.getShortTakeProfitOrderId() == null) { if (e.isHasShortOrder() && e.getGridPrice().compareTo(currentPrice) < 0 && !e.hasShortTakeProfitOrders()) { result.add(e); } } @@ -326,7 +327,7 @@ public static List<GridElement> findAllLongOrders(BigDecimal currentPrice) { List<GridElement> result = new ArrayList<>(); for (GridElement e : INDEX.values()) { if (e.isHasLongOrder() && e.getGridPrice().compareTo(currentPrice) > 0 && e.getLongTakeProfitOrderId() == null) { if (e.isHasLongOrder() && e.getGridPrice().compareTo(currentPrice) > 0 && !e.hasLongTakeProfitOrders()) { result.add(e); } } @@ -341,11 +342,11 @@ for (String oId : e.getShortOrderIds()) { SHORT_ORDER_ID_INDEX.put(oId, e); } if (e.getLongTakeProfitOrderId() != null) { LONG_TP_ORDER_ID_INDEX.put(e.getLongTakeProfitOrderId(), e); for (String tpId : e.getLongTakeProfitOrderIds()) { LONG_TP_ORDER_ID_INDEX.put(tpId, e); } if (e.getShortTakeProfitOrderId() != null) { SHORT_TP_ORDER_ID_INDEX.put(e.getShortTakeProfitOrderId(), e); for (String tpId : e.getShortTakeProfitOrderIds()) { SHORT_TP_ORDER_ID_INDEX.put(tpId, e); } for (String slId : e.getLongStopLossOrderIds()) { LONG_SL_ORDER_ID_INDEX.put(slId, e); @@ -378,8 +379,8 @@ this.shortTraderParam = builder.shortTraderParam; this.upId = builder.upId; this.downId = builder.downId; this.longTakeProfitOrderId = builder.longTakeProfitOrderId; this.shortTakeProfitOrderId = builder.shortTakeProfitOrderId; if (builder.longTakeProfitOrderIds != null) this.longTakeProfitOrderIds.addAll(builder.longTakeProfitOrderIds); if (builder.shortTakeProfitOrderIds != null) this.shortTakeProfitOrderIds.addAll(builder.shortTakeProfitOrderIds); if (builder.longOrderIds != null) this.longOrderIds.addAll(builder.longOrderIds); if (builder.shortOrderIds != null) this.shortOrderIds.addAll(builder.shortOrderIds); if (builder.longStopLossOrderIds != null) this.longStopLossOrderIds.addAll(builder.longStopLossOrderIds); @@ -480,19 +481,43 @@ @Deprecated public void setShortOrderId(String orderId) { if (orderId != null) shortOrderIds.add(orderId); } // ==================== 多仓止盈订单 ID ==================== // ==================== 多仓止盈订单 ID 列表 ==================== /** @return 多仓止盈订单 ID */ public String getLongTakeProfitOrderId() { return longTakeProfitOrderId; } /** 设置多仓止盈订单 ID */ public void setLongTakeProfitOrderId(String longTakeProfitOrderId) { this.longTakeProfitOrderId = longTakeProfitOrderId; } /** @return 多仓止盈订单 ID 列表 */ public List<String> getLongTakeProfitOrderIds() { return longTakeProfitOrderIds; } /** 添加多仓止盈订单 ID */ public void addLongTakeProfitOrderId(String orderId) { if (orderId != null) longTakeProfitOrderIds.add(orderId); } /** 移除多仓止盈订单 ID */ public boolean removeLongTakeProfitOrderId(String orderId) { return longTakeProfitOrderIds.remove(orderId); } /** 清空多仓止盈订单 ID */ public void clearLongTakeProfitOrderIds() { longTakeProfitOrderIds.clear(); } /** @return 是否有止盈单 */ public boolean hasLongTakeProfitOrders() { return !longTakeProfitOrderIds.isEmpty(); } /** @deprecated 返回最后一个止盈订单ID,用于单订单场景的兼容 */ @Deprecated public String getLongTakeProfitOrderId() { return longTakeProfitOrderIds.isEmpty() ? null : longTakeProfitOrderIds.get(longTakeProfitOrderIds.size() - 1); } /** @deprecated 添加到列表,用于兼容旧调用 */ @Deprecated public void setLongTakeProfitOrderId(String orderId) { if (orderId != null) longTakeProfitOrderIds.add(orderId); } // ==================== 空仓止盈订单 ID ==================== // ==================== 空仓止盈订单 ID 列表 ==================== /** @return 空仓止盈订单 ID */ public String getShortTakeProfitOrderId() { return shortTakeProfitOrderId; } /** 设置空仓止盈订单 ID */ public void setShortTakeProfitOrderId(String shortTakeProfitOrderId) { this.shortTakeProfitOrderId = shortTakeProfitOrderId; } /** @return 空仓止盈订单 ID 列表 */ public List<String> getShortTakeProfitOrderIds() { return shortTakeProfitOrderIds; } /** 添加空仓止盈订单 ID */ public void addShortTakeProfitOrderId(String orderId) { if (orderId != null) shortTakeProfitOrderIds.add(orderId); } /** 移除空仓止盈订单 ID */ public boolean removeShortTakeProfitOrderId(String orderId) { return shortTakeProfitOrderIds.remove(orderId); } /** 清空空仓止盈订单 ID */ public void clearShortTakeProfitOrderIds() { shortTakeProfitOrderIds.clear(); } /** @return 是否有止盈单 */ public boolean hasShortTakeProfitOrders() { return !shortTakeProfitOrderIds.isEmpty(); } /** @deprecated 返回最后一个止盈订单ID,用于单订单场景的兼容 */ @Deprecated public String getShortTakeProfitOrderId() { return shortTakeProfitOrderIds.isEmpty() ? null : shortTakeProfitOrderIds.get(shortTakeProfitOrderIds.size() - 1); } /** @deprecated 添加到列表,用于兼容旧调用 */ @Deprecated public void setShortTakeProfitOrderId(String orderId) { if (orderId != null) shortTakeProfitOrderIds.add(orderId); } // ==================== 多仓止损订单 ID ==================== @@ -567,10 +592,10 @@ private List<String> longOrderIds; /** 空仓挂单订单 ID 列表 */ private List<String> shortOrderIds; /** 多仓止盈订单 ID */ private String longTakeProfitOrderId; /** 空仓止盈订单 ID */ private String shortTakeProfitOrderId; /** 多仓止盈订单 ID 列表 */ private List<String> longTakeProfitOrderIds; /** 空仓止盈订单 ID 列表 */ private List<String> shortTakeProfitOrderIds; /** 多仓止损订单 ID 列表 */ private List<String> longStopLossOrderIds; /** 空仓止损订单 ID 列表 */ @@ -596,10 +621,10 @@ public Builder longOrderIds(List<String> longOrderIds) { this.longOrderIds = longOrderIds; return this; } /** 设置空仓挂单订单 ID 列表 */ public Builder shortOrderIds(List<String> shortOrderIds) { this.shortOrderIds = shortOrderIds; return this; } /** 设置多仓止盈订单 ID */ public Builder longTakeProfitOrderId(String longTakeProfitOrderId) { this.longTakeProfitOrderId = longTakeProfitOrderId; return this; } /** 设置空仓止盈订单 ID */ public Builder shortTakeProfitOrderId(String shortTakeProfitOrderId) { this.shortTakeProfitOrderId = shortTakeProfitOrderId; return this; } /** 设置多仓止盈订单 ID 列表 */ public Builder longTakeProfitOrderIds(List<String> longTakeProfitOrderIds) { this.longTakeProfitOrderIds = longTakeProfitOrderIds; return this; } /** 设置空仓止盈订单 ID 列表 */ public Builder shortTakeProfitOrderIds(List<String> shortTakeProfitOrderIds) { this.shortTakeProfitOrderIds = shortTakeProfitOrderIds; return this; } /** 设置多仓止损订单 ID 列表 */ public Builder longStopLossOrderIds(List<String> longStopLossOrderIds) { this.longStopLossOrderIds = longStopLossOrderIds; return this; } /** 设置空仓止损订单 ID 列表 */