|  |  | 
 |  |  | 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 java.util.ArrayList; | 
 |  |  | import java.util.Date; | 
 |  |  | import java.util.List; | 
 |  |  | import java.util.Map; | 
 |  |  | import javax.annotation.Resource; | 
 |  |  | import java.math.BigDecimal; | 
 |  |  | import java.util.*; | 
 |  |  | import java.util.concurrent.PriorityBlockingQueue; | 
 |  |  |  | 
 |  |  | @Slf4j | 
 |  |  | 
 |  |  |  | 
 |  |  |     @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 | 
 |  |  | 
 |  |  |         List<AscBigDecimal> list = new ArrayList<AscBigDecimal>(); | 
 |  |  |         // 找到所有比当前价格大的 是需要操作的 | 
 |  |  |         if (b != null && b.compareTo(now) <= 0) { | 
 |  |  |             log.info("--->{}", b.getValue()); | 
 |  |  |             // 可以操作 | 
 |  |  |             System.out.println("当前价格:" + price + "---正序---" + "队列价格:" + b.getValue().toPlainString() + " time:" + new Date()); | 
 |  |  |             log.info("--->>>>{}", queue.peek().getValue()); | 
 |  |  |             while (queue.peek() != null && queue.peek().compareTo(now) <= 0) { | 
 |  |  |                 // 可以发送消息操作 | 
 |  |  |                 list.add(queue.remove()); | 
 |  |  | 
 |  |  |         // 找到比当前价格还大的就是需要操作的 开多止损 | 
 |  |  |         // 即最大的币当前价大 那么需要开多止损 | 
 |  |  |         if (b != null && b.compareTo(now) <= 0) { | 
 |  |  |             log.info("--->{}", b.getValue()); | 
 |  |  |             // 可以操作 | 
 |  |  |             System.out.println("当前价格:" + price + "---倒序操作---" + "队列:" + b.getValue().toPlainString() + " time:" + new Date()); | 
 |  |  |  | 
 |  |  |             log.info("--->>>>{}", queue.peek().getValue()); | 
 |  |  |             while (queue.peek() != null && queue.peek().compareTo(now) <= 0) { | 
 |  |  |                 // 可以发送消息操作 | 
 |  |  |                 list.add(queue.remove()); | 
 |  |  | 
 |  |  |  | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     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) { | 
 |  |  | 
 |  |  |             List<OrderModel> orderModelList = new ArrayList<OrderModel>(); | 
 |  |  |             // 3 正序 | 
 |  |  |             Map<String, List<OrderModel>> orderMap = PricePriorityQueue.getOrderMap(symbol, 3); | 
 |  |  |             log.info("--->{}", JSONObject.toJSONString(orderMap)); | 
 |  |  |             // 根据价格查询到对应的订单 | 
 |  |  |             for (AscBigDecimal asc : list) { | 
 |  |  |                 String key = asc.getValue().toPlainString(); | 
 |  |  |                 log.info("------>>>>>{}", key); | 
 |  |  |                 assert orderMap != null; | 
 |  |  |                 log.info("======={}", orderMap.containsKey(key)); | 
 |  |  |                 log.info("----->->{}, --> {}", JSONObject.toJSONString(orderMap), key); | 
 |  |  |                 if (orderMap.containsKey(key)) { | 
 |  |  |                     orderModelList.addAll(orderMap.get(key)); | 
 |  |  |                     orderMap.remove(key); | 
 |  |  |                 } | 
 |  |  |  | 
 |  |  |             } | 
 |  |  |             log.info("{}", orderModelList); | 
 |  |  |             log.info("------>{}", JSONObject.toJSONString(orderModelList)); | 
 |  |  |             if (CollectionUtils.isEmpty(orderModelList)) { | 
 |  |  |                 return; | 
 |  |  |             } | 
 |  |  |             System.out.println("本次执行的列表ASC"); | 
 |  |  |             System.out.println(JSONObject.toJSONString(orderModelList)); | 
 |  |  |             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> 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); | 
 |  |  | 
 |  |  |                         kkzsList.add(model); | 
 |  |  |                         break; | 
 |  |  |                     default: | 
 |  |  |                         log.info("#price-service unknow type#"); | 
 |  |  |                         log.info("#price-service unknown type#"); | 
 |  |  |                         break; | 
 |  |  |                 } | 
 |  |  |  | 
 |  |  | 
 |  |  |                 if (CollectionUtils.isNotEmpty(wtkkList)) { | 
 |  |  |                     orderProducer.sendLimit(JSONObject.toJSONString(wtkkList)); | 
 |  |  |                 } | 
 |  |  |                 if (CollectionUtils.isNotEmpty(wtpdList)) { | 
 |  |  |                     orderProducer.sendLimitClose(JSONObject.toJSONString(wtpdList)); | 
 |  |  |                 } | 
 |  |  |             } | 
 |  |  |         } | 
 |  |  |     } | 
 |  |  | 
 |  |  |             // 根据不同类型发送不同消息  1 倒序 2  正序 | 
 |  |  |             List<OrderModel> orderModelList = new ArrayList<OrderModel>(); | 
 |  |  |             Map<String, List<OrderModel>> orderMap = PricePriorityQueue.getOrderMap(symbol, 2); | 
 |  |  |             log.info("--->{}", JSONObject.toJSONString(orderMap)); | 
 |  |  |             // 根据价格查询到对应的订单 | 
 |  |  |             for (DescBigDecimal desc : list) { | 
 |  |  |                 String key = desc.getValue().toPlainString(); | 
 |  |  |                 log.info("------>>>>>{}", key); | 
 |  |  |                 assert orderMap != null; | 
 |  |  |                 log.info("======={}", orderMap.containsKey(key)); | 
 |  |  |                 log.info("----->->{}, --> {}", JSONObject.toJSONString(orderMap), key); | 
 |  |  |                 if (orderMap.containsKey(key)) { | 
 |  |  |                     orderModelList.addAll(orderMap.get(key)); | 
 |  |  |                     orderMap.remove(key); | 
 |  |  | 
 |  |  |  | 
 |  |  |             } | 
 |  |  |  | 
 |  |  |             log.info("{}", orderModelList); | 
 |  |  |             if (CollectionUtils.isEmpty(orderModelList)) { | 
 |  |  |                 return; | 
 |  |  |             } | 
 |  |  |             System.out.println("本次执行的列表Desc"); | 
 |  |  |             System.out.println(JSONObject.toJSONString(orderModelList)); | 
 |  |  |             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> 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); | 
 |  |  | 
 |  |  |                 } | 
 |  |  |                 if (CollectionUtils.isNotEmpty(wtkdList)) { | 
 |  |  |                     orderProducer.sendLimit(JSONObject.toJSONString(wtkdList)); | 
 |  |  |  | 
 |  |  |                 } | 
 |  |  |                 if (CollectionUtils.isNotEmpty(wtpkList)) { | 
 |  |  |                     orderProducer.sendLimitClose(JSONObject.toJSONString(wtpkList)); | 
 |  |  |                 } | 
 |  |  |             } | 
 |  |  |         } | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     @Transactional(rollbackFor = Exception.class) | 
 |  |  |     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; | 
 |  |  |                 } | 
 |  |  |  | 
 |  |  |                 synchronized (this) { | 
 |  |  |                     if (entry.getKey() != null) { | 
 |  |  |                         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); | 
 |  |  |                         } | 
 |  |  |                     } | 
 |  |  |                 } | 
 |  |  |             } | 
 |  |  |         } | 
 |  |  |     } | 
 |  |  | } |