Commit Graph
374 Commits
Author SHA1 Message Date
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
hjhan 4e67af52e8 refactor(interest): FundingLegRate 瘦身为单一 all-in 利率值对象
固定/浮动腿的区别是取率环节的事,计息只认一个数;以 Fixed/Floating 工厂
取代原 4 字段构造,不再为腿型背负多余字段。
2026-08-12 05:36:01 +08:00
hjhan 0c447ff5ff refactor: 提取融资腿计息领域模型 (AccrualPolicy/FundingLegRate/AccrualState/FundingLegAccrual) - 将 CalcDailySimpleInterestByEod 纯数学下沉至 FundingLegAccrual.AccrueSimpleEod, 消除 double 往返转换, 引入值对象收敛散落参数 2026-08-11 22:37:19 +08:00
hjhan 246392b665 cleanup: 删除 SwapInterestAdapter 半成品, 回退 SwapInterest 未提交改动
SwapInterestAdapter + SwapInterest 增强(annualized/segmentRates/
resetCarryInterest) + 配套测试 是半成品, 有G7/G8已知边界,
且破坏现有测试。按审查建议回退, 作为独立PR后续处理。

当前工作区只剩 BondIndex/RateYield 到期日修复。
验证: 编译0错误, 全量513测试7失败(基线一致)。
2026-08-11 19:23:21 +08:00
hjhan dafc06793e refactor: AddClientCashInCashOut 加 virtual + 修正架构文档
AddClientCashInCashOut 从 public 改为 public virtual,
使测试可 override, 消除 RecordMarginCashFlow 迁出的阻塞。
AddClientCash 已是 virtual, 这是统一两者。

修正 ARCHITECTURE.md: 保证金有余额和返息(非'没有计息基数')。

验证: 编译0错误, 全量513测试7失败(基线一致)。
2026-08-11 17:09:13 +08:00
hjhan 7cea09d976 docs: 新增 SwapModule ARCHITECTURE.md 架构说明
记录重构后的三大限界上下文结构:
- FundingLegs(融资腿) / Margin(保证金) / ReturnLegs(标的端)
- 命名规则(防歧义): Funding/Return/Margin/FloatRate
- Core 层纯函数库(SwapInterest + IIndexFixer)
- 已消除的 inline 重复清单
- 方向因子类型规范(int)
- 待后续改造的接缝预留

不改原有文档(corp-action-*.md 仍准确)。
2026-08-11 17:03:44 +08:00
hjhan 5a7869e0c9 fix(return-leg): PositionValueCalc.ratio 改为 int, 统一方向因子类型
ratio(收取=1/支付=-1)是方向因子, 应该是 int。
和 MtmCalc/DirectionRatio/MarginCalc 的 int 保持一致。

调用点(SwapEodPositionService 4处)原 ratio 是 decimal,
加 (int) 显式转换(值恒为 1或-1, 零精度损失)。

排查确认: 这是唯一的类型不一致, 其他纯函数参数类型均已统一。
验证: 编译0错误, 全量513测试7失败(基线一致)。
2026-08-11 16:59:22 +08:00
hjhan 16c44d3b50 refactor(return-leg): 抽取 PositionValueCalc 持仓价值汇总
SwapEodPositionService 8处 SwapPositionValue inline 公式替换:
- 4处 InterestProfitSum * ratio + PosiProfitSum → PositionValueCalc.Calc(带ratio)
- 4处 InterestProfitSum + PosiProfitSum → PositionValueCalc.Calc(默认ratio=1)

持仓价值 = 利息端收益 + 浮动端收益, 8处副本→1个纯函数。
这是最后一个高性价比的纯函数提取。

验证: 编译0错误, 全量513测试7失败(基线一致)。
2026-08-11 16:50:21 +08:00
hjhan 7e169dd53f fix(margin): 回退 PreviousBalance 简化, 恢复三级 fallback
尝试去掉第2级(TdInterestPrincipal)导致5个保证金测试失败。
根因: 生产 EOD 归档(eod_swap_position(35774))的 InterestPrincipalFix=0,
只有 TdInterestPrincipal 有值。第2级不是死代码, 不可删。

恢复三级 fallback, 补充注释说明每级命中的真实场景。
教训: 不能只看赋值点判断字段是否有值, 必须看实际数据。
验证: 编译0错误, 全量509测试7失败(基线一致)。
2026-08-11 15:46:28 +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 47422b9024 feat(margin): MarginAccount 加计息入口 AccrueInterest
保证金账户现在具备独立计息能力:
AccrueInterest(rate, start, end, boundary, annualDays)
委托 SwapInterest.AccrueSimple(余额×利率×天数/年化)。

保证金利息是券商对客户保证金存款付息, 方向与融资腿相反。
Margin 模块现在完全独立于 CalcNotionalByMode——
有自己的余额管理 + 计息入口, 不依赖融资腿框架。

测试: 3个计息用例(7天/零余额/释放后减半)。
CalcNotionalByMode 的 mode 5/6 case 仍在(下游路径共用),
待 EOD 归档改走 Margin 路径后再删。
验证: 编译0错误, 全量511测试7失败(基线一致)。
2026-08-11 13:03:38 +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 e35a738d2e refactor(funding-leg): 修复2个类名+补DividendCalc注释
采纳命名审查建议(优先级最高的2项):

1. UnderlyingFullPriceLeg → UnderlyingEntryFullPriceLeg
   补齐'期初(Entry)'——建仓时点全价, 非当前全价, 金融上是不同计息口径

2. FixedNotionalLeg → FixedAmountLeg
   去掉误导性的Notional——'固定值'是合同写死的数额, 不一定是名义本金,
   避免与ContractNotionalLeg的Notional概念撞车

3. DividendCalc 补充注释: 业务含义(债券票息+股票分红统一用Dividend)、
   税务口径(还原不含税再扣税)、命名保持理由

验证: 编译0错误, 全量507测试7失败(基线一致)。
2026-08-11 11:24:05 +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 c52d439469 refactor(return-leg): 4处增值税inline公式替换为 DividendCalc
SwapEodPositionService 4处 payment/(1+tax)*(1-tax) inline 替换:
- 1804/1906/2094: Math.Round(...) → DividendCalc.AfterTax(payment, tax)
- 1893: 中间计算(不四舍五入) → DividendCalc.AfterTaxRaw(total, tax)

修复 MtmCalcTest: UnrealizedPnl 第6参数 ratio 从 1m 改为 1(int)。
(1+tax) inline 公式出现次数: 4 → 0。

验证: 编译0错误, 全量507测试7失败(基线一致)。
2026-08-11 11:12:15 +08:00
hjhan 939d6a4271 fix(return-leg): MtmCalc.UnrealizedPnl ratio 参数改为 int
ratio(收取=1/支付=-1)和 shortRatio 一样是 int, 不是 decimal。
1695行 ratio 声明同步从 decimal 改为 int。
C# decimal*int 自动提升为 decimal, 不影响计算结果。

验证: 编译0错误, 全量507测试7失败(基线一致)。
2026-08-11 11:05:55 +08:00
hjhan 963f7a04e5 refactor(return-leg): 新增 DividendCalc 分红增值税后计算 + 修复 MtmCalc 参数类型
DividendCalc: 增值税后票息 = payment/(1+tax)*(1-tax),
原 SwapEodPositionService 4处重复此公式(1804/1894/1906/2095)。
本次先建纯函数+测试, 4处inline替换下次做。

附带修复: MtmCalc shortRatio 参数从 decimal 改为 int
(原代码6处声明中5处是int, 仅1696是decimal, 统一为int)。
1696行 shortRatio 声明同步改为 int。

验证: 编译0错误, DividendCalc 4个测试全过,
全量507测试7失败(基线一致)。
2026-08-11 11:04:08 +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 a26f848287 refactor(return-leg): 抽取 MtmCalc 标的端盯市计算
把 SwapEodPositionService 4处重复的 UnderlyingMarketValue 公式和
4处 PosiMtmPnL 公式抽成 MtmCalc 纯函数:
- MarketValue(price, qty, contractSize, shortRatio): 标的市值
- UnrealizedPnl(price, costGrossPrice, qty, contractSize, shortRatio, ratio): 盯市未实现盈亏

原代码4处副本(1731/1813/1898/2101 + 1741/1814/1899/2102)全部替换。
验证: 编译0错误, 全量503测试7失败(基线一致)。
2026-08-11 10:45:43 +08:00
hjhan 59651b802e refactor(return-leg): QuantityRollforward 改名 QtyRollforward
代码里统一用 Qty 表示数量, 命名更简洁。
文件/类/测试类同步改名: Quantity→Qty。
2026-08-11 10:40:17 +08:00
hjhan 279c640345 refactor(return-leg): 抽取 QuantityRollforward 标的端数量递推
把 SwapEodPositionService:1980 inline 的数量递推公式抽成独立纯函数:
  qty = 上一日终数量 + 开仓 - 平仓 + 公司行为调整

新增第4参数 corpActionDeltaQty(默认0,当前不影响行为),
预留给公司行为改造(送股/拆股/配股)。
原代码硬编码'数量只因交易变动'假设,改造时填入非零值即可。

ReturnLegs/QuantityRollforward.cs: 纯函数, 不依赖任何实例状态
ReturnLegs 测试: 8个(无交易/开仓/平仓/全平/超额/送股/拆股)
SwapEodPositionService: inline 2行 → QuantityRollforward.Calc() 1行

验证: 编译0错误, 全量497测试7失败(基线一致)。
2026-08-11 10:36:38 +08:00