Files
zszq-trs/UnitTestProject/Modules/SwapModule/Accrual/CompoundCarryInTest.cs
T

108 lines
5.0 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.
using Newtonsoft.Json;
using YLErp;
using YLErp.Modules.SwapModule;
using YLErp.Modules.SwapModule.Accrual;
namespace UnitTestProject.Modules.SwapModule.Accrual
{
/// <summary>
/// EQD-6977 carryInInterest 契约测试:
/// 1) 默认 0 与旧逐日循环逐位一致(加参零行为变化的安全证明);
/// 2) carry-in 仅在【首个重置日】并入计息基数(非窗口首日起息)——
/// 与"持有至到期"全期轨迹对齐的数学不变量:增量 = carryIn × 后续段日利率 × 后续段天数。
/// </summary>
[TestClass]
public class CompoundCarryInTest
{
private const decimal Notional = 100_000_000m;
private const decimal Spread = 0.0025m;
private const int AnnualDays = 365;
private static readonly DateTime StartDate = new(2026, 4, 21);
private static readonly DateTime EndDate = new(2026, 5, 11); // 21天 = 3×7,末日是重置日
private static swap_position CreatePosition()
{
return new swap_position
{
id = 1001, SwapTradeId = 1, PosiDirection = 0,
InterestDirection = (int)SwapDirectionEnum.收取,
InterestMode = (int)InterestModeEnum.标的期初全价,
InterestRateDefault = Spread,
InterestPrincipalFix = Notional,
PosiStartDate = StartDate, PosiMatuirityDate = StartDate.AddYears(1),
IsInitial = true, Invalid = false,
InterestType = (int)InterestTypeEnum.复利,
IsAnnualized = true, interest_rest_days = 7, interest_rule = 0,
FloatRateUnderlyingCode = null,
InterestSwapInterval = "[]"
};
}
private static List<(DateTime, decimal)> Segments()
=> new()
{
(StartDate, Spread),
(StartDate.AddDays(7), Spread),
(StartDate.AddDays(14), Spread),
};
private sealed class StubSvc : SwapDealService
{
public StubSvc() : base(new OptUserInfo(0, nameof(CompoundCarryInTest), OptUserFrom.UnitTest)) { }
}
[TestMethod]
public void carryIn_默认省略_与旧逐日循环一致()
{
var position = CreatePosition();
var flowEvent = new swap_flow_event { InterestRate = Spread };
decimal oldI = 0, oldTd = 0;
new StubSvc().CalcDailyCompoundInterest(EndDate, position, Notional, flowEvent,
AnnualDays, 0m, 1m, true, false, ref oldI, ref oldTd);
// 省略 carryInInterest(默认 0
var r1 = CompoundInterestAccrual.AccruePeriod(
notional: Notional, segmentRates: Segments(),
startDate: StartDate, endDate: EndDate,
boundary: AccrualBoundary.StartOnly, annualDays: AnnualDays, isAnnualized: true,
resetCarryInterest: 0m, realizedInterest: 0m, unwindFraction: 1m,
finalBasis: out _);
// 显式传 0 与省略等价
var r2 = CompoundInterestAccrual.AccruePeriod(
notional: Notional, segmentRates: Segments(),
startDate: StartDate, endDate: EndDate,
boundary: AccrualBoundary.StartOnly, annualDays: AnnualDays, isAnnualized: true,
resetCarryInterest: 0m, realizedInterest: 0m, unwindFraction: 1m,
finalBasis: out _, trace: null, carryInInterest: 0m);
Assert.AreEqual((double)oldI, (double)r1.Accrued, 0.0000001, "省略 carryIn 与旧实现一致");
Assert.AreEqual((double)r1.Accrued, (double)r2.Accrued, 0.0000001, "省略与显式0一致");
}
[TestMethod]
public void carryIn_仅在首重置日起息_增量等于后续两段复利()
{
const decimal carryIn = 1_000_000m;
decimal Accrued(decimal c)
=> CompoundInterestAccrual.AccruePeriod(
notional: Notional, segmentRates: Segments(),
startDate: StartDate, endDate: EndDate,
boundary: AccrualBoundary.Both, annualDays: AnnualDays, isAnnualized: true,
resetCarryInterest: 0m, realizedInterest: 0m, unwindFraction: 1m,
finalBasis: out _, trace: null, carryInInterest: c).Accrued;
var delta = Accrued(carryIn) - Accrued(0m);
// Both 边界下三段各 7 天。carryIn 于 4/28(首个重置日)并入基数:
// 首段 [4/21,4/28] 不含 carryIn;其后两段 carryIn 自身起息且其首段利息再复利。
// 精确增量 = c×d + (c + c×d)×d = c×(2d + d²) = c×((1+d)² 1)d = 7天利率因子。
var d = Spread * 7m / AnnualDays;
var expected = carryIn * (2m * d + d * d);
Assert.AreEqual((double)expected, (double)delta, 0.001,
"carryIn 增量 = 首个重置日起息的两段复利,首段不含 carryIn");
}
}
}