挂单撤单新增参数cancel_grid_count

This commit is contained in:
tony 2026-07-20 22:15:09 +08:00
parent b956b2a344
commit e66520b5da
2 changed files with 26 additions and 17 deletions

View File

@ -5,7 +5,7 @@
<groupId>com.sample</groupId> <groupId>com.sample</groupId>
<artifactId>trend-grid-strategy</artifactId> <artifactId>trend-grid-strategy</artifactId>
<version>1.0.4-SNAPSHOT</version> <version>1.0.5-SNAPSHOT</version>
<name>trend-grid-strategy</name> <name>trend-grid-strategy</name>
<description>趋势网格策略</description> <description>趋势网格策略</description>

View File

@ -36,17 +36,13 @@ import java.util.concurrent.CopyOnWriteArrayList;
* <p> * <p>
* 网格间距 = atr_multiplier × ATR,并限制在 [min_gap, max_gap] 区间。 * 网格间距 = atr_multiplier × ATR,并限制在 [min_gap, max_gap] 区间。
* 下方买单始终最多挂 1 笔:成交后再挂下一格,不会批量铺满网格。 * 下方买单始终最多挂 1 笔:成交后再挂下一格,不会批量铺满网格。
* 若现价相对挂单价上行超过间距 × 1.5,撤单并按现价重新挂靠下一格,避免买单悬空。 * 若挂单价低于「现价 − 撤单网格数 × 间距」,撤单并按现价重新挂靠下一格,避免买单长期悬空。
* 采用限价挂单买入,买单成交后在「入场价 + 间距」挂限价卖单止盈。 * 采用限价挂单买入,买单成交后在「入场价 + 间距」挂限价卖单止盈。
* 网格间距变化时不撤销已有挂单,已挂卖单价格保持不变。 * 网格间距变化时不撤销已有挂单,已挂卖单价格保持不变。
*/ */
public class AutoGridStrategy extends BaseStrategy { public class AutoGridStrategy extends BaseStrategy {
private static final String MARGIN_MODE = "crossed"; 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); private final Logger logger = LoggerFactory.getLogger(AutoGridStrategy.class);
@ -68,6 +64,10 @@ public class AutoGridStrategy extends BaseStrategy {
* 最大持仓层数;达到后不再新建买单。待成交买单始终最多 1 笔。 * 最大持仓层数;达到后不再新建买单。待成交买单始终最多 1 笔。
*/ */
private int gridCount; private int gridCount;
/**
* 撤单网格数 N:挂单价低于「现价 − N × 间距」时撤单重挂(例:间距 5、N=5 → 撤现价−25 以下的买单)。
*/
private int cancelGridCount;
// 精度 // 精度
private int pricePrecision = 2; 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", tryParams("base_quantity", "atr_multiplier", "grid_grp_script", "kline_interval", "kline_period",
"grid_adjust_interval", "min_gap", "max_gap", "upper_bound", "lower_bound", "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"); baseQuantity = getRunParams().getBigDecimal("base_quantity");
atrMultiplier = getRunParams().getBigDecimal("atr_multiplier"); atrMultiplier = getRunParams().getBigDecimal("atr_multiplier");
@ -285,6 +285,10 @@ public class AutoGridStrategy extends BaseStrategy {
if (gridCount <= 0) { if (gridCount <= 0) {
throw new IllegalArgumentException("grid_count 必须大于 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) { if (lowerBound.compareTo(upperBound) >= 0) {
throw new IllegalArgumentException("lower_bound 必须小于 upper_bound"); 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("upperBound", upperBound == null ? null : upperBound.doubleValue());
variables.put("lowerBound", lowerBound == null ? null : lowerBound.doubleValue()); variables.put("lowerBound", lowerBound == null ? null : lowerBound.doubleValue());
variables.put("gridCount", gridCount); variables.put("gridCount", gridCount);
variables.put("cancelGridCount", cancelGridCount);
variables.put("leverage", leverage); variables.put("leverage", leverage);
variables.put("pricePrecision", pricePrecision); variables.put("pricePrecision", pricePrecision);
variables.put("quantityPrecision", quantityPrecision); 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() { private void rebaseStalePendingBuy() {
if (!canPlaceBuyOrders() || currentPrice == null || currentGridGap == null if (!canPlaceBuyOrders() || currentPrice == null || currentGridGap == null
|| currentGridGap.compareTo(BigDecimal.ZERO) <= 0) { || currentGridGap.compareTo(BigDecimal.ZERO) <= 0 || cancelGridCount <= 0) {
return; return;
} }
@ -535,9 +541,10 @@ public class AutoGridStrategy extends BaseStrategy {
} }
GridBuyOrder pending = pendingBuyOrders.values().iterator().next(); GridBuyOrder pending = pendingBuyOrders.values().iterator().next();
BigDecimal distance = currentPrice.subtract(pending.buyPrice); BigDecimal cancelBelow = currentPrice.subtract(
BigDecimal threshold = currentGridGap.multiply(BUY_REBASE_MULTIPLIER); currentGridGap.multiply(BigDecimal.valueOf(cancelGridCount)));
if (distance.compareTo(threshold) <= 0) { // 只撤「现价 − N×间距」以下的挂单,避免半格内频繁追价
if (pending.buyPrice.compareTo(cancelBelow) >= 0) {
return; return;
} }
@ -549,12 +556,13 @@ public class AutoGridStrategy extends BaseStrategy {
try { try {
bitGetClient.cancelOrder(getSymbol(), pending.exchangeOrderId, pending.clientOrderId, null); bitGetClient.cancelOrder(getSymbol(), pending.exchangeOrderId, pending.clientOrderId, null);
pendingBuyOrders.remove(priceKey(pending.buyPrice)); pendingBuyOrders.remove(priceKey(pending.buyPrice));
logger.info("买单距现价过远,撤单重挂:旧价={},现价={},间距={},阈值={},新价={}", logger.info("买单距现价过远,撤单重挂:旧价={},现价={},间距={},撤单网格数={},撤单线={},新价={}",
pending.buyPrice, currentPrice, currentGridGap, threshold, newPrice); pending.buyPrice, currentPrice, currentGridGap, cancelGridCount, cancelBelow, newPrice);
submitAlarm("买单追价重挂", submitAlarm("买单追价重挂",
String.format("旧挂单价 %s,现价 %s,超过间距×1.5(%s),重挂至 %s", String.format("旧挂单价 %s 低于撤单线 %s(现价 %s − %d×间距 %s),重挂至 %s",
formatPrice(pending.buyPrice), formatPrice(currentPrice), formatPrice(pending.buyPrice), formatPrice(cancelBelow),
formatPrice(threshold), formatPrice(newPrice)), formatPrice(currentPrice), cancelGridCount, formatPrice(currentGridGap),
formatPrice(newPrice)),
AlarmLevel.INFO); AlarmLevel.INFO);
placeLimitBuyOrder(newPrice); placeLimitBuyOrder(newPrice);
} catch (Exception e) { } catch (Exception e) {
@ -1214,6 +1222,7 @@ public class AutoGridStrategy extends BaseStrategy {
sb.append("<div>当前价:").append(formatPrice(currentPrice)).append("</div>"); sb.append("<div>当前价:").append(formatPrice(currentPrice)).append("</div>");
sb.append("<div>ATR:").append(formatPrice(currentAtr)).append("</div>"); sb.append("<div>ATR:").append(formatPrice(currentAtr)).append("</div>");
sb.append("<div>网格间距:").append(formatPrice(currentGridGap)).append("</div>"); sb.append("<div>网格间距:").append(formatPrice(currentGridGap)).append("</div>");
sb.append("<div>撤单网格数:").append(cancelGridCount).append("</div>");
sb.append("<div>待成交买单:").append(pendingBuyOrders.size()).append(" / 1 笔</div>"); sb.append("<div>待成交买单:").append(pendingBuyOrders.size()).append(" / 1 笔</div>");
sb.append("<div>待成交卖单:").append(pendingSellOrders.size()).append(" 笔</div>"); sb.append("<div>待成交卖单:").append(pendingSellOrders.size()).append(" 笔</div>");
sb.append("<div>持仓待止盈:").append(openPositions.size()) sb.append("<div>持仓待止盈:").append(openPositions.size())