using BaseOUDAL; using ClosedXML.Report.Utils; using CsvHelper; using DocumentFormat.OpenXml.Spreadsheet; using MathNet.Numerics.LinearAlgebra.Factorization; using MoreLinq; using Newtonsoft.Json; using NPOI.POIFS.NIO; using NPOI.SS.Formula.Functions; using NPOI.SS.UserModel; using Org.BouncyCastle.Asn1.Cmp; using Org.BouncyCastle.Math.EC.Multiplier; using Org.BouncyCastle.Ocsp; using Qdp.Pricing.Library.Common.Products.Abs; using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Security.Cryptography.Xml; using YLErp.BLL; using YLErp.DBModels; using YLErp.DBModels.Consts; using YLErp.DBModels.Enums; using YLErp.Model; using YLErp.Model.Enum; using YLErp.Models; using YLErp.Modules.DataProviderModule; using YLErp.Modules.EodModule; using YLErp.Modules.ReportModule; using YLErp.Modules.TradeModule; using YLErp.QdpModule; using static alglib; using static YLErp.ConsGlobal; using static YLErp.DBModels.ConsTrade; namespace YLErp.Modules.SwapModule { /// /// 互换流水日终归档服务 /// public class SwapEodPositionService : SwapTradeBaseService { private List marginTypes = new List() { (int)InterestModeEnum.追加预付金, (int)InterestModeEnum.初始预付金 }; public SwapEodPositionService(OptUserInfo optUser) : base(optUser) { } public SwapEodPositionService(YLBaseService baseService) : base(baseService) { } /// /// 多空组合 互换流水合成持仓 /// /// 清算日期 public void SwapFlowEventCompose(DateTime tradeDate) { new SysJobService(UserInfo).UpdateJob("互换流水合成持仓", 41, "互换流水合成持仓进行中"); var eventQueryGroup = DbContext.swap_flow_event.Where(n => n.DataState == (int)SwapFlowDateStateEnum.等待完成 && n.EventDate == tradeDate).GroupBy(g => g.SwapTradeId); foreach (var eventQueryGroupItem in eventQueryGroup) { ComposePage(eventQueryGroupItem.Key, eventQueryGroupItem.ToList(), tradeDate); } new SysJobService(UserInfo).UpdateJob("互换流水合成持仓", 42, "互换流水合成持仓完成"); } /// /// 多空组合 互换流水合成持仓 /// /// 清算日期 public void SwapFlowEventCompose(List flowEventIds, DateTime tradeDate) { new SysJobService(UserInfo).UpdateJob("互换流水合成持仓", 41, "互换流水合成持仓进行中"); var eventQueryGroup = DbContext.swap_flow_event.Where(n => flowEventIds.Contains(n.id)).ToList().GroupBy(g => g.SwapTradeId); foreach (var eventQueryGroupItem in eventQueryGroup) { ComposePage(eventQueryGroupItem.Key, eventQueryGroupItem.ToList(), tradeDate, false); } new SysJobService(UserInfo).UpdateJob("互换流水合成持仓", 42, "互换流水合成持仓完成"); } /// /// 收盘生成归档信息 /// /// 结算日期 public void SwapPositionCompose(DateTime settleDate, DateTime preSettleDate, IEnumerable ClientIds) { var dateStr = settleDate.ToString("yyyy-MM-dd"); var tradePredicate = PredicateBuilder.Create(n => n.ValidState != ConsGlobal.InValid && n.TradeType == "收益互换" && n.TradeDate <= settleDate && n.ExerciseDate >= settleDate && (n.TradeStatus == ConsTrade.确认成交 || n.UnWindDate >= settleDate) ); if (ClientIds != null && ClientIds.Any()) { tradePredicate = tradePredicate.And(x => ClientIds.Contains(x.ClientId)); } var tradeQueryList = DbContext.trade.Where(tradePredicate).ToList(); var tradeIds = tradeQueryList.Select(s => s.id).ToList(); var allTradePositionList = DbContext.swap_position.Where(t => tradeIds.Contains(t.SwapTradeId) && !t.Invalid).ToList(); var tradePositionList = allTradePositionList.Where(t => t.IsInitial).ToList(); var tradeRealPositionList = allTradePositionList.Where(t => !t.IsInitial).ToList(); var tradeExtendList = DbContext.trade_extend.Where(x => tradeIds.Contains(x.TradeId)).ToList(); var eodSwapPositionList = DbContext.eod_swap_position.Where(x => x.ValueDate >= preSettleDate && x.ValueDate <= settleDate && tradeIds.Contains(x.SwapTradeId) && !x.Invalid).ToList(); var eodSwapList = DbContext.eod_swap.Where(x => x.ValueDate == preSettleDate).ToList(); List eventTyps = new List() { (int)SwapEventTypeEnum.平仓, (int)SwapEventTypeEnum.互换, (int)SwapEventTypeEnum.自动互换 }; foreach (var td in tradeQueryList) { var trans = DbContext.Database.BeginTransaction(); try { List removeEventTyps = new List() { (int)SwapEventTypeEnum.自动互换 }; bool longShort = td.StructureType == ClientMarginTypeEnum.多空组合.ToString(); ClearSwapPositions(td, settleDate, removeEventTyps, true); var positions = tradePositionList.Where(x => x.SwapTradeId == td.id); var realPositions = tradeRealPositionList.Where(s => s.SwapTradeId == td.id); var posiList = positions.Where(x => x.PosiQuantity > 0).ToList(); var realPosiList = realPositions.ToList(); var interestList = positions.Where(x => x.InterestDirection > 0).ToList(); DateTime posiDate = td.TradeDate.Value;//交易日期 var lastEodSwap = eodSwapList.FirstOrDefault(x => x.SwapTradeId == td.id); //上一交易日无日终归档,且不是交易日期,且当前收盘日期不是交易日期,报错 if (lastEodSwap == null && settleDate > posiDate) { throw new Exception($"交易{td.TradeNumber}在上一交易日【{preSettleDate:yyyy-MM-dd}】未收盘"); } var allEodPositions = eodSwapPositionList.Where(x => x.ValueDate >= preSettleDate && x.SwapTradeId == td.id && !x.Invalid); var eodPositions = allEodPositions.Where(x => x.ValueDate == preSettleDate).ToList();//上一日终持仓信息 var tradeExtend = tradeExtendList.FirstOrDefault(x => x.TradeId == td.id); td.trade_extend = tradeExtend; var todyEodPositions = allEodPositions.Where(x => x.ValueDate == settleDate).ToList(); var allPositionQty = realPositions.Sum(x => x.PosiQuantity);//总剩余持仓数量 if (longShort && td.ExerciseDate.Value == settleDate && allPositionQty != 0)//多空组合判断是否已到到期日且无持仓信息 { throw new Exception($"交易【{td.TradeNumber}】到期扔有持仓信息"); } //实际自动互换数据开头已删除 var longEventTypes = eventTyps; longEventTypes.Add((int)SwapFlowEventTypeEnum.开仓); var flowEvents = new List(); Expression> eventExpression = x => x.SwapTradeId == td.id && x.DataState == (int)SwapFlowDateStateEnum.完成 && longEventTypes.Contains(x.EventType); if (settleDate == td.TradeDate) { eventExpression = eventExpression.And(x => x.EventDate == settleDate); } else { eventExpression = eventExpression.And(x => x.UnwindDate == settleDate); } flowEvents = DbContext.swap_flow_event.Where(eventExpression).ToList(); var preDealDate = GetPreDealDate(td.id, settleDate, eventTyps);//上一次平仓/互换/自动互换处理日期 List autoInterests = new List();//自动互换利息腿信息 //处理浮动腿 var curEodPosis = DealFloatPositions(posiList, realPosiList, eodPositions, todyEodPositions, settleDate, td, preSettleDate, flowEvents); var posiLongNotional = curEodPosis.Where(s => s.PositionType == (int)PositionTypeFlag.Long).Sum(s => s.PosiNotionalValue); var posiShortNotional = curEodPosis.Where(s => s.PositionType == (int)PositionTypeFlag.Short).Sum(s => s.PosiNotionalValue); var closePosiNotional = curEodPosis.Where(s => s.TdCloseQty > 0).Sum(s => s.TdCloseQty * s.ContractSize); if (longShort) { closePosiNotional = curEodPosis.Where(s => s.TdCloseQty > 0).Sum(s => s.TdCloseQty * s.ContractSize * s.PosiNetPrice); } var grossPrice = curEodPosis.Where(x => x.PosiDirection > 0).FirstOrDefault()?.PosiGrossPrice ?? 0; //处理利息腿 DealInterests(interestList, eodPositions, todyEodPositions, settleDate, td, flowEvents, autoInterests, lastEodSwap, posiLongNotional, posiShortNotional, closePosiNotional, grossPrice); DealAutoInterests(autoInterests, td, settleDate, preDealDate, posiLongNotional + posiShortNotional); //多空组合判断是否已到到期日且无持仓信息 if (longShort && td.ExerciseDate.Value == settleDate && allPositionQty == 0) { td.TradeStatus = "已到期"; td.UnWindDate = settleDate; } DbContext.SaveChanges(); trans.Commit(); } catch (Exception ex) { trans.Rollback(); throw new Exception(ex.Message, ex); } finally { trans.Dispose(); } } } /// /// 框架合约汇总 /// /// /// /// public void SwapEodCompose(DateTime settleDate, DateTime preSettleDate, IEnumerable ClientIds) { var tradePredicate = PredicateBuilder.Create(n => n.ValidState != ConsGlobal.InValid && n.TradeType == "收益互换" && n.TradeDate <= settleDate && n.ExerciseDate >= settleDate && (n.TradeStatus == ConsTrade.确认成交 || n.UnWindDate >= settleDate) ); if (ClientIds != null && ClientIds.Any()) { tradePredicate = tradePredicate.And(x => ClientIds.Contains(x.ClientId)); } var tradeQueryList = DbContext.trade.Where(tradePredicate).ToList(); foreach (var td in tradeQueryList) { SaveEodSwap(td, settleDate, preSettleDate); } DbContext.SaveChanges(); } /// /// 追加预付金腿归档 /// /// /// public void SwapEodMarginCompose(DateTime settleDate, IEnumerable ClientIds) { var tradePredicate = PredicateBuilder.Create(n => n.ValidState != ConsGlobal.InValid && n.TradeType == "收益互换" && n.TradeDate <= settleDate && n.ExerciseDate >= settleDate && (n.TradeStatus == ConsTrade.确认成交 || n.UnWindDate >= settleDate) ); if (ClientIds != null && ClientIds.Any()) { tradePredicate = tradePredicate.And(x => ClientIds.Contains(x.ClientId)); } var marginPredicate = PredicateBuilder.Create(n => n.IsInitial && n.HappenDate == settleDate && n.InterestMode == (int)InterestModeEnum.追加预付金 && !n.Invalid); var query = from p in DbContext.swap_position.Where(marginPredicate) join t in DbContext.trade.Where(tradePredicate) on p.SwapTradeId equals t.id select new { t, p }; var result = query.ToList(); var posiIds = result.Select(x => x.p.id).ToList(); var eodSwapPositions = DbContext.eod_swap_position.Where(x => posiIds.Contains(x.SwapTradeId) && x.ValueDate == settleDate).ToList(); foreach (var item in result) { List interestList = new List() { item.p }; var td = item.t; var eodPosi = eodSwapPositions.FirstOrDefault(x => x.PositionId == item.p.id); var posiLongNotional = eodSwapPositions.Where(s => s.PositionType == (int)PositionTypeFlag.Long).Sum(s => s.PosiNotionalValue); var posiShortNotional = eodSwapPositions.Where(s => s.PositionType == (int)PositionTypeFlag.Short).Sum(s => s.PosiNotionalValue); var grossPrice = eodSwapPositions.Where(x => x.PosiDirection > 0).FirstOrDefault()?.PosiGrossPrice ?? 0; //处理利息腿 DealInterest(item.p, eodPosi, settleDate, td, posiLongNotional, posiShortNotional, grossPrice); } if (result.Count > 0) { DbContext.SaveChanges(); } } /// /// 处理利息腿归档 /// /// 利息腿持仓信息 /// 上一日终归档持仓信息 /// 当日归档持仓信息 /// 当前结算日期 /// 互换交易主信息 /// 当日平仓/互换信息 /// 自动互换集合 /// 多空组合平仓利息腿信息 /// 上一日终框架合约 private void DealInterests(List interestList, List eodPositions, List todyEodPositions, DateTime settleDate, trade td, List flowEvents, List autoInterests, eod_swap lastEodSwap, decimal posiLongNational, decimal posiShortNational, decimal closeNational, decimal grossPrice) { var hasClose = flowEvents.Any(x => x.EventType == (int)SwapEventTypeEnum.平仓); var hasSwap = flowEvents.Any(x => x.EventType == (int)SwapEventTypeEnum.互换); foreach (var interest in interestList) { if (interest.InterestMode == (int)InterestModeEnum.追加预付金 && interest.HappenDate < settleDate) { continue; } var eodPosition = eodPositions.FirstOrDefault(x => x.PositionId == interest.id);//上一日日终利息信息 可能不存在 var tdEodPosition = todyEodPositions.FirstOrDefault(x => x.PositionId == interest.id && x.IsSkipEod);//当前结算日日终利息信息 var insterval = interest.SwapIntervalList.FirstOrDefault(x => x.Date == settleDate && x.Settlement == 1);//自动互换观察日信息 List dealInterests = new List(); dealInterests.AddRange(flowEvents); var dealInterest = dealInterests.FirstOrDefault(n => n.PositionId == interest.id);//当日是否做过互换或平仓 var swapEvents = flowEvents.Where(x => (x.EventType == (int)SwapEventTypeEnum.互换 || x.EventType == (int)SwapEventTypeEnum.平仓) && x.PositionId == interest.id).ToList(); //如果当日有互换/当日有平仓 不再重新生成或更新 if (insterval != null && !hasSwap) { if (!hasClose)//当日无平仓 { var _autoInterests = SaveAutoEodInterestPosition(eodPosition, tdEodPosition, interest, td, settleDate, insterval, lastEodSwap, posiLongNational, posiShortNational, grossPrice); if (_autoInterests.Count > 0) { autoInterests.AddRange(_autoInterests); } } else { var _autoInterests = SaveAutoEodWithCloseInterestPosition(eodPosition, tdEodPosition, interest, td, settleDate, insterval, posiLongNational, posiShortNational, swapEvents, closeNational, true, grossPrice); if (_autoInterests.Count > 0) { autoInterests.AddRange(_autoInterests); } } } else if (hasSwap)//当日有互换,根据互换事件重新生成 { SaveEodInterestPosition(eodPosition, tdEodPosition, interest, td, settleDate, swapEvents); } else if (hasClose) { SaveAutoEodWithCloseInterestPosition(eodPosition, tdEodPosition, interest, td, settleDate, insterval, posiLongNational, posiShortNational, swapEvents, closeNational, false, grossPrice); } else//无自动互换、互换/平仓,复制上一日终信息,并计算当日新增利息 { SaveEodInterestPositionCopy(eodPosition, tdEodPosition, settleDate, td, interest, lastEodSwap, true, posiLongNational, posiShortNational, grossPrice); } } } /// /// 处理利息腿归档 /// /// 利息腿持仓信息 /// 上一日终归档持仓信息 /// 当日归档持仓信息 /// 当前结算日期 /// 互换交易主信息 /// 当日平仓/互换信息 /// 自动互换集合 /// 多空组合平仓利息腿信息 /// 上一日终框架合约 private void DealInterest(swap_position interest, eod_swap_position tdEodPosition, DateTime settleDate, trade td, decimal posiLongNational, decimal posiShortNational, decimal grossPrice) { var insterval = interest.SwapIntervalList.FirstOrDefault(x => x.Date == settleDate && x.Settlement == 1);//自动互换观察日信息 SaveAutoEodInterestPosition(null, tdEodPosition, interest, td, settleDate, insterval, null, posiLongNational, posiShortNational, grossPrice); } /// /// 处理浮动腿归档 /// /// /// /// /// /// /// /// /// private List DealFloatPositions(List posiList, List realPosiList, List eodPositions, List todyEodPositions, DateTime settleDate, trade td, DateTime preSettleDate, List flowEvents) { List list = new List(); foreach (var posi in posiList) { var eodPosition = eodPositions.Where(x => x.PositionId == posi.id).FirstOrDefault();//上一日日终持仓信息 var tdEodPosition = todyEodPositions.FirstOrDefault(x => x.PositionId == posi.id && x.IsSkipEod);//当前结算日日终持仓信息 var unwindEvents = flowEvents.Where(x => x.PositionId == posi.id).ToList();//当前日平仓信息 var realPosition = realPosiList.FirstOrDefault(s => s.PositionId == posi.id); eod_swap_position eodPosi = new eod_swap_position(); if (eodPosition == null || td.StartDate == settleDate) { if (tdEodPosition == null)//第一次生成归档信息 { eodPosi = SaveCurrentEodInitalPosi(posi, td, settleDate, preSettleDate, unwindEvents); } else { tdEodPosition.PosiFeePending = posi.PosiTradingFeePending; UpdateHTData(tdEodPosition); tdEodPosition.PosiNotionalValue = tdEodPosition.PosiQuantity * tdEodPosition.ContractSize; tdEodPosition.PosiNetFeePrice = posi.PosiNetFeePrice; tdEodPosition.PosiNetNoFeePrice = posi.PosiNetNoFeePrice; } } else if (unwindEvents.Count() == 0) { eodPosi = CopyEodPosition(eodPosition, tdEodPosition, td, settleDate, preSettleDate); } else { eodPosi = UpdateEodPosition(posi, eodPosition, tdEodPosition, td, settleDate, preSettleDate, unwindEvents); } list.Add(eodPosi); } return list; } /// /// 处理自动互换数据 /// /// /// /// /// private void DealAutoInterests(List autoInterests, trade td, DateTime settleDate, DateTime? preDealDate, decimal StockEqvNotional) { if (autoInterests.Count == 0) { return; } //生成自动互换事件 UnwindData unwindData = new UnwindData(); unwindData.SwapTradeId = td.id; unwindData.ValueDate = settleDate; if (preDealDate.HasValue) { unwindData.StartDate = preDealDate.Value; } else { unwindData.StartDate = td.StartDate.Value; } unwindData.NotionalValue = Convert.ToDecimal(td.OriginalStockEqvNotional ?? 0); unwindData.PosiNotionalValue = StockEqvNotional; autoInterests.ForEach(x => { var ratio = x.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1; unwindData.SwapCloseAmount = unwindData.SwapCloseAmount + x.InterestClosePnL; unwindData.SwapRealizedPnL = unwindData.SwapCloseAmount; }); SaveAutoSwapDeal(td, autoInterests, unwindData); } /// /// 保存自动互换数据信息 /// /// /// private long SaveAutoSwapDeal(trade td, List flowEvents, UnwindData unwindData) { //td.UnWindDate = unwindData.ValueDate; int clientCashId = CloseTrade_ClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapCloseAmount), ClientCashInCashOut.系统操作_互换, unwindData.ValueDate); string data = JsonConvert.SerializeObject(unwindData); var swapEvent = new SwapEventService(this).AddSwapEventDate(unwindData.ValueDate, unwindData.SwapTradeId, (int)SwapEventTypeEnum.自动互换, data, clientCashId, true, "系统操作-自动互换");//将互换总额存入事件 flowEvents.ForEach(x => { x.EventId = swapEvent.id; DbContext.swap_flow_event.Add(x); }); UpdateInitalPostion(flowEvents, td.id); return swapEvent.id; } /// /// 互换更新实时持仓信息 /// /// private void UpdateInitalPostion(List flowEvents, int swapTradeId) { var positions = DbContext.swap_position.Where(x => !x.IsInitial && x.SwapTradeId == swapTradeId && !x.Invalid); foreach (var position in positions) { var interest = flowEvents.FirstOrDefault(x => x.PositionId == position.id); if (interest != null) { position.InterestAmount += interest.InterestAmount; UpdateDbOption(position); } } } /// /// 分页合成互换流水开平仓事件,暂时只按加权平均处理 /// /// 互换交易id /// 清算日期 private void ComposePage(int swapTradeId, List flowEvents, DateTime tradeDate, bool needTrans = true) { // 同一标的 事件编码一致 var eventQuery = flowEvents.OrderBy(o => o.PositionId).ToList(); if (eventQuery.Count == 0) { return; } SwapTradeService swapTradeService = new SwapTradeService(this); var trans = needTrans ? DbContext.Database.BeginTransaction() : null; try { UnwindData unwindData = new UnwindData(); unwindData.SwapTradeId = swapTradeId; var swapEvent = new SwapEventService(this).AddSwapEventDate(tradeDate, swapTradeId, (int)SwapEventTypeEnum.合成持仓, string.Empty, 0, true, "系统操作-自动合成持仓");//不用回退,回退时过滤该事件类型 var td = DbContext.trade.Find(swapTradeId); var preSettleDate = GetPreValueDate(tradeDate);//上一交易日期 List eventTyps = new List() { (int)SwapEventTypeEnum.平仓, (int)SwapEventTypeEnum.互换, (int)SwapEventTypeEnum.自动互换 }; List removeEventTyps = new List() { (int)SwapEventTypeEnum.平仓, (int)SwapEventTypeEnum.互换, (int)SwapEventTypeEnum.自动互换 }; ClearSwapPositions(td, tradeDate, removeEventTyps, false); td.trade_extend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == swapTradeId); var allEodPositions = DbContext.eod_swap_position.Where(x => x.SwapTradeId == swapTradeId && !x.Invalid && x.ValueDate >= preSettleDate); //var curentInvalidEodPostions = allEodPositions.Where(x => x.SwapTradeId == swapTradeId && !x.Invalid && x.ValueDate >= tradeDate);//废弃当前清算日期之后的归档数据 //curentInvalidEodPostions.ForEach(x => //{ // x.Invalid = true; //}); var eodPositions = allEodPositions.Where(x => x.ValueDate == preSettleDate).ToList();//上一日终持仓信息 var positions = DbContext.swap_position.Where(x => x.SwapTradeId == swapTradeId && !x.Invalid).ToList(); var oriPositions = positions.Where(x => x.IsInitial).ToList(); var realPositions = positions.Where(x => !x.IsInitial).ToList(); var fpositions = positions.Where(x => x.PosiDirection > 0).ToList(); decimal tdCloseQty = 0; decimal totalPosiNotionalValue = 0;//总剩余名义本金 decimal tdCloseNotionalValue = 0;//当日平仓名义本金 var preDealDate = GetPreDealDate(swapTradeId, tradeDate, eventTyps);//上一次平仓/互换/自动互换事件日期 var lastEodSwap = DbContext.eod_swap.FirstOrDefault(x => x.SwapTradeId == swapTradeId && x.ValueDate == preSettleDate); decimal stockEqvNotional = lastEodSwap == null ? Convert.ToDecimal(td.OriginalStockEqvNotional ?? 0) : lastEodSwap.NotionalValue;//上一日名义本金 unwindData.NotionalValue = stockEqvNotional; List longshortCloseInterests = new List(); decimal tradePrice = 0;//开仓费 decimal allPosiNotionalValue = 0; decimal longNotionalValue = realPositions.Where(s => s.PositionType == (int)PositionTypeFlag.Long).Sum(s => s.PosiNotionalValue);//剩余多头名义本金规模 decimal shortNotionalValue = realPositions.Where(s => s.PositionType == (int)PositionTypeFlag.Short).Sum(s => s.PosiNotionalValue);//剩余空头名义本金规模 foreach (var eventGroup in eventQuery.GroupBy(g => g.PositionId))//持仓标的腿合成持仓 { var eventList = eventGroup.ToList(); var eventFlow = eventGroup.Last(); var fposition = fpositions.FirstOrDefault(n => n.PositionId == eventGroup.Key); var position = fpositions.FirstOrDefault(n => n.id == eventGroup.Key); var eodPayPosition = allEodPositions.Where(x => x.PositionId == eventFlow.PositionId).OrderByDescending(o => o.ValueDate).FirstOrDefault();//浮动腿 日终持仓信息 decimal netPrice = fposition == null ? 0 : fposition.PosiNetPrice;//持仓均价 decimal grossPrice = fposition == null ? 0 : fposition.PosiGrossPrice;//持仓均价-不含费 decimal netFeePrice = fposition == null ? 0 : fposition.PosiNetFeePrice ?? 0;//持仓净价-含费 decimal netNoFeePrice = fposition == null ? 0 : fposition.PosiNetNoFeePrice ?? 0;//持仓净价-不含费 decimal tradingFee = fposition == null ? 0 : fposition.PosiTradingFeePending;//持仓交易费用 decimal payQty = fposition == null ? 0 : fposition.PosiQuantity;//持仓数量 decimal posiNotionalValue = fposition == null ? 0 : fposition.PosiNotionalValue;//剩余名义本金 decimal dividendIn = 0;//当日浮动端分红 decimal tdDividendIn = 0;//当日浮动端平仓盈亏分红 decimal openQty = fposition == null ? 0 : fposition.PosiQuantity;//开仓数量 decimal openAmount = fposition == null ? 0 : openQty * grossPrice;//开仓累计成交金额不含费 decimal openAmountFee = fposition == null ? 0 : openQty * netPrice;//开仓累计成交金额含费 decimal openAmountNetFee = fposition == null ? 0 : openQty * netFeePrice;//开仓累计成交净价金额含费 decimal openAmountNet = fposition == null ? 0 : openQty * netNoFeePrice;//开仓累计成交净价金额不含费 decimal closeQty = 0;//当日平仓数量 decimal closeFee = 0;//当日平仓费用 decimal closeMtmPnl = 0;//当日平仓盈亏 var posiType = fposition == null ? 0 : fposition.PositionType; var swapInsertEnum = SwapPushDataEnum.收益互换交易新增; var swapPosiEnum = SwapPushDataEnum.互换资产交易新增; if (fposition != null) { swapInsertEnum = SwapPushDataEnum.收益互换交易修改; swapPosiEnum = SwapPushDataEnum.互换资产交易修改; } eventList.ForEach(x => { x.EventId = swapEvent.id; decimal ratio = x.EventType == 1 ? 1 : -1;//开仓为加法,平仓为减法 tradingFee = tradingFee + x.TradingFeePending;//开仓累计 if (x.EventType == 1) { openAmountFee = openAmountFee + x.TradingAmountFeeAvg * x.Quantity; openAmount = openAmount + x.TradingAmountAvg * x.Quantity; openAmountNetFee = openAmountNetFee + (x.TradingAmountNetFeeAvg * x.Quantity) ?? 0; openAmountNet = openAmountNet + (x.TradingAmountNetAvg * x.Quantity) ?? 0; openQty = openQty + x.Quantity; if (posiType != x.PositionType) { payQty = x.Quantity; posiType = x.PositionType; } else { payQty = payQty + x.Quantity; } } else { decimal amount = x.MarkClosePnl + x.CloseFee + x.DividendIn; //记录资金记录 if (amount != 0) { int clientCashId = CloseTrade_ClientCashInCashOut(td, Convert.ToDouble(x.MarkClosePnl), ClientCashInCashOut.系统操作_平仓费, x.UnwindDate.Value); x.ClientCashId = clientCashId; } payQty = payQty - x.Quantity; closeQty = closeQty + x.Quantity; closeFee = closeFee + x.CloseFee; tdDividendIn = tdDividendIn + x.DividendIn; tdCloseNotionalValue = tdCloseNotionalValue + x.TradingAmount; } closeMtmPnl = closeMtmPnl + x.MarkClosePnl; x.PositionQty = payQty; dividendIn = dividendIn + x.DividendIn; x.DataState = (int)SwapFlowDateStateEnum.完成; }); tdCloseQty += closeQty; if (eventFlow.EventType == 1)//最后一条是开仓 { payQty = eventFlow.Quantity; } bool newOpen = openQty != 0 && closeQty == 0; if (openQty != 0 && closeQty == 0)//只有开仓,价格加权平均 { netPrice = openAmountFee / openQty;//持仓均价=((上一日持仓含费均价*上一日持仓数量)+(开仓成交均价*开仓数量))/所有开仓数量 grossPrice = openAmount / openQty;//持仓均价-不含费=((上一日持仓不含费均价*上一日持仓数量)+(开仓成交均价-不含费*开仓数量))/所有开仓数量 netFeePrice = openAmountNetFee / openQty; netNoFeePrice = openAmountNet / openQty; } else if (posiType != fposition?.PositionType)//平仓完新开仓 { netPrice = eventFlow.TradingAmountFeeAvg; grossPrice = eventFlow.TradingAmountAvg; netFeePrice = eventFlow.TradingAmountNetFeeAvg ?? 0; netNoFeePrice = eventFlow.TradingAmountNetAvg ?? 0; } if (eodPayPosition == null)//无日终持仓 { eodPayPosition = new eod_swap_position(); eodPayPosition.PosiStartDate = eventFlow.PayDate.Value; eodPayPosition.ClientId = td.ClientId; eodPayPosition.SwapTradeId = td.id; eodPayPosition.ContractSize = eventFlow.ContractSize; } posiNotionalValue = eventFlow.ContractSize * netPrice * Math.Abs(payQty); totalPosiNotionalValue = totalPosiNotionalValue + posiNotionalValue; allPosiNotionalValue += posiNotionalValue; tradePrice += SaveEodPosition(eodPayPosition, td, eventFlow, netPrice, grossPrice, netFeePrice, netNoFeePrice, payQty, tradingFee, posiNotionalValue, dividendIn, tdDividendIn, closeQty, closeFee, closeMtmPnl, posiType, fposition == null); } if (tdCloseQty != 0) { td.HasPartialUnWind = 1; } // td.StockEqvNotional += Convert.ToDouble(totalPosiNotionalValue); td.TradePrice += Convert.ToDouble(tradePrice); unwindData.PosiNotionalValue = allPosiNotionalValue; unwindData.CloseNotionalValue = tdCloseNotionalValue; swapEvent.EventData = JsonHelper.Serialize(unwindData); DbContext.SaveChanges(); SaveEodSwap(td, tradeDate,preSettleDate); DbContext.SaveChanges(); trans?.Commit(); } catch (Exception ex) { trans?.Rollback(); throw new Exception(ex.Message, ex); } finally { trans?.Dispose(); } } /// /// 产生互换用 /// /// 上一日日终归档 /// 当日归档 /// 持仓腿 /// 主体交易 /// 计息开始日 /// 计息结束日 /// 平仓金额 private void SaveEodInterestPosition(eod_swap_position eodPayPosition, eod_swap_position newEodPayPosition, swap_position position, trade td, DateTime valueDate, List flowEvents) { if (eodPayPosition == null) { eodPayPosition = new eod_swap_position(); eodPayPosition.ClientId = td.ClientId; eodPayPosition.SwapTradeId = td.id; eodPayPosition.PosiStartDate = position.PosiStartDate; eodPayPosition.PosiMatuirityDate = td.ExerciseDate.Value; } var tradeExtend = td.trade_extend.ExtendObj; decimal ratio = position.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;//收取为正,支付为负 if (marginTypes.Contains(position.InterestMode)) { ratio = -ratio; } if (newEodPayPosition == null) { newEodPayPosition = new eod_swap_position(); newEodPayPosition.ClientId = td.ClientId; newEodPayPosition.SwapTradeId = td.id; newEodPayPosition.PosiStartDate = position.PosiStartDate; eodPayPosition.PosiMatuirityDate = td.ExerciseDate.Value; } if (newEodPayPosition.IsSkipEod) { newEodPayPosition.FloatRate = flowEvents.FirstOrDefault()?.FloatRate ?? 0; newEodPayPosition.FloatRateUnderlyingCode = position.FloatRateUnderlyingCode; return; } newEodPayPosition.ValueDate = valueDate; newEodPayPosition.PositionId = position.id; newEodPayPosition.ClientId = td.ClientId; newEodPayPosition.SwapTradeId = td.id; UpdateDbOption(newEodPayPosition); newEodPayPosition.PosiStatus = 0; newEodPayPosition.Invalid = false; //持仓内容-利息腿 newEodPayPosition.InterestDirection = position.InterestDirection; newEodPayPosition.InterestMode = position.InterestMode; newEodPayPosition.InterestPrincipalFix = position.InterestPrincipalFix; newEodPayPosition.InterestRateDefault = position.InterestRateDefault; newEodPayPosition.InterestSwapInterval = position.InterestSwapInterval; newEodPayPosition.IsAnnualized = position.IsAnnualized; newEodPayPosition.HappenDate = position.HappenDate; newEodPayPosition.Currency = position.Currency; newEodPayPosition.InterestType = position.InterestType; newEodPayPosition.FloatRate = flowEvents.FirstOrDefault()?.FloatRate ?? 0; newEodPayPosition.FloatRateUnderlyingCode = position.FloatRateUnderlyingCode; newEodPayPosition.InterestFeePending = 0; //利息端估值用信息 newEodPayPosition.TdInterestPrincipal = flowEvents.FirstOrDefault()?.InterestPrincipal ?? 0; newEodPayPosition.TdInterestRate = flowEvents.FirstOrDefault()?.InterestRate ?? 0; //当日已实现 newEodPayPosition.TdInterestFee = flowEvents.Sum(x => x.InterestFee); newEodPayPosition.TdCloseInterest = flowEvents.Sum(x => x.InterestAmount); newEodPayPosition.TdCloseInterestFee = newEodPayPosition.TdInterestFee; //持仓内容-利息腿-损益统计(本方视角) newEodPayPosition.TdInterestIncome = newEodPayPosition.TdCloseInterest; newEodPayPosition.InterestIncomeSum = eodPayPosition.InterestIncomeSum + newEodPayPosition.TdInterestIncome - newEodPayPosition.TdCloseInterest; newEodPayPosition.InterestFeeSum = eodPayPosition.InterestFeeSum + newEodPayPosition.TdInterestFee - newEodPayPosition.TdCloseInterestFee; newEodPayPosition.InterestProfitSum = newEodPayPosition.InterestIncomeSum + newEodPayPosition.InterestFeeSum; //持仓价值 newEodPayPosition.SwapPositionValue = newEodPayPosition.InterestProfitSum + newEodPayPosition.PosiProfitSum; //累计已实现 newEodPayPosition.RealizedInterest = eodPayPosition.RealizedInterest + newEodPayPosition.TdCloseInterest * ratio; newEodPayPosition.RealizedInterestFee = eodPayPosition.RealizedInterestFee + newEodPayPosition.TdCloseInterestFee; var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, valueDate , seekPreday: true, currencyRateType: position.InterestDirection == (int)SwapDirectionEnum.收取 ? CurrencyRateType.Buy : CurrencyRateType.Sell); newEodPayPosition.TdCurrency = Convert.ToDecimal(currencyRate); if (newEodPayPosition.id == 0) { DbContext.eod_swap_position.Add(newEodPayPosition); } } /// /// 自动互换用,当日无互换,当日无平仓 /// /// 上一日日终持仓 /// 当前收盘日日终持仓 /// 利息腿信息 /// 框架合约 /// 当前收盘日 /// 当前观察日 /// 上一平仓/互换日期 /// 当日平仓金额 /// 上一日终框架合约估值 private List SaveAutoEodInterestPosition(eod_swap_position eodPayPosition, eod_swap_position newEodPayPosition, swap_position position, trade td, DateTime valueDate, IntervalModel interval, eod_swap lastEodSwap, decimal posiLongNotional, decimal posiShortNational, decimal grossPrice) { var tradeExtend = td.trade_extend.ExtendObj; decimal oriPosiNotionalValue = posiLongNotional + posiShortNational; decimal posiNotionalValue = oriPosiNotionalValue; bool longShort = td.StructureType == ClientMarginTypeEnum.多空组合.ToString(); if (lastEodSwap != null) { posiNotionalValue = lastEodSwap.NotionalValue; } decimal closePercent = 1; decimal ratio = position.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;//收取为正,支付为负 if (marginTypes.Contains(position.InterestMode)) { ratio = -ratio; } if (eodPayPosition == null) { eodPayPosition = new eod_swap_position(); eodPayPosition.ClientId = td.ClientId; eodPayPosition.SwapTradeId = td.id; eodPayPosition.PosiStartDate = td.StartDate.Value; eodPayPosition.PosiMatuirityDate = td.ExerciseDate.Value; } if (newEodPayPosition == null) { newEodPayPosition = eodPayPosition.Clone(); newEodPayPosition.IsSkipEod = false; newEodPayPosition.id = 0; } List positions = new List(); positions.Add(position); List preEodPositions = new List(); preEodPositions.Add(eodPayPosition); var interests = new SwapDealService(this).GetInterests(td, td.trade_extend, valueDate, valueDate, preEodPositions, positions, posiNotionalValue, posiLongNotional, posiShortNational, posiNotionalValue, closePercent, (int)SwapEventTypeEnum.自动互换, false, true, grossPrice, true); decimal InterestAmount = interests.Sum(x => x.InterestAmount); decimal TdInterestAmount = interests.Sum(x => x.TdInterestAmount); if (newEodPayPosition.IsSkipEod) { newEodPayPosition.FloatRate = interests.Count > 0 ? interests.First().FloatRate ?? 0 : 0; newEodPayPosition.FloatRateUnderlyingCode = position.FloatRateUnderlyingCode; return new List(); } newEodPayPosition.ValueDate = valueDate; newEodPayPosition.PositionId = position.id; UpdateDbOption(newEodPayPosition); newEodPayPosition.Invalid = false; //持仓内容-利息腿 newEodPayPosition.InterestDirection = position.InterestDirection; newEodPayPosition.InterestMode = position.InterestMode; newEodPayPosition.InterestPrincipalFix = position.InterestPrincipalFix; newEodPayPosition.InterestRateDefault = position.InterestRateDefault; newEodPayPosition.InterestSwapInterval = position.InterestSwapInterval; newEodPayPosition.IsAnnualized = position.IsAnnualized; newEodPayPosition.HappenDate = position.HappenDate; newEodPayPosition.Currency = position.Currency; newEodPayPosition.InterestType = position.InterestType; newEodPayPosition.FloatRate = interests.Count > 0 ? interests.First().FloatRate ?? 0 : 0; newEodPayPosition.FloatRateUnderlyingCode = position.FloatRateUnderlyingCode; newEodPayPosition.InterestFeePending = 0; //利息端估值用信息 newEodPayPosition.TdInterestPrincipal = interests.Count > 0 ? interests.First().InterestPrincipal : 0; newEodPayPosition.TdInterestRate = interval.Rate; //当日已实现 //newEodPayPosition.TdInterestFee = 0; newEodPayPosition.TdCloseInterest = InterestAmount; // newEodPayPosition.TdCloseInterestFee = newEodPayPosition.TdInterestFee; //持仓内容-利息腿-损益统计(本方视角) newEodPayPosition.TdInterestIncome = TdInterestAmount; newEodPayPosition.InterestIncomeSum = 0; newEodPayPosition.InterestFeeSum = eodPayPosition.InterestFeeSum + newEodPayPosition.TdInterestFee - newEodPayPosition.TdCloseInterestFee; newEodPayPosition.InterestProfitSum = newEodPayPosition.InterestIncomeSum + newEodPayPosition.InterestFeeSum; //持仓价值 newEodPayPosition.SwapPositionValue = newEodPayPosition.SwapPositionValue = newEodPayPosition.InterestProfitSum + newEodPayPosition.PosiProfitSum; //累计已实现 newEodPayPosition.RealizedInterest = eodPayPosition.RealizedInterest + newEodPayPosition.TdCloseInterest * ratio; newEodPayPosition.RealizedInterestFee = eodPayPosition.RealizedInterestFee + newEodPayPosition.TdCloseInterestFee; newEodPayPosition.RealizedPnl = newEodPayPosition.RealizedInterest + newEodPayPosition.RealizedInterestFee; var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, valueDate , seekPreday: true, currencyRateType: position.InterestDirection == (int)SwapDirectionEnum.收取 ? CurrencyRateType.Buy : CurrencyRateType.Sell); newEodPayPosition.TdCurrency = Convert.ToDecimal(currencyRate); if (newEodPayPosition.id == 0) { DbContext.eod_swap_position.Add(newEodPayPosition); } return interests; } /// /// 自动互换用,当日无互换,当日有平仓 /// /// 上一日日终持仓 /// 当前收盘日日终持仓 不可能为空 /// 利息腿信息 /// 框架合约 /// 当前收盘日 /// 当前观察日 /// 上一平仓/互换日期 /// 当日平仓金额 /// 上一日终框架合约估值 /// 平仓主信息 private List SaveAutoEodWithCloseInterestPosition(eod_swap_position eodPayPosition, eod_swap_position newEodPayPosition, swap_position position, trade td, DateTime valueDate, IntervalModel interval, decimal posiLongNotional, decimal posiShortNational, List flowEvents, decimal closeNational, bool autoSwap, decimal grossPrice) { var tradeExtend = td.trade_extend.ExtendObj; decimal oriPosiNotionalValue = posiLongNotional + posiShortNational + closeNational; decimal posiNotionalValue = oriPosiNotionalValue; decimal ratio = position.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;//收取为正,支付为负 if (marginTypes.Contains(position.InterestMode)) { ratio = -ratio; } if (eodPayPosition == null) { eodPayPosition = new eod_swap_position(); eodPayPosition.ClientId = td.ClientId; eodPayPosition.SwapTradeId = td.id; eodPayPosition.PosiStartDate = td.StartDate.Value; eodPayPosition.PosiMatuirityDate = td.ExerciseDate.Value; } if (newEodPayPosition == null) { newEodPayPosition = eodPayPosition.Clone(); newEodPayPosition.id = 0; } decimal closePercent = oriPosiNotionalValue == 0 ? 0 : closeNational / oriPosiNotionalValue; var eventType = autoSwap ? (int)SwapEventTypeEnum.自动互换 : (int)SwapEventTypeEnum.平仓; bool longShort = td.StructureType == ClientMarginTypeEnum.多空组合.ToString(); if (!autoSwap) { closePercent = oriPosiNotionalValue == 0 ? 0 : closeNational / oriPosiNotionalValue; if (longShort) { closePercent = 0; } } List positions = new List(); positions.Add(position); List preEodPositions = new List(); preEodPositions.Add(eodPayPosition); var interests = new SwapDealService(this).GetInterests(td, td.trade_extend, valueDate, valueDate, preEodPositions, positions, posiNotionalValue, posiLongNotional, posiShortNational, closeNational, 1, eventType, false, true, grossPrice, true); decimal TdInterestAmount = interests.Sum(x => x.TdInterestAmount); if (newEodPayPosition.IsSkipEod) { newEodPayPosition.FloatRate = interests.Count > 0 ? interests.First().FloatRate ?? 0 : 0; newEodPayPosition.FloatRateUnderlyingCode = position.FloatRateUnderlyingCode; return new List(); } newEodPayPosition.ValueDate = valueDate; newEodPayPosition.PositionId = position.id; UpdateDbOption(newEodPayPosition); newEodPayPosition.Invalid = false; //持仓内容-利息腿 newEodPayPosition.InterestDirection = position.InterestDirection; newEodPayPosition.InterestMode = position.InterestMode; newEodPayPosition.InterestPrincipalFix = position.InterestPrincipalFix; newEodPayPosition.InterestRateDefault = position.InterestRateDefault; newEodPayPosition.InterestSwapInterval = position.InterestSwapInterval; newEodPayPosition.IsAnnualized = position.IsAnnualized; newEodPayPosition.HappenDate = position.HappenDate; newEodPayPosition.Currency = position.Currency; newEodPayPosition.InterestType = position.InterestType; newEodPayPosition.FloatRate = interests.Count > 0 ? interests.First().FloatRate ?? 0 : 0; newEodPayPosition.FloatRateUnderlyingCode = position.FloatRateUnderlyingCode; newEodPayPosition.InterestFeePending = 0; //利息端估值用信息 newEodPayPosition.TdInterestPrincipal = interests.Count > 0 ? interests.First().InterestPrincipal : 0; newEodPayPosition.TdInterestPrincipal *= (1 - closePercent); if (interval != null) { newEodPayPosition.TdInterestRate = interval.Rate; } else { newEodPayPosition.TdInterestRate = flowEvents.FirstOrDefault()?.InterestRate ?? 0; } //当日已实现,平仓时已处理 newEodPayPosition.TdInterestFee = flowEvents.Sum(s => s.InterestFee); newEodPayPosition.TdCloseInterestFee = newEodPayPosition.TdInterestFee; if (closePercent == 1) { newEodPayPosition.TdCloseInterest = eodPayPosition.InterestIncomeSum; newEodPayPosition.InterestIncomeSum = 0; } else { newEodPayPosition.TdCloseInterest = (eodPayPosition.InterestIncomeSum + TdInterestAmount) * closePercent; newEodPayPosition.InterestIncomeSum = eodPayPosition.InterestIncomeSum + TdInterestAmount - newEodPayPosition.TdCloseInterest; } //持仓内容-利息腿-损益统计(本方视角) newEodPayPosition.TdInterestIncome = TdInterestAmount * (1 - closePercent); newEodPayPosition.InterestFeeSum = eodPayPosition.InterestFeeSum + newEodPayPosition.TdInterestFee - newEodPayPosition.TdCloseInterestFee; newEodPayPosition.InterestProfitSum = newEodPayPosition.InterestIncomeSum + newEodPayPosition.InterestFeeSum; //持仓价值 newEodPayPosition.SwapPositionValue = newEodPayPosition.InterestProfitSum + newEodPayPosition.PosiProfitSum; //累计已实现 newEodPayPosition.RealizedInterest = eodPayPosition.RealizedInterest + newEodPayPosition.TdCloseInterest * ratio; newEodPayPosition.RealizedInterestFee = eodPayPosition.RealizedInterestFee + newEodPayPosition.TdCloseInterestFee; newEodPayPosition.RealizedPnl = newEodPayPosition.RealizedInterest + newEodPayPosition.RealizedInterestFee; var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, valueDate , seekPreday: true, currencyRateType: position.InterestDirection == (int)SwapDirectionEnum.收取 ? CurrencyRateType.Buy : CurrencyRateType.Sell); newEodPayPosition.TdCurrency = Convert.ToDecimal(currencyRate); if (newEodPayPosition.id == 0) { DbContext.eod_swap_position.Add(newEodPayPosition); } return interests; } /// /// 利息腿复制上一日终归档信息 /// /// 上一日终利息腿持仓信息 /// 利息腿信息 /// 上一交易日 /// 当前结算日 /// 互换交易主干 private void SaveEodInterestPositionCopy(eod_swap_position eodPayPosition, eod_swap_position newEodPayPosition, DateTime valueDate, trade td, swap_position position, eod_swap lastEodSwap, bool needPrice, decimal posiLongNational, decimal posiShortNational, decimal grossPrice) { List intervals = position.SwapIntervalList; var tradeExtend = td.trade_extend.ExtendObj; if (eodPayPosition == null) { //if (position.PosiStartDate > valueDate) //{ // return; //} eodPayPosition = new eod_swap_position(); eodPayPosition.InterestDirection = position.InterestDirection; eodPayPosition.ClientId = td.ClientId; eodPayPosition.SwapTradeId = td.id; //eodPayPosition.PositionId = position.id; 为了算利息时找不到给日期重新赋值 eodPayPosition.InterestMode = position.InterestMode; eodPayPosition.InterestPrincipalFix = position.InterestPrincipalFix; eodPayPosition.InterestRateDefault = position.InterestRateDefault; eodPayPosition.InterestSwapInterval = position.InterestSwapInterval; eodPayPosition.TdInterestPrincipal = position.InterestMode == 1 ? eodPayPosition.InterestPrincipalFix : posiLongNational + posiShortNational; eodPayPosition.PosiStartDate = td.StartDate.Value; eodPayPosition.PosiMatuirityDate = td.ExerciseDate.Value; eodPayPosition.IsAnnualized = position.IsAnnualized; eodPayPosition.HappenDate = position.HappenDate; eodPayPosition.Currency = position.Currency; eodPayPosition.InterestType = position.InterestType; eodPayPosition.FloatRate = position.FloatRate; eodPayPosition.FloatRateUnderlyingCode = position.FloatRateUnderlyingCode; } if (newEodPayPosition == null) { newEodPayPosition = eodPayPosition.Clone(); newEodPayPosition.id = 0; newEodPayPosition.PositionId = position.id; newEodPayPosition.IsSkipEod = false; } bool longShort = td.StructureType == ClientMarginTypeEnum.多空组合.ToString(); decimal oriPosiNotionalValue = posiLongNational + posiShortNational; decimal posiNotionalValue = oriPosiNotionalValue; if (lastEodSwap == null) { lastEodSwap = new eod_swap(); } decimal closePercent = oriPosiNotionalValue == 0 ? 0 : posiNotionalValue / oriPosiNotionalValue; if (longShort) { closePercent = 0; } if (td.ExerciseDate == valueDate) { closePercent = 1; } decimal ratio = eodPayPosition.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;//收取为正,支付为负 if (marginTypes.Contains(position.InterestMode)) { ratio = -ratio; } List positions = new List { position }; List preEodPositions = new List(); if (eodPayPosition.id != 0) { preEodPositions.Add(eodPayPosition); } var interests = new SwapDealService(this).GetInterests(td, td.trade_extend, valueDate, valueDate, preEodPositions, positions, posiNotionalValue, posiLongNational, posiShortNational, posiNotionalValue, closePercent, 0, false, needPrice, grossPrice); UpdateDbOption(newEodPayPosition); if (newEodPayPosition.IsSkipEod) { newEodPayPosition.FloatRate = interests.Count > 0 ? interests.First().FloatRate ?? 0 : 0; return; } newEodPayPosition.PosiStatus = 0; newEodPayPosition.Invalid = false; newEodPayPosition.ValueDate = valueDate; decimal InterestAmount = interests.Sum(s => s.InterestAmount); decimal TdInterestAmount = interests.Sum(x => x.TdInterestAmount); //利息端估值用信息 newEodPayPosition.TdInterestPrincipal = interests.Count > 0 ? interests.First().InterestPrincipal : 0; newEodPayPosition.TdInterestRate = interests.Count > 0 ? interests.First().InterestRate : 0; newEodPayPosition.FloatRate = interests.Count > 0 ? interests.First().FloatRate ?? 0 : 0; //当日已实现 newEodPayPosition.TdCloseInterest = 0; newEodPayPosition.TdCloseInterestFee = 0; //持仓内容-利息腿-损益统计(本方视角) newEodPayPosition.TdInterestIncome = TdInterestAmount; newEodPayPosition.TdInterestFee = 0; newEodPayPosition.InterestIncomeSum = eodPayPosition.InterestIncomeSum + newEodPayPosition.TdInterestIncome - newEodPayPosition.TdCloseInterest; newEodPayPosition.InterestFeeSum = eodPayPosition.InterestFeeSum + newEodPayPosition.TdInterestFee - newEodPayPosition.TdCloseInterestFee; newEodPayPosition.InterestProfitSum = newEodPayPosition.InterestIncomeSum + newEodPayPosition.InterestFeeSum; //持仓价值 newEodPayPosition.SwapPositionValue = newEodPayPosition.InterestProfitSum + newEodPayPosition.PosiProfitSum; //累计已实现 newEodPayPosition.RealizedInterest = eodPayPosition.RealizedInterest + newEodPayPosition.TdCloseInterest * ratio; newEodPayPosition.RealizedInterestFee = eodPayPosition.RealizedInterestFee + newEodPayPosition.TdCloseInterestFee; newEodPayPosition.RealizedPnl = newEodPayPosition.RealizedInterest + newEodPayPosition.RealizedInterestFee; var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, valueDate , seekPreday: true, currencyRateType: eodPayPosition.InterestDirection == (int)SwapDirectionEnum.收取 ? CurrencyRateType.Buy : CurrencyRateType.Sell); newEodPayPosition.TdCurrency = Convert.ToDecimal(currencyRate); if (newEodPayPosition.id == 0) { DbContext.eod_swap_position.Add(newEodPayPosition); } } /// /// 持仓腿生成期初持仓及归档信息 /// /// 上一日终持仓信息 /// 合约框架 /// 最后一条事件流水 /// 期初价格 /// 期初价格不含费 /// 剩余持仓数量 /// 开仓交易佣金费用 /// 当前剩余名义本金 /// 当日浮动端分红 /// 当日浮动端平仓盈亏分红 /// 当日平仓数量 /// 当日平仓费用 /// 当日浮动盈亏 private decimal SaveEodPosition(eod_swap_position newEodPayPosition, trade td, swap_flow_event eventFlow, decimal netPrice, decimal grossPrice, decimal netFeePrice, decimal netNoFeePrice, decimal payQty, decimal tradingFee, decimal posiNotionalValue, decimal dividendIn, decimal tdDividendIn, decimal closeQty, decimal closeFee, decimal closeMtmPnl, int posiType, bool open) { payQty = Math.Abs(payQty); decimal ratio = eventFlow.PayDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;//收取为正,支付为负 decimal shortRatio = newEodPayPosition.PositionType == (int)PositionTypeFlag.Long ? 1 : -1;//多空方向 newEodPayPosition.ValueDate = eventFlow.PayDate.Value; newEodPayPosition.PositionId = eventFlow.PositionId; newEodPayPosition.ClientId = td.ClientId; newEodPayPosition.SwapTradeId = td.id; //持仓内容-浮动收益腿 newEodPayPosition.PosiDirection = eventFlow.PayDirection; newEodPayPosition.PositionType = posiType; newEodPayPosition.UnderlyingCode = eventFlow.UnderlyingCode; newEodPayPosition.UnderlyingInstrumentType = eventFlow.UnderlyingInstrumentType; newEodPayPosition.ContractSize = eventFlow.ContractSize; newEodPayPosition.CountRatio = eventFlow.CountRatio; newEodPayPosition.PosiNetPrice = netPrice; newEodPayPosition.PosiGrossPrice = grossPrice; newEodPayPosition.PosiNetFeePrice = netFeePrice; newEodPayPosition.PosiNetNoFeePrice = netNoFeePrice; newEodPayPosition.PosiQuantity = payQty; newEodPayPosition.PosiNotionalValue = posiNotionalValue; newEodPayPosition.PosiMatuirityDate = td.ExerciseDate.Value; if (newEodPayPosition.PosiQuantity == 0) { newEodPayPosition.PosiMatuirityDate = eventFlow.PayDate.Value; } //else //{ // newEodPayPosition.PosiMatuirityDate = td.ExerciseDate.Value; //} newEodPayPosition.PosiFeePending = tradingFee; //浮动端估值用信息 newEodPayPosition.UnderlyingPrice = UnderlyingCodePrice(newEodPayPosition.UnderlyingCode, eventFlow.EventDate, out decimal vobp); newEodPayPosition.UnderlyingMarketValue = newEodPayPosition.UnderlyingPrice * newEodPayPosition.PosiQuantity * newEodPayPosition.ContractSize * shortRatio; //当日已实现 newEodPayPosition.TdCloseQty = closeQty; newEodPayPosition.TdChangedQty = 0; newEodPayPosition.TdCloseMtmPnl = closeMtmPnl * ratio; newEodPayPosition.TdCloseDividend = tdDividendIn * ratio; newEodPayPosition.TdCloseFee = closeFee * ratio; //持仓内容-浮动收益腿-损益统计(本方视角 newEodPayPosition.TdPosiDividend = dividendIn * ratio; newEodPayPosition.PosiMtmPnL = (newEodPayPosition.UnderlyingPrice - newEodPayPosition.PosiGrossPrice) * newEodPayPosition.PosiQuantity * newEodPayPosition.ContractSize * shortRatio * ratio; newEodPayPosition.PosiDividendSum = newEodPayPosition.TdPosiDividend - newEodPayPosition.TdCloseDividend; newEodPayPosition.PosiProfitSum = newEodPayPosition.PosiMtmPnL + newEodPayPosition.PosiDividendSum + newEodPayPosition.VTradingFee; //持仓价值 newEodPayPosition.SwapPositionValue = newEodPayPosition.InterestProfitSum + newEodPayPosition.PosiProfitSum; //累计已实现 newEodPayPosition.RealizedFee = closeFee; newEodPayPosition.RealizedMtmPnL = newEodPayPosition.TdCloseMtmPnl; newEodPayPosition.RealizedDividend = newEodPayPosition.TdCloseDividend; newEodPayPosition.RealizedPnl = newEodPayPosition.TdCloseMtmPnl + newEodPayPosition.TdCloseDividend + newEodPayPosition.TdCloseFee; newEodPayPosition.PosiStatus = payQty == 0 ? 1 : 0; newEodPayPosition.DV01 = newEodPayPosition.PosiQuantity * newEodPayPosition.ContractSize * vobp * shortRatio * ratio * 0.01m; UpdateDbOption(newEodPayPosition); newEodPayPosition.Invalid = false; var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, eventFlow.EventDate , seekPreday: true, currencyRateType: posiNotionalValue < 0 ? CurrencyRateType.Buy : CurrencyRateType.Sell); newEodPayPosition.TdCurrency = Convert.ToDecimal(currencyRate); decimal posiTradingFee = 0; if (open)//更新新开仓持仓腿信息,因为在生成开仓事件时,先生成了空的持仓腿信息 { if (td.trade_extend.ExtendObj.NeedOpenFee && td.TradeDate == eventFlow.EventDate)//开仓 { posiTradingFee = Math.Abs(newEodPayPosition.PosiTradingFee) * Convert.ToDecimal(currencyRate); } UpdateSwapPosition(newEodPayPosition, td.TradeNumber); } UpdateSwapPositionWithRealTime(newEodPayPosition); if (newEodPayPosition.id == 0) { DbContext.eod_swap_position.Add(newEodPayPosition); } return posiTradingFee; } /// ///当日无平仓,无互换,生成持仓腿日终归档,适用于上一日终存在 /// /// 上一日日终归档信息 /// 当日日终归档信息 /// 当日平仓/互换事件信息 /// 交易信息 private eod_swap_position CopyEodPosition(eod_swap_position eod, eod_swap_position curretEod, trade td, DateTime valueDate, DateTime preSettleDate) { if (curretEod == null) { curretEod = eod.Clone(); curretEod.id = 0; curretEod.ValueDate = valueDate; curretEod.IsSkipEod = false; } var um = DataCacheProvider.GetUnderlyingDataSource().GetData(eod.UnderlyingCode); if (um == null) { return curretEod; } var dealDate = um.IsBond() ? preSettleDate : curretEod.ValueDate; int shortRatio = eod.PositionType == (int)PositionTypeFlag.Long ? 1 : -1; int directionRatio = eod.PosiDirection == (int)SwapDirectionEnum.收取 ? 1 : -1; curretEod.PosiStatus = curretEod.PosiQuantity == 0 ? 1 : 0; var price = UnderlyingCodePrice(eod.UnderlyingCode, dealDate, out decimal vobp); if (curretEod.PosiStatus == 1) { curretEod.PosiNotionalValue = 0; } if (curretEod.IsSkipEod) { curretEod.DV01 = curretEod.PosiQuantity * curretEod.ContractSize * vobp * shortRatio * directionRatio * 0.01m; curretEod.PosiNotionalValue = curretEod.PosiQuantity * curretEod.ContractSize; curretEod.PosiNetFeePrice = eod.PosiNetFeePrice; curretEod.PosiNetNoFeePrice = eod.PosiNetNoFeePrice; curretEod.PosiFeePending= eod.PosiFeePending; UpdateHTData(curretEod); UpdateDbOption(curretEod); return curretEod; } curretEod.UnderlyingPrice = price; curretEod.UnderlyingMarketValue = curretEod.UnderlyingPrice * curretEod.PosiQuantity * curretEod.ContractSize * shortRatio; curretEod.PosiMtmPnL = (curretEod.UnderlyingPrice - curretEod.PosiGrossPrice) * curretEod.PosiQuantity * curretEod.ContractSize * shortRatio * directionRatio; curretEod.TdPosiDividend = 0; curretEod.PosiDividendSum = eod.PosiDividendSum + curretEod.TdPosiDividend; curretEod.PosiProfitSum = curretEod.PosiMtmPnL + curretEod.PosiDividendSum+ curretEod.VTradingFee; curretEod.TdCloseFee = 0; curretEod.TdCloseQty = 0; curretEod.TdCloseMtmPnl = 0; curretEod.TdCloseDividend = 0; curretEod.RealizedMtmPnL = eod.RealizedMtmPnL + curretEod.TdCloseMtmPnl; curretEod.RealizedDividend = eod.RealizedDividend + curretEod.TdCloseDividend; curretEod.RealizedFee = eod.RealizedFee + curretEod.TdCloseFee; curretEod.RealizedPnl = eod.RealizedPnl + curretEod.TdCloseMtmPnl + curretEod.TdCloseDividend + curretEod.TdCloseFee; var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, td.StartDate.Value , seekPreday: true, currencyRateType: curretEod.PosiDirection == (int)SwapDirectionEnum.收取 ? CurrencyRateType.Buy : CurrencyRateType.Sell); curretEod.TdCurrency = Convert.ToDecimal(currencyRate); //持仓价值 curretEod.SwapPositionValue = curretEod.InterestProfitSum + curretEod.PosiProfitSum; curretEod.DV01 = curretEod.PosiQuantity * curretEod.ContractSize * vobp * shortRatio * directionRatio * 0.01m; UpdateDbOption(curretEod); curretEod.Invalid = false; if (curretEod.id == 0) { DbContext.eod_swap_position.Add(curretEod); } return curretEod; } /// /// 更新浮动端收益 /// /// private void UpdateHTData(eod_swap_position curretEod) { UpdateVTradingFee(curretEod); curretEod.PosiProfitSum = curretEod.HTPosiProfitSum + curretEod.VTradingFee; curretEod.SwapPositionValue = curretEod.HTSwapPositionValue + curretEod.VTradingFee; } /// /// 更新虚拟交易费用 /// /// private void UpdateVTradingFee(eod_swap_position curretEod) { //int ratio = curretEod.PositionType == (int)PositionTypeFlag.Long ? -1 : 1; //int shortRatio = curretEod.PositionType == (int)PositionTypeFlag.Long ? 1 : -1; //var priceFee = curretEod.PosiQuantity == 0 ? 0 : curretEod.UnderlyingPrice + curretEod.PosiFeePending / curretEod.PosiQuantity * ratio; //curretEod.VTradingFee = -(priceFee - curretEod.PosiNetPrice - curretEod.UnderlyingPrice + curretEod.PosiGrossPrice) * curretEod.PosiNotionalValue * shortRatio; curretEod.VTradingFee = curretEod.PosiFeePending * 2; } /// /// 当日有平仓/互换,生成持仓腿日终归档,适用于上一日终存在 /// /// /// /// /// private eod_swap_position UpdateEodPosition(swap_position swapPosition, eod_swap_position eod, eod_swap_position curretEod, trade td, DateTime valueDate, DateTime preSettleDate, List unwindEvents) { if (curretEod == null) { curretEod = eod.Clone(); curretEod.id = 0; curretEod.ValueDate = valueDate; curretEod.IsSkipEod = false; } var um = DataCacheProvider.GetUnderlyingDataSource().GetData(eod.UnderlyingCode); if (um == null) { return curretEod; } var dealDate = um.IsBond() ? preSettleDate : curretEod.ValueDate; int shortRatio = eod.PositionType == (int)PositionTypeFlag.Long ? 1 : -1; int directionRatio = eod.PosiDirection == (int)SwapDirectionEnum.收取 ? 1 : -1; var price = UnderlyingCodePrice(eod.UnderlyingCode, dealDate, out decimal vobp); SetPriceInfoByFlowEvent(eod, curretEod, unwindEvents, swapPosition, !curretEod.IsSkipEod); if (curretEod.IsSkipEod) { curretEod.DV01 = curretEod.PosiQuantity * curretEod.ContractSize * vobp * shortRatio * directionRatio * 0.01m; UpdateHTData(curretEod); UpdateDbOption(curretEod); return curretEod; } curretEod.UnderlyingPrice = price; curretEod.UnderlyingMarketValue = curretEod.UnderlyingPrice * curretEod.PosiQuantity * curretEod.ContractSize * shortRatio; curretEod.PosiMtmPnL = (curretEod.UnderlyingPrice - curretEod.PosiGrossPrice) * curretEod.PosiQuantity * curretEod.ContractSize * shortRatio * directionRatio; curretEod.PosiDividendSum = eod.PosiDividendSum + curretEod.TdPosiDividend; curretEod.PosiProfitSum = curretEod.PosiMtmPnL + curretEod.PosiDividendSum + curretEod.VTradingFee; curretEod.RealizedMtmPnL = eod.RealizedMtmPnL + curretEod.TdCloseMtmPnl; curretEod.RealizedDividend = eod.RealizedDividend + curretEod.TdCloseDividend; curretEod.RealizedFee = eod.RealizedFee + curretEod.TdCloseFee; curretEod.RealizedPnl = eod.RealizedPnl + curretEod.TdCloseMtmPnl + curretEod.TdCloseDividend + curretEod.TdCloseFee; curretEod.PosiStatus = curretEod.PosiQuantity == 0 ? 1 : 0; if (curretEod.PosiStatus == 1) { curretEod.PosiNotionalValue = 0; } var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, td.StartDate.Value , seekPreday: true, currencyRateType: curretEod.PosiDirection == (int)SwapDirectionEnum.收取 ? CurrencyRateType.Buy : CurrencyRateType.Sell); curretEod.TdCurrency = Convert.ToDecimal(currencyRate); //持仓价值 curretEod.SwapPositionValue = curretEod.InterestProfitSum + curretEod.PosiProfitSum; curretEod.DV01 = curretEod.PosiQuantity * curretEod.ContractSize * vobp * shortRatio * directionRatio * 0.01m; UpdateDbOption(curretEod); curretEod.Invalid = false; if (curretEod.id == 0) { DbContext.eod_swap_position.Add(curretEod); } return curretEod; } /// /// 根据开平仓事件算价格及后付费用 /// /// /// /// public void SetPriceInfoByFlowEvent(eod_swap_position eod, eod_swap_position curretEod, List unwindEvents, swap_position position, bool coverageQty=true) { if (eod.PosiDirection == 0) { return; } int shortRatio = eod.PositionType == (int)PositionTypeFlag.Long ? 1 : -1; int directionRatio = eod.PosiDirection == (int)SwapDirectionEnum.收取 ? 1 : -1; var unwindFlowEvents = unwindEvents.Where(x => x.EventType == (int)SwapFlowEventTypeEnum.平仓).ToList(); var openFlowEvents = unwindEvents.Where(x => x.EventType == (int)SwapFlowEventTypeEnum.开仓).ToList(); decimal unwindQty = unwindFlowEvents.Sum(s => s.Quantity); decimal openQty = openFlowEvents.Sum(s => s.Quantity); var qty = eod.PosiQuantity + openQty - unwindQty; if (coverageQty) { curretEod.PosiQuantity = qty < 0 ? 0 : Math.Abs(qty); } if (unwindEvents.Count == 0) { curretEod.PosiNetPrice = position.PosiNetPrice; curretEod.PosiGrossPrice = position.PosiGrossPrice; curretEod.PosiNetFeePrice = position.PosiNetFeePrice; curretEod.PosiNetNoFeePrice = position.PosiNetNoFeePrice; if (coverageQty) { curretEod.PosiQuantity = position.PosiQuantity; } curretEod.PosiTradingFee = position.PosiTradingFee; } else { var eventTradingFee = unwindEvents.Where(x => x.EventType == (int)SwapFlowEventTypeEnum.开仓 || x.EventType == (int)SwapFlowEventTypeEnum.平仓).Sum(s => s.TradingFeePending * (s.EventType == (int)SwapFlowEventTypeEnum.开仓 ? 1 : -1)); curretEod.PosiFeePending = eod.PosiFeePending + eventTradingFee; if (openFlowEvents.Count() == 0) { curretEod.PosiNetPrice = eod.PosiNetPrice; curretEod.PosiGrossPrice = eod.PosiGrossPrice; curretEod.PosiNetFeePrice = eod.PosiNetFeePrice; curretEod.PosiNetNoFeePrice = eod.PosiNetNoFeePrice; curretEod.PosiFeePending = curretEod.PosiFeePending<0 ? 0: curretEod.PosiFeePending; } else //平仓数量一定<持仓数量 { var posiQty = eod.PosiQuantity - unwindQty; if (posiQty < 0) { posiQty = 0; } curretEod.PosiGrossPrice = (eod.PosiGrossPrice * posiQty + openFlowEvents.Sum(a => a.Quantity * a.TradingAmountAvg)) / (eod.PosiQuantity + openQty); curretEod.PosiNetNoFeePrice = (eod.PosiNetNoFeePrice * posiQty + openFlowEvents.Sum(a => a.Quantity * a.TradingAmountNetAvg)) / (eod.PosiQuantity + openQty); curretEod.PosiNetFeePrice = curretEod.PosiQuantity == 0 ? eod.PosiNetFeePrice : curretEod.PosiNetNoFeePrice + curretEod.PosiFeePending / curretEod.PosiQuantity * shortRatio; curretEod.PosiNetPrice = curretEod.PosiQuantity == 0 ? eod.PosiNetPrice : curretEod.PosiGrossPrice + curretEod.PosiFeePending / curretEod.PosiQuantity * shortRatio; } curretEod.PosiNotionalValue = curretEod.PosiQuantity * curretEod.ContractSize; curretEod.TdPosiDividend = unwindEvents.Sum(x => x.DividendIn) * directionRatio; //curretEod.TdCloseFee = unwindEvents.Sum(x => x.TradingFee); curretEod.TdCloseQty = unwindQty; curretEod.TdCloseMtmPnl = unwindEvents.Sum(x => x.MarkClosePnl); } UpdateVTradingFee(curretEod); } /// /// 单标的浮动腿 首次归档 /// /// /// /// private eod_swap_position SaveCurrentEodInitalPosi(swap_position position, trade td, DateTime settleDate, DateTime preSettleDate, List unwindEvents) { eod_swap_position curretEod = new eod_swap_position(); var um = DataCacheProvider.GetUnderlyingDataSource().GetData(position.UnderlyingCode); if (um == null) { return curretEod; } var dealDate = um.IsBond() ? preSettleDate : settleDate; curretEod.ValueDate = settleDate; curretEod.PosiStartDate = position.PosiStartDate; curretEod.PosiMatuirityDate = td.ExerciseDate.Value; curretEod.SwapTradeId = td.id; curretEod.PositionId = position.id; curretEod.ClientId = td.ClientId; int shortRatio = position.PositionType == (int)PositionTypeFlag.Long ? 1 : -1; int directionRatio = position.PosiDirection == (int)SwapDirectionEnum.收取 ? 1 : -1; curretEod.PositionType = position.PositionType; var 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, }; curretEod.PosiDirection = position.PosiDirection; curretEod.UnderlyingCode = position.UnderlyingCode; curretEod.UnderlyingInstrumentType = position.UnderlyingInstrumentType; curretEod.SwapTradeId = position.SwapTradeId; curretEod.ContractSize = position.ContractSize; curretEod.CountRatio = position.CountRatio; curretEod.PosiTradingFee = position.PosiTradingFee; curretEod.UnderlyingPrice = UnderlyingCodePrice(position.UnderlyingCode, dealDate, out decimal vobp); SetPriceInfoByFlowEvent(eod, curretEod, unwindEvents, position); if (settleDate == td.TradeDate) { curretEod.UnderlyingPrice = curretEod.PosiGrossPrice; curretEod.TdCloseMtmPnl = 0; curretEod.TdCloseFee = 0; } curretEod.TdCloseDividend = curretEod.TdPosiDividend; curretEod.UnderlyingMarketValue = curretEod.UnderlyingPrice * curretEod.PosiQuantity * curretEod.ContractSize * shortRatio; curretEod.PosiNotionalValue = curretEod.PosiQuantity * curretEod.ContractSize; curretEod.PosiMtmPnL = (curretEod.UnderlyingPrice - curretEod.PosiGrossPrice) * curretEod.PosiQuantity * curretEod.ContractSize * shortRatio * directionRatio; curretEod.PosiDividendSum = curretEod.TdPosiDividend; curretEod.PosiProfitSum = curretEod.PosiMtmPnL + curretEod.PosiDividendSum + curretEod.VTradingFee; curretEod.RealizedMtmPnL = curretEod.TdCloseMtmPnl; curretEod.RealizedDividend = curretEod.TdCloseDividend; curretEod.RealizedFee = curretEod.TdCloseFee; curretEod.RealizedPnl = curretEod.TdCloseMtmPnl + curretEod.TdCloseDividend + curretEod.TdCloseFee; curretEod.PosiStatus = curretEod.PosiQuantity == 0 ? 1 : 0; if (curretEod.PosiStatus == 1) { curretEod.PosiNotionalValue = 0; } //持仓价值 curretEod.SwapPositionValue = curretEod.InterestProfitSum + curretEod.PosiProfitSum; curretEod.DV01 = curretEod.PosiQuantity * curretEod.ContractSize * vobp * shortRatio * directionRatio * 0.01m; var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, td.StartDate.Value , seekPreday: true, currencyRateType: curretEod.PosiDirection == (int)SwapDirectionEnum.收取 ? CurrencyRateType.Buy : CurrencyRateType.Sell); curretEod.TdCurrency = Convert.ToDecimal(currencyRate); UpdateDbOption(curretEod); curretEod.Invalid = false; DbContext.eod_swap_position.Add(curretEod); return curretEod; } /// /// 获取标的收盘价格 /// /// 标的代码 /// 收盘日 /// public decimal UnderlyingCodePrice(string code, DateTime settleDate, out decimal vobp) { vobp = 0; var data = DataCacheProvider.GetUnderlyingDataSource().GetData(code); if (data == null) { return 0; } if (data.IsBond()) { return BondPrice(data, settleDate, out vobp); } var price = data.Price ?? 0; if (EodPriceQueryService.TryGetEodPrice(settleDate, code, out var eodPrice)) { price = eodPrice.GetPrice(SettlementTypeEnum.ClosePrice); } return Convert.ToDecimal(price); } /// /// 获取债券收盘价格 /// /// /// /// public decimal BondPrice(underlying_manager data, DateTime settleDate, out decimal vobp) { vobp = 0; var price = data.Price ?? 0; if (EodPriceQueryService.TryGetBondEodPrice(settleDate, data.UnderlyingCode, out var eodPrice)) { price = eodPrice.GetPrice(SettlementTypeEnum.ClosePrice); vobp = eodPrice.Vobp ?? 0; } else { price = price * Convert.ToDouble(ConsGlobal.bondPriceMultiple); } return Convert.ToDecimal(price); } /// /// 框架合约估值 /// /// 互换交易 /// 收盘日 private void SaveEodSwap(trade td, DateTime settleDate,DateTime preSettleDate) { var eod_Swaps = DbContext.eod_swap.Where(x => x.SwapTradeId == td.id && x.ValueDate >= preSettleDate && x.ValueDate<= settleDate).ToList(); var eod_Swap = eod_Swaps.FirstOrDefault(x => x.ValueDate == settleDate); var preEodSwap= eod_Swaps.FirstOrDefault(x => x.ValueDate == preSettleDate); if (eod_Swap == null) { eod_Swap = new eod_swap(); } var tradeSpan = DbContext.trade_span.FirstOrDefault(x => x.TradeId == td.id && x.ValueDate == settleDate); var eodSwapPositions = DbContext.eod_swap_position.Where(x => x.SwapTradeId == td.id && x.ValueDate == settleDate && !x.Invalid).ToList(); var interestPositions = eodSwapPositions.Where(x => string.IsNullOrEmpty(x.UnderlyingCode)).ToList();//利息腿 var positions = eodSwapPositions.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode)).ToList();//持仓腿 eod_Swap.NotionalValueLong = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Long).Sum(s => s.PosiNotionalValue); eod_Swap.NotionalValueShort = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Short).Sum(s => s.PosiNotionalValue); eod_Swap.NotionalValue = eod_Swap.NotionalValueLong + eod_Swap.NotionalValueShort; if (eod_Swap.IsSkipEod == true) { var vtradingFee = eodSwapPositions.Sum(x => x.VTradingFee); eod_Swap.VTradingFee = vtradingFee; eod_Swap.DV01 = positions.Sum(s => s.DV01 ?? 0); eod_Swap.PostionValue = eod_Swap.HTPostionValue + vtradingFee; eod_Swap.FloatingPnL = eod_Swap.HTFloatingPnL + vtradingFee; eod_Swap.TdRealizedPnL = eod_Swap.RealizedPnL - (preEodSwap?.RealizedPnL??0);// 当日已实现-上日已实现 UpdateDbOption(eod_Swap); return; } eod_Swap.SwapTradeId = td.id; eod_Swap.SwapTradeNo = td.TradeNumber; eod_Swap.ClientId = td.ClientId; eod_Swap.BookId = td.AssetId; eod_Swap.ValueDate = settleDate; eod_Swap.StructureType = td.StructureType; eod_Swap.MarketValueLong = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Long).Sum(s => s.UnderlyingMarketValue); eod_Swap.MarketValueShort = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Short).Sum(s => s.UnderlyingMarketValue); eod_Swap.FloatingPnL = positions.Sum(s => s.PosiProfitSum); decimal interestPnL = 0; interestPositions.ForEach(x => { decimal ratio = x.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;//收取为正,支付为负 if (marginTypes.Contains(x.InterestMode)) { ratio = -ratio; } interestPnL += x.InterestProfitSum * ratio; }); eod_Swap.InterestPnL = interestPnL; decimal tdRealizedPnL = 0; eodSwapPositions.ForEach(x => { decimal ratio = x.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;//收取为正,支付为负 if (marginTypes.Contains(x.InterestMode)) { ratio = -ratio; } tdRealizedPnL += x.TdCloseMtmPnl + x.TdCloseDividend + x.TdCloseFee + x.TdCloseInterest * ratio + x.TdCloseInterestFee; }); eod_Swap.TdRealizedPnL = tdRealizedPnL; eod_Swap.PostionValue = eodSwapPositions.Sum(s => s.SwapPositionValue); eod_Swap.RealizedPnL = eodSwapPositions.Sum(s => s.RealizedMtmPnL + s.RealizedDividend + s.RealizedFee + s.RealizedInterest + s.RealizedInterestFee); eod_Swap.TdCloseQty = positions.Sum(s => s.TdCloseQty); eod_Swap.DV01 = positions.Sum(s => s.DV01 ?? 0); var initMargin = Convert.ToDecimal(tradeSpan?.InitialMargin ?? 0); var maintainMargin = Convert.ToDecimal(tradeSpan?.WorstCastClientPayable ?? 0); if (initMargin < 0) { eod_Swap.InitMarginLoss = Math.Abs(initMargin); } else { eod_Swap.InitMarginGain = Math.Abs(initMargin); } if (maintainMargin < 0) { eod_Swap.PostionMarginLoss = Math.Abs(maintainMargin); } else { eod_Swap.PostionMarginGain = Math.Abs(maintainMargin); } UpdateDbOption(eod_Swap); if (eod_Swap.id == 0) { DbContext.eod_swap.Add(eod_Swap); } } /// /// 单标的修改当天 框架合约信息 /// /// /// private void UpdateEodSwap(trade td, DateTime settleDate) { eod_swap eod_Swap = DbContext.eod_swap.FirstOrDefault(x => x.SwapTradeId == td.id && x.ValueDate == settleDate); if (eod_Swap == null) { eod_Swap = new eod_swap(); eod_Swap.SwapTradeId = td.id; eod_Swap.ValueDate = settleDate; DbContext.eod_swap.Add(eod_Swap); } var eodSwapPositions = DbContext.eod_swap_position.Where(x => x.SwapTradeId == td.id && x.ValueDate == settleDate && !x.Invalid).ToList(); var interestPositions = eodSwapPositions.Where(x => string.IsNullOrEmpty(x.UnderlyingCode)).ToList();//利息腿 var positions = eodSwapPositions.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode)).ToList();//持仓腿 eod_Swap.NotionalValue = Convert.ToDecimal(td.StockEqvNotional); eod_Swap.NotionalValueLong = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Long).Sum(s => s.PosiNotionalValue); eod_Swap.NotionalValueShort = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Short).Sum(s => s.PosiNotionalValue); eod_Swap.MarketValueLong = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Long).Sum(s => s.UnderlyingMarketValue); eod_Swap.MarketValueShort = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Short).Sum(s => s.UnderlyingMarketValue); eod_Swap.FloatingPnL = positions.Sum(s => s.PosiProfitSum); interestPositions.ForEach(x => { decimal ratio = x.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;//收取为正,支付为负 if (marginTypes.Contains(x.InterestMode)) { ratio = -ratio; } eod_Swap.InterestPnL += x.InterestProfitSum * ratio; }); eodSwapPositions.ForEach(x => { decimal ratio = x.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;//收取为正,支付为负 if (marginTypes.Contains(x.InterestMode)) { ratio = -ratio; } eod_Swap.TdRealizedPnL += x.TdCloseMtmPnl + x.TdCloseDividend + x.TdCloseFee + x.TdCloseInterest * ratio + x.TdCloseInterestFee; }); eod_Swap.PostionValue = eodSwapPositions.Sum(s => s.SwapPositionValue); eod_Swap.RealizedPnL = eodSwapPositions.Sum(s => s.RealizedMtmPnL + s.RealizedDividend + s.RealizedFee + s.RealizedInterest + s.RealizedInterestFee); eod_Swap.TdCloseQty = positions.Sum(s => s.TdCloseQty); eod_Swap.DV01 = positions.Sum(s => s.DV01 ?? 0); var tradeInitMarginObj = DbContext.trade_initial_margin.FirstOrDefault(x => x.TradeId == td.id); var initMarginList = interestPositions.Where(x => x.InterestMode == (int)InterestModeEnum.初始预付金 && x.HappenDate == settleDate).ToList(); var addMarginList = interestPositions.Where(x => x.InterestMode == (int)InterestModeEnum.追加预付金 && x.HappenDate == settleDate).ToList(); eod_Swap.InitMarginGain += initMarginList.Where(s => s.InterestDirection == (int)SwapDirectionEnum.收取).ToList().Sum(s => s.InterestPrincipalFix); eod_Swap.InitMarginLoss += initMarginList.Where(s => s.InterestDirection == (int)SwapDirectionEnum.支付).ToList().Sum(s => s.InterestPrincipalFix); eod_Swap.PostionMarginGain += addMarginList.Where(s => s.InterestDirection == (int)SwapDirectionEnum.收取).ToList().Sum(s => s.InterestPrincipalFix); eod_Swap.PostionMarginLoss += addMarginList.Where(s => s.InterestDirection == (int)SwapDirectionEnum.支付).ToList().Sum(s => s.InterestPrincipalFix); UpdateDbOption(eod_Swap); DbContext.SaveChanges(); } /// /// 获取多空组合 平仓详细 /// /// /// /// public SwapLongShortCloseModel GetCloseDetails(int tradeId, DateTime valueDate) { SwapLongShortCloseModel closeModel = new SwapLongShortCloseModel(); var eodPositions = DbContext.eod_swap_position.Where(x => x.SwapTradeId == tradeId && x.ValueDate == valueDate && !x.Invalid).ToList(); var flowEvents = DbContext.swap_flow_event.Where(x => x.SwapTradeId == tradeId && x.EventDate == valueDate && x.DataState == (int)SwapFlowDateStateEnum.完成 && x.EventType == (int)SwapEventTypeEnum.平仓 && string.IsNullOrEmpty(x.UnderlyingCode)).ToList(); closeModel.DealPositions = eodPositions.Where(x => x.TdCloseQty != 0).ToList(); closeModel.DealInterests = flowEvents; return closeModel; } /// /// 查询互换日终持仓风险-明细 /// /// /// public SearchListResult SearchEodPositionList(EodSwapPositionQueryRequest req) { var predicate = PredicateBuilder.Create(n => !n.Invalid && n.PosiDirection > 0); var tradePredicate = PredicateBuilder.Create(n => n.TradeType == "收益互换" && n.ValidState != "InValid"); if (req.ValueDate.HasValue) { predicate = predicate.And(n => n.ValueDate == req.ValueDate); //tradePredicate = tradePredicate.And(n => n.StartDate <= req.ValueDate); } if (!string.IsNullOrEmpty(req.TradeNumber)) { tradePredicate = tradePredicate.And(n => n.TradeNumber.Contains(req.TradeNumber.Trim())); } if (req.UserAssets != null || req.UserClients != null) { tradePredicate = tradePredicate.And(q => req.UserAssets.Contains(q.AssetId) || req.UserClients.Contains(q.ClientId)); } if (req.ClientIds != null && req.ClientIds.Any(x => x > 0)) { tradePredicate = tradePredicate.And(q => req.ClientIds.Contains(q.ClientId)); } if (req.BookIds != null && req.BookIds.Any(x => x > 0)) { tradePredicate = tradePredicate.And(q => req.BookIds.Contains(q.AssetId)); } if (req.UnderlyingIds != null) { var underlyingCodes = new List(); foreach (var id in req.UnderlyingIds) { var un = UnderlyingDataProvider.GetUnderlying(id); if (un != null) { underlyingCodes.Add(un.UnderlyingCode); } } predicate = predicate.And(n => underlyingCodes.Contains(n.UnderlyingCode)); } var positionQuery = DbContext.eod_swap_position.Where(predicate); var tradeQuery = DbContext.trade.Where(tradePredicate); var query = from position in positionQuery join td in tradeQuery on position.SwapTradeId equals td.id select new SwapPositionResponse { eodPosition = position, TradeDate = td.StartDate.Value, SwapTradeNo = td.TradeNumber, StructureType = td.StructureType, ClientName = td.ClientName, ClientId = td.ClientId, }; if (string.IsNullOrEmpty(req.sidx)) { req.sidx = "SwapTradeNo,eodPosition.id"; req.sord = "asc"; } var retListResult = query.ToSearchList(req); foreach (var item in retListResult.rows) { var client = DataCacheProvider.GetClientDataSource().GetData(item.ClientId); item.SwapTradeTypeStr = client?.SwapTradeTypeStr; var multiplier = ConsGlobal.InstrumentType.IsBond(item.eodPosition.UnderlyingInstrumentType) ? 100 : 1; item.eodPosition.PosiNetPrice *= multiplier; item.eodPosition.PosiGrossPrice *= multiplier; item.eodPosition.PosiNetFeePrice *= multiplier; item.eodPosition.PosiNetNoFeePrice *= multiplier; item.eodPosition.UnderlyingPrice *= multiplier; } return retListResult; } /// /// 查询互换日终持仓风险-框架合约 /// /// /// public SearchListResult SearchEodSwapList(EodSwapQueryRequest req) { var predicate = PredicateBuilder.Create(n => 1 == 1); var tradePredicate = PredicateBuilder.Create(n => n.TradeType == "收益互换" && n.ValidState != "InValid"); if (req.ValueDate.HasValue) { predicate = predicate.And(n => n.ValueDate == req.ValueDate); // tradePredicate = tradePredicate.And(n=>n.StartDate<=req.ValueDate); } if (!string.IsNullOrEmpty(req.TradeNumber)) { tradePredicate = tradePredicate.And(n => n.TradeNumber.Contains(req.TradeNumber.Trim())); } if (req.UserAssets != null || req.UserClients != null) { tradePredicate = tradePredicate.And(q => req.UserAssets.Contains(q.AssetId) || req.UserClients.Contains(q.ClientId)); } if (req.ClientIds != null && req.ClientIds.Any(x => x > 0)) { tradePredicate = tradePredicate.And(q => req.ClientIds.Contains(q.ClientId)); } if (req.BookIds != null && req.BookIds.Any(x => x > 0)) { tradePredicate = tradePredicate.And(q => req.BookIds.Contains(q.AssetId)); } var positionQuery = DbContext.eod_swap.Where(predicate); var tradeQuery = DbContext.trade.Where(tradePredicate); var query = from position in positionQuery join td in tradeQuery on position.SwapTradeId equals td.id select new EodSwapResponse { position = position, TradeDate = td.StartDate.Value, SwapTradeNo = td.TradeNumber, StructureType = td.StructureType, ClientName = td.ClientName, AssetBookName = td.AssetBookName, ClientId = td.ClientId }; if (string.IsNullOrEmpty(req.sidx)) { req.sidx = "SwapTradeNo,position.id"; req.sord = "asc"; } DbContext.SetDebugLog(); var retListResult = query.ToSearchList(req); foreach (var item in retListResult.rows) { var client = DataCacheProvider.GetClientDataSource().GetData(item.ClientId); item.SwapTradeTypeStr = client?.SwapTradeTypeStr; } return retListResult; } /// /// 获取互换交易日终持仓数据 /// /// /// /// public List GetPreEodPositions(int tradeId, DateTime valueDate) { return DbContext.eod_swap_position.Where(x => x.SwapTradeId == tradeId && x.ValueDate == valueDate && !x.Invalid).ToList(); } /// /// 获取互换交易日终持仓数据集合 /// /// /// /// public List GetEodSwaps(List tradeIds, DateTime valueDate) { return DbContext.eod_swap.Where(x => tradeIds.Contains(x.SwapTradeId) && x.ValueDate == valueDate).ToList(); } /// /// 获取互换交易某交易日区间框架合约数据 /// /// /// /// /// public List GetEodSwaps(List tradeIds, DateTime valueDate, DateTime preValueDate) { return DbContext.eod_swap.Where(x => tradeIds.Contains(x.SwapTradeId) && x.ValueDate <= valueDate && x.ValueDate >= preValueDate).ToList(); } /// /// 获取互换交易某日终持仓数据 /// /// /// /// /// public List GetEodPositions(List tradeIds, DateTime valueDate, DateTime preValueDate) { return DbContext.eod_swap_position.Where(x => tradeIds.Contains(x.SwapTradeId) && !string.IsNullOrEmpty(x.UnderlyingCode) && x.ValueDate <= valueDate && x.ValueDate >= preValueDate && !x.Invalid).ToList(); } /// /// 获取互换交易某区间日终持仓估值-按产品要求 /// /// /// public SearchListResult SearchPositionList(ClientSwapPositionRequest req) { var retListResult = GetSearchPositionList(req); var clientDataSource = DataCacheProvider.GetClientDataSource(); var underlyDataSource = DataCacheProvider.GetUnderlyingDataSource(); foreach (var item in retListResult.rows) { var client = clientDataSource.GetData(item.ClientId); item.ClientNumber = client.Number; if (!string.IsNullOrEmpty(item.eodPosition.UnderlyingCode)) { var underly = underlyDataSource.GetData(item.eodPosition.UnderlyingCode); if (underly != null) { item.eodPosition.UnderlyingName = underly.UnderlyingName; } } } return retListResult; } /// /// 获取互换交易某区间日终持仓估值-按山证要求 /// /// /// public SearchListResult SearchEodPositionList(ClientSwapPositionRequest req) { var retListResult = GetSearchEodPositionList(req); return retListResult; } /// /// 互换持仓明细查询 /// /// /// private SearchListResult GetSearchPositionList(ClientSwapPositionRequest req) { var predicate = PredicateBuilder.Create(n => !n.Invalid && n.PositionType > 0 && n.PosiStatus == 0); var tradePredicate = PredicateBuilder.Create(n => n.TradeType == "收益互换" && n.ValidState != "InValid"); if (req.ClientId > 0) { predicate = predicate.And(x => x.ClientId == req.ClientId); } if (req.ValueDate != null) { predicate = predicate.And(x => x.ValueDate == req.ValueDate); tradePredicate = tradePredicate.And(n => n.StartDate <= req.ValueDate); } var positionQuery = DbContext.eod_swap_position.Where(predicate); var tradeQuery = DbContext.trade.Where(tradePredicate); var query = from position in positionQuery join td in tradeQuery on position.SwapTradeId equals td.id select new SwapPositionResponse { eodPosition = position, TradeDate = td.StartDate.Value, SwapTradeNo = td.TradeNumber, StructureType = td.StructureType, ClientName = td.ClientName, ClientId = td.ClientId, InitialMarginDirection = position.PosiDirection }; if (string.IsNullOrEmpty(req.sidx)) { req.sidx = "SwapTradeNo,eodPosition.id"; req.sord = "asc"; } var retListResult = query.ToSearchList(req); foreach (var item in retListResult.rows) { SetClientEodPosition(item.eodPosition); SetPosiPrice(item.eodPosition); } return retListResult; } private SearchListResult GetSearchEodPositionList(ClientSwapPositionRequest req) { var predicate = PredicateBuilder.Create(n => !n.Invalid && n.PosiQuantity > 0); var interestPredicate = PredicateBuilder.Create(n => !n.Invalid && n.InterestDirection > 0); var tradePredicate = PredicateBuilder.Create(n => n.StructureType == req.StructureType && n.ValidState != "InValid"); if (req.ClientId > 0) { predicate = predicate.And(x => x.ClientId == req.ClientId); } if (req.ValueDateFrom != null) { predicate = predicate.And(x => x.ValueDate >= req.ValueDateFrom); } if (req.ValueDate != null) { predicate = predicate.And(x => x.ValueDate == req.ValueDate); tradePredicate = tradePredicate.And(x => req.ValueDate >= x.StartDate); } var positionQuery = DbContext.eod_swap_position.Where(predicate); var tradeQuery = DbContext.trade.Where(tradePredicate); var query = from position in positionQuery join td in tradeQuery on position.SwapTradeId equals td.id join tcrConfirm in DbContext.trade_contract_r.Where(O => O.Type == ContractTypeEnum.Trade && O.IsValid) on td.id equals tcrConfirm.TradeId into tcrConfirms from tcrConfirm in tcrConfirms.DefaultIfEmpty() select new EodSwapPositionResponse { position = position, ClientName = td.ClientName, ConfrimNo = tcrConfirm.ContractCode, TradeNumber = td.TradeNumber, StructureType = td.StructureType, UnwindDate = td.UnWindDate, TradeStatus = td.TradeStatus }; if (string.IsNullOrEmpty(req.sidx)) { req.sidx = "position.id"; req.sord = "asc"; } var retListResult = query.ToSearchList(req); var tradeIds = retListResult.rows.Select(s => s.position.SwapTradeId).ToList(); interestPredicate = interestPredicate.And(x => tradeIds.Contains(x.SwapTradeId)); var valueDates = retListResult.rows.Select(s => s.position.ValueDate).Distinct().ToList(); var eodPositions = DbContext.eod_swap_position.Where(interestPredicate).ToList(); var positions = DbContext.swap_position.Where(x => tradeIds.Contains(x.SwapTradeId) && x.InterestMode == (int)InterestModeEnum.初始预付金 && x.IsInitial && !x.Invalid).ToList(); var tradeExtends = DbContext.trade_extend.Where(x => tradeIds.Contains(x.TradeId)).ToList(); Dictionary tradeDic = new Dictionary(); foreach (var item in retListResult.rows) { var tradeExtend = tradeExtends.FirstOrDefault(x => x.TradeId == item.position.SwapTradeId); var eventDate = item.position.ValueDate; if (tradeExtend != null) { eventDate = QdpCalendarHelper.GetNonHoliday(eventDate.AddDays(tradeExtend.ExtendObj.SettlementRules)); } item.DayCount = (eventDate - item.position.PosiStartDate).Days+1; //item.position.PosiProfitSum += item.position.VTradingFee-item.position.PosiFeePending; SetClientEodPosition(item.position); //item.position.PosiProfitSum += item.TradingFee; item.NetSettmentAmount = item.position.PosiProfitSum; var margins = positions.Where(x => x.SwapTradeId == item.position.SwapTradeId); var interests = eodPositions.Where(x => x.SwapTradeId == item.position.SwapTradeId && x.ValueDate == item.position.ValueDate); var eodMargins = interests.Where(x => marginTypes.Contains(x.InterestMode)); var eodInterests = interests.Where(x => !marginTypes.Contains(x.InterestMode)); item.OpenMarginAmount = margins.Sum(s => s.InterestPrincipalFix * (s.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1)); item.OpenMarginRate = margins.Sum(s => s.InterestRateDefault * (s.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1)); item.MarginInterestAmount = eodMargins.Sum(s => s.InterestIncomeSum * (s.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1)); item.InterestAmount = eodInterests.Sum(s => s.InterestIncomeSum * (s.InterestDirection == (int)SwapDirectionEnum.收取 ? -1 : 1)); if (item.StructureType != "普通债券类收益互换") { var floatRateInterest = eodInterests.FirstOrDefault(x => !string.IsNullOrEmpty(x.FloatRateUnderlyingCode)); item.NetSettmentAmount += item.InterestAmount; if (floatRateInterest != null) { item.InterestRate = floatRateInterest.InterestRateDefault; item.FloatRateUnderlyingCode = floatRateInterest.FloatRateUnderlyingCode; } else { item.InterestRate = eodInterests.FirstOrDefault()?.InterestRateDefault ?? 0; } } else { item.InterestRate = eodInterests.Sum(s => s.InterestRateDefault); item.NetSettmentAmount += item.InterestAmount + item.MarginInterestAmount + eodMargins.Sum(s => s.InterestPrincipalFix * (s.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1)); } if (item.position.PosiNotionalValue != 0 && item.position.PosiNetPrice != 0) { item.FloatRateAbs = item.position.PosiNotionalValue == 0 ? 0 : item.InterestAmount / (item.position.PosiNotionalValue * item.position.PosiNetPrice); } SetPosiPrice(item.position); } return retListResult; } /// /// 设置客户视角 /// /// private void SetClientEodPosition(eod_swap_position position) { position.TdCloseDividend = -position.TdCloseDividend; position.TdCloseMtmPnl = -position.TdCloseMtmPnl; position.TdCloseFee = -position.TdCloseFee; position.TdCloseInterest = -position.TdCloseInterest; position.TdCloseInterestFee = -position.TdCloseInterestFee; position.RealizedMtmPnL = -position.RealizedMtmPnL; position.RealizedDividend = -position.RealizedDividend; position.RealizedFee = -position.RealizedFee; position.RealizedInterest = -position.RealizedInterest; position.RealizedInterestFee = -position.RealizedInterestFee; position.RealizedPnl = -position.RealizedPnl; position.InterestProfitSum = -position.InterestProfitSum; position.PosiProfitSum = -position.PosiProfitSum; position.VTradingFee = -position.VTradingFee; position.SwapPositionValue = -position.SwapPositionValue; } private void SetPosiPrice(eod_swap_position position) { var um = DataCacheProvider.GetUnderlyingDataSource().GetData(position.UnderlyingCode); if (um != null && um.IsBond()) { position.PosiNetPrice *= 100; position.UnderlyingPrice *= 100; position.PosiGrossPrice *= 100; position.PosiNetFeePrice *= 100; position.PosiNetNoFeePrice *= 100; return; } } /// /// 确认交易后,生成eod数据 /// /// public void InitSaveEodSwapPosition(trade td) { //if (td.StartDate > td.TradeDate) //{ // return; //} var positions = DbContext.swap_position.Where(x => x.SwapTradeId == td.id && x.IsInitial && !x.Invalid).ToList(); var posiList = positions.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode)).ToList(); var interestList = positions.Where(x => string.IsNullOrEmpty(x.UnderlyingCode)).ToList(); var eodSwapPostions = DbContext.eod_swap_position.Where(x => x.SwapTradeId == td.id); var eodSwaps = DbContext.eod_swap.Where(x => x.SwapTradeId == td.id); td.trade_extend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == td.id); List eventTypes = new List() { (int)SwapFlowEventTypeEnum.平仓, (int)SwapFlowEventTypeEnum.互换, (int)SwapFlowEventTypeEnum.自动互换, (int)SwapFlowEventTypeEnum.开仓 }; var flowEvents = GetSwapFlowEvents(td.id, td.StartDate.Value, eventTypes);//当日平仓/互换/自动互换信息 DbContext.eod_swap_position.RemoveRange(eodSwapPostions); DbContext.eod_swap.RemoveRange(eodSwaps); var preday = QdpCalendarHelper.GetNonHolidayDefore(td.StartDate.Value.AddDays(-1)); var preSettleDate= QdpCalendarHelper.GetNonHolidayDefore(td.TradeDate.Value.AddDays(-1)); var posiLongNotional = positions.Where(s => s.PositionType == (int)PositionTypeFlag.Long).Sum(s => s.PosiNotionalValue); var posiShortNotional = positions.Where(s => s.PositionType == (int)PositionTypeFlag.Short).Sum(s => s.PosiNotionalValue); var grossPrice = posiList.FirstOrDefault()?.PosiGrossPrice ?? 0; foreach (var item in interestList) { SaveEodInterestPositionCopy(null, null, td.StartDate.Value, td, item, null, false, posiLongNotional, posiShortNotional, grossPrice); } foreach (var item in posiList) { SaveCurrentEodInitalPosi(item, td, td.StartDate.Value, preday, flowEvents); } DbContext.SaveChanges(); SaveEodSwap(td, td.StartDate.Value, preSettleDate); DbContext.SaveChanges(); } /// /// 获取客户互换持仓信息 /// /// /// /// public List GetSwapPositions(int clientId, DateTime valueDate) { var trades = DbContext.trade.Where(x => x.TradeType == "收益互换" && x.ClientId == clientId && ConsTrade.LiveTradeStatusList.Contains(x.TradeStatus) && x.ValidState != "InValid").ToList(); var tradeIds = trades.Select(s => s.id).ToList(); var eodSwaps = DbContext.eod_swap_position.Where(x => tradeIds.Contains(x.SwapTradeId) && x.ValueDate == valueDate).ToList(); var tradeExtends = DbContext.trade_extend.Where(x => tradeIds.Contains(x.TradeId)).ToList(); return ConvertEodPnl(eodSwaps, trades, tradeExtends); } /// /// 获取客户互换历史持仓信息 /// /// /// /// public List GetSwapHistoryPositions(int clientId, DateTime valueDate) { var trades = DbContext.trade.Where(x => x.TradeType == "收益互换" && x.ClientId == clientId && x.ExerciseDate <= valueDate && ConsTrade.TradeCompleteStatus.Contains(x.TradeStatus) && x.ValidState != "InValid").ToList(); var tradeIds = trades.Select(s => s.id).ToList(); var eodSwaps = DbContext.eod_swap_position.Where(x => tradeIds.Contains(x.SwapTradeId) && x.ValueDate == valueDate).ToList(); var tradeExtends = DbContext.trade_extend.Where(x => tradeIds.Contains(x.TradeId)).ToList(); return ConvertEodPnl(eodSwaps, trades, tradeExtends); } private List ConvertEodPnl(List eodSwaps, List trades, List tradeExtends) { List list = new List(); foreach (var item in eodSwaps) { var tradeOrigin = trades.First(x => x.id == item.SwapTradeId); var realizedPnL = item.RealizedMtmPnL + item.RealizedDividend + item.RealizedFee + item.RealizedInterest + item.RealizedInterestFee; var tdExtend = tradeExtends.First(x => x.TradeId == item.SwapTradeId); eod_position model = new eod_position() { TradeId = tradeOrigin.id, TradeType = tradeOrigin.TradeType, ClientId = tradeOrigin.ClientId, TradeNumber = tradeOrigin.TradeNumber, TradeDate = tradeOrigin.TradeDate, ExerciseDate = tradeOrigin.ExerciseDate, PrincipalRate = tradeOrigin.PrincipalRate ?? 0, BasisUnderlyingCode = tradeOrigin.BasisUnderlyingCode, UnderlyingCode = item.UnderlyingCode, BasisGap = tradeOrigin.BasisGap ?? 0, Lots = Convert.ToDouble(item.PosiQuantity), ParticipationRate = tradeOrigin.ParticipationRate ?? 0, NoRiskRate = tradeOrigin.NoRiskRate ?? 0, UnderlyingPrice = Convert.ToDouble(item.UnderlyingPrice), Pv = Convert.ToDouble(item.UnderlyingMarketValue) * -1, RoundedPv = Math.Round(Convert.ToDouble(item.UnderlyingMarketValue), 2) * -1, Pnl = Convert.ToDouble(realizedPnL) * -1, RoundedPnl = Math.Round(Convert.ToDouble(realizedPnL), 2) * -1, ValueDate = item.ValueDate, PvDouble = Convert.ToDouble(item.UnderlyingMarketValue), PnlDouble = Convert.ToDouble(realizedPnL), PositionRelizedAmount = Convert.ToDouble(realizedPnL) * -1, InstrumentType = tradeOrigin.UnderlyingInstrumentType, IsGroup = tradeOrigin.IsGroup, SettlementType = tradeOrigin.SettlementType, SettlementFlag = tradeOrigin.SettlementFlag, tradeOrigin = tradeOrigin.Clone(), Vol = 0, Delta = 0, Gamma = 0, Theta = 0, Vega = 0, Rho = 0, GammaCash = 0 }; SetDicValue(model, item, tdExtend.ExtendObj.AnnualDays); list.Add(model); } return list; } /// /// 设置持仓导出字典信息 /// /// /// /// private void SetDicValue(eod_position model, eod_swap_position item, int annualDays) { var extDic = model.trade.MetaDic; if (!string.IsNullOrEmpty(item.UnderlyingCode)) { var underlyingAssetName = DataCacheProvider.GetUnderlyingDataSource().GetData(item.UnderlyingCode)?.UnderlyingName; decimal posiTradingFeeUnit = 0; if (item.PosiQuantity != 0) { posiTradingFeeUnit = item.PosiTradingFee / item.PosiQuantity; } if (item.PosiDirection == (int)SwapDirectionEnum.支付) { extDic["互换_支付方标的代码"] = item.UnderlyingCode; extDic["互换_支付方标的名称"] = underlyingAssetName; extDic["互换_支付方期初标的价格"] = item.PosiGrossPrice.OtcFormatUmPrice(); extDic["互换_支付方交易数量"] = item.PosiQuantity.OtcFormatNotional(); extDic["互换_支付方到期标的价格"] = item.UnderlyingPrice.OtcFormatUmPrice(); extDic["互换_支付方单位交易费用"] = posiTradingFeeUnit.OtcFormatUmPrice(); extDic["互换_支付方初始预付金"] = item.RealizedFee.OtcFormatPercent(); extDic["互换_支付方交易费用"] = item.PosiTradingFee.OtcFormatUmPrice(); extDic["互换_支付方多空方向"] = item.PositionType == (int)PositionTypeFlag.Long ? "多头" : "空头"; } else { extDic["互换_收取方标的代码"] = item.UnderlyingCode; extDic["互换_收取方标的名称"] = underlyingAssetName; extDic["互换_收取方期初标的价格"] = item.PosiGrossPrice.OtcFormatUmPrice(); extDic["互换_收取方交易数量"] = item.PosiQuantity.OtcFormatNotional(); extDic["互换_收取方到期标的价格"] = item.UnderlyingPrice.OtcFormatUmPrice(); extDic["互换_收取方单位交易费用"] = posiTradingFeeUnit.OtcFormatUmPrice(); extDic["互换_收取方初始预付金"] = ""; extDic["互换_收取方交易费用"] = item.PosiTradingFee.OtcFormatUmPrice(); extDic["互换_收取方多空方向"] = item.PositionType == (int)PositionTypeFlag.Long ? "多头" : "空头"; } } else { if (item.InterestDirection == (int)SwapDirectionEnum.支付) { extDic["互换_支付方互换利率"] = item.InterestRateDefault.OtcFormatPercent(); extDic["互换_支付方固定收益"] = ""; } else { extDic["互换_收取方互换利率"] = item.InterestRateDefault.OtcFormatPercent(); extDic["互换_收取方固定收益"] = ""; } } extDic["互换_互换日期"] = ""; extDic["年化天数"] = annualDays.ToString(); } } }