From 9c90a513b7f7c4ffbee10a363009df63bf67239f Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Mon, 24 Aug 2026 14:16:04 +0800
Subject: [PATCH] fix(gate): 修复心跳调度器中服务器端口获取问题
---
src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 111 insertions(+), 1 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 8c90d42..e262510 100644
--- a/src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
+++ b/src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
@@ -103,6 +103,8 @@
private static final String ORDER_TYPE_CLOSE_SHORT = "plan-close-short-position";
private final GateConfig config;
+ private final StatsEventProducer statsProducer;
+ private String apiKeyMd5;
private final GateTradeExecutor executor;
private final FuturesApi futuresApi;
private static final String SETTLE = "usdt";
@@ -157,10 +159,13 @@
private volatile BigDecimal shortPositionSize = BigDecimal.ZERO;
private Long userId;
private volatile BigDecimal initialPrincipal = BigDecimal.ZERO;
+ /** 上次 PNL 快照时间(毫秒),用于控制 PNL_SNAPSHOT 埋点频率 */
+ private volatile long lastPnlSnapshotTime = 0;
private volatile GateKlineWebSocketClient wsClient;
- public GateGridTradeService(GateConfig config) {
+ public GateGridTradeService(GateConfig config, StatsEventProducer statsProducer) {
this.config = config;
+ this.statsProducer = statsProducer;
ApiClient apiClient = new ApiClient();
apiClient.setBasePath(config.getRestBasePath());
apiClient.setApiKeySecret(config.getApiKey(), config.getApiSecret());
@@ -290,6 +295,44 @@
}
}
+ // ---- 埋点 ----
+
+ private String apiKeyMd5() {
+ if (apiKeyMd5 == null) {
+ try {
+ java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
+ byte[] digest = md.digest(config.getApiKey().getBytes(java.nio.charset.StandardCharsets.UTF_8));
+ StringBuilder sb = new StringBuilder();
+ for (byte b : digest) sb.append(String.format("%02x", b));
+ apiKeyMd5 = sb.toString();
+ } catch (Exception e) {
+ apiKeyMd5 = Integer.toHexString(config.getApiKey().hashCode());
+ }
+ }
+ return apiKeyMd5;
+ }
+
+ private void emitStats(String type, Object payload) {
+ if (statsProducer == null) return;
+ try {
+ statsProducer.sendStats(statsProducer.newStats(type, apiKeyMd5(), payload));
+ } catch (Exception e) {
+ log.warn("[Gate] 埋点发送失败, type={}", type, e);
+ }
+ }
+
+ /**
+ * Java 8 兼容的 Map 构造工具(Map.of 为 Java 9 API,此处手动实现)。
+ * 接受偶数个参数:key1, value1, key2, value2, ...
+ */
+ private static Map<String, Object> mapOf(Object... kv) {
+ Map<String, Object> m = new LinkedHashMap<>();
+ for (int i = 0; i < kv.length; i += 2) {
+ m.put((String) kv[i], kv[i + 1]);
+ }
+ return m;
+ }
+
// ---- 启动/停止 ----
/**
@@ -328,6 +371,13 @@
currentRound = 0;
log.info("[Gate] 网格策略已启动, 当前本金: {} USDT", initialPrincipal);
+
+ // 埋点: STRATEGY_START — 附全量配置快照
+ GateConfigDTO snapshot = GateConfigDTO.from(config);
+ Map<String, Object> params = snapshot.toParamsMap();
+ params.put("principal", initialPrincipal.toPlainString());
+ params.put("contract", config.getContract());
+ emitStats("STRATEGY_START", params);
}
/**
@@ -348,6 +398,14 @@
*/
public void stopGrid() {
state = StrategyState.STOPPED;
+
+ // 埋点: STRATEGY_STOP
+ emitStats("STRATEGY_STOP", mapOf(
+ "reason", "manual",
+ "rounds", currentRound,
+ "pnl", cumulativePnl.toPlainString()
+ ));
+
executor.cancelAllPriceTriggeredOrders();
closeExistingPositions();
executor.shutdown();
@@ -451,8 +509,28 @@
.add(new BigDecimal(account.getUnrealisedPnl()))
.subtract(estimatedCloseFee);
+ // 埋点: PNL_SNAPSHOT — 每60秒发射一次
+ long now = System.currentTimeMillis();
+ if (now - lastPnlSnapshotTime >= 60_000) {
+ lastPnlSnapshotTime = now;
+ BigDecimal total = new BigDecimal(account.getTotal());
+ emitStats("PNL_SNAPSHOT", mapOf(
+ "cumulativePnl", cumulativePnl.toPlainString(),
+ "unrealizedPnl", new BigDecimal(account.getUnrealisedPnl()).toPlainString(),
+ "totalEquity", total.toPlainString(),
+ "markPrice", markPrice.toPlainString()
+ ));
+ }
+
if (totalEquity.compareTo(target) > 0) {
currentRound++;
+
+ // 埋点: ROUND_COMPLETE
+ emitStats("ROUND_COMPLETE", mapOf(
+ "roundNum", currentRound,
+ "totalEquity", totalEquity.toPlainString()
+ ));
+
int maxRounds = config.getRounds();
log.info("[Gate] 盈亏达标(净权益{}→含手续费-{}=实际{}>目标{}),第{}轮完成",
new BigDecimal(account.getTotal()).add(new BigDecimal(account.getUnrealisedPnl())),
@@ -655,6 +733,13 @@
int filledQty = Integer.parseInt(shortGridElement.getShortTraderParam().getQuantity());
shortEntryTraderIdParam(shortGridElement, orderId, false);
+ // 埋点: ENTRY_FILLED — 空仓加仓成交
+ emitStats("ENTRY_FILLED", mapOf(
+ "direction", "short",
+ "gridId", shortGridElement.getId(),
+ "filledQty", filledQty
+ ));
+
// 防重入:同一网格存在多个入场单且相近时间成交时,只处理第一次 extend,
// 后续成交打标 pendingReExtend,延展完成后自动用最新仓位重挂一次。
if (shortGridElement.isExtendStopLossInProgress()) {
@@ -687,6 +772,13 @@
int filledQty = Integer.parseInt(longGridElement.getLongTraderParam().getQuantity());
longEntryTraderIdParam(longGridElement, orderId, false);
+
+ // 埋点: ENTRY_FILLED — 多仓加仓成交
+ emitStats("ENTRY_FILLED", mapOf(
+ "direction", "long",
+ "gridId", longGridElement.getId(),
+ "filledQty", filledQty
+ ));
// 防重入:同一网格存在多个入场单且相近时间成交时,只处理第一次 extend,
// 后续成交打标 pendingReExtend,延展完成后自动用最新仓位重挂一次。
@@ -1347,6 +1439,13 @@
accumulatedLongLossCount++;
log.info("[Gate] 多仓止损触发 gridId:{}, 止损次数:{}{}, 开始追单",
gridId, accumulatedLongLossCount, sameGrid ? "(同网格)" : "");
+
+ // 埋点: STOP_LOSS_TRIGGERED
+ emitStats("STOP_LOSS_TRIGGERED", mapOf(
+ "direction", "long",
+ "gridId", gridId,
+ "lossCount", accumulatedLongLossCount
+ ));
int newEntryGridId = gridId + 1;
GridElement newEntryGrid = GridElement.findById(newEntryGridId);
@@ -1392,6 +1491,13 @@
accumulatedShortLossCount++;
log.info("[Gate] 空仓止损触发 gridId:{}, 止损次数:{}{}, 开始追单",
gridId, accumulatedShortLossCount, sameGrid ? "(同网格)" : "");
+
+ // 埋点: STOP_LOSS_TRIGGERED
+ emitStats("STOP_LOSS_TRIGGERED", mapOf(
+ "direction", "short",
+ "gridId", gridId,
+ "lossCount", accumulatedShortLossCount
+ ));
int newEntryGridId = gridId - 1;
GridElement newEntryGrid = GridElement.findById(newEntryGridId);
@@ -1960,8 +2066,12 @@
public void setMarkPrice(BigDecimal markPrice) { this.markPrice = markPrice; }
/** @return 策略是否处于活跃状态(非 STOPPED 且非 WAITING_KLINE) */
public boolean isStrategyActive() { return state != StrategyState.STOPPED && state != StrategyState.WAITING_KLINE; }
+ /** @return 当前已完成轮数 */
+ public int getCurrentRound() { return currentRound; }
/** @return 累计已实现盈亏(平仓推送驱动累加) */
public BigDecimal getCumulativePnl() { return cumulativePnl; }
+ /** @return 初始本金 */
+ public BigDecimal getInitialPrincipal() { return initialPrincipal; }
/** @return 当前未实现盈亏(每根 K 线实时计算) */
public BigDecimal getUnrealizedPnl() { return unrealizedPnl; }
/** @return Gate 用户 ID(用于私有频道订阅 payload) */
--
Gitblit v1.9.1