Files
zszq-trs/UnitTestProject/Modules/SwapModule/SwapFixedLegRealizedPnlTest.cs
T
hjhan a153a1d1a6 refactor(swap-eod): 抽出期末头寸纯函数并放宽可见性
SetFixedLegRealizedPnl 替换4处复制粘贴(清理L1296双分号); NormalizeInterestSignForReport 替换16行内联报表分支; CalculateWeightedMarginInterest private->public static。均为纯静态无实例依赖,配套无库单测锁定行为。
2026-07-21 15:28:13 +08:00

163 lines
7.0 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.DBModels;
namespace YLErp.Modules.SwapModule
{
/// <summary>
/// SwapEodPositionService.SetFixedLegRealizedPnl 的回归测试。
/// -----------------------------------------------------------------
/// 守卫提交 f4ffe710 "fix(swap): 修复期末头寸已实现盈亏计算问题"。
///
/// 4 处 SaveAutoEodInterestPosition/SaveEodInterestPosition 路径原本各自复制粘贴:
/// newEodPayPosition.RealizedPnl = RealizedInterest + RealizedInterestFee
/// 其中 L1296 还遗留了双分号笔误 ";;"。抽为单一纯函数后消除复制粘贴风险,
/// 并锁定"固定利息腿累计已实现盈亏 = 累计利息 + 累计利息费用"口径。
///
/// 单测覆盖:正/负/零/混合符号、大额、InterestFee 为零等场景。
/// </summary>
[TestClass]
public class SwapFixedLegRealizedPnlTest
{
// ================================================================
// 场景1:收取方向,利息与利息费用均为正
// ================================================================
[TestMethod]
public void 收取方向_利息与费用均为正_求和()
{
var pos = NewPosition(realizedInterest: 1000m, realizedInterestFee: 200m);
SwapEodPositionService.SetFixedLegRealizedPnl(pos);
Assert.AreEqual(1200m, pos.RealizedPnl, 0.0001m,
"1000 + 200 = 1200");
}
// ================================================================
// 场景2:支付方向,利息与利息费用均为负
// ================================================================
[TestMethod]
public void 支付方向_利息与费用均为负_求和()
{
var pos = NewPosition(realizedInterest: -1000m, realizedInterestFee: -200m);
SwapEodPositionService.SetFixedLegRealizedPnl(pos);
Assert.AreEqual(-1200m, pos.RealizedPnl, 0.0001m,
"-1000 + (-200) = -1200");
}
// ================================================================
// 场景3:利息费用为零 —— RealizedPnl = RealizedInterest
// 回归场景:部分路径利息费用未发生,确保不误乘/不丢值
// ================================================================
[TestMethod]
public void 利息费用为零_盈亏等于利息()
{
var pos = NewPosition(realizedInterest: 500m, realizedInterestFee: 0m);
SwapEodPositionService.SetFixedLegRealizedPnl(pos);
Assert.AreEqual(500m, pos.RealizedPnl, 0.0001m,
"利息费用=0 时 RealizedPnl = RealizedInterest");
}
// ================================================================
// 场景4:利息为零 —— RealizedPnl = RealizedInterestFee
// ================================================================
[TestMethod]
public void 利息为零_盈亏等于利息费用()
{
var pos = NewPosition(realizedInterest: 0m, realizedInterestFee: 300m);
SwapEodPositionService.SetFixedLegRealizedPnl(pos);
Assert.AreEqual(300m, pos.RealizedPnl, 0.0001m,
"利息=0 时 RealizedPnl = RealizedInterestFee");
}
// ================================================================
// 场景5:两者均为零 —— RealizedPnl = 0
// ================================================================
[TestMethod]
public void 利息与费用均为零_盈亏为零()
{
var pos = NewPosition(realizedInterest: 0m, realizedInterestFee: 0m);
SwapEodPositionService.SetFixedLegRealizedPnl(pos);
Assert.AreEqual(0m, pos.RealizedPnl, 0.0001m,
"两者均为 0 时 RealizedPnl = 0");
}
// ================================================================
// 场景6:混合符号(利息负、利息费用正)—— 直接求和
// 回归场景:避免有人误加 Math.Abs 或方向判断
// ================================================================
[TestMethod]
public void 混合符号_利息负费用正_直接求和()
{
var pos = NewPosition(realizedInterest: -800m, realizedInterestFee: 100m);
SwapEodPositionService.SetFixedLegRealizedPnl(pos);
Assert.AreEqual(-700m, pos.RealizedPnl, 0.0001m,
"-800 + 100 = -700,不引入 Abs/方向判断");
}
// ================================================================
// 场景7:大额 —— 验证 decimal 精度无溢出
// ================================================================
[TestMethod]
public void 大额_decimal精度无溢出()
{
var pos = NewPosition(realizedInterest: 279_486_108.21m, realizedInterestFee: 13_668.02m);
SwapEodPositionService.SetFixedLegRealizedPnl(pos);
Assert.AreEqual(279_499_776.23m, pos.RealizedPnl, 0.0001m,
"大额 decimal 求和精度保持");
}
// ================================================================
// 场景8null 入参 —— 抛 NullReferenceException(现状锚点)
// 生产代码未加 null 检查,直接解引用 position 抛 NRE。
// 若未来改为 ArgumentNullException,此处需同步更新。
// ================================================================
[TestMethod]
public void Null入参_抛NullReferenceException()
{
Assert.ThrowsException<NullReferenceException>(() =>
SwapEodPositionService.SetFixedLegRealizedPnl(null!));
}
// ================================================================
// 场景9:覆盖原值 —— 验证是赋值而非累加
// 回归场景:防止有人误改为 += 导致重复计算
// ================================================================
[TestMethod]
public void 原有RealizedPnl被覆盖_非累加()
{
var pos = NewPosition(realizedInterest: 100m, realizedInterestFee: 50m);
pos.RealizedPnl = 9999m; // 预置一个非零旧值
SwapEodPositionService.SetFixedLegRealizedPnl(pos);
Assert.AreEqual(150m, pos.RealizedPnl, 0.0001m,
"应直接覆盖为 150,而非累加旧值 9999");
}
// ================================================================
// Helper:构造 eod_swap_position(只设置参与计算的 2 个字段)
// ================================================================
private static eod_swap_position NewPosition(
decimal realizedInterest,
decimal realizedInterestFee)
{
return new eod_swap_position
{
RealizedInterest = realizedInterest,
RealizedInterestFee = realizedInterestFee
};
}
}
}