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
{
///
/// 预付金模板V2 取数帮助类:按交易绑定的模板(trade_margin_template → margin_template_v2 → margin_template_detail)
/// 取 初始预付金率x / 维持预付金率y,供交易确认书、预付金计算等消费点统一使用。
/// 匹配维度:生效日期(ValueDate 最新)→ 利率债期限档(SpanConfig.BondTerm,四档,空=全部兜底)→ 标的资产类型(UnderlyingType 标志位)。
///
public static class MarginTemplateV2RateHelper
{
///
/// 取数结果。率为小数(如 0.1 表示 10%,与数据库一致),调用方直接用。
///
public class MarginRateResult
{
///
/// 命中的模板
///
public margin_template_v2 Template { get; set; }
///
/// 命中的明细参数行(无预付金规则时为 null)
///
public margin_template_detail Detail { get; set; }
///
/// 初始预付金率 x(小数)
///
public decimal? InitRate { get; set; }
///
/// 维持预付金率 y(小数)
///
public decimal? MaintainRate { get; set; }
}
///
/// 按交易绑定的模板取 初始/维持预付金率。
///
/// 交易ID
/// 标的代码(用于计算利率债期限档)
/// 标的资产类型(trade/swap_position 的 UnderlyingInstrumentType,如 TBonds)
/// 业务日期
/// 命中返回结果;交易未绑定模板、模板无效、规则非 无预付金/区间追保结构、或明细无匹配行时返回 null(由调用方决定兜底)
public static MarginRateResult GetTradeMarginRate(int tradeId, string underlyingCode, string underlyingInstrumentType, DateTime valueDate)
{
using (var db = new YLContext())
{
return GetTradeMarginRate(tradeId, underlyingCode, underlyingInstrumentType, valueDate, db);
}
}
///
/// 按交易绑定的模板取 初始/维持预付金率(调用方传入 DbContext,供批量场景复用连接)。
///
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(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)
};
}
///
/// 标的细分分类判定钩子(转债ETF/科创债ETF/中债指数等)。
/// 本期默认返回 null → 走通配行兜底,行为与现状一致;后续需求按业务给的判定规则(代码段/标的维护字段)实现。
///
private static string GetUnderlyingCategory(string underlyingCode, string underlyingInstrumentType)
{
return null;
}
///
/// 明细率值换算:页面上 vue-number-input 以 % 展示,数据库存的就是小数(如 0.05 表示 5%),直接取用
///
private static decimal? ToDecimalRate(double? ratio)
{
return ratio.HasValue ? (decimal)ratio.Value : (decimal?)null;
}
}
}