Merge remote-tracking branch 'origin/glms/feature/1.4.2' into glms/feature/1.4.2
This commit is contained in:
@@ -145,7 +145,10 @@ namespace YLErp.Modules.SwapModule
|
||||
if (autoInterval != null)
|
||||
break;
|
||||
}
|
||||
DealAutoInterests(autoInterests, td, settleDate, preDealDate, posiLongNotional + posiShortNotional, autoInterval, curEodPosis, tradeExtend);
|
||||
// 自动互换(仅利息/预付金,不含分红)
|
||||
DealAutoInterests(autoInterests, td, settleDate, preDealDate, posiLongNotional + posiShortNotional, autoInterval);
|
||||
// 分红独立处理:只要当天有债券需要分红,则生成分红自动互换,与利息互换无关
|
||||
DealDividends(curEodPosis, td, settleDate, tradeExtend);
|
||||
//多空组合判断是否已到到期日且无持仓信息
|
||||
if (longShort && td.ExerciseDate.Value == settleDate && allPositionQty == 0)
|
||||
{
|
||||
@@ -373,114 +376,121 @@ namespace YLErp.Modules.SwapModule
|
||||
/// <param name="interval">自动互换观察日信息,用于获取结算日期</param>
|
||||
/// <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)
|
||||
/// <summary>
|
||||
/// 自动互换(仅利息/预付金,不含分红)
|
||||
/// </summary>
|
||||
private void DealAutoInterests(List<swap_flow_event> autoInterests, trade td, DateTime settleDate, DateTime? preDealDate, decimal StockEqvNotional, IntervalModel interval)
|
||||
{
|
||||
if (autoInterests.Count == 0 ||curEodPositions == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
//生成自动互换事件
|
||||
if (autoInterests.Count == 0) return;
|
||||
|
||||
UnwindData unwindData = new UnwindData();
|
||||
unwindData.SwapTradeId = td.id;
|
||||
unwindData.ValueDate = settleDate;
|
||||
if (preDealDate.HasValue)
|
||||
{
|
||||
unwindData.StartDate = preDealDate.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
unwindData.StartDate = td.StartDate.Value;
|
||||
}
|
||||
unwindData.StartDate = preDealDate ?? td.StartDate.Value;
|
||||
unwindData.NotionalValue = Convert.ToDecimal(td.OriginalStockEqvNotional ?? 0);
|
||||
unwindData.PosiNotionalValue = StockEqvNotional;
|
||||
unwindData.PayDate = settleDate;
|
||||
|
||||
// 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;
|
||||
autoInterests.ForEach(x => x.PayDate = settleDate);
|
||||
|
||||
// 给已有利息腿统一赋 PayDate
|
||||
autoInterests.ForEach(x => x.PayDate = autoSwapPayDate);
|
||||
|
||||
// 预付金腿类型列表:初始预付金、追加预付金
|
||||
var premiumModes = new List<int>() { (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 =>
|
||||
{
|
||||
var ratio = x.InterestDirection == (int)SwapDirectionEnum.收取 ? -1 : 1;
|
||||
premiumTotal += x.InterestClosePnL * ratio;
|
||||
});
|
||||
unwindData.SwapMarginRebatePnl = premiumTotal; // 预付金腿金额
|
||||
unwindData.SwapMarginRebatePnl = premiumTotal;
|
||||
|
||||
// 利息腿金额(总金额减去预付金腿金额)
|
||||
decimal interestTotal = 0;
|
||||
interestLegs.ForEach(x =>
|
||||
{
|
||||
var ratio = x.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;
|
||||
interestTotal += x.InterestClosePnL * ratio;
|
||||
});
|
||||
unwindData.SwapCloseAmount = interestTotal; // 利息腿金额
|
||||
unwindData.SwapCloseAmount = interestTotal;
|
||||
unwindData.SwapDividendPnl = 0;
|
||||
unwindData.SwapRealizedPnL = unwindData.SwapCloseAmount + unwindData.SwapMarginRebatePnl;
|
||||
|
||||
SaveAutoSwapDeal(td, autoInterests, unwindData, interval);
|
||||
}
|
||||
/// <summary>
|
||||
/// 分红独立处理:当天有债券需要分红时,生成独立的分红自动互换事件
|
||||
/// </summary>
|
||||
private void DealDividends(List<eod_swap_position> curEodPositions, trade td, DateTime settleDate, trade_extend tradeExtend)
|
||||
{
|
||||
if (curEodPositions == null) return;
|
||||
var hasDividend = curEodPositions.Any(x => x.PosiDividendSum != 0);
|
||||
if (!hasDividend) return;
|
||||
|
||||
var dividendPayDateOffset = tradeExtend?.ExtendObj?.DividendPayDate ?? 1;
|
||||
if (dividendPayDateOffset <= 0) return;
|
||||
|
||||
var payDays = dividendPayDateOffset - 1;
|
||||
var dividendPayDate = QdpCalendarHelper.GetNonHoliday(settleDate.AddDays(payDays));
|
||||
|
||||
// 处理浮动端待实现分红:将其转为已实现(到期结算日不处理)
|
||||
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;
|
||||
|
||||
// 将EOD持仓的待实现分红转为已实现
|
||||
eodPosi.TdCloseDividend += eodPosi.PosiDividendSum;
|
||||
eodPosi.TdPosiDividend = 0;
|
||||
eodPosi.RealizedDividend += eodPosi.PosiDividendSum;
|
||||
eodPosi.PosiDividendSum = 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 = dividendPayDate,
|
||||
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;
|
||||
|
||||
eodPosi.TdCloseDividend += eodPosi.PosiDividendSum;
|
||||
eodPosi.TdPosiDividend = 0;
|
||||
eodPosi.RealizedDividend += eodPosi.PosiDividendSum;
|
||||
eodPosi.PosiDividendSum = 0;
|
||||
eodPosi.PosiProfitSum -= eodPosi.TdCloseDividend;
|
||||
//互换持仓价值要去掉已实现的
|
||||
eodPosi.SwapPositionValue -= eodPosi.PosiDividendSum;
|
||||
//已实现盈亏要加上已实现的
|
||||
eodPosi.RealizedPnl += eodPosi.PosiDividendSum;
|
||||
}
|
||||
|
||||
// 分红收支加入总实现盈亏
|
||||
UnwindData unwindData = new UnwindData();
|
||||
unwindData.SwapTradeId = td.id;
|
||||
unwindData.ValueDate = settleDate;
|
||||
unwindData.StartDate = td.StartDate.Value;
|
||||
unwindData.NotionalValue = Convert.ToDecimal(td.OriginalStockEqvNotional ?? 0);
|
||||
unwindData.PosiNotionalValue = curEodPositions.Sum(x => x.PosiNotionalValue);
|
||||
unwindData.PayDate = dividendPayDate;
|
||||
unwindData.SwapDividendPnl = dividendTotal;
|
||||
unwindData.SwapCloseAmount = 0;
|
||||
unwindData.SwapMarginRebatePnl = 0;
|
||||
unwindData.SwapRealizedPnL = dividendTotal;
|
||||
|
||||
// 总实现盈亏
|
||||
unwindData.SwapRealizedPnL = unwindData.SwapCloseAmount + unwindData.SwapMarginRebatePnl + dividendTotal;
|
||||
|
||||
SaveAutoSwapDeal(td, autoInterests, unwindData, interval, dividendEvents);
|
||||
SaveAutoSwapDeal(td,null , unwindData,null, dividendEvents:dividendEvents);
|
||||
}
|
||||
/// <summary>
|
||||
/// 保存自动互换数据信息
|
||||
@@ -504,7 +514,7 @@ namespace YLErp.Modules.SwapModule
|
||||
// 预付金腿:单独插入一条资金记录(系统操作_预付金返息)
|
||||
if (unwindData.SwapMarginRebatePnl != 0)
|
||||
{
|
||||
AddClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapMarginRebatePnl), ClientCashInCashOut.系统操作_预付金返息, unwindData.ValueDate);
|
||||
clientCashId = AddClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapMarginRebatePnl), ClientCashInCashOut.系统操作_预付金返息, unwindData.ValueDate);
|
||||
}
|
||||
|
||||
// 分红:使用派息支付日偏移记录资金记录
|
||||
@@ -513,16 +523,21 @@ namespace YLErp.Modules.SwapModule
|
||||
var dividendPayDate = (dividendEvents != null && dividendEvents.Count > 0)
|
||||
? dividendEvents.First().PayDate.Value
|
||||
: unwindData.ValueDate;
|
||||
AddClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapDividendPnl), ClientCashInCashOut.系统操作_互换, dividendPayDate);
|
||||
clientCashId = 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 =>
|
||||
if (flowEvents!=null)
|
||||
{
|
||||
x.EventId = swapEvent.id;
|
||||
DbContext.swap_flow_event.Add(x);
|
||||
});
|
||||
flowEvents.ForEach(x =>
|
||||
{
|
||||
x.EventId = swapEvent.id;
|
||||
DbContext.swap_flow_event.Add(x);
|
||||
});
|
||||
UpdateInitalPostion(flowEvents, td.id);
|
||||
}
|
||||
|
||||
// 保存分红事件
|
||||
if (dividendEvents != null)
|
||||
{
|
||||
@@ -531,8 +546,8 @@ namespace YLErp.Modules.SwapModule
|
||||
x.EventId = swapEvent.id;
|
||||
DbContext.swap_flow_event.Add(x);
|
||||
});
|
||||
UpdateInitalPostion(dividendEvents, td.id);
|
||||
}
|
||||
UpdateInitalPostion(flowEvents, td.id);
|
||||
return swapEvent.id;
|
||||
}
|
||||
/// <summary>
|
||||
@@ -1450,14 +1465,12 @@ 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;
|
||||
// 有互换事件时,分红已全量结算,不再查分红,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))
|
||||
// 分红与互换无关,只要持仓>0且起始日早于当前日,正常计算当日分红
|
||||
if (valueDate > td.StartDate.Value && (curretEod.PosiQuantity > 0))
|
||||
{
|
||||
decimal tax = um.ValueAddedTax ?? 0;
|
||||
BondPaymentService bondPaymentService = new BondPaymentService(UserInfo);
|
||||
decimal payment = bondPaymentService.CalcPayment(curretEod.UnderlyingCode, eod.ValueDate, valueDate, curretEod.PosiQuantity, shortRatio, directionRatio);
|
||||
// 考虑增值税
|
||||
curretEod.TdPosiDividend = Math.Round(payment / (1 + tax) * (1 - tax), 2);
|
||||
}
|
||||
curretEod.RealizedMtmPnL = eod.RealizedMtmPnL + curretEod.TdCloseMtmPnl;
|
||||
@@ -1466,16 +1479,12 @@ 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;
|
||||
|
||||
// 浮动端待实现收益·分红(有互换事件时全量结算归0;否则用当前持仓从起始日重算)
|
||||
if (hasSwapEvent)
|
||||
{
|
||||
curretEod.PosiDividendSum = 0;
|
||||
}
|
||||
else if (curretEod.PosiQuantity > 0)
|
||||
// 分红与互换解耦:持仓>0时从起始日重算待实现分红,不再受互换事件影响
|
||||
if (curretEod.PosiQuantity > 0)
|
||||
{
|
||||
decimal tax = um.ValueAddedTax ?? 0;
|
||||
BondPaymentService bondPaymentService = new BondPaymentService(UserInfo);
|
||||
|
||||
@@ -184,7 +184,7 @@ const vue = new Vue({
|
||||
let totalDividend = parseFloat(thisObj.deal.PositionQty) * resp.obj.totalInterest * ratio * floatRatio;
|
||||
let consumedDividend = parseFloat(resp.obj.consumedDividend ?? 0);
|
||||
// 互换是全量消费,consumedDividend>0 表示分红已被当天互换消费,归0
|
||||
thisObj.floatPosition.DividendIn = Math.abs(consumedDividend) > 0 ? 0 : parseFloat(totalDividend.toFixed(2));
|
||||
thisObj.floatPosition.DividendIn = Math.abs(consumedDividend) > 0 ? parseFloat((totalDividend - consumedDividend).toFixed(2)) : parseFloat(totalDividend.toFixed(2));
|
||||
thisObj.floatPosition.DividendPending = 0;
|
||||
thisObj.calcFloatClosePnl();
|
||||
thisObj.dataFormat();
|
||||
|
||||
@@ -283,7 +283,7 @@ const vue = new Vue({
|
||||
let totalDividend = parseFloat(thisObj.deal.CloseQty) * resp.obj.totalInterest * ratio * floatRatio;
|
||||
let consumedDividend = parseFloat(resp.obj.consumedDividend ?? 0);
|
||||
// 互换是全量消费,consumedDividend>0 表示分红已被当天互换消费,归0
|
||||
thisObj.floatPosition.DividendIn = Math.abs(consumedDividend) > 0 ? 0 : parseFloat(totalDividend.toFixed(2));
|
||||
thisObj.floatPosition.DividendIn = Math.abs(consumedDividend) > 0 ? parseFloat((totalDividend - consumedDividend).toFixed(2)) : parseFloat(totalDividend.toFixed(2));
|
||||
var posiQty = parseFloat(thisObj.floatPosition.Quantity) - parseFloat(thisObj.deal.CloseQty);
|
||||
thisObj.floatPosition.DividendPending = parseFloat((posiQty * resp.obj.totalInterest * ratio * floatRatio).toFixed(2));
|
||||
thisObj.calcFloatClosePnl();
|
||||
|
||||
Reference in New Issue
Block a user