refactor(swap): EQD-6968 取价口径自洽化——排除日纯跳过+持仓延续重置日显式再定盘
取价规则只由交易属性决定(算不算尾/interest_rule/重置日历),平仓只切窗口: ① BuildSegmentRates 排除日(不算尾尾日)一概不取价(有价也不取)——事件利率恒为 末段已消费利率,与金额同源、与平仓时刻无关(旧口径下午平仓落库新利率,金额 却用旧利率,且上午/下午记录不同);金额零变更(排除日本就零消费)。 ② GetFloatRate 不算尾重置日不再先试取尾日定盘(原'有价则取'分支删除)。 ③ 剩余持仓的新周期利率改由 SwapEodPositionService 显式获取:平仓日恰为重置日 且剩余>0 时,快照 FloatRate 经 ResolveOngoingResetFixing 再定盘为当日新定盘 (生产 GetFixingOrThrow,缺价抛——与 ByEod 重置日/EodCheckSettlePrice 同口径; 原'有价则取'顺带承担此载体职责,现显式分离)。全平/算尾/观察日无需再定盘。 ④ 无日终快照播种 priorValueDate 恒=开始日-1:首重置日(=开始日)的定盘覆盖 [开始日,下一重置日)全部计息日(不算头时 7/7 起仍属首段),必须落在取价窗内。 原仅算头回拨一天,不算头时首段误用种子利率(靠②的尾日取价回填掩盖,现已根治)。 '不算头少计一天'仍由计息边界 IncludeStart=false 承担,天数不变、利率归位。 FIX 日志同步:排除日不取价/EodCloseRefix 再定盘 全程可审计。 验证:全量 976 例 145 败与改动前基线失败名单 diff=0;内存套件(含 ContractReferenceOracle 金标准/InterestEodTailSnapshot)全绿
This commit is contained in:
@@ -850,8 +850,9 @@ namespace YLErp.Modules.SwapModule
|
|||||||
/// EQD-6968 取价依赖三判定之一。锚点口径注记:GetFloatRate 传交易起始日 td.StartDate,
|
/// EQD-6968 取价依赖三判定之一。锚点口径注记:GetFloatRate 传交易起始日 td.StartDate,
|
||||||
/// 分段重放/当日归周期判定传 position.PosiStartDate——非初始持仓(部分平仓剩余仓)两锚点可能不同,
|
/// 分段重放/当日归周期判定传 position.PosiStartDate——非初始持仓(部分平仓剩余仓)两锚点可能不同,
|
||||||
/// 本方法只收口公式、不统一锚点(统一属行为变更,需业务定调)。
|
/// 本方法只收口公式、不统一锚点(统一属行为变更,需业务定调)。
|
||||||
|
/// SwapEodPositionService 的"持仓延续腿重置日再定盘"亦用本判定(td.StartDate 锚点)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static bool IsResetDay(DateTime date, DateTime anchorDate, int period)
|
internal static bool IsResetDay(DateTime date, DateTime anchorDate, int period)
|
||||||
=> (date - anchorDate).Days % period == 0;
|
=> (date - anchorDate).Days % period == 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -879,6 +880,17 @@ namespace YLErp.Modules.SwapModule
|
|||||||
return preEod.FloatRate;
|
return preEod.FloatRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EQD-6968 口径自洽化:不算尾(calcLast=false)时 endDate 当天不计息,其定盘一概不取
|
||||||
|
// (有价也不取)——事件/回写利率与金额同源(末段已消费利率),杜绝"上午/下午落库利率不同"。
|
||||||
|
// 剩余持仓的新周期利率由 SwapEodPositionService 的"重置日再定盘"显式获取,不靠此处顺带。
|
||||||
|
if (!swap && !calcLast && isResetDay)
|
||||||
|
{
|
||||||
|
var keptNoFetch = preEod.id != 0 ? preEod.FloatRate : position.FloatRate;
|
||||||
|
SwapCalcTrace.Critical(
|
||||||
|
$"FIX GetFloatRate p{position.id} 不算尾重置日→不取尾日定盘,沿用已有利率={keptNoFetch:P6}(来源={(preEod.id != 0 ? "preEod.FloatRate" : "position.FloatRate")})");
|
||||||
|
return keptNoFetch;
|
||||||
|
}
|
||||||
|
|
||||||
if (IndexFixer.TryGetFixing(rateDate, position.FloatRateUnderlyingCode, out decimal rate))
|
if (IndexFixer.TryGetFixing(rateDate, position.FloatRateUnderlyingCode, out decimal rate))
|
||||||
{
|
{
|
||||||
SwapCalcTrace.Critical($"FIX GetFloatRate p{position.id} 重置日→取{rateDate:yyyy-MM-dd}定盘={rate:P6}");
|
SwapCalcTrace.Critical($"FIX GetFloatRate p{position.id} 重置日→取{rateDate:yyyy-MM-dd}定盘={rate:P6}");
|
||||||
@@ -1115,11 +1127,12 @@ namespace YLErp.Modules.SwapModule
|
|||||||
preEod.FloatRate = floatRate;
|
preEod.FloatRate = floatRate;
|
||||||
preEod.TdInterestPrincipal = posiPrincipal;
|
preEod.TdInterestPrincipal = posiPrincipal;
|
||||||
preEod.PosiNotionalValue = posiPrincipal;
|
preEod.PosiNotionalValue = posiPrincipal;
|
||||||
preEod.ValueDate = td.StartDate.Value;
|
// priorValueDate 恒取 开始日-1:首个重置日(=开始日)的定盘覆盖 [开始日,下一重置日) 全部计息日
|
||||||
if (calcFirst)
|
//(不算头时 7/7 起的计息日仍属首段),必须落在取价窗内。原实现仅算头回拨一天,
|
||||||
{
|
// 不算头时 fetchAfter=开始日 会跳过首重置日取价、首段误用种子利率
|
||||||
preEod.ValueDate = preEod.ValueDate.AddDays(-1);
|
//(历史上靠 GetFloatRate 尾日取价回填种子掩盖;EQD-6968 自洽化后暴露并根治)。
|
||||||
}
|
// "不算头少计一天"由计息边界 IncludeStart=false 承担,与此处无关。
|
||||||
|
preEod.ValueDate = td.StartDate.Value.AddDays(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return InitSwapDealInterest(td, valueDate, endDate, rate, position, add, swap, posiPrincipal,
|
return InitSwapDealInterest(td, valueDate, endDate, rate, position, add, swap, posiPrincipal,
|
||||||
@@ -1359,10 +1372,10 @@ namespace YLErp.Modules.SwapModule
|
|||||||
{
|
{
|
||||||
var resetDate = startDate.AddDays(i);
|
var resetDate = startDate.AddDays(i);
|
||||||
bool needFetch = (fetchAfterDate == null || resetDate > fetchAfterDate.Value);
|
bool needFetch = (fetchAfterDate == null || resetDate > fetchAfterDate.Value);
|
||||||
// 算头不算尾(calcLast=false)时,endDate 当天不计息,其重置日利率不参与计息。
|
// 算头不算尾(calcLast=false)时,endDate 当天不计息,其重置日利率不参与计息——
|
||||||
// 从 relaxedFixingFromDate(缺省取 endDate)起的重置日:有价则取(满足重置日=平仓日用新利率),
|
// 排除日(EQD-6968 自洽化)一概不取价(有价也不取),currentFloat 保持末段已消费利率:
|
||||||
// 缺价则跳过取价——currentFloat 保持不变(沿用上一重置日利率),不回退、不告警、不抛异常。
|
// 事件/快照回写的浮动利率与金额同源、与平仓时刻无关。剩余持仓的新周期利率由
|
||||||
// 算尾(calcLast=true)时所有重置日定盘照常强制取价。
|
// SwapEodPositionService 的"重置日再定盘"显式获取,不靠排除日顺带。算尾照常强制取价。
|
||||||
bool isExcludedEnd = !calcLast && resetDate >= (relaxedFixingFromDate ?? endDate);
|
bool isExcludedEnd = !calcLast && resetDate >= (relaxedFixingFromDate ?? endDate);
|
||||||
if (needFetch && !isExcludedEnd)
|
if (needFetch && !isExcludedEnd)
|
||||||
{
|
{
|
||||||
@@ -1370,19 +1383,8 @@ namespace YLErp.Modules.SwapModule
|
|||||||
}
|
}
|
||||||
else if (needFetch && isExcludedEnd)
|
else if (needFetch && isExcludedEnd)
|
||||||
{
|
{
|
||||||
// 有价则取新利率(重置日=平仓日时仍优先使用新利率);无价则跳过——该日利率不参与计息
|
SwapCalcTrace.Critical(
|
||||||
var fixingDate = IndexFixerBase.GetFixingDate(resetDate, position.interest_rule);
|
$"FIX Segment p{position.id} {resetDate:yyyy-MM-dd} 排除日(不计息)→不取价,沿用末段={currentFloat:P6}");
|
||||||
if (IndexFixer.TryGetFixing(fixingDate, position.FloatRateUnderlyingCode, out decimal r))
|
|
||||||
{
|
|
||||||
currentFloat = r;
|
|
||||||
SwapCalcTrace.Critical(
|
|
||||||
$"FIX Segment p{position.id} {resetDate:yyyy-MM-dd} 排除日有价→取新定盘(取价日={fixingDate:yyyy-MM-dd})={r:P6}");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SwapCalcTrace.Critical(
|
|
||||||
$"FIX Segment p{position.id} {resetDate:yyyy-MM-dd} 排除日缺价→跳过取价,沿用={currentFloat:P6}(该日不计息)");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
rates.Add((resetDate, spread + currentFloat));
|
rates.Add((resetDate, spread + currentFloat));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,6 +117,14 @@ namespace YLErp.Modules.SwapModule
|
|||||||
req.ClosePosiNotionalValue, req.ClosePercent, req.EventType, req.TdClose,
|
req.ClosePosiNotionalValue, req.ClosePercent, req.EventType, req.TdClose,
|
||||||
req.OrginPv, req.Add, settment: false, req.NewCalcLast, req.CloseList);
|
req.OrginPv, req.Add, settment: false, req.NewCalcLast, req.CloseList);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 持仓延续腿重置日再定盘(EQD-6968 口径自洽化接缝)。
|
||||||
|
/// 生产:Fr007IndexFixer.GetFixingOrThrow——缺价抛异常,与 ByEod 重置日再定盘/EodCheckSettlePrice
|
||||||
|
/// 同口径(EOD 时点当日 FR007 已由收盘前检查把关);测试:override 注入受控定盘。
|
||||||
|
/// </summary>
|
||||||
|
protected virtual decimal ResolveOngoingResetFixing(swap_position position, DateTime valueDate)
|
||||||
|
=> Fr007IndexFixer.Instance.GetFixingOrThrow(valueDate, position.interest_rule, position.FloatRateUnderlyingCode);
|
||||||
|
|
||||||
// FindTrade 已上提到基类 SwapTradeBaseService(三子类实现一致,消除重复)
|
// FindTrade 已上提到基类 SwapTradeBaseService(三子类实现一致,消除重复)
|
||||||
|
|
||||||
/// <summary>查找交易扩展(生产: DbContext.trade_extend;测试: 内存字典)</summary>
|
/// <summary>查找交易扩展(生产: DbContext.trade_extend;测试: 内存字典)</summary>
|
||||||
@@ -1361,6 +1369,20 @@ namespace YLErp.Modules.SwapModule
|
|||||||
//持仓内容-利息腿(FloatRate 取计息结果)。InterestPrincipalFix 保持腿现值:
|
//持仓内容-利息腿(FloatRate 取计息结果)。InterestPrincipalFix 保持腿现值:
|
||||||
// ResolveInterestLegPositions 已提供平仓后的实时剩余本金,日终不再重复扣减(勿恢复 *(1-closePercent))。
|
// ResolveInterestLegPositions 已提供平仓后的实时剩余本金,日终不再重复扣减(勿恢复 *(1-closePercent))。
|
||||||
CopyInterestLegFields(newEodPayPosition, position, interests.Count > 0 ? interests.First().FloatRate ?? 0 : 0);
|
CopyInterestLegFields(newEodPayPosition, position, interests.Count > 0 ? interests.First().FloatRate ?? 0 : 0);
|
||||||
|
// ── 持仓延续腿重置日再定盘(EQD-6968 自洽化)──
|
||||||
|
// 排除日取价已收口为"纯跳过":事件利率=末段已消费利率。但剩余持仓自当日起进入新计息周期,
|
||||||
|
// 快照 FloatRate 是后续非重置日(ByEod 沿用 preEod.FloatRate)与当日应计(intersetAcmount)的
|
||||||
|
// 利率载体——平仓日恰为重置日时必须显式取当日新定盘(与 ByEod 日增路径的重置日行为同构)。
|
||||||
|
// 全平(剩余=0)/算尾(事件利率已是新定盘)/观察日(autoSwap 恒1已含当日)无需再定盘。
|
||||||
|
if (!autoSwap && !calcLast && posiNotionalValue > 0m
|
||||||
|
&& !string.IsNullOrEmpty(position.FloatRateUnderlyingCode)
|
||||||
|
&& SwapDealService.IsResetDay(valueDate, td.StartDate.Value, position.interest_rest_days ?? 1))
|
||||||
|
{
|
||||||
|
var ongoingFixing = ResolveOngoingResetFixing(position, valueDate);
|
||||||
|
SwapCalcTrace.Critical(
|
||||||
|
$"FIX EodCloseRefix p{position.id} {valueDate:yyyy-MM-dd} 平仓日=重置日→剩余持仓快照再定盘 {newEodPayPosition.FloatRate:P6}→{ongoingFixing:P6}");
|
||||||
|
newEodPayPosition.FloatRate = ongoingFixing;
|
||||||
|
}
|
||||||
//利息端估值用信息
|
//利息端估值用信息
|
||||||
// TdInterestPrincipal 是“下一日继续计息的收盘后本金”,不是原始合同规模,也不是本次平仓本金。
|
// TdInterestPrincipal 是“下一日继续计息的收盘后本金”,不是原始合同规模,也不是本次平仓本金。
|
||||||
// 模式9单利直接取剩余名义本金;复利还要保留重置时已经并入本金的待实现利息。
|
// 模式9单利直接取剩余名义本金;复利还要保留重置时已经并入本金的待实现利息。
|
||||||
|
|||||||
Reference in New Issue
Block a user