diff --git a/UnitTestProject/Modules/SwapModule/Accrual/CompoundPeriodShadowTest.cs b/UnitTestProject/Modules/SwapModule/Accrual/CompoundPeriodShadowTest.cs
index bcba5199..d1fd0a26 100644
--- a/UnitTestProject/Modules/SwapModule/Accrual/CompoundPeriodShadowTest.cs
+++ b/UnitTestProject/Modules/SwapModule/Accrual/CompoundPeriodShadowTest.cs
@@ -87,7 +87,7 @@ namespace UnitTestProject.Modules.SwapModule.Accrual
(StartDate.AddDays(14), allInRate),
};
var result = FundingLegAccrual.AccrueCompoundPeriod(
- principal: Notional,
+ notional: Notional,
segmentRates: segRates,
startDate: StartDate,
endDate: EndDate,
@@ -129,7 +129,7 @@ namespace UnitTestProject.Modules.SwapModule.Accrual
(StartDate.AddDays(14), allInRate),
};
var result = FundingLegAccrual.AccrueCompoundPeriod(
- principal: Notional * closePct,
+ notional: Notional * closePct,
segmentRates: segRates,
startDate: StartDate,
endDate: EndDate,
@@ -137,8 +137,8 @@ namespace UnitTestProject.Modules.SwapModule.Accrual
annualDays: AnnualDays,
isAnnualized: true,
resetCarryInterest: carry,
- consumedInterest: consumed,
- closePercent: closePct);
+ realizedInterest: consumed,
+ unwindFraction: closePct);
Console.WriteLine($"旧: I={oldI} Td={oldTd}");
Console.WriteLine($"新: Accrued={result.Accrued} AccruedToday={result.AccruedToday}");
@@ -171,7 +171,7 @@ namespace UnitTestProject.Modules.SwapModule.Accrual
(StartDate.AddDays(14), allInRate),
};
var result = FundingLegAccrual.AccrueCompoundPeriod(
- principal: Notional,
+ notional: Notional,
segmentRates: segRates,
startDate: StartDate,
endDate: EndDate,
diff --git a/UnitTestProject/Modules/SwapModule/Accrual/SimplePeriodShadowTest.cs b/UnitTestProject/Modules/SwapModule/Accrual/SimplePeriodShadowTest.cs
index 36f6d3ac..6d42707d 100644
--- a/UnitTestProject/Modules/SwapModule/Accrual/SimplePeriodShadowTest.cs
+++ b/UnitTestProject/Modules/SwapModule/Accrual/SimplePeriodShadowTest.cs
@@ -86,9 +86,9 @@ namespace UnitTestProject.Modules.SwapModule.Accrual
var segRates = new List<(DateTime, decimal)> { (StartDate, Spread) };
var accrualPrincipal = 0m + Notional - Notional; // 差分 = 0
var result = FundingLegAccrual.AccrueSimplePeriod(
- priorUnrealized: 0m,
- accrualPrincipal: 0m, // 差分=0(无归档时 preEod.TdInterestPrincipal=0)
- closeRatio: 1m,
+ priorAccrued: 0m,
+ notional: 0m,
+ unwindFraction: 1m,
segmentRates: segRates,
startDate: StartDate,
endDate: EndDate,
@@ -131,9 +131,9 @@ namespace UnitTestProject.Modules.SwapModule.Accrual
var accrualPrincipal = Notional + Notional - Notional;
var segRates = new List<(DateTime, decimal)> { (StartDate, Spread) };
var result = FundingLegAccrual.AccrueSimplePeriod(
- priorUnrealized: 200_000m * 0.5m, // InterestProfitSum × closePercent
- accrualPrincipal: accrualPrincipal,
- closeRatio: 0.5m,
+ priorAccrued: 200_000m * 0.5m,
+ notional: accrualPrincipal,
+ unwindFraction: 0.5m,
segmentRates: segRates,
startDate: StartDate,
endDate: EndDate,
diff --git a/YLErpDAL/Modules/SwapModule/Accrual/FundingLegAccrual.cs b/YLErpDAL/Modules/SwapModule/Accrual/FundingLegAccrual.cs
index aef143f2..d291a37b 100644
--- a/YLErpDAL/Modules/SwapModule/Accrual/FundingLegAccrual.cs
+++ b/YLErpDAL/Modules/SwapModule/Accrual/FundingLegAccrual.cs
@@ -5,6 +5,14 @@ namespace YLErp.Modules.SwapModule.Accrual;
///
/// 融资腿计息编排层——纯数学部分(替换 SwapDealService 内 CalcDaily* 家族的纯计算)。
+///
+/// 命名规范(对齐 QuantLib / Strata):
+/// - notional/principal → 计息基数
+/// - accrued → 累计应计利息
+/// - unwindFraction → 平仓比例(0~1)
+/// - realizedInterest → 历史已结利息(legacy: consumedInterest)
+/// - referenceNotional → 差分公式参考本金(legacy: orginPv/originalPv)
+/// - accrualBasis → 逐段滚动的计息基数(legacy: dynomicPrincipal)
///
public static class FundingLegAccrual
{
@@ -13,77 +21,82 @@ public static class FundingLegAccrual
///
/// 单利日终计息(替换 CalcDailySimpleInterestByEod 的纯数学部分)。
///
+ /// 上一日终累计应计利息。
+ /// 上一日终计息基数。
+ /// 当前持仓名义本金。
+ /// 平仓比例(EOD 恒为 1)。
+ /// 差分公式参考本金(legacy: orginPv)。
public static InterestResult AccrueSimpleEod(
- decimal priorUnrealized,
- decimal priorAccrualPrincipal,
- decimal positionPrincipal,
- decimal closeRatio,
- decimal originalPv,
+ decimal priorAccrued,
+ decimal priorNotional,
+ decimal notional,
+ decimal unwindFraction,
+ decimal referenceNotional,
FundingLegRate rate,
AccrualPolicy policy,
DateTime eodDate,
AccrualTrace? trace = null)
{
- var baseTdInterestPrincipal = priorAccrualPrincipal + positionPrincipal - originalPv;
- var baseInterestPrincipal = baseTdInterestPrincipal * closeRatio;
+ var basis = priorNotional + notional - referenceNotional;
+ var displayBasis = basis * unwindFraction;
- var combinedRate = rate.AllInRate;
- var dayInterest = baseInterestPrincipal * combinedRate;
- var tdInterest = baseTdInterestPrincipal * combinedRate;
+ var allInRate = rate.AllInRate;
+ var dayInterest = displayBasis * allInRate;
+ var tdInterest = basis * allInRate;
if (policy.IsAnnualized)
{
dayInterest /= policy.AnnualDays;
tdInterest /= policy.AnnualDays;
}
- var totalUnrealized = priorUnrealized + dayInterest;
+ var totalAccrued = priorAccrued + dayInterest;
var result = new InterestResult(
- SwapInterest.Round(totalUnrealized, Precision),
+ SwapInterest.Round(totalAccrued, Precision),
SwapInterest.Round(tdInterest, Precision));
- trace?.Day(0, eodDate, combinedRate, baseInterestPrincipal, dayInterest, totalUnrealized);
+ trace?.Day(0, eodDate, allInRate, displayBasis, dayInterest, totalAccrued);
trace?.MarkEnd(result.Accrued, result.AccruedToday);
return result;
}
///
/// 复利日终计息(替换 CalcDailyCompoundInterestByEod 的纯数学部分)。
- /// 重置日:本金 = positionPrincipal + priorUnrealized × remainingPercent。
- /// 非重置日:本金 = priorAccrualPrincipal + positionPrincipal - originalPv。
+ /// 重置日:basis = notional + priorAccrued × remainingFraction。
+ /// 非重置日:basis = priorNotional + notional - referenceNotional。
///
public static InterestResult AccrueCompoundEod(
- decimal priorUnrealized,
- decimal priorAccrualPrincipal,
- decimal positionPrincipal,
- decimal closeRatio,
- decimal originalPv,
+ decimal priorAccrued,
+ decimal priorNotional,
+ decimal notional,
+ decimal unwindFraction,
+ decimal referenceNotional,
FundingLegRate rate,
AccrualPolicy policy,
bool isResetDay,
- decimal remainingPercent,
+ decimal remainingFraction,
DateTime eodDate,
AccrualTrace? trace = null)
{
- var baseTdInterestPrincipal = isResetDay
- ? positionPrincipal + priorUnrealized * remainingPercent
- : priorAccrualPrincipal + positionPrincipal - originalPv;
- var baseInterestPrincipal = baseTdInterestPrincipal * closeRatio;
+ var basis = isResetDay
+ ? notional + priorAccrued * remainingFraction
+ : priorNotional + notional - referenceNotional;
+ var displayBasis = basis * unwindFraction;
- var combinedRate = rate.AllInRate;
- var dayInterest = baseInterestPrincipal * combinedRate;
- var tdInterest = baseTdInterestPrincipal * combinedRate;
+ var allInRate = rate.AllInRate;
+ var dayInterest = displayBasis * allInRate;
+ var tdInterest = basis * allInRate;
if (policy.IsAnnualized)
{
dayInterest /= policy.AnnualDays;
tdInterest /= policy.AnnualDays;
}
- var totalUnrealized = priorUnrealized * closeRatio + dayInterest;
+ var totalAccrued = priorAccrued * unwindFraction + dayInterest;
var result = new InterestResult(
- SwapInterest.Round(totalUnrealized, Precision),
+ SwapInterest.Round(totalAccrued, Precision),
SwapInterest.Round(tdInterest, Precision));
- trace?.Day(0, eodDate, combinedRate, baseInterestPrincipal, dayInterest, totalUnrealized);
+ trace?.Day(0, eodDate, allInRate, displayBasis, dayInterest, totalAccrued);
trace?.MarkEnd(result.Accrued, result.AccruedToday);
return result;
}
@@ -92,10 +105,15 @@ public static class FundingLegAccrual
/// 单利多日计息(替换 CalcDailySimpleInterest 的纯数学部分)。
/// 本金全程恒定,按重置日分段取利率。
///
+ /// 上一日终累计应计利息。
+ /// 计息基数(差分,全程恒定)。
+ /// 平仓比例。
+ /// 分段利率表:(段起日, all-in利率),按日期升序。
+ /// 上一日终归档日(只算此日之后的利息)。
public static InterestResult AccrueSimplePeriod(
- decimal priorUnrealized,
- decimal accrualPrincipal,
- decimal closeRatio,
+ decimal priorAccrued,
+ decimal notional,
+ decimal unwindFraction,
IReadOnlyList<(DateTime StartDate, decimal Rate)> segmentRates,
DateTime startDate,
DateTime endDate,
@@ -104,16 +122,14 @@ public static class FundingLegAccrual
int annualDays,
bool isAnnualized)
{
- var displayPrincipal = accrualPrincipal * closeRatio;
- decimal interest = priorUnrealized;
- decimal tdInterest = priorUnrealized;
+ var displayBasis = notional * unwindFraction;
+ decimal accrued = priorAccrued;
var segStart = startDate;
var segIncludeStart = boundary.IncludeStart;
for (int si = 0; si < segmentRates.Count; si++)
{
- var (segRateStart, segRate) = segmentRates[si];
var segEnd = si < segmentRates.Count - 1
? segmentRates[si + 1].StartDate
: endDate;
@@ -125,28 +141,28 @@ public static class FundingLegAccrual
var days = SwapInterest.AccrualDays(effectiveStart, segEnd, segBoundary);
if (days <= 0) { segStart = segEnd; segIncludeStart = false; continue; }
- var dailyRate = isAnnualized ? segRate / annualDays : segRate;
- var daily = SwapInterest.Round(displayPrincipal * dailyRate, Precision);
- var segInterest = SwapInterest.Round(daily * days, Precision);
-
- interest += segInterest;
- tdInterest += segInterest;
+ var dailyRate = isAnnualized ? segmentRates[si].Rate / annualDays : segmentRates[si].Rate;
+ accrued += displayBasis * dailyRate * days;
segStart = segEnd;
segIncludeStart = false;
}
return new InterestResult(
- SwapInterest.Round(interest, Precision),
- SwapInterest.Round(tdInterest, Precision));
+ SwapInterest.Round(accrued, Precision),
+ SwapInterest.Round(accrued, Precision));
}
///
/// 复利多日计息(替换 CalcDailyCompoundInterest 的纯数学部分)。
/// 从 startDate 到 endDate 全程重放,每个重置日把累计利息并入本金。
///
+ /// 本次平仓名义本金(已按 unwindFraction 缩放)。
+ /// 末日重置时的存量利息(非末日传 0)。
+ /// 历史已结利息(legacy: consumedInterest)。
+ /// 平仓比例。
public static InterestResult AccrueCompoundPeriod(
- decimal principal,
+ decimal notional,
IReadOnlyList<(DateTime StartDate, decimal Rate)> segmentRates,
DateTime startDate,
DateTime endDate,
@@ -154,49 +170,41 @@ public static class FundingLegAccrual
int annualDays,
bool isAnnualized,
decimal resetCarryInterest = 0m,
- decimal consumedInterest = 0m,
- decimal closePercent = 1m)
+ decimal realizedInterest = 0m,
+ decimal unwindFraction = 1m)
{
- decimal dynomicPrincipal = principal;
- decimal interest = 0m;
- decimal tdInterest = 0m;
+ decimal accrualBasis = notional;
+ decimal accrued = 0m;
for (int si = 0; si < segmentRates.Count; si++)
{
- var (segStart, segRate) = segmentRates[si];
var segEnd = si < segmentRates.Count - 1
? segmentRates[si + 1].StartDate
: endDate;
// 重置日并本金
- dynomicPrincipal = si == 0
- ? principal
- : principal + interest;
+ accrualBasis = si == 0 ? notional : notional + accrued;
- // 末日重置且 resetCarryInterest 非零:用存量替代
+ // 末日重置且 carry 非零:用存量替代
if (segEnd == endDate && si > 0 && resetCarryInterest != 0m)
- dynomicPrincipal = principal + resetCarryInterest;
+ accrualBasis = notional + resetCarryInterest;
// 半开区间:重置日归下一段(旧代码逐日循环中重置日先更新本金再算息)
var segIncludeStart = (si == 0) ? boundary.IncludeStart : true;
var segIncludeEnd = (segEnd == endDate) ? boundary.IncludeEnd : false;
- var days = SwapInterest.AccrualDays(segStart, segEnd, AccrualBoundary.Of(segIncludeStart, segIncludeEnd));
+ var days = SwapInterest.AccrualDays(segmentRates[si].StartDate, segEnd,
+ AccrualBoundary.Of(segIncludeStart, segIncludeEnd));
if (days <= 0) continue;
- var dailyRate = isAnnualized ? segRate / annualDays : segRate;
- var daily = dynomicPrincipal * dailyRate;
- var segInterest = daily * days;
-
- interest += segInterest;
- tdInterest += segInterest;
+ var dailyRate = isAnnualized ? segmentRates[si].Rate / annualDays : segmentRates[si].Rate;
+ accrued += accrualBasis * dailyRate * days;
}
// 扣除历史已结利息
- interest -= consumedInterest * closePercent;
- tdInterest -= consumedInterest * closePercent;
+ accrued -= realizedInterest * unwindFraction;
return new InterestResult(
- SwapInterest.Round(interest, Precision),
- SwapInterest.Round(tdInterest, Precision));
+ SwapInterest.Round(accrued, Precision),
+ SwapInterest.Round(accrued, Precision));
}
}
diff --git a/YLErpDAL/Modules/SwapModule/SwapDealService.cs b/YLErpDAL/Modules/SwapModule/SwapDealService.cs
index 63ca2edc..d4696526 100644
--- a/YLErpDAL/Modules/SwapModule/SwapDealService.cs
+++ b/YLErpDAL/Modules/SwapModule/SwapDealService.cs
@@ -1601,11 +1601,11 @@ namespace YLErp.Modules.SwapModule
// 完整计息 trace:收集器由适配器创建,随后经 SwapCalcTrace 常驻落盘(关键路径日志,无条件)。
var interestTrace = new AccrualTrace();
var result = FundingLegAccrual.AccrueSimpleEod(
- priorUnrealized: preEodPosition.InterestProfitSum,
- priorAccrualPrincipal: preEodPosition.TdInterestPrincipal,
- positionPrincipal: posiPrincipal,
- closeRatio: closePercent,
- originalPv: orginPv,
+ priorAccrued: preEodPosition.InterestProfitSum,
+ priorNotional: preEodPosition.TdInterestPrincipal,
+ notional: posiPrincipal,
+ unwindFraction: closePercent,
+ referenceNotional: orginPv,
rate: legRate,
policy: accrualPolicy,
eodDate: endDate,