using YLErp.BLL; using YLErp.DBModels; using YLErp.Enums; using YLErp.Modules.MarginModule; namespace YLErp.Modules.CalcModules { /// /// GetRateByTemplate 资产类型先行匹配回归(连 dev 库,2026-08-27 顺序裁定:先品种后期限): /// 期限档仅利率债允许配置,品种匹配在期限之前——防止非利率债标的被利率债期限档行截胡 /// (交易2567 实证:信用债标的按 "<5y" 精确匹配到利率债行,信用债行 BondTerm 空永远不参与)。 /// 标的代码用库里不存在的代码(GetApplicableMarginTerm 无标的兜底返回 <5y),不依赖真实标的行情数据。 /// 测试数据全部带 "ZZZ-品种先行测试-" 名称前缀,TestInitialize/TestCleanup 双向清理。 /// [TestClass] public class MarginTemplateV2InstrumentFirstMatchTest { private const string Marker = "ZZZ-品种先行测试-"; private DateTime EffectiveDate = new DateTime(2000, 1, 1); private YLContext db; [TestInitialize] public void Init() { db = new YLContext(); Cleanup(); } [TestCleanup] public void CleanupFixture() { Cleanup(); db.Dispose(); } private void Cleanup() { var templateIds = db.margin_template_v2.Where(x => x.Name.StartsWith(Marker)).Select(x => x.id).ToList(); if (templateIds.Count > 0) { db.margin_template_detail.RemoveRange(db.margin_template_detail.Where(x => templateIds.Contains(x.MarginTemplateId))); db.margin_template_v2.RemoveRange(db.margin_template_v2.Where(x => templateIds.Contains(x.id))); db.SaveChanges(); } } private margin_template_v2 AddTieredTemplate() { var t = new margin_template_v2 { Name = Marker + "分档", IsDefault = false, IsForClient = false, IsValid = true, TradeTypes = "收益互换", RuleType = (int)MarginRuleTypeEnum.区间追保结构, UnderlyingSeperateType = (int)UnderlyingSeperateTypeEnum.CustomInstrumentType, ValueDate = EffectiveDate }; db.margin_template_v2.Add(t); db.SaveChanges(); return t; } private void AddDetail(int templateId, UnderlyingTypeEnum underlyingType, string bondTermJson, double initRate, double maintainRate) { db.margin_template_detail.Add(new margin_template_detail { MarginTemplateId = templateId, ValueDate = EffectiveDate, UnderlyingType = underlyingType, SpanConfigJson = bondTermJson, MarginRatio1 = initRate, MarginRatio2 = maintainRate }); } /// /// 信用债标的不被利率债期限档行截胡:term 恒为 "<5y"(标的不存在兜底), /// 旧序会精确命中利率债 <5y 行;新序品种先行应命中信用债行(BondTerm 空)。 /// [TestMethod] public void TI_001_信用债标的_命中信用债行_不被利率债期限档截胡() { var tpl = AddTieredTemplate(); AddDetail(tpl.id, UnderlyingTypeEnum.TBonds, "{\"BondTerm\":\"<5y\"}", 0.11, 0.12); AddDetail(tpl.id, UnderlyingTypeEnum.CreditBonds, null, 0.05, 0.06); db.SaveChanges(); var rate = MarginTemplateV2RateHelper.GetRateByTemplate(tpl, "ZZZ-NOT-EXIST-CD.IB", "CreditBonds", DateTime.Today, db); Assert.IsNotNull(rate, "品种先行后信用债行(BondTerm 空)应经期限兜底命中"); Assert.AreEqual(0.05m, rate.InitRate.Value, "应取信用债行的初始预付金率,而非利率债 <5y 行的 0.11"); Assert.AreEqual(0.06m, rate.MaintainRate.Value, "应取信用债行的维持预付金率,而非利率债 <5y 行的 0.12"); } /// /// 利率债标的行为不变:品种命中利率债行后,期限精确档 "<5y" 命中对应期限行(压过 5y-10y 行)。 /// [TestMethod] public void TI_002_利率债标的_品种内期限精确档仍生效() { var tpl = AddTieredTemplate(); AddDetail(tpl.id, UnderlyingTypeEnum.TBonds, "{\"BondTerm\":\"<5y\"}", 0.11, 0.12); AddDetail(tpl.id, UnderlyingTypeEnum.TBonds, "{\"BondTerm\":\"5y-10y\"}", 0.13, 0.14); db.SaveChanges(); var rate = MarginTemplateV2RateHelper.GetRateByTemplate(tpl, "ZZZ-NOT-EXIST-TB.IB", "TBonds", DateTime.Today, db); Assert.IsNotNull(rate); Assert.AreEqual(0.11m, rate.InitRate.Value, "期限兜底 <5y 时应精确命中 <5y 档行"); Assert.AreEqual(0.12m, rate.MaintainRate.Value); } /// /// 模板未配标的品种时的既有兜底不变:品种行与通配行均无 → 不缩小行集,回落期限匹配(与旧序一致)。 /// [TestMethod] public void TI_003_模板未配品种_回落期限匹配_行为不变() { var tpl = AddTieredTemplate(); AddDetail(tpl.id, UnderlyingTypeEnum.TBonds, "{\"BondTerm\":\"<5y\"}", 0.11, 0.12); db.SaveChanges(); var rate = MarginTemplateV2RateHelper.GetRateByTemplate(tpl, "ZZZ-NOT-EXIST-CF.IB", "CommodityFutures", DateTime.Today, db); Assert.IsNotNull(rate, "品种落空应回落到期限匹配(旧行为兜底),不应返回 null"); Assert.AreEqual(0.11m, rate.InitRate.Value); } /// /// 品种行与期限行均无法匹配时返回 null:非利率债标的不再"借用"利率债期限档行, /// 由调用方按无预付金要求兜底(引擎不产出 trade_span)。 /// [TestMethod] public void TI_004_品种与期限均无匹配行_返回null() { var tpl = AddTieredTemplate(); AddDetail(tpl.id, UnderlyingTypeEnum.TBonds, "{\"BondTerm\":\"5y-10y\"}", 0.13, 0.14); db.SaveChanges(); var rate = MarginTemplateV2RateHelper.GetRateByTemplate(tpl, "ZZZ-NOT-EXIST-CD.IB", "CreditBonds", DateTime.Today, db); Assert.IsNull(rate, "信用债标的不应命中利率债 5y-10y 期限行"); } } }