refactor(eod): 提取InterestIncomeCalc.DailyAccrual/RollRealized + MtmCalc.ReturnLegProfitSum
候选B: 3处日利息 principal*(rate+float)/annualDays → InterestIncomeCalc.DailyAccrual 候选D: 4处 PosiProfitSum=Mtm+Div+Fee → MtmCalc.ReturnLegProfitSum 候选E: 4对 RealizedInterest/RealizedInterestFee 滚存 → InterestIncomeCalc.RollRealized SwapModule零回归(7基线/510通过)
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
namespace YLErp.Modules.SwapModule.ReturnLegs;
|
||||
|
||||
/// <summary>
|
||||
/// EOD 利息腿展示值计算——轻量单日公式(非 SwapDealService 的分段计息引擎)。
|
||||
/// </summary>
|
||||
public static class InterestIncomeCalc
|
||||
{
|
||||
/// <summary>日应计利息 = 本金 × (固定利率 + 浮动利差) ÷ 年化天数(若年化)。
|
||||
/// 原 3 处内联 `principal*(rate+float); if(annualized) /=annualDays` 收口到此。</summary>
|
||||
public static decimal DailyAccrual(decimal principal, decimal rate, decimal floatRate, bool isAnnualized, int annualDays)
|
||||
{
|
||||
var amount = principal * (rate + floatRate);
|
||||
return isAnnualized ? amount / annualDays : amount;
|
||||
}
|
||||
|
||||
/// <summary>已实现利息滚存。(prev + today×ratio, prevFee + todayFee)。
|
||||
/// 原 4 处内联 2 行赋值收口到此。</summary>
|
||||
public static (decimal Interest, decimal Fee) RollRealized(
|
||||
decimal prevInterest, decimal prevFee, decimal closeInterest, decimal closeInterestFee, int ratio)
|
||||
=> (prevInterest + closeInterest * ratio, prevFee + closeInterestFee);
|
||||
}
|
||||
@@ -19,4 +19,8 @@ public static class MtmCalc
|
||||
/// <param name="ratio">收取=1, 支付=-1。</param>
|
||||
public static decimal UnrealizedPnl(decimal price, decimal costGrossPrice, decimal qty, decimal contractSize, int shortRatio, int ratio)
|
||||
=> (price - costGrossPrice) * qty * contractSize * shortRatio * ratio;
|
||||
|
||||
/// <summary>浮动端总未实现盈亏 = 盯市盈亏 + 分红 + 待结费用。原 4 处内联收口到此。</summary>
|
||||
public static decimal ReturnLegProfitSum(decimal mtmPnl, decimal dividendSum, decimal feePending)
|
||||
=> mtmPnl + dividendSum + feePending;
|
||||
}
|
||||
|
||||
@@ -1081,11 +1081,9 @@ namespace YLErp.Modules.SwapModule
|
||||
newEodPayPosition.TdCloseInterest = flowEvents.Sum(x => x.InterestAmount);
|
||||
newEodPayPosition.TdCloseInterestFee = newEodPayPosition.TdInterestFee;
|
||||
//持仓内容-利息腿-损益统计(本方视角)
|
||||
var intersetAcmount = newEodPayPosition.TdInterestPrincipal * (newEodPayPosition.TdInterestRate + newEodPayPosition.FloatRate);
|
||||
if (position.IsAnnualized)
|
||||
{
|
||||
intersetAcmount /= tradeExtend.AnnualDays;
|
||||
}
|
||||
var intersetAcmount = InterestIncomeCalc.DailyAccrual(
|
||||
newEodPayPosition.TdInterestPrincipal, newEodPayPosition.TdInterestRate, newEodPayPosition.FloatRate,
|
||||
newEodPayPosition.IsAnnualized, tradeExtend.AnnualDays);
|
||||
newEodPayPosition.TdInterestIncome = intersetAcmount;// 要算一下当天产生的利息
|
||||
var interestIncomeBeforeSettlement = eodPayPosition.InterestIncomeSum + newEodPayPosition.TdInterestIncome;
|
||||
var interestFeeBeforeSettlement = eodPayPosition.InterestFeeSum + newEodPayPosition.TdInterestFee;
|
||||
@@ -1111,8 +1109,9 @@ namespace YLErp.Modules.SwapModule
|
||||
newEodPayPosition.SwapPositionValue = PositionValueCalc.Calc(newEodPayPosition.InterestProfitSum, newEodPayPosition.PosiProfitSum, (int)ratio);
|
||||
|
||||
//累计已实现
|
||||
newEodPayPosition.RealizedInterest = eodPayPosition.RealizedInterest + newEodPayPosition.TdCloseInterest * ratio;
|
||||
newEodPayPosition.RealizedInterestFee = eodPayPosition.RealizedInterestFee + newEodPayPosition.TdCloseInterestFee;
|
||||
var rolled = InterestIncomeCalc.RollRealized(eodPayPosition.RealizedInterest, eodPayPosition.RealizedInterestFee, newEodPayPosition.TdCloseInterest, newEodPayPosition.TdCloseInterestFee, ratio);
|
||||
newEodPayPosition.RealizedInterest = rolled.Interest;
|
||||
newEodPayPosition.RealizedInterestFee = rolled.Fee;
|
||||
SetFixedLegRealizedPnl(newEodPayPosition);
|
||||
var currencyRate = GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, valueDate, true,
|
||||
DirectionRatio.RateType(position.InterestDirection));
|
||||
@@ -1250,8 +1249,9 @@ namespace YLErp.Modules.SwapModule
|
||||
newEodPayPosition.SwapPositionValue = PositionValueCalc.Calc(newEodPayPosition.InterestProfitSum, newEodPayPosition.PosiProfitSum, (int)ratio);
|
||||
|
||||
//累计已实现
|
||||
newEodPayPosition.RealizedInterest = eodPayPosition.RealizedInterest + newEodPayPosition.TdCloseInterest * ratio;
|
||||
newEodPayPosition.RealizedInterestFee = eodPayPosition.RealizedInterestFee + newEodPayPosition.TdCloseInterestFee;
|
||||
var rolled = InterestIncomeCalc.RollRealized(eodPayPosition.RealizedInterest, eodPayPosition.RealizedInterestFee, newEodPayPosition.TdCloseInterest, newEodPayPosition.TdCloseInterestFee, ratio);
|
||||
newEodPayPosition.RealizedInterest = rolled.Interest;
|
||||
newEodPayPosition.RealizedInterestFee = rolled.Fee;
|
||||
SetFixedLegRealizedPnl(newEodPayPosition);
|
||||
var currencyRate = GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, valueDate, true,
|
||||
DirectionRatio.RateType(position.InterestDirection));
|
||||
@@ -1408,11 +1408,9 @@ namespace YLErp.Modules.SwapModule
|
||||
newEodPayPosition.TdCloseInterest = manualSettledInterestAmount + autoSettledInterestAmount;
|
||||
// intersetAcmount 是收盘后本金的一天应计展示值。算尾部分平仓时,下面的复利分支会改用
|
||||
// 平仓前全额本金重算当天新增,但跨日携带的 TdInterestPrincipal 仍只能是剩余本金。
|
||||
var intersetAcmount = newEodPayPosition.TdInterestPrincipal * (newEodPayPosition.TdInterestRate + newEodPayPosition.FloatRate);
|
||||
if (position.IsAnnualized)
|
||||
{
|
||||
intersetAcmount /= tradeExtend.AnnualDays;
|
||||
}
|
||||
var intersetAcmount = InterestIncomeCalc.DailyAccrual(
|
||||
newEodPayPosition.TdInterestPrincipal, newEodPayPosition.TdInterestRate, newEodPayPosition.FloatRate,
|
||||
newEodPayPosition.IsAnnualized, tradeExtend.AnnualDays);
|
||||
newEodPayPosition.TdInterestIncome = autoSwap
|
||||
? intersetAcmount
|
||||
: !hasPreviousEod
|
||||
@@ -1462,12 +1460,9 @@ namespace YLErp.Modules.SwapModule
|
||||
var accrualPrincipal = calcLast
|
||||
? fullPrincipal
|
||||
: newEodPayPosition.TdInterestPrincipal;
|
||||
newEodPayPosition.TdInterestIncome = accrualPrincipal
|
||||
* (newEodPayPosition.TdInterestRate + newEodPayPosition.FloatRate);
|
||||
if (position.IsAnnualized)
|
||||
{
|
||||
newEodPayPosition.TdInterestIncome /= tradeExtend.AnnualDays;
|
||||
}
|
||||
newEodPayPosition.TdInterestIncome = InterestIncomeCalc.DailyAccrual(
|
||||
accrualPrincipal, newEodPayPosition.TdInterestRate, newEodPayPosition.FloatRate,
|
||||
newEodPayPosition.IsAnnualized, tradeExtend.AnnualDays);
|
||||
}
|
||||
if (!autoSwap
|
||||
&& closePercent > 0m && closePercent < 1m
|
||||
@@ -1513,8 +1508,9 @@ namespace YLErp.Modules.SwapModule
|
||||
//累计已实现
|
||||
// RealizedInterest 只增不回滚:上日累计已实现 + 当日结息按方向后的金额。
|
||||
// 收取腿的 -37119.14 会把累计已实现更新为 -37119.14;后续普通 EOD 保持该值。
|
||||
newEodPayPosition.RealizedInterest = eodPayPosition.RealizedInterest + newEodPayPosition.TdCloseInterest * ratio;
|
||||
newEodPayPosition.RealizedInterestFee = eodPayPosition.RealizedInterestFee + newEodPayPosition.TdCloseInterestFee;
|
||||
var rolled = InterestIncomeCalc.RollRealized(eodPayPosition.RealizedInterest, eodPayPosition.RealizedInterestFee, newEodPayPosition.TdCloseInterest, newEodPayPosition.TdCloseInterestFee, ratio);
|
||||
newEodPayPosition.RealizedInterest = rolled.Interest;
|
||||
newEodPayPosition.RealizedInterestFee = rolled.Fee;
|
||||
SetFixedLegRealizedPnl(newEodPayPosition);
|
||||
var currencyRate = GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, valueDate, true,
|
||||
DirectionRatio.RateType(position.InterestDirection));
|
||||
@@ -1631,8 +1627,9 @@ namespace YLErp.Modules.SwapModule
|
||||
newEodPayPosition.SwapPositionValue = PositionValueCalc.Calc(newEodPayPosition.InterestProfitSum, newEodPayPosition.PosiProfitSum, (int)ratio);
|
||||
|
||||
//累计已实现
|
||||
newEodPayPosition.RealizedInterest = eodPayPosition.RealizedInterest + newEodPayPosition.TdCloseInterest * ratio;
|
||||
newEodPayPosition.RealizedInterestFee = eodPayPosition.RealizedInterestFee + newEodPayPosition.TdCloseInterestFee;
|
||||
var rolled = InterestIncomeCalc.RollRealized(eodPayPosition.RealizedInterest, eodPayPosition.RealizedInterestFee, newEodPayPosition.TdCloseInterest, newEodPayPosition.TdCloseInterestFee, ratio);
|
||||
newEodPayPosition.RealizedInterest = rolled.Interest;
|
||||
newEodPayPosition.RealizedInterestFee = rolled.Fee;
|
||||
SetFixedLegRealizedPnl(newEodPayPosition);
|
||||
var currencyRate = GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, valueDate, true,
|
||||
DirectionRatio.RateType(eodPayPosition.InterestDirection));
|
||||
@@ -1724,7 +1721,7 @@ namespace YLErp.Modules.SwapModule
|
||||
newEodPayPosition.TdPosiDividend = Math.Round(dividendIn * ratio, 2);
|
||||
newEodPayPosition.PosiMtmPnL = MtmCalc.UnrealizedPnl(newEodPayPosition.UnderlyingPrice, newEodPayPosition.PosiGrossPrice, newEodPayPosition.PosiQuantity, newEodPayPosition.ContractSize, shortRatio, (int)ratio);
|
||||
newEodPayPosition.PosiDividendSum = Math.Round(newEodPayPosition.TdPosiDividend - newEodPayPosition.TdCloseDividend, 2);
|
||||
newEodPayPosition.PosiProfitSum = newEodPayPosition.PosiMtmPnL + newEodPayPosition.PosiDividendSum + newEodPayPosition.PosiFeePending;
|
||||
newEodPayPosition.PosiProfitSum = MtmCalc.ReturnLegProfitSum(newEodPayPosition.PosiMtmPnL, newEodPayPosition.PosiDividendSum, newEodPayPosition.PosiFeePending);
|
||||
|
||||
//持仓价值
|
||||
newEodPayPosition.SwapPositionValue = PositionValueCalc.Calc(newEodPayPosition.InterestProfitSum, newEodPayPosition.PosiProfitSum);
|
||||
@@ -1797,7 +1794,7 @@ namespace YLErp.Modules.SwapModule
|
||||
curretEod.PosiMtmPnL = MtmCalc.UnrealizedPnl(curretEod.UnderlyingPrice, curretEod.PosiGrossPrice, curretEod.PosiQuantity, curretEod.ContractSize, shortRatio, directionRatio);
|
||||
//curretEod.TdPosiDividend = 0;
|
||||
//curretEod.PosiDividendSum = eod.PosiDividendSum + curretEod.TdPosiDividend;
|
||||
curretEod.PosiProfitSum = curretEod.PosiMtmPnL + curretEod.PosiDividendSum + curretEod.PosiFeePending;
|
||||
curretEod.PosiProfitSum = MtmCalc.ReturnLegProfitSum(curretEod.PosiMtmPnL, curretEod.PosiDividendSum, curretEod.PosiFeePending);
|
||||
curretEod.TdCloseFee = 0;
|
||||
curretEod.TdCloseQty = 0;
|
||||
curretEod.TdCloseMtmPnl = 0;
|
||||
@@ -1910,7 +1907,7 @@ namespace YLErp.Modules.SwapModule
|
||||
SetFloatingRealizedPnl(curretEod);
|
||||
curretEod.SwapPositionValue -= curretEod.TdCloseDividend;
|
||||
|
||||
curretEod.PosiProfitSum = curretEod.PosiMtmPnL + curretEod.PosiDividendSum + curretEod.PosiFeePending;
|
||||
curretEod.PosiProfitSum = MtmCalc.ReturnLegProfitSum(curretEod.PosiMtmPnL, curretEod.PosiDividendSum, curretEod.PosiFeePending);
|
||||
if (curretEod.PosiStatus == 1)
|
||||
{
|
||||
curretEod.PosiNotionalValue = 0;
|
||||
@@ -2083,7 +2080,7 @@ namespace YLErp.Modules.SwapModule
|
||||
|
||||
curretEod.UnderlyingMarketValue = MtmCalc.MarketValue(curretEod.UnderlyingPrice, curretEod.PosiQuantity, curretEod.ContractSize, shortRatio);
|
||||
curretEod.PosiMtmPnL = MtmCalc.UnrealizedPnl(curretEod.UnderlyingPrice, curretEod.PosiGrossPrice, curretEod.PosiQuantity, curretEod.ContractSize, shortRatio, directionRatio);
|
||||
curretEod.PosiProfitSum = curretEod.PosiMtmPnL + curretEod.PosiDividendSum + curretEod.PosiFeePending;
|
||||
curretEod.PosiProfitSum = MtmCalc.ReturnLegProfitSum(curretEod.PosiMtmPnL, curretEod.PosiDividendSum, curretEod.PosiFeePending);
|
||||
curretEod.RealizedMtmPnL = curretEod.TdCloseMtmPnl;
|
||||
curretEod.RealizedDividend = curretEod.TdCloseDividend;
|
||||
curretEod.RealizedFee = curretEod.TdCloseFee;
|
||||
|
||||
Reference in New Issue
Block a user