Administrator
4 days ago b7e21f850f899813f397f5d2c35659bd48437990
refactor(gateApi): 重构网格交易服务为持续循环模式

- 移除循环次数限制,改为基于止盈止损条件的持续运行
- 新增仓位推送回调处理,支持实时仓位状态更新
- 实现止盈触发后的自动补仓机制
- 优化K线回调逻辑,首次价格就绪时执行双开操作
- 统一使用history_pnl进行盈亏统计和停止条件判断
- 添加仓位频道订阅功能,支持实时仓位数据接收
- 移除手动平仓逻辑,改用平台止盈止损自动处理
4 files modified
1 files added
488 ■■■■■ changed files
src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java 114 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/gateApi/GateKlineWebSocketClient.java 75 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/gateApi/GateWebSocketClientMain.java 16 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/gateApi/GateWebSocketClientManager.java 40 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/gateApi/gateApi-logic.md 243 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
@@ -15,6 +15,30 @@
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
 * Gate 网格交易服务类。
 *
 * <h3>策略概述</h3>
 * 多空双开 → 各放止盈条件单 → 仓位推送驱动补仓 → history_pnl 判断停止
 *
 * <h3>触发逻辑</h3>
 * <pre>
 *   K线首次价格就绪 → dualOpenPositions()     // 多空双开 + 止盈条件单
 *   仓位推送 size=0  → reopenXxxPosition()     // 该方向被止盈平掉 → 补开
 *   仓位推送 history_pnl ≥ overallTp → 停止
 *   仓位推送 history_pnl ≤ -maxLoss → 停止
 * </pre>
 *
 * <h3>止盈计算</h3>
 * 多头止盈价 = entryPrice × (1 + gridRate)<br>
 * 空头止盈价 = entryPrice × (1 - gridRate)
 *
 * <h3>依赖</h3>
 * 使用 {@code io.gate:gate-api (7.2.71)} SDK 通过 REST API 下单,
 * 市场数据由 {@link GateKlineWebSocketClient} 通过 WebSocket 提供。
 *
 * @author Administrator
 */
@Slf4j
public class GateGridTradeService {
@@ -31,19 +55,36 @@
    private final String quantity;
    private final String positionMode;
    /** 策略是否处于运行状态 */
    private volatile boolean strategyActive = false;
    /** 是否已完成首次双开 */
    private volatile boolean dualOpened = false;
    /** 多头仓位是否活跃 */
    private volatile boolean longActive = false;
    /** 空头仓位是否活跃 */
    private volatile boolean shortActive = false;
    /** 多头入场价 */
    private BigDecimal longEntryPrice;
    /** 空头入场价 */
    private BigDecimal shortEntryPrice;
    /** WebSocket 推送的最新 K 线收盘价 */
    private volatile BigDecimal lastKlinePrice;
    /** 服务器返回的累计已实现盈亏 */
    private BigDecimal totalHistoryPnl = BigDecimal.ZERO;
    /**
     * 构造函数,初始化 Gate 期货 API 客户端。
     *
     * @param contract     合约名称(如 XAU_USDT)
     * @param leverage     杠杆倍数
     * @param marginMode   保证金模式(cross/isolated)
     * @param positionMode 持仓模式(single/dual/dual_plus)
     * @param gridRate    网格间距比例(如 0.0035)
     * @param overallTp   整体止盈阈值(USDT)
     * @param maxLoss     最大亏损阈值(USDT)
     * @param quantity     下单数量(合约张数)
     */
    public GateGridTradeService(String apiKey, String apiSecret,
                                 String contract, String leverage,
                                String marginMode, String positionMode,
@@ -65,6 +106,9 @@
        this.futuresApi = new FuturesApi(apiClient);
    }
    /**
     * 初始化账户。设置杠杆、查询余额、切换持仓模式。
     */
    public void init() {
        try {
            futuresApi.updateContractPositionLeverageCall(
@@ -86,6 +130,9 @@
        }
    }
    /**
     * 启动网格策略。策略激活后等待 K 线价格就绪,然后自动首次双开。
     */
    public void startGrid() {
        if (strategyActive) {
            log.warn("[GateGrid] 策略已在运行中");
@@ -96,13 +143,19 @@
        log.info("[GateGrid] 网格策略启动,等待K线价格...");
    }
    /**
     * 停止网格策略。
     */
    public void stopGrid() {
        strategyActive = false;
        log.info("[GateGrid] 网格策略已停止, history_pnl: {}", totalHistoryPnl);
    }
    /**
     * K线回调:存储最新价格,首次价格就绪时双开
     * K 线回调入口。由  调用。
     * 首次收到价格时触发多空双开,后续仅缓存最新价格供补仓使用。
     *
     * @param closePrice K 线收盘价
     */
    public void onKline(BigDecimal closePrice) {
        lastKlinePrice = closePrice;
@@ -116,7 +169,20 @@
    }
    /**
     * 仓位推送回调:检测止盈平仓 → 补仓,累加 history_pnl → 判断停止
     * 仓位推送回调入口。由 {@link GateKlineWebSocketClient} 调用。
     * 根据仓位模式(dual_long/dual_short)和 size 判断:
     * <ul>
     *   <li>size=0 且之前活跃 → 该方向被平仓 → 补开</li>
     *   <li>size>0 → 确认仓位活跃,更新入场价</li>
     * </ul>
     * 每次推送更新 history_pnl 并检查停止条件。
     *
     * @param contract   合约名
     * @param mode       仓位模式(dual_long / dual_short)
     * @param size       仓位数量(0 表示无仓位)
     * @param entryPrice 入场价格
     * @param historyPnl  已实现累计盈亏
     * @param realisedPnl 已实现盈亏
     */
    public void onPositionUpdate(String contract, String mode, BigDecimal size,
                                  BigDecimal entryPrice, BigDecimal historyPnl,
@@ -151,6 +217,13 @@
        checkStopConditions();
    }
    /**
     * 检查策略停止条件。满足任一即置 strategyActive=false:
     * <ul>
     *   <li>累计盈利 ≥ overallTp</li>
     *   <li>累计亏损 ≤ -maxLoss</li>
     * </ul>
     */
    private void checkStopConditions() {
        if (totalHistoryPnl.compareTo(overallTp) >= 0) {
            log.info("[GateGrid] 累计止盈 {} 达到 {} USDT,停止策略", totalHistoryPnl, overallTp);
@@ -163,6 +236,10 @@
        }
    }
    /**
     * 首次多空双开。使用当前 K 线价格以市价单同时开多和开空,
     * 开仓成功后立即为每个方向创建止盈条件单。
     */
    private void dualOpenPositions() {
        if (lastKlinePrice == null) {
            log.warn("[GateGrid] K线价格未就绪,跳过双开");
@@ -204,6 +281,9 @@
        }
    }
    /**
     * 补开多头仓位。多头被止盈平掉后调用,市价重新开多并创建止盈单。
     */
    private void reopenLongPosition() {
        if (lastKlinePrice == null || !strategyActive) {
            return;
@@ -229,6 +309,9 @@
        }
    }
    /**
     * 补开空头仓位。空头被止盈平掉后调用,市价重新开空并创建止盈单。
     */
    private void reopenShortPosition() {
        if (lastKlinePrice == null || !strategyActive) {
            return;
@@ -254,18 +337,34 @@
        }
    }
    /**
     * 创建多头止盈条件单。
     * 触发价 = entryPrice × (1 + gridRate),价格 ≥ 触发价时平多。
     */
    private void placeLongTp(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);
    }
    /**
     * 创建空头止盈条件单。
     * 触发价 = entryPrice × (1 - gridRate),价格 ≤ 触发价时平空。
     */
    private void placeShortTp(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);
    }
    /**
     * 通过 Gate REST API 创建止盈条件单。
     *
     * @param triggerPrice 触发价格
     * @param rule         触发规则(1: ≥, 2: ≤)
     * @param orderType    止盈止损类型(close-long-position / close-short-position)
     * @param autoSize      双仓平仓方向(close_long / close_short)
     */
    private void placePriceTriggeredOrder(BigDecimal triggerPrice,
                                           FuturesPriceTrigger.RuleEnum rule,
                                           String orderType,
@@ -300,6 +399,9 @@
        }
    }
    /**
     * 打印当前网格配置和入场信息。
     */
    private void printGridInfo() {
        BigDecimal longTp = BigDecimal.ZERO;
        BigDecimal shortTp = BigDecimal.ZERO;
@@ -318,6 +420,7 @@
        log.info("=====================================");
    }
    /** 对数量取反(开多用正数,开空用负数) */
    private String negateQuantity(String qty) {
        if (qty.startsWith("-")) {
            return qty.substring(1);
@@ -325,6 +428,7 @@
        return "-" + qty;
    }
    /** 安全转换字符串为 BigDecimal,null 返回 0 */
    private BigDecimal safeDecimal(String val) {
        if (val == null || val.isEmpty()) {
            return BigDecimal.ZERO;
src/main/java/com/xcong/excoin/modules/gateApi/GateKlineWebSocketClient.java
@@ -16,14 +16,33 @@
import java.math.BigDecimal;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
/**
 * Gate K线 WebSocket 客户端类,用于连接 Gate 的 WebSocket 接口,
 * 实时获取并处理 K线(candlestick)数据。
 * 同时支持心跳检测、自动重连以及异常恢复机制。
 * Gate WebSocket 客户端,通过 Java-WebSocket 库连接 Gate.io 的 WebSocket 接口。
 *
 * <h3>订阅频道</h3>
 * <ul>
 *   <li>{@code futures.candlesticks} — 1m K 线数据(公开频道)</li>
 *   <li>{@code futures.positions} — 用户仓位更新(私有频道,需 HMAC-SHA512 签名认证)</li>
 *   <li>{@code futures.ping} — 应用层心跳</li>
 * </ul>
 *
 * <h3>数据流</h3>
 * <pre>
 *   onMessage → handleWebSocketMessage → 按 channel 分流:
 *     futures.pong       → 取消心跳超时
 *     subscribe/unsubscribe/error → 日志
 *     futures.candlesticks → processPushDataV2 → GateGridTradeService.onKline()
 *     futures.positions    → processPositionData  → GateGridTradeService.onPositionUpdate()
 * </pre>
 *
 * <h3>连接管理</h3>
 * 支持心跳检测、指数退避重连(最多 3 次)、优雅关闭。
 *
 * @author Administrator
 */
@Slf4j
@@ -35,23 +54,35 @@
    private WebSocketClient webSocketClient;
    private ScheduledExecutorService heartbeatExecutor;
    private volatile ScheduledFuture<?> pongTimeoutFuture;
    /** 最后收到消息的时间戳,用于心跳超时检测 */
    private final AtomicReference<Long> lastMessageTime = new AtomicReference<>(System.currentTimeMillis());
    // 连接状态标志
    /** 连接状态 */
    private final AtomicBoolean isConnected = new AtomicBoolean(false);
    /** 连接中标记,防止重复连接 */
    private final AtomicBoolean isConnecting = new AtomicBoolean(false);
    /** 初始化标记,防止重复初始化 */
    private final AtomicBoolean isInitialized = new AtomicBoolean(false);
    /** K 线频道 */
    private static final String CHANNEL = "futures.candlesticks";
    /** 仓位频道(私有,需认证) */
    private static final String POSITIONS_CHANNEL = "futures.positions";
    /** 应用层 ping 频道 */
    private static final String FUTURES_PING = "futures.ping";
    /** 应用层 pong 频道 */
    private static final String FUTURES_PONG = "futures.pong";
    /** K 线周期 */
    private static final String GATE_INTERVAL = "1m";
    /** 订阅合约 */
    private static final String GATE_CONTRACT = "XAUT_USDT";
    /** 网格交易策略实例 */
    private GateGridTradeService gridTradeService;
    /** API 密钥,用于私有频道签名 */
    private final String apiKey;
    /** API 密钥 */
    private final String apiSecret;
    private static final char[] HEX_ARRAY = "0123456789abcdef".toCharArray();
@@ -251,6 +282,10 @@
        webSocketClient.send(subscribeMsg.toJSONString());
        log.info("已发送 K线频道订阅请求,合约: {}, 周期: {}", GATE_CONTRACT, GATE_INTERVAL);
    }
    /**
     * 发送应用层 ping 请求。
     * 用于探测连接状态,服务器会返回 futures.pong。
     */
    private void subscribePingChannels() {
        JSONObject subscribeMsg = new JSONObject();
        subscribeMsg.put("time", System.currentTimeMillis() / 1000);
@@ -259,6 +294,10 @@
        log.info("已发送 futures.ping");
    }
    /**
     * 订阅仓位频道(私有频道,需 HMAC-SHA512 签名认证)。
     * 签名算法: Hex(HmacSHA512(secret, "channel={channel}&event={event}&time={time}"))
     */
    private void subscribePositionsChannels() {
        JSONObject subscribeMsg = new JSONObject();
        long timeSec = System.currentTimeMillis() / 1000;
@@ -280,25 +319,38 @@
        log.info("已发送仓位频道订阅请求(含认证),合约: {}", GATE_CONTRACT);
    }
    /**
     * 计算 Gate API v4 的 HMAC-SHA512 签名。
     *
     * @param channel 频道名
     * @param event   事件名(subscribe/unsubscribe)
     * @param timeSec 时间戳(秒)
     * @return 十六进制签名字符串
     */
    private String hs512Sign(String channel, String event, long timeSec) {
        try {
            String message = "channel=" + channel + "&event=" + event + "&time=" + timeSec;
            Mac mac = Mac.getInstance("HmacSHA512");
            SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(), "HmacSHA512");
            SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA512");
            mac.init(spec);
            byte[] hash = mac.doFinal(message.getBytes());
            byte[] hash = mac.doFinal(message.getBytes(StandardCharsets.UTF_8));
            StringBuilder hex = new StringBuilder(hash.length * 2);
            for (byte b : hash) {
                hex.append(HEX_ARRAY[(b >> 4) & 0xF]);
                hex.append(HEX_ARRAY[b & 0xF]);
            }
            return hex.toString();
            String sign = hex.toString();
            log.debug("签名计算, message: {}, sign: {}", message, sign);
            return sign;
        } catch (Exception e) {
            log.error("签名计算失败", e);
            return "";
        }
    }
    /**
     * 取消订阅 K 线频道。
     */
    private void unsubscribeKlineChannels() {
        JSONObject unsubscribeMsg = new JSONObject();
        unsubscribeMsg.put("time", System.currentTimeMillis() / 1000);
@@ -312,6 +364,9 @@
        log.info("已发送 K线频道取消订阅请求,合约: {}, 周期: {}", GATE_CONTRACT, GATE_INTERVAL);
    }
    /**
     * 取消订阅仓位频道。
     */
    private void unsubscribePositionsChannels() {
        JSONObject unsubscribeMsg = new JSONObject();
        unsubscribeMsg.put("time", System.currentTimeMillis() / 1000);
@@ -434,6 +489,12 @@
        }
    }
    /**
     * 解析仓位推送数据,提取目标合约的仓位信息并回调交易服务。
     * 推送字段: contract, mode(dual_long/dual_short), size, entry_price, history_pnl, realised_pnl
     *
     * @param response 仓位推送的 JSON 对象
     */
    private void processPositionData(JSONObject response) {
        try {
            JSONArray resultArray = response.getJSONArray("result");
src/main/java/com/xcong/excoin/modules/gateApi/GateWebSocketClientMain.java
@@ -1,19 +1,21 @@
package com.xcong.excoin.modules.gateApi;
import com.xcong.excoin.modules.okxNewPrice.OkxWebSocketClientManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
 * Gate 网格交易的独立测试入口(main 方法)。
 * 通过 Spring XML 上下文初始化管理器,运行一段时间后手动关闭。
 *
 * @author Administrator
 */
public class GateWebSocketClientMain {
    public static void main(String[] args) throws InterruptedException {
        // 使用Spring上下文初始化管理器
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        GateWebSocketClientManager manager = context.getBean(GateWebSocketClientManager.class);
        // 运行一段时间以观察结果
        Thread.sleep(1200000000L); // 运行一小时
        // 关闭连接
        Thread.sleep(1200000000L);
        manager.destroy();
    }
}
}
src/main/java/com/xcong/excoin/modules/gateApi/GateWebSocketClientManager.java
@@ -2,7 +2,6 @@
import com.xcong.excoin.modules.okxNewPrice.celue.CaoZuoService;
import com.xcong.excoin.modules.okxNewPrice.okxWs.wanggeList.WangGeListService;
import com.xcong.excoin.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -12,7 +11,32 @@
import java.math.BigDecimal;
/**
 * 管理 Gate WebSocket 客户端和网格交易服务实例
 * Gate 模块入口管理类,作为 Spring Bean 负责组件的生命周期。
 *
 * <h3>启动流程</h3>
 * <pre>
 *   @PostConstruct init():
 *     1. new GateGridTradeService(参数) → init()     // 设杠杆、查余额、切双向持仓
 *     2. new GateKlineWebSocketClient → init()        // 连接 WebSocket,订阅 K 线 + 仓位
 *     3. gridTradeService.startGrid()                 // 激活策略,等待 K 线触发首次双开
 * </pre>
 *
 * <h3>销毁流程</h3>
 * <pre>
 *   @PreDestroy destroy():
 *     1. gridTradeService.stopGrid()                  // 停止策略
 *     2. klinePriceClient.destroy()                   // 取消订阅 → 关闭 WS → 关闭线程池
 * </pre>
 *
 * <h3>配置参数</h3>
 * 当前配置在代码中硬编码:
 * <ul>
 *   <li>合约: XAU_USDT, 杠杆: 30x, 全仓, 双向持仓</li>
 *   <li>网格间距: 0.35%, 整体止盈: 0.5 USDT, 最大亏损: 7.5 USDT</li>
 *   <li>数量: 10 张</li>
 * </ul>
 *
 * @author Administrator
 */
@Slf4j
@Component
@@ -22,12 +46,19 @@
    @Autowired
    private WangGeListService wangGeListService;
    /** K 线 WebSocket 客户端 */
    private GateKlineWebSocketClient klinePriceClient;
    /** 网格交易服务 */
    private GateGridTradeService gridTradeService;
    /** Gate 测试网 API Key */
    private static final String API_KEY = "d90ca272391992b8e74f8f92cedb21ec";
    /** Gate 测试网 API Secret */
    private static final String API_SECRET = "1861e4f52de4bb53369ea3208d9ede38ece4777368030f96c77d27934c46c274";
    /**
     * Spring 容器启动后自动调用。初始化网格交易服务和 WebSocket 客户端。
     */
    @PostConstruct
    public void init() {
        log.info("开始初始化GateWebSocketClientManager");
@@ -57,6 +88,9 @@
        }
    }
    /**
     * Spring 容器销毁前自动调用。停止策略并关闭所有连接。
     */
    @PreDestroy
    public void destroy() {
        log.info("开始销毁GateWebSocketClientManager");
@@ -83,4 +117,4 @@
    public GateGridTradeService getGridTradeService() {
        return gridTradeService;
    }
}
}
src/main/java/com/xcong/excoin/modules/gateApi/gateApi-logic.md
New file
@@ -0,0 +1,243 @@
# Gate Api 模块 — 网格交易系统
## 文件列表
| 文件 | 类型 | 说明 |
|------|------|------|
| [GateWebSocketClientManager](#gatewebsocketclientmanager) | `@Component` | 启动入口,生命周期管理 |
| [GateKlineWebSocketClient](#gateklinewebsocketclient) | WebSocket 客户端 | K 线 + 仓位数据监听 |
| [GateGridTradeService](#gategridtradeservice) | 交易服务 | 网格策略 + REST 下单 |
| [GateWebSocketClientMain](#gatewebsocketclientmain) | main 入口 | 独立测试启动 |
| [Example.java](#examplejava) | 示例 | Gate SDK 用法参考 |
---
## 架构总览
```
┌────────────────────────────────────────────────────────────┐
│                  GateWebSocketClientManager                │
│                    (Spring @Component)                     │
│                                                            │
│  @PostConstruct init():                                    │
│    ┌──────────────────────┐   ┌─────────────────────────┐  │
│    │ GateGridTradeService │   │ GateKlineWebSocketClient │  │
│    │  ▶ init()            │   │  ▶ init()                │  │
│    │  ▶ startGrid()       │←──│  ▶ connect()             │  │
│    └──────┬───────────────┘   └───────────┬─────────────┘  │
│           │ REST API                      │ WebSocket       │
│           ▼                               ▼                │
│     Gate Testnet API           Gate Testnet WS             │
│  (https://api-testnet...)   (wss://ws-testnet.gate.com)   │
└────────────────────────────────────────────────────────────┘
```
---
## 数据流
```
WebSocket 推送
├─ futures.candlesticks (update)
│   └─ GateKlineWebSocketClient.processPushDataV2()
│       ├─ 解析: o/h/l/c/v/a/t/w
│       ├─ 打印 K 线日志
│       └─ GateGridTradeService.onKline(closePx)
│           ├─ 首次 → dualOpenPositions() → 开多 + 开空 + TP 单
│           └─ 后续 → 仅缓存 lastKlinePrice
├─ futures.positions (update) [需 HMAC-SHA512 签名]
│   └─ GateKlineWebSocketClient.processPositionData()
│       ├─ 解析: contract/mode/size/entry_price/history_pnl/realised_pnl
│       └─ GateGridTradeService.onPositionUpdate(...)
│           ├─ size=0 && longActive → reopenLongPosition()
│           ├─ size=0 && shortActive → reopenShortPosition()
│           ├─ size>0 → 确认仓位活跃
│           └─ checkStopConditions(history_pnl)
├─ futures.pong
│   └─ cancelPongTimeout()
└─ subscribe/unsubscribe/error
    └─ 日志输出
```
---
## 策略时序
### 阶段 1:启动与初始化
```
Spring 启动
  → GateWebSocketClientManager.init()
    → GateGridTradeService.init()
      → REST: 设杠杆 30x cross
      → REST: 查账户余额
      → REST: 切双向持仓 dual
    → GateKlineWebSocketClient.init()
      → WebSocket: connect()
        → onOpen: subscribe candlesticks + positions + ping
    → GateGridTradeService.startGrid()
      → strategyActive = true
```
### 阶段 2:首次开仓
```
K 线推送 closePrice=80000
  → onKline(80000)
    → dualOpened = true
    → dualOpenPositions()
      → REST: 市价开多 10 张 → longEntryPrice=80000
      → REST: 创建多头止盈单 TP=80000×1.0035=80280
      → REST: 市价开空 10 张 → shortEntryPrice=80000
      → REST: 创建空头止盈单 TP=80000×0.9965=79720
```
### 阶段 3:止盈触发 → 补仓
```
仓位推送: mode=dual_long, size=0  (多头被止盈平掉了)
  → longActive=true && size=0
    → longActive=false
    → reopenLongPosition()
      → REST: 市价开多 10 张 (用最新 K 线价)
      → REST: 创建新的多头止盈单
仓位推送: history_pnl=0.12  (未达阈值,继续)
```
### 阶段 4:停止
```
仓位推送: history_pnl ≥ 0.5 (累计盈利达标)
  → strategyActive = false  → 不再补仓
仓位推送: history_pnl ≤ -7.5 (亏损超限)
  → strategyActive = false  → 不再补仓
```
---
## GateWebSocketClientManager
**角色**: Spring Bean 入口,组装并管理所有 Gate 模块组件。
**关键方法**:
- `init()`: 创建 `GateGridTradeService` → 初始化 → 创建 `GateKlineWebSocketClient` → 启动策略
- `destroy()`: 停止策略 → 关闭 WebSocket
**当前参数**(硬编码在 `init()` 中):
| 参数 | 值 | 说明 |
|------|-----|------|
| 合约 | XAU_USDT | 黄金 |
| 杠杆 | 30x | 全仓模式 |
| 持仓模式 | dual | 双向持仓 |
| 网格间距 | 0.0035 | 0.35% |
| 整体止盈 | 0.5 USDT | |
| 最大亏损 | 7.5 USDT | 本金 50×15% |
| 下单量 | 10 张 | |
---
## GateKlineWebSocketClient
**角色**: WebSocket 客户端,订阅 Gate 的行情和仓位频道。
**订阅频道**:
| 频道 | 类型 | 认证 | 用途 |
|------|------|------|------|
| `futures.candlesticks` | 公开 | 否 | 1m K 线数据 |
| `futures.positions` | 私有 | HMAC-SHA512 | 用户仓位更新 |
| `futures.ping` | 公开 | 否 | 应用层心跳 |
**签名算法**(`futures.positions` 订阅时使用):
```
message = "channel=futures.positions&event=subscribe&time={秒级时间戳}"
SIGN = Hex(HmacSHA512(apiSecret, message))
```
**连接管理**:
- 心跳: 10 秒超时,每 25 秒检查,超时发 ping
- 重连: 指数退避,最多 3 次,初始 5 秒
- 关闭: 先取消订阅 → 等 500ms → 关闭连接 → 关闭线程池
**消息路由** (`handleWebSocketMessage`):
| channel | event | 处理 |
|---------|-------|------|
| `futures.pong` | — | `cancelPongTimeout()` |
| — | `subscribe` | 打日志 |
| — | `unsubscribe` | 打日志 |
| — | `error` | 打错误日志 |
| `futures.candlesticks` | `update`/`all` | `processPushDataV2()` |
| `futures.positions` | `update`/`all` | `processPositionData()` |
---
## GateGridTradeService
**角色**: 使用 Gate SDK (`io.gate:gate-api:7.2.71`) 通过 REST API 执行合约下单。
**状态机**:
```
strategyActive=false ──startGrid()──→ strategyActive=true (等待K线)
                                          │
                                    onKline(price)
                                          │
                                    dualOpened=true
                                    dualOpenPositions()
                                          │
                              ┌───────────┴───────────┐
                              ▼                       ▼
                         longActive=true         shortActive=true
                              │                       │
                    仓位推送 size=0             仓位推送 size=0
                              │                       │
                              ▼                       ▼
                       reopenLong()             reopenShort()
                              │                       │
                              └───────────┬───────────┘
                                          │
                                    checkStopConditions()
                                          │
                              ┌───────────┴───────────┐
                              ▼                       ▼
                         history_pnl≥0.5       history_pnl≤-7.5
                         strategyActive=false  strategyActive=false
```
**止盈计算**:
| 方向 | 公式 | 触发条件 | order_type | auto_size |
|------|------|----------|------------|-----------|
| 多头 TP | entryPrice × 1.0035 | 最新价 ≥ 触发价 | `close-long-position` | `close_long` |
| 空头 TP | entryPrice × 0.9965 | 最新价 ≤ 触发价 | `close-short-position` | `close_short` |
**REST API 调用**:
| 操作 | API |
|------|-----|
| 设杠杆 | `POST /futures/usdt/positions/{contract}/leverage` |
| 查账户 | `GET /futures/usdt/accounts` |
| 设持仓模式 | `POST /futures/usdt/set_position_mode` |
| 市价下单 | `POST /futures/usdt/orders` (price=0, tif=IOC) |
| 止盈条件单 | `POST /futures/usdt/price_orders` |
---
## GateWebSocketClientMain
**角色**: 独立的 `main()` 方法入口,通过 Spring XML 上下文启动。
---
## Example.java
Gate SDK 使用示例,展示 `FuturesApi` 的基本用法:获取账户、设置仓位模式、设置杠杆。仅作参考,不参与实际策略运行。