package com.xcong.excoin.modules.gateApi; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.math.BigDecimal; import java.util.LinkedHashMap; import java.util.Map; /** * Gate 策略配置 DTO,用于 Web 控制面板参数传递。 * apiSecret 不暴露给前端。 */ @Data @Builder @NoArgsConstructor @AllArgsConstructor public class GateConfigDTO { /** API Key */ private String apiKey; /** 合约名称,如 ETH_USDT */ private String contract; /** 杠杆倍数 */ private String leverage; /** 保证金模式:cross / isolated */ private String marginMode; /** 持仓模式:single / dual */ private String positionMode; /** 网格间距比例 */ private BigDecimal gridRate; /** 预期收益(USDT) */ private BigDecimal expectedProfit; /** 最大亏损(USDT) */ private BigDecimal maxLoss; /** 基底开仓张数 */ private String baseQuantity; /** 每次下单张数 */ private String quantity; /** 最大持仓张数(单方向),0=不限制 */ private int maxPositionSize; /** 止损阶梯次数,0=禁用 */ private int stopLossCount; /** 止盈网格跨度:相邻止盈单跨越的网格数量,默认 2 */ private int takeProfitGridSpan; /** 策略重启跨度阈值,0=禁用 */ private int restartGridSpan; /** 价格精度 */ private int priceScale; /** 合约乘数 */ private BigDecimal contractMultiplier; /** 未实现盈亏计价模式:LAST_PRICE / MARK_PRICE */ private String unrealizedPnlPriceMode; /** 是否为生产环境 */ 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; /** 超额止盈开关:true=挂单成交后将超出基础仓位的部分挂止盈单,默认 false */ private boolean placeExcessTakeProfit; /** * 从 GateConfig 构建 DTO(不暴露 apiSecret)。 */ public static GateConfigDTO from(GateConfig config) { return GateConfigDTO.builder() .apiKey(config.getApiKey()) .contract(config.getContract()) .leverage(config.getLeverage()) .marginMode(config.getMarginMode()) .positionMode(config.getPositionMode()) .gridRate(config.getGridRate()) .expectedProfit(config.getExpectedProfit()) .maxLoss(config.getMaxLoss()) .baseQuantity(config.getBaseQuantity()) .quantity(config.getQuantity()) .maxPositionSize(config.getMaxPositionSize()) .stopLossCount(config.getStopLossCount()) .takeProfitGridSpan(config.getTakeProfitGridSpan()) .restartGridSpan(config.getRestartGridSpan()) .priceScale(config.getPriceScale()) .contractMultiplier(config.getContractMultiplier()) .unrealizedPnlPriceMode(config.getUnrealizedPnlPriceMode() != null ? 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.isPlaceExcessTakeProfit()) .build(); } /** * 所有可调参数的扁平快照 — stats 埋点 + STRATEGY_START payload 用。 * 新增参数只需在此方法加一行,无需修改 stats 消费者。 */ public Map toParamsMap() { Map 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); 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(false) .build(); } }