fix(swap): 修正掉期产品保证金利息计算逻辑
- 修正FloatingUnrealizedPnl字段定义,包含未结交易费用 - 更新MarginInterestGain和MarginInterestLoss字段的文档说明 - 添加CalculateSwapRealizedPnl方法处理保证金腿利息现金流方向 - 修正RealizedPnL计算逻辑,避免保证金利息重复计算 - 调整保证金本金和利息计算口径的一致性处理 - 修正保证金利息现金流方向与本金方向的相反关系
This commit is contained in:
@@ -186,7 +186,7 @@ namespace YLErp.DBModels
|
||||
public decimal PeriodAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 合约浮动端待实现收益,仅取浮动腿盯市收益,
|
||||
/// 合约浮动端待实现收益,取浮动腿盯市收益及未结交易费用,
|
||||
/// 不包含期间付息/分红,避免与 <see cref="PeriodAmount"/> 重复。
|
||||
/// </summary>
|
||||
public decimal FloatingUnrealizedPnl { get; set; }
|
||||
@@ -209,12 +209,14 @@ namespace YLErp.DBModels
|
||||
public decimal? PeriodPaymentValuation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 我方收取的保证金利息累计额,按保证金腿方向归集并取绝对值展示。
|
||||
/// 我方收取的保证金利息累计额。保证金腿原始“支付”方向表示
|
||||
/// 对手方向我方支付保证金,利息现金流方向与保证金本金方向相反。
|
||||
/// </summary>
|
||||
public decimal MarginInterestGain { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 我方支付的保证金利息累计额,按保证金腿方向归集并取绝对值展示。
|
||||
/// 我方支付的保证金利息累计额。保证金腿原始“收取”方向表示
|
||||
/// 我方收取对手方保证金,应向对手方支付利息;支付金额以负数展示。
|
||||
/// </summary>
|
||||
public decimal MarginInterestLoss { get; set; }
|
||||
}
|
||||
|
||||
@@ -1964,7 +1964,10 @@ namespace YLErp.Modules.SwapModule
|
||||
});
|
||||
eod_Swap.InterestPnL = interestPnL;
|
||||
eod_Swap.PostionValue = eodSwapPositions.Sum(s => s.SwapPositionValue);
|
||||
eod_Swap.RealizedPnL = eodSwapPositions.Sum(s => s.RealizedPnl);
|
||||
// 保证金腿的利息现金流方向与保证金本金方向相反。
|
||||
// 不能直接汇总 RealizedPnl,否则“收取客户保证金”的腿会把应支付给客户的
|
||||
// 利息作为收益相加。逐腿按利息方向转换后再生成框架合约已实现收益。
|
||||
eod_Swap.RealizedPnL = eodSwapPositions.Sum(CalculateSwapRealizedPnl);
|
||||
eod_Swap.TdRealizedPnL = eod_Swap.RealizedPnL - (preEodSwap?.RealizedPnL ?? 0);
|
||||
eod_Swap.TdCloseQty = positions.Sum(s => s.TdCloseQty);
|
||||
var initMargin = Convert.ToDecimal(tradeSpan?.InitialMargin ?? 0);
|
||||
@@ -2037,7 +2040,7 @@ namespace YLErp.Modules.SwapModule
|
||||
eod_Swap.TdRealizedPnL += x.TdCloseMtmPnl + x.TdCloseDividend + x.TdCloseFee + x.TdCloseInterest * ratio + x.TdCloseInterestFee;
|
||||
});
|
||||
eod_Swap.PostionValue = eodSwapPositions.Sum(s => s.SwapPositionValue);
|
||||
eod_Swap.RealizedPnL = eodSwapPositions.Sum(s => s.RealizedMtmPnL + s.RealizedDividend + s.RealizedFee + s.RealizedInterest + s.RealizedInterestFee);
|
||||
eod_Swap.RealizedPnL = eodSwapPositions.Sum(CalculateSwapRealizedPnl);
|
||||
eod_Swap.TdCloseQty = positions.Sum(s => s.TdCloseQty);
|
||||
var tradeInitMarginObj = DbContext.trade_initial_margin.FirstOrDefault(x => x.TradeId == td.id);
|
||||
var initMarginList = interestPositions.Where(x => x.InterestMode == (int)InterestModeEnum.初始预付金 && x.HappenDate == settleDate).ToList();
|
||||
@@ -2050,6 +2053,27 @@ namespace YLErp.Modules.SwapModule
|
||||
DbContext.SaveChanges();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 汇总单条日终腿的我方已实现收益。
|
||||
/// 浮动腿及普通利息腿维持数据库记录的方向;初始/追加预付金腿的利息
|
||||
/// 则与保证金本金方向相反。这样“收取对手方保证金”产生的利息会作为
|
||||
/// 我方支付给对手方的成本计入,而不会错误增加框架合约已实现收益。
|
||||
/// </summary>
|
||||
private decimal CalculateSwapRealizedPnl(eod_swap_position position)
|
||||
{
|
||||
var interestRatio = position.InterestDirection == (int)SwapDirectionEnum.收取 ? 1m : -1m;
|
||||
if (marginTypes.Contains(position.InterestMode))
|
||||
{
|
||||
interestRatio = -interestRatio;
|
||||
}
|
||||
|
||||
return position.RealizedMtmPnL
|
||||
+ position.RealizedDividend
|
||||
+ position.RealizedFee
|
||||
+ position.RealizedInterest * interestRatio
|
||||
+ position.RealizedInterestFee;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取多空组合 平仓详细
|
||||
/// </summary>
|
||||
@@ -2241,7 +2265,11 @@ namespace YLErp.Modules.SwapModule
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x))
|
||||
.Distinct());
|
||||
item.PeriodAmount = floatingLegs.Sum(x => x.RealizedDividend + x.PosiDividendSum);
|
||||
item.FloatingUnrealizedPnl = floatingLegs.Sum(x => x.PosiMtmPnL);
|
||||
// PosiProfitSum = 标的盯市收益 + 未结交易费用 + 待实现付息/分红。
|
||||
// 风险页的“合约浮动端待实现收益”需要保留未结交易费用,
|
||||
// 但期间付息/分红由 PeriodAmount 单列展示并参与对应估值口径,
|
||||
// 因此仅扣除 PosiDividendSum,不能直接使用 PosiMtmPnL。
|
||||
item.FloatingUnrealizedPnl = floatingLegs.Sum(x => x.PosiProfitSum - x.PosiDividendSum);
|
||||
item.InterestPaymentMethod = dividendPayDate == 0 ? "到期轧差" : "派息日支付";
|
||||
if (dividendPayDate == 0)
|
||||
{
|
||||
@@ -2251,12 +2279,34 @@ namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
item.PeriodPaymentValuation = item.FloatingUnrealizedPnl + item.position.InterestPnL;
|
||||
}
|
||||
// eod_swap 的保证金本金来自 trade_span;缺少 span 数据时会被保存为 0。
|
||||
// 本风险页改按日终保证金腿展示,且该页面保证金本金采用原始本金的 1/10 口径。
|
||||
// 利息仍使用原始本金累积值,不能同步缩放,否则会破坏保证金利息金额。
|
||||
item.position.InitMarginGain = marginLegs
|
||||
.Where(x => x.InterestMode == (int)InterestModeEnum.初始预付金
|
||||
&& x.InterestDirection == (int)SwapDirectionEnum.收取)
|
||||
.Sum(x => Math.Abs(x.InterestPrincipalFix)) / 10m;
|
||||
item.position.InitMarginLoss = marginLegs
|
||||
.Where(x => x.InterestMode == (int)InterestModeEnum.初始预付金
|
||||
&& x.InterestDirection == (int)SwapDirectionEnum.支付)
|
||||
.Sum(x => Math.Abs(x.InterestPrincipalFix)) / 10m;
|
||||
item.position.PostionMarginGain = marginLegs
|
||||
.Where(x => x.InterestMode == (int)InterestModeEnum.追加预付金
|
||||
&& x.InterestDirection == (int)SwapDirectionEnum.收取)
|
||||
.Sum(x => Math.Abs(x.InterestPrincipalFix)) / 10m;
|
||||
item.position.PostionMarginLoss = marginLegs
|
||||
.Where(x => x.InterestMode == (int)InterestModeEnum.追加预付金
|
||||
&& x.InterestDirection == (int)SwapDirectionEnum.支付)
|
||||
.Sum(x => Math.Abs(x.InterestPrincipalFix)) / 10m;
|
||||
|
||||
// 保证金本金方向与我方的利息现金流方向相反:原始“收取”保证金
|
||||
// 表示我方占用客户资金,应向客户支付利息;支付金额按负数展示。
|
||||
item.MarginInterestGain = marginLegs
|
||||
.Where(x => x.InterestDirection == (int)SwapDirectionEnum.收取)
|
||||
.Sum(x => Math.Abs(x.InterestIncomeSum));
|
||||
item.MarginInterestLoss = marginLegs
|
||||
.Where(x => x.InterestDirection == (int)SwapDirectionEnum.支付)
|
||||
.Sum(x => Math.Abs(x.InterestIncomeSum));
|
||||
item.MarginInterestLoss = marginLegs
|
||||
.Where(x => x.InterestDirection == (int)SwapDirectionEnum.收取)
|
||||
.Sum(x => -Math.Abs(x.InterestIncomeSum));
|
||||
}
|
||||
|
||||
var dv01 = query.Sum(O => O.position.dv01??0);
|
||||
|
||||
Reference in New Issue
Block a user