refactor(swap): relaxedFixingFromDate 改名 exclusionStart——名实归位

该参数诞生于"有价则取/缺价回退"口径(55363e9f 由 exclusionEndDate 改名而来),
自洽化(fafae5ca)后排除日一概不取价,"放宽取价"语义已不存在——名字回到
排除区间本义,并与 FIX 日志既有用词"排除起点"对齐:
- BuildSegmentRates/CalcDailyCompoundInterest/CalcDailySimpleInterest 三签名
  + 全平重放传参点 + 方法摘要补 exclusionStart 参数语义文档
零行为变更。

验证:内存套件 113/113;全量 981 例 145 败与基线 diff=0
This commit is contained in:
hjhan
2026-08-19 11:41:18 +08:00
parent f8049f81f8
commit 43fb83ed00
2 changed files with 13 additions and 11 deletions
@@ -577,9 +577,9 @@ namespace YLErp.Modules.SwapModule
StringAssert.Contains(o.Ex.Message, "FR007");
}
// ── 到期日当天全平:replayEndDate=endDate+1 补计分支(relaxedFixingFromDate 的存在理由)──
// ── 到期日当天全平:replayEndDate=endDate+1 补计分支(exclusionStart 的存在理由)──
// 不算尾时 InitInterestDate 把 endDate 回拨一天;最终全平的历史差分重放需把窗口补回真实
// 平仓/到期日(replayEndDate=endDate+1),但该边界日的定盘经 relaxedFixingFromDate 标记为
// 平仓/到期日(replayEndDate=endDate+1),但该边界日的定盘经 exclusionStart 标记为
// "有价则取/缺价跳过"——否则到期日上午全平会被尾日价误拦(EQD-6968 在到期日的镜像场景)。
[TestMethod]
+11 -9
View File
@@ -1294,7 +1294,7 @@ namespace YLErp.Modules.SwapModule
// 计算截至本次平仓日的累计利息 amountAtEnd
CalcDailyCompoundInterest(replayEndDate, position, closePosiNotionalValue,
interestAtEnd, annualDays, floateRate, closePrecent,
calcFirst, calcLast, ref amountAtEnd, ref tdAmountAtEnd, consumedInterest, relaxedFixingFromDate: endDate);
calcFirst, calcLast, ref amountAtEnd, ref tdAmountAtEnd, consumedInterest, exclusionStart: endDate);
var interestAtPreviousEod = new swap_flow_event { InterestRate = rate };
decimal amountAtPreviousEod = 0m;
decimal tdAmountAtPreviousEod = 0m;
@@ -1356,11 +1356,13 @@ namespace YLErp.Modules.SwapModule
/// <summary>
/// 按重置周期切分利率段,每段记录 all-in 利率(spread+fixing)。返回 (分段列表, 末段浮动利率)。
/// fetchAfterDate: 仅该日期之后的重置日才取 FR007(单利传 ValueDate,复利传 null 全程取)。
/// exclusionStart: 排除区间起点——该日期起(含)的重置日视为"排除日"(不算尾的不计息边界日),
/// 一概不取价;缺省=endDate。仅不算尾(calcLast=false)生效;算尾所有重置日照常强制取价。
/// </summary>
private (List<(DateTime StartDate, decimal Rate)> Segments, decimal LastFloat) BuildSegmentRates(
DateTime startDate, DateTime endDate, int interestPeriod,
swap_position position, decimal spread, decimal initialFloat,
DateTime? fetchAfterDate, bool calcLast = true, DateTime? relaxedFixingFromDate = null)
DateTime? fetchAfterDate, bool calcLast = true, DateTime? exclusionStart = null)
{
var rates = new List<(DateTime, decimal)>();
var calcDays = (endDate - startDate).Days;
@@ -1368,7 +1370,7 @@ namespace YLErp.Modules.SwapModule
SwapCalcTrace.Critical(
$"FIX Segments p{position.id} [{startDate:yyyy-MM-dd}→{endDate:yyyy-MM-dd}] period={interestPeriod} " +
$"fetchAfter={(fetchAfterDate?.ToString("yyyy-MM-dd") ?? "")} calcLast={calcLast} " +
$"排除起点={(relaxedFixingFromDate?.ToString("yyyy-MM-dd") ?? (calcLast ? "" : endDate.ToString("yyyy-MM-dd")))} seed={initialFloat:P6} spread={spread:P6}");
$"排除起点={(exclusionStart?.ToString("yyyy-MM-dd") ?? (calcLast ? "" : endDate.ToString("yyyy-MM-dd")))} seed={initialFloat:P6} spread={spread:P6}");
for (int i = 0; i <= calcDays; i += interestPeriod)
{
var resetDate = startDate.AddDays(i);
@@ -1377,7 +1379,7 @@ namespace YLErp.Modules.SwapModule
// 排除日(EQD-6968 自洽化)一概不取价(有价也不取),currentFloat 保持末段已消费利率:
// 事件/快照回写的浮动利率与金额同源、与平仓时刻无关。剩余持仓的新周期利率由
// SwapEodPositionService 的"重置日再定盘"显式获取,不靠排除日顺带。算尾照常强制取价。
bool isExcludedEnd = !calcLast && resetDate >= (relaxedFixingFromDate ?? endDate);
bool isExcludedEnd = !calcLast && resetDate >= (exclusionStart ?? endDate);
if (needFetch && !isExcludedEnd)
{
currentFloat = ResolveFloatRate(position, resetDate, currentFloat);
@@ -1405,17 +1407,17 @@ namespace YLErp.Modules.SwapModule
/// <returns></returns>
public void CalcDailyCompoundInterest(DateTime endDate, swap_position position, decimal principal, swap_flow_event flowEvent,
int annualDays, decimal floateRate, decimal closePercent, bool calcFirst, bool calcLast,
ref decimal InterestAmount, ref decimal TdInterestAmount, decimal consumedInterest = 0m, decimal resetCarryInterest = 0m, DateTime? relaxedFixingFromDate = null)
ref decimal InterestAmount, ref decimal TdInterestAmount, decimal consumedInterest = 0m, decimal resetCarryInterest = 0m, DateTime? exclusionStart = null)
{
var startDate = position.PosiStartDate;
int interestPeriod = position.interest_rest_days ?? 1;
// 分段取率:复利全程重放,每个重置日(含 startDate)取 FR007fetchAfterDate=null)。
// calcLast 透传,与下方 AccruePeriod 的边界同源,避免两处漂移;
// relaxedFixingFromDate 仅重放补计不算尾漏计利息时非 null(=真实平仓日),锚定尾日跳过取价起点。
// exclusionStart 仅重放补计不算尾漏计利息时非 null(=真实平仓日),锚定尾日跳过取价起点。
var (segmentRates, currentFloat) = BuildSegmentRates(
startDate, endDate, interestPeriod, position, flowEvent.InterestRate, floateRate,
fetchAfterDate: null, calcLast: calcLast, relaxedFixingFromDate: relaxedFixingFromDate);
fetchAfterDate: null, calcLast: calcLast, exclusionStart: exclusionStart);
// 纯函数复利计息:分段重置日并本金 + resetCarryInterest + 扣 consumedInterest
var interestTrace = new AccrualTrace();
@@ -1445,7 +1447,7 @@ namespace YLErp.Modules.SwapModule
/// <summary>
/// 计算单利 盘中(按重置天数分段,每段使用对应浮动利率)
/// </summary>
public void CalcDailySimpleInterest(eod_swap_position preEodPosition, DateTime endDate, swap_position position, decimal posiPrincipal, swap_flow_event flowEvent, int annualDays, decimal floateRate, decimal closePercent, decimal orginPv, bool calcFirst, bool calcLast, ref decimal InterestAmount, ref decimal TdInterestAmount, decimal consumedInterest = 0m, DateTime? relaxedFixingFromDate = null)
public void CalcDailySimpleInterest(eod_swap_position preEodPosition, DateTime endDate, swap_position position, decimal posiPrincipal, swap_flow_event flowEvent, int annualDays, decimal floateRate, decimal closePercent, decimal orginPv, bool calcFirst, bool calcLast, ref decimal InterestAmount, ref decimal TdInterestAmount, decimal consumedInterest = 0m, DateTime? exclusionStart = null)
{
var startDate = position.PosiStartDate;
int interestPeriod = position.interest_rest_days ?? 1;
@@ -1458,7 +1460,7 @@ namespace YLErp.Modules.SwapModule
// calcLast 透传,与下方 AccruePeriod 的边界同源,避免两处漂移。
var (segmentRates, currentFloat) = BuildSegmentRates(
startDate, endDate, interestPeriod, position, flowEvent.InterestRate, floateRate,
fetchAfterDate: preEodPosition.ValueDate, calcLast: calcLast, relaxedFixingFromDate: relaxedFixingFromDate);
fetchAfterDate: preEodPosition.ValueDate, calcLast: calcLast, exclusionStart: exclusionStart);
// 纯函数计息:Accrued=缩放累计(InterestAmount)AccruedToday=未缩放累计(TdInterestAmount)
var interestTrace = new AccrualTrace();