refactor(swap): 保证金腿在 GetInterests 循环入口提前路由,融资腿分支树去除 MarginModes 依赖
- 保证金腿(5/6)在最外层路由并 continue:本金(InterestPrincipalFix×closePrecent)、 方向翻转、CalcMarginInterest 收口一处;newClosePercent 在保证金腿恒等于 closePrecent,直接传参 - 融资腿(1/2/9)分支树内 3 处 MarginModes.Contains 归零,后续融资腿改动与保证金解耦 - GetFixedRate 前移为两路共用(纯读函数,已验证无副作用) - 有意跳过 GetFloatRate:CalcMarginInterest 纯固定利率(FundingLegRate.Fixed/ FloatRate 恒 0),浮动取价对保证金无意义;脏数据 FloatRateUnderlyingCode 缺价时不再阻断保证金结算(唯一行为变化,方向更安全) - 验证:SwapModule 537 用例 A/B(带改/不带改)摘要完全一致(71F/459P/7S), 失败项均为本机无库环境失败;内网全量单测待跑 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -657,27 +657,30 @@ namespace YLErp.Modules.SwapModule
|
||||
// true 跳过 不计利息; false 正常利息
|
||||
bool swap = InitInterestDate(unwindDate, preDealDate, td, tdClose, out DateTime startDate, out DateTime endDate);
|
||||
|
||||
// 计算名义本金
|
||||
decimal closePrincipal;
|
||||
decimal posiPrincipal;
|
||||
decimal newClosePercent = closePrecent;
|
||||
var mode = (InterestModeEnum)position.InterestMode;
|
||||
// 获取利率(保证金/融资腿共用:SwapIntervalList 取当日适用固定利率 + 精度收口)
|
||||
decimal rate = Math.Round(GetFixedRate(position, unwindDate), InterestCalculationPrecision, MidpointRounding.AwayFromZero); // 做精度调整 原数据有精度误差
|
||||
|
||||
// ── 边界隔离:保证金腿(5/6)在循环最外层路由,后续融资腿分支树不感知保证金概念 ──
|
||||
// 有意跳过 GetFloatRate:CalcMarginInterest 纯固定利率(FundingLegRate.Fixed)且 FloatRate 恒 0,
|
||||
// 浮动取价/回写对保证金无意义;即使脏数据填了 FloatRateUnderlyingCode 且缺价,也不应阻断保证金结算。
|
||||
if (MarginModes.Contains(position.InterestMode))
|
||||
{
|
||||
// 保证金腿: 计息基数 = InterestPrincipalFix(保证金余额)
|
||||
closePrincipal = position.InterestPrincipalFix * closePrecent;
|
||||
posiPrincipal = position.InterestPrincipalFix;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 融资腿(1/2/9): 走策略工厂
|
||||
var r = FundingLegStrategyFactory.Get(mode)
|
||||
.CalcNotional(position.InterestPrincipalFix, posiNotionalValue, closePrecent);
|
||||
closePrincipal = r.ClosePrincipal;
|
||||
posiPrincipal = r.PosiPrincipal;
|
||||
newClosePercent = r.ClosePercent;
|
||||
positionClone.InterestDirection = MarginCalc.FlipDirection(position.InterestDirection);
|
||||
// 保证金腿: 计息基数 = InterestPrincipalFix(保证金余额),无融资腿差分公式与 orginPv 维度 hack
|
||||
interests.Add(CalcMarginInterest(td, valueDate, endDate, positionClone, rate,
|
||||
position.InterestPrincipalFix * closePrecent, position.InterestPrincipalFix,
|
||||
closePrecent, annualDays, calcFirst, calcLast || newCalcLast, preEodPosition, eventType, add, settment, swap));
|
||||
continue;
|
||||
}
|
||||
|
||||
// 计算名义本金(以下仅融资腿 1/2/9:走策略工厂)
|
||||
var mode = (InterestModeEnum)position.InterestMode;
|
||||
var r = FundingLegStrategyFactory.Get(mode)
|
||||
.CalcNotional(position.InterestPrincipalFix, posiNotionalValue, closePrecent);
|
||||
decimal closePrincipal = r.ClosePrincipal;
|
||||
decimal posiPrincipal = r.PosiPrincipal;
|
||||
decimal newClosePercent = r.ClosePercent;
|
||||
|
||||
// 根因位置:SwapEodPositionService.SaveAutoEodWithCloseInterestPosition 在平仓后收盘时传入
|
||||
// “收盘后剩余本金 + closePercent=1”,与盘中“平仓前本金 + 实际关闭比例”不是同一语义。
|
||||
// GetInterests 同时被盘中试算和 EOD 平仓后收盘调用:后者传入的
|
||||
@@ -686,29 +689,17 @@ namespace YLErp.Modules.SwapModule
|
||||
// 模式2(合约名义本金规模)的本次结息本金必须始终是实际平仓额,因此无条件覆盖,
|
||||
// 否则会错误地用剩余 70 结算本次平掉的 30。模式9(标的期初全价)的部分平仓
|
||||
// 仍保留既有的剩余/复利动态本金承接逻辑;仅最终全平时 posi=0,才覆盖以避免结息本金为 0。
|
||||
if ((InterestModeEnum)position.InterestMode == InterestModeEnum.合约名义本金规模
|
||||
|| ((InterestModeEnum)position.InterestMode == InterestModeEnum.标的期初全价
|
||||
if (mode == InterestModeEnum.合约名义本金规模
|
||||
|| (mode == InterestModeEnum.标的期初全价
|
||||
&& posiNotionalValue == 0m))
|
||||
{
|
||||
closePrincipal = closePosiNotionalValue;
|
||||
}
|
||||
if (MarginModes.Contains(position.InterestMode))
|
||||
{
|
||||
positionClone.InterestDirection = MarginCalc.FlipDirection(position.InterestDirection);
|
||||
}
|
||||
|
||||
// 获取利率
|
||||
decimal rate = Math.Round(GetFixedRate(position, unwindDate), InterestCalculationPrecision, MidpointRounding.AwayFromZero); // 做精度调整 原数据有精度误差
|
||||
decimal floatRate = GetFloatRate(position, preEodPosition, td.StartDate.Value, endDate, interestPeriod, swap, positionClone);
|
||||
|
||||
// 根据场景计算利息
|
||||
if (MarginModes.Contains(position.InterestMode))
|
||||
{
|
||||
// 保证金腿(5/6):专属计息,notional 直接取保证金余额,无融资腿差分公式与 orginPv 维度 hack
|
||||
interests.Add(CalcMarginInterest(td, valueDate, endDate, positionClone, rate, closePrincipal, posiPrincipal,
|
||||
newClosePercent, annualDays, calcFirst, calcLast||newCalcLast, preEodPosition, eventType, add, settment, swap));
|
||||
}
|
||||
else if (settment)
|
||||
if (settment)
|
||||
{
|
||||
// 收盘归档场景,使用 CalcEodInterest
|
||||
interests.Add(CalcEodInterest(td, valueDate, positionClone, rate, floatRate, closePrincipal, posiPrincipal, annualDays, calcFirst, calcLast, preEodPosition, eventType, add));
|
||||
@@ -722,7 +713,7 @@ namespace YLErp.Modules.SwapModule
|
||||
: 0m;
|
||||
interests.Add(CalcUnwindInterest(td, valueDate, endDate, positionClone, rate, floatRate, posiPrincipal,
|
||||
closePrincipal, newClosePercent, annualDays, preEodPosition, eventType, add, swap, orginPv, calcFirst,
|
||||
calcLast||newCalcLast, consumedInterest));
|
||||
calcLast || newCalcLast, consumedInterest));
|
||||
}
|
||||
}
|
||||
//当日有平仓或互换记录时,避免重复结算
|
||||
|
||||
Reference in New Issue
Block a user