From f658569891db433854221b80f0a9fa99608cff64 Mon Sep 17 00:00:00 2001
From: zj <1772600164@qq.com>
Date: Fri, 03 Apr 2026 18:22:34 +0800
Subject: [PATCH] 1
---
trading-order-service/src/main/java/com/yami/trading/service/contract/ContractOrderService.java | 293 ++++++++++++++++++++++++++++++++++++++++++----------------
1 files changed, 210 insertions(+), 83 deletions(-)
diff --git a/trading-order-service/src/main/java/com/yami/trading/service/contract/ContractOrderService.java b/trading-order-service/src/main/java/com/yami/trading/service/contract/ContractOrderService.java
index f29d4a5..4ccdca1 100644
--- a/trading-order-service/src/main/java/com/yami/trading/service/contract/ContractOrderService.java
+++ b/trading-order-service/src/main/java/com/yami/trading/service/contract/ContractOrderService.java
@@ -63,6 +63,7 @@
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
@@ -73,6 +74,7 @@
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@@ -377,7 +379,10 @@
* 收益
*/
BigDecimal volume = order.getVolume();
- BigDecimal profit = settle(order, order.getVolume());
+ BigDecimal fundingFee = this.calculateFundingFee(order, volume, new Date());
+ BigDecimal profit = settle(order, order.getVolume()).subtract(fundingFee);
+ order.setAmountClose(order.getAmountClose().subtract(fundingFee));
+ order.setFundingFee(defaultZero(order.getFundingFee()).add(fundingFee));
String symbol = order.getSymbol();
// Item item = itemService.findBySymbol(symbol);
// profit = exchangeRateService.getUsdtByType(profit, item.getType());
@@ -406,11 +411,6 @@
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) {
@@ -466,6 +466,7 @@
if (cacheProfit != null) {
contractOrder.setProfit(cacheProfit.getProfit());
contractOrder.setCloseAvgPrice(cacheProfit.getCloseAvgPrice());
+ contractOrder.setForceClosePrice(cacheProfit.getForceClosePrice());
} else {
contractOrder.setProfit(BigDecimal.ZERO);
@@ -630,11 +631,6 @@
* @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) {
@@ -642,7 +638,16 @@
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()); //订单总金额
@@ -669,87 +674,203 @@
}
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;
+ }
+
+ private BigDecimal calculateFundingFee(ContractOrder order, BigDecimal closeVolume, Date closeTime) {
+ if (order == null || closeVolume == null || closeTime == null) {
+ return BigDecimal.ZERO;
+ }
+ if (closeVolume.compareTo(BigDecimal.ZERO) <= 0
+ || order.getVolumeOpen() == null
+ || order.getVolumeOpen().compareTo(BigDecimal.ZERO) <= 0
+ || order.getCreateTime() == null) {
+ return BigDecimal.ZERO;
+ }
+
+ long holdHours = Duration.between(order.getCreateTime().toInstant(), closeTime.toInstant()).toHours();
+ long settlementPeriods = holdHours / 4;
+ if (settlementPeriods <= 0) {
+ return BigDecimal.ZERO;
+ }
+
+ BigDecimal fundingRateTotal = BigDecimal.ZERO;
+ for (int i = 0; i < settlementPeriods; i++) {
+ double periodRate = ThreadLocalRandom.current().nextDouble(-0.001D, 0.0010000001D);
+ fundingRateTotal = fundingRateTotal.add(BigDecimal.valueOf(periodRate));
+ }
+
+ BigDecimal currentVolume = defaultZero(order.getVolume());
+ if (currentVolume.compareTo(BigDecimal.ZERO) <= 0) {
+ return BigDecimal.ZERO;
+ }
+
+ BigDecimal closeRatio = closeVolume.divide(currentVolume, 10, RoundingMode.HALF_UP);
+ BigDecimal borrowedBase = getCurrentBorrowedAmount(order).multiply(closeRatio);
+ return borrowedBase.multiply(fundingRateTotal).setScale(8, RoundingMode.HALF_UP);
+ }
+
+ 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());
/**
* 进入市场
@@ -769,7 +890,7 @@
*/
Trader trader = traderService.findByPartyIdAndChecked(applyOrder.getPartyId(), 1); // 交易员存在
if (trader != null) {
- traderFollowUserOrderService.traderOpen(order, contractApplyOrderService, this, 1); // 交易员跟随者开启永续合约委托, 加个跟单标识
+ traderFollowUserOrderService.traderOpen(traderOpenOrder, contractApplyOrderService, this, 1); // 交易员跟随者开启永续合约委托, 加个跟单标识
}
/**
@@ -820,7 +941,11 @@
/**
* 平仓退回的金额
*/
+ BigDecimal fundingFee = this.calculateFundingFee(order, volume, new Date());
BigDecimal profit = this.settle(order, volume);
+ BigDecimal netProfit = profit.subtract(fundingFee);
+ order.setAmountClose(order.getAmountClose().subtract(fundingFee));
+ order.setFundingFee(defaultZero(order.getFundingFee()).add(fundingFee));
update(order);
Wallet wallet = this.walletService.findByUserId(order.getPartyId());
BigDecimal amount_before = wallet.getMoney();
@@ -828,14 +953,15 @@
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(fundingFee));
if (applyOrder.getVolume().compareTo(BigDecimal.ZERO) <= 0) {
applyOrder.setState(ContractApplyOrder.STATE_CREATED);
}
@@ -921,9 +1047,10 @@
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());
--
Gitblit v1.9.3