diff --git a/UnitTestProject/Modules/SwapModule/InitUnwindTradingFeeTest.cs b/UnitTestProject/Modules/SwapModule/InitUnwindTradingFeeTest.cs
index 8ebfb199..34000da7 100644
--- a/UnitTestProject/Modules/SwapModule/InitUnwindTradingFeeTest.cs
+++ b/UnitTestProject/Modules/SwapModule/InitUnwindTradingFeeTest.cs
@@ -1,4 +1,3 @@
-using System.Reflection;
using YLErp.DBModels;
namespace YLErp.Modules.SwapModule
@@ -7,26 +6,10 @@ namespace YLErp.Modules.SwapModule
public class InitUnwindTradingFeeTest
{
private static decimal InvokeCalcInitTradingFee(swap_position position, UnwindData unwindData)
- {
- var method = typeof(SwapDealService).GetMethod(
- "CalcInitTradingFee",
- BindingFlags.NonPublic | BindingFlags.Static);
-
- Assert.IsNotNull(method, "未找到 CalcInitTradingFee 私有静态方法");
-
- return (decimal)method.Invoke(null, new object[] { position, unwindData });
- }
+ => TradingFeeCalc.CalcInitTradingFee(position, unwindData);
private static decimal InvokeCalcInitTradingFeePending(swap_position oriPosition, swap_position position, UnwindData unwindData)
- {
- var method = typeof(SwapDealService).GetMethod(
- "CalcInitTradingFeePending",
- BindingFlags.NonPublic | BindingFlags.Static);
-
- Assert.IsNotNull(method, "CalcInitTradingFeePending was not found");
-
- return (decimal)method.Invoke(null, new object[] { oriPosition, position, unwindData });
- }
+ => TradingFeeCalc.CalcInitTradingFeePending(oriPosition, position, unwindData);
[TestMethod]
public void 百分比模式_按平仓名义本金计算并四舍五入到两位()
diff --git a/YLErpDAL/Modules/SwapModule/SwapDealService.cs b/YLErpDAL/Modules/SwapModule/SwapDealService.cs
index 8ae2c8d3..7364c691 100644
--- a/YLErpDAL/Modules/SwapModule/SwapDealService.cs
+++ b/YLErpDAL/Modules/SwapModule/SwapDealService.cs
@@ -46,106 +46,17 @@ namespace YLErp.Modules.SwapModule
/// 原 private 改 protected virtual,使测试 stub 可整体 override,规避内部 new SwapEventService 连库。
protected virtual long SaveSwapDeal(UnwindData unwindData, int eventType, int clientCashId, string eventResason = "", bool approve = false)
{
- NormalizeNotionalValues(unwindData);
+ UnwindNormalizer.NormalizeNotionalValues(unwindData);
return SaveSwapDealInternal(unwindData, eventType, clientCashId, eventResason, approve);
}
- private 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);
- }
-
- private 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;
- }
-
- private 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;
- }
-
- private static bool IsFullCloseAfterDeduction(UnwindData unwindData, double remainingNotional, double remainingQuantity)
- {
- return unwindData.ClosePercent == 1 || (remainingNotional == 0 && remainingQuantity == 0);
- }
-
// 待实现利息会进入 decimal(30,12) 日终快照;精度常量统一引用 SwapInterest.FundingLegPrecision,消除重复定义。
private const int InterestCalculationPrecision = SwapInterest.FundingLegPrecision;
- ///
- /// 手工平仓、手工互换及收益结算的利息事件按金额两位落库。
- /// 自动平仓保留原有计算与落库口径,不适用本阶段的手工结算规则。
- ///
- private static bool NormalizeSettledInterestAmounts(IEnumerable 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;
- }
-
// 客户现金在 SaveSwapDeal 之前创建,手工结算必须先收敛流水并重算汇总金额。
private void NormalizeManualSettlementAmounts(UnwindData unwindData, int eventType, string eventReason)
{
- if (!NormalizeSettledInterestAmounts(unwindData.FlowEvents, eventType, eventReason))
+ if (!UnwindNormalizer.NormalizeSettledInterestAmounts(unwindData.FlowEvents, eventType, eventReason))
{
return;
}
@@ -372,7 +283,7 @@ namespace YLErp.Modules.SwapModule
floatEvent.UnderlyingInstrumentType = position.UnderlyingInstrumentType;
floatEvent.CloseFee = 0;
floatEvent.BeforeCloseFee = oriPosition.PosiTradingFeePending;
- floatEvent.TradingFee = CalcInitTradingFee(oriPosition, unwindData);
+ floatEvent.TradingFee = TradingFeeCalc.CalcInitTradingFee(oriPosition, unwindData);
floatEvent.PosiTradingFeeUnit = oriPosition?.PosiTradingFeeUnit ?? 0;
floatEvent.PosiFeeType = oriPosition?.PosiFeeType ?? 0;
floatEvent.MarkClosePnl = 0;
@@ -387,7 +298,7 @@ namespace YLErp.Modules.SwapModule
floatEvent.ContractSize = position.ContractSize;
floatEvent.TradingAmount = floatEvent.Quantity * floatEvent.ContractSize;
var ratio = -DirectionRatio.ReceivePay(position.PosiDirection);
- floatEvent.TradingFeePending = CalcInitTradingFeePending(oriPosition, position, unwindData);
+ floatEvent.TradingFeePending = TradingFeeCalc.CalcInitTradingFeePending(oriPosition, position, unwindData);
floatEvent.DataState = (int)SwapFlowDateStateEnum.完成;
floatEvent.InterestMode = position.InterestMode;
floatEvent.ClientId = td.ClientId;
@@ -397,37 +308,6 @@ namespace YLErp.Modules.SwapModule
}
return unwindData;
}
- private static decimal CalcInitTradingFee(swap_position oriPosition, UnwindData unwindData)
- {
- if (oriPosition == null || unwindData == null)
- {
- return 0;
- }
-
- if (oriPosition.PosiFeeType == 1)
- {
- return Math.Round(oriPosition.PosiTradingFeeUnit * unwindData.CloseQty, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
- }
-
- return Math.Round(oriPosition.PosiTradingFeeUnit / 100m * unwindData.CloseNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
- }
-
- private static decimal CalcInitTradingFeePending(swap_position oriPosition, swap_position position, UnwindData unwindData)
- {
- if (oriPosition == null || unwindData == null || oriPosition.PosiTradingFeeUnit == 0)
- {
- return position?.PosiTradingFeePending ?? 0;
- }
-
- var closeBase = oriPosition.PosiFeeType == 1 ? unwindData.CloseQty : unwindData.CloseNotionalValue;
- var originalBase = oriPosition.PosiFeeType == 1 ? unwindData.NotionalQty : unwindData.NotionalValue;
- if (originalBase <= 0)
- {
- return position?.PosiTradingFeePending ?? 0;
- }
-
- return Math.Round(oriPosition.PosiTradingFeePending * closeBase / originalBase, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
- }
///
/// 校验上日是否收盘
///
@@ -1573,16 +1453,16 @@ namespace YLErp.Modules.SwapModule
{
throw new ServiceException("未找到交易信息");
}
- NormalizeEventUnwindDate(unwindData);
- NormalizeNotionalValues(unwindData);
+ UnwindNormalizer.NormalizeEventUnwindDate(unwindData);
+ UnwindNormalizer.NormalizeNotionalValues(unwindData);
NormalizeManualSettlementAmounts(unwindData, (int)SwapEventTypeEnum.平仓, "系统操作_平仓");
//CheckLastEod(unwindData.ValueDate, td.StartDate.Value, unwindData.SwapTradeId); //去掉平仓收盘限制
// 前端按"占期初(original)"语义传 ClosePercent(A);后端全链路按"占剩余(remaining)"语义(B)消费。
// 入口统一转换为 B,落库展示用的 A 由 SaveSwapDealInternal 还原。
unwindData.ClosePercent = ToRemainingClosePercent(unwindData.ClosePercent, unwindData.NotionalValue, unwindData.PosiNotionalValue);
- if (NormalizeFullCloseRequest(unwindData))
+ if (UnwindNormalizer.NormalizeFullCloseRequest(unwindData))
{
- RecalculateNormalizedUnwindAmounts(unwindData);
+ UnwindNormalizer.RecalculateNormalizedUnwindAmounts(unwindData);
}
ValidateFrontendPnL(unwindData, isIncome: false); // 只读校验告警,不阻断交易
bool cofirm = false;
@@ -1595,7 +1475,7 @@ namespace YLErp.Modules.SwapModule
var eventId = SaveSwapDeal(unwindData, (int)SwapEventTypeEnum.平仓, clientCashId, "系统操作_平仓");
var remainingStockEqvNotional = Math.Round(td.StockEqvNotional - Convert.ToDouble(unwindData.CloseNotionalValue), ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
var remainingTradeAmount = td.TradeAmount - Convert.ToDouble(unwindData.CloseQty);
- var isFullClose = IsFullCloseAfterDeduction(unwindData, remainingStockEqvNotional, remainingTradeAmount);
+ var isFullClose = UnwindNormalizer.IsFullCloseAfterDeduction(unwindData, remainingStockEqvNotional, remainingTradeAmount);
if (isFullClose)
{
td.TradeStatus = "已平仓";
@@ -1968,7 +1848,7 @@ namespace YLErp.Modules.SwapModule
{
throw new ServiceException("未找到交易信息");
}
- NormalizeEventUnwindDate(unwindData);
+ UnwindNormalizer.NormalizeEventUnwindDate(unwindData);
unwindData.SwapRealizedPnL = unwindData.SwapCloseAmount;
NormalizeManualSettlementAmounts(unwindData, (int)SwapEventTypeEnum.平仓, "系统操作_平仓");
var trans = DbContext.Database.BeginTransaction();
@@ -2006,7 +1886,7 @@ namespace YLErp.Modules.SwapModule
{
throw new ServiceException("未找到交易信息");
}
- NormalizeEventUnwindDate(unwindData);
+ UnwindNormalizer.NormalizeEventUnwindDate(unwindData);
unwindData.SwapRealizedPnL = unwindData.SwapCloseAmount;
NormalizeManualSettlementAmounts(unwindData, (int)SwapEventTypeEnum.互换, "系统操作_互换");
var trans = DbContext.Database.BeginTransaction();
@@ -2046,7 +1926,7 @@ namespace YLErp.Modules.SwapModule
{
throw new ServiceException("未找到交易信息");
}
- NormalizeEventUnwindDate(unwindData);
+ UnwindNormalizer.NormalizeEventUnwindDate(unwindData);
ValidateIncomeValueDate(unwindData, td);
NormalizeManualSettlementAmounts(unwindData, (int)SwapEventTypeEnum.互换, "系统操作_互换");
//CheckLastEod(unwindData.ValueDate, td.StartDate.Value, unwindData.SwapTradeId); //去掉平仓收盘限制
@@ -2084,8 +1964,8 @@ namespace YLErp.Modules.SwapModule
throw new Exception("该笔交易状态为平仓待复核,未找到相关记录,请检查该笔交易是否有效");
}
swapEvent.unwindData = JsonConvert.DeserializeObject(swapEvent.EventData);
- NormalizeEventUnwindDate(swapEvent.unwindData);
- NormalizeNotionalValues(swapEvent.unwindData);
+ UnwindNormalizer.NormalizeEventUnwindDate(swapEvent.unwindData);
+ UnwindNormalizer.NormalizeNotionalValues(swapEvent.unwindData);
// Stored events keep display ratio A; approval calculations consume remaining ratio B.
swapEvent.unwindData.ClosePercent = ToRemainingClosePercent(
swapEvent.unwindData.ClosePercent,
@@ -2100,9 +1980,9 @@ namespace YLErp.Modules.SwapModule
swapEvent.unwindData.FlowEvents = flowList;
if (eventType == (int)SwapEventTypeEnum.平仓)
{
- if (NormalizeFullCloseRequest(swapEvent.unwindData))
+ if (UnwindNormalizer.NormalizeFullCloseRequest(swapEvent.unwindData))
{
- RecalculateNormalizedUnwindAmounts(swapEvent.unwindData);
+ UnwindNormalizer.RecalculateNormalizedUnwindAmounts(swapEvent.unwindData);
}
}
if (eventType == (int)SwapEventTypeEnum.互换)
@@ -2131,7 +2011,7 @@ namespace YLErp.Modules.SwapModule
{
var remainingStockEqvNotional = Math.Round(td.StockEqvNotional - Convert.ToDouble(swapEvent.unwindData.CloseNotionalValue), ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
var remainingTradeAmount = td.TradeAmount - Convert.ToDouble(swapEvent.unwindData.CloseQty);
- var isFullClose = IsFullCloseAfterDeduction(swapEvent.unwindData, remainingStockEqvNotional, remainingTradeAmount);
+ var isFullClose = UnwindNormalizer.IsFullCloseAfterDeduction(swapEvent.unwindData, remainingStockEqvNotional, remainingTradeAmount);
if (isFullClose)
{
td.TradeStatus = "已平仓";
@@ -2176,7 +2056,7 @@ namespace YLErp.Modules.SwapModule
{
throw new ServiceException("未找到交易信息");
}
- NormalizeEventUnwindDate(unwindData);
+ UnwindNormalizer.NormalizeEventUnwindDate(unwindData);
if (eventType == (int)SwapEventTypeEnum.互换)
{
ValidateIncomeValueDate(unwindData, td);
@@ -2190,9 +2070,9 @@ namespace YLErp.Modules.SwapModule
unwindData.ClosePercent = ToRemainingClosePercent(unwindData.ClosePercent, unwindData.NotionalValue, unwindData.PosiNotionalValue);
if (eventType == (int)SwapEventTypeEnum.平仓)
{
- if (NormalizeFullCloseRequest(unwindData))
+ if (UnwindNormalizer.NormalizeFullCloseRequest(unwindData))
{
- RecalculateNormalizedUnwindAmounts(unwindData);
+ UnwindNormalizer.RecalculateNormalizedUnwindAmounts(unwindData);
}
}
string action = eventType == (int)SwapEventTypeEnum.互换 ? ClientCashInCashOut.系统操作_互换 : ClientCashInCashOut.系统操作_平仓费;
@@ -2225,11 +2105,6 @@ namespace YLErp.Modules.SwapModule
}
}
- private static void NormalizeEventUnwindDate(UnwindData unwindData)
- {
- unwindData.UnwindDate = unwindData.ValueDate;
- }
-
///
/// 保存平仓/互换事件
///
@@ -2243,7 +2118,7 @@ namespace YLErp.Modules.SwapModule
throw new ServiceException("未找到交易信息");
}
var flowList = new List(unwindData.FlowEvents);
- NormalizeSettledInterestAmounts(flowList, eventType, eventResason);
+ UnwindNormalizer.NormalizeSettledInterestAmounts(flowList, eventType, eventResason);
unwindData.FlowEvents.Clear();
// 落库展示用"占期初(original)"语义(A);计算链(费用递减/全平判定)用"占剩余(remaining)"语义(B)。
// 序列化前把 ClosePercent 还原为 A,序列化后立即还原回 B 供后续使用。
diff --git a/YLErpDAL/Modules/SwapModule/TradingFeeCalc.cs b/YLErpDAL/Modules/SwapModule/TradingFeeCalc.cs
new file mode 100644
index 00000000..3c9cea1c
--- /dev/null
+++ b/YLErpDAL/Modules/SwapModule/TradingFeeCalc.cs
@@ -0,0 +1,43 @@
+using YLErp.DBModels;
+using YLErp.DBModels.Consts;
+
+namespace YLErp.Modules.SwapModule;
+
+///
+/// 平仓手续费计算——纯 static,无 this 依赖。
+/// 从 SwapDealService 提取,零行为变更。
+///
+public static class TradingFeeCalc
+{
+ public static decimal CalcInitTradingFee(swap_position oriPosition, UnwindData unwindData)
+ {
+ if (oriPosition == null || unwindData == null)
+ {
+ return 0;
+ }
+
+ if (oriPosition.PosiFeeType == 1)
+ {
+ return Math.Round(oriPosition.PosiTradingFeeUnit * unwindData.CloseQty, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
+ }
+
+ return Math.Round(oriPosition.PosiTradingFeeUnit / 100m * unwindData.CloseNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
+ }
+
+ public static decimal CalcInitTradingFeePending(swap_position oriPosition, swap_position position, UnwindData unwindData)
+ {
+ if (oriPosition == null || unwindData == null || oriPosition.PosiTradingFeeUnit == 0)
+ {
+ return position?.PosiTradingFeePending ?? 0;
+ }
+
+ var closeBase = oriPosition.PosiFeeType == 1 ? unwindData.CloseQty : unwindData.CloseNotionalValue;
+ var originalBase = oriPosition.PosiFeeType == 1 ? unwindData.NotionalQty : unwindData.NotionalValue;
+ if (originalBase <= 0)
+ {
+ return position?.PosiTradingFeePending ?? 0;
+ }
+
+ return Math.Round(oriPosition.PosiTradingFeePending * closeBase / originalBase, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
+ }
+}
diff --git a/YLErpDAL/Modules/SwapModule/UnwindNormalizer.cs b/YLErpDAL/Modules/SwapModule/UnwindNormalizer.cs
new file mode 100644
index 00000000..63db255c
--- /dev/null
+++ b/YLErpDAL/Modules/SwapModule/UnwindNormalizer.cs
@@ -0,0 +1,104 @@
+using YLErp.Helpers;
+using YLErp.Modules.SwapModule.Margin;
+
+namespace YLErp.Modules.SwapModule;
+
+///
+/// 平仓数据(UnwindData)规范化——纯 static,无 this 依赖。
+/// 从 SwapDealService 提取,零行为变更。
+///
+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);
+ }
+
+ ///
+ /// 手工平仓、手工互换及收益结算的利息事件按金额两位落库。
+ /// 自动平仓保留原有计算与落库口径,不适用本阶段的手工结算规则。
+ ///
+ internal static bool NormalizeSettledInterestAmounts(IEnumerable 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;
+ }
+}