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; } }