单利与复利语义完全不同(单利本金恒定/复利重置日并本金), 拆成两个独立静态类,各自只含自己的方法: SimpleInterestAccrual: - AccrueEod (原AccrueSimpleEod) - AccruePeriod (原AccrueSimplePeriod) CompoundInterestAccrual: - EodBasis (原CompoundEodBasis) - AccrueEod (原AccrueCompoundEod) - AccruePeriod (原AccrueCompoundPeriod) 方法名去掉Simple/Compound前缀(类名已携带类型),消除冗余 SwapModule零回归(7基线/510通过)
107 lines
4.1 KiB
C#
107 lines
4.1 KiB
C#
using YLErp.Core.Interest;
|
||
using YLErp.Derivatives.Interest;
|
||
|
||
namespace YLErp.Modules.SwapModule.Accrual;
|
||
|
||
/// <summary>
|
||
/// 单利计息纯函数——EOD 单日 + intraday 多日。
|
||
/// 单利特征:本金全程恒定(无并本金),按重置日分段取利率。
|
||
/// </summary>
|
||
public static class SimpleInterestAccrual
|
||
{
|
||
private const int Precision = SwapInterest.FundingLegPrecision;
|
||
|
||
/// <summary>
|
||
/// 单利日终计息(替换 CalcDailySimpleInterestByEod 的纯数学部分)。
|
||
/// EOD 无差分:basis = priorNotional(昨日终滚动计息基数)。
|
||
/// </summary>
|
||
public static InterestResult AccrueEod(
|
||
decimal priorAccrued,
|
||
decimal priorNotional,
|
||
decimal unwindFraction,
|
||
FundingLegRate rate,
|
||
AccrualPolicy policy,
|
||
DateTime eodDate,
|
||
AccrualTrace? trace = null)
|
||
{
|
||
var basis = priorNotional;
|
||
var displayBasis = basis * unwindFraction;
|
||
|
||
var allInRate = rate.AllInRate;
|
||
var dayInterest = displayBasis * allInRate;
|
||
var tdInterest = basis * allInRate;
|
||
if (policy.IsAnnualized)
|
||
{
|
||
dayInterest /= policy.AnnualDays;
|
||
tdInterest /= policy.AnnualDays;
|
||
}
|
||
|
||
var totalAccrued = priorAccrued + dayInterest;
|
||
var result = new InterestResult(
|
||
SwapInterest.Round(totalAccrued, Precision),
|
||
SwapInterest.Round(tdInterest, Precision));
|
||
|
||
trace?.Day(0, eodDate, allInRate, displayBasis, dayInterest, totalAccrued);
|
||
trace?.MarkEnd(result.Accrued, result.AccruedToday);
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 单利多日计息(替换 CalcDailySimpleInterest 的纯数学部分)。
|
||
/// 本金全程恒定,按重置日分段取利率。
|
||
/// Accrued = 缩放累计(InterestAmount),AccruedToday = 未缩放累计(TdInterestAmount)。
|
||
/// </summary>
|
||
public static InterestResult AccruePeriod(
|
||
decimal priorAccrued,
|
||
decimal notional,
|
||
decimal unwindFraction,
|
||
IReadOnlyList<(DateTime StartDate, decimal Rate)> segmentRates,
|
||
DateTime startDate,
|
||
DateTime endDate,
|
||
DateTime priorValueDate,
|
||
AccrualBoundary boundary,
|
||
int annualDays,
|
||
bool isAnnualized,
|
||
AccrualTrace? trace = null)
|
||
{
|
||
var displayBasis = notional * unwindFraction;
|
||
decimal accrued = priorAccrued; // 缩放累计 → InterestAmount
|
||
decimal accruedUnscaled = priorAccrued; // 未缩放累计 → TdInterestAmount
|
||
|
||
trace?.MarkStart(startDate, endDate, boundary, annualDays, isAnnualized);
|
||
|
||
var segStart = startDate;
|
||
|
||
for (int si = 0; si < segmentRates.Count; si++)
|
||
{
|
||
var segEnd = si < segmentRates.Count - 1
|
||
? segmentRates[si + 1].StartDate
|
||
: endDate;
|
||
|
||
var effectiveStart = segStart > priorValueDate ? segStart : priorValueDate.AddDays(1);
|
||
if (effectiveStart > segEnd) { segStart = segEnd; continue; }
|
||
|
||
// calcFirst 只跳过 startDate 本身;其余天(含重置日、ValueDate+1)只要 > ValueDate 恒纳入。
|
||
var includeStart = effectiveStart == startDate ? boundary.IncludeStart : true;
|
||
var isLastSegment = si == segmentRates.Count - 1;
|
||
var segBoundary = AccrualBoundary.Of(includeStart, isLastSegment && boundary.IncludeEnd);
|
||
var days = SwapInterest.AccrualDays(effectiveStart, segEnd, segBoundary);
|
||
if (days <= 0) { segStart = segEnd; continue; }
|
||
|
||
var dailyRate = isAnnualized ? segmentRates[si].Rate / annualDays : segmentRates[si].Rate;
|
||
var segInterest = displayBasis * dailyRate * days;
|
||
accrued += segInterest;
|
||
accruedUnscaled += notional * dailyRate * days;
|
||
trace?.Segment(si, effectiveStart, segEnd, days, segmentRates[si].Rate, displayBasis, segInterest, accrued);
|
||
|
||
segStart = segEnd;
|
||
}
|
||
|
||
var result = new InterestResult(
|
||
SwapInterest.Round(accrued, Precision),
|
||
SwapInterest.Round(accruedUnscaled, Precision));
|
||
trace?.MarkEnd(result.Accrued, result.AccruedToday);
|
||
return result;
|
||
}
|
||
}
|