From cd3de8987bca1a9c29b542813b87ee06889af56a Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Mon, 21 Sep 2026 10:00:47 +0800
Subject: [PATCH] feat(gate): 添加自定义止损功能

---
 src/main/java/com/xcong/excoin/modules/gateApi/GateConfigDTO.java |   99 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 99 insertions(+), 0 deletions(-)

diff --git a/src/main/java/com/xcong/excoin/modules/gateApi/GateConfigDTO.java b/src/main/java/com/xcong/excoin/modules/gateApi/GateConfigDTO.java
index 98e1ef1..8a7b16c 100644
--- a/src/main/java/com/xcong/excoin/modules/gateApi/GateConfigDTO.java
+++ b/src/main/java/com/xcong/excoin/modules/gateApi/GateConfigDTO.java
@@ -6,6 +6,8 @@
 import lombok.NoArgsConstructor;
 
 import java.math.BigDecimal;
+import java.util.LinkedHashMap;
+import java.util.Map;
 
 /**
  * Gate 策略配置 DTO,用于 Web 控制面板参数传递。
@@ -55,6 +57,26 @@
     private boolean isProduction;
     /** 价格驱动开关:true=启用K线推送中的价格驱动逻辑,false=禁用。null=未设置(默认true) */
     private Boolean priceDriveEnabled;
+    /** 策略运行轮数上限:达到盈利后重启计数,达到此值后不再重启。0=不限 */
+    private int rounds;
+    /** 止损次数统计方式:"single"=单向分别统计 / "dual"=双向统一统计,默认 "dual" */
+    private String stopLossCountMode;
+    /** 加仓间隔:每隔多少次止损触发一次加仓,默认 3 */
+    private int addPositionInterval;
+    /** 加仓数量:每次加仓追加的张数,默认 1 */
+    private int addPositionQuantity;
+    /** 单边最大仓位量:单向持仓张数上限,超出则按上限挂单,0=不限制 */
+    private int maxPositionPerSide;
+    /** 加仓启动阈值:前N次止损不触发加仓,默认 1 */
+    private int addPositionStartThreshold;
+    /** 止盈挂单模式:off=关闭 / concentrated=集中止盈 / dispersed=分散止盈,默认 off */
+    private String placeExcessTakeProfit;
+    /** 自定义止损开关:true=启用自定义止损触发价,false=使用默认阶梯止损。null=未设置(默认false) */
+    private Boolean customStopLossEnabled;
+    /** 多仓自定义止损触发价(价格跌至该值触发平多),null=未设置 */
+    private BigDecimal longCustomStopLossPrice;
+    /** 空仓自定义止损触发价(价格涨至该值触发平空),null=未设置 */
+    private BigDecimal shortCustomStopLossPrice;
 
     /**
      * 从 GateConfig 构建 DTO(不暴露 apiSecret)。
@@ -81,6 +103,83 @@
                         ? config.getUnrealizedPnlPriceMode().name() : "LAST_PRICE")
                 .isProduction(config.isProduction())
                 .priceDriveEnabled(config.isPriceDriveEnabled())
+                .rounds(config.getRounds())
+                .stopLossCountMode(config.getStopLossCountMode())
+                .addPositionInterval(config.getAddPositionInterval())
+                .addPositionQuantity(config.getAddPositionQuantity())
+                .maxPositionPerSide(config.getMaxPositionPerSide())
+                .addPositionStartThreshold(config.getAddPositionStartThreshold())
+                .placeExcessTakeProfit(config.getPlaceExcessTakeProfit())
+                .customStopLossEnabled(config.isCustomStopLossEnabled())
+                .longCustomStopLossPrice(config.getLongCustomStopLossPrice())
+                .shortCustomStopLossPrice(config.getShortCustomStopLossPrice())
+                .build();
+    }
+
+    /**
+     * 所有可调参数的扁平快照 — stats 埋点 + STRATEGY_START payload 用。
+     * 新增参数只需在此方法加一行,无需修改 stats 消费者。
+     */
+    public Map<String, Object> toParamsMap() {
+        Map<String, Object> m = new LinkedHashMap<>();
+        m.put("contract", contract);
+        m.put("leverage", leverage);
+        m.put("gridRate", gridRate);
+        m.put("expectedProfit", expectedProfit);
+        m.put("maxLoss", maxLoss);
+        m.put("baseQuantity", baseQuantity);
+        m.put("quantity", quantity);
+        m.put("maxPositionSize", maxPositionSize);
+        m.put("stopLossCount", stopLossCount);
+        m.put("takeProfitGridSpan", takeProfitGridSpan);
+        m.put("priceDriveEnabled", priceDriveEnabled);
+        m.put("rounds", rounds);
+        m.put("stopLossCountMode", stopLossCountMode);
+        m.put("addPositionInterval", addPositionInterval);
+        m.put("addPositionQuantity", addPositionQuantity);
+        m.put("maxPositionPerSide", maxPositionPerSide);
+        m.put("addPositionStartThreshold", addPositionStartThreshold);
+        m.put("placeExcessTakeProfit", placeExcessTakeProfit);
+        m.put("customStopLossEnabled", customStopLossEnabled);
+        m.put("longCustomStopLossPrice", longCustomStopLossPrice);
+        m.put("shortCustomStopLossPrice", shortCustomStopLossPrice);
+        return m;
+    }
+
+    /**
+     * 带默认值的工厂方法 — 统一 Manager 和 HTML 的默认值入口。
+     */
+    public static GateConfigDTO defaultsFor(String apiKey) {
+        return GateConfigDTO.builder()
+                .apiKey(apiKey)
+                .contract("ETH_USDT")
+                .leverage("100")
+                .marginMode("cross")
+                .positionMode("dual")
+                .gridRate(new BigDecimal("0.005"))
+                .expectedProfit(new BigDecimal("0.15"))
+                .maxLoss(new BigDecimal("1.5"))
+                .baseQuantity("2")
+                .quantity("2")
+                .maxPositionSize(4)
+                .stopLossCount(0)
+                .takeProfitGridSpan(2)
+                .restartGridSpan(0)
+                .priceScale(1)
+                .contractMultiplier(new BigDecimal("0.001"))
+                .unrealizedPnlPriceMode("LAST_PRICE")
+                .isProduction(true)
+                .priceDriveEnabled(true)
+                .rounds(0)
+                .stopLossCountMode("dual")
+                .addPositionInterval(3)
+                .addPositionQuantity(1)
+                .maxPositionPerSide(0)
+                .addPositionStartThreshold(1)
+                .placeExcessTakeProfit("off")
+                .customStopLossEnabled(false)
+                .longCustomStopLossPrice(null)
+                .shortCustomStopLossPrice(null)
                 .build();
     }
 }

--
Gitblit v1.9.1