fix: 修复更多金融计算bug
1. PortfolioNavService: CalculateAndSaveDailyNavAsync 卖出成本计算 - 使用动态持仓数量字典,而非当前持仓状态 - 确保多次卖出时比例计算正确 2. PortfolioService: GetPortfolioByIdAsync 盈亏率计算 - 先转换汇率再计算盈亏率,避免汇率变化影响 - 正确处理跨币种持仓的盈亏计算
This commit is contained in:
parent
2a297081b0
commit
82264ecc25
@ -189,7 +189,9 @@ public class PortfolioNavService : IPortfolioNavService
|
||||
.ToListAsync();
|
||||
|
||||
decimal totalCost = 0;
|
||||
var holdingsCost = new Dictionary<string, decimal>(); // 每个标的的累计成本
|
||||
// 每个标的的累计成本和数量(用于准确计算卖出比例)
|
||||
var holdingsCost = new Dictionary<string, decimal>();
|
||||
var holdingsShares = new Dictionary<string, decimal>();
|
||||
|
||||
foreach (var tx in transactions)
|
||||
{
|
||||
@ -202,36 +204,33 @@ 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 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 计算净值
|
||||
decimal nav = totalCost > 0 ? totalValue / totalCost : 1.0m;
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user