using BaseOUDAL; using ClosedXML.Report.Options; using Confluent.Kafka; using CsvHelper; using MoreLinq; using Newtonsoft.Json; using NPOI.POIFS.NIO; using Org.BouncyCastle.Ocsp; using Qdp.ComputeServiceV2.Data.CommonModels.TradeInfos; using Qdp.Foundation.Implementations; using Qdp.Pricing.Ecosystem.Trade.FixedIncome; using Qdp.Pricing.Library.Base.Utilities; using Qdp.Pricing.Library.Common.Products.Rates; using System; using System.Data; using System.Linq.Expressions; using System.Text; using YLErp.BLL; using YLErp.Configuration; using YLErp.Configuration.Enums; using YLErp.CustomizedBizLogic; using YLErp.DBModels; using YLErp.DBModels.Enums; using YLErp.Enums; using YLErp.Helpers; using YLErp.Model; using YLErp.Model.Enum; using YLErp.Models; using YLErp.Modules.DataProviderModule; using YLErp.Modules.EodModule; using YLErp.Modules.RiskModule; using YLErp.Modules.SalesModule; using YLErp.Modules.TradeModule; using YLErp.Modules.TradeModule.DealModule; using YLErp.Modules.TradeModule.QueryModule; using YLErp.Modules.UnderlyingModule; using YLErp.QdpModule; using static YLErp.ConsGlobal; namespace YLErp.Modules.SwapModule { /// /// 新版互换交易服务 /// public class SwapTradeService : SwapTradeBaseService { public SwapTradeService(OptUserInfo optUser) : base(optUser) { } public SwapTradeService(YLBaseService baseService) : base(baseService) { } private static decimal ValidateDeliveryPrice(decimal price, string fieldName) { var roundedPrice = Math.Round(price, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero); if (price != roundedPrice) { throw new ServiceException($"{fieldName}最多保留{ConsGlobal.SwapDeliveryPriceRound}位小数"); } return roundedPrice; } private static decimal? RoundSwapBondNetPriceAndYtm(decimal? value) { return value.HasValue ? Math.Round(value.Value, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero) : null; } #region 互换交易保存 /// /// 新版收益互换预付金校验 /// public string ValidateMargin(trade req) { var errorMsg = string.Empty; var client = DataCacheProvider.GetClientDataSource().GetData(req.ClientId); if (!client.DerivativesInvestmentVarieties.Contains((int)DerivativesInvestmentVarietiesEnum.场外互换 + "")) { return $"客户:{client.Name}未设置交易种类“场外互换”,无法生成互换交易!"; } //收益互换初始预付金校验 errorMsg = checkMarginRate(req); return errorMsg; } /// /// 互换交易保存 /// /// /// public trade SaveTrade(trade req) { var um = checkUnderlying(req); trade dbTrade = new trade(); //交易保存处理 var tradeNumberGenerated = PrepareTrade(req, TradeSourceEnum.系统交易, um); var trans = DbContext.Database.BeginTransaction(); try { var isAddNew = req.id == 0; dbTrade = isAddNew ? InnerSaveNewTrade(req, tradeNumberGenerated) : InnerSaveEditTrade(req, out var changeConfirmStatus); if (req.SalesCommission != null) { req.SalesCommission.ClientId = dbTrade.ClientId; } if (PS.Config.ErpElement.SalesCommissionCalculation == "默认" || PS.Config.ErpElement.SalesCommissionCalculation == "" || PS.Config.ErpElement.SalesCommissionCalculation == null) { new SalesCommissionDataService(this).SaveTradeCommission(dbTrade.id, req.SalesCommission, false); } else if (PS.Config.ErpElement.SalesCommissionCalculation == "公式1") { if (req.SalesCommission?.SalesIds != null && req.SalesCommission.SalesIds.Any()) { new SalesCommissionDetailDataService(this).SaveTradeCommissionDetail(dbTrade.id, req.SalesCommission.ClientId, req.SalesCommission.Commission, req.SalesCommission.CommissionFixed, req.SalesCommission.SalesIds, false); } else { new SalesCommissionDetailDataService(this).RemoveTradeCommissionDetail(dbTrade.id); } } trans.Commit(); } finally { trans.Dispose(); } return dbTrade; } /// /// 单标的生成开仓事件 /// /// public void AddPositionEvent(trade td, string optLog) { var initials = DbContext.swap_position.Where(x => x.SwapTradeId == td.id && x.IsInitial && !x.Invalid).ToList(); if (td.StructureType == "多空组合") { initials = initials.Where(x => x.PosiDirection == 0).ToList(); } new SwapFlowEventService(this).InitEvent(initials, td, optLog); } /// /// 删除互换交易 /// /// public void deleteTrade(int id, bool addLog = true) { var dbTrade = DbContext.trade.Find(id); if (dbTrade == null) { throw new ServiceException("找不到交易信息"); } dbTrade.OptId = UserId; dbTrade.OptName = UserName; dbTrade.OptDate = OptDate; dbTrade.ValidState = ConsGlobal.InValid; var tradeNumber = dbTrade.TradeNumber; if (!string.IsNullOrWhiteSpace(dbTrade.TradeNumber)) { dbTrade.TradeNumber = dbTrade.TradeNumber.Insert(0, "XX"); var contractBll = new TradeContractBLL(UserId, UserName); contractBll.DeleteConfirmBook(dbTrade.id); } var swapPositions = DbContext.swap_position.Where(x => x.SwapTradeId == id).ToList(); swapPositions.ForEach(x => { x.Invalid = true; x.OptTime = OptDate; }); new SwapEventService(this).AddSwapEventDate(DateTime.Now.Date, id, (int)SwapEventTypeEnum.删除, string.Empty, 0, false); DeleteTradeDetials(id); DeleteTradeCashInCashOut(id); DbContext.SaveChanges(); } private void DeleteTradeDetials(int tradeId) { var eodSwapPosiSql = $"{nameof(eod_swap_position.SwapTradeId)}={tradeId}"; var eodSwapSql = $"{nameof(eod_swap.SwapTradeId)}={tradeId}"; DbContext.BulkDelete(eodSwapPosiSql); DbContext.BulkDelete(eodSwapSql); } public void DeleteTradeCashInCashOut(int tradeId) { var predicate_cashIncashOut = PredicateBuilder.Create(x => x.TradeId == tradeId); var delCashInCashOutArr = DbContext.ClientCashInCashOut.Where(predicate_cashIncashOut); DbContext.ClientCashInCashOut.RemoveRange(delCashInCashOutArr); } /// /// 单标的初始化实时持仓 /// /// public void InitialPosition(trade td, bool save = false) { var swapPostions = DbContext.swap_position.Where(x => x.SwapTradeId == td.id); var initials = swapPostions.Where(x => x.IsInitial); var positions = swapPostions.Where(x => !x.IsInitial); if (td.StructureType == "多空组合") { initials = initials.Where(x => x.PosiDirection == 0); positions = swapPostions.Where(x => !x.IsInitial && x.PosiDirection == 0); } List _Positions = new List(positions); DbContext.swap_position.RemoveRange(positions); foreach (var item in initials) { if (string.IsNullOrEmpty(item.PosiNumber)) { item.PosiNumber = $"{td.TradeNumber}-{item.id}"; } if (td.StructureType == "多空组合") { var position = _Positions.FirstOrDefault(x => x.PositionId == item.id); if (position == null) { position = item.Clone(); position.id = 0; position.PositionId = item.id; position.IsInitial = false; DbContext.swap_position.Add(position); } } else { var position = item.Clone(); position.id = 0; position.PositionId = item.id; position.IsInitial = false; DbContext.swap_position.Add(position); } } if (save) { DbContext.SaveChanges(); } } //新增交易保存 private trade InnerSaveNewTrade(trade req, bool tradeNumberGenerated) { var dbTrade = req.Clone(); // 新增场景同样从 swap_positions 取 InitYtm,无则默认 0,避免数据库 NOT NULL 约束报错 dbTrade.InitYtm = req.swap_positions.FirstOrDefault(p => p.InitYtm != null)?.InitYtm ?? 0; DbContext.trade.Add(dbTrade); InnerSaveTrade(true, dbTrade, ""); return dbTrade; } /// /// 新增互换交易并确认开仓 /// /// /// /// /// /// /// /// public trade NewSwapTrade(swap_flow_merge flowMerge, Client client, AssetUnit asset, underlying_manager underlying, SwapFloatRate swapFloatRate, string clearingAgency, string structureType = "普通债券类收益互换", bool cashNeedAfter = false) { int SwapEndDays = UnderlyingHelper.GetApplicableMarginRate(client.id,underlying.UnderlyingCode,flowMerge.OccurTime)?.swap_days??14; var td = PrepareTrade(flowMerge, client, asset, underlying, SwapEndDays, structureType); PrepareTradeExtend(flowMerge, td, underlying, swapFloatRate); td.TradeNumber = BizLogicSingleton.Instance.GenerateTradeNumberBeforeConfirm(td, DbContext); flowMerge.SwapTradeNo = td.TradeNumber; PrepareSwapTrade(td, TradeSourceEnum.系统交易, underlying); DbContext.trade.Add(td); DbContext.SaveChanges(); td.trade_Initial_Margin = new trade_initial_margin() { TradeId = td.id, MarginType = 1, Direction = (int)SwapDirectionEnum.收取, MarginValue = 0, }; AddTradeMeta(false, td.id, YLErp.DBModels.Consts.ConsTradeMetaKey.MainProtocolCode, client.MainProtocolCode); AddTradeMeta(false, td.id, YLErp.DBModels.Consts.ConsTradeMetaKey.SupProtocolCode, client.SupProtocolCode); AddTradeMeta(false, td.id, YLErp.DBModels.Consts.ConsTradeMetaKey.TradingPlace, "柜台市场"); if (!string.IsNullOrEmpty(clearingAgency)) { AddTradeMeta(false, td.id, YLErp.DBModels.Consts.ConsTradeMetaKey.ClearingAgency, clearingAgency); } TradeExtendJson tradeExtendJson = new TradeExtendJson() { FlowBookMode = (int)FlowBookModeEnum.先进先出, FloatingPnlAnnualized = false, NeedOpenFee = true, OpenFeeType = 1, Direction = 1, InterestCalcMode = "10", SettlementRules = cashNeedAfter ? 1 : 0, }; if (structureType != "普通债券类收益互换") { tradeExtendJson.FlowBookMode = (int)FlowBookModeEnum.加权平均; } td.trade_extend = new trade_extend() { TradeId = td.id, ExtendJson = JsonHelper.Serialize(tradeExtendJson) }; DbContext.trade_initial_margin.Add(td.trade_Initial_Margin); DbContext.trade_extend.Add(td.trade_extend); foreach (var item in td.swap_positions) { item.SwapTradeId = td.id; DbContext.swap_position.Add(item); } DbContext.SaveChanges(); new TradeConfirmService(UserInfo).SwapTradeConfirm(td, "流水自动簿记确认交易", true, flowMerge.OccurTime, "流水自动"); return td; } /// /// 准备框架合约数据 /// /// /// /// /// /// private trade PrepareTrade(swap_flow_merge flowMerge, Client client, AssetUnit asset, underlying_manager underlying,int swapEndDays, string structureType = "普通债券类收益互换") { trade td = new trade() { TradeType = "收益互换", BuySell = "卖出", StartDate = flowMerge.OccurTime, TradeDate = flowMerge.OccurTime, TraderId = asset.TraderIdsInt.FirstOrDefault(), TraderName = asset.TraderNamesList.FirstOrDefault(), MarginTemplateName = "系统默认", OpponentRole = "乙方", StructureType = structureType, InitialMargin = 0, ClientId = client.id, ClientNumber = client.Number, ClientName = client.Name, AssetId = asset.id, AssetBookName = asset.Name, Notional = Convert.ToDouble(flowMerge.TradingQtyAbs), TradeAmount = Convert.ToDouble(flowMerge.TradingQtyAbs), StockEqvNotional = Math.Round(Convert.ToDouble(flowMerge.TradingAmount), ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero), IsAutoGenerate = true, }; if (flowMerge.SettleDate.HasValue) { td.StartDate = flowMerge.SettleDate.Value; } td.ExerciseDate = QdpCalendarHelper.GetNonHolidayDefore(td.StartDate.Value.AddDays(swapEndDays)); PrepareUnderlying(td, underlying); PrepareSwapTradeClient(td, underlying); td.TradeOldStatus = ConsTrade.新增待确认; td.OptId = UserId; td.OptName = UserName; td.OptDate = OptDate; td.CreateDate = OptDate; td.ValidState = "Valid"; td.TradeSource = "系统交易"; td.TradeStatus = ConsTrade.确认成交; td.InitYtm = RoundSwapBondNetPriceAndYtm(flowMerge.InitYtm) ?? 0; return td; } /// /// 准备框架合约明细数据 /// /// /// /// /// private void PrepareTradeExtend(swap_flow_merge flowMerge, trade td, underlying_manager underlying, SwapFloatRate swapFloatRate) { swap_position floatPosition = new swap_position() { PosiDirection = (int)SwapDirectionEnum.支付, PositionType = flowMerge.BsType, UnderlyingCode = flowMerge.UnderlyingCode, CountRatio = underlying.CountRatio, ContractSize = Convert.ToDecimal(underlying.ContractSize), PosiNetPrice = flowMerge.TradingAmountFeeAvgAbs, PosiGrossPrice = Math.Round( flowMerge.TradingAmountAvg, underlying.IsBond() ? ConsGlobal.PriceRound : ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero), PosiNetFeePrice = flowMerge.TradingAmountNetFeeAvg ?? 0, PosiNetNoFeePrice = flowMerge.TradingAmountNetAvg ?? 0, PosiQuantity = flowMerge.TradingQtyAbs, PosiNotionalValue = Math.Round(flowMerge.TradingAmount, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero), PosiTradingFeePending = flowMerge.TradingFeePending, PosiTradingFee = 0, PosiTradingFeeUnit = 0, SwapTradeId = td.id, PosiMatuirityDate = td.ExerciseDate.Value, PosiStartDate = td.StartDate.Value, IsInitial = true, OptTime = DateTime.Now, OptId = UserInfo.UserId, OptName = UserInfo.UserName, UnderlyingInstrumentType = underlying.UnderlyingInstrumentType, InitYtm = RoundSwapBondNetPriceAndYtm(flowMerge.InitYtm) }; td.swap_positions.Add(floatPosition); swap_position interestPosition = new swap_position() { InterestDirection = flowMerge.BsType == (int)PositionTypeFlag.Long ? (int)SwapDirectionEnum.收取 : (int)SwapDirectionEnum.支付, InterestRateDefault = 0, InterestMode = (int)InterestModeEnum.标的期初全价, InterestPrincipalFix = 0, InterestType = 1, IsAnnualized = true, SwapTradeId = td.id, PosiMatuirityDate = td.ExerciseDate.Value, PosiStartDate = td.StartDate.Value, IsInitial = true, interest_rest_days = 7, OptTime = DateTime.Now, OptId = UserInfo.UserId, OptName = UserInfo.UserName, }; if (swapFloatRate != null) { interestPosition.InterestRateDefault = flowMerge.BsType == (int)PositionTypeFlag.Long ? (swapFloatRate.LongPricePoint ?? 0) : (swapFloatRate.ShortPricePoint ?? 0) * -1; interestPosition.InterestRateDefault = interestPosition.InterestRateDefault * 0.0001m; interestPosition.FloatRateUnderlyingCode = swapFloatRate.BaseUnderlyingCode; interestPosition.interest_rule = swapFloatRate.interest_rule; interestPosition.interest_rest_days = swapFloatRate.interest_rest_days; interestPosition.IsAnnualized = swapFloatRate.is_annualized ?? false; interestPosition.InterestType = swapFloatRate.interest_type??0; } var interval = new IntervalModel() { Date = td.ExerciseDate.Value, Rate = interestPosition.InterestRateDefault, Settlement = 0 }; List intervalModels = new List() { interval }; interestPosition.InterestSwapInterval = JsonConvert.SerializeObject(intervalModels); td.swap_positions.Add(interestPosition); } //修改交易保存 private trade InnerSaveEditTrade(trade req, out bool changeConfirmStatus) { var dbTrade = DbContext.trade.Find(req.id); if (dbTrade == null) { throw new ServiceException("保存失败,数据不存在"); } dbTrade.SalesCommission = new SalesCommissionDataService(OptUser).GetTradeCommissionInfo(dbTrade.id); if (dbTrade.SalesCommission?.Commission == null && req.SalesCommission == null) { dbTrade.SalesCommission = null; } if (!ConsGlobal.IsValid(dbTrade.ValidState)) { throw new NotSupportedException("不支持对已无效的交易进行修改:" + dbTrade.TradeNumber); } if (valuedateBLL.SystemDate.EditTradeNeedRemark) { req.MetaDic.TryGetValue("remark", out var remark); req.MetaDic.Remove("remark"); } //使用明确的IsUsePremiumRate布尔值 if (!req.IsUsePremiumRate.HasValue && dbTrade.IsUsePremiumRate.HasValue) { req.IsUsePremiumRate = false; } req.CalcFlag = dbTrade.CalcFlag; var oldClientId = dbTrade.ClientId; var oldTradeStatus = dbTrade.TradeStatus; var tradeDateChanged = dbTrade.TradeDate != req.TradeDate; //移除价格观察 if (dbTrade.UnderlyingId != req.UnderlyingId) { RemoveEntities(x => x.TradeId == req.id); } //交易编号调整 var canGenerateTradeNumber = false; if (req.ValidState == "InValid") { if (!string.IsNullOrWhiteSpace(dbTrade.TradeNumber)) { new TradeDocumentDataService(this).DeleteConfirmBook(dbTrade.id); if (dbTrade.TradeNumber.StartsWith("CW")) { req.TradeNumber = dbTrade.TradeNumber.Replace("CW", "XX"); } else // if (PS.Config.Is光大光子) { req.TradeNumber = dbTrade.TradeNumber.Insert(0, "XX"); } } } else { canGenerateTradeNumber = string.IsNullOrWhiteSpace(req.TradeNumber); if (canGenerateTradeNumber) { req.TradeNumber = dbTrade.TradeNumber; } } req.ContractVersion = dbTrade.ContractVersion; req.PairTrade = dbTrade.PairTrade; req.CreateDate = dbTrade.CreateDate; //确认成交状态的交易修改后变为原始标记 req.DividendDate = new DateTime(2000, 1, 1); //记录交易变更信息 var changsStr = string.Empty; changeConfirmStatus = true; var changes = DataChangeHelper.GetDataChanges(dbTrade, req); var dbTradeMeta = DbContext.TradeMeta.Where(a => a.TradeId == dbTrade.id).ToDictionary(a => a.MetaKey, b => b.MetaValue); var changMetas = new List>(); var keys = new string[] { "交易场所", "清算机构", "主协议编号", "补充协议编号" }; foreach (var key in keys) { bool isdbMeta = dbTradeMeta.TryGetValue(key, out string oldVal); bool isreqMeta = req.MetaDic.TryGetValue(key, out string newVal); if (!isdbMeta && !isreqMeta || oldVal == newVal || (string.IsNullOrWhiteSpace(oldVal) && string.IsNullOrWhiteSpace(newVal))) { continue; } var changeMeta = new List() { "", key, oldVal, newVal }; changMetas.Add(changeMeta); } if (changMetas.Any()) { changes.AddRange(changMetas); } changsStr = changes.ToJson(); if (dbTrade.TradeStatus != ConsTrade.新增待确认) { //修改备注和簿记不需要提交审核和审批 changeConfirmStatus = !changes.All(n => string.IsNullOrEmpty(n[0]) || n[0] == nameof(trade.Comments) || n[0] == nameof(trade.AssetId) || n[0] == nameof(trade.AssetBookName)); } //更新数据库实体 UpdateEntity(dbTrade, req); //交易状态处理 if (dbTrade.CheckTradeUpdate == Convert.ToInt32(TradeCheckEnum.StatusOfNew) || dbTrade.TradeStatus == ConsTrade.确认成交) { if (changeConfirmStatus) { dbTrade.TradeStatus = ConsTrade.修改待确认; dbTrade.CheckTradeUpdate = Convert.ToInt32(TradeCheckEnum.StatusOfOld); dbTrade.CheckStatus = null; } } else if (dbTrade.TradeStatus != ConsTrade.修改待确认) { dbTrade.TradeStatus = ConsTrade.新增待确认; dbTrade.CheckTradeUpdate = Convert.ToInt32(TradeCheckEnum.StatusOfOld); dbTrade.CheckStatus = null; //如果是新增待确认并且修改了客户,需要重新生成交易编号 if (oldClientId != dbTrade.ClientId && canGenerateTradeNumber) { dbTrade.TradeNumber = BizLogicSingleton.Instance.GenerateTradeNumberBeforeConfirm(dbTrade, DbContext); } } dbTrade.TradeOldStatus = oldTradeStatus; //重置历史数据(20210928:如果是当天修改也支持重置) var resetHisData = dbTrade.TradeStatus == "新增待确认" || tradeDateChanged || valuedateBLL.ValueDate == dbTrade.TradeDate; if (resetHisData) { dbTrade.TradeSavedVol = PS.Config.Is厦门象屿 ? dbTrade.TradeCloseVolatility : dbTrade.TradeOpenVolatility; if (PS.Config.ErpElement.VolMode == Configuration.VolModeEnum.VolSurface) { dbTrade.TradeSavedVol = dbTrade.Vol; } } dbTrade.trade_Initial_Margin = req.trade_Initial_Margin; dbTrade.trade_extend = req.trade_extend; dbTrade.swap_positions = req.swap_positions; dbTrade.MetaDic = req.MetaDic; dbTrade.InitYtm = RoundSwapBondNetPriceAndYtm(req.swap_positions.FirstOrDefault(p => p.InitYtm != null)?.InitYtm); InnerSaveTrade(false, dbTrade, changsStr, changeConfirmStatus); return dbTrade; } //用于交易(不能是结构化主交易)入库后续处理 //changeConfirmStatus:保存交易修改时需要 private void InnerSaveTrade(bool isAddNew, trade dbTrade, string changsStr, bool changeConfirmStatus = false) { //保存修改 DbContext.SaveChanges(); SaveTradeExend(dbTrade); SaveTradeMargin(dbTrade); SaveSwapPositions(dbTrade.swap_positions, dbTrade); ClearSwapPositions(dbTrade); //交易元数据 if (dbTrade.MetaDic.Any()) { foreach (var kv in dbTrade.MetaDic) { if (isAddNew) { if (!string.IsNullOrEmpty(kv.Value)) { AddTradeMeta(false, dbTrade.id, kv.Key, kv.Value); } } else { AddOrUpdateTradeMeta(false, dbTrade.id, kv.Key, kv.Value); } } } if (changeConfirmStatus) { //删除交易确认书 TODO new TradeDocumentDataService(this).DeleteBooksAfterEditTrade(dbTrade); } int eventType = isAddNew ? (int)SwapEventTypeEnum.新增交易 : (int)SwapEventTypeEnum.修改交易; new SwapEventService(UserInfo).AddSwapEventDate(dbTrade.TradeDate.Value, dbTrade.id, eventType, "", 0, true, "", true); DbContext.SaveChanges(); } /// /// 添加交易元数据 /// private void AddTradeMeta(bool saveChanges, int tradeId, string metaKey, string metaValue) { DbContext.TradeMeta.Add(new TradeMeta { TradeId = tradeId, MetaKey = metaKey, MetaValue = metaValue, CreateTime = DateTime.Now }); if (saveChanges) { DbContext.SaveChanges(); } } /// /// 添加或更新交易元数据 /// private void AddOrUpdateTradeMeta(bool saveChanges, int tradeId, string metaKey, string metaValue) { new TradeMetaService(this).AddTradeMeta(tradeId, metaKey, metaValue, saveChanges); } /// /// 保存交易扩展信息 /// /// private void SaveTradeExend(trade dbTrade) { RemoveEntities(x => x.TradeId == dbTrade.id); var trade_extend = dbTrade.trade_extend.Clone(); trade_extend.TradeId = dbTrade.id; DbContext.trade_extend.Add(trade_extend); } /// /// 保存交易预付金信息 /// public void SaveTradeMargin(trade dbTrade) { RemoveEntities(x => x.TradeId == dbTrade.id); var trade_Initial_Margin = dbTrade.trade_Initial_Margin.Clone(); trade_Initial_Margin.TradeId = dbTrade.id; DbContext.trade_initial_margin.Add(trade_Initial_Margin); } private bool PrepareTrade(trade req, TradeSourceEnum dataSource, underlying_manager um) { bool tradeNumberGenerated = false; PrepareInitialMargin(req); var isAddNew = req.id == 0; if (isAddNew) { req.TradeStatus = ConsTrade.新增待确认; req.TradeSource = dataSource.ToString(); if (string.IsNullOrWhiteSpace(req.TradeNumber) || (PS.Config.Company == CompanyEnum.光大光子 && (req.TradeNumber == "STG" || req.TradeNumber == "STD" || req.TradeNumber == "FWD" || req.TradeNumber == "SPD"))) { tradeNumberGenerated = true; if (req.UnderlyingCode != null && req.UnderlyingCode != "") { PrepareUnderlying(req, um); } req.TradeNumber = BizLogicSingleton.Instance.GenerateTradeNumberBeforeConfirm(req, DbContext); } else if (DbContext.trade.Any(n => n.TradeNumber == req.TradeNumber)) { throw new ServiceException($"交易编号'{req.TradeNumber}'已经存在"); } else if (PS.Config.ErpElement.UpperTradeNumber) { req.TradeNumber = req.TradeNumber.ToUpperInvariant(); } } else if (!string.IsNullOrWhiteSpace(req.TradeNumber)) { if (DbContext.trade.Any(n => n.id != req.id && n.TradeNumber == req.TradeNumber)) { throw new ServiceException($"交易编号'{req.TradeNumber}'已经存在"); } if (PS.Config.ErpElement.UpperTradeNumber) { req.TradeNumber = req.TradeNumber.ToUpperInvariant(); } } PrepareSingleTrade(req, dataSource, isAddNew, um); req.PrincipalRateWrite = req.PrincipalRateWrite.IsNormalize() ? req.PrincipalRateWrite : null; return tradeNumberGenerated; } private static void PrepareInitialMargin(trade req) { // 初始预付金依赖最终入库的名义本金,须先统一金额精度,避免两者无法勾稽。 req.StockEqvNotional = Math.Round(req.StockEqvNotional, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); req.InitialMargin = Convert.ToDouble(req.trade_Initial_Margin.MarginValue); if (req.trade_Initial_Margin.MarginType == 0) { req.InitialMargin = req.StockEqvNotional == 0 ? 0 : Convert.ToDouble(req.trade_Initial_Margin.MarginValue) * req.StockEqvNotional; } } //准备单个交易 private trade PrepareSingleTrade(trade req, TradeSourceEnum dataSource, bool isAddNew, underlying_manager um) { //准备交易基础信息 PrepareBaseInfo(req, um); PrepareSwapTrade(req, dataSource, um); req.OptId = UserId; req.OptName = UserName; req.OptDate = OptDate; if (isAddNew) { req.ValidState = ConsGlobal.Valid; req.CreateDate = DateTime.Now; req.TradeSavedVol = PS.Config.Is厦门象屿 ? req.TradeCloseVolatility : req.TradeOpenVolatility; if (PS.Config.ErpElement.VolMode == Configuration.VolModeEnum.VolSurface) { req.TradeSavedVol = req.Vol; } } return req; } //准备互换交易 private void PrepareSwapTrade(trade req, TradeSourceEnum dataSource, underlying_manager um) { PrepareSwapTradeClient(req, um); req.OriginalNotional = req.Notional; req.BuySell = "买入"; if (req.StructureType == ClientMarginTypeEnum.多空组合.ToString()) { req.BuySell = req.trade_extend.ExtendObj.Direction == 1 ? "买入" : "卖出"; } else { var swapPosition = req.swap_positions.FirstOrDefault(x => !string.IsNullOrEmpty(x.UnderlyingCode)); req.BuySell = swapPosition.PosiDirection == 1 ? "买入" : "卖出"; var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(req.QuoteCurrency, req.SettlementCurrency, req.TradeDate.Value , seekPreday: dataSource == TradeSourceEnum.系统交易); var tradingFees = req.swap_positions.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode)).Sum(x => x.PosiTradingFee * (x.PosiDirection == 1 ? -1 : 1)); req.TradePrice = Convert.ToDouble(tradingFees) * currencyRate; req.SpotPrice = Convert.ToDouble(swapPosition.PosiNetPrice); } req.Strike = null; req.StockEqvNotional = Math.Round(req.StockEqvNotional, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); req.OriginalStockEqvNotional = req.StockEqvNotional; req.StockEqvNotionalReal = req.StockEqvNotional; } //互换交易 private void PrepareSwapTradeClient(trade req, underlying_manager _underlying) { var client = PrepareBaseTradeClient(req); if (client.EvaluateExpireDate < req.TradeDate) { throw new ServiceException("适当性评估已经过期,只有在适当性有效期内才可以新开仓"); } req.ClientName = client.Name; req.SettlementCurrency = client.SettlementCurrency; } //客户信息 private Client PrepareBaseTradeClient(trade req) { if (req.ClientId < 1) { throw new ServiceException("客户信息不存在"); } var client = DbContextFactory.GetClientDbContext(OptUser).client.FirstOrDefault(n => n.id == req.ClientId); if (client == null) { throw new ServiceException("客户信息不存在"); } if (client.ProcessStatus == "已休眠" || client.ProcessStatus == "已销户") { if (!client.ProcessOptDate.HasValue || client.ProcessOptDate.Value < req.TradeDate.Value) { var tradeDateStr = req.TradeDate.Value.ToString("yyyy-MM-dd"); var processDateStr = client.ProcessOptDate?.ToString("yyyy-MM-dd HH:mm:ss"); throw new ServiceException($"客户'{client.Name}'{client.ProcessStatus}并且处理日期{processDateStr}早于交易日期'{tradeDateStr}',无法保存交易!"); } } else if (client.ProcessStatus != "已开户") { throw new ServiceException($"客户'{client.Name}'尚未完成开户!"); } return client; } //准备交易基础信息(标的、簿记、交易日期) private void PrepareBaseInfo(trade req, underlying_manager um) { if (!req.TradeDate.HasValue) { throw new ServiceException("交易日期 必须填写"); } if (req.TradeDate.Value.Year < 2000) { throw new ServiceException("交易日期 填写错误:" + req.TradeDate.Value.ToString("yyyy-MM-dd")); } if (QdpCalendarHelper.IsHoliday(req.TradeDate.Value) && req.TradeType != "现金流交易") { throw new ServiceException("交易日期不能是假日:" + req.TradeDate.Value.ToString("yyyy-MM-dd")); } //如果起始日没有值,默认赋值交易日 if (!req.StartDate.HasValue) { req.StartDate = req.TradeDate; } //-------------------------------------- // 标的信息 //-------------------------------------- if (req.StructureType != ClientMarginTypeEnum.多空组合.ToString()) { PrepareUnderlying(req, um); } //-------------------------------------- // 簿记帐户 //-------------------------------------- if (req.AssetId < 1) { throw new ServiceException("请选择簿记账户"); } var _assetUnit = DbContext.assetunit.Where(n => n.id == req.AssetId) .Select(n => new AssetUnitDto { id = n.id, Name = n.Name, TraderIds = n.TraderIds, UserGroup = n.UserGroup }).FirstOrDefault(); if (_assetUnit == null) { throw new ServiceException("簿记账户不存在"); } req.AssetBookName = _assetUnit.Name; //-------------------------------------- // 交易员信息(如果没有交易员,查找簿记账户中的交易员) //-------------------------------------- if (string.IsNullOrWhiteSpace(req.TraderName)) { //有交易员id,通过id查名字 if (req.TraderId > 0) { req.TraderName = UserBLL.GetNameById(req.TraderId); } else { //当前登录用户作为交易员 req.TraderId = UserId; req.TraderName = UserName; } } req.TradeAmount = Convert.ToDouble(req.swap_positions.Sum(s => s.PosiQuantity)); } //准备交易标的信息 public underlying_manager PrepareUnderlying(trade req, underlying_manager _underlying) { var underlyingCode = req.UnderlyingCode ?? string.Empty; if (_underlying == null) { _underlying = UnderlyingDataProvider.GetUnderlying(underlyingCode); } if (_underlying == null) { throw new ServiceException("标的信息不存在:" + req.UnderlyingCode); } if (_underlying.IsFutures() && _underlying.MaturityDate == null) { throw new ServiceException("标的到期日不存在:" + req.UnderlyingCode); } req.UnderlyingId = _underlying.id; req.UnderlyingCode = _underlying.UnderlyingCode; req.VarietyId = _underlying.UnderlyingTypeId; req.UnderlyingAssetClass = _underlying.UnderlyingType; req.UnderlyingAssetName = _underlying.UnderlyingName; req.MaturityDate = _underlying.IsFutures() ? _underlying.MaturityDate : null; req.UnderlyingInstrumentType = _underlying.GetMainType(); req.CountRatio = _underlying.CountRatio; //权益类标的关联的交易如果没有设置分红率则取标的上的分红率,如果标的分红率为空则取0 if (!req.DividendRate.HasValue && _underlying.CalcTypeIsStock()) { req.DividendRate = _underlying.DividendRate ?? 0; } return _underlying; } /// /// 收益互换初始预付金校验 /// /// /// private string checkMarginRate(trade trade) { var errorMsg = ""; var marginValue = trade.trade_Initial_Margin.MarginValue; if (trade.trade_Initial_Margin.MarginType == (int)TradeMarginTypeEnum.固定金额) { marginValue = trade.StockEqvNotional == 0 ? 0 : trade.trade_Initial_Margin.MarginValue / Convert.ToDecimal(trade.StockEqvNotional); } //多空组合 if (ClientMarginTypeEnum.多空组合.ToString().Equals(trade.StructureType)) { if (marginValue < 0.25m) { errorMsg = $"根据监管要求权益类多空组合型收益互换预付金不得低于25%,当前交易预付金率为:{marginValue * 100}%,不符合监管要求!"; } } else { //获取标的品种 var underlingData = new UnderlyingDataProvider(); var swapposition = trade.swap_positions.FirstOrDefault(x => !string.IsNullOrEmpty(x.UnderlyingCode)); trade.UnderlyingCode = swapposition == null ? "" : swapposition.UnderlyingCode; var underlying = underlingData.GetUnderlying(trade.UnderlyingCode); var variety = underlingData.GetVariety(underlying.UnderlyingTypeId); if (variety == null) { errorMsg = $"所设置标的未对应品种,请设置对应品种后再进行交易!"; } else { //股票、窄基股票指数及其产品、信用债 初始预付金比率 必须大于等于100% if ((variety.AssetType == "个股" || (variety.AssetType == "指数" && "窄基指数".Equals(variety.IndexType))) && marginValue < 1) { errorMsg = $"所交易标的为股票或窄基指数及其产品、信用债,初始预付金比例不得低于100%!,当前交易预付金率为:{marginValue * 100}%,不符合监管要求!"; } else if ((variety.AssetType == "指数" && "宽基指数".Equals(variety.IndexType)) && Convert.ToDecimal(marginValue) < 0.5m) { //挂钩宽基股票指数及其产品,无对应期货品种的 向单一交易对手方收取的预付金比例不得低于50% errorMsg = $"所交易标的挂钩宽基股票指数及其产品,无对应期货品种的,初始预付金比例不得低于50%!,当前交易预付金率为:{marginValue * 100}%,不符合监管要求!"; } else if (marginValue < Convert.ToDecimal(variety.Margin ?? 0.0)) { //有对应期货或集中交易品种的 不得低于品种设置的最低预付金比例 errorMsg = $"所交易标的对应期货或集中交易品种的,初始预付金比例不得低于品种[{variety.VarietyName}]所设置最低预付金比例{variety.Margin * 100}%!,当前交易预付金率为:{marginValue * 100}%,不符合监管要求!"; } } } return errorMsg; } /// /// 标的校验 /// /// /// private underlying_manager checkUnderlying(trade trade) { if (trade.StructureType == ClientMarginTypeEnum.多空组合.ToString()) { return null; } var underlingData = new UnderlyingDataProvider(); var posi = trade.swap_positions.Where(x => x.PosiDirection > 0).FirstOrDefault(); if (posi == null) { throw new ServiceException("缺少标的信息"); } var underlying = underlingData.GetUnderlying(posi.UnderlyingCode); // 标的代码 if (underlying == null) { throw new ServiceException("标的代码 必须在系统中存在"); } if (underlying.UnderlyingInstrumentType == "Stock" && !underlying.IsCombined()) { var limit = LimitRangeEnum.Swap; var limitStatus = new StockBlackWhiteService(UserInfo).GetStockBlackWhiteList(limit, out var Codes); if (Codes != null) { if (limitStatus == Codes.Contains(underlying.UnderlyingCode)) { if (limitStatus) { throw new ServiceException("标的代码 填写错误,存在于黑名单中,请求值:" + underlying.UnderlyingCode); } else { throw new ServiceException("标的代码 填写错误,不存在于白名单中,请求值:" + underlying.UnderlyingCode); } } } } trade.UnderlyingCode = underlying.UnderlyingCode; trade.UnderlyingId = underlying.id; trade.UnderlyingAssetClass = underlying.UnderlyingType; trade.UnderlyingInstrumentType = underlying.UnderlyingInstrumentType; trade.UnderlyingAssetName = underlying.UnderlyingName; return underlying; } #endregion #region 互换交易查询 /// /// 互换交易列表查询 /// /// /// /// public SearchListResult SearchList(SwapTradeQueryRequest req, out tradeGridSum gsum) { var query = CreateTradeQuery(req); var retListResult = new SearchListResult(); if (req.ValueDate != null) { var dateStr = req.ValueDate?.ToString("yyyy-MM-dd"); var _query = from t in query join sp in DbContext.swap_position on t.id equals sp.SwapTradeId where sp.InterestSwapInterval.Contains(dateStr) && !sp.Invalid select t; retListResult = _query.ToSearchList(req); } else { retListResult = query.ToSearchList(req); } gsum = new tradeGridSum(); if (query.Any()) { gsum.TradePriceSum = query.Sum(q => q.TradePrice); } var tradeids = retListResult.rows.Select(r => r.id).ToList(); var list = DbContext.trade_contract_r.Where(O => tradeids.Contains(O.TradeId) && O.IsValid && O.Type == "交易确认书").AsEnumerable(); var contractCodeDict = list.GroupBy(O => O.TradeId).ToDictionary(K => K.Key, V => V.LastOrDefault().ContractCode); //获取销售提成信息 foreach (var item in retListResult.rows) { item.ContractCode = contractCodeDict.TryGetValue(item.id, out var code) ? code : ""; item.SalesCommission = new SalesModule.SalesCommissionDataService(OptUser).GetTradeCommissionInfo(item.id); } retListResult.Sum = gsum; return retListResult; } private Expression> BuildTradeQuery(TradeReq req) { var predicate = PredicateBuilder.Create(n => n.ValidState != "InValid" && n.TradeType == "收益互换"); if (req.UserAssets != null && req.UserClients != null) { predicate = predicate.And(n => req.UserAssets.Contains(n.AssetId) || req.UserClients.Contains(n.ClientId)); } if (req.AssetIdList.Any()) { predicate = predicate.And(d => req.AssetIdList.Contains(d.AssetId)); } if (!string.IsNullOrEmpty(req.TraderNames)) { predicate = predicate.And(d => req.TraderNamesList.Contains(d.TraderId)); } if (!string.IsNullOrEmpty(req.TradeNumber)) { predicate = predicate.And(d => d.TradeNumber.Contains(req.TradeNumber)); } if (req.TradeStatusList != null) { predicate = predicate.And(d => req.TradeStatusList.Contains(d.TradeStatus)); } if (req.AssetIdList.Any()) { predicate = predicate.And(d => req.AssetIdList.Contains(d.AssetId)); } if (req.ClientIdsInt.Any()) { predicate = predicate.And(d => req.ClientIdsInt.Contains(d.ClientId)); } if (req.StartDateStart != DateTime.MinValue) { predicate = predicate.And(d => d.StartDate >= req.StartDateStart); } if (req.StartDateEnd != DateTime.MinValue) { var StartDateTemp = req.StartDateEnd.AddDays(1); predicate = predicate.And(d => d.StartDate < StartDateTemp); } if (req.ExerciseDateEnd == null) { req.ExerciseDateEnd = DateTime.MaxValue; } if (req.ExerciseDateStart == null) { req.ExerciseDateStart = DateTime.MinValue; } predicate = predicate.And(d => d.ExerciseDate >= req.ExerciseDateStart && d.ExerciseDate <= req.ExerciseDateEnd); return predicate; } /// /// 获取互换交易详情 /// /// /// public trade GetSwapTrade(int intid) { var tradeObj = DbContext.trade.Find(intid); if (tradeObj == null) { return null; } //需要审批或者复核的交易都会显示行权审核提交按钮 tradeObj.MetaDic = new TradeMetaService(UserInfo).GetTradeMeta(tradeObj.id); tradeObj.trade_Initial_Margin = DbContext.trade_initial_margin.FirstOrDefault(x => x.TradeId == intid); if (tradeObj.trade_Initial_Margin == null) { tradeObj.trade_Initial_Margin = new trade_initial_margin(); } tradeObj.trade_extend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == intid); tradeObj.swap_positions = DbContext.swap_position.Where(x => x.SwapTradeId == intid && !x.Invalid).ToList(); tradeObj.swap_positions = tradeObj.swap_positions.Where(x => x.PosiQuantity > 0 || x.InterestDirection > 0).ToList(); var intervalPositions = tradeObj.swap_positions.Where(x => string.IsNullOrEmpty(x.UnderlyingCode) && x.IsInitial).ToList(); var intervalPositionIds = intervalPositions.Select(s => s.id).ToList(); var tradeObervations = DbContext.trade_obervation.Where(x => intervalPositionIds.Contains(x.PositionId)); foreach (var position in intervalPositions) { position.Obervation = tradeObervations.FirstOrDefault(s => s.PositionId == position.id); } var positionIds = tradeObj.swap_positions.Select(x => x.id); tradeObj.ClientCashInCashOutList = DbContext.ClientCashInCashOut.Where(x => x.ValidState != ConsGlobal.InValid && x.TradeId == intid).OrderBy(o => o.HappenDate).ToList(); List eventTyps = new List() { (int)SwapEventTypeEnum.平仓, (int)SwapEventTypeEnum.互换, (int)SwapEventTypeEnum.自动互换 }; var allSwapEvents = GetSwapEvents(intid, eventTyps).OrderBy(o => o.id).ToList();//历史平仓/互换记录 tradeObj.swap_Events.AddRange(allSwapEvents); if (tradeObj.swap_Events.Count > 0) { tradeObj.eod_swaps = DbContext.eod_swap.Where(x => x.SwapTradeId == intid).ToList(); tradeObj.eod_swaps.ForEach(x => { var eod = tradeObj.eod_swaps.Where(t => t.ValueDate < x.ValueDate && t.id != x.id).OrderBy(o => o.ValueDate).FirstOrDefault(); if (eod != null) { x.CloseStartDate = eod.ValueDate; } else { x.CloseStartDate = x.ValueDate; } }); } foreach (var item in tradeObj.swap_positions.Where(x => x.IsInitial)) { if (!string.IsNullOrEmpty(item.UnderlyingCode)) { item.underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(item.UnderlyingCode); if (item.underlying != null && item.underlying.IsBond() && !string.IsNullOrEmpty(item.underlying.ExJson)) { var bond = JsonHelper.Deserialize(item.underlying.ExJson); item.underlying.UnderlyingFullName = bond.UnderlyingFullName; item.underlying.UnderlyingIssuer = bond.UnderlyingIssuer; item.underlying.IssueSize = bond.IssueSize; } } } return tradeObj; } /// /// 撤回审批 /// /// public void TradeRevoke(int intid) { var tradeObj = DbContext.trade.Find(intid); if (tradeObj == null) { return; } var eventTypes = new List() { (int)SwapEventTypeEnum.修改交易, (int)SwapEventTypeEnum.新增交易, (int)SwapEventTypeEnum.平仓, (int)SwapEventTypeEnum.互换 }; var lastEvent = DbContext.swap_event.Where(x => x.SwapTradeId == intid && eventTypes.Contains(x.EventType) && !x.Invalid).OrderByDescending(o => o.OptTime).FirstOrDefault(); if (tradeObj.TradeStatus == ConsTrade.平仓待复核 || tradeObj.TradeStatus == ConsTrade.互换待复核) { tradeObj.TradeStatus = ConsTrade.确认成交; } else { if (lastEvent != null && lastEvent.EventType == (int)SwapEventTypeEnum.修改交易) { tradeObj.TradeStatus = ConsTrade.修改待确认; } else { tradeObj.TradeStatus = ConsTrade.新增待确认; } } tradeObj.CheckStatus = null; tradeObj.ProcessOrderId = 0; tradeObj.IsApproval = false; if (lastEvent != null) { lastEvent.Invalid = true; } DbContext.SaveChanges(); return; } private IQueryable CreateTradeQuery(SwapTradeQueryRequest req) { var predicate = PredicateBuilder.Create(n => n.ValidState != "InValid" && n.TradeType == "收益互换"); var query = DbContext.trade.Where(predicate); if (req.ClientIds != null && req.ClientIds.Count > 0) { query = query.Where(d => req.ClientIds.Contains(d.ClientId)); } if (req.TradeIds != null && req.TradeIds.Count > 0) { query = query.Where(d => req.TradeIds.Contains(d.TraderId)); } if (req.AssetIds != null && req.AssetIds.Count > 0) { query = query.Where(d => req.AssetIds.Contains(d.AssetId)); } if (req.TradeDateStart.HasValue) { query = query.Where(d => d.TradeDate >= req.TradeDateStart); } if (req.TradeDateEnd.HasValue) { var TradeDateTemp = req.TradeDateEnd.Value.AddDays(1); query = query.Where(d => d.TradeDate < TradeDateTemp); } if (req.ExerciseDateEnd.HasValue) { query = query.Where(d => d.ExerciseDate <= req.ExerciseDateEnd); } if (req.ExerciseDateStart.HasValue) { query = query.Where(d => d.ExerciseDate >= req.ExerciseDateStart); } if (!string.IsNullOrEmpty(req.TradeNumber)) { query = query.Where(d => d.TradeNumber.Contains(req.TradeNumber)); } if (req.TradeStatus != null && req.TradeStatus.Count > 0) { query = query.Where(d => req.TradeStatus.Contains(d.TradeStatus)); } if (string.IsNullOrEmpty(req.sidx)) { req.sidx = "id"; req.sord = "desc"; }; query = query.OrderByDescending(s => s.OptDate); return query; } #endregion /// /// 获取互换交易对手方 交易编码 /// /// /// public List GetSwapTradeNumbers(int clientId) { var tradeNumbers = DbContext.trade.Where(t => t.ClientId == clientId && t.TradeType == "收益互换" && t.TradeStatus == ConsTrade.确认成交 && t.StructureType == ClientMarginTypeEnum.多空组合.ToString() && t.ValidState != "InValid").Select(s => new SelectItem { Text = s.TradeNumber, Value = s.id.ToString() }).ToList(); return tradeNumbers; } /// /// 保存交易腿信息 /// /// 持仓腿信息 /// 交易id /// 成交日期 public void SaveSwapPositions(List swap_positions, trade td) { var swapPositionList = DbContext.swap_position.Where(x => x.SwapTradeId == td.id); var swapPositionIds = swap_positions.Select(s => s.id).ToList(); var intervalPositionIds = swapPositionList.Where(x => string.IsNullOrEmpty(x.UnderlyingCode) && x.IsInitial).Select(s => s.id); var obervations = DbContext.trade_obervation.Where(o => intervalPositionIds.Contains(o.PositionId)); var originalSwapPositions = swapPositionList.Where(x => x.IsInitial); var realTimeSwapPositions = swapPositionList.Where(x => !x.IsInitial); var deleteSwapPositions = swapPositionList.Where(x => !swapPositionIds.Contains(x.id)); if (td.StructureType == "多空组合") { deleteSwapPositions = swapPositionList.Where(x => !swapPositionIds.Contains(x.id) && x.PosiDirection == 0); realTimeSwapPositions = swapPositionList.Where(x => !swapPositionIds.Contains(x.id) && x.PosiDirection == 0 && !x.IsInitial); var positions = swapPositionList.Where(x => x.PosiDirection > 0); var direction = td.trade_extend.ExtendObj.Direction; foreach (var item in positions) { item.PosiDirection = direction; } } DbContext.swap_position.RemoveRange(realTimeSwapPositions); DbContext.swap_position.RemoveRange(deleteSwapPositions); DbContext.trade_obervation.RemoveRange(obervations); foreach (var swap in swap_positions) { var position = originalSwapPositions.FirstOrDefault(x => x.id == swap.id); if (position == null) { position = new swap_position(); } position.ContractSize = swap.ContractSize; position.CountRatio = swap.CountRatio; var ratio = swap.PositionType == (int)PositionTypeFlag.Short ? -1 : 1; position.id = swap.id; position.PositionType = swap.PositionType; position.PosiTradingFee = swap.PosiTradingFee; position.PosiTradingFee=Math.Round(position.PosiTradingFee, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); position.PosiTradingFeeUnit = swap.PosiTradingFeeUnit; position.PosiTradingFeePending = swap.PosiTradingFeePending; position.PosiTradingFeePending = Math.Round(position.PosiTradingFeePending, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); position.UnderlyingCode = swap.UnderlyingCode; position.UnderlyingInstrumentType = swap.UnderlyingInstrumentType; position.PosiDirection = swap.PosiDirection; // position.PosiGrossPrice = string.IsNullOrEmpty(swap.UnderlyingCode) // ? Math.Round(swap.PosiGrossPrice, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero) // : ValidateDeliveryPrice(swap.PosiGrossPrice, "期初交割价"); var storagePriceRound = ConsGlobal.InstrumentType.IsBond(swap.UnderlyingInstrumentType) ? ConsGlobal.PriceRound : ConsGlobal.SwapDeliveryPriceRound; position.PosiGrossPrice = Math.Round(swap.PosiGrossPrice, storagePriceRound, MidpointRounding.AwayFromZero); position.PosiNetPrice = swap.PosiQuantity == 0 ? 0 : (position.PosiGrossPrice + (position.PosiTradingFeePending / swap.PosiQuantity) * ratio); position.PosiNetPrice = Math.Round(position.PosiNetPrice, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero); position.PosiNetNoFeePrice = RoundSwapBondNetPriceAndYtm(swap.PosiNetNoFeePrice); position.PosiNetFeePrice = swap.PosiQuantity == 0 ? 0 : (swap.PosiNetNoFeePrice + (position.PosiTradingFeePending / swap.PosiQuantity) * ratio); position.PosiNetFeePrice = Math.Round(position.PosiNetFeePrice??0, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero); position.PosiNotionalValue = Math.Round(swap.PosiNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); position.PosiQuantity = swap.PosiQuantity; position.InterestDirection = swap.InterestDirection; position.InterestMode = swap.InterestMode; position.InterestRateDefault = swap.InterestRateDefault; position.InterestPrincipalFix = swap.InterestPrincipalFix; position.InterestSwapInterval = swap.InterestSwapInterval; position.PosiStartDate = td.StartDate.Value; position.PosiMatuirityDate = td.ExerciseDate.Value; position.SwapTradeId = td.id; position.IsInitial = true; position.OptTime = DateTime.Now; position.OptId = UserInfo.UserId; position.OptName = UserInfo.UserName; position.IsAnnualized = swap.IsAnnualized; position.HappenDate = swap.HappenDate; position.Currency = swap.Currency; position.InterestType = swap.InterestType; position.FloatRate = swap.FloatRate; position.FloatRateUnderlyingCode = swap.FloatRateUnderlyingCode; position.interest_rest_days = swap.interest_rest_days; position.interest_rule = swap.interest_rule; position.InitYtm = RoundSwapBondNetPriceAndYtm(swap.InitYtm); if (position.InitYtm != null && position.InitYtm > 0) { td.InitYtm = position.InitYtm; } if (position.id == 0) { DbContext.swap_position.Add(position); DbContext.SaveChanges(); position.PosiNumber = $"{td.TradeNumber}-{position.id}"; } if (swap.Obervation != null) { swap.Obervation.OptTime = DateTime.Now; swap.Obervation.OptId = UserInfo.UserId; swap.Obervation.OptName = UserInfo.UserName; swap.Obervation.PositionId = position.id; DbContext.trade_obervation.Add(swap.Obervation); } } } /// /// 修改清除互换持仓所有信息 /// /// public void ClearSwapPositions(trade td) { var tradeId = td.id; var swapFlowEvents = DbContext.swap_flow_event.Where(x => x.SwapTradeId == tradeId && x.DataState != (int)SwapFlowDateStateEnum.废弃); var eodSwapPositions = DbContext.eod_swap_position.Where(x => x.SwapTradeId == tradeId); var eodSwaps = DbContext.eod_swap.Where(x => x.SwapTradeId == tradeId); var clientcashinouts = DbContext.ClientCashInCashOut.Where(x => x.TradeId == tradeId && x.Action == ClientCashInCashOut.系统操作_应付预付金); swapFlowEvents.ForEach(x => { x.DataState = (int)SwapFlowDateStateEnum.废弃; }); //DbContext.swap_flow_event.RemoveRange(swapFlowEvents); DbContext.eod_swap_position.RemoveRange(eodSwapPositions); DbContext.eod_swap.RemoveRange(eodSwaps); DbContext.ClientCashInCashOut.RemoveRange(clientcashinouts); } /// /// 修改展期信息,互换观察日到期日期修改 /// /// 互换交易id /// 旧到期日 /// 新到期日 public void UpdateObservationDay(int tradeId, DateTime oldExerciseDate, DateTime exerciseDate) { var dateStr = exerciseDate.ToString("yyyy-MM-dd"); var oldDateStr = oldExerciseDate.ToString("yyyy-MM-dd"); var interestPostions = DbContext.swap_position.Where(x => x.SwapTradeId == tradeId && string.IsNullOrEmpty(x.UnderlyingCode) && !x.Invalid).ToList(); interestPostions.ForEach(x => { if (x.SwapIntervalList.Count == 1 && x.SwapIntervalList.Any(t => t.Date == oldExerciseDate)) { x.InterestSwapInterval = x.InterestSwapInterval.Replace(oldDateStr, dateStr); } }); } /// /// 交易回退 /// /// /// public trade GetTrade(int tradeId) { var td = DbContext.trade.Find(tradeId); return td; } /// /// 互换交易回退 /// /// 交易id /// 回退日期 /// 回到开仓 public void TradeBack(int tradeId, DateTime valueDate, bool del = false) { var td = DbContext.trade.Find(tradeId); if (td == null) { throw new ServiceException("交易不存在"); } bool backToBegin = td.TradeDate == valueDate; var swapPositions = DbContext.swap_position.Where(x => x.SwapTradeId == tradeId && !x.Invalid).ToList(); td.trade_extend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == td.id); //展期 var swapEvent = DbContext.swap_event.Where(x => x.SwapTradeId == tradeId && x.EventType == (int)SwapEventTypeEnum.展期 && !x.Invalid && x.ValueDate <= valueDate).OrderByDescending(o => o.ValueDate).FirstOrDefault(); var trans = DbContext.Database.BeginTransaction(); try { var swapEventService = new SwapEventService(this); td.TradeStatus = "确认成交"; td.ProcessOrderId = 0; td.ProcessStatus = null; if (backToBegin) { td.StockEqvNotional = Math.Round(td.OriginalStockEqvNotional ?? 0, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); td.UnWindDate = null; td.HasPartialUnWind = null; SingleTradeBackToBegin(td, swapPositions); } else { TradeBackByDate(td, valueDate, swapPositions); } if (swapEvent != null)//展期 { swapEventService.DeleteExtensionTime(swapEvent.id); } InvalidTradeOptionDatasByDate(tradeId, valueDate, backToBegin); swapEventService.AddSwapEventDate(valueDate, tradeId, (int)SwapEventTypeEnum.回退, string.Empty, 0, false, $"交易回退至{valueDate:yyyy年MM月dd日}"); DbContext.SaveChanges(); if (del) { new TradeRiskCheckLogService(UserInfo).DeleteLog(td.TradeNumber, valueDate);//重置风控记录 } if (backToBegin) //重置开仓记录 { DeleteTradeCashInCashOut(tradeId); DeleteTradeDetials(tradeId); DbContext.SaveChanges(); if (del) { swapPositions.ForEach(x => { x.Invalid = true; x.OptTime = OptDate; }); td.ValidState = "InValid"; var tradeNumber = td.TradeNumber; td.TradeNumber = td.TradeNumber.Insert(0, "XX"); td.OptDate = OptDate; swapEventService.AddSwapEventDate(valueDate, tradeId, (int)SwapEventTypeEnum.删除, string.Empty, 0, false); DbContext.SaveChanges(); new TradeDocumentDataService(this).InnerDeleteConfirmBook(tradeId); trans.Commit(); return; } //new SwapEodPositionService(this).InitSaveEodSwapPosition(td); var happenDate = td.TradeDate.Value; if (td.trade_extend != null && !td.trade_extend.ExtendObj.NeedOpenFee) { happenDate = td.ExerciseDate.Value; } ResetMarginAmount(td, happenDate, swapPositions); } //new SwapFlowService(this).DeleteSwapFlow(tradeId, valueDate, backToBegin); trans.Commit(); } catch (Exception ex) { trans.Rollback(); throw ex; } finally { trans.Dispose(); } } /// /// 重新补录预付金记录 /// /// /// /// private void ResetMarginAmount(trade td, DateTime happenDate, List swapPositions) { new ClientCashInCashOutService(this).SaveSwapTradeClientCash(td, td.TradePrice ?? 0, happenDate, 0); foreach (var marginPositions in swapPositions.Where(x => x.HappenDate != null && x.IsInitial && x.InterestMode != (int)InterestModeEnum.追加预付金).GroupBy(g => g.HappenDate)) { var marginHappenDate = marginPositions.Key; foreach (var marginPosition in marginPositions) { marginPosition.Obervation = DbContext.trade_obervation.FirstOrDefault(x => x.PositionId == marginPosition.id); bool generateMargin = true; if (marginPosition.Obervation != null) { if (!marginPosition.Obervation.IsDeductPrincipal) { generateMargin = false; } } if (generateMargin) { var marginAmount = marginPosition.InterestPrincipalFix * (marginPosition.InterestDirection == 1 ? -1 : 1); new ClientCashInCashOutService(this).SaveSwapTradeClientCash(td, Convert.ToDouble(marginAmount), marginHappenDate.HasValue ? marginHappenDate.Value : td.TradeDate.Value, marginPosition.id, ClientCashInCashOut.系统操作_应付预付金); } } } } /// /// 获取持仓腿信息 /// /// 叫一起集合 /// 标的代码 /// public IEnumerable GetSwapPositions(List tradeIds, HashSet _underlyingCodeFilterSet) { Expression> expression = x => tradeIds.Contains(x.SwapTradeId) && !string.IsNullOrEmpty(x.UnderlyingCode) && x.IsInitial && !x.Invalid; if (_underlyingCodeFilterSet != null) { expression = expression.And(x => _underlyingCodeFilterSet.Contains(x.UnderlyingCode)); } return DbContext.swap_position.Where(expression); } /// /// 单标的回退到初始 /// /// /// private void SingleTradeBackToBegin(trade td, List swapPositions) { var positions = swapPositions.Where(x => !x.IsInitial).ToList(); var marginPositions = swapPositions.Where(x => x.IsInitial && x.InterestMode == (int)InterestModeEnum.追加预付金).ToList(); var orignPositions = swapPositions.Where(x => x.IsInitial && x.InterestMode != (int)InterestModeEnum.追加预付金).ToList(); DbContext.swap_position.RemoveRange(positions); DbContext.swap_position.RemoveRange(marginPositions); td.TradeAmount = 0; foreach (var item in orignPositions) { var interest = item.Clone(); interest.id = 0; interest.PositionId = item.id; interest.IsInitial = false; td.TradeAmount = td.TradeAmount + Convert.ToDouble(interest.PosiQuantity); DbContext.swap_position.Add(interest); } } /// /// 回到某个事件之前 /// /// /// private void TradeBackByDate(trade td, DateTime valueDate, List swapPositions) { SwapEodPositionService eodPositionService = new SwapEodPositionService(this); SwapDealService swapDealService = new SwapDealService(this); var preDay = valueDate.AddDays(-1); var eodSwapPositionList = DbContext.eod_swap_position.Where(x => x.ValueDate == preDay && x.SwapTradeId == td.id).ToList(); var swapFlowEvents = DbContext.swap_flow_event.Where(x => x.SwapTradeId == td.id && x.EventDate >= valueDate && x.DataState > (int)SwapFlowDateStateEnum.废弃).ToList(); var positions = swapPositions.Where(x => x.PosiDirection > 0 && !x.IsInitial).ToList(); foreach (var posi in positions) { var eodPosi = eodSwapPositionList.FirstOrDefault(x => x.PositionId == posi.PositionId); if (eodPosi != null) { posi.InterestAmount = eodPosi.RealizedInterest; posi.InterestFeePending = eodPosi.InterestFeePending; posi.InterestPrincipalFix= eodPosi.InterestPrincipalFix; posi.PosiTradingFeePending = eodPosi.PosiFeePending; posi.PosiDividendIncome = eodPosi.PosiDividendSum; posi.PosiQuantity = eodPosi.PosiQuantity; posi.PosiNetPrice = eodPosi.PosiNetPrice; posi.PosiGrossPrice = eodPosi.PosiGrossPrice; posi.PosiNetFeePrice = eodPosi.PosiNetFeePrice; posi.PosiNetNoFeePrice = eodPosi.PosiNetNoFeePrice; posi.PosiNotionalValue = Math.Round(eodPosi.PosiNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); if (posi.PosiDirection > 0) { td.StockEqvNotional = Math.Round(Convert.ToDouble(posi.PosiNotionalValue), ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero); td.TradeAmount = Convert.ToDouble(posi.PosiQuantity); } } } td.UnWindDate = null; td.UnWindNotional = null; //DbContext.swap_position.RemoveRange(positions); //swapDealService.UpdateInitalPosition(valueDate,td, swapPositions, swapFlowEvents); DbContext.swap_flow_event.RemoveRange(swapFlowEvents); var clientCashs = DbContext.ClientCashInCashOut.Where(x => x.TradeId == td.id && x.HappenDate >= valueDate).ToList(); DbContext.ClientCashInCashOut.RemoveRange(clientCashs); DbContext.SaveChanges(); } /// /// 多空组合回到初始 /// /// private void LongshortTradeBackToBegin(List swapPositions) { var positions = swapPositions.Where(x => !x.IsInitial || x.InterestDirection == 0).ToList(); var orignPositions = swapPositions.Where(x => x.IsInitial && x.InterestDirection > 0).ToList(); foreach (var item in positions) { item.Invalid = true; } //DbContext.swap_position.RemoveRange(positions); foreach (var item in orignPositions) { var interest = item.Clone(); interest.id = 0; interest.PositionId = item.id; interest.IsInitial = false; DbContext.swap_position.Add(interest); } } /// /// 废弃或删除回退日期之后的数据 /// /// /// private void InvalidTradeOptionDatasByDate(int tradeId, DateTime valueDate, bool backToBegin) { var swapEvents = DbContext.swap_event.Where(x => !x.Invalid && x.SwapTradeId == tradeId && x.ValueDate >= valueDate).ToList(); var swapEodPositions = DbContext.eod_swap_position.Where(x => x.SwapTradeId == tradeId && x.ValueDate >= valueDate); var swapEods = DbContext.eod_swap.Where(x => x.SwapTradeId == tradeId && x.ValueDate >= valueDate); var swapFlowEvents = DbContext.swap_flow_event.Where(x => x.SwapTradeId == tradeId && x.EventDate >= valueDate); var firstConfirm = false; swapEvents.ForEach(x => { if (backToBegin && !firstConfirm && x.EventType == (int)SwapEventTypeEnum.确认交易) { firstConfirm = true; } else { x.Invalid = true; } }); swapFlowEvents.ForEach(x => { if (backToBegin && x.EventType == (int)SwapFlowEventTypeEnum.开仓) { } else { x.DataState = (int)SwapFlowDateStateEnum.废弃; } }); //DbContext.swap_flow_event.RemoveRange(swapFlowEvents); DbContext.eod_swap_position.RemoveRange(swapEodPositions); DbContext.eod_swap.RemoveRange(swapEods); } } }