fix: 首页组合收益率实时计算

This commit is contained in:
OpenClaw Agent 2026-03-16 08:56:14 +00:00
parent c7712e57bb
commit 71aa7211d6

View File

@ -176,31 +176,61 @@ public class PortfolioService : IPortfolioService
foreach (var p in portfolios) foreach (var p in portfolios)
{ {
// 获取持仓数量 // 获取该组合的所有持仓
var positionCount = _db.Queryable<Position>() var positions = _db.Queryable<Position>()
.Where(pos => pos.PortfolioId == p.Id) .Where(pos => pos.PortfolioId == p.Id)
.Count(); .ToList();
// 获取今日盈亏(从净值历史) int positionCount = positions.Count;
var todayNav = _db.Queryable<PortfolioNavHistory>()
.Where(n => n.PortfolioId == p.Id && n.NavDate == DateTime.Today)
.First();
double todayProfit = 0; // 实时计算当前市值和总成本
if (todayNav != null && p.TotalValue > 0) decimal totalValue = 0;
decimal totalCost = 0;
decimal todayProfit = 0;
string portfolioCurrency = p.Currency ?? "CNY";
foreach (var pos in positions)
{ {
// 今日盈亏 = 今日市值 - 昨日市值 if (pos.StockCode == null || pos.Currency == null)
var yesterdayNav = _db.Queryable<PortfolioNavHistory>()
.Where(n => n.PortfolioId == p.Id && n.NavDate < DateTime.Today)
.OrderByDescending(n => n.NavDate)
.First();
if (yesterdayNav != null)
{ {
todayProfit = (double)(todayNav.TotalValue - yesterdayNav.TotalValue); continue;
} }
// 获取实时价格
decimal currentPrice = pos.AvgPrice;
decimal previousClose = pos.AvgPrice;
try
{
var priceResponse = _marketDataService.GetPriceAsync(pos.StockCode, pos.AssetType ?? "Stock").GetAwaiter().GetResult();
if (priceResponse.Price > 0)
{
currentPrice = priceResponse.Price;
previousClose = priceResponse.PreviousClose > 0 ? priceResponse.PreviousClose : currentPrice;
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "获取标的 {StockCode} 实时价格失败,使用成本价", pos.StockCode);
}
decimal positionValue = pos.Shares * currentPrice;
decimal positionCost = pos.Shares * pos.AvgPrice;
decimal positionTodayProfit = previousClose > 0 ? pos.Shares * (currentPrice - previousClose) : 0;
// 汇率转换到组合本位币
var valueInTarget = _exchangeRateService.ConvertAmountAsync(positionValue, pos.Currency, portfolioCurrency).GetAwaiter().GetResult();
var costInTarget = _exchangeRateService.ConvertAmountAsync(positionCost, pos.Currency, portfolioCurrency).GetAwaiter().GetResult();
var todayProfitInTarget = _exchangeRateService.ConvertAmountAsync(positionTodayProfit, pos.Currency, portfolioCurrency).GetAwaiter().GetResult();
totalValue += valueInTarget;
totalCost += costInTarget;
todayProfit += todayProfitInTarget;
} }
// 计算收益率
double returnRate = totalCost > 0 ? (double)((totalValue - totalCost) / totalCost * 100) : 0;
returnRate = Math.Round(returnRate, 2);
result.Add(new PortfolioListItem result.Add(new PortfolioListItem
{ {
Id = p.Id, Id = p.Id,
@ -211,11 +241,11 @@ public class PortfolioService : IPortfolioService
IconChar = p.Name?.Substring(0, 1).ToUpper() ?? "P", IconChar = p.Name?.Substring(0, 1).ToUpper() ?? "P",
IconBgClass = "bg-blue-100", IconBgClass = "bg-blue-100",
IconTextClass = "text-blue-700", IconTextClass = "text-blue-700",
Value = (double)p.TotalValue, Value = (double)totalValue,
Currency = p.Currency, Currency = p.Currency,
ReturnRate = (double)p.ReturnRate, ReturnRate = returnRate,
ReturnType = p.ReturnRate >= 0 ? "positive" : "negative", ReturnType = returnRate >= 0 ? "positive" : "negative",
TodayProfit = todayProfit, TodayProfit = (double)todayProfit,
TodayProfitCurrency = p.Currency TodayProfitCurrency = p.Currency
}); });
} }