客户保证金率设置需求变更:不需要根据发行年限,且增加30年以上一档,交易确认书保证金率获取也需要修改

This commit is contained in:
shangzhongyuan
2025-07-22 17:41:50 +08:00
parent 4e3a00523d
commit 75793f9f4f
8 changed files with 81 additions and 127 deletions
@@ -30,6 +30,11 @@ namespace YLErp.DBModels
/// </summary>
public const string ThirtyYear = "30Y";
/// <summary>
/// 30年以上期
/// </summary>
public const string OverThirtyYear = "Over30Y";
/// <summary>
/// 所有允许的期限值
/// </summary>
@@ -39,7 +44,8 @@ namespace YLErp.DBModels
TwoYear,
FiveYear,
TenYear,
ThirtyYear
ThirtyYear,
OverThirtyYear
};
/// <summary>
@@ -66,6 +72,7 @@ namespace YLErp.DBModels
FiveYear => "5年",
TenYear => "10年",
ThirtyYear => "30年",
OverThirtyYear => "30年以上",
_ => term?.Replace("Y", "年") ?? "默认"
};
}
@@ -1,6 +1,7 @@
using Microsoft.VisualBasic;
using Newtonsoft.Json.Linq;
using NPOI.SS.Formula.Functions;
using Org.BouncyCastle.Ocsp;
using System.Diagnostics;
using System.Net.Http.Headers;
using System.Text.RegularExpressions;
@@ -11,6 +12,7 @@ using YLErp.DBModels.Enums;
using YLErp.Enums;
using YLErp.Plugins.TradeDocGenerator;
using YLErp.Plugins.TradeDocGenerator.Abstracts;
using static NPOI.HSSF.Util.HSSFColor;
namespace YLErp.Plugins.ShanXi.DocumentGenerator
{
@@ -100,7 +102,6 @@ namespace YLErp.Plugins.ShanXi.DocumentGenerator
var clientduties = Context.GetClientDuties();
var table1 = new JArray();
var table2 = new JArray();
var clientMarginRate = Context.GetClientMarginRate(client.id);
dic["交易编号"] = GetContractNo(out string contractIndex);
dic["交易对手方全称"] = client.ClientType != "产品" ? client.Name : $"{client.Manager}作为管理人代表{client.Name}";
dic["主协议编号"] = client.MainProtocolCode;
@@ -126,7 +127,7 @@ namespace YLErp.Plugins.ShanXi.DocumentGenerator
var initialMarginQuery = swapPositions.Where(x => x.InterestMode == 5);
var additionMarginQuery = swapPositions.Where(x => x.InterestMode == 6);
var underlyingCode = swapPosition?.UnderlyingCode;
var clientMarginRate = YLErp.Modules.UnderlyingModule.UnderlyingHelper.GetApplicableMarginRate(client.id, underlyingCode, trade.TradeDate.Value);
if (count == 0)
{
var interestMargin = allinterestMargins.FirstOrDefault(o => o.interest_rest_days != null);
@@ -197,9 +198,9 @@ namespace YLErp.Plugins.ShanXi.DocumentGenerator
//}
//dic["计算基准"] = calculationBasis;
dic["初始保障金率"] = ((clientMarginRate?.InitMarginRate ?? 0) * 100).ToString("0.####") + "%";
dic["维持保障金率"] = ((clientMarginRate?.MaintenanceRate ?? 0) * 100).ToString("0.####") + "%";
dic["初始保障金率"] = ((clientMarginRate?.init_rate ?? 0) * 100).ToString("0.####") + "%";
dic["维持保障金率"] = ((clientMarginRate?.maintain_rate ?? 0) * 100).ToString("0.####") + "%";
dic["期初预付金".Insert("期初预付金".Length, "2?")] = (StockEqvNotionalRealSum * Convert.ToDouble(clientMarginRate?.init_rate??0)).ToString("0.##");
//var initialMarginSum = initialMarginQuery.Sum(x =>
// x.InterestDirection == 2 ? x.InterestPrincipalFix * -1 : x.InterestPrincipalFix);
//dic["期初预付金".Insert("期初预付金".Length, "2?")] = initialMarginSum.ToString("0.##");
@@ -224,7 +225,7 @@ namespace YLErp.Plugins.ShanXi.DocumentGenerator
FormatToDict("交易费用", (double)((swapPosition?.PosiTradingFeePending * 2) ?? 0), row);
row["期初预付金利率"] = ((initialMarginQuery.FirstOrDefault()?.InterestRateDefault) ?? 0) * 100;
row["追加预付金返息率"] = ((clientMarginRate?.AddToMarginRebateRate) ?? 0) * 100;
//row["追加预付金返息率"] = ((clientMarginRate?.AddToMarginRebateRate) ?? 0) * 100;
if (!string.IsNullOrEmpty(underlyingCode))
{
var um = Context.GetTradeUnderlying(underlyingCode);
@@ -258,7 +259,7 @@ namespace YLErp.Plugins.ShanXi.DocumentGenerator
dic["交易费率"] = dic["基本费率"];
dic["交易费用"] = PosiTradingFeePending;
dic["名义本金".Insert("名义本金".Length, "2?")] = StockEqvNotionalRealSum.ToString("0.##");
dic["期初预付金".Insert("期初预付金".Length, "2?")] = (StockEqvNotionalRealSum * clientMarginRate?.InitMarginRate)?.ToString("0.##");
clientduties = clientduties.Where(x => x.ContactTypeId.Contains("3")).OrderBy(o => o.id).ToList();//交易确认书邮件接收人
@@ -9,6 +9,7 @@
<ItemGroup>
<ProjectReference Include="..\..\Framework\YLErp.Core\YLErp.Core.csproj" />
<ProjectReference Include="..\..\YLErpDAL\YLErpDAL.csproj" />
</ItemGroup>
<ItemGroup>
@@ -9,76 +9,54 @@ namespace YLErp.UnitTestProject.Modules.UnderlyingModule
[TestMethod]
public void GetApplicableMarginTerm_RuleTable_AllCases()
{
// 发行期限不限(OpenDate=null),测试(0.00,2.00],应返回2年
// <=2年,应返回2年
var code1 = "UNLIMITED_2Y";
var maturity1 = new DateTime(2030, 1, 1);
var underlying1 = CreateTestUnderlying ( code1, null,maturity1);
var underlying1 = CreateTestUnderlying(code1, null, maturity1);
var valueDate1 = maturity1.AddYears(-1); // 剩余1年
var result1=UnderlyingHelper.GetApplicableMarginTerm(code1, valueDate1);
Assert.AreEqual(ConsMarginTerm.TwoYear, result1, "(0,2]年 不限 应为2年");
var result1 = UnderlyingHelper.GetApplicableMarginTerm(code1, valueDate1);
Assert.AreEqual(ConsMarginTerm.TwoYear, result1, "<=2年 应为2年");
// (2.00,2.25] <5年,应返回2
var code2 = "LT5_2_25";
var open2 = new DateTime(2020, 1, 1); var maturity2 = new DateTime(2024, 3, 1); // 4.17年
var underlying2 = CreateTestUnderlying(code2, open2, maturity2);
var valueDate2 = maturity2.AddYears(-2).AddMonths(-2); // 剩余约2.17
// (2,5]年,应返回5
var code2 = "UNLIMITED_5Y";
var maturity2 = new DateTime(2030, 1, 1);
var underlying2 = CreateTestUnderlying(code2, null, maturity2);
var valueDate2 = maturity2.AddYears(-3); // 剩余3
var result2 = UnderlyingHelper.GetApplicableMarginTerm(code2, valueDate2);
Assert.AreEqual(ConsMarginTerm.TwoYear, result2, "(2,2.25]年 <5 应为2年");
Assert.AreEqual(ConsMarginTerm.FiveYear, result2, "(2,5]年 应为5年");
// (2.00,2.25] >=5年,应返回5
var code3 = "GE5_2_25";
var open3 = new DateTime(2020, 1, 1); var maturity3 = new DateTime(2026, 1, 1); // 6年
var underlying3 = CreateTestUnderlying(code3, open3, maturity3);
var valueDate3_2 = maturity3.AddYears(-2).AddMonths(-1); // 剩余约2.08
// (5,10]年,应返回10
var code3 = "UNLIMITED_10Y";
var maturity3 = new DateTime(2040, 1, 1);
var underlying3 = CreateTestUnderlying(code3, null, maturity3);
var valueDate3 = maturity3.AddYears(-7); // 剩余13
var valueDate3_2 = maturity3.AddYears(-10); // 剩余10年
var result3 = UnderlyingHelper.GetApplicableMarginTerm(code3, valueDate3_2);
Assert.AreEqual(ConsMarginTerm.FiveYear, result3, "(2,2.25]年 >=5 应为5年");
Assert.AreEqual(ConsMarginTerm.TenYear, result3, "(5,10]年 应为10年");
// (2.25,5.00] 不限,应返回5
var code4 = "UNLIMITED_5Y";
var maturity4 = new DateTime(2030, 1, 1);
// (10,30]年,应返回30
var code4 = "UNLIMITED_30Y";
var maturity4 = new DateTime(2050, 1, 1);
var underlying4 = CreateTestUnderlying(code4, null, maturity4);
var valueDate4 = maturity4.AddYears(-3); // 剩余3
var valueDate4 = maturity4.AddYears(-28); // 剩余28
var result4 = UnderlyingHelper.GetApplicableMarginTerm(code4, valueDate4);
Assert.AreEqual(ConsMarginTerm.FiveYear, result4, "(2.25,5]年 不限 应为5年");
Assert.AreEqual(ConsMarginTerm.ThirtyYear, result4, "(10,30]年 应为30年");
// (5.00,5.25] <7年,应返回5年
var code5 = "LT7_5_25";
var open5 = new DateTime(2020, 1, 1); var maturity5 = new DateTime(2026, 1, 1); // 6年
var underlying5 = CreateTestUnderlying(code5, open5, maturity5);
var valueDate5 = maturity5.AddYears(-5).AddMonths(-1); // 剩余约5.08
var result5 = UnderlyingHelper.GetApplicableMarginTerm(underlying5.UnderlyingCode, valueDate5);
Assert.AreEqual(ConsMarginTerm.FiveYear, result5, "(5,5.25]年 <7 应为5年");
// >30年,应返回30年以上
var code5 = "OVER_30Y";
var maturity5 = new DateTime(2060, 1, 1);
var underlying5 = CreateTestUnderlying(code5, null, maturity5);
var valueDate5 = maturity5.AddYears(-31); // 剩余31
var result5 = UnderlyingHelper.GetApplicableMarginTerm(code5, valueDate5);
Assert.AreEqual(ConsMarginTerm.OverThirtyYear, result5, ">30年 应为30年以上");
// (5.00,5.25] >=7年,应返回10年
var code6 = "GE7_5_25";
var open6 = new DateTime(2015, 1, 1); var maturity6 = new DateTime(2023, 2, 1); // 8.08年
var underlying6 = CreateTestUnderlying(code6, open6, maturity6);
var valueDate6 = maturity6.AddYears(-5).AddMonths(-1); // 剩余约5.08年
// 无效的到期日,应为默认
var code6 = "INVALID_TERM";
DateTime? maturity6 = null;
var underlying6 = CreateTestUnderlying(code6, null, maturity6);
var valueDate6 = DateTime.Now.Date;
var result6 = UnderlyingHelper.GetApplicableMarginTerm(code6, valueDate6);
Assert.AreEqual(ConsMarginTerm.TenYear, result6, "(5,5.25]年 >=7 应为10年");
// (5.25,25.00] 不限,应返回10年
var code7 = "UNLIMITED_10Y";
var maturity7 = new DateTime(2040, 1, 1);
var underlying7 = CreateTestUnderlying(code7, null, maturity7);
var valueDate7 = maturity7.AddYears(-10); // 剩余10年
var result7 = UnderlyingHelper.GetApplicableMarginTerm(code7, valueDate7);
Assert.AreEqual(ConsMarginTerm.TenYear, result7, "(5.25,25]年 不限 应为10年");
// (25.00,30.00] 不限,应返回30年
var code8 = "UNLIMITED_30Y";
var maturity8 = new DateTime(2050, 1, 1);
var underlying8 = CreateTestUnderlying(code8, null, maturity8);
var valueDate8 = maturity8.AddYears(-28); // 剩余28年
var result8 = UnderlyingHelper.GetApplicableMarginTerm(code8, valueDate8);
Assert.AreEqual(ConsMarginTerm.ThirtyYear, result8, "(25,30]年 不限 应为30年");
var code9 = "INVALID_TERM";
DateTime? maturity9 = null;
var underlying9 = CreateTestUnderlying(code9, null, maturity9);
var valueDate9 = DateTime.Now.Date;
var result9 = UnderlyingHelper.GetApplicableMarginTerm(code9, valueDate9);
Assert.AreEqual(ConsMarginTerm.Default, result9, "无效的到期日 不限 应为默认");
Assert.AreEqual(ConsMarginTerm.Default, result6, "无效的到期日 应为默认");
}
#region
+6 -2
View File
@@ -8,10 +8,14 @@ namespace YLErp.Model
public class ClientMarginDetailDto
{
/// <summary>
/// 利率债期限
/// 债期限
/// </summary>
public string bond_term { get; set; }
/// <summary>
/// 债券期限显示
/// </summary>
public string bond_term_desc => ConsMarginTerm.GetDisplayName(bond_term);
/// <summary>
/// 初始保证金率(%) - 可空
/// </summary>
@@ -298,15 +298,11 @@ namespace YLErp.Modules.UnderlyingModule
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(underlyingCode);
if (underlying == null || !underlying.MaturityDate.HasValue)
return ConsMarginTerm.Default;
// 计算发行年限(从上市日期到到期日期)
var issueTermYears = getYearTerm(underlying.OpenDate, underlying.MaturityDate);
// 计算剩余期限(从估值日期到到期日期)
var remainingTermYears = getYearTerm(valueDate, underlying.MaturityDate);
// 根据业务规则确定适用的保证金率期限
return DetermineMarginTerm(issueTermYears, remainingTermYears);
return DetermineMarginTerm(remainingTermYears);
}
catch (Exception ex)
{
@@ -317,55 +313,32 @@ namespace YLErp.Modules.UnderlyingModule
}
/// <summary>
/// 根据发行年限和剩余年限确定适用的保证金率期限
/// 规则表格:
/// | 剩余期限(年) | 发行期限(年) | 则适用于 |
/// |------------------|---------------|----------|
/// | (0.00,2.00] | 不限 | 2年 |
/// | (2.00,2.25] | <5 | 2年 |
/// | (2.00,2.25] | >=5 | 5年 |
/// | (2.25,5.00] | 不限 | 5年 |
/// | (5.00,5.25] | <7 | 5年 |
/// | (5.00,5.25] | >=7 | 10年 |
/// | (5.25,25.00] | 不限 | 10年 |
/// | (25.00,30.00] | 不限 | 30年 |
/// 说明:发行年限为null或0时视为“不限”
/// 根据剩余年限确定适用的保证金率期限(新版规则,仅看剩余期限)
/// | 剩余期限(年) | 则适用于 |
/// |------------------|----------|
/// | <=2 | 2年档位 |
/// | (2,5] | 5年 |
/// | (5,10] | 10年 |
/// | (10,30] | 30年 |
/// | >30 | 30年以上 |
/// 未设置或无法计算剩余期限,使用“默认”
/// </summary>
private static string DetermineMarginTerm(double? issueYears, double? remainingYears)
private static string DetermineMarginTerm( double? remainingYears)
{
// (0.00,2.00]年 不限 2年
if (remainingYears > 0.00 && remainingYears <= 2.00)
// 新版规则:只看剩余期限
if (!remainingYears.HasValue)
return ConsMarginTerm.Default;
if (remainingYears <= 2.0)
return ConsMarginTerm.TwoYear;
// (2.00,2.25] <5 2年
if (remainingYears > 2.00 && remainingYears <= 2.25 && issueYears.HasValue && issueYears < 5)
return ConsMarginTerm.TwoYear;
// (2.00,2.25] >=5 5年
if (remainingYears > 2.00 && remainingYears <= 2.25 && issueYears.HasValue && issueYears >= 5)
if (remainingYears > 2.0 && remainingYears <= 5.0)
return ConsMarginTerm.FiveYear;
// (2.25,5.00] 不限 5年
if (remainingYears > 2.25 && remainingYears <= 5.00)
return ConsMarginTerm.FiveYear;
// (5.00,5.25] <7 5年
if (remainingYears > 5.00 && remainingYears <= 5.25 && issueYears.HasValue && issueYears < 7)
return ConsMarginTerm.FiveYear;
// (5.00,5.25] >=7 10年
if (remainingYears > 5.00 && remainingYears <= 5.25 && issueYears.HasValue && issueYears >= 7)
if (remainingYears > 5.0 && remainingYears <= 10.0)
return ConsMarginTerm.TenYear;
// (5.25,25.00] 不限 10年
if (remainingYears > 5.25 && remainingYears <= 25.00)
return ConsMarginTerm.TenYear;
// (25.00,30.00] 不限 30年
if (remainingYears > 25.00 && remainingYears <= 30.00)
if (remainingYears > 10.0 && remainingYears <= 30.0)
return ConsMarginTerm.ThirtyYear;
// 其他情况返回默认
if (remainingYears > 30.0)
return ConsMarginTerm.OverThirtyYear;
return ConsMarginTerm.Default;
}
}
@@ -161,7 +161,7 @@
@for (int i = 0; i < details.Count; i++)
{
var detail = details[i];
var displayName = detail.bond_term == "" ? "默认" : detail.bond_term.Replace("Y", "年");
var displayName = YLErp.DBModels.ConsMarginTerm.GetDisplayName(detail.bond_term);
<tr data-index="@i">
<td>
<input type="hidden" name="details[@i].bond_term" value="@detail.bond_term" />
@@ -247,19 +247,9 @@
}
}
// 辅助函数:格式化期限显示
function formatBondTerm(bondTerm) {
if (!bondTerm || bondTerm === '') return '默认';
if (bondTerm === null || bondTerm === undefined) return '--';
// 将Y替换为年
var formatted = bondTerm.toString().replace(/Y/g, '年');
return escapeHtml(formatted);
}
// 辅助函数:生成详情单元格HTML
function generateDetailCells(detail) {
return '<td>' + formatBondTerm(detail.bond_term) + '</td>' +
return '<td>' + detail.bond_term_desc + '</td>' +
'<td class="text-right">' + formatPercentage(detail.init_rate) + '</td>' +
'<td class="text-right">' + formatPercentage(detail.maintain_rate) + '</td>' +
'<td class="text-center">' + (detail.swap_days || '--') + '天</td>';