| | |
| | | |
| | | log.info("[Gate] 重置策略,开始第{}轮...", currentRound); |
| | | state = StrategyState.STOPPED; |
| | | try { |
| | | futuresApi.cancelPriceTriggeredOrderList(SETTLE, config.getContract()); |
| | | } catch (ApiException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | closeExistingPositions(); |
| | | // 提交到 executor 末尾:单线程FIFO保证前面所有平仓/取消任务完成后才重置 |
| | | // 关键修复:取消+平仓+延时+重启必须整体放进 executor 队列末尾,让上一轮排队的 |
| | | // 挂单任务先执行完(挂到交易所),随后的 cancel 才能取消到它们。若 cancel 仍留在 |
| | | // WS 线程同步执行,会早于上一轮挂单任务,取消不到"尚未挂出"的单(竞态)。 |
| | | executor.submitTask(() -> { |
| | | try { |
| | | futuresApi.cancelPriceTriggeredOrderList(SETTLE, config.getContract()); |
| | | } catch (ApiException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | closeExistingPositions(); |
| | | try { Thread.sleep(3000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } |
| | | // 注意:startGrid() 会将 currentRound 归零,这里需要保留 |
| | | int savedRound = currentRound; |
| | |
| | | lastShortStopLossGridId = Integer.MAX_VALUE; |
| | | // [Gate-需求2] 加仓后先撤空仓所有止盈+止损,再查交易所持仓后重挂 |
| | | cancelAllShortTakeProfitsAndStopLosses(); |
| | | // REST 查询可能因交易所延迟返回旧值,与 WS 本地缓存取最大值兜底 |
| | | int posSize = Math.max(queryPositionSize(Position.ModeEnum.DUAL_SHORT), shortPositionSize.intValue()); |
| | | // REST/WS 持仓可能因交易所延迟返回旧值(仓位归零后重开甚至为 0), |
| | | // 与本次成交张数 filledQty 取最大值兜底,避免 posSize=0 导致止盈止损全部跳过 |
| | | int posSize = Math.max(Math.max(queryPositionSize(Position.ModeEnum.DUAL_SHORT), shortPositionSize.intValue()), filledQty); |
| | | extendShortStopLoss(posSize, shortGridElement.getId()); |
| | | // [Gate] 止盈挂单:超出基础仓位的部分,挂在多仓第一止损位 |
| | | if (GateConfig.EXCESS_TP_CONCENTRATED.equals(config.getPlaceExcessTakeProfit())) { |
| | |
| | | lastLongStopLossGridId = Integer.MAX_VALUE; |
| | | // [Gate-需求2] 加仓后先撤多仓所有止盈+止损,再查交易所持仓后重挂 |
| | | cancelAllLongTakeProfitsAndStopLosses(); |
| | | // REST 查询可能因交易所延迟返回旧值,与 WS 本地缓存取最大值兜底 |
| | | int posSize = Math.max(queryPositionSize(Position.ModeEnum.DUAL_LONG), longPositionSize.intValue()); |
| | | // REST/WS 持仓可能因交易所延迟返回旧值(仓位归零后重开甚至为 0), |
| | | // 与本次成交张数 filledQty 取最大值兜底,避免 posSize=0 导致止盈止损全部跳过 |
| | | int posSize = Math.max(Math.max(queryPositionSize(Position.ModeEnum.DUAL_LONG), longPositionSize.intValue()), filledQty); |
| | | extendLongStopLoss(posSize, longGridElement.getId()); |
| | | // [Gate] 止盈挂单:超出基础仓位的部分,挂在空仓第一止损位 |
| | | if (GateConfig.EXCESS_TP_CONCENTRATED.equals(config.getPlaceExcessTakeProfit())) { |
| | |
| | | |
| | | /** |
| | | * 分散止盈:把超出基础仓位的部分,从入场网格下一格开始逐格挂止盈单, |
| | | * 一直挂到对手盘止损位的前一格(不含止损位那一格)。 |
| | | * 一直挂到对手盘止损位(包含止损位那一格)。 |
| | | * |
| | | * <p>数量分配:每个网格至少挂 1 张;当 excessQty < 网格数时,张数不够则从起点逐格各挂 1 张、 |
| | | * 挂满 excessQty 张即停止;当 excessQty ≥ 网格数时,每格 excessQty÷n,余数堆最后一格(即止损位的前一格)。 |
| | | * 挂满 excessQty 张即停止;当 excessQty ≥ 网格数时,每格 excessQty÷n,余数堆最后一格(即止损位那一格)。 |
| | | * |
| | | * <p>方向:多仓成交向价格更高方向(id 递增)铺到空仓第一止损位; |
| | | * 空仓成交向价格更低方向(id 递减)铺到多仓第一止损位。 |
| | |
| | | } |
| | | int dir = isLong ? 1 : -1; |
| | | int startId = entryGridId + dir; |
| | | // 分配范围到止损位的前一格截止(不含止损位那一格) |
| | | int endId = endElem.getId() - dir; |
| | | // 分配范围到止损位那一格截止(包含止损位) |
| | | int endId = endElem.getId(); |
| | | |
| | | // 终点必须在起点前进方向上,否则退化为集中止盈(全部挂对手盘止损位) |
| | | boolean valid = isLong ? (endId >= startId) : (endId <= startId); |
| | |
| | | for (int i = 0; i < n; i++) { |
| | | int qty; |
| | | if (excessQty >= n) { |
| | | // 超额仓位 ≥ 网格数:每格 perGrid,余数堆最后一格(即止损位的前一格) |
| | | // 超额仓位 ≥ 网格数:每格 perGrid,余数堆最后一格(即止损位那一格) |
| | | qty = perGrid; |
| | | if (i == n - 1) { |
| | | qty += remainder; |
| | |
| | | * 基础止盈:对基础仓位(baseQuantity)也挂止盈单,与超额止盈并行。 |
| | | * |
| | | * <p>规则:从入场网格下一格(entryGridId ± 1)开始,每格 baseTakeProfitPerGrid 张, |
| | | * 最多挂 baseTakeProfitGridCount 格,挂到止损位的前一格截止(与分散止盈同起点/同终点)。 |
| | | * 最多挂 baseTakeProfitGridCount 格,挂到止损位前 2 格截止(即对手盘仓位线)。 |
| | | * |
| | | * <p>边界:累计张数到 min(baseQuantity, 当前持仓) 即停(张数不足则最后一格挂剩余 = "挂几个算几个"); |
| | | * 网格不足时挂到停止位置结束。 |
| | |
| | | } |
| | | int dir = isLong ? 1 : -1; |
| | | int startId = entryGridId + dir; |
| | | // 分配范围到止损位的前一格截止(与分散止盈一致) |
| | | int endId = endElem.getId() - dir; |
| | | // 分配范围到止损位前 2 格截止(即对手盘仓位线) |
| | | int endId = endElem.getId() - 2 * dir; |
| | | |
| | | BigDecimal remaining = BigDecimal.valueOf(maxQty); |
| | | int placed = 0; |