refactor(interest): SwapDealService 6处取价收敛到 IIndexFixer

修正上次尝试的失败: 静态 Fr007IndexFixer.Instance 绕过了
TryGetFloatRate(virtual)接缝, 导致测试 stub 失效(CI_007/CI_008失败)。

本次方案: 新增 SwapDealIndexFixer(实例级, 委托 TryGetFloatRate):
- SwapDealService.IndexFixer 属性 lazy 初始化, 包 SwapDealIndexFixer
- 6处 GetNonHolidayDefore+TryGetFloatRate 全部替换为 IndexFixerBase+IndexFixer
- TryGetFloatRate(protected virtual)保留不动, 测试 stub 机制完全不受影响

验证: CI_007/CI_008 从失败转为通过, 全量480测试7失败(与基线一致,零回归)。
SwapDealService 内 GetNonHolidayDefore 出现次数: 6 → 0(全部收敛到 IndexFixerBase)。
This commit is contained in:
hjhan
2026-08-11 09:17:42 +08:00
parent 7d0397fc44
commit 59c16aabc1
2 changed files with 50 additions and 28 deletions
@@ -0,0 +1,25 @@
using System;
using YLErp.Derivatives.Interest;
namespace YLErp.Modules.SwapModule;
/// <summary>
/// SwapDealService 专用取价器:通过实例方法委托给 TryGetFloatRate(virtual)
/// 而非 Fr007IndexFixer.Instance 那样直接调 EodPriceQueryService。
///
/// 为什么不用静态 InstanceSwapDealService.TryGetFloatRate 是 protected virtual
/// 测试通过 override 它注入 stub 利率。静态 Instance 绕过这个接缝会让 45 个测试失败。
/// 本类把 TryGetFloatRate 包成 IIndexFixer,既保留 virtual 接缝,又用上 IndexFixerBase。
/// </summary>
internal sealed class SwapDealIndexFixer : IndexFixerBase, IIndexFixer
{
private readonly SwapDealService _owner;
internal SwapDealIndexFixer(SwapDealService owner) => _owner = owner;
public bool TryGetFixing(DateTime fixingDate, string underlyingCode, out decimal rate)
{
bool ok = _owner.TryGetFloatRate(fixingDate, underlyingCode, out double r);
rate = Convert.ToDecimal(r);
return ok;
}
}
+25 -28
View File
@@ -5,6 +5,7 @@ using System.Linq.Expressions;
using YLErp.BLL;
using YLErp.BLL.Eod;
using YLErp.DBModels.Enums;
using YLErp.Derivatives.Interest;
using YLErp.Helpers;
using YLErp.Modules.DataProviderModule;
using YLErp.Modules.EodModule;
@@ -23,6 +24,10 @@ namespace YLErp.Modules.SwapModule
return EodPriceQueryService.TryGetPrice(valueDate, underlyingCode, out rate);
}
private IIndexFixer _indexFixer;
/// <summary>FR007 取价器,委托 TryGetFloatRate(保留 virtual 接缝供测试 stub)。</summary>
protected virtual IIndexFixer IndexFixer => _indexFixer ??= new SwapDealIndexFixer(this);
#region Seamsoverride DB/
// FindTrade 已上提到基类 SwapTradeBaseService(三子类实现一致,消除重复)
@@ -1037,9 +1042,8 @@ namespace YLErp.Modules.SwapModule
if (string.IsNullOrEmpty(position.FloatRateUnderlyingCode)) return position.FloatRate;
int days = (endDate - startDate).Days;
DateTime rateDate = days % period == 0
? QdpCalendarHelper.GetNonHolidayDefore(endDate.AddDays(position.interest_rule ?? 0))
: QdpCalendarHelper.GetNonHolidayDefore(startDate.AddDays(position.interest_rule ?? 0));
DateTime rateDate = IndexFixerBase.GetFixingDate(
days % period == 0 ? endDate : startDate, position.interest_rule);
if (preEod.id != 0 && days % period != 0)
{
@@ -1047,9 +1051,9 @@ namespace YLErp.Modules.SwapModule
return preEod.FloatRate;
}
if (TryGetFloatRate(rateDate, position.FloatRateUnderlyingCode, out double rate))
if (IndexFixer.TryGetFixing(rateDate, position.FloatRateUnderlyingCode, out decimal rate))
{
position.FloatRate = positionClone.FloatRate = Convert.ToDecimal(rate);
position.FloatRate = positionClone.FloatRate = rate;
return position.FloatRate;
}
if (!swap) throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{rateDate:yyyy年MM月dd日}的价格");
@@ -1352,14 +1356,14 @@ namespace YLErp.Modules.SwapModule
if (accrueDate >= startDate && i % interestPeriod == 0
&& !string.IsNullOrEmpty(position.FloatRateUnderlyingCode))
{
var fr007RateDate = QdpCalendarHelper.GetNonHolidayDefore(accrueDate.AddDays(position.interest_rule ?? 0));
if (TryGetFloatRate(fr007RateDate, position.FloatRateUnderlyingCode, out double floatRate1))
var fixingDate = IndexFixerBase.GetFixingDate(accrueDate, position.interest_rule);
if (IndexFixer.TryGetFixing(fixingDate, position.FloatRateUnderlyingCode, out decimal fixing))
{
if (floatRate1 != 0) floatRate = floatRate1;
if (fixing != 0m) floatRate = Convert.ToDouble(fixing);
}
else
{
throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{fr007RateDate:yyyy年MM月dd日}的价格");
throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{fixingDate:yyyy年MM月dd日}的价格");
}
}
if (accrueDate >= startDate)
@@ -1443,14 +1447,14 @@ namespace YLErp.Modules.SwapModule
// tdDynomicPrincipal = flowEvent.InterestPrincipal 使本金累积乘 closePercent^N)。
if (i % interestPeriod == 0 && !string.IsNullOrEmpty(position.FloatRateUnderlyingCode))
{
var fr007RateDate = QdpCalendarHelper.GetNonHolidayDefore(accrueDate.AddDays(position.interest_rule ?? 0));
if (TryGetFloatRate(fr007RateDate, position.FloatRateUnderlyingCode, out double floatRate1))
var fixingDate = IndexFixerBase.GetFixingDate(accrueDate, position.interest_rule);
if (IndexFixer.TryGetFixing(fixingDate, position.FloatRateUnderlyingCode, out decimal fixing))
{
if (floatRate1 != 0) floatRate = floatRate1;
if (fixing != 0m) floatRate = Convert.ToDouble(fixing);
}
else
{
throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{fr007RateDate:yyyy年MM月dd日}的价格");
throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{fixingDate:yyyy年MM月dd日}的价格");
}
}
@@ -1507,18 +1511,14 @@ namespace YLErp.Modules.SwapModule
tdDynomicPrincipal = tdDynomicPrincipal + interestProfitSum * remainingPercent;
if (!string.IsNullOrEmpty(position.FloatRateUnderlyingCode))
{
// 获取合适的 rateDate
var fr007RateDate = QdpCalendarHelper.GetNonHolidayDefore(endDate.AddDays(position.interest_rule ?? 0)); // 获取前一工作日;
if (TryGetFloatRate(fr007RateDate, position.FloatRateUnderlyingCode, out double floatRate1))
var fixingDate = IndexFixerBase.GetFixingDate(endDate, position.interest_rule);
if (IndexFixer.TryGetFixing(fixingDate, position.FloatRateUnderlyingCode, out decimal fixing))
{
if (floatRate1 != 0)
{
floatRate = floatRate1;
}
if (fixing != 0m) floatRate = Convert.ToDouble(fixing);
}
else
{
throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{fr007RateDate:yyyy年MM月dd日}的价格");
throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{fixingDate:yyyy年MM月dd日}的价格");
}
}
@@ -1572,17 +1572,14 @@ namespace YLErp.Modules.SwapModule
// 获取新的浮动利率
if (!string.IsNullOrEmpty(position.FloatRateUnderlyingCode))
{
var fr007RateDate = QdpCalendarHelper.GetNonHolidayDefore(endDate.AddDays(position.interest_rule ?? 0));
if (TryGetFloatRate(fr007RateDate, position.FloatRateUnderlyingCode, out double newFloatRate))
var fixingDate = IndexFixerBase.GetFixingDate(endDate, position.interest_rule);
if (IndexFixer.TryGetFixing(fixingDate, position.FloatRateUnderlyingCode, out decimal fixing))
{
if (newFloatRate != 0)
{
floatRate = newFloatRate;
}
if (fixing != 0m) floatRate = Convert.ToDouble(fixing);
}
else
{
throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{fr007RateDate:yyyy年MM月dd日}的价格");
throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{fixingDate:yyyy年MM月dd日}的价格");
}
}
}