SetFixedLegRealizedPnl 替换4处复制粘贴(清理L1296双分号); NormalizeInterestSignForReport 替换16行内联报表分支; CalculateWeightedMarginInterest private->public static。均为纯静态无实例依赖,配套无库单测锁定行为。
163 lines
7.0 KiB
C#
163 lines
7.0 KiB
C#
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 求和精度保持");
|
||
}
|
||
|
||
// ================================================================
|
||
// 场景8:null 入参 —— 抛 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
|
||
};
|
||
}
|
||
}
|
||
}
|