Files
zszq-trs/UnitTestProject/Modules/SwapModule/SwapSpanBalanceCalcTest.cs
T

160 lines
8.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using YLErp.Modules.SwapModule.Margin;
namespace YLErp.Modules.SwapModule
{
/// <summary>
/// R2 阶段三 §3.2 可用资金/追保/可取资金公式测试(定稿 2026-08-28 口径,实时/EOD/报告三处共用 SwapSpanBalanceCalc):
/// 客户维度可用 = Max(现金结存 + 授信 − 已使用授信 + 初始保证金 − 维持保证金, 0);
/// 合约维度可用 = Max(现金结存 + 授信 − 已使用授信, 0)(定稿删除"−交易维度追加保证金",避免与现金结存/已使用授信双重扣减);
/// 追保金额两维度均 Max(...,0) 截断、恒 ≥ 0(只追不退);合约维度(2026-08-28 调整)= 当日追加保证金现金部分合计
/// (不与现金结存轧差、闲置现金不冲抵、授信不追),客户维度 = Max((维持−初始) − (现金+授信−已使用), 0);
/// 可取资金只算现金部分:合约维度 = Max(现金结存 + min(持仓盈亏,0), 0),客户维度 = Max(现金结存 + 初始 − 维持 + min(持仓盈亏,0), 0)。
/// 授信额度为 credit.Credit 合计(保存时已折算比例),现金结存=期末结存(阶段二起授信不进资金)。
/// </summary>
[TestClass]
public class SwapSpanBalanceCalcTest
{
[TestMethod]
public void SB_001_客户维度_现金充足()
{
//现金200 + 授信100 已使用20 + 初始100 维持150 = 230
Assert.AreEqual(230, SwapSpanBalanceCalc.CalcClientDimensionAvailable(
cashBalance: 200, totalCredit: 100, usedCredit: 20, initialMargin: 100, maintenanceMargin: 150), 1e-6);
}
[TestMethod]
public void SB_002_客户维度_现金不足_截断为0()
{
//现金20 + 0 0 + 100 130 = −10 → 0(追加10未补足时无可用资金)
Assert.AreEqual(0, SwapSpanBalanceCalc.CalcClientDimensionAvailable(
cashBalance: 20, totalCredit: 0, usedCredit: 0, initialMargin: 100, maintenanceMargin: 130), 1e-6);
}
[TestMethod]
public void SB_003_客户维度_授信可用额参与覆盖()
{
//现金20 + 授信100 已使用20 + 100 130 = 70
Assert.AreEqual(70, SwapSpanBalanceCalc.CalcClientDimensionAvailable(
cashBalance: 20, totalCredit: 100, usedCredit: 20, initialMargin: 100, maintenanceMargin: 130), 1e-6);
}
/// <summary>
/// 合约维度可用资金(定稿 2026-08-28):Max(现金结存 + 授信 − 已使用授信, 0),不再减交易维度追加保证金。
/// </summary>
[TestMethod]
public void SB_004_合约维度_可用资金()
{
//现金20 + 授信100 已使用20 = 100
Assert.AreEqual(100, SwapSpanBalanceCalc.CalcContractDimensionAvailable(
cashBalance: 20, totalCredit: 100, usedCredit: 20), 1e-6);
}
[TestMethod]
public void SB_005_合约维度_扣尽截断为0()
{
//现金20 + 授信0 已使用30 = 10 → 0
Assert.AreEqual(0, SwapSpanBalanceCalc.CalcContractDimensionAvailable(
cashBalance: 20, totalCredit: 0, usedCredit: 30), 1e-6);
}
/// <summary>
/// 客户维度追保金额(定稿 2026-08-28Max(...,0) 截断,恒 ≥ 0,只追不退)。
/// </summary>
[TestMethod]
public void SB_010_客户维度追保金额_正数需追保()
{
//追保差额 (维持150 − 初始100)=50;资金 现金5+授信0−已使用0=5 → 追保 = 50 5 = 45
Assert.AreEqual(45, SwapSpanBalanceCalc.CalcClientDimensionCallMargin(
cashBalance: 5, totalCredit: 0, usedCredit: 0, initialMargin: 100, maintenanceMargin: 150), 1e-6);
}
[TestMethod]
public void SB_011_客户维度追保金额_盈余截断为0()
{
//差额 (维持130−初始100)=30,资金 20+10020=100 → 30 100 = −70 → 截断为 0(资金富余不展示负数)
Assert.AreEqual(0, SwapSpanBalanceCalc.CalcClientDimensionCallMargin(
cashBalance: 20, totalCredit: 100, usedCredit: 20, initialMargin: 100, maintenanceMargin: 130), 1e-6);
}
/// <summary>
/// 客户维度追保金额与可用资金公式互为反向(各加 Max 截断):追保 = Max(−(未截断可用资金), 0)。
/// </summary>
[TestMethod]
public void SB_012_客户维度追保与可用资金反向一致()
{
var cash = 20d;
var credit = 100d;
var used = 20d;
var initial = 100d;
var maintenance = 130d;
var available = SwapSpanBalanceCalc.CalcClientDimensionAvailable(cash, credit, used, initial, maintenance);
var callMargin = SwapSpanBalanceCalc.CalcClientDimensionCallMargin(cash, credit, used, initial, maintenance);
var availableUnfloored = cash + credit - used + initial - maintenance;
Assert.AreEqual(Math.Max(-availableUnfloored, 0), callMargin, 1e-9);
//可用资金被 0 截断时追保为正(需追保),两者不矛盾
if (availableUnfloored < 0)
{
Assert.AreEqual(0, available, 1e-9);
Assert.IsTrue(callMargin > 0);
}
}
/// <summary>
/// 合约维度追保金额(2026-08-28 新口径):当日产生的追加保证金现金部分合计,不与现金结存轧差、
/// 闲置现金不冲抵、授信部分不追;Max(...,0) 兜底截断(正常无负记录,追保回落不返还)。
/// </summary>
[TestMethod]
public void SB_013_合约维度追保金额_当日现金追加全额()
{
//场景:入金60万、现金初保50万(闲置10万),当日需追加40万(现金)——
//旧口径缺口法追 30万(闲置现金被冲抵);新口径追当日现金追加全额 40万
Assert.AreEqual(400000, SwapSpanBalanceCalc.CalcContractDimensionCallMargin(400000), 1e-6);
}
[TestMethod]
public void SB_014_合约维度追保金额_无当日追加为0()
{
//当日追加全走授信(无现金流水)/当日无追保 → 0;负值兜底截断为 0
Assert.AreEqual(0, SwapSpanBalanceCalc.CalcContractDimensionCallMargin(0), 1e-6);
Assert.AreEqual(0, SwapSpanBalanceCalc.CalcContractDimensionCallMargin(-100), 1e-6);
}
/// <summary>
/// 可取资金(定稿 2026-08-28):只算现金部分(授信不可取现);min(持仓盈亏,0) 只扣浮亏、浮盈不放行。
/// </summary>
[TestMethod]
public void SB_015_客户维度可取资金_浮亏扣减()
{
//现金200 + 初始100 维持150 + min(30,0) = 120
Assert.AreEqual(120, SwapSpanBalanceCalc.CalcClientDimensionDesirableFund(
cashBalance: 200, initialMargin: 100, maintenanceMargin: 150, positionPnl: -30), 1e-6);
}
[TestMethod]
public void SB_016_客户维度可取资金_浮盈不放行()
{
//现金200 + 初始100 维持150 + min(50,0)=0 → 150
Assert.AreEqual(150, SwapSpanBalanceCalc.CalcClientDimensionDesirableFund(
cashBalance: 200, initialMargin: 100, maintenanceMargin: 150, positionPnl: 50), 1e-6);
}
[TestMethod]
public void SB_017_客户维度可取资金_截断为0()
{
//现金20 + 初始100 维持150 + min(40,0) = 70 → 0
Assert.AreEqual(0, SwapSpanBalanceCalc.CalcClientDimensionDesirableFund(
cashBalance: 20, initialMargin: 100, maintenanceMargin: 150, positionPnl: -40), 1e-6);
}
[TestMethod]
public void SB_018_合约维度可取资金()
{
//Max(100 + min(30,0), 0) = 70;浮亏超过现金时截断为 0
Assert.AreEqual(70, SwapSpanBalanceCalc.CalcContractDimensionDesirableFund(cashBalance: 100, positionPnl: -30), 1e-6);
Assert.AreEqual(0, SwapSpanBalanceCalc.CalcContractDimensionDesirableFund(cashBalance: 20, positionPnl: -50), 1e-6);
//浮盈不放行:Max(100 + min(80,0), 0) = 100
Assert.AreEqual(100, SwapSpanBalanceCalc.CalcContractDimensionDesirableFund(cashBalance: 100, positionPnl: 80), 1e-6);
}
}
}