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); } } }