From 0910969e3f360801ed382b4505f2434f4f5dccff Mon Sep 17 00:00:00 2001 From: hjhan Date: Thu, 2 Jul 2026 10:47:23 +0800 Subject: [PATCH] =?UTF-8?q?fix(swap):=20PosiDividendSum=E7=B2=BE=E5=BA=A6?= =?UTF-8?q?=E5=B7=AE=E5=BC=82=E4=BF=AE=E5=A4=8D(GLMS-20260105-0007)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题:互换后PosiDividendSum=-89999.64(应为-90000),差0.36元。 根因:UpdateEodPosition cs:1635从头重算PosiDividendSum (Round(全程,2)-已实现),与CopyEodPosition逐天递增 (每天Round(增量,2)累积)产生舍入误差累积。 修复:cs:1635改为递增模式 PosiDividendSum = eod.PosiDividendSum + TdPosiDividend - TdCloseDividend 与InterestIncomeSum的递增模式一致,避免从头重算的精度差异。 验证(golden录制/回放TDD): - 真实数据录制GLMS-20260105-0007的eod快照+输入数据 - 回放重新调UpdateEodPosition:修复前=-89999.64(红灯), 修复后=-90000(绿灯,精确匹配) - 121个测试全通过,无回归 --- .../SwapModule/GLMS20260105GoldenTest.cs | 124 ++- .../golden_GLMS-20260105-0007.json | 888 +++++++++++++++++- .../SwapModule/SwapEodPositionService.cs | 10 +- 3 files changed, 982 insertions(+), 40 deletions(-) diff --git a/UnitTestProject/Modules/SwapModule/GLMS20260105GoldenTest.cs b/UnitTestProject/Modules/SwapModule/GLMS20260105GoldenTest.cs index b694f6fa..b70cdb0a 100644 --- a/UnitTestProject/Modules/SwapModule/GLMS20260105GoldenTest.cs +++ b/UnitTestProject/Modules/SwapModule/GLMS20260105GoldenTest.cs @@ -58,6 +58,11 @@ namespace YLErp.Modules.SwapModule var keyDates = new[] { swapDate.AddDays(-1), swapDate, swapDate.AddDays(1) }; var keyFloatEods = floatEods.Where(x => keyDates.Contains(x.ValueDate)).ToList(); + // 录制 UpdateEodPosition 的输入(互换前日eod + 互换flow_event + 持仓 + 交易) + var preSwapEod = floatEods.FirstOrDefault(x => x.ValueDate == swapDate.AddDays(-1)); + var swapFlowEvents = flows.Where(x => x.EventDate == swapDate && x.PositionId == preSwapEod?.PositionId).ToList(); + var swapPosition = positions.FirstOrDefault(x => x.id == preSwapEod?.PositionId); + var golden = new JObject { ["TradeNumber"] = TradeNumber, @@ -98,6 +103,19 @@ namespace YLErp.Modules.SwapModule } golden["AllFloatEodDividends"] = allArray; + // 录制回放所需的输入数据(用于重新调 UpdateEodPosition) + if (preSwapEod != null && swapPosition != null) + { + var settings = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; + golden["ReplayInput"] = new JObject + { + ["Trade"] = JObject.FromObject(td, JsonSerializer.Create(settings)), + ["Position"] = JObject.FromObject(swapPosition, JsonSerializer.Create(settings)), + ["PreSwapEod"] = JObject.FromObject(preSwapEod, JsonSerializer.Create(settings)), + ["SwapFlowEvents"] = JArray.FromObject(swapFlowEvents, JsonSerializer.Create(settings)) + }; + } + string json = JsonConvert.SerializeObject(golden, Formatting.Indented); string path = Path.Combine(GoldenDir, $"golden_{TradeNumber}.json"); File.WriteAllText(path, json); @@ -131,9 +149,10 @@ namespace YLErp.Modules.SwapModule #region 回放:读 golden 验证精度差异 /// - /// 回放 golden,验证互换后 PosiDividendSum 的精度差异。 - /// 当前代码会失败(差异=0.36),这就是红灯。 - /// 修复后(cs:1635 改为递增模式)应通过。 + /// 回放 golden:用真实数据重新调 UpdateEodPosition,验证修复后 PosiDividendSum 正确。 + /// + /// 红灯(修复前):从头重算产生 0.36 差异 + /// 绿灯(修复后):递增模式,PosiDividendSum = 前日 + 新计 - 实现 = -90000 /// [TestMethod] public void Replay_VerifyPrecisionDiff() @@ -152,41 +171,82 @@ namespace YLErp.Modules.SwapModule 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(); - Console.WriteLine($"SwapDate = {swapDateStr}"); - - JObject preSwap = null, swapDay = null; - var swapDate = DateTime.Parse(swapDateStr); - foreach (var e in keyEods) + var replayInput = golden["ReplayInput"]; + if (replayInput == null) { - var dateStr = e["ValueDate"]?.Value(); - if (dateStr == swapDate.AddDays(-1).ToString("yyyy-MM-dd")) preSwap = e as JObject; - if (dateStr == swapDateStr) swapDay = e as JObject; + Assert.Inconclusive("golden 缺少 ReplayInput(请重新录制)"); + return; } - Assert.IsNotNull(preSwap, "找不到互换前eod"); - Assert.IsNotNull(swapDay, "找不到互换日eod"); + var settings = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; + var td = replayInput["Trade"]!.ToObject(JsonSerializer.Create(settings)); + var position = replayInput["Position"]!.ToObject(JsonSerializer.Create(settings)); + var preSwapEod = replayInput["PreSwapEod"]!.ToObject(JsonSerializer.Create(settings)); + var swapFlowEvents = replayInput["SwapFlowEvents"]!.ToObject>(JsonSerializer.Create(settings)); - decimal preDividendSum = preSwap["PosiDividendSum"]!.Value(); - decimal swapCloseDividend = swapDay["TdCloseDividend"]!.Value(); - decimal actualDividendSum = swapDay["PosiDividendSum"]!.Value(); - decimal expectedDividendSum = preDividendSum - swapCloseDividend; + var swapDateStr = golden["SwapDate"]!.Value(); + var swapDate = DateTime.Parse(swapDateStr); + Console.WriteLine($"SwapDate = {swapDateStr}"); + Console.WriteLine($"互换前 PosiDividendSum = {preSwapEod.PosiDividendSum}"); + Console.WriteLine($"互换 DividendIn = {string.Join(",", swapFlowEvents.Select(x => x.DividendIn))}"); - Console.WriteLine($"互换前 PosiDividendSum = {preDividendSum}"); - Console.WriteLine($"互换实现 TdCloseDividend = {swapCloseDividend}"); - Console.WriteLine($"期望 PosiDividendSum = {preDividendSum} - ({swapCloseDividend}) = {expectedDividendSum}"); - Console.WriteLine($"实际 PosiDividendSum = {actualDividendSum}"); - Console.WriteLine($"差异 = {actualDividendSum - expectedDividendSum}"); + // 用修复后的代码重新调 UpdateEodPosition + var service = new ReplayStubService(preSwapEod.UnderlyingCode); + var result = service.ExecuteUpdateEodPosition( + position, preSwapEod, td, swapDate, swapDate.AddDays(-1), swapFlowEvents); - // 核心断言:互换后 PosiDividendSum 应精确 = 前日 - 实现 - // 当前代码会失败(差异=0.36),这就是红灯 - Assert.AreEqual(expectedDividendSum, actualDividendSum, - $"互换后 PosiDividendSum 应精确={expectedDividendSum},实际={actualDividendSum}," + - $"差异={actualDividendSum - expectedDividendSum}(从头重算 vs 递增的舍入累积)"); + // 期望:PosiDividendSum = 前日 + 当天新计 - 实现 + decimal expected = preSwapEod.PosiDividendSum + result.TdPosiDividend - result.TdCloseDividend; + Console.WriteLine($"\n修复后结果:"); + Console.WriteLine($" TdPosiDividend = {result.TdPosiDividend}"); + Console.WriteLine($" TdCloseDividend = {result.TdCloseDividend}"); + Console.WriteLine($" PosiDividendSum = {result.PosiDividendSum}"); + Console.WriteLine($" 期望 = {preSwapEod.PosiDividendSum} + {result.TdPosiDividend} - ({result.TdCloseDividend}) = {expected}"); + + Assert.AreEqual(expected, result.PosiDividendSum, + $"修复后 PosiDividendSum 应=前日+新计-实现={expected},实际={result.PosiDividendSum}"); + Console.WriteLine($"\n✅ 修复验证通过:PosiDividendSum={result.PosiDividendSum} = {expected}"); + } + + #endregion + + #region 回放用 Stub + + private sealed class ReplayStubService : SwapEodPositionService + { + private readonly string _underlyingCode; + public ReplayStubService(string underlyingCode) : base(new OptUserInfo(0, "Replay", OptUserFrom.UnitTest)) + { + _underlyingCode = underlyingCode; + } + + protected override underlying_manager GetUnderlyingData(string underlyingCode) + { + // 返回最小可用数据(增值税=0) + return new underlying_manager { ValueAddedTax = 0m }; + } + + protected override decimal GetUnderlyingPrice(string code, DateTime settleDate, out decimal vobp) + { + vobp = 0m; + return 1.01m; // 固定价格 + } + + protected override decimal CalcBondPayment(string underlyingCode, DateTime fromDate, DateTime toDate, decimal qty, int shortRatio, int directionRatio) + { + // 返回0:互换日的 TdPosiDividend=0(无新增分红),聚焦验证 PosiDividendSum 的递增逻辑 + return 0m; + } + + protected override void SaveAllChanges() { } + protected override double GetCurrencyRate(string q, string s, DateTime d, bool p, CurrencyRateType t) => 1.0; + + public eod_swap_position ExecuteUpdateEodPosition( + swap_position swapPosition, eod_swap_position eod, trade td, + DateTime valueDate, DateTime preSettleDate, List unwindEvents) + { + return UpdateEodPosition(swapPosition, eod, null, td, valueDate, preSettleDate, unwindEvents); + } } #endregion diff --git a/UnitTestProject/Resources/GoldenFiles/GLMS20260105/golden_GLMS-20260105-0007.json b/UnitTestProject/Resources/GoldenFiles/GLMS20260105/golden_GLMS-20260105-0007.json index cc5d8ee7..c617a192 100644 --- a/UnitTestProject/Resources/GoldenFiles/GLMS20260105/golden_GLMS-20260105-0007.json +++ b/UnitTestProject/Resources/GoldenFiles/GLMS20260105/golden_GLMS-20260105-0007.json @@ -390,5 +390,891 @@ "TdPosiDividend": 0.0, "RealizedDividend": -400.000000 } - ] + ], + "ReplayInput": { + "Trade": { + "SubTrades": null, + "YesterDayTradeSavedVol": null, + "PV": null, + "RealizedPnl": null, + "PositionPnl": null, + "MaturityWorkDay": 124, + "PairTradeNumber": null, + "ShowNotional": -50000000.0, + "MaturityDay": 182, + "TradeOpenVolatilityString": "0.00%", + "InitialSpotPriceOriginal": null, + "TradeOriginalAmount": null, + "ExerciseDateString": "2026-12-31", + "ActualStrike": null, + "StrikeString": "0.000000000", + "TradeSinglePriceString": "0.00%", + "TradeDateString": "2026-01-05", + "LotsNewInfo": null, + "UnWindPrice": null, + "StockEqvNotionalToShow": 50500000.0, + "SettlementTypeDesc": "收盘价", + "SettlementDesc": null, + "VarietyId": null, + "SyntheticUnderlyingTipsInfo": null, + "StrikeToShow": "0.000000000", + "CurNotional": 0.0, + "LastDayNotional": 0.0, + "Amount": null, + "ContractCode": null, + "InitialMarginRatio": null, + "UnderlyingPrice": null, + "UnderlyingName": null, + "DeltaInLots": null, + "ValueStatus": null, + "UnWindUnderlyingPrice": null, + "UnWindTradePrice": null, + "UnWindTotalAmount": null, + "UnWindFee": null, + "UnWindTimes": 0, + "KnockInOutStatusObservation": null, + "DividendRatio": 0.0, + "InitYtm": 0.025000, + "CalcId": null, + "TTMDays": null, + "UnderlyingInstrumentTypeCn": "利率债", + "ExerciseModeCn": "", + "ClientNumber": null, + "SummaryType": "收益互换", + "QuoteUnitSingle": null, + "QuoteUnit": null, + "trade_forward": { + "id": 0, + "TradeId": 0, + "OpenCommission": 0.0, + "AnnualMarginRate": 0.0, + "AnnualStoragePrice": 0.0, + "ForwardValue": 0.0, + "ObservationStart": null, + "ObservationDates": null, + "OptId": null, + "OptName": null, + "OptDate": null, + "EncryptId": "93JNozbumCxdv7dwztjpOg" + }, + "trade_asian_option": { + "StrikeType": null, + "PayoffType": null, + "SettleMode": null, + "StrikeGearingFactor": 1.0, + "AveragingPeriodStartDate": null, + "ObservationDates": null, + "EnhancedPrice": 0.0, + "StrikeTypeCn": "", + "PayoffTypeCn": "", + "Fixings": null, + "id": 0, + "TradeId": 0, + "OptId": null, + "OptName": null, + "OptDate": null, + "EncryptId": "93JNozbumCxdv7dwztjpOg" + }, + "trade_rainbow_option": { + "UnderlyingAssetCode": null, + "Strike": null, + "UnderlyingMaturityDate": null, + "UnderlyingId1": null, + "UnderlyingId2": null, + "Strike2": null, + "SpotPrice1": null, + "SpotPrice2": null, + "RainbowType": null, + "UnderlyingAssetCode2": null, + "CorRelation": null, + "CashAmount": null, + "Vol2": 0.0, + "UnderlyingCodes": [ + null, + null + ], + "Strikes": [ + 0.0, + 0.0 + ], + "SpotPrices": [ + 0.0, + 0.0 + ], + "id": 0, + "TradeId": 0, + "OptId": null, + "OptName": null, + "OptDate": null, + "EncryptId": "93JNozbumCxdv7dwztjpOg" + }, + "trade_barrier_option": { + "KnockInOutStatus": null, + "KnockInOutStatusCn": "观察中", + "KnockInOutDate": null, + "KnockInOutNotional": null, + "BarrierShift": null, + "BarrierTypeEn": null, + "IsDiscrete": false, + "BarrierPrice": null, + "UpperBarrierPrice": null, + "Discrete": null, + "BarrierType": null, + "Rebate": null, + "RebateRate": null, + "RebateHigh": null, + "RebateHighRate": null, + "RebateAnnualizedAtKO": false, + "RebateDayCount": null, + "ObservationDates": null, + "LatestObservationDate": null, + "RebateType": null, + "RebateTypeCn": "", + "id": 0, + "TradeId": 0, + "OptId": null, + "OptName": null, + "OptDate": null, + "EncryptId": "93JNozbumCxdv7dwztjpOg" + }, + "trade_risky_option": { + "Strike1": null, + "Strike2": null, + "Strike3": null, + "ParticipationRate1": null, + "ParticipationRate2": null, + "ParticipationRate3": null, + "id": 0, + "TradeId": 0, + "OptId": null, + "OptName": null, + "OptDate": null, + "EncryptId": "93JNozbumCxdv7dwztjpOg" + }, + "trade_binary_option": { + "RebateType": null, + "RebateTypeCn": "", + "CashOrNothingAmount": null, + "CashOrNothingAmountHigh": null, + "CashOrNothingAmountRate": null, + "CashOrNothingAmountHighRate": null, + "PayoffType": "", + "UpperBarrier": null, + "RebateDayCount": null, + "RebateAnnualizedAtKO": false, + "ObservationDates": null, + "LatestObservationDate": null, + "UpperBarrierRelative": "0", + "MonitorType": null, + "IsDiscreteMonitored": false, + "Offset": null, + "id": 0, + "TradeId": 0, + "OptId": null, + "OptName": null, + "OptDate": null, + "EncryptId": "93JNozbumCxdv7dwztjpOg" + }, + "trade_spread_option": { + "UnderlyingMaturityDate2": null, + "UnderlyingMaturityDate3": null, + "UnderlyingMaturityDate4": null, + "Payoff": "S1-S2", + "UnderlyingId1": null, + "UnderlyingId2": null, + "UnderlyingId3": null, + "UnderlyingId4": null, + "UnderlyingAssetClass1": null, + "UnderlyingAssetClass2": null, + "UnderlyingAssetClass3": null, + "UnderlyingAssetClass4": null, + "UnderlyingAssetCode1": null, + "UnderlyingAssetCode2": null, + "UnderlyingAssetCode3": null, + "UnderlyingAssetCode4": null, + "UnderlyingAssetName1": null, + "UnderlyingAssetName2": null, + "UnderlyingAssetName3": null, + "UnderlyingAssetName4": null, + "TradeCloseVolatility1": null, + "TradeCloseVolatility2": null, + "TradeCloseVolatility3": null, + "TradeCloseVolatility4": null, + "TradeOpenVolatility1": null, + "TradeOpenVolatility2": null, + "TradeOpenVolatility3": null, + "TradeOpenVolatility4": null, + "NumOfSmoothingDays1": null, + "NumOfSmoothingDays2": null, + "NumOfSmoothingDays3": null, + "NumOfSmoothingDays4": null, + "Vol1": null, + "Vol2": null, + "Vol3": null, + "Vol4": null, + "Correlation12": null, + "Correlation13": null, + "Correlation14": null, + "Correlation23": null, + "Correlation24": null, + "Correlation34": null, + "id": 0, + "TradeId": 0, + "OptId": null, + "OptName": null, + "OptDate": null, + "EncryptId": "93JNozbumCxdv7dwztjpOg" + }, + "trade_double_sharkfin_option": { + "KnockInOutStatus": null, + "KnockInOutStatusCn": "观察中", + "KnockInOutDate": null, + "IsDiscrete": false, + "BarrierHigh": 0.0, + "BarrierLow": 0.0, + "StrikeHigh": null, + "StrikeLow": null, + "CallParticipationRate": null, + "PutParticipationRate": null, + "Discrete": null, + "Rebate": null, + "RebateRate": null, + "RebateHigh": null, + "RebateHighRate": null, + "RebateType": null, + "ObservationDates": null, + "LatestObservationDate": null, + "id": 0, + "TradeId": 0, + "OptId": null, + "OptName": null, + "OptDate": null, + "EncryptId": "93JNozbumCxdv7dwztjpOg" + }, + "trade_autocall": { + "KnockInOutStatus": null, + "KnockInOutStatusCn": "观察中", + "KnockInOutDate": null, + "KOBarrier": 0.0, + "KIBarrier": 0.0, + "CouponBarrier": 0.0, + "Coupon": 0.0, + "IsFixedCoupon": false, + "CouponPayType": 0, + "SpreadStrike1": null, + "SpreadStrike": null, + "CouponPayAtMaturity": false, + "IncludeCouponAfterKI": false, + "KOObservationDates": null, + "CouponObservation": null, + "LatestKOObservationDate": null, + "LatestKOBarrier": null, + "ObservationDates": null, + "LatestObservationDate": null, + "IsAnnualized2": false, + "AnnualizeFactor2": null, + "CouponDayCount": "Act365", + "HappenedObservations": null, + "KIPayoffType": 0, + "CouponIncludeStartDate": null, + "CouponUsePaymentDate": null, + "id": 0, + "TradeId": 0, + "OptId": null, + "OptName": null, + "OptDate": null, + "EncryptId": "93JNozbumCxdv7dwztjpOg" + }, + "trade_snowball": { + "IsInitialKnockedIn": false, + "KnockInOutStatus": null, + "KnockInOutStatusCn": "观察中", + "KnockInOutDate": null, + "KOBarrier": 0.0, + "KIBarrier": 0.0, + "Coupon": 0.0, + "KOObservationDates": null, + "KOObservationSettleDates": null, + "LatestKOObservationDate": null, + "LatestKOBarrier": null, + "ObservationDates": null, + "LatestObservationDate": null, + "KORebate": 0.0, + "KORebateType": 0, + "IsFixedCoupon": false, + "SpreadStrikeAtKO": null, + "SpreadStrikeAtKO1": null, + "SpreadStrikeAtMaturity1": null, + "SpreadStrikeAtMaturity": null, + "KOPayoffType": 0, + "KIPayoffType": 0, + "AnnualizedPremiumRate": null, + "IsAnnualized2": false, + "AnnualizeFactor2": null, + "InitialAdvance": null, + "PeriodAdvance": null, + "FontEarning": null, + "ConfirmedLine": null, + "ConfirmedFloor": null, + "CouponDayCount": null, + "PrepaymentUsed": false, + "PrepaymentRatio": null, + "PrepaymentInterestRate": null, + "PrepaymentConvertCashRate": null, + "CouponIncludeEndDate": null, + "EnhancedParticipationRate": null, + "KIParticipationRate": null, + "PrincipalProtectionRate": null, + "KIObservationType": 0, + "KOBarrierAdjustStep": 0.0, + "CouponIncludeStartDate": null, + "CouponUsePaymentDate": null, + "id": 0, + "TradeId": 0, + "OptId": null, + "OptName": null, + "OptDate": null, + "EncryptId": "93JNozbumCxdv7dwztjpOg" + }, + "trade_rangeaccrual": { + "LowerRange": 0.0, + "UpperRange": 0.0, + "BonusRate": 0.0, + "ObservationDates": null, + "LatestObservationDate": null, + "HappenedObservations": null, + "id": 0, + "TradeId": 0, + "OptId": null, + "OptName": null, + "OptDate": null, + "EncryptId": "93JNozbumCxdv7dwztjpOg" + }, + "trade_airbag": { + "KnockInOutStatus": null, + "KnockInOutStatusCn": "观察中", + "KnockInOutDate": null, + "KnockInOutNotional": null, + "HighStrike": 0.0, + "HasPayoffLimit": false, + "Barrier": 0.0, + "KIParticipationRate": 0.0, + "NotKIParticipationRate": null, + "IsDiscreteMonitored": false, + "id": 0, + "TradeId": 0, + "OptId": null, + "OptName": null, + "OptDate": null, + "EncryptId": "93JNozbumCxdv7dwztjpOg" + }, + "trade_accumulator_option": { + "KOBarrier": null, + "KOObservationDates": null, + "KOObservationSettleDates": null, + "AccumuTradeAmount": 0.0, + "OriginalAccumuTradeAmount": 0.0, + "AccumuType": "", + "PayoffType": null, + "Coupon": 0.0, + "CouponPercent": false, + "IsFixedCoupon": false, + "CouponDayCount": null, + "PutMultiplier": 1.0, + "CallMultiplier": 1.0, + "EarlyTerminate": false, + "SettlementMode": null, + "SettlementMode2": null, + "SettlementMode3": null, + "KnockOutDate": null, + "ForwardDateType": null, + "ForwardPriceType": null, + "AccumulatorStructureType": 0, + "Coupon2": null, + "Strike2": null, + "Strike3": null, + "Multiplier": null, + "Multiplier2": null, + "Multiplier3": null, + "KnockInOutStatusCn": "观察中", + "LatestObservationDate": null, + "StrikeGearingFactor": 1.0, + "LatestKOBarrier": null, + "latestCoupon": null, + "ForwardPriceType2": null, + "ForwardDateType2": null, + "ForwardPriceType3": null, + "ForwardDateType3": null, + "id": 0, + "TradeId": 0, + "OptId": null, + "OptName": null, + "OptDate": null, + "EncryptId": "93JNozbumCxdv7dwztjpOg" + }, + "trade_swap": { + "AnnualDays": null, + "IsGetFloatingProfit": false, + "GetVarietyId": null, + "GetUnderlyingId": null, + "GetUnderlyingCode": null, + "GetLongShort": null, + "GetSpotPrice": null, + "GetOpenPrice": null, + "GetTradePrice": null, + "GetSwapTimeAndRate": null, + "GetSwapRate": 0.0, + "GetFixedProfit": null, + "GetSingleFee": null, + "GetUnAnnualRate": null, + "GetMarginRate": 0.0, + "GetFinalPrice": null, + "GetNotional": null, + "GetTradeAmount": null, + "GetLot": null, + "IsPayFloatingProfit": false, + "PayVarietyId": null, + "PayUnderlyingId": null, + "PayUnderlyingCode": null, + "PayLongShort": null, + "PaySpotPrice": null, + "PayOpenPrice": null, + "PayTradePrice": null, + "PaySwapTimeAndRate": null, + "PaySwapRate": 0.0, + "PayFixedProfit": null, + "PaySingleFee": null, + "RateCalcMode": "11", + "IncludeFirstDay": true, + "PayUnAnnualRate": null, + "PayMarginRate": 0.0, + "PayFinalPrice": null, + "PayNotional": null, + "PayTradeAmount": null, + "PayLot": null, + "FlowId": null, + "AnnualVarIncome": false, + "SwapType": null, + "SettlementPayType": 0, + "IsTradePriceWhenOpen": false, + "IsShare": false, + "OriginalTradeId": null, + "GetCountRatio": 0.0, + "PayCountRatio": 0.0, + "GetContractSize": 0.0, + "PayContractSize": 0.0, + "id": 0, + "TradeId": 0, + "OptId": null, + "OptName": null, + "OptDate": null, + "EncryptId": "93JNozbumCxdv7dwztjpOg" + }, + "get_trade_swap_details": [], + "pay_trade_swap_details": [], + "trade_underlying_enhance": { + "AnnualizedEnhanceRate": 0.0, + "id": 0, + "TradeId": 0, + "OptId": null, + "OptName": null, + "OptDate": null, + "EncryptId": "93JNozbumCxdv7dwztjpOg" + }, + "trade_cashflow": { + "ProfitRate": 0.0, + "RateType": 0, + "DepositType": 0, + "PrepayRatio": 0.0, + "ProfitDayCount": "Act365", + "id": 0, + "TradeId": 0, + "OptId": null, + "OptName": null, + "OptDate": null, + "EncryptId": "93JNozbumCxdv7dwztjpOg" + }, + "trade_custom": { + "ObservationDates": null, + "LatestObservationDate": null, + "id": 0, + "TradeId": 0, + "OptId": null, + "OptName": null, + "OptDate": null, + "EncryptId": "93JNozbumCxdv7dwztjpOg" + }, + "trade_extend": { + "TradeId": 0, + "ExtendJson": null, + "CompanyJson": null, + "ExtendObj": { + "FlowBookMode": 0, + "FloatingPnlAnnualized": false, + "NeedOpenFee": false, + "OpenFeeType": 0, + "AnnualDays": 365, + "InterestCalcMode": "11", + "Direction": 0, + "SettlementRules": 0, + "DividendPayDate": 1 + } + }, + "swap_positions": [], + "inital_eod_swap_positions": [], + "eod_swap_positions": [], + "trade_Initial_Margin": { + "TradeId": 0, + "Direction": 1, + "MarginType": 0, + "MarginValue": 0.0 + }, + "swap_Flow_Events": [], + "swap_Events": [], + "eod_swaps": [], + "ClientCashInCashOutList": [], + "SalesCommission": null, + "trade_cash": null, + "RemarkInfo": "到期付息(手动付息后次日部分平仓)", + "eod_trade": null, + "SettlementAmount": null, + "ActualExerciseDate": null, + "trade_swap_gj": { + "LongShort": null, + "OpenPrice": null, + "OpenCurrencyRate": null, + "TradeCommission": null, + "PricingOriginalStockEqvNotional": null, + "PricingStockEqvNotional": null, + "SwapTimeAndRate": null + }, + "Tags": null, + "OutputTags": null, + "SaveExt": null, + "TradeType": "收益互换", + "TradeSavedVol": null, + "CreatorId": null, + "CreatorName": null, + "CreateDate": "2026-07-01T20:58:14", + "PairTrade": "", + "TradeSinglePrice": null, + "GroupId": null, + "GroupName": "", + "NumOfSmoothingDays": null, + "ParentTradeId": 0, + "TradeCloseVolatility": null, + "TradeOpenVolatility": null, + "UnWindDate": "2026-03-03T00:00:00", + "UnWindNotional": null, + "CheckStatus": 0, + "CheckTradeUpdate": 1, + "InitialSpotPrice": 1.000004, + "IsMoneynessOption": null, + "TradeAmount": 50000000.0, + "TradeUnit": "", + "StockEqvNotional": 50500000.0, + "StockEqvNotionalMax": null, + "StockEqvNotionalReal": 50500000.0, + "VolType": null, + "UnderlyingInstrumentType": "TBonds", + "ExerciseDate": "2026-12-31T00:00:00", + "TraderName": "唐善洁", + "TraderId": 164, + "Vol": null, + "Day1Pnl": null, + "MidVol": null, + "ExchangeRate": null, + "Strike": null, + "UnderlyingId": 140677, + "BasisUnderlyingId": null, + "BasisUnderlyingCode": null, + "BasisGap": null, + "ExchangeOptionCode": null, + "AssetBookName": "测试簿记", + "AssetId": 40, + "Notional": 50000000.0, + "OptionType": null, + "PricingModel": null, + "ExerciseMode": null, + "NoRiskRate": null, + "SpotPrice": 1.000004, + "TradeNumber": "GLMS-20260105-0007", + "ClientId": 43, + "ClientName": "善洁测试用产品二号", + "UnderlyingCode": "230004.IB", + "UnderlyingAssetClass": "其他市场债券", + "TradeDate": "2026-01-05T00:00:00", + "BuySell": "卖出", + "StartDate": "2026-01-05T00:00:00", + "MaturityDate": null, + "ObservationDateStr": null, + "TradePrice": 0.0, + "AccurateTradePrice": null, + "TradeStatus": "确认成交", + "ProcessStatus": "通过审批", + "ProcessOrderId": -2, + "ProcessOrderBranch": 0, + "ProcessOptDate": "2026-07-01T23:36:20", + "SettlementType": 0, + "ValidState": "Valid", + "Comments": "到期付息(手动付息后次日部分平仓)", + "SentMailCount": null, + "Lots": null, + "HasPartialUnWind": 1, + "OriginalNotional": 0.0, + "TradeSource": "系统交易", + "OriginalStockEqvNotional": 50500000.0, + "OriginalStockEqvNotionalV2": 50500000.0, + "IsUsePremiumRate": true, + "IsTradePricePayType": true, + "IsAnnualized": false, + "AnnualizeFactor": null, + "DividendRate": null, + "ParticipationRate": 1.0, + "PrincipalRate": null, + "OriginalPrincipalSum": null, + "PrincipalRateWrite": null, + "SinglePrincipalWrite": null, + "SettlementDate": null, + "SettlementFlag": 0, + "SettlementFlagStr": "否", + "SettlementFlagDate": null, + "SettlementFlagOptId": null, + "SettlementFlagOptName": null, + "CalcFlag": 0, + "IsSingleContract": null, + "PremiumPayDate": null, + "PremiumRate": null, + "UnderlyingAssetName": "23附息国债04", + "UnderlyingVariety": null, + "ContractVersion": null, + "FinalPrice": null, + "OpponentRole": "乙方", + "DurationDays": null, + "StructureType": "普通债券类收益互换", + "InitialMargin": 0.0, + "StructureIntroduction": null, + "MarginTemplateName": "系统默认", + "MarginType": 0, + "MarginRate": 0.0, + "PositionMarginRate": 0.0, + "QuoteCurrency": null, + "SettlementCurrency": null, + "IsGroup": 0, + "ExtendInfo": null, + "IsNight": false, + "Propertys": [], + "ExtendOptionTypeInfo": null, + "AccumulatorOptionId": null, + "OptId": 164, + "OptName": "唐善洁", + "OptDate": "2026-07-01T23:36:20", + "IsApproval": false, + "IsAutoGenerate": null, + "HTStatus": null, + "Warning": false, + "DividendDate": "2000-01-01T00:00:00", + "CountRatio": 1, + "CallPut": "", + "IsMoneynessOptionData": false, + "MetaDic": {}, + "TradeExtendJson": null, + "TradeMultipleType": "收益互换", + "Fixing": null, + "id": 1924, + "EncryptId": "LKPDwn1OC-Vzkg-aD-cnDA" + }, + "Position": { + "PositionId": 0, + "SwapTradeId": 1924, + "PosiDirection": 2, + "PositionType": 1, + "UnderlyingCode": "230004.IB", + "underlying": null, + "CountRatio": 1.000000, + "ContractSize": 1.000000, + "PosiNetPrice": 1.01000400000, + "PosiGrossPrice": 1.01000000000, + "PosiNetFeePrice": 1.00000400000, + "PosiNetNoFeePrice": 1.00000000000, + "PosiNotionalValue": 50500000.000000, + "PosiQuantity": 50000000.000000, + "PosiTradingFee": 0.0, + "PosiTradingFeeUnit": 0.0, + "PosiStartDate": "2026-01-05T00:00:00", + "PosiMatuirityDate": "2026-12-31T00:00:00", + "PosiDividendIncome": 0.0, + "PosiTradingFeePending": 200.000000, + "InterestDirection": 0, + "InterestRateDefault": 0.0, + "FloatRate": 0.0, + "FloatRateUnderlyingCode": null, + "InterestMode": 0, + "InterestModeStr": "Unknown", + "InterestType": 0, + "InterestPrincipalFix": 0.0, + "InterestSwapInterval": null, + "InterestFeePending": 0.0, + "InterestAmount": 0.0, + "IsInitial": true, + "IsAnnualized": false, + "HappenDate": null, + "Currency": "CNY", + "PosiNumber": "GLMS-20260105-0007-35148", + "UnderlyingInstrumentType": "TBonds", + "InitYtm": 0.025000, + "Invalid": false, + "interest_rest_days": 7, + "interest_rule": null, + "SwapIntervalList": [], + "Obervation": null, + "PositionIdPadding": "00000000", + "id": 35148, + "EncryptId": "bwG1iqm2wVDuqrh3JW4jCg", + "OptId": 164, + "OptName": "唐善洁", + "OptTime": "2026-07-01T21:20:32" + }, + "PreSwapEod": { + "ValueDate": "2026-03-02T00:00:00", + "ClientId": 43, + "SwapTradeId": 1924, + "PositionId": 35148, + "PosiDirection": 2, + "PosiDirectionStr": "支付", + "PositionType": 1, + "PositionTypeStr": "B", + "UnderlyingCode": "230004.IB", + "UnderlyingName": null, + "ContractSize": 1.000000, + "CountRatio": 1.000000, + "PosiNetPrice": 1.01000400000, + "PosiGrossPrice": 1.01000000000, + "PosiQuantity": 50000000.000000, + "PosiNotionalValue": 50500000.000000, + "PosiTradingFee": 0.0, + "PosiStartDate": "2026-01-05T00:00:00", + "PosiMatuirityDate": "2026-12-31T00:00:00", + "UnderlyingPrice": 1.02000000000, + "UnderlyingMarketValue": 51000000.000000, + "TdPosiDividend": -90400.000000, + "PosiMtmPnL": -500000.000000, + "PosiDividendSum": -90400.000000, + "PosiFeePending": 200.000000, + "PosiProfitSum": -590200.000000, + "InterestDirection": 0, + "InterestMode": 0, + "InterestType": 0, + "InterestPrincipalFix": 0.0, + "InterestRateDefault": 0.0, + "InterestSwapInterval": null, + "InterestFeePending": 0.0, + "TdInterestPrincipal": 0.0, + "TdInterestRate": 0.0, + "TdInterestIncome": 0.0, + "TdInterestFee": 0.0, + "InterestIncomeSum": 0.0, + "InterestFeeSum": 0.0, + "InterestProfitSum": 0.0, + "SwapPositionValue": -590200.000000, + "TdCloseQty": 0.0, + "TdChangedQty": 0.0, + "TdCloseMtmPnl": 0.0, + "TdCloseDividend": 0.0, + "TdCloseFee": 0.0, + "TdCloseInterest": 0.0, + "TdCloseInterestFee": 0.0, + "RealizedMtmPnL": 0.0, + "RealizedDividend": 0.0, + "RealizedFee": 0.0, + "RealizedInterest": 0.0, + "RealizedInterestFee": 0.0, + "RealizedPnl": 0.0, + "TdCurrency": 1.000000, + "PosiStatus": 0, + "Invalid": false, + "FloatRate": 0.0, + "FloatRateUnderlyingCode": null, + "IsAnnualized": false, + "HappenDate": null, + "Currency": null, + "UnderlyingInstrumentType": "TBonds", + "PosiNetFeePrice": 1.00000400000, + "PosiNetNoFeePrice": 1.00000000000, + "VTradingFee": 400.000000, + "interest_rest_days": null, + "interest_rule": null, + "dv01": 0.0, + "PositionIdPadding": "00035148", + "id": 403417, + "EncryptId": "2LJcLhCf_19_0FHneAgs2Q", + "OptId": 164, + "OptName": "唐善洁", + "OptTime": "2026-07-02T00:17:55" + }, + "SwapFlowEvents": [ + { + "EventDate": "2026-03-03T00:00:00", + "SwapTradeId": 1924, + "SwapTradeNo": null, + "SwapPositionIdPadding": "00035148", + "EventType": 3, + "EventTypeStr": "互换", + "EventReason": "交易", + "PayDirection": 2, + "PayDirectionStr": "支付", + "PositionType": 1, + "PositionTypeStr": "B", + "UnderlyingCode": "230004.IB", + "MatuirityDate": null, + "Quantity": 0.0, + "PositionQty": 0.0, + "CountRatio": 0.0, + "ContractSize": 1.000000, + "PosiNetPrice": 0.0, + "PosiGrossPrice": 0.0, + "TradingAmount": 51005202.000000, + "TradingAmountAvg": 1.01000400000, + "TradingAmountFeeAvg": 1.01000400000, + "TradingFee": 0.0, + "TradingFeePending": 0.0, + "DividendPending": 0.0, + "MarkClosePnl": 0.0, + "DividendIn": -400.000000, + "CloseFee": 0.0, + "BeforeCloseFee": 0.0, + "PositionId": 35148, + "DataState": 100, + "ClientCashId": 0, + "InterestDirection": 0, + "InterestDirectionStr": "", + "InterestMode": 0, + "UnderlyingInstrumentType": "TBonds", + "InterestModeStr": "", + "InterestPrincipal": 0.0, + "InterestRate": 0.0, + "InterestSwapInterval": null, + "InterestAmount": 0.0, + "TdInterestAmount": 0.0, + "InterestFee": 0.0, + "InterestClosePnL": 0.0, + "EventId": 20929, + "ClientId": 43, + "FloatRate": null, + "PayDate": "2026-03-03T00:00:00", + "TradingAmountNetFeeAvg": 1.00000400000, + "TradingAmountNetAvg": 1.00000000000, + "UnwindDate": "2026-03-03T00:00:00", + "OptLog": null, + "SwapIntervalList": [], + "FloatPnlSum": -400.000000, + "InitYtm": null, + "id": 16219, + "EncryptId": "63o4MwWpLrn3ImiMTmCuXQ", + "OptId": 0, + "OptName": null, + "OptTime": "0001-01-01T00:00:00" + } + ] + } } \ No newline at end of file diff --git a/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs b/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs index e2864304..454b5a5c 100644 --- a/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs +++ b/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs @@ -1624,15 +1624,11 @@ namespace YLErp.Modules.SwapModule curretEod.RealizedDividend = curretEod.RealizedDividend + curretEod.TdCloseDividend; - // 分红与互换解耦:持仓>0时从起始日重算待实现分红,不再受互换事件影响 - // 修改,互换事件会影响待实现的分红的,现在要算上 + // 分红与互换解耦:持仓>0时待实现分红用递增模式(前日+当天新计-当天实现), + // 与 CopyEodPosition 的逐天递增口径一致,避免从头重算的舍入累积差异。 if (curretEod.PosiQuantity > 0) { - //decimal tax = um.ValueAddedTax ?? 0; - //BondPaymentService bondPaymentService = new BondPaymentService(UserInfo); - //decimal totalPayment = bondPaymentService.CalcPayment(curretEod.UnderlyingCode, td.StartDate.Value, valueDate, (decimal)originNotional, shortRatio, directionRatio); - //var totalInterest = totalPayment / (1 + tax) * (1 - tax); - curretEod.PosiDividendSum = Math.Round(totalInterest,2) - curretEod.RealizedDividend; + curretEod.PosiDividendSum = eod.PosiDividendSum + curretEod.TdPosiDividend - curretEod.TdCloseDividend; } else {