Administrator
5 days ago 83017f78860526483a24e89052534222fd2e6466
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
package com.xcong.excoin.modules.okxApi.wsHandler;
 
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xcong.excoin.modules.okxApi.OkxGridTradeService;
import lombok.extern.slf4j.Slf4j;
import org.java_websocket.client.WebSocketClient;
 
/**
 * OKX 私有频道 WS 处理器的抽象基类。
 *
 * <h3>与 Gate 版本的关键区别</h3>
 * <ul>
 *   <li>OKX 私有频道连接到<b>独立的私有 WebSocket 端点</b> ({@code wss://ws.okx.com:8443/ws/v5/private}),
 *       需要先发送 login 消息认证,不同于 Gate 的单一 WS + 签名订阅模式。</li>
 *   <li>订阅格式使用 {@code op/subscribe} 而非 event/subscribe。</li>
 *   <li>签名算法使用 HMAC-SHA256 + Base64,而非 Gate 的 HMAC-SHA512 + Hex。</li>
 * </ul>
 *
 * <h3>架构</h3>
 * 公有频道(如 k-line)连接到 public WS 端点,无需认证。
 * 私有频道(如 positions、orders-algo)连接到 private WS 端点,由
 * {@link com.xcong.excoin.modules.okxApi.OkxKlineWebSocketClient} 负责
 * 在连接建立时发送 login 消息认证。
 *
 * <h3>订阅格式</h3>
 * <pre>
 * {"op":"subscribe","args":[{"channel":"positions","instType":"SWAP"}]}
 * {"op":"subscribe","args":[{"channel":"orders-algo","instType":"SWAP","instId":"ETH-USDT-SWAP"}]}
 * </pre>
 *
 * <h3>取消订阅格式</h3>
 * <pre>
 * {"op":"unsubscribe","args":[{"channel":"positions","instType":"SWAP"}]}
 * </pre>
 *
 * @author Administrator
 */
@Slf4j
public abstract class AbstractOkxPrivateChannelHandler implements OkxChannelHandler {
 
    /** 频道名称,如 "positions"、"orders-algo" */
    private final String channelName;
 
    /** OKX API Key */
    protected final String apiKey;
 
    /** OKX API Secret(用于签名) */
    protected final String apiSecret;
 
    /** OKX API Passphrase */
    protected final String passphrase;
 
    /** 交易对标识,如 "ETH-USDT-SWAP" */
    private final String instId;
 
    /** 网格交易服务实例 */
    private final OkxGridTradeService gridTradeService;
 
    /** 订阅确认状态 */
    private volatile boolean subscribed = false;
 
    /**
     * 构造私有频道处理器。
     *
     * @param channelName      频道名称(如 "positions"、"orders-algo")
     * @param apiKey           OKX API Key
     * @param apiSecret        OKX API Secret
     * @param passphrase       OKX API Passphrase
     * @param instId           交易对标识(如 "ETH-USDT-SWAP")
     * @param gridTradeService 网格交易服务实例
     */
    public AbstractOkxPrivateChannelHandler(String channelName,
                                             String apiKey, String apiSecret,
                                             String passphrase,
                                             String instId,
                                             OkxGridTradeService gridTradeService) {
        this.channelName = channelName;
        this.apiKey = apiKey;
        this.apiSecret = apiSecret;
        this.passphrase = passphrase;
        this.instId = instId;
        this.gridTradeService = gridTradeService;
    }
 
    /**
     * @return 频道名称(如 "positions")
     */
    @Override
    public String getChannelName() {
        return channelName;
    }
 
    /**
     * @return 交易对标识(如 "ETH-USDT-SWAP")
     */
    @Override
    public String getInstId() {
        return instId;
    }
 
    /**
     * 发送订阅请求。
     * 默认实现发送 {@code {"op":"subscribe","args":[{"channel":channelName,"instType":"SWAP"}]}}。
     * 子类可覆盖以添加额外参数(如 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", channelName);
        arg.put("instType", "SWAP");
        args.add(arg);
        msg.put("args", args);
        ws.send(msg.toJSONString());
        log.info("[OKX-WS] 订阅私有频道: {}, instType: SWAP", channelName);
    }
 
    /**
     * 发送取消订阅请求。
     *
     * @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", channelName);
        arg.put("instType", "SWAP");
        args.add(arg);
        msg.put("args", args);
        ws.send(msg.toJSONString());
        log.info("[OKX-WS] 取消订阅私有频道: {}", channelName);
    }
 
    /**
     * @return 网格交易服务实例
     */
    protected OkxGridTradeService getGridTradeService() {
        return gridTradeService;
    }
 
    // ==================== 订阅状态 ====================
 
    @Override
    public boolean isSubscribed() {
        return subscribed;
    }
 
    @Override
    public void setSubscribed(boolean subscribed) {
        this.subscribed = subscribed;
    }
}