Administrator
2025-12-12 ec7a3e6d2bd050f89047079d2c1d71e8f51e0c47
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package com.xcong.excoin.modules.okxNewPrice.okxWs;
 
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.CoinEnums;
import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.OrderParamEnums;
import com.xcong.excoin.modules.okxNewPrice.okxpi.MallUtils;
import com.xcong.excoin.modules.okxNewPrice.utils.WsMapBuild;
import com.xcong.excoin.modules.okxNewPrice.utils.WsParamBuild;
import io.micrometer.core.instrument.util.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.java_websocket.client.WebSocketClient;
 
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
 
/**
 * 账户 WebSocket 处理类,用于订阅 OKX 的账户频道并处理账户信息推送。
 * 包含账户资金状态判断、计算下单保证金及保存到 Redis 的逻辑。
 *
 * @author Administrator
 */
@Slf4j
public class AccountWs {
 
    public static  final Map<String,String> ACCOUNTWSMAP = new ConcurrentHashMap<>();
    /**
     * 账户频道名称常量
     */
    public static final String ACCOUNTWS_CHANNEL = "account";
 
    /**
     * 订阅账户频道
     *
     * @param webSocketClient WebSocket 客户端实例
     * @param option          请求选项(如 unsubscribe 或 subscribe)
     */
    public static void subscribeAccountChannel(WebSocketClient webSocketClient, String option) {
        try {
            JSONArray argsArray = new JSONArray();
            JSONObject args = new JSONObject();
            args.put("channel", ACCOUNTWS_CHANNEL);
            args.put("ccy", CoinEnums.USDT.getCode());
            argsArray.add(args);
 
            String connId = MallUtils.getOrderNum(ACCOUNTWS_CHANNEL);
            JSONObject jsonObject = WsParamBuild.buildJsonObject(connId, option, argsArray);
            webSocketClient.send(jsonObject.toJSONString());
            log.info("发送账户频道:{}", option);
        } catch (Exception e) {
            log.error("订阅账户频道构建失败", e);
        }
    }
 
    /**
     * 处理账户频道推送的数据
     *
     * @param response   推送的 JSON 数据对象
     */
    public static void handleEvent(JSONObject response) {
 
 
        log.info("开始执行AccountWs......{}",ACCOUNTWS_CHANNEL);
        try {
            JSONArray dataArray = response.getJSONArray("data");
            if (dataArray == null || dataArray.isEmpty()) {
                log.warn("账户频道数据为空");
                return;
            }
 
            for (int i = 0; i < dataArray.size(); i++) {
                try {
                    JSONObject accountData = dataArray.getJSONObject(i);
                    JSONArray detailsArray = accountData.getJSONArray("details");
                    if (detailsArray == null || detailsArray.isEmpty()) {
                        log.warn("账户频道{}数据为空",CoinEnums.USDT.getCode());
                        continue;
                    }
 
                    for (int j = 0; j < detailsArray.size(); j++) {
                        JSONObject detail = detailsArray.getJSONObject(j);
//                        log.info("账户详情: {}", JSONUtil.formatJsonStr(String.valueOf(detail)));
                        //需要获取的参数
                        String ccyKey = "ccy";
                        String availBalKey = "availBal";
                        String cashBalKey = "cashBal";
                        String eqKey = "eq";
                        String uplKey = "upl";
 
                        String ccy = WsMapBuild.parseStringSafe( detail.getString(ccyKey));
                        WsMapBuild.saveStringToMap(ACCOUNTWSMAP, ccyKey, ccy);
 
                        String availBal = WsMapBuild.parseStringSafe(detail.getString(availBalKey));
                        WsMapBuild.saveStringToMap(ACCOUNTWSMAP, availBalKey, availBal);
 
                        String cashBal = WsMapBuild.parseStringSafe(detail.getString(cashBalKey));
                        WsMapBuild.saveStringToMap(ACCOUNTWSMAP, cashBalKey, cashBal);
 
                        String eq = WsMapBuild.parseStringSafe(detail.getString(eqKey));
                        WsMapBuild.saveStringToMap(ACCOUNTWSMAP, eqKey, eq);
 
                        String upl = WsMapBuild.parseStringSafe(detail.getString(uplKey));
                        WsMapBuild.saveStringToMap(ACCOUNTWSMAP, uplKey, upl);
 
                        BigDecimal cashBalDecimal = WsMapBuild.parseBigDecimalSafe(cashBal);
                        if (cashBalDecimal.compareTo(BigDecimal.ZERO) == 0) {
                            log.warn("账户频道无效的账户余额数据,跳过处理");
                            continue;
                        }
 
                        // 根据可用余额计算下单总保证金
                        String total_order_usdtpecent = InstrumentsWs.INSTRUMENTSWSMAP.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));
 
                        log.info(
                                "账户详情-币种: {}, 可用余额: {}, 现金余额: {}, 余额: {}, 全仓未实现盈亏: {}, 下单总保证金: {}",
                                ccy, availBal, cashBal, eq, upl, totalOrderUsdt
                        );
                    }
                } catch (Exception innerEx) {
                    log.warn("处理账户频道数据失败", innerEx);
                }
            }
        } catch (Exception e) {
            log.error("处理账户频道推送数据失败", e);
        }
    }
 
}