Files
zszq-trs/UnitTestProject/Modules/SwapModule/GetInterestsUnitTest_T0.cs
T
张名锐 2ee38384ac fix(swap): 修复预付金腿计息基数计算问题
- 在多个单元测试文件中添加InterestIncomeSum字段以保持数据一致性
- 修改GLMS20260105PartialCloseDividendBugTest测试,改进EOD快照验证逻辑
- 修复SwapDealService中预付金腿的orginPv计算逻辑,使用上一日保证金本金作为基准
- 更新SwapPositionComposeScenarioTest中的测试数据结构和利率设置
- 修正平仓日利息计算精度问题,使用Math.Round确保计算准确性
2026-08-09 22:28:40 +08:00

1080 lines
54 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.DBModels;
using YLErp.DBModels.Enums;
namespace YLErp.Modules.SwapModule
{
/// <summary>
/// 互换利息计算单元测试 - T+0场景
/// ================================================================
/// T+0定义:起息日(StartDate) = 成交日(TradeDate),不额外加1天
/// TradeDate=2026-04-27, StartDate=2026-04-27
/// ----------------------------------------------------------------
/// 测试口径:
/// "10" = 算头不算尾(含起息日,不含操作日)
/// "11" = 算头算尾(含起息日和操作日)
/// ----------------------------------------------------------------
/// 与T+1的关键差异:
/// T+1: StartDate=4/28, 4/29平仓(算头不算尾)→1天
/// T+0: StartDate=4/27, 4/28平仓(算头不算尾)→1天 (所有天数+1)
/// ----------------------------------------------------------------
/// Excel覆盖的T+0算头不算尾场景:
/// 固定利率:T+0固定正利率、T+0固定负利率
/// 浮动利率:T+0浮动加点(当前营业日/前一营业日/单利)
/// 每个场景 × 4业务场景(浮动×3)
/// ================================================================
/// </summary>
[TestClass]
public class GetInterestsUnitTest_T0
{
#region 内部类:浮动利率模拟服务
private sealed class StubSwapDealService : SwapDealService
{
private readonly IReadOnlyDictionary<DateTime, double> _floatRates;
public StubSwapDealService(OptUserInfo optUser, IReadOnlyDictionary<DateTime, double> floatRates) : base(optUser)
{
_floatRates = floatRates;
}
protected override bool TryGetFloatRate(DateTime valueDate, string underlyingCode, out double rate)
{
if (!string.Equals(underlyingCode, "FR007", StringComparison.OrdinalIgnoreCase))
{
rate = 0;
return false;
}
if (_floatRates.TryGetValue(valueDate.Date, out rate)) return true;
rate = 0;
return false;
}
}
#endregion
#region 测试常量
private const decimal Principal = 1000m;
private const decimal FixedRate = 0.01m;
private const decimal FixedRatePositive = 0.0075m;
private const decimal FixedRateNegative = -0.0105m;
private const decimal FloatMinusRate = -0.021m;
private const decimal FloatPlusRate = 0.0025m;
private const int AnnualDays = 365;
private const int ResetPeriod = 3;
private const int ResetPeriodFixed = 1;
private const int InterestRule_Pre = -1;
private const int InterestRule_Cur = 0;
// T+0: StartDate = TradeDate(不额外加1天)
private static readonly DateTime TradeDate = new(2026, 4, 27);
private static readonly DateTime StartDate = new(2026, 4, 27); // = TradeDate
private static readonly DateTime ExerciseDate = new(2027, 4, 27);
private SwapDealService _service;
private IReadOnlyDictionary<DateTime, double> _floatRates;
[TestInitialize]
public void Init()
{
_floatRates = new Dictionary<DateTime, double>
{
[new DateTime(2026, 4, 24)] = 0.001, // InterestRule_Pre: GetNonHolidayDefore(4/26日)→4/24
[new DateTime(2026, 4, 26)] = 0.001, // 新增:T+0前一营业日场景需要
[new DateTime(2026, 4, 27)] = 0.001,
[new DateTime(2026, 4, 28)] = 0.001,
[new DateTime(2026, 4, 29)] = 0.001,
[new DateTime(2026, 4, 30)] = 0.002,
[new DateTime(2026, 5, 1)] = 0.002, // 复利从头算需要完整日期范围
[new DateTime(2026, 5, 3)] = 0.002, // 复利重置日取FR007
[new DateTime(2026, 5, 5)] = 0.002, // InterestRule_Pre取率日
[new DateTime(2026, 5, 6)] = 0.002,
[new DateTime(2027, 4, 23)] = 0.001,
[new DateTime(2027, 4, 24)] = 0.001,
[new DateTime(2027, 4, 25)] = 0.001,
[new DateTime(2027, 4, 26)] = 0.001,
[new DateTime(2027, 4, 27)] = 0.001
};
_service = new StubSwapDealService(
new OptUserInfo(0, nameof(GetInterestsUnitTest_T0), OptUserFrom.UnitTest),
_floatRates);
}
#endregion
#region 测试数据构建器
private static trade CreateTrade(string interestCalcMode, int interestRule = InterestRule_Pre)
{
var extend = new trade_extend
{
TradeId = 1,
ExtendJson = JsonConvert.SerializeObject(new TradeExtendJson
{
AnnualDays = AnnualDays,
InterestCalcMode = interestCalcMode,
SettlementRules = interestRule
})
};
return new trade
{
id = 1, TradeNumber = "UT-SWAP-INT-T0-001", ClientId = 999998,
TradeType = "收益互换", TradeDate = TradeDate, StartDate = StartDate,
ExerciseDate = ExerciseDate, TradeStatus = "确认成交", ValidState = "Valid",
trade_extend = extend
};
}
private static swap_position CreateFloatInterestPosition(
int interestRule = InterestRule_Cur, InterestTypeEnum interestType = InterestTypeEnum.单利,
decimal fixedRate = 0.01m, SwapDirectionEnum direction = SwapDirectionEnum.收取)
{
var intervalModels = new List<IntervalModel>
{
new IntervalModel { Date = ExerciseDate, Rate = fixedRate, Settlement = 0 }
};
return new swap_position
{
id = 1001, SwapTradeId = 1, PositionType = (int)PositionTypeFlag.Unknown,
InterestDirection = (int)direction, InterestMode = (int)InterestModeEnum.标的期初全价,
InterestRateDefault = fixedRate, InterestPrincipalFix = Principal,
PosiStartDate = StartDate, PosiMatuirityDate = ExerciseDate,
IsInitial = true, Invalid = false, InterestType = (int)interestType,
IsAnnualized = true, interest_rest_days = ResetPeriod,
interest_rule = interestRule, FloatRateUnderlyingCode = "FR007",
InterestSwapInterval = JsonConvert.SerializeObject(intervalModels)
};
}
private static swap_position CreateFixedInterestPosition(
decimal fixedRate = 0.0075m, int interestRule = InterestRule_Cur,
SwapDirectionEnum direction = SwapDirectionEnum.收取)
{
var intervalModels = new List<IntervalModel>
{
new IntervalModel { Date = ExerciseDate, Rate = fixedRate, Settlement = 0 }
};
return new swap_position
{
id = 1001, SwapTradeId = 1, PositionType = (int)PositionTypeFlag.Unknown,
InterestDirection = (int)direction, InterestMode = (int)InterestModeEnum.合约名义本金规模,
InterestRateDefault = fixedRate, InterestPrincipalFix = Principal,
PosiStartDate = StartDate, PosiMatuirityDate = ExerciseDate,
IsInitial = true, Invalid = false, InterestType = (int)InterestTypeEnum.单利,
IsAnnualized = true, interest_rest_days = ResetPeriodFixed,
interest_rule = interestRule, FloatRateUnderlyingCode = null,
InterestSwapInterval = JsonConvert.SerializeObject(intervalModels)
};
}
private static eod_swap_position CreateEodPosition(DateTime valueDate, decimal tdPrincipal, decimal floatRate, decimal interestSum)
{
return new eod_swap_position
{
id = 1, SwapTradeId = 1, PositionId = 1001, ValueDate = valueDate,
ClientId = 999998, FloatRate = floatRate, TdInterestPrincipal = tdPrincipal,
PosiNotionalValue = tdPrincipal, InterestIncomeSum = interestSum,
InterestProfitSum = interestSum
};
}
/// <summary>
/// 计算含预EOD利息的总期望利息(匹配生产代码中间舍入行为)
/// ---------------------------------------------------------------
/// 生产代码先取 preEod.InterestProfitSum(已舍入到11位的DB值),
/// 再加上新期间日度原始利息,最后再舍入一次
/// </summary>
private static decimal ExpectedInterestWithPreEod(
int newDays, decimal fixedRate, decimal floatRate, decimal principal,
decimal preEodInterestSum, decimal closePercent)
{
var yearlyRate = fixedRate + floatRate;
var newRawInterest = principal * yearlyRate * newDays / AnnualDays;
return Math.Round(preEodInterestSum * closePercent + newRawInterest * closePercent,
ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
}
/// <summary>
/// 容忍末位差异的利息比较(允许相差2位)
/// </summary>
private static void AssertInterestEqual(decimal expected, decimal actual)
{
var tolerance = 1m / (decimal)Math.Pow(10, ConsGlobal.PriceRound - 2);
Assert.IsTrue(Math.Abs(expected - actual) <= tolerance,
string.Format("Expected: {0}, Actual: {1}, Diff: {2}", expected, actual, expected - actual));
}
private static decimal ExpectedInterest(int days, decimal fixedRate, decimal floatRate, decimal principal)
{
var yearlyRate = fixedRate + floatRate;
var interest = principal * yearlyRate * days / AnnualDays;
return Math.Round(interest, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
}
#endregion
#region 通用调用方法
// --- 浮动利率 ---
private swap_flow_event CalcFloatUnwind(string calcMode, DateTime valueDate, DateTime unwindDate,
decimal closePercent, int interestRule, decimal fixedRate, InterestTypeEnum interestType,
List<eod_swap_position> eodPositions = null,
decimal posiNotional = Principal, List<swap_flow_event> closeList = null,
bool newCalcLast = false)
{
eodPositions ??= new List<eod_swap_position>();
var td = CreateTrade(calcMode, interestRule);
var position = CreateFloatInterestPosition(interestRule, interestType, fixedRate);
var interests = _service.GetInterests(td, td.trade_extend, valueDate, unwindDate,
eodPositions, new List<swap_position> { position },
posiNotional, posiNotional, posiNotional, posiNotional, closePercent,
(int)SwapEventTypeEnum.平仓,
false, false, 0, posiNotional, false, settment: false, newCalcLast: newCalcLast, closeList: closeList);
AssertInterestEqual(1, interests.Count);
return interests[0];
}
private swap_flow_event CalcFloatEod(string calcMode, DateTime valueDate,
int interestRule, decimal fixedRate, InterestTypeEnum interestType,
List<eod_swap_position> eodPositions = null, List<swap_flow_event> closeList = null)
{
eodPositions ??= new List<eod_swap_position>();
var td = CreateTrade(calcMode, interestRule);
var position = CreateFloatInterestPosition(interestRule, interestType, fixedRate);
var interests = _service.GetInterests(td, td.trade_extend, valueDate, valueDate,
eodPositions, new List<swap_position> { position },
Principal, Principal, Principal, Principal, 1m,
(int)SwapEventTypeEnum.平仓,
false, false, 0, Principal, false, settment: true, newCalcLast: false, closeList: closeList);
AssertInterestEqual(1, interests.Count);
return interests[0];
}
// --- 固定利率 ---
private swap_flow_event CalcFixedUnwind(string calcMode, DateTime valueDate, DateTime unwindDate,
decimal closePercent, int interestRule, decimal fixedRate,
List<eod_swap_position> eodPositions = null,
decimal posiNotional = Principal, List<swap_flow_event> closeList = null,
bool newCalcLast = false)
{
eodPositions ??= new List<eod_swap_position>();
var td = CreateTrade(calcMode, interestRule);
var position = CreateFixedInterestPosition(fixedRate, interestRule);
var interests = _service.GetInterests(td, td.trade_extend, valueDate, unwindDate,
eodPositions, new List<swap_position> { position },
posiNotional, posiNotional, posiNotional, posiNotional, closePercent,
(int)SwapEventTypeEnum.平仓,
false, false, 0, posiNotional, false, settment: false, newCalcLast: newCalcLast, closeList: closeList);
AssertInterestEqual(1, interests.Count);
return interests[0];
}
private swap_flow_event CalcFixedEod(string calcMode, DateTime valueDate,
int interestRule, decimal fixedRate, List<eod_swap_position> eodPositions = null,
List<swap_flow_event> closeList = null)
{
eodPositions ??= new List<eod_swap_position>();
var td = CreateTrade(calcMode, interestRule);
var position = CreateFixedInterestPosition(fixedRate, interestRule);
var interests = _service.GetInterests(td, td.trade_extend, valueDate, valueDate,
eodPositions, new List<swap_position> { position },
Principal, Principal, Principal, Principal, 1m,
(int)SwapEventTypeEnum.平仓,
false, false, 0, Principal, false, settment: true, newCalcLast: false, closeList: closeList);
AssertInterestEqual(1, interests.Count);
return interests[0];
}
#endregion
// ================================================================
// T+0场景:StartDate = TradeDate = 2026-04-27
// 算头不算尾("10")4/27平仓→0天, 4/28平仓→1天, 4/29平仓→2天
// 算头算尾("11")4/27平仓→1天, 4/28平仓→2天, 4/29平仓→3天
// ================================================================
#region 1. T+0固定正利率 算头不算尾("10") - interest_rule=0
/// <summary>
/// [UT_T0_FIX_POS_001] T+0固定正利率算头不算尾-未收盘平仓
/// ---------------------------------------------------------------
/// StartDate=4/27, 4/28平仓, 算头不算尾→S=4/27,E=4/27→1天
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FIX_POS_001()
{
var interest = CalcFixedUnwind("10", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 1m,
InterestRule_Cur, FixedRatePositive);
AssertInterestEqual(ExpectedInterest(1, FixedRatePositive, 0m, Principal), interest.InterestAmount);
}
/// <summary>
/// [UT_T0_FIX_POS_002] T+0固定正利率算头不算尾-收盘后次日全部平仓
/// ---------------------------------------------------------------
/// 4/27收盘+4/28全平, 算头不算尾→1+1=2天
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FIX_POS_002()
{
var eod = new List<eod_swap_position>
{
CreateEodPosition(new DateTime(2026, 4, 27), Principal, 0m,
ExpectedInterest(1, FixedRatePositive, 0m, Principal))
};
var interest = CalcFixedUnwind("10", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 1m,
InterestRule_Cur, FixedRatePositive, eod);
AssertInterestEqual(ExpectedInterest(1, FixedRatePositive, 0m, Principal), interest.InterestAmount);
}
/// <summary>
/// [UT_T0_FIX_POS_003] T+0固定正利率算头不算尾-部分平仓
/// ---------------------------------------------------------------
/// 4/27收盘+4/28半平50%, 算头不算尾→1天×50%
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FIX_POS_003()
{
var eod = new List<eod_swap_position>
{
CreateEodPosition(new DateTime(2026, 4, 27), Principal, 0m,
ExpectedInterest(1, FixedRatePositive, 0m, Principal))
};
var interest = CalcFixedUnwind("10", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 0.5m,
InterestRule_Cur, FixedRatePositive, eod);
AssertInterestEqual(ExpectedInterest(1, FixedRatePositive, 0m, Principal * 0.5m), interest.InterestAmount);
}
/// <summary>
/// [UT_T0_FIX_POS_004] T+0固定正利率算头不算尾-部分平仓后全平
/// ---------------------------------------------------------------
/// 4/28半平50%→1天×50%; 5/6全平剩余→EOD=4/28, newCalcLast=true
/// 算头不算尾,newCalcLast强制算尾: 4/29~5/6=8天
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FIX_POS_004()
{
// 4/28部分平仓50%(算头不算尾→S=4/27,E=4/27→1天)
var u1 = CalcFixedUnwind("10", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 0.5m,
InterestRule_Cur, FixedRatePositive);
AssertInterestEqual(ExpectedInterest(1, FixedRatePositive, 0m, Principal * 0.5m), u1.InterestAmount);
// 5/6全平剩余50%EOD=4/28, newCalcLast=true强制算尾: 4/29~5/6=8天)
var eod = new List<eod_swap_position>
{
CreateEodPosition(new DateTime(2026, 4, 28), Principal * 0.5m, 0m,
ExpectedInterest(2, FixedRatePositive, 0m, Principal * 0.5m))
};
var u2 = CalcFixedUnwind("10", new DateTime(2026, 5, 6), new DateTime(2026, 5, 6), 1m,
InterestRule_Cur, FixedRatePositive, eod, Principal * 0.5m, newCalcLast: true);
var expected = ExpectedInterestWithPreEod(8, FixedRatePositive, 0m, Principal * 0.5m,
ExpectedInterest(2, FixedRatePositive, 0m, Principal * 0.5m), 1m);
AssertInterestEqual(expected, u2.InterestAmount);
}
#endregion
#region 2. T+0固定负利率 算头不算尾("10") - interest_rule=0
/// <summary>
/// [UT_T0_FIX_NEG_001] T+0固定负利率算头不算尾-未收盘平仓
/// ---------------------------------------------------------------
/// StartDate=4/27, 4/28平仓, 算头不算尾→1天, 负利率-1.05%
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FIX_NEG_001()
{
var interest = CalcFixedUnwind("10", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 1m,
InterestRule_Cur, FixedRateNegative);
AssertInterestEqual(ExpectedInterest(1, FixedRateNegative, 0m, Principal), interest.InterestAmount);
}
/// <summary>
/// [UT_T0_FIX_NEG_002] T+0固定负利率算头不算尾-收盘后次日全部平仓
/// ---------------------------------------------------------------
/// 4/27收盘+4/28全平, 算头不算尾→2天, 负利率-1.05%
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FIX_NEG_002()
{
var eod = new List<eod_swap_position>
{
CreateEodPosition(new DateTime(2026, 4, 27), Principal, 0m,
ExpectedInterest(1, FixedRateNegative, 0m, Principal))
};
var interest = CalcFixedUnwind("10", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 1m,
InterestRule_Cur, FixedRateNegative, eod);
AssertInterestEqual(ExpectedInterest(1, FixedRateNegative, 0m, Principal), interest.InterestAmount);
}
/// <summary>
/// [UT_T0_FIX_NEG_003] T+0固定负利率算头不算尾-部分平仓
/// ---------------------------------------------------------------
/// 4/27收盘+4/28半平50%, 算头不算尾→1天×50%
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FIX_NEG_003()
{
var eod = new List<eod_swap_position>
{
CreateEodPosition(new DateTime(2026, 4, 27), Principal, 0m,
ExpectedInterest(2, FixedRateNegative, 0m, Principal * 0.5m))
};
var interest = CalcFixedUnwind("10", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 0.5m,
InterestRule_Cur, FixedRateNegative, eod);
AssertInterestEqual(ExpectedInterest(1, FixedRateNegative, 0m, Principal)*0.5m, interest.InterestAmount);
}
/// <summary>
/// [UT_T0_FIX_NEG_004] T+0固定负利率算头不算尾-部分平仓后全平
/// ---------------------------------------------------------------
/// 4/28半平50%→1天×50%; 5/6全平剩余→newCalcLast=true, 8天×50%
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FIX_NEG_004()
{
var u1 = CalcFixedUnwind("10", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 0.5m,
InterestRule_Cur, FixedRateNegative);
AssertInterestEqual(ExpectedInterest(1, FixedRateNegative, 0m, Principal * 0.5m), u1.InterestAmount);
var eod = new List<eod_swap_position>
{
CreateEodPosition(new DateTime(2026, 4, 28), Principal * 0.5m, 0m,
ExpectedInterest(2, FixedRateNegative, 0m, Principal * 0.5m))
};
var u2 = CalcFixedUnwind("10", new DateTime(2026, 5, 6), new DateTime(2026, 5, 6), 1m,
InterestRule_Cur, FixedRateNegative, eod, Principal * 0.5m, newCalcLast: true);
var expected = ExpectedInterestWithPreEod(8, FixedRateNegative, 0m, Principal * 0.5m,
ExpectedInterest(2, FixedRateNegative, 0m, Principal * 0.5m), 1m);
AssertInterestEqual(expected, u2.InterestAmount);
}
#endregion
#region 3. T+0浮动加点(当前营业日,复利)算头不算尾("10")
/// <summary>
/// [UT_T0_FLT_PLUS_CUR_002] T+0浮动加点(当前营业日,复利)算头不算尾-收盘后次日全平
/// ---------------------------------------------------------------
/// 4/27收盘+4/28全平, 算头不算尾→2天, FR007+0.25%
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FLT_PLUS_CUR_002()
{
var eod = new List<eod_swap_position>
{
CreateEodPosition(new DateTime(2026, 4, 27), Principal, 0.001m,
ExpectedInterest(1, FloatPlusRate, 0.001m, Principal))
};
var interest = CalcFloatUnwind("10", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 1m,
InterestRule_Cur, FloatPlusRate, InterestTypeEnum.复利, eod);
AssertInterestEqual(ExpectedInterest(1, FloatPlusRate, 0.001m, Principal), interest.InterestAmount);
}
/// <summary>
/// [UT_T0_FLT_PLUS_CUR_003] T+0浮动加点(当前营业日,复利)算头不算尾-部分平仓
/// ---------------------------------------------------------------
/// 4/27收盘+4/28半平50%, 算头不算尾→2天×50%
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FLT_PLUS_CUR_003()
{
var eod = new List<eod_swap_position>
{
CreateEodPosition(new DateTime(2026, 4, 27), Principal, 0.001m,
ExpectedInterest(1, FloatPlusRate, 0.001m, Principal))
};
var interest = CalcFloatUnwind("10", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 0.5m,
InterestRule_Cur, FloatPlusRate, InterestTypeEnum.复利, eod);
AssertInterestEqual(ExpectedInterest(1, FloatPlusRate, 0.001m, Principal*0.5m), interest.InterestAmount);
}
/// <summary>
/// [UT_T0_FLT_PLUS_CUR_004] T+0浮动加点(当前营业日,复利)算头不算尾-部分平仓后全平
/// ---------------------------------------------------------------
/// 4/28半平50%→1天; 5/6全平剩余→复利从头算9天[27-29][30-2][3-5]
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FLT_PLUS_CUR_004()
{
var u1 = CalcFloatUnwind("10", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 0.5m,
InterestRule_Cur, FloatPlusRate, InterestTypeEnum.复利);
AssertInterestEqual(ExpectedInterest(1, FloatPlusRate, 0.001m, Principal * 0.5m), u1.InterestAmount);
var eod = new List<eod_swap_position>();
var u2 = CalcFloatUnwind("10", new DateTime(2026, 5, 6), new DateTime(2026, 5, 6), 1m,
InterestRule_Cur, FloatPlusRate, InterestTypeEnum.复利, eod, Principal * 0.5m);
// 复利从头算:9天, 每3天重置, [27-29]@0.35%, [30-2,3-5]@0.45%
var principal = Principal * 0.5m;
var rate1 = FloatPlusRate + 0.001m;
var rate2 = FloatPlusRate + 0.002m;
decimal interest = 0m, dynomic = principal;
for (int d = 0; d < 9; d++)
{
if (d % 3 == 0) dynomic = principal + interest;
interest += dynomic * (d < 3 ? rate1 : rate2) / AnnualDays;
}
var expected = Math.Round(interest, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
AssertInterestEqual(expected, u2.InterestAmount);
}
#endregion
#region 4. T+0浮动加点(前一营业日,复利)算头不算尾("10")
/// <summary>
/// [UT_T0_FLT_PLUS_PRE_002] T+0浮动加点(前一营业日,复利)算头不算尾-收盘后次日全平
/// ---------------------------------------------------------------
/// 4/27收盘+4/28全平, 算头不算尾→1天
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FLT_PLUS_PRE_002()
{
var eod = new List<eod_swap_position>
{
CreateEodPosition(new DateTime(2026, 4, 27), Principal, 0.001m,
ExpectedInterest(1, FloatPlusRate, 0.001m, Principal))
};
var interest = CalcFloatUnwind("10", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 1m,
InterestRule_Pre, FloatPlusRate, InterestTypeEnum.复利, eod);
AssertInterestEqual(ExpectedInterest(1, FloatPlusRate, 0.001m, Principal), interest.InterestAmount);
}
/// <summary>
/// [UT_T0_FLT_PLUS_PRE_003] T+0浮动加点(前一营业日,复利)算头不算尾-部分平仓
/// ---------------------------------------------------------------
/// 4/27收盘+4/28半平50%, 算头不算尾→1天, 复利从头算
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FLT_PLUS_PRE_003()
{
var eod = new List<eod_swap_position>
{
CreateEodPosition(new DateTime(2026, 4, 27), Principal, 0.001m,
ExpectedInterest(2, FloatPlusRate, 0.001m, Principal * 0.5m))
};
var interest = CalcFloatUnwind("10", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 0.5m,
InterestRule_Pre, FloatPlusRate, InterestTypeEnum.复利, eod);
AssertInterestEqual(ExpectedInterest(1, FloatPlusRate, 0.001m, Principal * 0.5m), interest.InterestAmount);
}
/// <summary>
/// [UT_T0_FLT_PLUS_PRE_004] T+0浮动加点(前一营业日,复利)算头不算尾-部分平仓后全平
/// ---------------------------------------------------------------
/// 4/28半平→1天@0.10%; 5/6全平→复利从头算9天
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FLT_PLUS_PRE_004()
{
var u1 = CalcFloatUnwind("10", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 0.5m,
InterestRule_Pre, FloatPlusRate, InterestTypeEnum.复利);
AssertInterestEqual(ExpectedInterest(1, FloatPlusRate, 0.001m, Principal * 0.5m), u1.InterestAmount);
var eod = new List<eod_swap_position>
{
CreateEodPosition(new DateTime(2026, 4, 28), Principal * 0.5m, 0.001m,
ExpectedInterest(2, FloatPlusRate, 0.001m, Principal * 0.5m))
};
var u2 = CalcFloatUnwind("10", new DateTime(2026, 5, 6), new DateTime(2026, 5, 6), 1m,
InterestRule_Pre, FloatPlusRate, InterestTypeEnum.复利, eod, Principal * 0.5m);
// 复利从头算:9天, 每3天重置, [27-29]@0.001, [30-2]@0.001, [3-5]@0.002 → 6@0.35%+3@0.45%
var principal = Principal * 0.5m;
var rate1 = FloatPlusRate + 0.001m;
var rate2 = FloatPlusRate + 0.002m;
decimal interest2 = 0m, dynomic = principal;
for (int d = 0; d < 9; d++)
{
if (d % 3 == 0) dynomic = principal + interest2;
interest2 += dynomic * (d < 6 ? rate1 : rate2) / AnnualDays;
}
var expected = Math.Round(interest2, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
AssertInterestEqual(expected, u2.InterestAmount);
}
#endregion
#region 5. T+0浮动加点(单利)算头不算尾("10")
/// <summary>
/// [UT_T0_FLT_PLUS_SI_002] T+0浮动加点(单利)算头不算尾-收盘后次日全平
/// ---------------------------------------------------------------
/// 4/27收盘+4/28全平, 算头不算尾→2天, 单利
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FLT_PLUS_SI_002()
{
var eod = new List<eod_swap_position>
{
CreateEodPosition(new DateTime(2026, 4, 27), Principal, 0.001m,
ExpectedInterest(1, FloatPlusRate, 0.001m, Principal))
};
var interest = CalcFloatUnwind("10", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 1m,
InterestRule_Pre, FloatPlusRate, InterestTypeEnum.单利, eod);
AssertInterestEqual(ExpectedInterest(1, FloatPlusRate, 0.001m, Principal), interest.InterestAmount);
}
/// <summary>
/// [UT_T0_FLT_PLUS_SI_003] T+0浮动加点(单利)算头不算尾-部分平仓
/// ---------------------------------------------------------------
/// 4/27收盘+4/28半平50%, 算头不算尾→1天×50%
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FLT_PLUS_SI_003()
{
var eod = new List<eod_swap_position>
{
CreateEodPosition(new DateTime(2026, 4, 27), Principal, 0.001m,
ExpectedInterest(1, FloatPlusRate, 0.001m, Principal))
};
var interest = CalcFloatUnwind("10", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 0.5m,
InterestRule_Pre, FloatPlusRate, InterestTypeEnum.单利, eod);
AssertInterestEqual(ExpectedInterest(1, FloatPlusRate, 0.001m, Principal*0.5m), interest.InterestAmount);
}
/// <summary>
/// [UT_T0_FLT_PLUS_SI_004] T+0浮动加点(单利)算头不算尾-部分平仓后全平
/// ---------------------------------------------------------------
/// 4/28半平→1天; 5/6全平→9天
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FLT_PLUS_SI_004()
{
var u1 = CalcFloatUnwind("10", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 0.5m,
InterestRule_Pre, FloatPlusRate, InterestTypeEnum.单利);
AssertInterestEqual(ExpectedInterest(1, FloatPlusRate, 0.001m, Principal * 0.5m), u1.InterestAmount);
var eod = new List<eod_swap_position>
{
CreateEodPosition(new DateTime(2026, 4, 28), Principal * 0.5m, 0.001m,
ExpectedInterest(2, FloatPlusRate, 0.001m, Principal * 0.5m))
};
var u2 = CalcFloatUnwind("10", new DateTime(2026, 5, 6), new DateTime(2026, 5, 6), 1m,
InterestRule_Pre, FloatPlusRate, InterestTypeEnum.单利, eod, Principal * 0.5m);
// 单利: 9天, [27,28,29]@0.001 +[30,1,2]@0.001 + [3,4,5]@0.002
var raw = Principal * 0.5m * (FloatPlusRate + 0.001m) * 6 / AnnualDays
+ Principal * 0.5m * (FloatPlusRate + 0.002m) * 3 / AnnualDays;
var expected = Math.Round(raw, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
AssertInterestEqual(expected, u2.InterestAmount);
}
#endregion
#region 6. T+0固定正利率 算头算尾("11") - interest_rule=0
/// <summary>
/// [UT_T0_FIX_POS_11_001] 算头算尾 T+0固定正利率-未收盘平仓
/// ---------------------------------------------------------------
/// StartDate=4/27, 4/28平仓, 算头算尾→S=4/27,E=4/28→2天
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FIX_POS_11_001()
{
var i = CalcFixedUnwind("11", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 1m,
InterestRule_Cur, FixedRatePositive);
AssertInterestEqual(ExpectedInterest(2, FixedRatePositive, 0m, Principal), i.InterestAmount);
}
/// <summary>
/// [UT_T0_FIX_POS_11_002] 算头算尾 T+0固定正利率-收盘后次日全部平仓
/// ---------------------------------------------------------------
/// 4/27收盘+4/28全平, 算头算尾→1+1=2天
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FIX_POS_11_002()
{
var eod = new List<eod_swap_position>
{
CreateEodPosition(new DateTime(2026, 4, 27), Principal, 0m,
ExpectedInterest(1, FixedRatePositive, 0m, Principal))
};
var i = CalcFixedUnwind("11", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 1m,
InterestRule_Cur, FixedRatePositive, eod);
AssertInterestEqual(ExpectedInterest(2, FixedRatePositive, 0m, Principal), i.InterestAmount);
}
/// <summary>
/// [UT_T0_FIX_POS_11_003] 算头算尾 T+0固定正利率-部分平仓
/// ---------------------------------------------------------------
/// 4/27收盘+4/28半平50%, 算头算尾→2天×50%
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FIX_POS_11_003()
{
var eod = new List<eod_swap_position>
{
CreateEodPosition(new DateTime(2026, 4, 27), Principal, 0m,
ExpectedInterest(2, FixedRatePositive, 0m, Principal * 0.5m))
};
var i = CalcFixedUnwind("11", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 0.5m,
InterestRule_Cur, FixedRatePositive, eod);
AssertInterestEqual(ExpectedInterest(1, FixedRatePositive, 0m, Principal), i.InterestAmount);
}
/// <summary>
/// [UT_T0_FIX_POS_11_004] 算头算尾 T+0固定正利率-部分平仓后全平
/// ---------------------------------------------------------------
/// 4/28半平→2天×50%; 5/6全平→EOD=4/28, 算头算尾(newCalcLast=true无影响)
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FIX_POS_11_004()
{
var u1 = CalcFixedUnwind("11", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 0.5m,
InterestRule_Cur, FixedRatePositive);
AssertInterestEqual(ExpectedInterest(2, FixedRatePositive, 0m, Principal * 0.5m), u1.InterestAmount);
var eod = new List<eod_swap_position>
{
CreateEodPosition(new DateTime(2026, 4, 28), Principal * 0.5m, 0m,
ExpectedInterest(2, FixedRatePositive, 0m, Principal * 0.5m))
};
var u2 = CalcFixedUnwind("11", new DateTime(2026, 5, 6), new DateTime(2026, 5, 6), 1m,
InterestRule_Cur, FixedRatePositive, eod, Principal * 0.5m, newCalcLast: true);
AssertInterestEqual(ExpectedInterestWithPreEod(8, FixedRatePositive, 0m, Principal * 0.5m,
ExpectedInterest(2, FixedRatePositive, 0m, Principal * 0.5m), 1m), u2.InterestAmount);
}
#endregion
#region 7. T+0浮动加点(当前营业日)算头算尾("11")
/// <summary>
/// [UT_T0_FLT_PLUS_CUR_11_002] 算头算尾 T+0浮动加点(当前营业日,复利)-收盘后次日全平
/// ---------------------------------------------------------------
/// 4/27收盘+4/28全平, 算头算尾→2天, 复利从头算
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FLT_PLUS_CUR_11_002()
{
var eod = new List<eod_swap_position>
{
CreateEodPosition(new DateTime(2026, 4, 27), Principal, 0.001m,
ExpectedInterest(1, FloatPlusRate, 0.001m, Principal))
};
var i = CalcFloatUnwind("11", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 1m,
InterestRule_Cur, FloatPlusRate, InterestTypeEnum.复利, eod);
AssertInterestEqual(ExpectedInterest(2, FloatPlusRate, 0.001m, Principal), i.InterestAmount);
}
/// <summary>
/// [UT_T0_FLT_PLUS_CUR_11_003] 算头算尾 T+0浮动加点(当前营业日,复利)-部分平仓
/// ---------------------------------------------------------------
/// 4/27收盘+4/28半平50%, 算头算尾→2天, 复利从头算
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FLT_PLUS_CUR_11_003()
{
var eod = new List<eod_swap_position>
{
CreateEodPosition(new DateTime(2026, 4, 27), Principal, 0.001m,
ExpectedInterest(2, FloatPlusRate, 0.001m, Principal * 0.5m))
};
var i = CalcFloatUnwind("11", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 0.5m,
InterestRule_Cur, FloatPlusRate, InterestTypeEnum.复利, eod);
AssertInterestEqual(ExpectedInterest(2, FloatPlusRate, 0.001m, Principal * 0.5m), i.InterestAmount);
}
/// <summary>
/// [UT_T0_FLT_PLUS_CUR_11_004] 算头算尾 T+0浮动加点(当前营业日,复利)-部分平仓后全平
/// ---------------------------------------------------------------
/// 4/28半平→2天; 5/6全平→复利从头算10天 [27-29] [30-2] [3-5] [6]
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FLT_PLUS_CUR_11_004()
{
var u1 = CalcFloatUnwind("11", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 0.5m,
InterestRule_Cur, FloatPlusRate, InterestTypeEnum.复利);
AssertInterestEqual(ExpectedInterest(2, FloatPlusRate, 0.001m, Principal * 0.5m), u1.InterestAmount);
var eod = new List<eod_swap_position>
{
CreateEodPosition(new DateTime(2026, 4, 28), Principal * 0.5m, 0.001m,
ExpectedInterest(2, FloatPlusRate, 0.001m, Principal * 0.5m))
};
var u2 = CalcFloatUnwind("11", new DateTime(2026, 5, 6), new DateTime(2026, 5, 6), 1m,
InterestRule_Cur, FloatPlusRate, InterestTypeEnum.复利, eod, Principal * 0.5m, newCalcLast: false);
// 复利从头算:10天, 每3天重置, [27-29]@0.35%, [30-2,3-5,6]@0.45%
var principal = Principal * 0.5m;
var rate1 = FloatPlusRate + 0.001m;
var rate2 = FloatPlusRate + 0.002m;
decimal interest = 0m, dynomic = principal;
for (int d = 0; d < 10; d++)
{
if (d % 3 == 0) dynomic = principal + interest;
interest += dynomic * (d < 3 ? rate1 : rate2) / AnnualDays;
}
var expected1 = Math.Round(interest, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
AssertInterestEqual(expected1, u2.InterestAmount);
}
#endregion
#region 8. T+0浮动加点(前一营业日)算头算尾("11")
/// <summary>
/// [UT_T0_FLT_PLUS_PRE_11_002] 算头算尾 T+0浮动加点(前一营业日,复利)-收盘后次日全平
/// ---------------------------------------------------------------
/// 4/27收盘+4/28全平, 算头算尾→2天, 复利从头算
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FLT_PLUS_PRE_11_002()
{
var eod = new List<eod_swap_position>
{
CreateEodPosition(new DateTime(2026, 4, 27), Principal, 0.001m,
ExpectedInterest(1, FloatPlusRate, 0.001m, Principal))
};
var i = CalcFloatUnwind("11", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 1m,
InterestRule_Pre, FloatPlusRate, InterestTypeEnum.复利, eod);
AssertInterestEqual(ExpectedInterest(2, FloatPlusRate, 0.001m, Principal), i.InterestAmount);
}
/// <summary>
/// [UT_T0_FLT_PLUS_PRE_11_003] 算头算尾 T+0浮动加点(前一营业日,复利)-部分平仓
/// ---------------------------------------------------------------
/// 4/27收盘+4/28半平50%, 算头算尾→2天, 复利从头算
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FLT_PLUS_PRE_11_003()
{
var eod = new List<eod_swap_position>
{
CreateEodPosition(new DateTime(2026, 4, 27), Principal, 0.001m,
ExpectedInterest(2, FloatPlusRate, 0.001m, Principal * 0.5m))
};
var i = CalcFloatUnwind("11", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 0.5m,
InterestRule_Pre, FloatPlusRate, InterestTypeEnum.复利, eod);
AssertInterestEqual(ExpectedInterest(2, FloatPlusRate, 0.001m, Principal * 0.5m), i.InterestAmount);
}
/// <summary>
/// [UT_T0_FLT_PLUS_PRE_11_004] 算头算尾 T+0浮动加点(前一营业日,复利)-部分平仓后全平
/// ---------------------------------------------------------------
/// 4/28半平→2天@0.10%; 5/6全平→复利从头算10天
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FLT_PLUS_PRE_11_004()
{
var u1 = CalcFloatUnwind("11", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 0.5m,
InterestRule_Pre, FloatPlusRate, InterestTypeEnum.复利);
AssertInterestEqual(ExpectedInterest(2, FloatPlusRate, 0.001m, Principal * 0.5m), u1.InterestAmount);
var eod = new List<eod_swap_position>
{
CreateEodPosition(new DateTime(2026, 4, 28), Principal * 0.5m, 0.001m,
ExpectedInterest(2, FloatPlusRate, 0.001m, Principal * 0.5m))
};
var u2 = CalcFloatUnwind("11", new DateTime(2026, 5, 6), new DateTime(2026, 5, 6), 1m,
InterestRule_Pre, FloatPlusRate, InterestTypeEnum.复利, eod, Principal * 0.5m);
// 复利从头算:10天, 每3天重置, [27-29,30-2]@0.001, [3-5,6]@0.002 → 6@0.35%+4@0.45%
var principal = Principal * 0.5m;
var rate1 = FloatPlusRate + 0.001m;
var rate2 = FloatPlusRate + 0.002m;
decimal interest = 0m, dynomic = principal;
for (int d = 0; d < 10; d++)
{
if (d % 3 == 0) dynomic = principal + interest;
interest += dynomic * (d < 6 ? rate1 : rate2) / AnnualDays;
}
var expected1 = Math.Round(interest, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
AssertInterestEqual(expected1, u2.InterestAmount);
}
#endregion
#region 9. T+0浮动加点(单利)算头算尾("11")
/// <summary>
/// [UT_T0_FLT_PLUS_SI_11_002] 算头算尾 T+0浮动加点(单利)-收盘后次日全平
/// ---------------------------------------------------------------
/// 4/27收盘+4/28全平, 算头算尾→2天, 单利
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FLT_PLUS_SI_11_002()
{
var eod = new List<eod_swap_position>
{
CreateEodPosition(new DateTime(2026, 4, 27), Principal, 0.001m,
ExpectedInterest(1, FloatPlusRate, 0.001m, Principal))
};
var i = CalcFloatUnwind("11", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 1m,
InterestRule_Pre, FloatPlusRate, InterestTypeEnum.单利, eod);
AssertInterestEqual(ExpectedInterest(2, FloatPlusRate, 0.001m, Principal), i.InterestAmount);
}
/// <summary>
/// [UT_T0_FLT_PLUS_SI_11_003] 算头算尾 T+0浮动加点(单利)-部分平仓
/// ---------------------------------------------------------------
/// 4/27收盘+4/28半平50%, 算头算尾→2天×50%
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FLT_PLUS_SI_11_003()
{
var eod = new List<eod_swap_position>
{
CreateEodPosition(new DateTime(2026, 4, 27), Principal, 0.001m,
ExpectedInterest(2, FloatPlusRate, 0.001m, Principal * 0.5m))
};
var i = CalcFloatUnwind("11", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 0.5m,
InterestRule_Pre, FloatPlusRate, InterestTypeEnum.单利, eod);
AssertInterestEqual(ExpectedInterest(1, FloatPlusRate, 0.001m, Principal), i.InterestAmount);
}
/// <summary>
/// [UT_T0_FLT_PLUS_SI_11_004] 算头算尾 T+0浮动加点(单利)-部分平仓后全平
/// ---------------------------------------------------------------
/// 4/28半平→2天; 5/6全平→算头算尾
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FLT_PLUS_SI_11_004()
{
var u1 = CalcFloatUnwind("11", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 0.5m,
InterestRule_Pre, FloatPlusRate, InterestTypeEnum.单利);
AssertInterestEqual(ExpectedInterest(2, FloatPlusRate, 0.001m, Principal * 0.5m), u1.InterestAmount);
var eod = new List<eod_swap_position>
{
CreateEodPosition(new DateTime(2026, 4, 28), Principal * 0.5m, 0.001m,
ExpectedInterest(2, FloatPlusRate, 0.001m, Principal * 0.5m))
};
var u2 = CalcFloatUnwind("11", new DateTime(2026, 5, 6), new DateTime(2026, 5, 6), 1m,
InterestRule_Pre, FloatPlusRate, InterestTypeEnum.单利, eod, Principal * 0.5m);
// 单利: 10天, [27,28,29]@0.001 + [30,1,2]@0.001 + [3,4,5,6]@0.002 → 3@0.35% + 3@0.35% + 3@0.45%
var raw1 = Principal * 0.5m * (FloatPlusRate + 0.001m) * 6 / AnnualDays
+ Principal * 0.5m * (FloatPlusRate + 0.002m) * 4 / AnnualDays;
var expected1 = Math.Round(raw1, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
AssertInterestEqual(expected1, u2.InterestAmount);
}
#endregion
#region 10. T+0固定利率 - 收盘归档
/// <summary>
/// [UT_T0_FIX_EOD_001] T+0固定利率算头不算尾-首日收盘归档
/// ---------------------------------------------------------------
/// 4/27(起息日)执行收盘, 算头不算尾→1天
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FIX_EOD_001()
{
var i = CalcFixedEod("10", new DateTime(2026, 4, 27), InterestRule_Cur, FixedRatePositive);
AssertInterestEqual(ExpectedInterest(1, FixedRatePositive, 0m, Principal), i.InterestAmount);
}
/// <summary>
/// [UT_T0_FIX_EOD_002] T+0固定利率算头不算尾-连续收盘
/// ---------------------------------------------------------------
/// 4/27收盘+4/28收盘, 算头不算尾→各1天
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FIX_EOD_002()
{
var e1 = CalcFixedEod("10", new DateTime(2026, 4, 27), InterestRule_Cur, FixedRatePositive);
var expected1 = ExpectedInterest(1, FixedRatePositive, 0m, Principal);
AssertInterestEqual(expected1, e1.InterestAmount);
var e2 = CalcFixedEod("10", new DateTime(2026, 4, 28), InterestRule_Cur, FixedRatePositive,
new List<eod_swap_position> { CreateEodPosition(new DateTime(2026, 4, 27), Principal, 0m, expected1) });
AssertInterestEqual(expected1*2, e2.InterestAmount);
}
#endregion
#region 11. T+0浮动利率 - 基础场景
/// <summary>
/// [UT_T0_FLT_BASE_001] T+0浮动利率基础-起息日平仓→0天
/// ---------------------------------------------------------------
/// StartDate=4/27, 4/27平仓, 算头不算尾→0天
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FLT_BASE_001()
{
var i = CalcFloatUnwind("10", new DateTime(2026, 4, 27), new DateTime(2026, 4, 27), 1m,
InterestRule_Cur, FixedRate, InterestTypeEnum.单利);
AssertInterestEqual(0m, i.InterestAmount);
}
/// <summary>
/// [UT_T0_FLT_BASE_002] T+0浮动利率基础-第2天全平→1天
/// ---------------------------------------------------------------
/// StartDate=4/27, 4/28平仓, 算头不算尾→1天
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FLT_BASE_002()
{
var i = CalcFloatUnwind("10", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 1m,
InterestRule_Cur, FixedRate, InterestTypeEnum.单利);
AssertInterestEqual(ExpectedInterest(1, FixedRate, 0.001m, Principal), i.InterestAmount);
}
/// <summary>
/// [UT_T0_FLT_BASE_003] T+0浮动利率基础-首日收盘→1天
/// ---------------------------------------------------------------
/// 4/27执行收盘, 算头不算尾→1天
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FLT_BASE_003()
{
var i = CalcFloatEod("10", new DateTime(2026, 4, 27), InterestRule_Cur, FixedRate, InterestTypeEnum.单利);
AssertInterestEqual(ExpectedInterest(1, FixedRate, 0.001m, Principal), i.InterestAmount);
}
/// <summary>
/// [UT_T0_FLT_BASE_004] T+0浮动利率基础-首日收盘+次日全平→2天
/// ---------------------------------------------------------------
/// 4/27收盘+4/28全平, 算头不算尾→2天
/// ---------------------------------------------------------------
/// </summary>
[TestMethod]
public void UT_T0_FLT_BASE_004()
{
var eod = new List<eod_swap_position>
{
CreateEodPosition(new DateTime(2026, 4, 27), Principal, 0.001m,
ExpectedInterest(1, FixedRate, 0.001m, Principal))
};
var i = CalcFloatUnwind("10", new DateTime(2026, 4, 28), new DateTime(2026, 4, 28), 1m,
InterestRule_Cur, FixedRate, InterestTypeEnum.单利, eod);
AssertInterestEqual(ExpectedInterest(1, FixedRate, 0.001m, Principal), i.InterestAmount);
}
#endregion
}
}