Commit Graph
401 Commits
Author SHA1 Message Date
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
hjhan c0ac749e5b refactor(swap): 删除未接线的衡泰平仓消费死链 AutoSwapUnwindFromConsumer
- 移除 SwapDealService.AutoSwapUnwindFromConsumer 及其私有辅助
  GetUnwindInterestsByHT(全仓零调用方,随山证 v2.3.0 拷贝引入后从未接线)
- git rm 删除仅被该死链引用的 HengTaiModel/SwapUnwindReq.cs(全仓零引用)
- 修正 UnderlyingEntryFullPriceLeg.cs 中指向已删方法的文档注释
- 保留真实平仓路径 DealUnwind / CalcCloseAmount 等共享活代码
2026-08-14 15:14:09 +08:00
hjhan fffdea4526 fix(EQD-7004): 分红关键链路日志 Debug 提升为 Info(生产可见,k8s console 可输出)
bdba5fbe 中加的分红日志原为 Debug 级;但生产 NLog minlevel=Info,k8s console 打不出来。

- 按决策改为 Info 级(5 处): BondPaymentService.GetBondPayments(reg_date 过滤区间+命中条数)、SwapDealService.GetPreEodPositionByDate(EOD 定位+回退)、GetPreEodDividendSum(取EOD日期+PosiDividendSum)、方案C 平仓预览/收益结算两处 DividendIn 赋值

此后日志一律不使用 Debug 级(生产不可见)。任务编号 EQD-7004。
2026-08-14 12:53:39 +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
hjhan 909111ad2c docs(margin): 修正 CalcMarginInterest remarks——SwapIntervalList 是互换观察日排期而非FR007
第三次修正(前两次基于错误理解)。查证代码后准确描述:
- SwapIntervalList 是"互换观察日排期"(阶梯利率表 + 结息日历),非 FR007 浮动(浮动由 FloatRateUnderlyingCode + interest_rest_days 独立驱动);保证金前端亦开放分段录入
- rate 来自 GetFixedRate(SwapIntervalList 取 Date≤unwindDate 最近段)
- 盘中用该 rate 全程,与旧 CalcDailySimpleInterest 完全一致(BuildSegmentRates 的 spread 也是 GetFixedRate 单一值全程,不按 SwapIntervalList 切段)——阶梯利率盘中半路变更的精细处理是既有未覆盖口径,非本次引入;EOD 因每日重取 GetFixedRate 能正确反映阶梯
纯注释,零行为变化。
2026-08-14 08:22:49 +08:00
hjhan 67d7733ed6 docs(margin): 修正 CalcMarginInterest remarks——SwapIntervalList 单段是保证金本性而非限制
第2点措辞修正:原写"SwapIntervalList 必须单段(多段会漏分段)"误导,把业务本性误述成代码限制。
实际:保证金=固定利率(业务定义),SwapIntervalList 多段是融资腿 FR007 概念与保证金无关;
CalcMarginInterest 只消费 rate(GetFixedRate 已处理分段),不直接读 SwapIntervalList,
单段 segmentRates 是对固定利率的正确建模。纯注释,零行为变化。
2026-08-14 08:14:32 +08:00
hjhan 692b8d753b docs(margin): 补强 CalcMarginInterest 隐式假设/契约注释 + SwapEodPositionService orginPv 冗余说明
- CalcMarginInterest 加 <remarks>:显性化 4 点——InterestType 必须单利、SwapIntervalList 必须单段(违反静默算错、无 assert,前端保证)、方向须调用方翻转、preEod 首日就地修改;附定位指引(SwapCalcTrace 反推 accrualBasis)
- SwapEodPositionService 三处 FixedAmountAndMargin 的 orginPv 赋值加注释:仅固定值腿(mode 1)生效,保证金(5/6)被 CalcMarginInterest 忽略

纯注释,零代码行为变化;全量 SwapModule 524/524 通过。
2026-08-13 18:32:22 +08:00
hjhan 9df39491da fix(dividend): 分红按债权登记日(reg_date)口径计提,手动互换读取当日EOD快照 (GLMS-20260105-0006)
- BondPayment 实体补映射 reg_date(债权登记日,对应 bond_payment_info.reg_date 列)
- BondPaymentService.GetBondPayments 改按 reg_date 过滤(原错用 pay_date_PL/支付日;pay_date_PL 是理论付息日,仍属支付日,非登记日)
- SwapDealService.GetPreEodPositionByDate 由 ValueDate < dealDate 改 <=,登记日当天手动互换读当日EOD分红而非T-1
- 新增 GLMS20260105_0006_RegisterDateDividendTest 回归测试(手工合成内存,先RED复现后GREEN 2/2)

根因:bond_payment_info 表仅有 pay_date_PL(理论付息日)/pay_date_act(实际付息日) 两个支付日,缺登记日;
代码用支付日判定票息归属,且手动互换只读 T-1 EOD 快照;登记日≠支付日(恰差一工作日)时缺陷被掩盖,
导致对话框分红收益显示 0。
2026-08-13 12:31:45 +08:00
hjhan dea9c4cba5 docs(margin): 修复保证金计息重构后的过时文档与注释
按最新代码(CalcMarginInterest 已接生产、orginPv hack 已删、MarginAccount 未接线、保证金精度=12)修正:
- 删除 orginPv 维度重映射的孤儿 summary(方法已删)
- SwapDealService 注释:去掉 orginPv(InitSwapDealInterest)/保证金腿 引用
- SwapInterest/AccrualContext/InterestRate:去掉 Precision=11 是"保证金腿"、"保证金场景"等错误归因(保证金实际跑精度12)
- MarginAccount/MarginBalance:标注"尚未接线",生产入口指向 CalcMarginInterest,去掉"余额×利率×天数"过度简化
- 测试注释:去掉"无 orginPv/差分"(盘中保留差分)、"提交2 待切换"(已完成)

仅文档/注释,零代码行为变化。
2026-08-13 10:58:47 +08:00
hjhan 9b29211d3c refactor(margin): 保证金计息切换到 CalcMarginInterest + 删 orginPv 外部维度 hack
- GetInterests 保证金分支(5/6)切到 CalcMarginInterest,不再走融资腿通用 CalcEodInterest/CalcUnwindInterest
- 删除 InitSwapDealInterest 的保证金 orginPv 维度 hack(保证金已不走该方法,成为死代码)
- CalcMarginInterest 修正三处与旧管线的对齐(全量回归发现):
  · 加 endDate 参数(盘中用 InitInterestDate 的 endDate,否则少算天数)
  · calcLast 合并 newCalcLast(与 CalcUnwindInterest 一致,算尾)
  · 盘中保留 accrualBasis 差分(posiPrincipal 对齐状态路径相关,单一本金变量无法覆盖;orginPv 内部按 PreviousBalance 算,消除外部维度 hack)
- 更新 Shadow/GoldenReplay 测试调用点(加 endDate)

修正说明:盘中“消除差分”不可行——SPC_006(position 已对齐) 与 PrepaidPrincipalCloseTrace(position 未对齐) 期望相反,差分 accrualBasis 经 orginPv 自适应两种状态。CalcMarginInterest 真实收益收敛为:保证金领域独立 + orginPv 内聚 + 消除浮动/分段/复利死分支,而非消除差分。EOD 路径无差分(用昨日终本金)。

验证:全量 SwapModule 522/522 通过(0 失败);真实库黄金回放 60 条 0 差异。
2026-08-13 10:39:03 +08:00
hjhan a86b496f87 test(margin): 新增 CalcMarginInterest 影子对账——保证金计息消除 orginPv/差分,与旧管线数值一致
- 新增 CalcMarginInterest(SwapDealService):保证金专属计息,复用 SimpleInterestAccrual 纯函数,notional 直接取保证金余额(EOD=昨日终本金/盘中=今日本金),消除融资腿差分公式 accrualBasis=TdInterestPrincipal+posiPrincipal-orginPv 与 orginPv 维度 hack(对保证金 accrualBasis 恒等于 posiPrincipal,差分冗余)
- 保留累计语义(priorAccrued+增量),满足下游 SwapEodPositionService 字段契约
- 新增 MarginInterestShadowTest:5 场景对账(EOD 续接/首日、盘中全平/部分平仓/互换),新旧 InterestAmount/TdInterestAmount 严格一致
- 本提交仅影子对账,生产路径未改(GetInterests 仍走 CalcEodInterest/CalcUnwindInterest);下一步提交2 切换生产 + 删 orginPv hack

零生产风险;编译 0 错误;影子对账 5/5 + 现有保证金/利息回归 60/60 通过。
2026-08-13 09:37:42 +08:00
hjhan 70550af29a refactor(funding-leg): 清理CalcNotional死参数posiLong/posiShort+修正过时InterestMode注释
- 删除 IFundingLegStrategy.CalcNotional 的 posiLong/posiShort 死参数(多空存续腿界面已禁用,三个实现均不读取),同步三个实现签名、SwapDealService 唯一调用点、FundingLegStrategyTest 7 处调用
- 修正 SwapPosition/SwapFlowEvent/EodSwapPosition 的 InterestMode 字段注释(去掉已删的 3/4,补全 5/6/9)
- 重写 SwapUnwindFloatingLegDiagnosticTdd 过时类注释

零行为变化;编译 0 错误;FundingLegStrategyTest 11/11 通过。
2026-08-13 08:51:21 +08:00
hjhan dfdc890749 test: 补全 SaveAutoSwapDeal 的测试 seam——修复6个AS_*测试失败
- SwapEodPositionService: AddSwapEvent 虚方法(已存在)替换 line 741 的 new SwapEventService 直调

- SwapEodPositionService: 新增 PersistFlowEvent 虚方法,替换 line 749/760 的 DbContext.swap_flow_event.Add 直调

- TestableSwapEodPositionService: PersistFlowEvent 改为 override

- BondTrsAutoSwapScenarioTest.AutoSwapEodService: override PersistFlowEvent 同步到 PersistedFlowEvents

测试: 511→517 通过,7→0 失败
2026-08-12 18:23:51 +08:00
hjhan f1f7cae832 refactor(accrual): 抽取BuildSegmentRates共享分段取率+清理needPrice死参数
- 抽取 BuildSegmentRates:统一单利/复利分段取率循环,参数化 fetchAfterDate (单利传 ValueDate 仅取新段,复利传 null 全程取)

- 修复 BuildSegmentRates static→instance:访问实例属性 IndexFixer

- 清理 CalcDaily 层 needPrice 死参数:4个 CalcDaily* 方法签名移除 needPrice;上层 CalcSwapInterests/GetInterests 保留 (virtual seam/位置参数兼容)

- 修复 6 处调用点 needPrice 参数传递

- 新增影子测试:单利+FR007浮动+部分平仓+历史归档,验证 segmentRates 一致

- 新增 TdCarryInCharacterizationTest:钉死部分平仓 TdInterestAmount carry-in 行为

测试: 511通过 / 7预存在失败(数据依赖) / 9跳过
2026-08-12 18:02:36 +08:00
hjhan c412f918d9 refactor(swap): Phase3 提取ClosePercentMath共享模块
SwapDealService的4个public static ClosePercent方法body搬到ClosePercentMath.cs:
- ResolveUnwindPreviousNotional (上一日终浮动端名义本金)
- ToRemainingClosePercent (A→B 占期初→占剩余)
- ToOriginalClosePercent (B→A 逆转换)
- CalcDefaultInitClosePercent (默认占期初比例)

DealService保留4个一行转发壳, 外部调用(含6+测试文件)零改动
核实EodService内联closePercent公式与此不同, 非重复, 不替换
SwapModule零回归(7基线/510通过)
2026-08-12 17:31:39 +08:00
hjhan 69295f31ce refactor(eod): Phase2 提取EodPnlCalculator——11个static方法搬出SwapEodPositionService
SwapEodPositionService的static纯计算方法搬到EodPnlCalculator.cs:
- private→internal: RoundMoney/RoundEodInterest/NormalizeEodPositionForStorage/SetFloatingRealizedPnl/FillPositionLegSummary/SumInterestPnL/CalculateWeightedMarginRate
- public留转发壳: CalculateSwapRealizedPnl/NormalizeInterestSignForReport/CalculateWeightedMarginInterest/SetFixedLegRealizedPnl

SwapEodPositionService内所有调用点加EodPnlCalculator前缀
SwapModule零回归(7基线/510通过)
2026-08-12 16:46:13 +08:00
hjhan 40756a28b3 refactor(accrual): BuildLegRate/BuildEodPolicy移到各自类型(Phase1补完)
BuildLegRate → FundingLegRate.Build(position, spread, float) — 利率值对象的自然工厂
BuildEodPolicy → AccrualPolicy.BuildEod(position, annualDays, isCompound) — 计息政策的自然工厂

SwapDealService删除2个private static定义, 4处调用点改用类型方法
SwapModule零回归(7基线/510通过)
2026-08-12 16:32:04 +08:00
hjhan b9362ea700 refactor(swap): Phase1 提取UnwindNormalizer+TradingFeeCalc出SwapDealService
SwapDealService的10个private static纯逻辑方法搬到两个新文件:
- UnwindNormalizer.cs: NormalizeNotionalValues/FullCloseRequest/Recalculate/IsFullClose/SettledInterestAmounts/EventUnwindDate (6个)
- TradingFeeCalc.cs: CalcInitTradingFee/CalcInitTradingFeePending (2个)

SwapDealService内21处调用点加类名前缀, 反射测试改为直接调用(public)
SwapModule零回归(7基线/510通过)
2026-08-12 16:26:12 +08:00
hjhan 89eae7555f docs: 任务4 EOD续接风险分析 + SwapDealService方向比例收口
新增 docs/eod-continuation-proposal.md:
- 记录4个风险(并本金起点/consumedInterest语义/resetCarry/全平重放)
- 记录验证方案(6场景影子测试)
- 标注待立项,不混入日常重构

SwapDealService 收尾: 3处内联方向比例→DirectionRatio.ReceivePay
- line389: PosiDirection收取?-1:1 → -ReceivePay
- line1104/1235: InterestDirection==1?1:-1 → ReceivePay

SwapModule零回归(7基线/510通过)
2026-08-12 15:24:15 +08:00
张名锐 304e04d60a Merge remote-tracking branch 'origin/glms/feature/1.4.2' into glms/feature/1.4.2 2026-08-12 15:10:06 +08:00
hjhan 8419d53421 refactor(eod): MtmCalc.BlendPrice提取加权均价混合(候选J)
4处内联 (prevPrice*prevQty + sum) / total → MtmCalc.BlendPrice(...)
SwapModule零回归(7基线/510通过)

候选K(单点TdRealizedPnL)/L(2处guard差InterestType)/M(2处guard)/N(单点)为低频,
提取收益不足以抵消新增方法的开销, 暂不提取
2026-08-12 15:03:00 +08:00
hjhan dbdc975da4 refactor(eod): 提取FillPositionLegSummary/SumInterestPnL消除eod_swap汇总重复(候选H)
SaveEodSwap与UpdateEodSwap各有~15行相同的持仓腿汇总代码:
- NotionalValueLong/Short(rounded) + MarketValueLong/Short + FloatingPnL + dv01 + TdCloseQty
→ FillPositionLegSummary(eod_Swap, positions)

- 利息腿ForEach按InterestLegPnl汇总InterestPnL
→ SumInterestPnL(interestPositions)

两处合计净减~18行
SwapModule零回归(7基线/510通过)
2026-08-12 14:53:41 +08:00
hjhan aeb55a01ad refactor(eod): 提取InterestIncomeCalc.DailyAccrual/RollRealized + MtmCalc.ReturnLegProfitSum
候选B: 3处日利息 principal*(rate+float)/annualDays → InterestIncomeCalc.DailyAccrual
候选D: 4处 PosiProfitSum=Mtm+Div+Fee → MtmCalc.ReturnLegProfitSum
候选E: 4对 RealizedInterest/RealizedInterestFee 滚存 → InterestIncomeCalc.RollRealized

SwapModule零回归(7基线/510通过)
2026-08-12 14:50:24 +08:00
张名锐 2035e1df4c fix(swap): 修复EOD平仓后收盘时结息本金计算错误
- 修正SwapDealService.GetInterests方法中平仓后结息本金语义错位问题
- 区分EOD平仓后收盘与盘中平仓前本金传参的不同语义
- 针对模式2合约名义本金规模强制覆盖为实际平仓额
- 保持模式9标的期初全价在全平时的零值处理逻辑
- 添加债券份额调整公司行动支持和记录日期资格判断
- 实现EOD仓位复制和更新时的公司行动应用逻辑
2026-08-12 14:40:03 +08:00
hjhan 1aa0579fbd refactor(eod): MarginModes.FixedAmountAndMargin + DirectionRatio.RateType提取
候选F: 3处内联 new List{固定值,初始预付金,追加预付金} → MarginModes.FixedAmountAndMargin
候选I: 7处内联 收取?Buy:Sell → DirectionRatio.RateType(direction)

SwapModule零回归(7基线/510通过)
2026-08-12 14:38:04 +08:00
hjhan af94003939 refactor(eod): DirectionRatio.ReceivePay/InterestLegPnl替换21处内联三元
SwapEodPositionService中散落的收取?1:-1三元表达式和ratio+margin flip块:
- 9处InterestLegPnl块(3-5行→1行): 收取?1:-1 + if(MarginModes)ratio=-ratio
- 12处ReceivePay三元: 收取?1:-1 → DirectionRatio.ReceivePay(dir)
- 含2处反向(收取?-1:1 → -DirectionRatio.ReceivePay)

净减约30行, 消除+1/-1写反的风险
SwapModule零回归(7基线/510通过)
2026-08-12 14:33:23 +08:00
hjhan bfa230381b refactor(accrual): 提取BuildLegRate/BuildEodPolicy消除EOD wrapper构造重复
EOD CompoundEod + SimpleEod 各有一份相同的FundingLegRate构造(3行)
和AccrualPolicy构造(5行), 共16行重复。

提取为:
- BuildLegRate(position, spread, effectiveFloat): 固定腿→Fixed, 浮动腿→Floating
- BuildEodPolicy(position, annualDays, isCompound): 算头算尾+重置周期

每个EOD wrapper从10行构造代码→2行调用, 净减12行
SwapModule零回归(7基线/510通过)
2026-08-12 14:01:24 +08:00
hjhan 9d321378ac cleanup(accrual): 删除orginPv死参数(3个方法签名+所有调用点)
源码实证orginPv在5/6个CalcDaily方法中是死参数(方法体不读取):
- CalcDailyCompoundInterest: 删 decimal orginPv(3个调用点同步删)
- CalcDailyCompoundInterestByEod: 删(1个调用点+4个测试同步删)
- CalcDailySimpleInterestByEod: 删(1个调用点+2个测试同步删)

保留orginPv的方法(真实消费者):
- CalcDailySimpleInterest: 差分公式 posiPrincipal - orginPv
- GetInterests/CalcUnwindInterest/InitSwapDealInterest: 转发链

SwapModule零回归(7基线/510通过)
2026-08-12 13:58:43 +08:00
hjhan 9d8cfb5ad0 refactor(accrual): 拆分FundingLegAccrual→SimpleInterestAccrual/CompoundInterestAccrual
单利与复利语义完全不同(单利本金恒定/复利重置日并本金),
拆成两个独立静态类,各自只含自己的方法:

SimpleInterestAccrual:
- AccrueEod (原AccrueSimpleEod)
- AccruePeriod (原AccrueSimplePeriod)

CompoundInterestAccrual:
- EodBasis (原CompoundEodBasis)
- AccrueEod (原AccrueCompoundEod)
- AccruePeriod (原AccrueCompoundPeriod)

方法名去掉Simple/Compound前缀(类名已携带类型),消除冗余
SwapModule零回归(7基线/510通过)
2026-08-12 13:40:47 +08:00
hjhan 57156feec1 refactor(accrual): 提取ResolveFloatRate收口FR007定盘逻辑
EOD单日取率(CompoundEod/SimpleEod)与intraday多日取率(BuildSegmentRates)
原各有一份相同的GetFixingDate+TryGetFixing+throw逻辑(~8行/处,3处共24行)。

提取为私有 ResolveFloatRate(position, date, fallback):
- 无浮动标的返回fallback; 取到非零fixing返回fixing; 取不到抛异常
- EOD: isResetDay ? ResolveFloatRate(...) : floateRate (1行替代12行)
- BuildSegmentRates: 循环内直接调用(1行替代8行)

净减~15行, FR007定盘逻辑收口到一处
SwapModule零回归(7基线/510通过)
2026-08-12 13:37:40 +08:00
hjhan c86633479d refactor(accrual): 接线SimpleInterest/CompoundInterest内部 + 分段修复 + 去重
CalcDailySimpleInterest/CalcDailyCompoundInterest内部逐日循环→分段纯函数(签名不变):
- AccrueSimplePeriod: AccruedToday改未缩放累计(TdInterestAmount口径)
- AccrueCompoundPeriod: 新增out finalBasis供flowEvent.InterestPrincipal精确赋值
- 修复5处分段边界bug(includeStart/includeEnd/resetCarryInterest interestPeriod=1场景)
- 提取BuildSegmentRates消除SimpleInterest/CompoundInterest取率重复
- 影子测试补AccruedToday断言关闭测试盲区

SwapModule零回归(7基线/510通过)
2026-08-12 13:09:17 +08:00
hjhan ff937f24e7 refactor(accrual): EOD去退化差分——删除baseNotional死参数
EOD路径 orginPv≡posiPrincipal(调用点line1091/1096实证),notional==baseNotional恒成立,
差分项 notional-baseNotional 恒为0,baseNotional 是死参数。

简化:
- AccrueSimpleEod: 删 notional+baseNotional 两参数,basis=priorNotional 直赋值
- AccrueCompoundEod: 删 baseNotional 参数,非重置日 basis=priorNotional
- SimpleEodBasis 整个删除(不再需要)
- CompoundEodBasis 去掉 baseNotional 参数
- EodContext trace 去掉 baseNotional
- 差分注释块删除(EOD不再有差分)

差分逻辑 notional-baseNotional 只该存在于 unwind 专用路径(Period函数本就没有baseNotional)
净减20行(18增/38删),SwapModule零回归(7基线/508通过/8跳过)
2026-08-12 10:58:53 +08:00
hjhan ffd539d8c3 refactor(accrual): 抽出CompoundEodBasis/SimpleEodBasis消除基数公式重复
- 基数差分公式(priorNotional+notional-baseNotional)原在AccrueCompoundEod内部与调用点各算一遍
- 抽成public static单一真相源: CompoundEodBasis/SimpleEodBasis, 纯函数与调用方共用
- 差分语义文档集中到一处(EOD退化为priorNotional / unwind增量生效)
- 吸收外部review: 三元拆具名方法 + 消重复, 但否决priorFloatingLegNotional(orginPv非浮动腿概念)
- 影子测试16/16绿, SwapModule零回归(7基线/508通过)
2026-08-12 10:48:04 +08:00
hjhan 3b02440979 refactor(accrual): 接线CalcDailyCompoundInterestByEod→AccrueCompoundEod + 全链路Trace
- AccrualTrace新增Segment(分段区间/天数/基数/利率)与EodContext(重置日/平仓比例/基数分量)语义方法
- AccrueSimplePeriod/AccrueCompoundPeriod补全AccrualTrace?参数:MarkStart→逐段Segment→复利Rollover→Unwind→MarkEnd
- CalcDailyCompoundInterestByEod内部实现下沉至FundingLegAccrual.AccrueCompoundEod纯函数
  删除调试垃圾LogFactory('test').Error,取率/重置日/remainingFraction逻辑保留
- flowEvent.InterestPrincipal/FloatRate副作用在调用方保留(下游EOD播种次日TdInterestPrincipal)
- 全量SwapModule测试零回归(7基线失败/508通过/8跳过)
2026-08-12 10:37:52 +08:00
hjhan 2f7caa7a1f refactor(accrual): referenceNotional→baseNotional, notional统一
- referenceNotional → baseNotional(更短更清晰)
- 新代码统一用 notional(不用 principal):
  QuantLib/Strata 对 swap leg 用 notional 是业界标准
- DB字段(InterestPrincipalFix等)保持不动(legacy)
- 注释标注 legacy 对照(orginPv/originalPv/dynomicPrincipal)

验证: 编译0错误, 7影子测试全过, 全量523测试7失败(基线一致)。
2026-08-12 10:20:13 +08:00
hjhan ce6c0812e1 refactor(accrual): 参数命名对齐业界标准(QuantLib/Strata)
FundingLegAccrual 4个方法的参数命名全面纠正:

| 旧名              | 新名               | 理由 |
|-------------------|-------------------|------|
| originalPv        | referenceNotional | Pv(现值)概念错误,实际是参考本金 |
| closeRatio/closePercent | unwindFraction | 统一,对齐 ApplyUnwind.unwindPercent |
| consumedInterest  | realizedInterest  | 对齐 AccrualState.RealizedInterest |
| priorUnrealized   | priorAccrued      | 对齐 InterestResult.Accrued |
| priorAccrualPrincipal | priorNotional  | 简洁,对齐 QuantLib notional |
| positionPrincipal | notional          | 简化,去掉冗余前缀 |
| dynomicPrincipal  | accrualBasis      | 修正typo+用业界术语 |

同步更新:
- SwapDealService.CalcDailySimpleInterestByEod 调用点
- 3个影子测试文件命名参数
- SwapInterest.Round 改为 public(所有Round收口一处)

删除半成品残留: SwapInterest_CompoundInArrears_RolloverTimingTests.cs

验证: 编译0错误, 7个影子测试全过, 全量520测试7失败(基线一致)。
2026-08-12 10:11:26 +08:00
hjhan 3f194f7ccc fix(accrual): Round收口到SwapInterest.Round + 复利分段边界修复
3项修复:

1. SwapInterest.Round 从 private 改为 public:
   所有计息路径收口到一处, FundingLegAccrual 不再用裸 Math.Round

2. ApplyUnwind 精度警告:
   注释标注'资金腿务必传 FundingLegPrecision=12', 默认11是易忘的坑

3. AccrueCompoundPeriod 分段边界修复(根因):
   旧bug: 中间段 IncludeEnd=true 导致重置日利息用了旧本金
   修复: 中间段 IncludeEnd=false(半开区间), 重置日归下一段用新本金
   对齐旧逐日循环语义: 重置日先更新本金再算息

同时: 中间值不Round(对齐旧代码只在最终输出Round一次)
清理: 删除 SwapInterestAccrueCompoundInArrearsTest 半成品残留

验证: 编译0错误, 7个影子测试全过, 全量520测试7失败(基线一致)。
2026-08-12 09:59:53 +08:00
hjhan 552fb2061b Merge branch 'glms/feature/1.4.2' of http://git.yiliantech.com/gitlab/otc-dev/zszq-trs into glms/feature/1.4.2 2026-08-12 09:48:53 +08:00
hjhan 431ac9b502 feat(accrual): 新增 AccrueCompoundPeriod + 影子测试(3个全过)
第3步迁移: CalcDailyCompoundInterest(盘中复利多日)。

新增纯函数 AccrueCompoundPeriod:
- 从 PosiStartDate 到 endDate 全程重放
- 每个重置日并本金: principal + replayed interest
- 末日(endDate)为重置日且 resetCarryInterest!=0 时用存量替代
- consumedInterest × closePercent 在末尾扣除
- 分段模型替代逐日循环, 段内本金恒定+利率恒定

影子测试(3个,全过):
- 固定利率全平+末日重置日: 旧新一致
- 部分平仓30%+consumedInterest扣除: 旧新一致
- 算头算尾(calcMode=11): 旧新一致

清理: 删除 SwapInterestAccrueCompoundInArrearsTest(半成品残留,
依赖已回退的 SwapInterest 签名)。
修复: SwapInterestAccrueCompoundInArrearsTest 缺 using(已随文件删除)。

验证: 编译0错误, 全量520测试7失败(基线一致)。
4个CalcDaily*方法的新架构纯函数全部就绪, 旧方法暂保留对比。
2026-08-12 09:48:49 +08:00
张名锐 225e0e3e80 Merge remote-tracking branch 'origin/glms/feature/1.4.2' into glms/feature/1.4.2 2026-08-12 09:45:19 +08:00
张名锐 ec820a4974 Merge branch 'glms/feature/1.4.2_0808' into glms/feature/1.4.2 2026-08-12 09:44:51 +08:00
hjhan 963b5e66af feat(accrual): 新增 AccrueSimplePeriod + 影子测试
第2步迁移: CalcDailySimpleInterest(盘中单利多日)。

新增纯函数 AccrueSimplePeriod:
- 单利特征: 计息本金恒定(差分 = priorAccrualPrincipal + positionPrincipal - originalPv)
- 按重置日分段, 每段用 AccrualDays 算天数×日利息(无逐日循环)
- 续接 priorValueDate 之后的日期

修复: SwapInterest.Round 是 private, 新方法改用 Math.Round。
修复: IReadOnlyList 无 IndexOf, 改用 for 循环索引。

影子测试(2个,全过):
- 固定利率无归档: 旧新一致(差分本金=0,利息=0,符合旧逻辑)
- 有归档续接+部分平仓50%: 旧新一致

验证: 编译0错误, 全量517测试7失败(基线一致)。
2026-08-12 09:27:20 +08:00
hjhan 217c2d5826 feat(accrual): 新增 FundingLegAccrual.AccrueCompoundEod + 影子测试
第1步迁移: CalcDailyCompoundInterestByEod(EOD复利单日)。

新增纯函数 AccrueCompoundEod:
- 重置日: 本金 = positionPrincipal + priorUnrealized × remainingPercent
- 非重置日: 本金 = priorAccrualPrincipal + positionPrincipal - originalPv
- InterestAmount = priorUnrealized × closeRatio + dayInterest
- TdInterestAmount = dayInterest(不含 priorUnrealized)

影子测试(2个,全过):
- 重置日场景: 旧新 InterestAmount/TdInterestAmount 一致
- 非重置日场景: 同上

旧方法暂不删——新旧并存,对比验证后再替换调用点。
验证: 编译0错误, 全量515测试7失败(基线一致)。
2026-08-12 09:15:01 +08:00
hjhan 5dc8bdbf9e refactor(interest): 计息 trace 命名对齐业界最佳实践
- InterestTrace → AccrualTrace:追踪对象是"计息过程"而非泛指的 Interest,与仓库
  既有的 SwapCalcTrace(-Trace 后缀约定) 及 AccrualContext/AccrualBoundary/AccrualPolicy
  家族一致;AccrualTrace 经 SwapCalcTrace.Write 常驻落盘,二者职责分清(收集器 vs 落盘通道)。
- InterestStep → AccrualTraceEvent、InterestTraceEntry → AccrualTraceEntry:
  条目类别对应 QuantLib/Strata 的"事件"概念(AccrualTraceEvent),命名自描述。
- SwapCalcTrace.Persist → Write:"Persist" 易误读为落库持久化;此处只是写入日志通道,
  且类内已有私有 Record(string),故用 Write 避免歧义。

纯改名、数值逻辑零改动;YLErpDAL + UnitTestProject 编译 0 错。
2026-08-12 07:05:41 +08:00
hjhan be3be877ec refactor(interest): 引入 AccrualContext 并将 AccrueCompound 重命名为 AccrueCompoundInArrears
1. 新增 AccrualContext 只读值对象,把 annualDays/precision/trace 三个"与金额利率无关"
   的横向参数打包为上下文成员。计息纯函数只吃一个 ctx,签名去噪、可读性提升;
   ctx 为只读值对象(非类的实例/静态字段),不破坏纯函数(线程安全、可重入、可测)。
   trace 因此成为"传入上下文的成员"而非散落参数,满足"默认成员而非参数"的诉求。

2. AccrueCompound → AccrueCompoundInArrears:精确表达 OIS/SOFR/FR007 的
   compounded-in-arrears 语义(每子区间取定盘、段末滚入本金),与
   InterestRate.Compounding.Compounded 闭式分支(TRS 下死路径)一刀两断、不再混淆。

纯增量、数值逻辑不变;YLErpDAL + UnitTestProject 编译 0 错 0 警;FR007 两段切换场景
实跑 trace 与旧版数值完全一致。
2026-08-12 06:13:38 +08:00
hjhan fcf52a305e feat(interest): 融资腿计息接入 trace 并常驻落盘
FundingLegAccrual.AccrueSimpleEod 接 InterestTrace;SwapCalcTrace 新增 Persist
把收集器条目经 Critical 无条件落盘(与开关无关);已迁移的 EOD 单利适配器
CalcDailySimpleInterestByEod 创建并 Persist trace。纯增量、数值逻辑不变。
2026-08-12 06:05:02 +08:00
hjhan 63bfc8ca8f refactor(interest): SwapDealService 接入 FundingLegRate 工厂并复用 FundingLegPrecision
取率处改用 Fixed/Floating 工厂;InterestCalculationPrecision 引用
SwapInterest.FundingLegPrecision,消除重复常量定义。
2026-08-12 05:36:02 +08:00
hjhan dee491b8d0 refactor(interest): FundingLegAccrual 删除危险骨架,仅保留纯函数 AccrueSimpleEod
移除标注"切勿在未迁移前接入生产"的 AccrueDay/AccruePeriod 骨架(含 IIndexFixer/
Func 取价),保留与旧实现逐字对齐的纯函数,消除两套计息逻辑并存的隐患。
2026-08-12 05:36:02 +08:00
hjhan e7514a2d84 cleanup(interest): 删除 AccrualPolicy 未使用的 FromLegacy 工厂与冗余 using
FromLegacy 无调用方;移除 YLErp.DBModels using 后 InterestTypeEnum 仅存于注释。
2026-08-12 05:36:02 +08:00