From 1e38b457f1cd063e851140e82fec20655bee2304 Mon Sep 17 00:00:00 2001 From: dongzp_book <90fanhua@gmail.com> Date: Wed, 8 Jul 2026 23:47:17 +0800 Subject: [PATCH] =?UTF-8?q?=E7=8E=B0=E4=BB=B7=E9=AB=98=E4=BA=8E=E6=8C=82?= =?UTF-8?q?=E5=8D=95=E4=BB=B7=E8=B6=85=E8=BF=87=E3=80=8C=E9=97=B4=E8=B7=9D?= =?UTF-8?q?=20=C3=97=20=E8=AF=A5=E5=80=8D=E6=95=B0=E3=80=8D=E6=97=B6?= =?UTF-8?q?=EF=BC=8C=E6=92=A4=E5=8D=95=E9=87=8D=E6=8C=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trend/strategy/AutoGridStrategy.java | 80 +++++++++++++++++-- 1 file changed, 74 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/sample/trend/strategy/AutoGridStrategy.java b/src/main/java/com/sample/trend/strategy/AutoGridStrategy.java index a028324..120b0c1 100644 --- a/src/main/java/com/sample/trend/strategy/AutoGridStrategy.java +++ b/src/main/java/com/sample/trend/strategy/AutoGridStrategy.java @@ -35,12 +35,17 @@ import java.util.concurrent.CopyOnWriteArrayList; *
* 网格间距 = atr_multiplier × ATR,并限制在 [min_gap, max_gap] 区间。 * 下方买单始终最多挂 1 笔:成交后再挂下一格,不会批量铺满网格。 + * 若现价相对挂单价上行超过间距 × 1.5,撤单并按现价重新挂靠下一格,避免买单悬空。 * 采用限价挂单买入,市价止盈卖出。 * 网格间距变化时不撤销已有挂单,已成交仓位按最新间距计算止盈价。 */ public class AutoGridStrategy extends BaseStrategy { private static final String MARGIN_MODE = "crossed"; + /** + * 现价高于挂单价超过「间距 × 该倍数」时,撤单重挂 + */ + private static final BigDecimal BUY_REBASE_MULTIPLIER = new BigDecimal("1.5"); private final Logger logger = LoggerFactory.getLogger(AutoGridStrategy.class); @@ -303,6 +308,7 @@ public class AutoGridStrategy extends BaseStrategy { checkPriceBounds(); refreshGridGap(false); processTakeProfit(); + rebaseStalePendingBuy(); replenishBuyGrids(); updateDashboard(); } catch (Exception e) { @@ -455,17 +461,79 @@ public class AutoGridStrategy extends BaseStrategy { return; } - BigDecimal nextBuyPrice = alignPriceDown(currentPrice, currentGridGap); - if (nextBuyPrice.compareTo(currentPrice) >= 0) { - nextBuyPrice = nextBuyPrice.subtract(currentGridGap); - } - - if (nextBuyPrice.compareTo(lowerBound) >= 0 && !hasBuyOrderAt(nextBuyPrice)) { + BigDecimal nextBuyPrice = calcBuyPriceBelowCurrent(); + if (nextBuyPrice != null + && nextBuyPrice.compareTo(lowerBound) >= 0 + && !hasBuyOrderAt(nextBuyPrice)) { placeLimitBuyOrder(nextBuyPrice); } } } + /** + * 价格单边上行时,下方买单会悬空永远不成交。 + * 当 现价 - 挂单价 > 间距 × 1.5 时,撤单并按现价重新对齐挂单(例:1532 / 间距 5 → 1530)。 + */ + private void rebaseStalePendingBuy() { + if (!canPlaceBuyOrders() || currentPrice == null || currentGridGap == null + || currentGridGap.compareTo(BigDecimal.ZERO) <= 0) { + return; + } + + synchronized (tradeLock) { + if (pendingBuyOrders.isEmpty()) { + return; + } + + GridBuyOrder pending = pendingBuyOrders.values().iterator().next(); + BigDecimal distance = currentPrice.subtract(pending.buyPrice); + BigDecimal threshold = currentGridGap.multiply(BUY_REBASE_MULTIPLIER); + if (distance.compareTo(threshold) <= 0) { + return; + } + + BigDecimal newPrice = calcBuyPriceBelowCurrent(); + if (newPrice == null + || newPrice.compareTo(lowerBound) < 0 + || newPrice.compareTo(pending.buyPrice) == 0) { + return; + } + + try { + bitGetClient.cancelOrder(getSymbol(), pending.exchangeOrderId, pending.clientOrderId, null); + pendingBuyOrders.remove(priceKey(pending.buyPrice)); + logger.info("买单距现价过远,撤单重挂:旧价={},现价={},间距={},阈值={},新价={}", + pending.buyPrice, currentPrice, currentGridGap, threshold, newPrice); + submitAlarm("买单追价重挂", + String.format("旧挂单价 %s,现价 %s,超过间距×1.5(%s),重挂至 %s", + formatPrice(pending.buyPrice), formatPrice(currentPrice), + formatPrice(threshold), formatPrice(newPrice)), + AlarmLevel.INFO); + placeLimitBuyOrder(newPrice); + } catch (Exception e) { + logger.error("买单追价重挂失败,旧价={}", pending.buyPrice, e); + submitAlarm("买单追价重挂失败", + String.format("旧价 %s:%s", formatPrice(pending.buyPrice), e.getMessage()), + AlarmLevel.ERROR); + } + } + } + + /** + * 现价向下对齐到网格,确保严格低于现价。 + */ + private BigDecimal calcBuyPriceBelowCurrent() { + if (currentPrice == null || currentGridGap == null + || currentGridGap.compareTo(BigDecimal.ZERO) <= 0) { + return null; + } + BigDecimal nextBuyPrice = alignPriceDown(currentPrice, currentGridGap); + if (nextBuyPrice.compareTo(currentPrice) >= 0) { + nextBuyPrice = nextBuyPrice.subtract(currentGridGap); + } + return normalizePrice(nextBuyPrice); + } + /** * 买单成交后,挂「成交价再下一格」的限价买单。 */