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