From 0c389d73cc79a0027cf779721c6bf184b12f5d86 Mon Sep 17 00:00:00 2001
From: zj <1772600164@qq.com>
Date: Tue, 30 Sep 2025 18:53:21 +0800
Subject: [PATCH] 1

---
 trading-order-common/src/main/java/com/yami/trading/common/util/UTCDateUtils.java                             |   12 ++-
 trading-order-bean/src/main/java/com/yami/trading/bean/exchange/dto/ExchangeSymbolDto.java                    |   25 ++++++
 trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiWalletController.java                    |    3 
 trading-order-huobi/src/main/java/com.yami.trading.huobi/data/internal/DataDBServiceImpl.java                 |   11 +-
 trading-order-service/src/main/java/com/yami/trading/service/exchange/impl/ExchangeApplyOrderServiceImpl.java |  131 ++++++++++++++++++++------------
 5 files changed, 123 insertions(+), 59 deletions(-)

diff --git a/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiWalletController.java b/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiWalletController.java
index b90503b..21b46b1 100644
--- a/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiWalletController.java
+++ b/trading-order-admin/src/main/java/com/yami/trading/api/controller/ApiWalletController.java
@@ -1,5 +1,6 @@
 package com.yami.trading.api.controller;
 
+import cn.hutool.core.collection.CollectionUtil;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.yami.trading.api.dto.ChannelBlockchainDto;
 import com.yami.trading.api.model.GetChannelBlockchainModel;
@@ -275,7 +276,7 @@
         }
         List<WalletExtend> walletExtends = null;
         log.info(list_symbol.toString() + "=============");
-        if (StringUtils.isNotEmpty(partyId)) {
+        if (StringUtils.isNotEmpty(partyId) && CollectionUtil.isNotEmpty(list_symbol)) {
             walletExtends = this.walletService.findExtend(partyId, list_symbol);
         }
         if (null == walletExtends) {
diff --git a/trading-order-bean/src/main/java/com/yami/trading/bean/exchange/dto/ExchangeSymbolDto.java b/trading-order-bean/src/main/java/com/yami/trading/bean/exchange/dto/ExchangeSymbolDto.java
index 35d6490..3cd2bab 100644
--- a/trading-order-bean/src/main/java/com/yami/trading/bean/exchange/dto/ExchangeSymbolDto.java
+++ b/trading-order-bean/src/main/java/com/yami/trading/bean/exchange/dto/ExchangeSymbolDto.java
@@ -1,5 +1,7 @@
 package com.yami.trading.bean.exchange.dto;
 
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
@@ -20,13 +22,34 @@
     private  double positionVolume;//持仓数
     private  double price;  //成本
     private  double currentPrice;//现价
-
+    /**
+     * "open":买入 "close":卖出
+     */
+    private String offset;
     private  double profitLoss=0;  //总盈亏
+    /**
+     * 状态。submitted 已提交,canceled 已撤销, created 委托完成
+     */
+    private String state = "submitted";
+
+    /**
+     * 平仓时间戳
+     */
+    private Long closeTimeTs;
+
 
     private double toDayProfitLoss=0; //今日盈亏
 
     private  double profitLossPercentage; //总盈亏百分比
 
     private  double toDayProfitLossPercentage; //今日盈亏百分比
+    /**
+     * 累计盈亏
+     */
+    private double profitTotal;
 
+    /**
+     * 当日盈亏(结算)
+     */
+    private double todayProfit;
 }
diff --git a/trading-order-common/src/main/java/com/yami/trading/common/util/UTCDateUtils.java b/trading-order-common/src/main/java/com/yami/trading/common/util/UTCDateUtils.java
index 2b96c20..aa21201 100644
--- a/trading-order-common/src/main/java/com/yami/trading/common/util/UTCDateUtils.java
+++ b/trading-order-common/src/main/java/com/yami/trading/common/util/UTCDateUtils.java
@@ -7,9 +7,7 @@
 import java.text.DateFormat;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
-import java.time.LocalDateTime;
-import java.time.LocalTime;
-import java.time.ZoneOffset;
+import java.time.*;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.TimeZone;
@@ -189,5 +187,11 @@
         long endTimestamp = endDate.toInstant(ZoneOffset.of("+8")).toEpochMilli();
         return new Tuple(startTimestamp,endTimestamp);
     }
-    
+    public static boolean isTimestampFromToday(long timestamp, ZoneId zoneId) {
+        Instant instant = Instant.ofEpochSecond(timestamp);
+        LocalDate timestampDate = instant.atZone(zoneId).toLocalDate();
+        LocalDate today = LocalDate.now();
+
+        return timestampDate.isEqual(today);
+    }
 }
diff --git a/trading-order-huobi/src/main/java/com.yami.trading.huobi/data/internal/DataDBServiceImpl.java b/trading-order-huobi/src/main/java/com.yami.trading.huobi/data/internal/DataDBServiceImpl.java
index a9a5394..f1316e7 100644
--- a/trading-order-huobi/src/main/java/com.yami.trading.huobi/data/internal/DataDBServiceImpl.java
+++ b/trading-order-huobi/src/main/java/com.yami.trading.huobi/data/internal/DataDBServiceImpl.java
@@ -1,6 +1,7 @@
 package com.yami.trading.huobi.data.internal;
 
 
+import cn.hutool.core.collection.CollectionUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.yami.trading.bean.data.domain.Realtime;
 import com.yami.trading.common.config.RequestDataHelper;
@@ -48,11 +49,13 @@
 
         // 最近60s内实时价格集合
         List<Realtime> list = DataCache.latestRealTimeMap_60s.get(symbol);
-        if (list.size() >= KlineConstant.LATEST_REALTIME_LIST_MAX) {
-            list.remove(0);
+        if(CollectionUtil.isNotEmpty(list)){
+            if (list.size() >= KlineConstant.LATEST_REALTIME_LIST_MAX) {
+                list.remove(0);
+            }
+            list.add(realtime);
+            DataCache.latestRealTimeMap_60s.put(symbol, list);
         }
-        list.add(realtime);
-        DataCache.latestRealTimeMap_60s.put(symbol, list);
         RealtimeQueue.add(realtime);
     }
 
diff --git a/trading-order-service/src/main/java/com/yami/trading/service/exchange/impl/ExchangeApplyOrderServiceImpl.java b/trading-order-service/src/main/java/com/yami/trading/service/exchange/impl/ExchangeApplyOrderServiceImpl.java
index 635ba0d..3b355fa 100644
--- a/trading-order-service/src/main/java/com/yami/trading/service/exchange/impl/ExchangeApplyOrderServiceImpl.java
+++ b/trading-order-service/src/main/java/com/yami/trading/service/exchange/impl/ExchangeApplyOrderServiceImpl.java
@@ -7,6 +7,7 @@
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.google.gson.Gson;
+import com.yami.trading.bean.data.domain.Kline;
 import com.yami.trading.bean.data.domain.Realtime;
 import com.yami.trading.bean.exchange.ExchangeApplyOrder;
 import com.yami.trading.bean.exchange.dto.ExchangeApplyOrderDto;
@@ -40,6 +41,7 @@
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.text.DecimalFormat;
+import java.time.ZoneId;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -169,16 +171,12 @@
 
     @Override
     public List<ExchangeSymbolDto> getETFListByUserId(String userId, String type) {
-
         List<String> symbols = itemService.findByType(type).stream().map(Item::getSymbol).collect(Collectors.toList());
         symbols.add("-1");
-        log.info(JSONUtil.toJsonStr(symbols));
         LambdaQueryWrapper<ExchangeApplyOrder> lambdaQueryWrapper = Wrappers.<ExchangeApplyOrder>query().lambda();
         lambdaQueryWrapper.eq(ExchangeApplyOrder::getPartyId, userId);
         lambdaQueryWrapper.in(ExchangeApplyOrder::getSymbol, symbols);
-        lambdaQueryWrapper.notIn(ExchangeApplyOrder::getOffset, ExchangeApplyOrder.OFFSET_CLOSE);
-//        lambdaQueryWrapper.notIn(ExchangeApplyOrder::getOrderPriceType, "opponent");
-        lambdaQueryWrapper.eq(ExchangeApplyOrder::getState, ExchangeApplyOrder.STATE_CREATED);
+        lambdaQueryWrapper.eq(ExchangeApplyOrder::getState, ExchangeApplyOrder.STATE_SUBMITTED);
         lambdaQueryWrapper.orderByDesc(ExchangeApplyOrder::getCreateTime);
         List<ExchangeApplyOrder> list = list(lambdaQueryWrapper);
         return getDataList(list);
@@ -203,7 +201,6 @@
     }
 
     public List<ExchangeSymbolDto> getDataList(List<ExchangeApplyOrder> dbList) {
-
         List<ExchangeSymbolDto> result = new ArrayList<>();
         Map<String, List<ExchangeSymbolDto>> map = new HashMap<>();
         for (ExchangeApplyOrder order : dbList) {
@@ -211,70 +208,106 @@
             if (exchangeSymbolDtos == null) {
                 exchangeSymbolDtos = new ArrayList<>();
             }
-//            Realtime realtime = new Realtime();
-//            realtime.setClose(new BigDecimal(99));
-//            realtime.setOpen(new BigDecimal(100));
-//            ExchangeSymbolDto exchangeSymbolDto = new ExchangeSymbolDto();
-//            exchangeSymbolDto.setVolume(order.getVolume());
-//            exchangeSymbolDto.setName("test");
-//            exchangeSymbolDto.setOpenPrice(realtime.getOpen().doubleValue());
-//            exchangeSymbolDto.setCurrentPrice(realtime.getClose().doubleValue());
-//            exchangeSymbolDto.setPrice(order.getClosePrice());
-//            exchangeSymbolDtos.add(exchangeSymbolDto);
-//            map.put(order.getSymbol(), exchangeSymbolDtos);
             List<Realtime> symbolList = dataService.realtime(order.getSymbol());
             if (!CollectionUtil.isEmpty(symbolList)) {
                 Realtime realtime = symbolList.get(0);
                 ExchangeSymbolDto exchangeSymbolDto = new ExchangeSymbolDto();
-                exchangeSymbolDto.setVolume(order.getVolume());
+                exchangeSymbolDto.setVolume(order.getVolume() != 0 ? order.getVolume() : order.getSymbolValue());
                 exchangeSymbolDto.setName(realtime.getName());
-                exchangeSymbolDto.setOpenPrice(realtime.getOpen().doubleValue());
+                exchangeSymbolDto.setOpenPrice(order.getPrice());
                 exchangeSymbolDto.setCurrentPrice(realtime.getClose().doubleValue());
                 exchangeSymbolDto.setPrice(order.getClosePrice());
+                exchangeSymbolDto.setOffset(order.getOffset());
+                exchangeSymbolDto.setState(order.getState());
+                exchangeSymbolDto.setCloseTimeTs(order.getCreateTimeTs());
                 exchangeSymbolDtos.add(exchangeSymbolDto);
                 map.put(order.getSymbol(), exchangeSymbolDtos);
             }
         }
         for (String key : map.keySet()) {
             List<ExchangeSymbolDto> list = map.get(key);
+            Item item = itemService.findBySymbol(key);
             double volume = 0; //可用
-            double cost = 0.0;
+            double costValue = 0.0;//总价值
             double marketValue = 0; //市值
             double currentPrice = 0; //当前价格
-            double profitLoss = 0; //总盈亏
-            double toDayProfitLoss = 0; //今日总盈亏
-            double openPrice = 0;
+            double profitLoss = 0; //浮盈
+            double toDayProfitLoss = 0; //今日盈亏(浮动)
+            double todayProfit = 0; //今日盈亏(结算)
+            double profitTotal = 0; //累计盈亏
+            double openPrice = 0;//开仓价格
             String name = "";
-            for (ExchangeSymbolDto dto : list) {
-                volume += dto.getVolume();
+            BigDecimal price = BigDecimal.ZERO;//成本价
+            List<ExchangeSymbolDto> sort = list.stream().sorted(Comparator.comparing(ExchangeSymbolDto::getCloseTimeTs)).collect(Collectors.toList());
+            for (ExchangeSymbolDto dto : sort) {
                 name = dto.getName();
-                cost += (dto.getVolume() * dto.getPrice());
                 currentPrice = dto.getCurrentPrice();
-                openPrice = dto.getOpenPrice();
-                marketValue += (dto.getCurrentPrice() * dto.getVolume());
+                openPrice = dto.getPrice();
+                //成本价值
+                double cost = dto.getVolume() * dto.getPrice();
+
+                if (dto.getOffset().equals(ExchangeApplyOrder.OFFSET_CLOSE)) {
+                    volume -= dto.getVolume();
+
+                    if (ExchangeApplyOrder.STATE_CREATED.equalsIgnoreCase(dto.getState())) {
+                        profitTotal += dto.getVolume() * (dto.getPrice() - price.doubleValue());
+                        if (dto.getCloseTimeTs() != null && UTCDateUtils.isTimestampFromToday(dto.getCloseTimeTs(), ZoneId.systemDefault())) {
+                            todayProfit += dto.getVolume() * (dto.getPrice() - price.doubleValue());
+                        }
+                        costValue -= (dto.getVolume() * dto.getPrice());
+                        marketValue -= (dto.getCurrentPrice() * dto.getVolume());
+                    }
+                    if (volume == 0) {
+                        price = BigDecimal.ZERO;
+                    }
+                } else {
+                    volume += dto.getVolume();
+                    if (volume != 0) {
+                        price = price.multiply(BigDecimal.valueOf(volume - dto.getVolume()));
+                        price = price.add(BigDecimal.valueOf(cost));
+                        price = price.divide(BigDecimal.valueOf(volume), item.getDecimals(), RoundingMode.FLOOR);
+                    } else {
+                        price = BigDecimal.ZERO;
+                    }
+                    costValue += (dto.getVolume() * dto.getPrice());
+                    marketValue += (dto.getCurrentPrice() * dto.getVolume());
+                    //当日浮动盈亏
+                    List<Kline> kline = dataService.kline(key, Kline.PERIOD_1DAY);
+                    kline = kline.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
+                    double diff = 0.0;
+                    if (CollectionUtil.isNotEmpty(kline)) {
+                        Kline day = kline.get(0);
+                        Long ts = day.getTs() / 1000;
+                        Long timeTs = dto.getCloseTimeTs();
+                        if (timeTs > ts) {
+                            diff = currentPrice - openPrice;
+                        } else {
+                            diff = currentPrice - day.getClose().doubleValue();
+                        }
+                    }
+                    toDayProfitLoss += diff * dto.getVolume();
+                }
             }
-            double price = 0;
-            if (volume == 0) {
-                price = 0;
-            } else {
-                price = cost / volume;
+            if (volume > 0) {
+                //总浮动盈亏
+                profitLoss = marketValue - costValue;
+                ExchangeSymbolDto exchangeSymbolDto = new ExchangeSymbolDto();
+                exchangeSymbolDto.setVolume(new BigDecimal(volume).setScale(4, RoundingMode.HALF_UP).doubleValue());
+                exchangeSymbolDto.setPositionVolume(new BigDecimal(volume).setScale(4, RoundingMode.HALF_UP).doubleValue());
+                exchangeSymbolDto.setPrice(price.setScale(4, RoundingMode.HALF_UP).doubleValue());
+                exchangeSymbolDto.setName(name);
+                exchangeSymbolDto.setSymbol(key);
+                exchangeSymbolDto.setToDayProfitLoss(new BigDecimal(toDayProfitLoss).setScale(4, RoundingMode.HALF_UP).doubleValue());
+                exchangeSymbolDto.setToDayProfitLossPercentage(calculateProfitPercentage(openPrice, currentPrice));
+                exchangeSymbolDto.setCurrentPrice(new BigDecimal(currentPrice).setScale(4, RoundingMode.HALF_UP).doubleValue());
+                exchangeSymbolDto.setOpenPrice(new BigDecimal(openPrice).setScale(4, RoundingMode.HALF_UP).doubleValue());
+                exchangeSymbolDto.setMarketValue(new BigDecimal(marketValue).setScale(4, RoundingMode.HALF_UP).doubleValue());
+                exchangeSymbolDto.setProfitLoss(new BigDecimal(profitLoss).setScale(4, RoundingMode.HALF_UP).doubleValue());
+                exchangeSymbolDto.setProfitLossPercentage(calculateProfitPercentage(price.doubleValue(), currentPrice));
+                exchangeSymbolDto.setProfitTotal(new BigDecimal(profitTotal).setScale(4, RoundingMode.HALF_UP).doubleValue());
+                exchangeSymbolDto.setTodayProfit(todayProfit);
+                result.add(exchangeSymbolDto);
             }
-            toDayProfitLoss = (marketValue - (openPrice * volume));
-            profitLoss = marketValue - cost;
-            ExchangeSymbolDto exchangeSymbolDto = new ExchangeSymbolDto();
-            exchangeSymbolDto.setVolume(new BigDecimal(volume).setScale(2, RoundingMode.HALF_UP).doubleValue());
-            exchangeSymbolDto.setPositionVolume(new BigDecimal(volume).setScale(2, RoundingMode.HALF_UP).doubleValue());
-            exchangeSymbolDto.setPrice(new BigDecimal(price).setScale(2, RoundingMode.HALF_UP).doubleValue());
-            exchangeSymbolDto.setName(name);
-            exchangeSymbolDto.setSymbol(key);
-            exchangeSymbolDto.setToDayProfitLoss(new BigDecimal(toDayProfitLoss).setScale(2, RoundingMode.HALF_UP).doubleValue());
-            exchangeSymbolDto.setToDayProfitLossPercentage(calculateProfitPercentage(openPrice, currentPrice));
-            exchangeSymbolDto.setCurrentPrice(new BigDecimal(currentPrice).setScale(2, RoundingMode.HALF_UP).doubleValue());
-            exchangeSymbolDto.setOpenPrice(new BigDecimal(openPrice).setScale(2, RoundingMode.HALF_UP).doubleValue());
-            exchangeSymbolDto.setMarketValue(new BigDecimal(marketValue).setScale(2, RoundingMode.HALF_UP).doubleValue());
-            exchangeSymbolDto.setProfitLoss(new BigDecimal(profitLoss).setScale(2, RoundingMode.HALF_UP).doubleValue());
-            exchangeSymbolDto.setProfitLossPercentage(calculateProfitPercentage(price, currentPrice));
-            result.add(exchangeSymbolDto);
         }
         return result;
     }

--
Gitblit v1.9.3