From ed57e750b5e2cf14fe5d447ff318228f8df77d23 Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Fri, 05 Jun 2026 16:48:51 +0800
Subject: [PATCH] refactor(okx): 移除算法单频道处理器并优化网格交易重置逻辑
---
src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxGridTradeService.java | 455 ++++++++++++--------------------------------------------
1 files changed, 101 insertions(+), 354 deletions(-)
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxGridTradeService.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxGridTradeService.java
index aa9f09b..84a5e49 100644
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxGridTradeService.java
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxGridTradeService.java
@@ -57,6 +57,7 @@
private final OkxConfig config;
private final OkxTradeExecutor executor;
private final OKXAccount okxAccount;
+ private final StopLossManager stopLossManager;
private volatile StrategyState state = StrategyState.WAITING_KLINE;
@@ -79,6 +80,16 @@
/** 基底空头是否已开 */
private volatile boolean baseShortOpened = false;
+ // ---- WS 订阅就绪标志 ----
+ /** candle1m (Business WS) 订阅已确认 */
+ private volatile boolean candle1mSubscribed = false;
+ /** positions (Private WS) 订阅已确认 */
+ private volatile boolean positionsSubscribed = false;
+ /** orders (Private WS) 订阅已确认 */
+ private volatile boolean ordersSubscribed = false;
+ /** 等待所有订阅就绪期间缓存的最新 K 线价格 */
+ private volatile BigDecimal pendingKlinePrice = null;
+
private volatile boolean shortActive = false;
private volatile boolean longActive = false;
@@ -95,6 +106,7 @@
this.config = config;
this.okxAccount = okxAccount;
this.executor = new OkxTradeExecutor(okxAccount, config.getInstId(), config.getTdMode());
+ this.stopLossManager = new StopLossManager(config, executor);
}
// ---- 初始化 ----
@@ -195,6 +207,10 @@
baseShortOpened = false;
longActive = false;
shortActive = false;
+ candle1mSubscribed = false;
+ positionsSubscribed = false;
+ ordersSubscribed = false;
+ pendingKlinePrice = null;
shortPriceQueue.clear();
longPriceQueue.clear();
currentLongOrderIds.clear();
@@ -253,27 +269,23 @@
updateUnrealizedPnl();
if (state == StrategyState.STOPPED) {
- executor.cancelAllAlgoOrders();
- closeExistingPositions();
+ // stopGrid() 已做清理,仅打印日志不重复操作
BigDecimal totalPnl = cumulativePnl.add(unrealizedPnl);
log.info("[OKX] 已实现:{}, 未实现:{}, 合计:{}", cumulativePnl, unrealizedPnl, totalPnl);
- startGrid();
return;
}
if (state == StrategyState.WAITING_KLINE) {
+ // 等待所有 WS 订阅就绪后再开仓
+ if (!allSubscriptionsReady()) {
+ pendingKlinePrice = closePrice;
+ log.info("[OKX] 等待所有 WS 订阅就绪(candle1m={}, positions={}, orders={}), 当前价: {}",
+ candle1mSubscribed, positionsSubscribed, ordersSubscribed, closePrice);
+ return;
+ }
state = StrategyState.OPENING;
- log.info("[OKX] 首根K线到达,开基底仓位 多空各{}张...", config.getBaseQuantity());
- executor.openLong(config.getBaseQuantity(), (orderId) -> {
- OkxTraderParam baseLongTp = OkxTraderParam.builder().entryOrderId(orderId).build();
- config.setBaseLongTraderParam(baseLongTp);
- tryGenerateQueues(); // 异步回调到达后重试,防止 WS 推送先到导致 NPE
- }, null);
- executor.openShort(config.getBaseQuantity(), (orderId) -> {
- OkxTraderParam baseShortTp = OkxTraderParam.builder().entryOrderId(orderId).build();
- config.setBaseShortTraderParam(baseShortTp);
- tryGenerateQueues(); // 异步回调到达后重试,防止 WS 推送先到导致 NPE
- }, null);
+ log.info("[OKX] 首根K线到达,所有订阅就绪,开基底仓位 多空各{}张...", config.getBaseQuantity());
+ openBasePositions();
return;
}
@@ -281,6 +293,47 @@
return;
}
checkProfitAndReset();
+ }
+
+ /**
+ * WS 订阅成功确认回调,由各 {@link OkxGridChannelHandler#onSubscribed()} 触发。
+ * 当所有订阅就绪且已有缓存的 K 线价格时,自动触发开仓。
+ */
+ public void onSubscriptionConfirmed(String channel) {
+ if ("candle1m".equals(channel)) candle1mSubscribed = true;
+ else if ("positions".equals(channel)) positionsSubscribed = true;
+ else if ("orders".equals(channel)) ordersSubscribed = true;
+
+ log.info("[OKX] 订阅就绪: {}, 全部就绪: {}", channel, allSubscriptionsReady());
+
+ // 所有订阅就绪 + 有缓存 K 线价格 + 仍处于等待状态 → 触发开仓
+ if (allSubscriptionsReady() && pendingKlinePrice != null && state == StrategyState.WAITING_KLINE) {
+ BigDecimal price = pendingKlinePrice;
+ pendingKlinePrice = null;
+ log.info("[OKX] 所有 WS 订阅就绪,触发开仓, 价格: {}", price);
+ state = StrategyState.OPENING;
+ log.info("[OKX] 首根K线到达,所有订阅就绪,开基底仓位 多空各{}张...", config.getBaseQuantity());
+ openBasePositions();
+ }
+ }
+
+ /** @return true 表示 candle1m + positions + orders 三个频道均已订阅成功 */
+ private boolean allSubscriptionsReady() {
+ return candle1mSubscribed && positionsSubscribed && ordersSubscribed;
+ }
+
+ /** 市价双开基底仓位(多 + 空),对齐 Gate 版本逻辑 */
+ private void openBasePositions() {
+ executor.openLong(config.getBaseQuantity(), (orderId) -> {
+ OkxTraderParam baseLongTp = OkxTraderParam.builder().entryOrderId(orderId).build();
+ config.setBaseLongTraderParam(baseLongTp);
+ tryGenerateQueues();
+ }, null);
+ executor.openShort(config.getBaseQuantity(), (orderId) -> {
+ OkxTraderParam baseShortTp = OkxTraderParam.builder().entryOrderId(orderId).build();
+ config.setBaseShortTraderParam(baseShortTp);
+ tryGenerateQueues();
+ }, null);
}
// ---- 仓位推送回调 ----
@@ -308,7 +361,6 @@
}
} else {
if (longActive && state == StrategyState.ACTIVE) {
- log.info("[OKX] 多仓持仓归零,重置策略");
handlePositionZeroAndReset("多仓");
}
longActive = false;
@@ -330,7 +382,6 @@
}
} else {
if (shortActive && state == StrategyState.ACTIVE) {
- log.info("[OKX] 空仓持仓归零,重置策略");
handlePositionZeroAndReset("空仓");
}
shortActive = false;
@@ -364,32 +415,32 @@
return;
}
- // 匹配止损单
+ // 匹配止损单 → 委托 StopLossManager
OkxGridElement byLongStopLoss = OkxGridElement.findByLongStopLossOrderId(algoId);
if (byLongStopLoss != null) {
- handleLongStopLossTriggered(byLongStopLoss);
+ stopLossManager.handleLongStopLossTriggered(byLongStopLoss, longEntryPrice);
return;
}
OkxGridElement byShortStopLoss = OkxGridElement.findByShortStopLossOrderId(algoId);
if (byShortStopLoss != null) {
- handleShortStopLossTriggered(byShortStopLoss);
+ stopLossManager.handleShortStopLossTriggered(byShortStopLoss, shortEntryPrice);
return;
}
- // 匹配挂单 —— 条件单成交后:清空挂单状态 + 追挂止损 + 挂止盈单
+ // 匹配挂单 —— 条件单成交后:清空挂单状态 + 追挂止损
OkxGridElement shortGridElement = OkxGridElement.findByShortOrderId(algoId);
if (shortGridElement != null && shortGridElement.isHasShortOrder()) {
int filledQty = Integer.parseInt(shortGridElement.getShortTraderParam().getQuantity());
- shortEntryTraderIdParam(shortGridElement, null, false);
- extendShortStopLoss(filledQty);
+ stopLossManager.clearShortEntryState(shortGridElement);
+ stopLossManager.extendShortStopLoss(filledQty);
log.info("[OKX] 空单成交 gridId:{}, qty:{}, 追挂止损", shortGridElement.getId(), filledQty);
return;
}
OkxGridElement longGridElement = OkxGridElement.findByLongOrderId(algoId);
if (longGridElement != null && longGridElement.isHasLongOrder()) {
int filledQty = Integer.parseInt(longGridElement.getLongTraderParam().getQuantity());
- longEntryTraderIdParam(longGridElement, null, false);
- extendLongStopLoss(filledQty);
+ stopLossManager.clearLongEntryState(longGridElement);
+ stopLossManager.extendLongStopLoss(filledQty);
log.info("[OKX] 多单成交 gridId:{}, qty:{}, 追挂止损", longGridElement.getId(), filledQty);
return;
}
@@ -412,9 +463,18 @@
return;
}
- generateShortQueue();
- generateLongQueue();
- updateGridElements();
+ // 委托 GridQueueBuilder 构建价格队列 + GridElements
+ List<BigDecimal> tmpShort = GridQueueBuilder.buildShortQueue(config, shortBaseEntryPrice);
+ List<BigDecimal> tmpLong = GridQueueBuilder.buildLongQueue(config, shortBaseEntryPrice);
+ synchronized (shortPriceQueue) {
+ shortPriceQueue.clear();
+ shortPriceQueue.addAll(tmpShort);
+ }
+ synchronized (longPriceQueue) {
+ longPriceQueue.clear();
+ longPriceQueue.addAll(tmpLong);
+ }
+ GridQueueBuilder.buildGridElements(config, shortPriceQueue, longPriceQueue, shortBaseEntryPrice);
// 标记基座挂单
OkxGridElement baseGridElement = OkxGridElement.findById(0);
@@ -423,336 +483,14 @@
baseGridElement.setShortOrderId(baseShortTp.getEntryOrderId());
baseGridElement.setHasShortOrder(true);
- // 挂多仓止损 (id=-2 到 -11),每格 quantity 张
- for (int id = -2; id >= -11; id--) {
- OkxGridElement elem = OkxGridElement.findById(id);
- if (elem == null) continue;
- BigDecimal triggerPrice = elem.getGridPrice();
- int finalId = id;
- executor.placeTakeProfit(triggerPrice.toString(), "sell", "long", config.getQuantity(),
- profitId -> {
- elem.setLongStopLossOrderId(profitId);
- OkxGridElement.refreshIndices();
- log.info("[OKX] 多仓止损已挂, gridId:{}, 触发价:{}, qty:{}, stopLossId:{}",
- finalId, triggerPrice, config.getQuantity(), profitId);
- });
- }
-
- // 挂空仓止损 (id=2 到 11),每格 quantity 张
- for (int id = 2; id <= 11; id++) {
- OkxGridElement elem = OkxGridElement.findById(id);
- if (elem == null) continue;
- BigDecimal triggerPrice = elem.getGridPrice();
- int finalId = id;
- executor.placeTakeProfit(triggerPrice.toString(), "buy", "short", config.getQuantity(),
- profitId -> {
- elem.setShortStopLossOrderId(profitId);
- OkxGridElement.refreshIndices();
- log.info("[OKX] 空仓止损已挂, gridId:{}, 触发价:{}, qty:{}, stopLossId:{}",
- finalId, triggerPrice, config.getQuantity(), profitId);
- });
- }
-
- log.info("[OKX] 止损单已全部挂完, 空仓止损: 2~11, 多仓止损: -2~-11");
+ // 委托 StopLossManager 挂止损单
+ stopLossManager.setupBaseStopLosses();
state = StrategyState.ACTIVE;
}
}
- private void generateShortQueue() {
- shortPriceQueue.clear();
- int prec = config.getPriceScale();
- BigDecimal step = shortBaseEntryPrice.multiply(config.getGridRate()).setScale(prec, RoundingMode.HALF_UP);
- config.setStep(step);
- BigDecimal elem = shortBaseEntryPrice.subtract(step).setScale(prec, RoundingMode.HALF_UP);
- for (int i = 0; i < config.getGridQueueSize(); i++) {
- shortPriceQueue.add(elem);
- elem = elem.subtract(step).setScale(prec, RoundingMode.HALF_UP);
- if (elem.compareTo(BigDecimal.ZERO) <= 0) break;
- }
- shortPriceQueue.sort((a, b) -> b.compareTo(a));
- log.info("[OKX] 空队列:{}", shortPriceQueue);
- }
- private void generateLongQueue() {
- longPriceQueue.clear();
- int prec = config.getPriceScale();
- BigDecimal step = config.getStep();
- BigDecimal elem = shortBaseEntryPrice.add(step).setScale(prec, RoundingMode.HALF_UP);
- for (int i = 0; i < config.getGridQueueSize(); i++) {
- longPriceQueue.add(elem);
- elem = elem.add(step).setScale(prec, RoundingMode.HALF_UP);
- }
- longPriceQueue.sort(BigDecimal::compareTo);
- log.info("[OKX] 多队列:{}", longPriceQueue);
- }
-
- private void updateGridElements() {
- List<OkxGridElement> elements = new ArrayList<>();
- int shortSize = shortPriceQueue.size();
- int longSize = longPriceQueue.size();
- int prec = config.getPriceScale();
- BigDecimal step = config.getStep();
- String qty = config.getQuantity();
-
- // 空仓队列: id=-1, -2, ...
- for (int i = 0; i < shortSize; i++) {
- int id = -(i + 1);
- Integer upId = (i == 0) ? 0 : id + 1;
- Integer downId = (i == shortSize - 1) ? null : id - 1;
- BigDecimal price = shortPriceQueue.get(i);
- OkxTraderParam longParam = OkxTraderParam.builder()
- .direction(OkxTraderParam.Direction.LONG)
- .entryPrice(price).takeProfitPrice(price.add(step).setScale(prec, RoundingMode.HALF_UP)).quantity(qty).build();
- OkxTraderParam shortParam = OkxTraderParam.builder()
- .direction(OkxTraderParam.Direction.SHORT)
- .entryPrice(price).takeProfitPrice(price.subtract(step).setScale(prec, RoundingMode.HALF_UP)).quantity(qty).build();
- elements.add(OkxGridElement.builder().id(id).gridPrice(price).upId(upId).downId(downId)
- .longTraderParam(longParam).shortTraderParam(shortParam).build());
- }
-
- // 位置 0: 基底价格
- {
- BigDecimal price = shortBaseEntryPrice;
- OkxTraderParam longParam = OkxTraderParam.builder()
- .direction(OkxTraderParam.Direction.LONG)
- .entryPrice(price).takeProfitPrice(price.add(step).setScale(prec, RoundingMode.HALF_UP)).quantity(qty).build();
- OkxTraderParam shortParam = OkxTraderParam.builder()
- .direction(OkxTraderParam.Direction.SHORT)
- .entryPrice(price).takeProfitPrice(price.subtract(step).setScale(prec, RoundingMode.HALF_UP)).quantity(qty).build();
- elements.add(OkxGridElement.builder().id(0).gridPrice(price)
- .upId(shortSize > 0 ? 1 : null).downId(longSize > 0 ? -1 : null)
- .longTraderParam(longParam).shortTraderParam(shortParam).build());
- }
-
- // 多仓队列: id=1, 2, ...
- for (int i = 0; i < longSize; i++) {
- int id = i + 1;
- Integer downId = (i == 0) ? 0 : id - 1;
- Integer upId = (i == longSize - 1) ? null : id + 1;
- BigDecimal price = longPriceQueue.get(i);
- OkxTraderParam longParam = OkxTraderParam.builder()
- .direction(OkxTraderParam.Direction.LONG)
- .entryPrice(price).takeProfitPrice(price.add(step).setScale(prec, RoundingMode.HALF_UP)).quantity(qty).build();
- OkxTraderParam shortParam = OkxTraderParam.builder()
- .direction(OkxTraderParam.Direction.SHORT)
- .entryPrice(price).takeProfitPrice(price.subtract(step).setScale(prec, RoundingMode.HALF_UP)).quantity(qty).build();
- elements.add(OkxGridElement.builder().id(id).gridPrice(price).upId(upId).downId(downId)
- .longTraderParam(longParam).shortTraderParam(shortParam).build());
- }
-
- config.setGridElements(elements);
- log.info("[OKX] 网格元素列表已构建, 共{}个元素", elements.size());
- }
-
- // ---- 止损触发处理 ----
-
- /**
- * 多仓止损触发处理(Gate 模式逐步缩进)。
- * 止损触发后向基底方向缩进 1 格挂条件多单,数量 = |触发价 - 当前持仓均价| / 网格步长,取整。
- * 若 N>2,先取消上一步的旧挂单。
- */
- private void handleLongStopLossTriggered(OkxGridElement gridElement) {
- int gridId = gridElement.getId();
- int N = Math.abs(gridId);
- gridElement.setLongStopLossOrderId(null);
- log.info("[OKX] 多仓止损触发 gridId:{}, 逐步缩进", gridId);
-
- int newEntryGridId = -(N - 1);
- OkxGridElement newEntryGrid = OkxGridElement.findById(newEntryGridId);
- if (newEntryGrid == null) {
- OkxGridElement.refreshIndices();
- log.warn("[OKX] 多仓止损触发 gridId:{} 找不到入单网格({})", gridId, newEntryGridId);
- return;
- }
-
- if (N > 2) {
- int cancelGridId = -(N - 2);
- OkxGridElement cancelGrid = OkxGridElement.findById(cancelGridId);
- if (cancelGrid != null && cancelGrid.isHasLongOrder()) {
- executor.cancelAlgoOrder(cancelGrid.getLongOrderId(), oid -> {
- longEntryTraderIdParam(cancelGrid, null, false);
- log.info("[OKX] 多仓止损触发, 取消gridId:{}的多单", cancelGridId);
- });
- }
- }
-
- BigDecimal triggerPrice = newEntryGrid.getGridPrice();
- BigDecimal priceDiff = longEntryPrice.subtract(triggerPrice).abs();
- // 精度补偿:步长被setScale截断,priceDiff/step可能产生1.99998→Down截断为1的问题
- BigDecimal epsilon = new BigDecimal("0.00000001");
- int count = priceDiff.add(epsilon).divide(config.getStep(), 0, RoundingMode.DOWN).intValue();
- count = Math.max(1, count);
- int entryQty = count * Integer.parseInt(config.getQuantity());
- String size = String.valueOf(entryQty);
- log.info("[OKX] 多仓止损触发 gridId:{}, 在gridId:{}挂{}张多单(价差:{},步长:{},count:{},qty:{})",
- gridId, newEntryGridId, entryQty, priceDiff, config.getStep(), count, config.getQuantity());
- newEntryGrid.getLongTraderParam().setQuantity(size);
- placeEntryOrderWithPreFlag(newEntryGrid, true, triggerPrice, size);
- }
-
- /**
- * 空仓止损触发处理(Gate 模式逐步缩进)。
- * 止损触发后向基底方向缩进 1 格挂条件空单,数量 = |触发价 - 当前持仓均价| / 网格步长,取整。
- * 若 N>2,先取消上一步的旧挂单。
- */
- private void handleShortStopLossTriggered(OkxGridElement gridElement) {
- int gridId = gridElement.getId();
- int N = gridId;
- gridElement.setShortStopLossOrderId(null);
- log.info("[OKX] 空仓止损触发 gridId:{}, 逐步缩进", gridId);
-
- int newEntryGridId = N - 1;
- OkxGridElement newEntryGrid = OkxGridElement.findById(newEntryGridId);
- if (newEntryGrid == null) {
- OkxGridElement.refreshIndices();
- log.warn("[OKX] 空仓止损触发 gridId:{} 找不到入单网格({})", gridId, newEntryGridId);
- return;
- }
-
- if (N > 2) {
- int cancelGridId = N - 2;
- OkxGridElement cancelGrid = OkxGridElement.findById(cancelGridId);
- if (cancelGrid != null && cancelGrid.isHasShortOrder()) {
- executor.cancelAlgoOrder(cancelGrid.getShortOrderId(), oid -> {
- shortEntryTraderIdParam(cancelGrid, null, false);
- log.info("[OKX] 空仓止损触发, 取消gridId:{}的空单", cancelGridId);
- });
- }
- }
-
- BigDecimal triggerPrice = newEntryGrid.getGridPrice();
- BigDecimal priceDiff = shortEntryPrice.subtract(triggerPrice).abs();
- // 精度补偿:步长被setScale截断,priceDiff/step可能产生1.99998→Down截断为1的问题
- BigDecimal epsilon = new BigDecimal("0.00000001");
- int count = priceDiff.add(epsilon).divide(config.getStep(), 0, RoundingMode.DOWN).intValue();
- count = Math.max(1, count);
- int entryQty = count * Integer.parseInt(config.getQuantity());
- String size = String.valueOf(entryQty);
- log.info("[OKX] 空仓止损触发 gridId:{}, 在gridId:{}挂{}张空单(价差:{},步长:{},count:{},qty:{})",
- gridId, newEntryGridId, entryQty, priceDiff, config.getStep(), count, config.getQuantity());
- newEntryGrid.getShortTraderParam().setQuantity(size);
- placeEntryOrderWithPreFlag(newEntryGrid, false, triggerPrice, size);
- }
-
- private void extendLongStopLoss(int filledQty) {
- // filledQty 为本次新增止损张数 = count * quantity, 需要按 quantity 为粒度拆分为 count 个止损单
- int qty = Integer.parseInt(config.getQuantity());
- int stopLossCount = filledQty / qty;
- int furthestSlId = 0;
- for (OkxGridElement e : config.getGridElements()) {
- if (e.getLongStopLossOrderId() != null && e.getId() < furthestSlId) {
- furthestSlId = e.getId();
- }
- }
- if (furthestSlId == 0) furthestSlId = -11;
- log.info("[OKX] 多仓追挂止损, 当前最远止损gridId:{}, 追加{}单, 每单{}张", furthestSlId, stopLossCount, qty);
- for (int i = 0; i < stopLossCount; i++) {
- int newSlId = furthestSlId - i - 1;
- OkxGridElement elem = OkxGridElement.findById(newSlId);
- if (elem == null) continue;
- BigDecimal triggerPrice = elem.getGridPrice();
- int finalSlId = newSlId;
- executor.placeTakeProfit(triggerPrice.toString(), "sell", "long", config.getQuantity(),
- profitId -> {
- elem.setLongStopLossOrderId(profitId);
- OkxGridElement.refreshIndices();
- log.info("[OKX] 多仓止损追加, gridId:{}, 触发价:{}, stopLossId:{}", finalSlId, triggerPrice, profitId);
- });
- }
- }
-
- private void extendShortStopLoss(int filledQty) {
- int qty = Integer.parseInt(config.getQuantity());
- int stopLossCount = filledQty / qty;
- int furthestSlId = 0;
- for (OkxGridElement e : config.getGridElements()) {
- if (e.getShortStopLossOrderId() != null && e.getId() > furthestSlId) {
- furthestSlId = e.getId();
- }
- }
- if (furthestSlId == 0) furthestSlId = 11;
- log.info("[OKX] 空仓追挂止损, 当前最远止损gridId:{}, 追加{}单, 每单{}张", furthestSlId, stopLossCount, qty);
- for (int i = 0; i < stopLossCount; i++) {
- int newSlId = furthestSlId + i + 1;
- OkxGridElement elem = OkxGridElement.findById(newSlId);
- if (elem == null) continue;
- BigDecimal triggerPrice = elem.getGridPrice();
- int finalSlId = newSlId;
- executor.placeTakeProfit(triggerPrice.toString(), "buy", "short", config.getQuantity(),
- profitId -> {
- elem.setShortStopLossOrderId(profitId);
- OkxGridElement.refreshIndices();
- log.info("[OKX] 空仓止损追加, gridId:{}, 触发价:{}, stopLossId:{}", finalSlId, triggerPrice, profitId);
- });
- }
- }
-
- // ---- 辅助方法 ----
-
- private void longTakeProfitTraderIdParam(OkxGridElement baseElement, String profitId, boolean flag) {
- OkxTraderParam tp = baseElement.getLongTraderParam();
- tp.setTakeProfitOrderId(profitId);
- tp.setTakeProfitPlaced(flag);
- baseElement.setLongTakeProfitOrderId(profitId);
- OkxGridElement.refreshIndices();
- }
-
- private void shortTakeProfitTraderIdParam(OkxGridElement baseElement, String profitId, boolean flag) {
- OkxTraderParam tp = baseElement.getShortTraderParam();
- tp.setTakeProfitOrderId(profitId);
- tp.setTakeProfitPlaced(flag);
- baseElement.setShortTakeProfitOrderId(profitId);
- OkxGridElement.refreshIndices();
- }
-
- private void longEntryTraderIdParam(OkxGridElement baseElement, String entryId, boolean flag) {
- OkxTraderParam tp = baseElement.getLongTraderParam();
- tp.setEntryOrderId(entryId);
- tp.setEntryOrderPlaced(flag);
- baseElement.setHasLongOrder(flag);
- baseElement.setLongOrderId(entryId);
- OkxGridElement.refreshIndices();
- }
-
- private void shortEntryTraderIdParam(OkxGridElement baseElement, String entryId, boolean flag) {
- OkxTraderParam tp = baseElement.getShortTraderParam();
- tp.setEntryOrderId(entryId);
- tp.setEntryOrderPlaced(flag);
- baseElement.setHasShortOrder(flag);
- baseElement.setShortOrderId(entryId);
- OkxGridElement.refreshIndices();
- }
-
- private void placeEntryOrderWithPreFlag(OkxGridElement gridElement, boolean isLong,
- BigDecimal triggerPrice, String size) {
- if (isLong) {
- gridElement.setHasLongOrder(true);
- } else {
- gridElement.setHasShortOrder(true);
- }
- String side = isLong ? "buy" : "sell";
- String posSide = isLong ? "long" : "short";
- executor.placeConditionalEntryOrder(triggerPrice.toString(), side, posSide, size,
- orderId -> {
- if (isLong) {
- longEntryTraderIdParam(gridElement, orderId, true);
- } else {
- shortEntryTraderIdParam(gridElement, orderId, true);
- }
- },
- () -> {
- if (isLong) {
- gridElement.setHasLongOrder(false);
- gridElement.setLongOrderId(null);
- } else {
- gridElement.setHasShortOrder(false);
- gridElement.setShortOrderId(null);
- }
- OkxGridElement.refreshIndices();
- log.warn("[OKX] 条件单创建失败,回滚标志位 gridId:{}, isLong:{}", gridElement.getId(), isLong);
- }
- );
- }
+ // ---- 盈亏计算 ----
private void updateUnrealizedPnl() {
if (lastKlinePrice == null || lastKlinePrice.compareTo(BigDecimal.ZERO) == 0) return;
@@ -813,7 +551,11 @@
state = StrategyState.STOPPED;
closeExistingPositions();
executor.cancelAllAlgoOrders();
- startGrid();
+ // 提交到 executor 末尾:单线程FIFO保证前面所有平仓/取消任务完成后才重置
+ executor.submitTask(() -> {
+ try { Thread.sleep(3000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
+ startGrid();
+ });
}
} catch (Exception e) {
log.warn("[OKX] 盈亏检查失败", e);
@@ -821,10 +563,15 @@
}
private void handlePositionZeroAndReset(String direction) {
+ log.info("[OKX] {}持仓归零,重置策略", direction);
state = StrategyState.STOPPED;
executor.cancelAllAlgoOrders();
closeExistingPositions();
- startGrid();
+ // 提交到 executor 末尾:FIFO保证平仓完成后再重置
+ executor.submitTask(() -> {
+ try { Thread.sleep(3000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
+ startGrid();
+ });
}
// ---- getters ----
--
Gitblit v1.9.1