| | |
| | | /** 多头是否活跃(有仓位) */ |
| | | private volatile boolean longActive = false; |
| | | |
| | | /** 多头累计止损次数(加仓订单成交后归零),同一网格多次止损不累加 */ |
| | | /** 多头累计止损次数(始终递增,加仓订单成交后归零) */ |
| | | private volatile int accumulatedLongLossCount = 0; |
| | | /** 空头累计止损次数(加仓订单成交后归零),同一网格多次止损不累加 */ |
| | | /** 空头累计止损次数(始终递增,加仓订单成交后归零) */ |
| | | private volatile int accumulatedShortLossCount = 0; |
| | | /** 最近触发止损的多头网格ID,用于判断是否是同一网格的连续止损 */ |
| | | /** 最近触发止损的多头网格ID */ |
| | | private volatile int lastLongStopLossGridId = Integer.MAX_VALUE; |
| | | /** 最近触发止损的空头网格ID,用于判断是否是同一网格的连续止损 */ |
| | | /** 最近触发止损的空头网格ID */ |
| | | private volatile int lastShortStopLossGridId = Integer.MAX_VALUE; |
| | | |
| | | private volatile BigDecimal lastKlinePrice; |
| | |
| | | if (StrUtil.isNotEmpty(tradeId) && !tradeId.equals("0") ){ |
| | | int filledQty = Integer.parseInt(shortGridElement.getShortTraderParam().getQuantity()); |
| | | shortEntryTraderIdParam(shortGridElement, orderId, false); |
| | | accumulatedShortLossCount = 0; // 空单成交,重置止损追单计数 |
| | | accumulatedShortLossCount = 0; |
| | | lastShortStopLossGridId = Integer.MAX_VALUE; |
| | | // [Gate-需求2] 加仓后先撤空仓所有止盈+止损,再查交易所持仓后重挂 |
| | | cancelAllShortTakeProfitsAndStopLosses(); |
| | |
| | | // } |
| | | // ); |
| | | |
| | | // 空仓持仓超过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(); |
| | | // 空仓止盈:超额部分挂止盈,从entry网格向下逐个挂 |
| | | int shortBaseQty = Integer.parseInt(config.getBaseQuantity()); |
| | | int shortGridQty = Integer.parseInt(config.getQuantity()); |
| | | int shortTpCount = posSize > shortBaseQty ? (posSize - shortBaseQty) / shortGridQty : 0; |
| | | |
| | | // // 找多仓第一个(最近的)止损位置 |
| | | // int firstLongSlId = 0; |
| | | // for (GridElement e : config.getGridElements()) { |
| | | // if (e.hasLongStopLossOrders()) { |
| | | // if (firstLongSlId == 0 || e.getId() > firstLongSlId) { |
| | | // firstLongSlId = e.getId(); |
| | | // } |
| | | // } |
| | | // } |
| | | |
| | | for (int i = 0; i < shortExcessCount; i++) { |
| | | int 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); |
| | | } |
| | | ); |
| | | for (int i = 0; i < shortTpCount; i++) { |
| | | int 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 filledQty = Integer.parseInt(longGridElement.getLongTraderParam().getQuantity()); |
| | | longEntryTraderIdParam(longGridElement, orderId, false); |
| | | accumulatedLongLossCount = 0; // 多单成交,重置止损追单计数 |
| | | accumulatedLongLossCount = 0; |
| | | lastLongStopLossGridId = Integer.MAX_VALUE; |
| | | // [Gate-需求2] 加仓后先撤多仓所有止盈+止损,再查交易所持仓后重挂 |
| | | cancelAllLongTakeProfitsAndStopLosses(); |
| | |
| | | // } |
| | | // ); |
| | | |
| | | // 多仓持仓超过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(); |
| | | // 多仓止盈:超额部分挂止盈,从entry网格向上逐个挂 |
| | | int longBaseQty = Integer.parseInt(config.getBaseQuantity()); |
| | | int longGridQty = Integer.parseInt(config.getQuantity()); |
| | | int longTpCount = posSize > longBaseQty ? (posSize - longBaseQty) / longGridQty : 0; |
| | | |
| | | // // 找空仓第一个(最近的)止损位置 |
| | | // int firstShortSlId = 0; |
| | | // for (GridElement e : config.getGridElements()) { |
| | | // if (e.hasShortStopLossOrders()) { |
| | | // if (firstShortSlId == 0 || e.getId() < firstShortSlId) { |
| | | // firstShortSlId = e.getId(); |
| | | // } |
| | | // } |
| | | // } |
| | | |
| | | for (int i = 0; i < longExcessCount; i++) { |
| | | int 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); |
| | | } |
| | | ); |
| | | for (int i = 0; i < longTpCount; i++) { |
| | | int 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); |
| | | } |
| | | ); |
| | | } |
| | | } |
| | | } |
| | |
| | | gridElement.removeLongStopLossOrderId(orderId); |
| | | |
| | | int gridId = gridElement.getId(); |
| | | // 同一网格的多个止损单触发不累加计数,视为同一批次 |
| | | if (gridId != lastLongStopLossGridId) { |
| | | accumulatedLongLossCount++; |
| | | lastLongStopLossGridId = gridId; |
| | | } |
| | | log.info("[Gate] 多仓止损触发 gridId:{}, 止损次数:{}, 开始追单", gridId, accumulatedLongLossCount); |
| | | boolean sameGrid = (gridId == lastLongStopLossGridId); |
| | | accumulatedLongLossCount++; |
| | | lastLongStopLossGridId = gridId; |
| | | log.info("[Gate] 多仓止损触发 gridId:{}, 止损次数:{}{}, 开始追单", |
| | | gridId, accumulatedLongLossCount, sameGrid ? "(同网格)" : ""); |
| | | int newEntryGridId = gridId + 1; |
| | | |
| | | GridElement newEntryGrid = GridElement.findById(newEntryGridId); |
| | |
| | | BigDecimal triggerPrice = newEntryGrid.getGridPrice(); |
| | | int posSize = Math.min(queryPositionSize(Position.ModeEnum.DUAL_LONG), longPositionSize.intValue()); |
| | | int maxPos = config.getMaxPositionSize(); |
| | | int targetAmount = accumulatedLongLossCount + 1; |
| | | int targetAmount = sameGrid ? Integer.parseInt(config.getQuantity()) : accumulatedLongLossCount + 1; |
| | | int addSize; |
| | | if (maxPos > 0) { |
| | | int remainingRoom = maxPos - posSize; |
| | |
| | | gridElement.removeShortStopLossOrderId(orderId); |
| | | |
| | | int gridId = gridElement.getId(); |
| | | // 同一网格的多个止损单触发不累加计数,视为同一批次 |
| | | if (gridId != lastShortStopLossGridId) { |
| | | accumulatedShortLossCount++; |
| | | lastShortStopLossGridId = gridId; |
| | | } |
| | | log.info("[Gate] 空仓止损触发 gridId:{}, 止损次数:{}, 开始追单", gridId, accumulatedShortLossCount); |
| | | boolean sameGrid = (gridId == lastShortStopLossGridId); |
| | | accumulatedShortLossCount++; |
| | | lastShortStopLossGridId = gridId; |
| | | log.info("[Gate] 空仓止损触发 gridId:{}, 止损次数:{}{}, 开始追单", |
| | | gridId, accumulatedShortLossCount, sameGrid ? "(同网格)" : ""); |
| | | int newEntryGridId = gridId - 1; |
| | | |
| | | GridElement newEntryGrid = GridElement.findById(newEntryGridId); |
| | |
| | | BigDecimal triggerPrice = newEntryGrid.getGridPrice(); |
| | | int posSize = Math.min(queryPositionSize(Position.ModeEnum.DUAL_SHORT), shortPositionSize.intValue()); |
| | | int maxPos = config.getMaxPositionSize(); |
| | | int targetAmount = accumulatedShortLossCount + 1; |
| | | int targetAmount = sameGrid ? Integer.parseInt(config.getQuantity()) : accumulatedShortLossCount + 1; |
| | | int addSize; |
| | | if (maxPos > 0) { |
| | | int remainingRoom = maxPos - posSize; |
| | |
| | | private void extendLongStopLoss(int filledQty, int gridId) { |
| | | int qty = Integer.parseInt(config.getQuantity()); |
| | | int aCount = Math.max(0, filledQty / qty - 2); |
| | | log.info("[Gate] 多仓追挂止损, 成交{}张, gridId:{}, A({})x{} B({})x1 C({})x1", |
| | | filledQty, gridId, gridId - 2, aCount, gridId - 3, gridId - 4); |
| | | if (aCount < 0) return; |
| | | |
| | | // A 位置:挂 aCount 个独立止损单 |
| | | placeStopLossOrders(gridId - 2, aCount, qty, FuturesPriceTrigger.RuleEnum.NUMBER_2, |
| | | int a, b, c; |
| | | if (aCount == 0) { a = 2; b = 0; c = 0; } |
| | | else if (aCount == 1) { a = 2; b = 1; c = 0; } |
| | | else { a = aCount; b = 1; c = 1; } |
| | | |
| | | log.info("[Gate] 多仓追挂止损, 成交{}张, gridId:{}, A({})x{} B({})x{} C({})x{}", |
| | | filledQty, gridId, gridId - 2, a, gridId - 3, b, gridId - 4, c); |
| | | |
| | | if (a > 0) placeStopLossOrders(gridId - 2, a, qty, FuturesPriceTrigger.RuleEnum.NUMBER_2, |
| | | ORDER_TYPE_CLOSE_LONG, true); |
| | | // B 位置:挂 1 个 |
| | | placeStopLossOrders(gridId - 3, 1, qty, FuturesPriceTrigger.RuleEnum.NUMBER_2, |
| | | if (b > 0) placeStopLossOrders(gridId - 3, b, qty, FuturesPriceTrigger.RuleEnum.NUMBER_2, |
| | | ORDER_TYPE_CLOSE_LONG, true); |
| | | // C 位置:挂 1 个 |
| | | placeStopLossOrders(gridId - 4, 1, qty, FuturesPriceTrigger.RuleEnum.NUMBER_2, |
| | | if (c > 0) placeStopLossOrders(gridId - 4, c, qty, FuturesPriceTrigger.RuleEnum.NUMBER_2, |
| | | ORDER_TYPE_CLOSE_LONG, true); |
| | | } |
| | | |
| | | /** |
| | | * 空仓止损追单 — 3 个固定位置,从近到远分配合约。 |
| | | * A(gridId+2): (filledQty/qty - 2) 个独立止损单,每个 qty 张 |
| | | * B(gridId+3): 1 个止损单,qty 张 |
| | | * C(gridId+4): 1 个止损单,qty 张 |
| | | * 注意:空仓止损在 gridId 正方向,触发价 ≥ 网格价。 |
| | | * aCount<0跳过, aCount=0→A=2, aCount=1→A=2,B=1, aCount>=2→A=aCount,B=1,C=1 |
| | | */ |
| | | private void extendShortStopLoss(int filledQty, int gridId) { |
| | | int qty = Integer.parseInt(config.getQuantity()); |
| | | int aCount = Math.max(0, filledQty / qty - 2); |
| | | log.info("[Gate] 空仓追挂止损, 成交{}张, gridId:{}, A({})x{} B({})x1 C({})x1", |
| | | filledQty, gridId, gridId + 2, aCount, gridId + 3, gridId + 4); |
| | | if (aCount < 0) return; |
| | | |
| | | // A 位置:挂 aCount 个独立止损单 |
| | | placeStopLossOrders(gridId + 2, aCount, qty, FuturesPriceTrigger.RuleEnum.NUMBER_1, |
| | | int a, b, c; |
| | | if (aCount == 0) { a = 2; b = 0; c = 0; } |
| | | else if (aCount == 1) { a = 2; b = 1; c = 0; } |
| | | else { a = aCount; b = 1; c = 1; } |
| | | |
| | | log.info("[Gate] 空仓追挂止损, 成交{}张, gridId:{}, A({})x{} B({})x{} C({})x{}", |
| | | filledQty, gridId, gridId + 2, a, gridId + 3, b, gridId + 4, c); |
| | | |
| | | if (a > 0) placeStopLossOrders(gridId + 2, a, qty, FuturesPriceTrigger.RuleEnum.NUMBER_1, |
| | | ORDER_TYPE_CLOSE_SHORT, false); |
| | | // B 位置:挂 1 个 |
| | | placeStopLossOrders(gridId + 3, 1, qty, FuturesPriceTrigger.RuleEnum.NUMBER_1, |
| | | if (b > 0) placeStopLossOrders(gridId + 3, b, qty, FuturesPriceTrigger.RuleEnum.NUMBER_1, |
| | | ORDER_TYPE_CLOSE_SHORT, false); |
| | | // C 位置:挂 1 个 |
| | | placeStopLossOrders(gridId + 4, 1, qty, FuturesPriceTrigger.RuleEnum.NUMBER_1, |
| | | if (c > 0) placeStopLossOrders(gridId + 4, c, qty, FuturesPriceTrigger.RuleEnum.NUMBER_1, |
| | | ORDER_TYPE_CLOSE_SHORT, false); |
| | | } |
| | | |