Files
zszq-trs/UnitTestProject/Modules/SwapModule/SwapUnwindFloatingLegDiagnosticTdd.cs
T
hjhan e9b54bdcf6 cleanup: 删除死代码枚举 mode 3/4/7/8 及相关引用
mode 3(持仓名义本金)/4(持仓市值): 零引用死代码
mode 7/8(多空存续名义本金): 界面已禁用, 无历史数据(用户确认)

删除:
- InterestModeEnum: 移除 持仓名义本金/持仓市值/多头存续名义本金/空头存续名义本金
  枚举值保持显式(1/2/5/6/9), DB契约不变
- CalcNotionalByMode: 移除 mode 7/8 的 case
- ConsTrade.InterestModels/InterestNotionalModels: 移除 mode 7/8
- 删除3个 mode 7/8 相关测试方法(场景已不存在)

CalcNotionalByMode 现在只剩 mode 5/6(保证金)的 case,
待 Margin 独立计息入口建成后整体删除。
验证: 编译0错误, 全量508测试7失败(基线一致)。
2026-08-11 13:00:52 +08:00

99 lines
6.9 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 Newtonsoft.Json;
using YLErp.DBModels;
using YLErp.DBModels.Enums;
namespace YLErp.Modules.SwapModule
{
/// <summary>
/// 诊断测试:验证「浮动腿 fpositions 仍用 origPositions(orig 100M)」对本 deal 的
/// 预付金/返回预付金结果是否产生影响。结论预期:本 deal 利息腿只有 mode 9(标的期初全价)
/// 与 mode 5(初始预付金)CalcNotionalByMode 中 posiLong/posiShort 仅在「多头/空头存续名义本金」
/// 分支被消费(L709-716),故本 deal 即便 fpositions 用 orig 100M,预付金腿结果也不受其影响。
/// 本测试仅做诊断/验证,不改动任何生产代码;用反射调用 private CalcNotionalByMode 以直接证明
/// “mode 9 / mode 5 的 closePrincipal 不依赖 posiLong/posiShort”。
/// </summary>
[TestClass]
public class SwapUnwindFloatingLegDiagnosticTdd
{
private sealed class StubSwapDealService : SwapDealService
{
public StubSwapDealService(OptUserInfo optUser) : base(optUser) { }
protected override bool TryGetFloatRate(DateTime valueDate, string underlyingCode, out double rate)
{ rate = 0; return false; }
}
private const decimal OrigFix = 99_000m; // 期初预付金腿初始本金
private const decimal RealFix = 66_813.12m; // 实时预付金腿剩余本金(4 次平仓后)
private const decimal OrigLong = 100_000_000m; // 期初标的(多头)名义本金
private const decimal RealLong = 68_947_200m; // 实时标的(多头)剩余名义本金
private const decimal ClosePct = 0.1m; // 本次平仓比例 10%
private static readonly DateTime D0 = new(2026, 7, 1);
private static readonly DateTime D1 = new(2026, 7, 16);
private SwapDealService _svc;
[TestInitialize] public void Init() => _svc = new StubSwapDealService(new OptUserInfo(0, nameof(SwapUnwindFloatingLegDiagnosticTdd), OptUserFrom.UnitTest));
// ---- GLMS 双轨持仓构造 ----
private static swap_position OrigPrepay(decimal fix = OrigFix) => new swap_position
{ id = 35798, SwapTradeId = 1993, PosiDirection = 0, InterestMode = (int)InterestModeEnum.初始预付金,
InterestPrincipalFix = fix, IsInitial = true, Invalid = false, InterestType = (int)InterestTypeEnum.单利,
interest_rest_days = 1, InterestDirection = (int)SwapDirectionEnum.收取, InterestSwapInterval = "[]" };
private static swap_position RealPrepay(decimal fix = RealFix) => new swap_position
{ id = 35871, SwapTradeId = 1993, PositionId = 35798, PosiDirection = 0, InterestMode = (int)InterestModeEnum.初始预付金,
InterestPrincipalFix = fix, IsInitial = false, Invalid = false, InterestType = (int)InterestTypeEnum.单利,
interest_rest_days = 1, InterestDirection = (int)SwapDirectionEnum.收取, InterestSwapInterval = "[]" };
private static swap_position OrigBasePrice() => new swap_position
{ id = 35797, SwapTradeId = 1993, PosiDirection = 0, InterestMode = (int)InterestModeEnum.标的期初全价,
InterestPrincipalFix = 0, IsInitial = true, Invalid = false, InterestType = (int)InterestTypeEnum.单利,
interest_rest_days = 1, InterestSwapInterval = "[]" };
private static swap_position RealBasePrice() => new swap_position
{ id = 35870, SwapTradeId = 1993, PositionId = 35797, PosiDirection = 0, InterestMode = (int)InterestModeEnum.标的期初全价,
InterestPrincipalFix = 0, IsInitial = false, Invalid = false, InterestType = (int)InterestTypeEnum.单利,
interest_rest_days = 1, InterestSwapInterval = "[]" };
private static swap_position OrigLongLeg() => new swap_position
{ id = 35799, SwapTradeId = 1993, PosiDirection = 2, PositionType = (int)PositionTypeFlag.Long, InterestMode = 0,
PosiNotionalValue = OrigLong, IsInitial = true, Invalid = false };
private static swap_position RealLongLeg() => new swap_position
{ id = 35872, SwapTradeId = 1993, PositionId = 35799, PosiDirection = 2, PositionType = (int)PositionTypeFlag.Long, InterestMode = 0,
PosiNotionalValue = RealLong, IsInitial = false, Invalid = false };
private static trade MakeTrade()
{
var extend = new trade_extend { TradeId = 1993, ExtendJson = JsonConvert.SerializeObject(new TradeExtendJson
{ AnnualDays = 365, InterestCalcMode = "10", SettlementRules = 0 }) };
return new trade { id = 1993, TradeNumber = "GLMS-20260701-0008", ClientId = 999998, TradeType = "收益互换",
TradeDate = D0, StartDate = D0, ExerciseDate = D1, TradeStatus = "确认成交", ValidState = "Valid",
StockEqvNotional = (double)RealLong, Notional = (double)RealLong, trade_extend = extend };
}
/// <summary>用反射调用 private CalcNotionalByMode,直接证明各 mode 的 closePrincipal 是否依赖 posiLong/posiShort。</summary>
private (decimal close, decimal posi, decimal pct) CallCalcNotionalByMode(swap_position position, decimal closePct, decimal posiNotional, decimal posiLong, decimal posiShort)
{
var m = typeof(SwapDealService).GetMethod("CalcNotionalByMode", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
return ((decimal, decimal, decimal))m.Invoke(_svc, new object[] { position, closePct, posiNotional, posiLong, posiShort });
}
[TestMethod]
public void 诊断_mode9_标的期初全价_closePrincipal_不依赖posiLong_而用posiNotional()
{
// mode 9 分支:closePrincipal = posiNotional * closePercent
var baseP = OrigBasePrice();
var (close, posi, _) = CallCalcNotionalByMode(baseP, ClosePct, RealLong * ClosePct, OrigLong, 0m);
Console.WriteLine($"[mode9] posiNotional={RealLong * ClosePct} posiLong(orig)={OrigLong} → closePrincipal={close}");
Assert.AreEqual(RealLong * ClosePct * ClosePct, close, "mode9 应 = posiNotional(=real剩余*closePct) * closePct,与 posiLong(orig 100M) 无关");
}
[TestMethod]
public void 诊断_mode5_预付金_closePrincipal_用自身Fix_不依赖posiLong()
{
// mode 5 分支:closePrincipal = position.InterestPrincipalFix * closePercent(用 Clone 后的 real Fix
var prepay = RealPrepay(); // Fix = RealFix(66,813.12)
var (close, posi, _) = CallCalcNotionalByMode(prepay, ClosePct, RealLong * ClosePct, OrigLong, 0m);
Console.WriteLine($"[mode5] Fix(cloned real)={RealFix} posiLong(orig)={OrigLong} → closePrincipal={close}");
Assert.AreEqual(RealFix * ClosePct, close, "mode5 应 = 实时腿剩余本金(real Fix) * closePct,与 posiLong(orig 100M) 无关");
Assert.AreNotEqual(OrigFix * ClosePct, close, "务必不是期初 99,000 * closePct(证明后端修复生效)");
}
}
}