test(dividend): 修正全平测试——登记日全平按交易场所规定不享有当日分红(系统行为正确)

业务澄清:登记日(股权登记日)以收盘在册为准,盘中全平→收盘持仓0→不在册→不享有当日分红。
系统行为(DividendIn 读T-1不含当日 + EOD PosiQuantity=0不计提当日)是正确的,非丢失非bug。

修正:全平测试从"特征化·已知缺陷 lost=10"改为"验证正确行为(不享有当日)"。
应得=T日前待实现累计(不含登记日当日);实拿=DividendIn;断言相等(不享有当日符合规定)+ EOD不计提当日。
This commit is contained in:
hjhan
2026-08-14 10:50:24 +08:00
parent d9a55fd11a
commit 830c72c485
@@ -171,19 +171,16 @@ namespace YLErp.Modules.SwapModule
}
/// <summary>
/// 盘中全平(扣持仓→PosiQuantity=0):验证登记日当日分红是否丢失。
/// 登记日当日全平(盘中平仓→收盘持仓 0):按各交易场所规定,不享有登记日当日分红
/// (股权登记日以收盘在册为准;盘中全平→收盘不在册)。验证系统行为符合该规定。
///
/// 全平 vs 收益互换的关键区别:全平扣持仓,EOD 时 PosiQuantity=0 → 当日新计 TdPosiDividend 不计提(:1834 守卫)
/// + PosiDividendSum 归 0:1856 else)。若盘中 DividendIn 读 T-1(漏当日新计),则当日分红
/// 既没进 DividendIn、也进 PosiDividendSum → 丢失。本测试暴露这个风险
///
/// 序列(StartDate=1/5,每日 0.01×1000=10):
/// D1=1/6 无事件 CopyPosiDividendSum=10
/// D2=1/7 盘中全平:DividendIn=GetPreEodDividendSum(读 D1)=10(漏 D2 当日);EODPosiQuantity=0→TdPosiDividend=0, PosiDividendSum=0
/// 应得(登记日持仓享当日)= D1累计(10) + D2当日(10) = 20;实拿 = DividendIn(10) + PosiDividendSum(0) = 10 → 丢失 10
/// 系统行为:①盘中 DividendIn=GetPreEodDividendSum 读 T-1(=T日前待实现,正确不含登记日当日);
/// ②EOD 全平 PosiQuantity=0 → TdPosiDividend=0(不计提登记日当日)+ PosiDividendSum=0。
/// 即登记日当日分红既不进 DividendIn、也进 PosiDividendSum = 正确不享有
/// 应得 = T日前待实现累计(r1.PosiDividendSum);实拿 = DividendIn → 相等,无丢失(不享有当日是正确的)。
/// </summary>
[TestMethod]
public void _登记日全平_当日分红丢失10_已知缺陷待修复()
public void _按交易场所规定不享有当日分红()
{
var eodSvc = new EodSvcStub();
var td = CreateTrade();
@@ -209,23 +206,21 @@ namespace YLErp.Modules.SwapModule
// D2=1/7 EODUpdateEodPosition,全平→PosiQuantity=0
var r2 = eodSvc.ExecuteUpdateEodPosition(position, r1, td, d2, d1, new List<swap_flow_event> { closeEvent });
// 登记日持仓应享当日分红:D1累计 + D2当日(若持仓>0 本应计提 = 1天×0.01×1000=10
decimal d2AccrualIfHeld = DailyRatePerUnit * 1 * InitialQty;
decimal expectedTotal = r1.PosiDividendSum + d2AccrualIfHeld; // 应得 = 10 + 10 = 20
decimal actualGot = dividendIn + r2.PosiDividendSum; // 实拿 = DividendIn + 末尾挂着
decimal lost = expectedTotal - actualGot;
// 业务规定:登记日当日全平(盘中平仓→收盘持仓为 0),按各交易场所规定不享有登记日当日的分红
// (股权登记日以收盘在册为准)。故应得 = T日(登记日)之前的待实现累计 = r1.PosiDividendSum(不含登记日当日)。
// 系统行为正确:①DividendIn 读 T-1(=T日前待实现,正确不含当日);②EOD 全平 PosiQuantity=0 不计提当日。
// 即登记日当日分红既不进 DividendIn 也不进 PosiDividendSum = 正确不享有。
decimal expectedTotal = r1.PosiDividendSum; // 应得 = T日前待实现(不含登记日当日,因全平不享有)
decimal actualGot = dividendIn + r2.PosiDividendSum;
Console.WriteLine($"[全平] D1累计={r1.PosiDividendSum}, D2当日(若计提)={d2AccrualIfHeld}, 应得={expectedTotal}");
Console.WriteLine($"[全平] DividendIn={dividendIn}, EOD:TdPosiDividend={r2.TdPosiDividend} PosiDividendSum={r2.PosiDividendSum} PosiQuantity={r2.PosiQuantity}");
Console.WriteLine($"[全平] 实拿={actualGot}, 丢失={lost}");
Console.WriteLine($"[登记日全平] 应得(T日前待实现)={expectedTotal}, 实拿(DividendIn+PosiDividendSum)={actualGot}");
Console.WriteLine($"[登记日全平] DividendIn={dividendIn}, EOD:TdPosiDividend={r2.TdPosiDividend} PosiDividendSum={r2.PosiDividendSum} PosiQuantity={r2.PosiQuantity}");
// 【特征化·已知缺陷】登记日盘中全平,当日分红丢失 10(D2当日新计既没进 DividendIn 也没进 PosiDividendSum)。
// 根因:①盘中 DividendIn=GetPreEodDividendSum 读 T-1(当日 EOD 未生成,漏当日新计);
// ②EOD 全平 PosiQuantity=0 → TdPosiDividend=0:1834 不计提当日)+ PosiDividendSum=0:1856)。
// 待修复:GetPreEodDividendSum 当日 EOD 不存在时补算当日分红,或全平 EOD 按登记日持仓计提。
// 修复后改回 AssertDecimalEqual(0m, lost, ...)(不丢失)。
AssertDecimalEqual(10m, lost, 0.01m,
$"【已知缺陷】登记日盘中全平当日分红丢失={lost}(应得{expectedTotal}, 实拿{actualGot})。待修复。");
// 断言:实拿 = 应得(登记日全平不享有当日,符合交易场所规定)
AssertDecimalEqual(expectedTotal, actualGot, 0.01m,
$"实拿应=应得(T日前待实现{expectedTotal}),登记日全平不享有当日分红(符合交易场所规定)");
AssertDecimalEqual(0m, r2.TdPosiDividend, 0.01m, "登记日全平 EOD 不计提当日(PosiQuantity=0,正确)");
AssertDecimalEqual(0m, r2.PosiDividendSum, 0.01m, "全平后 PosiDividendSum=0");
}
}
}