diff --git a/Framework/YLErp.Core/Interest/SwapInterest.cs b/Framework/YLErp.Core/Interest/SwapInterest.cs
new file mode 100644
index 00000000..f9ca0ad3
--- /dev/null
+++ b/Framework/YLErp.Core/Interest/SwapInterest.cs
@@ -0,0 +1,213 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace YLErp.Derivatives.Interest;
+
+// ─────────────────────────────────────────────────────────────────────────────
+// 词汇表(本文件只允许出现下列用词,同一概念不得出现第二种叫法)
+//
+// 概念 唯一用词 与既有代码的对应
+// ───────────────────────────────────────────────────────────────────
+// 区间起点/终点 Start / End startDate / endDate
+// 计息 Accrue CalcDailySimpleInterest / CalcDailyCompoundInterest
+// 平仓 Unwind unwindPercent(既有字段 closePercent)
+// 已实现利息 Realized realizedInterest(legacy 字段 consumedInterest)
+// 待实现收益 Unrealized 预付金模式下的待实现收益余额
+// 计息基数 principal principal / dynomicPrincipal
+// 年化天数 annualDays tradeExtend.ExtendObj.AnnualDays
+//
+// 入参一律沿用既有代码的字段名,调用点两边读起来同名,不产生心智翻译成本。
+// 出参改用自描述名(Accrued / AccruedToday),因为 "Td" 对新读者是黑话。
+// ─────────────────────────────────────────────────────────────────────────────
+
+///
+/// 计息区间边界(算头 / 算尾)。
+/// 用具名值取代两个相邻 bool,物理上杜绝 calcFirst / calcLast 传反这一类历史缺陷。
+///
+public readonly struct AccrualBoundary
+{
+ /// 算头:含 startDate。
+ public bool IncludeStart { get; }
+
+ /// 算尾:含 endDate。
+ public bool IncludeEnd { get; }
+
+ private AccrualBoundary(bool includeStart, bool includeEnd)
+ => (IncludeStart, IncludeEnd) = (includeStart, includeEnd);
+
+ /// 算头算尾 [start, end]。
+ public static readonly AccrualBoundary Both = new(true, true);
+
+ /// 算头不算尾 [start, end)。
+ public static readonly AccrualBoundary StartOnly = new(true, false);
+
+ /// 不算头算尾 (start, end]。
+ public static readonly AccrualBoundary EndOnly = new(false, true);
+
+ /// 不算头不算尾 (start, end)。
+ public static readonly AccrualBoundary None = new(false, false);
+
+ /// 由既有 calcFirst / calcLast 布尔对构造,供旧调用方渐进迁移。
+ public static AccrualBoundary Of(bool includeStart, bool includeEnd) => new(includeStart, includeEnd);
+
+ public override string ToString()
+ => $"{(IncludeStart ? "算头" : "不算头")}{(IncludeEnd ? "算尾" : "不算尾")}";
+}
+
+///
+/// 计息结果。Accrued → 记账字段 InterestAmount / InterestProfitSum;AccruedToday → TdInterestAmount。
+///
+public readonly struct InterestResult
+{
+ /// 区间累计应计利息。
+ public decimal Accrued { get; }
+
+ /// 末日(当日)应计利息。
+ 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}";
+}
+
+///
+/// 收益互换(TRS)利息腿计算——纯函数。
+///
+/// 设计约束:
+/// 1. 无副作用——不读写 flowEvent、不取利率、不连库、不碰任何共享可变状态;
+/// 2. 同 input → 同 output,结果仅通过返回值流出;
+/// 3. 正交轴(算头算尾 / 单利复利 / 平仓 / 待实现收益)各自独立,互不耦合;
+/// 4. 调用方负责「取利率 + 构造日期区间 + 落库」,本类只算账。
+/// 由此,corp action 调整价格 / 数量时只需把新的 principal 与 rate 喂入,计息逻辑一行不动。
+///
+/// 领域口径:本系统利息腿是单边融资腿,任一时点只有一个生效利率(见 SwapDealService 的
+/// floateRate 单一入参),不存在 IRS 那种 fixedRate − floatingRate 轧差;
+/// 权益腿盈亏与平仓费用属三腿汇总层,不在本类职责内。
+///
+/// 为何不复用 Qdp 的 IDayCount:
+/// a. 语义——Qdp 的 DaysInPeriod = end − start 是写死的半开区间,只能表达四种算头算尾中的一种;
+/// b. 精度——Qdp 返回 double 年化系数,本系统 decimal 且日息先 Round 再乘天数,
+/// Round(P*r/365, 11) * n ≠ P*r*(n/365),与 Excel 对账口径不同;
+/// c. 依赖方向——Qdp 用自有 Date 类型,引入会让 YLErp.Core 反向依赖定价库。
+///
+public static class SwapInterest
+{
+ /// 系统统一价格精度位数。
+ public const int Precision = 11;
+
+ /// 年化天数常量(合约字段存的是 int,故不用 enum)。
+ public const int Act365 = 365;
+
+ public const int Act360 = 360;
+
+ /// 应计天数。边界规则由日期区间表达,计息函数内不再出现 flag 分支。
+ 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;
+ }
+
+ /// 单利:计息基数固定,每日利息相同,无逐日循环。
+ public static InterestResult AccrueSimple(
+ decimal principal,
+ decimal rate,
+ DateTime startDate,
+ DateTime endDate,
+ AccrualBoundary boundary,
+ int annualDays,
+ int precision = Precision)
+ {
+ var days = AccrualDays(startDate, endDate, boundary);
+ var daily = Round(principal * rate / annualDays, precision);
+ return new InterestResult(Round(daily * days, precision), daily);
+ }
+
+ ///
+ /// 复利:按重置日切段,段间把累计利息并入计息基数。
+ /// 段内复用 AccrueSimple(仍无逐日循环);重置日是唯一并本金的地方。
+ ///
+ public static InterestResult AccrueCompound(
+ decimal principal,
+ decimal rate,
+ DateTime startDate,
+ DateTime endDate,
+ AccrualBoundary boundary,
+ IReadOnlyList resetDates,
+ int annualDays,
+ int precision = Precision)
+ {
+ var basis = principal;
+ decimal accrued = 0m, accruedToday = 0m;
+
+ var segEnds = (resetDates ?? Array.Empty())
+ .Where(d => d > startDate && d < endDate)
+ .OrderBy(d => d)
+ .Append(endDate)
+ .ToArray();
+
+ var segStart = startDate;
+ var segIncludeStart = boundary.IncludeStart;
+
+ foreach (var segEnd in segEnds)
+ {
+ var segBoundary = AccrualBoundary.Of(segIncludeStart, segEnd == endDate && boundary.IncludeEnd);
+ var seg = AccrueSimple(basis, rate, segStart, segEnd, segBoundary, annualDays, precision);
+
+ accrued += seg.Accrued;
+ accruedToday = seg.AccruedToday;
+ basis += seg.Accrued; // 仅在重置日并本金
+ segStart = segEnd;
+ segIncludeStart = false; // 后续段不算头
+ }
+
+ return new InterestResult(accrued, accruedToday);
+ }
+
+ ///
+ /// 平仓(Unwind)缩放——全仓唯一缩放点,物理上杜绝 unwindPercent 被重复相乘。
+ /// 全平即 unwindPercent = 1,不另设方法。
+ ///
+ /// 已实现 / 未实现边界:传入的 是平仓前仍「未实现(unrealized)」的
+ /// 累计应计利息;本方法按比例缩放后返回「平仓后剩余未实现」部分,并扣除历史累计「已实现(realized)」
+ /// 的 。被平仓比例 unwindPercent 对应的那一份 accrued,
+ /// 即在此刻「实现(realized)」,由调用方记入 realizedInterest。
+ ///
+ /// 平仓前累计应计利息(未实现)。
+ ///
+ /// 平仓比例(0~1,实为 ratio 非百分数)。
+ /// 对应既有字段 closePercent;分母口径必须与传入 所依据的持仓数量一致——
+ /// 是「本次计算依据的持仓」而非「初始建仓」,历史缺陷正来自这个歧义。
+ ///
+ /// 已实现利息累计(legacy 字段 consumedInterest):历史各次 unwind 已确认、应从剩余未实现中扣除的部分。
+ public static InterestResult ApplyUnwind(
+ InterestResult accrued,
+ decimal unwindPercent,
+ decimal realizedInterest = 0m,
+ int precision = Precision)
+ {
+ var remaining = 1m - unwindPercent;
+ return new InterestResult(
+ Round(accrued.Accrued * remaining - realizedInterest, precision),
+ Round(accrued.AccruedToday * remaining, precision));
+ }
+
+ /// 待实现收益余额滚动(预付金 / 授信模式)。
+ /// 上期待实现收益余额。
+ /// 本期新增。
+ /// 本期 unwind 应扣减(即本期实现的份额)。
+ public static decimal AccrueUnrealized(
+ decimal openingUnrealized,
+ decimal todayIncome,
+ decimal unwindDeduction,
+ int precision = Precision)
+ => Round(openingUnrealized + todayIncome - unwindDeduction, precision);
+
+ private static decimal Round(decimal value, int precision)
+ => Math.Round(value, precision, MidpointRounding.AwayFromZero);
+}