test(swap): EQD-6968 自洽化契约钉死——事件利率确定性+快照再定盘载体+CI_007/008 新口径

- Determinism_NoTail_EventFloatRateIndependentOfPublishTime:尾日价缺(上午) vs
  有(下午)两世界,金额与事件 FloatRate 完全一致(=末段已消费利率0.01425)
- CloseOnly_平仓日为重置日_剩余持仓快照再定盘(+非重置日反例):经 DealInterests
  真路由断言快照 FloatRate=当日新定盘(RefixCalls 计数)——载体职责显式化验收
- CalcFirst 用例去掉 closeRate 免疫 hack:priorValueDate 根治后首重置日恒取价
- CI_007 更名+断言翻转:平仓日=重置日+不算尾 → 事件利率=末段已消费利率(旧);
  原断言(取新定盘)正是被替换的旧口径契约,根因注释留痕 GLMS-JIATT-20260805
- CI_008:本金结转断言保留,利率断言改为末段旧利率

验证:ConsumedInterestScenario 9/9、InterestEodTailSnapshot+Fr007 36/36、
全量 976 例 145 败与基线 diff=0
This commit is contained in:
hjhan
2026-08-19 11:07:10 +08:00
parent fafae5cac0
commit 6e2cae7c1f
3 changed files with 142 additions and 43 deletions
@@ -60,6 +60,16 @@ namespace YLErp.Modules.SwapModule
return CalcResult;
}
/// <summary>持仓延续腿重置日再定盘接缝:计数并返回受控新定盘(不连库)</summary>
public decimal RefixResult { get; set; }
public int RefixCalls { get; private set; }
protected override decimal ResolveOngoingResetFixing(swap_position position, DateTime valueDate)
{
RefixCalls++;
return RefixResult;
}
public List<swap_flow_event> ExecuteDealInterests(
List<swap_position> interestList, List<eod_swap_position> eodPositions,
DateTime settleDate, trade td, List<swap_flow_event> flowEvents,
@@ -223,5 +233,87 @@ namespace YLErp.Modules.SwapModule
Assert.AreEqual(5.295342465753m, p.SwapPositionValue);
Assert.AreEqual(1.0m, p.TdCurrency);
}
#region EQD-6968
private const decimal OldFloat = 0.01425m;
private const decimal NewFloat = 0.0143m;
/// <summary>4/27+147 天周期的重置日平仓</summary>
private static readonly DateTime ResetSettle = StartDate.AddDays(14);
/// <summary>4/27+10:非重置日平仓(10%7≠0</summary>
private static readonly DateTime NonResetSettle = StartDate.AddDays(10);
private static swap_position CreateFloatLegPosition() => new()
{
id = 1001, SwapTradeId = 1, InterestDirection = (int)SwapDirectionEnum.,
InterestMode = (int)InterestModeEnum., InterestRateDefault = Rate,
InterestPrincipalFix = Principal, PosiStartDate = StartDate,
PosiMatuirityDate = new DateTime(2027, 4, 27), IsInitial = true,
InterestType = (int)InterestTypeEnum., IsAnnualized = true,
interest_rest_days = 7, interest_rule = 0,
FloatRateUnderlyingCode = "FR007",
InterestSwapInterval = JsonConvert.SerializeObject(new List<IntervalModel>
{ new IntervalModel { Date = StartDate, Rate = Rate, Settlement = 1 } })
};
private static eod_swap_position CreatePreEodBefore(DateTime settle, decimal accumulated) => new()
{
id = 100, PositionId = 1001, ValueDate = settle.AddDays(-1),
InterestDirection = (int)SwapDirectionEnum., InterestMode = (int)InterestModeEnum.,
InterestIncomeSum = accumulated, InterestProfitSum = accumulated,
InterestRateDefault = Rate, TdInterestPrincipal = Principal,
InterestType = (int)InterestTypeEnum., IsAnnualized = true, interest_rest_days = 7
};
private static swap_flow_event CalcResultWithFloat(decimal floatRate)
{
var e = CreateCalcResult();
e.FloatRate = floatRate;
return e;
}
/// <summary>
/// 平仓日恰为重置日且剩余持仓>0:快照 FloatRate 必须显式再定盘为当日新定盘——
/// 它是后续非重置日(ByEod 沿用 preEod.FloatRate)与当日应计(intersetAcmount)的利率载体。
/// 排除日"纯跳过"后事件利率=末段已消费利率(OldFloat),载体职责与本步骤显式分离。
/// </summary>
[TestMethod]
public void CloseOnly_平仓日为重置日_剩余持仓快照再定盘()
{
var service = new TailStubService
{
CalcResult = new List<swap_flow_event> { CalcResultWithFloat(OldFloat) },
RefixResult = NewFloat,
};
service.ExecuteDealInterests(
new List<swap_position> { CreateFloatLegPosition() },
new List<eod_swap_position> { CreatePreEodBefore(ResetSettle, Accrued10d) },
ResetSettle, CreateTrade(), new List<swap_flow_event> { CreateCloseEvent() },
Remaining, ClosedNotional, 100m, Remaining);
Assert.AreEqual(1, service.RefixCalls, "不算尾+平仓日=重置日+剩余>0:应恰好显式再定盘一次");
Assert.AreEqual(NewFloat, service.PersistedPositions.Single().FloatRate,
"剩余持仓快照利率=当日新定盘(非事件末段旧利率)");
}
[TestMethod]
public void CloseOnly_平仓日非重置日_不再定盘_快照沿用事件利率()
{
var service = new TailStubService
{
CalcResult = new List<swap_flow_event> { CalcResultWithFloat(OldFloat) },
RefixResult = NewFloat,
};
service.ExecuteDealInterests(
new List<swap_position> { CreateFloatLegPosition() },
new List<eod_swap_position> { CreatePreEodBefore(NonResetSettle, Accrued10d) },
NonResetSettle, CreateTrade(), new List<swap_flow_event> { CreateCloseEvent() },
Remaining, ClosedNotional, 100m, Remaining);
Assert.AreEqual(0, service.RefixCalls, "非重置日平仓:无需再定盘");
Assert.AreEqual(OldFloat, service.PersistedPositions.Single().FloatRate,
"快照沿用事件末段已消费利率(周期未切换)");
}
#endregion
}
}