refactor(swap): EQD-6977 拆分 CalcPenalty 计息分支提升可读性
将 CalcPenalty 内复利/单利两个 if/else 大块抽为 AccrueCompound/AccrueSimple 私有方法(返回 (accrued, finalBasis)),主体只表达分派+组装事件。签名与行为零变化,Penalty 套件 11/11 通过。
This commit is contained in:
@@ -69,50 +69,13 @@ public static class SwapPenaltyInterestCalculator
|
||||
var boundary = AccrualBoundary.Of(includeStart: !unwindDaySettled, includeEnd: maturityCalcLast);
|
||||
var allInRate = frozenRate.AllInRate;
|
||||
|
||||
decimal amount;
|
||||
decimal finalBasis;
|
||||
if (policy.IsCompound)
|
||||
{
|
||||
// 复利:即使利率冻结为单值,也须按重置日分段(并本金发生在分段边界),每段同一冻结利率。
|
||||
// notional = 本金 + 已并入最近重置日的利息:全期轨迹中当前重置段的滚动基数,
|
||||
// 段内(含罚息窗口首段)每一天都在其上计息——平仓日落在段中间时与全期逐日对齐的关键。
|
||||
var segments = BuildFrozenSegments(unwindDate, maturityDate, policy.ResetPeriodDays, resetAnchor, allInRate);
|
||||
var r = CompoundInterestAccrual.AccruePeriod(
|
||||
notional: closePrincipal + capitalizedInterest,
|
||||
segmentRates: segments,
|
||||
startDate: unwindDate,
|
||||
endDate: maturityDate,
|
||||
boundary: boundary,
|
||||
annualDays: policy.AnnualDays,
|
||||
isAnnualized: policy.IsAnnualized,
|
||||
resetCarryInterest: 0m,
|
||||
realizedInterest: 0m,
|
||||
unwindFraction: 1m,
|
||||
finalBasis: out finalBasis,
|
||||
trace: trace,
|
||||
carryInInterest: carryInInterest);
|
||||
amount = r.Accrued;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 单利:无并本金语义,冻结利率即单段全程
|
||||
var r = SimpleInterestAccrual.AccruePeriod(
|
||||
priorAccrued: 0m,
|
||||
notional: closePrincipal,
|
||||
unwindFraction: 1m,
|
||||
segmentRates: new List<(DateTime StartDate, decimal Rate)> { (unwindDate, allInRate) },
|
||||
startDate: unwindDate,
|
||||
endDate: maturityDate,
|
||||
priorValueDate: unwindDate.AddDays(-1),
|
||||
boundary: boundary,
|
||||
annualDays: policy.AnnualDays,
|
||||
isAnnualized: policy.IsAnnualized,
|
||||
trace: trace);
|
||||
amount = r.Accrued;
|
||||
finalBasis = closePrincipal;
|
||||
}
|
||||
// 按计息方式分派到对应的纯计息路径;二者均产出 (累计利息, 末段计息基数)。
|
||||
// 计息数学细节下沉到具名方法,使本方法只表达“分派 + 组装事件”的编排意图,便于阅读与单测。
|
||||
var (accrued, finalBasis) = policy.IsCompound
|
||||
? AccrueCompound(closePrincipal, capitalizedInterest, carryInInterest, unwindDate, maturityDate, boundary, policy, resetAnchor, allInRate, trace)
|
||||
: AccrueSimple(closePrincipal, unwindDate, maturityDate, boundary, policy, allInRate, trace);
|
||||
|
||||
var rounded = InterestMath.Round(amount, InterestMath.FundingLegPrecision);
|
||||
var rounded = InterestMath.Round(accrued, InterestMath.FundingLegPrecision);
|
||||
var interest = new swap_flow_event
|
||||
{
|
||||
SwapTradeId = td.id,
|
||||
@@ -137,6 +100,58 @@ public static class SwapPenaltyInterestCalculator
|
||||
return interest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 复利罚息计息:即使冻结利率为单值,也必须按重置日分段(并本金发生在分段边界),每段同一冻结利率。
|
||||
/// notional = 本金 + 已并入最近重置日的利息(capitalizedInterest):全期轨迹中当前重置段的滚动基数,
|
||||
/// 段内每一天都在其上计息——平仓日落在段中间时与全期逐日对齐的关键。carryInInterest 在首个窗口重置日并入。
|
||||
/// 返回 (累计利息, 末段计息基数)。
|
||||
/// </summary>
|
||||
private static (decimal Accrued, decimal FinalBasis) AccrueCompound(
|
||||
decimal closePrincipal, decimal capitalizedInterest, decimal carryInInterest,
|
||||
DateTime unwindDate, DateTime maturityDate, AccrualBoundary boundary, AccrualPolicy policy,
|
||||
DateTime resetAnchor, decimal allInRate, AccrualTrace? trace)
|
||||
{
|
||||
var segments = BuildFrozenSegments(unwindDate, maturityDate, policy.ResetPeriodDays, resetAnchor, allInRate);
|
||||
var r = CompoundInterestAccrual.AccruePeriod(
|
||||
notional: closePrincipal + capitalizedInterest,
|
||||
segmentRates: segments,
|
||||
startDate: unwindDate,
|
||||
endDate: maturityDate,
|
||||
boundary: boundary,
|
||||
annualDays: policy.AnnualDays,
|
||||
isAnnualized: policy.IsAnnualized,
|
||||
resetCarryInterest: 0m,
|
||||
realizedInterest: 0m,
|
||||
unwindFraction: 1m,
|
||||
finalBasis: out var finalBasis,
|
||||
trace: trace,
|
||||
carryInInterest: carryInInterest);
|
||||
return (r.Accrued, finalBasis);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单利罚息计息:无并本金语义,冻结利率即单段全程。finalBasis 恒为本金(单利不滚基数)。
|
||||
/// 返回 (累计利息, 末段计息基数)。
|
||||
/// </summary>
|
||||
private static (decimal Accrued, decimal FinalBasis) AccrueSimple(
|
||||
decimal closePrincipal, DateTime unwindDate, DateTime maturityDate,
|
||||
AccrualBoundary boundary, AccrualPolicy policy, decimal allInRate, AccrualTrace? trace)
|
||||
{
|
||||
var r = SimpleInterestAccrual.AccruePeriod(
|
||||
priorAccrued: 0m,
|
||||
notional: closePrincipal,
|
||||
unwindFraction: 1m,
|
||||
segmentRates: new List<(DateTime StartDate, decimal Rate)> { (unwindDate, allInRate) },
|
||||
startDate: unwindDate,
|
||||
endDate: maturityDate,
|
||||
priorValueDate: unwindDate.AddDays(-1),
|
||||
boundary: boundary,
|
||||
annualDays: policy.AnnualDays,
|
||||
isAnnualized: policy.IsAnnualized,
|
||||
trace: trace);
|
||||
return (r.Accrued, closePrincipal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 复利冻结分段:段边界 = 窗口内重置日((d - anchor) % period == 0,对齐 IsResetDay 公式),
|
||||
/// 每段填同一冻结利率。首段必为 (unwindDate, rate);[start,end] 含端点的重置日也生成段
|
||||
|
||||
Reference in New Issue
Block a user