feat(swap): 添加公司行为现金分红按登记日权益数量计算功能
- 新增 CalcBondPayment 方法支持传入公司行为权益数量参数 - 在 SwapEodPositionService 中识别现金分红类型的公司行为持仓 - 修改 CopyEodPosition 和 UpdateEodPosition 方法传递公司行为前的数量信息 - 更新 SaveCurrentEodInitalPosi 方法支持公司行为数量计算 - 在 BondPaymentService 中实现公司行为现金分红按登记日权益数量计算逻辑 - 添加单元测试验证公司行为现金分红使用除权前数量的计算功能
This commit is contained in:
@@ -357,6 +357,19 @@ namespace YLErp.Modules.SwapModule
|
||||
directionRatio);
|
||||
}
|
||||
|
||||
protected virtual decimal CalcBondPayment(string underlyingCode, DateTime fromDate, DateTime toDate,
|
||||
decimal qty, int shortRatio, int directionRatio, decimal? corporateActionQty)
|
||||
{
|
||||
if (!corporateActionQty.HasValue)
|
||||
{
|
||||
return CalcBondPayment(underlyingCode, fromDate, toDate, qty, shortRatio, directionRatio);
|
||||
}
|
||||
|
||||
var service = new BondPaymentService(UserInfo);
|
||||
var payments = service.GetBondPayments(underlyingCode, fromDate, toDate);
|
||||
return service.CalcPayment(payments, qty, shortRatio, directionRatio, corporateActionQty);
|
||||
}
|
||||
|
||||
// ---- SwapPositionCompose 路径专用 seam(借鉴 testable 分支)----
|
||||
|
||||
/// <summary>查找收盘所需的活跃互换交易(生产: DbContext.trade.Where;测试: 内存列表)</summary>
|
||||
@@ -611,6 +624,11 @@ namespace YLErp.Modules.SwapModule
|
||||
var corporateActionBeforePositions = BuildCorporateActionBeforePositions(
|
||||
eodPositions, // 上一日终持仓
|
||||
posiList);
|
||||
var corporateActionCashDividendBeforePositions = corporateActionBeforePositions
|
||||
.Where(position => !string.IsNullOrWhiteSpace(position.UnderlyingCode)
|
||||
&& exDividendByCode.TryGetValue(position.UnderlyingCode, out var dividend)
|
||||
&& dividend.GiveCashAmount != 0m)
|
||||
.ToList();
|
||||
|
||||
// 交易首日恰逢 EffectiveDate 时,在内存克隆上生成除权后的开盘基线,应用生效日公司行为。
|
||||
// 有上一份 EOD 时沿用 PrepareFundOpeningEodPositions,避免重复套系数。
|
||||
@@ -627,7 +645,8 @@ namespace YLErp.Modules.SwapModule
|
||||
settleDate, // 收盘日期
|
||||
td, // 交易
|
||||
preSettleDate, // 上一交易日
|
||||
flowEvents); // 流水事件
|
||||
flowEvents, // 流水事件
|
||||
corporateActionCashDividendBeforePositions);
|
||||
|
||||
// 现金分红不在登记日直接累加;Copy/Update EOD 通过 CalcBondPayment
|
||||
// 读取 EffectiveDate 命中的 ex_dividend_info,并生成 TdPosiDividend。
|
||||
@@ -1435,7 +1454,8 @@ namespace YLErp.Modules.SwapModule
|
||||
DateTime settleDate,
|
||||
trade td,
|
||||
DateTime preSettleDate,
|
||||
List<swap_flow_event> flowEvents)
|
||||
List<swap_flow_event> flowEvents,
|
||||
IReadOnlyCollection<eod_swap_position> corporateActionBeforePositions = null)
|
||||
{
|
||||
string settleDateStr = settleDate.ToString("yyyy-MM-dd");
|
||||
string preSettleDateStr = preSettleDate.ToString("yyyy-MM-dd");
|
||||
@@ -1457,18 +1477,20 @@ namespace YLErp.Modules.SwapModule
|
||||
var tdEodPosition = todyEodPositions.FirstOrDefault(x => x.PositionId == posi.id);//当前结算日日终持仓信息
|
||||
var unwindEvents = flowEvents.Where(x => x.PositionId == posi.id).ToList();//当前日平仓信息
|
||||
var realPosition = realPosiList.FirstOrDefault(s => s.PositionId == posi.id);
|
||||
var corporateActionBeforeQuantity = corporateActionBeforePositions?
|
||||
.FirstOrDefault(x => x.PositionId == posi.id)?.PosiQuantity;
|
||||
eod_swap_position eodPosi = new eod_swap_position();
|
||||
if (eodPosition == null)
|
||||
{
|
||||
eodPosi = SaveCurrentEodInitalPosi(posi, td, settleDate, preSettleDate, unwindEvents);
|
||||
eodPosi = SaveCurrentEodInitalPosi(posi, td, settleDate, preSettleDate, unwindEvents, corporateActionBeforeQuantity);
|
||||
}
|
||||
else if (unwindEvents.Count() == 0)
|
||||
{
|
||||
eodPosi = CopyEodPosition(eodPosition, tdEodPosition, td, settleDate, preSettleDate);
|
||||
eodPosi = CopyEodPosition(eodPosition, tdEodPosition, td, settleDate, preSettleDate, corporateActionBeforeQuantity);
|
||||
}
|
||||
else
|
||||
{
|
||||
eodPosi = UpdateEodPosition(posi, eodPosition, tdEodPosition, td, settleDate, preSettleDate, unwindEvents);
|
||||
eodPosi = UpdateEodPosition(posi, eodPosition, tdEodPosition, td, settleDate, preSettleDate, unwindEvents, corporateActionBeforeQuantity);
|
||||
}
|
||||
Log.Info($"eodPosi为:{JsonHelper.Serialize(eodPosi, false)}");
|
||||
list.Add(eodPosi);
|
||||
@@ -2626,7 +2648,7 @@ namespace YLErp.Modules.SwapModule
|
||||
/// <param name="todayPositions">当日日终归档信息</param>
|
||||
/// <param name="swap_Deals">当日平仓/互换事件信息</param>
|
||||
/// <param name="td">交易信息</param>
|
||||
protected eod_swap_position CopyEodPosition(eod_swap_position eod, eod_swap_position curretEod, trade td, DateTime valueDate, DateTime preSettleDate)
|
||||
protected eod_swap_position CopyEodPosition(eod_swap_position eod, eod_swap_position curretEod, trade td, DateTime valueDate, DateTime preSettleDate, decimal? corporateActionBeforeQuantity = null)
|
||||
{
|
||||
if (curretEod == null)
|
||||
{
|
||||
@@ -2648,7 +2670,7 @@ namespace YLErp.Modules.SwapModule
|
||||
decimal tax = um.ValueAddedTax ?? 0;
|
||||
if (valueDate > td.StartDate.Value && curretEod.PosiQuantity > 0)
|
||||
{
|
||||
decimal payment = CalcBondPayment(curretEod.UnderlyingCode, eod.ValueDate, valueDate, curretEod.PosiQuantity, shortRatio, directionRatio);
|
||||
decimal payment = CalcBondPayment(curretEod.UnderlyingCode, eod.ValueDate, valueDate, curretEod.PosiQuantity, shortRatio, directionRatio, corporateActionBeforeQuantity);
|
||||
curretEod.TdPosiDividend = DividendCalc.AfterTax(payment, tax);
|
||||
}
|
||||
curretEod.PosiDividendSum = eod.PosiQuantity > 0 ? Math.Round(eod.PosiDividendSum + curretEod.TdPosiDividend, 2) : 0;
|
||||
@@ -2710,7 +2732,7 @@ namespace YLErp.Modules.SwapModule
|
||||
/// <param name="curretEod"></param>
|
||||
/// <param name="td"></param>
|
||||
/// <param name="valueDate"></param>
|
||||
protected eod_swap_position UpdateEodPosition(swap_position swapPosition, eod_swap_position eod, eod_swap_position curretEod, trade td, DateTime valueDate, DateTime preSettleDate, List<swap_flow_event> unwindEvents)
|
||||
protected eod_swap_position UpdateEodPosition(swap_position swapPosition, eod_swap_position eod, eod_swap_position curretEod, trade td, DateTime valueDate, DateTime preSettleDate, List<swap_flow_event> unwindEvents, decimal? corporateActionBeforeQuantity = null)
|
||||
{
|
||||
if (curretEod == null)
|
||||
{
|
||||
@@ -2744,7 +2766,7 @@ namespace YLErp.Modules.SwapModule
|
||||
// 修改,互换事件会影响待实现的分红的,现在要算上
|
||||
if (valueDate > td.StartDate.Value && (curretEod.PosiQuantity > 0))
|
||||
{
|
||||
decimal payment = CalcBondPayment(curretEod.UnderlyingCode, eod.ValueDate, valueDate, curretEod.PosiQuantity, shortRatio, directionRatio);
|
||||
decimal payment = CalcBondPayment(curretEod.UnderlyingCode, eod.ValueDate, valueDate, curretEod.PosiQuantity, shortRatio, directionRatio, corporateActionBeforeQuantity);
|
||||
curretEod.TdPosiDividend = DividendCalc.AfterTax(payment, tax);
|
||||
}
|
||||
curretEod.RealizedMtmPnL = eod.RealizedMtmPnL + curretEod.TdCloseMtmPnl;
|
||||
@@ -2871,7 +2893,8 @@ namespace YLErp.Modules.SwapModule
|
||||
/// <param name="position"></param>
|
||||
/// <param name="td"></param>
|
||||
/// <param name="settleDate"></param>
|
||||
protected eod_swap_position SaveCurrentEodInitalPosi(swap_position position, trade td, DateTime settleDate, DateTime preSettleDate, List<swap_flow_event> unwindEvents)
|
||||
protected eod_swap_position SaveCurrentEodInitalPosi(swap_position position, trade td, DateTime settleDate,
|
||||
DateTime preSettleDate, List<swap_flow_event> unwindEvents, decimal? corporateActionBeforeQuantity = null)
|
||||
{
|
||||
eod_swap_position curretEod = new eod_swap_position();
|
||||
var um = GetUnderlyingData(position.UnderlyingCode);
|
||||
@@ -2924,7 +2947,7 @@ namespace YLErp.Modules.SwapModule
|
||||
if (!hasSwapEvent && settleDate > td.StartDate.Value && curretEod.PosiQuantity > 0)
|
||||
{
|
||||
decimal tax = um.ValueAddedTax ?? 0;
|
||||
decimal payment = CalcBondPayment(curretEod.UnderlyingCode, td.StartDate.Value, settleDate, curretEod.PosiQuantity, shortRatio, directionRatio);
|
||||
decimal payment = CalcBondPayment(curretEod.UnderlyingCode, td.StartDate.Value, settleDate, curretEod.PosiQuantity, shortRatio, directionRatio, corporateActionBeforeQuantity);
|
||||
payment = DividendCalc.AfterTax(payment, tax);
|
||||
//var consumedDividend = CalcConsumedDividend(curretEod, unwindEvents); 首日应该没有分红
|
||||
curretEod.TdPosiDividend = payment;
|
||||
|
||||
Reference in New Issue
Block a user