| | |
| | | accumulatedShortLossCount = 0; |
| | | shortPriceQueue.clear(); |
| | | longPriceQueue.clear(); |
| | | totalShortPriceQueue.clear(); |
| | | totalLongPriceQueue.clear(); |
| | | currentLongOrderIds.clear(); |
| | | currentShortOrderIds.clear(); |
| | | // 每次重启重新获取当前本金 |
| | |
| | | if (shortGridElement.isHasShortOrder() && StrUtil.isNotEmpty(tradeId) && !tradeId.equals("0") ){ |
| | | int filledQty = Integer.parseInt(shortGridElement.getShortTraderParam().getQuantity()); |
| | | shortEntryTraderIdParam(shortGridElement, null, false); |
| | | // [Gate-需求2] 加仓后先撤空仓所有止盈+止损,再重挂 |
| | | // [Gate-需求2] 加仓后先撤空仓所有止盈+止损,再查交易所持仓后重挂 |
| | | cancelAllShortTakeProfitsAndStopLosses(); |
| | | extendShortStopLoss(filledQty,shortGridElement.getId()); |
| | | int posSize = queryPositionSize(Position.ModeEnum.DUAL_SHORT); |
| | | extendShortStopLoss(posSize, shortGridElement.getId()); |
| | | accumulatedShortLossCount = 0; // 加仓订单成交,重置止损累计 |
| | | log.info("[Gate] 空单成交 gridId:{}", filledQty); |
| | | log.info("[Gate] 空单成交 gridId:{}, 当前持仓:{}张", filledQty, posSize); |
| | | |
| | | // 空仓持仓超过baseQuantity时,从gridId-2开始向外追挂止盈 |
| | | BigDecimal shortBaseQty = new BigDecimal(config.getBaseQuantity()); |
| | | BigDecimal shortGridQty = new BigDecimal(config.getQuantity()); |
| | | if (shortPositionSize.compareTo(shortBaseQty) > 0) { |
| | | BigDecimal shortExcess = shortPositionSize.subtract(shortBaseQty); |
| | | if (BigDecimal.valueOf(posSize).compareTo(shortBaseQty) > 0) { |
| | | BigDecimal shortExcess = BigDecimal.valueOf(posSize).subtract(shortBaseQty); |
| | | int shortExcessCount = shortExcess.divide(shortGridQty, 0, RoundingMode.DOWN).intValue(); |
| | | for (int i = 0; i < shortExcessCount; i++) { |
| | | int tpGridId = shortGridElement.getId() - 2 - i; |
| | |
| | | |
| | | int filledQty = Integer.parseInt(longGridElement.getLongTraderParam().getQuantity()); |
| | | longEntryTraderIdParam(longGridElement, null, false); |
| | | // [Gate-需求2] 加仓后先撤多仓所有止盈+止损,再重挂 |
| | | // [Gate-需求2] 加仓后先撤多仓所有止盈+止损,再查交易所持仓后重挂 |
| | | cancelAllLongTakeProfitsAndStopLosses(); |
| | | extendLongStopLoss(filledQty,longGridElement.getId()); |
| | | int posSize = queryPositionSize(Position.ModeEnum.DUAL_LONG); |
| | | extendLongStopLoss(posSize, longGridElement.getId()); |
| | | accumulatedLongLossCount = 0; // 加仓订单成交,重置止损累计 |
| | | log.info("[Gate] 多单成交 gridId:{}", filledQty); |
| | | log.info("[Gate] 多单成交 gridId:{}, 当前持仓:{}张", filledQty, posSize); |
| | | |
| | | // 多仓持仓超过baseQuantity时,从gridId+2开始向外追挂止盈 |
| | | BigDecimal longBaseQty = new BigDecimal(config.getBaseQuantity()); |
| | | BigDecimal longGridQty = new BigDecimal(config.getQuantity()); |
| | | if (longPositionSize.compareTo(longBaseQty) > 0) { |
| | | BigDecimal longExcess = longPositionSize.subtract(longBaseQty); |
| | | if (BigDecimal.valueOf(posSize).compareTo(longBaseQty) > 0) { |
| | | BigDecimal longExcess = BigDecimal.valueOf(posSize).subtract(longBaseQty); |
| | | int longExcessCount = longExcess.divide(longGridQty, 0, RoundingMode.DOWN).intValue(); |
| | | for (int i = 0; i < longExcessCount; i++) { |
| | | int tpGridId = longGridElement.getId() + 2 + i; |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 查询交易所当前持仓张数(绝对值)。加仓/开仓后重挂止盈止损时调用, |
| | | * 绕过本地 WS 推送缓存避免时序竞态,直接拿到交易所权威数据。 |
| | | * |
| | | * @param mode 持仓模式(DUAL_LONG / DUAL_SHORT) |
| | | * @return 持仓张数(绝对值),查询失败返回 0 |
| | | */ |
| | | private int queryPositionSize(Position.ModeEnum mode) { |
| | | try { |
| | | List<Position> positions = futuresApi.listPositions(SETTLE).execute(); |
| | | if (positions != null) { |
| | | for (Position p : positions) { |
| | | if (mode == p.getMode() && config.getContract().equals(p.getContract())) { |
| | | return new BigDecimal(p.getSize()).abs().intValue(); |
| | | } |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | log.warn("[Gate] 查询{}持仓失败", mode, e); |
| | | } |
| | | return 0; |
| | | } |
| | | |
| | | // ---- 网格队列处理 ---- |
| | | |
| | |
| | | */ |
| | | private void generateShortQueue() { |
| | | shortPriceQueue.clear(); |
| | | totalShortPriceQueue.clear(); |
| | | totalLongPriceQueue.clear(); |
| | | int prec = config.getPriceScale(); |
| | | BigDecimal step = shortBaseEntryPrice.multiply(config.getGridRate()).setScale(prec, RoundingMode.HALF_UP); |
| | | config.setStep(step); |
| | |
| | | elements.add(GridElement.builder() |
| | | .id(0) |
| | | .gridPrice(price) |
| | | .upId(shortSize > 0 ? 1 : null) |
| | | .downId(longSize > 0 ? -1 : null) |
| | | .upId(longSize > 0 ? 1 : null) |
| | | .downId(shortSize > 0 ? -1 : null) |
| | | .longTraderParam(longParam) |
| | | .shortTraderParam(shortParam) |
| | | .build()); |
| | |
| | | }); |
| | | } |
| | | } |
| | | |
| | | // ========== 止盈/止损取消辅助方法 ========== |
| | | |
| | | /** |
| | | * 取消最远的多仓止损订单。 |
| | | * 多仓止损在 gridId 负方向,最远 = id 最小。 |
| | | */ |
| | | private void cancelFarthestLongStopLoss() { |
| | | GridElement farthest = null; |
| | | for (GridElement e : config.getGridElements()) { |
| | | if (e.getLongStopLossOrderId() != null) { |
| | | if (farthest == null || e.getId() < farthest.getId()) { |
| | | farthest = e; |
| | | } |
| | | } |
| | | } |
| | | if (farthest != null) { |
| | | String slId = farthest.getLongStopLossOrderId(); |
| | | farthest.setLongStopLossOrderId(null); |
| | | GridElement.refreshIndices(); |
| | | GridElement finalFarthest = farthest; |
| | | executor.cancelConditionalOrder(slId, oid -> |
| | | log.info("[Gate] 止盈触发, 取消最远多仓止损 gridId:{}, orderId:{}", finalFarthest.getId(), slId)); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 取消最远的空仓止损订单。 |
| | | * 空仓止损在 gridId 正方向,最远 = id 最大。 |
| | | */ |
| | | private void cancelFarthestShortStopLoss() { |
| | | GridElement farthest = null; |
| | | for (GridElement e : config.getGridElements()) { |
| | | if (e.getShortStopLossOrderId() != null) { |
| | | if (farthest == null || e.getId() > farthest.getId()) { |
| | | farthest = e; |
| | | } |
| | | } |
| | | } |
| | | if (farthest != null) { |
| | | String slId = farthest.getShortStopLossOrderId(); |
| | | farthest.setShortStopLossOrderId(null); |
| | | GridElement.refreshIndices(); |
| | | GridElement finalFarthest = farthest; |
| | | executor.cancelConditionalOrder(slId, oid -> |
| | | log.info("[Gate] 止盈触发, 取消最远空仓止损 gridId:{}, orderId:{}", finalFarthest.getId(), slId)); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 取消所有多仓止盈 + 多仓止损订单(加仓后重建前清场)。 |
| | | */ |
| | | private void cancelAllLongTakeProfitsAndStopLosses() { |
| | | for (GridElement e : config.getGridElements()) { |
| | | String tpId = e.getLongTakeProfitOrderId(); |
| | | if (tpId != null) { |
| | | e.setLongTakeProfitOrderId(null); |
| | | executor.cancelConditionalOrder(tpId, oid -> {}); |
| | | } |
| | | String slId = e.getLongStopLossOrderId(); |
| | | if (slId != null) { |
| | | e.setLongStopLossOrderId(null); |
| | | executor.cancelConditionalOrder(slId, oid -> {}); |
| | | } |
| | | } |
| | | GridElement.refreshIndices(); |
| | | log.info("[Gate] 已提交取消所有多仓止盈+止损"); |
| | | } |
| | | |
| | | /** |
| | | * 取消所有空仓止盈 + 空仓止损订单(加仓后重建前清场)。 |
| | | */ |
| | | private void cancelAllShortTakeProfitsAndStopLosses() { |
| | | for (GridElement e : config.getGridElements()) { |
| | | String tpId = e.getShortTakeProfitOrderId(); |
| | | if (tpId != null) { |
| | | e.setShortTakeProfitOrderId(null); |
| | | executor.cancelConditionalOrder(tpId, oid -> {}); |
| | | } |
| | | String slId = e.getShortStopLossOrderId(); |
| | | if (slId != null) { |
| | | e.setShortStopLossOrderId(null); |
| | | executor.cancelConditionalOrder(slId, oid -> {}); |
| | | } |
| | | } |
| | | GridElement.refreshIndices(); |
| | | log.info("[Gate] 已提交取消所有空仓止盈+止损"); |
| | | } |
| | | |
| | | // ========== 止损追单 ========== |
| | | |
| | | private void extendLongStopLoss(int filledQty,int gridId) { |
| | | int furthestSlId = 0; |