From 23ece6103fd890655f0eef79331d3d73921611a2 Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Wed, 07 Jan 2026 11:59:23 +0800
Subject: [PATCH] feat(trade): 优化交易系统止损逻辑和订单处理机制
---
src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxQuantWebSocketClient.java | 7 -
src/main/java/com/xcong/excoin/modules/okxNewPrice/celue/CaoZuoServiceImpl.java | 11 ++
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/PositionsWs.java | 40 ---------
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/OrderInfoWs.java | 26 +++++
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/TradeOrderWs.java | 99 ++----------------------
5 files changed, 50 insertions(+), 133 deletions(-)
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxQuantWebSocketClient.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxQuantWebSocketClient.java
index 3a37a34..65f5676 100644
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxQuantWebSocketClient.java
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxQuantWebSocketClient.java
@@ -395,15 +395,14 @@
// 这会导致多账号之间的数据冲突。需要进一步修改这些类的设计,让数据存储与特定账号关联
if (OrderInfoWs.ORDERINFOWS_CHANNEL.equals(channel)) {
- TradeRequestParam tradeRequestParam = OrderInfoWs.handleEvent(response, redisUtils, account.name());
- TradeOrderWs.orderZhiYingEvent(webSocketClient, tradeRequestParam);
+ List<TradeRequestParam> tradeRequestParams = OrderInfoWs.handleEvent(response, redisUtils, account.name());
+ TradeOrderWs.orderZhiYingEvent(webSocketClient, tradeRequestParams);
}else if (AccountWs.ACCOUNTWS_CHANNEL.equals(channel)) {
AccountWs.handleEvent(response, account.name());
} else if (PositionsWs.POSITIONSWS_CHANNEL.equals(channel)) {
- List<TradeRequestParam> tradeRequestParams = PositionsWs.handleEvent(response, account.name());
- TradeOrderWs.orderZhiSunEvent(webSocketClient, tradeRequestParams);
+ PositionsWs.handleEvent(response, account.name());
} else if (BalanceAndPositionWs.CHANNEL_NAME.equals(channel)) {
BalanceAndPositionWs.handleEvent(response);
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/celue/CaoZuoServiceImpl.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/celue/CaoZuoServiceImpl.java
index 1adb9fd..db8cd0c 100644
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/celue/CaoZuoServiceImpl.java
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/celue/CaoZuoServiceImpl.java
@@ -81,6 +81,17 @@
// 账户预期亏损金额比这个还小时,立即止损
if (realKuiSunAmount.compareTo(zhiSunAmount) > 0){
log.warn("账户冷静止损......");
+ //目前止损掉损失较大的一个方向
+ String positionAccountName = PositionsWs.initAccountName(accountName, posSide);
+ BigDecimal upl = PositionsWs.getAccountMap(positionAccountName).get("upl");
+ String posSideOther = CoinEnums.POSSIDE_LONG.getCode().equals(posSide) ? CoinEnums.POSSIDE_SHORT.getCode() : CoinEnums.POSSIDE_LONG.getCode();
+ String positionAccountOther = PositionsWs.initAccountName(accountName, posSideOther);
+ BigDecimal uplOther = PositionsWs.getAccountMap(positionAccountOther).get("upl");
+ if (upl.compareTo(uplOther) > 0){
+ log.warn("{}的亏损{},{}的亏损{},止损{}......",posSide,upl,posSideOther,uplOther,uplOther);
+ posSide = posSideOther;
+ }
+
WsMapBuild.saveStringToMap(InstrumentsWs.getAccountMap(accountName), CoinEnums.OUT.name(), OrderParamEnums.OUT_YES.getValue());
tradeRequestParam.setTradeType(OrderParamEnums.TRADE_YES.getValue());
return caoZuoZhiSunEvent(accountName, markPx, posSide);
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/OrderInfoWs.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/OrderInfoWs.java
index 333d3b1..945081d 100644
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/OrderInfoWs.java
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/OrderInfoWs.java
@@ -16,6 +16,8 @@
import java.math.BigDecimal;
import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -69,7 +71,7 @@
private static final String STATE_KEY = "state";
private static final String FILLFEE_KEY = "fillFee";
private static final String POSSIDE_KEY = "posSide";
- public static TradeRequestParam handleEvent(JSONObject response, RedisUtils redisUtils, String accountName) {
+ public static List<TradeRequestParam> handleEvent(JSONObject response, RedisUtils redisUtils, String accountName) {
log.info("开始执行OrderInfoWs......");
try {
@@ -140,7 +142,7 @@
log.info("{}: 订单详情已完成: {}, 自定义编号: {}", accountName, CoinEnums.HE_YUE.getCode(), clOrdId);
-
+ List<TradeRequestParam> tradeRequestParamList = new ArrayList<>();
TradeRequestParam tradeRequestParam = new TradeRequestParam();
tradeRequestParam.setAccountName(accountName);
@@ -176,7 +178,25 @@
tradeRequestParam.setSide(CoinEnums.POSSIDE_LONG.getCode().equals(posSide) ? CoinEnums.SIDE_SELL.getCode() : CoinEnums.SIDE_BUY.getCode());
tradeRequestParam.setClOrdId(WsParamBuild.getOrderNum(side));
tradeRequestParam.setSz(accFillSz);
- return tradeRequestParam;
+ tradeRequestParamList.add(tradeRequestParam);
+
+ // 1. 判断当前价格属于哪个网格去限价止损
+ WangGeListEnum gridByPriceNew = WangGeListEnum.getGridByPrice(new BigDecimal(avgPx));
+ if (gridByPriceNew != null) {
+ TradeRequestParam tradeRequestParamZhiSun = new TradeRequestParam();
+ tradeRequestParamZhiSun.setAccountName(accountName);
+ tradeRequestParamZhiSun.setMarkPx(String.valueOf(gridByPriceNew.getZhi_sun_dian()));
+ tradeRequestParamZhiSun.setInstId(CoinEnums.HE_YUE.getCode());
+ tradeRequestParamZhiSun.setTdMode(CoinEnums.CROSS.getCode());
+ tradeRequestParamZhiSun.setPosSide(posSide);
+ tradeRequestParamZhiSun.setOrdType(CoinEnums.ORDTYPE_LIMIT.getCode());
+ tradeRequestParamZhiSun.setTradeType(OrderParamEnums.TRADE_YES.getValue());
+ tradeRequestParamZhiSun.setSide(CoinEnums.POSSIDE_LONG.getCode().equals(posSide) ? CoinEnums.SIDE_SELL.getCode() : CoinEnums.SIDE_BUY.getCode());
+ tradeRequestParamZhiSun.setClOrdId(WsParamBuild.getOrderNum(side));
+ tradeRequestParamZhiSun.setSz(accFillSz);
+ tradeRequestParamList.add(tradeRequestParamZhiSun);
+ }
+ return tradeRequestParamList;
}
return null;
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/PositionsWs.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/PositionsWs.java
index afcf78e..7136774 100644
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/PositionsWs.java
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/PositionsWs.java
@@ -65,7 +65,7 @@
initParam(arg, accountName,CoinEnums.POSSIDE_SHORT.getCode());
}
- public static List<TradeRequestParam> handleEvent(JSONObject response, String accountName) {
+ public static void handleEvent(JSONObject response, String accountName) {
log.info("开始执行PositionsWs......");
@@ -73,10 +73,8 @@
JSONArray dataArray = response.getJSONArray("data");
if (dataArray == null || dataArray.isEmpty()) {
log.info("账户持仓频道数据为空,等待更新");
- return null;
+ return;
}
-
- List<TradeRequestParam> tradeRequestParamList = new ArrayList<>();
for (int i = 0; i < dataArray.size(); i++) {
JSONObject posData = dataArray.getJSONObject(i);
@@ -118,45 +116,11 @@
);
//先更新缓存
Map<String, BigDecimal> stringBigDecimalMap = initParam(posData, accountName, posSide);
- //构建止损参数
- if (stringBigDecimalMap.get("pos").compareTo(BigDecimal.ZERO) > 0){
- TradeRequestParam tradeRequestParam = new TradeRequestParam();
- // 1. 判断当前价格属于哪个网格
- WangGeListEnum gridByPriceNew = WangGeListEnum.getGridByPrice(new BigDecimal(avgPx));
- if (gridByPriceNew != null) {
- String zhiSunDian = gridByPriceNew.getZhi_sun_dian();
- String fangXiang = gridByPriceNew.getFang_xiang();
- BigDecimal fangXiangNow = stringBigDecimalMap.get("posSide");
- if (fangXiangNow.equals(fangXiang)){
- tradeRequestParam.setOrdType(CoinEnums.ORDTYPE_LIMIT.getCode());
- tradeRequestParam.setMarkPx(String.valueOf(zhiSunDian));
- }else{
- tradeRequestParam.setOrdType(CoinEnums.ORDTYPE_MARKET.getCode());
- tradeRequestParam.setMarkPx(String.valueOf(markPx));
- }
- }else{
- tradeRequestParam.setOrdType(CoinEnums.ORDTYPE_MARKET.getCode());
- tradeRequestParam.setMarkPx(String.valueOf(markPx));
- }
-
-
- tradeRequestParam.setAccountName(accountName);
- tradeRequestParam.setInstId(CoinEnums.HE_YUE.getCode());
- tradeRequestParam.setTdMode(CoinEnums.CROSS.getCode());
- tradeRequestParam.setPosSide(posSide);
- tradeRequestParam.setTradeType(OrderParamEnums.TRADE_YES.getValue());
- tradeRequestParam.setSide(CoinEnums.POSSIDE_LONG.getCode().equals(posSide) ? CoinEnums.SIDE_SELL.getCode() : CoinEnums.SIDE_BUY.getCode());
- tradeRequestParam.setClOrdId(WsParamBuild.getOrderNum(tradeRequestParam.getSide()));
- tradeRequestParam.setSz(String.valueOf(stringBigDecimalMap.get("pos")));
- tradeRequestParamList.add(tradeRequestParam);
- }
}
}
- return tradeRequestParamList;
} catch (Exception e) {
log.error("处理持仓频道推送数据失败", e);
}
- return null;
}
private static Map<String, BigDecimal> initParam(JSONObject posData, String accountName,String posSide) {
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/TradeOrderWs.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/TradeOrderWs.java
index 5586822..36b99e1 100644
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/TradeOrderWs.java
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/TradeOrderWs.java
@@ -130,91 +130,12 @@
}
}
- public static void orderZhiYingEvent(WebSocketClient webSocketClient, TradeRequestParam tradeRequestParam) {
+ public static void orderZhiYingEvent(WebSocketClient webSocketClient, List<TradeRequestParam> tradeRequestParams) {
- log.info("开始执行TradeOrderWs......");
- if (tradeRequestParam == null){
-
- log.warn("下单{}参数缺失,取消发送",tradeRequestParam);
- return;
- }
- String accountName = tradeRequestParam.getAccountName();
- String markPx = tradeRequestParam.getMarkPx();
- String instId = tradeRequestParam.getInstId();
- String tdMode = tradeRequestParam.getTdMode();
- String posSide = tradeRequestParam.getPosSide();
- String ordType = tradeRequestParam.getOrdType();
-
- String tradeType = tradeRequestParam.getTradeType();
-
- String clOrdId = tradeRequestParam.getClOrdId();
- String side = tradeRequestParam.getSide();
- String sz = tradeRequestParam.getSz();
- /**
- * 校验必要参数
- * 验证下单参数是否存在空值
- */
- if (
- StrUtil.isBlank(accountName)
- || StrUtil.isBlank(instId)
- || StrUtil.isBlank(tdMode)
- || StrUtil.isBlank(posSide)
- || StrUtil.isBlank(ordType)
- || StrUtil.isBlank(clOrdId)
- || StrUtil.isBlank(side)
- || StrUtil.isBlank(sz)
- || StrUtil.isBlank(markPx)
-
- ){
- log.warn("下单参数缺失,取消发送");
- return;
- }
- log.info("账户:{},触发价格:{},币种:{},方向:{},买卖:{},数量:{},是否允许下单:{},编号:{},",
- accountName, markPx, instId, posSide,side, sz, tradeType, clOrdId);
- //验证是否允许下单
- if (StrUtil.isNotEmpty(tradeType) && OrderParamEnums.TRADE_NO.getValue().equals(tradeType)) {
- log.warn("账户{}不允许下单,取消发送", accountName);
- return;
- }
-
- /**
- * 检验账户和仓位是否准备就绪
- * 开多:买入开多(side 填写 buy; posSide 填写 long )
- * 开空:卖出开空(side 填写 sell; posSide 填写 short ) 需要检验账户通道是否准备就绪
- * 平多:卖出平多(side 填写 sell;posSide 填写 long )
- * 平空:买入平空(side 填写 buy; posSide 填写 short ) 需要检验仓位通道是否准备就绪
- */
-
- try {
- JSONArray argsArray = new JSONArray();
- JSONObject args = new JSONObject();
- args.put("instId", instId);
- args.put("tdMode", tdMode);
- args.put("clOrdId", clOrdId);
- args.put("side", side);
-
- args.put("posSide", posSide);
- args.put("ordType", ordType);
- args.put("sz", sz);
- args.put("px", markPx);
- argsArray.add(args);
-
- String connId = WsParamBuild.getOrderNum(ORDERWS_CHANNEL);
- JSONObject jsonObject = WsParamBuild.buildJsonObject(connId, ORDERWS_CHANNEL, argsArray);
- webSocketClient.send(jsonObject.toJSONString());
- log.info("发送下单频道:{},数量:{}", side, sz);
-
- } catch (Exception e) {
- log.error("下单构建失败", e);
- }
- }
-
- public static void orderZhiSunEvent(WebSocketClient webSocketClient, List<TradeRequestParam> tradeRequestParams) {
-
- log.info("开始止损......");
- if (CollUtil.isEmpty(tradeRequestParams)){
- log.error("止损下单{}参数缺失,取消发送", JSONUtil.parse(tradeRequestParams));
+ log.info("开始执行限价{}......",JSONUtil.parse(tradeRequestParams));
+ if (tradeRequestParams == null){
+ log.warn("限价下单参数缺失,取消发送");
return;
}
for (TradeRequestParam tradeRequestParam : tradeRequestParams){
@@ -246,16 +167,17 @@
|| StrUtil.isBlank(markPx)
){
- log.error("止损下单参数缺失,取消发送");
+ log.warn("下单参数缺失,取消发送");
return;
}
- log.info("止损账户:{},触发价格:{},币种:{},方向:{},买卖:{},数量:{},是否允许下单:{},编号:{},",
+ log.info("账户:{},触发价格:{},币种:{},方向:{},买卖:{},数量:{},是否允许下单:{},编号:{},",
accountName, markPx, instId, posSide,side, sz, tradeType, clOrdId);
//验证是否允许下单
if (StrUtil.isNotEmpty(tradeType) && OrderParamEnums.TRADE_NO.getValue().equals(tradeType)) {
- log.error("止损账户{}不允许下单,取消发送", accountName);
+ log.warn("账户{}不允许下单,取消发送", accountName);
return;
}
+
/**
* 检验账户和仓位是否准备就绪
* 开多:买入开多(side 填写 buy; posSide 填写 long )
@@ -263,6 +185,7 @@
* 平多:卖出平多(side 填写 sell;posSide 填写 long )
* 平空:买入平空(side 填写 buy; posSide 填写 short ) 需要检验仓位通道是否准备就绪
*/
+
try {
JSONArray argsArray = new JSONArray();
JSONObject args = new JSONObject();
@@ -280,13 +203,13 @@
String connId = WsParamBuild.getOrderNum(ORDERWS_CHANNEL);
JSONObject jsonObject = WsParamBuild.buildJsonObject(connId, ORDERWS_CHANNEL, argsArray);
webSocketClient.send(jsonObject.toJSONString());
- log.info("止损已发送......");
+ log.info("发送下单频道:{},数量:{}", side, sz);
+
} catch (Exception e) {
log.error("下单构建失败", e);
}
}
}
-
/**
--
Gitblit v1.9.1