377 lines
15 KiB
C#
377 lines
15 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// 标的数据帮助类
|
|
/// </summary>
|
|
public static class UnderlyingHelper
|
|
{
|
|
readonly static IYLCache ylCache;
|
|
static UnderlyingHelper()
|
|
{
|
|
ylCache = YLServiceLocator.ServiceProvider.GetService<IYLCache>();
|
|
}
|
|
/// <summary>
|
|
/// 获取组合标的品种对象
|
|
/// </summary>
|
|
public static Variety GetSyntheticVariety()
|
|
{
|
|
var variety = DataCacheProvider.GetVarietyDataSource().AsQueryable()
|
|
.FirstOrDefault(v => v.VarietyCode == "组合标的");
|
|
|
|
if (variety != null)
|
|
{
|
|
return variety;
|
|
}
|
|
|
|
lock (DataCacheProvider.GetVarietyDataSource())
|
|
{
|
|
using (var db = DbContextFactory.GetYLDbContext())
|
|
{
|
|
variety = db.variety.FirstOrDefault(n => n.VarietyCode == "组合标的");
|
|
|
|
if (variety != null)
|
|
{
|
|
return variety;
|
|
}
|
|
|
|
variety = new Variety()
|
|
{
|
|
VarietyCode = "组合标的",
|
|
VarietyName = "组合标的",
|
|
TradingMarketId = 0,
|
|
TradeUnit = "1手/份",
|
|
QuoteUnit = "元(人民币)/份",
|
|
DeliveryType = "",
|
|
HasNightMarket = false,
|
|
ShortName = "组合标的",
|
|
CommissionType = "固定",
|
|
CloseTodayCommissionType = "固定",
|
|
//UnderlyingInstrumentType = "CommodityFutures",
|
|
VolatilityRate = "",
|
|
VolatilityAdjust = 0,
|
|
AssetType = "",
|
|
OptId = 0,
|
|
OptName = "系统",
|
|
OptDate = DateTime.Now,
|
|
};
|
|
|
|
db.variety.Add(variety);
|
|
db.SaveChanges();
|
|
|
|
return variety;
|
|
}
|
|
}
|
|
}
|
|
/// <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;
|
|
|
|
// 2.1 尝试使用标的资产上手动设置的债券期限
|
|
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(underlyingCode);
|
|
if (underlying != null && !string.IsNullOrEmpty(underlying.ExJson))
|
|
{
|
|
var bondInfo = JsonHelper.Deserialize<UnderlyingBond>(underlying.ExJson);
|
|
if (bondInfo != null && !string.IsNullOrEmpty(bondInfo.BondTerm))
|
|
{
|
|
// 优先使用标的资产上设置的债券期限
|
|
applicableTerm = bondInfo.BondTerm;
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
detail = details.FirstOrDefault(d => d.bond_term == ConsMarginTerm.UnderFiveYear);
|
|
}
|
|
return detail;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"获取保证金率详情时发生异常: {ex.Message}");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 清除指定标的的保证金率缓存
|
|
/// </summary>
|
|
/// <param name="underlyingCode">标的代码,为空时清除全部</param>
|
|
public static void ClearMarginRateCache(string underlyingCode = null)
|
|
{
|
|
try
|
|
{
|
|
if (ylCache == null) return;
|
|
|
|
var cachePattern = string.IsNullOrEmpty(underlyingCode)
|
|
? "ClientMarginRate:*"
|
|
: $"ClientMarginRate:*:{underlyingCode}:*";
|
|
ylCache.BatchDelete(cachePattern);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogFactory.GetLogger("UnderlyingHelper").Error($"清除保证金率缓存时发生异常: {ex.Message}", ex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取篮子标的品种对象
|
|
/// </summary>
|
|
public static Variety GetBasketVariety()
|
|
{
|
|
return DataCacheProvider.GetVarietyDataSource().AsQueryable()
|
|
.FirstOrDefault(v => v.VarietyCode == "篮子标的") ??
|
|
new Variety
|
|
{
|
|
VarietyCode = "篮子标的",
|
|
VarietyName = "篮子标的",
|
|
QuoteUnit = "元(人民币)/股",
|
|
TradeUnit = "100股/手"
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取标的品种代码 例:BU,BU-CU
|
|
/// </summary>
|
|
/// <param name="underlyingCode"></param>
|
|
/// <returns></returns>
|
|
public static string GetUnderlyingVarietyCode(string underlyingCode)
|
|
{
|
|
var ret = "";
|
|
var un = DataCacheProvider.GetUnderlyingDataSource().GetData(underlyingCode);
|
|
if (un != null)
|
|
{
|
|
if (un.UnderlyingType == "组合标的")
|
|
{
|
|
var synthetic = DataCacheProvider.GetUnderlyingDataSource().GetSyntheticUnderlying(underlyingCode);
|
|
if (synthetic != null)
|
|
{
|
|
var sb = new StringBuilder();
|
|
var va1 = DataCacheProvider.GetVariety(synthetic.UnderlyingCode1);
|
|
var va2 = DataCacheProvider.GetVariety(synthetic.UnderlyingCode2);
|
|
var va3 = DataCacheProvider.GetVariety(synthetic.UnderlyingCode3);
|
|
var va4 = DataCacheProvider.GetVariety(synthetic.UnderlyingCode4);
|
|
if (va1 != null)
|
|
{
|
|
sb.Append(va1.VarietyCode).Append('-');
|
|
}
|
|
if (va2 != null)
|
|
{
|
|
sb.Append(va2.VarietyCode).Append('-');
|
|
}
|
|
if (va3 != null)
|
|
{
|
|
sb.Append(va3.VarietyCode).Append('-');
|
|
}
|
|
if (va4 != null)
|
|
{
|
|
sb.Append(va4.VarietyCode).Append('-');
|
|
}
|
|
ret = sb.ToString().Substring(0, sb.Length - 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var va = DataCacheProvider.GetVariety(un.UnderlyingCode);
|
|
ret = va.VarietyCode;
|
|
}
|
|
}
|
|
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 || !sourceDate.HasValue)
|
|
return null;
|
|
|
|
if (calcDate.Value > sourceDate.Value)
|
|
throw new ArgumentException("计算日期必须小于原日期", nameof(calcDate));
|
|
|
|
int totalMonths = (sourceDate.Value.Year - calcDate.Value.Year) * 12
|
|
+ (sourceDate.Value.Month - calcDate.Value.Month);
|
|
|
|
double fractionalMonth = 0;
|
|
if (sourceDate.Value.Day != calcDate.Value.Day)
|
|
{
|
|
int daysInMonth = DateTime.DaysInMonth(sourceDate.Value.Year, sourceDate.Value.Month);
|
|
fractionalMonth = (sourceDate.Value.Day - calcDate.Value.Day) / (double)daysInMonth;
|
|
}
|
|
|
|
double yearTerm = (totalMonths + fractionalMonth) / 12.0;
|
|
return yearTerm;
|
|
}
|
|
/// <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.UnderFiveYear;
|
|
|
|
try
|
|
{
|
|
// 获取标的信息
|
|
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(underlyingCode);
|
|
if (underlying == null || !underlying.MaturityDate.HasValue)
|
|
return ConsMarginTerm.UnderFiveYear;
|
|
// 计算剩余期限(从估值日期到到期日期)
|
|
var remainingTermYears = getYearTerm(valueDate, underlying.MaturityDate);
|
|
|
|
// 根据业务规则确定适用的保证金率期限
|
|
return DetermineMarginTerm(remainingTermYears);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// 记录异常日志
|
|
Console.WriteLine($"计算保证金率期限时发生异常: {ex.Message}");
|
|
return ConsMarginTerm.UnderFiveYear;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 根据剩余年限确定适用的保证金率期限(利率债TRS)
|
|
/// | 剩余期限(年) | 则适用于 |
|
|
/// |------------------|-------------|
|
|
/// | <=5 | <5y |
|
|
/// | (5,10] | 5y-10y |
|
|
/// | (10,30] | 10y-30y |
|
|
/// | >30 | >30y |
|
|
/// 未设置或无法计算剩余期限,使用”默认”
|
|
/// </summary>
|
|
private static string DetermineMarginTerm(double? remainingYears)
|
|
{
|
|
// 利率债TRS规则:只看剩余期限
|
|
if (!remainingYears.HasValue)
|
|
return ConsMarginTerm.UnderFiveYear;
|
|
|
|
if (remainingYears <= 5.0)
|
|
return ConsMarginTerm.UnderFiveYear;
|
|
if (remainingYears > 5.0 && remainingYears <= 10.0)
|
|
return ConsMarginTerm.FiveToTenYear;
|
|
if (remainingYears > 10.0 && remainingYears <= 30.0)
|
|
return ConsMarginTerm.TenToThirtyYear;
|
|
return ConsMarginTerm.OverThirtyYear;
|
|
}
|
|
}
|
|
}
|