Merge branch 'glms/feature/1.4.2' of http://git.yiliantech.com/gitlab/otc-dev/zszq-trs into glms/feature/1.4.2
This commit is contained in:
@@ -93,39 +93,9 @@ public static class PenaltyInterestFeeMerger
|
||||
}
|
||||
|
||||
// 复利承接:实际滚动基数中已并入部分(①)+ 段内实际已计利息(②)。单利无并本金语义恒 0。
|
||||
// ① 的取值依赖平仓日是否为重置日、有无日终快照(数据契约):
|
||||
// 段中平仓 + 有快照:TdInterestPrincipal 即当前段滚动基数(=本金+①),直接作差;
|
||||
// 段中平仓 + 无快照:兜底取 normalEvent.InterestPrincipal——复利重放(CalcDailyCompoundInterest)
|
||||
// 会把它写为末次并本金后的基数(=被平份额本金+①),同样是实际值而非推导值;
|
||||
// 重置日当天平仓:快照基数仍是【上一段】的(今日并入尚未发生),须改取
|
||||
// preEod.InterestIncomeSum(昨日全部待实现利息 = 今日并入新段基数的那部分)。
|
||||
decimal capitalized = 0m, carryIn = 0m;
|
||||
if (isCompound)
|
||||
{
|
||||
var periodDays = position.interest_rest_days ?? 1;
|
||||
var unwindOnResetDay = SwapDealService.IsResetDay(unwindDate, position.PosiStartDate, periodDays);
|
||||
if (unwindOnResetDay)
|
||||
{
|
||||
capitalized = (preEod?.InterestIncomeSum ?? 0m) * share;
|
||||
if (preEod == null && (unwindDate - position.PosiStartDate).Days >= periodDays)
|
||||
trace?.Note($"PENALTY|p{position.id} 注意 无preEod且平仓日=重置日:①退化0(此前重置并入额缺失,请核对日终归档完整性)");
|
||||
}
|
||||
else if (preEod != null)
|
||||
{
|
||||
capitalized = Math.Max(0m, preEod.TdInterestPrincipal * share - closePrincipal);
|
||||
}
|
||||
else
|
||||
{
|
||||
capitalized = Math.Max(0m, normalEvent.InterestPrincipal - closePrincipal);
|
||||
}
|
||||
// ① 不得超过实结金额(数据异常时钳制并留痕,避免负②进入计息)
|
||||
if (capitalized > Math.Max(0m, normalEvent.InterestAmount))
|
||||
{
|
||||
trace?.Note($"PENALTY|p{position.id} 注意 承接①钳制:推导 {capitalized:F4} > 实结 {normalEvent.InterestAmount:F4}(快照/事件数据异常,请核对 preEod.TdInterestPrincipal/InterestIncomeSum)");
|
||||
capitalized = Math.Max(0m, normalEvent.InterestAmount);
|
||||
}
|
||||
carryIn = normalEvent.InterestAmount - capitalized;
|
||||
}
|
||||
var (capitalized, carryIn) = isCompound
|
||||
? ResolveCompoundCarry(position, normalEvent, preEod, closePrincipal, share, unwindDate, trace)
|
||||
: (0m, 0m);
|
||||
|
||||
var policy = AccrualPolicy.BuildEod(position, annualDays, isCompound);
|
||||
// 锚点 = PosiStartDate:与正常计息重放(CalcDailyCompoundInterest 的分段网格)一致,延期腿勿用 td.StartDate
|
||||
@@ -149,4 +119,54 @@ public static class PenaltyInterestFeeMerger
|
||||
$"罚息={penalty:F2} → InterestFee {feeBefore:F2}→{normalEvent.InterestFee:F2} PnL含罚息={normalEvent.InterestClosePnL:F2}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 复利承接量:① 实际滚动基数中已并入部分 + ② 最近重置日后实际已计利息(= 实结 − ①)。
|
||||
///
|
||||
/// ① 的取值依赖平仓日是否为重置日、有无日终快照(数据契约):
|
||||
/// 段中平仓 + 有快照:TdInterestPrincipal 即当前段滚动基数(=本金+①),直接作差;
|
||||
/// 段中平仓 + 无快照:兜底取 normalEvent.InterestPrincipal——复利重放(CalcDailyCompoundInterest)
|
||||
/// 会把它写为末次并本金后的基数(=被平份额本金+①),同样是实际值而非推导值;
|
||||
/// 重置日当天平仓:快照基数仍是【上一段】的(今日并入尚未发生),须改取
|
||||
/// preEod.InterestIncomeSum(昨日全部待实现利息 = 今日并入新段基数的那部分)。
|
||||
/// </summary>
|
||||
private static (decimal Capitalized, decimal CarryIn) ResolveCompoundCarry(
|
||||
swap_position position, swap_flow_event normalEvent, eod_swap_position? preEod,
|
||||
decimal closePrincipal, decimal share, DateTime unwindDate, AccrualTrace? trace)
|
||||
{
|
||||
var periodDays = position.interest_rest_days ?? 1;
|
||||
var unwindOnResetDay = SwapDealService.IsResetDay(unwindDate, position.PosiStartDate, periodDays);
|
||||
|
||||
var capitalized = 0m;
|
||||
if (unwindOnResetDay)
|
||||
{
|
||||
capitalized = (preEod?.InterestIncomeSum ?? 0m) * share;
|
||||
if (preEod == null && (unwindDate - position.PosiStartDate).Days >= periodDays)
|
||||
trace?.Note($"PENALTY|p{position.id} 注意 无preEod且平仓日=重置日:①退化0(此前重置并入额缺失,请核对日终归档完整性)");
|
||||
}
|
||||
else if (preEod != null)
|
||||
{
|
||||
capitalized = Math.Max(0m, preEod.TdInterestPrincipal * share - closePrincipal);
|
||||
}
|
||||
else
|
||||
{
|
||||
capitalized = Math.Max(0m, normalEvent.InterestPrincipal - closePrincipal);
|
||||
// 兜底①=0 但账龄已过重置周期:复利每周期并本,理应 ①>0——多为 interestWindowEmpty
|
||||
// (当日已结息)早退未重放覆盖种子值、或日终归档缺失。留痕含两侧基数与账龄,供直接定位根因。
|
||||
var ageDays = (unwindDate - position.PosiStartDate).Days;
|
||||
if (capitalized == 0m && ageDays >= periodDays)
|
||||
trace?.Note(
|
||||
$"PENALTY|p{position.id} 注意 无preEod兜底①=0但账龄{ageDays}天≥重置周期{periodDays}天:" +
|
||||
$"事件基数{normalEvent.InterestPrincipal:F2}=平仓本金{closePrincipal:F2}(疑似interestWindowEmpty种子未重放/日终归档缺失," +
|
||||
$"请核对swap_flow_event.InterestPrincipal重放回写与eod_swap_position归档)");
|
||||
}
|
||||
|
||||
// ① 不得超过实结金额(数据异常时钳制并留痕,避免负②进入计息)
|
||||
if (capitalized > Math.Max(0m, normalEvent.InterestAmount))
|
||||
{
|
||||
trace?.Note($"PENALTY|p{position.id} 注意 承接①钳制:推导 {capitalized:F4} > 实结 {normalEvent.InterestAmount:F4}(快照/事件数据异常,请核对 preEod.TdInterestPrincipal/InterestIncomeSum)");
|
||||
capitalized = Math.Max(0m, normalEvent.InterestAmount);
|
||||
}
|
||||
return (capitalized, normalEvent.InterestAmount - capitalized);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user