Commit Graph
71 Commits
Author SHA1 Message Date
张名锐 0755233ab6 fix(swap): 统一处理【名义本金】数值精度舍入问题
- 在多个位置添加 Math.Round 函数确保金额计算精度
- 新增 NormalizeNotionalValues 方法统一处理 UnwindData 中的名义本金舍入
- 修复交易平仓时 StockEqvNotional 扣减计算的精度问题
- 解决持仓数据 PosiNotionalValue 的精度舍入处理
- 修复前端页面显示格式化问题
- 添加单元测试验证名义本金舍入逻辑正确性
2026-07-22 13:05:49 +08:00
hjhan e7c6e432e1 test: 修复陈旧 golden 与恢复 Step1 Ignore
golden_互换结清后待实现归零.json 的 RealizedPnl 0->8.219(f4ffe710 修复后未重录,8.219为正确值); SwapReEodDeleteManualCashRecordTest.Step1 恢复 [Ignore](与类头契约一致,避免污染入库 golden)。
2026-07-21 15:28:17 +08:00
hjhan 985162242d feat(greeks): 新增 risk-factor bump 计算与注册
ParameterBase.Clone() 保留运行时类型深拷贝; ValueCalculator 两个薄接入方法; GreeksBumpCalculator/GreeksRiskFactor 引擎。加法性重定价桥,不动现有定价输出。
2026-07-21 15:28:15 +08:00
hjhan a179675efd refactor(swap-deal): 前端盈亏校验抽为 SwapFrontendPnlValidator
BuildFrontendValidationDiffs/FrontendPnlDiff/AddDiffIfOverThreshold 从2043行 SwapDealService 移至新文件,本文件仅留调用+日志。纯位置迁移,零行为变更。
2026-07-21 15:28:14 +08:00
hjhan a153a1d1a6 refactor(swap-eod): 抽出期末头寸纯函数并放宽可见性
SetFixedLegRealizedPnl 替换4处复制粘贴(清理L1296双分号); NormalizeInterestSignForReport 替换16行内联报表分支; CalculateWeightedMarginInterest private->public static。均为纯静态无实例依赖,配套无库单测锁定行为。
2026-07-21 15:28:13 +08:00
hjhan 494a7a06cc test(swap): 修正平仓利息复现用例标的代码为 2500002.IB
- 将 GLMS20260703CloseInterestTest 中 UnderlyingCode 由 511160.SH 更正为真实标的 2500002.IB
- 核心复现用例(复利真实FR007→-13667.85 / 单利→-13668.02)保留, 作为代码级验算样例
2026-07-20 18:41:16 +08:00
hjhan 98bfe8fb5d test(swap): 增加 GLMS-20260703-0002 平仓利息复现用例(真实FR007+真实函数)
- 调用系统 GetInterests 真实路径(复利/单利两条), 用截图中的真实 FR007 定盘值
  在 07-06/07-13 两个重置点真实取数, 复现系统值 -13,667.85 与业务实际值 -13,668.02
- 断言两者差异 0.17 元来自复利第二段本金并入
- 仅保留调用系统函数的断言, 不内嵌手算, 验算见 GLMS20260703_平仓利息验算.xlsx
2026-07-20 18:31:19 +08:00
hjhan 42602055de test(eod): 为 FR007 入库 UnderlyingId 守卫补纯函数单测
- EodPriceService 抽出纯函数 ResolveUnderlyingIdForCode(行为不变),SyncUnderlyingIdFromCode 调用之,便于无库单测
- 新增 EodPriceUnderlyingIdGuardTest:7 用例全过,覆盖空代码/标的不存在维持原值、已一致不改动、FR007 错行(511160.SH=2173889、159111.SZ=2173890)校正为 2170838、非错配不互相覆盖
- 对应根因:GLMS-20260701 FR007 价格行 UnderlyingId 错挂导致网页查得到/结算查不到
2026-07-17 11:24:11 +08:00
hjhan e261137c1d fix(swap): 修复多次部分平仓 ClosePercent 口径(A占期初→B占剩余)转换缺失
- SwapDealService.ApplySwapTrade 入口补 ToRemainingClosePercent(A→B) 转换,与 SwapUnwind 保持一致;缺此转换导致 SaveSwapDealInternal 的 B→A 还原出错(GLMS-20260701-0006:第二次部分平仓50%被错误还原为32.5%)
- unwindSwapTrade.js 改用 swapCalc.calcCloseQtyByOriginalPercent 的 roundHalfAwayFromZero,规避 JS 浮点精度偏差(32500000*(0.5/0.65) 应为 25000000)
- SwapUnwind.cshtml 引入 swapCalc.js
- 新增 ApplySwapTradeClosePercentBugTest 回归测试;补充 swapCalc.test.js 精度用例与 GLMS20260701DbDiagnoseTest 诊断测试([Ignore])
2026-07-17 10:59:18 +08:00
张名锐 415539704d fix(swap): 修复债券结息价差盈亏计算逻辑
- 将价差盈亏计算从使用CloseNotionalValue改为使用PositionQty和ContractSize
- 更新前端JavaScript代码中的计算公式,按持仓数量和合约乘数计算价差盈亏
- 在UnwindInput模型中添加PositionQty和ContractSize字段
- 修正后台计算服务中的数据映射逻辑
- 添加FC_009测试用例验证债券价差按数量计算的正确性
- 更新现有测试用例的输入参数以匹配新的计算方式
2026-07-17 10:58:31 +08:00
张名锐 f37a5b997c Merge remote-tracking branch 'origin/glms/feature/1.4.2' into glms/feature/1.4.2 2026-07-17 10:02:18 +08:00
张名锐 ce0908c53b fix(swap): 修复浮动端已实现盈亏计算逻辑
- 添加了SetFloatingRealizedPnl方法统一计算已实现盈亏
- 已实现盈亏现在正确包含盯市、分红和费用三个组成部分
- 修复了多处RealizedPnl计算错误,确保数值准确性
- 为DF_008测试场景添加了专门的单元测试验证
- 移除了重复的盈亏计算代码,提高代码可维护性
2026-07-17 10:02:06 +08:00
hjhan 21b86c871f test(trade): 补 23108016 互换/期权本次名义本金分支单元测试
抽 CalcSwapCloseNotionalFromEventData 和 CalcOptionCloseNotional 为 public static 纯函数, BuildTriggerContext 了结场景改为调用这两个函数. 新增 13 个单元测试覆盖: 互换分支 EventData null/空/非法JSON/正数/负数/零 容错与绝对值, 期权分支 两者null/单边null/正数相乘/负数容错 绝对值语义. 重点锁定静默 catch 容错契约, 防止 EventData 异常时名义本金变 0 绕过审批阈值.
2026-07-17 08:45:49 +08:00
hjhan 2395aa9856 test(swap): 补 6676b625 掉期保证金利息方向反向单元测试
抽 CalculateSwapRealizedPnl 为 public static 纯函数(用 ConsTrade.InterestMarginModels 替代实例 marginTypes), 新增 8 个单元测试覆盖: 非保证金腿收取/支付原向, 保证金腿(初始预付金/追加预付金)收取/支付反向, 完整 5 字段汇总两种方向, RealizedInterest=0 边界. 锁定 6676b625 修复的保证金腿利息方向反向契约, 防止后续误改回归.
2026-07-17 08:40:19 +08:00
hjhan 96a64ae82d test(swap): 修复 GLMS20260701DbDiagnoseTest 编译错误并加 2.3c 模拟 API 流程
L208-209 Convert.ToDecimal 修复 double?  decimal 隐式转换错误(CS0266)和非 nullable double ?? 0 错误(CS0019). 新增 2.3b 直接调用 ResolveInterestLegPositions 验证 Clone 行为, 2.3c 模拟 controller 完整流程(ToRemainingClosePercent + GetUnwindInterests) 等价于 HTTP API. 该测试带 [TestCategory(DbDiagnose)] 标记不进 CI, 作为可重复手动诊断工具保留.
2026-07-17 08:24:00 +08:00
hjhan 43679393bb test(swap): 补 e2fb456b InitUnwind 默认 ClosePercent 单元测试
抽 CalcDefaultInitClosePercent 静态纯函数(PosiNotionalValue/NotionalValue), InitUnwind L270 改为调用此函数, 新增 6 个单元测试覆盖: 未平仓/已平30%/除零保护/GLMS-20260701-0013 真实快照/与 ToRemainingClosePercent 联动自洽不变式/全平完边界场景. 填补 e2fb456b 提交的测试覆盖缺口.
2026-07-17 08:23:50 +08:00
hjhan 238824f4f4 fix(预付金返回/平仓比例): 恢复占期初语义并在后端入口统一转占剩余
之前提交 4f46e263 误将平仓比例改为占剩余持仓(Definition B)语义,与需求不符。
正确需求:每次打开平仓/提前终止均基于最开始原始名义本金(Definition A),
但每次返回的预付金金额需正确(后端按占剩余计算)。

- SwapDealService: 新增 ToRemainingClosePercent/ToOriginalClosePercent 双语义转换;
  SwapUnwind 入口把前端传来的占期初(A)转为占剩余(B),全计算链公式不动;
  SaveSwapDealInternal 序列化前还原为 A 落库,事件列表读取即显示占期初比例。
- SwapTrade2Controller.GetUnwindInterestList: 接收期初/剩余名义本金,预览利息按 B 计算。
- unwindSwapTrade.js: 回退为占期初语义(ClosePercent/平仓名义本金用 NotionalValue,
  oriClosePercent=剩余/期初),提前终止同理。
- 单测 UW_007/008 覆盖 A→B 全平/部分平仓判定。

不影响收盘后盯市/估值(仅平仓入口与展示口径)。
2026-07-16 19:54:26 +08:00
hjhan a17cdc2b74 fix(期权到期): 债券标的到期取价补查中债估值表,并标注债券净/全价映射不一致(Layer2)
- EodPriceQueryService 新增 TryGetSettlementEodPrice:债券走中债估值、期货/股票走原路径
- 单笔 tradeExpireInner + 批量 MultipleTradeExpireConfirm 改用统一方法,修 GLMS-20260715-0002 债券期权到期报'结算价未找到'
- 移除批量路径未初始化的 EodPriceProvider(对债券无效且有误导性的 footgun)
- Layer2:标注 EodPriceProvider.Initialize 与 GetBondPrice 债券 ClosePrice/SettlePrice 净全价定义相反,待统一(不改逻辑)
- 新增白盒单测覆盖债券标的到期取价(3用例 DB驱动,均通过)
2026-07-16 14:41:44 +08:00
hjhan 3c9d22f60d test(swap): 浮动腿 fpositions 用 orig 不影响预付金结果 — 诊断测试
验证 GLMS-20260701-0008 的预付金 bug 修复无需触碰浮动腿(fpositions / posiLong/posiShort):

- 本 deal 利息腿仅 mode 9(标的期初全价) + mode 5(初始预付金),无「多头/空头存续名义本金」腿。
- CalcNotionalByMode 中 posiLong/posiShort 仅在「多头/空头存续名义本金」分支被消费(L709-716),
  mode 9/5 均不依赖之;故 fpositions 用 orig 100M 不影响预付金「返回预付金」计算。
- 诊断用例用反射调 private CalcNotionalByMode 实证:mode9 closePrincipal=posiNotional×比例、
  mode5 closePrincipal=实时腿 Fix×比例,均与 posiLong(orig 100M) 无关;并证明若存在
  多头存续名义本金腿则 orig 会算错(10M vs 正确 6.89M)——普通收益互换的潜在同类 bug,本 deal 不触发,属修复范围外。

不改动任何生产代码。3 个诊断用例全过。
2026-07-16 13:00:21 +08:00
hjhan d51dd8a60b test(swap): 回归测试对齐 GLMS-20260701-0008 四次部分平仓快照
生产 deal 又平仓两次(共四次),实时预付金腿 InterestPrincipalFix 由 73,260
降至 66,813.12(4 次返还 9,900+15,840+3,663+2,783.88=32,186.88;
99,000−32,186.88=66,813.12,与 dev 库实时腿完全勾稽)。

- SwapUnwindPrepayOrigVsRealBugTdd:RemainingFix 常量 73,260→66,813.12;
  头部注释同步为四次平仓;新增 GLMS20260701_四次部分平仓_LiveSnapshot 用例,
  把 4 次平仓真实数据硬编码为忠实回归(日后该 deal 再平仓需同步更新)。
- 纳入此前遗漏提交的连库诊断测试 GLMS20260701DbDiagnoseTest.cs
  (使用 DbContextFactory 取连接串,无硬编码密码,安全)。

验证:dotnet test --filter SwapUnwindPrepay → 26 passed(19+7)失败 0;
YLErpDAL / UnitTestProject 编译通过(仅既有警告)。
2026-07-16 11:09:47 +08:00
hjhan 24266cfad4 fix(swap): 多次部分平仓预付金返还默认显示初始值(GLMS-20260701-0008)
根因: GetUnwindInterests 利息腿迭代源取 origPositions(IsInitial=1),
其预付金腿 InterestPrincipalFix 恒为初始值(99000); 而"当前剩余本金"(73260)
存于实时腿 realPositions(IsInitial=0)。CalcNotionalByMode 与 InitSwapDealInterest
均读 position.InterestPrincipalFix, 导致多次部分平仓后平仓页默认按初始本金计算
返还。首次平仓 orig==real 掩盖了 bug, 故此前"只修好一次部分平仓"。

修复: 抽纯函数 ResolveInterestLegPositions(orig, real)——迭代源仍用 origPositions
(保留 orig.id -> eod_swap_position.PositionId 的日终匹配, 全库 eod 均按 orig.id 归档,
换 realPositions 会破坏 preEod 匹配导致利息重算错误), 仅对预付金腿(初始5/追加6)
Clone 覆盖 InterestPrincipalFix 为实时腿剩余本金; real.PositionId == orig.id 精确
1:1 关联; orig==real 时返回原始腿本身不克隆。

测试: 新增 SwapUnwindPrepayOrigVsRealBugTdd(6 用例)直接单测该纯函数, 覆盖既有
19 个用例(均绕过 orig/real 选择)的盲区。dotnet test --filter SwapUnwindPrepay
25 passed(19+6) 失败 0。
2026-07-16 10:49:12 +08:00
hjhan 7689436364 test(swap): 补 CalcDailySimpleInterest 全腿模式+日终路径覆盖,锁定无更大影响
用户要求确认 CalcDailySimpleInterest 所有调用方(含日终)覆盖完整性。
代码事实纠正:日终(settment=true)走 CalcEodInterest → CalcDailySimpleInterestByEod
(closePercent 硬编码 1m、且该函数从不改写 InterestPrincipal),根本不调用本函数;
本函数唯一真实调用链 = GetInterests(settment=false) → CalcUnwindInterest。
故'含日终'的正确命题是:日终不受此 bug 影响,应有用例锁定该不变量。

新增 CalcByMode 驱动各 InterestMode + 日终路径(rest_days=7, closePercent<1):
- 追加预付金(6)/多头存续(7)/空头存续(8)/合约名义本金规模(2):断言线性 closePrincipal
- 固定值(1):强制 newClosePercent=1,对平仓比例免疫,断言恒=Fix
- 日终 预付金(5)/标的期初全价(9):断言结果恒为线性 closePrincipal(证明 ByEod 正确变体不受影响)
SwapUnwindPrepayPrincipalBugTdd 现 19 绿;SwapModule 全量 196 通过/4 跳过,无回归。
2026-07-14 17:23:33 +08:00
hjhan 9bf3c4b9c6 test(swap): 补非预付金单利腿(标的期初全价)部分平仓回归,实证修复对所有单利腿通用
用户追问修复是否波及非预付金腿。CalcDailySimpleInterest 是所有单利腿
(InterestType=0)的盘中计息通用函数,预付金腿(mode5/6)只是使用者之一。
新增非预付金腿(标的期初全价=9,orginPv 走交易名义本金、不对齐 Fix)用例:
- 50%%→500,000、10%%→100,000、100%%→1,000,000(N=1,000,000)
- buggy 会给 N×closePercent^7,证明非预付金腿此前也中招、修复后统一线性缩放
- 100%% 用例锚定 closePercent=1 时修复前后恒等(零影响于日常计息/全平)
SwapUnwindPrepayPrincipalBugTdd 现 12 绿。
2026-07-14 16:45:27 +08:00
hjhan 83a55dab33 fix(swap): 修复平仓预付金应返还本金被 closePercent^N 指数级缩小
根因:CalcDailySimpleInterest 单利非重置日 else 分支曾把已×closePercent 的
InterestPrincipal 回填给计息基数 tdDynomicPrincipal,使下一个非重置日再乘一次,
累积成 InterestPrincipal = Fix × closePercent^N(N=计息天数),部分平仓时指数级缩小。
100% 因 1^N=1 不显现,故此前漏测。

修复:非重置日与重置日、日终 CalcDailySimpleInterestByEod 三者对齐——
显示本金 InterestPrincipal = 基数×closePercent(只缩放一次),计息基数不缩放。

TDD 铁证(GLMS-20260701-0006, interest_rest_days=7, Fix=9,180,000):
- 修复前 50%→71,718.75(=Fix×0.5^7)、10%→0.918(=Fix×0.1^7)
- 修复后 50%→4,590,000、10%→918,000、100%→9,180,000
新增 CalcUnwindMultiDay helper + 3 用例,SwapUnwindPrepayPrincipalBugTdd 共 9 绿,
SwapModule 全量 186 通过无回归。
2026-07-14 16:33:59 +08:00
hjhan bfcfc74717 test: 平仓预付金bug回归测试贴合生产铁证(精确notional + 不计息断言)
- 客户截图级用例 notional 由反推的 306,191,860.22 更正为生产精确值
  306,191,860.26,使 2*Fix-Notional = -287,820,348.64 与生产 event15997 精确 0 误差。
- 生产该预付金腿三条 swap_flow_event 的 InterestAmount 全=0(债券类预付金腿不计息),
  仅 InterestPrincipal 中招;故客户级/Trade1813 用例改断言 InterestAmount==0、用 rate=0 贴合生产。
- 显式带息加固用例(rate=0.01,验证计息基数也基于保证金本金自身)保留,但注释标明为
  合成场景、非该笔生产的真实症状,避免与生产现象混淆。
- 固化生产双平仓记录佐证:同腿"盘中错(event15997)/EOD对(event15998)"两条平仓,
  恰好印证修复方向(盘中 orginPv 对齐 EOD=Fix)正确。
2026-07-14 14:46:42 +08:00
hjhan f7988e8fd3 fix: 修正预付金腿平仓计息基数 orginPv 维度错误导致应返还本金巨负
平仓(盘中)路径对预付金(保证金)腿将 orginPv 取为整笔交易名义本金,
使公式 dynomicPrincipal=TdInterestPrincipal+posiPrincipal-orginPv 把名义本金
(千万~亿级)当减项扣掉,导致"应返还本金"(InterestPrincipal)与计息基数变成巨负值。

修复:仅对初始预付金(5)/追加预付金(6)腿,将 orginPv 对齐为自身保证金本金
InterestPrincipalFix,与日终(EOD)路径 SwapEodPositionService 中预付金腿
orginPv=InterestPrincipalFix 的既有正确行为保持一致。债券本金腿(模式9)及其它
计息模式仍用交易名义本金,不受影响。

新增 SwapUnwindPrepayPrincipalBugTdd 回归测试(6 用例全绿),含客户截图级
0 误差复现、真实库 Trade1813 复现、多次部分平仓及计息基数断言。
2026-07-14 11:33:15 +08:00
hjhan 0fda5fd1cc fix: 日终价格「数据来源」改为价格行自身语义(人工/系统两档)
移除中债估值细分与操作人姓名显示,按价格行自身 DataSource 列判定:期货/股票读 DataSource(otc-marketdata 同步写系统、手工写人工);债券 update_user 非空→人工、否则→系统。筛选仅人工/系统两选项,异常来源值返回空。ResolveBondDisplaySource 精简为单参纯函数。
2026-07-13 15:42:28 +08:00
hjhan 02624abf75 test: 日终价格回归测试(日期窗口纯单测 + 新增可见性DB集成测试)
- EodPriceService 提取 static ResolveValueDateWindow(reqStart,reqEnd),使日期窗口逻辑可脱离数据库单测

- EodPriceDtoTest 新增4个纯单测:未传日期回退±1年、起止都填今天→仅今天窗口、显式区间原样生效、结束日今天上界为明天;锁定'页面始终5条'根因

- EodPriceGoldenReplayTest 新增 Record_NewRecordVisibility([Ignore]手动跑):直接跑 SearchUnderlyingList,锁定(a)今天+已上市可查 (b)日期0001查不出 (c)未上市查不出

- 构建0错误;EodPriceDtoTest 19通过0失败
2026-07-13 13:37:18 +08:00
hjhan 760ffd62e6 fix: 日终债券来源固定中债估值、人工改动显示操作人并补上传溯源
china_bond_valuation 来源恒为中债估值(聚源仅转发,无人手工维护),不再按 JSID 推断系统/人工;eod 两表仍用自带 DataSource(人工/系统)。债券被人工改动(update_user 非空)时,日终列表来源列显示操作人姓名,否则显示中债估值;操作人经已有 create_user/update_user 列溯源(零 DDL)。手工上传 xlsx 的债券分支补戳操作人,与表单编辑口径一致。配套单测 EodPriceDtoTest / EodPriceGoldenReplayTest + golden JSON。
2026-07-13 11:00:33 +08:00
hjhan 86825cb2a1 test(swap): SwapPositionComposeScenarioTest(4场景)+CopyEodPosition/UpdateEodPosition/SaveCurrentEodInitalPosi走PersistEodSwapPosition seam
借鉴 testable 分支 SwapPositionComposeScenarioTest,基于当前分支 seam 重写4场景:
- SPC_001 首次归档(无前日eod):PosiQuantity=初始持仓
- SPC_002 Copy分支(有前日eod无事件):PosiGrossPrice原样复制
- SPC_003 Update分支(有平仓事件):PosiQuantity=1000-400=600, TdCloseQty=400
- SPC_004 未收盘抛异常(非交易首日无前日eod)

SwapEodPositionService 改动(零行为变更):
- CopyEodPosition/UpdateEodPosition/SaveCurrentEodInitalPosi 的 DbContext.eod_swap_position.Add
  改为调 PersistEodSwapPosition seam(3处),使测试可拦截持久化

利息腿场景(自动互换)因 CalcSwapInterests 参数适配复杂留后续。
SwapModule 177测试全绿(+4),无回归。
2026-07-03 18:37:46 +08:00
hjhan 4fede8339a refactor(test): SwapDealSettlementTest拆分为SwapUnwind/SwapIncome+提取共享stub
按业务拆分原 SwapDealSettlementTest(8场景)为3个文件,提升可读性:
- TestableSwapDealService.cs: 共享stub(7个seam override)+工厂方法(CreateTrade/CreateUnwindData)
- SwapUnwindScenarioTest.cs: 平仓全流程6场景(UW_001~006: 全平/部分平/含预付金/含费价重算/审核/提交)
- SwapIncomeScenarioTest.cs: 结息2场景(SI_001~002: 正常结息/含预付金返息)

命名对齐 testable 分支风格(SwapUnwindScenarioTest),场景前缀改 UW_/SI_ 更直观。
删除 SwapDealSettlementTest.cs(内容已拆分迁移)。
SwapModule 173测试全绿,无回归。
2026-07-03 18:12:52 +08:00
hjhan 99958f9b43 test(swap): SwapDealSettlementTest补部分平仓+含预付金场景(借鉴testable)
借鉴 testable 分支 SwapUnwindScenarioTest.Scenario2,补当前分支缺失的2个场景:

SD_007 部分平仓:CloseMethod=部分平仓 → HasPartialUnWind=1,TradeStatus不变,
  StockEqvNotional/TradeAmount按比例扣减(原100万-50万=50万)
SD_008 含预付金平仓:SwapMarginAmount≠0 → 2条资金流水(平仓费+应付预付金)

SwapDealSettlementTest 现8场景,SwapModule 173测试全绿(+2),无回归。
2026-07-03 18:03:38 +08:00
hjhan b501e21904 test(swap): 启用MergeCompose场景6/7+SwapFlowEventService录制钩子+RecordingSwapFlowEventService
1. MergeComposeScenarioTest 场景6/7 启用(去掉Ignore):
   此前失败是stale binary(未重新编译),重新build后8场景全部通过。
   - 场景6 单条merge无持仓→DealNoPosition创建交易
   - 场景7 两条merge无持仓→创建交易+平仓

2. SwapFlowEventService 补OnBefore/OnAfterMergePageEvent录制钩子(默认空实现):
   - OnBefore: DB查询完成、合成逻辑执行前触发,可捕获输入快照
   - OnAfter: PersistEvents后触发,捕获输出事件
   生产路径为空方法体,零行为影响。

3. 移植RecordingSwapFlowEventService(借鉴testable分支):
   override OnAfterMergePageEvent录制golden文件,备用录制工具。
   内联GoldenFileModel(简单POCO)。FullRecord_WithPreBookingPositions连库测试自动跳过。

SwapModule 171测试全绿,无回归。
2026-07-03 09:38:05 +08:00
hjhan f0ef37b599 test(swap): SwapFlowEventService seam改造+MergePageEventScenarioTest(6场景全绿)
借鉴 testable 分支,只取 seam(排除 PosiPnl/NetSettmentAmount 公式改动和 MergePageEvent 拆分),
填补当前分支对开平仓合成(MergePageEvent)零覆盖的空白。

SwapFlowEventService 新增11个 protected virtual seam:
- FindTrade/FindTradeExtend/FindPositions/FindAndInvalidateFutureEvents
- GetUnderlying/GetNextBusinessDay/ResolvePositionId
- PersistEvents(替代InitEvent内联Add+SaveChanges)/BeginTransaction/CommitTransaction/RollbackTransaction
MergePageEvent/InitEvent: private→protected virtual,内联DB调用替换为seam。
InitEvent 移除 DbContext.swap_flow_event.Add(转入PersistEvents,纯函数化)。
SwapTradeBaseService.UpdateDbOption: public void→public virtual(测试override用)。

新增测试(借鉴testable分支):
- TestableSwapFlowEventService.cs(集中式共享包装类)
- MergePageEventScenarioTest.cs(6场景全绿):
  Scenario1 单条无持仓开仓 / Scenario2 同向追加 / Scenario3 反向全平
  Scenario4 反向部分平+开 / Scenario5 一开一平 / Scenario6 复杂组合

**未借鉴**:SearchPositionFlowEvent的PosiPnl/NetSettmentAmount公式改动(行为变更)、
MergePageEvent拆分ProcessSingleFlow等(非必需重构)。
SwapModule 169测试全绿(+6),无回归。
2026-07-03 09:26:13 +08:00
hjhan 6e9bae94c5 test(swap): MergeRestModeCompose seam调用替换+移植MergeComposeScenarioTest(6/8场景)
借鉴 testable 分支,把 SwapTradeAutoService 的 MergeRestModeCompose/MergeRestModelItem/
DealNoPosition/DealHasPosition 内联DB调用替换为seam调用,生产行为不变。

替换的调用点:
- MergeRestModeCompose: FindActiveSwapTrades/FindTradeExtends/FindActivePositions/GetNextBusinessDayBefore/QueryFloatRates
- MergeRestModelItem: FindClient/GetEtradingRule/FindAssetUnit/FindUnderlying/GetSwapFloatRate/FindFlowEventsForCashCheck/FindValidTrades/SaveChanges
- DealNoPosition: CreateNewSwapTrade/AutoSwapUnwind
- DealHasPosition: AutoSwapUnwind

新增测试(借鉴testable分支):
- TestableSwapTradeAutoService.cs(集中式共享包装类)
- MergeComposeScenarioTest.cs(8场景):
  Scenario1-5/8 通过: 客户/权限/账户/标的校验链 + 空流水返回
  Scenario6/7 [Ignore]: 创建交易路径,DealNoPosition内部实现细节待对齐后启用

SwapModule 163测试全绿(+6),无回归。
2026-07-03 09:20:59 +08:00
hjhan e4a92c93ee feat(swap): 后端只读校验告警-搬FrontendCalcReference+SwapIncome/SwapUnwind插校验
前端保持快速反馈(用户改输入立即算),后端不替代前端,仅做合理性兜底校验。

1. 搬FrontendCalcReference到YLErpDAL/Helpers(测试与生产共用同一份公式,避免分叉)
   - 从FrontendCalcCharacterizationTest提取,改public,8个FC测试搬迁后全绿验证一致

2. SwapDealService新增ValidateFrontendPnL只读校验:
   - 从unwindData.FlowEvents取浮动腿,PosiGrossPrice==0时跳过(避免误报)
   - 调FrontendCalcReference重算,与前端传值逐字段比对
   - 差异>0.01记Logger.Error(带输入快照便于排查)
   - 整体try/catch吞异常,校验自身错误绝不阻断交易
   - SwapUnwind/SwapIncome各插一处(FindTrade后、ExecuteInTransaction前)

不碰NLog配置(告警进Console,生产由运维采集)。SwapModule 157测试全绿。
2026-07-03 09:04:57 +08:00
hjhan 129e4bed0d test(swap): 补ApproveSwapTrade/ApplySwapTrade测试+3个seam(借鉴testable分支)
借鉴 origin/glms/feature/refactor-swap-event-testable 的 SwapUnwindScenarioTest 场景4/5,
补全当前分支缺失的平仓审批流程覆盖。

SwapDealService 新增3个 protected virtual seam:
- FindSwapEvent(查待审核事件)
- FindFlowEventsByEventId(查事件关联流水)
- CloseReCheckSetTrade(前置校验包装)
ApproveSwapTrade/ApplySwapTrade 重构为调seam,生产行为不变。

SwapDealSettlementTest 新增2个测试(SD_005/006,共6个):
- SD_005 ApproveSwapTrade 全平仓审核: 反序列化swap_event.EventData→资金流水→TradeStatus
- SD_006 ApplySwapTrade 提交审核: CloseReCheck前置校验+SwapRealizedPnL=SwapCloseAmount

融合策略: 保留当前分支SwapIncome资金流水断言独特价值(SD_001/002),
借鉴testable分支审批流程覆盖(SD_005/006),不二选一。
SwapModule 157测试全绿,无回归。
2026-07-03 08:30:36 +08:00
hjhan cf0d965ff8 test(swap): 前端计算逻辑特征化测试(8场景含用户可变输入)
用C#忠实重写前端JS公式作参考实现(FrontendCalcReference),冻结前端计算行为,
为后端校验/重算兜底提供金标准(注:前端保持快速反馈,不搬到后端)。

8个场景覆盖用户可变输入分支:
- FC_001-005 平仓页: 默认值/改标的价格/改平仓数量/改利息金额/非债券空头方向因子
- FC_006-008 结息页: 全量结算/改标的价格/含利息腿与预付金腿总额

参考实现内部用规范命名(EntryDirtyPrice/ExitDirtyFeePrice/floatRatio/longRatio),
践行《互换价格字段命名规范决策文档》,作命名规范活样板。
2026-07-03 08:22:55 +08:00
hjhan a207927f1b test(swap): SwapDealService抽7个virtual seam + 手动结算内存测试
攻克SwapIncome/SwapUnwind零单元测试覆盖的盲区(此前仅DBRecording,CI不跑)。

SwapDealService补7个protected virtual seam(参考已验证的SwapEodPositionService模式):
- FindTrade/AddClientCash/SaveSwapDeal(原private改protected virtual)
- SaveAllChanges/ExecuteInTransaction/CallSaveSwapTradeClientCash/TriggerRealtimeSwapPosition
生产实现行为不变,SwapIncome/SwapUnwind/ApproveSwapTrade重构为调seam。

新增SwapDealSettlementTest.cs(4个内存测试):
- SD_001 SwapIncome资金流水金额=-SwapRealizedPnL
- SD_002 含预付金返息双流水
- SD_003 SwapUnwind全平仓持仓归零+状态变更
- SD_004 DealFloatPosition含费价重算(后端唯二真做计算处)
红灯验证通过(改错金额符号测试立即失败)。
2026-07-03 08:22:43 +08:00
hjhan f88da59874 日期 事件 期望行为
D1=2026-01-06	无事件(Copy 分支)	TdPosiDividend=1天增量,PosiDividendSum=增量
D2=2026-01-07	部分平仓 30%(EventType=2, Quantity=300, DividendIn=按比例)	PosiQuantity→700,TdPosiDividend按(700)算,PosiDividendSum递推
D3=2026-01-08	无事件	增量按剩余700算
D4=2026-01-09	互换结算(EventType=3, DividendIn=部分待实现金额,不扣持仓)	PosiQuantity不变=700,TdCloseDividend=互换DividendIn,PosiDividendSum递推
D5=2026-01-10	无事件	继续按700累积
D6=2026-01-11	全平(EventType=2, Quantity=700, 全部待实现作DividendIn)	PosiQuantity→0,PosiDividendSum=0(else分支)
2026-07-02 12:59:16 +08:00
hjhan 0910969e3f fix(swap): PosiDividendSum精度差异修复(GLMS-20260105-0007)
问题:互换后PosiDividendSum=-89999.64(应为-90000),差0.36元。
根因:UpdateEodPosition cs:1635从头重算PosiDividendSum
  (Round(全程,2)-已实现),与CopyEodPosition逐天递增
  (每天Round(增量,2)累积)产生舍入误差累积。

修复:cs:1635改为递增模式
  PosiDividendSum = eod.PosiDividendSum + TdPosiDividend - TdCloseDividend
  与InterestIncomeSum的递增模式一致,避免从头重算的精度差异。

验证(golden录制/回放TDD):
- 真实数据录制GLMS-20260105-0007的eod快照+输入数据
- 回放重新调UpdateEodPosition:修复前=-89999.64(红灯),
  修复后=-90000(绿灯,精确匹配)
- 121个测试全通过,无回归
2026-07-02 10:47:23 +08:00
hjhan 0e045d89db test(swap): GLMS-20260105-0007分红精度差异golden红灯(复现0.36差异)
用真实测试库数据录制golden,回放复现0.36精度差异:
- 互换前 PosiDividendSum=-90400,互换支付-400
- 期望 PosiDividendSum=-90000,实际=-89999.64,差异0.36
- 回放断言失败(红灯坐实bug)

根因:UpdateEodPosition cs:1635从头重算PosiDividendSum
(Round(全程,2)-已实现),与CopyEodPosition逐天递增
(每天Round(增量,2)累积)产生舍入误差。

录制方法Record_FromRealDB标[Ignore],手动跑生成golden。
回放方法Replay_VerifyPrecisionDiff进CI,当前红灯(期望=-90000,实际=-89999.64)。
2026-07-02 10:28:23 +08:00
hjhan d11e332ff3 test(swap): golden录制/回放基础设施+DealInterests场景
引入golden回放框架,补充合成测试的精确值验证缺口。

新增GoldenReplayFramework.cs:
- GoldenScenarioModel: 通用golden数据模型(输入+期望输出)
- GoldenAssert: 精确字段对比(容许指定位数误差),逐字段验证

新增DealInterestsGoldenReplayTest.cs:
- Record_AllGoldenScenarios: 生成golden JSON(标Ignore,手动跑)
- Replay_AllGoldenFiles: 读golden重跑+精确对比(进CI)

2个golden场景:
- 互换结清后待实现归零: InterestIncomeSum=0.82191780822(精确到11位)
- 普通日归档递增(回放暂不支持自动重放,留后续)

golden文件持久化到Resources/GoldenFiles/DealInterestsGolden/

价值:重构时如果任何字段变了(哪怕第8位小数),回放立刻失败。
守恒测试验证大方向对,golden验证精确值对。

验证: 119+1(回放)=120全通过。
2026-07-02 09:37:18 +08:00
hjhan 64f0c82bbb test(swap): 多步生命周期守恒测试(数学不变量验证)
用数学守恒约束验证利息在多次操作后不丢失/不重复。
这类测试的价值:不管代码怎么改,只要守恒不成立就报错。

4个守恒场景:
- MS_001: 连续收盘10天,每天增量之和=10天总利息(防指数增长/丢失)
- MS_002: 半平50%+后续全平,两次利息都>0不为负(防consumedInterest扣过头)
- MS_003: 互换结清后再平仓只有增量(单利靠eod归零传递)
- MS_004: 多次互换(第5天+第10天)+最终平仓(第15天),
  累计consumed+平仓=全程复利利息(守恒不变量)

StubDealService支持构造注入consumedInterest和floatRate。
CalcEodInterest返回(当日增量,全程累计)元组,正确区分TdInterestAmount和InterestAmount。

验证: 115+4=119全通过。
2026-07-02 09:20:41 +08:00
hjhan fb0ab3e948 test(swap): ClearSwapPositions重收盘清理合成测试(78751f0a覆盖)
验证78751f0a的核心修复:重收盘清理自动互换资金记录时排除手动互换。

4个测试场景:
- CSW_001: 自动互换资金记录被正确选中删除
- CSW_002: 手动互换资金记录被排除(manualClientCashIds排除逻辑)
- CSW_003: 已在excludedClientCashIds中的记录不重复选中
- CSW_004: 预付金返息记录也参与清理(手动被排除)

Stub override 3个虚方法注入内存数据:
FindFlowEventsByEventIds/FindManualClientCashIds/FindClientCashRecords

至此4个最近修复全部有合成测试覆盖:
①复利consumedInterest() ②eod InterestIncomeSum()
③重收盘误删资金() ④分红重复计算()
验证: 111+4=115全通过。
2026-07-02 09:11:38 +08:00
hjhan eaa4ed735a test(swap): DealFloatPositions分红场景测试(盯市盈亏+分红+增值税)
扩展StubEodService override 3个外部依赖虚方法:
- GetUnderlyingPrice: 注入固定标的价格
- GetUnderlyingData: 注入标的缓存(增值税率)
- CalcBondPayment: 注入固定债券付息

新增3个分红场景测试:
- DF_005: CopyEodPosition盯市盈亏=(price-grossPrice)×qty×方向
- DF_006: 分红计算 TdPosiDividend=payment/(1+tax)×(1-tax)
- DF_007: 增值税调整(tax=6%): 100/1.06×0.94≈88.68

覆盖了cf2f8b6e分红重复计算修复所在的浮动腿归档链路。
验证: 108+3=111全通过。
2026-07-02 09:02:43 +08:00
hjhan fee2203d6e test(swap): DealFloatPositions浮动腿归档合成测试(testable迁移)
新增4个浮动腿归档场景测试:
- DF_001: 空持仓返回空列表
- DF_002: 首日无eod走SaveCurrentEodInitalPosi分支
- DF_003: SaveCurrentEodInitalPosi纯计算字段验证(数量/均价/类型)
- DF_004: 有eod无平仓走CopyEodPosition分支(分支选择验证)

DealFloatPositions和子方法无直接DB调用(数据从参数传入),
但CopyEodPosition/UpdateEodPosition依赖外部数据源
(DataCacheProvider/UnderlyingCodePrice/BondPaymentService),
值验证需后续加接缝。当前覆盖分支选择+首日初始化值验证。

验证: 104+4=108全通过。
2026-07-02 08:49:50 +08:00
hjhan 7f0529bbed test(swap): ComposePage流水合成持仓合成测试(testable迁移)
新增4个ComposePage场景测试(参考testable分支ComposePageScenarioTest):
- CP_001: 空事件→不创建持仓
- CP_002: 单条开仓→创建1条持仓,均价=开仓价
- CP_003: 两条开仓(同标的)→加权均价
- CP_004: 开仓+平仓→平仓扣减数量+产生资金记录

改动(SwapEodPositionService.cs):
- SaveEodPosition改为protected virtual(可override绕过UpdateSwapPosition连库)
- SaveEodPosition内部DbContext.Add替换为PersistEodSwapPosition

StubEodPositionService override 12个虚方法:
FindTrade/FindTradeExtend/FindEodSwapPositions/FindSwapPositions/
FindEodSwap/AddSwapEvent/AddClientCash/SaveEodSwapRecord/
ClearSwapPositionsForCompose/PersistEodSwapPosition/SaveAllChanges/
GetCurrencyRate + SaveEodPosition

验证: 100+4=104全通过(T0/T1=89+DealInterests=7+ConsumedInterest=4+ComposePage=4)。
2026-07-02 08:39:39 +08:00
hjhan f501ee95a7 test(swap): 复利consumedInterest扣除合成测试(补c6adb3bb覆盖缺口)
c6adb3bb加的复利consumedInterest扣除此前无任何测试覆盖。
现在用4个合成测试完整验证:

改动(SwapDealService.cs):
- GetConsumedInterest加virtual关键字(测试可override注入内存值)

新增(ConsumedInterestScenarioTest.cs):
- StubSwapDealService override TryGetFloatRate+GetConsumedInterest
  (模仿T0/T1的StubSwapDealService模式)
- CI_001: 基线(consumedInterest=0)拿到全程复利利息
- CI_002: 注入consumedInterest后利息=基线-consumed(直接验证cs:793)
- CI_003: 守恒(consumed+利息=基线)×3组不同consumed值
- CI_004: 全部已结(consumed=基线)平仓≈0不为负(防双重扣减)

关键发现:测试必须传settment:false走盘中平仓路径(CalcUnwindInterest),
settment:true(默认)走收盘归档路径(CalcEodInterest)不传consumedInterest。

验证: 89(T0/T1)+7(DealInterests)+4(ConsumedInterest)=100全通过。
2026-07-01 18:40:48 +08:00
hjhan be88442c91 test(swap): 扩展DealInterests测试-多次互换守恒+预付金ratio翻转
新增2个测试场景:
- DI_SWAP_MULTI_001: 攒10天→互换结清→再攒5天→再互换结清
  守恒约束: RealizedInterest+InterestIncomeSum=全程利息(17天)
  验证多次互换结算后利息不丢失/不重复
- DI_MARGIN_001: 预付金腿(初始预付金,marginTypes)互换结清后
  RealizedInterest应为负(ratio翻转:收取→-1→支付方向)
  验证cs:789-793的marginTypes ratio翻转逻辑

验证: 89(T0/T1)+7(DealInterests)=96全通过,无回归。
2026-07-01 18:25:44 +08:00