diff --git a/Framework/YLErp.Core/Interest/AccrualContext.cs b/Framework/YLErp.Core/Interest/AccrualContext.cs index 8e7dd77f..2a1f6273 100644 --- a/Framework/YLErp.Core/Interest/AccrualContext.cs +++ b/Framework/YLErp.Core/Interest/AccrualContext.cs @@ -18,7 +18,7 @@ public readonly struct AccrualContext /// 年化天数(365 / 360)。 public int AnnualDays { get; } - /// 舍入精度位数。默认 11(仅未接线的 MarginAccount.AccrueInterest 走此默认;生产融资腿/保证金腿均显式用 FundingLegPrecision=12)。 + /// 舍入精度位数。默认 11(生产融资腿/保证金腿均显式传入 FundingLegPrecision=12)。 public int Precision { get; } /// 可选 trace 收集器;为 null 时不记录(纯计算场景直接传 null,与开关无关)。 diff --git a/UnitTestProject/Modules/SwapModule/Margin/MarginLegTest.cs b/UnitTestProject/Modules/SwapModule/Margin/MarginLegTest.cs deleted file mode 100644 index 156eb932..00000000 --- a/UnitTestProject/Modules/SwapModule/Margin/MarginLegTest.cs +++ /dev/null @@ -1,121 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using YLErp.Derivatives.Interest; -using YLErp.Modules.SwapModule.Margin; - -namespace UnitTestProject.Modules.SwapModule.Margin -{ - /// - /// 保证金账户(MarginAccount)单测。验证余额变动(追加/释放/返还)。 - /// 保证金就是保证金——有余额、有利率、有利息,不存在"计息基数/Notional"概念。 - /// - [TestClass] - public class MarginLegTest - { - private const decimal Opening = 2_000_000m; - - #region MarginAccount 余额变动 - - [TestMethod] - public void 账户_初始余额等于期初保证金() - { - var account = new MarginAccount(new MarginBalance(Opening)); - Assert.AreEqual(Opening, account.Balance.Balance); - } - - [TestMethod] - public void 账户_追加保证金_余额增加() - { - var account = new MarginAccount(new MarginBalance(Opening)); - account.Deposit(500_000m); - Assert.AreEqual(2_500_000m, account.Balance.Balance); - } - - [TestMethod] - public void 账户_释放保证金_余额减少() - { - var account = new MarginAccount(new MarginBalance(Opening)); - account.Withdraw(800_000m); - Assert.AreEqual(1_200_000m, account.Balance.Balance); - } - - [TestMethod] - public void 账户_释放超过余额_不低于零() - { - var account = new MarginAccount(new MarginBalance(Opening)); - account.Withdraw(3_000_000m); - Assert.AreEqual(0m, account.Balance.Balance, "保证金余额不低于零"); - } - - #endregion - - #region 三种保证金形态解析器 - - [TestMethod] - public void 三种形态解析器_各自返回正确Form和余额() - { - IMarginResolver cash = new CashMargin(); - IMarginResolver credit = new CreditMargin(); - IMarginResolver guarantee = new GuaranteeMargin(); - - Assert.AreEqual(MarginForm.Cash, cash.Form); - Assert.AreEqual(MarginForm.Credit, credit.Form); - Assert.AreEqual(MarginForm.Guarantee, guarantee.Form); - - Assert.AreEqual(Opening, cash.Resolve(Opening).Balance); - Assert.AreEqual(Opening, credit.Resolve(Opening).Balance); - Assert.AreEqual(Opening, guarantee.Resolve(Opening).Balance); - } - - #endregion - - #region MarginAccount 计息 - - [TestMethod] - public void 计息_单利7天_余额200万年化3pct() - { - var account = new MarginAccount(new MarginBalance(2_000_000m)); - // 200万 × 3% / 365 × 7天 = 1150.68... - var r = account.AccrueInterest( - rate: 0.03m, - startDate: new System.DateTime(2026, 5, 4), - endDate: new System.DateTime(2026, 5, 11), - boundary: AccrualBoundary.StartOnly, - annualDays: 365); - - Assert.IsTrue(r.Accrued > 0, "7天利息应大于0"); - System.Console.WriteLine($"保证金7天利息={r.Accrued}"); - } - - [TestMethod] - public void 计息_零余额_利息为零() - { - var account = new MarginAccount(new MarginBalance(0m)); - var r = account.AccrueInterest(0.03m, - new System.DateTime(2026, 5, 4), new System.DateTime(2026, 5, 11), - AccrualBoundary.StartOnly, 365); - - Assert.AreEqual(0m, r.Accrued); - } - - [TestMethod] - public void 计息_释放后余额减少_利息相应减少() - { - var full = new MarginAccount(new MarginBalance(2_000_000m)); - var half = new MarginAccount(new MarginBalance(2_000_000m)); - half.Withdraw(1_000_000m); - - var rFull = full.AccrueInterest(0.03m, - new System.DateTime(2026, 5, 4), new System.DateTime(2026, 5, 11), - AccrualBoundary.StartOnly, 365); - var rHalf = half.AccrueInterest(0.03m, - new System.DateTime(2026, 5, 4), new System.DateTime(2026, 5, 11), - AccrualBoundary.StartOnly, 365); - - Assert.IsTrue(rHalf.Accrued < rFull.Accrued, "释放后利息应更少"); - Assert.IsTrue(System.Math.Abs(rFull.Accrued - rHalf.Accrued * 2m) < 0.01m, - "余额减半, 利息也应减半"); - } - - #endregion - } -} diff --git a/YLErpDAL/Modules/SwapModule/ARCHITECTURE.md b/YLErpDAL/Modules/SwapModule/ARCHITECTURE.md index 43f2b474..1ddf3913 100644 --- a/YLErpDAL/Modules/SwapModule/ARCHITECTURE.md +++ b/YLErpDAL/Modules/SwapModule/ARCHITECTURE.md @@ -52,11 +52,7 @@ SwapModule/ │ ├── Margin/ 保证金(mode 5/6) │ ├── MarginModes mode 判断(含 ForLinq for EF Core) -│ ├── MarginBalance 保证金余额(值对象) -│ ├── MarginAccount 余额管理 + AccrueInterest 计息入口 -│ ├── MarginCalc 纯函数(PreviousBalance/FlipDirection/AccumulateSettlement) -│ ├── IMarginResolver 保证金形态接口 -│ └── Cash/Credit/Guarantee 三种形态实现 +│ └── MarginCalc 纯函数(PreviousBalance/FlipDirection/AccumulateSettlement) │ ├── ReturnLegs/ 标的端 │ ├── ReturnLegSummary 标的端汇总值 @@ -132,7 +128,7 @@ Unknown = 0 |---|---|---| | 公司行为(送股/拆股) | QtyRollforward.corpActionDeltaQty | ✅ | | 公司行为(登记日快照) | DividendCalc + BondPayment | 见 corp-action-refactor-proposal.md | -| 保证金配置/规则/占用 | MarginAccount + MarginCalc | ✅ | +| 保证金配置/规则/占用 | MarginCalc | ✅ | | RecordMarginCashFlow 迁入 Margin | AddClientCash 加 virtual | 待做 | | EOD 编排拆分 | SwapPositionCompose | 待业务需求驱动 | ``` diff --git a/YLErpDAL/Modules/SwapModule/Margin/CashMargin.cs b/YLErpDAL/Modules/SwapModule/Margin/CashMargin.cs deleted file mode 100644 index 128f34ba..00000000 --- a/YLErpDAL/Modules/SwapModule/Margin/CashMargin.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace YLErp.Modules.SwapModule.Margin; - -/// 现金保证金:余额 = 现金余额。 -public sealed class CashMargin : IMarginResolver -{ - public MarginForm Form => MarginForm.Cash; - - public MarginBalance Resolve(decimal postedAmount) - => new(postedAmount); -} diff --git a/YLErpDAL/Modules/SwapModule/Margin/CreditMargin.cs b/YLErpDAL/Modules/SwapModule/Margin/CreditMargin.cs deleted file mode 100644 index dd3c0032..00000000 --- a/YLErpDAL/Modules/SwapModule/Margin/CreditMargin.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace YLErp.Modules.SwapModule.Margin; - -/// 授信保证:余额 = 已用授信额度。 -public sealed class CreditMargin : IMarginResolver -{ - public MarginForm Form => MarginForm.Credit; - - public MarginBalance Resolve(decimal postedAmount) - => new(postedAmount); -} diff --git a/YLErpDAL/Modules/SwapModule/Margin/GuaranteeMargin.cs b/YLErpDAL/Modules/SwapModule/Margin/GuaranteeMargin.cs deleted file mode 100644 index 8506a687..00000000 --- a/YLErpDAL/Modules/SwapModule/Margin/GuaranteeMargin.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace YLErp.Modules.SwapModule.Margin; - -/// 担保品:余额 = 担保品市值。 -public sealed class GuaranteeMargin : IMarginResolver -{ - public MarginForm Form => MarginForm.Guarantee; - - public MarginBalance Resolve(decimal postedAmount) - => new(postedAmount); -} diff --git a/YLErpDAL/Modules/SwapModule/Margin/IMarginResolver.cs b/YLErpDAL/Modules/SwapModule/Margin/IMarginResolver.cs deleted file mode 100644 index 1e24f876..00000000 --- a/YLErpDAL/Modules/SwapModule/Margin/IMarginResolver.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace YLErp.Modules.SwapModule.Margin; - -/// 保证金形态:现金 / 授信 / 担保。预留扩展。 -public enum MarginForm -{ - /// 现金保证金:余额 = 现金余额。 - Cash, - /// 授信保证:余额 = 已用授信额度。 - Credit, - /// 担保品:余额 = 担保品市值。 - Guarantee, -} - -/// -/// 按保证金形态解析余额。三种形态可互换地产出一个 MarginBalance(满足 LSP), -/// 这是保证金领域唯一合理的多态点(差异仅在"余额如何取得")。 -/// 具体余额来源(资金流水 / 授信占用 / 担保估值)后续按形态填充。 -/// -public interface IMarginResolver -{ - MarginForm Form { get; } - - MarginBalance Resolve(decimal postedAmount); -} diff --git a/YLErpDAL/Modules/SwapModule/Margin/MarginAccount.cs b/YLErpDAL/Modules/SwapModule/Margin/MarginAccount.cs deleted file mode 100644 index 33a5474f..00000000 --- a/YLErpDAL/Modules/SwapModule/Margin/MarginAccount.cs +++ /dev/null @@ -1,42 +0,0 @@ -using YLErp.Core.Interest; -using YLErp.Derivatives.Interest; - -namespace YLErp.Modules.SwapModule.Margin; - -/// -/// 保证金账户。管理保证金余额的变动(追加/释放/返还),并提供计息入口(预留抽象,尚未接线)。 -/// -/// 保证金是独立的资金管理概念(初始保证金/维持保证金/保证金余额/追保),与融资腿(funding leg)无关。 -/// 生产保证金计息入口为 SwapDealService.CalcMarginInterest(仍以 InterestMode 5/6 标识): -/// EOD 用昨日终本金 preEod.TdInterestPrincipal(无差分);盘中用 accrualBasis 差分(orginPv 经 PreviousBalance)。 -/// 本类尚未被生产代码实例化——其扁平"余额×利率×天数"模型无法表达盘中差分与多行分段,留作未来简化抽象。 -/// -public sealed class MarginAccount -{ - /// 当前保证金余额。 - public MarginBalance Balance { get; private set; } - - public MarginAccount(MarginBalance openingBalance) - => Balance = openingBalance; - - /// 追加保证金(余额增加)。 - public void Deposit(decimal amount) - => Balance = new MarginBalance(Balance.Balance + amount); - - /// 释放/返还保证金(余额减少,不低于 0)。 - public void Withdraw(decimal amount) - => Balance = new MarginBalance(Math.Max(0m, Balance.Balance - amount)); - - /// - /// 按当前余额计算保证金利息。委托 SwapInterest.AccrueSimple。 - /// 注意:当前未被生产代码调用——生产保证金计息入口为 SwapDealService.CalcMarginInterest - /// (处理 EOD 昨日终本金与盘中差分;本方法的扁平余额模型不覆盖盘中差分口径)。 - /// - /// 保证金利率(年化,如 0.03 = 3%)。 - /// 计息开始日。 - /// 计息结束日。 - /// 算头算尾规则。 - /// 年化天数(365 或 360)。 - public InterestResult AccrueInterest(decimal rate, System.DateTime startDate, System.DateTime endDate, AccrualBoundary boundary, int annualDays) - => SwapInterest.AccrueSimple(new AccrualContext(annualDays), Balance.Balance, rate, startDate, endDate, boundary); -} diff --git a/YLErpDAL/Modules/SwapModule/Margin/MarginBalance.cs b/YLErpDAL/Modules/SwapModule/Margin/MarginBalance.cs deleted file mode 100644 index abb50ece..00000000 --- a/YLErpDAL/Modules/SwapModule/Margin/MarginBalance.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace YLErp.Modules.SwapModule.Margin; - -/// -/// 保证金余额。现金、授信、担保等多种保证金形态的统一表达。 -/// -/// 保证金就是保证金——有余额、有利率、有利息,不存在"计息基数/Notional"概念。 -/// 余额随追加/释放/盈亏变动,利息由计息层(SwapDealService.CalcMarginInterest)按 EOD 昨日终本金 / 盘中差分口径计算。 -/// -public readonly struct MarginBalance -{ - /// 保证金余额:现金余额 / 授信占用 / 担保品市值。 - public decimal Balance { get; } - - public MarginBalance(decimal balance) - => Balance = balance; -}