From 82264ecc2565be4b5b465038cb2f24c353e57c8d Mon Sep 17 00:00:00 2001 From: OpenClaw Agent Date: Wed, 25 Mar 2026 04:09:55 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=9B=B4=E5=A4=9A?= =?UTF-8?q?=E9=87=91=E8=9E=8D=E8=AE=A1=E7=AE=97bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. PortfolioNavService: CalculateAndSaveDailyNavAsync 卖出成本计算 - 使用动态持仓数量字典,而非当前持仓状态 - 确保多次卖出时比例计算正确 2. PortfolioService: GetPortfolioByIdAsync 盈亏率计算 - 先转换汇率再计算盈亏率,避免汇率变化影响 - 正确处理跨币种持仓的盈亏计算 --- AssetManager.Services/PortfolioNavService.cs | 29 ++++++++++---------- AssetManager.Services/PortfolioService.cs | 8 ++++-- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/AssetManager.Services/PortfolioNavService.cs b/AssetManager.Services/PortfolioNavService.cs index e8f6b95..94b0682 100644 --- a/AssetManager.Services/PortfolioNavService.cs +++ b/AssetManager.Services/PortfolioNavService.cs @@ -189,7 +189,9 @@ public class PortfolioNavService : IPortfolioNavService .ToListAsync(); decimal totalCost = 0; - var holdingsCost = new Dictionary(); // 每个标的的累计成本 + // 每个标的的累计成本和数量(用于准确计算卖出比例) + var holdingsCost = new Dictionary(); + var holdingsShares = new Dictionary(); foreach (var tx in transactions) { @@ -202,33 +204,30 @@ public class PortfolioNavService : IPortfolioNavService txAmount, tx.Currency, targetCurrency); totalCost += txAmountInTarget; - // 更新该标的的累计成本 + // 更新该标的的累计成本和数量 if (holdingsCost.ContainsKey(tx.StockCode)) { holdingsCost[tx.StockCode] += txAmountInTarget; + holdingsShares[tx.StockCode] += tx.Amount; } else { holdingsCost[tx.StockCode] = txAmountInTarget; + holdingsShares[tx.StockCode] = tx.Amount; } } else if (tx.Type == "sell") { // 卖出时按比例减少该标的的累计成本 - if (holdingsCost.ContainsKey(tx.StockCode) && tx.Amount > 0) + if (holdingsCost.ContainsKey(tx.StockCode) && holdingsShares[tx.StockCode] > 0) { - // 需要知道当时该标的的总数量来计算比例 - // 从 Position 表获取当前持仓数量(不精确,但作为近似) - var position = positions.FirstOrDefault(p => p.StockCode == tx.StockCode); - if (position != null && position.Shares > 0) - { - // 近似:用当前持仓数量 + 卖出数量 作为原来数量 - decimal originalShares = position.Shares + tx.Amount; - decimal soldRatio = tx.Amount / originalShares; - decimal costToReduce = holdingsCost[tx.StockCode] * soldRatio; - holdingsCost[tx.StockCode] -= costToReduce; - totalCost -= costToReduce; - } + decimal currentShares = holdingsShares[tx.StockCode]; + decimal soldRatio = tx.Amount / currentShares; + decimal costToReduce = holdingsCost[tx.StockCode] * soldRatio; + + holdingsCost[tx.StockCode] -= costToReduce; + holdingsShares[tx.StockCode] -= tx.Amount; + totalCost -= costToReduce; } } } diff --git a/AssetManager.Services/PortfolioService.cs b/AssetManager.Services/PortfolioService.cs index 9920bc7..74c928a 100755 --- a/AssetManager.Services/PortfolioService.cs +++ b/AssetManager.Services/PortfolioService.cs @@ -496,15 +496,17 @@ public class PortfolioService : IPortfolioService decimal positionValue = pos.Shares * CurrentPrice; decimal cost = pos.Shares * pos.AvgPrice; - decimal Profit = positionValue - cost; - double ProfitRate = cost > 0 ? (double)(Profit / cost * 100) : 0; decimal TodayProfit = previousClose > 0 ? pos.Shares * (CurrentPrice - previousClose) : 0; - // 转换为组合本位币 + // 转换为组合本位币(先转换,再计算盈亏率,避免汇率变化影响) decimal positionValueInTarget = await _exchangeRateService.ConvertAmountAsync(positionValue, pos.Currency, targetCurrency); decimal costInTarget = await _exchangeRateService.ConvertAmountAsync(cost, pos.Currency, targetCurrency); decimal todayProfitInTarget = await _exchangeRateService.ConvertAmountAsync(TodayProfit, pos.Currency, targetCurrency); + // 用目标币种计算盈亏率(正确处理汇率变化) + decimal ProfitInTarget = positionValueInTarget - costInTarget; + double ProfitRate = costInTarget > 0 ? (double)(ProfitInTarget / costInTarget * 100) : 0; + totalPortfolioValue += positionValueInTarget; totalCost += costInTarget; totalTodayProfit += todayProfitInTarget;