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.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.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.context.annotation.Lazy;
|
import org.springframework.stereotype.Service;
|
|
import java.math.BigDecimal;
|
import java.math.RoundingMode;
|
import java.util.Date;
|
import java.util.List;
|
|
@Slf4j
|
@Service
|
public class ContractOrderCalculationServiceImpl implements ContractOrderCalculationService {
|
@Autowired
|
private ItemService itemService;
|
/**
|
* 平仓线 110%(订金价值 /收益=110%)
|
*/
|
public BigDecimal order_close_line = new BigDecimal("1.1");
|
/**
|
* 平仓方式 1全仓 2单个持仓
|
*/
|
public int order_close_line_type = 1;
|
@Autowired
|
private ContractOrderService contractOrderService;
|
@Qualifier("dataService")
|
@Autowired
|
@Lazy
|
private DataService dataService;
|
@Autowired
|
private WalletService walletService;
|
|
private SysparaService sysparaService;
|
|
public void saveCalculation(String order_no, List<ContractOrder> partyContractOrders) {
|
|
try {
|
ContractOrder order = contractOrderService.findByOrderNoRedis(order_no);
|
if (order == null || !ContractOrder.STATE_SUBMITTED.equals(order.getState())) {
|
/**
|
* 状态已改变,退出处理
|
*/
|
return;
|
}
|
List<Realtime> list = this.dataService.realtime(order.getSymbol());
|
if (list.size() == 0) {
|
return;
|
}
|
Realtime realtime = list.get(0);
|
|
BigDecimal close = realtime.getClose();
|
settle(order, "watch", close, partyContractOrders);
|
|
BigDecimal add = order.getTradeAvgPrice().add(order.getPips());
|
BigDecimal subtract = order.getTradeAvgPrice().subtract(order.getPips());
|
if (ContractOrder.DIRECTION_BUY.equals(order.getDirection())) {
|
|
/*
|
* 0 买涨
|
*/
|
if (close.compareTo(add) >= 0) {
|
settle(order, "profit", close, partyContractOrders);
|
}
|
|
if (close.compareTo(subtract) <= 0) {
|
settle(order, "loss", close, partyContractOrders);
|
}
|
|
} else {
|
/*
|
* 1 买跌
|
*/
|
if (close.compareTo(subtract) <= 0) {
|
settle(order, "profit", close, partyContractOrders);
|
}
|
if (close.compareTo(add) >= 0) {
|
settle(order, "loss", close, partyContractOrders);
|
}
|
}
|
} catch (Throwable e) {
|
log.error("ContractOrderCalculatio run fail", e);
|
}
|
|
}
|
|
/**
|
* 盈亏计算
|
*
|
* @param profit_loss profit 盈 loss亏
|
* @param currentPrice 当前点位
|
*/
|
public void settle(ContractOrder order, String profit_loss, BigDecimal currentPrice, List<ContractOrder> partyContractOrders) {
|
|
applyMarkPriceToOrder(order, currentPrice);
|
|
/**
|
* 止盈价
|
*/
|
BigDecimal profitStop = order.getStopPriceProfit();
|
if (profitStop != null && profitStop.compareTo(BigDecimal.ZERO) > 0 && ContractOrder.DIRECTION_BUY.equals(order.getDirection())) {
|
/*
|
* 买涨
|
*/
|
if (currentPrice.compareTo(profitStop) >= 0) {
|
this.contractOrderService.saveClose(order.getPartyId().toString(), order.getOrderNo(), null);
|
return;
|
}
|
} else if (profitStop != null && profitStop.compareTo(BigDecimal.ZERO) > 0
|
&& ContractOrder.DIRECTION_SELL.equals(order.getDirection())) {
|
/**
|
* 买跌
|
*/
|
if (currentPrice.compareTo(profitStop) <= 0) {
|
this.contractOrderService.saveClose(order.getPartyId().toString(), order.getOrderNo(), null);
|
return;
|
}
|
}
|
|
/**
|
* 止亏线
|
*/
|
BigDecimal loss_stop = order.getStopPriceLoss();
|
|
if (loss_stop != null && loss_stop.compareTo(BigDecimal.ZERO) > 0 && ContractOrder.DIRECTION_BUY.equals(order.getDirection())) {
|
/*
|
* 买涨
|
*/
|
if (currentPrice.compareTo(loss_stop) <= 0) {
|
this.contractOrderService.saveClose(order.getPartyId().toString(), order.getOrderNo(), null);
|
return;
|
|
}
|
} else if (loss_stop != null && loss_stop.compareTo(BigDecimal.ZERO) > 0 && ContractOrder.DIRECTION_SELL.equals(order.getDirection())) {
|
/**
|
* 买跌
|
*/
|
|
if (currentPrice.compareTo(loss_stop) >= 0) {
|
this.contractOrderService.saveClose(order.getPartyId().toString(), order.getOrderNo(), null);
|
return;
|
}
|
}
|
ContractOrderProfit cp = contractOrderService.getCacheProfit(order.getUuid());
|
BigDecimal profit1 = cp != null ? cp.getProfit() : defaultZero(order.getProfit());
|
order.setForceClosePrice(calculateForceClosePriceForOrder(order));
|
this.contractOrderService.updateByIdBuffer(order);
|
if (order_close_line_type == 1) {
|
Wallet wallet = this.walletService.findByUserId(order.getPartyId().toString());
|
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())));
|
}
|
|
if (totalEquity.compareTo(BigDecimal.ZERO) <= 0) {
|
/**
|
* 触发全仓强平
|
*/
|
this.contractOrderService.saveClose(order.getPartyId().toString(), order.getOrderNo(), "强平");
|
|
}
|
} else {
|
if (profit1.compareTo(BigDecimal.ZERO) >= 0 || profit1.abs().compareTo(new BigDecimal("0.000001")) < 0) {
|
return;
|
}
|
BigDecimal divide = order.getDeposit().divide(profit1.abs(), 10, RoundingMode.HALF_UP);
|
if (divide.compareTo(order_close_line.divide(new BigDecimal(100), 10, RoundingMode.HALF_UP)) <= 0) {
|
/**
|
* 低于系统默认平仓线,进行强平
|
*/
|
this.contractOrderService.saveClose(order.getPartyId().toString(), order.getOrderNo(), "强平");
|
}
|
}
|
}
|
|
@Override
|
public String calculateForceClosePriceForOrder(ContractOrder order) {
|
if (order == null || defaultZero(order.getVolume()).compareTo(BigDecimal.ZERO) <= 0) {
|
return BigDecimal.ZERO.toPlainString();
|
}
|
BigDecimal forceClose;
|
if (order_close_line_type == 1) {
|
Wallet wallet = this.walletService.findByUserId(order.getPartyId().toString());
|
forceClose = calculateType1ForceClosePrice(order, wallet);
|
} else {
|
forceClose = calculateType2ForceClosePrice(order);
|
}
|
if (forceClose.compareTo(BigDecimal.ZERO) < 0) {
|
forceClose = BigDecimal.ZERO;
|
}
|
Integer decimal = itemService.getDecimal(order.getSymbol());
|
return forceClose.setScale(decimal, RoundingMode.HALF_UP).toPlainString();
|
}
|
|
private BigDecimal defaultZero(BigDecimal value) {
|
return value == null ? BigDecimal.ZERO : value;
|
}
|
|
/**
|
* 合约张数对应标的数量系数(每张面值,与 countSheets 一致,默认 0.01)
|
*/
|
private BigDecimal getContractFaceValue(ContractOrder order) {
|
Item item = itemService.findBySymbol(order.getSymbol());
|
if (item != null && item.getFaceValue() > 0) {
|
return BigDecimal.valueOf(item.getFaceValue());
|
}
|
return new BigDecimal("0.01");
|
}
|
|
/**
|
* 每张张数对应的价格敏感度:盈亏/强平 = 价差 × volume × faceValue
|
*/
|
private BigDecimal getVolumePriceFactor(ContractOrder order) {
|
return defaultZero(order.getVolume()).multiply(getContractFaceValue(order));
|
}
|
|
private BigDecimal calculateType1ForceClosePrice(ContractOrder order, Wallet wallet) {
|
BigDecimal tradeAvgPrice = defaultZero(order.getTradeAvgPrice());
|
BigDecimal volumeFactor = getVolumePriceFactor(order);
|
if (volumeFactor.compareTo(BigDecimal.ZERO) <= 0 || tradeAvgPrice.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(volumeFactor, 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 tradeAvgPrice = defaultZero(order.getTradeAvgPrice());
|
BigDecimal volumeFactor = getVolumePriceFactor(order);
|
if (volumeFactor.compareTo(BigDecimal.ZERO) <= 0 || tradeAvgPrice.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(volumeFactor, 10, RoundingMode.HALF_UP);
|
if (ContractOrder.DIRECTION_BUY.equalsIgnoreCase(order.getDirection())) {
|
return tradeAvgPrice.subtract(priceOffset);
|
}
|
return tradeAvgPrice.add(priceOffset);
|
}
|
|
private void applyMarkPriceToOrder(ContractOrder order, BigDecimal currentPrice) {
|
BigDecimal tradeAvgPrice = defaultZero(order.getTradeAvgPrice());
|
if (tradeAvgPrice.compareTo(BigDecimal.ZERO) <= 0) {
|
return;
|
}
|
BigDecimal volumeFactor = getVolumePriceFactor(order);
|
if (volumeFactor.compareTo(BigDecimal.ZERO) <= 0) {
|
return;
|
}
|
// 与平仓结算一致:盈亏 = (现价 - 开仓价) × 张数 × 面值
|
BigDecimal point = currentPrice.subtract(tradeAvgPrice);
|
BigDecimal priceProfit = point.multiply(volumeFactor).setScale(6, RoundingMode.DOWN);
|
if (!ContractOrder.DIRECTION_BUY.equals(order.getDirection())) {
|
priceProfit = priceProfit.negate();
|
}
|
BigDecimal fundingPnl = contractOrderService.calculateAccruedFundingPnl(order, currentPrice, new Date());
|
order.setProfit(priceProfit.add(fundingPnl));
|
order.setCloseAvgPrice(currentPrice);
|
this.contractOrderService.updateByIdBuffer(order);
|
}
|
|
public void setDataService(DataService dataService) {
|
this.dataService = dataService;
|
}
|
|
public void setSysparaService(SysparaService sysparaService) {
|
this.sysparaService = sysparaService;
|
}
|
|
public void setContractOrderService(ContractOrderService contractOrderService) {
|
this.contractOrderService = contractOrderService;
|
}
|
|
public void setWalletService(WalletService walletService) {
|
this.walletService = walletService;
|
}
|
|
public void setOrder_close_line(BigDecimal order_close_line) {
|
this.order_close_line = order_close_line;
|
}
|
|
public void setOrder_close_line_type(int order_close_line_type) {
|
this.order_close_line_type = order_close_line_type;
|
}
|
|
@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, list.get(0).getClose());
|
}
|
}
|