1. 新增 AccrualContext 只读值对象,把 annualDays/precision/trace 三个"与金额利率无关" 的横向参数打包为上下文成员。计息纯函数只吃一个 ctx,签名去噪、可读性提升; ctx 为只读值对象(非类的实例/静态字段),不破坏纯函数(线程安全、可重入、可测)。 trace 因此成为"传入上下文的成员"而非散落参数,满足"默认成员而非参数"的诉求。 2. AccrueCompound → AccrueCompoundInArrears:精确表达 OIS/SOFR/FR007 的 compounded-in-arrears 语义(每子区间取定盘、段末滚入本金),与 InterestRate.Compounding.Compounded 闭式分支(TRS 下死路径)一刀两断、不再混淆。 纯增量、数值逻辑不变;YLErpDAL + UnitTestProject 编译 0 错 0 警;FR007 两段切换场景 实跑 trace 与旧版数值完全一致。
72 lines
3.3 KiB
C#
72 lines
3.3 KiB
C#
using System;
|
||
|
||
namespace YLErp.Core.Interest;
|
||
|
||
/// <summary>
|
||
/// 利率 + 计息方式(单利 / 复利 / 连续复利)。
|
||
///
|
||
/// <para><b>通用金融原语,与互换、衍生品、任何具体业务均无耦合</b>——谁需要算利息都能用。
|
||
/// 利息计算不是互换特有的,所以它不住在 SwapModule,也不带任何 swap 词汇。</para>
|
||
///
|
||
/// <para>用法(年化时间 t,如 30天/365):</para>
|
||
/// <list type="bullet">
|
||
/// <item><description>计息因子 = <see cref="CompoundFactor(decimal)"/>;含息额 = 本金 × 因子;</description></item>
|
||
/// <item><description>利息 = 本金 × (因子 − 1) = <see cref="Interest(decimal, decimal)"/>。</description></item>
|
||
/// </list>
|
||
///
|
||
/// <para>与 QuantLib 模型一致:单利 / 复利 / 连续复利只是 <see cref="Compounding"/> 的一个分支,
|
||
/// 不是三套独立方法。TRS 的"重置日并本金"属于离散复利,用 <see cref="Compounding.Simple"/>
|
||
/// 按段计息、段末把利息滚入本金即可(见 SwapInterest.AccrueCompoundInArrears),无需 Pow/Exp,decimal 精度无损。</para>
|
||
///
|
||
/// <para>互换特有的会计态(每日先舍入再乘天数、平仓缩放、跨日滚动本金)不属于本原语,
|
||
/// 请在各自的 accrual 层处理。</para>
|
||
/// </summary>
|
||
public enum Compounding
|
||
{
|
||
/// <summary>单利:因子 = 1 + r·t。</summary>
|
||
Simple,
|
||
/// <summary>复利(理想化闭式):因子 = (1 + r/f)^(f·t),f 为年复利频次。</summary>
|
||
Compounded,
|
||
/// <summary>连续复利:因子 = e^(r·t)。</summary>
|
||
Continuous
|
||
}
|
||
|
||
/// <summary>
|
||
/// 不可变利率值对象。构造即完整,无副作用。
|
||
/// </summary>
|
||
public readonly struct InterestRate
|
||
{
|
||
/// <summary>年化利率 r。</summary>
|
||
public decimal Rate { get; }
|
||
|
||
/// <summary>计息方式。</summary>
|
||
public Compounding Compounding { get; }
|
||
|
||
/// <summary>年复利频次(仅 <see cref="Compounding.Compounded"/> 使用,其余忽略,默认 1)。</summary>
|
||
public int Frequency { get; }
|
||
|
||
public InterestRate(decimal rate, Compounding compounding, int frequency = 1)
|
||
=> (Rate, Compounding, Frequency) = (rate, compounding, frequency);
|
||
|
||
/// <summary>
|
||
/// 计息因子(输入年化时间 t)。
|
||
/// <list type="bullet">
|
||
/// <item><description><see cref="Compounding.Simple"/>:decimal 精确运算。</description></item>
|
||
/// <item><description><see cref="Compounding.Compounded"/> / <see cref="Compounding.Continuous"/>:闭式(double 计算后回 decimal),
|
||
/// 满足通用定价 / 保证金场景;若要 decimal 精度的离散重置日复利,请用 Simple 按段计息并滚动本金。</description></item>
|
||
/// </list>
|
||
/// </summary>
|
||
public decimal CompoundFactor(decimal t)
|
||
=> Compounding switch
|
||
{
|
||
Compounding.Simple => 1m + Rate * t,
|
||
Compounding.Compounded => (decimal)Math.Pow((double)(1m + Rate / Frequency), (double)(Frequency * t)),
|
||
Compounding.Continuous => (decimal)Math.Exp((double)(Rate * t)),
|
||
_ => throw new ArgumentOutOfRangeException(nameof(Compounding))
|
||
};
|
||
|
||
/// <summary>利息 = 本金 × (因子 − 1)。</summary>
|
||
public decimal Interest(decimal principal, decimal t)
|
||
=> principal * (CompoundFactor(t) - 1m);
|
||
}
|