Files
zszq-trs/YLErpDAL/Modules/MarginModule/MarginTemplateV2RateHelper.cs
T

170 lines
7.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Linq;
using YLErp.BLL;
using YLErp.DBModels;
using YLErp.Enums;
using YLErp.Modules.UnderlyingModule;
namespace YLErp.Modules.MarginModule
{
/// <summary>
/// 预付金模板V2 取数帮助类:按交易绑定的模板(trade_margin_template → margin_template_v2 → margin_template_detail
/// 取 初始预付金率x / 维持预付金率y,供交易确认书、预付金计算等消费点统一使用。
/// 匹配维度:生效日期(ValueDate 最新)→ 利率债期限档(SpanConfig.BondTerm,四档,空=全部兜底)→ 标的资产类型(UnderlyingType 标志位)。
/// </summary>
public static class MarginTemplateV2RateHelper
{
/// <summary>
/// 取数结果。率为小数(如 0.1 表示 10%,与数据库一致),调用方直接用。
/// </summary>
public class MarginRateResult
{
/// <summary>
/// 命中的模板
/// </summary>
public margin_template_v2 Template { get; set; }
/// <summary>
/// 命中的明细参数行(无预付金规则时为 null)
/// </summary>
public margin_template_detail Detail { get; set; }
/// <summary>
/// 初始预付金率 x(小数)
/// </summary>
public decimal? InitRate { get; set; }
/// <summary>
/// 维持预付金率 y(小数)
/// </summary>
public decimal? MaintainRate { get; set; }
}
/// <summary>
/// 按交易绑定的模板取 初始/维持预付金率。
/// </summary>
/// <param name="tradeId">交易ID</param>
/// <param name="underlyingCode">标的代码(用于计算利率债期限档)</param>
/// <param name="underlyingInstrumentType">标的资产类型(trade/swap_position 的 UnderlyingInstrumentType,如 TBonds</param>
/// <param name="valueDate">业务日期</param>
/// <returns>命中返回结果;交易未绑定模板、模板无效、规则非 无预付金/区间追保结构、或明细无匹配行时返回 null(由调用方决定兜底)</returns>
public static MarginRateResult GetTradeMarginRate(int tradeId, string underlyingCode, string underlyingInstrumentType, DateTime valueDate)
{
using (var db = new YLContext())
{
return GetTradeMarginRate(tradeId, underlyingCode, underlyingInstrumentType, valueDate, db);
}
}
/// <summary>
/// 按交易绑定的模板取 初始/维持预付金率(调用方传入 DbContext,供批量场景复用连接)。
/// </summary>
public static MarginRateResult GetTradeMarginRate(int tradeId, string underlyingCode, string underlyingInstrumentType, DateTime valueDate, YLContext db)
{
//1.交易绑定(ValueDate 最新)
var binding = db.trade_margin_template.AsNoTracking()
.Where(x => x.TradeId == tradeId && x.ValueDate <= valueDate)
.OrderByDescending(x => x.ValueDate)
.FirstOrDefault();
if (binding == null)
{
return null;
}
var template = db.margin_template_v2.AsNoTracking().FirstOrDefault(x => x.id == binding.MarginTemplateId);
if (template == null || !template.IsValid)
{
return null;
}
//2.无预付金规则:率直接为 0
if (template.RuleType == (int)MarginRuleTypeEnum.无预付金)
{
return new MarginRateResult { Template = template, InitRate = 0m, MaintainRate = 0m };
}
if (template.RuleType != (int)MarginRuleTypeEnum.区间追保结构)
{
//其他规则不在本帮助类支持范围,显式返回 null
return null;
}
//3.区间追保结构:取 ValueDate 最新生效的一组明细(同一 ValueDate 下有多行参数组)
var detailQuery = db.margin_template_detail.AsNoTracking()
.Where(x => x.MarginTemplateId == template.id && x.ValueDate <= valueDate);
if (!detailQuery.Any())
{
return null;
}
var latestValueDate = detailQuery.Max(x => x.ValueDate);
var details = detailQuery.Where(x => x.ValueDate == latestValueDate).ToList();
//4.利率债期限档匹配:精确档 → "全部"BondTerm 为空)兜底
var term = UnderlyingHelper.GetApplicableMarginTerm(underlyingCode, valueDate);
var matched = details.Where(x => x.SpanConfig != null && x.SpanConfig.BondTerm == term).ToList();
if (!matched.Any())
{
matched = details.Where(x => x.SpanConfig == null || string.IsNullOrEmpty(x.SpanConfig.BondTerm)).ToList();
}
if (!matched.Any())
{
return null;
}
//5.标的资产类型匹配(仅当模板选了"按资产类型分类"):精确标志位 → 通配行(None/All)兜底
//标的细分分类判定钩子:本期默认返回 null → 走下方原有标志位匹配/通配行兜底,行为与现状一致;
//后续需求(转债ETF/科创债ETF/中债指数等细分)实现判定规则后,此处按 (BondTerm, EtfKind) 元组匹配明细行。
var underlyingCategory = GetUnderlyingCategory(underlyingCode, underlyingInstrumentType);
if (underlyingCategory != null)
{
//TODO: 后续需求实现:按 (SpanConfig.BondTerm, SpanConfig.EtfKind) 元组匹配明细行,替代/补充下方标志位匹配
}
if (template.UnderlyingSeperateType == (int)UnderlyingSeperateTypeEnum.CustomInstrumentType
&& Enum.TryParse<UnderlyingTypeEnum>(underlyingInstrumentType, out var instrumentFlag))
{
var byInstrument = matched.Where(x => (x.UnderlyingType & instrumentFlag) > 0).ToList();
if (byInstrument.Any())
{
matched = byInstrument;
}
else
{
var wildcard = matched.Where(x => x.UnderlyingType == UnderlyingTypeEnum.None || x.UnderlyingType == UnderlyingTypeEnum.All).ToList();
if (wildcard.Any())
{
matched = wildcard;
}
}
}
var detail = matched.First();
return new MarginRateResult
{
Template = template,
Detail = detail,
InitRate = ToDecimalRate(detail.MarginRatio1),
MaintainRate = ToDecimalRate(detail.MarginRatio2)
};
}
/// <summary>
/// 标的细分分类判定钩子(转债ETF/科创债ETF/中债指数等),public 供单元测试回归默认行为。
/// 本期默认返回 null → 走通配行兜底,行为与现状一致;后续需求按业务给的判定规则(代码段/标的维护字段)实现。
/// </summary>
public static string GetUnderlyingCategory(string underlyingCode, string underlyingInstrumentType)
{
return null;
}
/// <summary>
/// 明细率值换算:页面上 vue-number-input 以 % 展示,数据库存的就是小数(如 0.05 表示 5%),直接取用
/// </summary>
private static decimal? ToDecimalRate(double? ratio)
{
return ratio.HasValue ? (decimal)ratio.Value : (decimal?)null;
}
}
}