diff --git a/pom.xml b/pom.xml index 2dc4bb2..ee24d4a 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.sample trend-grid-strategy - 1.0.4-SNAPSHOT + 1.0.5-SNAPSHOT trend-grid-strategy 趋势网格策略 diff --git a/src/main/java/com/sample/trend/strategy/AutoGridStrategy.java b/src/main/java/com/sample/trend/strategy/AutoGridStrategy.java index c794009..3eb4638 100644 --- a/src/main/java/com/sample/trend/strategy/AutoGridStrategy.java +++ b/src/main/java/com/sample/trend/strategy/AutoGridStrategy.java @@ -36,17 +36,13 @@ 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); @@ -68,6 +64,10 @@ public class AutoGridStrategy extends BaseStrategy { * 最大持仓层数;达到后不再新建买单。待成交买单始终最多 1 笔。 */ private int gridCount; + /** + * 撤单网格数 N:挂单价低于「现价 − N × 间距」时撤单重挂(例:间距 5、N=5 → 撤现价−25 以下的买单)。 + */ + private int cancelGridCount; // 精度 private int pricePrecision = 2; @@ -264,7 +264,7 @@ public class AutoGridStrategy extends BaseStrategy { tryParams("base_quantity", "atr_multiplier", "grid_grp_script", "kline_interval", "kline_period", "grid_adjust_interval", "min_gap", "max_gap", "upper_bound", "lower_bound", - "grid_count", "test_mode"); + "grid_count", "cancel_grid_count", "test_mode"); baseQuantity = getRunParams().getBigDecimal("base_quantity"); atrMultiplier = getRunParams().getBigDecimal("atr_multiplier"); @@ -285,6 +285,10 @@ public class AutoGridStrategy extends BaseStrategy { if (gridCount <= 0) { throw new IllegalArgumentException("grid_count 必须大于 0"); } + cancelGridCount = getRunParams().getInteger("cancel_grid_count"); + if (cancelGridCount <= 0) { + throw new IllegalArgumentException("cancel_grid_count 必须大于 0"); + } if (lowerBound.compareTo(upperBound) >= 0) { throw new IllegalArgumentException("lower_bound 必须小于 upper_bound"); @@ -432,6 +436,7 @@ public class AutoGridStrategy extends BaseStrategy { variables.put("upperBound", upperBound == null ? null : upperBound.doubleValue()); variables.put("lowerBound", lowerBound == null ? null : lowerBound.doubleValue()); variables.put("gridCount", gridCount); + variables.put("cancelGridCount", cancelGridCount); variables.put("leverage", leverage); variables.put("pricePrecision", pricePrecision); variables.put("quantityPrecision", quantityPrecision); @@ -521,11 +526,12 @@ public class AutoGridStrategy extends BaseStrategy { /** * 价格单边上行时,下方买单会悬空永远不成交。 - * 当 现价 - 挂单价 > 间距 × 1.5 时,撤单并按现价重新对齐挂单(例:1532 / 间距 5 → 1530)。 + * 当挂单价低于「现价 − 撤单网格数 × 间距」时,撤单并按现价重新对齐挂单 + * (例:现价 1532、间距 5、撤单网格数 5 → 撤销 1507 以下的买单,再挂到现价下方一格)。 */ private void rebaseStalePendingBuy() { if (!canPlaceBuyOrders() || currentPrice == null || currentGridGap == null - || currentGridGap.compareTo(BigDecimal.ZERO) <= 0) { + || currentGridGap.compareTo(BigDecimal.ZERO) <= 0 || cancelGridCount <= 0) { return; } @@ -535,9 +541,10 @@ public class AutoGridStrategy extends BaseStrategy { } GridBuyOrder pending = pendingBuyOrders.values().iterator().next(); - BigDecimal distance = currentPrice.subtract(pending.buyPrice); - BigDecimal threshold = currentGridGap.multiply(BUY_REBASE_MULTIPLIER); - if (distance.compareTo(threshold) <= 0) { + BigDecimal cancelBelow = currentPrice.subtract( + currentGridGap.multiply(BigDecimal.valueOf(cancelGridCount))); + // 只撤「现价 − N×间距」以下的挂单,避免半格内频繁追价 + if (pending.buyPrice.compareTo(cancelBelow) >= 0) { return; } @@ -549,12 +556,13 @@ public class AutoGridStrategy extends BaseStrategy { try { bitGetClient.cancelOrder(getSymbol(), pending.exchangeOrderId, pending.clientOrderId, null); pendingBuyOrders.remove(priceKey(pending.buyPrice)); - logger.info("买单距现价过远,撤单重挂:旧价={},现价={},间距={},阈值={},新价={}", - pending.buyPrice, currentPrice, currentGridGap, threshold, newPrice); + logger.info("买单距现价过远,撤单重挂:旧价={},现价={},间距={},撤单网格数={},撤单线={},新价={}", + pending.buyPrice, currentPrice, currentGridGap, cancelGridCount, cancelBelow, newPrice); submitAlarm("买单追价重挂", - String.format("旧挂单价 %s,现价 %s,超过间距×1.5(%s),重挂至 %s", - formatPrice(pending.buyPrice), formatPrice(currentPrice), - formatPrice(threshold), formatPrice(newPrice)), + String.format("旧挂单价 %s 低于撤单线 %s(现价 %s − %d×间距 %s),重挂至 %s", + formatPrice(pending.buyPrice), formatPrice(cancelBelow), + formatPrice(currentPrice), cancelGridCount, formatPrice(currentGridGap), + formatPrice(newPrice)), AlarmLevel.INFO); placeLimitBuyOrder(newPrice); } catch (Exception e) { @@ -1214,6 +1222,7 @@ public class AutoGridStrategy extends BaseStrategy { sb.append("

当前价:").append(formatPrice(currentPrice)).append("
"); sb.append("
ATR:").append(formatPrice(currentAtr)).append("
"); sb.append("
网格间距:").append(formatPrice(currentGridGap)).append("
"); + sb.append("
撤单网格数:").append(cancelGridCount).append("
"); sb.append("
待成交买单:").append(pendingBuyOrders.size()).append(" / 1 笔
"); sb.append("
待成交卖单:").append(pendingSellOrders.size()).append(" 笔
"); sb.append("
持仓待止盈:").append(openPositions.size())