diff --git a/YLErpDAL/Modules/RiskEngine/测试用例.md b/YLErpDAL/Modules/RiskEngine/测试用例.md index 2eb3b5fe..a268de5b 100644 --- a/YLErpDAL/Modules/RiskEngine/测试用例.md +++ b/YLErpDAL/Modules/RiskEngine/测试用例.md @@ -2180,17 +2180,16 @@ WHERE t.id = @TradeId; ## 19. 规则 8 查询结果排查 SQL -规则 8:支付日为银行间交易日(本地)。用于核对交易平仓窗口的支付日期是否为银行间交易日;若支付日期未落在 IB 日历的非交易日列表中,则命中。 +规则 8:支付日为银行间交易日(本地)。用于核对互换交易-交易查看-交易平仓窗口中本次提交的支付日期是否为银行间交易日;若支付日期未落在 IB 日历的非交易日列表中,则命中。 取数流程: ```text -1. 根据 TradeId 查 swap_flow_event。 -2. 限定 SwapTradeId = TradeId、EventType = 2、DataState <> 0,取平仓事件。 -3. 取 swap_flow_event.PayDate,作为交易平仓窗口的支付日期。 -4. 根据 PayDate.Value.Year 查询 calendar 表中 Country = 'IB' 的银行间日历。 -5. calendar.HolidayJson 存储该年非交易日,格式为 yyyy,MM,dd。 -6. 若 HolidayJson 不包含支付日期对应的 yyyy,MM,dd,则说明支付日期是银行间交易日,命中规则。 +1. 用户从互换交易-交易查看进入交易平仓窗口。 +2. 取交易平仓窗口本次提交的支付日期,即 UnwindData.PayDate。 +3. 根据 PayDate.Value.Year 查询 calendar 表中 Country = 'IB' 的银行间日历。 +4. calendar.HolidayJson 存储该年非交易日,格式为 yyyy,MM,dd。 +5. 若 HolidayJson 不包含支付日期对应的 yyyy,MM,dd,则说明支付日期是银行间交易日,命中规则。 ``` 规则公式: @@ -2202,10 +2201,9 @@ WHERE t.id = @TradeId; 规则字段口径: ```text -支付日期:swap_flow_event.PayDate,对应交易平仓窗口的支付日期 -平仓事件:swap_flow_event.EventType = 2 +支付日期:UnwindData.PayDate,对应互换交易-交易查看-交易平仓窗口的支付日期 银行间日历:calendar.Country = 'IB' -日历年份:calendar.Year = swap_flow_event.PayDate.Year +日历年份:calendar.Year = UnwindData.PayDate.Year 非交易日:calendar.HolidayJson ``` @@ -2216,6 +2214,17 @@ WHERE t.id = @TradeId; ``` +变量 Roslyn 示例:变量名为“平仓支付日期为银行间交易日”,DataType 为 Bool;规则前端配置“平仓支付日期为银行间交易日 == true”。该变量必须使用本次平仓请求中的 PayDate,不能查询 swap_flow_event 历史平仓事件,否则首次提交和修改支付日期后的试算会取不到当前页面值。 + +```csharp +if (!PayDate.HasValue) + throw new Exception("平仓支付日期为空"); + +return new RiskVariableValueDetail( + RiskCalendarHelper.IsInterbankTradingDay(DbContext, PayDate.Value), + $"交易ID {TradeId},本次平仓支付日期为{PayDate.Value:yyyy-MM-dd}"); +``` + 注释规则定义: ```csharp @@ -2223,8 +2232,8 @@ WHERE t.id = @TradeId; //{ // Id = 1000008, // RuleName = "支付日为银行间交易日(本地)", -// RuleText = "取值字段:通过 DbContext.swap_flow_event 按 SwapTradeId=TradeId、EventType=2、DataState<>0 取平仓事件的 PayDate,PayDate 对应交易平仓窗口的支付日期;通过 DbContext.calendar 按 Country='IB' 且 Year=PayDate.Year 取银行间日历,HolidayJson 存储该年非交易日。计算逻辑:若支付日期对应的 yyyy,MM,dd 不存在于 IB 日历 HolidayJson 中,则说明支付日期是银行间交易日,触发审批。", -// RuleExpr = "DbContext.swap_flow_event.Any(e => e.SwapTradeId == TradeId && e.EventType == 2 && e.DataState != 0 && e.PayDate.HasValue) && !DbContext.calendar.First(c => c.Country == \"IB\" && c.Year == DbContext.swap_flow_event.Where(e => e.SwapTradeId == TradeId && e.EventType == 2 && e.DataState != 0 && e.PayDate.HasValue).OrderByDescending(e => e.id).First().PayDate.Value.Year && c.ValidState != \"InValid\").HolidayJson.Contains(DbContext.swap_flow_event.Where(e => e.SwapTradeId == TradeId && e.EventType == 2 && e.DataState != 0 && e.PayDate.HasValue).OrderByDescending(e => e.id).First().PayDate.Value.ToString(\"yyyy,MM,dd\"))", +// RuleText = "取值字段:取互换交易-交易查看-交易平仓窗口本次提交的 UnwindData.PayDate,PayDate 对应页面支付日期;通过 DbContext.calendar 按 Country='IB' 且 Year=PayDate.Year 取银行间日历,HolidayJson 存储该年非交易日。计算逻辑:若支付日期对应的 yyyy,MM,dd 不存在于 IB 日历 HolidayJson 中,则说明支付日期是银行间交易日,触发审批。", +// RuleExpr = "PayDate.HasValue && RiskCalendarHelper.IsInterbankTradingDay(DbContext, PayDate.Value)", // Version = 1, // Status = RiskRuleStatus.Active, // OptId = 0, @@ -2236,6 +2245,8 @@ WHERE t.id = @TradeId; //}); ``` +变量值核对 SQL:用于核对已落库平仓事件的支付日期是否为银行间交易日;首次提交前的页面值应以请求中的 UnwindData.PayDate 为准,不能只依赖该 SQL。 + ```sql SET @TradeId = 3001699; @@ -2274,33 +2285,54 @@ WHERE t.id = @TradeId; ## 20. 规则 9 查询结果排查 SQL -规则 9:到期日为银行间交易日(本地)。用于核对互换交易界面的到期日是否为银行间交易日;若到期日未落在 IB 日历的非交易日列表中,则命中。 +规则 9:到期日为银行间交易日(本地)。用于核对新增互换交易的到期日期是否为银行间交易日;若到期日期未落在 IB 日历的非交易日列表中,则命中。 取数流程: ```text -1. 根据 TradeId 查当前 trade。 -2. 取 trade.ExerciseDate,作为互换交易界面的到期日。 -3. 根据 ExerciseDate.Value.Year 查询 calendar 表中 Country = 'IB' 的银行间日历。 -4. calendar.HolidayJson 存储该年非交易日,格式为 yyyy,MM,dd。 -5. 若 HolidayJson 不包含到期日对应的 yyyy,MM,dd,则说明到期日是银行间交易日,命中规则。 +1. 根据 TradeId 查 swap_position。 +2. 限定 SwapTradeId = TradeId、IsInitial = 1,取新增互换交易生成的期初持仓。 +3. 取 swap_position.PosiMatuirityDate,作为新增互换交易的到期日期。 +4. 根据 PosiMatuirityDate.Value.Year 查询 calendar 表中 Country = 'IB' 的银行间日历。 +5. calendar.HolidayJson 存储该年非交易日,格式为 yyyy,MM,dd。 +6. 若 HolidayJson 不包含到期日期对应的 yyyy,MM,dd,则说明到期日期是银行间交易日,命中规则。 ``` 规则公式: ```text -到期日 ∉ IB 日历非交易日 +到期日期 ∉ IB 日历非交易日 ``` 规则字段口径: ```text -到期日:trade.ExerciseDate,对应互换交易界面的到期日 +到期日期:swap_position.PosiMatuirityDate,对应新增互换交易的到期日期 +期初持仓:swap_position.IsInitial = 1 银行间日历:calendar.Country = 'IB' -日历年份:calendar.Year = trade.ExerciseDate.Year +日历年份:calendar.Year = swap_position.PosiMatuirityDate.Year 非交易日:calendar.HolidayJson ``` +变量 Roslyn 示例:变量名为“互换到期日期为银行间交易日”,DataType 为 Bool;规则前端配置“互换到期日期为银行间交易日 == true”。该变量取新增互换交易生成的期初持仓到期日期,即 swap_position.PosiMatuirityDate。 + +```csharp +var maturityDate = DbContext.swap_position + .Where(p => p.SwapTradeId == TradeId + && p.IsInitial + && p.PosiMatuirityDate.HasValue) + .OrderBy(p => p.id) + .Select(p => p.PosiMatuirityDate) + .FirstOrDefault(); + +if (!maturityDate.HasValue) + throw new Exception("互换到期日期为空"); + +return new RiskVariableValueDetail( + RiskCalendarHelper.IsInterbankTradingDay(DbContext, maturityDate.Value), + $"交易ID {TradeId},互换到期日期为{maturityDate.Value:yyyy-MM-dd}"); +``` + 注释规则定义: ```csharp @@ -2308,8 +2340,8 @@ WHERE t.id = @TradeId; //{ // Id = 1000009, // RuleName = "到期日为银行间交易日(本地)", -// RuleText = "取值字段:通过 DbContext.trade 按 TradeId 取当前交易的 ExerciseDate,ExerciseDate 对应互换交易界面的到期日;通过 DbContext.calendar 按 Country='IB' 且 Year=ExerciseDate.Year 取银行间日历,HolidayJson 存储该年非交易日。计算逻辑:若到期日对应的 yyyy,MM,dd 不存在于 IB 日历 HolidayJson 中,则说明到期日是银行间交易日,触发审批。", -// RuleExpr = "DbContext.trade.First(t => t.id == TradeId).ExerciseDate.HasValue && !DbContext.calendar.First(c => c.Country == \"IB\" && c.Year == DbContext.trade.First(t => t.id == TradeId).ExerciseDate.Value.Year && c.ValidState != \"InValid\").HolidayJson.Contains(DbContext.trade.First(t => t.id == TradeId).ExerciseDate.Value.ToString(\"yyyy,MM,dd\"))", +// RuleText = "取值字段:通过 DbContext.swap_position 按 SwapTradeId=TradeId、IsInitial=true 取新增互换交易生成的期初持仓 PosiMatuirityDate,PosiMatuirityDate 对应新增互换交易的到期日期;通过 DbContext.calendar 按 Country='IB' 且 Year=PosiMatuirityDate.Year 取银行间日历,HolidayJson 存储该年非交易日。计算逻辑:若到期日期对应的 yyyy,MM,dd 不存在于 IB 日历 HolidayJson 中,则说明到期日期是银行间交易日,触发审批。", +// RuleExpr = "DbContext.swap_position.Any(p => p.SwapTradeId == TradeId && p.IsInitial && p.PosiMatuirityDate.HasValue) && RiskCalendarHelper.IsInterbankTradingDay(DbContext, DbContext.swap_position.Where(p => p.SwapTradeId == TradeId && p.IsInitial && p.PosiMatuirityDate.HasValue).OrderBy(p => p.id).First().PosiMatuirityDate.Value)", // Version = 1, // Status = RiskRuleStatus.Active, // OptId = 0, @@ -2327,19 +2359,30 @@ SET @TradeId = 3001699; SELECT t.id AS TradeId, t.TradeType, - t.ExerciseDate AS MaturityDate, + sp.id AS SwapPositionId, + sp.IsInitial, + sp.PosiMatuirityDate AS MaturityDate, c.Country AS CalendarCountry, c.Year AS CalendarYear, - DATE_FORMAT(t.ExerciseDate, '%Y,%m,%d') AS MaturityDateText, + DATE_FORMAT(sp.PosiMatuirityDate, '%Y,%m,%d') AS MaturityDateText, CASE - WHEN t.ExerciseDate IS NOT NULL - AND c.HolidayJson NOT LIKE CONCAT('%', DATE_FORMAT(t.ExerciseDate, '%Y,%m,%d'), '%') THEN 1 + WHEN sp.PosiMatuirityDate IS NOT NULL + AND c.HolidayJson NOT LIKE CONCAT('%', DATE_FORMAT(sp.PosiMatuirityDate, '%Y,%m,%d'), '%') THEN 1 ELSE 0 END AS IsMaturityDateIBTradingDay FROM trade t +LEFT JOIN ( + SELECT * + FROM swap_position + WHERE SwapTradeId = @TradeId + AND IsInitial = 1 + AND PosiMatuirityDate IS NOT NULL + ORDER BY id + LIMIT 1 +) sp ON sp.SwapTradeId = t.id LEFT JOIN calendar c ON c.Country = 'IB' - AND c.Year = YEAR(t.ExerciseDate) + AND c.Year = YEAR(sp.PosiMatuirityDate) AND c.ValidState <> 'InValid' WHERE t.id = @TradeId; ``` @@ -2348,17 +2391,16 @@ WHERE t.id = @TradeId; ## 21. 规则 10 查询结果排查 SQL -规则 10:平仓日为银行间交易日(本地)。用于核对交易平仓窗口的平仓日期是否为银行间交易日;若平仓日期未落在 IB 日历的非交易日列表中,则命中。 +规则 10:平仓日为银行间交易日(本地)。用于核对互换交易-交易查看-交易平仓窗口中本次提交的平仓日期是否为银行间交易日;若平仓日期未落在 IB 日历的非交易日列表中,则命中。 取数流程: ```text -1. 根据 TradeId 查 swap_flow_event。 -2. 限定 SwapTradeId = TradeId、EventType = 2、DataState <> 0,取平仓事件。 -3. 取 swap_flow_event.UnwindDate,作为交易平仓窗口的平仓日期。 -4. 根据 UnwindDate.Value.Year 查询 calendar 表中 Country = 'IB' 的银行间日历。 -5. calendar.HolidayJson 存储该年非交易日,格式为 yyyy,MM,dd。 -6. 若 HolidayJson 不包含平仓日期对应的 yyyy,MM,dd,则说明平仓日期是银行间交易日,命中规则。 +1. 用户从互换交易-交易查看进入交易平仓窗口。 +2. 取交易平仓窗口本次提交的平仓日期,即 UnwindData.UnwindDate。 +3. 根据 UnwindDate.Value.Year 查询 calendar 表中 Country = 'IB' 的银行间日历。 +4. calendar.HolidayJson 存储该年非交易日,格式为 yyyy,MM,dd。 +5. 若 HolidayJson 不包含平仓日期对应的 yyyy,MM,dd,则说明平仓日期是银行间交易日,命中规则。 ``` 规则公式: @@ -2370,10 +2412,9 @@ WHERE t.id = @TradeId; 规则字段口径: ```text -平仓日期:swap_flow_event.UnwindDate,对应交易平仓窗口的平仓日期 -平仓事件:swap_flow_event.EventType = 2 +平仓日期:UnwindData.UnwindDate,对应互换交易-交易查看-交易平仓窗口的平仓日期 银行间日历:calendar.Country = 'IB' -日历年份:calendar.Year = swap_flow_event.UnwindDate.Year +日历年份:calendar.Year = UnwindData.UnwindDate.Year 非交易日:calendar.HolidayJson ``` @@ -2384,6 +2425,17 @@ WHERE t.id = @TradeId; ``` +变量 Roslyn 示例:变量名为“平仓日期为银行间交易日”,DataType 为 Bool;规则前端配置“平仓日期为银行间交易日 == true”。该变量必须使用本次平仓请求中的 UnwindDate,不能查询 swap_flow_event 历史平仓事件,否则首次提交和修改平仓日期后的试算会取不到当前页面值。 + +```csharp +if (!UnwindDate.HasValue) + throw new Exception("平仓日期为空"); + +return new RiskVariableValueDetail( + RiskCalendarHelper.IsInterbankTradingDay(DbContext, UnwindDate.Value), + $"交易ID {TradeId},本次平仓日期为{UnwindDate.Value:yyyy-MM-dd}"); +``` + 注释规则定义: ```csharp @@ -2391,8 +2443,8 @@ WHERE t.id = @TradeId; //{ // Id = 1000010, // RuleName = "平仓日为银行间交易日(本地)", -// RuleText = "取值字段:通过 DbContext.swap_flow_event 按 SwapTradeId=TradeId、EventType=2、DataState<>0 取平仓事件的 UnwindDate,UnwindDate 对应交易平仓窗口的平仓日期;通过 DbContext.calendar 按 Country='IB' 且 Year=UnwindDate.Year 取银行间日历,HolidayJson 存储该年非交易日。计算逻辑:若平仓日期对应的 yyyy,MM,dd 不存在于 IB 日历 HolidayJson 中,则说明平仓日期是银行间交易日,触发审批。", -// RuleExpr = "DbContext.swap_flow_event.Any(e => e.SwapTradeId == TradeId && e.EventType == 2 && e.DataState != 0 && e.UnwindDate.HasValue) && !DbContext.calendar.First(c => c.Country == \"IB\" && c.Year == DbContext.swap_flow_event.Where(e => e.SwapTradeId == TradeId && e.EventType == 2 && e.DataState != 0 && e.UnwindDate.HasValue).OrderByDescending(e => e.id).First().UnwindDate.Value.Year && c.ValidState != \"InValid\").HolidayJson.Contains(DbContext.swap_flow_event.Where(e => e.SwapTradeId == TradeId && e.EventType == 2 && e.DataState != 0 && e.UnwindDate.HasValue).OrderByDescending(e => e.id).First().UnwindDate.Value.ToString(\"yyyy,MM,dd\"))", +// RuleText = "取值字段:取互换交易-交易查看-交易平仓窗口本次提交的 UnwindData.UnwindDate,UnwindDate 对应页面平仓日期;通过 DbContext.calendar 按 Country='IB' 且 Year=UnwindDate.Year 取银行间日历,HolidayJson 存储该年非交易日。计算逻辑:若平仓日期对应的 yyyy,MM,dd 不存在于 IB 日历 HolidayJson 中,则说明平仓日期是银行间交易日,触发审批。", +// RuleExpr = "UnwindDate.HasValue && RiskCalendarHelper.IsInterbankTradingDay(DbContext, UnwindDate.Value)", // Version = 1, // Status = RiskRuleStatus.Active, // OptId = 0, @@ -2404,6 +2456,8 @@ WHERE t.id = @TradeId; //}); ``` +变量值核对 SQL:用于核对已落库平仓事件的平仓日期是否为银行间交易日;首次提交前的页面值应以请求中的 UnwindData.UnwindDate 为准,不能只依赖该 SQL。 + ```sql SET @TradeId = 3001699;