Administrator
yesterday 60d0e9bd70f87de69b378ab63bac60394e3ff696
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
package com.xcong.excoin.modules.gateApi;
 
import com.xcong.excoin.modules.gateApi.wsHandler.handler.CandlestickChannelHandler;
import com.xcong.excoin.modules.gateApi.wsHandler.handler.PositionClosesChannelHandler;
import com.xcong.excoin.modules.gateApi.wsHandler.handler.PositionsChannelHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
 
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
 
/**
 * Gate 模块 Spring 入口,管理多个账号实例的生命周期。
 *
 * <h3>启动流程 ({@code @PostConstruct})</h3>
 * <ol>
 *   <li>遍历 {@link #buildAccountConfigs()} 构建每个账号的 {@link GateConfig}</li>
 *   <li>每个账号创建独立的 {@link AccountInstance}(含 service + wsClient)</li>
 *   <li>逐个 init → startGrid</li>
 * </ol>
 *
 * <h3>销毁流程 ({@code @PreDestroy})</h3>
 * <ol>
 *   <li>遍历 instances:stopGrid + wsClient.destroy()</li>
 * </ol>
 *
 * <h3>添加新账号</h3>
 * 在 {@link #buildAccountConfigs()} 方法中添加新的 {@link GateConfig} 即可。
 * 通过 {@link GateConfig#getLabel()} 区分日志输出,
 * 通过 {@link GateConfig#isProduction()} 控制模拟盘/实盘环境。
 *
 * @author Administrator
 */
@Slf4j
@Component
public class GateWebSocketClientManager {
 
    /** 所有账号实例列表 */
    private final List<AccountInstance> instances = new ArrayList<>();
 
    /**
     * 封装一个账号的完整运行时组件。
     */
    private static class AccountInstance {
        final String label;
        final GateConfig config;
        final GateGridTradeService service;
        final GateKlineWebSocketClient wsClient;
 
        AccountInstance(GateConfig config) {
            this.label = config.getLabel();
            this.config = config;
            this.service = new GateGridTradeService(config);
            this.wsClient = new GateKlineWebSocketClient(config.getWsUrl());
        }
 
        void init() {
            service.init();
            wsClient.addChannelHandler(new CandlestickChannelHandler(config.getContract(), service));
            wsClient.addChannelHandler(new PositionsChannelHandler(
                    config.getApiKey(), config.getApiSecret(), config.getContract(), service));
            wsClient.addChannelHandler(new PositionClosesChannelHandler(
                    config.getApiKey(), config.getApiSecret(), config.getContract(), service));
            wsClient.init();
            log.info("[{}] WS已连接, 已注册 3 个频道处理器", label);
            service.startGrid();
        }
 
        void destroy() {
            log.info("[{}] 开始销毁...", label);
            service.stopGrid();
            wsClient.destroy();
            log.info("[{}] 销毁完成", label);
        }
    }
 
    /**
     * 在此方法中配置所有账号。
     * <p>每个 {@link GateConfig} 对应一个账号,label 用于日志区分,
     * isProduction 控制连接测试网(true=实盘)还是测试网(false=模拟盘)。
     */
    private static GateConfig[] buildAccountConfigs() {
        return new GateConfig[]{
                GateConfig.builder()
                        .label("模拟盘1")
                        .apiKey("d90ca272391992b8e74f8f92cedb21ec")
                        .apiSecret("1861e4f52de4bb53369ea3208d9ede38ece4777368030f96c77d27934c46c274")
                        .contract("ETH_USDT")
                        .leverage("100")
                        .marginMode("CROSS")
                        .positionMode("dual")
                        .gridRate(new BigDecimal("0.0015"))
                        .overallTp(new BigDecimal("5"))
                        .maxLoss(new BigDecimal("15"))
                        .quantity("1")
                        .contractMultiplier(new BigDecimal("0.01"))
                        .unrealizedPnlPriceMode(GateConfig.PnLPriceMode.LAST_PRICE)
                        .isProduction(false)
                        .reopenMaxRetries(3)
                        .build(),
        };
    }
 
    @PostConstruct
    public void init() {
        GateConfig[] configs = buildAccountConfigs();
        log.info("[管理器] 开始初始化 {} 个账号...", configs.length);
        for (GateConfig config : configs) {
            try {
                AccountInstance instance = new AccountInstance(config);
                instance.init();
                instances.add(instance);
            } catch (Exception e) {
                log.error("[管理器] 账号 {} 初始化失败", config.getLabel(), e);
            }
        }
 
        log.info("[管理器] 初始化完成, 成功启动 {}/{} 个账号", instances.size(), configs.length);
    }
 
    @PreDestroy
    public void destroy() {
        log.info("[管理器] 开始销毁 {} 个账号...", instances.size());
        for (AccountInstance instance : instances) {
            try {
                instance.destroy();
            } catch (Exception e) {
                log.error("[管理器] 账号 {} 销毁失败", instance.label, e);
            }
        }
        log.info("[管理器] 销毁完成");
    }
 
    /** 获取账号数量 */
    public int getInstanceCount() {
        return instances.size();
    }
 
    /** 获取指定索引的 GridTradeService(用于外部监控/查询) */
    public GateGridTradeService getGridTradeService(int index) {
        return instances.get(index).service;
    }
}