From 2e6cbd3ee1c2d7e1aa73f4392c79bfbfacbcb67d Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Mon, 15 Dec 2025 14:03:36 +0800
Subject: [PATCH] feat(okx): 支持多账号WebSocket连接管理
---
src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxWebSocketClientManager.java | 101 +++++++++++
src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxQuantWebSocketClient.java | 70 ++++---
src/main/java/com/xcong/excoin/modules/okxNewPrice/celue/CaoZuoServiceImpl.java | 60 +++---
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/PositionsWs.java | 47 +++--
src/main/java/com/xcong/excoin/modules/okxNewPrice/celue/CaoZuoService.java | 2
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/LoginWs.java | 8
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/OrderInfoWs.java | 37 ++-
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/TradeOrderWs.java | 36 ++-
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/AccountWs.java | 40 ++-
src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxWebSocketClientMain.java | 15 +
src/main/java/com/xcong/excoin/modules/okxNewPrice/zhanghu/ApiMessageServiceImpl.java | 11
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/enums/ExchangeInfoEnum.java | 26 +-
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/InstrumentsWs.java | 33 ++-
13 files changed, 318 insertions(+), 168 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 70069ef..b99d26c 100644
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxQuantWebSocketClient.java
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxQuantWebSocketClient.java
@@ -30,15 +30,11 @@
* @author Administrator
*/
@Slf4j
-@Component
-@ConditionalOnProperty(prefix = "app", name = "quant", havingValue = "true")
public class OkxQuantWebSocketClient {
- @Autowired
- private WangGeService wangGeService;
- @Autowired
- private CaoZuoService caoZuoService;
- @Autowired
- private RedisUtils redisUtils;
+ private final WangGeService wangGeService;
+ private final CaoZuoService caoZuoService;
+ private final RedisUtils redisUtils;
+ private final ExchangeInfoEnum account;
private WebSocketClient webSocketClient;
private ScheduledExecutorService heartbeatExecutor;
@@ -48,6 +44,14 @@
// 连接状态标志
private final AtomicBoolean isConnected = new AtomicBoolean(false);
private final AtomicBoolean isConnecting = new AtomicBoolean(false);
+
+ public OkxQuantWebSocketClient(ExchangeInfoEnum account, WangGeService wangGeService,
+ CaoZuoService caoZuoService, RedisUtils redisUtils) {
+ this.account = account;
+ this.wangGeService = wangGeService;
+ this.caoZuoService = caoZuoService;
+ this.redisUtils = redisUtils;
+ }
private static final String WS_URL_MONIPAN = "wss://wspap.okx.com:8443/ws/v5/private";
private static final String WS_URL_SHIPAN = "wss://ws.okx.com:8443/ws/v5/private";
@@ -169,12 +173,12 @@
}
try {
- InstrumentsWs.handleEvent();
+ InstrumentsWs.handleEvent(account.name());
wangGeService.initWangGe();
SSLConfig.configureSSL();
System.setProperty("https.protocols", "TLSv1.2,TLSv1.3");
String WS_URL = WS_URL_MONIPAN;
- if (ExchangeInfoEnum.OKX_UAT.isAccountType()){
+ if (account.isAccountType()){
WS_URL = WS_URL_SHIPAN;
}
URI uri = new URI(WS_URL);
@@ -199,7 +203,7 @@
// 棜查应用是否正在关闭
if (!sharedExecutor.isShutdown()) {
resetHeartbeatTimer();
- websocketLogin();
+ websocketLogin(account);
} else {
log.warn("应用正在关闭,忽略WebSocket连接成功回调");
}
@@ -249,8 +253,8 @@
}
}
- private void websocketLogin() {
- LoginWs.websocketLogin(webSocketClient);
+ private void websocketLogin(ExchangeInfoEnum account) {
+ LoginWs.websocketLogin(webSocketClient, account);
}
private void subscribeBalanceAndPositionChannel(String option) {
@@ -278,7 +282,7 @@
private void handleWebSocketMessage(String message) {
try {
if ("pong".equals(message)) {
- log.debug("收到心跳响应");
+ log.debug("{}: 收到心跳响应", account.name());
cancelPongTimeout();
return;
}
@@ -289,26 +293,26 @@
String code = response.getString("code");
if ("0".equals(code)) {
String connId = response.getString("connId");
- log.info("WebSocket登录成功, connId: {}", connId);
+ log.info("{}: WebSocket登录成功, connId: {}", account.name(), connId);
subscribeAccountChannel(SUBSCRIBE);
subscribeOrderInfoChannel(SUBSCRIBE);
subscribePositionChannel(SUBSCRIBE);
} else {
- log.error("WebSocket登录失败, code: {}, msg: {}", code, response.getString("msg"));
+ log.error("{}: WebSocket登录失败, code: {}, msg: {}", account.name(), code, response.getString("msg"));
}
} else if ("subscribe".equals(event)) {
subscribeEvent(response);
} else if ("error".equals(event)) {
- log.error("订阅错误: code={}, msg={}",
- response.getString("code"), response.getString("msg"));
+ log.error("{}: 订阅错误: code={}, msg={}",
+ account.name(), response.getString("code"), response.getString("msg"));
} else if ("channel-conn-count".equals(event)) {
- log.info("连接限制更新: channel={}, connCount={}",
- response.getString("channel"), response.getString("connCount"));
+ log.info("{}: 连接限制更新: channel={}, connCount={}",
+ account.name(), response.getString("channel"), response.getString("connCount"));
} else {
processPushData(response);
}
} catch (Exception e) {
- log.error("处理WebSocket消息失败: {}", message, e);
+ log.error("{}: 处理WebSocket消息失败: {}", account.name(), message, e);
}
}
@@ -325,13 +329,13 @@
return;
}
if (OrderInfoWs.ORDERINFOWS_CHANNEL.equals(channel)) {
- OrderInfoWs.initEvent(response);
+ OrderInfoWs.initEvent(response, account.name());
}
if (AccountWs.ACCOUNTWS_CHANNEL.equals(channel)) {
- AccountWs.initEvent(response);
+ AccountWs.initEvent(response, account.name());
}
if (PositionsWs.POSITIONSWS_CHANNEL.equals(channel)) {
- PositionsWs.initEvent(response);
+ PositionsWs.initEvent(response, account.name());
}
}
@@ -347,30 +351,32 @@
if (TradeOrderWs.ORDERWS_CHANNEL.equals(op)) {
// 直接使用Object类型接收,避免强制类型转换
Object data = response.get("data");
- log.info("收到下单推送结果: {}", JSON.toJSONString(data));
+ log.info("{}: 收到下单推送结果: {}", account.name(), JSON.toJSONString(data));
return;
}
}
JSONObject arg = response.getJSONObject("arg");
if (arg == null) {
- log.warn("无效的推送数据,缺少 'arg' 字段 :{}",response);
+ log.warn("{}: 无效的推送数据,缺少 'arg' 字段 :{}", account.name(), response);
return;
}
String channel = arg.getString("channel");
if (channel == null) {
- log.warn("无效的推送数据,缺少 'channel' 字段{}",response);
+ log.warn("{}: 无效的推送数据,缺少 'channel' 字段{}", account.name(), response);
return;
}
+ // 注意:当前实现中,OrderInfoWs等类使用静态Map存储数据
+ // 这会导致多账号之间的数据冲突。需要进一步修改这些类的设计,让数据存储与特定账号关联
if (OrderInfoWs.ORDERINFOWS_CHANNEL.equals(channel)) {
- OrderInfoWs.handleEvent(response, redisUtils);
+ OrderInfoWs.handleEvent(response, redisUtils, account.name());
}else if (AccountWs.ACCOUNTWS_CHANNEL.equals(channel)) {
- AccountWs.handleEvent(response);
- String side = caoZuoService.caoZuo();
- TradeOrderWs.orderEvent(webSocketClient, side);
+ AccountWs.handleEvent(response, account.name());
+ String side = caoZuoService.caoZuo(account.name());
+ TradeOrderWs.orderEvent(webSocketClient, side, account.name());
} else if (PositionsWs.POSITIONSWS_CHANNEL.equals(channel)) {
- PositionsWs.handleEvent(response);
+ 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/OkxWebSocketClientMain.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxWebSocketClientMain.java
index 063e02b..9eebd2d 100644
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxWebSocketClientMain.java
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxWebSocketClientMain.java
@@ -1,16 +1,21 @@
package com.xcong.excoin.modules.okxNewPrice;
+import com.xcong.excoin.modules.okxNewPrice.celue.CaoZuoServiceImpl;
+import com.xcong.excoin.modules.okxNewPrice.wangge.WangGeServiceImpl;
+import com.xcong.excoin.utils.RedisUtils;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
public class OkxWebSocketClientMain {
public static void main(String[] args) throws InterruptedException {
- OkxQuantWebSocketClient clientV2 = new OkxQuantWebSocketClient();
- // 手动调用初始化方法
- clientV2.init();
+ // 使用Spring上下文初始化管理器
+ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
+ OkxWebSocketClientManager manager = context.getBean(OkxWebSocketClientManager.class);
// 运行一段时间以观察结果
Thread.sleep(1200000000L); // 运行一小时
// 关闭连接
- clientV2.destroy();
-
+ manager.destroy();
}
}
\ No newline at end of file
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxWebSocketClientManager.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxWebSocketClientManager.java
new file mode 100644
index 0000000..d8981d0
--- /dev/null
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxWebSocketClientManager.java
@@ -0,0 +1,101 @@
+package com.xcong.excoin.modules.okxNewPrice;
+
+import com.xcong.excoin.modules.okxNewPrice.celue.CaoZuoService;
+import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.ExchangeInfoEnum;
+import com.xcong.excoin.modules.okxNewPrice.wangge.WangGeService;
+import com.xcong.excoin.utils.RedisUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 管理多个OKX WebSocket客户端实例,每个实例对应一个账号
+ */
+@Slf4j
+@Component
+@ConditionalOnProperty(prefix = "app", name = "quant", havingValue = "true")
+public class OkxWebSocketClientManager {
+ @Autowired
+ private WangGeService wangGeService;
+ @Autowired
+ private CaoZuoService caoZuoService;
+ @Autowired
+ private RedisUtils redisUtils;
+
+ // 存储所有WebSocket客户端实例,key为账号类型名称
+ private final Map<String, OkxQuantWebSocketClient> clientMap = new ConcurrentHashMap<>();
+
+ /**
+ * 初始化方法,在Spring Bean构造完成后执行
+ * 创建并初始化所有账号的WebSocket客户端实例
+ */
+ @PostConstruct
+ public void init() {
+ log.info("开始初始化OkxWebSocketClientManager");
+
+ // 获取所有ExchangeInfoEnum枚举值
+ ExchangeInfoEnum[] accounts = ExchangeInfoEnum.values();
+
+ // 为每个账号创建一个WebSocket客户端实例
+ for (ExchangeInfoEnum account : accounts) {
+ try {
+ OkxQuantWebSocketClient client = new OkxQuantWebSocketClient(account, wangGeService, caoZuoService, redisUtils);
+ clientMap.put(account.name(), client);
+ client.init();
+ log.info("已初始化账号 {} 的WebSocket客户端", account.name());
+ } catch (Exception e) {
+ log.error("初始化账号 {} 的WebSocket客户端失败", account.name(), e);
+ }
+ }
+
+ log.info("OkxWebSocketClientManager初始化完成");
+ }
+
+ /**
+ * 销毁方法,在Spring Bean销毁前执行
+ * 关闭所有WebSocket客户端连接和相关资源
+ */
+ @PreDestroy
+ public void destroy() {
+ log.info("开始销毁OkxWebSocketClientManager");
+
+ // 关闭所有客户端实例
+ for (Map.Entry<String, OkxQuantWebSocketClient> entry : clientMap.entrySet()) {
+ try {
+ OkxQuantWebSocketClient client = entry.getValue();
+ client.destroy();
+ log.info("已销毁账号 {} 的WebSocket客户端", entry.getKey());
+ } catch (Exception e) {
+ log.error("销毁账号 {} 的WebSocket客户端失败", entry.getKey(), e);
+ }
+ }
+
+ // 清空客户端映射
+ clientMap.clear();
+
+ log.info("OkxWebSocketClientManager销毁完成");
+ }
+
+ /**
+ * 获取指定账号的WebSocket客户端实例
+ * @param accountName 账号类型名称
+ * @return WebSocket客户端实例
+ */
+ public OkxQuantWebSocketClient getClient(String accountName) {
+ return clientMap.get(accountName);
+ }
+
+ /**
+ * 获取所有WebSocket客户端实例
+ * @return 所有客户端实例的集合
+ */
+ public Collection<OkxQuantWebSocketClient> getAllClients() {
+ return clientMap.values();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/celue/CaoZuoService.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/celue/CaoZuoService.java
index 9c83d0f..2a42f06 100644
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/celue/CaoZuoService.java
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/celue/CaoZuoService.java
@@ -5,5 +5,5 @@
*/
public interface CaoZuoService {
- String caoZuo();
+ String caoZuo(String accountName);
}
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 320423e..e7363bd 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
@@ -39,26 +39,26 @@
* @return 返回操作类型字符串(如买入BUY、卖出SELL等),如果无有效操作则返回null
*/
@Override
- public String caoZuo() {
+ public String caoZuo(String accountName) {
log.info("开始执行操作CaoZuoServiceImpl......");
- String accountReadyState = AccountWs.ACCOUNTWSMAP.get(CoinEnums.READY_STATE.name());
+ String accountReadyState = AccountWs.getAccountMap(accountName).get(CoinEnums.READY_STATE.name());
if (!CoinEnums.READY_STATE_YES.getCode().equals(accountReadyState)) {
log.info("账户通道未就绪,取消发送");
return null;
}
- BigDecimal positionsReadyState = PositionsWs.POSITIONSWSMAP.get(CoinEnums.READY_STATE.name()) == null
- ? BigDecimal.ZERO : PositionsWs.POSITIONSWSMAP.get(CoinEnums.READY_STATE.name());
+ BigDecimal positionsReadyState = PositionsWs.getAccountMap(accountName).get(CoinEnums.READY_STATE.name()) == null
+ ? BigDecimal.ZERO : PositionsWs.getAccountMap(accountName).get(CoinEnums.READY_STATE.name());
if (WsMapBuild.parseBigDecimalSafe(CoinEnums.READY_STATE_YES.getCode()).compareTo(positionsReadyState) != 0) {
log.info("仓位通道未就绪,取消发送");
return null;
}
// 系统设置的开关,等于冷静中,则代表不开仓
- String outStr = InstrumentsWs.INSTRUMENTSWSMAP.get(CoinEnums.OUT.name());
+ String outStr = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.OUT.name());
if (OrderParamEnums.OUT_YES.getValue().equals(outStr)){
log.error("冷静中,不允许下单......");
return null;
}
- BigDecimal cashBal = WsMapBuild.parseBigDecimalSafe(AccountWs.ACCOUNTWSMAP.get("cashBal"));
+ BigDecimal cashBal = WsMapBuild.parseBigDecimalSafe(AccountWs.getAccountMap(accountName).get("cashBal"));
// 判断账户余额是否充足
if (cashBal.compareTo(BigDecimal.ZERO) <= 0){
@@ -69,12 +69,12 @@
* 判断止损抗压
*/
// 实际亏损金额
- BigDecimal realKuiSunAmount = WsMapBuild.parseBigDecimalSafe(AccountWs.ACCOUNTWSMAP.get("upl"));
+ BigDecimal realKuiSunAmount = WsMapBuild.parseBigDecimalSafe(AccountWs.getAccountMap(accountName).get("upl"));
log.info("未实现盈亏: {}", realKuiSunAmount);
- String zhiSunPercent = InstrumentsWs.INSTRUMENTSWSMAP.get(CoinEnums.ZHI_SUN.name());
+ String zhiSunPercent = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.ZHI_SUN.name());
BigDecimal zhiSunAmount = cashBal.multiply(new BigDecimal(zhiSunPercent));
log.info("预期亏损金额: {}", zhiSunAmount);
- String kangYaPercent = InstrumentsWs.INSTRUMENTSWSMAP.get(CoinEnums.KANG_CANG.name());
+ String kangYaPercent = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.KANG_CANG.name());
BigDecimal kangYaAmount = cashBal.multiply(new BigDecimal(kangYaPercent));
log.info("预期抗仓金额: {}", kangYaAmount);
@@ -83,7 +83,7 @@
// 账户预期亏损金额比这个还小时,立即止损
if (realKuiSunAmount.compareTo(zhiSunAmount) > 0){
log.error("账户冷静止损......");
- WsMapBuild.saveStringToMap(InstrumentsWs.INSTRUMENTSWSMAP, CoinEnums.OUT.name(), OrderParamEnums.OUT_YES.getValue());
+ WsMapBuild.saveStringToMap(InstrumentsWs.getAccountMap(accountName), CoinEnums.OUT.name(), OrderParamEnums.OUT_YES.getValue());
return OrderParamEnums.OUT.getValue();
}
// 判断抗压
@@ -93,22 +93,22 @@
}
}
- if (PositionsWs.POSITIONSWSMAP.get("pos") == null){
+ if (PositionsWs.getAccountMap(accountName).get("pos") == null){
log.error("没有获取到持仓信息,等待初始化......");
return null;
}
- BigDecimal pos = PositionsWs.POSITIONSWSMAP.get("pos");
+ BigDecimal pos = PositionsWs.getAccountMap(accountName).get("pos");
if (BigDecimal.ZERO.compareTo( pos) >= 0) {
log.error("持仓数量为零,进行初始化订单");
return OrderParamEnums.INIT.getValue();
}
// 判断是否保证金超标
- if (PositionsWs.POSITIONSWSMAP.get("imr") == null){
+ if (PositionsWs.getAccountMap(accountName).get("imr") == null){
log.error("没有获取到持仓信息,等待初始化......");
return null;
}
- BigDecimal ordFrozImr = PositionsWs.POSITIONSWSMAP.get("imr");
- BigDecimal totalOrderUsdt = WsMapBuild.parseBigDecimalSafe(AccountWs.ACCOUNTWSMAP.get(CoinEnums.TOTAL_ORDER_USDT.name()));
+ BigDecimal ordFrozImr = PositionsWs.getAccountMap(accountName).get("imr");
+ BigDecimal totalOrderUsdt = WsMapBuild.parseBigDecimalSafe(AccountWs.getAccountMap(accountName).get(CoinEnums.TOTAL_ORDER_USDT.name()));
if (ordFrozImr.compareTo(totalOrderUsdt) >= 0){
log.error("已满仓......");
return OrderParamEnums.HOLDING.getValue();
@@ -116,8 +116,8 @@
try {
// 获取标记价格和平均持仓价格
- BigDecimal markPx = PositionsWs.POSITIONSWSMAP.get("markPx");
- BigDecimal avgPx = PositionsWs.POSITIONSWSMAP.get("avgPx");
+ BigDecimal markPx = PositionsWs.getAccountMap(accountName).get("markPx");
+ BigDecimal avgPx = PositionsWs.getAccountMap(accountName).get("avgPx");
log.info("开仓价格: {}, 当前价格:{},匹配队列中......", avgPx, markPx);
// 初始化网格队列
@@ -126,7 +126,7 @@
PriorityBlockingQueue<AscBigDecimal> queuePingCang = wangGeService.initPingCang(avgPx, queueAsc);
// 处理订单价格在队列中的情况
- String orderPrice = OrderInfoWs.ORDERINFOWSMAP.get("orderPrice");
+ String orderPrice = OrderInfoWs.getAccountMap(accountName).get("orderPrice");
handleOrderPriceInQueues(orderPrice, queueKaiCang, queuePingCang);
// 判断是加仓还是减仓
if (avgPx.compareTo(markPx) > 0) {
@@ -139,8 +139,8 @@
DescBigDecimal kaiCang = queueKaiCang.peek();
if (kaiCang != null && markPx.compareTo(kaiCang.getValue()) <= 0 && avgPx.compareTo(kaiCang.getValue()) >= 0) {
log.info("开始加仓...开仓队列价格大于当前价格{}>{}", kaiCang.getValue(), markPx);
- WsMapBuild.saveStringToMap(OrderInfoWs.ORDERINFOWSMAP, "orderPrice", String.valueOf(markPx));
- boolean buyCntTimeFlag = buyCntTimeEvent(avgPx, markPx);
+ WsMapBuild.saveStringToMap(OrderInfoWs.getAccountMap(accountName), "orderPrice", String.valueOf(markPx));
+ boolean buyCntTimeFlag = buyCntTimeEvent(accountName, avgPx, markPx);
if (buyCntTimeFlag){
log.info("加仓参数准备成功......");
return OrderParamEnums.BUY.getValue();
@@ -163,23 +163,23 @@
if (pingCang != null && markPx.compareTo(pingCang.getValue()) >= 0 && avgPx.compareTo(pingCang.getValue()) < 0) {
log.info("开始减仓...平仓队列价格小于当前价格{}<={}", pingCang.getValue(), markPx);
// 手续费
- BigDecimal feeValue = PositionsWs.POSITIONSWSMAP.get("fee").multiply(new BigDecimal("2"));
+ BigDecimal feeValue = PositionsWs.getAccountMap(accountName).get("fee").multiply(new BigDecimal("2"));
//未实现收益
- BigDecimal uplValue = PositionsWs.POSITIONSWSMAP.get("upl");
+ BigDecimal uplValue = PositionsWs.getAccountMap(accountName).get("upl");
//已实现收益
- BigDecimal realizedPnlValue = PositionsWs.POSITIONSWSMAP.get("realizedPnl");
+ BigDecimal realizedPnlValue = PositionsWs.getAccountMap(accountName).get("realizedPnl");
realizedPnlValue = realizedPnlValue.add(feeValue);
//持仓保证金
- BigDecimal imr = PositionsWs.POSITIONSWSMAP.get("imr");
- String pingCangImr = InstrumentsWs.INSTRUMENTSWSMAP.get(CoinEnums.PING_CANG_SHOUYI.name());
+ BigDecimal imr = PositionsWs.getAccountMap(accountName).get("imr");
+ String pingCangImr = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.PING_CANG_SHOUYI.name());
BigDecimal imrValue = imr.multiply(new BigDecimal(pingCangImr));
if (realizedPnlValue.compareTo(BigDecimal.ZERO) <= 0) {
BigDecimal realizedPnlValueZheng = realizedPnlValue.multiply(new BigDecimal("-1"));
if (uplValue.compareTo(realizedPnlValue) > 0 && uplValue.compareTo(imrValue.add(realizedPnlValueZheng)) >= 0) {
log.info("当前未实现盈亏:{}大于预计收益>{},赚钱咯", uplValue, imrValue.add(realizedPnlValueZheng));
- WsMapBuild.saveStringToMap(OrderInfoWs.ORDERINFOWSMAP, "orderPrice", String.valueOf(markPx));
+ WsMapBuild.saveStringToMap(OrderInfoWs.getAccountMap(accountName), "orderPrice", String.valueOf(markPx));
return OrderParamEnums.SELL.getValue();
}else{
log.info("当前未实现盈亏:{}没有大于预计收益>{},钱在路上了", uplValue, imrValue.add(realizedPnlValueZheng));
@@ -187,7 +187,7 @@
}
}else {
if (uplValue.compareTo(imrValue.add(feeValue)) >= 0) {
- WsMapBuild.saveStringToMap(OrderInfoWs.ORDERINFOWSMAP, "orderPrice", String.valueOf(markPx));
+ WsMapBuild.saveStringToMap(OrderInfoWs.getAccountMap(accountName), "orderPrice", String.valueOf(markPx));
log.info("当前未实现盈亏:{}大于预计收益>{},赚钱咯", uplValue, imrValue.add(feeValue));
return OrderParamEnums.SELL.getValue();
}else{
@@ -208,16 +208,16 @@
}
}
- private boolean buyCntTimeEvent(BigDecimal avgPx, BigDecimal markPx){
+ private boolean buyCntTimeEvent(String accountName, BigDecimal avgPx, BigDecimal markPx){
//判断当前价格和开仓价格直接间隔除以间距,取整,获取的数量是否大于等于0,如果大于0,则下单基础张数*倍数
- String buyCntTime = InstrumentsWs.INSTRUMENTSWSMAP.get(CoinEnums.BUY_CNT_TIME.name());
+ String buyCntTime = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.BUY_CNT_TIME.name());
BigDecimal subtract = avgPx.subtract(markPx);
BigDecimal divide = subtract.divide(new BigDecimal(buyCntTime), 0, RoundingMode.DOWN);
if (divide.compareTo(BigDecimal.ZERO) <= 0){
log.warn("加仓次数间隔时间小于0,不加仓");
return false;
}
- return WsMapBuild.saveStringToMap(TradeOrderWs.TRADEORDERWSMAP, "buyCntTime",String.valueOf(divide));
+ return WsMapBuild.saveStringToMap(TradeOrderWs.getAccountMap(accountName), "buyCntTime",String.valueOf(divide));
}
/**
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/AccountWs.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/AccountWs.java
index 401ad3b..bca358b 100644
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/AccountWs.java
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/AccountWs.java
@@ -26,7 +26,13 @@
@Slf4j
public class AccountWs {
- public static final Map<String,String> ACCOUNTWSMAP = new ConcurrentHashMap<>();
+ // 使用双层Map,第一层key为账号名称,第二层key为数据key
+ private static final Map<String, Map<String, String>> ACCOUNTWSMAP = new ConcurrentHashMap<>();
+
+ // 获取指定账号的Map,如果不存在则创建
+ public static Map<String, String> getAccountMap(String accountName) {
+ return ACCOUNTWSMAP.computeIfAbsent(accountName, k -> new ConcurrentHashMap<>());
+ }
/**
* 账户频道名称常量
*/
@@ -55,18 +61,19 @@
}
}
- public static void initEvent(JSONObject response) {
+ public static void initEvent(JSONObject response, String accountName) {
// log.info("订阅成功: {}", response.getJSONObject("arg"));
JSONObject arg = response.getJSONObject("arg");
- initParam(arg);
+ initParam(arg, accountName);
}
/**
* 处理账户频道推送的数据
*
* @param response 推送的 JSON 数据对象
+ * @param accountName 账号名称
*/
- public static void handleEvent(JSONObject response) {
+ public static void handleEvent(JSONObject response, String accountName) {
// log.info("开始执行AccountWs......{}",ACCOUNTWS_CHANNEL);
@@ -88,7 +95,7 @@
for (int j = 0; j < detailsArray.size(); j++) {
JSONObject detail = detailsArray.getJSONObject(j);
- initParam(detail);
+ initParam(detail, accountName);
}
} catch (Exception innerEx) {
log.warn("处理账户频道数据失败", innerEx);
@@ -105,35 +112,36 @@
public static final String cashBalKey = "cashBal";
public static final String eqKey = "eq";
public static final String uplKey = "upl";
- private static void initParam(JSONObject detail) {
+ private static void initParam(JSONObject detail, String accountName) {
+ Map<String, String> accountMap = getAccountMap(accountName);
String ccy = WsMapBuild.parseStringSafe( detail.getString(ccyKey));
- WsMapBuild.saveStringToMap(ACCOUNTWSMAP, ccyKey, ccy);
+ WsMapBuild.saveStringToMap(accountMap, ccyKey, ccy);
String availBal = WsMapBuild.parseStringSafe(detail.getString(availBalKey));
- WsMapBuild.saveStringToMap(ACCOUNTWSMAP, availBalKey, availBal);
+ WsMapBuild.saveStringToMap(accountMap, availBalKey, availBal);
String cashBal = WsMapBuild.parseStringSafe(detail.getString(cashBalKey));
- WsMapBuild.saveStringToMap(ACCOUNTWSMAP, cashBalKey, cashBal);
+ WsMapBuild.saveStringToMap(accountMap, cashBalKey, cashBal);
String eq = WsMapBuild.parseStringSafe(detail.getString(eqKey));
- WsMapBuild.saveStringToMap(ACCOUNTWSMAP, eqKey, eq);
+ WsMapBuild.saveStringToMap(accountMap, eqKey, eq);
String upl = WsMapBuild.parseStringSafe(detail.getString(uplKey));
- WsMapBuild.saveStringToMap(ACCOUNTWSMAP, uplKey, upl);
+ WsMapBuild.saveStringToMap(accountMap, uplKey, upl);
BigDecimal cashBalDecimal = WsMapBuild.parseBigDecimalSafe(cashBal);
// 根据可用余额计算下单总保证金
- String total_order_usdtpecent = InstrumentsWs.INSTRUMENTSWSMAP.get(CoinEnums.TOTAL_ORDER_USDTPECENT.name());
+ String total_order_usdtpecent = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.TOTAL_ORDER_USDTPECENT.name());
BigDecimal total_order_usdt_factor = WsMapBuild.parseBigDecimalSafe(total_order_usdtpecent);
BigDecimal totalOrderUsdt = cashBalDecimal.multiply(total_order_usdt_factor).setScale(2, RoundingMode.DOWN);
- WsMapBuild.saveStringToMap(ACCOUNTWSMAP, CoinEnums.TOTAL_ORDER_USDT.name(), String.valueOf(totalOrderUsdt));
+ WsMapBuild.saveStringToMap(accountMap, CoinEnums.TOTAL_ORDER_USDT.name(), String.valueOf(totalOrderUsdt));
- WsMapBuild.saveStringToMap(ACCOUNTWSMAP, CoinEnums.READY_STATE.name(), CoinEnums.READY_STATE_YES.getCode());
+ WsMapBuild.saveStringToMap(accountMap, CoinEnums.READY_STATE.name(), CoinEnums.READY_STATE_YES.getCode());
log.info(
- "账户详情-币种: {}, 可用余额: {}, 现金余额: {}, 余额: {}, 全仓未实现盈亏: {}, 下单总保证金: {}",
- ccy, availBal, cashBal, eq, upl, totalOrderUsdt
+ "{}: 账户详情-币种: {}, 可用余额: {}, 现金余额: {}, 余额: {}, 全仓未实现盈亏: {}, 下单总保证金: {}",
+ accountName, ccy, availBal, cashBal, eq, upl, totalOrderUsdt
);
}
}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/InstrumentsWs.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/InstrumentsWs.java
index fd784d5..04f1f11 100644
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/InstrumentsWs.java
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/InstrumentsWs.java
@@ -14,24 +14,29 @@
@Slf4j
public class InstrumentsWs {
- public static final Map<String, String> INSTRUMENTSWSMAP = new ConcurrentHashMap<>();
+ public static final Map<String, Map<String, String>> INSTRUMENTSWSMAP = new ConcurrentHashMap<>();
public static final String INSTRUMENTSWS_CHANNEL = "instruments";
- public static void handleEvent() {
+ public static Map<String, String> getAccountMap(String accountName) {
+ return INSTRUMENTSWSMAP.computeIfAbsent(accountName, k -> new ConcurrentHashMap<>());
+ }
+
+ public static void handleEvent(String accountName) {
// log.info("开始执行InstrumentsWs......");
- WsMapBuild.saveStringToMap(INSTRUMENTSWSMAP, CoinEnums.HE_YUE.name(), CoinEnums.HE_YUE.getCode());
- WsMapBuild.saveStringToMap(INSTRUMENTSWSMAP, CoinEnums.CTVAL.name(), CoinEnums.CTVAL.getCode());
- WsMapBuild.saveStringToMap(INSTRUMENTSWSMAP, CoinEnums.TICKSZ.name(), CoinEnums.TICKSZ.getCode());
- WsMapBuild.saveStringToMap(INSTRUMENTSWSMAP, CoinEnums.MINSZ.name(), CoinEnums.MINSZ.getCode());
- WsMapBuild.saveStringToMap(INSTRUMENTSWSMAP, CoinEnums.OUT.name(), OrderParamEnums.OUT_NO.getValue());
- WsMapBuild.saveStringToMap(INSTRUMENTSWSMAP, CoinEnums.LEVERAGE.name(), CoinEnums.LEVERAGE.getCode());
- WsMapBuild.saveStringToMap(INSTRUMENTSWSMAP, CoinEnums.BUY_CNT.name(), CoinEnums.BUY_CNT.getCode());
- WsMapBuild.saveStringToMap(INSTRUMENTSWSMAP, CoinEnums.BUY_CNT_TIME.name(), CoinEnums.BUY_CNT_TIME.getCode());
- WsMapBuild.saveStringToMap(INSTRUMENTSWSMAP, CoinEnums.ZHI_SUN.name(), CoinEnums.ZHI_SUN.getCode());
- WsMapBuild.saveStringToMap(INSTRUMENTSWSMAP, CoinEnums.KANG_CANG.name(), CoinEnums.KANG_CANG.getCode());
- WsMapBuild.saveStringToMap(INSTRUMENTSWSMAP, CoinEnums.PING_CANG_SHOUYI.name(), CoinEnums.PING_CANG_SHOUYI.getCode());
- WsMapBuild.saveStringToMap(INSTRUMENTSWSMAP, CoinEnums.TOTAL_ORDER_USDTPECENT.name(), CoinEnums.TOTAL_ORDER_USDTPECENT.getCode());
+ Map<String, String> accountMap = getAccountMap(accountName);
+ WsMapBuild.saveStringToMap(accountMap, CoinEnums.HE_YUE.name(), CoinEnums.HE_YUE.getCode());
+ WsMapBuild.saveStringToMap(accountMap, CoinEnums.CTVAL.name(), CoinEnums.CTVAL.getCode());
+ WsMapBuild.saveStringToMap(accountMap, CoinEnums.TICKSZ.name(), CoinEnums.TICKSZ.getCode());
+ WsMapBuild.saveStringToMap(accountMap, CoinEnums.MINSZ.name(), CoinEnums.MINSZ.getCode());
+ WsMapBuild.saveStringToMap(accountMap, CoinEnums.OUT.name(), OrderParamEnums.OUT_NO.getValue());
+ WsMapBuild.saveStringToMap(accountMap, CoinEnums.LEVERAGE.name(), CoinEnums.LEVERAGE.getCode());
+ WsMapBuild.saveStringToMap(accountMap, CoinEnums.BUY_CNT.name(), CoinEnums.BUY_CNT.getCode());
+ WsMapBuild.saveStringToMap(accountMap, CoinEnums.BUY_CNT_TIME.name(), CoinEnums.BUY_CNT_TIME.getCode());
+ WsMapBuild.saveStringToMap(accountMap, CoinEnums.ZHI_SUN.name(), CoinEnums.ZHI_SUN.getCode());
+ WsMapBuild.saveStringToMap(accountMap, CoinEnums.KANG_CANG.name(), CoinEnums.KANG_CANG.getCode());
+ WsMapBuild.saveStringToMap(accountMap, CoinEnums.PING_CANG_SHOUYI.name(), CoinEnums.PING_CANG_SHOUYI.getCode());
+ WsMapBuild.saveStringToMap(accountMap, CoinEnums.TOTAL_ORDER_USDTPECENT.name(), CoinEnums.TOTAL_ORDER_USDTPECENT.getCode());
}
}
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/LoginWs.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/LoginWs.java
index d7face2..eb3e416 100644
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/LoginWs.java
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/LoginWs.java
@@ -13,7 +13,7 @@
*/
@Slf4j
public class LoginWs {
- public static void websocketLogin(WebSocketClient webSocketClient) {
+ public static void websocketLogin(WebSocketClient webSocketClient, ExchangeInfoEnum account) {
// log.info("开始执行LoginWs......");
try {
@@ -21,10 +21,10 @@
JSONArray argsArray = new JSONArray();
JSONObject loginArgs = new JSONObject();
// 获取登录凭证信息(需要从配置或Redis中获取)
- String apiKey = ExchangeInfoEnum.OKX_UAT.getApiKey();
- String passphrase = ExchangeInfoEnum.OKX_UAT.getPassphrase();
+ String apiKey = account.getApiKey();
+ String passphrase = account.getPassphrase();
String timestamp = String.valueOf(System.currentTimeMillis() /1000);
- String sign = SignUtils.signWebsocket(timestamp, ExchangeInfoEnum.OKX_UAT.getSecretKey());
+ String sign = SignUtils.signWebsocket(timestamp, account.getSecretKey());
loginArgs.put("apiKey", apiKey);
loginArgs.put("passphrase", passphrase);
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 5250828..8dc0946 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
@@ -22,7 +22,13 @@
@Slf4j
public class OrderInfoWs {
- public static final Map<String,String> ORDERINFOWSMAP = new ConcurrentHashMap<>();
+ // 使用双层Map,第一层key为账号名称,第二层key为数据key
+ public static final Map<String, Map<String, String>> ORDERINFOWSMAP = new ConcurrentHashMap<>();
+
+ // 获取指定账号的Map,如果不存在则创建
+ public static Map<String, String> getAccountMap(String accountName) {
+ return ORDERINFOWSMAP.computeIfAbsent(accountName, k -> new ConcurrentHashMap<>());
+ }
public static final String ORDERINFOWS_CHANNEL = "orders";
@@ -44,7 +50,7 @@
}
}
- public static void initEvent(JSONObject response) {
+ public static void initEvent(JSONObject response, String accountName) {
// log.info("订阅成功: {}", response.getJSONObject("arg"));
}
@@ -58,7 +64,7 @@
private static final String ACCFILLSZ_KEY = "accFillSz";
private static final String AVGPX_KEY = "avgPx";
private static final String STATE_KEY = "state";
- public static void handleEvent(JSONObject response, RedisUtils redisUtils) {
+ public static void handleEvent(JSONObject response, RedisUtils redisUtils, String accountName) {
// log.info("开始执行OrderInfoWs......");
try {
@@ -85,30 +91,35 @@
String state = detail.getString(STATE_KEY);
log.info(
- "订单详情-币种: {}, 系统编号: {}, 自定义编号: {}, 订单方向: {}, 交易模式: {}," +
+ "{}: 订单详情-币种: {}, 系统编号: {}, 自定义编号: {}, 订单方向: {}, 交易模式: {}," +
" 累计成交数量: {}, 成交均价: {}, 订单状态: {}",
- instId, ordId, clOrdId, side, tdMode,
+ accountName, instId, ordId, clOrdId, side, tdMode,
accFillSz, avgPx,state
);
- String clOrdIdStr = TradeOrderWs.TRADEORDERWSMAP.get("clOrdId");
- String stateStr = TradeOrderWs.TRADEORDERWSMAP.get("state");
+ String clOrdIdStr = TradeOrderWs.getAccountMap(accountName).get("clOrdId");
+ String stateStr = TradeOrderWs.getAccountMap(accountName).get("state");
if (
StrUtil.isNotBlank(clOrdIdStr)
&& clOrdId.equals(clOrdIdStr)
&& StrUtil.isNotBlank(stateStr)
&& state.equals(stateStr)
){
+ Map<String, String> accountMap = getAccountMap(accountName);
//记录成交均价
- if (ORDERINFOWSMAP.get("orderPrice") == null){
- WsMapBuild.saveStringToMap(ORDERINFOWSMAP, "orderPrice",avgPx);
+ if (accountMap.get("orderPrice") == null){
+ WsMapBuild.saveStringToMap(accountMap, "orderPrice",avgPx);
}
- WsMapBuild.saveStringToMap(TradeOrderWs.TRADEORDERWSMAP, "state", CoinEnums.ORDER_LIVE.getCode());
+ WsMapBuild.saveStringToMap(TradeOrderWs.getAccountMap(accountName), "state", CoinEnums.ORDER_LIVE.getCode());
- WsMapBuild.saveBigDecimalToMap(PositionsWs.POSITIONSWSMAP, CoinEnums.READY_STATE.name(), WsMapBuild.parseBigDecimalSafe(CoinEnums.READY_STATE_NO.getCode()));
- WsMapBuild.saveStringToMap(AccountWs.ACCOUNTWSMAP, CoinEnums.READY_STATE.name(), CoinEnums.READY_STATE_NO.getCode());
+ // 使用账号特定的Map
+ Map<String, BigDecimal> positionsMap = PositionsWs.getAccountMap(accountName);
+ WsMapBuild.saveBigDecimalToMap(positionsMap, CoinEnums.READY_STATE.name(), WsMapBuild.parseBigDecimalSafe(CoinEnums.READY_STATE_NO.getCode()));
+
+ Map<String, String> accountWsMap = AccountWs.getAccountMap(accountName);
+ WsMapBuild.saveStringToMap(accountWsMap, CoinEnums.READY_STATE.name(), CoinEnums.READY_STATE_NO.getCode());
- log.info("订单详情已完成: {}, 自定义编号: {}", CoinEnums.HE_YUE.getCode(), clOrdId);
+ log.info("{}: 订单详情已完成: {}, 自定义编号: {}", accountName, CoinEnums.HE_YUE.getCode(), clOrdId);
}
}
} catch (Exception e) {
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 75e230a..3b08fde 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
@@ -19,7 +19,13 @@
@Slf4j
public class PositionsWs {
- public static final Map<String,BigDecimal> POSITIONSWSMAP = new ConcurrentHashMap<>();
+ // 使用双层Map,第一层key为账号名称,第二层key为数据key
+ public static final Map<String, Map<String, BigDecimal>> POSITIONSWSMAP = new ConcurrentHashMap<>();
+
+ // 获取指定账号的Map,如果不存在则创建
+ public static Map<String, BigDecimal> getAccountMap(String accountName) {
+ return POSITIONSWSMAP.computeIfAbsent(accountName, k -> new ConcurrentHashMap<>());
+ }
public static final String POSITIONSWS_CHANNEL = "positions";
@@ -41,13 +47,13 @@
}
}
- public static void initEvent(JSONObject response) {
+ public static void initEvent(JSONObject response, String accountName) {
// log.info("订阅成功,数据初始化: {}", response.getJSONObject("arg"));
JSONObject arg = response.getJSONObject("arg");
- initParam(arg);
+ initParam(arg, accountName);
}
- public static void handleEvent(JSONObject response) {
+ public static void handleEvent(JSONObject response, String accountName) {
// log.info("开始执行PositionsWs......");
@@ -56,7 +62,7 @@
if (dataArray == null || dataArray.isEmpty()) {
// log.info("账户持仓频道数据为空,已当前价买入,并且初始化网格");
JSONObject posData = new JSONObject();
- initParam(posData);
+ initParam(posData, accountName);
return;
}
@@ -87,19 +93,19 @@
String fee = posData.getString("fee");
String fundingFee = posData.getString("fundingFee");
log.info(
- "账户持仓频道-产品类型: {}, 保证金模式: {}, 持仓方向: {}, 持仓数量: {}, 开仓平均价: {}, "
+ "{}: 账户持仓频道-产品类型: {}, 保证金模式: {}, 持仓方向: {}, 持仓数量: {}, 开仓平均价: {}, "
+ "未实现收益: {}, 未实现收益率: {}, 杠杆倍数: {}, 预估强平价: {}, 初始保证金: {}, "
+ "维持保证金率: {}, 维持保证金: {}, 以美金价值为单位的持仓数量: {}, 占用保证金的币种: {}, "
+ "最新成交价: {}, 最新指数价格: {}, 盈亏平衡价: {}, 已实现收益: {}, 累计已结算收益: {}"
+ "最新标记价格: {},累计手续费: {},累计持仓费: {},",
- instId, mgnMode, posSide, pos, avgPx,
+ accountName, instId, mgnMode, posSide, pos, avgPx,
upl, uplRatio, lever, liqPx, imr,
mgnRatio, mmr, notionalUsd, ccy,
last, idxPx, bePx, realizedPnl, settledPnl,
markPx,fee,fundingFee
);
- initParam(posData);
+ initParam(posData, accountName);
}
}
} catch (Exception e) {
@@ -107,18 +113,19 @@
}
}
- private static void initParam(JSONObject posData) {
- WsMapBuild.saveBigDecimalToMap(POSITIONSWSMAP, "avgPx", WsMapBuild.parseBigDecimalSafe(posData.getString("avgPx")));
- WsMapBuild.saveBigDecimalToMap(POSITIONSWSMAP, "pos", WsMapBuild.parseBigDecimalSafe(posData.getString("pos")));
- WsMapBuild.saveBigDecimalToMap(POSITIONSWSMAP, "upl", WsMapBuild.parseBigDecimalSafe(posData.getString("upl")));
- WsMapBuild.saveBigDecimalToMap(POSITIONSWSMAP, "imr", WsMapBuild.parseBigDecimalSafe(posData.getString("imr")));
- WsMapBuild.saveBigDecimalToMap(POSITIONSWSMAP, "mgnRatio", WsMapBuild.parseBigDecimalSafe(posData.getString("mgnRatio")));
- WsMapBuild.saveBigDecimalToMap(POSITIONSWSMAP, "markPx", WsMapBuild.parseBigDecimalSafe(posData.getString("markPx")));
- WsMapBuild.saveBigDecimalToMap(POSITIONSWSMAP, "bePx", WsMapBuild.parseBigDecimalSafe(posData.getString("bePx")));
- WsMapBuild.saveBigDecimalToMap(POSITIONSWSMAP, "realizedPnl", WsMapBuild.parseBigDecimalSafe(posData.getString("realizedPnl")));
- WsMapBuild.saveBigDecimalToMap(POSITIONSWSMAP, "fee", WsMapBuild.parseBigDecimalSafe(posData.getString("fee")));
- WsMapBuild.saveBigDecimalToMap(POSITIONSWSMAP, "fundingFee", WsMapBuild.parseBigDecimalSafe(posData.getString("fundingFee")));
+ private static void initParam(JSONObject posData, String accountName) {
+ Map<String, BigDecimal> accountMap = getAccountMap(accountName);
+ WsMapBuild.saveBigDecimalToMap(accountMap, "avgPx", WsMapBuild.parseBigDecimalSafe(posData.getString("avgPx")));
+ WsMapBuild.saveBigDecimalToMap(accountMap, "pos", WsMapBuild.parseBigDecimalSafe(posData.getString("pos")));
+ WsMapBuild.saveBigDecimalToMap(accountMap, "upl", WsMapBuild.parseBigDecimalSafe(posData.getString("upl")));
+ WsMapBuild.saveBigDecimalToMap(accountMap, "imr", WsMapBuild.parseBigDecimalSafe(posData.getString("imr")));
+ WsMapBuild.saveBigDecimalToMap(accountMap, "mgnRatio", WsMapBuild.parseBigDecimalSafe(posData.getString("mgnRatio")));
+ WsMapBuild.saveBigDecimalToMap(accountMap, "markPx", WsMapBuild.parseBigDecimalSafe(posData.getString("markPx")));
+ WsMapBuild.saveBigDecimalToMap(accountMap, "bePx", WsMapBuild.parseBigDecimalSafe(posData.getString("bePx")));
+ WsMapBuild.saveBigDecimalToMap(accountMap, "realizedPnl", WsMapBuild.parseBigDecimalSafe(posData.getString("realizedPnl")));
+ WsMapBuild.saveBigDecimalToMap(accountMap, "fee", WsMapBuild.parseBigDecimalSafe(posData.getString("fee")));
+ WsMapBuild.saveBigDecimalToMap(accountMap, "fundingFee", WsMapBuild.parseBigDecimalSafe(posData.getString("fundingFee")));
- WsMapBuild.saveBigDecimalToMap(POSITIONSWSMAP, CoinEnums.READY_STATE.name(), WsMapBuild.parseBigDecimalSafe(CoinEnums.READY_STATE_YES.getCode()));
+ WsMapBuild.saveBigDecimalToMap(accountMap, CoinEnums.READY_STATE.name(), WsMapBuild.parseBigDecimalSafe(CoinEnums.READY_STATE_YES.getCode()));
}
}
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 8d5a1f7..03662c2 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
@@ -22,20 +22,26 @@
@Slf4j
public class TradeOrderWs {
- public static final Map<String,String> TRADEORDERWSMAP = new ConcurrentHashMap<>();
+ // 使用双层Map,第一层key为账号名称,第二层key为数据key
+ public static final Map<String, Map<String,String>> TRADEORDERWSMAP = new ConcurrentHashMap<>();
+
+ // 获取指定账号的Map,如果不存在则创建
+ public static Map<String, String> getAccountMap(String accountName) {
+ return TRADEORDERWSMAP.computeIfAbsent(accountName, k -> new ConcurrentHashMap<>());
+ }
public static final String ORDERWS_CHANNEL = "order";
- public static void orderEvent(WebSocketClient webSocketClient, String side) {
+ public static void orderEvent(WebSocketClient webSocketClient, String side, String accountName) {
// log.info("开始执行TradeOrderWs......");
- String accountReadyState = AccountWs.ACCOUNTWSMAP.get(CoinEnums.READY_STATE.name());
+ String accountReadyState = AccountWs.getAccountMap(accountName).get(CoinEnums.READY_STATE.name());
if (!CoinEnums.READY_STATE_YES.getCode().equals(accountReadyState)) {
log.info("账户通道未就绪,取消发送");
return;
}
- BigDecimal positionsReadyState = PositionsWs.POSITIONSWSMAP.get(CoinEnums.READY_STATE.name()) == null
- ? BigDecimal.ZERO : PositionsWs.POSITIONSWSMAP.get(CoinEnums.READY_STATE.name());
+ BigDecimal positionsReadyState = PositionsWs.getAccountMap(accountName).get(CoinEnums.READY_STATE.name()) == null
+ ? BigDecimal.ZERO : PositionsWs.getAccountMap(accountName).get(CoinEnums.READY_STATE.name());
if (WsMapBuild.parseBigDecimalSafe(CoinEnums.READY_STATE_YES.getCode()).compareTo(positionsReadyState) != 0) {
log.info("仓位通道未就绪,取消发送");
return;
@@ -52,19 +58,19 @@
}else if (OrderParamEnums.OUT.getValue().equals(side)){
log.info("当前状态为止损");
side = OrderParamEnums.SELL.getValue();
- buyCnt = String.valueOf(PositionsWs.POSITIONSWSMAP.get("pos"));
+ buyCnt = String.valueOf(PositionsWs.getAccountMap(accountName).get("pos"));
}else if (OrderParamEnums.INIT.getValue().equals(side)){
log.info("当前状态为初始化");
side = OrderParamEnums.BUY.getValue();
- buyCnt = InstrumentsWs.INSTRUMENTSWSMAP.get(CoinEnums.BUY_CNT.name());
+ buyCnt = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.BUY_CNT.name());
}else if (OrderParamEnums.BUY.getValue().equals(side)){
log.info("当前状态为加仓");
- String buyCntTime = TRADEORDERWSMAP.get("buyCntTime");
- String buyCntStr = InstrumentsWs.INSTRUMENTSWSMAP.get(CoinEnums.BUY_CNT.name());
+ String buyCntTime = getAccountMap(accountName).get("buyCntTime");
+ String buyCntStr = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.BUY_CNT.name());
buyCnt = String.valueOf(new BigDecimal(buyCntTime).multiply(new BigDecimal(buyCntStr)));
}else if (OrderParamEnums.SELL.getValue().equals(side)){
log.info("当前状态为减仓");
- buyCnt = String.valueOf(PositionsWs.POSITIONSWSMAP.get("pos"));
+ buyCnt = String.valueOf(PositionsWs.getAccountMap(accountName).get("pos"));
}else{
log.warn("交易状态异常,取消发送");
return;
@@ -93,12 +99,12 @@
webSocketClient.send(jsonObject.toJSONString());
log.info("发送下单频道:{},数量:{}", side, buyCnt);
- WsMapBuild.saveStringToMap(TRADEORDERWSMAP, "buyCntTime",String.valueOf(BigDecimal.ONE));
- WsMapBuild.saveStringToMap(TRADEORDERWSMAP, "clOrdId", clOrdId);
- WsMapBuild.saveStringToMap(TRADEORDERWSMAP, "state", CoinEnums.ORDER_FILLED.getCode());
+ WsMapBuild.saveStringToMap(getAccountMap(accountName), "buyCntTime",String.valueOf(BigDecimal.ONE));
+ WsMapBuild.saveStringToMap(getAccountMap(accountName), "clOrdId", clOrdId);
+ WsMapBuild.saveStringToMap(getAccountMap(accountName), "state", CoinEnums.ORDER_FILLED.getCode());
- WsMapBuild.saveBigDecimalToMap(PositionsWs.POSITIONSWSMAP, CoinEnums.READY_STATE.name(), WsMapBuild.parseBigDecimalSafe(CoinEnums.READY_STATE_NO.getCode()));
- WsMapBuild.saveStringToMap(AccountWs.ACCOUNTWSMAP, CoinEnums.READY_STATE.name(), CoinEnums.READY_STATE_NO.getCode());
+ WsMapBuild.saveBigDecimalToMap(PositionsWs.getAccountMap(accountName), CoinEnums.READY_STATE.name(), WsMapBuild.parseBigDecimalSafe(CoinEnums.READY_STATE_NO.getCode()));
+ WsMapBuild.saveStringToMap(AccountWs.getAccountMap(accountName), CoinEnums.READY_STATE.name(), CoinEnums.READY_STATE_NO.getCode());
} catch (Exception e) {
log.error("下单构建失败", e);
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/enums/ExchangeInfoEnum.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/enums/ExchangeInfoEnum.java
index c970375..59c80ed 100644
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/enums/ExchangeInfoEnum.java
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/enums/ExchangeInfoEnum.java
@@ -10,28 +10,28 @@
public enum ExchangeInfoEnum {
/**
- * 模拟盘账户信息
+ * 模拟盘账户1信息
* 存储了模拟盘交易所需的API密钥、秘钥和通过码
*/
- OKX_UAT("ffb4e79f-fcf5-4afb-82c5-2fbb64123f61",
+ OKX_UAT1("ffb4e79f-fcf5-4afb-82c5-2fbb64123f61",
"AA06C5ED1D7C7F5AFE6484052E231C55",
+ "Aa12345678@",
+ false),
+
+ /**
+ * 模拟盘账户2信息
+ * 存储了模拟盘交易所需的API密钥、秘钥和通过码
+ */
+ OKX_UAT2("7a023eb2-06c0-4255-9969-b86ea1cef0d7",
+ "D0106A4D63BD22BEAB9CBA8F41219661",
"Aa12345678@",
false);
/**
- * 模拟盘账户信息
+ * 模拟盘账户3信息
* 存储了模拟盘交易所需的API密钥、秘钥和通过码
*/
-// OKX_UAT("7a023eb2-06c0-4255-9969-b86ea1cef0d7",
-// "D0106A4D63BD22BEAB9CBA8F41219661",
-// "Aa12345678@",
-// false);
-
-// /**
-// * 模拟盘账户信息
-// * 存储了模拟盘交易所需的API密钥、秘钥和通过码
-// */
-// OKX_UAT("0769b50c-2c36-4310-8bd9-cad6bc6c9d8f",
+// OKX_UAT3("0769b50c-2c36-4310-8bd9-cad6bc6c9d8f",
// "7AF4A574BC44907CE76BBFF91F53852D",
// "Aa123456@",
// false);
diff --git a/src/main/java/com/xcong/excoin/modules/okxNewPrice/zhanghu/ApiMessageServiceImpl.java b/src/main/java/com/xcong/excoin/modules/okxNewPrice/zhanghu/ApiMessageServiceImpl.java
index 6402d0f..c8b8f0e 100644
--- a/src/main/java/com/xcong/excoin/modules/okxNewPrice/zhanghu/ApiMessageServiceImpl.java
+++ b/src/main/java/com/xcong/excoin/modules/okxNewPrice/zhanghu/ApiMessageServiceImpl.java
@@ -19,13 +19,14 @@
quantApiMessage.setExchange(okx);
quantApiMessage.setMemberId(1L);
-
- quantApiMessage.setAccountType(ExchangeInfoEnum.OKX_UAT.isAccountType()? "true":"false");
+ // 根据传入的账号名称获取对应的账号信息
+ ExchangeInfoEnum account = ExchangeInfoEnum.valueOf(okx);
+ quantApiMessage.setAccountType(account.isAccountType()? "true":"false");
quantApiMessage.setState(1);
quantApiMessage.setIsTrade(1);
- quantApiMessage.setASecretkey(ExchangeInfoEnum.OKX_UAT.getApiKey());
- quantApiMessage.setBSecretkey(ExchangeInfoEnum.OKX_UAT.getSecretKey());
- quantApiMessage.setPassPhrass(ExchangeInfoEnum.OKX_UAT.getPassphrase());
+ quantApiMessage.setASecretkey(account.getApiKey());
+ quantApiMessage.setBSecretkey(account.getSecretKey());
+ quantApiMessage.setPassPhrass(account.getPassphrase());
return quantApiMessage;
}
--
Gitblit v1.9.1