Files
zszq-trs/UnitTestProject/Modules/SwapModule/InterestLegs/InterestLegStrategyTest.cs
T
hjhan aca834d8dc refactor(interest): 新增预付金腿策略 + 策略工厂
新增(零生产改动):
- InterestLegs/PrepayLeg.cs
  mode 5 初始预付金 / mode 6 追加预付金 共用。
  计息基数=InterestPrincipalFix, 平仓本金=Fix×closePercent。
  构造时校验mode合法性(只接受5/6)。
  方向翻转/orginPv重映射等独有逻辑后续单独抽。

- InterestLegs/InterestLegStrategyFactory.cs
  按InterestMode返回对应策略, 收敛switch分发。
  死代码mode 3/4/7/8不注册, 传入会抛ArgumentException。

测试(14个全过):
- 预付金: 部分平仓/全平/非法mode构造
- 工厂: 各活跃mode返回正确类型/未注册mode抛异常/int重载等价

fixing接入尝试(已回退):
- 尝试用Fr007IndexFixer.Instance替换SwapDealService的6处取价
- 发现问题: 静态Instance绕过TryGetFloatRate(virtual)接缝,
  破坏测试stub机制(CI_007/CI_008失败)
- 已回退, 下次改用实例级IIndexFixer(委托TryGetFloatRate)方式接入
2026-08-10 18:40:04 +08:00

179 lines
7.1 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 System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using YLErp.DBModels.Enums;
using YLErp.Modules.SwapModule.InterestLegs;
namespace UnitTestProject.Modules.SwapModule.InterestLegs
{
/// <summary>
/// 利息腿策略单测。验证每个策略的 CalcNotional 与现有 CalcNotionalByMode switch 完全一致。
/// 这组测试是后续"迁移调用点"的安全网——迁移前后行为必须不变。
/// </summary>
[TestClass]
public class InterestLegStrategyTest
{
private const decimal Fix = 2_000_000m; // 合约固定本金
private const decimal Notional = 100_000_000m; // 剩余名义本金 1 亿
private const decimal LongNotional = 60_000_000m;
private const decimal ShortNotional = 40_000_000m;
#region 固定值(mode 1):恒=Fix,不随比例变
[TestMethod]
public void 固定值_部分平仓_计息基数恒等于Fix()
{
var leg = new FixedNotionalLeg();
var r = leg.CalcNotional(Fix, Notional, LongNotional, ShortNotional, 0.5m);
Assert.AreEqual(Fix, r.ClosePrincipal, "平仓本金恒=Fix");
Assert.AreEqual(Fix, r.PosiPrincipal, "持仓本金恒=Fix");
Assert.AreEqual(1m, r.ClosePercent, "有效比例恒=1(固定值不随比例缩放)");
}
[TestMethod]
public void 固定值_全平_计息基数仍等于Fix()
{
var leg = new FixedNotionalLeg();
var r = leg.CalcNotional(Fix, Notional, LongNotional, ShortNotional, 1m);
Assert.AreEqual(Fix, r.ClosePrincipal, "全平本金仍=Fix");
}
#endregion
#region 合约名义本金规模(mode 2):按比例线性缩放
[TestMethod]
public void 合约名义本金_部分平仓_本金按比例缩放()
{
var leg = new ContractNotionalLeg();
var r = leg.CalcNotional(Fix, Notional, LongNotional, ShortNotional, 0.5m);
Assert.AreEqual(50_000_000m, r.ClosePrincipal, "平仓本金=Notional×50%");
Assert.AreEqual(Notional, r.PosiPrincipal, "持仓本金=Notional全额");
Assert.AreEqual(0.5m, r.ClosePercent, "有效比例=入参");
}
[TestMethod]
public void 合约名义本金_全平_本金等于全额()
{
var leg = new ContractNotionalLeg();
var r = leg.CalcNotional(Fix, Notional, LongNotional, ShortNotional, 1m);
Assert.AreEqual(Notional, r.ClosePrincipal, "全平本金=Notional");
}
[TestMethod]
public void 合约名义本金_零平仓_本金为零()
{
var leg = new ContractNotionalLeg();
var r = leg.CalcNotional(Fix, Notional, LongNotional, ShortNotional, 0m);
Assert.AreEqual(0m, r.ClosePrincipal, "零平仓本金=0");
Assert.AreEqual(Notional, r.PosiPrincipal, "持仓本金仍=Notional");
}
#endregion
#region 标的期初全价(mode 9):主路径公式与mode2相同
[TestMethod]
public void 标的期初全价_部分平仓_主路径公式同mode2()
{
var leg = new UnderlyingFullPriceLeg();
var r = leg.CalcNotional(Fix, Notional, LongNotional, ShortNotional, 0.5m);
Assert.AreEqual(50_000_000m, r.ClosePrincipal, "平仓本金=Notional×50%(与mode2主路径一致)");
Assert.AreEqual(Notional, r.PosiPrincipal);
Assert.AreEqual(0.5m, r.ClosePercent);
}
[TestMethod]
public void 标的期初全价_全平_本金等于全额()
{
var leg = new UnderlyingFullPriceLeg();
var r = leg.CalcNotional(Fix, Notional, LongNotional, ShortNotional, 1m);
Assert.AreEqual(Notional, r.ClosePrincipal);
}
#endregion
#region 预付金(mode 5/6):按Fix缩放posiPrincipal=Fix全额
[TestMethod]
public void 预付金_初始_部分平仓_本金按Fix缩放()
{
var leg = new PrepayLeg(InterestModeEnum.初始预付金);
var r = leg.CalcNotional(Fix, Notional, LongNotional, ShortNotional, 0.5m);
Assert.AreEqual(1_000_000m, r.ClosePrincipal, "平仓本金=Fix×50%");
Assert.AreEqual(Fix, r.PosiPrincipal, "持仓本金=Fix全额");
Assert.AreEqual(0.5m, r.ClosePercent);
}
[TestMethod]
public void 预付金_追加_全平_本金等于Fix()
{
var leg = new PrepayLeg(InterestModeEnum.追加预付金);
var r = leg.CalcNotional(Fix, Notional, LongNotional, ShortNotional, 1m);
Assert.AreEqual(Fix, r.ClosePrincipal, "全平本金=Fix");
}
[TestMethod]
public void 预付金_构造非法mode应抛异常()
{
Assert.ThrowsException<ArgumentException>(() =>
new PrepayLeg(InterestModeEnum.固定值));
}
#endregion
#region 守卫:策略对应各自mode
[TestMethod]
public void 各策略对应正确枚举值()
{
Assert.AreEqual(InterestModeEnum.固定值, new FixedNotionalLeg().Mode);
Assert.AreEqual(InterestModeEnum.合约名义本金规模, new ContractNotionalLeg().Mode);
Assert.AreEqual(InterestModeEnum.标的期初全价, new UnderlyingFullPriceLeg().Mode);
Assert.AreEqual(InterestModeEnum.初始预付金, new PrepayLeg(InterestModeEnum.初始预付金).Mode);
Assert.AreEqual(InterestModeEnum.追加预付金, new PrepayLeg(InterestModeEnum.追加预付金).Mode);
}
#endregion
#region 工厂:按mode分发
[TestMethod]
public void 工厂_返回各活跃mode的策略()
{
Assert.IsInstanceOfType(InterestLegStrategyFactory.Get(InterestModeEnum.固定值), typeof(FixedNotionalLeg));
Assert.IsInstanceOfType(InterestLegStrategyFactory.Get(InterestModeEnum.合约名义本金规模), typeof(ContractNotionalLeg));
Assert.IsInstanceOfType(InterestLegStrategyFactory.Get(InterestModeEnum.标的期初全价), typeof(UnderlyingFullPriceLeg));
Assert.IsInstanceOfType(InterestLegStrategyFactory.Get(InterestModeEnum.初始预付金), typeof(PrepayLeg));
Assert.IsInstanceOfType(InterestLegStrategyFactory.Get(InterestModeEnum.追加预付金), typeof(PrepayLeg));
}
[TestMethod]
public void 工厂_未注册mode抛异常()
{
Assert.ThrowsException<ArgumentException>(() =>
InterestLegStrategyFactory.Get(InterestModeEnum.持仓名义本金));
Assert.ThrowsException<ArgumentException>(() =>
InterestLegStrategyFactory.Get(InterestModeEnum.持仓市值));
}
[TestMethod]
public void 工厂_int重载和枚举重载等价()
{
var byEnum = InterestLegStrategyFactory.Get(InterestModeEnum.固定值);
var byInt = InterestLegStrategyFactory.Get((int)InterestModeEnum.固定值);
Assert.AreEqual(byEnum.Mode, byInt.Mode);
}
#endregion
}
}