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失败(基线一致)。
99 lines
6.9 KiB
C#
99 lines
6.9 KiB
C#
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(证明后端修复生效)");
|
||
}
|
||
}
|
||
}
|