8302a0d3a49e25b9c2aa5f68000b29210b4fd556..269e8765fabd893d47a1ce276304256650c8e99e
2025-12-16 Administrator
fix(okxWs): 调整账户持仓为空时的处理逻辑
269e87 diff | tree
2025-12-16 Administrator
fix(okxWs): 防止空指针异常导致订单信息处理中断
d1532c diff | tree
2025-12-16 Administrator
fix(okxNewPrice): 修复网格策略平仓逻辑空指针异常
605b7d diff | tree
2025-12-16 Administrator
feat(okxWs): 更新网格列表枚举值并新增DOWN_ONE项
84a3ac diff | tree
2025-12-16 Administrator
fix(okxNewPrice): 修复账户名称初始化逻辑
ff095d diff | tree
2025-12-16 Administrator
feat(okxWs): 启用持仓频道日志记录功能
45572e diff | tree
2025-12-16 Administrator
fix(okxNewPrice): 优化加仓减仓日志描述
a9feaa diff | tree
2025-12-16 Administrator
fix(okxNewPrice): 更新队列显示标签
5cdfd0 diff | tree
2025-12-16 Administrator
fix(okxWs): 调整抗压比例和开仓张数倍数配置
f96979 diff | tree
2025-12-16 Administrator
fix(okxNewPrice): 修复加仓次数计算逻辑
d81e77 diff | tree
2025-12-16 Administrator
chore(config): 注释掉 OKX_UAT2 配置项
2ae35a diff | tree
2025-12-16 Administrator
fix(newPrice): 更新OKX WebSocket URL为公共端点
dcb379 diff | tree
2025-12-16 Administrator
feat(okx): 更新OKX WebSocket客户端配置与价格订阅逻辑
c6dfb2 diff | tree
2025-12-16 Administrator
fix(okxNewPrice): 修复网格价格区间判断逻辑
a28baa diff | tree
2025-12-16 Administrator
feat(okx): 更新网格交易参数并修复初始化逻辑
6ee948 diff | tree
2025-12-16 Administrator
feat(okxNewPrice): 实现基于网格列表的交易策略
2e05d1 diff | tree
10 files modified
4 files added
694 ■■■■ changed files
src/main/java/com/xcong/excoin/modules/newPrice/OkxNewPriceWebSocketClient.java 40 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxQuantWebSocketClient.java 1 ●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/celue/CaoZuoServiceImpl.java 203 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/InstrumentsWs.java 1 ●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/OrderInfoWs.java 13 ●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/PositionsWs.java 29 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/TradeOrderWs.java 25 ●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/enums/CoinEnums.java 6 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/enums/ExchangeInfoEnum.java 10 ●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/enums/OrderParamEnums.java 1 ●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/wanggeList/WangGeListEnum.java 70 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/wanggeList/WangGeListQueue.java 79 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/wanggeList/WangGeListService.java 41 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/wanggeList/WangGeListServiceImpl.java 175 ●●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/newPrice/OkxNewPriceWebSocketClient.java
@@ -3,6 +3,8 @@
import com.alibaba.fastjson.JSON;
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.utils.SSLConfig;
import com.xcong.excoin.rabbit.pricequeue.WebsocketPriceService;
import com.xcong.excoin.utils.CoinTypeConvert;
import com.xcong.excoin.utils.RedisUtils;
@@ -28,7 +30,7 @@
 */
@Slf4j
@Component
@ConditionalOnProperty(prefix = "app", name = "websocket", havingValue = "true")
@ConditionalOnProperty(prefix = "app", name = "quant", havingValue = "true")
public class OkxNewPriceWebSocketClient {
    @Resource
    private WebsocketPriceService websocketPriceService;
@@ -40,12 +42,7 @@
    private volatile ScheduledFuture<?> pongTimeoutFuture;
    private final AtomicReference<Long> lastMessageTime = new AtomicReference<>(System.currentTimeMillis());
    private static final String WS_URL = "wss://ws.okx.com:8443/ws/v5/public";
    private static final String CHANNEL = "mark-price";
    private static final String[] INST_IDS = {
            "BTC-USDT", "ETH-USDT", "XRP-USDT", "LTC-USDT", "BCH-USDT", "ETC-USDT"
    };
    // 心跳超时时间(秒),小于30秒
    private static final int HEARTBEAT_TIMEOUT = 10;
@@ -84,6 +81,9 @@
        }
        sharedExecutor.shutdownNow();
    }
    private static final String WS_URL_MONIPAN = "wss://wspap.okx.com:8443/ws/v5/public";
    private static final String WS_URL_SHIPAN = "wss://ws.okx.com:8443/ws/v5/public";
    private static final boolean isAccountType = true;
    /**
     * 建立与 OKX WebSocket 服务器的连接。
@@ -91,6 +91,12 @@
     */
    private void connect() {
        try {
            SSLConfig.configureSSL();
            System.setProperty("https.protocols", "TLSv1.2,TLSv1.3");
            String WS_URL = WS_URL_MONIPAN;
            if (isAccountType){
                WS_URL = WS_URL_SHIPAN;
            }
            URI uri = new URI(WS_URL);
            webSocketClient = new WebSocketClient(uri) {
                @Override
@@ -144,12 +150,10 @@
        subscribeMsg.put("op", "subscribe");
        JSONArray argsArray = new JSONArray();
        for (String instId : INST_IDS) {
            JSONObject arg = new JSONObject();
            arg.put("channel", CHANNEL);
            arg.put("instId", instId);
            argsArray.add(arg);
        }
        JSONObject arg = new JSONObject();
        arg.put("channel", CHANNEL);
        arg.put("instId", CoinEnums.HE_YUE.getCode());
        argsArray.add(arg);
        subscribeMsg.put("args", argsArray);
        webSocketClient.send(subscribeMsg.toJSONString());
@@ -200,17 +204,9 @@
                        String markPx = priceData.getString("markPx");
                        String ts = priceData.getString("ts");
                        String redisKey = buildRedisKey(instId);
                        redisUtils.set(redisKey, markPx);
                        redisUtils.set(CoinEnums.HE_YUE.getCode(), markPx);
                        String symbol = CoinTypeConvert.okxConvert(instId);
                        if (symbol != null) {
                            redisUtils.set(CoinTypeConvert.convertToKey(symbol), markPx);
                            websocketPriceService.comparePriceAsc(symbol, markPx);
                            websocketPriceService.comparePriceDesc(symbol, markPx);
                        }
                        log.debug("更新最新价格: {} = {}, 时间: {}", redisKey, markPx, ts);
                        log.debug("更新最新价格: {} = {}, 币种: {}", CoinEnums.HE_YUE.getCode(), markPx, instId);
                    } catch (Exception innerEx) {
                        log.warn("处理单条价格数据失败", innerEx);
                    }
src/main/java/com/xcong/excoin/modules/okxNewPrice/OkxQuantWebSocketClient.java
@@ -174,7 +174,6 @@
        
        try {
            InstrumentsWs.handleEvent(account.name());
            wangGeService.initWangGe();
            SSLConfig.configureSSL();
            System.setProperty("https.protocols", "TLSv1.2,TLSv1.3");
            String WS_URL = WS_URL_MONIPAN;
src/main/java/com/xcong/excoin/modules/okxNewPrice/celue/CaoZuoServiceImpl.java
@@ -5,6 +5,9 @@
import com.xcong.excoin.modules.okxNewPrice.okxWs.*;
import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.CoinEnums;
import com.xcong.excoin.modules.okxNewPrice.okxWs.enums.OrderParamEnums;
import com.xcong.excoin.modules.okxNewPrice.okxWs.wanggeList.WangGeListEnum;
import com.xcong.excoin.modules.okxNewPrice.okxWs.wanggeList.WangGeListQueue;
import com.xcong.excoin.modules.okxNewPrice.okxWs.wanggeList.WangGeListService;
import com.xcong.excoin.modules.okxNewPrice.utils.WsMapBuild;
import com.xcong.excoin.modules.okxNewPrice.wangge.WangGeQueue;
import com.xcong.excoin.modules.okxNewPrice.wangge.WangGeService;
@@ -17,6 +20,7 @@
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Map;
import java.util.concurrent.PriorityBlockingQueue;
/**
@@ -31,6 +35,8 @@
public class CaoZuoServiceImpl implements CaoZuoService {
    private final WangGeService wangGeService;
    private final WangGeListService wangGeListService;
    private final RedisUtils redisUtils;
    /**
     * 执行主要的操作逻辑,包括读取合约状态、获取市场价格信息,
@@ -40,29 +46,39 @@
     */
    @Override
    public String caoZuo(String accountName) {
        String posSide = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.POSSIDE.name());
        if (CoinEnums.POSSIDE_LONG.getCode().equals(posSide)){
            return caoZuoLong(accountName);
        }else if (CoinEnums.POSSIDE_SHORT.getCode().equals(posSide)){
            return caoZuoShort(accountName);
        }else{
            log.error("账户未设置持仓方向......");
            return null;
        }
    }
    @Override
    public String caoZuoLong(String accountName) {
        log.info("开始看涨执行操作CaoZuoServiceImpl......");
        String accountReadyState = AccountWs.getAccountMap(accountName).get(CoinEnums.READY_STATE.name());
        if (!CoinEnums.READY_STATE_YES.getCode().equals(accountReadyState)) {
            log.info("账户通道未就绪,取消发送");
            return null;
        }
        BigDecimal positionsReadyState = PositionsWs.getAccountMap(accountName).get(CoinEnums.READY_STATE.name()) == null
                ? BigDecimal.ZERO : PositionsWs.getAccountMap(accountName).get(CoinEnums.READY_STATE.name());
        String markPx = ObjectUtil.isEmpty(redisUtils.getString(CoinEnums.HE_YUE.getCode())) ? "0" : redisUtils.getString(CoinEnums.HE_YUE.getCode());
        WangGeListEnum gridByPrice = WangGeListEnum.getGridByPrice(new BigDecimal(markPx));
        if (gridByPrice == null){
            log.error("没有获取到网格参数......");
            return null;
        }
        log.info("当前网格: {}", gridByPrice.name());
        Map<String, String> accountMap = InstrumentsWs.getAccountMap(accountName);
        String wanggeName = accountMap.get(CoinEnums.WANG_GE_OLD.name());
        /**
         * 如果下单的网格不属于同一个网格,则先止损掉老的网格的仓位
         */
        if (StrUtil.isNotEmpty(wanggeName) && !wanggeName.equals(gridByPrice.name())){
            log.error("正在止损老的网格仓位......");
            WangGeListEnum oldWangge = WangGeListEnum.getByName(wanggeName);
            if (oldWangge != null){
                WsMapBuild.saveStringToMap(accountMap, CoinEnums.POSSIDE.name(), oldWangge.getFang_xiang());
                return OrderParamEnums.OUT.getValue();
            }
        }
        String posSide = gridByPrice.getFang_xiang();
        log.info("仓位方向: {}", posSide);
        WsMapBuild.saveStringToMap(accountMap, CoinEnums.POSSIDE.name(), posSide);
        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("仓位通道未就绪,取消发送");
            log.info("仓位{}通道未就绪,取消发送",positionAccountName);
            return null;
        }
        // 系统设置的开关,等于冷静中,则代表不开仓
@@ -106,37 +122,57 @@
            }
        }
        if (PositionsWs.getAccountMap(accountName).get("pos") == null){
        if (PositionsWs.getAccountMap(positionAccountName).get("pos") == null){
            log.error("没有获取到持仓信息,等待初始化......");
            return null;
        }
        BigDecimal pos = PositionsWs.getAccountMap(accountName).get("pos");
        BigDecimal pos = PositionsWs.getAccountMap(positionAccountName).get("pos");
        if (BigDecimal.ZERO.compareTo( pos) >= 0) {
            log.error("持仓数量为零,进行初始化订单");
            return OrderParamEnums.INIT.getValue();
        }
        // 判断是否保证金超标
        if (PositionsWs.getAccountMap(accountName).get("imr") == null){
        if (PositionsWs.getAccountMap(positionAccountName).get("imr") == null){
            log.error("没有获取到持仓信息,等待初始化......");
            return null;
        }
        BigDecimal ordFrozImr = PositionsWs.getAccountMap(accountName).get("imr");
        BigDecimal ordFrozImr = PositionsWs.getAccountMap(positionAccountName).get("imr");
        BigDecimal totalOrderUsdt = WsMapBuild.parseBigDecimalSafe(AccountWs.getAccountMap(accountName).get(CoinEnums.TOTAL_ORDER_USDT.name()));
        if (ordFrozImr.compareTo(totalOrderUsdt) >= 0){
            log.error("已满仓......");
            return OrderParamEnums.HOLDING.getValue();
        }
        PriorityBlockingQueue<AscBigDecimal> ascBigDecimals = wangGeListService.initWangGe(markPx);
        if (ascBigDecimals == null){
            log.error("没有获取到网格队列......");
            return null;
        }
        if (CoinEnums.POSSIDE_LONG.getCode().equals(posSide)){
            return caoZuoLong(accountName);
        }else if (CoinEnums.POSSIDE_SHORT.getCode().equals(posSide)){
            return caoZuoShort(accountName);
        }else{
            log.error("账户未设置持仓方向......");
            return null;
        }
    }
    @Override
    public String caoZuoLong(String accountName) {
        log.info("开始看涨执行操作CaoZuoServiceImpl......");
        try {
            String positionAccountName = PositionsWs.initAccountName(accountName, CoinEnums.POSSIDE_LONG.getCode());
            // 获取标记价格和平均持仓价格
            BigDecimal markPx = PositionsWs.getAccountMap(accountName).get("markPx");
            BigDecimal avgPx = PositionsWs.getAccountMap(accountName).get("avgPx");
            BigDecimal markPx = PositionsWs.getAccountMap(positionAccountName).get("markPx");
            BigDecimal avgPx = PositionsWs.getAccountMap(positionAccountName).get("avgPx");
            log.info("开仓价格: {}, 当前价格:{},匹配队列中......", avgPx, markPx);
            // 初始化网格队列
            PriorityBlockingQueue<AscBigDecimal> queueAsc = WangGeQueue.getQueueAsc();
            PriorityBlockingQueue<DescBigDecimal> queueKaiCang = wangGeService.initKaiCang(avgPx, queueAsc);
            PriorityBlockingQueue<AscBigDecimal> queuePingCang = wangGeService.initPingCang(avgPx, queueAsc);
            PriorityBlockingQueue<AscBigDecimal> queueAsc = WangGeListQueue.getQueueAsc();
            PriorityBlockingQueue<DescBigDecimal> queueKaiCang = wangGeListService.initKaiCang(avgPx, queueAsc);
            PriorityBlockingQueue<AscBigDecimal> queuePingCang = wangGeListService.initPingCang(avgPx, queueAsc);
            // 处理订单价格在队列中的情况
            String orderPrice = OrderInfoWs.getAccountMap(accountName).get("orderPrice");
@@ -151,7 +187,7 @@
                }
                DescBigDecimal kaiCang = queueKaiCang.peek();
                if (kaiCang != null && markPx.compareTo(kaiCang.getValue()) <= 0 && avgPx.compareTo(kaiCang.getValue()) >= 0) {
                    log.info("开始加仓...开仓队列价格大于当前价格{}>{}", kaiCang.getValue(), markPx);
                    log.info("开始加仓...下限队列价格大于当前价格{}>{}", kaiCang.getValue(), markPx);
                    WsMapBuild.saveStringToMap(OrderInfoWs.getAccountMap(accountName), "orderPrice", String.valueOf(markPx));
                    boolean buyCntTimeFlag = buyCntTimeLongEvent(accountName, avgPx, markPx);
                    if (buyCntTimeFlag){
@@ -174,17 +210,17 @@
                }
                AscBigDecimal pingCang = queuePingCang.peek();
                if (pingCang != null && avgPx.compareTo(pingCang.getValue()) < 0) {
                    log.info("开始减仓...平仓队列价格大于当前价格{}<={}", pingCang.getValue(), avgPx);
                    log.info("开始减仓...上限队列价格大于开仓价格{}>{}", pingCang.getValue(), avgPx);
                    // 手续费
                    BigDecimal feeValue = PositionsWs.getAccountMap(accountName).get("fee").multiply(new BigDecimal("2"));
                    BigDecimal feeValue = PositionsWs.getAccountMap(positionAccountName).get("fee").multiply(new BigDecimal("2"));
                    //未实现收益
                    BigDecimal uplValue = PositionsWs.getAccountMap(accountName).get("upl");
                    BigDecimal uplValue = PositionsWs.getAccountMap(positionAccountName).get("upl");
                    //已实现收益
                    BigDecimal realizedPnlValue = PositionsWs.getAccountMap(accountName).get("realizedPnl");
                    BigDecimal realizedPnlValue = PositionsWs.getAccountMap(positionAccountName).get("realizedPnl");
                    realizedPnlValue = realizedPnlValue.add(feeValue);
                    //持仓保证金
                    BigDecimal imr = PositionsWs.getAccountMap(accountName).get("imr");
                    BigDecimal imr = PositionsWs.getAccountMap(positionAccountName).get("imr");
                    String pingCangImr = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.PING_CANG_SHOUYI.name());
                    BigDecimal imrValue = imr.multiply(new BigDecimal(pingCangImr));
@@ -223,91 +259,20 @@
    @Override
    public String caoZuoShort(String accountName) {
        log.info("开始看空执行操作CaoZuoServiceImpl......");
        String accountReadyState = AccountWs.getAccountMap(accountName).get(CoinEnums.READY_STATE.name());
        if (!CoinEnums.READY_STATE_YES.getCode().equals(accountReadyState)) {
            log.info("账户通道未就绪,取消发送");
            return null;
        }
        BigDecimal positionsReadyState = PositionsWs.getAccountMap(accountName).get(CoinEnums.READY_STATE.name()) == null
                ? BigDecimal.ZERO : PositionsWs.getAccountMap(accountName).get(CoinEnums.READY_STATE.name());
        if (WsMapBuild.parseBigDecimalSafe(CoinEnums.READY_STATE_YES.getCode()).compareTo(positionsReadyState) != 0) {
            log.info("仓位通道未就绪,取消发送");
            return null;
        }
        // 系统设置的开关,等于冷静中,则代表不开仓
        String outStr = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.OUT.name());
        if (OrderParamEnums.OUT_YES.getValue().equals(outStr)){
            log.error("冷静中,不允许下单......");
            return null;
        }
        BigDecimal cashBal = WsMapBuild.parseBigDecimalSafe(AccountWs.getAccountMap(accountName).get("cashBal"));
        // 判断账户余额是否充足
        if (cashBal.compareTo(BigDecimal.ZERO) <= 0){
            log.error("账户没有钱,请充值......");
            return null;
        }
        /**
         * 判断止损抗压
         */
        // 实际亏损金额
        BigDecimal realKuiSunAmount = WsMapBuild.parseBigDecimalSafe(AccountWs.getAccountMap(accountName).get("upl"));
        log.info("未实现盈亏: {}", realKuiSunAmount);
        String zhiSunPercent = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.ZHI_SUN.name());
        BigDecimal zhiSunAmount = cashBal.multiply(new BigDecimal(zhiSunPercent));
        log.info("预期亏损金额: {}", zhiSunAmount);
        String kangYaPercent = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.KANG_CANG.name());
        BigDecimal  kangYaAmount = cashBal.multiply(new BigDecimal(kangYaPercent));
        log.info("预期抗仓金额: {}", kangYaAmount);
        if (realKuiSunAmount.compareTo(BigDecimal.ZERO) < 0){
            realKuiSunAmount = realKuiSunAmount.multiply(new BigDecimal("-1"));
            // 账户预期亏损金额比这个还小时,立即止损
            if (realKuiSunAmount.compareTo(zhiSunAmount) > 0){
                log.error("账户冷静止损......");
                WsMapBuild.saveStringToMap(InstrumentsWs.getAccountMap(accountName), CoinEnums.OUT.name(),  OrderParamEnums.OUT_YES.getValue());
                return OrderParamEnums.OUT.getValue();
            }
            // 判断抗压
            if (realKuiSunAmount.compareTo(kangYaAmount) > 0 && realKuiSunAmount.compareTo(zhiSunAmount) <= 0){
                log.error("账户紧张扛仓......");
                return OrderParamEnums.HOLDING.getValue();
            }
        }
        if (PositionsWs.getAccountMap(accountName).get("pos") == null){
            log.error("没有获取到持仓信息,等待初始化......");
            return null;
        }
        BigDecimal pos = PositionsWs.getAccountMap(accountName).get("pos");
        if (BigDecimal.ZERO.compareTo( pos) >= 0) {
            log.error("持仓数量为零,进行初始化订单");
            return OrderParamEnums.INIT.getValue();
        }
        // 判断是否保证金超标
        if (PositionsWs.getAccountMap(accountName).get("imr") == null){
            log.error("没有获取到持仓信息,等待初始化......");
            return null;
        }
        BigDecimal ordFrozImr = PositionsWs.getAccountMap(accountName).get("imr");
        BigDecimal totalOrderUsdt = WsMapBuild.parseBigDecimalSafe(AccountWs.getAccountMap(accountName).get(CoinEnums.TOTAL_ORDER_USDT.name()));
        if (ordFrozImr.compareTo(totalOrderUsdt) >= 0){
            log.error("已满仓......");
            return OrderParamEnums.HOLDING.getValue();
        }
        try {
            String positionAccountName = PositionsWs.initAccountName(accountName, CoinEnums.POSSIDE_SHORT.getCode());
            // 获取标记价格和平均持仓价格
            BigDecimal markPx = PositionsWs.getAccountMap(accountName).get("markPx");
            BigDecimal avgPx = PositionsWs.getAccountMap(accountName).get("avgPx");
            BigDecimal markPx = PositionsWs.getAccountMap(positionAccountName).get("markPx");
            BigDecimal avgPx = PositionsWs.getAccountMap(positionAccountName).get("avgPx");
            log.info("开仓价格: {}, 当前价格:{},匹配队列中......", avgPx, markPx);
            // 初始化网格队列
            PriorityBlockingQueue<AscBigDecimal> queueAsc = WangGeQueue.getQueueAsc();
            PriorityBlockingQueue<DescBigDecimal> queueKaiCang = wangGeService.initKaiCang(avgPx, queueAsc);
            PriorityBlockingQueue<AscBigDecimal> queuePingCang = wangGeService.initPingCang(avgPx, queueAsc);
            PriorityBlockingQueue<AscBigDecimal> queueAsc = WangGeListQueue.getQueueAsc();
            PriorityBlockingQueue<DescBigDecimal> queueKaiCang = wangGeListService.initKaiCang(avgPx, queueAsc);
            PriorityBlockingQueue<AscBigDecimal> queuePingCang = wangGeListService.initPingCang(avgPx, queueAsc);
            // 处理订单价格在队列中的情况
            String orderPrice = OrderInfoWs.getAccountMap(accountName).get("orderPrice");
@@ -322,18 +287,18 @@
                }
                DescBigDecimal kaiCang = queueKaiCang.peek();
                if (kaiCang != null && avgPx.compareTo(kaiCang.getValue()) >= 0) {
                    log.info("开始减仓...减仓队列价格小于开仓价格{}>{}", kaiCang.getValue(), avgPx);
                    log.info("开始减仓...下限队列价格小于开仓价格{}<{}", kaiCang.getValue(), avgPx);
                    // 手续费
                    BigDecimal feeValue = PositionsWs.getAccountMap(accountName).get("fee").multiply(new BigDecimal("2"));
                    BigDecimal feeValue = PositionsWs.getAccountMap(positionAccountName).get("fee").multiply(new BigDecimal("2"));
                    //未实现收益
                    BigDecimal uplValue = PositionsWs.getAccountMap(accountName).get("upl");
                    BigDecimal uplValue = PositionsWs.getAccountMap(positionAccountName).get("upl");
                    //已实现收益
                    BigDecimal realizedPnlValue = PositionsWs.getAccountMap(accountName).get("realizedPnl");
                    BigDecimal realizedPnlValue = PositionsWs.getAccountMap(positionAccountName).get("realizedPnl");
                    realizedPnlValue = realizedPnlValue.add(feeValue);
                    //持仓保证金
                    BigDecimal imr = PositionsWs.getAccountMap(accountName).get("imr");
                    BigDecimal imr = PositionsWs.getAccountMap(positionAccountName).get("imr");
                    String pingCangImr = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.PING_CANG_SHOUYI.name());
                    BigDecimal imrValue = imr.multiply(new BigDecimal(pingCangImr));
@@ -370,7 +335,7 @@
                }
                AscBigDecimal pingCang = queuePingCang.peek();
                if (pingCang != null && markPx.compareTo(pingCang.getValue()) >= 0 && avgPx.compareTo(pingCang.getValue()) < 0) {
                    log.info("开始加仓...加仓队列价格小于当前价格{}<={}", pingCang.getValue(), markPx);
                    log.info("开始加仓...上限队列价格小于当前价格{}<={}", pingCang.getValue(), markPx);
                    WsMapBuild.saveStringToMap(OrderInfoWs.getAccountMap(accountName), "orderPrice", String.valueOf(markPx));
                    boolean buyCntTimeFlag = buyCntTimeShortEvent(accountName, avgPx, markPx);
                    if (buyCntTimeFlag){
@@ -397,7 +362,7 @@
        //判断当前价格和开仓价格直接间隔除以间距,取整,获取的数量是否大于等于0,如果大于0,则下单基础张数*倍数
        String buyCntTime = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.BUY_CNT_TIME.name());
        BigDecimal subtract = avgPx.subtract(markPx);
        BigDecimal divide = subtract.divide(new BigDecimal(buyCntTime), 0, RoundingMode.DOWN);
        BigDecimal divide = subtract.divide(new BigDecimal(buyCntTime), 0, RoundingMode.DOWN).add(BigDecimal.ONE);
        if (divide.compareTo(BigDecimal.ZERO) <= 0){
            log.warn("加仓次数间隔时间小于0,不加仓");
            return false;
@@ -409,7 +374,7 @@
        //判断当前价格和开仓价格直接间隔除以间距,取整,获取的数量是否大于等于0,如果大于0,则下单基础张数*倍数
        String buyCntTime = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.BUY_CNT_TIME.name());
        BigDecimal subtract = markPx.subtract(avgPx);
        BigDecimal divide = subtract.divide(new BigDecimal(buyCntTime), 0, RoundingMode.DOWN);
        BigDecimal divide = subtract.divide(new BigDecimal(buyCntTime), 0, RoundingMode.DOWN).add(BigDecimal.ONE);
        if (divide.compareTo(BigDecimal.ZERO) <= 0){
            log.warn("加仓次数间隔时间小于0,不加仓");
            return false;
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/InstrumentsWs.java
@@ -39,6 +39,5 @@
        WsMapBuild.saveStringToMap(accountMap, CoinEnums.KANG_CANG.name(), CoinEnums.KANG_CANG.getCode());
        WsMapBuild.saveStringToMap(accountMap, CoinEnums.PING_CANG_SHOUYI.name(), CoinEnums.PING_CANG_SHOUYI.getCode());
        WsMapBuild.saveStringToMap(accountMap, CoinEnums.TOTAL_ORDER_USDTPECENT.name(), CoinEnums.TOTAL_ORDER_USDTPECENT.getCode());
        WsMapBuild.saveStringToMap(accountMap, CoinEnums.POSSIDE.name(), CoinEnums.POSSIDE.getCode());
    }
}
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/OrderInfoWs.java
@@ -5,6 +5,7 @@
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.okxWs.wanggeList.WangGeListEnum;
import com.xcong.excoin.modules.okxNewPrice.okxpi.MallUtils;
import com.xcong.excoin.modules.okxNewPrice.utils.WsMapBuild;
import com.xcong.excoin.modules.okxNewPrice.utils.WsParamBuild;
@@ -112,10 +113,18 @@
                    }
                    WsMapBuild.saveStringToMap(TradeOrderWs.getAccountMap(accountName), "state", CoinEnums.ORDER_LIVE.getCode());
                    //保存上一个网格信息
                    WangGeListEnum gridByPrice = WangGeListEnum.getGridByPrice(new BigDecimal(avgPx));
                    if (gridByPrice != null){
                        Map<String, String> instrumentsMap = InstrumentsWs.getAccountMap(accountName);
                        WsMapBuild.saveStringToMap(instrumentsMap, CoinEnums.WANG_GE_OLD.name(), gridByPrice.name());
                    }
                    // 使用账号特定的Map
                    Map<String, BigDecimal> positionsMap = PositionsWs.getAccountMap(accountName);
                    String positionAccountName = PositionsWs.initAccountName(accountName, side);
                    Map<String, BigDecimal> positionsMap = PositionsWs.getAccountMap(positionAccountName);
                    WsMapBuild.saveBigDecimalToMap(positionsMap, CoinEnums.READY_STATE.name(), WsMapBuild.parseBigDecimalSafe(CoinEnums.READY_STATE_NO.getCode()));
                    Map<String, String> accountWsMap = AccountWs.getAccountMap(accountName);
                    WsMapBuild.saveStringToMap(accountWsMap, CoinEnums.READY_STATE.name(), CoinEnums.READY_STATE_NO.getCode());
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/PositionsWs.java
@@ -41,28 +41,31 @@
            String connId = MallUtils.getOrderNum(POSITIONSWS_CHANNEL);
            JSONObject jsonObject = WsParamBuild.buildJsonObject(connId, option, argsArray);
            webSocketClient.send(jsonObject.toJSONString());
//            log.info("发送持仓频道频道:{}", option);
            log.info("发送持仓频道频道:{}", option);
        } catch (Exception e) {
            log.error("订阅持仓频道频道构建失败", e);
        }
    }
    public static String initAccountName(String accountName, String posSide) {
        return accountName+"_"+ posSide;
    }
    public static void initEvent(JSONObject response, String accountName) {
//        log.info("订阅成功,数据初始化: {}", response.getJSONObject("arg"));
        log.info("订阅成功,数据初始化: {}", response.getJSONObject("arg"));
        JSONObject arg = response.getJSONObject("arg");
        initParam(arg, accountName);
        initParam(arg, accountName,CoinEnums.POSSIDE_LONG.getCode());
        initParam(arg, accountName,CoinEnums.POSSIDE_SHORT.getCode());
    }
    public static void handleEvent(JSONObject response, String accountName) {
//        log.info("开始执行PositionsWs......");
        log.info("开始执行PositionsWs......");
        try {
            JSONArray dataArray = response.getJSONArray("data");
            if (dataArray == null || dataArray.isEmpty()) {
//                log.info("账户持仓频道数据为空,已当前价买入,并且初始化网格");
                JSONObject posData = new JSONObject();
                initParam(posData, accountName);
                log.info("账户持仓频道数据为空,等待更新");
                return;
            }
@@ -70,7 +73,7 @@
                JSONObject posData = dataArray.getJSONObject(i);
                String instId = posData.getString("instId");
                if (CoinEnums.HE_YUE.getCode().equals(instId)) {
//                    log.info("查询到账户{}持仓数据",CoinEnums.HE_YUE.getCode());
                    log.info("查询到账户{}持仓数据",CoinEnums.HE_YUE.getCode());
                    String mgnMode = posData.getString("mgnMode");
                    String posSide = posData.getString("posSide");
                    String pos = posData.getString("pos");
@@ -98,14 +101,13 @@
                                    + "维持保证金率: {}, 维持保证金: {}, 以美金价值为单位的持仓数量: {}, 占用保证金的币种: {}, "
                                    + "最新成交价: {}, 最新指数价格: {}, 盈亏平衡价: {}, 已实现收益: {}, 累计已结算收益: {}"
                                    + "最新标记价格: {},累计手续费: {},累计持仓费: {},",
                            accountName, instId, mgnMode, posSide, pos, avgPx,
                            initAccountName(accountName, posSide), instId, mgnMode, posSide, pos, avgPx,
                            upl, uplRatio, lever, liqPx, imr,
                            mgnRatio, mmr, notionalUsd, ccy,
                            last, idxPx, bePx, realizedPnl, settledPnl,
                            markPx,fee,fundingFee
                    );
                    initParam(posData, accountName);
                    initParam(posData, accountName,posSide);
                }
            }
        } catch (Exception e) {
@@ -113,8 +115,9 @@
        }
    }
    private static void initParam(JSONObject posData, String accountName) {
        Map<String, BigDecimal> accountMap = getAccountMap(accountName);
    private static void initParam(JSONObject posData, String accountName,String posSide) {
        String accountNamePositons = initAccountName(accountName, posSide);
        Map<String, BigDecimal> accountMap = getAccountMap(accountNamePositons);
        WsMapBuild.saveBigDecimalToMap(accountMap, "avgPx", WsMapBuild.parseBigDecimalSafe(posData.getString("avgPx")));
        WsMapBuild.saveBigDecimalToMap(accountMap, "pos", WsMapBuild.parseBigDecimalSafe(posData.getString("pos")));
        WsMapBuild.saveBigDecimalToMap(accountMap, "upl", WsMapBuild.parseBigDecimalSafe(posData.getString("upl")));
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/TradeOrderWs.java
@@ -40,10 +40,12 @@
            log.info("账户通道未就绪,取消发送");
            return;
        }
        BigDecimal positionsReadyState = PositionsWs.getAccountMap(accountName).get(CoinEnums.READY_STATE.name()) == null
                ? BigDecimal.ZERO : PositionsWs.getAccountMap(accountName).get(CoinEnums.READY_STATE.name());
        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("仓位通道未就绪,取消发送");
            log.info("仓位{}通道未就绪,取消发送",positionAccountName);
            return;
        }
        // 校验必要参数
@@ -51,7 +53,6 @@
            log.warn("下单参数 side 为空,取消发送");
            return;
        }
        String posSide = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.POSSIDE.name());
        // 校验必要参数
        if (StrUtil.isBlank(posSide)) {
            log.warn("下单参数 posSide 为空,取消发送");
@@ -66,7 +67,7 @@
            }else if (OrderParamEnums.OUT.getValue().equals(side)){
                log.info("当前状态为止损");
                side = OrderParamEnums.SELL.getValue();
                buyCnt = String.valueOf(PositionsWs.getAccountMap(accountName).get("pos"));
                buyCnt = String.valueOf(PositionsWs.getAccountMap(positionAccountName).get("pos"));
            }else if (OrderParamEnums.INIT.getValue().equals(side)){
                log.info("当前状态为初始化");
                side = OrderParamEnums.BUY.getValue();
@@ -78,7 +79,7 @@
                buyCnt = String.valueOf(new BigDecimal(buyCntTime).multiply(new BigDecimal(buyCntStr)));
            }else if (OrderParamEnums.SELL.getValue().equals(side)){
                log.info("当前状态为减仓");
                buyCnt = String.valueOf(PositionsWs.getAccountMap(accountName).get("pos"));
                buyCnt = String.valueOf(PositionsWs.getAccountMap(positionAccountName).get("pos"));
            }else{
                log.warn("交易状态异常,取消发送");
                return;
@@ -90,14 +91,14 @@
            }else if (OrderParamEnums.OUT.getValue().equals(side)){
                log.info("当前状态为止损");
                side = OrderParamEnums.BUY.getValue();
                buyCnt = String.valueOf(PositionsWs.getAccountMap(accountName).get("pos"));
                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(accountName).get("pos"));
                buyCnt = String.valueOf(PositionsWs.getAccountMap(positionAccountName).get("pos"));
            }else if (OrderParamEnums.SELL.getValue().equals(side)){
                log.info("当前状态为加仓");
                String buyCntTime = getAccountMap(accountName).get("buyCntTime");
@@ -135,11 +136,11 @@
            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.saveStringToMap(getAccountMap(accountName), "clOrdId", clOrdId);
            WsMapBuild.saveStringToMap(getAccountMap(accountName), "state", CoinEnums.ORDER_FILLED.getCode());
            WsMapBuild.saveBigDecimalToMap(PositionsWs.getAccountMap(accountName), 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());
            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);
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/enums/CoinEnums.java
@@ -36,6 +36,8 @@
    USDT("USDT","USDT"),
    WANG_GE_OLD("上一个网格wang_ge_old", "0"),
    READY_STATE("是否准备就绪ready_state", "1"),
    READY_STATE_YES("准备就绪ready_state", "1"),
    READY_STATE_NO("未准备就绪ready_state", "0"),
@@ -44,12 +46,12 @@
    //下单的总保障金为账户总金额cashBal * TOTAL_ORDER_USDT用来做保证金
    TOTAL_ORDER_USDTPECENT("总保证金比例total_order_usdtpecent","0.05"),
    TOTAL_ORDER_USDT("总保证金totalOrderUsdt","0"),
    KANG_CANG("抗压比例KANG_CANG","0.7"),
    KANG_CANG("抗压比例KANG_CANG","0.8"),
    ZHI_SUN("止损比例ZHI_SUN","0.6"),
    //每次下单的张数
    BUY_CNT("每次开仓的张数buyCnt","0.1"),
    BUY_CNT_INIT("每次初始化开仓张数的基础值buyCntInit","0.2"),
    BUY_CNT_TIME("每次开仓张数的倍数基础值buyCntTime","5"),
    BUY_CNT_TIME("每次开仓张数的倍数基础值buyCntTime","2"),
    OUT("是否允许下单out","操作中"),
    CTVAL("合约面值ctVal","0.1"),
    TICKSZ("下单价格精度tickSz","2"),
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/enums/ExchangeInfoEnum.java
@@ -20,7 +20,7 @@
    OKX_UAT_ceshi("ffb4e79f-fcf5-4afb-82c5-2fbb64123f61",
            "AA06C5ED1D7C7F5AFE6484052E231C55",
            "Aa12345678@",
            false),
            false);
//
//    /**
//     * 模拟盘账户2信息
@@ -30,10 +30,10 @@
//            "2A5BD55BF0771F1ADF08AE0A2FB4D561",
//            "Aa12345678@",
//            true);
    OKX_UAT2("7a023eb2-06c0-4255-9969-b86ea1cef0d7",
            "D0106A4D63BD22BEAB9CBA8F41219661",
            "Aa12345678@",
            false);
//    OKX_UAT2("7a023eb2-06c0-4255-9969-b86ea1cef0d7",
//            "D0106A4D63BD22BEAB9CBA8F41219661",
//            "Aa12345678@",
//            false);
    /**
     * 模拟盘账户3信息
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/enums/OrderParamEnums.java
@@ -13,6 +13,7 @@
    OUT_YES("冷静中", "冷静中"),
    ORDERING("操作下单中", "ORDERING"),
    LIMIT("限价止损", "limit"),
    OUT("止损", "out"),
    INIT("初始化", "init"),
    HOLDING("持仓", "holding"),
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/wanggeList/WangGeListEnum.java
New file
@@ -0,0 +1,70 @@
package com.xcong.excoin.modules.okxNewPrice.okxWs.wanggeList;
import lombok.Getter;
import java.math.BigDecimal;
/**
 * @author Administrator
 * 网格数据枚举 数据
 * todo 后期考虑优化为可配置项
 */
@Getter
public enum WangGeListEnum {
    UP("上层做空", "2", "2950", "2940", "2", "short", "2950"),
    CENTER("中间指定一个方向", "2", "2940", "2930", "2", "long", "2930"),
    DOWN("下层做多", "2", "2930", "2920", "2", "long", "2920"),
    DOWN_ONE("下层做空", "2", "2920", "2910", "2", "short", "2920");
    private String name;
    private String xiaoshu_weishu;
    private String jiage_shangxian;
    private String jiage_xiaxian;
    private String jian_ju;
    private String fang_xiang;
    private String zhi_sun_dian;
    WangGeListEnum(String name, String xiaoshu_weishu, String jiage_shangxian, String jiage_xiaxian, String jian_ju, String fang_xiang, String zhi_sun_dian) {
        this.name = name;
        this.xiaoshu_weishu = xiaoshu_weishu;
        this.jiage_shangxian = jiage_shangxian;
        this.jiage_xiaxian = jiage_xiaxian;
        this.jian_ju = jian_ju;
        this.fang_xiang = fang_xiang;
        this.zhi_sun_dian = zhi_sun_dian;
    }
    /**
     * 根据价格获取匹配的网格信息
     * @param price 待比较的价格
     * @return 匹配的网格枚举信息,如果没有匹配项则返回null
     */
    public static WangGeListEnum getGridByPrice(BigDecimal price) {
        for (WangGeListEnum grid : WangGeListEnum.values()) {
            BigDecimal upperLimit = new BigDecimal(grid.jiage_shangxian);
            BigDecimal lowerLimit = new BigDecimal(grid.jiage_xiaxian);
            // 确保上限大于下限
            if (upperLimit.compareTo(lowerLimit) > 0) {
                // 检查价格是否在区间内
                if (price.compareTo(lowerLimit) > 0 && price.compareTo(upperLimit) <= 0) {
                    return grid;
                }
            }
        }
        return null;
    }
    /**
     * 根据枚举名称获取枚举
     */
    public static WangGeListEnum getByName(String name) {
        for (WangGeListEnum grid : WangGeListEnum.values()) {
            if (grid.name.equals(name)) {
                return grid;
            }
        }
        return null;
    }
}
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/wanggeList/WangGeListQueue.java
New file
@@ -0,0 +1,79 @@
package com.xcong.excoin.modules.okxNewPrice.okxWs.wanggeList;
import com.xcong.excoin.rabbit.pricequeue.AscBigDecimal;
import com.xcong.excoin.rabbit.pricequeue.DescBigDecimal;
import java.util.concurrent.PriorityBlockingQueue;
/**
 * 网格交易队列管理类
 *
 * 用于管理系统中各种网格交易相关的优先级阻塞队列,
 * 包括完整的网格队列、平仓队列和开仓队列。
 *
 * @author Administrator
 */
public class WangGeListQueue {
    //------------------------------------------------------------------------------------------------------------------
    //------------------------------------------------------------------------------------------------------------------
    // todo 系统启动后,初始化网格队列
    /**
     * 完整的网格 头元素最小
     */
    public static PriorityBlockingQueue<AscBigDecimal> QUEUE_ASC = null;
    //------------------------------------------------------------------------------------------------------------------
    //------------------------------------------------------------------------------------------------------------------
    // todo 当用户下了第一单后,根据开仓价格初始化网格平仓队列和开仓队列
    /**
     * 网格平仓队列 头元素最小
     */
    public static PriorityBlockingQueue<AscBigDecimal> QUEUE_PINGCANG_ASC = null;
    /**
     * 网格开仓队列 头元素最大
     */
    public static PriorityBlockingQueue<DescBigDecimal> QUEUE_KAICANG_DESC = null;
    /**
     * 获取完整的网格队列(升序)
     * 如果队列未初始化则创建新的优先级阻塞队列
     *
     * @return 返回升序排列的PriorityBlockingQueue队列,队列头部元素最小
     */
    public static PriorityBlockingQueue<AscBigDecimal> getQueueAsc() {
        if (QUEUE_ASC == null) {
            QUEUE_ASC = new PriorityBlockingQueue<AscBigDecimal>();
        }
        return QUEUE_ASC;
    }
    /**
     * 获取网格平仓队列(升序)
     * 如果队列未初始化则创建新的优先级阻塞队列
     *
     * @return 返回升序排列的PriorityBlockingQueue队列,队列头部元素最小
     */
    public static PriorityBlockingQueue<AscBigDecimal> getPingCang() {
        if (QUEUE_PINGCANG_ASC == null) {
            QUEUE_PINGCANG_ASC = new PriorityBlockingQueue<AscBigDecimal>();
        }
        return QUEUE_PINGCANG_ASC;
    }
    /**
     * 获取网格开仓队列(降序)
     * 如果队列未初始化则创建新的优先级阻塞队列
     *
     * @return 返回降序排列的PriorityBlockingQueue队列,队列头部元素最大
     */
    public static PriorityBlockingQueue<DescBigDecimal> getKaiCang() {
        if (QUEUE_KAICANG_DESC == null) {
            QUEUE_KAICANG_DESC = new PriorityBlockingQueue<DescBigDecimal>();
        }
        return QUEUE_KAICANG_DESC;
    }
}
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/wanggeList/WangGeListService.java
New file
@@ -0,0 +1,41 @@
package com.xcong.excoin.modules.okxNewPrice.okxWs.wanggeList;
import com.xcong.excoin.rabbit.pricequeue.AscBigDecimal;
import com.xcong.excoin.rabbit.pricequeue.DescBigDecimal;
import java.math.BigDecimal;
import java.util.concurrent.PriorityBlockingQueue;
/**
 * 网格交易服务接口
 * 定义了网格交易的核心操作方法,包括初始化网格、开仓和平仓等操作
 * @author Administrator
 */
public interface WangGeListService {
    /**
     * 初始化网格交易
     * 创建并初始化用于网格交易的价格队列,按照价格升序排列
     * @return 初始化结果信息,返回按价格升序排列的阻塞队列
     */
    PriorityBlockingQueue<AscBigDecimal> initWangGe(String markPx);
    /**
     * 初始化开仓操作
     * 根据指定价格初始化开仓队列,将开仓价格点加入到价格队列中
     * @param jiaGe 开仓价格
     * @param queueAsc 价格队列,用于存储按升序排列的价格点
     */
    PriorityBlockingQueue<DescBigDecimal> initKaiCang(BigDecimal jiaGe, PriorityBlockingQueue<AscBigDecimal> queueAsc);
    /**
     * 初始化平仓操作
     * 根据指定价格初始化平仓队列,将平仓价格点加入到价格队列中
     * @param jiaGe 开仓价格
     * @param queueAsc 价格队列,用于存储按升序排列的价格点
     */
    PriorityBlockingQueue<AscBigDecimal> initPingCang(BigDecimal jiaGe, PriorityBlockingQueue<AscBigDecimal> queueAsc);
}
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/wanggeList/WangGeListServiceImpl.java
New file
@@ -0,0 +1,175 @@
package com.xcong.excoin.modules.okxNewPrice.okxWs.wanggeList;
import com.xcong.excoin.rabbit.pricequeue.AscBigDecimal;
import com.xcong.excoin.rabbit.pricequeue.DescBigDecimal;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.concurrent.PriorityBlockingQueue;
/**
 * 网格交易服务实现类,用于初始化价格网格、开仓和平仓操作。
 *
 * @author Administrator
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class WangGeListServiceImpl implements WangGeListService {
    /**
     * 初始化价格网格队列。根据配置的价格上限、下限和间隔生成一系列价格点,
     * 并将这些价格点存入升序优先阻塞队列中。
     *
     * @return 返回初始化完成的升序价格队列;若初始化失败则返回null
     */
    @Override
    public PriorityBlockingQueue<AscBigDecimal> initWangGe(String markPx) {
        log.info("网格初始化中");
        PriorityBlockingQueue<AscBigDecimal> queueAsc = WangGeListQueue.getQueueAsc();
        queueAsc.clear();
        //获取WangGeListEnum全部网格参数
        WangGeListEnum gridByPrice = WangGeListEnum.getGridByPrice(new BigDecimal(markPx));
        if (gridByPrice == null){
            log.error("没有获取到网格参数......");
            return null;
        }
        String shangxianValue = gridByPrice.getJiage_shangxian();
        String xiaxianValue = gridByPrice.getJiage_xiaxian();
        String jianjuValue = gridByPrice.getJian_ju();
        String weishuValueStr = gridByPrice.getXiaoshu_weishu();
        try {
            BigDecimal shangxian = new BigDecimal(shangxianValue);
            BigDecimal xiaxian = new BigDecimal(xiaxianValue);
            BigDecimal jianju = new BigDecimal(jianjuValue);
            if (jianju.compareTo(BigDecimal.ZERO) == 0) {
                log.error("价格间隔不能为0");
                return null;
            }
            int weishu = Integer.parseInt(weishuValueStr);
            BigDecimal diff = shangxian.subtract(xiaxian);
            int count = diff.divide(jianju, 0, RoundingMode.DOWN).intValue();
            BigDecimal currentStep = BigDecimal.ZERO;
            for (int i = 0; i <= count; i++) {
                BigDecimal stepMultiplier = currentStep.multiply(jianju);
                BigDecimal wangGeJiaGe = xiaxian.add(stepMultiplier).setScale(weishu, RoundingMode.DOWN);
                AscBigDecimal ascBigDecimal = new AscBigDecimal(wangGeJiaGe.toString());
                queueAsc.add(ascBigDecimal);
                currentStep = currentStep.add(BigDecimal.ONE);
            }
            if (queueAsc.isEmpty()) {
                log.info("网格初始化失败");
                return null;
            }
            log.info("网格初始化成功");
            return queueAsc;
        } catch (NumberFormatException e) {
            log.error("解析价格参数失败", e);
            return null;
        } catch (Exception e) {
            log.error("初始化网格发生未知异常", e);
            return null;
        }
    }
    /**
     * 根据当前价格初始化开仓队列。遍历已有的升序价格队列,
     * 将小于当前价格的所有价格点加入降序的开仓队列中。
     *
     * @param jiaGe   当前价格
     * @param queueAsc 已初始化的价格升序队列
     */
    @Override
    public PriorityBlockingQueue<DescBigDecimal> initKaiCang(BigDecimal jiaGe, PriorityBlockingQueue<AscBigDecimal> queueAsc) {
        PriorityBlockingQueue<DescBigDecimal> queueKaiCang = WangGeListQueue.getKaiCang();
        queueKaiCang.clear();
        AscBigDecimal now = new AscBigDecimal(jiaGe.toString());
        for (AscBigDecimal ascBigDecimal : queueAsc) {
            if (ascBigDecimal.compareTo(now) < 0) {
                DescBigDecimal kaiCangJia = new DescBigDecimal(ascBigDecimal.getValue().toString());
                queueKaiCang.add(kaiCangJia);
            }
        }
        StringBuilder kaiCangStr = new StringBuilder();
        kaiCangStr.append("下限队列: [");
        boolean first = true;
        for (DescBigDecimal item : queueKaiCang) {
            if (!first) {
                kaiCangStr.append(", ");
            }
            kaiCangStr.append(item.getValue());
            first = false;
        }
        kaiCangStr.append("]");
        log.info(kaiCangStr.toString());
        return queueKaiCang;
    }
    /**
     * 根据当前价格初始化平仓队列。遍历已有的升序价格队列,
     * 将大于当前价格的所有价格点加入升序的平仓队列中。
     *
     * @param jiaGe   当前价格
     * @param queueAsc 已初始化的价格升序队列
     */
    @Override
    public PriorityBlockingQueue<AscBigDecimal> initPingCang(BigDecimal jiaGe, PriorityBlockingQueue<AscBigDecimal> queueAsc) {
        PriorityBlockingQueue<AscBigDecimal> queuePingCang = WangGeListQueue.getPingCang();
        queuePingCang.clear();
        AscBigDecimal now = new AscBigDecimal(jiaGe.toString());
        for (AscBigDecimal ascBigDecimal : queueAsc) {
            if (ascBigDecimal.compareTo(now) > 0) {
                queuePingCang.add(ascBigDecimal);
            }
        }
        StringBuilder pingCangStr = new StringBuilder();
        pingCangStr.append("上限队列: [");
        boolean first = true;
        for (AscBigDecimal item : queuePingCang) {
            if (!first) {
                pingCangStr.append(", ");
            }
            pingCangStr.append(item.getValue());
            first = false;
        }
        pingCangStr.append("]");
        log.info(pingCangStr.toString());
        return queuePingCang;
    }
    /**
     * 主方法,用于测试网格初始化及开仓/平仓逻辑。
     * 示例使用固定价格"0.355"进行模拟调用。
     *
     * @param args 启动参数(未使用)
     */
    public static void main(String[] args) {
        WangGeListServiceImpl wangGeService = new WangGeListServiceImpl();
        String openPx = "2875";
        String markPx = "2905";
        String orderPx = "2895";
        PriorityBlockingQueue<AscBigDecimal> queueAsc = wangGeService.initWangGe(openPx);
        if (queueAsc != null) {
            wangGeService.initKaiCang(new BigDecimal(orderPx), queueAsc);
            wangGeService.initPingCang(new BigDecimal(orderPx), queueAsc);
        }
    }
}