refactor: 提取融资腿计息领域模型 (AccrualPolicy/FundingLegRate/AccrualState/FundingLegAccrual) - 将 CalcDailySimpleInterestByEod 纯数学下沉至 FundingLegAccrual.AccrueSimpleEod, 消除 double 往返转换, 引入值对象收敛散落参数
This commit is contained in:
@@ -7,6 +7,7 @@ using YLErp.Derivatives.Interest;
|
||||
using YLErp.Helpers;
|
||||
using YLErp.Modules.DataProviderModule;
|
||||
using YLErp.Modules.EodModule;
|
||||
using YLErp.Modules.SwapModule.Accrual;
|
||||
using YLErp.Modules.SwapModule.FundingLegs;
|
||||
using YLErp.Modules.SwapModule.Margin;
|
||||
using YLErp.Modules.SwapModule.ReturnLegs;
|
||||
@@ -1552,49 +1553,54 @@ namespace YLErp.Modules.SwapModule
|
||||
/// </summary>
|
||||
public void CalcDailySimpleInterestByEod(eod_swap_position preEodPosition, DateTime endDate, DateTime tradeDate, swap_position position, decimal principal, decimal posiPrincipal, swap_flow_event flowEvent, int annualDays, bool needPrice, decimal floateRate, decimal closePercent, decimal orginPv, ref decimal InterestAmount, ref decimal TdInterestAmount)
|
||||
{
|
||||
decimal interestProfitSum = preEodPosition.InterestProfitSum;
|
||||
int interestPeriod = position.interest_rest_days ?? 1;
|
||||
double floatRate = Convert.ToDouble(floateRate);
|
||||
var calcDays = (endDate - tradeDate).Days;
|
||||
// 修复:首次操作时(preEodPosition.id == 0),TdInterestPrincipal 需要正确初始化
|
||||
// 首次操作(preEod.id == 0):计息基数按存量本金初始化——保留旧行为(含对 preEod 的就地修正)。
|
||||
if (preEodPosition.id == 0)
|
||||
{
|
||||
preEodPosition.TdInterestPrincipal = posiPrincipal;
|
||||
}
|
||||
|
||||
// 检查是否到达重置周期
|
||||
if (calcDays % interestPeriod == 0)
|
||||
// 取率:重置日按 interest_rule 重新定盘浮动利率(GLMS-JIATT-20260805 根因——
|
||||
// 重置日=平仓日必须用新利率,否则沿用旧周期利率并污染后续 EOD)。
|
||||
decimal effectiveFloat = floateRate;
|
||||
int interestPeriod = position.interest_rest_days ?? 1;
|
||||
if ((endDate - tradeDate).Days % interestPeriod == 0
|
||||
&& !string.IsNullOrEmpty(position.FloatRateUnderlyingCode))
|
||||
{
|
||||
// 获取新的浮动利率
|
||||
if (!string.IsNullOrEmpty(position.FloatRateUnderlyingCode))
|
||||
var fixingDate = IndexFixerBase.GetFixingDate(endDate, position.interest_rule);
|
||||
if (IndexFixer.TryGetFixing(fixingDate, position.FloatRateUnderlyingCode, out decimal fixing))
|
||||
{
|
||||
var fixingDate = IndexFixerBase.GetFixingDate(endDate, position.interest_rule);
|
||||
if (IndexFixer.TryGetFixing(fixingDate, position.FloatRateUnderlyingCode, out decimal fixing))
|
||||
{
|
||||
if (fixing != 0m) floatRate = Convert.ToDouble(fixing);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{fixingDate:yyyy年MM月dd日}的价格");
|
||||
}
|
||||
if (fixing != 0m) effectiveFloat = fixing;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{fixingDate:yyyy年MM月dd日}的价格");
|
||||
}
|
||||
}
|
||||
|
||||
flowEvent.FloatRate = Convert.ToDecimal(floatRate);
|
||||
var baseTdInterestPrincipal = preEodPosition.TdInterestPrincipal + posiPrincipal - orginPv;
|
||||
var baseInterestPrincipal = baseTdInterestPrincipal * closePercent;
|
||||
flowEvent.FloatRate = effectiveFloat;
|
||||
|
||||
// 修复:正确计算本次利息(基于实际持仓本金)
|
||||
decimal interest = baseInterestPrincipal * (flowEvent.InterestRate + Convert.ToDecimal(floatRate));
|
||||
decimal tdinterest = baseTdInterestPrincipal * (flowEvent.InterestRate + Convert.ToDecimal(floatRate));
|
||||
if (position.IsAnnualized)
|
||||
{
|
||||
interest /= annualDays;
|
||||
tdinterest /= annualDays;
|
||||
}
|
||||
|
||||
InterestAmount = Math.Round(interestProfitSum + interest, InterestCalculationPrecision, MidpointRounding.AwayFromZero);
|
||||
TdInterestAmount = Math.Round(tdinterest, InterestCalculationPrecision, MidpointRounding.AwayFromZero);
|
||||
// 纯数学下沉至 FundingLegAccrual(DDD 命名 + 末位生产精度 12 舍入),行为与上版逐字对齐。
|
||||
// 利率构成按腿型封装:固定腿 → FixedRate;浮动腿 → Spread + IndexFixing(沿用旧实现 InterestRate+浮动利率 的口径)。
|
||||
var isFixedLeg = string.IsNullOrEmpty(position.FloatRateUnderlyingCode);
|
||||
var legRate = isFixedLeg
|
||||
? new FundingLegRate(fixedRate: flowEvent.InterestRate)
|
||||
: new FundingLegRate(spread: flowEvent.InterestRate, indexFixing: effectiveFloat);
|
||||
var accrualPolicy = new AccrualPolicy(
|
||||
convention: AccrualBoundary.Both,
|
||||
isCompound: false,
|
||||
resetPeriodDays: position.interest_rest_days ?? 1,
|
||||
annualDays: annualDays,
|
||||
isAnnualized: position.IsAnnualized);
|
||||
var result = FundingLegAccrual.AccrueSimpleEod(
|
||||
priorUnrealized: preEodPosition.InterestProfitSum,
|
||||
priorAccrualPrincipal: preEodPosition.TdInterestPrincipal,
|
||||
positionPrincipal: posiPrincipal,
|
||||
closeRatio: closePercent,
|
||||
originalPv: orginPv,
|
||||
rate: legRate,
|
||||
policy: accrualPolicy);
|
||||
InterestAmount = result.Accrued;
|
||||
TdInterestAmount = result.AccruedToday;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user