From bae908e42a8f96042684eaff203fba0aedcdee6c Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Mon, 01 Jun 2026 10:38:43 +0800
Subject: [PATCH] feat(gateApi): 添加基底开仓和预期收益配置及止损机制
---
src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java | 343 +++++++++++++++++++++++++++++-------------
src/main/java/com/xcong/excoin/modules/gateApi/GridElement.java | 71 ++++++++
src/main/java/com/xcong/excoin/modules/gateApi/GateWebSocketClientManager.java | 24 ++
src/main/java/com/xcong/excoin/modules/gateApi/GateConfig.java | 18 ++
4 files changed, 342 insertions(+), 114 deletions(-)
diff --git a/src/main/java/com/xcong/excoin/modules/gateApi/GateConfig.java b/src/main/java/com/xcong/excoin/modules/gateApi/GateConfig.java
index a583c2f..9b207cc 100644
--- a/src/main/java/com/xcong/excoin/modules/gateApi/GateConfig.java
+++ b/src/main/java/com/xcong/excoin/modules/gateApi/GateConfig.java
@@ -81,6 +81,10 @@
private final BigDecimal maxLoss;
/** 下单数量(合约张数) */
private final String quantity;
+ /** 基底开仓张数(初始化时多空各开的张数,如 "10") */
+ private final String baseQuantity;
+ /** 预期收益(USDT),unrealisedPnl + available > 初始本金 + 此值时重置 */
+ private final BigDecimal expectedProfit;
/** 是否为生产环境 */
private final boolean isProduction;
/** 补仓最大重试次数 */
@@ -115,6 +119,8 @@
this.overallTp = builder.overallTp;
this.maxLoss = builder.maxLoss;
this.quantity = builder.quantity;
+ this.baseQuantity = builder.baseQuantity;
+ this.expectedProfit = builder.expectedProfit;
this.isProduction = builder.isProduction;
this.reopenMaxRetries = builder.reopenMaxRetries;
this.gridQueueSize = builder.gridQueueSize;
@@ -183,6 +189,10 @@
public BigDecimal getMaxLoss() { return maxLoss; }
/** @return 每次下单的张数(如 "1" 表示 1 张合约) */
public String getQuantity() { return quantity; }
+ /** @return 基底开仓张数(初始化时多空各开的张数,如 "10") */
+ public String getBaseQuantity() { return baseQuantity; }
+ /** @return 预期收益(USDT),unrealisedPnl + available > 初始本金 + 此值时重置 */
+ public BigDecimal getExpectedProfit() { return expectedProfit; }
/** @return 网格价格队列的容量上限(超出时截断尾部) */
public int getGridQueueSize() { return gridQueueSize; }
@@ -271,6 +281,10 @@
private BigDecimal maxLoss = new BigDecimal("7.5");
/** 每次下单张数,默认 "1" */
private String quantity = "1";
+ /** 基底开仓张数,默认 "10"(初始化时多空各开10张) */
+ private String baseQuantity = "10";
+ /** 预期收益(USDT),默认 0.5 */
+ private BigDecimal expectedProfit = new BigDecimal("0.5");
/** 是否为生产环境,默认 false(测试网) */
private boolean isProduction = false;
/** 补仓最大重试次数,默认 3 */
@@ -306,6 +320,10 @@
public Builder maxLoss(BigDecimal maxLoss) { this.maxLoss = maxLoss; return this; }
/** 设置每次下单张数 */
public Builder quantity(String quantity) { this.quantity = quantity; return this; }
+ /** 设置基底开仓张数 */
+ public Builder baseQuantity(String baseQuantity) { this.baseQuantity = baseQuantity; return this; }
+ /** 设置预期收益(USDT) */
+ public Builder expectedProfit(BigDecimal expectedProfit) { this.expectedProfit = expectedProfit; return this; }
/** 设置环境(true=实盘生产网 / false=模拟盘测试网) */
public Builder isProduction(boolean isProduction) { this.isProduction = isProduction; return this; }
/** 设置补仓最大重试次数 */
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 b3a4ea7..a51fb12 100644
--- a/src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
+++ b/src/main/java/com/xcong/excoin/modules/gateApi/GateGridTradeService.java
@@ -359,14 +359,14 @@
//初始化0位置的开仓,并且用空的开仓价格,作为价格基准来划分网格
if (state == StrategyState.WAITING_KLINE) {
state = StrategyState.OPENING;
- log.info("[Gate] 首根K线到达,开基底仓位...");
- executor.openLong(config.getQuantity(), (orderId) -> {
+ log.info("[Gate] 首根K线到达,开基底仓位 多空各{}张...", config.getBaseQuantity());
+ executor.openLong(config.getBaseQuantity(), (orderId) -> {
TraderParam baseLongTp = TraderParam.builder()
.entryOrderId(orderId)
.build();
config.setBaseLongTraderParam(baseLongTp);
}, null);
- executor.openShort(negate(config.getQuantity()), (orderId) -> {
+ executor.openShort(negate(config.getBaseQuantity()), (orderId) -> {
TraderParam baseShortTp = TraderParam.builder()
.entryOrderId(orderId)
.build();
@@ -379,8 +379,7 @@
if (state != StrategyState.ACTIVE) {
return;
}
- processLongGrid(closePrice);
- processShortGrid(closePrice);
+ checkProfitAndReset();
}
// ---- 仓位推送回调 ----
@@ -432,6 +431,10 @@
checkLongEntryOrderToCancel();
}
} else {
+ if (longActive && state == StrategyState.ACTIVE) {
+ log.info("[Gate] 多仓持仓归零,重置策略");
+ handlePositionZeroAndReset("多仓");
+ }
longActive = false;
longPositionSize = BigDecimal.ZERO;
}
@@ -451,6 +454,10 @@
checkLongEntryOrderToCancel();
}
} else {
+ if (shortActive && state == StrategyState.ACTIVE) {
+ log.info("[Gate] 空仓持仓归零,重置策略");
+ handlePositionZeroAndReset("空仓");
+ }
shortActive = false;
shortPositionSize = BigDecimal.ZERO;
}
@@ -716,9 +723,17 @@
return;
}
- /**
- * 匹配止盈单止盈
- */
+ GridElement longStopLossElem = GridElement.findByLongStopLossOrderId(orderId);
+ if (longStopLossElem != null) {
+ handleLongStopLossTriggered(longStopLossElem);
+ return;
+ }
+ GridElement shortStopLossElem = GridElement.findByShortStopLossOrderId(orderId);
+ if (shortStopLossElem != null) {
+ handleShortStopLossTriggered(shortStopLossElem);
+ return;
+ }
+
GridElement byShortTakeProfitOrderId = GridElement.findByShortTakeProfitOrderId(orderId);
if (byShortTakeProfitOrderId != null){
shortTakeProfitTraderIdParam(
@@ -748,56 +763,22 @@
TPonUserTradeLongEntry(byLongTakeProfitOrderId);
}
- /**
- * 匹配挂单
- */
-
GridElement shortGridElement = GridElement.findByShortOrderId(orderId);
if (shortGridElement != null) {
if (shortGridElement.isHasShortOrder() && !tradeId.equals("0")){
-
- onUserTradeShortEntry(shortGridElement);
- if (shortGridElement.getShortTakeProfitOrderId() == null){
- BigDecimal shortTp = shortGridElement.getShortTraderParam().getTakeProfitPrice();
- if (shortTp != null) {
- executor.placeTakeProfit(shortTp,
- FuturesPriceTrigger.RuleEnum.NUMBER_2,
- ORDER_TYPE_CLOSE_SHORT,
- config.getQuantity(),
- (profitId) -> {
- shortTakeProfitTraderIdParam(
- shortGridElement,
- profitId,
- true
- );
- });
- log.info("[Gate] 空单成交匹配止盈, orderId:{}, 止盈价:{}, size:{}", orderId, shortTp, config.getQuantity());
- }
- }
+ int filledQty = Integer.parseInt(shortGridElement.getShortTraderParam().getQuantity());
+ shortEntryTraderIdParam(shortGridElement, null, false);
+ extendShortStopLoss(filledQty);
+ log.info("[Gate] 空单成交 gridId:{}, qty:{}, 追挂止损", shortGridElement.getId(), filledQty);
}
}
GridElement longGridElement = GridElement.findByLongOrderId(orderId);
if (longGridElement != null) {
if (longGridElement.isHasLongOrder() && !tradeId.equals("0")){
-
- onUserTradeLongEntry(longGridElement);
- if (longGridElement.getLongTakeProfitOrderId() == null){
- BigDecimal longTp = longGridElement.getLongTraderParam().getTakeProfitPrice();
- if (longTp != null) {
- executor.placeTakeProfit(longTp,
- FuturesPriceTrigger.RuleEnum.NUMBER_1,
- ORDER_TYPE_CLOSE_LONG,
- negate(config.getQuantity()),
- (profitId) -> {
- longTakeProfitTraderIdParam(
- longGridElement,
- profitId,
- true
- );
- });
- log.info("[Gate] 多单成交匹配止盈, orderId:{}, 止盈价:{}, size:{}", orderId, longTp, negate(config.getQuantity()));
- }
- }
+ int filledQty = Integer.parseInt(longGridElement.getLongTraderParam().getQuantity());
+ longEntryTraderIdParam(longGridElement, null, false);
+ extendLongStopLoss(filledQty);
+ log.info("[Gate] 多单成交 gridId:{}, qty:{}, 追挂止损", longGridElement.getId(), filledQty);
}
}
}
@@ -909,75 +890,59 @@
*/
private void tryGenerateQueues() {
if (baseLongOpened && baseShortOpened) {
- //初始化空仓队列
generateShortQueue();
- //初始化多仓队列
generateLongQueue();
- //初始化网格数据
updateGridElements();
- /**
- * 挂初始位置多空仓条件单
- * 0位置的多单止盈
- * 0位置的空单止盈
- */
GridElement baseGridElement = GridElement.findById(0);
TraderParam baseLongTraderParam = config.getBaseLongTraderParam();
baseGridElement.setLongOrderId(baseLongTraderParam.getEntryOrderId());
baseGridElement.setHasLongOrder(true);
- //0位置的网格的多单止盈
- BigDecimal upTakeProfitPrice = baseGridElement.getLongTraderParam().getTakeProfitPrice();
- executor.placeTakeProfit(
- upTakeProfitPrice,
- FuturesPriceTrigger.RuleEnum.NUMBER_1,
- ORDER_TYPE_CLOSE_LONG,
- negate(config.getQuantity()),
- profitId -> {
- longTakeProfitTraderIdParam(
- baseGridElement,
- profitId,
- true
- );
- }
- );
- //0位置的网格的空单止盈
TraderParam baseShortTraderParam = config.getBaseShortTraderParam();
baseGridElement.setShortOrderId(baseShortTraderParam.getEntryOrderId());
baseGridElement.setHasShortOrder(true);
- BigDecimal downTakeProfitPrice = baseGridElement.getShortTraderParam().getTakeProfitPrice();
- executor.placeTakeProfit(
- downTakeProfitPrice,
- FuturesPriceTrigger.RuleEnum.NUMBER_2,
- ORDER_TYPE_CLOSE_SHORT,
- config.getQuantity(),
- profitId -> {
- shortTakeProfitTraderIdParam(
- baseGridElement,
- profitId,
- true
- );
- }
- );
- /**
- * 挂初始位置的up位置的多单
- * 挂初始位置的down位置的空单
- */
- Integer upId = baseGridElement.getUpId();
- GridElement upGridElementOne = GridElement.findById(upId);
- BigDecimal longTp = upGridElementOne.getGridPrice();
- placeEntryOrderWithPreFlag(upGridElementOne, true,
- longTp,
- FuturesPriceTrigger.RuleEnum.NUMBER_1,
- config.getQuantity());
- Integer downId = baseGridElement.getDownId();
- GridElement downGridElementOne = GridElement.findById(downId);
- BigDecimal shortTp = downGridElementOne.getGridPrice();
- placeEntryOrderWithPreFlag(downGridElementOne, false,
- shortTp,
- FuturesPriceTrigger.RuleEnum.NUMBER_2,
- negate(config.getQuantity()));
+ for (int id = 2; id <= 11; id++) {
+ GridElement elem = GridElement.findById(id);
+ if (elem == null) {
+ continue;
+ }
+ BigDecimal triggerPrice = elem.getGridPrice();
+ int finalId = id;
+ executor.placeTakeProfit(
+ triggerPrice,
+ FuturesPriceTrigger.RuleEnum.NUMBER_1,
+ ORDER_TYPE_CLOSE_SHORT,
+ "1",
+ profitId -> {
+ elem.setShortStopLossOrderId(profitId);
+ GridElement.refreshIndices();
+ log.info("[Gate] 空仓止损已挂, gridId:{}, 触发价:{}, stopLossId:{}", finalId, triggerPrice, profitId);
+ }
+ );
+ }
+ for (int id = -2; id >= -11; id--) {
+ GridElement elem = GridElement.findById(id);
+ if (elem == null) {
+ continue;
+ }
+ BigDecimal triggerPrice = elem.getGridPrice();
+ int finalId = id;
+ executor.placeTakeProfit(
+ triggerPrice,
+ FuturesPriceTrigger.RuleEnum.NUMBER_2,
+ ORDER_TYPE_CLOSE_LONG,
+ "-1",
+ profitId -> {
+ elem.setLongStopLossOrderId(profitId);
+ GridElement.refreshIndices();
+ log.info("[Gate] 多仓止损已挂, gridId:{}, 触发价:{}, stopLossId:{}", finalId, triggerPrice, profitId);
+ }
+ );
+ }
+
+ log.info("[Gate] 止损单已全部挂完, 空仓止损: 2~11, 多仓止损: -2~-11");
state = StrategyState.ACTIVE;
}
}
@@ -1385,6 +1350,172 @@
}
}
+ private void handleLongStopLossTriggered(GridElement gridElement) {
+ int gridId = gridElement.getId();
+ int N = Math.abs(gridId);
+ gridElement.setLongStopLossOrderId(null);
+ log.info("[Gate] 多仓止损触发 gridId:{}, 开始追单", gridId);
+
+ int newEntryGridId = -(N - 1);
+ int entryQty = N - 1;
+
+ GridElement newEntryGrid = GridElement.findById(newEntryGridId);
+ if (newEntryGrid == null) {
+ log.warn("[Gate] 多仓止损触发 but gridId:{} 不存在", newEntryGridId);
+ GridElement.refreshIndices();
+ return;
+ }
+
+ if (N > 2) {
+ int cancelGridId = -(N - 2);
+ GridElement cancelGrid = GridElement.findById(cancelGridId);
+ if (cancelGrid != null && cancelGrid.isHasLongOrder()) {
+ executor.cancelConditionalOrder(cancelGrid.getLongOrderId(), oid -> {
+ longEntryTraderIdParam(cancelGrid, null, false);
+ log.info("[Gate] 多仓止损触发, 取消gridId:{}的多单", cancelGridId);
+ });
+ }
+ }
+
+ String size = String.valueOf(entryQty);
+ BigDecimal triggerPrice = newEntryGrid.getGridPrice();
+ log.info("[Gate] 多仓止损触发 gridId:{}, 在gridId:{}挂{}张多单", gridId, newEntryGridId, entryQty);
+ newEntryGrid.getLongTraderParam().setQuantity(size);
+ placeEntryOrderWithPreFlag(newEntryGrid, true, triggerPrice,
+ FuturesPriceTrigger.RuleEnum.NUMBER_2, size);
+ }
+
+ private void handleShortStopLossTriggered(GridElement gridElement) {
+ int gridId = gridElement.getId();
+ int N = gridId;
+ gridElement.setShortStopLossOrderId(null);
+ log.info("[Gate] 空仓止损触发 gridId:{}, 开始追单", gridId);
+
+ int newEntryGridId = N - 1;
+ int entryQty = N - 1;
+
+ GridElement newEntryGrid = GridElement.findById(newEntryGridId);
+ if (newEntryGrid == null) {
+ log.warn("[Gate] 空仓止损触发 but gridId:{} 不存在", newEntryGridId);
+ GridElement.refreshIndices();
+ return;
+ }
+
+ if (N > 2) {
+ int cancelGridId = N - 2;
+ GridElement cancelGrid = GridElement.findById(cancelGridId);
+ if (cancelGrid != null && cancelGrid.isHasShortOrder()) {
+ executor.cancelConditionalOrder(cancelGrid.getShortOrderId(), oid -> {
+ shortEntryTraderIdParam(cancelGrid, null, false);
+ log.info("[Gate] 空仓止损触发, 取消gridId:{}的空单", cancelGridId);
+ });
+ }
+ }
+
+ String size = String.valueOf(entryQty);
+ BigDecimal triggerPrice = newEntryGrid.getGridPrice();
+ log.info("[Gate] 空仓止损触发 gridId:{}, 在gridId:{}挂{}张空单", gridId, newEntryGridId, entryQty);
+ newEntryGrid.getShortTraderParam().setQuantity(size);
+ placeEntryOrderWithPreFlag(newEntryGrid, false, triggerPrice,
+ FuturesPriceTrigger.RuleEnum.NUMBER_1, negate(size));
+ }
+
+ private void extendLongStopLoss(int filledQty) {
+ int furthestSlId = 0;
+ for (GridElement e : config.getGridElements()) {
+ if (e.getLongStopLossOrderId() != null && e.getId() < furthestSlId) {
+ furthestSlId = e.getId();
+ }
+ }
+ if (furthestSlId == 0) {
+ furthestSlId = -11;
+ }
+ log.info("[Gate] 多仓追挂止损, 当前最远止损gridId:{}, 追加{}张", furthestSlId, filledQty);
+ for (int i = 0; i < filledQty; i++) {
+ int newSlId = furthestSlId - i - 1;
+ GridElement elem = GridElement.findById(newSlId);
+ if (elem == null) {
+ continue;
+ }
+ BigDecimal triggerPrice = elem.getGridPrice();
+ int finalSlId = newSlId;
+ executor.placeTakeProfit(
+ triggerPrice,
+ FuturesPriceTrigger.RuleEnum.NUMBER_2,
+ ORDER_TYPE_CLOSE_LONG,
+ "-1",
+ profitId -> {
+ elem.setLongStopLossOrderId(profitId);
+ GridElement.refreshIndices();
+ log.info("[Gate] 多仓止损追加, gridId:{}, 触发价:{}, stopLossId:{}", finalSlId, triggerPrice, profitId);
+ }
+ );
+ }
+ }
+
+ private void extendShortStopLoss(int filledQty) {
+ int furthestSlId = 0;
+ for (GridElement e : config.getGridElements()) {
+ if (e.getShortStopLossOrderId() != null && e.getId() > furthestSlId) {
+ furthestSlId = e.getId();
+ }
+ }
+ if (furthestSlId == 0) {
+ furthestSlId = 11;
+ }
+ log.info("[Gate] 空仓追挂止损, 当前最远止损gridId:{}, 追加{}张", furthestSlId, filledQty);
+ for (int i = 0; i < filledQty; i++) {
+ int newSlId = furthestSlId + i + 1;
+ GridElement elem = GridElement.findById(newSlId);
+ if (elem == null) {
+ continue;
+ }
+ BigDecimal triggerPrice = elem.getGridPrice();
+ int finalSlId = newSlId;
+ executor.placeTakeProfit(
+ triggerPrice,
+ FuturesPriceTrigger.RuleEnum.NUMBER_1,
+ ORDER_TYPE_CLOSE_SHORT,
+ "1",
+ profitId -> {
+ elem.setShortStopLossOrderId(profitId);
+ GridElement.refreshIndices();
+ log.info("[Gate] 空仓止损追加, gridId:{}, 触发价:{}, stopLossId:{}", finalSlId, triggerPrice, profitId);
+ }
+ );
+ }
+ }
+
+ private void checkProfitAndReset() {
+ try {
+ FuturesAccount account = futuresApi.listFuturesAccounts(SETTLE);
+ BigDecimal unrealisedPnl = new BigDecimal(account.getCrossUnrealisedPnl());
+ BigDecimal available = new BigDecimal(account.getCrossAvailable());
+ BigDecimal totalEquity = unrealisedPnl.add(available);
+ BigDecimal target = initialPrincipal.add(config.getExpectedProfit());
+ log.info("[Gate] 盈亏检查 cross_unrealised_pnl:{}, cross_available:{}, 合计:{}, 目标:{}",
+ unrealisedPnl, available, totalEquity, target);
+ if (totalEquity.compareTo(target) > 0) {
+ log.info("[Gate] 盈亏达标({}>{}),重置策略", totalEquity, target);
+ closeExistingPositions();
+ futuresApi.cancelPriceTriggeredOrderList(SETTLE, config.getContract());
+ startGrid();
+ }
+ } catch (Exception e) {
+ log.warn("[Gate] 盈亏检查失败", e);
+ }
+ }
+
+ private void handlePositionZeroAndReset(String direction) {
+ try {
+ futuresApi.cancelPriceTriggeredOrderList(SETTLE, config.getContract());
+ } catch (Exception e) {
+ log.warn("[Gate] {}持仓归零后取消条件单失败", direction, e);
+ }
+ closeExistingPositions();
+ startGrid();
+ }
+
// ---- 保证金安全阀 ----
/**
diff --git a/src/main/java/com/xcong/excoin/modules/gateApi/GateWebSocketClientManager.java b/src/main/java/com/xcong/excoin/modules/gateApi/GateWebSocketClientManager.java
index ef58b29..4059f1e 100644
--- a/src/main/java/com/xcong/excoin/modules/gateApi/GateWebSocketClientManager.java
+++ b/src/main/java/com/xcong/excoin/modules/gateApi/GateWebSocketClientManager.java
@@ -76,10 +76,28 @@
// .isProduction(false)
// .reopenMaxRetries(3)
// .build();
+// //实盘
+// config = GateConfig.builder()
+// .apiKey("a2338398e00b7935104520e16be96918")
+// .apiSecret("9111d897f2346d5217619f2da76536632715fef4d7eb304c6c61e869a2a74e98")
+// .contract("ETH_USDT")
+// .leverage("100")
+// .marginMode("CROSS")
+// .positionMode("dual")
+// .gridRate(new BigDecimal("0.005"))
+// .overallTp(new BigDecimal("100"))
+// .maxLoss(new BigDecimal("15"))
+// .quantity("5")
+// .priceScale(2)
+// .contractMultiplier(new BigDecimal("0.01"))
+// .unrealizedPnlPriceMode(GateConfig.PnLPriceMode.LAST_PRICE)
+// .isProduction(true)
+// .reopenMaxRetries(3)
+// .build();
//实盘
config = GateConfig.builder()
- .apiKey("a2338398e00b7935104520e16be96918")
- .apiSecret("9111d897f2346d5217619f2da76536632715fef4d7eb304c6c61e869a2a74e98")
+ .apiKey("d90ca272391992b8e74f8f92cedb21ec")
+ .apiSecret("1861e4f52de4bb53369ea3208d9ede38ece4777368030f96c77d27934c46c274")
.contract("ETH_USDT")
.leverage("100")
.marginMode("CROSS")
@@ -91,7 +109,7 @@
.priceScale(2)
.contractMultiplier(new BigDecimal("0.01"))
.unrealizedPnlPriceMode(GateConfig.PnLPriceMode.LAST_PRICE)
- .isProduction(true)
+ .isProduction(false)
.reopenMaxRetries(3)
.build();
diff --git a/src/main/java/com/xcong/excoin/modules/gateApi/GridElement.java b/src/main/java/com/xcong/excoin/modules/gateApi/GridElement.java
index 010042e..8601f69 100644
--- a/src/main/java/com/xcong/excoin/modules/gateApi/GridElement.java
+++ b/src/main/java/com/xcong/excoin/modules/gateApi/GridElement.java
@@ -87,6 +87,10 @@
private String longTakeProfitOrderId;
/** 空仓止盈订单 ID */
private String shortTakeProfitOrderId;
+ /** 多仓止损订单 ID */
+ private String longStopLossOrderId;
+ /** 空仓止损订单 ID */
+ private String shortStopLossOrderId;
/** 全局 ID 索引,由 {@link GateConfig#setGridElements(List)} 触发重建,O(1) 查找 */
private static final Map<Integer, GridElement> INDEX = new ConcurrentHashMap<>();
@@ -100,6 +104,10 @@
private static final Map<String, GridElement> LONG_TP_ORDER_ID_INDEX = new ConcurrentHashMap<>();
/** 全局空仓止盈订单 ID 索引,由 {@link GateConfig#setGridElements(List)} 触发重建,O(1) 查找 */
private static final Map<String, GridElement> SHORT_TP_ORDER_ID_INDEX = new ConcurrentHashMap<>();
+ /** 全局多仓止损订单 ID 索引 */
+ private static final Map<String, GridElement> LONG_SL_ORDER_ID_INDEX = new ConcurrentHashMap<>();
+ /** 全局空仓止损订单 ID 索引 */
+ private static final Map<String, GridElement> SHORT_SL_ORDER_ID_INDEX = new ConcurrentHashMap<>();
/**
* 根据 ID 快速查找网格元素(O(1))。
@@ -162,6 +170,20 @@
}
/**
+ * 根据多仓止损订单 ID 快速查找网格元素(O(1))。
+ */
+ public static GridElement findByLongStopLossOrderId(String orderId) {
+ return LONG_SL_ORDER_ID_INDEX.get(orderId);
+ }
+
+ /**
+ * 根据空仓止损订单 ID 快速查找网格元素(O(1))。
+ */
+ public static GridElement findByShortStopLossOrderId(String orderId) {
+ return SHORT_SL_ORDER_ID_INDEX.get(orderId);
+ }
+
+ /**
* 从列表中重建全局 ID 索引和价格索引。
* 由 {@link GateConfig#setGridElements(List)} 在每次列表变更后调用。
*/
@@ -172,6 +194,8 @@
SHORT_ORDER_ID_INDEX.clear();
LONG_TP_ORDER_ID_INDEX.clear();
SHORT_TP_ORDER_ID_INDEX.clear();
+ LONG_SL_ORDER_ID_INDEX.clear();
+ SHORT_SL_ORDER_ID_INDEX.clear();
for (GridElement e : elements) {
INDEX.put(e.getId(), e);
putDynamicIndices(e);
@@ -191,6 +215,8 @@
SHORT_ORDER_ID_INDEX.clear();
LONG_TP_ORDER_ID_INDEX.clear();
SHORT_TP_ORDER_ID_INDEX.clear();
+ LONG_SL_ORDER_ID_INDEX.clear();
+ SHORT_SL_ORDER_ID_INDEX.clear();
for (GridElement e : INDEX.values()) {
putDynamicIndices(e);
}
@@ -205,9 +231,10 @@
sorted.sort((a, b) -> Integer.compare(a.getId(), b.getId()));
StringBuilder sb = new StringBuilder("\n========== 网格数据 ==========\n");
for (GridElement e : sorted) {
- if (e.isHasLongOrder() || e.isHasShortOrder()){
+ if (e.isHasLongOrder() || e.isHasShortOrder()
+ || e.getLongStopLossOrderId() != null || e.getShortStopLossOrderId() != null){
sb.append(String.format(
- " ID=%4d 价格=%s up=%s down=%s 多仓=%s(%s) 空仓=%s(%s) 多止盈=%s 空止盈=%s\n",
+ " ID=%4d 价格=%s up=%s down=%s 多仓=%s(%s) 空仓=%s(%s) 多止盈=%s 空止盈=%s 多止损=%s 空止损=%s\n",
e.getId(),
e.getGridPrice(),
e.getUpId(),
@@ -217,19 +244,23 @@
e.isHasShortOrder() ? "有" : "无",
e.getShortOrderId() != null ? e.getShortOrderId() : "-",
e.getLongTakeProfitOrderId() != null ? e.getLongTakeProfitOrderId() : "-",
- e.getShortTakeProfitOrderId() != null ? e.getShortTakeProfitOrderId() : "-"
+ e.getShortTakeProfitOrderId() != null ? e.getShortTakeProfitOrderId() : "-",
+ e.getLongStopLossOrderId() != null ? e.getLongStopLossOrderId() : "-",
+ e.getShortStopLossOrderId() != null ? e.getShortStopLossOrderId() : "-"
));
}
}
sb.append(String.format(
"------------------------------------------------------------\n" +
- " 索引统计: ID=%d 价格=%d 多仓订单ID=%d 空仓订单ID=%d 多止盈ID=%d 空止盈ID=%d\n",
+ " 索引统计: ID=%d 价格=%d 多仓订单ID=%d 空仓订单ID=%d 多止盈ID=%d 空止盈ID=%d 多止损ID=%d 空止损ID=%d\n",
INDEX.size(),
PRICE_INDEX.size(),
LONG_ORDER_ID_INDEX.size(),
SHORT_ORDER_ID_INDEX.size(),
LONG_TP_ORDER_ID_INDEX.size(),
- SHORT_TP_ORDER_ID_INDEX.size()
+ SHORT_TP_ORDER_ID_INDEX.size(),
+ LONG_SL_ORDER_ID_INDEX.size(),
+ SHORT_SL_ORDER_ID_INDEX.size()
));
sb.append(String.format(" 多仓订单ID索引: %s\n", LONG_ORDER_ID_INDEX.keySet()));
sb.append(String.format(" 空仓订单ID索引: %s\n", SHORT_ORDER_ID_INDEX.keySet()));
@@ -285,6 +316,12 @@
if (e.getShortTakeProfitOrderId() != null) {
SHORT_TP_ORDER_ID_INDEX.put(e.getShortTakeProfitOrderId(), e);
}
+ if (e.getLongStopLossOrderId() != null) {
+ LONG_SL_ORDER_ID_INDEX.put(e.getLongStopLossOrderId(), e);
+ }
+ if (e.getShortStopLossOrderId() != null) {
+ SHORT_SL_ORDER_ID_INDEX.put(e.getShortStopLossOrderId(), e);
+ }
}
/**
@@ -314,6 +351,8 @@
this.shortOrderId = builder.shortOrderId;
this.longTakeProfitOrderId = builder.longTakeProfitOrderId;
this.shortTakeProfitOrderId = builder.shortTakeProfitOrderId;
+ this.longStopLossOrderId = builder.longStopLossOrderId;
+ this.shortStopLossOrderId = builder.shortStopLossOrderId;
}
// ==================== 网格层级编号 ====================
@@ -400,6 +439,20 @@
/** 设置空仓止盈订单 ID */
public void setShortTakeProfitOrderId(String shortTakeProfitOrderId) { this.shortTakeProfitOrderId = shortTakeProfitOrderId; }
+ // ==================== 多仓止损订单 ID ====================
+
+ /** @return 多仓止损订单 ID */
+ public String getLongStopLossOrderId() { return longStopLossOrderId; }
+ /** 设置多仓止损订单 ID */
+ public void setLongStopLossOrderId(String longStopLossOrderId) { this.longStopLossOrderId = longStopLossOrderId; }
+
+ // ==================== 空仓止损订单 ID ====================
+
+ /** @return 空仓止损订单 ID */
+ public String getShortStopLossOrderId() { return shortStopLossOrderId; }
+ /** 设置空仓止损订单 ID */
+ public void setShortStopLossOrderId(String shortStopLossOrderId) { this.shortStopLossOrderId = shortStopLossOrderId; }
+
public static Builder builder() {
return new Builder();
}
@@ -438,6 +491,10 @@
private String longTakeProfitOrderId;
/** 空仓止盈订单 ID */
private String shortTakeProfitOrderId;
+ /** 多仓止损订单 ID */
+ private String longStopLossOrderId;
+ /** 空仓止损订单 ID */
+ private String shortStopLossOrderId;
/** 设置网格层级编号 */
public Builder id(int id) { this.id = id; return this; }
@@ -463,6 +520,10 @@
public Builder longTakeProfitOrderId(String longTakeProfitOrderId) { this.longTakeProfitOrderId = longTakeProfitOrderId; return this; }
/** 设置空仓止盈订单 ID */
public Builder shortTakeProfitOrderId(String shortTakeProfitOrderId) { this.shortTakeProfitOrderId = shortTakeProfitOrderId; return this; }
+ /** 设置多仓止损订单 ID */
+ public Builder longStopLossOrderId(String longStopLossOrderId) { this.longStopLossOrderId = longStopLossOrderId; return this; }
+ /** 设置空仓止损订单 ID */
+ public Builder shortStopLossOrderId(String shortStopLossOrderId) { this.shortStopLossOrderId = shortStopLossOrderId; return this; }
public GridElement build() {
return new GridElement(this);
--
Gitblit v1.9.1