diff --git a/UnitTestProject/Modules/SwapModule/SwapSpanBalanceCalcTest.cs b/UnitTestProject/Modules/SwapModule/SwapSpanBalanceCalcTest.cs
index 06535673..fda8849a 100644
--- a/UnitTestProject/Modules/SwapModule/SwapSpanBalanceCalcTest.cs
+++ b/UnitTestProject/Modules/SwapModule/SwapSpanBalanceCalcTest.cs
@@ -110,7 +110,7 @@ namespace YLErp.Modules.SwapModule
}
///
- /// 合约维度追保金额(需求原文 现金+授信−已使用 的应追加方向取值):账户透支为正=应补足,盈余为负。
+ /// 合约维度追保金额(Max((现金+授信−已使用)×−1, 0)):账户透支为正=应补足,盈余截断为 0。
///
[TestMethod]
public void SB_013_合约维度追保金额_透支为正()
@@ -121,10 +121,10 @@ namespace YLErp.Modules.SwapModule
}
[TestMethod]
- public void SB_014_合约维度追保金额_盈余为负()
+ public void SB_014_合约维度追保金额_盈余截断为0()
{
- //现金50 + 授信100 − 已使用20 = 130 → 追保 = −130(盈余可返还方向)
- Assert.AreEqual(-130, SwapSpanBalanceCalc.CalcContractDimensionCallMargin(
+ //现金50 + 授信100 − 已使用20 = 130 → 盈余,Max(...,0) 截断 → 追保 = 0
+ Assert.AreEqual(0, SwapSpanBalanceCalc.CalcContractDimensionCallMargin(
cashBalance: 50, totalCredit: 100, usedCredit: 20), 1e-6);
}
}
diff --git a/YLErpDAL/BLL/EodSettlement/ClientBalanceUtility.cs b/YLErpDAL/BLL/EodSettlement/ClientBalanceUtility.cs
index a4cc847b..d4158fbe 100644
--- a/YLErpDAL/BLL/EodSettlement/ClientBalanceUtility.cs
+++ b/YLErpDAL/BLL/EodSettlement/ClientBalanceUtility.cs
@@ -1,4 +1,4 @@
-using BaseOUDAL;
+using BaseOUDAL;
using DocumentFormat.OpenXml.Bibliography;
using DocumentFormat.OpenXml.Spreadsheet;
using MathNet.Numerics;
@@ -392,7 +392,7 @@ namespace YLErp.BLL.EodSettlement
}
// 是否追保/追保金额(阶段四 §4.2 按维度分流,允许负值=双向,不以 0 截断):
// 客户维度(==1)= (维持−初始) − (现金+授信−已使用),负=可返还;
- // 合约维度(==0)= −(现金+授信−已使用)(需求原文公式的应追加方向取值,开放问题2,两值均有产出);
+ // 合约维度(==0)= Max(−(现金+授信−已使用), 0)(需求原文公式,盈余截断为 0);
// 未配置(NULL 存量)维持旧口径:盯市低于维持时 = 初始保证金金额−盯市金额,否则 0
if (client?.MarginWatchRule == 1)
{
diff --git a/YLErpDAL/BLL/EodSettlement/RealTimeClientBanlanceService.cs b/YLErpDAL/BLL/EodSettlement/RealTimeClientBanlanceService.cs
index 804d3c98..f1740039 100644
--- a/YLErpDAL/BLL/EodSettlement/RealTimeClientBanlanceService.cs
+++ b/YLErpDAL/BLL/EodSettlement/RealTimeClientBanlanceService.cs
@@ -1,4 +1,4 @@
-using BaseOUDAL;
+using BaseOUDAL;
using Confluent.Kafka;
using DocumentFormat.OpenXml.Drawing.Charts;
using DocumentFormat.OpenXml.Office2010.Excel;
@@ -300,9 +300,9 @@ namespace YLErp.BLL.Eod
{
item.AvailableAmount = item.MarginBalance - item.FrozenMarginMoney;
}
- // 是否追保/追保金额(阶段四 §4.2 按维度分流,与 ClientBalanceUtility 报告口径一致):
+ // 是否追保/追保金额(按维度分流,与 ClientBalanceUtility 报告口径一致):
// 客户维度(==1)双向追保 = (维持−初始) − (现金+授信−已使用),允许负值(负=可返还);
- // 合约维度(==0)= −(现金+授信−已使用);NULL 存量维持旧口径
+ // 合约维度(==0)= Max(−(现金+授信−已使用), 0),盈余截断为 0;NULL 存量维持旧口径
if (ruleClient?.MarginWatchRule == 1)
{
item.MarginByPayableMarginTotal = SwapSpanBalanceCalc.CalcClientDimensionCallMargin(
diff --git a/YLErpDAL/Modules/SwapModule/Margin/SwapSpanBalanceCalc.cs b/YLErpDAL/Modules/SwapModule/Margin/SwapSpanBalanceCalc.cs
index c42a12da..3450b796 100644
--- a/YLErpDAL/Modules/SwapModule/Margin/SwapSpanBalanceCalc.cs
+++ b/YLErpDAL/Modules/SwapModule/Margin/SwapSpanBalanceCalc.cs
@@ -46,13 +46,12 @@ namespace YLErp.Modules.SwapModule.Margin
}
///
- /// 追保金额(合约维度,MarginWatchRule=0,阶段四 §4.2):−(现金结存 + 授信额度 − 已使用授信)。
- /// 需求原文公式为 现金结存+授信额度−已使用授信(与"交易维度追加保证金"的关系为开放问题2,两值均有产出),
- /// 此处取负对齐字段"正数=应追加"口径:账户透支(现金+授信不足)为正=应补足,盈余为负。
+ /// 追保金额(合约维度,MarginWatchRule=0):Max(−(现金结存 + 授信额度 − 已使用授信), 0)。
+ /// 即需求原文公式 Max((现金结存+授信额度−已使用授信)×−1, 0):账户透支(现金+授信不足)为正=应补足,盈余截断为 0(不展示负数)。
///
public static double CalcContractDimensionCallMargin(double cashBalance, double totalCredit, double usedCredit)
{
- return Math.Round(-(cashBalance + totalCredit - usedCredit), 2, MidpointRounding.AwayFromZero);
+ return Math.Max(Math.Round(-(cashBalance + totalCredit - usedCredit), 2, MidpointRounding.AwayFromZero), 0);
}
}
}
diff --git a/YLErpDAL/Modules/SwapModule/Margin/SwapSpanBalanceQueryService.cs b/YLErpDAL/Modules/SwapModule/Margin/SwapSpanBalanceQueryService.cs
index a599469b..0aecb695 100644
--- a/YLErpDAL/Modules/SwapModule/Margin/SwapSpanBalanceQueryService.cs
+++ b/YLErpDAL/Modules/SwapModule/Margin/SwapSpanBalanceQueryService.cs
@@ -11,7 +11,8 @@ namespace YLErp.Modules.SwapModule.Margin
/// - 互换初始保证金(净收取为正)= 客户 应付预付金 流水收付净额取反(客户应付入金记负、平仓返还为正,取负号后净收取为正);
/// - 交易维度追加保证金(合约维度)= Σ(维持保证金 − 累计保证金):
/// 维持保证金取当日 trade_span.Spv(区间追保结构引擎产出,我方净收取为正);
- /// 累计保证金 = 该交易 应付预付金+追加保证金 流水收付净额 + 追加保证金授信占用净额(§0 口径,阶段四 §4.1 起);
+ /// 累计保证金 = 该交易 应付预付金+追加保证金 流水收付净额 + 追加保证金授信占用净额(§0 口径,阶段四 §4.1 起)
+ /// + 初始预付金授信占用净额(非"追加保证金"前缀,授信垫付的初始预付金无现金流水,2026-08-27 与 EOD 追保侧同步修正);
/// 仅统计规则15(区间追保结构,R1 三层级解析,与引擎同口径)且有当日 trade_span 的交易。
///
public static class SwapSpanBalanceQueryService
@@ -101,11 +102,22 @@ namespace YLErp.Modules.SwapModule.Margin
.Select(g => new { TradeId = g.Key ?? 0, Sum = g.Sum(x => x.amount) })
.ToDictionary(x => x.TradeId, x => x.Sum);
+ //初始预付金的授信占用净额(非"追加保证金"前缀:簿记初始占用 + 平仓释放取负,Σ(amount) 即净已缴)——
+ //授信垫付的初始预付金不产生应付预付金流水,不计入会把授信初始占用当作未缴缺口多扣可用资金
+ //(与 SwapAdditionalMarginService EOD 追保侧同笔修正,2026-08-27 交易2538实证:初始授信200万未扣)
+ var initCreditOccupied = db.client_credit_inout.AsNoTracking()
+ .Where(x => x.trade_id != null && spanTradeIds.Contains(x.trade_id ?? 0)
+ && (x.remark == null || !x.remark.StartsWith(ClientCreditInoutService.AdditionalMarginRemark)))
+ .GroupBy(x => x.trade_id)
+ .Select(g => new { TradeId = g.Key ?? 0, Sum = g.Sum(x => x.amount) })
+ .ToDictionary(x => x.TradeId, x => x.Sum);
+
foreach (var group in maintenance.GroupBy(x => x.ClientId ?? 0))
{
var total = group.Sum(x => x.Spv
- (accumulated.TryGetValue(x.TradeId, out var acc) ? acc : 0d)
- - (addCreditOccupied.TryGetValue(x.TradeId, out var occupied) ? occupied : 0d));
+ - (addCreditOccupied.TryGetValue(x.TradeId, out var occupied) ? occupied : 0d)
+ - (initCreditOccupied.TryGetValue(x.TradeId, out var initOccupied) ? initOccupied : 0d));
result[group.Key] = total;
}