From e4c5d36a2da3fabd0f233df15a563d520d08b287 Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Wed, 24 Jun 2026 16:10:34 +0800
Subject: [PATCH] refactor(okx): 删除K线处理器并重命名止盈单方法为止损单
---
src/main/java/com/xcong/excoin/modules/okxApi/OkxGridTradeService.java | 8
/dev/null | 234 ---------------------------------------
src/main/java/com/xcong/excoin/modules/okxApi/OkxTradeExecutor.java | 71 ++++++++---
3 files changed, 55 insertions(+), 258 deletions(-)
diff --git a/src/main/java/com/xcong/excoin/modules/okxApi/OkxGridTradeService.java b/src/main/java/com/xcong/excoin/modules/okxApi/OkxGridTradeService.java
index f9e7ddd..62fdc13 100644
--- a/src/main/java/com/xcong/excoin/modules/okxApi/OkxGridTradeService.java
+++ b/src/main/java/com/xcong/excoin/modules/okxApi/OkxGridTradeService.java
@@ -641,7 +641,7 @@
}
BigDecimal triggerPrice = elem.getGridPrice();
int finalId = id;
- executor.placeTakeProfit(triggerPrice, "close_short", config.getQuantity(),
+ executor.placeStopLoss(triggerPrice, "close_short", config.getQuantity(),
profitId -> {
elem.setShortStopLossOrderId(profitId);
GridElement.refreshIndices();
@@ -655,7 +655,7 @@
}
BigDecimal triggerPrice = elem.getGridPrice();
int finalId = id;
- executor.placeTakeProfit(triggerPrice, "close_long", config.getQuantity(),
+ executor.placeStopLoss(triggerPrice, "close_long", config.getQuantity(),
profitId -> {
elem.setLongStopLossOrderId(profitId);
GridElement.refreshIndices();
@@ -1144,7 +1144,7 @@
}
BigDecimal triggerPrice = elem.getGridPrice();
int finalSlId = newSlId;
- executor.placeTakeProfit(triggerPrice, "close_long", config.getQuantity(),
+ executor.placeStopLoss(triggerPrice, "close_long", config.getQuantity(),
profitId -> {
elem.setLongStopLossOrderId(profitId);
GridElement.refreshIndices();
@@ -1172,7 +1172,7 @@
}
BigDecimal triggerPrice = elem.getGridPrice();
int finalSlId = newSlId;
- executor.placeTakeProfit(triggerPrice, "close_short", config.getQuantity(),
+ executor.placeStopLoss(triggerPrice, "close_short", config.getQuantity(),
profitId -> {
elem.setShortStopLossOrderId(profitId);
GridElement.refreshIndices();
diff --git a/src/main/java/com/xcong/excoin/modules/okxApi/OkxTradeExecutor.java b/src/main/java/com/xcong/excoin/modules/okxApi/OkxTradeExecutor.java
index 8c971f9..a7ee107 100644
--- a/src/main/java/com/xcong/excoin/modules/okxApi/OkxTradeExecutor.java
+++ b/src/main/java/com/xcong/excoin/modules/okxApi/OkxTradeExecutor.java
@@ -208,17 +208,12 @@
});
}
- // ==================== 止盈条件单 ====================
+ // ==================== 止盈/止损条件单 ====================
/**
- * 异步创建止盈条件单(OKX 算法订单 — conditional 类型)。
+ * 异步创建止盈条件单(OKX 算法订单 — conditional 类型,tpTriggerPx)。
*
- * <p>服务器监控价格,达到触发价后自动以市价平仓。
- * 使用 OKX 的 {@code order-algo} 接口,ordType=conditional。
- *
- * <h3>止盈失败兜底</h3>
- * 止盈单创建失败时立即调用 {@link #marketClose(String, String)} 市价平仓,
- * 确保仓位不会因止损条件未挂上而无保护。
+ * <p>止盈单创建失败时立即调用 {@link #marketClose(String, String)} 市价平仓兜底。
*
* @param triggerPrice 触发价格
* @param orderType 平仓类型:"close_long" 平多 / "close_short" 平空
@@ -229,6 +224,34 @@
String orderType,
String size,
Consumer<String> onSuccess) {
+ placeConditionalClose(triggerPrice, orderType, size, onSuccess, false);
+ }
+
+ /**
+ * 异步创建止损条件单(OKX 算法订单 — conditional 类型,slTriggerPx)。
+ *
+ * <p>止损单创建失败时立即调用 {@link #marketClose(String, String)} 市价平仓兜底。
+ *
+ * @param triggerPrice 触发价格
+ * @param orderType 平仓类型:"close_long" 平多 / "close_short" 平空
+ * @param size 平仓张数(正数,如 "15")
+ * @param onSuccess 成功回调,接收 algoId(可为 null)
+ */
+ public void placeStopLoss(BigDecimal triggerPrice,
+ String orderType,
+ String size,
+ Consumer<String> onSuccess) {
+ placeConditionalClose(triggerPrice, orderType, size, onSuccess, true);
+ }
+
+ /**
+ * 通用平仓条件单:isStopLoss=true 用 slTriggerPx/slOrdPx,false 用 tpTriggerPx/tpOrdPx。
+ */
+ private void placeConditionalClose(BigDecimal triggerPrice,
+ String orderType,
+ String size,
+ Consumer<String> onSuccess,
+ boolean isStopLoss) {
executor.execute(() -> {
String posSide = null;
try {
@@ -240,11 +263,11 @@
side = "buy";
posSide = "short";
} else {
- log.error("[TradeExec-OKX] 未知止盈类型: {}", orderType);
+ log.error("[TradeExec-OKX] 未知平仓类型: {}", orderType);
return;
}
- // OKX conditional 止盈止损使用 tpTriggerPx/tpOrdPx 或 slTriggerPx/slOrdPx
+ String label = isStopLoss ? "止损" : "止盈";
JSONObject body = new JSONObject();
body.put("instId", contract);
body.put("tdMode", "cross");
@@ -252,29 +275,35 @@
body.put("posSide", posSide);
body.put("ordType", "conditional");
body.put("sz", size);
- // "close_long"=平多(止盈), "close_short"=平空(止盈)
- body.put("tpTriggerPx", triggerPrice.stripTrailingZeros().toPlainString());
- body.put("tpTriggerPxType", "last");
- body.put("tpOrdPx", "-1");
+ // 止盈用 tp 系列字段,止损用 sl 系列字段
+ if (isStopLoss) {
+ body.put("slTriggerPx", triggerPrice.stripTrailingZeros().toPlainString());
+ body.put("slTriggerPxType", "last");
+ body.put("slOrdPx", "-1");
+ } else {
+ body.put("tpTriggerPx", triggerPrice.stripTrailingZeros().toPlainString());
+ body.put("tpTriggerPxType", "last");
+ body.put("tpOrdPx", "-1");
+ }
JSONObject resp = okPost("/api/v5/trade/order-algo", body.toJSONString());
String code = resp.getString("code");
if (!"0".equals(code)) {
- log.error("[TradeExec-OKX] 止盈单创建失败, code:{}, msg:{}, 立即市价止盈",
- code, resp.getString("msg"));
+ log.error("[TradeExec-OKX] {}单创建失败, code:{}, msg:{}, 立即市价{}",
+ label, code, resp.getString("msg"), label);
marketClose(size, posSide);
return;
}
JSONArray data = resp.getJSONArray("data");
String algoId = (data != null && !data.isEmpty())
? data.getJSONObject(0).getString("algoId") : null;
- log.info("[TradeExec-OKX] 止盈单已创建, triggerPx:{}, type:{}, sz:{}, algoId:{}",
- triggerPrice, orderType, size, algoId);
+ log.info("[TradeExec-OKX] {}单已创建, triggerPx:{}, type:{}, sz:{}, algoId:{}",
+ label, triggerPrice, orderType, size, algoId);
if (onSuccess != null) {
onSuccess.accept(algoId);
}
} catch (Exception e) {
- log.error("[TradeExec-OKX] 止盈单创建失败, triggerPx:{}, sz:{}, 立即市价止盈",
+ log.error("[TradeExec-OKX] 创建失败, triggerPx:{}, sz:{}, 立即市价{}",
triggerPrice, size, e);
if (posSide != null) {
marketClose(size, posSide);
@@ -465,7 +494,9 @@
for (int i = 0; i < data.size(); i++) {
JSONObject order = data.getJSONObject(i);
String algoId = order.getString("algoId");
- if (algoId == null) continue;
+ if (algoId == null) {
+ continue;
+ }
JSONObject item = new JSONObject();
item.put("algoId", algoId);
item.put("instId", contract);
diff --git a/src/main/java/com/xcong/excoin/modules/okxApi/wsHandler/handler/CandlestickOkxChannelHandler.java b/src/main/java/com/xcong/excoin/modules/okxApi/wsHandler/handler/CandlestickOkxChannelHandler.java
deleted file mode 100644
index 9a8d754..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxApi/wsHandler/handler/CandlestickOkxChannelHandler.java
+++ /dev/null
@@ -1,195 +0,0 @@
-package com.xcong.excoin.modules.okxApi.wsHandler.handler;
-
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
-import com.xcong.excoin.modules.okxApi.OkxGridTradeService;
-import com.xcong.excoin.modules.okxApi.wsHandler.OkxChannelHandler;
-import lombok.extern.slf4j.Slf4j;
-import org.java_websocket.client.WebSocketClient;
-
-import java.math.BigDecimal;
-
-/**
- * OKX K线频道处理器(candle1m)— 策略的唯一价格时间驱动源。
- *
- * <h3>定位</h3>
- * 这是一个<b>公开频道</b>,连接到 OKX 公开 WebSocket 端点,
- * 无需登录认证。订阅 1 分钟 K 线实时推送,每收到一根 K 线即触发
- * {@link OkxGridTradeService#onKline(BigDecimal)},由策略引擎决定是否开仓/止盈。
- *
- * <h3>订阅格式</h3>
- * <pre>
- * {"op":"subscribe","args":[{"channel":"candle1m","instId":"ETH-USDT-SWAP"}]}
- * </pre>
- *
- * <h3>数据推送格式</h3>
- * <pre>
- * {
- * "arg": {"channel":"candle1m","instId":"ETH-USDT-SWAP"},
- * "data": [
- * ["timestamp","open","high","low","close","vol","volCcy","volCcyQuote","confirm"]
- * ]
- * }
- * </pre>
- *
- * <h3>字段说明</h3>
- * <table>
- * <tr><th>索引</th><th>字段</th><th>含义</th></tr>
- * <tr><td>0</td><td>ts</td><td>K线起始时间(Unix ms)</td></tr>
- * <tr><td>1</td><td>o</td><td>开盘价</td></tr>
- * <tr><td>2</td><td>h</td><td>最高价</td></tr>
- * <tr><td>3</td><td>l</td><td>最低价</td></tr>
- * <tr><td>4</td><td>c</td><td><b>收盘价</b>(用于驱动策略)</td></tr>
- * <tr><td>5</td><td>vol</td><td>成交量(张)</td></tr>
- * <tr><td>6</td><td>volCcy</td><td>成交量(币)</td></tr>
- * <tr><td>7</td><td>volCcyQuote</td><td>成交量(USDT)</td></tr>
- * <tr><td>8</td><td>confirm</td><td>K线状态("0"=未完结,"1"=已完结)</td></tr>
- * </table>
- *
- * <h3>注意</h3>
- * 不判断 confirm 字段(K线是否完结)——策略需要 tick 级实时响应价格变动,
- * 而非等 1 分钟烛线完结后才行动。
- *
- * @author Administrator
- */
-@Slf4j
-public class CandlestickOkxChannelHandler implements OkxChannelHandler {
-
- /** 频道名称 */
- private static final String CHANNEL_NAME = "candle1m";
-
- /** 交易对标识,如 "ETH-USDT-SWAP" */
- private final String instId;
-
- /** 网格交易服务,接收 K 线回调 */
- private final OkxGridTradeService gridTradeService;
-
- /** 订阅确认状态 */
- private volatile boolean subscribed = false;
-
- /**
- * 构造 K 线频道处理器。
- *
- * @param instId 交易对标识(如 "ETH-USDT-SWAP")
- * @param gridTradeService OKX 网格交易策略服务实例
- */
- public CandlestickOkxChannelHandler(String instId, OkxGridTradeService gridTradeService) {
- this.instId = instId;
- this.gridTradeService = gridTradeService;
- }
-
- /**
- * @return 频道名称 "candle1m"
- */
- @Override
- public String getChannelName() {
- return CHANNEL_NAME;
- }
-
- /**
- * @return 交易对标识(如 "ETH-USDT-SWAP")
- */
- @Override
- public String getInstId() {
- return instId;
- }
-
- /**
- * 发送 K 线频道订阅请求(公开频道,无需签名)。
- *
- * <h3>订阅格式</h3>
- * <pre>
- * {"op":"subscribe","args":[{"channel":"candle1m","instId":"ETH-USDT-SWAP"}]}
- * </pre>
- *
- * @param ws OKX 公开 WebSocket 客户端
- */
- @Override
- public void subscribe(WebSocketClient ws) {
- JSONObject msg = new JSONObject();
- msg.put("op", "subscribe");
- JSONArray args = new JSONArray();
- JSONObject arg = new JSONObject();
- arg.put("channel", CHANNEL_NAME);
- arg.put("instId", instId);
- args.add(arg);
- msg.put("args", args);
- ws.send(msg.toJSONString());
- log.info("[OKX-WS] 订阅K线频道, instId: {}", instId);
- }
-
- /**
- * 发送 K 线频道取消订阅请求。
- *
- * @param ws OKX 公开 WebSocket 客户端
- */
- @Override
- public void unsubscribe(WebSocketClient ws) {
- JSONObject msg = new JSONObject();
- msg.put("op", "unsubscribe");
- JSONArray args = new JSONArray();
- JSONObject arg = new JSONObject();
- arg.put("channel", CHANNEL_NAME);
- arg.put("instId", instId);
- args.add(arg);
- msg.put("args", args);
- ws.send(msg.toJSONString());
- log.info("[OKX-WS] 取消订阅K线频道, instId: {}", instId);
- }
-
- /**
- * 处理 K 线推送消息。
- *
- * <h3>数据提取</h3>
- * 从 {@code data[0]} 数组中提取索引 4(收盘价 close),
- * 传给 {@code gridTradeService.onKline(closePrice)}。
- *
- * <h3>OKX 数据格式</h3>
- * data 是一个二维数组,第一层是 K 线条数(通常 1 条),
- * 第二层是各字段值。data[0][4] = 收盘价。
- *
- * @param response WebSocket 推送的完整 JSON
- * @return true 表示已处理(匹配成功)
- */
- @Override
- public boolean handleMessage(JSONObject response) {
- JSONObject arg = response.getJSONObject("arg");
- if (arg == null || !CHANNEL_NAME.equals(arg.getString("channel"))) {
- return false;
- }
- try {
- JSONArray dataArray = response.getJSONArray("data");
- if (dataArray == null || dataArray.isEmpty()) {
- log.debug("[OKX-WS] candle1m 数据为空");
- return true;
- }
- // data[0] 是一个数组: [ts, o, h, l, c, vol, volCcy, volCcyQuote, confirm]
- JSONArray candle = dataArray.getJSONArray(0);
- if (candle == null || candle.size() < 5) {
- log.warn("[OKX-WS] candle1m 数据格式异常: {}", dataArray);
- return true;
- }
- // 索引 4 = 收盘价 close
- BigDecimal closePrice = candle.getBigDecimal(4);
-
- if (gridTradeService != null && closePrice != null) {
- gridTradeService.onKline(closePrice);
- }
- } catch (Exception e) {
- log.error("[OKX-WS] 处理 candle1m 数据失败", e);
- }
- return true;
- }
-
- // ==================== 订阅状态 ====================
-
- @Override
- public boolean isSubscribed() {
- return subscribed;
- }
-
- @Override
- public void setSubscribed(boolean subscribed) {
- this.subscribed = subscribed;
- }
-}
diff --git a/src/main/java/com/xcong/excoin/modules/okxApi/wsHandler/handler/OrderAlgoOkxChannelHandler.java b/src/main/java/com/xcong/excoin/modules/okxApi/wsHandler/handler/OrderAlgoOkxChannelHandler.java
deleted file mode 100644
index 4fff35f..0000000
--- a/src/main/java/com/xcong/excoin/modules/okxApi/wsHandler/handler/OrderAlgoOkxChannelHandler.java
+++ /dev/null
@@ -1,234 +0,0 @@
-package com.xcong.excoin.modules.okxApi.wsHandler.handler;
-
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
-import com.xcong.excoin.modules.okxApi.OkxConfig;
-import com.xcong.excoin.modules.okxApi.OkxGridTradeService;
-import com.xcong.excoin.modules.okxApi.wsHandler.AbstractOkxPrivateChannelHandler;
-import lombok.extern.slf4j.Slf4j;
-import org.java_websocket.client.WebSocketClient;
-
-/**
- * OKX 条件订单频道处理器(orders-algo)。
- *
- * <h3>定位</h3>
- * 订阅用户条件订单(algo order)状态推送。当条件单状态变更(触发、取消等)时,
- * 获得 algoId、状态、订单类型等信息。相当于 Gate 的 {@code futures.autoorders} 频道。
- *
- * <h3>订阅格式</h3>
- * 私有频道,需要先登录认证。orders-algo 频道不支持 instId,需使用 instFamily:
- * <pre>
- * {"op":"subscribe","args":[{"channel":"orders-algo","instType":"SWAP","instFamily":"ETH-USDT"}]}
- * </pre>
- *
- * <h3>数据推送格式(条件单触发/成交)</h3>
- * <pre>
- * {
- * "arg": {"channel":"orders-algo","instType":"SWAP","instId":"ETH-USDT-SWAP"},
- * "data": [{
- * "algoId": "1234567890",
- * "state": "effective", // 状态: "effective"=已触发, "canceled"=已取消
- * "ordType": "conditional", // 订单类型
- * "actualSide": "buy", // 实际买卖方向: "buy"/"sell"
- * "posSide": "long", // 持仓方向: "long"/"short"/"net"
- * "sz": "1", // 委托数量
- * "triggerPx": "3000", // 触发价格
- * "ordPx": "3000.5", // 委托价格
- * "tradeId": "9876543210" // 关联交易ID
- * }]
- * }
- * </pre>
- *
- * <h3>状态映射</h3>
- * <ul>
- * <li>"effective" → status="finished"(已触发/已完成)</li>
- * <li>"canceled" → status="cancelled"(已取消)</li>
- * </ul>
- *
- * <h3>订单类型映射(orderType)</h3>
- * 根据 posSide 和 actualSide 组合推断订单类型:
- * <ul>
- * <li>posSide=long, actualSide=sell → "plan-close-long-position"(平多仓)</li>
- * <li>posSide=short, actualSide=buy → "plan-close-short-position"(平空仓)</li>
- * <li>posSide=net, actualSide=buy → "entry-long"(开多仓)</li>
- * <li>posSide=net, actualSide=sell → "entry-short"(开空仓)</li>
- * </ul>
- *
- * @author Administrator
- */
-@Slf4j
-public class OrderAlgoOkxChannelHandler extends AbstractOkxPrivateChannelHandler {
-
- /** OKX 条件订单频道名称 */
- private static final String CHANNEL_NAME = "orders-algo";
-
- /** OKX 配置 */
- private final OkxConfig config;
-
- /** 交易品种族(如 "ETH-USDT",从合约名 ETH-USDT-SWAP 派生) */
- private final String instFamily;
-
- /**
- * 构造条件订单频道处理器。
- *
- * @param config OKX 配置实例(提供合约名称等)
- * @param gridTradeService OKX 网格交易策略服务实例
- */
- public OrderAlgoOkxChannelHandler(OkxConfig config, OkxGridTradeService gridTradeService) {
- super(CHANNEL_NAME,
- config.getApiKey(), config.getApiSecret(), config.getPassphrase(),
- config.getContract(),
- gridTradeService);
- this.config = config;
- // orders-algo 频道不支持 instId,需用 instFamily:"ETH-USDT-SWAP" → "ETH-USDT"
- String contract = config.getContract();
- int lastDash = contract.lastIndexOf('-');
- this.instFamily = lastDash > 0 ? contract.substring(0, lastDash) : contract;
- }
-
- /**
- * 发送订阅请求,使用 instFamily 而非 instId。
- *
- * @param ws 私有频道 WebSocket 客户端
- */
- @Override
- public void subscribe(WebSocketClient ws) {
- JSONObject msg = new JSONObject();
- msg.put("op", "subscribe");
- JSONArray args = new JSONArray();
- JSONObject arg = new JSONObject();
- arg.put("channel", CHANNEL_NAME);
- arg.put("instType", "SWAP");
- arg.put("instFamily", instFamily); // orders-algo 频道用 instFamily,不能用 instId
- args.add(arg);
- msg.put("args", args);
- ws.send(msg.toJSONString());
- log.info("[OKX-WS] 订阅条件订单频道, instFamily: {}", instFamily);
- }
-
- /**
- * 发送取消订阅请求。
- *
- * @param ws 私有频道 WebSocket 客户端
- */
- @Override
- public void unsubscribe(WebSocketClient ws) {
- JSONObject msg = new JSONObject();
- msg.put("op", "unsubscribe");
- JSONArray args = new JSONArray();
- JSONObject arg = new JSONObject();
- arg.put("channel", CHANNEL_NAME);
- arg.put("instType", "SWAP");
- arg.put("instFamily", instFamily);
- args.add(arg);
- msg.put("args", args);
- ws.send(msg.toJSONString());
- log.info("[OKX-WS] 取消订阅条件订单频道, instFamily: {}", instFamily);
- }
-
- /**
- * 处理条件订单推送消息。
- *
- * <h3>处理流程</h3>
- * <ol>
- * <li>检查 arg.channel 是否匹配 "orders-algo"</li>
- * <li>遍历 data 数组,提取 algoId、state、actualSide、posSide、tradeId</li>
- * <li>映射 state → status(effective→finished, canceled→cancelled)</li>
- * <li>根据 posSide + actualSide 推断 orderType</li>
- * <li>调用 gridTradeService.onAutoOrder(algoId, status, reason, orderType, tradeId)</li>
- * </ol>
- *
- * @param response WebSocket 推送的完整 JSON
- * @return true 表示已处理(匹配成功)
- */
- @Override
- public boolean handleMessage(JSONObject response) {
- JSONObject arg = response.getJSONObject("arg");
- if (arg == null || !CHANNEL_NAME.equals(arg.getString("channel"))) {
- return false;
- }
- try {
- JSONArray dataArray = response.getJSONArray("data");
- if (dataArray == null || dataArray.isEmpty()) {
- return true;
- }
-
- String contract = config.getContract();
- for (int i = 0; i < dataArray.size(); i++) {
- JSONObject orderData = dataArray.getJSONObject(i);
-
- // 按合约名称精确过滤(instFamily 已做粗筛,这里精筛)
- String dataInstId = orderData.getString("instId");
- if (dataInstId == null || !contract.equals(dataInstId)) {
- continue;
- }
-
- String algoId = orderData.getString("algoId");
- String state = orderData.getString("state");
- String actualSide = orderData.getString("actualSide");
- String posSide = orderData.getString("posSide");
- String tradeId = orderData.getString("tradeId");
- String ordType = orderData.getString("ordType");
-
- // 状态映射
- String status;
- if ("effective".equals(state)) {
- status = "finished";
- } else if ("canceled".equals(state)) {
- status = "cancelled";
- } else {
- // 其他状态(如 "pending")暂不处理
- log.debug("[OKX-WS] orders-algo 忽略状态: algoId:{}, state:{}", algoId, state);
- continue;
- }
-
- // 推断 orderType
- String orderType = mapOrderType(posSide, actualSide);
-
- log.info("[OKX-WS] orders-algo 状态变更, algoId:{}, state:{}, status:{}, orderType:{}, ordType:{}, actualSide:{}, posSide:{}, tradeId:{}",
- algoId, state, status, orderType, ordType, actualSide, posSide, tradeId);
-
- if (getGridTradeService() != null) {
- getGridTradeService().onAutoOrder(algoId, status, state, orderType, tradeId);
- }
- }
- } catch (Exception e) {
- log.error("[OKX-WS] 处理 orders-algo 数据失败", e);
- }
- return true;
- }
-
- /**
- * 根据 OKX 的 posSide 和 actualSide 映射到策略内部的 orderType。
- *
- * <h3>映射规则</h3>
- * <table>
- * <tr><th>posSide</th><th>actualSide</th><th>含义</th><th>orderType</th></tr>
- * <tr><td>long</td><td>sell</td><td>平多仓</td><td>plan-close-long-position</td></tr>
- * <tr><td>short</td><td>buy</td><td>平空仓</td><td>plan-close-short-position</td></tr>
- * <tr><td>net</td><td>buy</td><td>开多仓</td><td>entry-long</td></tr>
- * <tr><td>net</td><td>sell</td><td>开空仓</td><td>entry-short</td></tr>
- * </table>
- *
- * @param posSide OKX 持仓方向("long"/"short"/"net")
- * @param actualSide OKX 实际买卖方向("buy"/"sell")
- * @return 策略内部的订单类型字符串
- */
- private String mapOrderType(String posSide, String actualSide) {
- // 平仓方向(止盈/止损触发)
- if ("long".equals(posSide) && "sell".equals(actualSide)) {
- return "plan-close-long-position";
- } else if ("short".equals(posSide) && "buy".equals(actualSide)) {
- return "plan-close-short-position";
- }
- // 开仓方向 — 覆盖 long_short_mode 和 net_mode 两种模式
- if (("long".equals(posSide) || "net".equals(posSide)) && "buy".equals(actualSide)) {
- return "entry-long";
- } else if (("short".equals(posSide) || "net".equals(posSide)) && "sell".equals(actualSide)) {
- return "entry-short";
- }
- // 默认值
- log.warn("[OKX-WS] 未知的 orderType 映射, posSide:{}, actualSide:{}", posSide, actualSide);
- return "unknown";
- }
-}
--
Gitblit v1.9.1