fix(swap): 统一处理【名义本金】数值精度舍入问题

- 在多个位置添加 Math.Round 函数确保金额计算精度
- 新增 NormalizeNotionalValues 方法统一处理 UnwindData 中的名义本金舍入
- 修复交易平仓时 StockEqvNotional 扣减计算的精度问题
- 解决持仓数据 PosiNotionalValue 的精度舍入处理
- 修复前端页面显示格式化问题
- 添加单元测试验证名义本金舍入逻辑正确性
This commit is contained in:
张名锐
2026-07-22 13:05:49 +08:00
parent e7c6e432e1
commit 0755233ab6
7 changed files with 53 additions and 21 deletions
+13 -4
View File
@@ -36,9 +36,17 @@ namespace YLErp.Modules.SwapModule
/// 原 private 改 protected virtual,使测试 stub 可整体 override,规避内部 new SwapEventService 连库。</summary>
protected virtual long SaveSwapDeal(UnwindData unwindData, int eventType, int clientCashId, string eventResason = "", bool approve = false)
{
NormalizeNotionalValues(unwindData);
return SaveSwapDealInternal(unwindData, eventType, clientCashId, eventResason, approve);
}
private static void NormalizeNotionalValues(UnwindData unwindData)
{
unwindData.NotionalValue = Math.Round(unwindData.NotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
unwindData.PosiNotionalValue = Math.Round(unwindData.PosiNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
unwindData.CloseNotionalValue = Math.Round(unwindData.CloseNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
}
/// <summary>保存所有变更(生产: DbContext.SaveChanges;测试: 空操作)</summary>
protected virtual void SaveAllChanges()
{
@@ -1222,6 +1230,7 @@ namespace YLErp.Modules.SwapModule
{
throw new ServiceException("未找到交易信息");
}
NormalizeNotionalValues(unwindData);
//CheckLastEod(unwindData.ValueDate, td.StartDate.Value, unwindData.SwapTradeId); //去掉平仓收盘限制
ValidateFrontendPnL(unwindData, isIncome: false); // 只读校验告警,不阻断交易
// 前端按"占期初(original)"语义传 ClosePercent(A);后端全链路按"占剩余(remaining)"语义(B)消费。
@@ -1252,7 +1261,7 @@ namespace YLErp.Modules.SwapModule
td.HasPartialUnWind = 1;
}
td.UnWindDate = unwindData.UnwindDate;
td.StockEqvNotional -= Convert.ToDouble(unwindData.CloseNotionalValue);
td.StockEqvNotional = Math.Round(td.StockEqvNotional - Convert.ToDouble(unwindData.CloseNotionalValue), ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
td.TradeAmount -= Convert.ToDouble(unwindData.CloseQty);
SaveAllChanges();
cofirm = true;
@@ -1568,7 +1577,7 @@ namespace YLErp.Modules.SwapModule
td.HasPartialUnWind = 1;
}
td.UnWindDate = unwindData.UnwindDate;
td.StockEqvNotional -= Convert.ToDouble(unwindData.CloseNotionalValue);
td.StockEqvNotional = Math.Round(td.StockEqvNotional - Convert.ToDouble(unwindData.CloseNotionalValue), ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
td.TradeAmount -= Convert.ToDouble(unwindData.CloseQty);
td.Notional = td.TradeAmount;
td.OptDate = DateTime.Now;
@@ -1767,7 +1776,7 @@ namespace YLErp.Modules.SwapModule
td.UnWindDate = swapEvent.unwindData.UnwindDate;
if (eventType != (int)SwapEventTypeEnum.)
{
td.StockEqvNotional -= Convert.ToDouble(swapEvent.unwindData.CloseNotionalValue);
td.StockEqvNotional = Math.Round(td.StockEqvNotional - Convert.ToDouble(swapEvent.unwindData.CloseNotionalValue), ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
td.TradeAmount -= Convert.ToDouble(swapEvent.unwindData.CloseQty);
}
@@ -1933,7 +1942,7 @@ namespace YLErp.Modules.SwapModule
{
// 平仓时才扣减持仓
position.PosiQuantity -= unwindData.CloseQty;
position.PosiNotionalValue -= unwindData.CloseNotionalValue;
position.PosiNotionalValue = Math.Round(position.PosiNotionalValue - unwindData.CloseNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
position.PosiTradingFee -= position.PosiTradingFee * unwindData.ClosePercent;
position.PosiTradingFeePending -= position.PosiTradingFeePending * unwindData.ClosePercent;
}