using ClosedXML.Report.Utils; using CsvHelper; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.Extensions.Logging; using MoreLinq.Extensions; using Newtonsoft.Json; using OfficeOpenXml.Drawing; using OfficeOpenXml.Drawing.Controls; using Org.BouncyCastle.Asn1.Ocsp; using Qdp.Pricing.Base.Implementations; using Qdp.Pricing.Core.Modules; using System.Linq.Expressions; using YLErp.BLL; using YLErp.BLL.Eod; using YLErp.DBModels; using YLErp.DBModels.Enums; using YLErp.Model; using YLErp.Models; using YLErp.Modules.DataProviderModule; using YLErp.Modules.EodModule; using YLErp.Modules.EodModule.QueryModule; using YLErp.Modules.TradeModule; using YLErp.Modules.TradeModule.DealModule; using YLErp.QdpModule; using static Microsoft.IO.RecyclableMemoryStreamManager; using static YLErp.ConsGlobal; namespace YLErp.Modules.SwapModule { public class SwapDealService : SwapTradeBaseService { public SwapDealService(OptUserInfo optUser) : base(optUser) { } public SwapDealService(YLBaseService baseService) : base(baseService) { } /// /// 平仓初始化 /// /// /// /// public UnwindData InitUnwind(int tradeId) { var td = DbContext.trade.Find(tradeId); var positions = DbContext.swap_position.Where(x => x.SwapTradeId == tradeId && !x.Invalid); var um = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode); bool commodity = ConsGlobal.InstrumentType.CalcTypeIsFutures(um.UnderlyingInstrumentType); List eventTyps = new List() { (int)SwapEventTypeEnum.自动互换, (int)SwapEventTypeEnum.互换 }; var dealDate = valuedateBLL.ValueDate <= td.ExerciseDate.Value ? valuedateBLL.ValueDate : td.ExerciseDate.Value; CheckLastEod(dealDate, td.TradeDate.Value, tradeId); var tradeExtend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == tradeId); td.trade_extend = tradeExtend; var position = positions.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode) && !x.IsInitial).FirstOrDefault(); var oriPosition = positions.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode) && x.IsInitial).FirstOrDefault(); var preDealDate = GetPreDealDate(tradeId, dealDate, eventTyps); var hasProcess = HasTradeProcess(); swap_flow_event floatEvent = new swap_flow_event(); UnwindData unwindData = new UnwindData(); if (((valuedateBLL.SystemDate.CloseReCheck == 1) || (valuedateBLL.SystemDate.CloseReApprove == 1 && hasProcess)) && td.TradeStatus == ConsTrade.平仓待复核) { var swapEvent = GetSwapEvent(tradeId, (int)SwapEventTypeEnum.平仓); if (swapEvent == null) { throw new Exception("该笔交易状态为平仓待复核,未找到相关记录,请检查该笔交易是否有效"); } unwindData = swapEvent.unwindData; } else { unwindData.CloseType = commodity ? 1 : 2; unwindData.StartDate = td.TradeDate.Value; if (preDealDate.HasValue) { unwindData.StartDate = preDealDate.Value; } unwindData.ValueDate = dealDate; unwindData.UnwindDate = dealDate; floatEvent.EventDate = dealDate; floatEvent.UnwindDate = unwindData.UnwindDate; floatEvent.PayDate = QdpCalendarHelper.GetNonHoliday(unwindData.UnwindDate.Value.AddDays(td.trade_extend.ExtendObj.SettlementRules)); unwindData.PayDate = floatEvent.PayDate; floatEvent.SwapTradeId = tradeId; floatEvent.SwapTradeNo = td.TradeNumber; unwindData.SwapTradeId = tradeId; unwindData.StructureType = td.StructureType; unwindData.NotionalValue = Convert.ToDecimal(td.OriginalStockEqvNotional ?? 0); unwindData.NotionalQty = positions.Where(x => x.IsInitial).Sum(s => s.PosiQuantity); unwindData.PosiNotionalValue = Convert.ToDecimal(td.StockEqvNotional); unwindData.PositionQty = position != null ? position.PosiQuantity : Convert.ToDecimal(td.TradeAmount); unwindData.AnnualDays = tradeExtend == null ? 365 : tradeExtend.ExtendObj.AnnualDays; unwindData.CloseMethod = (int)CloseMethodEnum.全部平仓; var closePercent = GetUnwindPercent(tradeId); unwindData.ClosePercent = 1 - closePercent; unwindData.CloseNotionalValue = unwindData.PosiNotionalValue; unwindData.CloseQty = unwindData.PositionQty; if (position != null) { floatEvent.PositionId = position.PositionId; floatEvent.EventType = (int)SwapEventTypeEnum.平仓; floatEvent.EventReason = "交易"; floatEvent.DividendIn = 0; floatEvent.UnderlyingCode = position.UnderlyingCode; floatEvent.UnderlyingInstrumentType = position.UnderlyingInstrumentType; floatEvent.CloseFee = 0; floatEvent.BeforeCloseFee = oriPosition.PosiTradingFeePending; floatEvent.MarkClosePnl = 0; floatEvent.PayDirection = position.PosiDirection; floatEvent.PosiGrossPrice = position.PosiGrossPrice; floatEvent.PosiNetPrice = position.PosiNetPrice; floatEvent.TradingAmountNetAvg = position.PosiNetNoFeePrice; floatEvent.PositionType = position.PositionType; floatEvent.TradingAmountNetFeeAvg = position.PosiNetFeePrice; floatEvent.Quantity = position.PosiQuantity; floatEvent.PositionQty = 0; floatEvent.ContractSize = position.ContractSize; floatEvent.TradingAmount = floatEvent.Quantity * floatEvent.ContractSize; var ratio = position.PosiDirection == (int)SwapDirectionEnum.收取 ? -1m : 1m; floatEvent.TradingFeePending = position.PosiTradingFeePending; floatEvent.DataState = (int)SwapFlowDateStateEnum.完成; floatEvent.InterestMode = position.InterestMode; floatEvent.ClientId = td.ClientId; floatEvent.SetOpt(UserInfo); } unwindData.FlowEvents.Add(floatEvent); } return unwindData; } /// /// 校验上日是否收盘 /// /// public void CheckEodTrade(int tradeId) { var td = DbContext.trade.Find(tradeId); var dealDate = valuedateBLL.ValueDate <= td.ExerciseDate.Value ? valuedateBLL.ValueDate : td.ExerciseDate.Value; CheckLastEod(dealDate, td.StartDate.Value, tradeId); } /// /// 多空组合 平仓初始化 /// /// /// /// public UnwindData InitLongShortUnwind(int tradeId, SwapEventTypeEnum eventTypeEnum) { var td = DbContext.trade.Find(tradeId); if (td == null) { throw new ServiceException("未找到交易信息"); } var positions = DbContext.swap_position.Where(x => x.SwapTradeId == tradeId && x.IsInitial && !x.Invalid); List eventTyps = new List() { (int)SwapEventTypeEnum.平仓, (int)SwapEventTypeEnum.互换, (int)SwapEventTypeEnum.自动互换 }; var dealDate = valuedateBLL.ValueDate <= td.ExerciseDate.Value ? valuedateBLL.ValueDate : td.ExerciseDate.Value; CheckLastEod(dealDate, td.TradeDate.Value, tradeId); var tradeExtend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == tradeId); td.trade_extend = tradeExtend; var preDealDate = GetPreDealDate(tradeId, dealDate, eventTyps); double stockEqvNotional = td.StockEqvNotional;//剩余名义本金 var hasProcess = HasTradeProcess(); swap_flow_event floatEvent = new swap_flow_event(); UnwindData unwindData = new UnwindData(); if (((valuedateBLL.SystemDate.CloseReCheck == 1) || (valuedateBLL.SystemDate.CloseReApprove == 1 && hasProcess)) && (td.TradeStatus == ConsTrade.平仓待复核 || td.TradeStatus == ConsTrade.互换待复核)) { var swapEvent = GetSwapEvent(tradeId, (int)eventTypeEnum); if (swapEvent == null) { throw new Exception("该笔交易状态为平仓待复核,未找到相关记录,请检查该笔交易是否有效"); } unwindData = swapEvent.unwindData; } else { unwindData.StartDate = td.TradeDate.Value; if (preDealDate.HasValue) { unwindData.StartDate = preDealDate.Value; } unwindData.ValueDate = dealDate; unwindData.UnwindDate = unwindData.UnwindDate; unwindData.PayDate = QdpCalendarHelper.GetNonHoliday(unwindData.UnwindDate.Value.AddDays(td.trade_extend.ExtendObj.SettlementRules)); unwindData.SwapTradeId = tradeId; unwindData.NotionalValue = Convert.ToDecimal(td.OriginalStockEqvNotional ?? 0); unwindData.NotionalQty = positions.Sum(s => s.PosiQuantity); unwindData.PosiNotionalValue = Convert.ToDecimal(stockEqvNotional); unwindData.PositionQty = 0;//平仓只做了结为0,互换用不上 unwindData.AnnualDays = tradeExtend == null ? 365 : tradeExtend.ExtendObj.AnnualDays; if (eventTypeEnum == SwapEventTypeEnum.平仓) { unwindData.FlowEvents = GetUnwindInterests(dealDate, unwindData.UnwindDate.Value, tradeId, 1, (int)SwapEventTypeEnum.平仓); } } return unwindData; } /// /// 平仓初始化 /// /// /// /// public UnwindData InitIncome(int tradeId) { var checkEventTypes = new List() { (int)SwapEventTypeEnum.互换, (int)SwapEventTypeEnum.自动互换 }; var td = DbContext.trade.Find(tradeId); var positions = DbContext.swap_position.Where(x => x.SwapTradeId == tradeId && !x.Invalid); var um = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode); List eventTypes = new List() { (int)SwapFlowEventTypeEnum.互换, (int)SwapFlowEventTypeEnum.自动互换 }; var dealDate = valuedateBLL.ValueDate < td.ExerciseDate.Value ? valuedateBLL.ValueDate : td.ExerciseDate.Value; CheckLastEod(dealDate, td.TradeDate.Value, tradeId); var tradeExtend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == tradeId); td.trade_extend = tradeExtend; var position = positions.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode) && !x.IsInitial).FirstOrDefault(); //var preSettleDate = CheckLastEod(dealDate, td.StartDate.Value, tradeId);//上一交易日期 var preDealDate = GetPreDealDate(tradeId, dealDate, eventTypes); var hasProcess = HasTradeProcess(); swap_flow_event floatEvent = new swap_flow_event(); UnwindData unwindData = new UnwindData(); if (((valuedateBLL.SystemDate.CloseReCheck == 1) || (valuedateBLL.SystemDate.CloseReApprove == 1 && hasProcess)) && td.TradeStatus == ConsTrade.互换待复核) { var swapEvent = GetSwapEvent(tradeId, (int)SwapEventTypeEnum.互换); if (swapEvent == null) { throw new Exception("该笔交易状态为平仓待复核,未找到相关记录,请检查该笔交易是否有效"); } unwindData = swapEvent.unwindData; } else { unwindData.StartDate = td.TradeDate.Value; if (preDealDate.HasValue) { unwindData.StartDate = preDealDate.Value; } unwindData.ValueDate = dealDate; unwindData.UnwindDate = dealDate; floatEvent.UnwindDate = unwindData.UnwindDate; floatEvent.EventDate = dealDate; unwindData.PayDate = QdpCalendarHelper.GetNonHoliday(unwindData.UnwindDate.Value.AddDays(td.trade_extend.ExtendObj.SettlementRules)); floatEvent.PayDate = unwindData.PayDate; floatEvent.SwapTradeId = tradeId; unwindData.SwapTradeId = tradeId; unwindData.StructureType = td.StructureType; unwindData.NotionalValue = Convert.ToDecimal(td.OriginalStockEqvNotional ?? 0); unwindData.NotionalQty = positions.Where(x => x.IsInitial).Sum(s => s.PosiQuantity); unwindData.PosiNotionalValue = Convert.ToDecimal(td.StockEqvNotional); unwindData.PositionQty = Convert.ToDecimal(td.TradeAmount); unwindData.AnnualDays = tradeExtend == null ? 365 : tradeExtend.ExtendObj.AnnualDays; unwindData.ClosePercent = unwindData.PosiNotionalValue / unwindData.NotionalValue; unwindData.CloseNotionalValue = unwindData.PosiNotionalValue; if (position != null) { floatEvent.EventType = (int)SwapEventTypeEnum.互换; floatEvent.EventReason = "交易"; floatEvent.PositionId = position.PositionId; floatEvent.DividendIn = 0; floatEvent.UnderlyingCode = position.UnderlyingCode; floatEvent.UnderlyingInstrumentType = position.UnderlyingInstrumentType; floatEvent.CloseFee = 0; floatEvent.MarkClosePnl = 0; floatEvent.PayDirection = position.PosiDirection; floatEvent.PosiGrossPrice = position.PosiGrossPrice; floatEvent.PosiNetPrice = position.PosiNetPrice; floatEvent.TradingAmountNetAvg = position.PosiNetNoFeePrice; floatEvent.TradingAmountNetFeeAvg = position.PosiNetFeePrice; floatEvent.PositionType = position.PositionType; floatEvent.Quantity = position.PosiQuantity; floatEvent.PositionQty = 0; floatEvent.ContractSize = position.ContractSize; floatEvent.TradingAmount = floatEvent.Quantity * floatEvent.ContractSize; floatEvent.ClientId = td.ClientId; floatEvent.DataState = (int)SwapFlowDateStateEnum.完成; } unwindData.FlowEvents.Add(floatEvent); } return unwindData; } /// /// 获取平仓利息端信息 /// /// 平仓日期 /// 交易id /// 平仓比例 /// /// public List GetUnwindInterests(DateTime valueDate, DateTime unwindDate, int tradeId, decimal closePercent, int eventType) { List interests = new List(); if (closePercent > 1) { closePercent = 1;//防篡改 } else if (closePercent < 0) { closePercent = 0; } var td = DbContext.trade.Find(tradeId); if (td == null) { throw new ServiceException("未找到交易信息"); } var allpositions = DbContext.swap_position.Where(x => x.SwapTradeId == tradeId && !x.Invalid).ToList(); var origPositions = allpositions.Where(x => x.IsInitial).ToList(); var realPostitions = allpositions.Where(x => !x.IsInitial).ToList(); var positions = origPositions.Where(x => x.PosiDirection == 0).ToList(); var fpositions = origPositions.Where(x => x.PosiDirection > 0).ToList(); var longPositions = fpositions.Where(x => x.PositionType == (int)PositionTypeFlag.Long).ToList(); var shortPositions = fpositions.Where(x => x.PositionType == (int)PositionTypeFlag.Short).ToList(); var tradeExtend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == tradeId); List eventTypes = new List() { (int)SwapEventTypeEnum.平仓, (int)SwapEventTypeEnum.互换, (int)SwapEventTypeEnum.自动互换 }; var lastEod = DbContext.eod_swap.Where(x => x.ValueDate < unwindDate && x.SwapTradeId == tradeId).OrderByDescending(o => o.ValueDate).FirstOrDefault(); var _preSetteDate = lastEod == null ? unwindDate.AddDays(-1) : lastEod.ValueDate; List lastEodPositions = new SwapEodPositionService(this).GetPreEodPositions(tradeId, _preSetteDate);//上一交易数据 var posiLongNotionalValue = longPositions.Sum(s => s.PosiNotionalValue);// 剩余名义本金 var posiShortNotionalValue = shortPositions.Sum(s => s.PosiNotionalValue);// 剩余名义本金 var stockEqvNotional = realPostitions.Where(x => x.PosiDirection > 0).Sum(s => s.PosiNotionalValue); var posiNotionalValue = stockEqvNotional * closePercent;//剩余名义本金 var grossPrice = realPostitions.Where(x => x.PosiDirection > 0).FirstOrDefault()?.PosiGrossPrice; bool tdClose = DbContext.swap_flow_event.Any(x => x.SwapTradeId == tradeId && x.UnwindDate == unwindDate && eventTypes.Contains(x.EventType) && x.DataState == (int)SwapFlowDateStateEnum.完成); interests = GetInterests(td, tradeExtend, valueDate, unwindDate, lastEodPositions, positions, stockEqvNotional, posiLongNotionalValue, posiShortNotionalValue, posiNotionalValue, closePercent, eventType, tdClose, false, grossPrice ?? 0, true, false, false); return interests; } /// /// 计算利息腿计息详细 /// /// 交易 /// 交易扩展数据 /// 操作日期 /// 上一日终持仓 /// 期初利率端 /// 持仓名义本金 /// 多头持仓名义本金 /// 空头持仓名义本金 /// 平仓名义本金 /// /// /// /// /// public List GetInterests( trade td, trade_extend tradeExtend, DateTime valueDate, DateTime unwindDate, List eodPositions, List positions, decimal posiNotionalValue, decimal posiLongNotionalValue, decimal posiShortNotionalValue, decimal closePosiNotionalValue, decimal closePrecent, int eventType, bool tdClose, bool needPrice, decimal grossPrice, bool add = false, bool calcLast = true, bool settment = true) { List interests = new List(); var annualDays = tradeExtend == null ? 365 : tradeExtend.ExtendObj.AnnualDays; foreach (var position in positions) { var _closePosiNotionalValue = closePosiNotionalValue; var _posiNotionalValue = posiNotionalValue; var preEodPosition = eodPositions.FirstOrDefault(x => x.PositionId == position.id); DateTime? preDealDate = null; var positionClone = position.Clone(); var newClosePercent = closePrecent; if (preEodPosition != null) { preDealDate = preEodPosition.ValueDate; } var swap = InitInterestDate(unwindDate, preDealDate, td, tdClose, calcLast, out DateTime startDate, out DateTime endDate);//不算头或不算尾情况,无利息 if (!preDealDate.HasValue) { preEodPosition = new eod_swap_position(); preEodPosition.PosiStartDate = startDate; preEodPosition.ValueDate = startDate; } var swapIntervalToday = position.SwapIntervalList.Where(x => x.Date <= startDate).OrderByDescending(o => o.Date).FirstOrDefault(); if (position.InterestMode == (int)InterestModeEnum.固定值) { _closePosiNotionalValue = position.InterestPrincipalFix; _posiNotionalValue = position.InterestPrincipalFix; newClosePercent = 1m; } else if (position.InterestMode == (int)InterestModeEnum.多头存续名义本金) { _closePosiNotionalValue = posiLongNotionalValue * closePrecent; _posiNotionalValue = posiLongNotionalValue; } else if (position.InterestMode == (int)InterestModeEnum.空头存续名义本金) { _closePosiNotionalValue = posiShortNotionalValue * closePrecent; _posiNotionalValue = posiShortNotionalValue; } else if (position.InterestMode == (int)InterestModeEnum.标的期初全价) { _closePosiNotionalValue = _posiNotionalValue * grossPrice * closePrecent; _posiNotionalValue = _posiNotionalValue * grossPrice; } else if (position.InterestMode == (int)InterestModeEnum.追加预付金 || position.InterestMode == (int)InterestModeEnum.初始预付金) { _closePosiNotionalValue = position.InterestPrincipalFix * closePrecent; _posiNotionalValue = position.InterestPrincipalFix * closePrecent; positionClone.InterestDirection = position.InterestDirection == (int)SwapDirectionEnum.收取 ? (int)SwapDirectionEnum.支付 : (int)SwapDirectionEnum.收取; } if (!string.IsNullOrEmpty(position.FloatRateUnderlyingCode)) { var rateDate = td.StartDate.Value.AddDays(-1); if (EodPriceQueryService.TryGetPrice(rateDate, position.FloatRateUnderlyingCode, out double floatRate)) { position.FloatRate = Convert.ToDecimal(floatRate); positionClone.FloatRate = position.FloatRate; if (preEodPosition.id == 0) { preEodPosition.FloatRate = positionClone.FloatRate; preEodPosition.TdInterestPrincipal = _posiNotionalValue; } } else if (needPrice) { throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{rateDate:yyyy年MM月dd日}的价格"); } } decimal rate = position.InterestRateDefault; if (swapIntervalToday == null)//当日无适用观察日 { var swapInterval = position.SwapIntervalList.Where(x => x.Date > startDate).OrderBy(o => o.Date).FirstOrDefault(); if (swapInterval != null) { rate = swapInterval.Rate; } } else { rate = swapIntervalToday.Rate; } swap_flow_event interest = InitSwapDealInterest(td, valueDate, endDate, rate, positionClone, add, swap, _posiNotionalValue, _closePosiNotionalValue, newClosePercent, annualDays, eventType, preEodPosition, needPrice, settment); interests.Add(interest); } return interests; } /// /// 初始化利息腿信息 /// /// 交易编码 /// 计息开始日期 /// 计息结束日期 /// 计息年化利率 /// 利息腿 /// 是否新增 /// 是否已互换 /// 上一日终归档 /// 当日适用名义本金 /// 当日平仓名义本金 /// 年化天数 /// private swap_flow_event InitSwapDealInterest(trade td, DateTime valueDate, DateTime endDate, decimal rate, swap_position position, bool add, bool swap, decimal posiNotionalValue, decimal closePosiNotionalValue, decimal closePrecent, int annualDays, int eventType, eod_swap_position preEodPosition, bool needPrice, bool settment ) { DateTime lastSwapDate = preEodPosition.ValueDate; decimal interestProfitSum = preEodPosition.InterestProfitSum; swap_flow_event interest = new swap_flow_event(); interest.SwapTradeId = td.id; interest.SwapTradeNo = td.TradeNumber; interest.EventType = eventType; interest.EventReason = "交易"; interest.EventDate = valueDate; interest.PositionId = position.id; interest.InterestDirection = position.InterestDirection; interest.InterestRate = rate; interest.InterestPrincipal = closePosiNotionalValue; interest.InterestSwapInterval = position.InterestSwapInterval; interest.InterestMode = position.InterestMode; interest.FloatRate = position.FloatRate; interest.DataState = (int)SwapFlowDateStateEnum.完成; interest.ClientId = td.ClientId; interest.UnwindDate = endDate; var itemDays = (endDate - lastSwapDate).Days; itemDays = itemDays == 0 ? 1 : itemDays; if (swap) { interest.InterestAmount = 0; interest.TdInterestAmount = 0; interest.InterestAmount = 0; interest.InterestClosePnL = 0; } else { decimal InterestAmount = 0; decimal TdInterestAmount = 0; var interestRatio = position.InterestDirection == 1 ? 1m : -1m; if (position.InterestType == (int)InterestTypeEnum.复利) { var floateRate = preEodPosition.FloatRate; if (settment)//收盘利息计算 { CalcDailyCompoundInterestByEod(preEodPosition, endDate, td.StartDate.Value, position, closePosiNotionalValue, posiNotionalValue, interest, annualDays, needPrice, floateRate, closePrecent, ref InterestAmount, ref TdInterestAmount); } else { CalcDailyCompoundInterest(preEodPosition, endDate, td.StartDate.Value, position, closePosiNotionalValue, posiNotionalValue, interest, annualDays, needPrice, floateRate, closePrecent, ref InterestAmount, ref TdInterestAmount); } } else { InterestAmount = closePosiNotionalValue * (interest.InterestRate + position.FloatRate); TdInterestAmount = posiNotionalValue * (interest.InterestRate + position.FloatRate); if (settment) { if (position.IsAnnualized) { InterestAmount = InterestAmount * ((decimal)itemDays / annualDays); TdInterestAmount = TdInterestAmount * ((decimal)itemDays / annualDays); } InterestAmount = (interestProfitSum * closePrecent) + InterestAmount; } else { if (itemDays > 1)//日期超算情况 { decimal days = (decimal)itemDays - 1m; if (position.IsAnnualized) { InterestAmount = InterestAmount * (days / annualDays); TdInterestAmount = TdInterestAmount * (days / annualDays); InterestAmount += (interestProfitSum * closePrecent); } } else { InterestAmount = interestProfitSum * closePrecent; } } } interest.InterestAmount = decimal.Parse(InterestAmount.ToString("0.0000")); interest.TdInterestAmount = decimal.Parse(TdInterestAmount.ToString("0.0000")); interest.InterestClosePnL = interest.InterestAmount * interestRatio; } if (add) { UpdateDbOption(interest); } return interest; } /// /// 计算复利 盘中 /// /// 上一互换日 /// 结算日期 /// 开仓日 /// 浮动标的 /// 计息基数 /// 固定利率 /// 是否年化 /// 年化天数 /// public void CalcDailyCompoundInterest(eod_swap_position preEodPosition, DateTime endDate, DateTime tradeDate, swap_position position, decimal principal, decimal posiPrincipal, swap_flow_event flowEvent, int annualDays, bool needPrice, decimal floateRate, decimal closePercent, ref decimal InterestAmount, ref decimal TdInterestAmount) { DateTime lastSwapDate = preEodPosition.ValueDate; decimal interestProfitSum = preEodPosition.InterestProfitSum; var TdInterestPrincipal = preEodPosition.TdInterestPrincipal; decimal interest = 0; decimal tdinterest = 0; int interestPeriod = 7; decimal dynomicPrincipal = principal; decimal tdDynomicPrincipal = posiPrincipal; var calcDays = (endDate - tradeDate).Days; double floatRate = Convert.ToDouble(floateRate); for (int i = 0; i <= calcDays; i++) { var rateDate = tradeDate.AddDays(i); if (rateDate > lastSwapDate || endDate == tradeDate) { if (i % interestPeriod == 0) { dynomicPrincipal = dynomicPrincipal + interest; tdDynomicPrincipal = tdDynomicPrincipal + interest; //if (rateDate > tradeDate) //{ // dynomicPrincipal += interestProfitSum; // tdDynomicPrincipal += interestProfitSum; //} if (!string.IsNullOrEmpty(position.FloatRateUnderlyingCode)) { var fr007RateDate = rateDate.AddDays(-1); if (EodPriceQueryService.TryGetPrice(fr007RateDate, position.FloatRateUnderlyingCode, out double floatRate1)) { if (floatRate1 != 0) { floatRate = floatRate1; } } else if (needPrice && QdpCalendarHelper.IsHoliday(fr007RateDate)) { throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{fr007RateDate:yyyy年MM月dd日}的价格"); } } flowEvent.InterestPrincipal = tdDynomicPrincipal; TdInterestPrincipal = tdDynomicPrincipal; } else { flowEvent.InterestPrincipal = TdInterestPrincipal * closePercent; tdDynomicPrincipal = flowEvent.InterestPrincipal; } flowEvent.FloatRate = Convert.ToDecimal(floatRate); var interest1 = flowEvent.InterestPrincipal * (flowEvent.InterestRate + Convert.ToDecimal(floatRate)); var tdinterest1 = tdDynomicPrincipal * (flowEvent.InterestRate + Convert.ToDecimal(floatRate)); if (position.IsAnnualized) { interest1 /= annualDays; tdinterest1 /= annualDays; } interest += decimal.Parse(interest1.ToString("0.0000")); tdinterest += decimal.Parse(tdinterest1.ToString("0.0000")); } else { interest = interestProfitSum * closePercent; tdinterest = interestProfitSum * closePercent; } } InterestAmount = interest; TdInterestAmount = tdinterest; } /// /// 计算复利 收盘 /// /// 上一互换日 /// 结算日期 /// 开仓日 /// 浮动标的 /// 计息基数 /// 固定利率 /// 是否年化 /// 年化天数 /// public void CalcDailyCompoundInterestByEod(eod_swap_position preEodPosition, DateTime endDate, DateTime tradeDate, swap_position position, decimal principal, decimal posiPrincipal, swap_flow_event flowEvent, int annualDays, bool needPrice, decimal floateRate, decimal closePercent, ref decimal InterestAmount, ref decimal TdInterestAmount) { decimal interestProfitSum = preEodPosition.InterestProfitSum; decimal interest = preEodPosition.TdInterestIncome; decimal tdinterest = preEodPosition.TdInterestIncome; int interestPeriod = 7; decimal tdDynomicPrincipal = posiPrincipal; double floatRate = Convert.ToDouble(floateRate); var days = (endDate - tradeDate).Days; if (days % interestPeriod == 0) { tdDynomicPrincipal = tdDynomicPrincipal + interestProfitSum; if (!string.IsNullOrEmpty(position.FloatRateUnderlyingCode)) { var fr007RateDate = endDate.AddDays(-1); if (EodPriceQueryService.TryGetPrice(fr007RateDate, position.FloatRateUnderlyingCode, out double floatRate1)) { if (floatRate1 != 0) { floatRate = floatRate1; } } else if (needPrice && QdpCalendarHelper.IsHoliday(fr007RateDate)) { throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{fr007RateDate:yyyy年MM月dd日}的价格"); } flowEvent.InterestPrincipal = tdDynomicPrincipal; } else { flowEvent.InterestPrincipal = preEodPosition.TdInterestPrincipal * closePercent; tdDynomicPrincipal = flowEvent.InterestPrincipal; } interest = flowEvent.InterestPrincipal * (flowEvent.InterestRate + Convert.ToDecimal(floatRate)); tdinterest = tdDynomicPrincipal * (flowEvent.InterestRate + Convert.ToDecimal(floatRate)); if (position.IsAnnualized) { interest /= annualDays; tdinterest /= annualDays; } } else { flowEvent.InterestPrincipal = preEodPosition.TdInterestPrincipal * closePercent; } flowEvent.FloatRate = Convert.ToDecimal(floatRate); InterestAmount = interest; TdInterestAmount = tdinterest; } /// /// 单标的平仓 /// /// /// public void SwapUnwind(UnwindData unwindData) { var td = DbContext.trade.Find(unwindData.SwapTradeId); if (td == null) { throw new ServiceException("未找到交易信息"); } CheckLastEod(unwindData.ValueDate, td.StartDate.Value, unwindData.SwapTradeId); var trans = DbContext.Database.BeginTransaction(); bool cofirm = false; try { int clientCashId = CloseTrade_ClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapRealizedPnL), ClientCashInCashOut.系统操作_平仓费, unwindData.ValueDate); if (unwindData.SwapMarginAmount != 0) { AddClientCashInCashOut(td, Convert.ToDouble(unwindData.SwapMarginAmount), ClientCashInCashOut.系统操作_应付预付金, unwindData.ValueDate); } if (unwindData.SwapMarginRebatePnl != 0) { AddClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapMarginRebatePnl), ClientCashInCashOut.系统操作_预付金返息, unwindData.ValueDate); } DealFloatPosition(unwindData); var flowList = new List(unwindData.FlowEvents); var eventId = SaveSwapDeal(unwindData, (int)SwapEventTypeEnum.平仓, clientCashId, "系统操作_平仓"); if (unwindData.CloseMethod == (int)CloseMethodEnum.全部平仓) { td.TradeStatus = "已平仓"; td.trade_extend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == td.id); if (td.trade_extend != null && !td.trade_extend.ExtendObj.NeedOpenFee) { new ClientCashInCashOutService(this).SaveSwapTradeClientCash(td, td.TradePrice ?? 0, unwindData.ValueDate, 0); } } else { td.HasPartialUnWind = 1; } td.UnWindDate = unwindData.UnwindDate; td.StockEqvNotional -= Convert.ToDouble(unwindData.CloseNotionalValue); td.TradeAmount -= Convert.ToDouble(unwindData.CloseQty); DbContext.SaveChanges(); trans.Commit(); cofirm = true; } catch (Exception ex) { trans.Rollback(); throw new Exception(ex.Message, ex); } finally { trans.Dispose(); } if (cofirm) { Task.Run(() => { RealtimePnlCalc.RealtimeSwapPosition(new OptUserInfo(0, "互换实时持仓服务", OptUserFrom.Service)); }); } } /// /// 自动全平仓 /// /// /// /// public void AuotoSwapUnwind(int tradeid, decimal unwindPrice, decimal unwindPriceFee, decimal unwindNetFee, decimal unwindNet, DateTime valueDate, decimal unwindQty, decimal mergeQty, decimal penddingFee) { unwindPriceFee = decimal.Parse(unwindPriceFee.ToString("F10")); var td = DbContext.trade.Find(tradeid); var positions = DbContext.swap_position.Where(x => x.SwapTradeId == td.id && !x.Invalid); List eventTypes = new List() { (int)SwapEventTypeEnum.平仓, (int)SwapEventTypeEnum.互换 }; var dealDate = valueDate; var tradeExtend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == td.id); td.trade_extend = tradeExtend; var position = positions.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode) && !x.IsInitial).FirstOrDefault(); var preDealDate = GetPreDealDate(td.id, dealDate, eventTypes); swap_flow_event floatEvent = new swap_flow_event(); UnwindData unwindData = new UnwindData(); unwindData.CloseType = 2; unwindData.StartDate = td.TradeDate.Value; if (preDealDate.HasValue) { unwindData.StartDate = preDealDate.Value; } unwindData.ValueDate = dealDate; unwindData.UnwindDate = QdpCalendarHelper.GetNonHoliday(dealDate.AddDays(1)); floatEvent.EventDate = dealDate; floatEvent.UnwindDate = unwindData.UnwindDate; floatEvent.PayDate = QdpCalendarHelper.GetNonHoliday(unwindData.UnwindDate.Value.AddDays(td.trade_extend.ExtendObj.SettlementRules)); unwindData.PayDate = floatEvent.PayDate; floatEvent.SwapTradeId = td.id; floatEvent.SwapTradeNo = td.TradeNumber; unwindData.SwapTradeId = td.id; unwindData.StructureType = td.StructureType; unwindData.NotionalValue = Convert.ToDecimal(td.OriginalStockEqvNotional ?? 0); unwindData.NotionalQty = positions.Where(x => x.IsInitial).Sum(s => s.PosiQuantity); unwindData.PosiNotionalValue = position != null ? position.PosiNotionalValue : Convert.ToDecimal(td.StockEqvNotional); unwindData.PositionQty = position != null ? position.PosiQuantity : Convert.ToDecimal(td.TradeAmount); var unwindPercent = unwindData.PositionQty == 0 ? 0 : unwindQty / unwindData.PositionQty; unwindData.AnnualDays = tradeExtend == null ? 365 : tradeExtend.ExtendObj.AnnualDays; unwindData.CloseMethod = unwindQty == unwindData.PositionQty ? (int)CloseMethodEnum.全部平仓 : (int)CloseMethodEnum.部分平仓; unwindData.ClosePercent = unwindData.PositionQty == 0 ? 0 : unwindPercent; unwindData.CloseNotionalValue = unwindData.PosiNotionalValue; unwindData.CloseQty = unwindQty; if (position != null) { decimal floatRatio = position.PosiDirection == 1 ? 1m : -1m; decimal longRatio = position.PositionType == 1 ? 1m : -1m; floatEvent.PositionId = position.PositionId; floatEvent.EventType = (int)SwapFlowEventTypeEnum.平仓; floatEvent.EventReason = "交易"; floatEvent.DividendIn = 0; floatEvent.UnderlyingCode = position.UnderlyingCode; floatEvent.UnderlyingInstrumentType = position.UnderlyingInstrumentType; floatEvent.CloseFee = 0; floatEvent.BeforeCloseFee = position.PosiTradingFee + position.PosiTradingFeePending; floatEvent.PayDirection = position.PosiDirection; floatEvent.PosiGrossPrice = position.PosiGrossPrice; floatEvent.PosiNetPrice = position.PosiNetPrice; floatEvent.PositionType = position.PositionType; floatEvent.Quantity = unwindData.CloseQty; floatEvent.PositionQty = unwindData.PositionQty - unwindData.CloseQty; floatEvent.ContractSize = position.ContractSize; floatEvent.DataState = (int)SwapFlowDateStateEnum.完成; floatEvent.InterestMode = position.InterestMode; floatEvent.TradingAmountAvg = unwindPrice; floatEvent.TradingAmountFeeAvg = unwindPriceFee; floatEvent.TradingAmountNetFeeAvg = unwindNetFee; floatEvent.TradingAmountNetAvg = unwindNet; floatEvent.TradingFeePending = position.PosiTradingFeePending * unwindData.ClosePercent; var mergeClosePercent = mergeQty == 0 ? 0 : unwindQty / mergeQty; floatEvent.TradingFee = penddingFee * mergeClosePercent; floatEvent.MarkClosePnl = (unwindPriceFee - position.PosiNetPrice) * unwindData.PosiNotionalValue * floatRatio * longRatio; floatEvent.MarkClosePnl = decimal.Parse(floatEvent.MarkClosePnl.ToString("0.00")); floatEvent.TradingAmount = floatEvent.Quantity * floatEvent.ContractSize; floatEvent.OptLog = "流水自动"; floatEvent.ClientId = td.ClientId; floatEvent.SetOpt(UserInfo); } unwindData.FlowEvents.Add(floatEvent); var interestPositions = GetUnwindInterests(unwindData.ValueDate, unwindData.UnwindDate.Value, td.id, 1, (int)SwapEventTypeEnum.平仓); interestPositions.ForEach(item => { item.OptLog = "流水自动"; }); unwindData.FlowEvents.AddRange(interestPositions); CalcCloseAmount(unwindData); DealUnwind(unwindData, td); } /// /// 衡泰新增平仓事件 /// /// /// /// /// /// public void AutoSwapUnwindFromConsumer(trade td, DateTime valueDate, DateTime payDate, decimal markClosePnl, decimal tradeinfFee, decimal interestAmount, decimal fee, decimal unwindQty, bool allClose) { List eventTypes = new List() { (int)SwapEventTypeEnum.平仓, (int)SwapEventTypeEnum.互换 }; var dealDate = valueDate; var tradeExtend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == td.id); td.trade_extend = tradeExtend; var position = DbContext.swap_position.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode) && x.IsInitial && !x.Invalid).FirstOrDefault(); var preDealDate = GetPreDealDate(td.id, dealDate, eventTypes); swap_flow_event floatEvent = new swap_flow_event(); UnwindData unwindData = new UnwindData(); unwindData.CloseType = 2; unwindData.StartDate = td.TradeDate.Value; if (preDealDate.HasValue) { unwindData.StartDate = preDealDate.Value; } unwindData.ValueDate = dealDate; floatEvent.EventDate = dealDate; unwindData.UnwindDate = QdpCalendarHelper.GetNonHoliday(dealDate.AddDays(1)); floatEvent.UnwindDate = unwindData.UnwindDate; floatEvent.PayDate = payDate; unwindData.PayDate = floatEvent.PayDate; floatEvent.SwapTradeId = td.id; floatEvent.SwapTradeNo = td.TradeNumber; unwindData.SwapTradeId = td.id; unwindData.StructureType = td.StructureType; unwindData.NotionalValue = Convert.ToDecimal(td.OriginalStockEqvNotional ?? 0); unwindData.NotionalQty = position.PosiQuantity; unwindData.PosiNotionalValue = Convert.ToDecimal(td.StockEqvNotional); unwindData.PositionQty = Convert.ToDecimal(td.TradeAmount); unwindData.AnnualDays = tradeExtend == null ? 365 : tradeExtend.ExtendObj.AnnualDays; unwindData.CloseMethod = allClose ? (int)CloseMethodEnum.全部平仓 : (int)CloseMethodEnum.部分平仓; unwindData.ClosePercent = allClose ? 1 : unwindQty / unwindData.NotionalQty; unwindData.CloseNotionalValue = allClose ? unwindData.PosiNotionalValue : unwindQty; unwindData.CloseQty = allClose ? unwindData.PositionQty : unwindQty; if (position != null) { decimal floatRatio = position.PosiDirection == 1 ? 1m : -1m; floatEvent.PositionId = position.id; floatEvent.EventType = (int)SwapEventTypeEnum.平仓; floatEvent.EventReason = "接口合约终止交易"; floatEvent.DividendIn = 0; floatEvent.UnderlyingCode = position.UnderlyingCode; floatEvent.UnderlyingInstrumentType = position.UnderlyingInstrumentType; floatEvent.CloseFee = 0; floatEvent.BeforeCloseFee = position.PosiTradingFee + position.PosiTradingFeePending; floatEvent.PayDirection = position.PosiDirection; floatEvent.PosiGrossPrice = position.PosiGrossPrice; floatEvent.PosiNetPrice = position.PosiNetPrice; floatEvent.TradingAmountNetAvg = position.PosiNetNoFeePrice; floatEvent.TradingFeePending = position.PosiTradingFeePending * unwindData.ClosePercent; floatEvent.TradingFee = tradeinfFee - floatEvent.TradingFeePending; floatEvent.MarkClosePnl = markClosePnl; floatEvent.TradingAmount = floatEvent.Quantity * floatEvent.ContractSize; floatEvent.PositionType = position.PositionType; floatEvent.Quantity = position.PosiQuantity; floatEvent.PositionQty = 0; floatEvent.ContractSize = position.ContractSize; floatEvent.DataState = (int)SwapFlowDateStateEnum.完成; floatEvent.InterestMode = position.InterestMode; floatEvent.TradingAmount = unwindData.CloseQty; floatEvent.ClientId = td.ClientId; floatEvent.OptLog = "衡泰同步"; floatEvent.SetOpt(UserInfo); } unwindData.FlowEvents.Add(floatEvent); var interestPositions = GetUnwindInterestsByHT(unwindData, td, interestAmount, fee); unwindData.FlowEvents.AddRange(interestPositions); CalcCloseAmount(unwindData); DealUnwind(unwindData, td, "合约终止接口回执"); } private List GetUnwindInterestsByHT(UnwindData unwindData, trade td, decimal interestAmount, decimal fee) { List interests = new List(); var allpositions = DbContext.swap_position.Where(x => x.SwapTradeId == unwindData.SwapTradeId && !x.Invalid && x.IsInitial && x.PosiDirection > 0).ToList(); var position = allpositions.Where(x => ConsTrade.InterestModels.Contains(x.InterestMode)).FirstOrDefault(); if (position == null) { return interests; } var grossPrice = allpositions.Where(x => x.PosiDirection > 0).FirstOrDefault()?.PosiGrossPrice ?? 0; var _closePosiNotionalValue = unwindData.CloseNotionalValue; var _posiNotionalValue = unwindData.PosiNotionalValue; var newClosePercent = unwindData.ClosePercent; foreach (var item in allpositions) { var positionClone = item.Clone(); var swapIntervalToday = position.SwapIntervalList.OrderByDescending(o => o.Date).FirstOrDefault(); if (item.InterestMode == (int)InterestModeEnum.固定值) { _closePosiNotionalValue = item.InterestPrincipalFix; _posiNotionalValue = item.InterestPrincipalFix; newClosePercent = 1m; } else if (item.InterestMode == (int)InterestModeEnum.标的期初全价) { _closePosiNotionalValue = _posiNotionalValue * grossPrice * newClosePercent; _posiNotionalValue = _posiNotionalValue * grossPrice; } else if (item.InterestMode == (int)InterestModeEnum.追加预付金 || item.InterestMode == (int)InterestModeEnum.初始预付金) { _closePosiNotionalValue = 0; positionClone.InterestDirection = position.InterestDirection == (int)SwapDirectionEnum.收取 ? (int)SwapDirectionEnum.支付 : (int)SwapDirectionEnum.收取; } decimal rate = item.InterestRateDefault; if (swapIntervalToday != null)//当日无适用观察日 { rate = swapIntervalToday.Rate; } swap_flow_event interest = new swap_flow_event(); interest.SwapTradeId = td.id; interest.SwapTradeNo = td.TradeNumber; interest.EventType = (int)SwapEventTypeEnum.平仓; interest.EventReason = "衡泰同步平仓"; interest.EventDate = unwindData.ValueDate; interest.PositionId = item.id; interest.InterestDirection = positionClone.InterestDirection; interest.InterestRate = rate; interest.InterestPrincipal = _closePosiNotionalValue; interest.InterestSwapInterval = item.InterestSwapInterval; interest.InterestMode = item.InterestMode; interest.FloatRate = item.FloatRate; interest.DataState = (int)SwapFlowDateStateEnum.完成; interest.ClientId = td.ClientId; interest.UnwindDate = unwindData.ValueDate; interest.PayDate = unwindData.PayDate; if (position != null && item.id == position.id) { interest.InterestAmount = interestAmount; interest.TdInterestAmount = interestAmount; interest.InterestClosePnL = interestAmount; interest.InterestFee = fee; } UpdateDbOption(interest); interests.Add(interest); } return interests; } private void DealUnwind(UnwindData unwindData, trade td, string actionMsg = "系统操作_自动平仓") { int clientCashId = CloseTrade_ClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapRealizedPnL), ClientCashInCashOut.系统操作_平仓费, unwindData.ValueDate); if (unwindData.SwapMarginAmount != 0) { AddClientCashInCashOut(td, Convert.ToDouble(unwindData.SwapMarginAmount), ClientCashInCashOut.系统操作_应付预付金, unwindData.ValueDate); } if (unwindData.SwapMarginRebatePnl != 0) { AddClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapMarginRebatePnl), ClientCashInCashOut.系统操作_预付金返息, unwindData.ValueDate); } var flowList = new List(unwindData.FlowEvents); var eventId = SaveSwapDeal(unwindData, (int)SwapEventTypeEnum.平仓, clientCashId, actionMsg); if (unwindData.CloseMethod == (int)CloseMethodEnum.全部平仓) { td.TradeStatus = "已平仓"; td.trade_extend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == td.id); if (td.trade_extend != null && !td.trade_extend.ExtendObj.NeedOpenFee) { new ClientCashInCashOutService(this).SaveSwapTradeClientCash(td, td.TradePrice ?? 0, unwindData.ValueDate, 0); } } else { td.HasPartialUnWind = 1; } td.UnWindDate = unwindData.UnwindDate; td.StockEqvNotional -= Convert.ToDouble(unwindData.CloseNotionalValue); td.TradeAmount -= Convert.ToDouble(unwindData.CloseQty); td.Notional = td.TradeAmount; DbContext.SaveChanges(); } /// /// 计算平仓总额 /// /// private void CalcCloseAmount(UnwindData unwindData) { var floatPosition = unwindData.FlowEvents.FirstOrDefault(x => !string.IsNullOrEmpty(x.UnderlyingCode)); var interestList = unwindData.FlowEvents.Where(x => string.IsNullOrEmpty(x.UnderlyingCode)); decimal floatRatio = floatPosition.PayDirection == 1 ? 1m : -1m; var pnl = floatPosition.MarkClosePnl; unwindData.SwapCloseAmount = pnl; unwindData.SwapRealizedPnL = pnl; unwindData.SwapMarginRebatePnl = 0; unwindData.SwapMarginAmount = 0; if (interestList != null) { interestList.ForEach(x => { if (x.InterestMode == (int)InterestModeEnum.追加预付金 || x.InterestMode == (int)InterestModeEnum.初始预付金) { decimal interestRatio = x.InterestDirection == 1 ? -1m : 1m; unwindData.SwapMarginRebatePnl += x.InterestClosePnL; unwindData.SwapMarginAmount += x.InterestPrincipal * interestRatio; } unwindData.SwapRealizedPnL += x.InterestClosePnL; unwindData.SwapCloseAmount += x.InterestClosePnL; }); } unwindData.SwapCloseAmount = decimal.Parse(unwindData.SwapCloseAmount.ToString("0.00")); unwindData.SwapRealizedPnL = unwindData.SwapCloseAmount; } /// /// 多空组合平仓 /// /// /// public void SwapLongShortUnwind(UnwindData unwindData) { var td = DbContext.trade.Find(unwindData.SwapTradeId); if (td == null) { throw new ServiceException("未找到交易信息"); } unwindData.SwapRealizedPnL = unwindData.SwapCloseAmount; var trans = DbContext.Database.BeginTransaction(); try { int clientCashId = CloseTrade_ClientCashInCashOut(td, Convert.ToDouble(unwindData.SwapCloseAmount), ClientCashInCashOut.系统操作_平仓费, unwindData.ValueDate); if (unwindData.SwapMarginAmount != 0) { AddClientCashInCashOut(td, Convert.ToDouble(unwindData.SwapMarginAmount), ClientCashInCashOut.系统操作_应付预付金, unwindData.ValueDate); } if (unwindData.SwapMarginRebatePnl != 0) { AddClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapMarginRebatePnl), ClientCashInCashOut.系统操作_预付金返息, unwindData.ValueDate); } SaveSwapDeal(unwindData, (int)SwapEventTypeEnum.平仓, clientCashId, "系统操作_平仓"); td.UnWindDate = unwindData.UnwindDate; td.StockEqvNotional = 0; td.TradeStatus = "已平仓"; DbContext.SaveChanges(); trans.Commit(); } catch (Exception ex) { trans.Rollback(); throw ex; } finally { trans.Dispose(); } } /// /// 多空组合互换 /// /// /// public void SwapLongShort(UnwindData unwindData) { var td = DbContext.trade.Find(unwindData.SwapTradeId); if (td == null) { throw new ServiceException("未找到交易信息"); } unwindData.SwapRealizedPnL = unwindData.SwapCloseAmount; var trans = DbContext.Database.BeginTransaction(); try { int clientCashId = CloseTrade_ClientCashInCashOut(td, Convert.ToDouble(unwindData.SwapCloseAmount), ClientCashInCashOut.系统操作_互换, unwindData.ValueDate); SaveSwapDeal(unwindData, (int)SwapEventTypeEnum.互换, clientCashId, "系统操作_互换"); trans.Commit(); } catch (Exception ex) { trans.Rollback(); throw ex; } finally { trans.Dispose(); } } /// /// 互换 /// /// /// public void SwapIncome(UnwindData unwindData) { var td = DbContext.trade.Find(unwindData.SwapTradeId); if (td == null) { throw new ServiceException("未找到交易信息"); } CheckLastEod(unwindData.ValueDate, td.StartDate.Value, unwindData.SwapTradeId); var trans = DbContext.Database.BeginTransaction(); bool confirm = false; try { int clientCashId = CloseTrade_ClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapRealizedPnL), ClientCashInCashOut.系统操作_互换, unwindData.ValueDate); if (unwindData.SwapMarginRebatePnl != 0) { AddClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapMarginRebatePnl), ClientCashInCashOut.系统操作_预付金返息, unwindData.ValueDate); } foreach (var item in unwindData.FlowEvents) { item.OptLog = "手工操作"; } SaveSwapDeal(unwindData, (int)SwapEventTypeEnum.互换, clientCashId, "系统操作_互换"); if (td.ExerciseDate <= unwindData.ValueDate) { td.Notional = 0; td.StockEqvNotional = 0; td.TradeStatus = "已到期"; } td.UnWindDate = unwindData.UnwindDate; DbContext.SaveChanges(); trans.Commit(); confirm = true; } catch (Exception ex) { trans.Rollback(); throw ex; } finally { trans.Dispose(); } } /// /// 互换/平仓审核通过 /// /// /// /// public void ApproveSwapTrade(trade td, int eventType) { Expression> eventExpression = x => x.SwapTradeId == td.id && !x.Invalid && x.EventType == eventType; var swapEvent = DbContext.swap_event.Where(eventExpression).OrderByDescending(o => o.id).FirstOrDefault(); if (swapEvent == null) { throw new Exception("该笔交易状态为平仓待复核,未找到相关记录,请检查该笔交易是否有效"); } swapEvent.unwindData = JsonConvert.DeserializeObject(swapEvent.EventData); var flowList = DbContext.swap_flow_event.Where(x => x.EventId == swapEvent.id).ToList(); string action = eventType == (int)SwapEventTypeEnum.互换 ? ClientCashInCashOut.系统操作_互换 : ClientCashInCashOut.系统操作_平仓费; int clientCashId = CloseTrade_ClientCashInCashOut(td, Convert.ToDouble(swapEvent.unwindData.SwapCloseAmount), action, swapEvent.unwindData.ValueDate); swapEvent.ClientCashId = clientCashId; if (td.StructureType != "多空组合") { if (eventType == (int)SwapEventTypeEnum.互换) { if (td.ExerciseDate <= swapEvent.unwindData.ValueDate) { td.Notional = 0; td.StockEqvNotional = 0; td.TradeStatus = ConsTrade.已到期; } else { td.TradeStatus = ConsTrade.确认成交; } } else { if (swapEvent.unwindData.CloseMethod == (int)CloseMethodEnum.全部平仓) { td.TradeStatus = ConsTrade.已平仓; td.Notional = 0; td.TradeAmount = 0; } else { td.TradeStatus = ConsTrade.确认成交; td.HasPartialUnWind = 1; td.TradeAmount -= Convert.ToDouble(swapEvent.unwindData.CloseQty); } td.StockEqvNotional -= Convert.ToDouble(swapEvent.unwindData.CloseNotionalValue); } td.UnWindDate = swapEvent.unwindData.UnwindDate; UpdateInitalPosition(flowList, swapEvent.unwindData, eventType); DbContext.SaveChanges(); } else { if (eventType == (int)SwapEventTypeEnum.平仓) { td.UnWindDate = swapEvent.unwindData.UnwindDate; td.StockEqvNotional = 0; td.TradeStatus = "已平仓"; } UpdateInitalPosition(flowList, swapEvent.unwindData, eventType); DbContext.SaveChanges(); } } /// /// 互换/平仓提交审核 /// /// /// /// public void ApplySwapTrade(UnwindData unwindData, int eventType) { var td = DbContext.trade.Find(unwindData.SwapTradeId); if (td == null) { throw new ServiceException("未找到交易信息"); } unwindData.SwapRealizedPnL = unwindData.SwapCloseAmount; var trans = DbContext.Database.BeginTransaction(); string action = eventType == (int)SwapEventTypeEnum.互换 ? ClientCashInCashOut.系统操作_互换 : ClientCashInCashOut.系统操作_平仓费; try { new TradeUnwindService(this).CloseReCheck_SetTrade(unwindData.SwapTradeId, eventType == (int)SwapEventTypeEnum.互换, true); SaveSwapDeal(unwindData, eventType, 0, action, true); DbContext.SaveChanges(); trans.Commit(); } catch (Exception ex) { trans.Rollback(); throw ex; } finally { trans.Dispose(); } } /// /// 保存平仓/互换事件 /// /// /// private long SaveSwapDeal(UnwindData unwindData, int eventType, int clientCashId, string eventResason = "", bool approve = false) { var flowList = new List(unwindData.FlowEvents); unwindData.FlowEvents.Clear(); string data = JsonConvert.SerializeObject(unwindData); var swapEvent = new SwapEventService(this).AddSwapEventDate(unwindData.ValueDate, unwindData.SwapTradeId, eventType, data, clientCashId, true, eventResason);//将平仓、互换总额存入事件 foreach (var item in flowList) { if (item.PositionType > 0 && eventType == (int)SwapEventTypeEnum.平仓) { item.Quantity = unwindData.CloseQty; item.PositionQty = unwindData.ClosePercent == 1 ? 0 : unwindData.PositionQty - unwindData.CloseQty; } item.PayDate = unwindData.PayDate; item.UnwindDate = unwindData.UnwindDate; item.EventId = swapEvent.id; DbContext.swap_flow_event.Add(item); } if (!approve) { UpdateInitalPosition(flowList, unwindData, eventType); } DbContext.SaveChanges(); return swapEvent.id; } /// /// 计算平仓数据 /// /// private void DealFloatPosition(UnwindData unwindData) { foreach (var item in unwindData.FlowEvents) { item.OptLog = "手工操作"; if (item.PositionType > 0 && item.EventType == (int)SwapEventTypeEnum.平仓) { decimal shortRatio = item.PositionType == (int)PositionTypeFlag.Long ? -1m : 1m; item.TradingAmountFeeAvg = item.TradingAmountAvg + item.TradingFeePending / unwindData.CloseQty * shortRatio; item.TradingAmountNetFeeAvg = item.TradingAmountNetAvg + item.TradingFeePending / unwindData.CloseQty * shortRatio; } } } /// /// 单标的互换更新实时持仓信息 /// /// private void UpdateInitalPosition(List flowList, UnwindData unwindData, int eventType) { var positions = DbContext.swap_position.Where(x => !x.IsInitial && x.SwapTradeId == unwindData.SwapTradeId && !x.Invalid); foreach (var position in positions) { if (!string.IsNullOrEmpty(position.UnderlyingCode)) { var dealFloat = flowList.FirstOrDefault(x => !string.IsNullOrEmpty(x.UnderlyingCode)); if (dealFloat != null) { decimal ratio = dealFloat.EventType == (int)SwapFlowEventTypeEnum.平仓 ? -1m : 1m; position.PosiTradingFeePending += dealFloat.TradingFeePending * ratio; position.PosiDividendIncome += dealFloat.DividendPending; } position.PosiQuantity -= unwindData.CloseQty; position.PosiNotionalValue = position.PosiQuantity * position.ContractSize; } else { var interest = flowList.FirstOrDefault(x => x.PositionId == position.PositionId); if (interest != null) { position.InterestAmount += interest.InterestAmount; position.InterestFeePending += interest.InterestFee; if ((interest.InterestMode == (int)InterestModeEnum.追加预付金 || interest.InterestMode == (int)InterestModeEnum.初始预付金) && eventType == (int)SwapEventTypeEnum.平仓) { position.InterestPrincipalFix -= interest.InterestPrincipal; } } } } } /// /// 单标的互换更新实时持仓信息 /// /// /// /// public void UpdateInitalPosition(DateTime valueDate, trade td, List positions, List swpFlowEvents) { var preSettleDate = GetPreValueDate(valueDate);//上一交易日期 var realPositions = positions.Where(x => !x.IsInitial); var originalPositions = positions.Where(x => x.IsInitial); var unwindFlowEvents = swpFlowEvents.Where(x => x.EventType == (int)SwapFlowEventTypeEnum.开仓 || x.EventType == (int)SwapFlowEventTypeEnum.平仓).ToList(); List preEodSwapPositions = DbContext.eod_swap_position.Where(x => !x.Invalid && x.SwapTradeId == td.id && valueDate == preSettleDate).ToList(); foreach (var position in realPositions) { var currEod = new eod_swap_position() { ContractSize = position.ContractSize }; var eod = preEodSwapPositions.FirstOrDefault(x => x.PositionId == position.PositionId); if (eod == null) { eod = new eod_swap_position() { ContractSize = position.ContractSize, PositionType = position.PositionType, PosiDirection = position.PosiDirection, PosiFeePending = 0, PosiNetPrice = position.PosiNetPrice, PosiGrossPrice = position.PosiGrossPrice, PosiNetFeePrice = position.PosiNetFeePrice, PosiNetNoFeePrice = position.PosiNetNoFeePrice, }; } new SwapEodPositionService(UserInfo).SetPriceInfoByFlowEvent(eod, currEod, unwindFlowEvents, position); var oriPosition = originalPositions.FirstOrDefault(x => x.id == position.PositionId); position.InterestAmount = currEod.RealizedInterest; position.InterestFeePending = currEod.InterestFeePending; position.PosiTradingFeePending = currEod.PosiFeePending; position.PosiDividendIncome = currEod.PosiDividendSum; position.PosiQuantity = currEod.PosiQuantity; position.PosiNetPrice = currEod.PosiNetPrice; position.PosiGrossPrice = currEod.PosiGrossPrice; position.PosiNetFeePrice = currEod.PosiNetFeePrice; position.PosiNetNoFeePrice = currEod.PosiNetNoFeePrice; position.PosiNotionalValue = currEod.PosiNotionalValue; td.StockEqvNotional = Convert.ToDouble(position.PosiNotionalValue); td.TradeAmount = Convert.ToDouble(position.PosiQuantity); } } /// /// 获取标的平仓比例 /// /// /// private decimal GetUnwindPercent(int tradeId) { var swapEvents = DbContext.swap_event.Where(x => x.SwapTradeId == tradeId && !x.Invalid && x.EventType == (int)SwapEventTypeEnum.平仓).ToList(); decimal unwindPercent = 0; foreach (var swapEvent in swapEvents) { if (!string.IsNullOrEmpty(swapEvent.EventData)) { swapEvent.unwindData = JsonConvert.DeserializeObject(swapEvent.EventData); unwindPercent += swapEvent.unwindData.ClosePercent; } } return unwindPercent; } } }