From 91c4a7a84f91518604a12cff816ff3f4e670477b Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Thu, 07 May 2026 17:30:51 +0800
Subject: [PATCH] refactor(gateApi): 简化 K线回调方法参数

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

diff --git a/src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java b/src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
new file mode 100644
index 0000000..ba76e6d
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
@@ -0,0 +1,417 @@
+package com.xcong.excoin.modules.gateApi;
+
+import io.gate.gateapi.ApiClient;
+import io.gate.gateapi.ApiException;
+import io.gate.gateapi.GateApiException;
+import io.gate.gateapi.api.FuturesApi;
+import io.gate.gateapi.models.FuturesAccount;
+import io.gate.gateapi.models.FuturesInitialOrder;
+import io.gate.gateapi.models.FuturesOrder;
+import io.gate.gateapi.models.FuturesPriceTrigger;
+import io.gate.gateapi.models.FuturesPriceTriggeredOrder;
+import io.gate.gateapi.models.TriggerOrderResponse;
+import lombok.extern.slf4j.Slf4j;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.List;
+
+/**
+ * Gate 网格交易服务类,使用 gate-api SDK 进行合约下单。
+ * 策略:多空双开 → 设置止盈止损点位 → 网格循环交易
+ *
+ * 测试参数:
+ *   品种: XAU_USDT(黄金)
+ *   杠杆: 100x(全仓)
+ *   数量: 0.01 XAU
+ *   网格: 0.0035(千分之三点五)
+ *   整体止盈: 0.5 USDT
+ *   循环次数: 3
+ *   报警: 本金亏损 15%(初始本金 50 USDT)
+ *
+ * @author Administrator
+ */
+@Slf4j
+public class GateGridTradeService {
+
+    private final ApiClient apiClient;
+    private final FuturesApi futuresApi;
+    private static final String SETTLE = "usdt";
+
+    private final String contract;
+    private final String leverage;
+    private final String marginMode;
+    private final BigDecimal gridRate;
+    private final BigDecimal overallTp;
+    private final int maxCycles;
+    private final BigDecimal maxLoss;
+    private final String quantity;
+    private final String positionMode;
+
+    private volatile boolean strategyActive = false;
+    private int currentCycle = 0;
+    private BigDecimal totalProfit = BigDecimal.ZERO;
+    private BigDecimal longEntryPrice;
+    private BigDecimal shortEntryPrice;
+    private Long longOrderId;
+    private Long shortOrderId;
+
+    private volatile BigDecimal lastClosePrice;
+
+    public GateGridTradeService(String apiKey, String apiSecret,
+                                 String contract, String leverage,
+                                String marginMode,String positionMode,
+                                 BigDecimal gridRate, BigDecimal overallTp,
+                                 int maxCycles, BigDecimal maxLoss,
+                                String quantity) {
+        this.contract = contract;
+        this.leverage = leverage;
+        this.marginMode = marginMode;
+        this.gridRate = gridRate;
+        this.overallTp = overallTp;
+        this.maxCycles = maxCycles;
+        this.maxLoss = maxLoss;
+        this.quantity = quantity;
+        this.positionMode = positionMode;
+
+        this.apiClient = new ApiClient();
+        this.apiClient.setBasePath("https://api-testnet.gateapi.io/api/v4");
+        this.apiClient.setApiKeySecret(apiKey, apiSecret);
+        this.futuresApi = new FuturesApi(apiClient);
+    }
+
+    /**
+     * 初始化账户:设置持仓模式 + 杠杆
+     */
+    public void init() {
+        try {
+            futuresApi.updateContractPositionLeverageCall(
+                    SETTLE, contract, leverage, marginMode, positionMode, null);
+            log.info("[GateGrid] 已设置杠杆: {}x, 保证金模式: {}", leverage, marginMode);
+
+            FuturesAccount account = futuresApi.listFuturesAccounts(SETTLE);
+            log.info("[GateGrid] 账户可用余额: {}, 总资产: {}",
+                    account.getAvailable(), account.getTotal());
+            String positionModeSet = account.getPositionMode();
+            if (!positionMode.equals(positionModeSet)){
+                futuresApi.setPositionMode(SETTLE, positionMode);
+            }
+            log.info("[GateGrid] 已设置双向持仓模式");
+        } catch (GateApiException e) {
+            log.error("[GateGrid] 初始化失败, label: {}, msg: {}", e.getErrorLabel(), e.getMessage());
+        } catch (ApiException e) {
+            log.error("[GateGrid] 初始化API调用失败, code: {}", e.getCode());
+        }
+    }
+
+    /**
+     * 启动网格策略
+     */
+    public void startGrid() {
+        if (strategyActive) {
+            log.warn("[GateGrid] 策略已在运行中");
+            return;
+        }
+        strategyActive = true;
+        currentCycle = 0;
+        totalProfit = BigDecimal.ZERO;
+        log.info("[GateGrid] 网格策略启动, cycle: {}", currentCycle + 1);
+        dualOpenPositions();
+    }
+
+    /**
+     * 停止网格策略
+     */
+    public void stopGrid() {
+        strategyActive = false;
+        closeAllPositions();
+        log.info("[GateGrid] 网格策略已停止, 总盈亏: {}, 循环: {}", totalProfit, currentCycle);
+    }
+
+    /**
+     * K线回调:收到新的收盘价
+     */
+    public void onKline(BigDecimal closePrice) {
+        lastClosePrice = closePrice;
+        if (!strategyActive) {
+            return;
+        }
+        checkPositions(closePrice);
+    }
+
+    /**
+     * 多空双开
+     */
+    private void dualOpenPositions() {
+        try {
+            FuturesOrder longOrder = new FuturesOrder();
+            longOrder.setContract(contract);
+            longOrder.setSize(quantity);
+            longOrder.setPrice("0");
+            longOrder.setTif(FuturesOrder.TifEnum.IOC);
+            longOrder.setText("t-grid-long-" + (currentCycle + 1));
+            FuturesOrder longResult = futuresApi.createFuturesOrder(SETTLE, longOrder, null);
+            longOrderId = longResult.getId();
+            longEntryPrice = safeDecimal(longResult.getFillPrice());
+            log.info("[GateGrid] 开多成功, price: {}, id: {}", longEntryPrice, longOrderId);
+            placeLongTpSl(longEntryPrice);
+
+            FuturesOrder shortOrder = new FuturesOrder();
+            shortOrder.setContract(contract);
+            shortOrder.setSize(negateQuantity(quantity));
+            shortOrder.setPrice("0");
+            shortOrder.setTif(FuturesOrder.TifEnum.IOC);
+            shortOrder.setText("t-grid-short-" + (currentCycle + 1));
+            FuturesOrder shortResult = futuresApi.createFuturesOrder(SETTLE, shortOrder, null);
+            shortOrderId = shortResult.getId();
+            shortEntryPrice = safeDecimal(shortResult.getFillPrice());
+            log.info("[GateGrid] 开空成功, price: {}, id: {}", shortEntryPrice, shortOrderId);
+            placeShortTpSl(shortEntryPrice);
+
+            printGridInfo();
+        } catch (GateApiException e) {
+            log.error("[GateGrid] 双开失败, label: {}, msg: {}", e.getErrorLabel(), e.getMessage());
+            strategyActive = false;
+        } catch (Exception e) {
+            log.error("[GateGrid] 双开异常", e);
+            strategyActive = false;
+        }
+    }
+
+    private void placeLongTpSl(BigDecimal entryPrice) {
+        BigDecimal tpPrice = entryPrice.multiply(BigDecimal.ONE.add(gridRate)).setScale(1, RoundingMode.HALF_UP);
+        placePriceTriggeredOrder(tpPrice, FuturesPriceTrigger.RuleEnum.NUMBER_1, "close-long-position", "close_long");
+        log.info("[GateGrid] 多头止盈已设置, TP:{}", tpPrice);
+    }
+
+    private void placeShortTpSl(BigDecimal entryPrice) {
+        BigDecimal tpPrice = entryPrice.multiply(BigDecimal.ONE.subtract(gridRate)).setScale(1, RoundingMode.HALF_UP);
+        placePriceTriggeredOrder(tpPrice, FuturesPriceTrigger.RuleEnum.NUMBER_2, "close-short-position", "close_short");
+        log.info("[GateGrid] 空头止盈已设置, TP:{}", tpPrice);
+    }
+
+    private void placePriceTriggeredOrder(BigDecimal triggerPrice,
+                                           FuturesPriceTrigger.RuleEnum rule,
+                                           String orderType,
+                                           String autoSize) {
+        try {
+            FuturesPriceTrigger trigger = new FuturesPriceTrigger();
+            trigger.setStrategyType(FuturesPriceTrigger.StrategyTypeEnum.NUMBER_0);
+            trigger.setPriceType(FuturesPriceTrigger.PriceTypeEnum.NUMBER_0);
+            trigger.setPrice(triggerPrice.toString());
+            trigger.setRule(rule);
+            trigger.setExpiration(0);
+
+            FuturesInitialOrder initial = new FuturesInitialOrder();
+            initial.setContract(contract);
+            initial.setSize(0L);
+            initial.setPrice("0");
+            initial.setTif(FuturesInitialOrder.TifEnum.IOC);
+            initial.setReduceOnly(true);
+            initial.setAutoSize(autoSize);
+
+            FuturesPriceTriggeredOrder order = new FuturesPriceTriggeredOrder();
+            order.setTrigger(trigger);
+            order.setInitial(initial);
+            order.setOrderType(orderType);
+
+            TriggerOrderResponse response = futuresApi.createPriceTriggeredOrder(SETTLE, order);
+            log.info("[GateGrid] 止盈条件单已创建, triggerPrice:{}, rule:{}, orderType:{}, autoSize:{}, id:{}",
+                    triggerPrice, rule, orderType, autoSize, response.getId());
+        } catch (Exception e) {
+            log.error("[GateGrid] 止盈条件单创建失败, triggerPrice:{}, rule:{}, orderType:{}, autoSize:{}",
+                    triggerPrice, rule, orderType, autoSize, e);
+        }
+    }
+
+    /**
+     * 检查多空仓位是否触及止盈止损
+     */
+    private void checkPositions(BigDecimal currentPrice) {
+        if (longEntryPrice == null || shortEntryPrice == null) {
+            return;
+        }
+
+        BigDecimal longTp = longEntryPrice.multiply(BigDecimal.ONE.add(gridRate)).setScale(1, RoundingMode.HALF_UP);
+        BigDecimal longSl = longEntryPrice.multiply(BigDecimal.ONE.subtract(gridRate)).setScale(1, RoundingMode.HALF_UP);
+        BigDecimal shortTp = shortEntryPrice.multiply(BigDecimal.ONE.subtract(gridRate)).setScale(1, RoundingMode.HALF_UP);
+        BigDecimal shortSl = shortEntryPrice.multiply(BigDecimal.ONE.add(gridRate)).setScale(1, RoundingMode.HALF_UP);
+
+        System.out.println("========== Gate 网格状态 ==========");
+        System.out.println("当前价格: " + currentPrice);
+        System.out.println("多头入场: " + longEntryPrice + " TP: " + longTp + " SL: " + longSl);
+        System.out.println("空头入场: " + shortEntryPrice + " TP: " + shortTp + " SL: " + shortSl);
+        System.out.println("累计盈亏: " + totalProfit + " 循环: " + currentCycle + "/" + maxCycles);
+        System.out.println("===================================");
+
+        // 多头止盈
+        if (currentPrice.compareTo(longTp) >= 0) {
+            log.info("[GateGrid] 多头止盈触发! entry:{}, current:{}", longEntryPrice, currentPrice);
+            BigDecimal cnt = new BigDecimal(quantity);
+            BigDecimal profit = currentPrice.subtract(longEntryPrice).multiply(cnt);
+            totalProfit = totalProfit.add(profit);
+            log.info("[GateGrid] 多头止盈 profit:{}, totalProfit:{}", profit, totalProfit);
+            closeLongPosition();
+            closeShortPosition();
+            currentCycle++;
+            checkStopConditions();
+            return;
+        }
+        // 多头止损
+        if (currentPrice.compareTo(longSl) <= 0) {
+            log.info("[GateGrid] 多头止损触发! entry:{}, current:{}", longEntryPrice, currentPrice);
+            BigDecimal cnt = new BigDecimal(quantity);
+            BigDecimal loss = longEntryPrice.subtract(currentPrice).multiply(cnt);
+            totalProfit = totalProfit.subtract(loss);
+            log.info("[GateGrid] 多头止损 loss:{}, totalProfit:{}", loss, totalProfit);
+            closeLongPosition();
+            currentCycle++;
+            checkStopConditions();
+            return;
+        }
+        // 空头止盈
+        if (currentPrice.compareTo(shortTp) <= 0) {
+            log.info("[GateGrid] 空头止盈触发! entry:{}, current:{}", shortEntryPrice, currentPrice);
+            BigDecimal cnt = new BigDecimal(quantity);
+            BigDecimal profit = shortEntryPrice.subtract(currentPrice).multiply(cnt);
+            totalProfit = totalProfit.add(profit);
+            log.info("[GateGrid] 空头止盈 profit:{}, totalProfit:{}", profit, totalProfit);
+            closeShortPosition();
+            closeLongPosition();
+            currentCycle++;
+            checkStopConditions();
+            return;
+        }
+        // 空头止损
+        if (currentPrice.compareTo(shortSl) >= 0) {
+            log.info("[GateGrid] 空头止损触发! entry:{}, current:{}", shortEntryPrice, currentPrice);
+            BigDecimal cnt = new BigDecimal(quantity);
+            BigDecimal loss = currentPrice.subtract(shortEntryPrice).multiply(cnt);
+            totalProfit = totalProfit.subtract(loss);
+            log.info("[GateGrid] 空头止损 loss:{}, totalProfit:{}", loss, totalProfit);
+            closeShortPosition();
+            currentCycle++;
+            checkStopConditions();
+        }
+    }
+
+    private void checkStopConditions() {
+        if (totalProfit.compareTo(overallTp) >= 0) {
+            log.info("[GateGrid] 达到整体止盈 {} USDT,停止策略", overallTp);
+            strategyActive = false;
+            return;
+        }
+        if (BigDecimal.ZERO.subtract(totalProfit).compareTo(maxLoss) >= 0) {
+            log.info("[GateGrid] 亏损 {} 达到上限 {} USDT,停止策略", totalProfit.negate(), maxLoss);
+            strategyActive = false;
+            return;
+        }
+        if (currentCycle >= maxCycles) {
+            log.info("[GateGrid] 达到最大循环次数 {},停止策略", maxCycles);
+            strategyActive = false;
+            return;
+        }
+        log.info("[GateGrid] 进入下一轮循环: {}", currentCycle + 1);
+        dualOpenPositions();
+    }
+
+    private void closeLongPosition() {
+        if (longEntryPrice == null) {
+            return;
+        }
+        try {
+            FuturesOrder closeOrder = new FuturesOrder();
+            closeOrder.setContract(contract);
+            closeOrder.setSize(negateQuantity(quantity));
+            closeOrder.setPrice("0");
+            closeOrder.setTif(FuturesOrder.TifEnum.IOC);
+            closeOrder.setReduceOnly(true);
+            closeOrder.setText("t-grid-close-long");
+            FuturesOrder result = futuresApi.createFuturesOrder(SETTLE, closeOrder, null);
+            log.info("[GateGrid] 平多成功, id: {}, fillPrice: {}", result.getId(), result.getFillPrice());
+        } catch (Exception e) {
+            log.error("[GateGrid] 平多失败", e);
+        }
+        longEntryPrice = null;
+        longOrderId = null;
+    }
+
+    private void closeShortPosition() {
+        if (shortEntryPrice == null) {
+            return;
+        }
+        try {
+            FuturesOrder closeOrder = new FuturesOrder();
+            closeOrder.setContract(contract);
+            closeOrder.setSize(quantity);
+            closeOrder.setPrice("0");
+            closeOrder.setTif(FuturesOrder.TifEnum.IOC);
+            closeOrder.setReduceOnly(true);
+            closeOrder.setText("t-grid-close-short");
+            FuturesOrder result = futuresApi.createFuturesOrder(SETTLE, closeOrder, null);
+            log.info("[GateGrid] 平空成功, id: {}, fillPrice: {}", result.getId(), result.getFillPrice());
+        } catch (Exception e) {
+            log.error("[GateGrid] 平空失败", e);
+        }
+        shortEntryPrice = null;
+        shortOrderId = null;
+    }
+
+    private void closeAllPositions() {
+        closeLongPosition();
+        closeShortPosition();
+    }
+
+    private void printGridInfo() {
+        BigDecimal longTp = BigDecimal.ZERO;
+        BigDecimal longSl = BigDecimal.ZERO;
+        BigDecimal shortTp = BigDecimal.ZERO;
+        BigDecimal shortSl = BigDecimal.ZERO;
+        if (longEntryPrice != null) {
+            longTp = longEntryPrice.multiply(BigDecimal.ONE.add(gridRate)).setScale(1, RoundingMode.HALF_UP);
+            longSl = longEntryPrice.multiply(BigDecimal.ONE.subtract(gridRate)).setScale(1, RoundingMode.HALF_UP);
+        }
+        if (shortEntryPrice != null) {
+            shortTp = shortEntryPrice.multiply(BigDecimal.ONE.subtract(gridRate)).setScale(1, RoundingMode.HALF_UP);
+            shortSl = shortEntryPrice.multiply(BigDecimal.ONE.add(gridRate)).setScale(1, RoundingMode.HALF_UP);
+        }
+        System.out.println("========== Gate 网格开仓 ==========");
+        System.out.println("合约: " + contract + " 杠杆: " + leverage + "x " + marginMode);
+        System.out.println("多头入场: " + longEntryPrice + " TP: " + longTp + " SL: " + longSl);
+        System.out.println("空头入场: " + shortEntryPrice + " TP: " + shortTp + " SL: " + shortSl);
+        System.out.println("数量: " + quantity + " 网格间距: " + gridRate.multiply(new BigDecimal("100")) + "%");
+        System.out.println("整体止盈: " + overallTp + " USDT 最大循环: " + maxCycles);
+        System.out.println("最大亏损: " + maxLoss + " USDT");
+        System.out.println("=====================================");
+    }
+
+    private String negateQuantity(String qty) {
+        if (qty.startsWith("-")) {
+            return qty.substring(1);
+        }
+        return "-" + qty;
+    }
+
+    private BigDecimal safeDecimal(String val) {
+        if (val == null || val.isEmpty()) {
+            return BigDecimal.ZERO;
+        }
+        return new BigDecimal(val);
+    }
+
+    public BigDecimal getLastClosePrice() {
+        return lastClosePrice;
+    }
+
+    public boolean isStrategyActive() {
+        return strategyActive;
+    }
+
+    public BigDecimal getTotalProfit() {
+        return totalProfit;
+    }
+
+    public int getCurrentCycle() {
+        return currentCycle;
+    }
+}

--
Gitblit v1.9.1