Bug①利息默认值偏大: 互换结清后eod的InterestProfitSum(待实现)没归零 (=RealizedInterest=77.26), 导致后续平仓默认值=77.26(本该0)+增量→偏大。 6-29收盘后三天对比坐实: 6-30默认83.70, 7-1默认90.14, 均含已实现的77.26。 根因 eod公式cs:829 对互换场景扣减失效(TdInterestIncome≈TdCloseInterest抵消)。 Bug②收益结算审核后状态卡死: ApproveSwapTrade(cs:1525)漏ExerciseDate判断, 到期互换审核后状态退回'确认成交'(CloseMethod默认0≠全部平仓1), 导致EodCheckMaturityTrade一直阻止收盘。引入点083848fb(2025-05-28)。 - 新增利息偏大分析文档(根因/修复方案A扣RealizedInterest/B eod归零) - 新增审核状态卡死分析文档(根因/修复补ExerciseDate判断) - 新增SwapPartialUnwindInterestDefaultTest(Step0探查/Step0b诊断/Step0d三天对比)
5.4 KiB
互换收益结算审核后状态卡死、阻止收盘问题分析
本文分析到期互换"收益结算(互换)审核通过后,交易状态不流转,导致收盘到期检查一直阻止"的问题。 成文于 2026-07-01。
一、问题概述
复现场景:一笔互换交易到期日(如 2026-06-29),用户在界面做"收益结算(互换)"并审核通过,但:
- 交易状态没变成"已到期"或"已平仓",仍是"确认成交"
- 不管做多少次收益结算+审核,状态依旧不变
- 收盘报错:
[检查当日到期交易]当日到期的场外交易未全部操作完成,请操作完成后再执行收盘操作:收益互换 GLMS-20260427-0001
二、根因:审核路径 ApproveSwapTrade 漏了到期日判断
代码对照(铁证)
免审核路径 SwapIncome(正确,基线就有) — SwapDealService.cs:1481-1485:
if (td.ExerciseDate <= unwindData.ValueDate) // 判断到期
{
td.Notional = 0;
td.StockEqvNotional = 0;
td.TradeStatus = "已到期"; // ✅ 到期 → 已到期
}
审核路径 ApproveSwapTrade(漏了) — SwapDealService.cs:1525-1538:
if (swapEvent.unwindData.CloseMethod == (int)CloseMethodEnum.全部平仓)
td.TradeStatus = "已平仓";
else
{
td.TradeStatus = ConsTrade.确认成交; // ❌ 到期互换落到这里!没有 ExerciseDate 判断
td.HasPartialUnWind = 1;
}
ApproveSwapTrade 完全没有 ExerciseDate 到期判断,而互换操作的 CloseMethod 又是默认值 0(InitIncome cs:216 从不设它)→ 0 ≠ 全部平仓(1) → 走 else → 状态退回"确认成交"。
为什么互换必然走 else 分支
CloseMethodEnum:Unknown=0 / 全部平仓=1 / 部分平仓=2- 互换初始化
InitIncome(cs:216-292)从不给unwindData.CloseMethod赋值 → 默认 0 - 只有平仓路径(
InitUnwind/InitLongShortUnwind)才设CloseMethod=全部平仓 - 0 ≠ 1 → 必走 else → 退回"确认成交"
收盘检查为什么一直阻止
EodCheckMaturityTrade.cs:27-33(到期检查):
var predicate = PredicateBuilder.Create<trade>(t =>
t.ExerciseDate >= startDate && t.ExerciseDate <= settleDate // 今日到期
&& t.TradeStatus == ConsTrade.确认成交 // ★ 状态还是"确认成交"
&& t.SettlementFlag != 1);
判断口径只看 TradeStatus。TradeCompleteStatus = { 已到期, 已执行, 已平仓 }(ConsTrade.cs:33)。审核后状态停在"确认成交"(不在完成列表里)→ 永远命中 → 永远阻止。
三、关于"提交数量但审核时数量0"
用户还反馈"提交了数量但审核时候数量0"。经查:
InitIncome(cs:216-292)从没给unwindData.CloseQty赋值(对比InitUnwindcs:90 有赋值)→CloseQty默认 0SaveSwapDeal(cs:1597)的item.Quantity = unwindData.CloseQty被eventType == 平仓条件挡住 → 互换(3)不执行
但这不是核心问题——核心是状态没流转。互换的 Quantity 语义与平仓不同(互换是收益结清,不是按数量平仓),数量为 0 可能是设计如此。状态卡死才是阻止收盘的直接原因。
四、引入时间(git blame)
| 路径 | 提交 | 时间 | 说明 |
|---|---|---|---|
SwapIncome 到期判断(正确) |
f9d8a256 |
2024-05-09 | 山证基线,一直有 |
ApproveSwapTrade 状态分支(漏判断) |
083848fb |
2025-05-28 | 审核流程重构,漏同步 ExerciseDate 判断 |
083848fb(2025-05-28)重构审核流程时把状态分支改成 CloseMethod==全部平仓 判断,但忘了同步 SwapIncome 里的 ExerciseDate 到期判断。
五、涉及文件
| 文件:行号 | 问题 |
|---|---|
SwapDealService.cs:1525-1538(ApproveSwapTrade) |
漏 ExerciseDate 判断,到期互换退回"确认成交" |
SwapDealService.cs:1481-1485(SwapIncome) |
正确对照(有到期判断) |
SwapDealService.cs:216(InitIncome) |
不设 CloseMethod → 默认 0,必然走 else |
EodCheckMaturityTrade.cs:27-33 |
阻止收盘的检查(只认 TradeStatus) |
ConsTrade.cs:33 |
TradeCompleteStatus 定义(确认成交不在内) |
六、修复方向(最小改动)
在 ApproveSwapTrade(cs:1534-1538)的 else 分支补到期判断,与 SwapIncome 对齐:
else
{
// 补:到期互换应设为"已到期"(与 SwapIncome cs:1481 对齐)
if (td.ExerciseDate <= swapEvent.unwindData.ValueDate)
{
td.Notional = 0;
td.StockEqvNotional = 0;
td.TradeStatus = "已到期";
}
else
{
td.TradeStatus = ConsTrade.确认成交;
td.HasPartialUnWind = 1;
}
}
改后到期互换审核通过 → 状态正确变成"已到期" → 收盘检查通过。
七、团队决策问题
- 互换的 CloseMethod 是否应该设值:当前
InitIncome不设 CloseMethod(默认 0),是否应在互换初始化时根据是否全额结清设为"全部平仓"?这会影响ApproveSwapTrade走哪个分支。 - 互换 Quantity=0 是否符合预期:
InitIncome不设 CloseQty,导致互换 flow_event 的 Quantity=0。需业务确认这是设计意图还是遗漏。 - 历史卡住的交易怎么处理:GLMS-20260427-0001 等已卡住的交易,是否需要手动改 TradeStatus 为"已到期"才能继续收盘?