Files
zszq-trs/UnitTestProject/Modules/SwapModule/SwapEodPositionRiskNewTabTest.cs
张名锐 7d0eb16f64 feat(eodswapposition): 新增框架合约新口径展示功能
- 在EodPnlCalculator中添加CalculateEodSwapRiskNewFields方法,实现新口径计算逻辑
- 创建EodSwapRiskNewFields和EodSwapRiskNewResponse模型类,支持新的展示字段
- 实现SearchEodSwapNewList查询方法,复用旧查询权限控制并计算新字段
- 添加index=3的新框架合约Tab页面,使用独立查询接口和列配置
- 前端JavaScript中实现colModelGridEodSwapNew列模型,替换旧字段并新增9个字段
- 完善导出功能支持新Tab的标准格式导出,保持与旧口径分离
- 更新测试用例验证新框架合约前端接线正确性
2026-08-25 13:49:30 +08:00

179 lines
7.1 KiB
C#
Raw Permalink 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 YLErp.DBModels;
using YLErp.DBModels.Enums;
namespace YLErp.Modules.SwapModule;
/// <summary>
/// EQD-7084 新“框架合约”Tab 的口径测试。
/// 纯计算测试不依赖数据库,直接锁定 EodPnlCalculator 的新口径。
/// </summary>
[TestClass]
public class SwapEodPositionRiskNewTabTest
{
[TestMethod]
public void 新口径_普通利息排除保证金_浮动收益剔除费用并保留估值总额()
{
var floating = new[]
{
// EOD 归一后,费用已经按我方收益视角落库;支付费用为负数。
FloatingLeg("600000", 1, 100m, 0m, -12m, "普通收益互换")
};
var interests = new[]
{
InterestLeg(1, (int)InterestModeEnum.固定值, 80m, 0.02m, 0.02m),
InterestLeg(1, (int)InterestModeEnum.初始预付金, 20m, 0.01m, 0.01m)
};
var fields = InvokeCalculation(
floating,
interests,
structureType: "普通收益互换",
notionalValue: 1_000m,
startDate: new DateTime(2026, 1, 1),
maturityDate: new DateTime(2026, 12, 31),
periodAmount: 5m,
dividendPayDate: 0);
Assert.AreEqual(100m, GetDecimal(fields, "FloatingUnrealizedPnl"), 0.0001m,
"新浮动端待实现收益应排除 PosiFeePendingPosiProfitSum(88) - PosiFeePending(-12) = 100");
Assert.AreEqual(-12m, GetDecimal(fields, "OpeningClosingFee"), 0.0001m,
"开平仓费用直接使用 EOD 已归一的 PosiFeePending");
Assert.AreEqual(80m, GetDecimal(fields, "OrdinaryInterestPnl"), 0.0001m,
"利息端待实现收益应排除初始/维持保证金腿");
Assert.AreEqual(-20m, GetDecimal(fields, "MarginInterestAmount"), 0.0001m,
"保证金利息仍应按保证金腿方向计入估值");
Assert.AreEqual(153m, GetDecimal(fields, "MaturityNettingValuation"), 0.0001m,
"估值应保持旧口径:100 - 12 + 80 - 20 + 5 = 153;费用只计一次");
}
[TestMethod]
public void 新口径_当前利率合计使用普通利息腿TdInterestRate_并识别FR007()
{
var fr007Leg = InterestLeg(2, (int)InterestModeEnum.合约名义本金规模, 20m, 0.03m, 0.018m);
fr007Leg.FloatRateUnderlyingCode = "FR007";
fr007Leg.FloatRate = 0.015m;
var fields = InvokeCalculation(
new[] { FloatingLeg("600001", 2, 100m, 0m, 0m, "普通收益互换") },
new[]
{
InterestLeg(1, (int)InterestModeEnum.固定值, 10m, 0.02m, 0.0125m),
fr007Leg
},
structureType: "普通收益互换",
notionalValue: 100m,
startDate: new DateTime(2026, 2, 1),
maturityDate: new DateTime(2026, 8, 1),
periodAmount: 0m,
dividendPayDate: 1);
Assert.AreEqual(0.0305m, GetDecimal(fields, "InterestRatePrice"), 0.0000001m,
"利率端价格应为普通利息腿当前 TdInterestRate 合计,而非默认利差合计");
Assert.AreEqual("FR007", GetString(fields, "InterestBenchmark"));
}
[TestMethod]
public void 新口径_普通利息腿无FR007时基准为固定利率()
{
var fields = InvokeCalculation(
new[] { FloatingLeg("600002", 1, 100m, 0m, 0m, "普通收益互换") },
new[] { InterestLeg(1, (int)InterestModeEnum.固定值, 10m, 0.02m, 0.0125m) },
structureType: "普通收益互换",
notionalValue: 100m,
startDate: new DateTime(2026, 2, 1),
maturityDate: new DateTime(2026, 8, 1),
periodAmount: 0m,
dividendPayDate: 1);
Assert.AreEqual("固定利率", GetString(fields, "InterestBenchmark"));
}
[TestMethod]
public void 新口径_债券期初价格按风险页约定放大百分价格_并保留合同字段()
{
var fields = InvokeCalculation(
new[] { FloatingLeg("110000", 1, 99.12m, 0m, 0m, "普通债券类收益互换", "Bond") },
new[] { InterestLeg(1, (int)InterestModeEnum.固定值, 1m, 0.01m, 0.01m) },
structureType: "普通债券类收益互换",
notionalValue: 9_900m,
startDate: new DateTime(2026, 3, 1),
maturityDate: new DateTime(2027, 3, 1),
periodAmount: 0m,
dividendPayDate: 1);
Assert.AreEqual(99.12m, GetDecimal(fields, "InitialPrice"), 0.0001m,
"债券日终 PosiGrossPrice 已由 SetPosiPrice 按风险页口径缩放,新接口不能再次乘 100");
Assert.AreEqual(9_900m, GetDecimal(fields, "NotionalQuantity"), 0.0001m);
Assert.AreEqual("多头", GetString(fields, "UnderlyingDirection"));
Assert.AreEqual("110000", GetString(fields, "UnderlyingCode"));
Assert.AreEqual("Bond", GetString(fields, "UnderlyingInstrumentType"));
Assert.AreEqual(new DateTime(2026, 3, 1), GetDate(fields, "ContractStartDate"));
Assert.AreEqual(new DateTime(2027, 3, 1), GetDate(fields, "ContractMaturityDate"));
}
private static object InvokeCalculation(
IEnumerable<eod_swap_position> floating,
IEnumerable<eod_swap_position> interests,
string structureType,
decimal notionalValue,
DateTime startDate,
DateTime maturityDate,
decimal periodAmount,
int dividendPayDate)
{
return EodPnlCalculator.CalculateEodSwapRiskNewFields(
floating,
interests,
structureType,
notionalValue,
startDate,
maturityDate,
periodAmount,
dividendPayDate);
}
private static decimal GetDecimal(object fields, string name)
=> Convert.ToDecimal(fields.GetType().GetProperty(name)!.GetValue(fields));
private static string GetString(object fields, string name)
=> (string)fields.GetType().GetProperty(name)!.GetValue(fields)!;
private static DateTime GetDate(object fields, string name)
=> (DateTime)fields.GetType().GetProperty(name)!.GetValue(fields)!;
private static eod_swap_position FloatingLeg(
string code,
int positionType,
decimal mtm,
decimal dividend,
decimal fee,
string structureType,
string instrumentType = null)
=> new()
{
UnderlyingCode = code,
UnderlyingInstrumentType = instrumentType ?? structureType,
PositionType = positionType,
PosiGrossPrice = mtm,
PosiMtmPnL = mtm,
PosiDividendSum = dividend,
PosiFeePending = fee,
PosiProfitSum = mtm + dividend + fee,
PosiNotionalValue = 100m
};
private static eod_swap_position InterestLeg(
int direction,
int mode,
decimal profit,
decimal defaultRate,
decimal currentRate)
=> new()
{
InterestDirection = direction,
InterestMode = mode,
InterestProfitSum = profit,
InterestRateDefault = defaultRate,
TdInterestRate = currentRate
};
}