| | |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | |
| | | import com.xcong.excoin.modules.gateApi.wsHandler.handler.CandlestickChannelHandler; |
| | | import com.xcong.excoin.modules.gateApi.wsHandler.handler.PositionClosesChannelHandler; |
| | | import com.xcong.excoin.modules.gateApi.wsHandler.handler.PositionsChannelHandler; |
| | | |
| | | /** |
| | | * Gate 网格交易服务 — 策略核心。 |
| | | * |
| | |
| | | /** 空仓止盈队列,降序排列(大→小),仓位推送时消费 */ |
| | | private final List<BigDecimal> shortTakeProfitQueue = Collections.synchronizedList(new ArrayList<>()); |
| | | |
| | | /** 当前多仓限价单 ID,用于取消旧单 */ |
| | | private volatile String currentLongOrderId; |
| | | /** 当前空仓限价单 ID,用于取消旧单 */ |
| | | private volatile String currentShortOrderId; |
| | | /** 当前多仓条件单 ID 集合,用于取消旧单 */ |
| | | private final List<String> currentLongOrderIds = Collections.synchronizedList(new ArrayList<>()); |
| | | /** 当前空仓条件单 ID 集合,用于取消旧单 */ |
| | | private final List<String> currentShortOrderIds = Collections.synchronizedList(new ArrayList<>()); |
| | | |
| | | /** 基底空头入场价 */ |
| | | private BigDecimal shortBaseEntryPrice; |
| | |
| | | longPriceQueue.clear(); |
| | | longTakeProfitQueue.clear(); |
| | | shortTakeProfitQueue.clear(); |
| | | currentLongOrderId = null; |
| | | currentShortOrderId = null; |
| | | currentLongOrderIds.clear(); |
| | | currentShortOrderIds.clear(); |
| | | log.info("[Gate] 网格策略已启动"); |
| | | } |
| | | |
| | |
| | | |
| | | executor.placeConditionalEntryOrder(longPriceQueue.get(0), |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_1, config.getQuantity(), |
| | | orderId -> { currentLongOrderId = orderId; log.info("[Gate] 初始条件多单已挂, id:{}, trigger:{}", orderId, longPriceQueue.get(0)); }, |
| | | orderId -> { currentLongOrderIds.add(orderId); log.info("[Gate] 初始条件多单已挂, id:{}, trigger:{}", orderId, longPriceQueue.get(0)); }, |
| | | null); |
| | | executor.placeConditionalEntryOrder(shortPriceQueue.get(0), |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_2, negate(config.getQuantity()), |
| | | orderId -> { currentShortOrderId = orderId; log.info("[Gate] 初始条件空单已挂, id:{}, trigger:{}", orderId, shortPriceQueue.get(0)); }, |
| | | orderId -> { currentShortOrderIds.add(orderId); log.info("[Gate] 初始条件空单已挂, id:{}, trigger:{}", orderId, shortPriceQueue.get(0)); }, |
| | | null); |
| | | |
| | | state = StrategyState.ACTIVE; |
| | |
| | | if (!isMarginSafe()) { |
| | | log.warn("[Gate] 保证金超限,跳过挂条件单"); |
| | | } else { |
| | | String oldLongId = currentLongOrderId; |
| | | executor.cancelConditionalOrder(oldLongId); |
| | | synchronized (currentLongOrderIds) { |
| | | for (String id : currentLongOrderIds) { |
| | | executor.cancelConditionalOrder(id); |
| | | } |
| | | currentLongOrderIds.clear(); |
| | | } |
| | | executor.placeConditionalEntryOrder(newShortFirst, |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_2, negate(config.getQuantity()), |
| | | orderId -> { currentShortOrderId = orderId; log.info("[Gate] 新条件空单, id:{}, trigger:{}", orderId, newShortFirst); }, |
| | | orderId -> { currentShortOrderIds.add(orderId); log.info("[Gate] 新条件空单, id:{}, trigger:{}", orderId, newShortFirst); }, |
| | | null); |
| | | executor.placeConditionalEntryOrder(newLongFirst, |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_1, config.getQuantity(), |
| | | orderId -> { currentLongOrderId = orderId; log.info("[Gate] 新条件多单, id:{}, trigger:{}", orderId, newLongFirst); }, |
| | | orderId -> { currentLongOrderIds.add(orderId); log.info("[Gate] 新条件多单, id:{}, trigger:{}", orderId, newLongFirst); }, |
| | | null); |
| | | |
| | | if (newShortFirst.compareTo(shortEntryPrice) > 0 |
| | | && newShortFirst.compareTo(longEntryPrice) < 0 |
| | | && longPositionSize.compareTo(new BigDecimal("3")) < 0) { |
| | | BigDecimal reverseLongTp = newShortFirst.add(step).setScale(1, RoundingMode.HALF_UP); |
| | | longTakeProfitQueue.add(reverseLongTp); |
| | | longTakeProfitQueue.sort(BigDecimal::compareTo); |
| | | executor.placeConditionalEntryOrder(newShortFirst, |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_1, config.getQuantity(), |
| | | orderId -> { currentLongOrderIds.add(orderId); }, |
| | | null); |
| | | log.info("[Gate] 反向条件多单已挂, trigger:{}, size:{}, 止盈:{}", newShortFirst, config.getQuantity(), reverseLongTp); |
| | | } |
| | | } |
| | | |
| | | if (isMarginSafe() |
| | | && shortEntryPrice.compareTo(BigDecimal.ZERO) > 0 |
| | | && longEntryPrice.compareTo(BigDecimal.ZERO) > 0 |
| | | && longEntryPrice.compareTo(shortEntryPrice) > 0 |
| | | && currentPrice.compareTo(shortEntryPrice) > 0 |
| | | && currentPrice.compareTo(longEntryPrice) < 0) { |
| | | executor.openLong(config.getQuantity(), null, null); |
| | | log.info("[Gate] 额外反向开多, 当前价:{} 在[{},{}]之间", currentPrice, shortEntryPrice, longEntryPrice); |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | if (!isMarginSafe()) { |
| | | log.warn("[Gate] 保证金超限,跳过挂条件单"); |
| | | } else { |
| | | String oldShortId = currentShortOrderId; |
| | | executor.cancelConditionalOrder(oldShortId); |
| | | synchronized (currentShortOrderIds) { |
| | | for (String id : currentShortOrderIds) { |
| | | executor.cancelConditionalOrder(id); |
| | | } |
| | | currentShortOrderIds.clear(); |
| | | } |
| | | executor.placeConditionalEntryOrder(newLongFirst, |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_1, config.getQuantity(), |
| | | orderId -> { currentLongOrderId = orderId; log.info("[Gate] 新条件多单, id:{}, trigger:{}", orderId, newLongFirst); }, |
| | | orderId -> { currentLongOrderIds.add(orderId); log.info("[Gate] 新条件多单, id:{}, trigger:{}", orderId, newLongFirst); }, |
| | | null); |
| | | executor.placeConditionalEntryOrder(newShortFirst, |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_2, negate(config.getQuantity()), |
| | | orderId -> { currentShortOrderId = orderId; log.info("[Gate] 新条件空单, id:{}, trigger:{}", orderId, newShortFirst); }, |
| | | orderId -> { currentShortOrderIds.add(orderId); log.info("[Gate] 新条件空单, id:{}, trigger:{}", orderId, newShortFirst); }, |
| | | null); |
| | | |
| | | if (newLongFirst.compareTo(shortEntryPrice) > 0 |
| | | && newLongFirst.compareTo(longEntryPrice) < 0 |
| | | && shortPositionSize.compareTo(new BigDecimal("3")) < 0) { |
| | | BigDecimal reverseShortTp = newLongFirst.subtract(step).setScale(1, RoundingMode.HALF_UP); |
| | | shortTakeProfitQueue.add(reverseShortTp); |
| | | shortTakeProfitQueue.sort((a, b) -> b.compareTo(a)); |
| | | executor.placeConditionalEntryOrder(newLongFirst, |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_2, negate(config.getQuantity()), |
| | | orderId -> { currentShortOrderIds.add(orderId); }, |
| | | null); |
| | | log.info("[Gate] 反向条件空单已挂, trigger:{}, size:{}, 止盈:{}", newLongFirst, negate(config.getQuantity()), reverseShortTp); |
| | | } |
| | | } |
| | | |
| | | if (isMarginSafe() |
| | | && shortEntryPrice.compareTo(BigDecimal.ZERO) > 0 |
| | | && longEntryPrice.compareTo(BigDecimal.ZERO) > 0 |
| | | && longEntryPrice.compareTo(shortEntryPrice) > 0 |
| | | && currentPrice.compareTo(shortEntryPrice) > 0 |
| | | && currentPrice.compareTo(longEntryPrice) < 0) { |
| | | executor.openShort(negate(config.getQuantity()), null, null); |
| | | log.info("[Gate] 额外反向开空, 当前价:{} 在[{},{}]之间", currentPrice, shortEntryPrice, longEntryPrice); |
| | | } |
| | | } |
| | | |
| | | // ---- 保证金安全阀 ---- |