10 files modified
4 files added
| | |
| | | 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; |
| | |
| | | */ |
| | | @Slf4j |
| | | @Component |
| | | @ConditionalOnProperty(prefix = "app", name = "websocket", havingValue = "true") |
| | | @ConditionalOnProperty(prefix = "app", name = "quant", havingValue = "true") |
| | | public class OkxNewPriceWebSocketClient { |
| | | @Resource |
| | | private WebsocketPriceService websocketPriceService; |
| | |
| | | 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; |
| | |
| | | } |
| | | 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 服务器的连接。 |
| | |
| | | */ |
| | | 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 |
| | |
| | | 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()); |
| | |
| | | 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); |
| | | } |
| | |
| | | |
| | | try { |
| | | InstrumentsWs.handleEvent(account.name()); |
| | | wangGeService.initWangGe(); |
| | | SSLConfig.configureSSL(); |
| | | System.setProperty("https.protocols", "TLSv1.2,TLSv1.3"); |
| | | String WS_URL = WS_URL_MONIPAN; |
| | |
| | | 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; |
| | |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.util.Map; |
| | | import java.util.concurrent.PriorityBlockingQueue; |
| | | |
| | | /** |
| | |
| | | public class CaoZuoServiceImpl implements CaoZuoService { |
| | | |
| | | private final WangGeService wangGeService; |
| | | private final WangGeListService wangGeListService; |
| | | private final RedisUtils redisUtils; |
| | | |
| | | /** |
| | | * 执行主要的操作逻辑,包括读取合约状态、获取市场价格信息, |
| | |
| | | */ |
| | | @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; |
| | | } |
| | | // 系统设置的开关,等于冷静中,则代表不开仓 |
| | |
| | | } |
| | | } |
| | | |
| | | 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"); |
| | |
| | | } |
| | | 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){ |
| | |
| | | } |
| | | 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)); |
| | | |
| | |
| | | |
| | | @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"); |
| | |
| | | } |
| | | 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)); |
| | | |
| | |
| | | } |
| | | 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){ |
| | |
| | | //判断当前价格和开仓价格直接间隔除以间距,取整,获取的数量是否大于等于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; |
| | |
| | | //判断当前价格和开仓价格直接间隔除以间距,取整,获取的数量是否大于等于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; |
| | |
| | | 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()); |
| | | } |
| | | } |
| | |
| | | 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; |
| | |
| | | } |
| | | 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()); |
| | | |
| | |
| | | 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; |
| | | } |
| | | |
| | |
| | | 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"); |
| | |
| | | + "维持保证金率: {}, 维持保证金: {}, 以美金价值为单位的持仓数量: {}, 占用保证金的币种: {}, " |
| | | + "最新成交价: {}, 最新指数价格: {}, 盈亏平衡价: {}, 已实现收益: {}, 累计已结算收益: {}" |
| | | + "最新标记价格: {},累计手续费: {},累计持仓费: {},", |
| | | 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) { |
| | |
| | | } |
| | | } |
| | | |
| | | 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"))); |
| | |
| | | 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; |
| | | } |
| | | // 校验必要参数 |
| | |
| | | log.warn("下单参数 side 为空,取消发送"); |
| | | return; |
| | | } |
| | | String posSide = InstrumentsWs.getAccountMap(accountName).get(CoinEnums.POSSIDE.name()); |
| | | // 校验必要参数 |
| | | if (StrUtil.isBlank(posSide)) { |
| | | log.warn("下单参数 posSide 为空,取消发送"); |
| | |
| | | }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(); |
| | |
| | | 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; |
| | |
| | | }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"); |
| | |
| | | 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); |
| | |
| | | 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"), |
| | |
| | | //下单的总保障金为账户总金额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"), |
| | |
| | | OKX_UAT_ceshi("ffb4e79f-fcf5-4afb-82c5-2fbb64123f61", |
| | | "AA06C5ED1D7C7F5AFE6484052E231C55", |
| | | "Aa12345678@", |
| | | false), |
| | | false); |
| | | // |
| | | // /** |
| | | // * 模拟盘账户2信息 |
| | |
| | | // "2A5BD55BF0771F1ADF08AE0A2FB4D561", |
| | | // "Aa12345678@", |
| | | // true); |
| | | OKX_UAT2("7a023eb2-06c0-4255-9969-b86ea1cef0d7", |
| | | "D0106A4D63BD22BEAB9CBA8F41219661", |
| | | "Aa12345678@", |
| | | false); |
| | | // OKX_UAT2("7a023eb2-06c0-4255-9969-b86ea1cef0d7", |
| | | // "D0106A4D63BD22BEAB9CBA8F41219661", |
| | | // "Aa12345678@", |
| | | // false); |
| | | |
| | | /** |
| | | * 模拟盘账户3信息 |
| | |
| | | OUT_YES("冷静中", "冷静中"), |
| | | |
| | | ORDERING("操作下单中", "ORDERING"), |
| | | LIMIT("限价止损", "limit"), |
| | | OUT("止损", "out"), |
| | | INIT("初始化", "init"), |
| | | HOLDING("持仓", "holding"), |
| New file |
| | |
| | | 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; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | 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; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | 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); |
| | | |
| | | |
| | | } |
| | | |
| New file |
| | |
| | | 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); |
| | | } |
| | | } |
| | | } |