Administrator
2025-12-16 f969796247af081af496df767814a31523c073bd
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
package com.xcong.excoin.modules.okxNewPrice.okxWs;
 
import cn.hutool.core.util.StrUtil;
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.utils.WsMapBuild;
import com.xcong.excoin.modules.okxNewPrice.utils.WsParamBuild;
import lombok.extern.slf4j.Slf4j;
import org.java_websocket.client.WebSocketClient;
 
import java.math.BigDecimal;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
 
/**
 * 交易订单处理类,负责构建和发送订单请求到OKX WebSocket
 * 
 * @author Administrator
 */
@Slf4j
public class TradeOrderWs {
 
    // 使用双层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, String accountName) {
 
//        log.info("开始执行TradeOrderWs......");
        String accountReadyState = AccountWs.getAccountMap(accountName).get(CoinEnums.READY_STATE.name());
        if (!CoinEnums.READY_STATE_YES.getCode().equals(accountReadyState)) {
            log.info("账户通道未就绪,取消发送");
            return;
        }
        String posSide = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.POSSIDE.name());
        String positionAccountName = PositionsWs.initAccountName(accountName, posSide);
        BigDecimal positionsReadyState = PositionsWs.getAccountMap(positionAccountName).get(CoinEnums.READY_STATE.name()) == null
                ? BigDecimal.ZERO : PositionsWs.getAccountMap(positionAccountName).get(CoinEnums.READY_STATE.name());
        if (WsMapBuild.parseBigDecimalSafe(CoinEnums.READY_STATE_YES.getCode()).compareTo(positionsReadyState) != 0) {
            log.info("仓位{}通道未就绪,取消发送",positionAccountName);
            return;
        }
        // 校验必要参数
        if (StrUtil.isBlank(side)) {
            log.warn("下单参数 side 为空,取消发送");
            return;
        }
        // 校验必要参数
        if (StrUtil.isBlank(posSide)) {
            log.warn("下单参数 posSide 为空,取消发送");
            return;
        }
 
        String buyCnt = "";
        if (CoinEnums.POSSIDE_LONG.getCode().equals(posSide)){
            if (OrderParamEnums.HOLDING.getValue().equals(side)){
                log.info("当前状态为持仓中,取消发送");
                return;
            }else if (OrderParamEnums.OUT.getValue().equals(side)){
                log.info("当前状态为止损");
                side = OrderParamEnums.SELL.getValue();
                buyCnt = String.valueOf(PositionsWs.getAccountMap(positionAccountName).get("pos"));
            }else if (OrderParamEnums.INIT.getValue().equals(side)){
                log.info("当前状态为初始化");
                side = OrderParamEnums.BUY.getValue();
                buyCnt = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.BUY_CNT_INIT.name());
            }else if (OrderParamEnums.BUY.getValue().equals(side)){
                log.info("当前状态为加仓");
                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.getAccountMap(positionAccountName).get("pos"));
            }else{
                log.warn("交易状态异常,取消发送");
                return;
            }
        }else if (CoinEnums.POSSIDE_SHORT.getCode().equals(posSide)){
            if (OrderParamEnums.HOLDING.getValue().equals(side)){
                log.info("当前状态为持仓中,取消发送");
                return;
            }else if (OrderParamEnums.OUT.getValue().equals(side)){
                log.info("当前状态为止损");
                side = OrderParamEnums.BUY.getValue();
                buyCnt = String.valueOf(PositionsWs.getAccountMap(positionAccountName).get("pos"));
            }else if (OrderParamEnums.INIT.getValue().equals(side)){
                log.info("当前状态为初始化");
                side = OrderParamEnums.SELL.getValue();
                buyCnt = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.BUY_CNT_INIT.name());
            }else if (OrderParamEnums.BUY.getValue().equals(side)){
                log.info("当前状态为减仓");
                buyCnt = String.valueOf(PositionsWs.getAccountMap(positionAccountName).get("pos"));
            }else if (OrderParamEnums.SELL.getValue().equals(side)){
                log.info("当前状态为加仓");
                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{
                log.warn("交易状态异常,取消发送");
                return;
            }
        }
 
 
        if (StrUtil.isBlank(buyCnt)) {
            log.warn("下单数量 buyCnt 为空,取消发送");
            return;
        }
 
        try {
            String clOrdId = WsParamBuild.getOrderNum(side);
            JSONArray argsArray = new JSONArray();
            JSONObject args = new JSONObject();
            args.put("instId", CoinEnums.HE_YUE.getCode());
            args.put("tdMode", CoinEnums.CROSS.getCode());
            args.put("clOrdId", clOrdId);
            args.put("side", side);
 
            args.put("posSide", posSide);
            args.put("ordType", CoinEnums.ORDTYPE_MARKET.getCode());
            args.put("sz", buyCnt);
            argsArray.add(args);
 
            String connId = WsParamBuild.getOrderNum(ORDERWS_CHANNEL);
            JSONObject jsonObject = WsParamBuild.buildJsonObject(connId, ORDERWS_CHANNEL, argsArray);
            webSocketClient.send(jsonObject.toJSONString());
            log.info("发送下单频道:{},数量:{}", side, buyCnt);
 
            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.getAccountMap(positionAccountName), 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);
        }
    }
 
 
 
    /**
     * 计算盈亏金额。
     *
     * @param faceValue 面值
     * @param position 持仓数量
     * @param markPrice 标记价格
     * @param openPrice 开仓价格
     * @param isLong 是否为多头仓位
     * @param minTickSz 最小变动单位精度
     * @return 盈亏金额,保留指定精度的小数位
     */
    public BigDecimal profit(BigDecimal faceValue, BigDecimal position,
                             BigDecimal markPrice, BigDecimal openPrice, boolean isLong, int minTickSz) {
        BigDecimal profit = BigDecimal.ZERO;
        if (isLong) {
            profit = markPrice.subtract(openPrice)
                    .multiply(faceValue)
                    .multiply(position);
        } else {
            profit = openPrice.subtract(markPrice)
                    .multiply(faceValue)
                    .multiply(position);
        }
        return profit.setScale(minTickSz, BigDecimal.ROUND_DOWN);
    }
 
}