Administrator
2026-06-02 85e1673c4683f4699106afe79405877903ce4eb5
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
package com.xcong.excoin.modules.okxNewPrice;
 
import com.xcong.excoin.modules.okxNewPrice.gridWs.OkxAlgoOrdersChannelHandler;
import com.xcong.excoin.modules.okxNewPrice.gridWs.OkxKlineChannelHandler;
import com.xcong.excoin.modules.okxNewPrice.gridWs.OkxPositionsChannelHandler;
import com.xcong.excoin.modules.okxNewPrice.okxpi.config.OKXAccount;
import com.xcong.excoin.modules.okxNewPrice.okxpi.config.enums.DefaultUrls;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
 
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.math.BigDecimal;
 
/**
 * OKX 网格交易系统 Spring 容器入口 — 组件组装 + 生命周期管理。
 *
 * <h3>组装顺序({@code @PostConstruct})</h3>
 * <ol>
 *   <li>{@link OkxConfig} — 构建配置(API 密钥、合约、策略参数)</li>
 *   <li>{@link OkxGridTradeService}.init() — 获取账户 → 清条件单 → 平仓 → 设杠杆</li>
 *   <li>{@link OkxGridWsClient} — 注册 3 个频道处理器 → init():建立 WS 连接并登录订阅</li>
 *   <li>{@code gridTradeService.startGrid()} — 状态重置,等待首根 K 线</li>
 * </ol>
 *
 * <h3>3 个频道处理器</h3>
 * <ol>
 *   <li>OkxKlineChannelHandler — candle1m,K线 → onKline()</li>
 *   <li>OkxPositionsChannelHandler — positions,持仓 → onPositionUpdate()</li>
 *   <li>OkxAlgoOrdersChannelHandler — orders-algo,条件单 → onOrderUpdate()</li>
 * </ol>
 *
 * <h3>销毁顺序({@code @PreDestroy})</h3>
 * <ol>
 *   <li>gridTradeService.stopGrid():取消所有条件单 → 关闭交易线程池</li>
 *   <li>gridWsClient.destroy():取消订阅 → 断开 WS → 关闭线程池</li>
 * </ol>
 *
 * @author Administrator
 */
@Slf4j
@Component
@ConditionalOnProperty(prefix = "app", name = "quant", havingValue = "true")
public class OkxWebSocketClientManager {
 
    /** 网格交易公共 WS 客户端(candle1m) */
    private OkxGridWsClient gridWsClientPublic;
    /** 网格交易私有 WS 客户端(positions / orders-algo) */
    private OkxGridWsClient gridWsClientPrivate;
    /** 网格交易策略服务 */
    private OkxGridTradeService gridTradeService;
    /** 统一配置 */
    private OkxConfig okxConfig;
 
    @PostConstruct
    public void init() {
        log.info("[OKX-Manager] 开始初始化...");
 
        try {
            // 获取账户配置
            ExchangeInfoEnum[] accounts = ExchangeInfoEnum.values();
            if (accounts == null || accounts.length == 0) {
                log.error("[OKX-Manager] 无可用账户,初始化失败");
                return;
            }
 
            // 使用第一个账户
            ExchangeInfoEnum primaryAccount = accounts[0];
            log.info("[OKX-Manager] 使用主账户: {}", primaryAccount.name());
 
            // 1. 创建 OKXAccount(REST API 调用需要)
            String baseUrl = primaryAccount.isAccountType() ? DefaultUrls.USDM_PROD_URL : DefaultUrls.USDM_UAT_URL;
            OKXAccount okxAccount = new OKXAccount(
                    baseUrl,
                    primaryAccount.getApiKey(),
                    primaryAccount.getSecretKey(),
                    primaryAccount.getPassphrase(),
                    !primaryAccount.isAccountType()
            );
 
            // 2. 构建 OkxConfig
            // TODO: 参数可通过配置文件/数据库动态读取
            okxConfig = OkxConfig.builder()
                    .apiKey(primaryAccount.getApiKey())
                    .secretKey(primaryAccount.getSecretKey())
                    .passphrase(primaryAccount.getPassphrase())
                    .instId("BTC-USDT-SWAP")
                    .leverage("100")
                    .tdMode("cross")
                    .gridRate(new BigDecimal("0.0025"))
                    .expectedProfit(new BigDecimal("2"))
                    .maxLoss(new BigDecimal("15"))
                    .quantity("1")
                    .baseQuantity("10")
                    .priceScale(2)
                    .ctVal(new BigDecimal("0.1"))
                    .isSimulate(!primaryAccount.isAccountType())
                    .gridQueueSize(300)
                    .marginRatioLimit(new BigDecimal("0.2"))
                    .build();
 
            // 3. 初始化交易服务:查账户 → 清条件单 → 平已有仓位 → 设杠杆
            gridTradeService = new OkxGridTradeService(okxConfig, okxAccount);
            gridTradeService.init();
 
            // 4. 创建 WS 客户端并注册频道处理器
            // 公共 WS:candle1m(K线数据)
            gridWsClientPublic = new OkxGridWsClient(primaryAccount, true);
            gridWsClientPublic.addChannelHandler(new OkxKlineChannelHandler(okxConfig.getInstId(), gridTradeService));
            gridWsClientPublic.init();
 
            // 私有 WS:positions + orders-algo(持仓 + 条件单)
            gridWsClientPrivate = new OkxGridWsClient(primaryAccount, false);
            gridWsClientPrivate.addChannelHandler(new OkxPositionsChannelHandler(okxConfig.getInstId(), gridTradeService));
            gridWsClientPrivate.addChannelHandler(new OkxAlgoOrdersChannelHandler(okxConfig.getInstId(), gridTradeService));
            gridWsClientPrivate.init();
 
            log.info("[OKX-Manager] WS已连接, 公共: candle1m, 私有: positions/orders-algo");
 
            // 5. 激活策略,等待首根 K 线触发基底双开
            gridTradeService.startGrid();
 
            log.info("[OKX-Manager] 初始化完成");
        } catch (Exception e) {
            log.error("[OKX-Manager] 初始化失败", e);
        }
    }
 
    /**
     * 销毁:停止策略 → 关闭交易线程池 → 取消 WS 订阅 → 断开连接 → 关闭 WS 线程池。
     */
    @PreDestroy
    public void destroy() {
        log.info("[OKX-Manager] 开始销毁...");
 
        if (gridTradeService != null) {
            try {
                gridTradeService.stopGrid();
            } catch (Exception e) {
                log.error("[OKX-Manager] 停止策略失败", e);
            }
        }
        if (gridWsClientPublic != null) {
            try {
                gridWsClientPublic.destroy();
            } catch (Exception e) {
                log.error("[OKX-Manager] 销毁公共WS客户端失败", e);
            }
        }
        if (gridWsClientPrivate != null) {
            try {
                gridWsClientPrivate.destroy();
            } catch (Exception e) {
                log.error("[OKX-Manager] 销毁私有WS客户端失败", e);
            }
        }
 
        log.info("[OKX-Manager] 销毁完成");
    }
 
    /** @return 网格交易策略服务实例 */
    public OkxGridTradeService getGridTradeService() { return gridTradeService; }
    /** @return 网格交易公共 WS 客户端实例 */
    public OkxGridWsClient getGridWsClientPublic() { return gridWsClientPublic; }
    /** @return 网格交易私有 WS 客户端实例 */
    public OkxGridWsClient getGridWsClientPrivate() { return gridWsClientPrivate; }
    public OkxConfig getOkxConfig() { return okxConfig; }
}