| | |
| | | * @param closePrice K 线收盘价(即当前最新成交价) |
| | | */ |
| | | public void onKline(BigDecimal closePrice) { |
| | | |
| | | lastKlinePrice = closePrice; |
| | | |
| | | //初始化0位置的开仓,并且用空的开仓价格,作为价格基准来划分网格 |
| | |
| | | return; |
| | | } |
| | | |
| | | |
| | | // checkProfitAndReset(); |
| | | |
| | | checkProfitAndReset(); |
| | | |
| | | if (state == StrategyState.ACTIVE && |
| | | longActive == false && |
| | |
| | | /** Gate 永续合约 taker 费率 0.05% */ |
| | | private static final BigDecimal TAKER_FEE_RATE = new BigDecimal("0.0005"); |
| | | private void checkProfitAndReset() { |
| | | if (state == StrategyState.STOPPED || state == StrategyState.WAITING_KLINE) { |
| | | return; |
| | | } |
| | | try { |
| | | |
| | | BigDecimal target = initialPrincipal.add(config.getExpectedProfit()); |
| | | |
| | | FuturesAccount account = futuresApi.listFuturesAccounts(SETTLE); |
| | | BigDecimal unrealisedPnl = new BigDecimal(account.getCrossUnrealisedPnl()); |
| | | BigDecimal available = new BigDecimal(account.getCrossAvailable()); |
| | | BigDecimal totalEquity = unrealisedPnl.add(available); |
| | | BigDecimal totalEquity = new BigDecimal(account.getTotal()).add(new BigDecimal(account.getUnrealisedPnl())); |
| | | |
| | | // 估算平仓手续费:(多仓张数+空仓张数) × 合约面值 × 当前价 × taker费率 |
| | | BigDecimal totalSize = longPositionSize.abs().add(shortPositionSize.abs()); |
| | | BigDecimal closeContractValue = |
| | | totalSize.multiply(config.getContractMultiplier()).multiply(lastKlinePrice != null ? lastKlinePrice : BigDecimal.ZERO); |
| | | BigDecimal estimatedFee = closeContractValue.multiply(TAKER_FEE_RATE); |
| | | BigDecimal netEquity = totalEquity.subtract(estimatedFee); |
| | | log.info("[Gate] 盈亏检查,总张数:{}, upl:{}, avail:{}, 合计:{}, 估手续费:{}, 净权益:{}, 目标:{}", |
| | | totalSize,unrealisedPnl, available, totalEquity, estimatedFee, netEquity, target); |
| | | if (netEquity.compareTo(target) > 0) { |
| | | log.info("[Gate] 盈亏达标(净权益{}>目标{}),重置策略", netEquity, target); |
| | | if (totalEquity.compareTo(target) > 0) { |
| | | log.info("[Gate] 盈亏达标(净权益{}>目标{}),重置策略", totalEquity, target); |
| | | state = StrategyState.STOPPED; |
| | | try { |
| | | futuresApi.cancelPriceTriggeredOrderList(SETTLE, config.getContract()); |
| | |
| | | return; |
| | | } |
| | | |
| | | // [Gate-需求1] 多仓止盈触发:清空止盈状态 + 取消最远多仓止损 + 检查是否最后一个止盈 |
| | | // [Gate-需求1] 多仓止盈触发:清空止盈状态 + 取消最近多仓止损 |
| | | GridElement longTpElem = GridElement.findByLongTakeProfitOrderId(orderId); |
| | | if (longTpElem != null && StrUtil.isNotEmpty(tradeId) && !tradeId.equals("0")) { |
| | | longTakeProfitTraderIdParam(longTpElem, null, false); |
| | | log.info("[Gate] 多仓止盈触发 gridId:{}, orderId:{}", longTpElem.getId(), orderId); |
| | | cancelFarthestLongStopLoss(); |
| | | // checkLastTakeProfitAndRestart(); |
| | | cancelNearestLongStopLoss(); |
| | | return; |
| | | } |
| | | // [Gate-需求1] 空仓止盈触发:清空止盈状态 + 取消最远空仓止损 + 检查是否最后一个止盈 |
| | | // [Gate-需求1] 空仓止盈触发:清空止盈状态 + 取消最近空仓止损 |
| | | GridElement shortTpElem = GridElement.findByShortTakeProfitOrderId(orderId); |
| | | if (shortTpElem != null && StrUtil.isNotEmpty(tradeId) && !tradeId.equals("0")) { |
| | | shortTakeProfitTraderIdParam(shortTpElem, null, false); |
| | | log.info("[Gate] 空仓止盈触发 gridId:{}, orderId:{}", shortTpElem.getId(), orderId); |
| | | cancelFarthestShortStopLoss(); |
| | | cancelNearestShortStopLoss(); |
| | | // checkLastTakeProfitAndRestart(); |
| | | return; |
| | | } |
| | |
| | | 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); |
| | | accumulatedShortLossCount = 0; // 空单成交,重置止损追单计数 |
| | | // [Gate-需求2] 加仓后先撤空仓所有止盈+止损,再查交易所持仓后重挂 |
| | | cancelAllShortTakeProfitsAndStopLosses(); |
| | | // REST 查询可能因交易所延迟返回旧值,与 WS 本地缓存取最大值兜底 |
| | | int posSize = Math.max(queryPositionSize(Position.ModeEnum.DUAL_SHORT), shortPositionSize.intValue()); |
| | | extendShortStopLoss(posSize, shortGridElement.getId()); |
| | | accumulatedShortLossCount = 0; // 加仓订单成交,重置止损累计 |
| | | log.info("[Gate] 空单成交 gridId:{}, 当前持仓:{}张", filledQty, posSize); |
| | | |
| | | // 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 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(); |
| | | } |
| | | } |
| | | } |
| | | // // 找多仓第一个(最近的)止损位置 |
| | | // 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; |
| | | if (firstLongSlId != 0) { |
| | | tpGridId = firstLongSlId - i; // 从多仓第一个止损位置开始,向下挂,间隔=1 |
| | | } else { |
| | | tpGridId = shortGridElement.getId() - 2 * (i + 1); // 无多仓止损时回退原逻辑 |
| | | } |
| | | int tpGridId = shortGridElement.getId() - 2 * (i + 1); |
| | | GridElement tpElem = GridElement.findById(tpGridId); |
| | | if (tpElem == null || tpElem.getShortTakeProfitOrderId() != null) { |
| | | continue; |
| | |
| | | } |
| | | 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); |
| | | accumulatedLongLossCount = 0; // 多单成交,重置止损追单计数 |
| | | // [Gate-需求2] 加仓后先撤多仓所有止盈+止损,再查交易所持仓后重挂 |
| | | cancelAllLongTakeProfitsAndStopLosses(); |
| | | // REST 查询可能因交易所延迟返回旧值,与 WS 本地缓存取最大值兜底 |
| | | int posSize = Math.max(queryPositionSize(Position.ModeEnum.DUAL_LONG), longPositionSize.intValue()); |
| | | extendLongStopLoss(posSize, longGridElement.getId()); |
| | | accumulatedLongLossCount = 0; // 加仓订单成交,重置止损累计 |
| | | log.info("[Gate] 多单成交 gridId:{}, 当前持仓:{}张", filledQty, posSize); |
| | | |
| | | // 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 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(); |
| | | } |
| | | } |
| | | } |
| | | // // 找空仓第一个(最近的)止损位置 |
| | | // 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; |
| | | if (firstShortSlId != 0) { |
| | | tpGridId = firstShortSlId + i; // 从空仓第一个止损位置开始,向上挂,间隔=1 |
| | | } else { |
| | | tpGridId = longGridElement.getId() + 2 * (i + 1); // 无空仓止损时回退原逻辑 |
| | | } |
| | | int tpGridId = longGridElement.getId() + 2 * (i + 1); |
| | | GridElement tpElem = GridElement.findById(tpGridId); |
| | | if (tpElem == null || tpElem.getLongTakeProfitOrderId() != null) { |
| | | continue; |
| | |
| | | baseGridElement.setShortOrderId(baseShortTraderParam.getEntryOrderId()); |
| | | baseGridElement.setHasShortOrder(true); |
| | | |
| | | // // 空仓止损对应的多仓止盈:多仓止盈挂在ID=3 |
| | | // int tpGridIdLong = 3; |
| | | // GridElement tpElemLong = GridElement.findById(tpGridIdLong); |
| | | // BigDecimal triggerPriceLong = tpElemLong.getGridPrice(); |
| | | // String sizeLong = config.getBaseQuantity(); |
| | | // executor.placeTakeProfit( |
| | | // triggerPriceLong, |
| | | // FuturesPriceTrigger.RuleEnum.NUMBER_1, |
| | | // ORDER_TYPE_CLOSE_LONG, |
| | | // negate(sizeLong), |
| | | // profitId -> { |
| | | // tpElemLong.setLongTakeProfitOrderId(profitId); |
| | | // GridElement.refreshIndices(); |
| | | // log.info("[Gate] 多仓止盈已挂(gridId:{}多止盈), 触发价:{}, takeProfitId:{}", |
| | | // tpGridIdLong, triggerPriceLong, profitId); |
| | | // } |
| | | // ); |
| | | // |
| | | // |
| | | // // 多仓止损对应的空仓止盈:空仓止盈挂在ID=-3 |
| | | // int tpGridIdShort = -3; |
| | | // GridElement tpElemShort = GridElement.findById(tpGridIdShort); |
| | | // BigDecimal triggerPriceShort = tpElemShort.getGridPrice(); |
| | | // String sizeShort = config.getBaseQuantity(); |
| | | // executor.placeTakeProfit( |
| | | // triggerPriceShort, |
| | | // FuturesPriceTrigger.RuleEnum.NUMBER_2, |
| | | // ORDER_TYPE_CLOSE_SHORT, |
| | | // sizeShort, |
| | | // profitId -> { |
| | | // tpElemShort.setShortTakeProfitOrderId(profitId); |
| | | // GridElement.refreshIndices(); |
| | | // log.info("[Gate] 空仓止盈已挂(gridId:{}空止盈), 触发价:{}, takeProfitId:{}", |
| | | // tpGridIdShort, triggerPriceShort, profitId); |
| | | // } |
| | | // ); |
| | | |
| | | // int shortTime = 2; |
| | | // GridElement elemShort = GridElement.findById(shortTime); |
| | | // if (elemShort != null) { |
| | |
| | | // ORDER_TYPE_CLOSE_SHORT, |
| | | // size, |
| | | // profitId -> { |
| | | // elemShort.setShortStopLossOrderId(profitId); |
| | | // elemShort.addShortStopLossOrderId(profitId); |
| | | // GridElement.refreshIndices(); |
| | | // log.info("[Gate] 空仓止损已挂, gridId:{}, 触发价:{}, stopLossId:{}", shortTime, triggerPrice, profitId); |
| | | // } |
| | |
| | | // ORDER_TYPE_CLOSE_LONG, |
| | | // negate(size), |
| | | // profitId -> { |
| | | // elemLong.setLongStopLossOrderId(profitId); |
| | | // elemLong.addLongStopLossOrderId(profitId); |
| | | // GridElement.refreshIndices(); |
| | | // log.info("[Gate] 多仓止损已挂, gridId:{}, 触发价:{}, stopLossId:{}", longTime, triggerPrice, profitId); |
| | | // } |
| | |
| | | ORDER_TYPE_CLOSE_SHORT, |
| | | size, |
| | | profitId -> { |
| | | elem.setShortStopLossOrderId(profitId); |
| | | elem.addShortStopLossOrderId(profitId); |
| | | GridElement.refreshIndices(); |
| | | log.info("[Gate] 空仓止损已挂, gridId:{}, 触发价:{}, stopLossId:{}", finalId, triggerPrice, profitId); |
| | | } |
| | | ); |
| | | } |
| | | |
| | | |
| | | |
| | | int longTime = Integer.parseInt(config.getBaseQuantity()) / Integer.parseInt(config.getQuantity()) + 1; |
| | |
| | | ORDER_TYPE_CLOSE_LONG, |
| | | negate(size), |
| | | profitId -> { |
| | | elem.setLongStopLossOrderId(profitId); |
| | | elem.addLongStopLossOrderId(profitId); |
| | | GridElement.refreshIndices(); |
| | | log.info("[Gate] 多仓止损已挂, gridId:{}, 触发价:{}, stopLossId:{}", finalId, triggerPrice, profitId); |
| | | } |
| | | ); |
| | | } |
| | | |
| | | log.info("[Gate] 止损单已全部挂完, 空仓止损: 2~{}, 多仓止损: -2~-{}", shortTime, longTime); |
| | | log.info("[Gate] 止损止盈单已全部挂完, 空仓止损:2~{}, 多仓止损:-2~-{}", shortTime, longTime); |
| | | |
| | | state = StrategyState.ACTIVE; |
| | | } |
| | |
| | | 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.setLongStopLossOrderId(null); |
| | | private void handleLongStopLossTriggered(GridElement gridElement, String orderId) { |
| | | gridElement.removeLongStopLossOrderId(orderId); |
| | | |
| | | accumulatedLongLossCount++; |
| | | int gridId = gridElement.getId(); |
| | | log.info("[Gate] 多仓止损触发 gridId:{}, 开始追单", gridId); |
| | | log.info("[Gate] 多仓止损触发 gridId:{}, 止损次数:{}, 开始追单", gridId, accumulatedLongLossCount); |
| | | int newEntryGridId = gridId + 1; |
| | | |
| | | GridElement newEntryGrid = GridElement.findById(newEntryGridId); |
| | |
| | | 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(); |
| | | int targetAmount = Integer.parseInt(config.getQuantity()) * 2; // quantity + 本次止损量 |
| | | 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); |
| | | } |
| | | // 止损追单:同一网格可有多笔挂单,不判断 isHasLongOrder,直接挂单 |
| | | BigDecimal triggerPrice = newEntryGrid.getGridPrice(); |
| | | int posSize = Math.min(queryPositionSize(Position.ModeEnum.DUAL_LONG), longPositionSize.intValue()); |
| | | int maxPos = config.getMaxPositionSize(); |
| | | int baseQty = Integer.parseInt(config.getBaseQuantity()); |
| | | int gridQty = Integer.parseInt(config.getQuantity()); |
| | | int targetAmount = baseQty + (accumulatedLongLossCount - 1) * gridQty; |
| | | int addSize; |
| | | if (maxPos > 0) { |
| | | int remainingRoom = maxPos - posSize; |
| | | if (remainingRoom <= 0) { |
| | | log.warn("[Gate] 多仓止损触发 gridId:{}, 当前持仓{}/{}已达上限,跳过追单", |
| | | gridId, posSize, maxPos); |
| | | addSize = 0; |
| | | } else { |
| | | addSize = targetAmount; |
| | | addSize = Math.min(remainingRoom, targetAmount); |
| | | } |
| | | if (addSize > 0) { |
| | | String size = String.valueOf(addSize); |
| | | log.info("[Gate] 多仓止损触发 gridId:{}, 在gridId:{}补{}张多单(当前{}/上限{})", |
| | | gridId, 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.setShortStopLossOrderId(null); |
| | | private void handleShortStopLossTriggered(GridElement gridElement, String orderId) { |
| | | gridElement.removeShortStopLossOrderId(orderId); |
| | | |
| | | accumulatedShortLossCount++; |
| | | int gridId = gridElement.getId(); |
| | | log.info("[Gate] 空仓止损触发 gridId:{}, 开始追单", gridId); |
| | | log.info("[Gate] 空仓止损触发 gridId:{}, 止损次数:{}, 开始追单", gridId, accumulatedShortLossCount); |
| | | int newEntryGridId = gridId - 1; |
| | | |
| | | GridElement newEntryGrid = GridElement.findById(newEntryGridId); |
| | |
| | | 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(); |
| | | int targetAmount = Integer.parseInt(config.getQuantity()) * 2; // quantity + 本次止损量 |
| | | 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); |
| | | } |
| | | // 止损追单:同一网格可有多笔挂单,不判断 isHasShortOrder,直接挂单 |
| | | BigDecimal triggerPrice = newEntryGrid.getGridPrice(); |
| | | int posSize = Math.min(queryPositionSize(Position.ModeEnum.DUAL_SHORT), shortPositionSize.intValue()); |
| | | int maxPos = config.getMaxPositionSize(); |
| | | int baseQty = Integer.parseInt(config.getBaseQuantity()); |
| | | int gridQty = Integer.parseInt(config.getQuantity()); |
| | | int targetAmount = baseQty + (accumulatedShortLossCount - 1) * gridQty; |
| | | int addSize; |
| | | if (maxPos > 0) { |
| | | int remainingRoom = maxPos - posSize; |
| | | if (remainingRoom <= 0) { |
| | | log.warn("[Gate] 空仓止损触发 gridId:{}, 当前持仓{}/{}已达上限,跳过追单", |
| | | gridId, posSize, maxPos); |
| | | addSize = 0; |
| | | } else { |
| | | addSize = targetAmount; |
| | | addSize = Math.min(remainingRoom, targetAmount); |
| | | } |
| | | if (addSize > 0) { |
| | | String size = String.valueOf(addSize); |
| | | log.info("[Gate] 空仓止损触发 gridId:{}, 在gridId:{}补{}张空单(当前{}/上限{})", |
| | | gridId, 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); |
| | | }); |
| | | } |
| | | } |
| | | |
| | | // 止损触发时,取消最远的空仓止盈订单 |
| | |
| | | } |
| | | |
| | | /** |
| | | * 取消最远的多仓止损订单。 |
| | | * 多仓止损在 gridId 负方向,最远 = id 最小。 |
| | | * 取消最近的多仓止损订单(A 位置,gridId 最大即最接近 0)。 |
| | | * 多仓止损在 gridId 负方向,A = gridId-2,是三个位置中 ID 最大(最接近 0)的那个。 |
| | | */ |
| | | private void cancelFarthestLongStopLoss() { |
| | | GridElement farthest = null; |
| | | private void cancelNearestLongStopLoss() { |
| | | GridElement nearest = null; |
| | | for (GridElement e : config.getGridElements()) { |
| | | if (e.getLongStopLossOrderId() != null) { |
| | | if (farthest == null || e.getId() < farthest.getId()) { |
| | | farthest = e; |
| | | if (e.hasLongStopLossOrders()) { |
| | | if (nearest == null || e.getId() > nearest.getId()) { |
| | | nearest = e; |
| | | } |
| | | } |
| | | } |
| | | if (farthest != null) { |
| | | String slId = farthest.getLongStopLossOrderId(); |
| | | farthest.setLongStopLossOrderId(null); |
| | | if (nearest != null && nearest.hasLongStopLossOrders()) { |
| | | List<String> ids = nearest.getLongStopLossOrderIds(); |
| | | String slId = ids.get(0); // 取第一个(最早加入的 = A 位置第一单) |
| | | nearest.removeLongStopLossOrderId(slId); |
| | | GridElement.refreshIndices(); |
| | | GridElement finalFarthest = farthest; |
| | | GridElement finalNearest = nearest; |
| | | executor.cancelConditionalOrder(slId, oid -> |
| | | log.info("[Gate] 止盈触发, 取消最远多仓止损 gridId:{}, orderId:{}", finalFarthest.getId(), slId)); |
| | | log.info("[Gate] 止盈触发, 取消最近多仓止损 gridId:{}, orderId:{}", finalNearest.getId(), slId)); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 取消最远的空仓止损订单。 |
| | | * 空仓止损在 gridId 正方向,最远 = id 最大。 |
| | | * 取消最近的空仓止损订单(A 位置,gridId 最小即最接近 0)。 |
| | | * 空仓止损在 gridId 正方向,A = gridId+2,是三个位置中 ID 最小(最接近 0)的那个。 |
| | | */ |
| | | private void cancelFarthestShortStopLoss() { |
| | | GridElement farthest = null; |
| | | private void cancelNearestShortStopLoss() { |
| | | GridElement nearest = null; |
| | | for (GridElement e : config.getGridElements()) { |
| | | if (e.getShortStopLossOrderId() != null) { |
| | | if (farthest == null || e.getId() > farthest.getId()) { |
| | | farthest = e; |
| | | if (e.hasShortStopLossOrders()) { |
| | | if (nearest == null || e.getId() < nearest.getId()) { |
| | | nearest = e; |
| | | } |
| | | } |
| | | } |
| | | if (farthest != null) { |
| | | String slId = farthest.getShortStopLossOrderId(); |
| | | farthest.setShortStopLossOrderId(null); |
| | | if (nearest != null && nearest.hasShortStopLossOrders()) { |
| | | List<String> ids = nearest.getShortStopLossOrderIds(); |
| | | String slId = ids.get(0); // 取第一个(最早加入的 = A 位置第一单) |
| | | nearest.removeShortStopLossOrderId(slId); |
| | | GridElement.refreshIndices(); |
| | | GridElement finalFarthest = farthest; |
| | | GridElement finalNearest = nearest; |
| | | executor.cancelConditionalOrder(slId, oid -> |
| | | log.info("[Gate] 止盈触发, 取消最远空仓止损 gridId:{}, orderId:{}", finalFarthest.getId(), slId)); |
| | | log.info("[Gate] 止盈触发, 取消最近空仓止损 gridId:{}, orderId:{}", finalNearest.getId(), slId)); |
| | | } |
| | | } |
| | | |
| | |
| | | e.setLongTakeProfitOrderId(null); |
| | | executor.cancelConditionalOrder(tpId, oid -> {}); |
| | | } |
| | | String slId = e.getLongStopLossOrderId(); |
| | | if (slId != null) { |
| | | e.setLongStopLossOrderId(null); |
| | | for (String slId : new ArrayList<>(e.getLongStopLossOrderIds())) { |
| | | executor.cancelConditionalOrder(slId, oid -> {}); |
| | | } |
| | | e.clearLongStopLossOrderIds(); |
| | | } |
| | | GridElement.refreshIndices(); |
| | | log.info("[Gate] 已提交取消所有多仓止盈+止损"); |
| | |
| | | e.setShortTakeProfitOrderId(null); |
| | | executor.cancelConditionalOrder(tpId, oid -> {}); |
| | | } |
| | | String slId = e.getShortStopLossOrderId(); |
| | | if (slId != null) { |
| | | e.setShortStopLossOrderId(null); |
| | | for (String slId : new ArrayList<>(e.getShortStopLossOrderIds())) { |
| | | executor.cancelConditionalOrder(slId, oid -> {}); |
| | | } |
| | | e.clearShortStopLossOrderIds(); |
| | | } |
| | | GridElement.refreshIndices(); |
| | | log.info("[Gate] 已提交取消所有空仓止盈+止损"); |
| | | } |
| | | |
| | | // ========== 止损追单 ========== |
| | | /** |
| | | * 首次挂单成交时,取消初始化阶段挂的所有止盈单(多+空),止损单保留不动。 |
| | | */ |
| | | private void cancelAllInitialTakeProfits() { |
| | | |
| | | private void extendLongStopLoss(int filledQty,int gridId) { |
| | | int furthestSlId = 0; |
| | | for (GridElement e : config.getGridElements()) { |
| | | if (e.getLongStopLossOrderId() != null && e.getId() < furthestSlId) { |
| | | furthestSlId = e.getId(); |
| | | } |
| | | |
| | | GridElement tpElemLong = GridElement.findById(3); |
| | | String longTpId = tpElemLong.getLongTakeProfitOrderId(); |
| | | if (longTpId != null) { |
| | | longTakeProfitTraderIdParam(tpElemLong, null, false); |
| | | executor.cancelConditionalOrder(longTpId, oid -> {}); |
| | | } |
| | | |
| | | int interval = 1; |
| | | if (furthestSlId == 0) { |
| | | furthestSlId = gridId; |
| | | interval = 2; |
| | | GridElement tpElemShort = GridElement.findById(-3); |
| | | String shortTpId = tpElemShort.getShortTakeProfitOrderId(); |
| | | if (shortTpId != null) { |
| | | shortTakeProfitTraderIdParam(tpElemShort, null, false); |
| | | executor.cancelConditionalOrder(shortTpId, oid -> {}); |
| | | } |
| | | int stopLossCount = filledQty / Integer.parseInt(config.getQuantity()); |
| | | log.info("[Gate] 多仓追挂止损, 当前最远止损gridId:{}, 成交{}张, 追加{}个止损单", furthestSlId, filledQty, stopLossCount); |
| | | for (int i = 0; i < stopLossCount; i++) { |
| | | int newSlId = furthestSlId - i - interval; |
| | | GridElement elem = GridElement.findById(newSlId); |
| | | if (elem == null) { |
| | | continue; |
| | | } |
| | | BigDecimal triggerPrice = elem.getGridPrice(); |
| | | int finalSlId = newSlId; |
| | | executor.placeTakeProfit( |
| | | triggerPrice, |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_2, |
| | | ORDER_TYPE_CLOSE_LONG, |
| | | negate(config.getQuantity()), |
| | | profitId -> { |
| | | elem.setLongStopLossOrderId(profitId); |
| | | GridElement.refreshIndices(); |
| | | log.info("[Gate] 多仓止损追加, gridId:{}, 触发价:{}, stopLossId:{}", finalSlId, triggerPrice, profitId); |
| | | } |
| | | ); |
| | | } |
| | | GridElement.refreshIndices(); |
| | | log.info("[Gate] 首次成交,取消所有初始化止盈单"); |
| | | } |
| | | |
| | | private void extendShortStopLoss(int filledQty, int gridId) { |
| | | int furthestSlId = 0; |
| | | for (GridElement e : config.getGridElements()) { |
| | | if (e.getShortStopLossOrderId() != null && e.getId() > furthestSlId) { |
| | | furthestSlId = e.getId(); |
| | | } |
| | | } |
| | | // ========== 止损追单(新逻辑:3 个固定位置,每个位置挂多个 1 张止损单) ========== |
| | | |
| | | int interval = 1; |
| | | if (furthestSlId == 0) { |
| | | furthestSlId = gridId; |
| | | interval = 2; |
| | | /** |
| | | * 多仓止损追单 — 3 个固定位置,从近到远分配合约。 |
| | | * A(gridId-2): (filledQty/qty - 2) 个独立止损单,每个 qty 张 |
| | | * B(gridId-3): 1 个止损单,qty 张 |
| | | * C(gridId-4): 1 个止损单,qty 张 |
| | | * 注意:多仓止损在 gridId 负方向,触发价 ≤ 网格价。 |
| | | */ |
| | | 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); |
| | | |
| | | // A 位置:挂 aCount 个独立止损单 |
| | | placeStopLossOrders(gridId - 2, aCount, qty, FuturesPriceTrigger.RuleEnum.NUMBER_2, |
| | | ORDER_TYPE_CLOSE_LONG, true); |
| | | // B 位置:挂 1 个 |
| | | placeStopLossOrders(gridId - 3, 1, qty, FuturesPriceTrigger.RuleEnum.NUMBER_2, |
| | | ORDER_TYPE_CLOSE_LONG, true); |
| | | // C 位置:挂 1 个 |
| | | placeStopLossOrders(gridId - 4, 1, 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 正方向,触发价 ≥ 网格价。 |
| | | */ |
| | | 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); |
| | | |
| | | // A 位置:挂 aCount 个独立止损单 |
| | | placeStopLossOrders(gridId + 2, aCount, qty, FuturesPriceTrigger.RuleEnum.NUMBER_1, |
| | | ORDER_TYPE_CLOSE_SHORT, false); |
| | | // B 位置:挂 1 个 |
| | | placeStopLossOrders(gridId + 3, 1, qty, FuturesPriceTrigger.RuleEnum.NUMBER_1, |
| | | ORDER_TYPE_CLOSE_SHORT, false); |
| | | // C 位置:挂 1 个 |
| | | placeStopLossOrders(gridId + 4, 1, qty, FuturesPriceTrigger.RuleEnum.NUMBER_1, |
| | | ORDER_TYPE_CLOSE_SHORT, false); |
| | | } |
| | | |
| | | /** |
| | | * 在指定网格位置挂 count 个独立止损单,每个 size 张。 |
| | | */ |
| | | private void placeStopLossOrders(int gridId, int count, int qty, |
| | | FuturesPriceTrigger.RuleEnum rule, |
| | | String orderType, boolean isLong) { |
| | | if (count <= 0) { |
| | | return; |
| | | } |
| | | int stopLossCount = filledQty / Integer.parseInt(config.getQuantity()); |
| | | log.info("[Gate] 空仓追挂止损, 当前最远止损gridId:{}, 成交{}张, 追加{}个止损单", furthestSlId, filledQty, stopLossCount); |
| | | for (int i = 0; i < stopLossCount; i++) { |
| | | int newSlId = furthestSlId + i + interval; |
| | | GridElement elem = GridElement.findById(newSlId); |
| | | if (elem == null) { |
| | | continue; |
| | | } |
| | | BigDecimal triggerPrice = elem.getGridPrice(); |
| | | int finalSlId = newSlId; |
| | | GridElement elem = GridElement.findById(gridId); |
| | | if (elem == null) { |
| | | log.warn("[Gate] 止损挂单位置不存在, gridId:{}", gridId); |
| | | return; |
| | | } |
| | | BigDecimal triggerPrice = elem.getGridPrice(); |
| | | for (int i = 0; i < count; i++) { |
| | | String size = isLong ? negate(String.valueOf(qty)) : String.valueOf(qty); |
| | | int finalGridId = gridId; |
| | | int finalI = i; |
| | | executor.placeTakeProfit( |
| | | triggerPrice, |
| | | FuturesPriceTrigger.RuleEnum.NUMBER_1, |
| | | ORDER_TYPE_CLOSE_SHORT, |
| | | config.getQuantity(), |
| | | triggerPrice, rule, orderType, size, |
| | | profitId -> { |
| | | elem.setShortStopLossOrderId(profitId); |
| | | if (isLong) { |
| | | elem.addLongStopLossOrderId(profitId); |
| | | } else { |
| | | elem.addShortStopLossOrderId(profitId); |
| | | } |
| | | GridElement.refreshIndices(); |
| | | log.info("[Gate] 空仓止损追加, gridId:{}, 触发价:{}, stopLossId:{}", finalSlId, triggerPrice, profitId); |
| | | log.info("[Gate] {}止损追加, gridId:{}, 触发价:{}, 第{}单, stopLossId:{}", |
| | | isLong ? "多仓" : "空仓", finalGridId, triggerPrice, finalI + 1, profitId); |
| | | } |
| | | ); |
| | | } |
| | |
| | | } |
| | | }, |
| | | () -> { |
| | | // 仅当列表为空(无其他有效订单)时才清预置标志,避免误伤其他并发挂单 |
| | | 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); |
| | | } |
| | | ); |
| | | } |