Commit Graph
236 Commits
Author SHA1 Message Date
hjhan ed86402aba test(swap): EQD-6977 罚息契约钉死——金标准恒等式(全期=已结+罚息,含不算尾/中段平仓双承接量) + 边界四象限 + 部分平仓 + 冻结取率规则 2026-08-20 11:51:25 +08:00
hjhan d1985231ee feat(swap): EQD-6977 复利纯函数支持 carryInInterest——窗口前已结利息随首个重置日资本化并持续留在基数(与全窗口重放轨迹严格一致);默认0零行为变化,含数学不变量测试 2026-08-20 11:43:27 +08:00
hjhan 952295a78a fix(fr007): 界面手工录入定盘精度 4→6 位——1.4150% 不再被截成 1.4200%
UAT 实测发现(EQD-6968 测试期间):录入 1.4150 落库变 0.0142。两层拆解:
- ÷100 为设计(界面按百分数录入,SwapflowList.js toNumber(value/100,6) 已传 6 位)
- Math.Round(price,4) 为精度截断:FR007 官方发布百分数下 4 位=小数 6 位,
  4 位舍入只保百分数下 2 位,丢 0.5bp(1 亿本金 7 天约 96 元)
修复:抽 RoundFr007Price(6位) 替换三处 Round(,4),与前端 6 位对齐;
DB 列 double(18,10) 已验证容纳;bond-sync 自动同步链(BigDecimal 全精度透传)
不经此路径零影响。已截断的历史行(如 2026-08-19 的 0.0142)不自愈,需重录。
附带修复 GLMS20260819Fr007TradeDiscoveryTest 编译错误:@"" 逐字字符串内
应用 "" 转义、UnderlyingCode→FutureContractId(实际列名)、EF6 Database.Connection
→ EF Core GetDbConnection()。

验证:SwapFlowFr007EntryPrecisionTest 2/2;全量 985 例 145 败与基线 diff=0
2026-08-19 14:29:16 +08:00
hjhan 43fb83ed00 refactor(swap): relaxedFixingFromDate 改名 exclusionStart——名实归位
该参数诞生于"有价则取/缺价回退"口径(55363e9f 由 exclusionEndDate 改名而来),
自洽化(fafae5ca)后排除日一概不取价,"放宽取价"语义已不存在——名字回到
排除区间本义,并与 FIX 日志既有用词"排除起点"对齐:
- BuildSegmentRates/CalcDailyCompoundInterest/CalcDailySimpleInterest 三签名
  + 全平重放传参点 + 方法摘要补 exclusionStart 参数语义文档
零行为变更。

验证:内存套件 113/113;全量 981 例 145 败与基线 diff=0
2026-08-19 11:41:18 +08:00
hjhan f8049f81f8 refactor(swap): InitInterestDate 删除死子句 td.StartDate>interestStart + 触发场景注释归因修正
分析定谳:interestStart 经三条赋值路径(=开始日/不算头+1天/preSettleDate 且仅当
≥interestStart 才覆盖)恒 ≥ td.StartDate,第二 OR 子句恒 false——死代码删除。
注释归因同时修正两侧旧错误:
- 原注释把"不算头首日"笼统挂在返回 true 上——实际由第一子句
  interestStart>interestEnd 兜住(StartDate+1>StartDate);
- 候选修正案"观察日当日已结息(preSettleDate 覆盖窗口末)"亦不精确——日期相等
  时函数返回 false,当日已结息的利息归零在 GetInterests closeList 净额层;
  只有相等叠加到期日不算尾回拨(endDate-1)才严格大于而触发。
新增三条 InitInterestDate 直测钉边界:不算头首日空窗/同日已结息日期相等非空/
同日已结息+到期日回拨空窗。

验证:GLMS20260817Fr007UnwindMorningTest 36/36;全量 981 例 145 败与基线 diff=0
2026-08-19 11:36:53 +08:00
hjhan 7b70030216 test(swap): 补快照利率携带契约——单利+不算头+有preEod 盲区定谳
该组合此前零覆盖。定谳结论:带 preEod 时 fetchAfter=preEod.ValueDate +
seed=preEod.FloatRate 是设计分工(≤上一日终的重置日沿用快照携带利率,>的重新
取价),与无日终场景的根本区别是种子有正确来源——开始日EOD因窗口为空走正常
取价,快照 FloatRate=开始日定盘(链条起点),并非旧 bug 复发。
- CarryForward_Simple_NoHead_PreEodAtStartCarriesFirstPeriodRate:
  preEod{ValueDate=开始日, FloatRate=开始日定盘} → PricedDates=0(不重复取价)、
  事件利率=快照利率、金额=上日待实现+3天×(spread+快照利率) 精确断言
- Eod_NoHead_StartDay_SnapshotRateCarriesFirstFixing:开始日EOD("00",窗口
  为空)→快照利率=首重置日定盘,钉死携带链条起点
Run 扩展:Outcome 暴露 Svc(取价审计) + BuildPreEod 支持指定利率。

验证:GLMS20260817Fr007UnwindMorningTest 33/33;全量 978 例 145 败与基线 diff=0
2026-08-19 11:31:19 +08:00
hjhan decbd83d0b refactor(swap): 误导布尔 swap 改名 interestWindowEmpty——按"计息窗口为空"本质命名
InitInterestDate 返回 true 的语义是"计息窗口为空→不计利息"(利率与金额归零),
判定只看日期窗口(interestStart>interestEnd)、与事件类型无关;旧名 swap 把
典型原因(互换当日已结息)当成了效果的名字,读者会误读为事件类型判断。
- SwapDealService: GetInterests 局部变量 + GetFloatRate/CalcUnwindInterest/
  InitSwapDealInterest/CalcMarginInterest 四个签名参数及分支/日志/注释同步
- SwapTradeBaseService.InitInterestDate 返回值语义补入 XML 文档
- MarginInterestGoldenReplay/Shadow 测试命名参数同步
零行为变更。

验证:相关套件 51/51;全量 978 例 145 败与基线 diff=0
2026-08-19 11:31:03 +08:00
hjhan 6e2cae7c1f 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
2026-08-19 11:07:10 +08:00
hjhan e779347488 test(swap): EQD-6968 补到期日当天全平补计分支——replayEndDate+1 的尾日取价豁免
relaxedFixingFromDate 的存在理由回归钉死:不算尾时 InitInterestDate 回拨
endDate 一天,最终全平的历史差分重放需补回真实平仓日(replayEndDate=endDate+1),
该边界日定盘经 relaxedFixingFromDate 走"有价则取/缺价跳过"——
否则到期日上午全平被尾日价误拦(EQD-6968 在到期日当天的镜像场景)。
- FinalClose_OnMaturityDay_NoTail_...Succeeds:不算尾+到期日缺价 → 放行
- Guard_FinalClose_OnMaturityDay_Tail_...StillThrows:算尾+到期日缺价 → 仍拦
注:全平重放分支 relaxedFixingFromDate 恒非 null(Red_Compound_FullClose_WithPreEod
已覆盖),本提交补的是 endDate<valueDate 时的 +1 补计子分支。

验证:GLMS20260817Fr007UnwindMorningTest 30/30 内存全绿
2026-08-19 10:53:34 +08:00
hjhan bf2adf0fae test(swap): EQD-6968 补 EOD 收盘归档取价链——此前 settment=true 零覆盖
EOD 不取尾日价靠两道闸:InitInterestDate 到期日回拨(endDate=D-1) +
CalcEodInterest calcToday=false(到期日且不算尾)跳过 ByEod 重算——ByEod 的
isResetDay→ResolveFloatRate 不看 calcLast,任一环回归即到期日收盘重新索要尾日价。
- Eod_NoTail_MaturityDayFr007Missing_Succeeds:不算尾+到期日(重置日)缺价 → 放行
- Eod_Tail_MaturityDayFr007Missing_StillThrows:算尾+到期日缺价 → 仍拦
- Eod_MidTradeResetDayFr007Missing_StillThrows:非到期重置日缺价 → 仍拦
  (盘中重置日新利率被持续持仓消费,ByEod 取价链是真实依赖,不算尾也不豁免)

验证:GLMS20260817Fr007UnwindMorningTest 28/28 内存全绿
2026-08-19 10:53:34 +08:00
hjhan 4d68133dcb test(swap): EQD-6968 补不算头不算尾(00)组合——CalcFirst=false 零覆盖盲区
- Red_Compound_NoHeadNoTail:00+缺平仓日价 → 放行(calcLast 同 false,与 10 同口径)
- CalcFirst_Simple_NoHeadDropsExactlyStartDayInterest:单利下 00 与 10 利息差
  恰好=开始日一天利息(精确断言,钉 CalcFirst 回归)。closeRate 取 7/6 定盘同值,
  免疫既有取价细节——排查中发现:无日终快照+00 时 fetchAfter=interestStart-1 会
  跳过开始日取价、首段利率用种子(GetFloatRate 回写的尾日定盘),与 10 的
  fetchAfter=开始日-1 不同(FIX 日志可复现,niche 场景:首个日终前平仓)。
Run 运行器扩参:settment / exerciseDate(供后续 EOD 与到期日用例)。

验证:GLMS20260817Fr007UnwindMorningTest 25/25 内存全绿
2026-08-19 10:53:34 +08:00
tengyufan 87a8b90eaf fix(ClientBlack): 支持删除未生效黑名单记录
- 未提交、新增已拒绝的黑名单记录可直接删除,不进入删除审批
- 已加入、删除已拒绝仍按原逻辑发起删除审批
- 新增审批中、删除审批中仍禁止删除
- 补充未生效状态可直接删除的状态机单测,45/45 通过
2026-08-19 10:07:39 +08:00
hjhan 54c4bf21c6 test(swap): PrevBizDay 用例对进程内 QDP 日历状态脱敏(修全量运行偶发红)
全量运行实测:其他用例会把 QDP "chn" 日历替换为"全营业日"退化态
(GetNonHolidayDefore(7/19)=7/19 不再回拨周五),三个 PrevBizDay 放行用例
单跑绿、全量红(取价日 7/19 不在市场)。生产库恒有真实日历无此问题。
- Fr007MarketPrevBizDay 同时供回拨日(7/3、7/10、7/17)与退化日(7/5、7/12、7/19)
  两套价——被测对象是取价放宽语义,不是日历本身;
- FixingDayMissing 守卫同时扣 7/17+7/19,两种日历态下都缺价仍拦。
验证:套件 23/23;全量 960 例 145 败与改动前基线失败名单 diff=0,11 个新用例全绿
2026-08-19 09:44:59 +08:00
hjhan 7cfa85a7cc test(swap): EQD-6968 补前一营业日基准组合+算尾非重置日放行(方案一场景2/3落格)
- PrevBizDay_TailCalced×2:算尾(11)+rule=-1+当日(7/20)未发布 → 放行(取价日回拨至7/17已发布)
- PrevBizDay_NoTail:不算尾(10)+rule=-1+当日未发布 → 放行
- PrevBizDay_FixingDayMissing:rule=-1 但取价日(7/17)本身缺价 → 仍拦(真实依赖守卫)
- TailCalced_NonResetDay×2:算尾(11)+当日非重置日(7/22)+当日缺价 → 放行(非重置日不取当日价)
新增 Fr007MarketPrevBizDay 市场:重置日7/6、7/13、7/20 回拨取价日7/3、7/10、7/17

验证:GLMS20260817Fr007UnwindMorningTest 23/23 内存全绿
2026-08-19 09:28:27 +08:00
hjhan f069c38d48 test(swap): EQD-6968 补算尾依赖守卫——重置日缺价仍拦+newCalcLast覆盖算尾仍拦
防'不算尾放宽'误扩到依赖场景(方案一场景4):
- Guard_TailCalced×2:算尾(11)+当日重置日+当日缺价 → 必须仍抛(复利/单利)
- Baseline_TailCalced:算尾+当日有价 → 成功(对照)
- Guard_NewCalcLast:交易10不算尾但本次平仓指定算尾 → effectiveCalcLast=true 仍拦
- Baseline_NewCalcLast:指定算尾+有价 → 成功
Run 运行器扩参:calcMode/interestRule/newCalcLast/market(本提交仅用 calcMode/newCalcLast)

验证:GLMS20260817Fr007UnwindMorningTest 17/17 内存全绿
2026-08-19 09:26:30 +08:00
hjhan 90d1d85b4a test(infra): DbDiagnose 用例库不可达自守卫——真探测一次+进程级缓存结论
原守卫 try 挂在 GetYLDbContext() 上永远打不中(EF context 构造不连库,
Connect Timeout 发生在首个查询),无库裸跑 14 个用例逐个假红且各耗 15s。
改为 DbDiagnoseGuard.RequireTestDb():CanConnect 真探测一次并缓存,
不可达全批 Inconclusive——裸跑不再有假失败噪音,探测成本 14×15s→1×15s。
连上库的机器行为不变(照常真跑)。
2026-08-18 09:58:16 +08:00
hjhan 7a5089555d fix(fr007-cache): 直查命中失效快照+去冗余排序+重载失败保旧快照防重试风暴
- EodPriceQueryService.TryGetPrice: FR007 快照 miss 直查库命中后 Invalidate,
  下次访问整表重载回 O(1),否则该键 TTL 窗口内每次读取都退化为点查
- 移除点查多余的 OrderByDescending(主键等值查询取首行即可)
- Fr007FixingCache.EnsureFresh: 重载失败(DB抖动)保留旧快照不抛并重置TTL时钟
  ——历史定盘不可变旧值依旧正确,同时防重试风暴;版本失效路径不受TTL掩盖
- 补两测试: 重载失败保快照窗口内不重试 / 首次装载失败miss不抛窗口内不重试
2026-08-18 09:33:28 +08:00
hjhan e219bdae18 feat(swap): FR007定盘整表快照缓存——预载全历史O(1)命中,miss不缓存,写侧失效+1min TTL兜底
数据形态:每工作日一行、发布后不变、全历史仅数千行 → 整表快照优于逐键缓存。
① Fr007FixingCache:首次访问批量SELECT全历史进字典,此后读取O(1)零查询;
   快照以引用替换发布(Volatile),读侧无撕裂,重载加锁去抖;
② 负缓存防线:miss(当日~11:15未发布/补录历史)永不进快照,TryGetPrice 回退直查库——
   否则发布后TTL窗口内仍查不到,上午平仓会一直用回退利率;
③ 写侧版本失效:SwapFlowService 新增/修改/删除 FR007 后 Invalidate,下次访问立即
   整表重载不等TTL;其余通用写入口(导入/复制/合成/直改库)靠TTL兜底,可见性≤1分钟;
④ TryGetPrice 仅 FR007 走缓存路由(全仓生产调用方仅 SwapDealService.TryGetFloatRate
   与 Fr007IndexFixer 两处,均定盘链),其他标的照旧直查,零影响;
⑤ YLErpDAL 增加 InternalsVisibleTo("UnitTestProject"),时钟/装载器测试接缝保持 internal。

验证:Fr007FixingCacheTest 5/5(预载命中/miss不缓存/写侧立即生效/TTL过期重载/稳态零重载);
GLMS20260817Fr007UnwindMorningTest 等内存套件 55/55 全绿。
2026-08-18 08:24:21 +08:00
hjhan a97476c4da EQD-6968: 不算尾缺价改沿用已有利率(删期初回退) + 取价决策FIX常驻日志
① GetFloatRate 不算尾缺价不再回退取期初定盘,改沿用已有利率
   (preEod.FloatRate/position.FloatRate)——其返回值仅作 preEod.id==0 的种子
   且会被分段首段强制取价覆盖,期初取价是无消费者的多余DB读且语义误导;
② BuildSegmentRates 等价重排为 if/else-if 结构,注释改"跳过取价/沿用"准确措辞
   (行为不变:排除日有价仍取新利率,缺价沿用上一重置日利率);
③ 防历史bug"取错重置日利率"复发:GetFloatRate/ResolveFloatRate/BuildSegmentRates
   三处取价决策经 SwapCalcTrace.Critical 常驻 Info 落盘——重置日判定、取价日
   (interest_rule回拨)、命中/缺失、不算尾缺价沿用来源、排除日跳过全程可审计;
④ 测试真内存化:Stub 补 GetConsumedInterest=>0 override(复利路径原偷连96库,
   与类注释"内存,不连库"矛盾);一致性断言措辞对齐新语义;
⑤ 修复 SwapDealService.cs 文件头双 BOM 损坏。

验证:GLMS20260817Fr007UnwindMorningTest 12/12 内存全绿;
邻近套件 67过/15败与改动前基线完全一致(15败均为沙箱无96库 Connect Timeout)。
2026-08-18 08:10:30 +08:00
hjhan fb5bbfaa99 test(swap-eod): 场景4 跨日毒链携带守卫改为生产自身 DailyAccrual 不变量(真捕获 mode9 误反推)
- 原引擎级差分版(734683ea)为假绿:固定 obsDate=2026-05-12 在 td.StartDate 重置日判定下恒为重置日,
  basis=notional+priorAccrued 不取 priorNotional(被误反推的 TdInterestPrincipal),故未捕获 bug。
- 现改为直接复用生产单一真相源 SwapEodPositionService:1388 的 TdInterestIncome=DailyAccrual(TdInterestPrincipal,…)
  纯函数:正确本金与“生产实际”本金各算一次 D+1 应计,二者唯一差异即 TdInterestPrincipal;误反推(膨胀~2.3x)
  使 D+1 应计同步膨胀,差远超容差→红。该不变量与重置日/平仓比例无关,稳健。
- 守卫置于单日 TdInterestPrincipal 不变量之前,使其成为首要捕获点(实跑验证:注入 mode9 误反推后
  首失败点即“场景4[跨日] 毒链携带 D+1 应计偏差 -5501.96”,correct=-4126.47 / poisoned=-9628.43)。
- 移除不再使用的 CalcNextDayInterest 引擎级辅助方法。
2026-08-17 18:53:30 +08:00
hjhan 734683eaca test(swap-eod): 场景4 跨日毒链携带断言——引擎级差分(正确本金 vs 生产实际本金各喂一次 GetInterests)
- 前一版在 rollforward eod 层断言 TdInterestPrincipal 被证伪(该 harness 中 DealInterests 收到 eodPosition=null,rollforward eod 恒 0;复利写带符号重放值)——正常日该字段非计息基数载体,断言选错层
- 现改为引擎级差分:partialEod.Clone() 修正 TdInterestPrincipal=expectedTdPrincipal 作对照组,与生产实际 partialEod 各喂 GetInterests(05-12,settment:true),比对次日本金 InterestPrincipal——经 SwapDealService:1045 priorNotional 通道验证携带,容差 max(0.01, 5%)
- 变异验证(已还原):临时令 :1422 守卫放行 mode9 复现 GLMS-20260421-0004 → 三守卫齐红:本文件不变量断言(Expected 212135529.97 / Actual 494982903.27 精确命中历史签名值)、DI_GLMS_20260421_0004、DI_EXCEL_SCENARIO4;还原后 24/24 + 全量 576/566/10/0 复绿
- 生产代码零变更
2026-08-17 18:36:54 +08:00
hjhan c42335363b test(swap-eod): SwapInterestScenario3And4FloatingTest 的 TdInterestPrincipal 改独立不变量(封死假绿)
场景4 原 TdInterestPrincipal 断言 = 生产公式镜像(reverseMode2 ? base*(1-cp)/cp : base),
与生产 if(calcLast && mode2) TdInterestPrincipal *= (1-cp)/cp 逐字同构,永远跟自己一致,
故 mode9 被错误反推(GLMS-20260421-0004 膨胀 ~2.3x)时测试仍绿——假绿。

改为:mode9 用独立 else if 锁死 expected=LastBaseInterestPrincipal(反推前 base),
彻底脱离 reverseMode2 布尔拼写,封死重基线陷阱。已实跑证明:注入 mode9 反推 bug 后
唯一变红 T+1浮动减点算头算尾(实测 TdInterestPrincipal=494982903.27),回滚生产恢复 24/24。
零生产改动。
2026-08-17 18:18:02 +08:00
hjhan 09fa96a93f refactor(swap-eod): L1 类内去重——CopyInterestLegFields/FinalizeInterestEodRoll 收口四 Save* 重复段(行为保真)
- 腿字段拷贝 3 处×14 行 → 入参化 helper(FloatRate 来源随场景传入,勿统一)
- 滚存收尾 4 处 → FinalizeInterestEodRoll(方向源参数化:三处 position、Copy 一处 eodPayPosition,勿统一;Persist 与日志留调用点保持原序)
- 差异保真:WithClose 不设 InterestFeePending;勿缩放注释保留升级
- 安全网:InterestEodTailSnapshotTest 三格尾部快照(AutoSettle/AutoSettleWithClose/CloseOnly,钉值特征化)+ golden 两格 + DI_EXCEL 家族
- 验证:快照 3/3 逐字段一致;全量 SwapModule 576/566/10/0 与去重前基线完全相同
- 注:本提交为工作区被还原后按原编辑重放,验证结果与首次执行一致
2026-08-17 18:15:09 +08:00
hjhan 65a367e139 test(swap-eod): 补 A 三个路由分支断言 DI_BRANCH_003/004/005(经真实 DealInterests 路由器,零生产改动) 2026-08-17 17:43:25 +08:00
hjhan 9ae2e66b75 refactor(swap-eod): L0 纯函数出仓——InterestEodScenario/ResolveInterestScenario/FindObservationInterval 移至独立 InterestEodScenarioDispatch(零行为变更)
- 上帝类减重 42 行;枚举与两个纯函数零实例状态、零 DB 依赖,符合 Accrual/ 既有纯函数迁移模式
- 测试不再继承 TestableSwapEodPositionService(原仅为够到 protected),plain 测试类
- SwapEodPositionService 两处调用点改为 InterestEodScenarioDispatch.FindObservationInterval 限定名
- 编译 0 错误;表驱动 8/8 通过
2026-08-17 17:36:41 +08:00
hjhan ec9c493420 refactor(swap-eod): 利息腿分派可读性收口——观察日命名统一+命名参数+查找助手+分派规格纯函数(零行为变更)
- insterval/autoInterval → observationInterval,SwapIntervalList→FindObservationInterval→形参 interval 全链同名
- :512/:525 调用点命名参数 autoSwap: true/false,签名未动
- 抽取 FindObservationInterval 消除两处重复观察日谓词(表达式逐字保留,null 语义不变)
- 新增 InterestEodScenario 枚举 + ResolveInterestScenario 影子纯函数 + 8 组合表驱动 InterestEodScenarioDispatchTest(分派结构未接线,生产执行路径不变)
- 编译 0 错误;表驱动 8/8 通过
2026-08-17 16:25:08 +08:00
hjhan b94cce68ee chore(cleanup): 删除裁决文档与 saga 注释——保留技术事实,剥离过程叙事(零逻辑改动)
删除 项目文档/双入口口径裁决-复利mode2部分平仓-20260816.md:
四轮状态改写的过程文档,终态有价值的技术事实已转移到代码注释与测试锚点。

保留的技术事实(新家):
- SwapEodPositionService:1325:恒1重算中间值不进结算现金流;系统端到端结果由
  DI_EXCEL_SCENARIO4 家族对账确认书公式保障(最终全平=剩余额×∏利率,2026-08-18
  手算复核);改动口径前必读该测试家族——破坏 ∏ 恒等式的调整会被其拦截
- ContractReferenceOracleTest:13:Excel 金标准期望值亦符合确认书公式(复核记录)
- EntrySemanticsTest 手算锚点 0.036165 保留(公式自含,去文档引用)

清理的 saga 痕迹:契约修复/裁决文档/暂缓/证伪 等过程性话语全部移除,
测试方法名与注释改为中性事实描述;[EOD平仓后收盘结息] 日志文案
'恒1全额结息(历史行为,契约修复暂缓)'→'全额结息(恒1惯例)'。

验证:编译 0 错误;EntrySemantics+Oracle 16/16 绿;全量 928=145/771/12
与基线逐位一致——纯注释/文档/日志文案改动,零逻辑变更。
2026-08-17 13:30:10 +08:00
hjhan 305f8a0322 EQD-6968: 补强 FR007 不算尾平仓误拦截回归套件(单利Red/数值一致性/非整倍数边界,重构为 Run 运行器) 2026-08-17 11:42:41 +08:00
hjhan bc401bd687 EQD-6968: 不算尾计息不再因平仓日FR007未发布误拦平仓(缺价回退上一重置日利率,有价仍取新利率) 2026-08-17 11:22:37 +08:00
hjhan 97aff02e1d test(infra): P0——ModuleInitializer 初始化段容错,测试库不可达不再连坐全部测试
问题(2026-08-16 栈实证):M1→AppManager.Initialize→InitializePsConfig→
InnerAppConfigService.ConfigDic 读 AppConfig 表→ServerVersion.AutoDetect 连库,
库不可达时 ModuleInitializer 抛异常→903 个测试全部'Unable to create instance'全红,
DB 断网期间阻塞全部开发(本周实证两天)。

修复:初始化段(AppManager.Initialize + DataCacheManager.UpdateOnce)包 try-catch,
失败降级为醒目警告(Console+NLog),纯内存测试照常可跑;依赖配置/缓存/库的测试
以各自连接错误失败(与降级前一致,不再伪装成类创建失败)。
完全跳过初始化仍用 YLErp_UNIT_TEST_SKIP_INITIALIZATION=1。
仅测试工程 Program.cs,零生产代码改动。

验证(库不可达、不开逃生门):修复前 0/17 类创建全失败;修复后 17/17 通过;
扩大集 48/48 通过(147ms)。常量类不受影响已证(ConsTrade.InterestMarginModels
为编译期常量,MarginModesTest 离线绿)。
2026-08-17 10:00:09 +08:00
hjhan 9efaa1b5cc chore(swap)+test: 删分红EOD历史死代码 + 补计算过程常驻日志(零行为语义变更);契约修复转为暂缓
死代码删除(SwapEodPositionService.UpdateEodPosition,论证+实证链完整):
- todayConsumedDividend/originNotional/totalPayment/totalInterest 自 0910969e(7/2 改递推式)
  起计算结果从未被消费,仅残留全历史 CalcBondPayment 只读查询+日志副作用,且构成
  脏数据(OriginalStockEqvNotional=null/PosiNetPrice=0)下 EOD 崩溃点(除零,实测复现)
- 配套边界测试 DividendEodNoDoubleCountTest.脏数据边界_*:删除前红(崩溃)→删除后绿
  (不抛异常+四输出字段与正常数据逐字段一致);stash 基线对比确认离线套件无其他差异
- 止血预案:git revert 本提交即回到已验证态

计算过程常驻日志(快速定位,纯增):
- [EOD平仓后收盘结息]:口径/autoSwap/平仓前/剩余/平掉额——排障第一入口
- [分红-EOD计提Copy/Update]:窗口/qty/税率/当日新计/账面前值→后值——与
  [分红-登记日口径]窗口命中日志构成分红计算全链
- [利息-全平专属分支]:全平分支触发标记,兼作契约修复落地后的验证哨兵

契约修复(EOD传真实比例)转为暂缓:影响快照种子,黄金回放验收门因96库网络未恢复
无法执行,按风险优先级撤回行为变更;方案+回归网(同形状等价oracle)+落地哨兵已
全部就绪并留档于裁决文档,DB恢复后可直接落地。
2026-08-16 15:47:09 +08:00
hjhan c9f44d4fde revert(test-infra): 测试库连接超时回退 2s→30s(恢复 8/14 前配置)
04b52061 将 Connection Timeout 30→2 属离线排障场景的急速失败配置,
对正常连库环境过紧(慢网/高延迟下秒死,且 ModuleInitializer 预热失败
拖垮整个测试程序集)。经实证:恢复 30s 后本机仍无法连库(秒败,
Unable to connect to any of the specified MySQL hosts),确认超时配置
非本次全红根因——根因为本机到 192.168.2.96 网络不可达(TCP/ICMP/ARP
三重验证)。2s 改动本身仍应回退以免放大慢网故障。.runsettings 为
opt-in(仅 --settings 显式启用)保留不动。
2026-08-16 13:25:50 +08:00
hjhanandClaude Opus 4.7 07764e7e3f test(swap): 契约参考实现落地,引擎主力族盘中重放首次对齐确认书公式 oracle
ContractReferenceCalc 独立实现 §8a 确认书公式(∏/重置期切分/末段收口/
利率确定日=重置日上一营业日),禁止引用生产计息类防同源;4 例手算锚点
自验证(真实规模 5000万/2.05%/90天=75999.04 与玩具锚点)。

引擎对照首批 3 例全绿:mode9/mode2 × 复利 × "10" × T+0 部分平仓30%,
盘中重放与契约 oracle 容差 0.01 元内逐分吻合——盘中口径=确认书公式
自此有机器强制,后续补格直接复用 oracle 供给(TEST-MATRIX §7.5 已登记)。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-08-16 12:19:28 +08:00
hjhan c94a3e8c97 test(swap): 铺 §7-1 守恒不变量护栏,守 EOD平仓后收盘×部分平仓 裸格(§6 空洞1)
新增 4 个免库纯内存测试(GetInterestsEntrySemanticsTest):①部分平仓 剩余持仓前递且 期初=剩余+平掉;②全平 剩余前递=0;③两次部分平仓 逐日守恒 期初-剩余前递=平掉;④ClosePercentMath 多次平仓累计比例=1-∏(1-各次剩余口径)纯数学。

守恒断言落在 preEod.PosiNotionalValue(剩余前递)而非 InterestPrincipal——后者在 InitSwapDealInterest:1164 后被利息算法重赋值(1197 复利特判/1266 单利路径),语义随入口/模式变,非稳健观测点;剩余前递由 CalcUnwindInterest:1084 在 preEod.id==0 时写入,与利息算法无关,最稳健。

用途:作为 GetInterests 早路由(Tier A)合入的前置护栏——funding-leg(mode2)不触发早路由 continue,故本类任何回归都直接暴露早路由对 EOD平仓后收盘路径的破坏。验证:YLErp_UNIT_TEST_SKIP_INITIALIZATION=1 dotnet test --filter Name~守恒 全过;整类仅 2 个旧复利测试因无 96 库失败,与本次无关。
2026-08-16 11:17:37 +08:00
hjhan 44e24b3172 refactor(margin): MarginModes 反转依赖 ConsTrade.InterestMarginModels 并删除 premiumModes 硬编码副本
- MarginModes.All/ForLinq/FixedAmountAndMargin 改为由框架常量 ConsTrade.InterestMarginModels 派生,消除 DAL 层独立重写集合(单一真源在 Core 层,本类退化为形态包装,避免新增保证金形态时口径分裂)
- SwapEodPositionService.premiumModes 局部 new List 字面量替换为 MarginModes.ForLinq(DAL 内部冗余副本消除)
- 重写 MarginModes.cs 过时的“复制 6 次/待收敛”注释,改为记录依赖方向与收敛历史;ForLinq 注释改为“派生自框架常量,仅做形态适配”
- 更新 MarginModesTest 类注释与一致性测试注释以反映派生关系
- 验证:YLErpDAL/UnitTestProject 编译 0 错误;MarginModesTest 5/5 通过
2026-08-16 10:47:45 +08:00
hjhan 04b5206134 test(infra): 缩短测试库连接超时 30s→2s 并加入 MSTest 并行 .runsettings
UnitTestProject/appsettings.json: 四个连接串 Connection Timeout=30→2,无库环境连不上 2 秒即失败,避免逐个 DB 测试 30s 空转;有库时 LAN 连接<1s 不受影响。新增 .runsettings: Workers=0(自动取 CPU 核数)+Scope=TestClass 类级并行,opt-in(仅 --settings 生效);共享静态单例(LogFactory/DataCacheManager/YLServiceLocator)启用前先跑串行基线确认无交叉污染。
2026-08-16 10:30:39 +08:00
hjhan ef37c8e71d refactor(swap): 合并多空名义本金参数为单一 posiTotalNotional 并加固测试
- DealInterests 的 posiLongNotionalValue + posiShortNotionalValue 合并为 posiTotalNotional(调用点以 posiLongNotional+posiShortNotional 求和传入),净减一个参数

- SwapDealService / SwapEodPositionService / InterestCalcRequest 同步收敛多空死管道参数

- 19 个测试调用点适配新签名

- SwapEodPositionServiceIntegrationTest 参数计数断言由裸数字改为参数名集合断言(CollectionAssert.AreEquivalent,对增删/重排/改名敏感)
2026-08-14 17:30:46 +08:00
hjhan 4a3fee9292 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跳过,与基线逐位一致。
2026-08-14 16:57:56 +08:00
hjhan 018d7e777f refactor(swap): InterestCalcRequest 参数对象——GetInterests 双显式入口收敛为单参数
新增 InterestCalcRequest(SwapModule 根):
- 承载 GetInterests 全部有效入参(needPrice/grossPrice 死参数不承载),私有构造;
- 仅两个场景工厂可构造:IntradayUnwind(平仓前剩余/实际平掉额/B语义比例)、
  EodPostCloseSettle(平仓后剩余/实际平掉额/恒1全额结息)——工厂形参名即场景语义,
  物理上防止两套名义本金语义混传(6fdc7d80 错账的温床);
- GetIntradayUnwindInterests / CalcEodPostCloseSettleInterests 签名收敛为单参数 req,
  生产调用点(GetUnwindInterests / SaveAutoEodWithCloseInterestPosition)改工厂构造;
- 原 20 参 GetInterests / 19 参 CalcSwapInterests 保留为底层实现与测试兼容层(十余处测试直调,不动)。

验证:定向 140 测试通过;全量 902(+3 字符化测试)= 145失败/745通过/12跳过,
与基线逐位一致,零回归。
2026-08-14 15:57:52 +08:00
hjhan b01b485ee4 test(swap): GetInterests 双入口语义字符化测试——并实测发现复利口径分歧
钉住三组现状(Step3 特判降级的前置回归网):
- 复利×mode2×部分平仓30%:盘中=0.036164835616(closePosi=平掉额300 全程重放);
  EOD=0.059041913305(preEod待实现0.05 + 平掉额末段增量0.009042,closePercent==1 分支)
  ⚠️ 两值不等=同一经济事件两种结息额的口径分歧,已留档待业务裁决(勿当既定正确)
- 单利×mode2:双入口数值留档(Console),断言非零
- 复利×mode9 全平(posi=0):兜底覆盖生效,结息额非零(兜底钉子)

基建复用 GetInterestsUnitTest_T0 口径(T+0、4/27起息、11算头算尾、FR007内存取价stub)。
2026-08-14 15:52:52 +08:00
hjhan e2431e9d44 refactor(accrual): 计息类型整体迁入 DAL——新增 Accrual/InterestMath,删 Core 未接线孤儿
搬迁(算法体逐字未动,仅换命名空间与归属):
- SwapInterest.Round/AccrualDays/FundingLegPrecision + AccrualBoundary/InterestResult
  → YLErpDAL/Modules/SwapModule/Accrual/InterestMath.cs
- AccrualTrace → Accrual/AccrualTrace.cs(被迫同迁:其 MarkStart 引用 AccrualBoundary,
  Core 不能反向依赖 DAL)
- 引用切换:Simple/CompoundInterestAccrual、AccrualPolicy、SwapCalcTrace、SwapDealService
  (保留 using YLErp.Derivatives.Interest——IIndexFixer/IndexFixerBase 留 Core)

删除(零生产引用,孤儿清零):
- Core:SwapInterest.cs 算法方法(AccrueSimple/AccrueCompoundInArrears/ApplyUnwind/
  AccrueUnrealized/ToInterestRate,未接线且与 DAL 生产实现舍入/rollover 口径已分叉)、
  AccrualContext.cs、InterestRate.cs
- DAL:AccrualState.cs(零引用死类)
- 测试:SwapInterest_CompoundInArrears_RolloverTimingTests.cs(仅测已删原语)

验证:两解决方案 Rebuild 0 错误;磁盘 SwapInterest. 残留 0;影子/分红/场景 86/86 通过
(含 Accrual 3 影子对账、Margin 影子、divPower 新增 AutoUnwindMultiPartial)。
注:AccrualContext 默认精度 11 与生产 12 的分叉隐患随删除一并消除;
已删原语若将来重建须先补对账测试,勿凭记忆复原(ARCHITECTURE.md 已留警告)。
2026-08-14 15:31:04 +08:00
张名锐 11a7aabce5 Merge branch 'glms/feature/0812_zmr_divPower' into glms/feature/1.4.2 2026-08-14 14:51:56 +08:00
hjhan 58bd27c596 test(EQD-7004): 补自动平仓路径多次部分平仓不重复计入的实证测试——驱动真实 BondPaymentService.CalcPayment(reg_date 口径×当次平仓量 unwindQty),验证总额按登记日持仓分摊、不自洽多算 2026-08-14 14:24:23 +08:00
hjhan 896a5c3dcc test(EQD-7004): DividendEodNoDoubleCountTest 的 EodSvcStub.CalcBondPayment 由线性假公式改为桥接真实 BondPaymentService(reg_date 口径),使登记日全平/盘中互换端到端测试真正跑生产票息计算 2026-08-14 13:37:52 +08:00
hjhan 6a25e4a2d8 test(EQD-7004): 补 reg_date 口径端到端测试——EOD引擎按债权登记日计提,登记日T+1全平/部分平仓经GetPreEodDividendSum正确读取 2026-08-14 13:13:17 +08:00
hjhan 37b889c3bf test(dividend): 补全平端到端测试——登记日全平按交易场所规定不享有当日分红
全平场景端到端测试(DividendIn 由 GetPreEodDividendSum 真实算,非喂常量)。
登记日当日全平(盘中平仓→收盘持仓0),按各交易场所规定不享有登记日当日分红(股权登记日以收盘在册为准)。
系统行为正确:①DividendIn 读 T-1(=T日前待实现,正确不含当日);②EOD 全平 PosiQuantity=0 不计提当日。
断言:应得(T日前待实现)==实拿(DividendIn)(不享有当日符合规定)+ EOD 不计提当日。

合并原 d9a55fd1 + 830c72c4:前者误判'丢失缺陷',后者按业务规则修正为'不享有、系统正确'。
2026-08-14 12:49:45 +08:00
hjhan 9bc6344f18 test(dividend): 补端到端测试——盘中互换DividendIn真实算+保存+EOD,验证不重复不丢失
填补 MU_001 缺口:MU_001 的互换 DividendIn 是测试喂的常量,本测试由生产方法 GetPreEodDividendSum 真实算出(读 EOD 快照),再喂给 EOD——覆盖"预览算 DividendIn + EOD 扣减"完整链路。

验证:盘中收益互换(不扣持仓) → DividendIn=GetPreEodDividendSum(读T-1) → 保存 → EOD:TdCloseDividend 扣减 DividendIn(不重复) + 当日新计进 PosiDividendSum(不丢失) + 守恒(全程新计-全程实现=末尾PosiDividendSum)。

内存 stub(DealSvcStub+EodSvcStub),不连库,进 CI。
2026-08-14 10:28:10 +08:00
hjhan bdba5fbecd fix(EQD-7004): 补全分红登记日口径修复的多次付息累计测试与Trace日志
原修复 9df39491 仅覆盖单次登记日(4/3)的两个单点修复,本次补全 EQD-7004 的完整验证与排查手段:

- 测试 GLMS20260105_0006 新增 4 个场景:5 期 reg_date 过滤口径各自命中正确子集;五期票息累计=5x36160=180800;auto 实现归0后下次登记日重新累加;4 期挂账累计读 144640

- 分红读取关键链路加 Debug 级日志(生产可关):BondPaymentService.GetBondPayments(reg_date 过滤区间+命中条数)、SwapDealService.GetPreEodPositionByDate(EOD 定位+回退)、GetPreEodDividendSum(取EOD日期+PosiDividendSum)、方案C 平仓预览/收益结算两处 DividendIn 赋值

实跑 dotnet test 全 GREEN(6/6)。任务编号 EQD-7004。
2026-08-14 10:05:01 +08:00
hjhan b2cbbff9e7 refactor(margin): 删除未接线的保证金死代码预留抽象
- 删除 MarginAccount 整类、IMarginResolver 子树(MarginForm/CashMargin/CreditMargin/GuaranteeMargin)、MarginBalance 只读 struct:全仓零生产实例化,结构上无法适配 CalcMarginInterest 的盘中差分模型。

- 删除仅覆盖上述死代码的 MarginLegTest(不损失活代码覆盖率)。

- 保留被 CalcMarginInterest 实际使用的 MarginCalc/MarginModes 活代码。

- 同步修剪 ARCHITECTURE.md 树图与对账表,清理 AccrualContext.cs 过期注释。
2026-08-14 08:55:37 +08:00
tengyufan 8afd449c40 feat(ClientBlack): 新增客户黑名单审批流程
- 黑名单新增、删除、批量导入、批量移出在配置审批节点后,审批通过才生效
- 新增黑名单待提交后可提交审批;仅首节点未审批时可撤回,进入后续节点禁止撤回
- 增加黑名单审批列表、审批弹窗、审批操作日志及状态筛选
- 支持审批配置中的“黑名单审批”流程及对应菜单、模块权限、操作权限
- 黑名单审批中禁止同名记录覆盖备注;已在黑名单中的同名导入会明确提示本次将修改备注
- 未配置审批节点时保持原有直接生效行为
- 增加黑名单审批状态机单测,39/39 通过
- 修正撤回全部失败时仍提示“撤回审批成功”的问题
2026-08-13 17:21:04 +08:00