按最新代码(CalcMarginInterest 已接生产、orginPv hack 已删、MarginAccount 未接线、保证金精度=12)修正: - 删除 orginPv 维度重映射的孤儿 summary(方法已删) - SwapDealService 注释:去掉 orginPv(InitSwapDealInterest)/保证金腿 引用 - SwapInterest/AccrualContext/InterestRate:去掉 Precision=11 是"保证金腿"、"保证金场景"等错误归因(保证金实际跑精度12) - MarginAccount/MarginBalance:标注"尚未接线",生产入口指向 CalcMarginInterest,去掉"余额×利率×天数"过度简化 - 测试注释:去掉"无 orginPv/差分"(盘中保留差分)、"提交2 待切换"(已完成) 仅文档/注释,零代码行为变化。
203 lines
10 KiB
C#
203 lines
10 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||
using Newtonsoft.Json;
|
||
using YLErp;
|
||
using YLErp.DBModels;
|
||
using YLErp.DBModels.Enums;
|
||
using YLErp.Modules.SwapModule;
|
||
using YLErp.Modules.SwapModule.Accrual;
|
||
using YLErp.Modules.SwapModule.Margin;
|
||
using YLErp.Derivatives.Interest;
|
||
|
||
namespace UnitTestProject.Modules.SwapModule.Margin
|
||
{
|
||
/// <summary>
|
||
/// 影子对账:保证金腿方法 CalcMarginInterest(EOD 用昨日终本金、盘中用 accrualBasis 差分)vs
|
||
/// 旧通用管线 CalcDailySimpleInterestByEod/CalcDailySimpleInterest。
|
||
///
|
||
/// 保证金是纯固定利率单利(FloatRateUnderlyingCode 恒空、InterestType 恒单利、SwapIntervalList 单段)。
|
||
/// 本测试在生产切到 CalcMarginInterest 后作为回归守护,确认其 InterestAmount/TdInterestAmount
|
||
/// 与旧纯函数(SimpleInterestAccrual)数值一致。覆盖 EOD 续接/首日、盘中全平/部分平仓/互换。
|
||
/// </summary>
|
||
[TestClass]
|
||
public class MarginInterestShadowTest
|
||
{
|
||
private const decimal Principal = 2_000_000m; // 保证金本金(InterestPrincipalFix)
|
||
private const decimal Rate = 0.03m; // 3% 年化固定利率
|
||
private const int AnnualDays = 365;
|
||
private static readonly DateTime StartDate = new(2026, 7, 1);
|
||
private static readonly DateTime ExerciseDate = new(2027, 6, 30);
|
||
|
||
private sealed class StubSvc : SwapDealService
|
||
{
|
||
public StubSvc() : base(new OptUserInfo(0, nameof(MarginInterestShadowTest), OptUserFrom.UnitTest)) { }
|
||
}
|
||
|
||
private static trade CreateTrade() => new trade
|
||
{
|
||
id = 1, TradeNumber = "UT-MARGIN-SHADOW", ClientId = 999998,
|
||
TradeType = "收益互换", TradeDate = StartDate, StartDate = StartDate,
|
||
ExerciseDate = ExerciseDate, TradeStatus = "确认成交", ValidState = "Valid",
|
||
trade_extend = new trade_extend
|
||
{
|
||
TradeId = 1,
|
||
ExtendJson = JsonConvert.SerializeObject(new TradeExtendJson
|
||
{ AnnualDays = AnnualDays, InterestCalcMode = "10", SettlementRules = 0 })
|
||
}
|
||
};
|
||
|
||
/// <summary>保证金腿(初始预付金 mode 5):固定利率、单利、年化、无浮动标的。</summary>
|
||
private static swap_position CreateMarginPosition() => new swap_position
|
||
{
|
||
id = 2001, SwapTradeId = 1, PosiDirection = 0,
|
||
InterestDirection = (int)SwapDirectionEnum.收取,
|
||
InterestMode = (int)InterestModeEnum.初始预付金,
|
||
InterestRateDefault = Rate, InterestPrincipalFix = Principal,
|
||
PosiStartDate = StartDate, PosiMatuirityDate = ExerciseDate,
|
||
IsInitial = true, Invalid = false,
|
||
InterestType = (int)InterestTypeEnum.单利,
|
||
IsAnnualized = true, interest_rest_days = 1, interest_rule = 0,
|
||
FloatRateUnderlyingCode = null, InterestSwapInterval = "[]"
|
||
};
|
||
|
||
/// <summary>构造昨日终 eod_swap_position(已含累计利息 InterestProfitSum 与昨日终本金)。</summary>
|
||
private static eod_swap_position CreatePreEod(DateTime valueDate, decimal profitSum) => new eod_swap_position
|
||
{
|
||
id = 1, SwapTradeId = 1, PositionId = 2001,
|
||
ValueDate = valueDate,
|
||
TdInterestPrincipal = Principal, InterestPrincipalFix = Principal,
|
||
InterestProfitSum = profitSum, PosiNotionalValue = Principal, FloatRate = 0m
|
||
};
|
||
|
||
// ──────────────────────────── EOD 路径 ────────────────────────────
|
||
|
||
/// <summary>EOD 续接单日:有历史归档,notional=昨日终本金。</summary>
|
||
[TestMethod]
|
||
public void 影子_EOD续接单日_新旧一致()
|
||
{
|
||
var td = CreateTrade();
|
||
var position = CreateMarginPosition();
|
||
var valueDate = StartDate.AddDays(5);
|
||
const decimal profitSum = 820m;
|
||
|
||
// 旧方法
|
||
decimal oldI = 0, oldTd = 0;
|
||
var svc = new StubSvc();
|
||
svc.CalcDailySimpleInterestByEod(CreatePreEod(StartDate.AddDays(4), profitSum),
|
||
valueDate, td.StartDate.Value, position, Principal, Principal,
|
||
new swap_flow_event { InterestRate = Rate }, AnnualDays, 0m, 1m, ref oldI, ref oldTd);
|
||
|
||
// 新方法(独立 preEod,相同初始值)
|
||
var newEvt = svc.CalcMarginInterest(td, valueDate, valueDate, position, Rate, Principal, Principal, 1m,
|
||
AnnualDays, calcFirst: true, calcLast: true,
|
||
CreatePreEod(StartDate.AddDays(4), profitSum), 0, add: false, settment: true, swap: false);
|
||
|
||
Console.WriteLine($"旧: I={oldI} Td={oldTd}");
|
||
Console.WriteLine($"新: I={newEvt.InterestAmount} Td={newEvt.TdInterestAmount} ClosePnL={newEvt.InterestClosePnL}");
|
||
Assert.AreEqual(oldI, newEvt.InterestAmount, "InterestAmount 一致");
|
||
Assert.AreEqual(oldTd, newEvt.TdInterestAmount, "TdInterestAmount 一致");
|
||
}
|
||
|
||
/// <summary>EOD 首日(preEod.id==0):首日初始化 notional=posiPrincipal。</summary>
|
||
[TestMethod]
|
||
public void 影子_EOD首日_新旧一致()
|
||
{
|
||
var td = CreateTrade();
|
||
var position = CreateMarginPosition();
|
||
var valueDate = StartDate;
|
||
|
||
decimal oldI = 0, oldTd = 0;
|
||
var svc = new StubSvc();
|
||
svc.CalcDailySimpleInterestByEod(new eod_swap_position { id = 0 },
|
||
valueDate, td.StartDate.Value, position, Principal, Principal,
|
||
new swap_flow_event { InterestRate = Rate }, AnnualDays, 0m, 1m, ref oldI, ref oldTd);
|
||
|
||
var newEvt = svc.CalcMarginInterest(td, valueDate, valueDate, position, Rate, Principal, Principal, 1m,
|
||
AnnualDays, calcFirst: true, calcLast: true,
|
||
new eod_swap_position { id = 0 }, 0, add: false, settment: true, swap: false);
|
||
|
||
Assert.AreEqual(oldI, newEvt.InterestAmount, "InterestAmount 一致");
|
||
Assert.AreEqual(oldTd, newEvt.TdInterestAmount, "TdInterestAmount 一致");
|
||
}
|
||
|
||
// ──────────────────────────── 盘中路径 ────────────────────────────
|
||
|
||
/// <summary>盘中全平(closePercent=1):新方法 notional=posiPrincipal,旧方法差分 accrualBasis 恒=posiPrincipal。</summary>
|
||
[TestMethod]
|
||
public void 影子_盘中全平_新旧一致()
|
||
{
|
||
var td = CreateTrade();
|
||
var position = CreateMarginPosition();
|
||
var valueDate = StartDate.AddDays(5);
|
||
const decimal profitSum = 820m;
|
||
|
||
// 旧方法:orginPv 经 PreviousBalance 对齐到昨日终保证金余额 → accrualBasis 恒= Principal
|
||
decimal oldI = 0, oldTd = 0;
|
||
var svc = new StubSvc();
|
||
var preEodOld = CreatePreEod(StartDate.AddDays(4), profitSum);
|
||
decimal orginPv = MarginCalc.PreviousBalance(preEodOld, Principal);
|
||
svc.CalcDailySimpleInterest(preEodOld, valueDate, position, Principal,
|
||
new swap_flow_event { InterestRate = Rate }, AnnualDays, 0m, 1m, orginPv,
|
||
calcFirst: true, calcLast: false, ref oldI, ref oldTd);
|
||
|
||
// 新方法:notional = posiPrincipal(无差分、无 orginPv)
|
||
var newEvt = svc.CalcMarginInterest(td, valueDate, valueDate, position, Rate, Principal, Principal, 1m,
|
||
AnnualDays, calcFirst: true, calcLast: false,
|
||
CreatePreEod(StartDate.AddDays(4), profitSum), 0, add: false, settment: false, swap: false);
|
||
|
||
Console.WriteLine($"旧: I={oldI} Td={oldTd}");
|
||
Console.WriteLine($"新: I={newEvt.InterestAmount} Td={newEvt.TdInterestAmount}");
|
||
Assert.AreEqual(oldI, newEvt.InterestAmount, "InterestAmount 一致");
|
||
Assert.AreEqual(oldTd, newEvt.TdInterestAmount, "TdInterestAmount 一致");
|
||
}
|
||
|
||
/// <summary>盘中部分平仓(closePercent=0.5):缩放累计,新旧线性等价。</summary>
|
||
[TestMethod]
|
||
public void 影子_盘中部分平仓_新旧一致()
|
||
{
|
||
var td = CreateTrade();
|
||
var position = CreateMarginPosition();
|
||
var valueDate = StartDate.AddDays(5);
|
||
const decimal profitSum = 820m;
|
||
const decimal closePct = 0.5m;
|
||
|
||
decimal oldI = 0, oldTd = 0;
|
||
var svc = new StubSvc();
|
||
var preEodOld = CreatePreEod(StartDate.AddDays(4), profitSum);
|
||
decimal orginPv = MarginCalc.PreviousBalance(preEodOld, Principal);
|
||
svc.CalcDailySimpleInterest(preEodOld, valueDate, position, Principal,
|
||
new swap_flow_event { InterestRate = Rate }, AnnualDays, 0m, closePct, orginPv,
|
||
calcFirst: true, calcLast: false, ref oldI, ref oldTd);
|
||
|
||
var newEvt = svc.CalcMarginInterest(td, valueDate, valueDate, position, Rate,
|
||
Principal * closePct, Principal, closePct,
|
||
AnnualDays, calcFirst: true, calcLast: false,
|
||
CreatePreEod(StartDate.AddDays(4), profitSum), 0, add: false, settment: false, swap: false);
|
||
|
||
Console.WriteLine($"旧: I={oldI} Td={oldTd}");
|
||
Console.WriteLine($"新: I={newEvt.InterestAmount} Td={newEvt.TdInterestAmount}");
|
||
Assert.AreEqual(oldI, newEvt.InterestAmount, "InterestAmount 一致");
|
||
Assert.AreEqual(oldTd, newEvt.TdInterestAmount, "TdInterestAmount 一致");
|
||
}
|
||
|
||
/// <summary>互换事件(swap=true,盘中):利息应归零。</summary>
|
||
[TestMethod]
|
||
public void 影子_盘中互换_利息归零()
|
||
{
|
||
var td = CreateTrade();
|
||
var position = CreateMarginPosition();
|
||
var valueDate = StartDate.AddDays(5);
|
||
|
||
var svc = new StubSvc();
|
||
var newEvt = svc.CalcMarginInterest(td, valueDate, valueDate, position, Rate, Principal, Principal, 1m,
|
||
AnnualDays, calcFirst: true, calcLast: false,
|
||
CreatePreEod(StartDate.AddDays(4), 820m), 0, add: false, settment: false, swap: true);
|
||
|
||
Assert.AreEqual(0m, newEvt.InterestAmount, "互换利息归零");
|
||
Assert.AreEqual(0m, newEvt.TdInterestAmount, "互换 TdInterestAmount 归零");
|
||
Assert.AreEqual(0m, newEvt.InterestClosePnL, "互换 InterestClosePnL 归零");
|
||
}
|
||
}
|
||
}
|