SwapDealService的10个private static纯逻辑方法搬到两个新文件: - UnwindNormalizer.cs: NormalizeNotionalValues/FullCloseRequest/Recalculate/IsFullClose/SettledInterestAmounts/EventUnwindDate (6个) - TradingFeeCalc.cs: CalcInitTradingFee/CalcInitTradingFeePending (2个) SwapDealService内21处调用点加类名前缀, 反射测试改为直接调用(public) SwapModule零回归(7基线/510通过)
105 lines
5.0 KiB
C#
105 lines
5.0 KiB
C#
using YLErp.Helpers;
|
|
using YLErp.Modules.SwapModule.Margin;
|
|
|
|
namespace YLErp.Modules.SwapModule;
|
|
|
|
/// <summary>
|
|
/// 平仓数据(UnwindData)规范化——纯 static,无 this 依赖。
|
|
/// 从 SwapDealService 提取,零行为变更。
|
|
/// </summary>
|
|
internal static class UnwindNormalizer
|
|
{
|
|
internal static void NormalizeNotionalValues(UnwindData unwindData)
|
|
{
|
|
unwindData.NotionalValue = Math.Round(unwindData.NotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
|
|
unwindData.PosiNotionalValue = Math.Round(unwindData.PosiNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
|
|
unwindData.CloseNotionalValue = Math.Round(unwindData.CloseNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
|
|
}
|
|
|
|
internal static bool NormalizeFullCloseRequest(UnwindData unwindData)
|
|
{
|
|
if (unwindData.CloseMethod != (int)CloseMethodEnum.全部平仓
|
|
&& unwindData.ClosePercent < 1
|
|
&& !(unwindData.PositionQty > 0 && unwindData.CloseQty >= unwindData.PositionQty)
|
|
&& !(unwindData.PosiNotionalValue > 0 && unwindData.CloseNotionalValue >= unwindData.PosiNotionalValue))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var closeQty = unwindData.CloseQty;
|
|
var closeNotionalValue = unwindData.CloseNotionalValue;
|
|
unwindData.ClosePercent = 1;
|
|
if (unwindData.PositionQty > 0) unwindData.CloseQty = unwindData.PositionQty;
|
|
if (unwindData.PosiNotionalValue > 0) unwindData.CloseNotionalValue = unwindData.PosiNotionalValue;
|
|
return closeQty != unwindData.CloseQty || closeNotionalValue != unwindData.CloseNotionalValue;
|
|
}
|
|
|
|
internal static void RecalculateNormalizedUnwindAmounts(UnwindData unwindData)
|
|
{
|
|
var floatLeg = unwindData.FlowEvents.FirstOrDefault(x => !string.IsNullOrEmpty(x.UnderlyingCode));
|
|
if (floatLeg == null || floatLeg.PosiGrossPrice == 0) return;
|
|
|
|
var input = new UnwindInput
|
|
{
|
|
Multiplier = ConsGlobal.InstrumentType.IsBond(floatLeg.UnderlyingInstrumentType) ? 100 : 1,
|
|
PosiGrossPrice = floatLeg.PosiGrossPrice,
|
|
TradingAmountAvg = floatLeg.TradingAmountAvg,
|
|
CloseQty = unwindData.CloseQty,
|
|
PositionQty = unwindData.PositionQty,
|
|
ContractSize = floatLeg.ContractSize,
|
|
CloseNotionalValue = unwindData.CloseNotionalValue,
|
|
PayDirection = floatLeg.PayDirection,
|
|
PositionType = floatLeg.PositionType,
|
|
TradingFee = floatLeg.TradingFee.ToString(),
|
|
TradingFeePending = floatLeg.TradingFeePending.ToString(),
|
|
DividendIn = floatLeg.DividendIn.ToString()
|
|
};
|
|
foreach (var leg in unwindData.FlowEvents.Where(x => string.IsNullOrEmpty(x.UnderlyingCode)))
|
|
{
|
|
var target = MarginModes.Contains(leg.InterestMode)
|
|
? input.MarginLegs
|
|
: input.InterestLegs;
|
|
target.Add(new LegInput { InterestClosePnL = leg.InterestClosePnL });
|
|
}
|
|
|
|
var result = FrontendCalcReference.CalcUnwind(input);
|
|
floatLeg.MarkClosePnl = result.MarkClosePnl;
|
|
unwindData.SwapCloseAmount = result.SwapCloseAmount;
|
|
unwindData.SwapRealizedPnL = result.SwapRealizedPnL;
|
|
unwindData.SwapMarginRebatePnl = result.SwapMarginRebatePnl;
|
|
}
|
|
|
|
internal static bool IsFullCloseAfterDeduction(UnwindData unwindData, double remainingNotional, double remainingQuantity)
|
|
{
|
|
return unwindData.ClosePercent == 1 || (remainingNotional == 0 && remainingQuantity == 0);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 手工平仓、手工互换及收益结算的利息事件按金额两位落库。
|
|
/// 自动平仓保留原有计算与落库口径,不适用本阶段的手工结算规则。
|
|
/// </summary>
|
|
internal static bool NormalizeSettledInterestAmounts(IEnumerable<swap_flow_event> flowEvents, int eventType, string eventReason)
|
|
{
|
|
if ((eventType != (int)SwapEventTypeEnum.平仓 && eventType != (int)SwapEventTypeEnum.互换)
|
|
|| eventReason == "系统操作_自动平仓")
|
|
{
|
|
return false;
|
|
}
|
|
|
|
foreach (var flowEvent in flowEvents.Where(x => string.IsNullOrEmpty(x.UnderlyingCode)))
|
|
{
|
|
flowEvent.InterestPrincipal = Math.Round(flowEvent.InterestPrincipal, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
|
|
flowEvent.InterestAmount = Math.Round(flowEvent.InterestAmount, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
|
|
flowEvent.TdInterestAmount = Math.Round(flowEvent.TdInterestAmount, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
|
|
flowEvent.InterestClosePnL = Math.Round(flowEvent.InterestClosePnL, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
|
|
flowEvent.InterestFee = Math.Round(flowEvent.InterestFee, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
internal static void NormalizeEventUnwindDate(UnwindData unwindData)
|
|
{
|
|
unwindData.UnwindDate = unwindData.ValueDate;
|
|
}
|
|
}
|