TRS-ZS-566、TRS-ZS-568 客户保证金率需求完成
This commit is contained in:
@@ -41,7 +41,7 @@ namespace YLErp.Cache
|
||||
/// <returns></returns>
|
||||
T StringGet<T>(string key) where T : class;
|
||||
|
||||
bool StringSetWithNoPrefix<T>(string key, object value) where T : class;
|
||||
bool StringSetWithNoPrefix<T>(string key, object value, TimeSpan? timeSpan) where T : class;
|
||||
T StringGetWithNoPrefix<T>(string key) where T : class;
|
||||
#region Batch Operate
|
||||
/// <summary>
|
||||
|
||||
@@ -231,9 +231,9 @@ namespace YLErp.Cache
|
||||
/// <param name="key"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public bool StringSetWithNoPrefix<T>(string key, object value) where T : class
|
||||
public bool StringSetWithNoPrefix<T>(string key, object value,TimeSpan? timeSpan) where T : class
|
||||
{
|
||||
return db.StringSet(key, JsonConvert.SerializeObject(value));
|
||||
return db.StringSet(key, JsonConvert.SerializeObject(value), timeSpan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace YLErp.DBModels
|
||||
{
|
||||
/// <summary>
|
||||
/// 数据模型基类
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public abstract class DBModelBaseV6 : DBModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 操作人ID
|
||||
/// </summary>
|
||||
[DisplayName("操作人")]
|
||||
[Column("opt_id")]
|
||||
public virtual int? OptId { set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作人名称
|
||||
/// </summary>
|
||||
[DisplayName("操作人")]
|
||||
[Column("opt_name")]
|
||||
public virtual string OptName { set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作时间
|
||||
/// </summary>
|
||||
[DisplayName("操作时间")]
|
||||
[Column("opt_date")]
|
||||
public virtual DateTime? OptDate { set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// 设置操作人操作时间
|
||||
/// </summary>
|
||||
public void SetOpt(DBModelWithOperator baseModel)
|
||||
{
|
||||
if (baseModel == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(baseModel));
|
||||
}
|
||||
|
||||
OptId = baseModel.OptId;
|
||||
OptName = baseModel.OptName;
|
||||
OptDate = baseModel.OptDate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置操作人操作时间
|
||||
/// </summary>
|
||||
public void SetOpt(OptUserInfo userInfo)
|
||||
{
|
||||
if (userInfo is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(userInfo));
|
||||
}
|
||||
|
||||
OptId = userInfo.UserId;
|
||||
OptName = userInfo.UserName;
|
||||
OptDate = DateTime.Now;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
namespace YLErp.DBModels
|
||||
{
|
||||
/// <summary>
|
||||
/// 保证金配置期限常量定义
|
||||
/// </summary>
|
||||
public static class ConsMarginTerm
|
||||
{
|
||||
/// <summary>
|
||||
/// 默认期限(空字符串)
|
||||
/// </summary>
|
||||
public const string Default = "";
|
||||
|
||||
/// <summary>
|
||||
/// 2年期
|
||||
/// </summary>
|
||||
public const string TwoYear = "2Y";
|
||||
|
||||
/// <summary>
|
||||
/// 5年期
|
||||
/// </summary>
|
||||
public const string FiveYear = "5Y";
|
||||
|
||||
/// <summary>
|
||||
/// 10年期
|
||||
/// </summary>
|
||||
public const string TenYear = "10Y";
|
||||
|
||||
/// <summary>
|
||||
/// 30年期
|
||||
/// </summary>
|
||||
public const string ThirtyYear = "30Y";
|
||||
|
||||
/// <summary>
|
||||
/// 所有允许的期限值
|
||||
/// </summary>
|
||||
public static readonly List<string> AllowedTerms = new List<string>
|
||||
{
|
||||
Default,
|
||||
TwoYear,
|
||||
FiveYear,
|
||||
TenYear,
|
||||
ThirtyYear
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// 验证期限是否有效
|
||||
/// </summary>
|
||||
/// <param name="term">期限值</param>
|
||||
/// <returns>是否有效</returns>
|
||||
public static bool IsValidTerm(string term)
|
||||
{
|
||||
return AllowedTerms.Contains(term ?? string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取期限的显示名称
|
||||
/// </summary>
|
||||
/// <param name="term">期限值</param>
|
||||
/// <returns>显示名称</returns>
|
||||
public static string GetDisplayName(string term)
|
||||
{
|
||||
return term switch
|
||||
{
|
||||
Default => "默认",
|
||||
TwoYear => "2年",
|
||||
FiveYear => "5年",
|
||||
TenYear => "10年",
|
||||
ThirtyYear => "30年",
|
||||
_ => term?.Replace("Y", "年") ?? "默认"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace YLErp.DBModels
|
||||
{
|
||||
/// <summary>
|
||||
/// 客户保证金配置
|
||||
/// </summary>
|
||||
[Table("client_margin_config")]
|
||||
public class client_margin_config : DBModelBaseV6
|
||||
{
|
||||
/// <summary>
|
||||
/// 客户ID
|
||||
/// </summary>
|
||||
[DisplayName("客户ID")]
|
||||
public int client_id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 客户名称
|
||||
/// </summary>
|
||||
[DisplayName("客户名称")]
|
||||
[NotMapped]
|
||||
public string client_name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 生效日期
|
||||
/// </summary>
|
||||
[DisplayName("生效日期")]
|
||||
public DateTime value_date { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace YLErp.DBModels
|
||||
{
|
||||
/// <summary>
|
||||
/// 客户保证金配置详情
|
||||
/// </summary>
|
||||
[Table("client_bond_trs_margin_detail")]
|
||||
public class client_margin_detail
|
||||
{
|
||||
/// <summary>
|
||||
/// 主键ID
|
||||
/// </summary>
|
||||
[DisplayName("主键ID")]
|
||||
[Key]
|
||||
public int id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 保证金关联主表id
|
||||
/// </summary>
|
||||
[DisplayName("配置ID")]
|
||||
public int config_id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 利率债期限
|
||||
/// </summary>
|
||||
[DisplayName("利率债期限")]
|
||||
public string bond_term { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 初始保证金率(%)
|
||||
/// </summary>
|
||||
[DisplayName("初始保证金率")]
|
||||
public decimal init_rate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 维持保证金率(%)
|
||||
/// </summary>
|
||||
[DisplayName("维持保证金率")]
|
||||
public decimal maintain_rate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 互换默认天数
|
||||
/// </summary>
|
||||
[DisplayName("互换默认天数")]
|
||||
public int swap_days { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
{
|
||||
public class YLUnitTestBase : YLBaseService
|
||||
{
|
||||
readonly StringBuilder _clearSQL;
|
||||
public readonly StringBuilder _clearSQL;
|
||||
|
||||
public YLUnitTestBase() : base(OptUserInfo.UnitTestUser)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
using YLErp.Modules.UnderlyingModule;
|
||||
|
||||
namespace YLErp.UnitTestProject.Modules.UnderlyingModule
|
||||
{
|
||||
[TestClass]
|
||||
public class UnderlyingHelperTest : YLUnitTestBase
|
||||
{
|
||||
|
||||
[TestMethod]
|
||||
public void GetApplicableMarginTerm_RuleTable_AllCases()
|
||||
{
|
||||
// 发行期限不限(OpenDate=null),测试(0.00,2.00],应返回2年
|
||||
var code1 = "UNLIMITED_2Y";
|
||||
var maturity1 = new DateTime(2030, 1, 1);
|
||||
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年");
|
||||
|
||||
// (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年
|
||||
var result2 = UnderlyingHelper.GetApplicableMarginTerm(code2, valueDate2);
|
||||
Assert.AreEqual(ConsMarginTerm.TwoYear, result2, "(2,2.25]年 <5 应为2年");
|
||||
|
||||
// (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年
|
||||
var result3 = UnderlyingHelper.GetApplicableMarginTerm(code3, valueDate3_2);
|
||||
Assert.AreEqual(ConsMarginTerm.FiveYear, result3, "(2,2.25]年 >=5 应为5年");
|
||||
|
||||
// (2.25,5.00] 不限,应返回5年
|
||||
var code4 = "UNLIMITED_5Y";
|
||||
var maturity4 = new DateTime(2030, 1, 1);
|
||||
var underlying4 = CreateTestUnderlying(code4, null, maturity4);
|
||||
var valueDate4 = maturity4.AddYears(-3); // 剩余3年
|
||||
var result4 = UnderlyingHelper.GetApplicableMarginTerm(code4, valueDate4);
|
||||
Assert.AreEqual(ConsMarginTerm.FiveYear, result4, "(2.25,5]年 不限 应为5年");
|
||||
|
||||
// (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年");
|
||||
|
||||
// (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 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, "无效的到期日 不限 应为默认");
|
||||
}
|
||||
|
||||
#region 辅助方法
|
||||
|
||||
/// <summary>
|
||||
/// 创建测试用的标的对象
|
||||
/// </summary>
|
||||
private underlying_manager CreateTestUnderlying(string code, DateTime? openDate, DateTime? maturityDate)
|
||||
{
|
||||
DbContext.Database.ExecuteSqlRaw($"DELETE FROM underlying_manager WHERE UnderlyingCode = '{code}'");
|
||||
var underlying = new underlying_manager
|
||||
{
|
||||
UnderlyingCode = code,
|
||||
OpenDate = openDate,
|
||||
MaturityDate = maturityDate,
|
||||
UnderlyingName = $"测试标的{code}",
|
||||
MarketCode = "TEST",
|
||||
MarketName = "测试市场",
|
||||
OptDate = DateTime.Now,
|
||||
OptId = UserId,
|
||||
OptName = UserName
|
||||
};
|
||||
|
||||
// 添加到数据库
|
||||
DbContext.underlying_manager.Add(underlying);
|
||||
DbContext.SaveChanges();
|
||||
|
||||
// 添加到清理列表
|
||||
AddClearSQL($"DELETE FROM underlying_manager WHERE UnderlyingCode = '{code}';");
|
||||
|
||||
return underlying;
|
||||
}
|
||||
/// <summary>
|
||||
/// 添加清理SQL
|
||||
/// </summary>
|
||||
private void AddClearSQL(string sql)
|
||||
{
|
||||
// _clearSQL 字段在 YLUnitTestBase 中定义
|
||||
// 这里假设 _clearSQL 是 protected 或 internal
|
||||
if (!string.IsNullOrWhiteSpace(sql))
|
||||
{
|
||||
_clearSQL.AppendLine(sql);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
{
|
||||
"ConnectionStrings": {
|
||||
"ylcms": "server=221.229.106.161;uid=roottest;pooling=true;port=20306;pwd=YieldChain!@#$2020;database=yltrs_ylcms;charset=utf8;Allow User Variables=True;SslMode=none;Connection Timeout=30;IgnoreCommandTransaction=true;",
|
||||
"yladmin": "server=221.229.106.161;uid=roottest;pooling=true;port=20306;pwd=YieldChain!@#$2020;database=yltrs_admin;charset=utf8;Allow User Variables=True;SslMode=none;Connection Timeout=30;IgnoreCommandTransaction=true;",
|
||||
"ylclient": "server=221.229.106.161;uid=roottest;pooling=true;port=20306;pwd=YieldChain!@#$2020;database=yltrs_client;charset=utf8;Allow User Variables=True;SslMode=none;Connection Timeout=30;IgnoreCommandTransaction=true;",
|
||||
"bondoms": "server=221.229.106.161;uid=roottest;pooling=true;port=20306;pwd=YieldChain!@#$2020;database=bond_oms;charset=utf8;Allow User Variables=True;SslMode=none;Connection Timeout=30;IgnoreCommandTransaction=true;"
|
||||
}
|
||||
,
|
||||
"ylcms": "server=139.196.109.225;uid=root;pooling=true;port=3306;pwd=Midnight001!@#$;database=yltrs_ylcms;charset=utf8;Allow User Variables=True;SslMode=none;Connection Timeout=30;IgnoreCommandTransaction=true;",
|
||||
"yladmin": "server=139.196.109.225;uid=root;pooling=true;port=3306;pwd=Midnight001!@#$;database=yltrs_admin;charset=utf8;Allow User Variables=True;SslMode=none;Connection Timeout=30;IgnoreCommandTransaction=true;",
|
||||
"ylclient": "server=139.196.109.225;uid=root;pooling=true;port=3306;pwd=Midnight001!@#$;database=yltrs_client;charset=utf8;Allow User Variables=True;SslMode=none;Connection Timeout=30;IgnoreCommandTransaction=true;",
|
||||
"bondoms": "server=139.196.109.225;uid=root;pooling=true;port=3306;pwd=Midnight001!@#$;database=bond_oms;charset=utf8;Allow User Variables=True;SslMode=none;Connection Timeout=30;IgnoreCommandTransaction=true;"
|
||||
},
|
||||
"LibreOffice": {
|
||||
"ExePath": "",
|
||||
"UserInstallation": "D:\\_work\\111\\libreoffice\\user1"
|
||||
|
||||
@@ -117,8 +117,6 @@ namespace YLErp.BLL.EodSettlement
|
||||
var lastpushLog = db.push_log.Where(x => x.data_type == (int)SwapPushDataEnum.日终估值获取 && x.data_state == (int)SwapPushDataStateEnum.接收处理成功 && x.create_time > valuedate).OrderByDescending(o => o.create_time).FirstOrDefault();
|
||||
//获取根据系统时间
|
||||
var lastBalanceDate = EodOperationBase.GetLastSettlementDate(valuedate);
|
||||
// 获取定义文件预付金率设置
|
||||
var clientMarginTemplates = db.client_marginrate.Where(x => x.ValueDate <= endDate).OrderByDescending(o => o.ValueDate).AsNoTracking().ToList();
|
||||
var lastDate = lastBalanceDate;
|
||||
|
||||
//查询历史记录
|
||||
@@ -306,7 +304,6 @@ namespace YLErp.BLL.EodSettlement
|
||||
|
||||
var lasttoDay = EodOperationBase.GetLastSettlementDate(lastDate, true);
|
||||
var lastClientBalanceDaily = db.ClientBalanceDaily.Where(a => a.BalanceDate == lasttoDay && a.ClientId == data.ClientId).FirstOrDefault();
|
||||
var marginRate = GetClientMarginRate(data.ClientId, clientMarginTemplates);
|
||||
balance.FrozenBalance = data.FrozenBalance;
|
||||
balance.FreezePremium = data.FreezePremium;
|
||||
balance.ReceivablesPremium = data.ReceivablesPremium;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using BaseOUDAL;
|
||||
using Confluent.Kafka;
|
||||
using DocumentFormat.OpenXml.Drawing.Charts;
|
||||
using DocumentFormat.OpenXml.Office2010.Excel;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -18,6 +19,7 @@ using YLErp.Modules;
|
||||
using YLErp.Modules.DataProviderModule;
|
||||
using YLErp.Modules.EodModule;
|
||||
using YLErp.Modules.EodModule.QueryModule;
|
||||
using YLErp.Modules.UnderlyingModule;
|
||||
|
||||
namespace YLErp.BLL.Eod
|
||||
{
|
||||
@@ -232,7 +234,6 @@ namespace YLErp.BLL.Eod
|
||||
.ToDictionary(p => p.Key, p => p.OptDate);
|
||||
|
||||
Dictionary<int, DateTime?> dicCashInOut = DbContext.ClientCashInCashOut.Where(x => clientIds.Contains((int)x.ClientId)).AsEnumerable().GroupBy(p => (int)p.ClientId).Select(p => new { p.Key, OptDate = p.Max(d => d.OptDate) }).ToDictionary(p => p.Key, p => p.OptDate);
|
||||
var clientMarginTemplates = DbContext.client_marginrate.Where(x => x.ValueDate <= calcDate).OrderByDescending(o => o.ValueDate).AsNoTracking().ToList();
|
||||
foreach (var item in _clientBalanceDic.Values)
|
||||
{
|
||||
#region 判断实时持仓数据是否最新(包含了最新交易操作的数据)
|
||||
@@ -266,9 +267,6 @@ namespace YLErp.BLL.Eod
|
||||
item.PositionNotionalPrincipal += stockEqvNotionalDict.ContainsKey(item.ClientId) ? stockEqvNotionalDict[item.ClientId] : 0;
|
||||
//可用名义本金规模
|
||||
item.AvailableStockEqvNotional = item.TotalCreditStockEqvNotional - item.PositionNotionalPrincipal;
|
||||
|
||||
|
||||
var marginRate = GetClientMarginRate(item.ClientId, clientMarginTemplates);
|
||||
//预付金金额=期末结存-初始预付金金额
|
||||
item.MarginBalance = item.AmountFund - item.MySideMargin;
|
||||
// 可用资金 = 期末结存 - 追保账户余额 - 初始保证金
|
||||
@@ -1378,24 +1376,13 @@ namespace YLErp.BLL.Eod
|
||||
foreach (var itemGroup in flowGroup)
|
||||
{
|
||||
var balance = clientBalanceDic[itemGroup.Key ?? 0];
|
||||
//string marginType = balance.ClientType == 1 ? "多空组合" : "品种";
|
||||
// 获取客户预付金比例设置
|
||||
var clientMarginTemplate = clientMarginTemplates.FirstOrDefault(x => x.ClientId == itemGroup.Key );
|
||||
if (clientMarginTemplate == null)
|
||||
{
|
||||
clientMarginTemplate = clientMarginTemplates.FirstOrDefault(x => x.ClientId == 0 );
|
||||
}
|
||||
// 没有设置预付金比例则跳过
|
||||
if (clientMarginTemplate == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var clientPositions = positions.Where(x => x.ClientId == itemGroup.Key).ToList();
|
||||
foreach (var item in itemGroup.GroupBy(s => s.UnderlyingCode))
|
||||
{
|
||||
var clientMarginDetail= UnderlyingHelper.GetApplicableMarginRate(itemGroup.Key ?? 0, item.Key, startDate);
|
||||
// 获取当前客户当前标的持仓数据,合并后的名义本金数量
|
||||
var positionLsit = clientPositions.Where(x => x.UnderlyingCode == item.Key).ToList();
|
||||
decimal money = CalcDmaMoney(positionLsit, item.ToList(), clientMarginTemplate);
|
||||
decimal money = CalcDmaMoney(positionLsit, item.ToList(), clientMarginDetail);
|
||||
balance.FrozenMarginMoney += Convert.ToDouble(money);
|
||||
}
|
||||
}
|
||||
@@ -1407,10 +1394,10 @@ namespace YLErp.BLL.Eod
|
||||
/// <param name="flows">流水列表</param>
|
||||
/// <param name="clientMarginTemplate">客户保证金模板</param>
|
||||
/// <returns>资金变化金额</returns>
|
||||
public decimal CalcDmaMoney(List<swap_flow> positions, List<swap_flow> flows, client_marginrate clientMarginTemplate)
|
||||
public decimal CalcDmaMoney(List<swap_flow> positions, List<swap_flow> flows, client_margin_detail clientMarginTemplate)
|
||||
{
|
||||
decimal money = 0;
|
||||
var marginRate = (decimal)clientMarginTemplate.InitMarginRate;
|
||||
var marginRate = clientMarginTemplate?.init_rate??0;
|
||||
|
||||
// 处理持仓与流水的平仓逻辑
|
||||
foreach (var position in positions.ToList()) // 使用 ToList() 避免修改集合时的问题
|
||||
|
||||
@@ -744,7 +744,7 @@ namespace YLErp.BLL.Eod
|
||||
if (resp!=null)
|
||||
{
|
||||
clientPosition.deal_yield_avg = resp.ytm* ConsGlobal.bondPriceMultiple;
|
||||
_yLCache.StringSetWithNoPrefix<CalBondResult>("TRS-BondFullPrice:" + clientPosition.security_id, resp);
|
||||
_yLCache.StringSetWithNoPrefix<CalBondResult>("TRS-BondFullPrice:" + clientPosition.security_id, resp,TimeSpan.FromHours(1));
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
|
||||
@@ -402,5 +402,8 @@ namespace YLErp.BLL
|
||||
public DbSet<TrsAccountManageDetail> trs_account_manage_detail { get; set; }
|
||||
|
||||
public DbSet<ClientBalanceView> clientBalanceView { get; set; }
|
||||
|
||||
public DbSet<client_margin_config> clientMarginConfig { get; set; }
|
||||
public DbSet<client_margin_detail> clientMarginDetail { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using BaseOUDAL;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace YLErp.Model
|
||||
{
|
||||
public class ClientMarginConfigReq
|
||||
{
|
||||
public int ClientId { get; set; }
|
||||
public DateTime? ValueDate { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace YLErp.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// 客户保证金配置详情DTO - 用于前端数据传输
|
||||
/// </summary>
|
||||
public class ClientMarginDetailDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 利率债期限
|
||||
/// </summary>
|
||||
public string bond_term { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 初始保证金率(%) - 可空
|
||||
/// </summary>
|
||||
public decimal? init_rate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 维持保证金率(%) - 可空
|
||||
/// </summary>
|
||||
public decimal? maintain_rate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 互换默认天数 - 可空
|
||||
/// </summary>
|
||||
public int? swap_days { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 客户保证金配置DTO - 用于前端数据传输
|
||||
/// </summary>
|
||||
public class ClientMarginConfigDto
|
||||
{
|
||||
public string EncryptId { get; set; }
|
||||
/// <summary>
|
||||
/// 配置ID
|
||||
/// </summary>
|
||||
public int id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 客户ID
|
||||
/// </summary>
|
||||
public int client_id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 客户名称
|
||||
/// </summary>
|
||||
public string client_name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 生效日期
|
||||
/// </summary>
|
||||
public DateTime value_date { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作人ID
|
||||
/// </summary>
|
||||
public int? OptId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作人姓名
|
||||
/// </summary>
|
||||
public string OptName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作时间
|
||||
/// </summary>
|
||||
public DateTime? OptDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 配置详情列表
|
||||
/// </summary>
|
||||
public List<ClientMarginDetailDto> details { get; set; } = new List<ClientMarginDetailDto>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace YLErp.Model
|
||||
{
|
||||
public class GetClientMarginDetailReq
|
||||
{
|
||||
public int clientId { get; set; }
|
||||
public string underlyingCode { get; set; }
|
||||
public DateTime valueDate { get; set;}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
using System.Data;
|
||||
using YLErp.Model;
|
||||
|
||||
namespace YLErp.Modules.MarginModule
|
||||
{
|
||||
public class ClientMarginConfigService : YLBaseService
|
||||
{
|
||||
public ClientMarginConfigService(OptUserInfo userInfo) : base(userInfo)
|
||||
{
|
||||
}
|
||||
|
||||
public void SaveMarginConfig(client_margin_config marginConfig)
|
||||
{
|
||||
if (marginConfig == null)
|
||||
{
|
||||
throw new ServiceException("无数据");
|
||||
}
|
||||
|
||||
var dbMarginConfig = new client_margin_config();
|
||||
dbMarginConfig.SetOpt(UserInfo);
|
||||
if (marginConfig.id != 0)
|
||||
{
|
||||
dbMarginConfig = DbContext.clientMarginConfig.Find(marginConfig.id);
|
||||
if (dbMarginConfig == null)
|
||||
{
|
||||
throw new ServiceException("未找到该记录");
|
||||
}
|
||||
dbMarginConfig.client_id = marginConfig.client_id;
|
||||
dbMarginConfig.value_date = marginConfig.value_date;
|
||||
|
||||
if (DbContext.clientMarginConfig.Any(o => o.client_id == marginConfig.client_id
|
||||
&& o.value_date == marginConfig.value_date
|
||||
&& o.id != marginConfig.id))
|
||||
{
|
||||
throw new ServiceException("同一客户不支持在同一生效日有多条记录!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dbMarginConfig = DbContext.clientMarginConfig.Where(o => o.client_id == marginConfig.client_id
|
||||
&& o.value_date == marginConfig.value_date).FirstOrDefault();
|
||||
if (dbMarginConfig == null)
|
||||
{
|
||||
marginConfig.SetOpt(UserInfo);
|
||||
DbContext.clientMarginConfig.Add(marginConfig);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ServiceException("同一客户不支持在同一生效日有多条记录");
|
||||
}
|
||||
}
|
||||
|
||||
DbContext.SaveChanges();
|
||||
}
|
||||
|
||||
public List<ClientMarginConfigDto> SearchList(ClientMarginConfigReq req)
|
||||
{
|
||||
var query = from config in DbContext.clientMarginConfig
|
||||
select new ClientMarginConfigDto
|
||||
{
|
||||
EncryptId=config.EncryptId,
|
||||
id = config.id,
|
||||
client_id = config.client_id,
|
||||
client_name = config.client_name,
|
||||
value_date = config.value_date,
|
||||
OptId = config.OptId,
|
||||
OptName = config.OptName,
|
||||
OptDate = config.OptDate
|
||||
};
|
||||
|
||||
if (req.ClientId > 0)
|
||||
{
|
||||
query = query.Where(o => o.client_id == req.ClientId);
|
||||
}
|
||||
|
||||
if (req.ValueDate.HasValue)
|
||||
{
|
||||
query = query.Where(o => o.value_date == req.ValueDate.Value);
|
||||
}
|
||||
|
||||
var configs = query.OrderByDescending(o=>o.id).ToList();
|
||||
|
||||
// 为每个配置加载5条固定期限的详情数据
|
||||
var fixedTerms = ConsMarginTerm.AllowedTerms.ToArray();
|
||||
foreach (var config in configs)
|
||||
{
|
||||
var existingDetails = DbContext.clientMarginDetail.Where(d => d.config_id == config.id).ToList();
|
||||
var detailDtos = new List<ClientMarginDetailDto>();
|
||||
|
||||
for (int i = 0; i < fixedTerms.Length; i++)
|
||||
{
|
||||
var term = fixedTerms[i];
|
||||
var existingDetail = existingDetails.FirstOrDefault(d => d.bond_term == term);
|
||||
if (existingDetail != null)
|
||||
{
|
||||
detailDtos.Add(new ClientMarginDetailDto
|
||||
{
|
||||
bond_term = existingDetail.bond_term,
|
||||
init_rate = existingDetail.init_rate * 100, // 转换为百分比显示
|
||||
maintain_rate = existingDetail.maintain_rate * 100, // 转换为百分比显示
|
||||
swap_days = existingDetail.swap_days
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
detailDtos.Add(new ClientMarginDetailDto
|
||||
{
|
||||
bond_term = term,
|
||||
init_rate = null,
|
||||
maintain_rate = null,
|
||||
swap_days = null
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 直接使用DTO格式返回给前端
|
||||
config.details = detailDtos;
|
||||
}
|
||||
|
||||
return configs;
|
||||
}
|
||||
|
||||
public void SaveMarginConfigWithDetails(client_margin_config config, List<client_margin_detail> details)
|
||||
{
|
||||
using (var trans = BeginTransaction())
|
||||
{
|
||||
try
|
||||
{
|
||||
// 保存主表
|
||||
SaveMarginConfig(config);
|
||||
|
||||
// 删除原有详情记录
|
||||
var existingDetails = DbContext.clientMarginDetail.Where(d => d.config_id == config.id).ToList();
|
||||
DbContext.clientMarginDetail.RemoveRange(existingDetails);
|
||||
|
||||
// 保存新的详情记录
|
||||
foreach (var detail in details)
|
||||
{
|
||||
detail.config_id = config.id;
|
||||
DbContext.clientMarginDetail.Add(detail);
|
||||
}
|
||||
|
||||
DbContext.SaveChanges();
|
||||
trans.Commit();
|
||||
}
|
||||
catch
|
||||
{
|
||||
trans.Rollback();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -437,7 +437,6 @@ namespace YLErp.Modules.SwapModule
|
||||
&& !ConsTrade.TradeCompleteStatus.Contains(t.TradeStatus)).ToList();
|
||||
var swapTradeIds = swaptrades.Select(s => s.id);
|
||||
var tradeExtends = DbContext.trade_extend.Where(x => swapTradeIds.Contains(x.TradeId));
|
||||
var clientMarginTemplates = DbContext.client_marginrate.Where(x => x.ValueDate <= valueDate).OrderByDescending(o => o.ValueDate).ToList();
|
||||
var restSwapTrades = new List<trade>();
|
||||
foreach (var swaptrade in swaptrades)
|
||||
{
|
||||
@@ -454,7 +453,7 @@ namespace YLErp.Modules.SwapModule
|
||||
int dealCount = 0;
|
||||
foreach (var groupItem in flowquery)
|
||||
{
|
||||
MergeRestModelItem(groupItem, restSwapTrades, swapPositions, floatRateQuery, clientMarginTemplates, ref dealCount, action);
|
||||
MergeRestModelItem(groupItem, restSwapTrades, swapPositions, floatRateQuery, ref dealCount, action);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
@@ -476,14 +475,13 @@ namespace YLErp.Modules.SwapModule
|
||||
&& !ConsTrade.TradeCompleteStatus.Contains(t.TradeStatus)).ToList();
|
||||
var swapTradeIds = swaptrades.Select(s => s.id);
|
||||
var swapPositions = DbContext.swap_position.Where(x => swapTradeIds.Contains(x.SwapTradeId) && x.PosiDirection > 0 && !x.IsInitial && x.PosiQuantity > 0 && !x.Invalid).ToList();
|
||||
var clientMarginTemplates = DbContext.client_marginrate.Where(x => x.ValueDate <= valueDate).OrderByDescending(o => o.ValueDate).ToList();
|
||||
var matuirityDate = QdpCalendarHelper.GetNonHolidayDefore(valueDate.AddDays(14));
|
||||
var floatRatePredicate = PredicateBuilder.Create<SwapFloatRate>(x => x.StartDate <= valueDate && x.EndDate >= matuirityDate);
|
||||
var floatRateQuery = DbContext.swap_float_rate.Where(floatRatePredicate);
|
||||
int dealCount = 0;
|
||||
foreach (var groupItem in flowquery)
|
||||
{
|
||||
MergeAvgModelItem(groupItem, swaptrades, swapPositions, floatRateQuery, clientMarginTemplates, ref dealCount, action);
|
||||
MergeAvgModelItem(groupItem, swaptrades, swapPositions, floatRateQuery, ref dealCount, action);
|
||||
}
|
||||
}
|
||||
public void UpdateSwapFlowState(List<swap_flow> swapFlows)
|
||||
@@ -507,8 +505,7 @@ namespace YLErp.Modules.SwapModule
|
||||
private void MergeRestModelItem(IGrouping<int?, swap_flow_merge> groupItem,
|
||||
List<trade> swaptrades,
|
||||
List<swap_position> swapPositions,
|
||||
IQueryable<SwapFloatRate> floatRateQuery,
|
||||
List<client_marginrate> client_Marginrates, ref int dealCount, Action<int>? action)
|
||||
IQueryable<SwapFloatRate> floatRateQuery, ref int dealCount, Action<int>? action)
|
||||
{
|
||||
var clientId = groupItem.Key;
|
||||
var client = DataCacheProvider.GetClientDataSource().GetData(clientId ?? 0);
|
||||
@@ -535,11 +532,6 @@ namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
throw new ServiceException($"{etradeRule.AssetAccount_0}的簿记账户未设置交易员");
|
||||
}
|
||||
var clientMarginTemplate = client_Marginrates.FirstOrDefault(x => x.ClientId == clientId);
|
||||
if (clientMarginTemplate == null)
|
||||
{
|
||||
clientMarginTemplate = client_Marginrates.FirstOrDefault(x => x.ClientId == 0);
|
||||
}
|
||||
var underlyingGroup = groupItem.GroupBy(g => g.UnderlyingCode);
|
||||
var clientSwapTrades = swaptrades.Where(x => x.ClientId == clientId).ToList();
|
||||
var clientSwapTradeIds = clientSwapTrades.Select(s => s.id);
|
||||
@@ -583,11 +575,11 @@ namespace YLErp.Modules.SwapModule
|
||||
}
|
||||
if (!hasPayPosition)//没有持仓
|
||||
{
|
||||
DealNoPosition(mergeList, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency, cashNeedAfter);
|
||||
DealNoPosition(mergeList, client, asset, underlying, floatRate, clearingAgency, cashNeedAfter);
|
||||
}
|
||||
else
|
||||
{
|
||||
DealHasPosition(mergeList, client, asset, underlying, floatRate, clientSwapPositionList, clientSwapTrades, clientMarginTemplate, clearingAgency, cashNeedAfter);
|
||||
DealHasPosition(mergeList, client, asset, underlying, floatRate, clientSwapPositionList, clientSwapTrades,clearingAgency, cashNeedAfter);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -607,8 +599,7 @@ namespace YLErp.Modules.SwapModule
|
||||
private void MergeAvgModelItem(IGrouping<int?, swap_flow_merge> groupItem,
|
||||
List<trade> swaptrades,
|
||||
List<swap_position> swapPositions,
|
||||
IQueryable<SwapFloatRate> floatRateQuery,
|
||||
List<client_marginrate> client_Marginrates, ref int dealCount, Action<int>? action)
|
||||
IQueryable<SwapFloatRate> floatRateQuery,ref int dealCount, Action<int>? action)
|
||||
{
|
||||
var clientId = groupItem.Key;
|
||||
var client = DataCacheProvider.GetClientDataSource().GetData(clientId ?? 0);
|
||||
@@ -619,11 +610,6 @@ namespace YLErp.Modules.SwapModule
|
||||
var clientSwapTrades = swaptrades.Where(x => x.ClientId == clientId).ToList();
|
||||
var clientSwapTradeIds = clientSwapTrades.Select(s => s.id);
|
||||
var clientSwapPositions = swapPositions.Where(x => clientSwapTradeIds.Contains(x.SwapTradeId));//现有客户持仓
|
||||
var clientMarginTemplate = client_Marginrates.FirstOrDefault(x => x.ClientId == clientId);
|
||||
if (clientMarginTemplate == null)
|
||||
{
|
||||
clientMarginTemplate = client_Marginrates.FirstOrDefault(x => x.ClientId == 0);
|
||||
}
|
||||
var underlyingCodes = underlyingGroup.Select(s => s.Key).ToList();
|
||||
var underlyings = DataCacheProvider.GetUnderlyingDataSource().AsQueryable(x => underlyingCodes.Contains(x.UnderlyingCode));
|
||||
foreach (var underlyingGroupItem in underlyingGroup)
|
||||
@@ -643,11 +629,11 @@ namespace YLErp.Modules.SwapModule
|
||||
if (!hasPayPosition)//没有持仓
|
||||
{
|
||||
var bsType = mergeList.OrderBy(o => o.OptTime).First().BsType;
|
||||
AvgDealNoPosition(mergeList, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency, bsType);
|
||||
AvgDealNoPosition(mergeList, client, asset, underlying, floatRate, clearingAgency, bsType);
|
||||
}
|
||||
else
|
||||
{
|
||||
AvgDealHasPosition(mergeList, client, asset, underlying, floatRate, clientSwapPositionList, clientSwapTrades, clientMarginTemplate, clearingAgency);
|
||||
AvgDealHasPosition(mergeList, client, asset, underlying, floatRate, clientSwapPositionList, clientSwapTrades, clearingAgency);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -667,7 +653,6 @@ namespace YLErp.Modules.SwapModule
|
||||
AssetUnit asset,
|
||||
underlying_manager underlying,
|
||||
SwapFloatRate floatRate,
|
||||
client_marginrate clientMarginTemplate,
|
||||
string clearingAgency,
|
||||
bool cashNeedAfter)
|
||||
{
|
||||
@@ -675,7 +660,7 @@ namespace YLErp.Modules.SwapModule
|
||||
swap_flow_merge flowMergeMax = mergeOrderList.First();//先开最早的一条
|
||||
swap_flow_merge flowMergeMin = mergeOrderList.Last();
|
||||
var swapTradeService = new SwapTradeService(UserInfo);
|
||||
var trade = swapTradeService.NewSwapTrade(flowMergeMax, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency, cashNeedAfter: cashNeedAfter);
|
||||
var trade = swapTradeService.NewSwapTrade(flowMergeMax, client, asset, underlying, floatRate, clearingAgency, cashNeedAfter: cashNeedAfter);
|
||||
flowMergeMax.SwapTradeNo = trade.TradeNumber;
|
||||
flowMergeMin.SwapTradeNo = trade.TradeNumber;
|
||||
if (mergeList.Count == 2)//有两条流水
|
||||
@@ -707,7 +692,7 @@ namespace YLErp.Modules.SwapModule
|
||||
var posi = DbContext.swap_position.FirstOrDefault(x => x.SwapTradeId == trade.id && x.PosiDirection > 0 && !x.IsInitial);
|
||||
SetNewOpenData(flowMergeMin, flowMergeClone, posi);
|
||||
}
|
||||
var trade2 = swapTradeService.NewSwapTrade(flowMergeClone, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency);
|
||||
var trade2 = swapTradeService.NewSwapTrade(flowMergeClone, client, asset, underlying, floatRate, clearingAgency);
|
||||
flowMergeMax.SwapTradeNo = trade2.TradeNumber;
|
||||
flowMergeMin.SwapTradeNo = trade2.TradeNumber;
|
||||
}
|
||||
@@ -728,7 +713,6 @@ namespace YLErp.Modules.SwapModule
|
||||
AssetUnit asset,
|
||||
underlying_manager underlying,
|
||||
SwapFloatRate floatRate,
|
||||
client_marginrate clientMarginTemplate,
|
||||
string clearingAgency,
|
||||
int byType)
|
||||
{
|
||||
@@ -736,9 +720,9 @@ namespace YLErp.Modules.SwapModule
|
||||
var sameFlow = swapFlows.Where(x => x.BsType == byType).FirstOrDefault();
|
||||
if (negativeFlow==null)
|
||||
{
|
||||
return NewSwapTrade(sameFlow, client, asset, underlying, floatRate, clientMarginTemplate,clearingAgency);
|
||||
return NewSwapTrade(sameFlow, client, asset, underlying, floatRate,clearingAgency);
|
||||
}
|
||||
return DealTwoDirectionFlows(sameFlow, negativeFlow, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency);
|
||||
return DealTwoDirectionFlows(sameFlow, negativeFlow, client, asset, underlying, floatRate, clearingAgency);
|
||||
}
|
||||
/// <summary>
|
||||
/// 当前无持仓,且有2个方向流水合成簿记
|
||||
@@ -758,7 +742,6 @@ namespace YLErp.Modules.SwapModule
|
||||
AssetUnit asset,
|
||||
underlying_manager underlying,
|
||||
SwapFloatRate floatRate,
|
||||
client_marginrate clientMarginTemplate,
|
||||
string clearingAgency)
|
||||
{
|
||||
var sameQty = sameFlow.TradingQty;
|
||||
@@ -771,7 +754,7 @@ namespace YLErp.Modules.SwapModule
|
||||
sameFlowClone= DataHelper.DeepCopyObject(negativeFlow);
|
||||
negaFlowClone = DataHelper.DeepCopyObject(sameFlow);
|
||||
}
|
||||
var trade = NewSwapTrade(sameFlowClone, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency);
|
||||
var trade = NewSwapTrade(sameFlowClone, client, asset, underlying, floatRate, clearingAgency);
|
||||
// 平仓
|
||||
new SwapDealService(UserInfo).AuotoSwapUnwind(trade.id,
|
||||
negaFlowClone.TradingAmountAvg,
|
||||
@@ -799,11 +782,10 @@ namespace YLErp.Modules.SwapModule
|
||||
AssetUnit asset,
|
||||
underlying_manager underlying,
|
||||
SwapFloatRate floatRate,
|
||||
client_marginrate clientMarginTemplate,
|
||||
string clearingAgency)
|
||||
{
|
||||
var swapTradeService = new SwapTradeService(UserInfo);
|
||||
var trade = swapTradeService.NewSwapTrade(flowMergeFirst, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency, LongShortStructType);
|
||||
var trade = swapTradeService.NewSwapTrade(flowMergeFirst, client, asset, underlying, floatRate, clearingAgency, LongShortStructType);
|
||||
flowMergeFirst.SwapTradeNo = trade.TradeNumber;
|
||||
flowMergeFirst.SwapTradeId = trade.id;
|
||||
DbContext.SaveChanges();
|
||||
@@ -889,17 +871,16 @@ namespace YLErp.Modules.SwapModule
|
||||
SwapFloatRate floatRate,
|
||||
List<swap_position> clientSwapPositionList,
|
||||
List<trade> clientSwapTrades,
|
||||
client_marginrate clientMarginTemplate,
|
||||
string clearingAgency,
|
||||
bool cashNeedAfter)
|
||||
{
|
||||
if (mergeList.Count == 1)//只有一条流水情况
|
||||
{
|
||||
DealSingleFlow(mergeList, clientSwapPositionList, clientSwapTrades, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency);
|
||||
DealSingleFlow(mergeList, clientSwapPositionList, clientSwapTrades, client, asset, underlying, floatRate,clearingAgency);
|
||||
}
|
||||
else
|
||||
{
|
||||
DealDoubleFlow(mergeList, clientSwapPositionList, clientSwapTrades, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency, cashNeedAfter);
|
||||
DealDoubleFlow(mergeList, clientSwapPositionList, clientSwapTrades, client, asset, underlying, floatRate, clearingAgency, cashNeedAfter);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
@@ -920,17 +901,16 @@ namespace YLErp.Modules.SwapModule
|
||||
SwapFloatRate floatRate,
|
||||
List<swap_position> clientSwapPositionList,
|
||||
List<trade> clientSwapTrades,
|
||||
client_marginrate clientMarginTemplate,
|
||||
string clearingAgency)
|
||||
{
|
||||
var firstFlow = flowList.First();
|
||||
if (flowList.Count==1)//只有一条流水情况
|
||||
{
|
||||
AvgDealSingleFlow(firstFlow, clientSwapPositionList, clientSwapTrades, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency);
|
||||
AvgDealSingleFlow(firstFlow, clientSwapPositionList, clientSwapTrades, client, asset, underlying, floatRate, clearingAgency);
|
||||
}
|
||||
else
|
||||
{
|
||||
AvgDealDoubleFlow(flowList, clientSwapPositionList, clientSwapTrades, client, asset, underlying, floatRate, clientMarginTemplate,clearingAgency);
|
||||
AvgDealDoubleFlow(flowList, clientSwapPositionList, clientSwapTrades, client, asset, underlying, floatRate,clearingAgency);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
@@ -951,7 +931,6 @@ namespace YLErp.Modules.SwapModule
|
||||
AssetUnit asset,
|
||||
underlying_manager underlying,
|
||||
SwapFloatRate floatRate,
|
||||
client_marginrate clientMarginTemplate,
|
||||
string clearingAgency)
|
||||
{
|
||||
var swapTradeService = new SwapTradeService(UserInfo);
|
||||
@@ -978,13 +957,13 @@ namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
SetNewOpenData(flowMergeMax, flowMergeClone, dealResult.Item4);
|
||||
}
|
||||
var trade = swapTradeService.NewSwapTrade(flowMergeClone, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency);
|
||||
var trade = swapTradeService.NewSwapTrade(flowMergeClone, client, asset, underlying, floatRate, clearingAgency);
|
||||
flowMergeMax.SwapTradeNo = trade.TradeNumber;
|
||||
}
|
||||
}
|
||||
else //只存在同向交易
|
||||
{
|
||||
var trade = swapTradeService.NewSwapTrade(flowMergeMax, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency);
|
||||
var trade = swapTradeService.NewSwapTrade(flowMergeMax, client, asset, underlying, floatRate, clearingAgency);
|
||||
flowMergeMax.SwapTradeNo = trade.TradeNumber;
|
||||
}
|
||||
}
|
||||
@@ -1027,7 +1006,6 @@ namespace YLErp.Modules.SwapModule
|
||||
AssetUnit asset,
|
||||
underlying_manager underlying,
|
||||
SwapFloatRate floatRate,
|
||||
client_marginrate clientMarginTemplate,
|
||||
string clearingAgency,
|
||||
bool cashNeedAfter)
|
||||
{
|
||||
@@ -1049,22 +1027,22 @@ namespace YLErp.Modules.SwapModule
|
||||
var negTrades = clientSwapTrades.Where(x => negTradeIds.Contains(x.id)).ToList();//取出与第一条流水方向相反的交易
|
||||
|
||||
//先处理第一条流水的反向持仓
|
||||
var firstTrade = DealDoubleFlowDetial(negTrades, negDirectionPositions, flowMergeFirstClone, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency, true, cashNeedAfter);
|
||||
var firstTrade = DealDoubleFlowDetial(negTrades, negDirectionPositions, flowMergeFirstClone, client, asset, underlying, floatRate, clearingAgency, true, cashNeedAfter);
|
||||
flowMergeFirst.SwapTradeNo = flowMergeFirstClone.SwapTradeNo;
|
||||
|
||||
//再处理第二条流水的反向持仓
|
||||
var lastTrade = DealDoubleFlowDetial(sameTrades, sameDirectionPositions, flowMergeLastClone, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency, false,false);
|
||||
var lastTrade = DealDoubleFlowDetial(sameTrades, sameDirectionPositions, flowMergeLastClone, client, asset, underlying, floatRate, clearingAgency, false,false);
|
||||
flowMergeLast.SwapTradeNo = flowMergeLastClone.SwapTradeNo;
|
||||
if (flowMergeFirstClone.BsType != flowMergeLastClone.BsType && firstTrade != null)
|
||||
{
|
||||
var trades = new List<trade> { firstTrade };
|
||||
var positions = DbContext.swap_position.Where(x => x.SwapTradeId == firstTrade.id && x.PosiDirection > 0 && !x.IsInitial && !x.Invalid).ToList();
|
||||
DealDoubleFlowDetial(trades, positions, flowMergeLastClone, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency, true,false);
|
||||
DealDoubleFlowDetial(trades, positions, flowMergeLastClone, client, asset, underlying, floatRate, clearingAgency, true,false);
|
||||
flowMergeLast.SwapTradeNo = flowMergeLastClone.SwapTradeNo;
|
||||
}
|
||||
else if (lastTrade==null)
|
||||
{
|
||||
swapTradeService.NewSwapTrade(flowMergeLastClone, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency);
|
||||
swapTradeService.NewSwapTrade(flowMergeLastClone, client, asset, underlying, floatRate, clearingAgency);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
@@ -1085,7 +1063,6 @@ namespace YLErp.Modules.SwapModule
|
||||
AssetUnit asset,
|
||||
underlying_manager underlying,
|
||||
SwapFloatRate floatRate,
|
||||
client_marginrate clientMarginTemplate,
|
||||
string clearingAgency)
|
||||
{
|
||||
var swapTradeService = new SwapTradeService(UserInfo);
|
||||
@@ -1097,11 +1074,11 @@ namespace YLErp.Modules.SwapModule
|
||||
// 同向新开
|
||||
if (flow.BsType== firstPosi.PositionType)
|
||||
{
|
||||
NewSwapTrade(flowClone, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency);
|
||||
NewSwapTrade(flowClone, client, asset, underlying, floatRate, clearingAgency);
|
||||
}
|
||||
else //反向先平仓,有剩余开仓
|
||||
{
|
||||
AvgDealUnwind(flowClone, clientSwapTrades, swapPositions, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency);
|
||||
AvgDealUnwind(flowClone, clientSwapTrades, swapPositions, client, asset, underlying, floatRate, clearingAgency);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
@@ -1122,7 +1099,6 @@ namespace YLErp.Modules.SwapModule
|
||||
AssetUnit asset,
|
||||
underlying_manager underlying,
|
||||
SwapFloatRate floatRate,
|
||||
client_marginrate clientMarginTemplate,
|
||||
string clearingAgency)
|
||||
{
|
||||
var swapTradeService = new SwapTradeService(UserInfo);
|
||||
@@ -1137,7 +1113,7 @@ namespace YLErp.Modules.SwapModule
|
||||
var flowSameClone = DataHelper.DeepCopyObject(flowSame);
|
||||
var flowNegClone = DataHelper.DeepCopyObject(flowNeg);
|
||||
//先平反向
|
||||
var trade= AvgDealUnwind(flowNegClone, clientSwapTrades, swapPositions, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency);
|
||||
var trade= AvgDealUnwind(flowNegClone, clientSwapTrades, swapPositions, client, asset, underlying, floatRate, clearingAgency);
|
||||
var newFlowList = new List<swap_flow>();
|
||||
if (trade!=null)
|
||||
{
|
||||
@@ -1162,12 +1138,12 @@ namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
flowSameClone.TradingQty = flowQty;
|
||||
flowSameClone.TradingAmount = flowSameClone.TradingQty;
|
||||
NewSwapTrade(flowSameClone, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency);
|
||||
NewSwapTrade(flowSameClone, client, asset, underlying, floatRate, clearingAgency);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NewSwapTrade(flowSameClone, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency);
|
||||
NewSwapTrade(flowSameClone, client, asset, underlying, floatRate, clearingAgency);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
@@ -1190,7 +1166,6 @@ namespace YLErp.Modules.SwapModule
|
||||
AssetUnit asset,
|
||||
underlying_manager underlying,
|
||||
SwapFloatRate floatRate,
|
||||
client_marginrate clientMarginTemplate,
|
||||
string clearingAgency)
|
||||
{
|
||||
var swapTradeService = new SwapTradeService(UserInfo);
|
||||
@@ -1227,7 +1202,7 @@ namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
swapFlow.TradingQty = flowQty;
|
||||
swapFlow.TradingAmount = swapFlow.TradingQty* swapFlow.TradingAmountAvg;
|
||||
return NewSwapTrade(swapFlow, client, asset, underlying, floatRate, clientMarginTemplate,clearingAgency);
|
||||
return NewSwapTrade(swapFlow, client, asset, underlying, floatRate, clearingAgency);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -1251,7 +1226,6 @@ namespace YLErp.Modules.SwapModule
|
||||
AssetUnit asset,
|
||||
underlying_manager underlying,
|
||||
SwapFloatRate floatRate,
|
||||
client_marginrate clientMarginTemplate,
|
||||
string clearingAgency,
|
||||
bool needOpen,
|
||||
bool cashNeedAfter
|
||||
@@ -1269,7 +1243,7 @@ namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
SetNewOpenData(flowMergeMax, flowMergeSameClone, dealResult.Item4);
|
||||
}
|
||||
return swapTradeService.NewSwapTrade(flowMergeSameClone, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency);
|
||||
return swapTradeService.NewSwapTrade(flowMergeSameClone, client, asset, underlying, floatRate, clearingAgency);
|
||||
}
|
||||
return null;
|
||||
|
||||
|
||||
@@ -247,12 +247,12 @@ namespace YLErp.Modules.SwapModule
|
||||
AssetUnit asset,
|
||||
underlying_manager underlying,
|
||||
SwapFloatRate swapFloatRate,
|
||||
client_marginrate clientMarginTemplate,
|
||||
string clearingAgency,
|
||||
string structureType = "普通债券类收益互换",
|
||||
bool cashNeedAfter = false)
|
||||
{
|
||||
var td = PrepareTrade(flowMerge, client, asset, underlying, clientMarginTemplate?.SwapEndDays??14, structureType);
|
||||
int SwapEndDays = UnderlyingHelper.GetApplicableMarginRate(client.id,underlying.UnderlyingCode,flowMerge.OccurTime)?.swap_days??14;
|
||||
var td = PrepareTrade(flowMerge, client, asset, underlying, SwapEndDays, structureType);
|
||||
PrepareTradeExtend(flowMerge, td, underlying, swapFloatRate);
|
||||
td.TradeNumber = BizLogicSingleton.Instance.GenerateTradeNumberBeforeConfirm(td, DbContext);
|
||||
flowMerge.SwapTradeNo = td.TradeNumber;
|
||||
|
||||
@@ -6,12 +6,14 @@ using System.Text;
|
||||
using YieldChain.Helpers;
|
||||
using YLErp.BLL;
|
||||
using YLErp.BLL.Eod;
|
||||
using YLErp.DBModels;
|
||||
using YLErp.DBModels.Enums;
|
||||
using YLErp.Model.Enum;
|
||||
using YLErp.Modules.RiskModule;
|
||||
using YLErp.Modules.SalesModule;
|
||||
using YLErp.Modules.SwapModule;
|
||||
using YLErp.Modules.SystemModule;
|
||||
using YLErp.Modules.UnderlyingModule;
|
||||
|
||||
namespace YLErp.Modules.TradeModule.DealModule
|
||||
{
|
||||
@@ -391,18 +393,12 @@ namespace YLErp.Modules.TradeModule.DealModule
|
||||
/// <param name="td"></param>
|
||||
private void InitTradeReport(trade td,int marginDiretion)
|
||||
{
|
||||
var clientMarginTemplates = DbContext.client_marginrate.Where(x => x.ValueDate <= td.TradeDate).OrderByDescending(o => o.ValueDate).AsNoTracking().ToList();
|
||||
var marinRate = clientMarginTemplates.Where(x => x.ClientId == td.ClientId).FirstOrDefault();
|
||||
var marinRate= UnderlyingHelper.GetApplicableMarginRate(td.ClientId, td.UnderlyingCode, td.TradeDate.Value);
|
||||
if (marinRate == null)
|
||||
{
|
||||
marinRate = clientMarginTemplates.Where(x => x.ClientId == 0).FirstOrDefault();
|
||||
}
|
||||
if (marinRate == null)
|
||||
{
|
||||
marinRate = new client_marginrate
|
||||
{
|
||||
InitMarginRate = 1,
|
||||
MaintenanceRate = 1,
|
||||
marinRate = new client_margin_detail() {
|
||||
init_rate = 1,
|
||||
maintain_rate = 1
|
||||
};
|
||||
}
|
||||
var trade_Report = DbContext.trade_Report.Where(x => x.TradeId == td.id).FirstOrDefault();
|
||||
@@ -420,8 +416,8 @@ namespace YLErp.Modules.TradeModule.DealModule
|
||||
trade_Report.IsPerformanceCollInterest = "否";
|
||||
trade_Report.PerformanceCollType = "0";
|
||||
trade_Report.PerformanceCollRange = "0";
|
||||
trade_Report.InitialPerformanceColl = (decimal)marinRate.InitMarginRate*100;
|
||||
trade_Report.RecoveryPerformanceColl = (decimal)marinRate.MaintenanceRate * 100;
|
||||
trade_Report.InitialPerformanceColl = (decimal)marinRate.init_rate*100;
|
||||
trade_Report.RecoveryPerformanceColl = (decimal)marinRate.maintain_rate * 100;
|
||||
trade_Report.UnwindPerformanceColl = trade_Report.RecoveryPerformanceColl;
|
||||
trade_Report.EodPerformanceColl = trade_Report.RecoveryPerformanceColl;
|
||||
trade_Report.PerformanceRemark = "详见交易确认书及定义性文件";
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using YieldChain.Commons;
|
||||
using YLErp.Cache;
|
||||
using YLErp.Commons;
|
||||
using YLErp.DBModels;
|
||||
|
||||
namespace YLErp.Modules.UnderlyingModule
|
||||
{
|
||||
@@ -7,6 +14,11 @@ namespace YLErp.Modules.UnderlyingModule
|
||||
/// </summary>
|
||||
public static class UnderlyingHelper
|
||||
{
|
||||
readonly static IYLCache ylCache;
|
||||
static UnderlyingHelper()
|
||||
{
|
||||
ylCache = YLServiceLocator.ServiceProvider.GetService<IYLCache>();
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取组合标的品种对象
|
||||
/// </summary>
|
||||
@@ -59,6 +71,115 @@ namespace YLErp.Modules.UnderlyingModule
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 根据客户ID、标的代码和计算日期计算适用的保证金率
|
||||
/// </summary>
|
||||
/// <param name="clientId">客户ID</param>
|
||||
/// <param name="underlyingCode">标的代码</param>
|
||||
/// <param name="valueDate">计算日期</param>
|
||||
/// <returns>适用的保证金率配置,包含初始保证金率和维持保证金率</returns>
|
||||
public static client_margin_detail GetApplicableMarginRate(int clientId, string underlyingCode, DateTime valueDate)
|
||||
{
|
||||
if (string.IsNullOrEmpty(underlyingCode))
|
||||
return null;
|
||||
|
||||
string cacheKey = $"Otc:ClientMarginRate:{clientId}:{underlyingCode}:{valueDate:yyyyMMdd}";
|
||||
try
|
||||
{
|
||||
if (ylCache!=null)
|
||||
{
|
||||
// 1. 先查Redis
|
||||
var cacheDetail = ylCache.StringGetWithNoPrefix<client_margin_detail>(cacheKey);
|
||||
if (cacheDetail != null)
|
||||
return cacheDetail;
|
||||
}
|
||||
|
||||
|
||||
// 2. 查数据库
|
||||
string applicableTerm = GetApplicableMarginTerm(underlyingCode, valueDate);
|
||||
var marginConfig = GetClientMarginConfig(clientId, valueDate);
|
||||
if (marginConfig == null)
|
||||
return null;
|
||||
var marginDetail = GetMarginDetailByTerm(marginConfig.id, applicableTerm);
|
||||
|
||||
// 3. 放入Redis
|
||||
if (marginDetail != null&& ylCache != null)
|
||||
{
|
||||
ylCache.StringSetWithNoPrefix<client_margin_detail>(cacheKey, marginDetail,TimeSpan.FromHours(3));
|
||||
}
|
||||
|
||||
return marginDetail;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogFactory.GetLogger("UnderlyingHelper").Error($"计算保证金率时发生异常: {ex.Message}", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取客户保证金配置
|
||||
/// </summary>
|
||||
/// <param name="clientId">客户ID</param>
|
||||
/// <param name="valueDate">估值日期</param>
|
||||
/// <returns>客户保证金配置</returns>
|
||||
private static client_margin_config GetClientMarginConfig(int clientId, DateTime valueDate)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var context = DbContextFactory.GetYLDbContext())
|
||||
{
|
||||
// 查找指定日期有效的客户保证金配置
|
||||
var config = context.clientMarginConfig
|
||||
.Where(c => c.client_id == clientId && c.value_date <= valueDate)
|
||||
.OrderByDescending(c => c.value_date)
|
||||
.FirstOrDefault();
|
||||
// 找不到,查找通用的那条
|
||||
if (config==null)
|
||||
{
|
||||
config = context.clientMarginConfig
|
||||
.Where(c => c.client_id == 0 && c.value_date <= valueDate)
|
||||
.OrderByDescending(c => c.value_date)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
return config;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"获取客户保证金配置时发生异常: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据配置ID和期限获取保证金率详情
|
||||
/// </summary>
|
||||
/// <param name="configId">配置ID</param>
|
||||
/// <param name="bondTerm">债券期限</param>
|
||||
/// <returns>保证金率详情</returns>
|
||||
private static client_margin_detail GetMarginDetailByTerm(int configId, string bondTerm)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var context = DbContextFactory.GetYLDbContext())
|
||||
{
|
||||
var details = context.clientMarginDetail
|
||||
.Where(d => d.config_id == configId).ToList();
|
||||
var detail = details.FirstOrDefault(d => d.bond_term == bondTerm);
|
||||
if (detail==null&& !string.IsNullOrEmpty(bondTerm))
|
||||
{
|
||||
detail=details.FirstOrDefault(d => string.IsNullOrEmpty(d.bond_term));
|
||||
}
|
||||
return detail;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"获取保证金率详情时发生异常: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取篮子标的品种对象
|
||||
@@ -124,5 +245,149 @@ namespace YLErp.Modules.UnderlyingModule
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取债券的期限,计算日期一定要小于原日期
|
||||
/// </summary>
|
||||
/// <param name="calcDate">计算日期</param>
|
||||
/// <param name="sourceDate">原日期</param>
|
||||
/// <returns>返回3.12格式</returns>
|
||||
public static double? getYearTerm(DateTime? calcDate, DateTime? sourceDate)
|
||||
{
|
||||
if (!calcDate.HasValue)
|
||||
{
|
||||
return null;//不限
|
||||
}
|
||||
if (sourceDate.HasValue)
|
||||
{
|
||||
if (calcDate > sourceDate)
|
||||
{
|
||||
throw new ArgumentException("计算日期必须小于原日期", nameof(calcDate));
|
||||
}
|
||||
//计算sourceDate与calcDate年限差
|
||||
int yearDiff = sourceDate.Value.Year - calcDate.Value.Year;
|
||||
int monthDiff = sourceDate.Value.Month - calcDate.Value.Month;
|
||||
int dayDiff = sourceDate.Value.Day - calcDate.Value.Day;
|
||||
|
||||
// 如果月差为负,说明还没到整年,需要向前借一年
|
||||
if (monthDiff < 0)
|
||||
{
|
||||
yearDiff -= 1;
|
||||
monthDiff += 12;
|
||||
}
|
||||
|
||||
// 如果天数为负,说明还没到整月,需要向前借一个月
|
||||
if (dayDiff < 0)
|
||||
{
|
||||
monthDiff -= 1;
|
||||
if (monthDiff < 0)
|
||||
{
|
||||
yearDiff -= 1;
|
||||
monthDiff += 12;
|
||||
}
|
||||
}
|
||||
|
||||
double yearTerm = yearDiff + monthDiff / 12.0;
|
||||
return yearTerm;
|
||||
}
|
||||
return null;//不限
|
||||
}
|
||||
/// <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年 |
|
||||
/// </summary>
|
||||
/// <param name="underlyingCode">标的代码</param>
|
||||
/// <param name="valueDate">估值日期</param>
|
||||
/// <returns>适用的保证金率期限,如果不适用则返回空字符串</returns>
|
||||
public static string GetApplicableMarginTerm(string underlyingCode, DateTime valueDate)
|
||||
{
|
||||
if (string.IsNullOrEmpty(underlyingCode))
|
||||
return ConsMarginTerm.Default;
|
||||
|
||||
try
|
||||
{
|
||||
// 获取标的信息
|
||||
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);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 记录异常日志
|
||||
Console.WriteLine($"计算保证金率期限时发生异常: {ex.Message}");
|
||||
return ConsMarginTerm.Default;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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时视为“不限”
|
||||
/// </summary>
|
||||
private static string DetermineMarginTerm(double? issueYears, double? remainingYears)
|
||||
{
|
||||
// (0.00,2.00]年 不限 2年
|
||||
if (remainingYears > 0.00 && remainingYears <= 2.00)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
return ConsMarginTerm.ThirtyYear;
|
||||
|
||||
// 其他情况返回默认
|
||||
return ConsMarginTerm.Default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
{Name:"互换簿记预设",Rights:["互换簿记预设"],Icon:"menu-icon iconfour"
|
||||
,SubItems:[
|
||||
{Name:"TRS用簿记账户设置",Rights:["互换簿记预设-TRS用簿记账户设置"],Url:"EtradingRule/Index"},
|
||||
{Name:"互换预付金率维护",Rights:["互换簿记预设-互换预付金率维护"],Url:"MarginRateSwap/MarginRateSwapList"},
|
||||
{Name:"互换预付金率维护",Rights:["互换簿记预设-互换预付金率维护"],Url:"ClientMarginConfig/ClientMarginConfigList"},
|
||||
{Name:"阶梯费率",Rights:["互换簿记预设-阶梯费率"],Url:"SwapRate"},
|
||||
{Name:"浮动利率",Rights:["互换簿记预设-浮动利率"],Url:"SwapFloatRate"},
|
||||
{Name:"簿记账户与衡泰关系",Rights:["互换簿记预设-簿记账户与衡泰关系"],Url:"EtradeAccount/Index"},
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using YLErp.Cache;
|
||||
using YLErp.Modules.MarginModule;
|
||||
|
||||
namespace YLErp.Web.Controllers
|
||||
{
|
||||
public class ClientMarginConfigController : BaseController
|
||||
{
|
||||
private IYLCache _cache;
|
||||
public ClientMarginConfigController(IYLCache cache)
|
||||
{
|
||||
_cache = cache;
|
||||
}
|
||||
[MyAuthorize("互换簿记预设-互换预付金率维护")]
|
||||
public ActionResult ClientMarginConfigList()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
[MyAuthorize("互换簿记预设-互换预付金率修改")]
|
||||
public ActionResult ClientMarginConfigEdit(string enid)
|
||||
{
|
||||
var id = DecryptInt(enid);
|
||||
var fixedTerms = ConsMarginTerm.AllowedTerms.ToArray();
|
||||
if (id == 0)
|
||||
{
|
||||
// 新增时,创建空的DTO记录
|
||||
var emptyDetails = new List<ClientMarginDetailDto>();
|
||||
|
||||
for (int i = 0; i < fixedTerms.Length; i++)
|
||||
{
|
||||
emptyDetails.Add(new ClientMarginDetailDto
|
||||
{
|
||||
bond_term = fixedTerms[i],
|
||||
init_rate = null,
|
||||
maintain_rate = null,
|
||||
swap_days = null
|
||||
});
|
||||
}
|
||||
ViewBag.Details = emptyDetails;
|
||||
return View(new client_margin_config() { value_date = DateTime.Now });
|
||||
}
|
||||
var marginConfig = yldb.clientMarginConfig.Find(id);
|
||||
|
||||
// 获取详情数据并转换为DTO
|
||||
var details = yldb.clientMarginDetail.Where(d => d.config_id == id).ToList();
|
||||
var detailDtos = new List<ClientMarginDetailDto>();
|
||||
|
||||
// 确保返回5条数据,按固定顺序
|
||||
for (int i = 0; i < fixedTerms.Count(); i++)
|
||||
{
|
||||
var term = fixedTerms[i];
|
||||
var existingDetail = details.FirstOrDefault(d => d.bond_term == term);
|
||||
if (existingDetail != null)
|
||||
{
|
||||
detailDtos.Add(new ClientMarginDetailDto
|
||||
{
|
||||
bond_term = existingDetail.bond_term,
|
||||
init_rate = existingDetail.init_rate * 100, // 转换为百分比显示
|
||||
maintain_rate = existingDetail.maintain_rate * 100, // 转换为百分比显示
|
||||
swap_days = existingDetail.swap_days
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
detailDtos.Add(new ClientMarginDetailDto
|
||||
{
|
||||
bond_term = term,
|
||||
init_rate = null,
|
||||
maintain_rate = null,
|
||||
swap_days = null
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ViewBag.Details = detailDtos;
|
||||
|
||||
return View(marginConfig);
|
||||
}
|
||||
|
||||
|
||||
|
||||
[MyAuthorize("互换簿记预设-互换预付金率修改")]
|
||||
public JsonResult ClientMarginConfigDelete(string enid)
|
||||
{
|
||||
var id = DecryptInt(enid);
|
||||
var marginConfig = yldb.clientMarginConfig.Find(id);
|
||||
if (marginConfig == null)
|
||||
{
|
||||
throw new ServiceException("数据库中未找到");
|
||||
}
|
||||
|
||||
// 删除详情记录
|
||||
var details = yldb.clientMarginDetail.Where(d => d.config_id == id).ToList();
|
||||
yldb.clientMarginDetail.RemoveRange(details);
|
||||
|
||||
// 删除主记录
|
||||
yldb.clientMarginConfig.Remove(marginConfig);
|
||||
yldb.SaveChanges();
|
||||
if (_cache!=null)
|
||||
{
|
||||
// 删除缓存ClientMarginRate开头的key
|
||||
_cache.BatchDelete("ClientMarginRate:*");
|
||||
}
|
||||
return JsonSuccess("已删除");
|
||||
}
|
||||
|
||||
[MyAuthorize("互换簿记预设-互换预付金率修改")]
|
||||
public JsonResult ClientMarginConfigEditJson(client_margin_config marginConfig, List<ClientMarginDetailDto> details)
|
||||
{
|
||||
// 将DTO转换为数据库实体,处理必填和可选逻辑
|
||||
var detailEntities = new List<client_margin_detail>();
|
||||
var hasDefaultTerm = false;
|
||||
|
||||
if (details != null)
|
||||
{
|
||||
foreach (var dto in details)
|
||||
{
|
||||
// 默认期限(空字符串)必填
|
||||
if (string.IsNullOrEmpty(dto.bond_term))
|
||||
{
|
||||
if (!dto.init_rate.HasValue || !dto.maintain_rate.HasValue)
|
||||
{
|
||||
return JsonError("默认期限的初始保证金率和维持保证金率必须填写");
|
||||
}
|
||||
hasDefaultTerm = true;
|
||||
|
||||
detailEntities.Add(new client_margin_detail
|
||||
{
|
||||
bond_term = "",
|
||||
init_rate = dto.init_rate.Value / 100, // 转换百分比
|
||||
maintain_rate = dto.maintain_rate.Value / 100, // 转换百分比
|
||||
swap_days = dto.swap_days ?? 0
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// 其他期限非必填,但如果填写了任何字段就需要验证
|
||||
if (dto.init_rate.HasValue || dto.maintain_rate.HasValue || dto.swap_days.HasValue)
|
||||
{
|
||||
// 如果swap_days有值,则init_rate和maintain_rate必须有值
|
||||
if (dto.swap_days.HasValue && (!dto.init_rate.HasValue || !dto.maintain_rate.HasValue))
|
||||
{
|
||||
return JsonError($"期限\"{dto.bond_term}\":当互换默认天数有值时,初始保证金率和维持保证金率必须填写");
|
||||
}
|
||||
|
||||
detailEntities.Add(new client_margin_detail
|
||||
{
|
||||
bond_term = dto.bond_term,
|
||||
init_rate = (dto.init_rate ?? 0) / 100, // 转换百分比
|
||||
maintain_rate = (dto.maintain_rate ?? 0) / 100, // 转换百分比
|
||||
swap_days = dto.swap_days ?? 0
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 验证默认期限必须填写
|
||||
if (!hasDefaultTerm)
|
||||
{
|
||||
return JsonError("默认期限的保证金配置信息必须填写");
|
||||
}
|
||||
|
||||
new ClientMarginConfigService(CurUser).SaveMarginConfigWithDetails(marginConfig, detailEntities);
|
||||
if (_cache != null)
|
||||
{
|
||||
// 删除缓存ClientMarginRate开头的key
|
||||
_cache.BatchDelete("ClientMarginRate:*");
|
||||
}
|
||||
return JsonSuccess("已修改");
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public JsonResult ClientMarginConfigQuery(ClientMarginConfigReq req)
|
||||
{
|
||||
var sList = new ClientMarginConfigService(CurUser).SearchList(req);
|
||||
GetExtendInfo(sList);
|
||||
return JsonSuccess("",sList);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public JsonResult GetMarginConfigDetails(int configId)
|
||||
{
|
||||
var details = yldb.clientMarginDetail.Where(d => d.config_id == configId).ToList();
|
||||
// 转换为百分比显示
|
||||
foreach (var detail in details)
|
||||
{
|
||||
detail.init_rate *= 100;
|
||||
detail.maintain_rate *= 100;
|
||||
}
|
||||
return Json(details);
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取适用金率
|
||||
/// </summary>
|
||||
/// <param name="req"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
public JsonResult GetApplicableMarginRate([FromBody]GetClientMarginDetailReq req)
|
||||
{
|
||||
var detail= YLErp.Modules.UnderlyingModule.UnderlyingHelper.GetApplicableMarginRate(req.clientId, req.underlyingCode, req.valueDate);
|
||||
return JsonSuccess("",detail);
|
||||
}
|
||||
|
||||
private void GetExtendInfo(IEnumerable<ClientMarginConfigDto> marginConfigs)
|
||||
{
|
||||
foreach (var item in marginConfigs)
|
||||
{
|
||||
if (item.client_id != 0)
|
||||
{
|
||||
var client = DataCacheProvider.GetClientDataSource().GetData(item.client_id);
|
||||
if (client != null)
|
||||
{
|
||||
item.client_name = client.Name;
|
||||
}
|
||||
}
|
||||
|
||||
// 百分比转换已在服务层处理,此处不需要重复转换
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ namespace YLErp.Web.Controllers
|
||||
db.trs_open_config.Add(model);
|
||||
}
|
||||
db.SaveChanges();
|
||||
_yLCache.StringSetWithNoPrefix<TrsOpenConfigDto>("TRS_Open_Hour", trsOpenConfig);
|
||||
_yLCache.StringSetWithNoPrefix<TrsOpenConfigDto>("TRS_Open_Hour", trsOpenConfig,TimeSpan.FromHours(8));
|
||||
return JsonSuccess("");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8846,18 +8846,13 @@ namespace YLErp.Web.Controllers
|
||||
var trade_Report = yldb.trade_Report.Where(x => x.TradeId == id).FirstOrDefault();
|
||||
if (trade_Report == null)
|
||||
{
|
||||
var clientMarginTemplates = yldb.client_marginrate.Where(x => x.ValueDate <= trade.TradeDate).OrderByDescending(o => o.ValueDate).AsNoTracking().ToList();
|
||||
var marinRate = clientMarginTemplates.Where(x => x.ClientId == trade.ClientId).FirstOrDefault();
|
||||
var marinRate = YLErp.Modules.UnderlyingModule.UnderlyingHelper.GetApplicableMarginRate(trade.ClientId, trade.UnderlyingCode, trade.TradeDate.Value);
|
||||
if (marinRate == null)
|
||||
{
|
||||
marinRate = clientMarginTemplates.Where(x => x.ClientId == 0).FirstOrDefault();
|
||||
}
|
||||
if (marinRate == null)
|
||||
{
|
||||
marinRate = new client_marginrate
|
||||
marinRate = new client_margin_detail()
|
||||
{
|
||||
InitMarginRate = 1,
|
||||
MaintenanceRate = 1,
|
||||
init_rate = 1,
|
||||
maintain_rate = 1
|
||||
};
|
||||
}
|
||||
trade_Report = new trade_report();
|
||||
@@ -8867,8 +8862,8 @@ namespace YLErp.Web.Controllers
|
||||
trade_Report.IsUsePerformanceColl = "否";
|
||||
trade_Report.PerformanceExplain = "详见交易确认书。";
|
||||
trade_Report.IsPerformanceCollInterest = "否";
|
||||
trade_Report.InitialPerformanceColl = (decimal)marinRate.InitMarginRate * 100;
|
||||
trade_Report.RecoveryPerformanceColl = (decimal)marinRate.MaintenanceRate * 100;
|
||||
trade_Report.InitialPerformanceColl = (decimal)marinRate.init_rate * 100;
|
||||
trade_Report.RecoveryPerformanceColl = (decimal)marinRate.maintain_rate * 100;
|
||||
trade_Report.UnwindPerformanceColl = trade_Report.RecoveryPerformanceColl;
|
||||
trade_Report.EodPerformanceColl = trade_Report.RecoveryPerformanceColl;
|
||||
trade_Report.PerformanceCollType = "0";
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
@using YLErp.Model
|
||||
@model client_margin_config
|
||||
@{
|
||||
ViewBag.Title = Model.id == 0 ? "新增客户保证金配置" : "编辑客户保证金配置";
|
||||
Layout = "~/Views/Shared/_InfoLayout.cshtml";
|
||||
var details = ViewBag.Details as List<ClientMarginDetailDto> ?? new List<ClientMarginDetailDto>();
|
||||
}
|
||||
@section CSS{
|
||||
<link href="~/Statics/libs/datetime/flatpickr/flatpickr.min.css" rel="stylesheet" />
|
||||
<style>
|
||||
.formlabel{
|
||||
width:128px !important;
|
||||
}
|
||||
</style>
|
||||
}
|
||||
@section JS{
|
||||
<script src="~/Statics/libs/datetime/flatpickr/flatpickr.min.js?v=@(HtmlUtil.JsVersion)"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
function saveConfig() {
|
||||
if (!$("#valueDate").val()) {
|
||||
main.message("生效日期不能为空");
|
||||
return false;
|
||||
}
|
||||
// 验证必填字段
|
||||
var clientId = $("#client_id").val();
|
||||
|
||||
// 收集表单数据
|
||||
var configData = {
|
||||
id: parseInt($("#id").val()) || 0,
|
||||
client_id: parseInt(clientId),
|
||||
value_date: $("#valueDate").val()
|
||||
};
|
||||
|
||||
// 收集详情数据并进行验证
|
||||
var details = [];
|
||||
var rows = document.querySelectorAll('#detailTableBody tr');
|
||||
var hasError = false;
|
||||
var errorMessage = "";
|
||||
|
||||
rows.forEach(function(row, index) {
|
||||
var bondTerm = row.querySelector('input[type="hidden"][name*="bond_term"]').value;
|
||||
var initRateInput = row.querySelector('input[name*="init_rate"]');
|
||||
var maintainRateInput = row.querySelector('input[name*="maintain_rate"]');
|
||||
var swapDaysInput = row.querySelector('input[name*="swap_days"]');
|
||||
|
||||
var initRateValue = initRateInput.value.trim();
|
||||
var maintainRateValue = maintainRateInput.value.trim();
|
||||
var swapDaysValue = swapDaysInput.value.trim();
|
||||
|
||||
var initRate = initRateValue === '' ? null : parseFloat(initRateValue);
|
||||
var maintainRate = maintainRateValue === '' ? null : parseFloat(maintainRateValue);
|
||||
var swapDays = swapDaysValue === '' ? null : parseInt(swapDaysValue);
|
||||
|
||||
// 验证数值不能为负数
|
||||
if (initRate !== null && initRate < 0) {
|
||||
hasError = true;
|
||||
errorMessage = "初始保证金率不能为负数";
|
||||
return;
|
||||
}
|
||||
if (maintainRate !== null && maintainRate < 0) {
|
||||
hasError = true;
|
||||
errorMessage = "维持保证金率不能为负数";
|
||||
return;
|
||||
}
|
||||
if (swapDays !== null && swapDays < 0) {
|
||||
hasError = true;
|
||||
errorMessage = "互换天数不能为负数";
|
||||
return;
|
||||
}
|
||||
|
||||
// 默认数据(第一条,bond_term为空)的所有字段都必填
|
||||
if (bondTerm === "") {
|
||||
if (initRate === null || maintainRate === null || swapDays === null) {
|
||||
hasError = true;
|
||||
errorMessage = "默认配置的所有字段都必须填写";
|
||||
return;
|
||||
}
|
||||
details.push({
|
||||
bond_term: bondTerm,
|
||||
init_rate: initRate,
|
||||
maintain_rate: maintainRate,
|
||||
swap_days: swapDays
|
||||
});
|
||||
} else {
|
||||
// 其他数据:当保证金率有值时,互换天数必填
|
||||
if ((initRate !== null || maintainRate !== null) && swapDays === null) {
|
||||
hasError = true;
|
||||
errorMessage = "当保证金率有值时,互换天数必须填写";
|
||||
return;
|
||||
}
|
||||
|
||||
// 当保证金率与互换天数都为空时,不加到details里
|
||||
if (initRate !== null || maintainRate !== null || swapDays !== null) {
|
||||
details.push({
|
||||
bond_term: bondTerm,
|
||||
init_rate: initRate,
|
||||
maintain_rate: maintainRate,
|
||||
swap_days: swapDays
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (hasError) {
|
||||
main.message(errorMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
var data = {
|
||||
marginConfig: configData,
|
||||
details: details
|
||||
};
|
||||
|
||||
main.post("/ClientMarginConfig/ClientMarginConfigEditJson", data).done(function (res) {
|
||||
window.parent.loadData();
|
||||
layer.closeMe();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 页面初始化
|
||||
$(function () {
|
||||
$(".datepicker").datepicker({ changeMonth: true, changeYear: true, showButtonPanel: true, showOtherMonths: true, selectOtherMonths: true });
|
||||
});
|
||||
</script>
|
||||
}
|
||||
|
||||
<form id="configForm" method="post" autocomplete="off">
|
||||
<div class="form-layout">
|
||||
@Html.HiddenFor(model => model.id)
|
||||
@Html.MyDropdownFor(model => model.client_id, ClientDataModel.GetAllClient())
|
||||
|
||||
<div id="ValueDateDiv" class="form-group col-md-6">
|
||||
<label class="formlabel">生效日期:</label>
|
||||
<input class="search-input datepicker" id="valueDate" name="valueDate" value="@(Model.id == 0 ? "" : Model.value_date.ToString("yyyy-MM-dd"))" type="text" autocomplete="off" required="required">
|
||||
<span style="color:red">*</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h4>保证金配置详情</h4>
|
||||
<br />
|
||||
|
||||
<table class="table table-bordered" id="detailTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>利率债期限</th>
|
||||
<th>初始保证金率(%)</th>
|
||||
<th>维持保证金率(%)</th>
|
||||
<th>互换默认天数</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="detailTableBody">
|
||||
@for (int i = 0; i < details.Count; i++)
|
||||
{
|
||||
var detail = details[i];
|
||||
var displayName = detail.bond_term == "" ? "默认" : detail.bond_term.Replace("Y", "年");
|
||||
<tr data-index="@i">
|
||||
<td>
|
||||
<input type="hidden" name="details[@i].bond_term" value="@detail.bond_term" />
|
||||
<input type="text" class="form-control" value="@displayName" readonly />
|
||||
</td>
|
||||
<td><input type="number" class="form-control" name="details[@i].init_rate" value="@(detail.init_rate?.ToString("0.##") ?? "")" step="0.01" min="0" max="100" /></td>
|
||||
<td><input type="number" class="form-control" name="details[@i].maintain_rate" value="@(detail.maintain_rate?.ToString("0.##") ?? "")" step="0.01" min="0" max="100" /></td>
|
||||
<td><input type="number" class="form-control" name="details[@i].swap_days" value="@(detail.swap_days?.ToString() ?? "")" min="0" /></td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-buttons">
|
||||
@MyControls.Btn("保存", "saveConfig()")
|
||||
@MyControls.Btn("关闭", "layer.closeMe()")
|
||||
</div>
|
||||
</form>
|
||||
@@ -0,0 +1,337 @@
|
||||
@model IEnumerable<client_margin_config>
|
||||
@{
|
||||
ViewBag.Title = "互换预付金率维护";
|
||||
Layout = "~/Views/Shared/_MainLayout.cshtml";
|
||||
var pageObj = new
|
||||
{
|
||||
canEdit = CurUser.基础参数管理.互换预付金率修改,
|
||||
};
|
||||
}
|
||||
<div class="searchdiv">
|
||||
@Html.MyAceDropdownInput("ClientId", "客户名称", ClientDataModel.GetAllClient(), true, true, null, false)
|
||||
@MyControls.SearchBtn()
|
||||
@if (pageObj.canEdit)
|
||||
{
|
||||
@MyControls.Btn("新增", "addConfig()")
|
||||
}
|
||||
<div class="row" style="margin-top:15px">
|
||||
<div class="col-md-12">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-striped" id="configTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="250">操作</th>
|
||||
<th>客户名称</th>
|
||||
<th>生效日期</th>
|
||||
<th>利率债期限</th>
|
||||
<th>初始保证金率</th>
|
||||
<th>维持保证金率</th>
|
||||
<th>互换默认天数</th>
|
||||
<th>操作人</th>
|
||||
<th>操作时间</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="configTableBody">
|
||||
<!-- 数据将通过Ajax加载 -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 页面权限配置
|
||||
var pageObj = @Html.Raw(Json.Serialize(pageObj));
|
||||
|
||||
$(document).ready(function () {
|
||||
loadData();
|
||||
initEventHandlers();
|
||||
});
|
||||
|
||||
// 初始化事件处理器
|
||||
function initEventHandlers() {
|
||||
// 回车键查询
|
||||
$('#ClientId, #ValueDate').on('keypress', function(e) {
|
||||
if (e.which === 13) {
|
||||
SearchClick();
|
||||
}
|
||||
});
|
||||
|
||||
// 表格行点击高亮
|
||||
$(document).on('click', '#configTable tbody tr', function() {
|
||||
$(this).siblings().removeClass('row-selected');
|
||||
$(this).addClass('row-selected');
|
||||
});
|
||||
|
||||
// 快捷键支持
|
||||
$(document).on('keydown', function(e) {
|
||||
// Ctrl+R 查询
|
||||
if (e.ctrlKey && e.which === 82) {
|
||||
e.preventDefault();
|
||||
loadData();
|
||||
}
|
||||
// F5 查询
|
||||
if (e.which === 116) {
|
||||
e.preventDefault();
|
||||
loadData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadData() {
|
||||
var searchData = {
|
||||
ClientId: $('#ClientId').val(),
|
||||
ValueDate: $('#ValueDate').val()
|
||||
};
|
||||
|
||||
main.post('/ClientMarginConfig/ClientMarginConfigQuery', searchData).done(function (result) {
|
||||
if (result.success) {
|
||||
renderTable(result.obj);
|
||||
} else {
|
||||
main.alert('加载数据失败:' + result.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function renderTable(data) {
|
||||
var tbody = $('#configTableBody');
|
||||
tbody.empty();
|
||||
|
||||
if (!data || data.length === 0) {
|
||||
tbody.append('<tr><td colspan="9" class="text-center text-muted"><i class="fa fa-info-circle"></i> 暂无数据</td></tr>');
|
||||
return;
|
||||
}
|
||||
|
||||
$.each(data, function (index, item) {
|
||||
try {
|
||||
var detailsCount = (item.details && item.details.length > 0) ? item.details.length : 1;
|
||||
|
||||
// 主行数据
|
||||
var mainRowClass = detailsCount > 1 ? 'main-row' : '';
|
||||
var row = '<tr class="' + mainRowClass + '">';
|
||||
|
||||
// 操作列 - 合并单元格
|
||||
row += '<td class="text-center" rowspan="' + detailsCount + '">';
|
||||
if (pageObj.canEdit) {
|
||||
row += '<input type="button" class="wentiEdit" title="修改" onclick="editConfig(\'' + item.EncryptId + '\')" value="修改" />';
|
||||
row += '<input type="button" class="wentiEdit" title="删除" onclick="deleteConfig(\'' + item.EncryptId + '\')" value="删除" />';
|
||||
} else {
|
||||
row += '<span class="text-muted"></span>';
|
||||
}
|
||||
row += '</td>';
|
||||
|
||||
// 客户名称 - 合并单元格
|
||||
row += '<td rowspan="' + detailsCount + '"><strong>' + escapeHtml(item.client_name || '--') + '</strong></td>';
|
||||
|
||||
// 生效日期 - 合并单元格
|
||||
var valueDate = formatDate(item.value_date);
|
||||
row += '<td rowspan="' + detailsCount + '">' + valueDate + '</td>';
|
||||
|
||||
// 显示第一条详情信息
|
||||
if (item.details && item.details.length > 0) {
|
||||
var detail = item.details[0];
|
||||
row += generateDetailCells(detail);
|
||||
} else {
|
||||
row += '<td class="text-muted">--</td>';
|
||||
row += '<td class="text-muted">--</td>';
|
||||
row += '<td class="text-muted">--</td>';
|
||||
row += '<td class="text-muted">--</td>';
|
||||
}
|
||||
|
||||
// 操作人和操作时间 - 合并单元格
|
||||
var operator = item.OptName || item.OptName || '--';
|
||||
var operateTime = formatDateTime(item.OptDate);
|
||||
row += '<td rowspan="' + detailsCount + '">' + escapeHtml(operator) + '</td>';
|
||||
row += '<td rowspan="' + detailsCount + '">' + operateTime + '</td>';
|
||||
row += '</tr>';
|
||||
|
||||
tbody.append(row);
|
||||
|
||||
// 如果有多个详情,显示其他详情行
|
||||
if (item.details && item.details.length > 1) {
|
||||
for (var i = 1; i < item.details.length; i++) {
|
||||
var detailRow = '<tr class="detail-row">';
|
||||
// 不需要操作、客户名称、生效日期、操作人、操作时间列,因为已经合并
|
||||
detailRow += generateDetailCells(item.details[i]);
|
||||
detailRow += '</tr>';
|
||||
tbody.append(detailRow);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('渲染表格行时出错:', error, item);
|
||||
var errorRow = '<tr><td colspan="9" class="text-danger"><i class="fa fa-exclamation-triangle"></i> 数据渲染异常</td></tr>';
|
||||
tbody.append(errorRow);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function SearchClick() {
|
||||
loadData();
|
||||
}
|
||||
|
||||
function addConfig() {
|
||||
main.infopage("新增互换预付金率", '/ClientMarginConfig/ClientMarginConfigEdit', { area: ['750px', '700px'] });
|
||||
}
|
||||
|
||||
function editConfig(id) {
|
||||
main.infopage("修改互换预付金率", '/ClientMarginConfig/ClientMarginConfigEdit?enid=' + id, { area: ['750px', '700px'] });
|
||||
}
|
||||
|
||||
function deleteConfig(id) {
|
||||
if (confirm('确定要删除这条记录吗?')) {
|
||||
$.post('/ClientMarginConfig/ClientMarginConfigDelete', { enid: id }, function (result) {
|
||||
if (result.success) {
|
||||
main.alert('删除成功');
|
||||
SearchClick();
|
||||
} else {
|
||||
main.alert('删除失败:' + result.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助函数:HTML转义
|
||||
function escapeHtml(text) {
|
||||
if (!text) return '--';
|
||||
var map = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": '''
|
||||
};
|
||||
return text.toString().replace(/[&<>"']/g, function(m) { return map[m]; });
|
||||
}
|
||||
|
||||
// 辅助函数:格式化日期
|
||||
function formatDate(dateStr) {
|
||||
if (!dateStr) return '--';
|
||||
try {
|
||||
var date = new Date(dateStr);
|
||||
if (isNaN(date.getTime())) return '--';
|
||||
return date.getFullYear() + '-' +
|
||||
String(date.getMonth() + 1).padStart(2, '0') + '-' +
|
||||
String(date.getDate()).padStart(2, '0');
|
||||
} catch (e) {
|
||||
return '--';
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助函数:格式化日期时间
|
||||
function formatDateTime(dateStr) {
|
||||
if (!dateStr) return '--';
|
||||
try {
|
||||
var date = new Date(dateStr);
|
||||
if (isNaN(date.getTime())) return '--';
|
||||
return date.getFullYear() + '-' +
|
||||
String(date.getMonth() + 1).padStart(2, '0') + '-' +
|
||||
String(date.getDate()).padStart(2, '0') + ' ' +
|
||||
String(date.getHours()).padStart(2, '0') + ':' +
|
||||
String(date.getMinutes()).padStart(2, '0');
|
||||
} catch (e) {
|
||||
return '--';
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助函数:格式化百分比
|
||||
function formatPercentage(value) {
|
||||
if (value === null || value === undefined || value === '') return '--';
|
||||
try {
|
||||
var num = parseFloat(value);
|
||||
if (isNaN(num)) return '--';
|
||||
return num.toFixed(2) + '%';
|
||||
} catch (e) {
|
||||
return '--';
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助函数:格式化期限显示
|
||||
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>' +
|
||||
'<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>';
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* 表格样式优化 */
|
||||
#configTable {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
#configTable th {
|
||||
background-color: #f8f9fa;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
#configTable td {
|
||||
vertical-align: middle;
|
||||
border: 1px solid #dee2e6;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
/* 文本对齐 */
|
||||
.text-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 空数据样式 */
|
||||
.text-muted {
|
||||
color: #6c757d;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* 错误信息样式 */
|
||||
.text-danger {
|
||||
color: #dc3545;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* 响应式表格 */
|
||||
.table-responsive {
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
/* 表格行悬停效果 */
|
||||
#configTable tbody tr:hover {
|
||||
background-color: #f8f9fa;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@media (max-width: 768px) {
|
||||
.responsive-table table {
|
||||
min-width: 800px;
|
||||
}
|
||||
|
||||
#configTable {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#configTable th,
|
||||
#configTable td {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user