#EQD-6196 国联民生-收益互换期间付息支持自动支付

This commit is contained in:
吴方海
2026-06-18 10:33:04 +08:00
parent a4237cc178
commit ab09cbc9ca
12 changed files with 214 additions and 43 deletions
@@ -144,7 +144,7 @@ namespace YLErp.Modules.SwapModule
if (autoInterval != null)
break;
}
DealAutoInterests(autoInterests, td, settleDate, preDealDate, posiLongNotional + posiShortNotional, autoInterval);
DealAutoInterests(autoInterests, td, settleDate, preDealDate, posiLongNotional + posiShortNotional, autoInterval, curEodPosis, tradeExtend);
//多空组合判断是否已到到期日且无持仓信息
if (longShort && td.ExerciseDate.Value == settleDate && allPositionQty == 0)
{
@@ -370,9 +370,11 @@ namespace YLErp.Modules.SwapModule
/// <param name="settleDate"></param>
/// <param name="swapDeals"></param>
/// <param name="interval">自动互换观察日信息,用于获取结算日期</param>
private void DealAutoInterests(List<swap_flow_event> autoInterests, trade td, DateTime settleDate, DateTime? preDealDate, decimal StockEqvNotional, IntervalModel interval)
/// <param name="curEodPositions">当日浮动端EOD持仓</param>
/// <param name="tradeExtend">交易扩展信息</param>
private void DealAutoInterests(List<swap_flow_event> autoInterests, trade td, DateTime settleDate, DateTime? preDealDate, decimal StockEqvNotional, IntervalModel interval, List<eod_swap_position> curEodPositions, trade_extend tradeExtend)
{
if (autoInterests.Count == 0)
if (autoInterests.Count == 0 && (curEodPositions == null || curEodPositions.All(x => x.PosiDividendSum == 0)))
{
return;
}
@@ -391,6 +393,16 @@ namespace YLErp.Modules.SwapModule
unwindData.NotionalValue = Convert.ToDecimal(td.OriginalStockEqvNotional ?? 0);
unwindData.PosiNotionalValue = StockEqvNotional;
// PayDate 统一用派息金额支付日 + 日历调整
var dividendPayDateOffset = tradeExtend?.ExtendObj?.DividendPayDate ?? 1;
// 0到期结算日 1派息日+0 2派息日+1 3派息日+2
var payDays = dividendPayDateOffset > 0 ? dividendPayDateOffset - 1 : 0;
var autoSwapPayDate = QdpCalendarHelper.GetNonHoliday(settleDate.AddDays(payDays));
unwindData.PayDate = autoSwapPayDate;
// 给已有利息腿统一赋 PayDate
autoInterests.ForEach(x => x.PayDate = autoSwapPayDate);
// 预付金腿类型列表:初始预付金、追加预付金
var premiumModes = new List<int>() { (int)InterestModeEnum., (int)InterestModeEnum. };
@@ -416,10 +428,58 @@ namespace YLErp.Modules.SwapModule
});
unwindData.SwapCloseAmount = interestTotal; // 利息腿金额
// 总实现盈亏
unwindData.SwapRealizedPnL = unwindData.SwapCloseAmount + unwindData.SwapMarginRebatePnl;
// 处理浮动端待实现分红:将其转为已实现(到期结算日不处理)
List<swap_flow_event> dividendEvents = new List<swap_flow_event>();
decimal dividendTotal = 0;
if (curEodPositions != null && dividendPayDateOffset > 0)
{
foreach (var eodPosi in curEodPositions.Where(x => x.PosiDividendSum != 0))
{
var dividendEvent = new swap_flow_event
{
SwapTradeId = td.id,
SwapTradeNo = td.TradeNumber,
EventType = (int)SwapEventTypeEnum.,
EventReason = "系统操作-自动互换",
EventDate = settleDate,
UnwindDate = settleDate,
PayDate = autoSwapPayDate,
PositionId = eodPosi.PositionId,
UnderlyingCode = eodPosi.UnderlyingCode,
PayDirection = eodPosi.PosiDirection,
PositionType = eodPosi.PositionType,
PositionQty = eodPosi.PosiQuantity,
Quantity = 0,
ContractSize = eodPosi.ContractSize,
TradingAmountAvg = eodPosi.PosiNetPrice,
PosiGrossPrice = eodPosi.PosiGrossPrice,
PosiNetPrice = eodPosi.PosiNetPrice,
MarkClosePnl = eodPosi.PosiDividendSum,
DividendIn = eodPosi.PosiDividendSum,
CloseFee = 0,
TradingFee = 0,
TradingFeePending = 0,
ClientId = td.ClientId,
DataState = (int)SwapFlowDateStateEnum.,
};
dividendEvents.Add(dividendEvent);
dividendTotal += eodPosi.PosiDividendSum;
SaveAutoSwapDeal(td, autoInterests, unwindData, interval);
// 将EOD持仓的待实现分红转为已实现
eodPosi.TdCloseDividend += eodPosi.PosiDividendSum;
eodPosi.TdPosiDividend = 0;
eodPosi.RealizedDividend += eodPosi.PosiDividendSum;
eodPosi.PosiDividendSum = 0;
}
}
// 分红收支加入总实现盈亏
unwindData.SwapDividendPnl = dividendTotal;
// 总实现盈亏
unwindData.SwapRealizedPnL = unwindData.SwapCloseAmount + unwindData.SwapMarginRebatePnl + dividendTotal;
SaveAutoSwapDeal(td, autoInterests, unwindData, interval, dividendEvents);
}
/// <summary>
/// 保存自动互换数据信息
@@ -427,7 +487,7 @@ namespace YLErp.Modules.SwapModule
/// <param name="td"></param>
/// <param name="swap_Deal"></param>
/// <param name="interval">自动互换观察日信息,用于获取结算日期</param>
private long SaveAutoSwapDeal(trade td, List<swap_flow_event> flowEvents, UnwindData unwindData, IntervalModel interval)
private long SaveAutoSwapDeal(trade td, List<swap_flow_event> flowEvents, UnwindData unwindData, IntervalModel interval, List<swap_flow_event> dividendEvents = null)
{
//td.UnWindDate = unwindData.ValueDate;
//优先使用 interval.SettlementDate 作为资金记录发生日期,如果没有则使用 ValueDate
@@ -446,6 +506,15 @@ namespace YLErp.Modules.SwapModule
AddClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapMarginRebatePnl), ClientCashInCashOut._预付金返息, unwindData.ValueDate);
}
// 分红:使用派息支付日偏移记录资金记录
if (unwindData.SwapDividendPnl != 0)
{
var dividendPayDate = (dividendEvents != null && dividendEvents.Count > 0)
? dividendEvents.First().PayDate.Value
: unwindData.ValueDate;
AddClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapDividendPnl), ClientCashInCashOut._互换, dividendPayDate);
}
string data = JsonConvert.SerializeObject(unwindData);
var swapEvent = new SwapEventService(this).AddSwapEventDate(unwindData.ValueDate, unwindData.SwapTradeId, (int)SwapEventTypeEnum., data, clientCashId, true, "系统操作-自动互换");//将互换总额存入事件
flowEvents.ForEach(x =>
@@ -453,6 +522,15 @@ namespace YLErp.Modules.SwapModule
x.EventId = swapEvent.id;
DbContext.swap_flow_event.Add(x);
});
// 保存分红事件
if (dividendEvents != null)
{
dividendEvents.ForEach(x =>
{
x.EventId = swapEvent.id;
DbContext.swap_flow_event.Add(x);
});
}
UpdateInitalPostion(flowEvents, td.id);
return swapEvent.id;
}
@@ -979,7 +1057,7 @@ namespace YLErp.Modules.SwapModule
newEodPayPosition.TdInterestFee = flowEvents.Sum(s => s.InterestFee);
newEodPayPosition.TdCloseInterestFee = newEodPayPosition.TdInterestFee;
newEodPayPosition.TdCloseInterest = flowEvents.Sum(s => s.InterestClosePnL);
var intersetAcmount = newEodPayPosition.TdInterestPrincipal*(newEodPayPosition.TdInterestRate + newEodPayPosition.FloatRate);
var intersetAcmount = newEodPayPosition.TdInterestPrincipal * (newEodPayPosition.TdInterestRate + newEodPayPosition.FloatRate);
if (position.IsAnnualized)
{
intersetAcmount /= tradeExtend.AnnualDays;
@@ -1368,7 +1446,9 @@ namespace YLErp.Modules.SwapModule
curretEod.UnderlyingMarketValue = curretEod.UnderlyingPrice * curretEod.PosiQuantity * curretEod.ContractSize * shortRatio;
curretEod.PosiMtmPnL = (curretEod.UnderlyingPrice - curretEod.PosiGrossPrice) * curretEod.PosiQuantity * curretEod.ContractSize * shortRatio * directionRatio;
curretEod.TdPosiDividend = 0;
if (valueDate > td.StartDate.Value && (curretEod.PosiQuantity > 0))
// 有互换事件时,分红已全量结算,不再查分红,TdPosiDividend和PosiDividendSum都归0
var hasSwapEvent = unwindEvents.Any(e => e.EventType == (int)SwapFlowEventTypeEnum. || e.EventType == (int)SwapFlowEventTypeEnum.);
if (!hasSwapEvent && valueDate > td.StartDate.Value && (curretEod.PosiQuantity > 0))
{
decimal tax = um.ValueAddedTax ?? 0;
BondPaymentService bondPaymentService = new BondPaymentService(UserInfo);
@@ -1382,12 +1462,26 @@ namespace YLErp.Modules.SwapModule
curretEod.RealizedPnl = eod.RealizedPnl + curretEod.TdCloseMtmPnl;
curretEod.PosiStatus = curretEod.PosiQuantity == 0 ? 1 : 0;
var closeQty = unwindEvents.Where(x => x.EventType == (int)SwapFlowEventTypeEnum.).ToList().Sum(s => s.Quantity);
// 当日浮动端平仓盈亏·分红 = 平仓数量/昨天剩余平仓数量 * 昨日浮动端待实现收益·分红
// 当日浮动端平仓盈亏·分红
curretEod.TdCloseDividend = unwindEvents.Sum(e => e.DividendIn);
curretEod.RealizedDividend = curretEod.RealizedDividend + curretEod.TdCloseDividend;
// 浮动端待实现收益·分红 = 昨日 + 当日浮动端分红 - 当日浮动端平仓盈亏·分红
curretEod.PosiDividendSum = eod.PosiDividendSum + curretEod.TdPosiDividend - curretEod.TdCloseDividend;
// 浮动端待实现收益·分红(有互换事件时全量结算归0;否则用当前持仓从起始日重算)
if (hasSwapEvent)
{
curretEod.PosiDividendSum = 0;
}
else if (curretEod.PosiQuantity > 0)
{
decimal tax = um.ValueAddedTax ?? 0;
BondPaymentService bondPaymentService = new BondPaymentService(UserInfo);
decimal totalPayment = bondPaymentService.CalcPayment(curretEod.UnderlyingCode, td.StartDate.Value, valueDate, curretEod.PosiQuantity, shortRatio, directionRatio);
curretEod.PosiDividendSum = totalPayment / (1 + tax) * (1 - tax);
}
else
{
curretEod.PosiDividendSum = 0;
}
curretEod.PosiProfitSum = curretEod.PosiMtmPnL + curretEod.PosiDividendSum + curretEod.PosiFeePending;
if (curretEod.PosiStatus == 1)
{
@@ -1464,7 +1558,7 @@ namespace YLErp.Modules.SwapModule
}
curretEod.PosiNotionalValue = curretEod.PosiGrossPrice * curretEod.PosiQuantity * curretEod.ContractSize;
curretEod.PosiNotionalValue = Math.Round(curretEod.PosiNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
curretEod.TdPosiDividend = unwindEvents.Sum(x => x.DividendIn) * directionRatio;
curretEod.TdCloseDividend = unwindEvents.Sum(x => x.DividendIn);
curretEod.TdCloseFee = unwindFlowEvents.Sum(x => x.TradingFee + x.TradingFeePending);
curretEod.TdCloseQty = unwindQty;
curretEod.TdCloseMtmPnl = unwindEvents.Sum(x => x.MarkClosePnl);
@@ -1526,7 +1620,21 @@ namespace YLErp.Modules.SwapModule
// //curretEod.TdCloseMtmPnl = 0;
// //curretEod.TdCloseFee = 0;
//}
curretEod.TdCloseDividend = curretEod.TdPosiDividend;
// TdCloseDividend 已由 SetPriceInfoByFlowEvent 设置
// 当日新增分红及待实现分红(有互换全量归0,开仓首日两者相同)
curretEod.TdPosiDividend = 0;
var hasSwapEvent = unwindEvents.Any(e => e.EventType == (int)SwapFlowEventTypeEnum. || e.EventType == (int)SwapFlowEventTypeEnum.);
if (!hasSwapEvent && settleDate > td.StartDate.Value && curretEod.PosiQuantity > 0)
{
decimal tax = um.ValueAddedTax ?? 0;
BondPaymentService bondPaymentService = new BondPaymentService(UserInfo);
decimal payment = bondPaymentService.CalcPayment(curretEod.UnderlyingCode, td.StartDate.Value, settleDate, curretEod.PosiQuantity, shortRatio, directionRatio);
payment = payment / (1 + tax) * (1 - tax);
curretEod.TdPosiDividend = payment;
curretEod.PosiDividendSum = payment;
}
curretEod.UnderlyingMarketValue = curretEod.UnderlyingPrice * curretEod.PosiQuantity * curretEod.ContractSize * shortRatio;
curretEod.PosiMtmPnL = (curretEod.UnderlyingPrice - curretEod.PosiGrossPrice) * curretEod.PosiQuantity * curretEod.ContractSize * shortRatio * directionRatio;
curretEod.PosiProfitSum = curretEod.PosiMtmPnL + curretEod.PosiDividendSum + curretEod.PosiFeePending;