| | |
| | | package com.yami.trading.service.contract; |
| | | |
| | | import com.yami.trading.bean.contract.domain.ContractOrder; |
| | | import com.yami.trading.bean.contract.domain.ContractOrderProfit; |
| | | import com.yami.trading.bean.data.domain.Realtime; |
| | | import com.yami.trading.bean.item.domain.Item; |
| | | import com.yami.trading.bean.model.Wallet; |
| | | import com.yami.trading.bean.syspara.domain.Syspara; |
| | | import com.yami.trading.common.constants.ContractRedisKeys; |
| | | import com.yami.trading.common.util.RedisUtil; |
| | | import com.yami.trading.common.util.ThreadUtils; |
| | | import com.yami.trading.service.WalletService; |
| | | import com.yami.trading.service.data.DataService; |
| | | import com.yami.trading.service.item.ItemService; |
| | | import com.yami.trading.service.syspara.SysparaService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.commons.lang3.ObjectUtils; |
| | | import org.apache.logging.log4j.LogManager; |
| | |
| | | import org.springframework.context.annotation.Lazy; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.time.Instant; |
| | | import java.time.LocalDate; |
| | | import java.time.ZoneId; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | |
| | | @Slf4j |
| | |
| | | private DataService dataService; |
| | | @Autowired |
| | | private WalletService walletService; |
| | | @Resource |
| | | private SysparaService sysparaService; |
| | | private Logger logger = LogManager.getLogger(ContractOrderCalculationServiceImpl.class); |
| | | |
| | | private BigDecimal defaultZero(BigDecimal value) { |
| | | return value == null ? BigDecimal.ZERO : value; |
| | | } |
| | | |
| | | private BigDecimal calculateType1ForceClosePrice(ContractOrder order, Wallet wallet) { |
| | | BigDecimal volume = defaultZero(order.getVolume()); |
| | | BigDecimal tradeAvgPrice = defaultZero(order.getTradeAvgPrice()); |
| | | if (volume.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | |
| | | List<ContractOrder> list = contractOrderService.findSubmitted(order.getPartyId(), null, null, null, null, null); |
| | | BigDecimal otherEquity = BigDecimal.ZERO; |
| | | for (ContractOrder contractOrder : list) { |
| | | if (ContractOrder.STATE_SUBMITTED.equals(contractOrder.getState())) { |
| | | contractOrderService.wrapProfit(contractOrder); |
| | | } |
| | | if (order.getUuid().equals(contractOrder.getUuid())) { |
| | | continue; |
| | | } |
| | | otherEquity = otherEquity.add(defaultZero(contractOrder.getProfit()).add(defaultZero(contractOrder.getDeposit()))); |
| | | } |
| | | |
| | | BigDecimal baseEquity = defaultZero(wallet.getMoney()) |
| | | .add(otherEquity) |
| | | .add(defaultZero(order.getDeposit())); |
| | | BigDecimal priceOffset = baseEquity.divide(volume, 10, RoundingMode.HALF_UP); |
| | | if (ContractOrder.DIRECTION_BUY.equalsIgnoreCase(order.getDirection())) { |
| | | return tradeAvgPrice.subtract(priceOffset); |
| | | } |
| | | return tradeAvgPrice.add(priceOffset); |
| | | } |
| | | |
| | | private BigDecimal calculateType2ForceClosePrice(ContractOrder order) { |
| | | BigDecimal volume = defaultZero(order.getVolume()); |
| | | BigDecimal tradeAvgPrice = defaultZero(order.getTradeAvgPrice()); |
| | | if (volume.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | BigDecimal thresholdRatio = order_close_line.divide(new BigDecimal(100), 10, RoundingMode.HALF_UP); |
| | | if (thresholdRatio.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return tradeAvgPrice; |
| | | } |
| | | BigDecimal availableDeposit = defaultZero(order.getDeposit()); |
| | | if (availableDeposit.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return tradeAvgPrice; |
| | | } |
| | | BigDecimal requiredLoss = availableDeposit.divide(thresholdRatio, 10, RoundingMode.HALF_UP); |
| | | BigDecimal priceOffset = requiredLoss.divide(volume, 10, RoundingMode.HALF_UP); |
| | | if (ContractOrder.DIRECTION_BUY.equalsIgnoreCase(order.getDirection())) { |
| | | return tradeAvgPrice.subtract(priceOffset); |
| | | } |
| | | return tradeAvgPrice.add(priceOffset); |
| | | } |
| | | |
| | | @Override |
| | | public void saveCalculation(String order_no) { |
| | | try { |
| | |
| | | Realtime realtime = list.get(0); |
| | | |
| | | BigDecimal close = new BigDecimal(realtime.getClose()); |
| | | settle(order, "watch", close); |
| | | |
| | | BigDecimal add = order.getTradeAvgPrice().add(order.getPips()); |
| | | BigDecimal subtract = order.getTradeAvgPrice().subtract(order.getPips()); |
| | |
| | | BigDecimal point = close.subtract(order.getTradeAvgPrice()).abs().divide(order.getPips(), 10, RoundingMode.HALF_UP); |
| | | // 根据偏 差点数和手数算出盈亏金额 |
| | | BigDecimal amount = order.getPipsAmount().multiply(point).multiply(order.getVolume()); |
| | | if (order.getDirection().equalsIgnoreCase(ContractOrder.DIRECTION_BUY)) { |
| | | return amount; |
| | | } else { |
| | | return amount.negate(); |
| | | } |
| | | BigDecimal pricePnl = order.getDirection().equalsIgnoreCase(ContractOrder.DIRECTION_BUY) |
| | | ? amount : amount.negate(); |
| | | BigDecimal funding = contractOrderService.calculateAccruedFundingPnl(order, close, new Date()); |
| | | return pricePnl.add(funding); |
| | | |
| | | } |
| | | |
| | |
| | | * 根据偏 差点数和手数算出盈亏金额 |
| | | */ |
| | | BigDecimal amount = order.getPipsAmount().multiply(point).multiply(order.getVolume()); |
| | | if (order.getDirection().equalsIgnoreCase(ContractOrder.DIRECTION_BUY)) { |
| | | return amount; |
| | | } else { |
| | | return amount.negate(); |
| | | } |
| | | BigDecimal pricePnl = order.getDirection().equalsIgnoreCase(ContractOrder.DIRECTION_BUY) |
| | | ? amount : amount.negate(); |
| | | BigDecimal funding = contractOrderService.calculateAccruedFundingPnl(order, close, new Date()); |
| | | return pricePnl.add(funding); |
| | | |
| | | } |
| | | |
| | |
| | | pips = new BigDecimal("0.01"); |
| | | } |
| | | |
| | | /** |
| | | * 根据价格变化百分比和保证金计算盈亏金额 |
| | | */ |
| | | |
| | | BigDecimal priceChangeRatio = currentPrice.subtract(order.getTradeAvgPrice()) |
| | | .divide(order.getTradeAvgPrice(), 6, RoundingMode.DOWN); |
| | | |
| | | BigDecimal margin = order.getUnitAmount().multiply(order.getVolumeOpen()); // 这是用户实际的投资金额 |
| | | |
| | | BigDecimal profitAmount = margin.multiply(priceChangeRatio); |
| | | |
| | | if (ContractOrder.DIRECTION_BUY.equals(order.getDirection())) { |
| | | order.setProfit(profitAmount.setScale(6,RoundingMode.DOWN)); |
| | | } else{ |
| | | order.setProfit(profitAmount.setScale(6,RoundingMode.DOWN).negate()); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 多次平仓价格不对,后续修 |
| | | */ |
| | | order.setCloseAvgPrice(currentPrice); |
| | | this.contractOrderService.updateByIdBuffer(order); |
| | | applyMarkPriceToOrder(order, currentPrice); |
| | | |
| | | /** |
| | | * 止盈价 |
| | |
| | | return; |
| | | } |
| | | } |
| | | BigDecimal profit1 = contractOrderService.getCacheProfit(order.getUuid()).getProfit(); |
| | | ContractOrderProfit cp = contractOrderService.getCacheProfit(order.getUuid()); |
| | | BigDecimal profit1 = cp != null ? cp.getProfit() : defaultZero(order.getProfit()); |
| | | if (order_close_line_type == 1) { |
| | | /** |
| | | * 收益 |
| | | */ |
| | | BigDecimal profit = BigDecimal.ZERO; |
| | | |
| | | Wallet wallet = this.walletService.findByUserId(order.getPartyId().toString()); |
| | | // 计算所有除自己以外的profit |
| | | BigDecimal profitExptThis = profit.subtract(profit1).subtract(order.getDeposit()); |
| | | /** |
| | | * profitAll+wallet<=0 |
| | | * profitAll<=wallet 强平 |
| | | * p1 +E (p2~pn) <=wallet |
| | | * (currentPrice-tradavg)*pipAmount*volume/pips + depost1 <=wallet-E(p2~pn) |
| | | */ |
| | | BigDecimal left = wallet.getMoney().negate().subtract(profitExptThis).subtract(order.getDeposit()); |
| | | BigDecimal pipsAmount = order.getPipsAmount(); |
| | | if(pipsAmount.doubleValue() <= 0.00){ |
| | | pipsAmount = new BigDecimal("0.01"); |
| | | } |
| | | BigDecimal overLine = (left.multiply(pips).divide(pipsAmount, 10, RoundingMode.HALF_UP) |
| | | .divide(order.getVolume(), 10, RoundingMode.HALF_UP)); |
| | | Integer decimal = itemService.getDecimal(order.getSymbol()); |
| | | BigDecimal forceClose = BigDecimal.ZERO; |
| | | // 买多,从买价跌多少 |
| | | if (order.getDirection().equalsIgnoreCase(ContractOrder.DIRECTION_BUY)) { |
| | | forceClose = order.getTradeAvgPrice().add(overLine).setScale(decimal, RoundingMode.HALF_UP); |
| | | //买跌,涨到多少 |
| | | } else { |
| | | forceClose = order.getTradeAvgPrice().subtract(overLine).setScale(decimal, RoundingMode.HALF_UP); |
| | | } |
| | | BigDecimal forceClose = calculateType1ForceClosePrice(order, wallet).setScale(decimal, RoundingMode.HALF_UP); |
| | | if (forceClose.compareTo(BigDecimal.ZERO) < 0) { |
| | | forceClose = BigDecimal.ZERO; |
| | | } |
| | | order.setForceClosePrice(forceClose.toPlainString()); |
| | | this.contractOrderService.updateByIdBuffer(order); |
| | | List<ContractOrder> list = contractOrderService.findSubmitted(order.getPartyId(), null, null, null, null, null); |
| | | BigDecimal totalEquity = defaultZero(wallet.getMoney()); |
| | | for(ContractOrder contractOrder :list) { |
| | | if(ContractOrder.STATE_SUBMITTED.equals(contractOrder.getState())){ |
| | | contractOrderService.wrapProfit(contractOrder); |
| | | } |
| | | totalEquity = totalEquity.add(defaultZero(contractOrder.getProfit()).add(defaultZero(contractOrder.getDeposit()))); |
| | | } |
| | | for (int i = 0; i < list.size(); i++) { |
| | | ContractOrder close_line = list.get(i); |
| | | profit = profit.add(close_line.getProfit().add(close_line.getDeposit())); |
| | | } |
| | | |
| | | if (profit.add(wallet.getMoney()).compareTo(BigDecimal.ZERO) <= 0) { |
| | | if (totalEquity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | /** |
| | | * 触发全仓强平 |
| | | */ |
| | |
| | | |
| | | } |
| | | } else { |
| | | Integer decimal = itemService.getDecimal(order.getSymbol()); |
| | | BigDecimal forceClose = calculateType2ForceClosePrice(order).setScale(decimal, RoundingMode.HALF_UP); |
| | | if (forceClose.compareTo(BigDecimal.ZERO) < 0) { |
| | | forceClose = BigDecimal.ZERO; |
| | | } |
| | | order.setForceClosePrice(forceClose.toPlainString()); |
| | | this.contractOrderService.updateByIdBuffer(order); |
| | | BigDecimal divide = order.getDeposit().divide(profit1.abs(), 10, RoundingMode.HALF_UP); |
| | | if (profit1.compareTo(BigDecimal.ZERO) < 0 && divide.compareTo(order_close_line.divide(new BigDecimal(100), 10, RoundingMode.HALF_UP)) <= 0) { |
| | | /** |
| | |
| | | this.order_close_line_type = order_close_line_type; |
| | | } |
| | | |
| | | private void applyMarkPriceToOrder(ContractOrder order, BigDecimal currentPrice) { |
| | | BigDecimal priceChangeRatio = currentPrice.subtract(order.getTradeAvgPrice()) |
| | | .divide(order.getTradeAvgPrice(), 6, RoundingMode.DOWN); |
| | | BigDecimal margin = order.getTradeAvgPrice().multiply(order.getVolume()); |
| | | BigDecimal profitAmount = margin.multiply(priceChangeRatio); |
| | | BigDecimal priceProfit; |
| | | if (ContractOrder.DIRECTION_BUY.equals(order.getDirection())) { |
| | | priceProfit = profitAmount.setScale(6, RoundingMode.DOWN); |
| | | } else { |
| | | priceProfit = profitAmount.setScale(6, RoundingMode.DOWN).negate(); |
| | | } |
| | | BigDecimal fundingPnl = contractOrderService.calculateAccruedFundingPnl(order, currentPrice, new Date()); |
| | | order.setProfit(priceProfit.add(fundingPnl)); |
| | | order.setCloseAvgPrice(currentPrice); |
| | | this.contractOrderService.updateByIdBuffer(order); |
| | | } |
| | | |
| | | @Override |
| | | public void refreshMarkPriceProfit(ContractOrder order) { |
| | | if (order == null || !ContractOrder.STATE_SUBMITTED.equals(order.getState())) { |
| | | return; |
| | | } |
| | | if (defaultZero(order.getVolume()).compareTo(BigDecimal.ZERO) <= 0) { |
| | | return; |
| | | } |
| | | List<Realtime> list = this.dataService.realtime(order.getSymbol()); |
| | | if (list.isEmpty()) { |
| | | return; |
| | | } |
| | | applyMarkPriceToOrder(order, BigDecimal.valueOf(list.get(0).getClose())); |
| | | } |
| | | |
| | | } |