Files
zszq-trs/UnitTestProject/Modules/SwapModule/GLMS20260105GoldenTest.cs
T
hjhan 0e045d89db test(swap): GLMS-20260105-0007分红精度差异golden红灯(复现0.36差异)
用真实测试库数据录制golden,回放复现0.36精度差异:
- 互换前 PosiDividendSum=-90400,互换支付-400
- 期望 PosiDividendSum=-90000,实际=-89999.64,差异0.36
- 回放断言失败(红灯坐实bug)

根因:UpdateEodPosition cs:1635从头重算PosiDividendSum
(Round(全程,2)-已实现),与CopyEodPosition逐天递增
(每天Round(增量,2)累积)产生舍入误差。

录制方法Record_FromRealDB标[Ignore],手动跑生成golden。
回放方法Replay_VerifyPrecisionDiff进CI,当前红灯(期望=-90000,实际=-89999.64)。
2026-07-02 10:28:23 +08:00

195 lines
9.2 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 Newtonsoft.Json.Linq;
using YLErp.DBModels;
using YLErp.DBModels.Enums;
namespace YLErp.Modules.SwapModule
{
/// <summary>
/// GLMS-20260105-0007 分红精度差异 - golden 录制/回放测试
/// ============================================================================
/// 用真实测试库数据录制 → golden JSON → 回放复现 0.36 差异
///
/// 问题:原有待实现分红-90400,互换支付-400后应为-90000,实际-89999.64
/// 根因:UpdateEodPosition cs:1635 从头重算 PosiDividendSum
/// 与 CopyEodPosition 逐天递增的舍入累积不一致
/// ============================================================================
[TestClass]
public class GLMS20260105GoldenTest
{
private const string TradeNumber = "GLMS-20260105-0007";
private static readonly string GoldenDir = Path.Combine(
AppDomain.CurrentDomain.BaseDirectory, "Resources", "GoldenFiles", "GLMS20260105");
#region 录制:从真实库读取数据,序列化为 golden
/// <summary>
/// 从真实测试库录制 GLMS-20260105-0007 的完整数据快照。
/// 标 [Ignore],手动跑一次生成 golden JSON。
/// </summary>
[TestMethod]
[Ignore]
[TestCategory("GoldenRecord")]
public void Record_FromRealDB()
{
YLContext db;
try { db = DbContextFactory.GetYLDbContext(); }
catch (Exception ex) { Assert.Inconclusive($"无法连接测试库:{ex.Message}"); return; }
try
{
Directory.CreateDirectory(GoldenDir);
var td = db.trade.FirstOrDefault(t => t.TradeNumber == TradeNumber);
Assert.IsNotNull(td, $"交易 {TradeNumber} 不存在");
var floatEods = db.eod_swap_position
.Where(x => x.SwapTradeId == td.id && x.PosiDirection > 0 && !x.Invalid)
.OrderBy(x => x.ValueDate).ToList();
var flows = db.swap_flow_event
.Where(x => x.SwapTradeId == td.id && x.DataState == (int)SwapFlowDateStateEnum.完成)
.OrderBy(x => x.EventDate).ThenBy(x => x.id).ToList();
var positions = db.swap_position
.Where(x => x.SwapTradeId == td.id && !x.Invalid).ToList();
var swapDate = flows.First(f => f.EventType == (int)SwapFlowEventTypeEnum.互换).EventDate;
var keyDates = new[] { swapDate.AddDays(-1), swapDate, swapDate.AddDays(1) };
var keyFloatEods = floatEods.Where(x => keyDates.Contains(x.ValueDate)).ToList();
var golden = new JObject
{
["TradeNumber"] = TradeNumber,
["TradeId"] = td.id,
["SwapDate"] = swapDate.ToString("yyyy-MM-dd"),
["Description"] = "分红精度差异:互换前-90400,互换后应为-90000,实际-89999.64"
};
// 关键3天的浮动腿eod(含精确字段值)
var keyArray = new JArray();
foreach (var e in keyFloatEods)
{
keyArray.Add(new JObject
{
["ValueDate"] = e.ValueDate.ToString("yyyy-MM-dd"),
["PositionId"] = e.PositionId,
["PosiDividendSum"] = e.PosiDividendSum,
["TdPosiDividend"] = e.TdPosiDividend,
["TdCloseDividend"] = e.TdCloseDividend,
["RealizedDividend"] = e.RealizedDividend,
["PosiQuantity"] = e.PosiQuantity,
["PosiMtmPnL"] = e.PosiMtmPnL
});
}
golden["KeyFloatEodPositions"] = keyArray;
// 完整浮动腿序列(用于分析精度累积过程)
var allArray = new JArray();
foreach (var e in floatEods)
{
allArray.Add(new JObject
{
["ValueDate"] = e.ValueDate.ToString("yyyy-MM-dd"),
["PosiDividendSum"] = e.PosiDividendSum,
["TdPosiDividend"] = e.TdPosiDividend,
["RealizedDividend"] = e.RealizedDividend
});
}
golden["AllFloatEodDividends"] = allArray;
string json = JsonConvert.SerializeObject(golden, Formatting.Indented);
string path = Path.Combine(GoldenDir, $"golden_{TradeNumber}.json");
File.WriteAllText(path, json);
Console.WriteLine($"录制完成: {path}");
Console.WriteLine($"\n关键数据:");
foreach (var e in keyFloatEods)
{
Console.WriteLine($" {e.ValueDate:yyyy-MM-dd}: PosiDividendSum={e.PosiDividendSum}, TdPosiDividend={e.TdPosiDividend}, TdCloseDividend={e.TdCloseDividend}, RealizedDividend={e.RealizedDividend}");
}
var preSwap = keyFloatEods.FirstOrDefault(x => x.ValueDate == swapDate.AddDays(-1));
var swapDay = keyFloatEods.FirstOrDefault(x => x.ValueDate == swapDate);
if (preSwap != null && swapDay != null)
{
decimal expected = preSwap.PosiDividendSum - swapDay.TdCloseDividend;
decimal actual = swapDay.PosiDividendSum;
Console.WriteLine($"\n精度分析:");
Console.WriteLine($" 互换前 PosiDividendSum = {preSwap.PosiDividendSum}");
Console.WriteLine($" 互换实现 TdCloseDividend = {swapDay.TdCloseDividend}");
Console.WriteLine($" 期望 PosiDividendSum = {preSwap.PosiDividendSum} - ({swapDay.TdCloseDividend}) = {expected}");
Console.WriteLine($" 实际 PosiDividendSum = {actual}");
Console.WriteLine($" 差异 = {actual - expected}");
}
}
finally { db?.Dispose(); }
}
#endregion
#region 回放:读 golden 验证精度差异
/// <summary>
/// 回放 golden,验证互换后 PosiDividendSum 的精度差异。
/// 当前代码会失败(差异=0.36),这就是红灯。
/// 修复后(cs:1635 改为递增模式)应通过。
/// </summary>
[TestMethod]
public void Replay_VerifyPrecisionDiff()
{
string sourceDir = Path.Combine(
AppDomain.CurrentDomain.BaseDirectory, "Resources", "GoldenFiles", "GLMS20260105");
if (!Directory.Exists(sourceDir))
{
Assert.Inconclusive($"golden 目录不存在: {sourceDir}(请先跑 Record_FromRealDB");
return;
}
var files = Directory.GetFiles(sourceDir, "*.json");
Assert.IsTrue(files.Length > 0, "应至少有1个golden文件");
var json = File.ReadAllText(files[0]);
var golden = JObject.Parse(json);
var keyEods = golden["KeyFloatEodPositions"] as JArray;
Assert.IsNotNull(keyEods, "KeyFloatEodPositions 缺失");
Assert.IsTrue(keyEods.Count >= 2, "应至少有互换前+互换日两条eod");
var swapDateStr = golden["SwapDate"]?.Value<string>();
Console.WriteLine($"SwapDate = {swapDateStr}");
JObject preSwap = null, swapDay = null;
var swapDate = DateTime.Parse(swapDateStr);
foreach (var e in keyEods)
{
var dateStr = e["ValueDate"]?.Value<string>();
if (dateStr == swapDate.AddDays(-1).ToString("yyyy-MM-dd")) preSwap = e as JObject;
if (dateStr == swapDateStr) swapDay = e as JObject;
}
Assert.IsNotNull(preSwap, "找不到互换前eod");
Assert.IsNotNull(swapDay, "找不到互换日eod");
decimal preDividendSum = preSwap["PosiDividendSum"]!.Value<decimal>();
decimal swapCloseDividend = swapDay["TdCloseDividend"]!.Value<decimal>();
decimal actualDividendSum = swapDay["PosiDividendSum"]!.Value<decimal>();
decimal expectedDividendSum = preDividendSum - swapCloseDividend;
Console.WriteLine($"互换前 PosiDividendSum = {preDividendSum}");
Console.WriteLine($"互换实现 TdCloseDividend = {swapCloseDividend}");
Console.WriteLine($"期望 PosiDividendSum = {preDividendSum} - ({swapCloseDividend}) = {expectedDividendSum}");
Console.WriteLine($"实际 PosiDividendSum = {actualDividendSum}");
Console.WriteLine($"差异 = {actualDividendSum - expectedDividendSum}");
// 核心断言:互换后 PosiDividendSum 应精确 = 前日 - 实现
// 当前代码会失败(差异=0.36),这就是红灯
Assert.AreEqual(expectedDividendSum, actualDividendSum,
$"互换后 PosiDividendSum 应精确={expectedDividendSum},实际={actualDividendSum}" +
$"差异={actualDividendSum - expectedDividendSum}(从头重算 vs 递增的舍入累积)");
}
#endregion
}
}