Administrator
2025-12-16 2e05d1bab1817d265d7eaef667811fe3a5dbcb67
feat(okxNewPrice): 实现基于网格列表的交易策略

- 新增网格列表枚举类WangGeListEnum,定义不同价格区间的网格参数
- 创建网格列表队列管理类WangGeListQueue,用于管理网格交易相关队列
- 实现网格列表服务接口及其实现类,支持动态初始化网格、开仓和平仓操作
- 修改CaoZuoServiceImpl以支持新网格列表逻辑,增加对旧网格仓位的止损处理
- 更新PositionsWs以支持按持仓方向区分账户名,改进仓位管理精度
- 在OrderInfoWs中记录上一个网格信息,增强交易状态跟踪
- 调整InstrumentsWs移除重复的持仓方向设置,优化初始化流程
- 扩展CoinEnums和OrderParamEnums枚举,增加新交易状态标识
- 优化TradeOrderWs下单逻辑,完善按持仓方向的仓位状态检查
8 files modified
4 files added
613 ■■■■ changed files
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 187 ●●●●● 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 11 ●●●● patch | view | raw | blame | history
src/main/java/com/xcong/excoin/modules/okxNewPrice/okxWs/PositionsWs.java 18 ●●●●● 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 2 ●●●●● 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 69 ●●●●● 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 178 ●●●●● patch | view | raw | blame | history
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,35 @@
     */
    @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;
        }
        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);
            WsMapBuild.saveStringToMap(accountMap, CoinEnums.POSSIDE.name(), oldWangge.getFang_xiang());
            return OrderParamEnums.OUT.getValue();
        }
        String posSide = gridByPrice.getFang_xiang();
        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 +118,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");
@@ -176,15 +208,15 @@
                if (pingCang != null && avgPx.compareTo(pingCang.getValue()) < 0) {
                    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 +255,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");
@@ -325,15 +286,15 @@
                    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));
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,16 @@
                    }
                    WsMapBuild.saveStringToMap(TradeOrderWs.getAccountMap(accountName), "state", CoinEnums.ORDER_LIVE.getCode());
                    //保存上一个网格信息
                    WangGeListEnum gridByPrice = WangGeListEnum.getGridByPrice(new BigDecimal(avgPx));
                    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
@@ -47,10 +47,15 @@
        }
    }
    public static String initAccountName(String accountName, String posSide) {
        return accountName+"_"+ posSide;
    }
    public static void initEvent(JSONObject response, String accountName) {
//        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) {
@@ -62,7 +67,8 @@
            if (dataArray == null || dataArray.isEmpty()) {
//                log.info("账户持仓频道数据为空,已当前价买入,并且初始化网格");
                JSONObject posData = new JSONObject();
                initParam(posData, accountName);
                initParam(posData, accountName,CoinEnums.POSSIDE_LONG.getCode());
                initParam(posData, accountName,CoinEnums.POSSIDE_SHORT.getCode());
                return;
            }
@@ -104,8 +110,7 @@
                            last, idxPx, bePx, realizedPnl, settledPnl,
                            markPx,fee,fundingFee
                    );
                    initParam(posData, accountName);
                    initParam(posData, accountName,posSide);
                }
            }
        } catch (Exception e) {
@@ -113,8 +118,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"),
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,69 @@
package com.xcong.excoin.modules.okxNewPrice.okxWs.wanggeList;
import lombok.Getter;
import java.math.BigDecimal;
/**
 * @author Administrator
 * 网格数据枚举 数据
 * todo 后期考虑优化为可配置项
 */
@Getter
public enum WangGeListEnum {
    UP("上层做空", "2", "3000", "3200", "10", "short", "3214"),
    CENTER("中间指定一个方向", "2", "3000", "2700", "5", "long", "2986"),
    DOWN("下层做多", "2", "2500", "2700", "10", "long", "2586");
    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.fang_xiang = 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,178 @@
package com.xcong.excoin.modules.okxNewPrice.okxWs.wanggeList;
import com.xcong.excoin.modules.okxNewPrice.wangge.WangGeEnum;
import com.xcong.excoin.modules.okxNewPrice.wangge.WangGeQueue;
import com.xcong.excoin.modules.okxNewPrice.wangge.WangGeService;
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 = WangGeQueue.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 = WangGeQueue.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 = WangGeQueue.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);
        }
    }
}