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三天对比)
130 lines
5.4 KiB
Markdown
130 lines
5.4 KiB
Markdown
# 互换收益结算审核后状态卡死、阻止收盘问题分析
|
||
|
||
> 本文分析到期互换"收益结算(互换)审核通过后,交易状态不流转,导致收盘到期检查一直阻止"的问题。
|
||
> 成文于 2026-07-01。
|
||
|
||
---
|
||
|
||
## 一、问题概述
|
||
|
||
**复现场景**:一笔互换交易到期日(如 2026-06-29),用户在界面做"收益结算(互换)"并审核通过,但:
|
||
- 交易状态没变成"已到期"或"已平仓",仍是"确认成交"
|
||
- 不管做多少次收益结算+审核,状态依旧不变
|
||
- 收盘报错:`[检查当日到期交易]当日到期的场外交易未全部操作完成,请操作完成后再执行收盘操作:收益互换 GLMS-20260427-0001`
|
||
|
||
---
|
||
|
||
## 二、根因:审核路径 ApproveSwapTrade 漏了到期日判断
|
||
|
||
### 代码对照(铁证)
|
||
|
||
**免审核路径 `SwapIncome`(正确,基线就有)** — `SwapDealService.cs:1481-1485`:
|
||
```csharp
|
||
if (td.ExerciseDate <= unwindData.ValueDate) // 判断到期
|
||
{
|
||
td.Notional = 0;
|
||
td.StockEqvNotional = 0;
|
||
td.TradeStatus = "已到期"; // ✅ 到期 → 已到期
|
||
}
|
||
```
|
||
|
||
**审核路径 `ApproveSwapTrade`(漏了)** — `SwapDealService.cs:1525-1538`:
|
||
```csharp
|
||
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`(到期检查):
|
||
```csharp
|
||
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"。经查:
|
||
|
||
1. `InitIncome`(cs:216-292)**从没给 `unwindData.CloseQty` 赋值**(对比 `InitUnwind` cs:90 有赋值)→ `CloseQty` 默认 0
|
||
2. `SaveSwapDeal`(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` 对齐:
|
||
|
||
```csharp
|
||
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;
|
||
}
|
||
}
|
||
```
|
||
|
||
改后到期互换审核通过 → 状态正确变成"已到期" → 收盘检查通过。
|
||
|
||
---
|
||
|
||
## 七、团队决策问题
|
||
|
||
1. **互换的 CloseMethod 是否应该设值**:当前 `InitIncome` 不设 CloseMethod(默认 0),是否应在互换初始化时根据是否全额结清设为"全部平仓"?这会影响 `ApproveSwapTrade` 走哪个分支。
|
||
2. **互换 Quantity=0 是否符合预期**:`InitIncome` 不设 CloseQty,导致互换 flow_event 的 Quantity=0。需业务确认这是设计意图还是遗漏。
|
||
3. **历史卡住的交易怎么处理**:GLMS-20260427-0001 等已卡住的交易,是否需要手动改 TradeStatus 为"已到期"才能继续收盘?
|