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;
namespace UnitTestProject.Modules.SwapModule.Accrual
{
///
/// 影子测试:CalcDailyCompoundInterestByEod(旧逐日循环)vs CompoundInterestAccrual.AccrueEod(新纯函数)。
/// 构造同一组参数,两套实现并行跑,断言结果一致(到分)。
///
[TestClass]
public class CompoundEodShadowTest
{
private const decimal Notional = 100_000_000m;
private const decimal FixedRate = 0.03m;
private const int AnnualDays = 365;
private static readonly DateTime TradeDate = new(2026, 4, 21);
private static readonly DateTime EodDate = new(2026, 4, 28); // 第7天=重置日
private static trade CreateTrade()
{
return new trade
{
id = 1, TradeNumber = "UT-SHADOW", ClientId = 999998,
TradeType = "收益互换", TradeDate = TradeDate, StartDate = TradeDate,
ExerciseDate = TradeDate.AddYears(1), TradeStatus = "确认成交", ValidState = "Valid",
trade_extend = new trade_extend
{
TradeId = 1,
ExtendJson = JsonConvert.SerializeObject(new TradeExtendJson
{
AnnualDays = AnnualDays, InterestCalcMode = "11", SettlementRules = 0
})
}
};
}
private static swap_position CreatePosition(int interestMode, int interestType, int resetDays)
{
return new swap_position
{
id = 1001, SwapTradeId = 1,
PosiDirection = 0,
InterestDirection = (int)SwapDirectionEnum.收取,
InterestMode = interestMode,
InterestRateDefault = FixedRate,
InterestPrincipalFix = Notional,
PosiStartDate = TradeDate,
PosiMatuirityDate = TradeDate.AddYears(1),
IsInitial = true, Invalid = false,
InterestType = interestType,
IsAnnualized = true,
interest_rest_days = resetDays,
interest_rule = 0,
FloatRateUnderlyingCode = null,
InterestSwapInterval = "[]"
};
}
private static eod_swap_position CreatePreEod(decimal tdPrincipal, decimal unrealized)
{
return new eod_swap_position
{
id = 1, SwapTradeId = 1, PositionId = 1001,
ValueDate = EodDate.AddDays(-1),
TdInterestPrincipal = tdPrincipal,
InterestProfitSum = unrealized,
PosiNotionalValue = Notional,
FloatRate = 0m
};
}
///
/// 重置日场景:EOD 恰为重置日(7天周期,第7天)。
///
[TestMethod]
public void 影子_重置日_旧新一致()
{
var position = CreatePosition((int)InterestModeEnum.合约名义本金规模, (int)InterestTypeEnum.复利, 7);
var preEod = CreatePreEod(Notional, 50_000m);
var flowEvent = new swap_flow_event { InterestRate = FixedRate };
// 旧方法
decimal oldInterest = 0, oldTd = 0;
var svc = new StubSvc();
svc.CalcDailyCompoundInterestByEod(preEod, EodDate, TradeDate, position,
Notional, Notional, flowEvent, AnnualDays, 0m, 1m,
ref oldInterest, ref oldTd);
// 新方法
var rate = FundingLegRate.Fixed(FixedRate);
var policy = new AccrualPolicy(AccrualBoundary.Both, true, 7, AnnualDays, true);
var remainingPercent = Math.Max(0m, Math.Min(1m, Notional / Notional));
var result = CompoundInterestAccrual.AccrueEod(
50_000m, Notional, Notional, 1m, rate, policy,
isResetDay: true, remainingPercent, EodDate);
Console.WriteLine($"重置日: 旧 InterestAmount={oldInterest} Td={oldTd}");
Console.WriteLine($"重置日: 新 Accrued={result.Accrued} AccruedToday={result.AccruedToday}");
Assert.AreEqual((double)oldInterest, (double)result.Accrued, 0.01, "InterestAmount 一致");
Assert.AreEqual((double)oldTd, (double)result.AccruedToday, 0.01, "TdInterestAmount 一致");
}
///
/// 非重置日场景:第3天(非7的倍数)。
///
[TestMethod]
public void 影子_非重置日_旧新一致()
{
var nonResetDate = new DateTime(2026, 4, 24); // 第3天
var position = CreatePosition((int)InterestModeEnum.合约名义本金规模, (int)InterestTypeEnum.复利, 7);
var preEod = CreatePreEod(Notional, 30_000m);
preEod.ValueDate = nonResetDate.AddDays(-1);
var flowEvent = new swap_flow_event { InterestRate = FixedRate };
// 旧方法
decimal oldInterest = 0, oldTd = 0;
var svc = new StubSvc();
svc.CalcDailyCompoundInterestByEod(preEod, nonResetDate, TradeDate, position,
Notional, Notional, flowEvent, AnnualDays, 0m, 1m,
ref oldInterest, ref oldTd);
// 新方法
var rate = FundingLegRate.Fixed(FixedRate);
var policy = new AccrualPolicy(AccrualBoundary.Both, true, 7, AnnualDays, true);
var result = CompoundInterestAccrual.AccrueEod(
30_000m, Notional, Notional, 1m, rate, policy,
isResetDay: false, 0m, nonResetDate);
Console.WriteLine($"非重置日: 旧 InterestAmount={oldInterest} Td={oldTd}");
Console.WriteLine($"非重置日: 新 Accrued={result.Accrued} AccruedToday={result.AccruedToday}");
Assert.AreEqual((double)oldInterest, (double)result.Accrued, 0.01, "InterestAmount 一致");
Assert.AreEqual((double)oldTd, (double)result.AccruedToday, 0.01, "TdInterestAmount 一致");
}
private sealed class StubSvc : SwapDealService
{
public StubSvc() : base(new OptUserInfo(0, nameof(CompoundEodShadowTest), OptUserFrom.UnitTest)) { }
public override decimal GetConsumedInterest(int tradeId, long positionId, DateTime beforeDate) => 0m;
}
}
}