refactor(swap)+test: 删 GetInterests/CalcSwapInterests 死参数 needPrice/grossPrice;补工厂→接缝映射钉子

死参数收口(另一半):
- SwapDealService.GetInterests 删 needPrice/grossPrice(体内零消费,2026-08 验证);
  InitSwapDealInterest.needPrice 同为死参数一并删
- SwapEodPositionService.CalcSwapInterests 签名+转发同步;两个 EOD 生产调用点
  (SaveAutoEodInterestPosition/SaveEodInterestPositionCopy) 重排实参;
  CalcEodPostCloseSettleInterests/GetIntradayUnwindInterests 委托同步
- 14 个测试文件 ~44 处直调点机械更新(8 处 override 签名 + 36 处调用实参)
- 注意:EOD 编排链(DealInterests→Save*家族)的 grossPrice(期初不含费价)有真实用途,保留未动

新增钉子:CalcEodPostCloseSettleInterests 工厂→接缝参数映射测试——
CalcSwapInterestsCapture 捕获 stub 断言 EodPostCloseSettle 的完整转发契约
(posi=平仓后剩余/closePosi=平掉额/恒1/settment:false/orginPv 等 11 项)。
该段位置转发含三个相邻同型 decimal,编译器不查错位,此测试兜底。

验证:定向 241 测试通过(含 T0/T1 Excel 验证期望值、EntrySemantics 精确值钉子——
任何 decimal 错位即红);全量 903=145失败/746通过/12跳过,与基线逐位一致。
This commit is contained in:
hjhan
2026-08-14 16:57:56 +08:00
parent 018d7e777f
commit 4a3fee9292
20 changed files with 172 additions and 89 deletions
@@ -147,7 +147,7 @@ namespace YLErp.Modules.SwapModule
var eodPostClose = CreateService().GetInterests(td, td.trade_extend, UnwindDate, UnwindDate,
eodPositions, positions, Remaining, Remaining, 0m, Closed, 1m,
(int)SwapEventTypeEnum., tdClose: false, needPrice: true, grossPrice: 1m, orginPv: PreClose,
(int)SwapEventTypeEnum., tdClose: false, orginPv: PreClose,
add: true, settment: false, newCalcLast: false, closeList: null);
Assert.AreEqual(1, intraday.Count);
@@ -182,7 +182,7 @@ namespace YLErp.Modules.SwapModule
var eodPostClose = CreateService().GetInterests(td, td.trade_extend, UnwindDate, UnwindDate,
eodPositions, positions, Remaining, Remaining, 0m, Closed, 1m,
(int)SwapEventTypeEnum., tdClose: false, needPrice: true, grossPrice: 1m, orginPv: PreClose,
(int)SwapEventTypeEnum., tdClose: false, orginPv: PreClose,
add: true, settment: false, newCalcLast: false, closeList: null);
Assert.AreEqual(1, intraday.Count);
@@ -210,7 +210,7 @@ namespace YLErp.Modules.SwapModule
// 全平:剩余=0,平掉=全部 1000
var result = CreateService().GetInterests(td, td.trade_extend, UnwindDate, UnwindDate,
eodPositions, positions, 0m, 0m, 0m, PreClose, 1m,
(int)SwapEventTypeEnum., tdClose: false, needPrice: true, grossPrice: 1m, orginPv: PreClose,
(int)SwapEventTypeEnum., tdClose: false, orginPv: PreClose,
add: true, settment: false, newCalcLast: false, closeList: null);
Assert.AreEqual(1, result.Count);
@@ -218,5 +218,90 @@ namespace YLErp.Modules.SwapModule
Assert.IsTrue(result[0].InterestAmount != 0m,
"mode9 全平时 posi=0,兜底必须以 closePosiNotionalValue(实际平掉额) 为结息本金,结息额非零(兜底钉子)");
}
#region CalcEodPostCloseSettleInterests
/// <summary>
/// 参数捕获 stub:拦下 CalcSwapInterests 的全部实参,不触库、不真算。
/// </summary>
private sealed class CalcSwapInterestsCapture : TestableSwapEodPositionService
{
public CalcSwapInterestsCapture() : base(nameof(GetInterestsEntrySemanticsTest)) { }
public List<swap_flow_event> CapturedCloseList = null;
public bool CapturedTdClose;
public int CapturedEventType;
public decimal CapturedPosiNotional;
public decimal CapturedClosePosiNotional;
public decimal CapturedClosePercent;
public decimal CapturedOrginPv;
public bool CapturedAdd;
public bool CapturedSettment;
public bool CapturedNewCalcLast;
public int CallCount;
protected override List<swap_flow_event> CalcSwapInterests(
trade td, trade_extend tradeExtend,
DateTime valueDate, DateTime unwindDate,
List<eod_swap_position> eodPositions, List<swap_position> positions,
decimal posiNotionalValue, decimal posiLongNotionalValue, decimal posiShortNotionalValue,
decimal closePosiNotionalValue, decimal closePrecent,
int eventType, bool tdClose,
decimal orginPv,
bool add = false, bool settment = true, bool newCalcLast = false,
List<swap_flow_event> closeList = null)
{
CallCount++;
CapturedTdClose = tdClose; CapturedEventType = eventType;
CapturedPosiNotional = posiNotionalValue; CapturedClosePosiNotional = closePosiNotionalValue;
CapturedClosePercent = closePrecent; CapturedOrginPv = orginPv;
CapturedAdd = add; CapturedSettment = settment; CapturedNewCalcLast = newCalcLast;
CapturedCloseList = closeList;
return new List<swap_flow_event>();
}
public List<swap_flow_event> ExposedEodPostCloseSettle(InterestCalcRequest req)
=> CalcEodPostCloseSettleInterests(req);
}
/// <summary>
/// 钉死 InterestCalcRequest.EodPostCloseSettle 工厂 → CalcEodPostCloseSettleInterests →
/// CalcSwapInterests 的位置参数转发契约。这段转发是位置传参最易错位的环节
/// posiNotionalValue/closePosiNotionalValue/orginPv 三个相邻同型 decimal,编译器不查错位),
/// 任何映射改动(含将来删 needPrice/grossPrice 死参数)都必须保持本断言绿。
/// </summary>
[TestMethod]
public void EOD平仓后收盘_工厂到接缝_参数映射钉死()
{
var td = CreateTrade();
var position = CreatePosition(InterestModeEnum., InterestTypeEnum.);
var preEod = CreatePreEod(interestSum: 0.05m, principal: PreClose);
var positions = new List<swap_position> { position };
var stub = new CalcSwapInterestsCapture();
var req = InterestCalcRequest.EodPostCloseSettle(
td, td.trade_extend, UnwindDate, UnwindDate,
new List<eod_swap_position> { preEod }, positions,
remainingNotionalAfterClose: Remaining, remainingLongNotional: Remaining, remainingShortNotional: 0m,
closedNotional: Closed,
eventType: (int)SwapEventTypeEnum., tdClose: false,
orginPv: PreClose, add: true, newCalcLast: false);
stub.ExposedEodPostCloseSettle(req);
Assert.AreEqual(1, stub.CallCount, "默认实现应恰好调用一次 CalcSwapInterests(虚接缝兼容既有测试替身)");
Assert.AreEqual(Remaining, stub.CapturedPosiNotional, "posiNotionalValue 位 = 平仓后剩余(700)——语义核心,错位即红");
Assert.AreEqual(Closed, stub.CapturedClosePosiNotional, "closePosiNotionalValue 位 = 实际平掉额(300)");
Assert.AreEqual(1m, stub.CapturedClosePercent, "closePrecent 恒 1(全额结息)");
Assert.AreEqual((int)SwapEventTypeEnum., stub.CapturedEventType);
Assert.IsFalse(stub.CapturedTdClose);
Assert.AreEqual(PreClose, stub.CapturedOrginPv, "orginPv 位 = 上一日终本金——与相邻 decimal 最易错位处");
Assert.IsTrue(stub.CapturedAdd);
Assert.IsFalse(stub.CapturedSettment, "settment=false:走盘中重放算法(EOD平仓后收盘复用重放)");
Assert.IsFalse(stub.CapturedNewCalcLast);
Assert.IsNull(stub.CapturedCloseList, "该场景不传 closeList");
}
#endregion
}
}