| | |
| | | GridElement longStopLossElem = GridElement.findByLongStopLossOrderId(orderId); |
| | | // if (longStopLossElem != null && longPositionSize.compareTo(BigDecimal.ZERO) > 0 && StrUtil.isNotEmpty(tradeId) && !tradeId.equals("0")) { |
| | | if (longStopLossElem != null && StrUtil.isNotEmpty(tradeId) && !tradeId.equals("0")) { |
| | | handleLongStopLossTriggered(longStopLossElem); |
| | | handleLongStopLossTriggered(longStopLossElem, orderId); |
| | | return; |
| | | } |
| | | GridElement shortStopLossElem = GridElement.findByShortStopLossOrderId(orderId); |
| | | // if (shortStopLossElem != null && shortPositionSize.compareTo(BigDecimal.ZERO) > 0 && StrUtil.isNotEmpty(tradeId) && !tradeId.equals("0")) { |
| | | if (shortStopLossElem != null && StrUtil.isNotEmpty(tradeId) && !tradeId.equals("0")) { |
| | | handleShortStopLossTriggered(shortStopLossElem); |
| | | handleShortStopLossTriggered(shortStopLossElem, orderId); |
| | | return; |
| | | } |
| | | |
| | | GridElement shortGridElement = GridElement.findByShortOrderId(orderId); |
| | | if (shortGridElement != null) { |
| | | if (shortGridElement.isHasShortOrder() && StrUtil.isNotEmpty(tradeId) && !tradeId.equals("0") ){ |
| | | if (StrUtil.isNotEmpty(tradeId) && !tradeId.equals("0") ){ |
| | | int filledQty = Integer.parseInt(shortGridElement.getShortTraderParam().getQuantity()); |
| | | shortEntryTraderIdParam(shortGridElement, null, false); |
| | | shortEntryTraderIdParam(shortGridElement, orderId, false); |
| | | // [Gate-需求2] 加仓后先撤空仓所有止盈+止损,再查交易所持仓后重挂 |
| | | cancelAllShortTakeProfitsAndStopLosses(); |
| | | // REST 查询可能因交易所延迟返回旧值,与 WS 本地缓存取最大值兜底 |
| | |
| | | } |
| | | GridElement longGridElement = GridElement.findByLongOrderId(orderId); |
| | | if (longGridElement != null) { |
| | | if (longGridElement.isHasLongOrder() && StrUtil.isNotEmpty(tradeId) && !tradeId.equals("0")){ |
| | | if (StrUtil.isNotEmpty(tradeId) && !tradeId.equals("0")){ |
| | | |
| | | int filledQty = Integer.parseInt(longGridElement.getLongTraderParam().getQuantity()); |
| | | longEntryTraderIdParam(longGridElement, null, false); |
| | | longEntryTraderIdParam(longGridElement, orderId, false); |
| | | // [Gate-需求2] 加仓后先撤多仓所有止盈+止损,再查交易所持仓后重挂 |
| | | cancelAllLongTakeProfitsAndStopLosses(); |
| | | // REST 查询可能因交易所延迟返回旧值,与 WS 本地缓存取最大值兜底 |
| | |
| | | TraderParam tp = baseElement.getLongTraderParam(); |
| | | tp.setEntryOrderId(entryId); |
| | | tp.setEntryOrderPlaced(flag); |
| | | baseElement.setHasLongOrder(flag); |
| | | baseElement.setLongOrderId(entryId); |
| | | if (flag && entryId != null) { |
| | | baseElement.addLongOrderId(entryId); |
| | | baseElement.setHasLongOrder(true); |
| | | } else if (!flag && entryId != null) { |
| | | baseElement.removeLongOrderId(entryId); |
| | | // 只有列表全部清空才关闭预置标志,避免还有未成交挂单时被 processGrid 误判 |
| | | baseElement.setHasLongOrder(baseElement.hasLongOrderIds()); |
| | | } |
| | | GridElement.refreshIndices(); |
| | | } |
| | | |
| | |
| | | TraderParam tp = baseElement.getShortTraderParam(); |
| | | tp.setEntryOrderId(entryId); |
| | | tp.setEntryOrderPlaced(flag); |
| | | baseElement.setHasShortOrder(flag); |
| | | baseElement.setShortOrderId(entryId); |
| | | if (flag && entryId != null) { |
| | | baseElement.addShortOrderId(entryId); |
| | | baseElement.setHasShortOrder(true); |
| | | } else if (!flag && entryId != null) { |
| | | baseElement.removeShortOrderId(entryId); |
| | | // 只有列表全部清空才关闭预置标志,避免还有未成交挂单时被 processGrid 误判 |
| | | baseElement.setHasShortOrder(baseElement.hasShortOrderIds()); |
| | | } |
| | | GridElement.refreshIndices(); |
| | | } |
| | | |
| | |
| | | ? cancelGridElement.getLongTraderParam().getQuantity() |
| | | : config.getBaseQuantity(); |
| | | if (cancelGridElement != null && cancelGridElement.isHasLongOrder()) { |
| | | String longOrderId = cancelGridElement.getLongOrderId(); |
| | | executor.cancelConditionalOrder(longOrderId, oid -> { |
| | | longEntryTraderIdParam(cancelGridElement, null, false); |
| | | log.info("[Gate] 多仓仓位归零, 取消gridId:{}的多单,{}", cancelGridElement.getId(),longOrderId); |
| | | }); |
| | | for (String longOrderId : new ArrayList<>(cancelGridElement.getLongOrderIds())) { |
| | | executor.cancelConditionalOrder(longOrderId, oid -> { |
| | | longEntryTraderIdParam(cancelGridElement, longOrderId, false); |
| | | log.info("[Gate] 多仓仓位归零, 取消gridId:{}的多单,{}", cancelGridElement.getId(), longOrderId); |
| | | }); |
| | | } |
| | | } |
| | | // log.info("[Gate-2] 多仓仓位归零 空仓队列触发, 匹配:{},当前价:{}", matched, currentPrice); |
| | | if (!newEntryGrid.isHasLongOrder()) { |
| | |
| | | * 看是否有空仓挂单,有就取消 |
| | | */ |
| | | if (cancelGridElement != null && cancelGridElement.isHasShortOrder()) { |
| | | String shortOrderId = cancelGridElement.getShortOrderId(); |
| | | executor.cancelConditionalOrder(shortOrderId, oid -> { |
| | | shortEntryTraderIdParam(cancelGridElement, null, false); |
| | | log.info("[Gate] 空仓仓位归零, 取消gridId:{}的多单{}", cancelGridElement.getId(),shortOrderId); |
| | | }); |
| | | for (String shortOrderId : new ArrayList<>(cancelGridElement.getShortOrderIds())) { |
| | | executor.cancelConditionalOrder(shortOrderId, oid -> { |
| | | shortEntryTraderIdParam(cancelGridElement, shortOrderId, false); |
| | | log.info("[Gate] 空仓仓位归零, 取消gridId:{}的多单{}", cancelGridElement.getId(), shortOrderId); |
| | | }); |
| | | } |
| | | } |
| | | // log.info("[Gate-4] 空仓仓位归零 多仓队列触发, 匹配:{},当前价:{}", matched, currentPrice); |
| | | if (!newEntryGrid.isHasShortOrder()){ |
| | |
| | | } |
| | | } |
| | | |
| | | private void handleLongStopLossTriggered(GridElement gridElement) { |
| | | gridElement.clearLongStopLossOrderIds(); |
| | | private void handleLongStopLossTriggered(GridElement gridElement, String orderId) { |
| | | gridElement.removeLongStopLossOrderId(orderId); |
| | | |
| | | accumulatedLongLossCount++; |
| | | int gridId = gridElement.getId(); |
| | |
| | | return; |
| | | } |
| | | |
| | | // [Gate-BugFix] 防止与"仓位归零"重复下单:若该网格已有挂单则跳过 |
| | | if (!newEntryGrid.isHasLongOrder()) { |
| | | BigDecimal triggerPrice = newEntryGrid.getGridPrice(); |
| | | |
| | | // 止损触发后持仓在减少,取REST和WS缓存中较小值更准确 |
| | | int posSize = Math.min(queryPositionSize(Position.ModeEnum.DUAL_LONG), longPositionSize.intValue()); |
| | | int maxPos = config.getMaxPositionSize(); |
| | | // 止损阶梯:止损次数≤阈值时挂单量=单笔数量,超过后恢复默认逻辑(quantity*2) |
| | | int targetAmount; |
| | | if (config.getStopLossCount() > 0 && accumulatedLongLossCount <= config.getStopLossCount()) { |
| | | targetAmount = Integer.parseInt(config.getQuantity()); |
| | | // 止损追单:同一网格可有多笔挂单,不判断 isHasLongOrder,直接挂单 |
| | | BigDecimal triggerPrice = newEntryGrid.getGridPrice(); |
| | | int posSize = Math.min(queryPositionSize(Position.ModeEnum.DUAL_LONG), longPositionSize.intValue()); |
| | | int maxPos = config.getMaxPositionSize(); |
| | | int targetAmount; |
| | | if (config.getStopLossCount() > 0 && accumulatedLongLossCount <= config.getStopLossCount()) { |
| | | targetAmount = Integer.parseInt(config.getQuantity()); |
| | | } else { |
| | | targetAmount = Integer.parseInt(config.getQuantity()) * 2; |
| | | } |
| | | int addSize; |
| | | if (maxPos > 0) { |
| | | int remainingRoom = maxPos - posSize; |
| | | if (remainingRoom <= 0) { |
| | | log.warn("[Gate] 多仓止损触发 gridId:{}, 当前持仓{}/{}已达上限,跳过追单", |
| | | gridId, posSize, maxPos); |
| | | addSize = 0; |
| | | } else { |
| | | targetAmount = Integer.parseInt(config.getQuantity()) * 2; // quantity + 本次止损量 |
| | | addSize = Math.min(remainingRoom, targetAmount); |
| | | } |
| | | int addSize; |
| | | if (maxPos > 0) { |
| | | int remainingRoom = maxPos - posSize; |
| | | if (remainingRoom <= 0) { |
| | | log.warn("[Gate] 多仓止损触发 gridId:{}, 当前持仓{}/{}已达上限,跳过追单", |
| | | gridId, posSize, maxPos); |
| | | addSize = 0; |
| | | } else { |
| | | addSize = Math.min(remainingRoom, targetAmount); |
| | | } |
| | | } else { |
| | | addSize = targetAmount; |
| | | } |
| | | if (addSize > 0) { |
| | | String size = String.valueOf(addSize); |
| | | log.info("[Gate] 多仓止损触发 gridId:{}, 止损次数:{}, 在gridId:{}补{}张多单(当前{}/上限{})", |
| | | gridId, accumulatedLongLossCount, newEntryGridId, size, posSize, maxPos > 0 ? maxPos : "无"); |
| | | newEntryGrid.getLongTraderParam().setQuantity(size); |
| | | placeEntryOrderWithPreFlag(newEntryGrid, true, triggerPrice, |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_1, size); |
| | | } |
| | | }else{ |
| | | log.warn("[Gate] 多仓止损触发 gridId:{}, 目标gridId:{}已有挂单,跳过重复下单", gridId, newEntryGridId); |
| | | } else { |
| | | addSize = targetAmount; |
| | | } |
| | | if (addSize > 0) { |
| | | String size = String.valueOf(addSize); |
| | | log.info("[Gate] 多仓止损触发 gridId:{}, 止损次数:{}, 在gridId:{}补{}张多单(当前{}/上限{})", |
| | | gridId, accumulatedLongLossCount, newEntryGridId, size, posSize, maxPos > 0 ? maxPos : "无"); |
| | | newEntryGrid.getLongTraderParam().setQuantity(size); |
| | | placeEntryOrderWithPreFlag(newEntryGrid, true, triggerPrice, |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_1, size); |
| | | } |
| | | |
| | | // 取消 gridId+2 位置的所有多单 |
| | | int cancelGridId = gridId + 2; |
| | | GridElement cancelGrid = GridElement.findById(cancelGridId); |
| | | if (cancelGrid != null && cancelGrid.isHasLongOrder()) { |
| | | executor.cancelConditionalOrder(cancelGrid.getLongOrderId(), oid -> { |
| | | longEntryTraderIdParam(cancelGrid, null, false); |
| | | log.info("[Gate] 多仓止损触发, 取消gridId:{}的多单", cancelGridId); |
| | | }); |
| | | for (String cancelOrderId : new ArrayList<>(cancelGrid.getLongOrderIds())) { |
| | | executor.cancelConditionalOrder(cancelOrderId, oid -> { |
| | | longEntryTraderIdParam(cancelGrid, cancelOrderId, false); |
| | | log.info("[Gate] 多仓止损触发, 取消gridId:{}的多单,{}", cancelGridId, cancelOrderId); |
| | | }); |
| | | } |
| | | } |
| | | |
| | | // 止损触发时,取消最远的多仓止盈订单 |
| | |
| | | } |
| | | } |
| | | |
| | | private void handleShortStopLossTriggered(GridElement gridElement) { |
| | | gridElement.clearShortStopLossOrderIds(); |
| | | private void handleShortStopLossTriggered(GridElement gridElement, String orderId) { |
| | | gridElement.removeShortStopLossOrderId(orderId); |
| | | |
| | | accumulatedShortLossCount++; |
| | | int gridId = gridElement.getId(); |
| | |
| | | return; |
| | | } |
| | | |
| | | // [Gate-BugFix] 防止与"仓位归零"重复下单:若该网格已有挂单则跳过 |
| | | if (!newEntryGrid.isHasShortOrder()) { |
| | | BigDecimal triggerPrice = newEntryGrid.getGridPrice(); |
| | | |
| | | // 止损触发后持仓在减少,取REST和WS缓存中较小值更准确 |
| | | int posSize = Math.min(queryPositionSize(Position.ModeEnum.DUAL_SHORT), shortPositionSize.intValue()); |
| | | int maxPos = config.getMaxPositionSize(); |
| | | // 止损阶梯:止损次数≤阈值时挂单量=单笔数量,超过后恢复默认逻辑(quantity*2) |
| | | int targetAmount; |
| | | if (config.getStopLossCount() > 0 && accumulatedShortLossCount <= config.getStopLossCount()) { |
| | | targetAmount = Integer.parseInt(config.getQuantity()); |
| | | // 止损追单:同一网格可有多笔挂单,不判断 isHasShortOrder,直接挂单 |
| | | BigDecimal triggerPrice = newEntryGrid.getGridPrice(); |
| | | int posSize = Math.min(queryPositionSize(Position.ModeEnum.DUAL_SHORT), shortPositionSize.intValue()); |
| | | int maxPos = config.getMaxPositionSize(); |
| | | int targetAmount; |
| | | if (config.getStopLossCount() > 0 && accumulatedShortLossCount <= config.getStopLossCount()) { |
| | | targetAmount = Integer.parseInt(config.getQuantity()); |
| | | } else { |
| | | targetAmount = Integer.parseInt(config.getQuantity()) * 2; |
| | | } |
| | | int addSize; |
| | | if (maxPos > 0) { |
| | | int remainingRoom = maxPos - posSize; |
| | | if (remainingRoom <= 0) { |
| | | log.warn("[Gate] 空仓止损触发 gridId:{}, 当前持仓{}/{}已达上限,跳过追单", |
| | | gridId, posSize, maxPos); |
| | | addSize = 0; |
| | | } else { |
| | | targetAmount = Integer.parseInt(config.getQuantity()) * 2; // quantity + 本次止损量 |
| | | addSize = Math.min(remainingRoom, targetAmount); |
| | | } |
| | | int addSize; |
| | | if (maxPos > 0) { |
| | | int remainingRoom = maxPos - posSize; |
| | | if (remainingRoom <= 0) { |
| | | log.warn("[Gate] 空仓止损触发 gridId:{}, 当前持仓{}/{}已达上限,跳过追单", |
| | | gridId, posSize, maxPos); |
| | | addSize = 0; |
| | | } else { |
| | | addSize = Math.min(remainingRoom, targetAmount); |
| | | } |
| | | } else { |
| | | addSize = targetAmount; |
| | | } |
| | | if (addSize > 0) { |
| | | String size = String.valueOf(addSize); |
| | | log.info("[Gate] 空仓止损触发 gridId:{}, 止损次数:{}, 在gridId:{}补{}张空单(当前{}/上限{})", |
| | | gridId, accumulatedShortLossCount, newEntryGridId, size, posSize, maxPos > 0 ? maxPos : "无"); |
| | | newEntryGrid.getShortTraderParam().setQuantity(size); |
| | | placeEntryOrderWithPreFlag(newEntryGrid, false, triggerPrice, |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_2, negate(size)); |
| | | } |
| | | }else{ |
| | | log.warn("[Gate] 空仓止损触发 gridId:{}, 目标gridId:{}已有挂单,跳过重复下单", gridId, newEntryGridId); |
| | | } else { |
| | | addSize = targetAmount; |
| | | } |
| | | if (addSize > 0) { |
| | | String size = String.valueOf(addSize); |
| | | log.info("[Gate] 空仓止损触发 gridId:{}, 止损次数:{}, 在gridId:{}补{}张空单(当前{}/上限{})", |
| | | gridId, accumulatedShortLossCount, newEntryGridId, size, posSize, maxPos > 0 ? maxPos : "无"); |
| | | newEntryGrid.getShortTraderParam().setQuantity(size); |
| | | placeEntryOrderWithPreFlag(newEntryGrid, false, triggerPrice, |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_2, negate(size)); |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | // 取消 gridId-2 位置的所有空单 |
| | | int cancelGridId = gridId - 2; |
| | | GridElement cancelGrid = GridElement.findById(cancelGridId); |
| | | if (cancelGrid != null && cancelGrid.isHasShortOrder()) { |
| | | executor.cancelConditionalOrder(cancelGrid.getShortOrderId(), oid -> { |
| | | shortEntryTraderIdParam(cancelGrid, null, false); |
| | | log.info("[Gate] 空仓止损触发, 取消gridId:{}的空单", cancelGridId); |
| | | }); |
| | | for (String cancelOrderId : new ArrayList<>(cancelGrid.getShortOrderIds())) { |
| | | executor.cancelConditionalOrder(cancelOrderId, oid -> { |
| | | shortEntryTraderIdParam(cancelGrid, cancelOrderId, false); |
| | | log.info("[Gate] 空仓止损触发, 取消gridId:{}的空单,{}", cancelGridId, cancelOrderId); |
| | | }); |
| | | } |
| | | } |
| | | |
| | | // 止损触发时,取消最远的空仓止盈订单 |
| | |
| | | } |
| | | }, |
| | | () -> { |
| | | // 仅当列表为空(无其他有效订单)时才清预置标志,避免误伤其他并发挂单 |
| | | if (isLong) { |
| | | gridElement.setHasLongOrder(false); |
| | | gridElement.setLongOrderId(null); |
| | | if (!gridElement.hasLongOrderIds()) { |
| | | gridElement.setHasLongOrder(false); |
| | | } |
| | | } else { |
| | | gridElement.setHasShortOrder(false); |
| | | gridElement.setShortOrderId(null); |
| | | if (!gridElement.hasShortOrderIds()) { |
| | | gridElement.setHasShortOrder(false); |
| | | } |
| | | } |
| | | GridElement.refreshIndices(); |
| | | log.warn("[Gate] 条件单创建失败,回滚标志位 gridId:{}, isLong:{}", gridElement.getId(), isLong); |
| | | log.warn("[Gate] 条件单创建失败 gridId:{}, isLong:{}", gridElement.getId(), isLong); |
| | | } |
| | | ); |
| | | } |