| package com.xcong.excoin.rabbit.pricequeue; | 
|   | 
| import cn.hutool.core.collection.CollUtil; | 
| import cn.hutool.core.map.MapUtil; | 
| import cn.hutool.core.util.StrUtil; | 
| import com.alibaba.fastjson.JSONObject; | 
| import com.xcong.excoin.common.contants.AppContants; | 
| import com.xcong.excoin.common.enumerates.CoinTypeEnum; | 
| import com.xcong.excoin.modules.contract.dao.ContractHoldOrderDao; | 
| import com.xcong.excoin.modules.contract.entity.ContractHoldOrderEntity; | 
| import com.xcong.excoin.modules.member.dao.MemberDao; | 
| import com.xcong.excoin.modules.member.dao.MemberWalletContractDao; | 
| import com.xcong.excoin.modules.member.entity.MemberEntity; | 
| import com.xcong.excoin.modules.member.entity.MemberWalletContractEntity; | 
| import com.xcong.excoin.modules.platform.entity.PlatformTradeSettingEntity; | 
| import com.xcong.excoin.rabbit.pricequeue.whole.HoldOrderDataModel; | 
| import com.xcong.excoin.rabbit.pricequeue.whole.WholeDataQueue; | 
| import com.xcong.excoin.rabbit.pricequeue.whole.WholePriceDataModel; | 
| import com.xcong.excoin.rabbit.producer.OrderProducer; | 
| import com.xcong.excoin.utils.*; | 
| import lombok.extern.slf4j.Slf4j; | 
| import org.apache.commons.collections.CollectionUtils; | 
| import org.springframework.beans.factory.annotation.Autowired; | 
| import org.springframework.stereotype.Component; | 
| import org.springframework.transaction.annotation.Transactional; | 
|   | 
| import javax.annotation.Resource; | 
| import java.math.BigDecimal; | 
| import java.util.*; | 
| import java.util.concurrent.PriorityBlockingQueue; | 
|   | 
| @Slf4j | 
| @Component | 
| public class WebsocketPriceService { | 
|   | 
|     @Autowired | 
|     OrderProducer orderProducer; | 
|     @Resource | 
|     private RedisUtils redisUtils; | 
|     @Resource | 
|     private ContractHoldOrderDao contractHoldOrderDao; | 
|     @Resource | 
|     private MemberDao memberDao; | 
|     @Resource | 
|     private MemberWalletContractDao memberWalletContractDao; | 
|   | 
|     @Resource | 
|     private CacheSettingUtils cacheSettingUtils; | 
|     /** | 
|      * @param symbol | 
|      * @param price | 
|      */ | 
|     public void comparePriceAsc(String symbol, String price) { | 
|         // 比较价格 正序的 最小元素在头部 开多止盈 开空止损等 | 
|         PriorityBlockingQueue<AscBigDecimal> queue = PricePriorityQueue.getQueueAsc(symbol); | 
|         // 最小的 | 
|         AscBigDecimal b = queue.peek(); | 
|         // 当前价 | 
|         AscBigDecimal now = new AscBigDecimal(price); | 
|         List<AscBigDecimal> list = new ArrayList<AscBigDecimal>(); | 
|         // 找到所有比当前价格大的 是需要操作的 | 
|         if (b != null && b.compareTo(now) <= 0) { | 
|             // 可以操作 | 
|             System.out.println("当前价格:" + price + "---正序---" + "队列价格:" + b.getValue().toPlainString() + " time:" + new Date()); | 
|             while (queue.peek() != null && queue.peek().compareTo(now) <= 0) { | 
|                 // 可以发送消息操作 | 
|                 list.add(queue.remove()); | 
|             } | 
|         } | 
|   | 
|         if (CollectionUtils.isNotEmpty(list)) { | 
|             dealAscPriceOrderAndSenMq(list, symbol); | 
|         } | 
|   | 
|     } | 
|   | 
|     public void comparePriceDesc(String symbol, String price) { | 
|         // 比较价格 倒叙的 开多止损  开空止盈 | 
|         PriorityBlockingQueue<DescBigDecimal> queue = PricePriorityQueue.getQueueDesc(symbol); | 
|         // 最大价格 | 
|         DescBigDecimal b = queue.peek(); | 
|         // 当前价格 | 
|         DescBigDecimal now = new DescBigDecimal(price); | 
|         List<DescBigDecimal> list = new ArrayList<DescBigDecimal>(); | 
|         // 找到比当前价格还大的就是需要操作的 开多止损 | 
|         // 即最大的币当前价大 那么需要开多止损 | 
|         if (b != null && b.compareTo(now) <= 0) { | 
|             // 可以操作 | 
|             System.out.println("当前价格:" + price + "---倒序操作---" + "队列:" + b.getValue().toPlainString() + " time:" + new Date()); | 
|   | 
|             while (queue.peek() != null && queue.peek().compareTo(now) <= 0) { | 
|                 // 可以发送消息操作 | 
|                 list.add(queue.remove()); | 
|                 log.info("#{}#", JSONObject.toJSONString(list)); | 
|             } | 
|         } | 
|         if (CollectionUtils.isNotEmpty(list)) { | 
|             dealDescPriceOrderAndSenMq(list, symbol); | 
|         } | 
|   | 
|     } | 
|   | 
|     private void addExecType(OrderModel model) { | 
|         List<Object> orderTypes = redisUtils.lGet(AppContants.RABBIT_TYPE + model.getOrderId(), 0, -1); | 
|         if (CollUtil.isNotEmpty(orderTypes)) { | 
|             orderTypes.add(model.getType()); | 
|         } else { | 
|             orderTypes = new ArrayList<>(); | 
|             orderTypes.add(model.getType()); | 
|         } | 
|   | 
|         redisUtils.lSet(AppContants.RABBIT_TYPE + model.getOrderId(), orderTypes, 10); | 
|         redisUtils.lSet(AppContants.MEMBER_TYPE + model.getMemberId(), orderTypes, 5); | 
|     } | 
|   | 
|     // 处理消息 正序的 包括 | 
|     // 1:买入委托2:开多3:开空4:平多5:平空6:爆仓平多7:爆仓平空8:撤单9:止盈平多10:止盈平空11:止损平多12:止损平空 | 
|     public void dealAscPriceOrderAndSenMq(List<AscBigDecimal> list, String symbol) { | 
|         if (CollectionUtils.isNotEmpty(list)) { | 
|             // 根据不同类型发送不同消息  1 倒序 2  正序 | 
|             List<OrderModel> orderModelList = new ArrayList<OrderModel>(); | 
|             // 3 正序 | 
|             Map<String, List<OrderModel>> orderMap = PricePriorityQueue.getOrderMap(symbol, 3); | 
|             // 根据价格查询到对应的订单 | 
|             for (AscBigDecimal asc : list) { | 
|                 String key = asc.getValue().toPlainString(); | 
|                 assert orderMap != null; | 
|                 log.info("----->->{}, --> {}", JSONObject.toJSONString(orderMap), key); | 
|                 if (orderMap.containsKey(key)) { | 
|                     orderModelList.addAll(orderMap.get(key)); | 
|                     orderMap.remove(key); | 
|                 } | 
|   | 
|             } | 
|             log.info("------>{}", JSONObject.toJSONString(orderModelList)); | 
|             if (CollectionUtils.isEmpty(orderModelList)) { | 
|                 return; | 
|             } | 
|             log.info("本次执行的列表ASC"); | 
|             // 根据订单的类型发送消息 | 
|             // 3:开空  7:爆仓平空 | 
|             // 9:止盈平多 12:止损平空 | 
|             for (OrderModel model : orderModelList) { | 
|                 /* | 
|                    问题: 1、逐仓: 当行情大时,若设置的止损点与爆仓过于接近,则可能会出现直接爆仓,而不止损的情况 | 
|                         2、全仓: 止盈价/止损价 设置的与委托平仓价相同,需优先处理止盈/止损 | 
|                    解决: 将订单ID作为Key, 该订单执行的队列类型集合作为value, 用于在执行爆仓、委托平仓时,是否存在止盈/止损,若存在则不执行该爆仓和委托平仓 | 
|                  */ | 
|                 addExecType(model); | 
|   | 
|                 // 止损平空 | 
|                 List<OrderModel> kkzsList = new ArrayList<OrderModel>(); | 
|                 // 止盈平多 | 
|                 List<OrderModel> kdzyList = new ArrayList<OrderModel>(); | 
|                 // 爆仓平空 | 
|                 List<OrderModel> bcList = new ArrayList<OrderModel>(); | 
|                 // 开空 | 
|                 List<OrderModel> wtkkList = new ArrayList<OrderModel>(); | 
|                 // 委托平多 | 
|                 List<OrderModel> wtpdList = new ArrayList<>(); | 
|                 switch (model.getType()) { | 
|                     case 3: | 
|                         wtkkList.add(model); | 
|                         break; | 
|                     case 4: | 
|                         wtpdList.add(model); | 
|                         break; | 
|                     case 7: | 
|                         bcList.add(model); | 
|                         break; | 
|                     case 9: | 
|                         kdzyList.add(model); | 
|                         break; | 
|                     case 12: | 
|                         kkzsList.add(model); | 
|                         break; | 
|                     default: | 
|                         log.info("#price-service unknown type#"); | 
|                         break; | 
|                 } | 
|   | 
|                 // 发送消息 | 
|                 if (CollectionUtils.isNotEmpty(kkzsList)) { | 
|                     String kkzs = JSONObject.toJSONString(kkzsList); | 
|                     orderProducer.sendLessLoss(kkzs); | 
|                 } | 
|                 if (CollectionUtils.isNotEmpty(kdzyList)) { | 
|                     String kdzy = JSONObject.toJSONString(kdzyList); | 
|                     orderProducer.sendMorePro(kdzy); | 
|                 } | 
|                 if (CollectionUtils.isNotEmpty(bcList)) { | 
|                     orderProducer.sendCoinout(JSONObject.toJSONString(bcList)); | 
|                 } | 
|                 if (CollectionUtils.isNotEmpty(wtkkList)) { | 
|                     orderProducer.sendLimit(JSONObject.toJSONString(wtkkList)); | 
|                 } | 
|                 if (CollectionUtils.isNotEmpty(wtpdList)) { | 
|                     orderProducer.sendLimitClose(JSONObject.toJSONString(wtpdList)); | 
|                 } | 
|             } | 
|         } | 
|     } | 
|   | 
|     // 处理消息 正序的 包括 | 
|     // 1:买入委托2:开多3:开空4:平多5:平空6:爆仓平多7:爆仓平空8:撤单9:止盈平多10:止盈平空11:止损平多12:止损平空 | 
|     public void dealDescPriceOrderAndSenMq(List<DescBigDecimal> list, String symbol) { | 
|         if (CollectionUtils.isNotEmpty(list)) { | 
|             // 根据不同类型发送不同消息  1 倒序 2  正序 | 
|             List<OrderModel> orderModelList = new ArrayList<OrderModel>(); | 
|             Map<String, List<OrderModel>> orderMap = PricePriorityQueue.getOrderMap(symbol, 2); | 
|             // 根据价格查询到对应的订单 | 
|             for (DescBigDecimal desc : list) { | 
|                 String key = desc.getValue().toPlainString(); | 
|                 assert orderMap != null; | 
|                 log.info("----->->{}, --> {}", JSONObject.toJSONString(orderMap), key); | 
|                 if (orderMap.containsKey(key)) { | 
|                     orderModelList.addAll(orderMap.get(key)); | 
|                     orderMap.remove(key); | 
|                 } | 
|   | 
|             } | 
|   | 
|             if (CollectionUtils.isEmpty(orderModelList)) { | 
|                 return; | 
|             } | 
|             log.info("本次执行的列表Desc"); | 
|             // 根据订单的类型发送消息 | 
|             // 2:开多6:爆仓平多 | 
|             // 10:止盈平空11:止损平多 | 
|             for (OrderModel model : orderModelList) { | 
|                 /* | 
|                    问题: 1、逐仓: 当行情大时,若设置的止损点与爆仓过于接近,则可能会出现直接爆仓,而不止损的情况 | 
|                         2、全仓: 止盈价/止损价 设置的与委托平仓价相同,需优先处理止盈/止损 | 
|                    解决: 将订单ID作为Key, 该订单执行的队列类型集合作为value, 用于在执行爆仓、委托平仓时,是否存在止盈/止损,若存在则不执行该爆仓和委托平仓 | 
|                  */ | 
|                 addExecType(model); | 
|   | 
|                 // 开空止盈 | 
|                 List<OrderModel> kkzyList = new ArrayList<OrderModel>(); | 
|                 // 开多止损 | 
|                 List<OrderModel> kdzsList = new ArrayList<OrderModel>(); | 
|                 // 爆仓 | 
|                 List<OrderModel> bcList = new ArrayList<OrderModel>(); | 
|                 // 开多委托 | 
|                 List<OrderModel> wtkdList = new ArrayList<OrderModel>(); | 
|                 // 委托平空 | 
|                 List<OrderModel> wtpkList = new ArrayList<>(); | 
|                 switch (model.getType()) { | 
|                     case 2: | 
|                         wtkdList.add(model); | 
|                         break; | 
|                     case 5: | 
|                         wtpkList.add(model); | 
|                         break; | 
|                     case 6: | 
|                         bcList.add(model); | 
|                         break; | 
|                     case 10: | 
|                         kkzyList.add(model); | 
|                         break; | 
|                     case 11: | 
|                         kdzsList.add(model); | 
|                         break; | 
|                     default: | 
|                         break; | 
|                 } | 
|   | 
|                 // 发送消息 | 
|                 if (CollectionUtils.isNotEmpty(kkzyList)) { | 
|                     String kkzy = JSONObject.toJSONString(kkzyList); | 
|                     orderProducer.sendLessPro(kkzy); | 
|                 } | 
|                 if (CollectionUtils.isNotEmpty(kdzsList)) { | 
|                     String kdzs = JSONObject.toJSONString(kdzsList); | 
|                     orderProducer.sendMoreLoss(kdzs); | 
|                 } | 
|                 if (CollectionUtils.isNotEmpty(bcList)) { | 
|                     orderProducer.sendCoinout(JSONObject.toJSONString(bcList)); | 
|                 } | 
|                 if (CollectionUtils.isNotEmpty(wtkdList)) { | 
|                     orderProducer.sendLimit(JSONObject.toJSONString(wtkdList)); | 
|                 } | 
|                 if (CollectionUtils.isNotEmpty(wtpkList)) { | 
|                     orderProducer.sendLimitClose(JSONObject.toJSONString(wtpkList)); | 
|                 } | 
|             } | 
|         } | 
|     } | 
|   | 
|     public void wholeBomb() { | 
|         Map<String, WholePriceDataModel> dataModelMap = WholeDataQueue.MAP; | 
|         if (CollUtil.isEmpty(dataModelMap)) { | 
|             return; | 
|         } | 
|   | 
|         for (Map.Entry<String, WholePriceDataModel> entry : dataModelMap.entrySet()) { | 
|             WholePriceDataModel wholePriceData = entry.getValue(); | 
|             List<HoldOrderDataModel> list = wholePriceData.getList(); | 
|   | 
|             if (CollUtil.isNotEmpty(list)) { | 
|                 BigDecimal totalProfitOrLoss = BigDecimal.ZERO; | 
|   | 
|                 MemberEntity memberEntity = memberDao.selectById(Long.parseLong(entry.getKey())); | 
|                 Map<String, BigDecimal> prices = new HashMap<>(); | 
|                 for (HoldOrderDataModel holdOrderData : list) { | 
|                     String price = redisUtils.getString(CoinTypeConvert.convertToKey(holdOrderData.getSymbol())); | 
|                     BigDecimal newPrice = new BigDecimal(price); | 
| //                    BigDecimal newPrice = new BigDecimal("29958.46627789"); | 
|   | 
|                     BigDecimal rewardRatio = null; | 
|                     if (ContractHoldOrderEntity.OPENING_TYPE_MORE == holdOrderData.getOpeningType()) { | 
|                         // (最新价-开仓价)*规格*张数 | 
|                         rewardRatio = newPrice.subtract(holdOrderData.getOpeningPrice()).multiply(holdOrderData.getSymbolSku()).multiply(new BigDecimal(holdOrderData.getSymbolCntSale())); | 
|                         // 开空 | 
|                     } else { | 
|                         // (开仓价-最新价)*规格*张数 | 
|                         rewardRatio = holdOrderData.getOpeningPrice().subtract(newPrice).multiply(holdOrderData.getSymbolSku()).multiply(new BigDecimal(holdOrderData.getSymbolCntSale())); | 
|                     } | 
|   | 
|                     if (memberEntity.getIsProfit() == MemberEntity.IS_PROFIT_Y) { | 
|                         PlatformTradeSettingEntity tradeSettingEntity = cacheSettingUtils.getTradeSetting(); | 
|                         if (rewardRatio.compareTo(BigDecimal.ZERO) > -1) { | 
|                             rewardRatio = rewardRatio.multiply(BigDecimal.ONE.subtract(tradeSettingEntity.getProfitParam())); | 
|                         } else { | 
| //                        rewardRatio = rewardRatio.multiply(BigDecimal.ONE.add(tradeSettingEntity.getProfitParam())); | 
|                         } | 
|                     } | 
|   | 
|                     holdOrderData.setRewardAmount(rewardRatio); | 
|                     holdOrderData.setClosingPrice(newPrice); | 
|                     totalProfitOrLoss = totalProfitOrLoss.add(rewardRatio).setScale(8, BigDecimal.ROUND_DOWN); | 
|                     prices.put(holdOrderData.getSymbol(), newPrice); | 
|                 } | 
|   | 
|                 BigDecimal holdBond = wholePriceData.getHoldBond(); | 
|                 BigDecimal balance = wholePriceData.getBalance(); | 
|                 if (balance.add(totalProfitOrLoss).compareTo(holdBond) > 0) { | 
|                     continue; | 
|                 } | 
|   | 
|                 log.info("过来过来"); | 
|                 synchronized(this) { | 
|                     log.info("爆仓啥的:{}", entry.getKey()); | 
|                     boolean b = redisUtils.setNotExist(AppContants.WHOLE_BOMB_PREFIX + entry.getKey(), 1, 5); | 
|                     if (b) { | 
|                         dataModelMap.remove(entry.getKey()); | 
|                         wholePriceData.setEquity(wholePriceData.getBalance().add(totalProfitOrLoss)); | 
|                         redisUtils.set(AppContants.WHOLE_BOMB_MAP, JSONObject.toJSONString(dataModelMap)); | 
|                         log.info("全仓爆仓触发:{}", JSONObject.toJSONString(wholePriceData)); | 
|                         wholePriceData.setPrices(prices); | 
|                         contractHoldOrderDao.updateMemberAllHoldOrderClosingStatus(wholePriceData.getMemberId()); | 
|                         orderProducer.sendWholeBomb(JSONObject.toJSONString(wholePriceData)); | 
|                     } | 
|                 } | 
|             } | 
|         } | 
|     } | 
|   | 
|   | 
|     public void wholeBomb(String symbol, String price) { | 
|         List<Long> memberIds = contractHoldOrderDao.selectMemberHasWholeOrder(); | 
|         CacheSettingUtils cacheSettingUtils = SpringContextHolder.getBean(CacheSettingUtils.class); | 
|   | 
|         if (CollUtil.isNotEmpty(memberIds)) { | 
|             for (Long memberId : memberIds) { | 
|                 List<ContractHoldOrderEntity> holdOrderEntities = contractHoldOrderDao.selectHoldOrderListByMemberId(memberId); | 
|                 MemberEntity memberEntity = memberDao.selectById(memberId); | 
|   | 
|                 if (CollUtil.isNotEmpty(holdOrderEntities)) { | 
|                     BigDecimal totalProfitOrLess = BigDecimal.ZERO; | 
|                     Map<String, Object> priceMap = new HashMap<>(); | 
|                     for (ContractHoldOrderEntity holdOrderEntity : holdOrderEntities) { | 
|                         String currentPrice = redisUtils.getString(CoinTypeConvert.convertToKey(holdOrderEntity.getSymbol())); | 
|                         priceMap.put(holdOrderEntity.getSymbol(), currentPrice); | 
|   | 
|                         BigDecimal lotNumber = cacheSettingUtils.getSymbolSku(holdOrderEntity.getSymbol()); | 
|                         BigDecimal profitOrLess = CalculateUtil.calOrderProfitOrLess(holdOrderEntity.getOpeningType(), new BigDecimal(currentPrice), holdOrderEntity.getOpeningPrice(), lotNumber, holdOrderEntity.getSymbolCntSale(), memberEntity.getIsProfit()); | 
|                         totalProfitOrLess = totalProfitOrLess.add(profitOrLess); | 
|                     } | 
|                     MemberWalletContractEntity wallet = memberWalletContractDao.findWalletContractByMemberIdAndSymbol(memberId, CoinTypeEnum.USDT.name()); | 
|   | 
|                     BigDecimal sub = wallet.getTotalBalance().add(totalProfitOrLess); | 
|                     BigDecimal target = wallet.getTotalBalance().multiply(BigDecimal.valueOf(0.01)); | 
| //                    log.info("sub : {}, target : {}", sub, target); | 
|                     if (sub.compareTo(target) <= 0) { | 
|                         List<OrderModel> list = new ArrayList<>(); | 
|                         OrderModel orderModel = new OrderModel(null, 0, price, symbol, memberId); | 
|                         list.add(orderModel); | 
|                         String content = JSONObject.toJSONString(list); | 
|   | 
|                         String key = AppContants.WHOLE_BOMB_PREFIX + memberId; | 
|                         Map<Object, Object> value = redisUtils.hmget(key); | 
|                         if (MapUtil.isEmpty(value)) { | 
|                             log.info("priceMap -- {}", priceMap); | 
|                             orderProducer.sendWholeBomb(content); | 
|                             contractHoldOrderDao.updateMemberAllHoldOrderClosingStatus(memberId); | 
|   | 
|                             redisUtils.hmset(key, priceMap); | 
|                         } | 
|                     } | 
|                 } | 
|             } | 
|         } | 
|     } | 
| } |