| | |
| | | @Autowired |
| | | private SysparaService sysparaService; |
| | | |
| | | @Autowired |
| | | private ContractPositionModeService contractPositionModeService; |
| | | |
| | | public IPage<ContractOrderDTO> listRecord(Page page, ContractOrderQuery query) { |
| | | return baseMapper.listRecord(page, query); |
| | | } |
| | |
| | | return rentalProfit; |
| | | } |
| | | |
| | | /** |
| | | * 单向持仓:反向开仓前先按张数平掉对向持仓,释放保证金后再开剩余仓位。 |
| | | * |
| | | * @return 对冲后仍需新开仓的张数 |
| | | */ |
| | | private BigDecimal netOppositePositions(ContractApplyOrder applyOrder, Realtime realtime, BigDecimal openVolume) { |
| | | if (openVolume == null || openVolume.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | String oppositeDirection = ContractOrder.DIRECTION_BUY.equals(applyOrder.getDirection()) |
| | | ? ContractOrder.DIRECTION_SELL |
| | | : ContractOrder.DIRECTION_BUY; |
| | | List<ContractOrder> oppositeList = findSubmitted( |
| | | applyOrder.getPartyId(), applyOrder.getSymbol(), oppositeDirection); |
| | | if (CollectionUtil.isEmpty(oppositeList)) { |
| | | return openVolume; |
| | | } |
| | | |
| | | BigDecimal remainingOpenVolume = openVolume; |
| | | for (ContractOrder opposite : oppositeList) { |
| | | if (remainingOpenVolume.compareTo(BigDecimal.ZERO) <= 0) { |
| | | break; |
| | | } |
| | | if (opposite.getVolume() == null || opposite.getVolume().compareTo(BigDecimal.ZERO) <= 0) { |
| | | continue; |
| | | } |
| | | if (!ContractLock.add(opposite.getOrderNo())) { |
| | | continue; |
| | | } |
| | | try { |
| | | BigDecimal closeVolume = remainingOpenVolume.min(opposite.getVolume()); |
| | | contractOrderCalculationService.refreshMarkPriceProfit(opposite); |
| | | BigDecimal profit = settle(opposite, closeVolume); |
| | | update(opposite); |
| | | |
| | | Wallet wallet = walletService.findByUserId(applyOrder.getPartyId()); |
| | | if (wallet.getMoney().add(profit).compareTo(BigDecimal.ZERO) < 0) { |
| | | profit = wallet.getMoney().negate(); |
| | | } |
| | | walletService.updateMoney(opposite.getSymbol(), applyOrder.getPartyId(), profit, BigDecimal.ZERO, |
| | | Constants.MONEYLOG_CATEGORY_CONTRACT, Constants.WALLET_USDT, |
| | | Constants.MONEYLOG_CONTENT_CONTRACT_CLOSE, |
| | | "反手对冲平仓,平仓合约数[" + closeVolume + "],订单号[" + opposite.getOrderNo() + "]"); |
| | | |
| | | if (ContractOrder.STATE_CREATED.equals(opposite.getState())) { |
| | | User party = userService.getById(opposite.getPartyId()); |
| | | if (party != null) { |
| | | if (party.getWithdrawLimitNowAmount() == null) { |
| | | party.setWithdrawLimitNowAmount(BigDecimal.ZERO); |
| | | } |
| | | party.setWithdrawLimitNowAmount(party.getWithdrawLimitNowAmount().add(opposite.getDepositOpen())); |
| | | userService.updateById(party); |
| | | } |
| | | userDataService.saveClose(opposite); |
| | | User partyUser = userService.getById(opposite.getPartyId()); |
| | | if (partyUser != null && Constants.SECURITY_ROLE_MEMBER.equals(partyUser.getRoleName())) { |
| | | tipService.deleteTip(opposite.getUuid().toString()); |
| | | } |
| | | } |
| | | remainingOpenVolume = remainingOpenVolume.subtract(closeVolume); |
| | | } finally { |
| | | ContractLock.remove(opposite.getOrderNo()); |
| | | } |
| | | } |
| | | return remainingOpenVolume; |
| | | } |
| | | |
| | | /** |
| | | * 反向开仓完全被对向持仓对冲,无需再开新仓。 |
| | | */ |
| | | private void completeNettedApplyOrder(ContractApplyOrder applyOrder, |
| | | BigDecimal originalDeposit, BigDecimal originalFee) { |
| | | applyOrder.setVolume(BigDecimal.ZERO); |
| | | applyOrder.setState(ContractApplyOrder.STATE_CREATED); |
| | | contractApplyOrderService.updateById(applyOrder); |
| | | if (ContractApplyOrder.ORDER_PRICE_TYPE_LIMIT.equals(applyOrder.getOrderPriceType())) { |
| | | BigDecimal refund = defaultZero(originalDeposit).add(defaultZero(originalFee)); |
| | | if (refund.compareTo(BigDecimal.ZERO) > 0) { |
| | | walletService.updateMoney(applyOrder.getSymbol(), applyOrder.getPartyId(), refund, BigDecimal.ZERO, |
| | | Constants.MONEYLOG_CATEGORY_CONTRACT, Constants.WALLET_USDT, |
| | | Constants.MONEYLOG_CONTENT_CONTRACT_CONCEL, |
| | | "反手完全对冲,退回保证金,订单号[" + applyOrder.getOrderNo() + "]"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 部分对冲后,按剩余比例调整委托单保证金/手续费;限价单退回多扣部分。 |
| | | */ |
| | | private void adjustApplyOrderAfterPartialNet(ContractApplyOrder applyOrder, |
| | | BigDecimal originalVolume, |
| | | BigDecimal remainingVolume, |
| | | BigDecimal originalMoney, |
| | | BigDecimal originalDeposit, |
| | | BigDecimal originalFee) { |
| | | BigDecimal ratio = remainingVolume.divide(originalVolume, 10, RoundingMode.HALF_UP); |
| | | BigDecimal remainingMoney = originalMoney.multiply(ratio).setScale(4, RoundingMode.DOWN); |
| | | BigDecimal remainingDeposit = defaultZero(originalDeposit).multiply(ratio).setScale(4, RoundingMode.DOWN); |
| | | BigDecimal remainingFee = defaultZero(originalFee).multiply(ratio).setScale(4, RoundingMode.DOWN); |
| | | |
| | | if (ContractApplyOrder.ORDER_PRICE_TYPE_LIMIT.equals(applyOrder.getOrderPriceType())) { |
| | | BigDecimal refund = defaultZero(originalDeposit).subtract(remainingDeposit) |
| | | .add(defaultZero(originalFee).subtract(remainingFee)); |
| | | if (refund.compareTo(BigDecimal.ZERO) > 0) { |
| | | walletService.updateMoney(applyOrder.getSymbol(), applyOrder.getPartyId(), refund, BigDecimal.ZERO, |
| | | Constants.MONEYLOG_CATEGORY_CONTRACT, Constants.WALLET_USDT, |
| | | Constants.MONEYLOG_CONTENT_CONTRACT_CONCEL, |
| | | "反手部分对冲,退回多余保证金,订单号[" + applyOrder.getOrderNo() + "]"); |
| | | } |
| | | } |
| | | applyOrder.setVolume(remainingVolume); |
| | | applyOrder.setVolumeOpen(remainingVolume); |
| | | applyOrder.setMoney(remainingMoney); |
| | | applyOrder.setDeposit(remainingDeposit); |
| | | applyOrder.setFee(remainingFee); |
| | | } |
| | | |
| | | private void applyLimitOpenToOrder(ContractOrder order, ContractApplyOrder applyOrder) { |
| | | order.setUnitAmount(applyOrder.getUnitAmount()); |
| | | order.setDepositOpen(applyOrder.getDeposit()); |
| | |
| | | |
| | | public void saveOpen(ContractApplyOrder applyOrder, Realtime realtime) { |
| | | Item item = this.itemService.findBySymbol(applyOrder.getSymbol()); |
| | | if (item == null) { |
| | | throw new YamiShopBindException("Parameter Error3"); |
| | | } |
| | | |
| | | // 单向持仓:反向开仓前先平掉对向仓位;双向持仓(默认)允许同币种多空并存 |
| | | if (contractPositionModeService.isOneWayMode()) { |
| | | BigDecimal originalVolume = applyOrder.getVolume(); |
| | | BigDecimal originalMoney = applyOrder.getMoney(); |
| | | BigDecimal originalDeposit = applyOrder.getDeposit(); |
| | | BigDecimal originalFee = applyOrder.getFee(); |
| | | |
| | | BigDecimal remainingVolume = netOppositePositions(applyOrder, realtime, originalVolume); |
| | | if (remainingVolume.compareTo(BigDecimal.ZERO) <= 0) { |
| | | completeNettedApplyOrder(applyOrder, originalDeposit, originalFee); |
| | | return; |
| | | } |
| | | if (remainingVolume.compareTo(originalVolume) < 0) { |
| | | adjustApplyOrderAfterPartialNet(applyOrder, originalVolume, remainingVolume, |
| | | originalMoney, originalDeposit, originalFee); |
| | | } |
| | | } |
| | | |
| | | if (ContractApplyOrder.ORDER_PRICE_TYPE_OPPONENT.equals(applyOrder.getOrderPriceType())) { |
| | | ensureOpponentOpenMargin(applyOrder); |
| | | } |
| | | |
| | | ContractOrder f = getOne(new LambdaQueryWrapper<>(ContractOrder.class) |
| | | .eq(ContractOrder::getPartyId, applyOrder.getPartyId()) |
| | |
| | | } else { |
| | | applyLimitOpenToOrder(order, applyOrder); |
| | | } |
| | | save(order); |
| | | //计算强平价格 |
| | | getStrongPrice(order); |
| | | update(order); |
| | | save(order); |
| | | refreshOrder(applyOrder, order); |
| | | } |
| | | } |