refactor(accrual): EOD去退化差分——删除baseNotional死参数
EOD路径 orginPv≡posiPrincipal(调用点line1091/1096实证),notional==baseNotional恒成立, 差分项 notional-baseNotional 恒为0,baseNotional 是死参数。 简化: - AccrueSimpleEod: 删 notional+baseNotional 两参数,basis=priorNotional 直赋值 - AccrueCompoundEod: 删 baseNotional 参数,非重置日 basis=priorNotional - SimpleEodBasis 整个删除(不再需要) - CompoundEodBasis 去掉 baseNotional 参数 - EodContext trace 去掉 baseNotional - 差分注释块删除(EOD不再有差分) 差分逻辑 notional-baseNotional 只该存在于 unwind 专用路径(Period函数本就没有baseNotional) 净减20行(18增/38删),SwapModule零回归(7基线/508通过/8跳过)
This commit is contained in:
@@ -41,10 +41,10 @@ public sealed class AccrualTrace
|
||||
=> Add(AccrualTraceEvent.DayAccrual, segStart,
|
||||
$" [seg{idx}] [{segStart:yyyy-MM-dd},{segEnd:yyyy-MM-dd}) days={days} rate={rate:P6} basis={basis:F4} segInterest={segInterest:F6} acc={accumulated:F6}");
|
||||
|
||||
/// <summary>EOD 上下文:计息日、重置日标志、平仓比例、差分基数公式各分量。供日终单日计息定位"重置日分支选对了吗"。</summary>
|
||||
public void EodContext(DateTime eodDate, bool isResetDay, decimal unwindFraction, decimal priorAccrued, decimal priorNotional, decimal notional, decimal baseNotional, decimal remainingFraction)
|
||||
/// <summary>EOD 上下文:计息日、重置日标志、平仓比例、基数各分量。供日终单日计息定位"重置日分支选对了吗"。</summary>
|
||||
public void EodContext(DateTime eodDate, bool isResetDay, decimal unwindFraction, decimal priorAccrued, decimal priorNotional, decimal notional, decimal remainingFraction)
|
||||
=> Add(AccrualTraceEvent.Start, eodDate,
|
||||
$" CTX {eodDate:yyyy-MM-dd} reset={isResetDay} unwind={unwindFraction:P2} priorAccrued={priorAccrued:F4} priorNotional={priorNotional:F4} notional={notional:F4} baseNotional={baseNotional:F4} remainingFrac={remainingFraction:P4}");
|
||||
$" CTX {eodDate:yyyy-MM-dd} reset={isResetDay} unwind={unwindFraction:P2} priorAccrued={priorAccrued:F4} priorNotional={priorNotional:F4} notional={notional:F4} remainingFrac={remainingFraction:P4}");
|
||||
|
||||
/// <summary>重置日<b>前</b>:生效利率(旧)与计息本金(滚动前)。利率/本金切换的"因"。</summary>
|
||||
public void ResetBefore(DateTime resetDate, decimal rateOld, decimal principalBefore)
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace UnitTestProject.Modules.SwapModule.Accrual
|
||||
var policy = new AccrualPolicy(AccrualBoundary.Both, true, 7, AnnualDays, true);
|
||||
var remainingPercent = Math.Max(0m, Math.Min(1m, Notional / Notional));
|
||||
var result = FundingLegAccrual.AccrueCompoundEod(
|
||||
50_000m, Notional, Notional, 1m, Notional, rate, policy,
|
||||
50_000m, Notional, Notional, 1m, rate, policy,
|
||||
isResetDay: true, remainingPercent, EodDate);
|
||||
|
||||
Console.WriteLine($"重置日: 旧 InterestAmount={oldInterest} Td={oldTd}");
|
||||
@@ -132,7 +132,7 @@ namespace UnitTestProject.Modules.SwapModule.Accrual
|
||||
var rate = FundingLegRate.Fixed(FixedRate);
|
||||
var policy = new AccrualPolicy(AccrualBoundary.Both, true, 7, AnnualDays, true);
|
||||
var result = FundingLegAccrual.AccrueCompoundEod(
|
||||
30_000m, Notional, Notional, 1m, Notional, rate, policy,
|
||||
30_000m, Notional, Notional, 1m, rate, policy,
|
||||
isResetDay: false, 0m, nonResetDate);
|
||||
|
||||
Console.WriteLine($"非重置日: 旧 InterestAmount={oldInterest} Td={oldTd}");
|
||||
|
||||
@@ -11,51 +11,35 @@ namespace YLErp.Modules.SwapModule.Accrual;
|
||||
/// - accrued → 累计应计利息
|
||||
/// - unwindFraction → 平仓比例(0~1)
|
||||
/// - realizedInterest → 历史已结利息(legacy: consumedInterest)
|
||||
/// - baseNotional → 差分公式基数(legacy: orginPv/originalPv)
|
||||
/// - accrualBasis → 逐段滚动的计息基数(legacy: dynomicPrincipal=dynamicPrincipal typo)
|
||||
/// - priorNotional → 昨日终滚动计息基数(legacy: TdInterestPrincipal / dynomicPrincipal)
|
||||
/// </summary>
|
||||
public static class FundingLegAccrual
|
||||
{
|
||||
private const int Precision = SwapInterest.FundingLegPrecision;
|
||||
|
||||
// ───────────────────────────────────────────────────────────────────
|
||||
// 计息基数(basis)——单一真相源,纯函数与调用方共用,消除公式重复。
|
||||
//
|
||||
// 差分语义:basis = priorNotional + (notional − baseNotional)
|
||||
// • priorNotional = 昨日终滚动计息基数(legacy: TdInterestPrincipal / dynomicPrincipal)
|
||||
// • notional − baseNotional = 名义本金绝对增量(加法式 carry,非比例缩放)
|
||||
// • EOD 路径:baseNotional == notional → 增量 0,basis 退化 = priorNotional
|
||||
// • unwind 路径:baseNotional = 上一日终浮动端名义本金(orginPv) ≠ notional → 增量生效
|
||||
// ───────────────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>单利日终计息基数(差分公式)。调用方与 AccrueSimpleEod 共用。</summary>
|
||||
public static decimal SimpleEodBasis(decimal priorNotional, decimal notional, decimal baseNotional)
|
||||
=> priorNotional + (notional - baseNotional);
|
||||
|
||||
/// <summary>复利日终计息基数:重置日 = notional + 累计利息×剩余比例(并本金);
|
||||
/// 非重置日 = 差分公式。remainingFraction 对齐 legacy 钳制到 [0,1]。调用方与 AccrueCompoundEod 共用。</summary>
|
||||
/// <summary>复利日终计息基数(单一真相源,纯函数与调用方共用):
|
||||
/// 重置日 = notional + 累计利息×剩余比例(利息并入本金);非重置日 = priorNotional(昨日滚动基数)。
|
||||
/// remainingFraction 对齐 legacy 钳制到 [0,1]。</summary>
|
||||
public static decimal CompoundEodBasis(
|
||||
bool isResetDay, decimal notional, decimal priorAccrued, decimal remainingFraction,
|
||||
decimal priorNotional, decimal baseNotional)
|
||||
bool isResetDay, decimal notional, decimal priorAccrued, decimal remainingFraction, decimal priorNotional)
|
||||
=> isResetDay
|
||||
? notional + priorAccrued * Math.Max(0m, Math.Min(1m, remainingFraction))
|
||||
: priorNotional + (notional - baseNotional);
|
||||
: priorNotional;
|
||||
|
||||
/// <summary>
|
||||
/// 单利日终计息(替换 CalcDailySimpleInterestByEod 的纯数学部分)。
|
||||
/// EOD 无差分:basis = priorNotional(昨日终滚动计息基数)。
|
||||
/// </summary>
|
||||
public static InterestResult AccrueSimpleEod(
|
||||
decimal priorAccrued,
|
||||
decimal priorNotional,
|
||||
decimal notional,
|
||||
decimal unwindFraction,
|
||||
decimal baseNotional,
|
||||
FundingLegRate rate,
|
||||
AccrualPolicy policy,
|
||||
DateTime eodDate,
|
||||
AccrualTrace? trace = null)
|
||||
{
|
||||
var basis = SimpleEodBasis(priorNotional, notional, baseNotional);
|
||||
var basis = priorNotional;
|
||||
var displayBasis = basis * unwindFraction;
|
||||
|
||||
var allInRate = rate.AllInRate;
|
||||
@@ -80,14 +64,13 @@ public static class FundingLegAccrual
|
||||
/// <summary>
|
||||
/// 复利日终计息(替换 CalcDailyCompoundInterestByEod 的纯数学部分)。
|
||||
/// 重置日:basis = notional + priorAccrued × remainingFraction(利息并入本金)。
|
||||
/// 非重置日:basis = 差分公式(priorNotional + notional − baseNotional)。
|
||||
/// 非重置日:basis = priorNotional(昨日终滚动计息基数)。
|
||||
/// </summary>
|
||||
public static InterestResult AccrueCompoundEod(
|
||||
decimal priorAccrued,
|
||||
decimal priorNotional,
|
||||
decimal notional,
|
||||
decimal unwindFraction,
|
||||
decimal baseNotional,
|
||||
FundingLegRate rate,
|
||||
AccrualPolicy policy,
|
||||
bool isResetDay,
|
||||
@@ -95,11 +78,11 @@ public static class FundingLegAccrual
|
||||
DateTime eodDate,
|
||||
AccrualTrace? trace = null)
|
||||
{
|
||||
var basis = CompoundEodBasis(isResetDay, notional, priorAccrued, remainingFraction, priorNotional, baseNotional);
|
||||
var basis = CompoundEodBasis(isResetDay, notional, priorAccrued, remainingFraction, priorNotional);
|
||||
var displayBasis = basis * unwindFraction;
|
||||
|
||||
var allInRate = rate.AllInRate;
|
||||
trace?.EodContext(eodDate, isResetDay, unwindFraction, priorAccrued, priorNotional, notional, baseNotional, remainingFraction);
|
||||
trace?.EodContext(eodDate, isResetDay, unwindFraction, priorAccrued, priorNotional, notional, remainingFraction);
|
||||
var dayInterest = displayBasis * allInRate;
|
||||
var tdInterest = basis * allInRate;
|
||||
if (policy.IsAnnualized)
|
||||
|
||||
@@ -1541,7 +1541,6 @@ namespace YLErp.Modules.SwapModule
|
||||
priorNotional: preEodPosition.TdInterestPrincipal,
|
||||
notional: posiPrincipal,
|
||||
unwindFraction: closePercent,
|
||||
baseNotional: orginPv,
|
||||
rate: legRate,
|
||||
policy: accrualPolicy,
|
||||
isResetDay: isResetDay,
|
||||
@@ -1551,10 +1550,10 @@ namespace YLErp.Modules.SwapModule
|
||||
SwapCalcTrace.Write(interestTrace);
|
||||
|
||||
// flowEvent.InterestPrincipal:当日计息基数(已按平仓比例缩放)——下游 EOD 用它播种次日 TdInterestPrincipal。
|
||||
// 复用 CompoundEodBasis 单一真相源(与 AccrueCompoundEod 内部同一公式),不再重复三元表达式。
|
||||
// 复用 CompoundEodBasis 单一真相源(与 AccrueCompoundEod 内部同一公式)。
|
||||
flowEvent.InterestPrincipal = FundingLegAccrual.CompoundEodBasis(
|
||||
isResetDay, posiPrincipal, preEodPosition.InterestProfitSum, remainingFraction,
|
||||
preEodPosition.TdInterestPrincipal, orginPv) * closePercent;
|
||||
preEodPosition.TdInterestPrincipal) * closePercent;
|
||||
|
||||
InterestAmount = result.Accrued;
|
||||
TdInterestAmount = result.AccruedToday;
|
||||
@@ -1608,9 +1607,7 @@ namespace YLErp.Modules.SwapModule
|
||||
var result = FundingLegAccrual.AccrueSimpleEod(
|
||||
priorAccrued: preEodPosition.InterestProfitSum,
|
||||
priorNotional: preEodPosition.TdInterestPrincipal,
|
||||
notional: posiPrincipal,
|
||||
unwindFraction: closePercent,
|
||||
baseNotional: orginPv,
|
||||
rate: legRate,
|
||||
policy: accrualPolicy,
|
||||
eodDate: endDate,
|
||||
|
||||
Reference in New Issue
Block a user