using YLErp.DBModels; using YLErp.Modules.SwapModule; using YLErp.Modules.SwapModule.Margin; namespace YLErp.Modules.SwapModule { /// /// R3 阶段四 §4.1 合约维度追加保证金计算测试(纯函数): /// 目标 = 维持保证金 − 应付预付金净收额(≤0 不追);当日新增 = 目标 − 已补足(现金记录累计+授信占用累计); /// 授信优先分配;重跑幂等(新增=0 不写);追保回落不返还(负缺口走可用资金公式)。 /// [TestClass] public class SwapAdditionalMarginCalcTest { [TestMethod] public void AM_001_目标计算_维持大于应付() { //维持 180 − 应付净收 100 = 80 Assert.AreEqual(80, SwapAdditionalMarginCalc.CalcTarget(180, 100), 1e-6); } [TestMethod] public void AM_002_目标计算_应付覆盖维持_不追() { Assert.AreEqual(0, SwapAdditionalMarginCalc.CalcTarget(80, 100), 1e-6); } [TestMethod] public void AM_003_目标计算_维持为负_我方净支付方向不追() { Assert.AreEqual(0, SwapAdditionalMarginCalc.CalcTarget(-50, 100), 1e-6); } [TestMethod] public void AM_004_分配_授信充足_全额授信() { var (credit, cash) = SwapAdditionalMarginCalc.Allocate(80, 100); Assert.AreEqual(80, credit, 1e-6); Assert.AreEqual(0, cash, 1e-6); } [TestMethod] public void AM_005_分配_授信不足_拆为授信加现金() { var (credit, cash) = SwapAdditionalMarginCalc.Allocate(80, 30); Assert.AreEqual(30, credit, 1e-6); Assert.AreEqual(50, cash, 1e-6); } [TestMethod] public void AM_006_分配_无授信_全额现金() { var (credit, cash) = SwapAdditionalMarginCalc.Allocate(80, 0); Assert.AreEqual(0, credit, 1e-6); Assert.AreEqual(80, cash, 1e-6); } [TestMethod] public void AM_007_分配_新增为零或负_不产生() { Assert.AreEqual((0d, 0d), SwapAdditionalMarginCalc.Allocate(0, 100)); Assert.AreEqual((0d, 0d), SwapAdditionalMarginCalc.Allocate(-5, 100)); } /// /// 幂等与跨日增量(资金记录存累计值的口径验证): /// 第1日 维持180/应付100 → 新增80;第2日 维持不变、已补足80 → 新增0(重跑不写); /// 第3日 维持升到200 → 目标100 − 已补足80 = 新增20(记录累计值更新为100,历史增量自然承载)。 /// [TestMethod] public void AM_008_跨日增量与幂等() { double fundedCash = 0, fundedCredit = 0; var payableNet = 100d; var day1 = Math.Round(SwapAdditionalMarginCalc.CalcTarget(180, payableNet) - fundedCash - fundedCredit, 2, MidpointRounding.AwayFromZero); Assert.AreEqual(80, day1, 1e-6); var (c1, m1) = SwapAdditionalMarginCalc.Allocate(day1, 30); fundedCredit += c1; fundedCash += m1; Assert.AreEqual(30, fundedCredit, 1e-6); Assert.AreEqual(50, fundedCash, 1e-6); //重跑同日:目标/已补足不变 → 新增 0 var day1Rerun = Math.Round(SwapAdditionalMarginCalc.CalcTarget(180, payableNet) - fundedCash - fundedCredit, 2, MidpointRounding.AwayFromZero); Assert.AreEqual(0, day1Rerun, 1e-6); //第2日 维持不变 → 已补足=目标 → 新增 0 var day2 = Math.Round(SwapAdditionalMarginCalc.CalcTarget(180, payableNet) - fundedCash - fundedCredit, 2, MidpointRounding.AwayFromZero); Assert.AreEqual(0, day2, 1e-6); //第3日 维持上升 → 只补差额 var day3 = Math.Round(SwapAdditionalMarginCalc.CalcTarget(200, payableNet) - fundedCash - fundedCredit, 2, MidpointRounding.AwayFromZero); Assert.AreEqual(20, day3, 1e-6); } /// /// 追保回落(维持下降到应付之下)不返还:目标为 0 → 已补足保持,新增 0; /// 超付部分由可用资金公式的负缺口(Σ维持−累计)体现,不产生返还记录。 /// [TestMethod] public void AM_009_追保回落不返还() { var target = SwapAdditionalMarginCalc.CalcTarget(50, 100); Assert.AreEqual(0, target, 1e-6); var (credit, cash) = SwapAdditionalMarginCalc.Allocate(target - 80, 100); Assert.AreEqual(0, credit, 1e-6); Assert.AreEqual(0, cash, 1e-6); } /// /// 追加保证金授信出入记录的 remark 前缀识别(RemoveByTrade 保留判定与累计口径共用)。 /// [TestMethod] public void AM_010_追加保证金记录识别() { Assert.IsTrue(ClientCreditInoutService.IsAdditionalMarginRecord( new client_credit_inout { change_type = client_credit_inout.ChangeTypeOccupy, remark = "追加保证金占用" })); Assert.IsFalse(ClientCreditInoutService.IsAdditionalMarginRecord( new client_credit_inout { change_type = client_credit_inout.ChangeTypeOccupy, remark = "簿记授信占用" })); Assert.IsFalse(ClientCreditInoutService.IsAdditionalMarginRecord( new client_credit_inout { remark = null })); } } }