From c7cb31dcafe3046f925f17e3d05604b35938199e Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Fri, 08 May 2026 12:57:05 +0800
Subject: [PATCH] refactor(gateApi): 重构 WebSocket 客户端架构并优化网格交易逻辑
---
src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java | 106 +++++++++++++++++++++++++----------------------------
1 files changed, 50 insertions(+), 56 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
index f06e972..311d8ff 100644
--- a/src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
+++ b/src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
@@ -21,14 +21,15 @@
* Gate 网格交易服务类。
*
* <h3>策略概述</h3>
- * 多空双开 → 各放止盈条件单 → 仓位推送驱动补仓 → history_pnl 判断停止
+ * 多空双开 → 各放止盈条件单 → 仓位推送检测平仓补仓 → 平仓推送累加 pnl → 判断停止
*
* <h3>触发逻辑</h3>
* <pre>
- * K线首次价格就绪 → dualOpenPositions() // 多空双开 + 止盈条件单
- * 仓位推送 size=0 → reopenXxxPosition() // 该方向被止盈平掉 → 补开
- * 仓位推送 history_pnl ≥ overallTp → 停止
- * 仓位推送 history_pnl ≤ -maxLoss → 停止
+ * K线首次价格就绪 → dualOpenPositions() // 多空双开 + 止盈条件单
+ * 仓位推送 size=0 → reopenXxxPosition() // 该方向被平仓 → 只补该方向
+ * 平仓推送 pnl → cumulativePnl += pnl // 累计盈亏
+ * cumulativePnl ≥ overallTp → 停止
+ * cumulativePnl ≤ -maxLoss → 停止
* </pre>
*
* <h3>止盈计算</h3>
@@ -37,7 +38,7 @@
*
* <h3>依赖</h3>
* 使用 {@code io.gate:gate-api (7.2.71)} SDK 通过 REST API 下单,
- * 市场数据由 {@link GateKlineWebSocketClient} 通过 WebSocket 提供。
+ * 市场数据由 WebSocket Handler 类提供。
*
* @author Administrator
*/
@@ -72,8 +73,8 @@
private BigDecimal shortEntryPrice;
/** WebSocket 推送的最新 K 线收盘价 */
private volatile BigDecimal lastKlinePrice;
- /** 服务器返回的累计已实现盈亏 */
- private BigDecimal totalHistoryPnl = BigDecimal.ZERO;
+ /** 平仓推送累计的盈亏 */
+ private volatile BigDecimal cumulativePnl = BigDecimal.ZERO;
/** 用户 ID,用于 WebSocket 私有频道订阅 */
private Long userId;
@@ -93,7 +94,7 @@
String contract, String leverage,
String marginMode, String positionMode,
BigDecimal gridRate, BigDecimal overallTp,
- int maxCycles, BigDecimal maxLoss,
+ BigDecimal maxLoss,
String quantity) {
this.contract = contract;
this.leverage = leverage;
@@ -111,7 +112,7 @@
}
/**
- * 初始化账户。设置杠杆、查询余额、切换持仓模式。
+ * 初始化账户。顺序:切持仓模式 → 清旧条件单 → 设杠杆 → 查余额。
*/
public void init() {
try {
@@ -120,6 +121,13 @@
this.userId = accountDetail.getUserId();
log.info("[GateGrid] 用户ID: {}", userId);
+ FuturesAccount account = futuresApi.listFuturesAccounts(SETTLE);
+ String positionModeSet = account.getPositionMode();
+ if (!positionMode.equals(positionModeSet)) {
+ futuresApi.setPositionMode(SETTLE, positionMode);
+ }
+ log.info("[GateGrid] 已设置持仓模式: {}", positionMode);
+
futuresApi.cancelPriceTriggeredOrderList(SETTLE, contract);
log.info("[GateGrid] 已取消所有既有止盈止损条件单");
@@ -127,14 +135,8 @@
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) {
@@ -151,7 +153,7 @@
return;
}
strategyActive = true;
- totalHistoryPnl = BigDecimal.ZERO;
+ cumulativePnl = BigDecimal.ZERO;
log.info("[GateGrid] 网格策略启动,等待K线价格...");
}
@@ -160,7 +162,7 @@
*/
public void stopGrid() {
strategyActive = false;
- log.info("[GateGrid] 网格策略已停止, history_pnl: {}", totalHistoryPnl);
+ log.info("[GateGrid] 网格策略已停止, cumulativePnl: {}", cumulativePnl);
}
/**
@@ -187,18 +189,15 @@
* <li>size=0 且之前活跃 → 该方向被平仓 → 补开</li>
* <li>size>0 → 确认仓位活跃,更新入场价</li>
* </ul>
- * 每次推送更新 history_pnl 并检查停止条件。
+ * 注意:累计盈亏由 onPositionClose 独立计算。
*
* @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,
- BigDecimal realisedPnl) {
+ BigDecimal entryPrice) {
if (!strategyActive) {
return;
}
@@ -224,8 +223,22 @@
shortEntryPrice = entryPrice;
}
}
+ }
- totalHistoryPnl = historyPnl;
+ /**
+ * 平仓推送回调入口。由 {@link GateKlineWebSocketClient} 调用。
+ * 累加平仓盈亏,每次平仓推送后检查停止条件。
+ *
+ * @param contract 合约名
+ * @param side 平仓方向(long / short)
+ * @param pnl 该次平仓的盈亏
+ */
+ public void onPositionClose(String contract, String side, BigDecimal pnl) {
+ if (!strategyActive) {
+ return;
+ }
+ cumulativePnl = cumulativePnl.add(pnl);
+ log.info("[GateGrid] 平仓盈亏累计, side:{}, pnl:{}, cumulativePnl:{}", side, pnl, cumulativePnl);
checkStopConditions();
}
@@ -237,13 +250,13 @@
* </ul>
*/
private void checkStopConditions() {
- if (totalHistoryPnl.compareTo(overallTp) >= 0) {
- log.info("[GateGrid] 累计止盈 {} 达到 {} USDT,停止策略", totalHistoryPnl, overallTp);
+ if (cumulativePnl.compareTo(overallTp) >= 0) {
+ log.info("[GateGrid] 累计止盈 {} 达到 {} USDT,停止策略", cumulativePnl, overallTp);
strategyActive = false;
return;
}
- if (totalHistoryPnl.compareTo(maxLoss.negate()) <= 0) {
- log.info("[GateGrid] 累计亏损 {} 达到上限 {} USDT,停止策略", totalHistoryPnl, maxLoss);
+ if (cumulativePnl.compareTo(maxLoss.negate()) <= 0) {
+ log.info("[GateGrid] 累计亏损 {} 达到上限 {} USDT,停止策略", cumulativePnl, maxLoss);
strategyActive = false;
}
}
@@ -381,36 +394,17 @@
FuturesPriceTrigger.RuleEnum rule,
String orderType,
String autoSize) {
+ FuturesPriceTriggeredOrder order = buildTriggeredOrder(triggerPrice, rule, orderType, 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:{}, orderType:{}, autoSize:{}, id:{}",
triggerPrice, orderType, autoSize, response.getId());
} catch (GateApiException e) {
if ("AUTO_USER_EXIST_POSITION_ORDER".equals(e.getErrorLabel())) {
- log.warn("[GateGrid] 止盈条件单已存在,取消旧单后重试, label:{}", e.getErrorLabel());
+ log.warn("[GateGrid] 止盈条件单已存在,取消旧单后重试");
try {
futuresApi.cancelPriceTriggeredOrderList(SETTLE, contract);
- TriggerOrderResponse response = futuresApi.createPriceTriggeredOrder(SETTLE, createPriceTriggeredOrderBean(triggerPrice, rule, orderType, autoSize));
+ TriggerOrderResponse response = futuresApi.createPriceTriggeredOrder(SETTLE, order);
log.info("[GateGrid] 止盈条件单重试成功, triggerPrice:{}, orderType:{}, autoSize:{}, id:{}",
triggerPrice, orderType, autoSize, response.getId());
} catch (Exception retryEx) {
@@ -427,10 +421,10 @@
}
}
- private FuturesPriceTriggeredOrder createPriceTriggeredOrderBean(BigDecimal triggerPrice,
- FuturesPriceTrigger.RuleEnum rule,
- String orderType,
- String autoSize) {
+ private FuturesPriceTriggeredOrder buildTriggeredOrder(BigDecimal triggerPrice,
+ FuturesPriceTrigger.RuleEnum rule,
+ String orderType,
+ String autoSize) {
FuturesPriceTrigger trigger = new FuturesPriceTrigger();
trigger.setStrategyType(FuturesPriceTrigger.StrategyTypeEnum.NUMBER_0);
trigger.setPriceType(FuturesPriceTrigger.PriceTypeEnum.NUMBER_0);
@@ -498,8 +492,8 @@
return strategyActive;
}
- public BigDecimal getTotalHistoryPnl() {
- return totalHistoryPnl;
+ public BigDecimal getCumulativePnl() {
+ return cumulativePnl;
}
public Long getUserId() {
--
Gitblit v1.9.1