143 lines
6.5 KiB
C#
143 lines
6.5 KiB
C#
using YLErp.DBModels;
|
||
using YLErp.Modules.SwapModule;
|
||
using YLErp.Modules.SwapModule.Margin;
|
||
|
||
namespace YLErp.Modules.SwapModule
|
||
{
|
||
/// <summary>
|
||
/// R3 阶段四 §4.1 合约维度追加保证金计算测试(纯函数):
|
||
/// 目标 = 维持保证金 − 应付预付金净收额(≤0 不追);当日新增 = 目标 − 已补足(现金记录累计+授信占用累计);
|
||
/// 授信优先分配;重跑幂等(新增=0 不写);追保回落不返还(负缺口走可用资金公式)。
|
||
/// </summary>
|
||
[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));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 幂等与跨日增量(资金记录存累计值的口径验证):
|
||
/// 第1日 维持180/应付100 → 新增80;第2日 维持不变、已补足80 → 新增0(重跑不写);
|
||
/// 第3日 维持升到200 → 目标100 − 已补足80 = 新增20(记录累计值更新为100,历史增量自然承载)。
|
||
/// </summary>
|
||
[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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 授信垫付的初始预付金计入已缴(2026-08-27 修正,交易2538实证):
|
||
/// 初始预付金走授信腿不产生应付预付金流水(现金净收额=0),已缴初始=授信初始占用净额——
|
||
/// 目标追加 = 维持 −(现金净收额+初始授信占用净额),否则每个结算日按维持全额重复开追加。
|
||
/// </summary>
|
||
[TestMethod]
|
||
public void AM_009_授信初始预付金计入已缴_不重复追加()
|
||
{
|
||
//交易2538 首日实际数据:维持2,808,000;初始预付金2,000,000全走授信(现金净收额0)
|
||
var payableNetCash = 0d;
|
||
var initCreditNet = 2_000_000d;
|
||
var target = SwapAdditionalMarginCalc.CalcTarget(2_808_000, payableNetCash + initCreditNet);
|
||
Assert.AreEqual(808_000, target, 1e-6);
|
||
|
||
//对照:修正前只扣现金净收额 → 目标2,808,000 全额追加(多收2,000,000 授信占用,即本BUG)
|
||
}
|
||
|
||
/// <summary>
|
||
/// 追保回落(维持下降到应付之下)不返还:目标为 0 → 已补足保持,新增 0;
|
||
/// 超付部分由可用资金公式的负缺口(Σ维持−累计)体现,不产生返还记录。
|
||
/// </summary>
|
||
[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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 追加保证金授信出入记录的 remark 前缀识别(RemoveByTrade 保留判定与累计口径共用)。
|
||
/// </summary>
|
||
[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 }));
|
||
}
|
||
}
|
||
}
|