Files
zszq-trs/YLErpDAL/Modules/SwapModule/Accrual/InterestMath.cs
T
hjhan e2431e9d44 refactor(accrual): 计息类型整体迁入 DAL——新增 Accrual/InterestMath,删 Core 未接线孤儿
搬迁(算法体逐字未动,仅换命名空间与归属):
- SwapInterest.Round/AccrualDays/FundingLegPrecision + AccrualBoundary/InterestResult
  → YLErpDAL/Modules/SwapModule/Accrual/InterestMath.cs
- AccrualTrace → Accrual/AccrualTrace.cs(被迫同迁:其 MarkStart 引用 AccrualBoundary,
  Core 不能反向依赖 DAL)
- 引用切换:Simple/CompoundInterestAccrual、AccrualPolicy、SwapCalcTrace、SwapDealService
  (保留 using YLErp.Derivatives.Interest——IIndexFixer/IndexFixerBase 留 Core)

删除(零生产引用,孤儿清零):
- Core:SwapInterest.cs 算法方法(AccrueSimple/AccrueCompoundInArrears/ApplyUnwind/
  AccrueUnrealized/ToInterestRate,未接线且与 DAL 生产实现舍入/rollover 口径已分叉)、
  AccrualContext.cs、InterestRate.cs
- DAL:AccrualState.cs(零引用死类)
- 测试:SwapInterest_CompoundInArrears_RolloverTimingTests.cs(仅测已删原语)

验证:两解决方案 Rebuild 0 错误;磁盘 SwapInterest. 残留 0;影子/分红/场景 86/86 通过
(含 Accrual 3 影子对账、Margin 影子、divPower 新增 AutoUnwindMultiPartial)。
注:AccrualContext 默认精度 11 与生产 12 的分叉隐患随删除一并消除;
已删原语若将来重建须先补对账测试,勿凭记忆复原(ARCHITECTURE.md 已留警告)。
2026-08-14 15:31:04 +08:00

105 lines
5.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
namespace YLErp.Modules.SwapModule.Accrual;
// ─────────────────────────────────────────────────────────────────────────────
// 词汇表(本文件只允许出现下列用词,同一概念不得出现第二种叫法)
//
// 概念 唯一用词 与既有代码的对应
// ───────────────────────────────────────────────────────────────────
// 区间起点/终点 Start / End startDate / endDate
// 计息 Accrue CalcDailySimpleInterest / CalcDailyCompoundInterest
// 平仓 Unwind unwindPercent(既有字段 closePercent
// 已实现利息 Realized realizedInterestlegacy 字段 consumedInterest
// 待实现收益 Unrealized 预付金模式下的待实现收益余额
// 计息基数 principal principal / dynomicPrincipal
// 年化天数 annualDays tradeExtend.ExtendObj.AnnualDays
//
// 入参一律沿用既有代码的字段名,调用点两边读起来同名,不产生心智翻译成本。
// 出参改用自描述名(Accrued / AccruedToday),因为 "Td" 对新读者是黑话。
// ─────────────────────────────────────────────────────────────────────────────
/// <summary>
/// 计息区间边界(算头 / 算尾)。
/// 用具名值取代两个相邻 bool,物理上杜绝 calcFirst / calcLast 传反这一类历史缺陷。
/// </summary>
public readonly struct AccrualBoundary
{
/// <summary>算头:含 startDate。</summary>
public bool IncludeStart { get; }
/// <summary>算尾:含 endDate。</summary>
public bool IncludeEnd { get; }
private AccrualBoundary(bool includeStart, bool includeEnd)
=> (IncludeStart, IncludeEnd) = (includeStart, includeEnd);
/// <summary>算头算尾 [start, end]。</summary>
public static readonly AccrualBoundary Both = new(true, true);
/// <summary>算头不算尾 [start, end)。</summary>
public static readonly AccrualBoundary StartOnly = new(true, false);
/// <summary>不算头算尾 (start, end]。</summary>
public static readonly AccrualBoundary EndOnly = new(false, true);
/// <summary>不算头不算尾 (start, end)。</summary>
public static readonly AccrualBoundary None = new(false, false);
/// <summary>由既有 calcFirst / calcLast 布尔对构造,供旧调用方渐进迁移。</summary>
public static AccrualBoundary Of(bool includeStart, bool includeEnd) => new(includeStart, includeEnd);
public override string ToString()
=> $"{(IncludeStart ? "算头" : "不算头")}{(IncludeEnd ? "算尾" : "不算尾")}";
}
/// <summary>
/// 计息结果。Accrued → 记账字段 InterestAmount / InterestProfitSumAccruedToday → TdInterestAmount。
/// </summary>
public readonly struct InterestResult
{
/// <summary>区间累计应计利息。</summary>
public decimal Accrued { get; }
/// <summary>末日(当日)应计利息。</summary>
public decimal AccruedToday { get; }
public InterestResult(decimal accrued, decimal accruedToday)
=> (Accrued, AccruedToday) = (accrued, accruedToday);
public static readonly InterestResult Zero = new(0m, 0m);
public override string ToString() => $"Accrued={Accrued}, AccruedToday={AccruedToday}";
}
/// <summary>
/// 利息腿共用数学工具:舍入、应计天数、精度常量。
///
/// <para><b>沿革</b>2026-08 自 Core 层 SwapInterest 迁入 DAL(生产消费面整体搬家)。
/// 原 SwapInterest 的算法方法(AccrueSimple/AccrueCompoundInArrears/ApplyUnwind/AccrueUnrealized
/// 与 AccrualContext/InterestRate 始终未接线(生产计息走本目录 Simple/CompoundInterestAccrual
/// 两者舍入与 rollover 口径已分叉),作为孤儿死代码删除——接线前须先补对账,勿凭记忆重建。</para>
///
/// <para>为何不复用 Qdp 的 IDayCount
/// a. 语义——Qdp 的 DaysInPeriod = end start 是写死的半开区间,只能表达四种算头算尾中的一种;
/// b. 精度——Qdp 返回 double 年化系数,本系统 decimal 对账;
/// c. 依赖方向——Qdp 用自有 Date 类型,引入会让本模块反向依赖定价库。</para>
/// </summary>
public static class InterestMath
{
/// <summary>资金腿与保证金腿的生产计息精度(落库/对账均以 12 位为准)。
/// 提升至公共常量,消除 SwapDealService 与 SimpleInterestAccrual 的重复定义。</summary>
public const int FundingLegPrecision = 12;
/// <summary>应计天数。边界规则由日期区间表达,计息函数内不再出现 flag 分支。</summary>
public static int AccrualDays(DateTime startDate, DateTime endDate, AccrualBoundary boundary)
{
var s = boundary.IncludeStart ? startDate : startDate.AddDays(1);
var e = boundary.IncludeEnd ? endDate : endDate.AddDays(-1);
var days = (int)(e - s).TotalDays + 1; // 含两端
return days < 0 ? 0 : days;
}
/// <summary>统一舍入:MidpointRounding.AwayFromZero。所有计息路径收口到此处,避免散落的 Math.Round 不一致。</summary>
public static decimal Round(decimal value, int precision)
=> Math.Round(value, precision, MidpointRounding.AwayFromZero);
}