package com.yami.trading.admin.facade;
|
|
import cn.hutool.core.collection.CollectionUtil;
|
import com.google.common.collect.Lists;
|
import com.yami.trading.bean.data.domain.Realtime;
|
import com.yami.trading.bean.item.domain.Item;
|
import com.yami.trading.bean.item.dto.MarketQuotations;
|
import com.yami.trading.bean.item.dto.MarketQuotationsAdjust;
|
import com.yami.trading.bean.model.Log;
|
import com.yami.trading.common.constants.Constants;
|
import com.yami.trading.common.lang.LangUtils;
|
import com.yami.trading.common.util.Arith;
|
import com.yami.trading.common.util.IPHelper;
|
import com.yami.trading.common.util.ThreadUtils;
|
import com.yami.trading.huobi.data.AdjustmentValueCache;
|
import com.yami.trading.huobi.data.DataCache;
|
import com.yami.trading.huobi.data.internal.AdjustmentValueService;
|
import com.yami.trading.huobi.data.model.AdjustmentValue;
|
import com.yami.trading.security.common.model.YamiSysUser;
|
import com.yami.trading.security.common.util.SecurityUtils;
|
import com.yami.trading.service.data.DataService;
|
import com.yami.trading.service.item.ItemService;
|
import com.yami.trading.service.system.LogService;
|
import lombok.extern.slf4j.Slf4j;
|
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.lang3.StringUtils;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.stereotype.Component;
|
|
import java.math.BigDecimal;
|
import java.math.RoundingMode;
|
import java.text.MessageFormat;
|
import java.util.*;
|
|
@Component
|
@Slf4j
|
public class MarketQuotationsFacade {
|
@Qualifier("dataService")
|
@Autowired
|
private DataService dataService;
|
|
@Autowired
|
private AdjustmentValueService adjustmentValueService;
|
|
@Autowired
|
private ItemService itemService;
|
|
@Autowired
|
private LogService logService;
|
|
/**
|
* 行情列表
|
*
|
* @return
|
*/
|
public List<MarketQuotations> marketQuotationsList(List<String> symbols) {
|
if (CollectionUtils.isEmpty(symbols)) {
|
return null;
|
}
|
List<MarketQuotations> resultList = Lists.newLinkedList();
|
List<Realtime> realtimes = this.dataService.realtime(StringUtils.join(symbols, ","));
|
Set<String> symbolKey = new HashSet<String>();
|
for (Realtime realtime : realtimes) {
|
Integer decimal = itemService.getDecimal(realtime.getSymbol());
|
MarketQuotations marketQuotations = new MarketQuotations();
|
if (symbolKey.contains(realtime.getSymbol())) continue;
|
String name = itemService.findBySymbol(realtime.getSymbol()).getName();
|
marketQuotations.setName(name);
|
|
marketQuotations.setSymbol(realtime.getSymbol());
|
BigDecimal currentValue = this.adjustmentValueService.getCurrentValue(realtime.getSymbol());
|
if (currentValue == null) {
|
marketQuotations.setAdjustValue("0");
|
marketQuotations.setNewPrice(Arith.str(realtime.getClose(), decimal));
|
} else {
|
marketQuotations.setAdjustValue(Arith.str(currentValue, decimal));
|
// 原来价格是调整之后减去调整值
|
marketQuotations.setNewPrice(realtime.getClose().subtract(currentValue).setScale(decimal, BigDecimal.ROUND_HALF_UP).toPlainString());
|
}
|
marketQuotations.setAfterValue(Arith.str(realtime.getClose(), decimal));
|
resultList.add(marketQuotations);
|
symbolKey.add(realtime.getSymbol());
|
}
|
return resultList;
|
}
|
|
/**
|
* 获取详情
|
*
|
* @param symbol
|
* @return
|
*/
|
public MarketQuotationsAdjust getDetails(String symbol) {
|
MarketQuotationsAdjust marketQuotationsAdjust = new MarketQuotationsAdjust();
|
Integer decimal = itemService.getDecimal(symbol);
|
marketQuotationsAdjust.setSymbol(symbol);
|
Realtime realtime = DataCache.getRealtime(symbol);
|
if (realtime == null) {
|
List<Realtime> realtimeList = this.dataService.realtime(symbol);
|
if (CollectionUtil.isEmpty(realtimeList)) {
|
log.error("{} 当前没有实时价格", symbol);
|
return marketQuotationsAdjust;
|
}
|
realtime = realtimeList.get(0);
|
}
|
Item item = this.itemService.findBySymbol(symbol);
|
BigDecimal currentValue = this.adjustmentValueService.getCurrentValue(symbol);
|
if (currentValue == null) {
|
marketQuotationsAdjust.setAdjustValue(Arith.str(0, decimal));
|
} else {
|
marketQuotationsAdjust.setAdjustValue(Arith.str(currentValue, decimal));
|
}
|
if (currentValue == null) {
|
marketQuotationsAdjust.setNewPrice(Arith.str(realtime.getClose(), decimal));
|
} else {
|
marketQuotationsAdjust.setNewPrice(realtime.getClose().subtract(currentValue).setScale(decimal, RoundingMode.HALF_UP).toPlainString());
|
}
|
marketQuotationsAdjust.setPips(Arith.str(item.getPips(), decimal));
|
marketQuotationsAdjust.setAfterValue(Arith.str(realtime.getClose(), decimal));
|
|
AdjustmentValue delayValue = this.adjustmentValueService.getDelayValue(symbol);
|
|
if (delayValue != null) {
|
marketQuotationsAdjust.setDelayValue(Arith.str(delayValue.getValue(), decimal));
|
marketQuotationsAdjust.setDelaySecond("" + delayValue.getSecond());
|
}
|
return marketQuotationsAdjust;
|
}
|
|
/**
|
* 行情调整预计算值
|
*
|
* @param symbol
|
* @param type
|
* @param value
|
* @return
|
*/
|
public Map<String, String> calculateValue(String symbol, String type, double value) {
|
Map<String, String> resultMap = new HashMap<String, String>();
|
Integer decimal = itemService.getDecimal(symbol);
|
|
Realtime realtime = null;
|
|
realtime = this.dataService.realtime(symbol).get(0);
|
Item item = this.itemService.findBySymbol(symbol);
|
BigDecimal currentValue = this.adjustmentValueService.getCurrentValue(symbol);
|
if (currentValue == null) {
|
resultMap.put("newPrice", Arith.str(realtime.getClose(), decimal));
|
} else {
|
resultMap.put("newPrice", realtime.getClose().subtract(currentValue).toPlainString());
|
}
|
|
double temp;
|
if (type.equalsIgnoreCase("0")) {
|
temp = Arith.add(value, item.getPips().doubleValue());
|
// 调整量
|
resultMap.put("adjustCurrentValue", Arith.str(temp, decimal));
|
// 调整后的值
|
resultMap.put("adjustValueAfter", Arith.add(realtime.getClose().doubleValue(), temp, decimal));
|
} else if (type.equalsIgnoreCase("1")) {
|
temp = Arith.sub(value, item.getPips().doubleValue());
|
|
resultMap.put("adjustCurrentValue", temp + "");
|
resultMap.put("adjustValueAfter", Arith.add(realtime.getClose().doubleValue(), temp, decimal));
|
} else {
|
temp = value;
|
resultMap.put("adjustCurrentValue", temp + "");
|
resultMap.put("adjustValueAfter", Arith.add(realtime.getClose().doubleValue(), temp, decimal));
|
}
|
|
if (currentValue == null) {
|
resultMap.put("adjustValue", Arith.str(item.getPips(), decimal));
|
} else {
|
resultMap.put("adjustValue", Arith.add(temp, currentValue.doubleValue(), decimal));
|
}
|
AdjustmentValue delayValue = this.adjustmentValueService.getDelayValue(symbol);
|
|
if (delayValue != null) {
|
resultMap.put("delayValue", Arith.str(delayValue.getValue(), decimal));
|
resultMap.put("delaySecond", delayValue.getSecond() + "");
|
}
|
return resultMap;
|
|
}
|
|
/**
|
* 提交调整(与 getValue.action 预计算使用同一套 type 规则)。
|
*/
|
public void adjust(String symbol, Double second, BigDecimal value, String type) {
|
AdjustmentValueCache.getDelayValue().remove(symbol);
|
AdjustmentValueCache.getPreAllocatedAdjustments().remove(symbol);
|
AdjustmentValueCache.getCurrentAdjustmentIndex().remove(symbol);
|
AdjustmentValueCache.getFrequency().remove(symbol);
|
|
String adjustType = StringUtils.isBlank(type) ? "2" : type;
|
BigDecimal effectiveDelta = resolveEffectiveAdjustDelta(symbol, adjustType, value);
|
if (effectiveDelta == null || effectiveDelta.compareTo(BigDecimal.ZERO) == 0) {
|
log.warn("行情调整无效 symbol={} value={} type={}", symbol, value, adjustType);
|
return;
|
}
|
|
BigDecimal beforeAdjust = adjustmentValueService.getCurrentValue(symbol);
|
double secondVal = second == null ? 0D : second;
|
|
String logContent = MessageFormat.format(
|
"ip:" + IPHelper.getIpAddr() + ",管理员调整行情,币种:{0},调整前累计:{1},本次增量:{2},type:{3},延迟秒:{4}",
|
symbol,
|
beforeAdjust == null ? "0" : beforeAdjust.toPlainString(),
|
effectiveDelta.toPlainString(),
|
adjustType,
|
secondVal);
|
|
adjustmentValueService.adjust(symbol, effectiveDelta, secondVal);
|
|
// 立即生效时同步内存行情,避免只改缓存累计值但页面仍显示旧价
|
if (secondVal <= 0) {
|
syncRealtimeCacheAfterAdjust(symbol, effectiveDelta);
|
} else {
|
log.info("延迟调整已提交 symbol={} 目标增量={} 时长约{}秒,价格将分步变化(非瞬间到位)",
|
symbol, effectiveDelta.toPlainString(), secondVal);
|
}
|
|
saveLog(logContent);
|
ThreadUtils.sleep(500);
|
}
|
|
/**
|
* 与 calculateValue 一致:type 0/1 在输入值基础上加减 pips,type 2 直接使用输入值作为本次增量。
|
*/
|
private BigDecimal resolveEffectiveAdjustDelta(String symbol, String type, BigDecimal inputValue) {
|
if (inputValue == null) {
|
return null;
|
}
|
Item item = itemService.findBySymbol(symbol);
|
if (item == null) {
|
return inputValue;
|
}
|
double pips = item.getPips() != null ? item.getPips().doubleValue() : 0D;
|
double v = inputValue.doubleValue();
|
if ("0".equalsIgnoreCase(type)) {
|
return BigDecimal.valueOf(Arith.add(v, pips));
|
}
|
if ("1".equalsIgnoreCase(type)) {
|
return BigDecimal.valueOf(Arith.sub(v, pips));
|
}
|
return inputValue;
|
}
|
|
/** 将本次增量反映到 DataCache,与 WebSocket/采集任务展示逻辑一致 */
|
private void syncRealtimeCacheAfterAdjust(String symbol, BigDecimal effectiveDelta) {
|
Realtime realtime = DataCache.getRealtime(symbol);
|
if (realtime == null) {
|
List<Realtime> list = dataService.realtime(symbol);
|
if (CollectionUtil.isEmpty(list)) {
|
return;
|
}
|
realtime = list.get(0);
|
}
|
Integer decimal = itemService.getDecimal(symbol);
|
realtime.setClose(realtime.getClose().add(effectiveDelta).setScale(decimal, RoundingMode.HALF_UP));
|
if (realtime.getAsk() != null) {
|
realtime.setAsk(realtime.getAsk().add(effectiveDelta).setScale(decimal, RoundingMode.HALF_UP));
|
}
|
if (realtime.getBid() != null) {
|
realtime.setBid(realtime.getBid().add(effectiveDelta).setScale(decimal, RoundingMode.HALF_UP));
|
}
|
DataCache.putRealtime(symbol, realtime);
|
}
|
|
public void saveLog(String content) {
|
YamiSysUser sysUser = SecurityUtils.getSysUser();
|
Log log = new Log();
|
log.setCategory(Constants.LOG_CATEGORY_OPERATION);
|
log.setOperator(sysUser.getUsername());
|
log.setUsername(sysUser.getUsername());
|
log.setUserId(sysUser.getUserId().toString());
|
log.setLog(content);
|
log.setCreateTime(new Date());
|
logService.save(log);
|
}
|
}
|