| | |
| | | import java.math.RoundingMode; |
| | | import java.text.ParseException; |
| | | import java.text.SimpleDateFormat; |
| | | import java.time.Instant; |
| | | import java.time.ZoneId; |
| | | import java.time.ZonedDateTime; |
| | | import java.time.temporal.ChronoUnit; |
| | | import java.util.ArrayList; |
| | | import java.util.Calendar; |
| | | import java.util.Date; |
| | |
| | | @Transactional |
| | | @Slf4j |
| | | public class ContractOrderService extends ServiceImpl<ContractOrderMapper, ContractOrder> { |
| | | |
| | | /** |
| | | * 资金费结算间隔(分钟):从当地日界 0:00 起每 N 分钟为一个结算点。 |
| | | * 240 即每 4 小时一档(0、4、8、12、16、20 点)。 |
| | | */ |
| | | private static final int FUNDING_SETTLEMENT_INTERVAL_MINUTES = 240; |
| | | |
| | | private Logger logger = LogManager.getLogger(ContractOrderService.class); |
| | | |
| | |
| | | private ContractApplyOrderService contractApplyOrderService; |
| | | @Autowired |
| | | private SysparaService sysparaService; |
| | | |
| | | @Autowired |
| | | @Lazy |
| | | private ContractOrderCalculationService contractOrderCalculationService; |
| | | |
| | | public IPage<ContractOrderDTO> listRecordCur(Page page, ContractOrderQuery query) { |
| | | if (query.getStartTime() != null) { |
| | |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * 收益 |
| | | */ |
| | | contractOrderCalculationService.refreshMarkPriceProfit(order); |
| | | |
| | | BigDecimal volume = order.getVolume(); |
| | | BigDecimal profit = settle(order, order.getVolume()); |
| | | if (volume.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return null; |
| | | } |
| | | |
| | | ContractOrderProfit cache = getCacheProfit(order.getUuid()); |
| | | BigDecimal markPrice = order.getTradeAvgPrice(); |
| | | if (cache != null && cache.getCloseAvgPrice() != null |
| | | && cache.getCloseAvgPrice().compareTo(BigDecimal.ZERO) > 0) { |
| | | markPrice = cache.getCloseAvgPrice(); |
| | | } |
| | | BigDecimal fundingPnlChunk = calculateAccruedFundingPnl(order, markPrice, new Date()); |
| | | |
| | | String symbol = order.getSymbol(); |
| | | Item item = itemService.findBySymbol(symbol); |
| | | BigDecimal closeFee = calculateCloseTradingFee(item, markPrice, volume); |
| | | |
| | | BigDecimal profit = settle(order, volume).subtract(closeFee); |
| | | |
| | | order.setFee(defaultZero(order.getFee()).add(closeFee)); |
| | | order.setAmountClose(order.getAmountClose().subtract(closeFee)); |
| | | order.setFundingFee(defaultZero(order.getFundingFee()).add(fundingPnlChunk)); |
| | | // Item item = itemService.findBySymbol(symbol); |
| | | // profit = exchangeRateService.getUsdtByType(profit, item.getType()); |
| | | if (ContractOrder.ORDER_FOLLOW == order.getFollow()) { // 跟单订单 |
| | |
| | | walletService.updateMoney(order.getSymbol(), partyId, profit, BigDecimal.ZERO, |
| | | Constants.MONEYLOG_CATEGORY_CONTRACT, Constants.WALLET_USDT, Constants.MONEYLOG_CONTENT_CONTRACT_CLOSE, "平仓,平仓合约数[" + volume + "],订单号[" + order.getOrderNo() + "]"); |
| | | } |
| | | order.setState(ContractOrder.STATE_CREATED); |
| | | order.setVolume(BigDecimal.ZERO); |
| | | order.setDeposit(BigDecimal.ZERO); |
| | | order.setCloseTime(DateUtil.currentSeconds()); |
| | | order.setCloseTimeTs(DateUtil.currentSeconds()); |
| | | // List<Realtime> list = this.dataService.realtime(order.getSymbol()); |
| | | // // 平仓时候把当前价格先更新回去 |
| | | // if (list.size() != 0) { |
| | |
| | | if (cacheProfit != null) { |
| | | contractOrder.setProfit(cacheProfit.getProfit()); |
| | | contractOrder.setCloseAvgPrice(cacheProfit.getCloseAvgPrice()); |
| | | contractOrder.setForceClosePrice(cacheProfit.getForceClosePrice()); |
| | | } else { |
| | | contractOrder.setProfit(BigDecimal.ZERO); |
| | | |
| | |
| | | * @param volume 平仓的张数 |
| | | */ |
| | | public BigDecimal settle(ContractOrder order, BigDecimal volume) { |
| | | /** |
| | | * 平仓比率 |
| | | */ |
| | | // BigDecimal rate = volume.divide(order.getVolumeOpen(), 10, RoundingMode.HALF_UP); |
| | | |
| | | ContractOrderProfit cacheProfit = getCacheProfit(order.getUuid()); |
| | | BigDecimal originProfit = BigDecimal.ZERO; |
| | | if (cacheProfit != null) { |
| | |
| | | order.setCloseAvgPrice(cacheProfit.getCloseAvgPrice()); |
| | | } |
| | | |
| | | BigDecimal profit = order.getDeposit().add(originProfit); |
| | | BigDecimal currentVolume = defaultZero(order.getVolume()); |
| | | if (currentVolume.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | BigDecimal closeRatio = volume.divide(currentVolume, 10, RoundingMode.HALF_UP); |
| | | BigDecimal currentDeposit = defaultZero(order.getDeposit()); |
| | | BigDecimal currentBorrowed = getCurrentBorrowedAmount(order); |
| | | BigDecimal releasedDeposit = currentDeposit.multiply(closeRatio).setScale(8, RoundingMode.HALF_UP); |
| | | BigDecimal realizedProfit = originProfit.multiply(closeRatio).setScale(8, RoundingMode.HALF_UP); |
| | | BigDecimal profit = releasedDeposit.add(realizedProfit); |
| | | |
| | | if (ContractOrder.ORDER_FOLLOW == order.getFollow()) { // 跟单还得减去利息收益 |
| | | BigDecimal orderAmount = order.getUnitAmount().multiply(order.getTradeAvgPrice()).multiply(order.getLeverRate()); //订单总金额 |
| | |
| | | } |
| | | |
| | | order.setAmountClose(order.getAmountClose().add(profit)); |
| | | order.setVolume(order.getVolume().subtract(volume)); |
| | | order.setDeposit(order.getDeposit().subtract(order.getDepositOpen())); |
| | | if (order.getVolume().compareTo(BigDecimal.ZERO) <= 0) { |
| | | BigDecimal remainVolume = currentVolume.subtract(volume); |
| | | BigDecimal remainDeposit = currentDeposit.subtract(releasedDeposit).max(BigDecimal.ZERO); |
| | | BigDecimal remainBorrowed = currentBorrowed.subtract(currentBorrowed.multiply(closeRatio).setScale(8, RoundingMode.HALF_UP)).max(BigDecimal.ZERO); |
| | | BigDecimal remainProfit = originProfit.subtract(realizedProfit).setScale(8, RoundingMode.HALF_UP); |
| | | order.setVolume(remainVolume.max(BigDecimal.ZERO)); |
| | | if (remainVolume.compareTo(BigDecimal.ZERO) > 0) { |
| | | order.setVolumeOpen(remainVolume); |
| | | order.setDeposit(remainDeposit); |
| | | order.setDepositOpen(remainDeposit); |
| | | order.setBorrowedAmount(remainBorrowed); |
| | | order.setProfit(remainProfit); |
| | | } else { |
| | | order.setState(ContractOrder.STATE_CREATED); |
| | | order.setCloseTime(DateUtil.currentSeconds()); |
| | | order.setCloseTimeTs(DateUtil.currentSeconds()); |
| | | order.setDeposit(BigDecimal.ZERO); |
| | | order.setBorrowedAmount(BigDecimal.ZERO); |
| | | order.setProfit(originProfit); |
| | | } |
| | | order.setProfit(originProfit); |
| | | return profit; |
| | | } |
| | | |
| | | /** |
| | | * 累计资金费带来的盈亏(正数表示用户获利):多仓为 -合约价值×费率×次数,空仓为 +合约价值×费率×次数。 |
| | | * 费率来自 syspara funding_fee;结算锚点为系统默认时区、从当天 0:00 起每 {@link #FUNDING_SETTLEMENT_INTERVAL_MINUTES} 分钟一档。 |
| | | */ |
| | | public BigDecimal calculateAccruedFundingPnl(ContractOrder order, BigDecimal markPrice, Date asOf) { |
| | | if (order == null || markPrice == null || asOf == null || order.getCreateTime() == null) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | BigDecimal vol = defaultZero(order.getVolume()); |
| | | if (vol.compareTo(BigDecimal.ZERO) <= 0 || markPrice.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | long periods = countFundingSettlementPoints( |
| | | order.getCreateTime().toInstant(), |
| | | asOf.toInstant(), |
| | | ZoneId.systemDefault(), |
| | | FUNDING_SETTLEMENT_INTERVAL_MINUTES); |
| | | if (periods <= 0) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | BigDecimal notional = markPrice.multiply(vol); |
| | | BigDecimal rate = getContractFundingRate(); |
| | | BigDecimal signed = ContractOrder.DIRECTION_BUY.equalsIgnoreCase(order.getDirection()) |
| | | ? rate.negate() : rate; |
| | | return notional.multiply(signed).multiply(BigDecimal.valueOf(periods)).setScale(8, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | public BigDecimal getContractFundingRate() { |
| | | try { |
| | | Syspara p = sysparaService.find("funding_fee"); |
| | | if (p == null || StringUtils.isEmptyString(p.getSvalue())) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | return new BigDecimal(p.getSvalue().trim()); |
| | | } catch (Exception e) { |
| | | log.warn("parse funding_fee syspara failed", e); |
| | | return BigDecimal.ZERO; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 平仓手续费:手续费率×平仓价×数量(与开仓计费规则一致:U 本位用 unitPercentage,否则 unitFee×价×量)。 |
| | | */ |
| | | public BigDecimal calculateCloseTradingFee(Item item, BigDecimal closePrice, BigDecimal closeVolume) { |
| | | if (item == null || closePrice == null || closeVolume == null |
| | | || closeVolume.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | Syspara syspara = sysparaService.find("u_standard_contract"); |
| | | if (ObjectUtils.isNotEmpty(syspara) && "1".equals(syspara.getSvalue())) { |
| | | return closePrice.multiply(closeVolume).multiply(BigDecimal.valueOf(item.getUnitPercentage())) |
| | | .setScale(8, RoundingMode.HALF_UP); |
| | | } |
| | | return BigDecimal.valueOf(item.getUnitFee()).multiply(closePrice).multiply(closeVolume) |
| | | .setScale(8, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | private static ZonedDateTime nextFundingSettlementStrictlyAfter(ZonedDateTime t, int intervalMinutes) { |
| | | int step = intervalMinutes <= 0 ? 240 : intervalMinutes; |
| | | ZonedDateTime tTrunc = t.withSecond(0).withNano(0); |
| | | ZonedDateTime dayStart = tTrunc.toLocalDate().atStartOfDay(tTrunc.getZone()); |
| | | long minutesFromDayStart = ChronoUnit.MINUTES.between(dayStart, tTrunc); |
| | | long slotIndex = minutesFromDayStart / step; |
| | | ZonedDateTime slotStart = dayStart.plusMinutes(slotIndex * step); |
| | | ZonedDateTime next = slotStart; |
| | | while (!next.isAfter(t)) { |
| | | next = next.plusMinutes(step); |
| | | } |
| | | return next; |
| | | } |
| | | |
| | | private static long countFundingSettlementPoints(Instant open, Instant end, ZoneId zone, int intervalMinutes) { |
| | | if (open == null || end == null || !end.isAfter(open)) { |
| | | return 0; |
| | | } |
| | | int step = intervalMinutes <= 0 ? 240 : intervalMinutes; |
| | | ZonedDateTime zEnd = end.atZone(zone); |
| | | ZonedDateTime cursor = nextFundingSettlementStrictlyAfter(open.atZone(zone), step); |
| | | long cnt = 0; |
| | | while (!cursor.isAfter(zEnd)) { |
| | | cnt++; |
| | | cursor = cursor.plusMinutes(step); |
| | | } |
| | | return cnt; |
| | | } |
| | | |
| | | private BigDecimal defaultZero(BigDecimal value) { |
| | | return value == null ? BigDecimal.ZERO : value; |
| | | } |
| | | |
| | | private BigDecimal calculateBorrowedAmount(BigDecimal positionBase, BigDecimal leverRate) { |
| | | BigDecimal safeBase = defaultZero(positionBase); |
| | | BigDecimal safeLeverRate = defaultZero(leverRate); |
| | | if (safeBase.compareTo(BigDecimal.ZERO) <= 0 || safeLeverRate.compareTo(BigDecimal.ONE) <= 0) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | return safeBase.multiply(safeLeverRate.subtract(BigDecimal.ONE)) |
| | | .divide(safeLeverRate, 10, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | private BigDecimal getCurrentBorrowedAmount(ContractOrder order) { |
| | | if (order == null) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | BigDecimal borrowedAmount = defaultZero(order.getBorrowedAmount()); |
| | | if (borrowedAmount.compareTo(BigDecimal.ZERO) > 0) { |
| | | return borrowedAmount; |
| | | } |
| | | return calculateBorrowedAmount(order.getDeposit(), order.getLeverRate()); |
| | | } |
| | | |
| | | private boolean shouldMergeCryptoPosition(Item item) { |
| | | return item != null && Item.cryptos.equalsIgnoreCase(item.getType()); |
| | | } |
| | | |
| | | private void mergeOpenPosition(ContractOrder order, ContractApplyOrder applyOrder, BigDecimal tradePrice) { |
| | | BigDecimal oldVolume = defaultZero(order.getVolume()); |
| | | BigDecimal newVolume = defaultZero(applyOrder.getVolume()); |
| | | BigDecimal totalVolume = oldVolume.add(newVolume); |
| | | if (totalVolume.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return; |
| | | } |
| | | |
| | | BigDecimal weightedTradeAmount = defaultZero(order.getTradeAvgPrice()).multiply(oldVolume) |
| | | .add(defaultZero(tradePrice).multiply(newVolume)); |
| | | BigDecimal avgTradePrice = weightedTradeAmount.divide(totalVolume, 10, RoundingMode.HALF_UP); |
| | | |
| | | order.setTradeAvgPrice(avgTradePrice); |
| | | order.setVolume(totalVolume); |
| | | order.setVolumeOpen(totalVolume); |
| | | order.setDeposit(defaultZero(order.getDeposit()).add(defaultZero(applyOrder.getDeposit()))); |
| | | order.setDepositOpen(defaultZero(order.getDepositOpen()).add(defaultZero(applyOrder.getDeposit()))); |
| | | order.setFee(defaultZero(order.getFee()).add(defaultZero(applyOrder.getFee()))); |
| | | order.setBorrowedAmount(getCurrentBorrowedAmount(order) |
| | | .add(calculateBorrowedAmount(applyOrder.getDeposit(), applyOrder.getLeverRate()))); |
| | | order.setLeverRate(applyOrder.getLeverRate()); |
| | | order.setOrderPriceType(applyOrder.getOrderPriceType()); |
| | | if (applyOrder.getStopPriceProfit() != null && applyOrder.getStopPriceProfit().compareTo(BigDecimal.ZERO) > 0) { |
| | | order.setStopPriceProfit(applyOrder.getStopPriceProfit()); |
| | | } |
| | | if (applyOrder.getStopPriceLoss() != null && applyOrder.getStopPriceLoss().compareTo(BigDecimal.ZERO) > 0) { |
| | | order.setStopPriceLoss(applyOrder.getStopPriceLoss()); |
| | | } |
| | | } |
| | | |
| | | @Transactional |
| | | public void saveOpen(ContractApplyOrder applyOrder, Realtime realtime) { |
| | | Item item = this.itemService.findBySymbol(applyOrder.getSymbol()); |
| | | BigDecimal tradePrice = BigDecimal.valueOf(realtime.getClose()); |
| | | ContractOrder order; |
| | | List<ContractOrder> submittedOrders = shouldMergeCryptoPosition(item) |
| | | ? this.findSubmitted(applyOrder.getPartyId(), applyOrder.getSymbol(), applyOrder.getDirection()) |
| | | : new ArrayList<>(); |
| | | if (CollectionUtil.isNotEmpty(submittedOrders)) { |
| | | order = submittedOrders.get(0); |
| | | mergeOpenPosition(order, applyOrder, tradePrice); |
| | | update(order); |
| | | } else { |
| | | order = new ContractOrder(); |
| | | order.setPartyId(applyOrder.getPartyId()); |
| | | order.setSymbol(applyOrder.getSymbol()); |
| | | String orderNo = com.yami.trading.common.util.DateUtil.formatDate(new Date(), "yyMMddHHmmss") + RandomUtil.getRandomNum(8); |
| | | order.setOrderNo(orderNo); |
| | | order.setDirection(applyOrder.getDirection()); |
| | | order.setLeverRate(applyOrder.getLeverRate()); |
| | | order.setVolume(applyOrder.getVolume()); |
| | | order.setVolumeOpen(applyOrder.getVolumeOpen()); |
| | | order.setOrderPriceType(applyOrder.getOrderPriceType()); |
| | | order.setUnitAmount(applyOrder.getUnitAmount()); |
| | | order.setFee(applyOrder.getFee()); |
| | | order.setFundingFee(BigDecimal.ZERO); |
| | | order.setBorrowedAmount(calculateBorrowedAmount(applyOrder.getDeposit(), applyOrder.getLeverRate())); |
| | | order.setDeposit(applyOrder.getDeposit()); |
| | | order.setDepositOpen(applyOrder.getDeposit()); |
| | | |
| | | ContractOrder order = new ContractOrder(); |
| | | order.setPartyId(applyOrder.getPartyId()); |
| | | order.setSymbol(applyOrder.getSymbol()); |
| | | String orderNo = com.yami.trading.common.util.DateUtil.formatDate(new Date(), "yyMMddHHmmss") + RandomUtil.getRandomNum(8); |
| | | order.setOrderNo(orderNo); |
| | | order.setDirection(applyOrder.getDirection()); |
| | | order.setLeverRate(applyOrder.getLeverRate()); |
| | | order.setVolume(applyOrder.getVolume()); |
| | | order.setVolumeOpen(applyOrder.getVolumeOpen()); |
| | | order.setOrderPriceType(applyOrder.getOrderPriceType()); |
| | | order.setUnitAmount(applyOrder.getUnitAmount()); |
| | | order.setFee(applyOrder.getFee()); |
| | | order.setDeposit(applyOrder.getDeposit()); |
| | | order.setDepositOpen(applyOrder.getDeposit()); |
| | | order.setTradeAvgPrice(tradePrice); |
| | | order.setStopPriceProfit(applyOrder.getStopPriceProfit()); |
| | | order.setStopPriceLoss(applyOrder.getStopPriceLoss()); |
| | | |
| | | order.setTradeAvgPrice(BigDecimal.valueOf(realtime.getClose())); |
| | | order.setStopPriceProfit(applyOrder.getStopPriceProfit()); |
| | | order.setStopPriceLoss(applyOrder.getStopPriceLoss()); |
| | | order.setPips(BigDecimal.valueOf(item.getPips())); |
| | | order.setPipsAmount(BigDecimal.valueOf(item.getPipsAmount())); |
| | | order.setFollow(applyOrder.getFollow()); |
| | | save(order); |
| | | RedisUtil.set(ContractRedisKeys.CONTRACT_ORDERNO + order.getOrderNo(), order); |
| | | |
| | | order.setPips(BigDecimal.valueOf(item.getPips())); |
| | | order.setPipsAmount(BigDecimal.valueOf(item.getPipsAmount())); |
| | | order.setFollow(applyOrder.getFollow()); |
| | | // 爆仓是爆整个钱包 |
| | | // BigDecimal forceClose = BigDecimal.ZERO; |
| | | // BigDecimal base = order.getDepositOpen().multiply(order.getPips()).divide(order.getPipsAmount(), 10, RoundingMode.HALF_UP).divide(order.getVolume(),10, RoundingMode.HALF_UP); |
| | | // if(order.getDirection().equalsIgnoreCase(ContractOrder.DIRECTION_BUY)){ |
| | | // forceClose = order.getTradeAvgPrice().subtract(base).setScale(item.getDecimals(), RoundingMode.HALF_UP); |
| | | // }else if(order.getDirection().equalsIgnoreCase(ContractOrder.DIRECTION_SELL)) { |
| | | // forceClose = order.getTradeAvgPrice().add(base).setScale(item.getDecimals(), RoundingMode.HALF_UP); |
| | | // } |
| | | // if(forceClose.compareTo(BigDecimal.ZERO) <0 ){ |
| | | // forceClose = BigDecimal.ZERO; |
| | | // } |
| | | // order.setForceClosePrice(forceClose.toPlainString()); |
| | | save(order); |
| | | RedisUtil.set(ContractRedisKeys.CONTRACT_ORDERNO + order.getOrderNo(), order); |
| | | Map<String, ContractOrder> map = RedisUtil |
| | | .get(ContractRedisKeys.CONTRACT_SUBMITTED_ORDER_PARTY_ID + order.getPartyId()); |
| | | if (map == null) { |
| | | map = new ConcurrentHashMap<String, ContractOrder>(); |
| | | } |
| | | map.put(order.getOrderNo(), order); |
| | | RedisUtil.set(ContractRedisKeys.CONTRACT_SUBMITTED_ORDER_PARTY_ID + order.getPartyId(), map); |
| | | |
| | | Map<String, ContractOrder> map = RedisUtil |
| | | .get(ContractRedisKeys.CONTRACT_SUBMITTED_ORDER_PARTY_ID + order.getPartyId()); |
| | | if (map == null) { |
| | | map = new ConcurrentHashMap<String, ContractOrder>(); |
| | | // 获取单个订单的合约总资产、总保证金、总未实现盈利 |
| | | Map<String, BigDecimal> contractAssetsOrder = this.walletService.getMoneyContractByOrder(order); |
| | | |
| | | BigDecimal contractAssets = RedisUtil.get(ContractRedisKeys.CONTRACT_ASSETS_PARTY_ID + order.getPartyId()); |
| | | if (contractAssets == null) { |
| | | contractAssets = BigDecimal.ZERO; |
| | | } |
| | | BigDecimal contractAssetsDeposit = RedisUtil.get(ContractRedisKeys.CONTRACT_ASSETS_DEPOSIT_PARTY_ID + order.getPartyId()); |
| | | if (contractAssetsDeposit == null) { |
| | | contractAssetsDeposit = BigDecimal.ZERO; |
| | | } |
| | | BigDecimal contractAssetsProfit = RedisUtil.get(ContractRedisKeys.CONTRACT_ASSETS_PROFIT_PARTY_ID + order.getPartyId()); |
| | | if (contractAssetsProfit == null) { |
| | | contractAssetsProfit = BigDecimal.ZERO; |
| | | } |
| | | RedisUtil.set(ContractRedisKeys.CONTRACT_ASSETS_PARTY_ID + order.getPartyId(), |
| | | contractAssets.add(contractAssetsOrder.get("money_contract"))); |
| | | RedisUtil.set(ContractRedisKeys.CONTRACT_ASSETS_DEPOSIT_PARTY_ID + order.getPartyId(), |
| | | contractAssetsDeposit.add(contractAssetsOrder.get("money_contract_deposit"))); |
| | | RedisUtil.set(ContractRedisKeys.CONTRACT_ASSETS_PROFIT_PARTY_ID + order.getPartyId(), |
| | | contractAssetsProfit.add(contractAssetsOrder.get("money_contract_profit"))); |
| | | } |
| | | map.put(order.getOrderNo(), order); |
| | | RedisUtil.set(ContractRedisKeys.CONTRACT_SUBMITTED_ORDER_PARTY_ID + order.getPartyId(), map); |
| | | |
| | | // 获取单个订单的合约总资产、总保证金、总未实现盈利 |
| | | Map<String, BigDecimal> contractAssetsOrder = this.walletService.getMoneyContractByOrder(order); |
| | | |
| | | BigDecimal contractAssets = RedisUtil.get(ContractRedisKeys.CONTRACT_ASSETS_PARTY_ID + order.getPartyId()); |
| | | if (contractAssets == null) { |
| | | contractAssets = BigDecimal.ZERO; |
| | | } |
| | | BigDecimal contractAssetsDeposit = RedisUtil.get(ContractRedisKeys.CONTRACT_ASSETS_DEPOSIT_PARTY_ID + order.getPartyId()); |
| | | if (contractAssetsDeposit == null) { |
| | | contractAssetsDeposit = BigDecimal.ZERO; |
| | | } |
| | | BigDecimal contractAssetsProfit = RedisUtil.get(ContractRedisKeys.CONTRACT_ASSETS_PROFIT_PARTY_ID + order.getPartyId()); |
| | | if (contractAssetsProfit == null) { |
| | | contractAssetsProfit = BigDecimal.ZERO; |
| | | } |
| | | RedisUtil.set(ContractRedisKeys.CONTRACT_ASSETS_PARTY_ID + order.getPartyId(), |
| | | contractAssets.add(contractAssetsOrder.get("money_contract"))); |
| | | RedisUtil.set(ContractRedisKeys.CONTRACT_ASSETS_DEPOSIT_PARTY_ID + order.getPartyId(), |
| | | contractAssetsDeposit.add(contractAssetsOrder.get("money_contract_deposit"))); |
| | | RedisUtil.set(ContractRedisKeys.CONTRACT_ASSETS_PROFIT_PARTY_ID + order.getPartyId(), |
| | | contractAssetsProfit.add(contractAssetsOrder.get("money_contract_profit"))); |
| | | ContractOrder traderOpenOrder = new ContractOrder(); |
| | | traderOpenOrder.setPartyId(order.getPartyId()); |
| | | traderOpenOrder.setOrderNo(order.getOrderNo()); |
| | | traderOpenOrder.setSymbol(order.getSymbol()); |
| | | traderOpenOrder.setDirection(order.getDirection()); |
| | | traderOpenOrder.setLeverRate(applyOrder.getLeverRate()); |
| | | traderOpenOrder.setVolume(applyOrder.getVolume()); |
| | | traderOpenOrder.setVolumeOpen(applyOrder.getVolumeOpen()); |
| | | traderOpenOrder.setUnitAmount(applyOrder.getUnitAmount()); |
| | | traderOpenOrder.setTradeAvgPrice(tradePrice); |
| | | traderOpenOrder.setStopPriceProfit(applyOrder.getStopPriceProfit()); |
| | | traderOpenOrder.setStopPriceLoss(applyOrder.getStopPriceLoss()); |
| | | |
| | | /** |
| | | * 进入市场 |
| | |
| | | */ |
| | | Trader trader = traderService.findByPartyIdAndChecked(applyOrder.getPartyId(), 1); // 交易员存在 |
| | | if (trader != null) { |
| | | traderFollowUserOrderService.traderOpen(order, contractApplyOrderService, this, 1); // 交易员跟随者开启永续合约委托, 加个跟单标识 |
| | | traderFollowUserOrderService.traderOpen(traderOpenOrder, contractApplyOrderService, this, 1); // 交易员跟随者开启永续合约委托, 加个跟单标识 |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | return applyOrder; |
| | | } |
| | | BigDecimal preVolume = order.getVolume(); |
| | | contractOrderCalculationService.refreshMarkPriceProfit(order); |
| | | |
| | | BigDecimal volume; |
| | | if (applyOrder.getVolume().compareTo(order.getVolume()) > 0) { |
| | | volume = order.getVolume(); |
| | | } else { |
| | | volume = applyOrder.getVolume(); |
| | | } |
| | | /** |
| | | * 平仓退回的金额 |
| | | */ |
| | | BigDecimal profit = this.settle(order, volume); |
| | | BigDecimal markPrice = BigDecimal.valueOf(realtime.getClose()); |
| | | ContractOrderProfit cache = getCacheProfit(order.getUuid()); |
| | | if (cache != null && cache.getCloseAvgPrice() != null |
| | | && cache.getCloseAvgPrice().compareTo(BigDecimal.ZERO) > 0) { |
| | | markPrice = cache.getCloseAvgPrice(); |
| | | } |
| | | BigDecimal fundingAccruedTotal = calculateAccruedFundingPnl(order, markPrice, new Date()); |
| | | BigDecimal fundingPnlChunk = fundingAccruedTotal.multiply(volume.divide(preVolume, 10, RoundingMode.HALF_UP)) |
| | | .setScale(8, RoundingMode.HALF_UP); |
| | | |
| | | Item item = itemService.findBySymbol(order.getSymbol()); |
| | | BigDecimal closeFee = calculateCloseTradingFee(item, markPrice, volume); |
| | | |
| | | BigDecimal profit = this.settle(order, volume).subtract(closeFee); |
| | | BigDecimal netProfit = profit; |
| | | |
| | | order.setFee(defaultZero(order.getFee()).add(closeFee)); |
| | | order.setAmountClose(order.getAmountClose().subtract(closeFee)); |
| | | order.setFundingFee(defaultZero(order.getFundingFee()).add(fundingPnlChunk)); |
| | | update(order); |
| | | Wallet wallet = this.walletService.findByUserId(order.getPartyId()); |
| | | BigDecimal amount_before = wallet.getMoney(); |
| | |
| | | String symbol = order.getSymbol(); |
| | | // Item item = itemService.findBySymbol(symbol); |
| | | // profit = exchangeRateService.getUsdtByType(profit, item.getType()); |
| | | if (wallet.getMoney().add(profit).compareTo(BigDecimal.ZERO) < 0) { |
| | | profit = wallet.getMoney().negate(); |
| | | if (wallet.getMoney().add(netProfit).compareTo(BigDecimal.ZERO) < 0) { |
| | | netProfit = wallet.getMoney().negate(); |
| | | } |
| | | walletService.updateMoney(symbol, order.getPartyId(), profit, BigDecimal.ZERO, |
| | | walletService.updateMoney(symbol, order.getPartyId(), netProfit, BigDecimal.ZERO, |
| | | Constants.MONEYLOG_CATEGORY_CONTRACT, Constants.WALLET_USDT, Constants.MONEYLOG_CONTENT_CONTRACT_CLOSE, "平仓,平仓合约数[" + volume + "],订单号[" + order.getOrderNo() + "]"); |
| | | |
| | | |
| | | applyOrder.setVolume(applyOrder.getVolume().subtract(volume)); |
| | | applyOrder.setFundingFee(defaultZero(applyOrder.getFundingFee()).add(fundingPnlChunk)); |
| | | if (applyOrder.getVolume().compareTo(BigDecimal.ZERO) <= 0) { |
| | | applyOrder.setState(ContractApplyOrder.STATE_CREATED); |
| | | } |
| | |
| | | map.put("stop_price_loss", order.getStopPriceLoss()); |
| | | } |
| | | map.put("state", order.getState()); |
| | | map.put("amount", order.getVolume().multiply(order.getUnitAmount())); |
| | | map.put("amount_open", order.getVolumeOpen().multiply(order.getUnitAmount())); |
| | | map.put("amount", order.getDeposit()); |
| | | map.put("amount_open", order.getDeposit()); |
| | | map.put("fee", order.getFee()); |
| | | map.put("funding_fee", order.getFundingFee()); |
| | | map.put("deposit", order.getDeposit()); |
| | | map.put("deposit_open", order.getDepositOpen()); |
| | | map.put("change_ratio", order.getChangeRatio()); |