Commit Graph
168 Commits
Author SHA1 Message Date
hjhan 01447ffe04 refactor(swap): GetInterests 传参语义显式化——盘中/EOD平仓后收盘拆双显式入口
问题:GetInterests 同名参数在两类调用方下语义相反,约定只存在于注释(2035e1df 固化):
  盘中(:489链)                EOD平仓后收盘(:1311链)
  posiNotionalValue  平仓前剩余本金  vs  平仓后剩余本金
  closePercent       实际平仓比例(B)  vs  恒1(全额结息)
  均走 settment:false 盘中重放算法。6fdc7d80 修的错账即两语义混用产物,
  mode2 无条件覆盖/mode9 全平兜底是粘合补丁。

改动(纯机械,零行为变化):
- SwapDealService 新增 GetIntradayUnwindInterests(preCloseNotional/closedNotional/
  closePercentRemaining 具名),GetUnwindInterests 切换调用;needPrice/grossPrice 为
  GetInterests 死参数(体内零消费),新入口不再暴露
- SwapEodPositionService 新增虚接缝 CalcEodPostCloseSettleInterests
  (remainingNotionalAfterClose/closedNotional/恒1),默认实现经 CalcSwapInterests
  转发——既有测试替身对该接缝的拦截不变;SaveAutoEodWithCloseInterestPosition 切换调用
- 原 GetInterests/CalcSwapInterests 签名与行为不动(测试直调兼容);EOD 增量路径
  (:1148/:1563, settment:true, closePosi=posi 同值) 语义自洽,本次不动

验证:全量 899 测试 145失败/742通过/12跳过——与 e2431e9d 基线逐位一致,零回归。
2026-08-14 15:39:44 +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
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 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 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 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
张名锐 2035e1df4c fix(swap): 修复EOD平仓后收盘时结息本金计算错误
- 修正SwapDealService.GetInterests方法中平仓后结息本金语义错位问题
- 区分EOD平仓后收盘与盘中平仓前本金传参的不同语义
- 针对模式2合约名义本金规模强制覆盖为实际平仓额
- 保持模式9标的期初全价在全平时的零值处理逻辑
- 添加债券份额调整公司行动支持和记录日期资格判断
- 实现EOD仓位复制和更新时的公司行动应用逻辑
2026-08-12 14:40:03 +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
张名锐 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 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 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 0c447ff5ff refactor: 提取融资腿计息领域模型 (AccrualPolicy/FundingLegRate/AccrualState/FundingLegAccrual) - 将 CalcDailySimpleInterestByEod 纯数学下沉至 FundingLegAccrual.AccrueSimpleEod, 消除 double 往返转换, 引入值对象收敛散落参数 2026-08-11 22:37:19 +08:00
hjhan 6fe5d81917 refactor(margin): 3个保证金纯函数迁入 MarginCalc, 改正命名
从 SwapDealService 剥离到 Margin/MarginCalc.cs:
- PreviousBalance: 取上一日终保证金余额(原 ResolveMarginOrginPv, 改正命名)
- FlipDirection: 保证金方向翻转(原 FlipMarginDirection)
- AccumulateSettlement: 平仓汇总(原 AccumulateMarginSettlement)

命名修正: orginPv 是融资腿概念, 保证金没有; 改为 PreviousBalance。
SwapDealService 里删掉这3个private方法, 调用点改用 MarginCalc.XXX。
RecordMarginCashFlow 暂留(依赖实例方法 AddClientCash)。

保证金纯函数现在全部在 Margin 模块, SwapDealService 只保留
RecordMarginCashFlow(资金记录, 需要实例上下文)。
验证: 编译0错误, 全量509测试7失败(基线一致)。
2026-08-11 14:35:42 +08:00
hjhan 2fbffcceb4 refactor(margin): 3处散落保证金资金记录收敛到 RecordMarginCashFlow
RecordMarginCashFlow 加委托重载, 统一 AddClientCash(virtual)和
AddClientCashInCashOut(非virtual)两条路径。

替换3处 inline:
- :1663 SwapUnwind 平仓(AddClientCash, 只有应付预付金)
- :1979 DealUnwind(AddClientCashInCashOut, 只有应付预付金)
- :2128 SwapIncome 互换(AddClientCash, 只有预付金返息)
- :2195 自动互换(AddClientCash, 只有应付预付金, swapEvent.unwindData)

注释掉的返息代码(//if SwapMarginRebatePnl)同步清理——
通过传 marginRebate:0 表达'不写返息', 不再留注释代码。

SwapDealService 保证金资金记录 inline: 4处 → 0。
全部走 RecordMarginCashFlow 的 2 个重载。
验证: 编译0错误, 全量509测试7失败(基线一致)。
2026-08-11 14:26:29 +08:00
hjhan 11923e11b7 refactor(margin): 抽取 RecordMarginCashFlow 保证金资金记录
SwapLongShortUnwind 里 inline 的保证金资金记录
(应付预付金 + 预付金返息)抽成独立方法。

SwapDealService 内保证金方法清单(待整体迁入Margin):
- ResolveMarginOrginPv        维度重映射
- FlipMarginDirection         方向翻转
- AccumulateMarginSettlement  平仓汇总
- RecordMarginCashFlow        资金记录(新)

验证: 编译0错误, 全量509测试7失败(基线一致)。
2026-08-11 13:48:06 +08:00
hjhan 870cd337f6 refactor(margin): 抽取 AccumulateMarginSettlement 保证金平仓汇总
把 SwapUnwind 里 inline 的保证金汇总逻辑(SwapMarginRebatePnl +
SwapMarginAmount)抽成独立方法, 与融资腿汇总分离。

原循环里保证金和融资腿混在一起, 现在保证金走
AccumulateMarginSettlement, 融资腿汇总留在主循环。
待迁入 Margin 模块。

验证: 编译0错误, 全量509测试7失败(基线一致)。
2026-08-11 13:18:20 +08:00
hjhan 29df4c480d refactor: 删除 CalcNotionalByMode, 保证金/融资腿计息基数内联到调用点
CalcNotionalByMode 已完成历史使命:
- 融资腿(1/2/9)迁入 FundingLegStrategyFactory
- 保证金(5/6)迁入 MarginModes.Contains 分支
- 死代码(3/4/7/8)已删

调用点(GetInterests:843)现在内联两条路径:
- 保证金: closePrincipal = Fix × closePercent (用 MarginModes.Contains 判断)
- 融资腿: FundingLegStrategyFactory.Get(mode).CalcNotional(...)

删除:
- CalcNotionalByMode 方法(含注释)
- 2个用反射调私有方法的诊断测试(验证的逻辑已被FundingLegStrategyTest覆盖)

验证: 编译0错误, 全量509测试7失败(基线一致)。
2026-08-11 13:13:32 +08:00
hjhan e9b54bdcf6 cleanup: 删除死代码枚举 mode 3/4/7/8 及相关引用
mode 3(持仓名义本金)/4(持仓市值): 零引用死代码
mode 7/8(多空存续名义本金): 界面已禁用, 无历史数据(用户确认)

删除:
- InterestModeEnum: 移除 持仓名义本金/持仓市值/多头存续名义本金/空头存续名义本金
  枚举值保持显式(1/2/5/6/9), DB契约不变
- CalcNotionalByMode: 移除 mode 7/8 的 case
- ConsTrade.InterestModels/InterestNotionalModels: 移除 mode 7/8
- 删除3个 mode 7/8 相关测试方法(场景已不存在)

CalcNotionalByMode 现在只剩 mode 5/6(保证金)的 case,
待 Margin 独立计息入口建成后整体删除。
验证: 编译0错误, 全量508测试7失败(基线一致)。
2026-08-11 13:00:52 +08:00
hjhan ed7b9102d4 docs: CalcNotionalByMode 补充过渡期注释, 说明保留原因
回应命名评审: 确认 FundingLegs 目录无旧文件残留(5个新文件),
旧类名/命名空间零引用。CalcNotionalByMode 有意保留:
- mode 5/6(保证金)待 Margin 独立入口建成后迁出
- mode 7/8(多空存续)界面已禁用, 保留为防御性兜底
待 mode 5/6 迁出后本方法可整体删除。
2026-08-11 12:55:02 +08:00
张名锐 b0cdf0bcbc style(SwapModule): 格式化代码
- 在 SwapDealService.cs 中标准化了多行参数的缩进和换行
- 修复了 SwapTradeBaseService.cs 中的条件运算符周围的空格
- 统一了方法调用中参数的排列格式
- 改进了代码的可读性和一致性
- 遵循了团队的代码风格规范
2026-08-11 12:33:34 +08:00
hjhan cbe5b58577 refactor: 新增 DirectionRatio 方向因子纯函数, 替换7处三元式
多空方向(PositionType==Long?1:-1)和收付方向(xxx==收取?1:-1)
在两个上帝类里重复 15+处, +1/-1 写反是常见 bug 源。

新增 ReturnLegs/DirectionRatio.cs:
- LongShort(positionType): 多头=+1, 空头=-1
- ReceivePay(direction): 收取=+1, 支付=-1

替换:
- SwapEodPositionService: 6处 shortRatio 三元式(含注释1处)
- SwapDealService: 1处 shortRatio + 1处 directionRatio

验证: 编译0错误, 全量511测试7失败(基线一致)。
2026-08-11 12:30:53 +08:00
hjhan d61ff742de refactor(return-leg): SwapDealService 增值税公式替换为 DividendCalc
SwapDealService:1806 inline 的 dividendIn/(1+tax)*(1-tax) 替换为
DividendCalc.AfterTaxRaw(中间计算不四舍五入)。

全库 payment/(1+tax)*(1-tax) inline 公式: 5处(SwapEod 4 + SwapDeal 1) → 0。
全部收敛到 DividendCalc.AfterTax / AfterTaxRaw。

验证: 编译0错误, 全量507测试7失败(基线一致)。
2026-08-11 11:18:56 +08:00
hjhan 946cbbb66d refactor: InterestLeg 改名 FundingLeg(业界标准 Financing Leg)
命名: InterestLeg→FundingLeg, 消除'interest'与通用'利息'歧义。
Funding精确表达'融资成本'(客户付券商的杠杆成本 spread+FR007)。

改名清单:
- IInterestLegStrategy → IFundingLegStrategy
- InterestLegStrategyFactory → FundingLegStrategyFactory
- InterestLegStrategyTest → FundingLegStrategyTest
- 命名空间 InterestLegs → FundingLegs
- 目录 InterestLegs/ → FundingLegs/
- SwapDealService 引用同步更新

验证: 编译0错误, 全量503测试7失败(基线一致)。
2026-08-11 10:52:04 +08:00
hjhan 82fb5e5916 refactor(margin): SwapDealService 5处保证金判断收敛到 MarginModes
把 5 处 inline 的 InterestModeEnum==5||6 判断改为 MarginModes.Contains:
- :99 前端参考计算分流(MarginLegs vs InterestLegs)
- :680 ResolveInterestLegPositions 持仓对齐
- :716 ResolveInterestLegPositionsAsOf 未来持仓 LINQ Where
- :722 ResolveInterestLegPositionsAsOf if 判断
- :2023 平仓 SwapMarginAmount 汇总
- :2441 UpdateInitalPosition 平仓递减

仅剩 CalcNotionalByMode 的 case 标签(switch case 须常量,无法用 Contains)。
SwapDealService 内 InterestModeEnum.初始预付金/追加预付金 引用: 10处→2处(case标签)。
验证: 编译0错误, 全量489测试7失败(基线一致)。
2026-08-11 10:30:44 +08:00
hjhan a868f3eb15 refactor(margin): 抽取 FlipMarginDirection 保证金方向翻转方法
2处 inline 的保证金方向翻转(收取↔支付)抽成独立方法 FlipMarginDirection。
保证金利息是券商付给客户, 方向与融资腿相反。

- GetInterests 路径(854行): if(5||6) → MarginModes.Contains + FlipMarginDirection
- 衡泰路径(1934行): 同上, 保留 _closePosiNotionalValue=0

判断从 InterestModeEnum==5||6 改为 MarginModes.Contains(统一口径)。
纯提取, 行为零变化。
验证: 编译0错误, 全量489测试7失败(基线一致)。
2026-08-11 10:27:18 +08:00
hjhan ae4af111f3 refactor(margin): 抽取 ResolveMarginOrginPv 保证金维度重映射方法
把 InitSwapDealInterest 里 8 行 inline 的 orginPv 重映射逻辑抽成独立方法
ResolveMarginOrginPv(position, preEodPosition, fallback)。

保证金腿被迫走融资腿差分公式(dynomicPrincipal = TdPrincipal + posi - orginPv),
但 orginPv 对融资腿是'交易名义本金',对保证金腿须对齐到'保证金余额',
否则维度不匹配算出巨负值。提取后 InitSwapDealInterest 更干净,
且新方法待迁入 Margin 模块。

判断从 InterestModeEnum==5||6 改为 MarginModes.Contains(统一口径)。
纯提取, 行为零变化。
验证: 编译0错误, 全量489测试7失败(基线一致)。
2026-08-11 10:23:22 +08:00