diff --git a/YLErpDAL/Modules/SwapModule/SwapDealService.cs b/YLErpDAL/Modules/SwapModule/SwapDealService.cs index 8772ad0c..9fea70ca 100644 --- a/YLErpDAL/Modules/SwapModule/SwapDealService.cs +++ b/YLErpDAL/Modules/SwapModule/SwapDealService.cs @@ -333,7 +333,7 @@ namespace YLErp.Modules.SwapModule var grossPrice = realPostitions.Where(x => x.PosiDirection > 0).FirstOrDefault()?.PosiGrossPrice; var closeList = DbContext.swap_flow_event.Where(x => x.SwapTradeId == tradeId && x.UnwindDate == unwindDate && eventTypes.Contains(x.EventType) && x.DataState == (int)SwapFlowDateStateEnum.完成).ToList(); bool tdClose = closeList.Count > 0; - interests = GetInterests(td, tradeExtend, valueDate, unwindDate, lastEodPositions, positions, stockEqvNotional, posiLongNotionalValue, posiShortNotionalValue, posiNotionalValue, closePercent, eventType, tdClose, false, grossPrice ?? 0, orginPv, true, false, closeList); + interests = GetInterests(td, tradeExtend, valueDate, unwindDate, lastEodPositions, positions, stockEqvNotional, posiLongNotionalValue, posiShortNotionalValue, posiNotionalValue, closePercent, eventType, tdClose, false, grossPrice ?? 0, orginPv, true, false,false, closeList); return interests; } /// @@ -372,6 +372,7 @@ namespace YLErp.Modules.SwapModule decimal orginPv, bool add = false, bool settment = true, + bool newCalcLast= false, List closeList = null) { List interests = new List(); @@ -409,7 +410,7 @@ namespace YLErp.Modules.SwapModule else { // 盘中互换场景,使用 CalcUnwindInterest - interests.Add(CalcUnwindInterest(td, valueDate, endDate, positionClone, rate, floatRate, posiPrincipal, closePrincipal, newClosePercent, annualDays, preEodPosition, eventType, add, swap, orginPv, calcFirst, calcLast)); + interests.Add(CalcUnwindInterest(td, valueDate, endDate, positionClone, rate, floatRate, posiPrincipal, closePrincipal, newClosePercent, annualDays, preEodPosition, eventType, add, swap, orginPv, calcFirst, calcLast||newCalcLast)); } } //当日有平仓或互换记录时,需要把平仓或互换已经结算的利息从计算结果中扣除,避免重复计算 @@ -711,8 +712,8 @@ namespace YLErp.Modules.SwapModule decimal interest = interestProfitSum * closePercent; decimal tdinterest = interestProfitSum * closePercent; int interestPeriod = position.interest_rest_days ?? 1; - decimal dynomicPrincipal = principal; - decimal tdDynomicPrincipal = posiPrincipal; + decimal dynomicPrincipal = (preEodPosition.TdInterestPrincipal + posiPrincipal - orginPv); + decimal tdDynomicPrincipal = dynomicPrincipal; var calcDays = (endDate - startDate).Days; double floatRate = Convert.ToDouble(floateRate); for (int i = 0; i <= calcDays; i++) @@ -727,8 +728,8 @@ namespace YLErp.Modules.SwapModule // 复利时:利息并入本金 if (compoundInterest) { - dynomicPrincipal = dynomicPrincipal + interest; - tdDynomicPrincipal = tdDynomicPrincipal + interest; + dynomicPrincipal = principal + interest; + tdDynomicPrincipal = principal + interest; } // 获取新的浮动利率 if (!string.IsNullOrEmpty(position.FloatRateUnderlyingCode)) @@ -751,9 +752,9 @@ namespace YLErp.Modules.SwapModule } else { - flowEvent.InterestPrincipal = (preEodPosition.TdInterestPrincipal + posiPrincipal - orginPv) * closePercent; + flowEvent.InterestPrincipal = tdDynomicPrincipal * closePercent; tdDynomicPrincipal = flowEvent.InterestPrincipal; - TdInterestPrincipal = (preEodPosition.TdInterestPrincipal + posiPrincipal - orginPv); + TdInterestPrincipal = tdDynomicPrincipal; } flowEvent.FloatRate = Convert.ToDecimal(floatRate); var interest1 = flowEvent.InterestPrincipal * (flowEvent.InterestRate + Convert.ToDecimal(floatRate)); diff --git a/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs b/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs index 05114ea3..62ffa53f 100644 --- a/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs +++ b/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs @@ -62,7 +62,7 @@ namespace YLErp.Modules.SwapModule public void SwapPositionCompose(DateTime settleDate, DateTime preSettleDate, IEnumerable ClientIds) { var dateStr = settleDate.ToString("yyyy-MM-dd"); - Log.Info("SwapPositionCompose:"+"settleDate:" + settleDate+ " preSettleDate:"+ preSettleDate+ " ClientIds:"+JsonHelper.Serialize(ClientIds)); + Log.Info("SwapPositionCompose:" + "settleDate:" + settleDate + " preSettleDate:" + preSettleDate + " ClientIds:" + JsonHelper.Serialize(ClientIds)); var tradePredicate = PredicateBuilder.Create(n => n.ValidState != ConsGlobal.InValid && n.TradeType == "收益互换" && n.TradeDate <= settleDate @@ -221,32 +221,32 @@ namespace YLErp.Modules.SwapModule decimal orginPv) { Log.Info("===================处理利息腿归档===================="); - + // 添加详细的参数验证日志 Log.Info($"[DealInterests] 参数验证 - settleDate: {settleDate:yyyy-MM-dd}, td.id: {td?.id}, td.TradeNumber: {td?.TradeNumber}"); Log.Info($"[DealInterests] 参数验证 - interestList.Count: {interestList?.Count ?? 0}, eodPositions.Count: {eodPositions?.Count ?? 0}, todyEodPositions.Count: {todyEodPositions?.Count ?? 0}"); Log.Info($"[DealInterests] 参数验证 - flowEvents.Count: {flowEvents?.Count ?? 0}, autoInterests.Count: {autoInterests?.Count ?? 0}"); Log.Info($"[DealInterests] 参数验证 - posiLongNational: {posiLongNational}, posiShortNational: {posiShortNational}, closeNational: {closeNational}, grossPrice: {grossPrice}, orginPv: {orginPv}"); - + // 验证关键参数 if (td == null) { Log.Info("[DealInterests] 参数验证: td (trade) 为 null"); throw new ArgumentNullException(nameof(td), "交易对象不能为null"); } - + if (interestList == null) { Log.Info($"[DealInterests] 参数验证: interestList 为 null, td.id: {td.id}"); throw new ArgumentNullException(nameof(interestList), "利息腿列表不能为null"); } - + if (flowEvents == null) { Log.Info($"[DealInterests] 参数验证: flowEvents 为 null, td.id: {td.id}"); throw new ArgumentNullException(nameof(flowEvents), "流水事件列表不能为null"); } - + if (autoInterests == null) { Log.Info($"[DealInterests] 参数验证: autoInterests 为 null, td.id: {td.id}"); @@ -390,14 +390,14 @@ namespace YLErp.Modules.SwapModule } unwindData.NotionalValue = Convert.ToDecimal(td.OriginalStockEqvNotional ?? 0); unwindData.PosiNotionalValue = StockEqvNotional; - + // 预付金腿类型列表:初始预付金、追加预付金 - var premiumModes = new List() {(int)InterestModeEnum.初始预付金, (int)InterestModeEnum.追加预付金 }; - + var premiumModes = new List() { (int)InterestModeEnum.初始预付金, (int)InterestModeEnum.追加预付金 }; + // 分别计算预付金腿和利息腿的金额 var premiumInterests = autoInterests.Where(x => premiumModes.Contains(x.InterestMode)).ToList(); var interestLegs = autoInterests.Where(x => !premiumModes.Contains(x.InterestMode)).ToList(); - + // 预付金腿金额 decimal premiumTotal = 0; premiumInterests.ForEach(x => @@ -406,7 +406,7 @@ namespace YLErp.Modules.SwapModule premiumTotal += x.InterestClosePnL * ratio; }); unwindData.SwapMarginRebatePnl = premiumTotal; // 预付金腿金额 - + // 利息腿金额(总金额减去预付金腿金额) decimal interestTotal = 0; interestLegs.ForEach(x => @@ -415,10 +415,10 @@ namespace YLErp.Modules.SwapModule interestTotal += x.InterestClosePnL * ratio; }); unwindData.SwapCloseAmount = interestTotal; // 利息腿金额 - + // 总实现盈亏 unwindData.SwapRealizedPnL = unwindData.SwapCloseAmount + unwindData.SwapMarginRebatePnl; - + SaveAutoSwapDeal(td, autoInterests, unwindData, interval); } /// @@ -432,7 +432,7 @@ namespace YLErp.Modules.SwapModule //td.UnWindDate = unwindData.ValueDate; //优先使用 interval.SettlementDate 作为资金记录发生日期,如果没有则使用 ValueDate var cashHappenDate = interval?.SettlementDate ?? unwindData.ValueDate; - + int clientCashId = 0; // 利息腿:插入资金记录(使用系统操作_互换) if (unwindData.SwapCloseAmount != 0) @@ -445,7 +445,7 @@ namespace YLErp.Modules.SwapModule { AddClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapMarginRebatePnl), ClientCashInCashOut.系统操作_预付金返息, unwindData.ValueDate); } - + string data = JsonConvert.SerializeObject(unwindData); var swapEvent = new SwapEventService(this).AddSwapEventDate(unwindData.ValueDate, unwindData.SwapTradeId, (int)SwapEventTypeEnum.自动互换, data, clientCashId, true, "系统操作-自动互换");//将互换总额存入事件 flowEvents.ForEach(x => @@ -731,7 +731,7 @@ namespace YLErp.Modules.SwapModule newEodPayPosition.InterestFeeSum = eodPayPosition.InterestFeeSum + newEodPayPosition.TdInterestFee - newEodPayPosition.TdCloseInterestFee; newEodPayPosition.InterestProfitSum = newEodPayPosition.InterestIncomeSum + newEodPayPosition.InterestFeeSum; //持仓价值 - newEodPayPosition.SwapPositionValue = newEodPayPosition.InterestProfitSum * ratio + newEodPayPosition.PosiProfitSum; + newEodPayPosition.SwapPositionValue = newEodPayPosition.InterestProfitSum * ratio + newEodPayPosition.PosiProfitSum; //累计已实现 newEodPayPosition.RealizedInterest = eodPayPosition.RealizedInterest + newEodPayPosition.TdCloseInterest * ratio; @@ -759,41 +759,41 @@ namespace YLErp.Modules.SwapModule private List SaveAutoEodInterestPosition(eod_swap_position eodPayPosition, eod_swap_position newEodPayPosition, swap_position position, trade td, DateTime valueDate, IntervalModel interval, eod_swap lastEodSwap, decimal posiLongNotional, decimal posiShortNational, decimal grossPrice, decimal orginPv) { Log.Info($"[SaveAutoEodInterestPosition] 开始执行 - valueDate: {valueDate:yyyy-MM-dd}, td.id: {td?.id}, position.id: {position?.id}"); - + // 详细的参数验证 if (td == null) { Log.Info("[SaveAutoEodInterestPosition] 参数验证: td (trade) 为 null"); throw new ArgumentNullException(nameof(td), "交易对象不能为null"); } - + if (position == null) { Log.Info($"[SaveAutoEodInterestPosition] 参数验证: position 为 null, td.id: {td.id}"); throw new ArgumentNullException(nameof(position), "持仓对象不能为null"); } - + if (interval == null) { Log.Info($"[SaveAutoEodInterestPosition] 参数验证: interval 为 null, td.id: {td.id}, position.id: {position.id}"); throw new ArgumentNullException(nameof(interval), "观察日信息不能为null"); } - + if (td.trade_extend == null) { Log.Info($"[SaveAutoEodInterestPosition] 参数验证: td.trade_extend 为 null, td.id: {td.id}, td.TradeNumber: {td.TradeNumber}"); throw new ArgumentNullException("td.trade_extend", "交易扩展信息不能为null"); } - + Log.Info($"eodPayPosition is {JsonHelper.Serialize(eodPayPosition, false)},newEodPayPosition is {JsonHelper.Serialize(newEodPayPosition, false)}"); - + // 验证 ExtendObj if (td.trade_extend.ExtendObj == null) { Log.Info($"[SaveAutoEodInterestPosition] 参数验证: td.trade_extend.ExtendObj 为 null, td.id: {td.id}"); throw new ArgumentNullException("td.trade_extend.ExtendObj", "交易扩展对象不能为null"); } - + var tradeExtend = td.trade_extend.ExtendObj; decimal oriPosiNotionalValue = posiLongNotional + posiShortNational; decimal posiNotionalValue = oriPosiNotionalValue; @@ -902,20 +902,18 @@ namespace YLErp.Modules.SwapModule Log.Info($"eodPayPosition is {JsonHelper.Serialize(eodPayPosition, false)},newEodPayPosition is {JsonHelper.Serialize(newEodPayPosition, false)}"); var tradeExtend = td.trade_extend.ExtendObj; decimal oriPosiNotionalValue = posiLongNotional + posiShortNational + closeNational; - decimal posiNotionalValue = oriPosiNotionalValue; + decimal posiNotionalValue = posiLongNotional + posiShortNational; decimal ratio = position.InterestDirection == (int)SwapDirectionEnum.收取 ? 1m : -1m;//收取为正,支付为负 if (marginTypes.Contains(position.InterestMode)) { ratio = -ratio; } - if (eodPayPosition == null) - { - eodPayPosition = new eod_swap_position(); - eodPayPosition.ClientId = td.ClientId; - eodPayPosition.SwapTradeId = td.id; - eodPayPosition.PosiStartDate = td.StartDate.Value; - eodPayPosition.PosiMatuirityDate = td.ExerciseDate.Value; - } + var lastInterestIncomeSum = eodPayPosition.InterestIncomeSum; + eodPayPosition = new eod_swap_position(); + eodPayPosition.ClientId = td.ClientId; + eodPayPosition.SwapTradeId = td.id; + eodPayPosition.PosiStartDate = td.StartDate.Value; + eodPayPosition.PosiMatuirityDate = td.ExerciseDate.Value; if (newEodPayPosition == null) { newEodPayPosition = eodPayPosition.Clone(); @@ -926,6 +924,10 @@ namespace YLErp.Modules.SwapModule { orginPv = eodPayPosition.InterestPrincipalFix; } + else + { + orginPv = posiNotionalValue; + } decimal closePercent = oriPosiNotionalValue == 0 ? 0 : closeNational / oriPosiNotionalValue; var eventType = autoSwap ? (int)SwapEventTypeEnum.自动互换 : (int)SwapEventTypeEnum.平仓; bool longShort = td.StructureType == ClientMarginTypeEnum.多空组合.ToString(); @@ -941,9 +943,9 @@ namespace YLErp.Modules.SwapModule positions.Add(position); List preEodPositions = new List(); preEodPositions.Add(eodPayPosition); - var interests = new SwapDealService(this).GetInterests(td, td.trade_extend, valueDate, valueDate, preEodPositions, positions, posiNotionalValue, posiLongNotional, posiShortNational, closeNational, 1, eventType, false, true, grossPrice, orginPv, true); + var interests = new SwapDealService(this).GetInterests(td, td.trade_extend, valueDate, valueDate, preEodPositions, positions, posiNotionalValue, posiLongNotional, posiShortNational, closeNational, 1, eventType, false, true, grossPrice, orginPv, true, settment: false, newCalcLast: true); decimal TdInterestAmount = interests.Sum(x => x.TdInterestAmount); - + decimal InterestAmount = interests.Sum(x => x.InterestAmount); newEodPayPosition.ValueDate = valueDate; newEodPayPosition.PositionId = position.id; UpdateDbOption(newEodPayPosition); @@ -964,7 +966,7 @@ namespace YLErp.Modules.SwapModule newEodPayPosition.interest_rest_days = position.interest_rest_days; newEodPayPosition.interest_rule = position.interest_rule; //利息端估值用信息 - newEodPayPosition.TdInterestPrincipal = interestModes.Contains(position.InterestMode) ? eodPayPosition.InterestPrincipalFix : posiLongNotional + posiShortNational; + newEodPayPosition.TdInterestPrincipal = interests.Count > 0 ? interests.First().InterestPrincipal : 0; if (interval != null) { newEodPayPosition.TdInterestRate = interval.Rate; @@ -977,8 +979,13 @@ namespace YLErp.Modules.SwapModule newEodPayPosition.TdInterestFee = flowEvents.Sum(s => s.InterestFee); newEodPayPosition.TdCloseInterestFee = newEodPayPosition.TdInterestFee; newEodPayPosition.TdCloseInterest = flowEvents.Sum(s => s.InterestClosePnL); - newEodPayPosition.TdInterestIncome = TdInterestAmount * (1 - closePercent); - Log.Info($"InterestIncomeSum is {eodPayPosition.InterestIncomeSum},TdInterestIncome is {newEodPayPosition.TdInterestIncome}" + + var intersetAcmount = newEodPayPosition.TdInterestPrincipal*(newEodPayPosition.TdInterestRate + newEodPayPosition.FloatRate); + if (position.IsAnnualized) + { + intersetAcmount /= tradeExtend.AnnualDays; + } + newEodPayPosition.TdInterestIncome = intersetAcmount; + Log.Info($"InterestIncomeSum is {lastInterestIncomeSum},TdInterestIncome is {newEodPayPosition.TdInterestIncome}" + $",TdCloseInterest is {newEodPayPosition.TdCloseInterest}"); Log.Info($"InterestFeeSum is {eodPayPosition.InterestFeeSum},TdInterestFee is {newEodPayPosition.TdInterestFee}" + $",TdCloseInterestFee is {newEodPayPosition.TdCloseInterestFee}"); @@ -988,7 +995,7 @@ namespace YLErp.Modules.SwapModule } else { - newEodPayPosition.InterestIncomeSum = eodPayPosition.InterestIncomeSum + newEodPayPosition.TdInterestIncome - newEodPayPosition.TdCloseInterest * ratio; + newEodPayPosition.InterestIncomeSum = InterestAmount; } //持仓内容-利息腿-损益统计(本方视角) newEodPayPosition.InterestFeeSum = eodPayPosition.InterestFeeSum + newEodPayPosition.TdInterestFee - newEodPayPosition.TdCloseInterestFee; @@ -1001,9 +1008,9 @@ namespace YLErp.Modules.SwapModule Log.Info($"InterestFeeSum is {eodPayPosition.InterestFeeSum},TdInterestFee is {newEodPayPosition.TdInterestFee}" + $",TdCloseInterestFee is {newEodPayPosition.TdCloseInterestFee}"); //累计已实现 - newEodPayPosition.RealizedInterest = eodPayPosition.RealizedInterest + newEodPayPosition.TdCloseInterest * ratio ; + newEodPayPosition.RealizedInterest = eodPayPosition.RealizedInterest + newEodPayPosition.TdCloseInterest * ratio; newEodPayPosition.RealizedInterestFee = eodPayPosition.RealizedInterestFee + newEodPayPosition.TdCloseInterestFee; - newEodPayPosition.RealizedPnl = newEodPayPosition.RealizedInterest + newEodPayPosition.RealizedInterestFee;; + newEodPayPosition.RealizedPnl = newEodPayPosition.RealizedInterest + newEodPayPosition.RealizedInterestFee; ; var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, valueDate , seekPreday: true, currencyRateType: position.InterestDirection == (int)SwapDirectionEnum.收取 ? CurrencyRateType.Buy : CurrencyRateType.Sell); newEodPayPosition.TdCurrency = Convert.ToDecimal(currencyRate); @@ -1040,7 +1047,7 @@ namespace YLErp.Modules.SwapModule eodPayPosition.ClientId = td.ClientId; eodPayPosition.SwapTradeId = td.id; //eodPayPosition.PositionId = position.id; 为了算利息时找不到给日期重新赋值 - + eodPayPosition.InterestMode = position.InterestMode; eodPayPosition.InterestPrincipalFix = position.InterestPrincipalFix; eodPayPosition.InterestRateDefault = position.InterestRateDefault; @@ -1123,7 +1130,7 @@ namespace YLErp.Modules.SwapModule newEodPayPosition.InterestFeeSum = eodPayPosition.InterestFeeSum + newEodPayPosition.TdInterestFee - newEodPayPosition.TdCloseInterestFee; newEodPayPosition.InterestProfitSum = newEodPayPosition.InterestIncomeSum + newEodPayPosition.InterestFeeSum; //持仓价值 - newEodPayPosition.SwapPositionValue = newEodPayPosition.InterestProfitSum * ratio + newEodPayPosition.PosiProfitSum; + newEodPayPosition.SwapPositionValue = newEodPayPosition.InterestProfitSum * ratio + newEodPayPosition.PosiProfitSum; //累计已实现 newEodPayPosition.RealizedInterest = eodPayPosition.RealizedInterest + newEodPayPosition.TdCloseInterest * ratio; @@ -2055,7 +2062,7 @@ namespace YLErp.Modules.SwapModule { eventDate = QdpCalendarHelper.GetNonHoliday(eventDate.AddDays(tradeExtend.ExtendObj.SettlementRules)); } - item.DayCount = Math.Max(0,(eventDate - item.position.PosiStartDate).Days + 1); + item.DayCount = Math.Max(0, (eventDate - item.position.PosiStartDate).Days + 1); //item.position.PosiProfitSum += item.position.VTradingFee-item.position.PosiFeePending; SetClientEodPosition(item.position); //item.position.PosiProfitSum += item.TradingFee;