| | |
| | | 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; |
| | |
| | | shortActive = false; |
| | | accumulatedLongLossCount = 0; |
| | | accumulatedShortLossCount = 0; |
| | | firstFillAfterInit = true; |
| | | shortPriceQueue.clear(); |
| | | longPriceQueue.clear(); |
| | | totalShortPriceQueue.clear(); |
| | |
| | | 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 本地缓存取最大值兜底 |
| | |
| | | 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); |
| | |
| | | |
| | | int filledQty = Integer.parseInt(longGridElement.getLongTraderParam().getQuantity()); |
| | | longEntryTraderIdParam(longGridElement, null, false); |
| | | // 首次挂单成交,取消初始化阶段挂的所有止盈单 |
| | | if (firstFillAfterInit) { |
| | | cancelAllInitialTakeProfits(); |
| | | firstFillAfterInit = false; |
| | | } |
| | | // [Gate-需求2] 加仓后先撤多仓所有止盈+止损,再查交易所持仓后重挂 |
| | | cancelAllLongTakeProfitsAndStopLosses(); |
| | | // REST 查询可能因交易所延迟返回旧值,与 WS 本地缓存取最大值兜底 |
| | |
| | | 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); |
| | | // } |
| | | // ); |
| | | // } |
| | | // } |
| | | } |
| | | } |
| | | } |
| | |
| | | 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, |
| | |
| | | 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, |
| | |
| | | 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) { |