refactor(swap): 误导布尔 swap 改名 interestWindowEmpty——按"计息窗口为空"本质命名
InitInterestDate 返回 true 的语义是"计息窗口为空→不计利息"(利率与金额归零), 判定只看日期窗口(interestStart>interestEnd)、与事件类型无关;旧名 swap 把 典型原因(互换当日已结息)当成了效果的名字,读者会误读为事件类型判断。 - SwapDealService: GetInterests 局部变量 + GetFloatRate/CalcUnwindInterest/ InitSwapDealInterest/CalcMarginInterest 四个签名参数及分支/日志/注释同步 - SwapTradeBaseService.InitInterestDate 返回值语义补入 XML 文档 - MarginInterestGoldenReplay/Shadow 测试命名参数同步 零行为变更。 验证:相关套件 51/51;全量 978 例 145 败与基线 diff=0
This commit is contained in:
@@ -128,7 +128,7 @@ namespace UnitTestProject.Modules.SwapModule.Margin
|
||||
newEvt = svc.CalcMarginInterest(td, valueDate, valueDate, posClone, rate,
|
||||
pos.InterestPrincipalFix, pos.InterestPrincipalFix, 1.0m,
|
||||
annualDays, calcFirst, calcLast, preEod,
|
||||
(int)SwapEventTypeEnum.自动互换, add: false, settment: true, swap: false);
|
||||
(int)SwapEventTypeEnum.自动互换, add: false, settment: true, interestWindowEmpty: false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace UnitTestProject.Modules.SwapModule.Margin
|
||||
// 新方法(独立 preEod,相同初始值)
|
||||
var newEvt = svc.CalcMarginInterest(td, valueDate, valueDate, position, Rate, Principal, Principal, 1m,
|
||||
AnnualDays, calcFirst: true, calcLast: true,
|
||||
CreatePreEod(StartDate.AddDays(4), profitSum), 0, add: false, settment: true, swap: false);
|
||||
CreatePreEod(StartDate.AddDays(4), profitSum), 0, add: false, settment: true, interestWindowEmpty: false);
|
||||
|
||||
Console.WriteLine($"旧: I={oldI} Td={oldTd}");
|
||||
Console.WriteLine($"新: I={newEvt.InterestAmount} Td={newEvt.TdInterestAmount} ClosePnL={newEvt.InterestClosePnL}");
|
||||
@@ -114,7 +114,7 @@ namespace UnitTestProject.Modules.SwapModule.Margin
|
||||
|
||||
var newEvt = svc.CalcMarginInterest(td, valueDate, valueDate, position, Rate, Principal, Principal, 1m,
|
||||
AnnualDays, calcFirst: true, calcLast: true,
|
||||
new eod_swap_position { id = 0 }, 0, add: false, settment: true, swap: false);
|
||||
new eod_swap_position { id = 0 }, 0, add: false, settment: true, interestWindowEmpty: false);
|
||||
|
||||
Assert.AreEqual(oldI, newEvt.InterestAmount, "InterestAmount 一致");
|
||||
Assert.AreEqual(oldTd, newEvt.TdInterestAmount, "TdInterestAmount 一致");
|
||||
@@ -143,7 +143,7 @@ namespace UnitTestProject.Modules.SwapModule.Margin
|
||||
// 新方法:notional = posiPrincipal(无差分、无 orginPv)
|
||||
var newEvt = svc.CalcMarginInterest(td, valueDate, valueDate, position, Rate, Principal, Principal, 1m,
|
||||
AnnualDays, calcFirst: true, calcLast: false,
|
||||
CreatePreEod(StartDate.AddDays(4), profitSum), 0, add: false, settment: false, swap: false);
|
||||
CreatePreEod(StartDate.AddDays(4), profitSum), 0, add: false, settment: false, interestWindowEmpty: false);
|
||||
|
||||
Console.WriteLine($"旧: I={oldI} Td={oldTd}");
|
||||
Console.WriteLine($"新: I={newEvt.InterestAmount} Td={newEvt.TdInterestAmount}");
|
||||
@@ -172,7 +172,7 @@ namespace UnitTestProject.Modules.SwapModule.Margin
|
||||
var newEvt = svc.CalcMarginInterest(td, valueDate, valueDate, position, Rate,
|
||||
Principal * closePct, Principal, closePct,
|
||||
AnnualDays, calcFirst: true, calcLast: false,
|
||||
CreatePreEod(StartDate.AddDays(4), profitSum), 0, add: false, settment: false, swap: false);
|
||||
CreatePreEod(StartDate.AddDays(4), profitSum), 0, add: false, settment: false, interestWindowEmpty: false);
|
||||
|
||||
Console.WriteLine($"旧: I={oldI} Td={oldTd}");
|
||||
Console.WriteLine($"新: I={newEvt.InterestAmount} Td={newEvt.TdInterestAmount}");
|
||||
@@ -191,7 +191,7 @@ namespace UnitTestProject.Modules.SwapModule.Margin
|
||||
var svc = new StubSvc();
|
||||
var newEvt = svc.CalcMarginInterest(td, valueDate, valueDate, position, Rate, Principal, Principal, 1m,
|
||||
AnnualDays, calcFirst: true, calcLast: false,
|
||||
CreatePreEod(StartDate.AddDays(4), 820m), 0, add: false, settment: false, swap: true);
|
||||
CreatePreEod(StartDate.AddDays(4), 820m), 0, add: false, settment: false, interestWindowEmpty: true);
|
||||
|
||||
Assert.AreEqual(0m, newEvt.InterestAmount, "互换利息归零");
|
||||
Assert.AreEqual(0m, newEvt.TdInterestAmount, "互换 TdInterestAmount 归零");
|
||||
|
||||
@@ -655,8 +655,8 @@ namespace YLErp.Modules.SwapModule
|
||||
|
||||
// 计算计息区间
|
||||
int interestPeriod = position.interest_rest_days ?? 1;
|
||||
// true 跳过 不计利息; false 正常利息
|
||||
bool swap = InitInterestDate(unwindDate, preDealDate, td, tdClose, out DateTime startDate, out DateTime endDate);
|
||||
// true=计息窗口为空(不计利息,利率与金额归零;典型场景=互换当日已结息,但判定只看日期窗口,与事件类型无关)
|
||||
bool interestWindowEmpty = InitInterestDate(unwindDate, preDealDate, td, tdClose, out DateTime startDate, out DateTime endDate);
|
||||
|
||||
// 获取利率(保证金/融资腿共用:SwapIntervalList 取当日适用固定利率 + 精度收口)
|
||||
decimal rate = Math.Round(GetFixedRate(position, unwindDate), InterestCalculationPrecision, MidpointRounding.AwayFromZero); // 做精度调整 原数据有精度误差
|
||||
@@ -670,7 +670,7 @@ namespace YLErp.Modules.SwapModule
|
||||
// 保证金腿: 计息基数 = InterestPrincipalFix(保证金余额),无融资腿差分公式与 orginPv 维度 hack
|
||||
interests.Add(CalcMarginInterest(td, valueDate, endDate, positionClone, rate,
|
||||
position.InterestPrincipalFix * closePrecent, position.InterestPrincipalFix,
|
||||
closePrecent, annualDays, calcFirst, effectiveCalcLast, preEodPosition, eventType, add, settment, swap));
|
||||
closePrecent, annualDays, calcFirst, effectiveCalcLast, preEodPosition, eventType, add, settment, interestWindowEmpty));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -697,7 +697,7 @@ namespace YLErp.Modules.SwapModule
|
||||
closePrincipal = closePosiNotionalValue;
|
||||
}
|
||||
|
||||
decimal floatRate = GetFloatRate(position, preEodPosition, td.StartDate.Value, endDate, interestPeriod, swap, positionClone, effectiveCalcLast);
|
||||
decimal floatRate = GetFloatRate(position, preEodPosition, td.StartDate.Value, endDate, interestPeriod, interestWindowEmpty, positionClone, effectiveCalcLast);
|
||||
|
||||
// 根据场景计算利息
|
||||
if (settment)
|
||||
@@ -713,7 +713,7 @@ namespace YLErp.Modules.SwapModule
|
||||
? GetConsumedInterest(td.id, position.id, endDate)
|
||||
: 0m;
|
||||
interests.Add(CalcUnwindInterest(td, valueDate, endDate, positionClone, rate, floatRate, posiPrincipal,
|
||||
closePrincipal, newClosePercent, annualDays, preEodPosition, eventType, add, swap, orginPv, calcFirst,
|
||||
closePrincipal, newClosePercent, annualDays, preEodPosition, eventType, add, interestWindowEmpty, orginPv, calcFirst,
|
||||
effectiveCalcLast, consumedInterest));
|
||||
}
|
||||
}
|
||||
@@ -858,7 +858,7 @@ namespace YLErp.Modules.SwapModule
|
||||
/// <summary>
|
||||
/// 获取浮动利率
|
||||
/// </summary>
|
||||
private decimal GetFloatRate(swap_position position, eod_swap_position preEod, DateTime startDate, DateTime endDate, int period, bool swap, swap_position positionClone, bool calcLast = true)
|
||||
private decimal GetFloatRate(swap_position position, eod_swap_position preEod, DateTime startDate, DateTime endDate, int period, bool interestWindowEmpty, swap_position positionClone, bool calcLast = true)
|
||||
{
|
||||
if (string.IsNullOrEmpty(position.FloatRateUnderlyingCode)) return position.FloatRate;
|
||||
|
||||
@@ -883,7 +883,7 @@ namespace YLErp.Modules.SwapModule
|
||||
// EQD-6968 口径自洽化:不算尾(calcLast=false)时 endDate 当天不计息,其定盘一概不取
|
||||
// (有价也不取)——事件/回写利率与金额同源(末段已消费利率),杜绝"上午/下午落库利率不同"。
|
||||
// 剩余持仓的新周期利率由 SwapEodPositionService 的"重置日再定盘"显式获取,不靠此处顺带。
|
||||
if (!swap && !calcLast && isResetDay)
|
||||
if (!interestWindowEmpty && !calcLast && isResetDay)
|
||||
{
|
||||
var keptNoFetch = preEod.id != 0 ? preEod.FloatRate : position.FloatRate;
|
||||
SwapCalcTrace.Critical(
|
||||
@@ -897,7 +897,7 @@ namespace YLErp.Modules.SwapModule
|
||||
position.FloatRate = positionClone.FloatRate = rate;
|
||||
return position.FloatRate;
|
||||
}
|
||||
if (!swap)
|
||||
if (!interestWindowEmpty)
|
||||
{
|
||||
if (calcLast)
|
||||
{
|
||||
@@ -911,7 +911,7 @@ namespace YLErp.Modules.SwapModule
|
||||
$"FIX GetFloatRate p{position.id} {rateDate:yyyy-MM-dd}缺价且不算尾→沿用已有利率={kept:P6}(来源={(preEod.id != 0 ? "preEod.FloatRate" : "position.FloatRate")})");
|
||||
return kept;
|
||||
}
|
||||
SwapCalcTrace.Critical($"FIX GetFloatRate p{position.id} 互换事件(swap=true)→利率不参与,返回0");
|
||||
SwapCalcTrace.Critical($"FIX GetFloatRate p{position.id} 计息窗口为空→利率不参与,返回0");
|
||||
return 0m;
|
||||
}
|
||||
|
||||
@@ -1013,12 +1013,12 @@ namespace YLErp.Modules.SwapModule
|
||||
/// 定位:SwapCalcTrace 落盘 AccrueEod/AccrualPeriod 的 notional/days/rate/accrued;盘中 accrualBasis 可从 trace 的 notional 反推。
|
||||
/// </remarks>
|
||||
/// <param name="settment">true=收盘归档(EOD),false=盘中平仓/互换。</param>
|
||||
/// <param name="swap">互换事件(仅盘中生效,true 时利息归零,同 InitSwapDealInterest)。</param>
|
||||
/// <param name="interestWindowEmpty">计息窗口为空(仅盘中生效,true 时利息归零,同 InitSwapDealInterest;典型场景=互换当日已结息)。</param>
|
||||
public swap_flow_event CalcMarginInterest(
|
||||
trade td, DateTime valueDate, DateTime endDate, swap_position position, decimal rate,
|
||||
decimal closePrincipal, decimal posiPrincipal, decimal closePercent,
|
||||
int annualDays, bool calcFirst, bool calcLast,
|
||||
eod_swap_position preEod, int eventType, bool add, bool settment, bool swap)
|
||||
eod_swap_position preEod, int eventType, bool add, bool settment, bool interestWindowEmpty)
|
||||
{
|
||||
// 当日是否计息(算头算尾)——同 CalcEodInterest
|
||||
bool calcToday = true;
|
||||
@@ -1054,8 +1054,8 @@ namespace YLErp.Modules.SwapModule
|
||||
UnwindDate = settment ? valueDate : endDate
|
||||
};
|
||||
|
||||
// 互换事件:利息归零(同 InitSwapDealInterest)
|
||||
if (swap && !settment)
|
||||
// 计息窗口为空:利息归零(同 InitSwapDealInterest;典型场景=互换事件)
|
||||
if (interestWindowEmpty && !settment)
|
||||
{
|
||||
interest.InterestAmount = 0m;
|
||||
interest.TdInterestAmount = 0m;
|
||||
@@ -1120,7 +1120,7 @@ namespace YLErp.Modules.SwapModule
|
||||
/// <summary>
|
||||
/// 计算盘中利息(平仓/互换)
|
||||
/// </summary>
|
||||
private swap_flow_event CalcUnwindInterest(trade td, DateTime valueDate, DateTime endDate, swap_position position, decimal rate, decimal floatRate, decimal posiPrincipal, decimal closePrincipal, decimal closePercent, int annualDays, eod_swap_position preEod, int eventType, bool add, bool swap, decimal orginPv, bool calcFirst, bool calcLast, decimal consumedInterest = 0m)
|
||||
private swap_flow_event CalcUnwindInterest(trade td, DateTime valueDate, DateTime endDate, swap_position position, decimal rate, decimal floatRate, decimal posiPrincipal, decimal closePrincipal, decimal closePercent, int annualDays, eod_swap_position preEod, int eventType, bool add, bool interestWindowEmpty, decimal orginPv, bool calcFirst, bool calcLast, decimal consumedInterest = 0m)
|
||||
{
|
||||
if (preEod.id == 0)
|
||||
{
|
||||
@@ -1135,7 +1135,7 @@ namespace YLErp.Modules.SwapModule
|
||||
preEod.ValueDate = td.StartDate.Value.AddDays(-1);
|
||||
}
|
||||
|
||||
return InitSwapDealInterest(td, valueDate, endDate, rate, position, add, swap, posiPrincipal,
|
||||
return InitSwapDealInterest(td, valueDate, endDate, rate, position, add, interestWindowEmpty, posiPrincipal,
|
||||
closePrincipal, closePercent, annualDays, eventType, preEod,
|
||||
orginPv, calcFirst, calcLast, consumedInterest);
|
||||
}
|
||||
@@ -1172,7 +1172,7 @@ namespace YLErp.Modules.SwapModule
|
||||
/// <param name="rate">计息年化利率</param>
|
||||
/// <param name="position">利息腿</param>
|
||||
/// <param name="add">是否新增</param>
|
||||
/// <param name="swap">是否已互换</param>
|
||||
/// <param name="interestWindowEmpty">计息窗口为空(InitInterestDate 判定:true=本次不计利息,利率与金额归零;典型场景=互换当日已结息)</param>
|
||||
/// <param name="preEodPosition">上一日终归档</param>
|
||||
/// <param name="posiNotionalValue">当日适用名义本金</param>
|
||||
/// <param name="closePosiNotionalValue">当日平仓名义本金</param>
|
||||
@@ -1184,7 +1184,7 @@ namespace YLErp.Modules.SwapModule
|
||||
decimal rate,
|
||||
swap_position position,
|
||||
bool add,
|
||||
bool swap,
|
||||
bool interestWindowEmpty,
|
||||
decimal posiNotionalValue,
|
||||
decimal closePosiNotionalValue,
|
||||
decimal closePrecent,
|
||||
@@ -1218,7 +1218,7 @@ namespace YLErp.Modules.SwapModule
|
||||
// 保证金腿已走 CalcMarginInterest(不经过本方法),orginPv 维度重映射不再需要;
|
||||
// orginPv 此处仅对融资腿生效(差分公式 accrualBasis = TdInterestPrincipal + posiPrincipal - orginPv)。
|
||||
|
||||
if (swap)
|
||||
if (interestWindowEmpty)
|
||||
{
|
||||
interest.InterestAmount = 0; // 利息金额
|
||||
interest.TdInterestAmount = 0; // 当日新增利息
|
||||
|
||||
@@ -365,6 +365,8 @@ namespace YLErp.Modules.SwapModule
|
||||
/// <param name="interestMode">计息方式</param>
|
||||
/// <param name="interestStart">计息开始日期</param>
|
||||
/// <param name="interestEnd">计息结束日期</param>
|
||||
/// <returns>true=计息窗口为空(interestStart>interestEnd,本次不计利息,调用方将利率与金额归零;
|
||||
/// 典型场景=互换当日已结息/不算头首日,但判定只看日期窗口,与事件类型无关);false=正常计息。</returns>
|
||||
public bool InitInterestDate(DateTime valueDate, DateTime? preSettleDate, trade td, bool tdClose, out DateTime interestStart, out DateTime interestEnd)
|
||||
{
|
||||
interestStart = td.StartDate.Value;
|
||||
|
||||
Reference in New Issue
Block a user