package com.xcong.excoin.modules.contract.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; import com.xcong.excoin.common.contants.AppContants; import com.xcong.excoin.common.enumerates.CoinTypeEnum; import com.xcong.excoin.common.enumerates.OrderClosingTypeEnum; import com.xcong.excoin.common.system.service.CommonService; import com.xcong.excoin.modules.contract.dao.ContractEntrustOrderDao; import com.xcong.excoin.modules.contract.dao.ContractHoldOrderDao; import com.xcong.excoin.modules.contract.dao.ContractOrderDao; import com.xcong.excoin.modules.contract.entity.ContractEntrustOrderEntity; import com.xcong.excoin.modules.contract.entity.ContractHoldOrderEntity; import com.xcong.excoin.modules.contract.entity.ContractOrderEntity; import com.xcong.excoin.modules.contract.mapper.ContractHoldOrderEntityMapper; import com.xcong.excoin.modules.contract.service.RabbitOrderService; import com.xcong.excoin.modules.documentary.common.NoticeConstant; import com.xcong.excoin.modules.documentary.dao.FollowFollowerOrderRelationDao; import com.xcong.excoin.modules.documentary.dao.FollowFollowerProfitDao; import com.xcong.excoin.modules.documentary.dao.FollowTraderInfoDao; import com.xcong.excoin.modules.documentary.dao.FollowTraderProfitDetailDao; import com.xcong.excoin.modules.documentary.entity.FollowFollowerOrderRelationEntity; import com.xcong.excoin.modules.documentary.entity.FollowFollowerProfitEntity; import com.xcong.excoin.modules.documentary.entity.FollowTraderInfoEntity; import com.xcong.excoin.modules.documentary.entity.FollowTraderProfitDetailEntity; import com.xcong.excoin.modules.documentary.service.FollowOrderOperationService; import com.xcong.excoin.modules.member.dao.MemberDao; import com.xcong.excoin.modules.member.dao.MemberSettingDao; import com.xcong.excoin.modules.member.dao.MemberWalletContractDao; import com.xcong.excoin.modules.member.entity.AgentReturnEntity; import com.xcong.excoin.modules.member.entity.MemberEntity; import com.xcong.excoin.modules.member.entity.MemberSettingEntity; import com.xcong.excoin.modules.member.entity.MemberWalletContractEntity; import com.xcong.excoin.modules.platform.entity.PlatformTradeSettingEntity; import com.xcong.excoin.rabbit.pricequeue.OrderModel; import com.xcong.excoin.utils.*; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * @author wzy * @date 2020-06-01 **/ @Slf4j @Service public class RabbitOrderServiceImpl implements RabbitOrderService { @Resource private MemberDao memberDao; @Resource private OrderWebsocketServiceImpl orderWebsocketService; @Resource private ContractHoldOrderDao contractHoldOrderDao; @Resource private ContractOrderDao contractOrderDao; @Resource private CommonService commonService; @Resource private MemberWalletContractDao memberWalletContractDao; @Resource private CacheSettingUtils cacheSettingUtils; @Resource private RedisUtils redisUtils; @Resource private MemberSettingDao memberSettingDao; @Resource private FollowFollowerOrderRelationDao followFollowerOrderRelationDao; @Resource private FollowOrderOperationService followOrderOperationService; @Resource private FollowTraderInfoDao followTraderInfoDao; @Resource private FollowTraderProfitDetailDao followTraderProfitDetailDao; @Resource private FollowFollowerProfitDao followFollowerProfitDao; @Resource private ContractEntrustOrderDao contractEntrustOrderDao; @Transactional(rollbackFor = Exception.class) @Override public void cancelHoldOrder(List ids) { if (CollUtil.isNotEmpty(ids)) { try { if (ids.size() == 1) { ContractHoldOrderEntity holdOrderEntity = contractHoldOrderDao.selectById(ids.get(0)); // 判断仓位类型是否逐仓 if (holdOrderEntity.getPositionType() == ContractEntrustOrderEntity.POSITION_TYPE_ADD) { // 逐仓平仓 cancelHoldOrderMethod(holdOrderEntity); } else { // 全仓模式平仓 closingWholeOrder(holdOrderEntity); } } else { List holdOrderEntities = contractHoldOrderDao.selectBatchIds(ids); if (CollUtil.isNotEmpty(holdOrderEntities)) { for (ContractHoldOrderEntity holdOrder : holdOrderEntities) { cancelHoldOrderMethod(holdOrder); } } } }catch (Exception e) { log.error("平仓异常", e); } } } @Transactional(rollbackFor = Exception.class) public void cancelHoldOrderMethod(ContractHoldOrderEntity holdOrderEntity) { String symbol = holdOrderEntity.getSymbol(); // 获取最新价 BigDecimal newPrice = new BigDecimal(redisUtils.getString(CoinTypeConvert.convertToKey(symbol))); MemberEntity memberEntity = memberDao.selectById(holdOrderEntity.getMemberId()); MemberWalletContractEntity walletContract = memberWalletContractDao.findWalletContractByMemberIdAndSymbol(holdOrderEntity.getMemberId(), CoinTypeEnum.USDT.name()); if (walletContract != null) { // 删除持仓表订单 contractHoldOrderDao.deleteById(holdOrderEntity.getId()); BigDecimal lotNumber = cacheSettingUtils.getSymbolSku(symbol); // 盈亏 BigDecimal profitOrLoss = BigDecimal.ZERO; Integer orderType = null; Integer closingType = null; MemberSettingEntity memberSettingEntity = memberSettingDao.selectMemberSettingByMemberId(memberEntity.getId()); // 开多 if (ContractHoldOrderEntity.OPENING_TYPE_MORE == holdOrderEntity.getOpeningType()) { newPrice = newPrice.multiply(BigDecimal.ONE.subtract(memberSettingEntity.getClosingSpread().divide(BigDecimal.valueOf(10000), 4, BigDecimal.ROUND_DOWN))); // (最新价-开仓价)*规格*张数 profitOrLoss = newPrice.subtract(holdOrderEntity.getOpeningPrice()).multiply(lotNumber).multiply(new BigDecimal(holdOrderEntity.getSymbolCnt())); orderType = ContractOrderEntity.ORDER_TYPE_CLOSE_MORE; closingType = OrderClosingTypeEnum.CLOSE_MORE.getValue(); // 开空 } else { newPrice = newPrice.multiply(BigDecimal.ONE.add(memberSettingEntity.getClosingSpread().divide(BigDecimal.valueOf(10000), 4, BigDecimal.ROUND_DOWN))); // (开仓价-最新价)*规格*张数 profitOrLoss = holdOrderEntity.getOpeningPrice().subtract(newPrice).multiply(lotNumber).multiply(new BigDecimal(holdOrderEntity.getSymbolCnt())); orderType = ContractOrderEntity.ORDER_TYPE_CLOSE_LESS; closingType = OrderClosingTypeEnum.CLOSE_LESS.getValue(); } if (memberEntity.getIsProfit() == MemberEntity.IS_PROFIT_Y) { PlatformTradeSettingEntity tradeSettingEntity = cacheSettingUtils.getTradeSetting(); if (profitOrLoss.compareTo(BigDecimal.ZERO) > -1) { profitOrLoss = profitOrLoss.multiply(BigDecimal.ONE.subtract(tradeSettingEntity.getProfitParam())); } else { profitOrLoss = profitOrLoss.multiply(BigDecimal.ONE.add(tradeSettingEntity.getProfitParam())); } } // 盈亏比例(回报率) BigDecimal rewardRatio = profitOrLoss.divide(holdOrderEntity.getBondAmount().subtract(holdOrderEntity.getOpeningFeeAmount()), 8, BigDecimal.ROUND_DOWN); FollowTraderInfoEntity traderInfoEntity = null; // 判断当前订单是否为跟单 if (ContractOrderEntity.CONTRACTTYPE_DOCUMENTARY == holdOrderEntity.getContractType()) { // 若不为交易员,则计算跟随者订单返利 if (!MemberEntity.IS_TRADER_Y.equals(memberEntity.getIsTrader())) { traderInfoEntity = followTraderInfoDao.selectTraderInfoByOrderId(holdOrderEntity.getId()); if (profitOrLoss.compareTo(BigDecimal.ZERO) > 0) { // 计算需返利给交易员的金额 BigDecimal returnMoney = profitOrLoss.multiply(traderInfoEntity.getProfitRatio()); profitOrLoss = profitOrLoss.subtract(returnMoney); MemberWalletContractEntity traderWallet = memberWalletContractDao.findWalletContractByMemberIdAndSymbol(traderInfoEntity.getMemberId(), CoinTypeEnum.USDT.name()); memberWalletContractDao.increaseWalletContractBalanceById(returnMoney, returnMoney, null, traderWallet.getId()); insertReturnProfitDetail(traderInfoEntity.getMemberId(), memberEntity.getId(), returnMoney, holdOrderEntity.getOrderNo()); } } } ContractOrderEntity contractOrderEntity = ContractHoldOrderEntityMapper.INSTANCE.holdOrderToOrder(holdOrderEntity); contractOrderEntity.setId(null); contractOrderEntity.setOrderType(orderType); contractOrderEntity.setClosingPrice(newPrice); contractOrderEntity.setClosingFeeAmount(holdOrderEntity.getOpeningFeeAmount()); contractOrderEntity.setClosingTime(new Date()); contractOrderEntity.setClosingType(closingType); contractOrderEntity.setRewardAmount(profitOrLoss); contractOrderEntity.setRewardRatio(rewardRatio); contractOrderDao.insert(contractOrderEntity); // 计算盈利或亏损后可用金额和总金额应该增加或减少的 BigDecimal addMoney = holdOrderEntity.getBondAmount().subtract(holdOrderEntity.getOpeningFeeAmount()).add(profitOrLoss); memberWalletContractDao.increaseWalletContractBalanceById(addMoney, profitOrLoss.subtract(contractOrderEntity.getOpeningFeeAmount()), null, walletContract.getId()); // 流水 LogRecordUtils.insertMemberAccountFlow(memberEntity.getId(), addMoney, walletContract.getAvailableBalance().add(addMoney), holdOrderEntity.getSymbol(), "平仓", "平仓"); // 计算佣金 ThreadPoolUtils.calReturnMoney(memberEntity.getId(), contractOrderEntity.getClosingFeeAmount(), contractOrderEntity, AgentReturnEntity.ORDER_TYPE_CLOSE); // 判断当前持仓是否为跟单订单 if (ContractOrderEntity.CONTRACTTYPE_DOCUMENTARY == holdOrderEntity.getContractType()) { updateFollowOrderRelation(holdOrderEntity.getId(), contractOrderEntity.getId()); // 若为交易员,则平仓跟随者订单 if (MemberEntity.IS_TRADER_Y.equals(memberEntity.getIsTrader())) { followOrderOperationService.closingFollowOrders(holdOrderEntity.getOrderNo()); } else { followFollowerProfitDao.updateFollowerProfitByTradeMemberId(holdOrderEntity.getBondAmount().subtract(holdOrderEntity.getOpeningFeeAmount()), profitOrLoss, traderInfoEntity.getMemberId(), memberEntity.getId()); LogRecordUtils.insertFollowerNotice(memberEntity.getId(), NoticeConstant.CLOSE_ORDER_TITLE, StrUtil.format(NoticeConstant.CLOSE_ORDER_CONTENT, contractOrderEntity.getSymbol(), contractOrderEntity.getClosingPrice(), profitOrLoss, traderInfoEntity.getNickname())); } } } } /** * 更新跟单订单关系 * * @param oldOrderId 当前持仓ID * @param newOrderId 历史订单ID */ public void updateFollowOrderRelation(Long oldOrderId, Long newOrderId) { FollowFollowerOrderRelationEntity orderRelationEntity = followFollowerOrderRelationDao.selectNowOneByorderId(oldOrderId); if (orderRelationEntity != null) { orderRelationEntity.setOrderId(newOrderId); orderRelationEntity.setOrderType(FollowFollowerOrderRelationEntity.ORDER_TYPE_HISTORY); followFollowerOrderRelationDao.updateById(orderRelationEntity); } } /** * 插入跟单返利明细 * * @param memberId 交易员会员ID * @param followMemberId 跟随者会员ID * @param amount 返利金额 * @param orderNo 订单编号 */ public void insertReturnProfitDetail(Long memberId, Long followMemberId, BigDecimal amount, String orderNo) { FollowTraderProfitDetailEntity profitDetailEntity = new FollowTraderProfitDetailEntity(); profitDetailEntity.setMemberId(memberId); profitDetailEntity.setFollowMemberId(followMemberId); profitDetailEntity.setAmount(amount); profitDetailEntity.setOrderNo(orderNo); followTraderProfitDetailDao.insert(profitDetailEntity); } /** * 全仓模式平仓逻辑 */ public void closingWholeOrder(ContractHoldOrderEntity holdOrderEntity) { String symbol = holdOrderEntity.getSymbol(); // 获取最新价 BigDecimal newPrice = new BigDecimal(redisUtils.getString(CoinTypeConvert.convertToKey(symbol))); MemberEntity memberEntity = memberDao.selectById(holdOrderEntity.getMemberId()); MemberWalletContractEntity walletContract = memberWalletContractDao.findWalletContractByMemberIdAndSymbol(holdOrderEntity.getMemberId(), CoinTypeConvert.convertContractTypeToCoin(symbol)); if (walletContract != null) { BigDecimal lotNumber = cacheSettingUtils.getSymbolSku(symbol); // 盈亏 BigDecimal profitOrLoss = BigDecimal.ZERO; Integer orderType = null; Integer closingType = null; // 获取平仓张数 Integer closeCnt = (Integer) redisUtils.get(AppContants.CLOSING_ORDER_PREFIX + holdOrderEntity.getId()); MemberSettingEntity memberSettingEntity = memberSettingDao.selectMemberSettingByMemberId(memberEntity.getId()); // 开多 if (ContractHoldOrderEntity.OPENING_TYPE_MORE == holdOrderEntity.getOpeningType()) { newPrice = newPrice.multiply(BigDecimal.ONE.subtract(memberSettingEntity.getClosingSpread().divide(BigDecimal.valueOf(10000), 4, BigDecimal.ROUND_DOWN))); // (最新价-开仓价)*规格*张数 profitOrLoss = newPrice.subtract(holdOrderEntity.getOpeningPrice()).multiply(lotNumber).multiply(new BigDecimal(closeCnt)); orderType = ContractOrderEntity.ORDER_TYPE_CLOSE_MORE; closingType = OrderClosingTypeEnum.CLOSE_MORE.getValue(); // 开空 } else { newPrice = newPrice.multiply(BigDecimal.ONE.add(memberSettingEntity.getClosingSpread().divide(BigDecimal.valueOf(10000), 4, BigDecimal.ROUND_DOWN))); // (开仓价-最新价)*规格*张数 profitOrLoss = holdOrderEntity.getOpeningPrice().subtract(newPrice).multiply(lotNumber).multiply(new BigDecimal(closeCnt)); orderType = ContractOrderEntity.ORDER_TYPE_CLOSE_LESS; closingType = OrderClosingTypeEnum.CLOSE_LESS.getValue(); } if (memberEntity.getIsProfit() == MemberEntity.IS_PROFIT_Y) { PlatformTradeSettingEntity tradeSettingEntity = cacheSettingUtils.getTradeSetting(); if (profitOrLoss.compareTo(BigDecimal.ZERO) > -1) { profitOrLoss = profitOrLoss.multiply(BigDecimal.ONE.subtract(tradeSettingEntity.getProfitParam())); } else { profitOrLoss = profitOrLoss.multiply(BigDecimal.ONE.add(tradeSettingEntity.getProfitParam())); } } log.info("profitOrLoss:{}", profitOrLoss); // 保证金 BigDecimal bondAmount = CalculateUtil.getBondAmount(holdOrderEntity.getOpeningPrice(), lotNumber, closeCnt, holdOrderEntity.getLeverRatio()); log.info("bondAmount:{}", bondAmount); // 平仓手续费 TODO 可能需要修复手续费 BigDecimal fee = BigDecimal.ZERO; if (holdOrderEntity.getSymbolCntSale() != 0) { fee = holdOrderEntity.getOpeningFeeAmount().divide(BigDecimal.valueOf(holdOrderEntity.getSymbolCnt()), 8, BigDecimal.ROUND_DOWN).multiply(BigDecimal.valueOf(closeCnt)); } else { fee = holdOrderEntity.getOpeningFeeAmount(); } log.info("fee:{}", fee); ContractOrderEntity contractOrderEntity = ContractHoldOrderEntityMapper.INSTANCE.holdOrderToOrder(holdOrderEntity); contractOrderEntity.setId(null); contractOrderEntity.setClosingPrice(newPrice); contractOrderEntity.setOrderType(orderType); contractOrderEntity.setClosingType(closingType); contractOrderEntity.setSymbolCnt(closeCnt); contractOrderEntity.setRewardAmount(profitOrLoss); contractOrderEntity.setBondAmount(bondAmount); contractOrderEntity.setClosingFeeAmount(fee); contractOrderDao.insert(contractOrderEntity); if (holdOrderEntity.getSymbolCntSale() != 0) { holdOrderEntity.setOpeningFeeAmount(holdOrderEntity.getOpeningFeeAmount().subtract(fee)); holdOrderEntity.setBondAmount(holdOrderEntity.getBondAmount().subtract(bondAmount)); contractHoldOrderDao.updateById(holdOrderEntity); } else { contractHoldOrderDao.deleteById(holdOrderEntity.getId()); } BigDecimal changeAmount = profitOrLoss.add(bondAmount); log.info("changeAmount : {}", changeAmount); BigDecimal total = profitOrLoss.subtract(fee); log.info("totalMoney : {}", total); memberWalletContractDao.increaseWalletContractBalanceById(changeAmount, total, null, walletContract.getId()); ThreadPoolUtils.sendWholeForceClosingPrice(holdOrderEntity.getSymbol(), memberEntity); // 计算佣金 ThreadPoolUtils.calReturnMoney(memberEntity.getId(), fee, contractOrderEntity, AgentReturnEntity.ORDER_TYPE_CLOSE); } } @Override public void entrustCloseOrder(List list) { if (CollUtil.isNotEmpty(list)) { List ids = new ArrayList<>(); list.forEach(model -> ids.add(model.getOrderId())); List contractEntrustOrderEntities = contractEntrustOrderDao.selectEntrustOrderListByIds(ids); if (CollUtil.isNotEmpty(contractEntrustOrderEntities)) { for (ContractEntrustOrderEntity entrustOrderEntity : contractEntrustOrderEntities) { closeOrder(entrustOrderEntity); } } else { log.info("平仓委托单未找到"); } } } private void closeOrder(ContractEntrustOrderEntity entrustOrder) { log.info("执行平仓委托"); } }