using System.Data; using System.Globalization; using System.Text.RegularExpressions; using YLErp.BLL; using YLErp.Commons; using YLErp.Configuration; using YLErp.CustomizedBizLogic; using YLErp.DBModels.Consts; using YLErp.DBModels.Enums; using YLErp.Enums; using YLErp.Model.Enum; using YLErp.Modules.CalculationModule; using YLErp.Modules.EodModule; using YLErp.Modules.TradeModule.DocGenerateModule; using YLErp.Modules.TradeModule.OrderModule; using YLErp.QdpModule; namespace YLErp.Modules.TradeModule.SwapModule { public class SwapTradeImportService : TradeServiceBase { public SwapTradeImportService(YLBaseService baseService) : base(baseService) { } public SwapTradeImportService(OptUserInfo userInfo) : base(userInfo) { } /// /// 从excel文件中导入场外期权交易 /// public void ImportSwapTradesFromExcel(Stream streamIn, out int totalNum, out int successNum) { if (PS.Config.Company == CompanyEnum.山西) { ImportFromExcelSX(streamIn, out totalNum, out successNum); } else { ImportFromExcel(streamIn, out totalNum, out successNum); } } #region 普通互换交易导入 /// /// 导入交易 /// /// /// 当前文件中的目标期权总条数 /// 成功入库的数量 public void ImportFromExcel(Stream streamIn, out int totalNum, out int successNum) { totalNum = 0; successNum = 0; var rowIndex = 0; var groupTradeList = new List(); var structureTradeList = new List(); try { var ds = Office.ExcelHelper.ReadExcelAsDataSet(streamIn, new[] { 0 }, 0); if (ds.Tables.Count < 1 || ds.Tables[0].Rows.Count < 3) { throw new ServiceException("读取导入数据失败:数据为空") { Tag = "111" }; } var table = ds.Tables[0]; var reader = new DataRowTopTypeReader(table); rowIndex = 2; totalNum = table.Rows.Count - rowIndex; foreach (var row in table.Rows.Cast().Skip(2)) { using (var trans = BeginTransaction()) { rowIndex++; if (row.ItemArray.All(n => string.IsNullOrWhiteSpace(n?.ToString()))) { totalNum--; continue; } reader.SetDataRow(row); //映射导入数据到交易对象 HandleTrade(reader); successNum++; trans.Commit(); } } } catch (ServiceException se) { if (se.Tag != null) { throw; } throw new ServiceException($"已成功导入{successNum}条;\n第{rowIndex}行,{se.Message}"); } catch (Exception ex) { LogFactory.GetLogger("导入期权交易").Error(ex); throw new ServiceException($"已成功导入{successNum}条,\n第{rowIndex}行,发生错误:{ex.Message}", ex); } } /// /// 导入交易 /// /// /// 当前文件中的目标期权总条数 /// 成功入库的数量 public void ImportToEndSwapTradesFromExcel(Stream streamIn, out int totalNum, out int successNum) { totalNum = 0; successNum = 0; var rowIndex = 0; try { var ds = Office.ExcelHelper.ReadExcelAsDataSet(streamIn, new[] { 0 }, 0); if (ds.Tables.Count < 1 || ds.Tables[0].Rows.Count < 2) { throw new ServiceException("读取导入数据失败:数据为空") { Tag = "111" }; } var table = ds.Tables[0]; var reader = new DataRowReader(table, 0); rowIndex = 1; totalNum = table.Rows.Count - rowIndex; foreach (var row in table.Rows.Cast().Skip(1)) { using (var trans = BeginTransaction()) { rowIndex++; if (row.ItemArray.All(n => string.IsNullOrWhiteSpace(n?.ToString()))) { totalNum--; continue; } reader.SetDataRow(row); HandleToEndSwapTrade(reader); successNum++; trans.Commit(); } } } catch (ServiceException se) { if (se.Tag != null) { throw; } throw new ServiceException($"已成功导入{successNum}条;\n第{rowIndex}行,{se.Message}"); } catch (Exception ex) { LogFactory.GetLogger("导入收益互换了结").Error(ex); throw new ServiceException($"已成功导入{successNum}条,\n第{rowIndex}行,发生错误:{ex.Message}", ex); } } private trade HandleTrade(DataRowTopTypeReader reader) { var td = new trade(); td.trade_swap = new trade_swap(); //基本要素 td.TradeNumber = reader.GetString("交易编号", false); td.AssetBookName = reader.GetString("簿记账户名称", true); td.TraderName = reader.GetString("交易员名称", true); new OtcOptionSaveChecker(this).CheckAssetBook(td).CheckTrader(td); td.TradeType = "收益互换"; td.ClientName = reader.GetString("交易对手方名称", true); if (string.IsNullOrWhiteSpace(td.ClientName)) { throw new ServiceException("交易对手方名称 必须填写"); } //交易日期 td.TradeDate = reader.GetDate("成交日期", true); td.StartDate = reader.GetDate("开始日期", true); td.ExerciseDate = reader.GetDate("到期日期", true); td.SettlementDate = td.ExerciseDate; td.StockEqvNotional = reader.GetDouble("名义本金", true) ?? 0; td.trade_swap.AnnualDays = reader.GetInt("年化天数", true); td.trade_swap.AnnualVarIncome = reader.GetString("浮动收益年化", true) == "是"; td.trade_swap.RateCalcMode = GetRateCalcMode(reader.GetString("计息方式", true)); td.trade_swap.IsTradePriceWhenOpen = reader.GetString("是否开仓时收取开仓费", true) == "是"; td.trade_swap.IsShare = reader.GetString("收费基本单位", true) == "手数"; var ExchangeRate = reader.GetDouble("汇率", false); td.Comments = reader.GetString("备注", false); var clientQuery = DbContextFactory.GetClientDbContext(OptUser).client.AsQueryable(); if (!string.IsNullOrWhiteSpace(td.ClientName)) { clientQuery = clientQuery.Where(c => c.Name == td.ClientName); } var client = clientQuery.Select(n => new { n.id, n.SettlementCurrency, n.DerivativesInvestmentVarieties,n.IsCentralClearing,n.CentralClearingPaltform,n.TradingPaltform }).FirstOrDefault(); if (client == null) { throw new ServiceException($"交易对手方不存在,客户名称:{td.ClientName}"); } else { if (!string.IsNullOrEmpty(client.IsCentralClearing)) { td.MetaDic.Add("中央对手方清算", client.IsCentralClearing); } if (!string.IsNullOrEmpty(client.IsCentralClearing)) { td.MetaDic.Add("中央清算平台", client.CentralClearingPaltform); } if (!string.IsNullOrEmpty(client.IsCentralClearing)) { td.MetaDic.Add("交易平台", client.TradingPaltform); } if (!client.DerivativesInvestmentVarieties.Contains((int)DerivativesInvestmentVarietiesEnum.场外互换 + "")) { throw new ServiceException($"客户:{td.ClientName}未设置交易种类“场外互换”,无法生成互换交易!"); } } td.ClientId = client.id; td.SettlementCurrency = client.SettlementCurrency; td.OpponentRole = "甲方"; var um = new underlying_manager(); var singleFee = 0.0; #region 交易员收取信息读取 reader.SetTopType("交易员收取"); td.trade_swap.GetLongShort = reader.GetString("多头空头", false); if (!string.IsNullOrWhiteSpace(td.trade_swap.GetLongShort)) { td.trade_swap.IsGetFloatingProfit = true; td.trade_swap.GetUnderlyingCode = reader.GetString("标的代码", true); um = DataCacheProvider.GetUnderlyingDataSource().GetData(td.trade_swap.GetUnderlyingCode); if (um == null) { throw new ServiceException($"{td.trade_swap.GetUnderlyingCode} 不存在,请先新建标的再导入"); } td.trade_swap.GetUnderlyingId = um.id; td.UnderlyingId = um.id; td.UnderlyingCode = um.UnderlyingCode; td.UnderlyingName = um.UnderlyingName; td.UnderlyingInstrumentType = um.UnderlyingInstrumentType; if (!td.trade_swap.IsShare) { td.trade_swap.GetNotional = reader.GetDouble("份额/手数", true); td.trade_swap.GetTradeAmount = td.trade_swap.GetNotional / um.CountRatio; td.trade_swap.GetLot = td.trade_swap.GetNotional / um.ContractSize; } else { td.trade_swap.GetLot = reader.GetDouble("份额/手数", true); td.trade_swap.GetNotional = td.trade_swap.GetLot * um.ContractSize; td.trade_swap.GetTradeAmount = td.trade_swap.GetNotional / um.CountRatio; } td.Notional = td.trade_swap.GetNotional ?? 0; td.TradeAmount = td.trade_swap.GetTradeAmount ?? 0; td.Lots = td.trade_swap.GetLot; //判断是否组合标的 var synthetic = DataCacheProvider.GetUnderlyingDataSource().GetSyntheticUnderlying(um.UnderlyingCode); //读取组合标的 if (synthetic != null) { reader.SetTopType("组合标的"); var sulist = new List(4); var model = synthetic.GetSyntheticPriceModel(); var codeSet = model.SuList.Select(n => n.UnderlyingCode).ToHashSet(StringComparer.OrdinalIgnoreCase); for (var i = 1; i <= 4; i++) { var code = reader.GetString("标的" + i + "_代码"); if (string.IsNullOrWhiteSpace(code)) { continue; } if (!codeSet.Remove(code)) { throw new ServiceException($"[组合标的]标的{i}_代码 填写错误,组合标的中不存在此标的:{code}"); } model.SuList.First(n => n.UnderlyingCode.Equals(code, StringComparison.OrdinalIgnoreCase)).Price = reader.GetDouble("标的" + i + "_价格", true).Value; } if (codeSet.Any()) { throw new ServiceException("[组合标的]未填写完整"); } td.trade_swap.GetSpotPrice = model.Price = model.SuList.Sum(n => n.Coefficient * n.Price) + model.Constant; td.MetaDic["组合标的"] = JsonHelper.ToJson(model); } else { td.trade_swap.GetSpotPrice = reader.GetDouble("期初标的价格", true); } td.SpotPrice = td.trade_swap.GetSpotPrice; } else { var str = reader.GetString("单位交易费用", true); if (!string.IsNullOrWhiteSpace(str)) { var percent = str.EndsWith("%"); if (percent) { var strTrim = str.TrimEnd('%'); if (double.TryParse(strTrim, out var num)) { td.trade_swap.GetUnAnnualRate = num / 100; } else { throw new ServiceException($"[交易员收取]单位交易费用 填写错误:{str}"); } } else { if (double.TryParse(str, out var num)) { singleFee = num; } else { throw new ServiceException($"[交易员收取]单位交易费用 填写错误:{str}"); } } } } td.trade_swap.GetSwapRate = reader.GetPercent("互换利率(年化)", false) ?? 0; if (td.trade_swap.GetSwapRate != 0) { td.trade_swap.GetSwapTimeAndRate = td.ExerciseDate.Value.ToString("yyyy-MM-dd") + ";" + td.trade_swap.GetSwapRate; } td.trade_swap.GetMarginRate = reader.GetPercent("初始预付金率", false) ?? 0; #endregion #region 交易员支付信息读取 reader.SetTopType("交易员支付"); td.trade_swap.PayLongShort = reader.GetString("多头空头", false); if (!string.IsNullOrWhiteSpace(td.trade_swap.PayLongShort)) { td.trade_swap.IsPayFloatingProfit = true; td.trade_swap.PayUnderlyingCode = reader.GetString("标的代码", true); um = DataCacheProvider.GetUnderlyingDataSource().GetData(td.trade_swap.PayUnderlyingCode); if (um == null) { throw new ServiceException($"{td.trade_swap.PayUnderlyingCode} 不存在,请先新建标的再导入"); } td.trade_swap.PayUnderlyingId = um.id; td.UnderlyingId = um.id; td.UnderlyingCode = um.UnderlyingCode; td.UnderlyingName = um.UnderlyingName; td.UnderlyingInstrumentType = um.UnderlyingInstrumentType; if (!td.trade_swap.IsShare) { td.trade_swap.PayNotional = reader.GetDouble("份额/手数", true); td.trade_swap.PayTradeAmount = td.trade_swap.PayNotional / um.CountRatio; td.trade_swap.PayLot = td.trade_swap.PayNotional / um.ContractSize; } else { td.trade_swap.PayLot = reader.GetDouble("份额/手数", true); td.trade_swap.PayNotional = td.trade_swap.PayLot * um.ContractSize; td.trade_swap.PayTradeAmount = td.trade_swap.PayNotional / um.CountRatio; } td.Notional = td.trade_swap.PayNotional ?? 0; td.TradeAmount = td.trade_swap.PayTradeAmount ?? 0; td.Lots = td.trade_swap.PayLot; //判断是否组合标的 var synthetic = DataCacheProvider.GetUnderlyingDataSource().GetSyntheticUnderlying(um.UnderlyingCode); //读取组合标的 if (synthetic != null) { reader.SetTopType("组合标的"); var sulist = new List(4); var model = synthetic.GetSyntheticPriceModel(); var codeSet = model.SuList.Select(n => n.UnderlyingCode).ToHashSet(StringComparer.OrdinalIgnoreCase); for (var i = 1; i <= 4; i++) { var code = reader.GetString("标的" + i + "_代码"); if (string.IsNullOrWhiteSpace(code)) { continue; } if (!codeSet.Remove(code)) { throw new ServiceException($"[组合标的]标的{i}_代码 填写错误,组合标的中不存在此标的:{code}"); } model.SuList.First(n => n.UnderlyingCode.Equals(code, StringComparison.OrdinalIgnoreCase)).Price = reader.GetDouble("标的" + i + "_价格", true).Value; } if (codeSet.Any()) { throw new ServiceException("[组合标的]未填写完整"); } td.trade_swap.PaySpotPrice = model.Price = model.SuList.Sum(n => n.Coefficient * n.Price) + model.Constant; td.MetaDic["组合标的2"] = JsonHelper.ToJson(model); } else { td.trade_swap.PaySpotPrice = reader.GetDouble("期初标的价格", true); } td.SpotPrice = td.trade_swap.PaySpotPrice; } else { var str = reader.GetString("单位交易费用", true); if (!string.IsNullOrWhiteSpace(str)) { var percent = str.EndsWith("%"); if (percent) { var strTrim = str.TrimEnd('%'); if (double.TryParse(strTrim, out var num)) { td.trade_swap.PayUnAnnualRate = num / 100; } else { throw new ServiceException($"[交易员支付]单位交易费用 填写错误:{str}"); } } else { if (double.TryParse(str, out var num)) { singleFee = num; } else { throw new ServiceException($"[交易员支付]单位交易费用 填写错误:{str}"); } } } } td.trade_swap.PaySwapRate = reader.GetPercent("互换利率(年化)", false) ?? 0; if (td.trade_swap.PaySwapRate != 0) { td.trade_swap.PaySwapTimeAndRate = td.ExerciseDate.Value.ToString("yyyy-MM-dd") + ";" + td.trade_swap.PaySwapRate; } td.trade_swap.PayMarginRate = reader.GetPercent("初始预付金率", false) ?? 0; #endregion if ((string.IsNullOrWhiteSpace(td.trade_swap.GetLongShort) && string.IsNullOrWhiteSpace(td.trade_swap.PayLongShort)) || (!string.IsNullOrWhiteSpace(td.trade_swap.GetLongShort) && !string.IsNullOrWhiteSpace(td.trade_swap.PayLongShort))) { throw new ServiceException($"注意“交易员收取”和“交易员支付”不可同时为浮动收益或利息收益!"); } //币种 var underlyingModel = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode); td.QuoteCurrency = DataCacheProvider.GetVarietyDataSource().GetData(underlyingModel.UnderlyingTypeId).QuoteCurrency; if (ExchangeRate != null) { if (td.QuoteCurrency != "CNY" && (!string.IsNullOrEmpty(td.QuoteCurrency))) { td.MetaDic.Add("ExchangeRate", ExchangeRate.ToString()); } } if (td.trade_swap.IsGetFloatingProfit) { if (!td.trade_swap.IsShare) { td.trade_swap.PaySingleFee = singleFee * um.ContractSize; } else { td.trade_swap.PaySingleFee = singleFee; } } else { if (!td.trade_swap.IsShare) { td.trade_swap.GetSingleFee = singleFee * um.ContractSize; } else { td.trade_swap.GetSingleFee = singleFee; } } var variety = DataCacheProvider.GetVariety(td.UnderlyingCode); if (variety == null) { throw new ServiceException($"该标的[{td.UnderlyingCode}]对应的品种在系统中不存在"); } else { if (string.IsNullOrWhiteSpace(variety.QuoteCurrency) && DbContext.currency.Any()) { throw new ServiceException($"标的代码[{td.UnderlyingCode}]对应的品种币种不能为空"); } else { td.QuoteCurrency = variety.QuoteCurrency; } var market = DataCacheProvider.GetMarketDataSource().AsQueryable().FirstOrDefault(x => x.MarketName == variety.TradingMarket); if (QdpCalendarHelper.IsHoliday((DateTime)td.TradeDate, string.IsNullOrWhiteSpace(market?.CalendarName) ? "chn" : market?.CalendarName)) { throw new ServiceException("成交日期:" + td.TradeDate + ",不能为节假日"); } if (QdpCalendarHelper.IsHoliday((DateTime)td.ExerciseDate, string.IsNullOrWhiteSpace(market?.CalendarName) ? "chn" : market?.CalendarName)) { throw new ServiceException("到期日期:" + td.ExerciseDate + ",不能为节假日"); } if (td.TradeDate > td.ExerciseDate) { throw new ServiceException("到期日期不能早于成交日期"); } } reader.SetTopType("了结信息"); var action = reader.GetString("了结方式", false); var hasSettleMode = !string.IsNullOrWhiteSpace(action); InnerSaveSwapTrade(td, hasSettleMode, false); #region 了结信息 if (hasSettleMode) { td.UnWindDate = reader.GetDate("了结日期", true); if (action == "互换" && td.UnWindDate != td.ExerciseDate) { throw new ServiceException($"只支持到期日互换,了结日期[{td.UnWindDate}]和到日期[{td.ExerciseDate}]不一致"); } td.FinalPrice = reader.GetDouble("了结标的价格", false); var eodpriceProvider = new YLErp.Modules.DataProviderModule.EodPriceProvider((DateTime)td.UnWindDate); if (td.FinalPrice == null) { td.FinalPrice = eodpriceProvider.GetPrice(td.UnderlyingCode, SettlementTypeEnum.ClosePrice);//平仓当天标的价格; } var annualFee = reader.GetDouble("利息金额", false); var costFee = reader.GetDouble("平仓手续费", false); UnwindSwapTradeCashHandle(td, action, annualFee, costFee); } #endregion //记录审核日志 DbContext.TradeAuditLog.Add(new TradeAuditLog { TradeId = td.id, Changes = null, DataType = "00", OptId = UserId, OptName = UserName, OptDate = OptDate, OptType = "导入交易", AuditFlag = TradeAuditFlag.operation }); DbContext.SaveChanges(); return td; } private trade HandleToEndSwapTrade(DataRowReader reader) { var tradeNumnr = reader.GetString("交易编号", false); var td = new trade(); var tdTable = DbContext.trade.FirstOrDefault(a => a.TradeNumber == tradeNumnr); if (tdTable == null) { throw new ServiceException($"此交易编号[{td.TradeNumber}]不存在"); } if (tdTable.TradeStatus != ConsTrade.确认成交) { throw new ServiceException("只有交易状态为‘确认成交’,才能进行批量了结导入"); } td = tdTable; td.trade_cash = new trade_cash(); td.trade_cash.UnwindStockEqvNotional = reader.GetDouble("了结名义本金", false) ?? 0; td.trade_cash.UnwindPercentRate = reader.GetPercent("了结比例", false) ?? 0; if (td.trade_cash.UnwindStockEqvNotional == 0 && td.trade_cash.UnwindPercentRate == 0) { throw new ServiceException($"交易编号[{td.TradeNumber}]中,了结名义本金与了结比例,必须填写一个值"); } td.UnWindDate = reader.GetDate("了结日期", true); var variety = DataCacheProvider.GetVariety(td.UnderlyingCode); if (variety == null) { throw new ServiceException($"该标的[{td.UnderlyingCode}]对应的品种在系统中不存在"); } else { var market = DataCacheProvider.GetMarketDataSource().AsQueryable().FirstOrDefault(x => x.MarketName == variety.TradingMarket); if (QdpCalendarHelper.IsHoliday((DateTime)td.UnWindDate, string.IsNullOrWhiteSpace(market?.CalendarName) ? "chn" : market?.CalendarName)) { throw new ServiceException("了结日期:" + td.UnWindDate + ",不能为节假日"); } } if (td.TradeDate > td.UnWindDate) { throw new ServiceException("了结日期必须要大于或等于成交日期"); } if (td.UnWindDate > td.ExerciseDate) { throw new ServiceException("了结日期必须要小于或等于到期日期"); } if (td.UnWindDate > valuedateBLL.ValueDate) { throw new ServiceException("了结日期必须要小于或等于系统日期"); } td.FinalPrice = reader.GetDouble("了结标的价格", false); var annualFeePay = reader.GetDouble("[支付]利息金额", false); var annualFeeGet = reader.GetDouble("[收取]利息金额", false); var costFee = reader.GetDouble("平仓手续费", false); UnwindSwapTradeCashHandle(td, annualFeeGet, annualFeePay, costFee); return td; } /// /// 写入交易记录 /// /// 交易记录 /// 是否存在了结方式 /// 是否历史导入 /// private void InnerSaveSwapTrade(trade td, bool hasSettleMode = true, bool isHistoryImport = true) { td.TradeStatus = ConsTrade.确认成交; if (!isHistoryImport && !hasSettleMode) { td.TradeStatus = ConsTrade.新增待确认; } if (string.IsNullOrWhiteSpace(td.TradeNumber)) { if (!GuolianContractNoGenerator.IsGuolianSwapTrade(td)) { td.TradeNumber = BizLogicSingleton.Instance.GenerateTradeNumberBeforeConfirm(td, DbContext); } } else { if (DbContext.trade.Any(x => x.TradeNumber == td.TradeNumber)) { throw new ServiceException($"存在重复的交易编号[{td.TradeNumber}]"); } } var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, td.TradeDate.Value, seekPreday: true); var tradePriceQuote = 0.0; if (td.trade_swap.IsPayFloatingProfit) { tradePriceQuote = ((td.trade_swap.GetSingleFee ?? 0) * (td.Lots ?? 0) + td.StockEqvNotional * (td.trade_swap.GetUnAnnualRate ?? 0)).FormatValue(2); td.trade_swap.GetTradePrice = tradePriceQuote; if (td.trade_swap.IsTradePriceWhenOpen) { td.TradePrice = (tradePriceQuote * currencyRate).FormatValue(2); } else { tradePriceQuote = 0; } td.BuySell = "卖出"; } else { tradePriceQuote = ((td.trade_swap.PaySingleFee ?? 0) * (td.Lots ?? 0) + td.StockEqvNotional * (td.trade_swap.PayUnAnnualRate ?? 0)).FormatValue(2); td.trade_swap.PayTradePrice = tradePriceQuote; if (td.trade_swap.IsTradePriceWhenOpen) { td.TradePrice = (tradePriceQuote * currencyRate).FormatValue(2); } else { tradePriceQuote = 0; } td.BuySell = "买入"; } td.OriginalNotional = td.Notional; td.OriginalStockEqvNotional = td.StockEqvNotional; td.StockEqvNotionalReal = td.StockEqvNotionalReal; td.IsUsePremiumRate = true; td.MarginTemplateName = null; td.MarginType = MarginTypeEnum.DEFAULT; td.IsTradePricePayType = true; td.TradeSource = TradeSourceEnum.导入交易.ToString(); td.OptId = UserId; td.OptName = UserName; td.OptDate = DateTime.Now; SetDBModelCreator(td); DbContext.trade.Add(td); DbContext.SaveChanges(); if (GuolianContractNoGenerator.TryGenerateTradeNumberAfterSave(DbContext, td)) { DbContext.SaveChanges(); } td.trade_swap.TradeId = td.id; td.trade_swap.SwapType = "普通"; td.trade_swap.OptId = UserId; td.trade_swap.OptName = UserName; td.trade_swap.OptDate = DateTime.Now; DbContext.trade_swap.Add(td.trade_swap); SaveTradeMeta(td); if (isHistoryImport || hasSettleMode) { var tc = new trade_cash { ValidState = "Valid" }; DbContext.trade_cash.Add(tc); tc.OptId = UserId; tc.OptName = UserName; tc.OptDate = DateTime.Now; tc.Action = ClientCashInCashOut.系统操作_期权费; tc.Amount = (td.TradePrice ?? 0) * (td.BuySell == "买入" ? -1 : 1); tc.QuoteAmount = tradePriceQuote * (td.BuySell == "买入" ? -1 : 1); tc.CurrencyRate = currencyRate; tc.ExceciseType = "现金"; tc.TradeId = td.id; tc.ValueDate = td.TradeDate.Value; tc.Notional = td.Notional; tc.TradeAmount = td.TradeAmount; tc.Status = TradeCashStatusEnum.已执行; tc.TradeType = td.BuySell; DbContext.SaveChanges(); new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(td, tc, tc.ValueDate); var tcdGet = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = tc.Amount, QuoteAmount = tc.QuoteAmount, TradeCashType = TradeCashTypeEnum.开仓手续费.ToString(), ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now }; DbContext.trade_cash_detail.Add(tcdGet); } DbContext.SaveChanges(); } private void SaveTradeMeta(trade t) { if (t != null && t.MetaDic != null && t.MetaDic.Count() > 0) { foreach (var kv in t.MetaDic) { if (!string.IsNullOrEmpty(kv.Value)) { AddTradeMeta(false, t.id, kv.Key, kv.Value); } } } } /// /// 了结导入数据处理 /// /// /// /// private void UnwindSwapTradeCashHandle(trade td, double? annualFeeGet, double? annualFeePay, double? costFee) { var client = DataCacheProvider.GetClientDataSource().GetData(td.ClientId); var um = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode); if (td.FinalPrice == null) { var eodpriceProvider = new YLErp.Modules.DataProviderModule.EodPriceProvider((DateTime)td.UnWindDate); if (eodpriceProvider.TryGetPrice(td.UnderlyingCode, SettlementTypeEnum.ClosePrice, out var price)) { td.FinalPrice = price; } else { throw new ServiceException($"未找到{td.UnderlyingCode}在{td.UnWindDate?.ToString("yyyy.M.d")}对应的收盘价"); } } td.trade_swap = DbContext.trade_swap.FirstOrDefault(x => x.TradeId == td.id); var unwindType = "部分平仓"; //获取平仓比例 if (td.trade_cash.UnwindPercentRate == 0 || (td.trade_cash.UnwindStockEqvNotional != 0 && td.trade_cash.UnwindPercentRate != 0)) { if (td.StockEqvNotional <= td.trade_cash.UnwindStockEqvNotional + 1e-10) { td.trade_cash.UnwindStockEqvNotional = td.StockEqvNotional; unwindType = "全部平仓"; } td.trade_cash.UnwindPercentRate = td.trade_cash.UnwindStockEqvNotional / td.OriginalStockEqvNotional; } else if ((td.StockEqvNotional / td.OriginalStockEqvNotional) <= td.trade_cash.UnwindPercentRate + 1e-10) { td.trade_cash.UnwindPercentRate = td.StockEqvNotional / td.OriginalStockEqvNotional; td.trade_cash.UnwindStockEqvNotional = td.StockEqvNotional; unwindType = "全部平仓"; } if (td.trade_cash.UnwindStockEqvNotional == 0) { if ((td.StockEqvNotional / td.OriginalStockEqvNotional) <= td.trade_cash.UnwindPercentRate + 1e-10) { td.trade_cash.UnwindPercentRate = td.StockEqvNotional / td.OriginalStockEqvNotional; unwindType = "全部平仓"; } td.trade_cash.UnwindStockEqvNotional = (double)(td.OriginalStockEqvNotional * td.trade_cash.UnwindPercentRate); } else if (td.StockEqvNotional <= td.trade_cash.UnwindStockEqvNotional + 1e-10) { td.trade_cash.UnwindPercentRate = td.StockEqvNotional / td.OriginalStockEqvNotional; td.trade_cash.UnwindStockEqvNotional = td.StockEqvNotional; unwindType = "全部平仓"; } //增加现金交割交易记录 var tc = new trade_cash(); tc.OptId = UserId; tc.OptName = UserName; tc.OptDate = OptDate; tc.ExceciseType = "现金"; tc.TradeType = td.BuySell; tc.CallPut = td.CallPut; tc.IsLastAction = true; tc.TradeId = td.id; tc.FinalPrice = td.FinalPrice; tc.Notional = td.Notional; tc.TradeAmount = td.TradeAmount; tc.UnwindType = unwindType; tc.UnwindNotional = td.trade_cash.UnwindPercentRate * td.OriginalNotional; tc.UnwindTradeAmount = tc.UnwindNotional / um.CountRatio; tc.UnwindPercentRate = td.trade_cash.UnwindPercentRate; tc.NotionalPercentRate = tc.UnwindPercentRate; if (td.trade_swap.IsPayFloatingProfit) { var tradeCashs = DbContext.trade_cash.Where(y => y.TradeId == td.id && y.Action == "系统操作-互换" && y.ValidState != "InValid" && !y.IsDeleted && y.ValueDate <= td.UnWindDate); var tradeCashIds = tradeCashs.Select(x => x.id); var tradeCash = tradeCashs.OrderByDescending(y => y.id).FirstOrDefault(); var cashSwaps = DbContext.trade_cash_swap.Where(x => x.TradeId == td.id && tradeCashIds.Contains(x.TradeCashId)).ToArray(); var tradeCashSwap = tradeCash != null ? cashSwaps.FirstOrDefault(x => x.TradeCashId == tradeCash.id) : null; //取最后一次手动收益; var lastManualCashSwap = cashSwaps.OrderByDescending(o => o.StartDate).FirstOrDefault(x => !x.IsAuto); var lastManualCash = lastManualCashSwap != null ? tradeCashs.FirstOrDefault(x => x.id == lastManualCashSwap.TradeCashId) : null; var initialAmountPayQuote = PayoffSwapCalcService.GetInitialAmountSwapPay(td, td.trade_swap, tradeCashSwap?.PayFinalPrice ?? td.SpotPrice ?? 0 , td.FinalPrice ?? 0, td.trade_cash.UnwindStockEqvNotional ?? 0, td.UnWindDate.Value, tradeCash?.ValueDate); DateTime endDate; var preSwapDate = PayoffSwapCalcService.GetSwapRateStartDate(td, td.trade_swap, td.UnWindDate.Value, tradeCash, lastManualCash, td.trade_swap.IsGetFloatingProfit, out endDate); var extraAmountGetQuote = annualFeeGet ?? PayoffSwapCalcService.GetExtraAmountBySwapRate(td.ClientId, td.TradeDate, td.trade_swap.GetSwapTimeAndRate, preSwapDate, endDate, td.trade_swap.AnnualDays ?? 0, td.trade_cash.UnwindStockEqvNotional ?? 0); var costFeeGetQuote = costFee ?? 0; var costTradePriceGetQuote = 0.0; if (!td.trade_swap.IsTradePriceWhenOpen) { costTradePriceGetQuote = PayoffSwapCalcService.GetCostFee(td, td, tc, true, true); } var quoteAmount = extraAmountGetQuote + costFeeGetQuote + costTradePriceGetQuote - initialAmountPayQuote; var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, td.UnWindDate.Value, seekPreday: true, currencyRateType: quoteAmount < 0 ? CurrencyRateType.Buy : CurrencyRateType.Sell); var initialAmountPay = (initialAmountPayQuote * currencyRate).FormatValue(2); var extraAmountGet = (extraAmountGetQuote * currencyRate).FormatValue(2); var costFeeGet = (costFeeGetQuote * currencyRate).FormatValue(2); var costTradePriceGet = (costTradePriceGetQuote * currencyRate).FormatValue(2); tc.CurrencyRate = currencyRate; tc.Action = ClientCashInCashOut.系统操作_平仓费; tc.IsLastAction = td.Notional <= 0 ? true : false; tc.Status = TradeCashStatusEnum.已执行; tc.ValueDate = td.UnWindDate.Value; tc.ValidState = "Valid"; tc.ExerciseWay = TradeCashExerciseWayEnum.提前终止行权; td.StockEqvNotional -= (double)(td.OriginalStockEqvNotional * tc.UnwindPercentRate); td.Notional -= (double)(td.OriginalNotional * tc.UnwindPercentRate); td.TradeAmount = td.Notional / um.CountRatio; td.UnWindNotional = tc.UnwindNotional; td.TradeStatus = tc.ValueDate == td.ExerciseDate ? "已到期" : (tc.UnwindType == "全部平仓" ? "已平仓" : td.TradeStatus); td.HasPartialUnWind = tc.UnwindType == "部分平仓" ? 1 : 0; var trade_cash_swap = new trade_cash_swap(); trade_cash_swap.StartDate = td.StartDate.Value; trade_cash_swap.PayStartPrice = td.trade_swap.PayFinalPrice ?? td.trade_swap.PaySpotPrice; trade_cash_swap.PayFinalPrice = tc.FinalPrice; trade_cash_swap.PayInitialAmount = initialAmountPay; trade_cash_swap.PaySwapRate = PayoffSwapCalcService.GetSwapRateByDate(td.trade_swap.PaySwapTimeAndRate, td.UnWindDate.Value); preSwapDate = PayoffSwapCalcService.GetSwapRateStartDate(td, td.trade_swap, td.UnWindDate.Value, tradeCash, lastManualCash, td.trade_swap.IsPayFloatingProfit, out endDate); var extraAmountPayQuote = annualFeePay ?? PayoffSwapCalcService.GetExtraAmountBySwapRate(td.ClientId, td.TradeDate, td.trade_swap.PaySwapTimeAndRate, preSwapDate, endDate, td.trade_swap.AnnualDays ?? 0, td.trade_cash.UnwindStockEqvNotional ?? 0); var extraAmountPay = (extraAmountPayQuote * currencyRate).FormatValue(2); trade_cash_swap.PayExtraAmount = extraAmountPay; trade_cash_swap.PayAmount = trade_cash_swap.PayInitialAmount + extraAmountPay; trade_cash_swap.GetExtraAmount = extraAmountGet; trade_cash_swap.GetCostFee = costFeeGet + costTradePriceGet; trade_cash_swap.GetAmount = (trade_cash_swap.GetExtraAmount ?? 0) + (trade_cash_swap.GetCostFee ?? 0); trade_cash_swap.GetSwapRate = PayoffSwapCalcService.GetSwapRateByDate(td.trade_swap.GetSwapTimeAndRate, td.UnWindDate.Value); tc.Amount = (trade_cash_swap.GetAmount ?? 0) - (trade_cash_swap.PayAmount ?? 0); tc.QuoteAmount = tc.Amount; DbContext.trade_cash.Add(tc); DbContext.SaveChanges(); trade_cash_swap.TradeId = tc.TradeId; trade_cash_swap.TradeCashId = tc.id; trade_cash_swap.OptId = tc.OptId; trade_cash_swap.OptName = tc.OptName; trade_cash_swap.OptDate = DateTime.Now; DbContext.trade_cash_swap.Add(trade_cash_swap); DbContext.SaveChanges(); var tcdGet = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = extraAmountGet - extraAmountPay, QuoteAmount = extraAmountGetQuote - extraAmountPayQuote, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.利息.ToString() }; DbContext.trade_cash_detail.Add(tcdGet); var tcdCostFeeGet = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = costFeeGet, QuoteAmount = costFeeGetQuote, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.了结手续费.ToString() }; DbContext.trade_cash_detail.Add(tcdCostFeeGet); var tcdCostTradePriceGet = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = costTradePriceGet, QuoteAmount = costTradePriceGetQuote, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.开仓手续费.ToString() }; DbContext.trade_cash_detail.Add(tcdCostTradePriceGet); var tcdPay = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = -initialAmountPay, QuoteAmount = -initialAmountPayQuote, ValueDate = tc.ValueDate, IsForGet = false, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.浮动收益.ToString() }; DbContext.trade_cash_detail.Add(tcdPay); } else { var tradeCashs = DbContext.trade_cash.Where(y => y.TradeId == td.id && y.Action == "系统操作-互换" && y.ValidState != "InValid" && !y.IsDeleted && y.ValueDate <= td.UnWindDate); var tradeCashIds = tradeCashs.Select(x => x.id); var tradeCash = tradeCashs.OrderByDescending(y => y.id).FirstOrDefault(); var cashSwaps = DbContext.trade_cash_swap.Where(x => x.TradeId == td.id && tradeCashIds.Contains(x.TradeCashId)).ToArray(); var tradeCashSwap = tradeCash != null ? cashSwaps.FirstOrDefault(x => x.TradeCashId == tradeCash.id) : null; //取最后一次手动收益; var lastManualCashSwap = cashSwaps.OrderByDescending(o => o.StartDate).FirstOrDefault(x => !x.IsAuto); var lastManualCash = lastManualCashSwap != null ? tradeCashs.FirstOrDefault(x => x.id == lastManualCashSwap.TradeCashId) : null; var initialAmountGetQuote = PayoffSwapCalcService.GetInitialAmountSwapGet(td, td.trade_swap, tradeCashSwap?.GetFinalPrice ?? td.SpotPrice ?? 0 , td.FinalPrice ?? 0, td.trade_cash.UnwindStockEqvNotional ?? 0, td.UnWindDate.Value, tradeCash?.ValueDate); DateTime endDate; var preSwapDate = PayoffSwapCalcService.GetSwapRateStartDate(td, td.trade_swap, td.UnWindDate.Value, tradeCash, lastManualCash, td.trade_swap.IsPayFloatingProfit, out endDate); var extraAmountPayQuote = annualFeePay ?? PayoffSwapCalcService.GetExtraAmountBySwapRate(td.ClientId, td.TradeDate, td.trade_swap.PaySwapTimeAndRate, preSwapDate, endDate, td.trade_swap.AnnualDays ?? 0, td.trade_cash.UnwindStockEqvNotional ?? 0); var costFeePayQuote = costFee ?? 0; var costTradePricePayQuote = 0.0; if (!td.trade_swap.IsTradePriceWhenOpen) { costTradePricePayQuote = PayoffSwapCalcService.GetCostFee(td, td, tc, false, true); } var quoteAmount = initialAmountGetQuote - extraAmountPayQuote - costFeePayQuote - costTradePricePayQuote; var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, td.UnWindDate.Value, seekPreday: true, currencyRateType: quoteAmount < 0 ? CurrencyRateType.Buy : CurrencyRateType.Sell); var initialAmountGet = (initialAmountGetQuote * currencyRate).FormatValue(2); var extraAmountPay = (extraAmountPayQuote * currencyRate).FormatValue(2); var costFeePay = (costFeePayQuote * currencyRate).FormatValue(2); var costTradePricePay = (costTradePricePayQuote * currencyRate).FormatValue(2); tc.CurrencyRate = currencyRate; tc.Action = ClientCashInCashOut.系统操作_平仓费; tc.IsLastAction = td.Notional <= 0 ? true : false; tc.Status = TradeCashStatusEnum.已执行; tc.ValueDate = td.UnWindDate.Value; tc.ValidState = "Valid"; tc.ExerciseWay = TradeCashExerciseWayEnum.提前终止行权; td.StockEqvNotional -= (double)(td.OriginalStockEqvNotional * tc.UnwindPercentRate); td.Notional -= (double)(td.OriginalNotional * tc.UnwindPercentRate); td.TradeAmount = td.Notional / um.CountRatio; td.UnWindNotional = tc.UnwindNotional; td.TradeStatus = tc.ValueDate == td.ExerciseDate ? "已到期" : (tc.UnwindType == "全部平仓" ? "已平仓" : td.TradeStatus); td.HasPartialUnWind = tc.UnwindType == "部分平仓" ? 1 : 0; var trade_cash_swap = new trade_cash_swap(); trade_cash_swap.StartDate = td.StartDate.Value; trade_cash_swap.GetStartPrice = td.trade_swap.GetFinalPrice ?? td.trade_swap.GetSpotPrice; trade_cash_swap.GetFinalPrice = tc.FinalPrice; trade_cash_swap.GetInitialAmount = initialAmountGet; trade_cash_swap.GetSwapRate = PayoffSwapCalcService.GetSwapRateByDate(td.trade_swap.GetSwapTimeAndRate, td.UnWindDate.Value); preSwapDate = PayoffSwapCalcService.GetSwapRateStartDate(td, td.trade_swap, td.UnWindDate.Value, tradeCash, lastManualCash, td.trade_swap.IsGetFloatingProfit, out endDate); var extraAmountGetQuote = annualFeeGet ?? PayoffSwapCalcService.GetExtraAmountBySwapRate(td.ClientId, td.TradeDate, td.trade_swap.GetSwapTimeAndRate, preSwapDate, endDate, td.trade_swap.AnnualDays ?? 0, td.trade_cash.UnwindStockEqvNotional ?? 0); var extraAmountGet = (extraAmountGetQuote * currencyRate).FormatValue(2); trade_cash_swap.GetExtraAmount = extraAmountGet; trade_cash_swap.GetAmount = initialAmountGet + extraAmountGet; trade_cash_swap.PayExtraAmount = extraAmountPay; trade_cash_swap.PayCostFee = costFeePay + costTradePricePay; trade_cash_swap.PayAmount = (trade_cash_swap.PayExtraAmount ?? 0) + (trade_cash_swap.PayCostFee ?? 0); trade_cash_swap.PaySwapRate = PayoffSwapCalcService.GetSwapRateByDate(td.trade_swap.PaySwapTimeAndRate, td.UnWindDate.Value); tc.Amount = (trade_cash_swap.GetAmount ?? 0) - (trade_cash_swap.PayAmount ?? 0); tc.QuoteAmount = tc.Amount; DbContext.trade_cash.Add(tc); DbContext.SaveChanges(); trade_cash_swap.TradeId = tc.TradeId; trade_cash_swap.TradeCashId = tc.id; trade_cash_swap.OptId = tc.OptId; trade_cash_swap.OptName = tc.OptName; trade_cash_swap.OptDate = DateTime.Now; DbContext.trade_cash_swap.Add(trade_cash_swap); DbContext.SaveChanges(); var tcdExtraAmount = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = extraAmountGet - extraAmountPay, QuoteAmount = extraAmountGetQuote - extraAmountPayQuote, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.利息.ToString() }; DbContext.trade_cash_detail.Add(tcdExtraAmount); var tcdCostFeeGet = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = -costFeePay, QuoteAmount = -costFeePayQuote, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.了结手续费.ToString() }; DbContext.trade_cash_detail.Add(tcdCostFeeGet); var tcdCostTradePriceGet = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = -costTradePricePay, QuoteAmount = -costTradePricePayQuote, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.开仓手续费.ToString() }; DbContext.trade_cash_detail.Add(tcdCostTradePriceGet); var tcdPay = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = initialAmountGet, QuoteAmount = initialAmountGetQuote, ValueDate = tc.ValueDate, IsForGet = false, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.浮动收益.ToString() }; DbContext.trade_cash_detail.Add(tcdPay); } //增加出入金记录 new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(td, tc, tc.ValueDate); var auditLog = new TradeAuditLog { TradeId = tc.TradeId, Changes = null, DataType = "00", OptType = "批量了结-平仓", OptId = UserId, OptName = UserName, OptDate = OptDate, AuditFlag = TradeAuditFlag.operation }; //记录审核日志 DbContext.TradeAuditLog.Add(auditLog); DbContext.SaveChanges(); } private void UnwindSwapTradeCashHandle(trade td, string action, double? annualFee, double? costFee) { var client = DataCacheProvider.GetClientDataSource().GetData(td.ClientId); var um = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode); //增加现金交割交易记录 var tc = new trade_cash(); DbContext.trade_cash.Add(tc); tc.OptId = UserId; tc.OptName = UserName; tc.OptDate = OptDate; tc.ExceciseType = "现金"; tc.TradeType = td.BuySell; tc.CallPut = td.CallPut; tc.Notional = td.Notional; tc.TradeAmount = td.TradeAmount; tc.IsLastAction = true; tc.TradeId = td.id; tc.FinalPrice = td.FinalPrice; tc.UnwindType = "全部平仓"; tc.UnwindNotional = td.Notional; tc.UnwindTradeAmount = td.TradeAmount; tc.UnwindPercentRate = 1; tc.NotionalPercentRate = tc.UnwindPercentRate; if (td.trade_swap.IsPayFloatingProfit) { var tradeCashs = DbContext.trade_cash.Where(y => y.TradeId == td.id && y.Action == "系统操作-互换" && y.ValidState != "InValid" && !y.IsDeleted && y.ValueDate <= td.UnWindDate); var tradeCashIds = tradeCashs.Select(x => x.id); var tradeCash = tradeCashs.OrderByDescending(y => y.id).FirstOrDefault(); var cashSwaps = DbContext.trade_cash_swap.Where(x => x.TradeId == td.id && tradeCashIds.Contains(x.TradeCashId)).ToArray(); var tradeCashSwap = tradeCash != null ? cashSwaps.FirstOrDefault(x => x.TradeCashId == tradeCash.id) : null; //取最后一次手动收益; var lastManualCashSwap = cashSwaps.OrderByDescending(o => o.StartDate).FirstOrDefault(x => !x.IsAuto); var lastManualCash = lastManualCashSwap != null ? tradeCashs.FirstOrDefault(x => x.id == lastManualCashSwap.TradeCashId) : null; var initialAmountPayQuote = PayoffSwapCalcService.GetInitialAmountSwapPay(td, td.trade_swap, tradeCashSwap?.PayFinalPrice ?? td.SpotPrice ?? 0 , td.FinalPrice ?? 0, (td.OriginalStockEqvNotional ?? 0) * (tc.UnwindPercentRate ?? 0), td.UnWindDate.Value, tradeCash?.ValueDate); DateTime endDate; var preSwapDate = PayoffSwapCalcService.GetSwapRateStartDate(td, td.trade_swap, td.UnWindDate.Value, tradeCash, lastManualCash, td.trade_swap.IsGetFloatingProfit, out endDate); var extraAmountGetQuote = annualFee ?? PayoffSwapCalcService.GetExtraAmountBySwapRate(td.ClientId, td.TradeDate, td.trade_swap.GetSwapTimeAndRate, preSwapDate, endDate, td.trade_swap.AnnualDays ?? 0, (td.OriginalStockEqvNotional ?? 0) * (tc.UnwindPercentRate ?? 0)); preSwapDate = PayoffSwapCalcService.GetSwapRateStartDate(td, td.trade_swap, td.UnWindDate.Value, tradeCash, lastManualCash, td.trade_swap.IsPayFloatingProfit, out endDate); var extraAmountPayQuote = annualFee ?? PayoffSwapCalcService.GetExtraAmountBySwapRate(td.ClientId, td.TradeDate, td.trade_swap.PaySwapTimeAndRate, preSwapDate, endDate, td.trade_swap.AnnualDays ?? 0, (td.OriginalStockEqvNotional ?? 0) * (tc.UnwindPercentRate ?? 0)); var costFeeGetQuote = costFee ?? 0; var costTradePriceGetQuote = 0.0; if (!td.trade_swap.IsTradePriceWhenOpen) { costTradePriceGetQuote = PayoffSwapCalcService.GetCostFee(td, td, tc, true, true); } var quoteAmount = extraAmountGetQuote + costFeeGetQuote + costTradePriceGetQuote - initialAmountPayQuote - extraAmountPayQuote; var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, td.UnWindDate.Value, seekPreday: true, currencyRateType: quoteAmount < 0 ? CurrencyRateType.Buy : CurrencyRateType.Sell); var extraAmountPay = (extraAmountPayQuote * currencyRate).FormatValue(2); var initialAmountPay = (initialAmountPayQuote * currencyRate).FormatValue(2); var extraAmountGet = (extraAmountGetQuote * currencyRate).FormatValue(2); var costFeeGet = (costFeeGetQuote * currencyRate).FormatValue(2); var costTradePriceGet = (costTradePriceGetQuote * currencyRate).FormatValue(2); td.TradeStatus = td.UnWindDate.Value == td.ExerciseDate ? "已到期" : "已平仓"; td.StockEqvNotional = 0; td.Notional = 0; td.TradeAmount = 0; td.UnWindNotional = tc.UnwindNotional; tc.Amount = extraAmountGet + costFeeGet + costTradePriceGet - initialAmountPay - extraAmountPay; tc.QuoteAmount = quoteAmount; tc.CurrencyRate = currencyRate; tc.Action = action == "平仓" ? ClientCashInCashOut.系统操作_平仓费 : ClientCashInCashOut.系统操作_互换; tc.IsLastAction = true; tc.Status = TradeCashStatusEnum.已执行; tc.ValueDate = td.UnWindDate.Value; tc.ValidState = "Valid"; tc.ExerciseWay = td.UnWindDate == td.ExerciseDate ? TradeCashExerciseWayEnum.到期行权 : TradeCashExerciseWayEnum.提前终止行权; DbContext.SaveChanges(); //增加出入金记录 new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(td, tc, tc.ValueDate); var trade_swap = DbContext.trade_swap.FirstOrDefault(x => x.TradeId == tc.TradeId); var trade_cash_swap = new trade_cash_swap(); trade_cash_swap.StartDate = td.StartDate.Value; trade_cash_swap.PayStartPrice = trade_swap.PayFinalPrice ?? trade_swap.PaySpotPrice; trade_cash_swap.PayFinalPrice = tc.FinalPrice; trade_cash_swap.PayExtraAmount = extraAmountPay; trade_cash_swap.PayInitialAmount = initialAmountPay; trade_cash_swap.PayAmount = extraAmountPay + initialAmountPay; trade_cash_swap.PaySwapRate = PayoffSwapCalcService.GetSwapRateByDate(td.trade_swap.PaySwapTimeAndRate, td.UnWindDate.Value); trade_cash_swap.GetExtraAmount = extraAmountGet; trade_cash_swap.GetCostFee = costFeeGet + costTradePriceGet; trade_cash_swap.GetAmount = extraAmountGet + costFeeGet + costTradePriceGet; trade_cash_swap.GetSwapRate = PayoffSwapCalcService.GetSwapRateByDate(td.trade_swap.GetSwapTimeAndRate, td.UnWindDate.Value); trade_cash_swap.TradeId = tc.TradeId; trade_cash_swap.TradeCashId = tc.id; trade_cash_swap.OptId = tc.OptId; trade_cash_swap.OptName = tc.OptName; trade_cash_swap.OptDate = DateTime.Now; DbContext.trade_cash_swap.Add(trade_cash_swap); var tcdExtraAmount = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = extraAmountGet - extraAmountPay, QuoteAmount = extraAmountGetQuote - extraAmountPayQuote, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.利息.ToString() }; DbContext.trade_cash_detail.Add(tcdExtraAmount); var tcdCostFeeGet = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = costFeeGet, QuoteAmount = costFeeGetQuote, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.了结手续费.ToString() }; DbContext.trade_cash_detail.Add(tcdCostFeeGet); var tcdCostTradePriceGet = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = costTradePriceGet, QuoteAmount = costTradePriceGetQuote, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.开仓手续费.ToString() }; DbContext.trade_cash_detail.Add(tcdCostTradePriceGet); var tcdInitialAmountPay = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = -initialAmountPay, QuoteAmount = -initialAmountPayQuote, ValueDate = tc.ValueDate, IsForGet = false, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.浮动收益.ToString() }; DbContext.trade_cash_detail.Add(tcdInitialAmountPay); } else { var tradeCashs = DbContext.trade_cash.Where(y => y.TradeId == td.id && y.Action == "系统操作-互换" && y.ValidState != "InValid" && !y.IsDeleted && y.ValueDate <= td.UnWindDate); var tradeCashIds = tradeCashs.Select(x => x.id); var tradeCash = tradeCashs.OrderByDescending(y => y.id).FirstOrDefault(); var cashSwaps = DbContext.trade_cash_swap.Where(x => x.TradeId == td.id && tradeCashIds.Contains(x.TradeCashId)).ToArray(); var tradeCashSwap = tradeCash != null ? cashSwaps.FirstOrDefault(x => x.TradeCashId == tradeCash.id) : null; //取最后一次手动收益; var lastManualCashSwap = cashSwaps.OrderByDescending(o => o.StartDate).FirstOrDefault(x => !x.IsAuto); var lastManualCash = lastManualCashSwap != null ? tradeCashs.FirstOrDefault(x => x.id == lastManualCashSwap.TradeCashId) : null; var initialAmountGetQuote = PayoffSwapCalcService.GetInitialAmountSwapGet(td, td.trade_swap, tradeCashSwap?.GetFinalPrice ?? td.SpotPrice ?? 0 , td.FinalPrice ?? 0, td.OriginalStockEqvNotional ?? 0, td.UnWindDate.Value, tradeCash?.ValueDate); DateTime endDate; var preSwapDate = PayoffSwapCalcService.GetSwapRateStartDate(td, td.trade_swap, td.UnWindDate.Value, tradeCash, lastManualCash, td.trade_swap.IsPayFloatingProfit, out endDate); var extraAmountPayQuote = annualFee ?? PayoffSwapCalcService.GetExtraAmountBySwapRate(td.ClientId, td.TradeDate, td.trade_swap.PaySwapTimeAndRate, preSwapDate, endDate, td.trade_swap.AnnualDays ?? 0, (td.OriginalStockEqvNotional ?? 0) * (tc.UnwindPercentRate ?? 0)); preSwapDate = PayoffSwapCalcService.GetSwapRateStartDate(td, td.trade_swap, td.UnWindDate.Value, tradeCash, lastManualCash, td.trade_swap.IsGetFloatingProfit, out endDate); var extraAmountGetQuote = annualFee ?? PayoffSwapCalcService.GetExtraAmountBySwapRate(td.ClientId, td.TradeDate, td.trade_swap.GetSwapTimeAndRate, preSwapDate, endDate, td.trade_swap.AnnualDays ?? 0, (td.OriginalStockEqvNotional ?? 0) * (tc.UnwindPercentRate ?? 0)); var costFeePayQuote = costFee ?? 0; var costTradePricePayQuote = 0.0; if (!td.trade_swap.IsTradePriceWhenOpen) { costTradePricePayQuote = PayoffSwapCalcService.GetCostFee(td, td, tc, false, true); } var quoteAmount = initialAmountGetQuote + extraAmountGetQuote - extraAmountPayQuote - costFeePayQuote - costTradePricePayQuote; var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, td.UnWindDate.Value, seekPreday: true, currencyRateType: quoteAmount < 0 ? CurrencyRateType.Buy : CurrencyRateType.Sell); var extraAmountGet = (extraAmountGetQuote * currencyRate).FormatValue(2); var initialAmountGet = (initialAmountGetQuote * currencyRate).FormatValue(2); var extraAmountPay = (extraAmountPayQuote * currencyRate).FormatValue(2); var costFeePay = (costFeePayQuote * currencyRate).FormatValue(2); var costTradePricePay = (costTradePricePayQuote * currencyRate).FormatValue(2); tc.Amount = initialAmountGet + extraAmountGet - extraAmountPay - costFeePay - costTradePricePay; tc.QuoteAmount = quoteAmount; tc.CurrencyRate = currencyRate; tc.Action = action == "平仓" ? ClientCashInCashOut.系统操作_平仓费 : ClientCashInCashOut.系统操作_互换; tc.IsLastAction = true; tc.Status = TradeCashStatusEnum.已执行; tc.ValueDate = td.UnWindDate.Value; tc.ValidState = "Valid"; tc.ExerciseWay = td.UnWindDate == td.ExerciseDate ? TradeCashExerciseWayEnum.到期行权 : TradeCashExerciseWayEnum.提前终止行权; DbContext.SaveChanges(); td.TradeStatus = tc.ValueDate == td.ExerciseDate ? "已到期" : "已平仓"; td.StockEqvNotional = 0; td.Notional = 0; td.TradeAmount = 0; td.UnWindNotional = tc.UnwindNotional; //增加出入金记录 new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(td, tc, tc.ValueDate); var trade_swap = DbContext.trade_swap.FirstOrDefault(x => x.TradeId == tc.TradeId); var trade_cash_swap = new trade_cash_swap(); trade_cash_swap.StartDate = td.StartDate.Value; trade_cash_swap.GetStartPrice = trade_swap.GetFinalPrice ?? trade_swap.GetSpotPrice; trade_cash_swap.GetFinalPrice = tc.FinalPrice; trade_cash_swap.GetInitialAmount = initialAmountGet; trade_cash_swap.GetExtraAmount = extraAmountGet; trade_cash_swap.GetAmount = initialAmountGet + extraAmountGet; trade_cash_swap.GetSwapRate = PayoffSwapCalcService.GetSwapRateByDate(td.trade_swap.GetSwapTimeAndRate, td.UnWindDate.Value); trade_cash_swap.PayExtraAmount = extraAmountPay; trade_cash_swap.PayCostFee = costFeePay + costTradePricePay; trade_cash_swap.PayAmount = extraAmountPay + costFeePay + costTradePricePay; trade_cash_swap.PaySwapRate = PayoffSwapCalcService.GetSwapRateByDate(td.trade_swap.PaySwapTimeAndRate, td.UnWindDate.Value); trade_cash_swap.TradeId = tc.TradeId; trade_cash_swap.TradeCashId = tc.id; trade_cash_swap.OptId = tc.OptId; trade_cash_swap.OptName = tc.OptName; trade_cash_swap.OptDate = DateTime.Now; DbContext.trade_cash_swap.Add(trade_cash_swap); var tcdExtraAmount = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = extraAmountGet - extraAmountPay, QuoteAmount = extraAmountGetQuote - extraAmountPayQuote, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.利息.ToString() }; DbContext.trade_cash_detail.Add(tcdExtraAmount); var tcdCostFeePay = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = -costFeePay, QuoteAmount = -costFeePayQuote, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.了结手续费.ToString() }; DbContext.trade_cash_detail.Add(tcdCostFeePay); var tcdCostTradePricePay = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = -costTradePricePay, QuoteAmount = -costTradePricePayQuote, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.开仓手续费.ToString() }; DbContext.trade_cash_detail.Add(tcdCostTradePricePay); var tcdInitialAmountGet = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = initialAmountGet, QuoteAmount = initialAmountGetQuote, ValueDate = tc.ValueDate, IsForGet = false, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.浮动收益.ToString() }; DbContext.trade_cash_detail.Add(tcdInitialAmountGet); } DbContext.SaveChanges(); } #endregion #region 互换交易历史导入 /// /// 导入交易 /// /// /// 当前文件中的目标期权总条数 /// 成功入库的数量 public void ImportSwapTradeHistroyDataFromExcel(Stream streamIn, out int totalNum, out int successNum) { totalNum = 0; successNum = 0; var rowIndex = 0; var groupTradeList = new List(); var structureTradeList = new List(); try { var ds = Office.ExcelHelper.ReadExcelAsDataSet(streamIn, new[] { 0 }, 0); if (ds.Tables.Count < 1 || ds.Tables[0].Rows.Count < 3) { throw new ServiceException("读取导入数据失败:数据为空") { Tag = "111" }; } var table = ds.Tables[0]; var reader = new DataRowTopTypeReader(table); rowIndex = 2; totalNum = table.Rows.Count - rowIndex; foreach (var row in table.Rows.Cast().Skip(2)) { using (var trans = BeginTransaction()) { rowIndex++; if (row.ItemArray.All(n => string.IsNullOrWhiteSpace(n?.ToString()))) { totalNum--; continue; } reader.SetDataRow(row); //映射导入数据到交易对象 HandleSwapTradeHistroyData(reader); successNum++; trans.Commit(); } } } catch (ServiceException se) { if (se.Tag != null) { throw; } throw new ServiceException($"已成功导入{successNum}条;\n第{rowIndex}行,{se.Message}"); } catch (Exception ex) { LogFactory.GetLogger("导入期权交易").Error(ex); throw new ServiceException($"已成功导入{successNum}条,\n第{rowIndex}行,发生错误:{ex.Message}", ex); } } private trade HandleSwapTradeHistroyData(DataRowTopTypeReader reader) { var td = new trade(); td.trade_swap = new trade_swap(); //基本要素 td.TradeNumber = reader.GetString("交易编号", false); td.AssetBookName = reader.GetString("簿记账户名称", true); td.TraderName = reader.GetString("交易员名称", true); new OtcOptionSaveChecker(this).CheckAssetBook(td).CheckTrader(td); td.TradeType = "收益互换"; td.ClientName = reader.GetString("交易对手方名称", true); if (string.IsNullOrWhiteSpace(td.ClientName)) { throw new ServiceException("交易对手方名称 必须填写"); } //交易日期 td.TradeDate = reader.GetDate("成交日期", true); td.StartDate = reader.GetDate("开始日期", true); td.ExerciseDate = reader.GetDate("到期日期", true); td.SettlementDate = td.ExerciseDate; td.StockEqvNotional = reader.GetDouble("名义本金", true) ?? 0; td.trade_swap.AnnualDays = reader.GetInt("年化天数", true); td.trade_swap.AnnualVarIncome = reader.GetString("浮动收益年化", true) == "是"; td.trade_swap.RateCalcMode = GetRateCalcMode(reader.GetString("计息方式", true)); td.trade_swap.IsTradePriceWhenOpen = reader.GetString("是否开仓时收取开仓费", true) == "是"; td.trade_swap.IsShare = reader.GetString("收费基本单位", true) == "手数"; var ExchangeRate = reader.GetDouble("汇率", false); td.Comments = reader.GetString("备注", false); var clientQuery = DbContextFactory.GetClientDbContext(OptUser).client.AsQueryable(); if (!string.IsNullOrWhiteSpace(td.ClientName)) { clientQuery = clientQuery.Where(c => c.Name == td.ClientName); } var client = clientQuery.Select(n => new { n.id, n.SettlementCurrency }).FirstOrDefault(); if (client == null) { throw new ServiceException($"交易对手方不存在,客户名称:{td.ClientName}"); } td.ClientId = client.id; td.SettlementCurrency = client.SettlementCurrency; td.OpponentRole = "乙方"; var um = new underlying_manager(); var singleFee = 0.0; #region 交易员收取信息读取 reader.SetTopType("交易员收取"); td.trade_swap.GetLongShort = reader.GetString("多头空头", false); if (!string.IsNullOrWhiteSpace(td.trade_swap.GetLongShort)) { td.trade_swap.IsGetFloatingProfit = true; td.trade_swap.GetUnderlyingCode = reader.GetString("标的代码", true); um = DataCacheProvider.GetUnderlyingDataSource().GetData(td.trade_swap.GetUnderlyingCode); if (um == null) { throw new ServiceException($"{td.trade_swap.GetUnderlyingCode} 不存在,请先新建标的再导入"); } td.trade_swap.GetUnderlyingId = um.id; td.UnderlyingId = um.id; td.UnderlyingCode = um.UnderlyingCode; td.UnderlyingName = um.UnderlyingName; td.UnderlyingInstrumentType = um.UnderlyingInstrumentType; if (!td.trade_swap.IsShare) { td.trade_swap.GetNotional = reader.GetDouble("份额/手数", true); td.trade_swap.GetTradeAmount = td.trade_swap.GetNotional / um.CountRatio; td.trade_swap.GetLot = td.trade_swap.GetNotional / um.ContractSize; } else { td.trade_swap.GetLot = reader.GetDouble("份额/手数", true); td.trade_swap.GetNotional = td.trade_swap.GetLot * um.ContractSize; td.trade_swap.GetTradeAmount = td.trade_swap.GetNotional / um.CountRatio; } td.Notional = td.trade_swap.GetNotional ?? 0; td.TradeAmount = td.trade_swap.GetTradeAmount ?? 0; td.Lots = td.trade_swap.GetLot; //判断是否组合标的 var synthetic = DataCacheProvider.GetUnderlyingDataSource().GetSyntheticUnderlying(um.UnderlyingCode); //读取组合标的 if (synthetic != null) { reader.SetTopType("组合标的"); var sulist = new List(4); var model = synthetic.GetSyntheticPriceModel(); var codeSet = model.SuList.Select(n => n.UnderlyingCode).ToHashSet(StringComparer.OrdinalIgnoreCase); for (var i = 1; i <= 4; i++) { var code = reader.GetString("标的" + i + "_代码"); if (string.IsNullOrWhiteSpace(code)) { continue; } if (!codeSet.Remove(code)) { throw new ServiceException($"[组合标的]标的{i}_代码 填写错误,组合标的中不存在此标的:{code}"); } model.SuList.First(n => n.UnderlyingCode.Equals(code, StringComparison.OrdinalIgnoreCase)).Price = reader.GetDouble("标的" + i + "_价格", true).Value; } if (codeSet.Any()) { throw new ServiceException("[组合标的]未填写完整"); } td.trade_swap.GetSpotPrice = model.Price = model.SuList.Sum(n => n.Coefficient * n.Price) + model.Constant; td.MetaDic["组合标的"] = JsonHelper.ToJson(model); } else { td.trade_swap.GetSpotPrice = reader.GetDouble("期初标的价格", true); } td.SpotPrice = td.trade_swap.GetSpotPrice; } else { var str = reader.GetString("单位交易费用", true); if (!string.IsNullOrWhiteSpace(str)) { var percent = str.EndsWith("%"); if (percent) { var strTrim = str.TrimEnd('%'); if (double.TryParse(strTrim, out var num)) { td.trade_swap.GetUnAnnualRate = num / 100; } else { throw new ServiceException($"[交易员收取]单位交易费用 填写错误:{str}"); } } else { if (double.TryParse(str, out var num)) { singleFee = num; } else { throw new ServiceException($"[交易员收取]单位交易费用 填写错误:{str}"); } } } } td.trade_swap.GetSwapRate = reader.GetPercent("互换利率(年化)", false) ?? 0; if (td.trade_swap.GetSwapRate >= 0) { td.trade_swap.GetSwapTimeAndRate = td.ExerciseDate + ";" + td.trade_swap.GetSwapRate; } td.trade_swap.GetMarginRate = reader.GetPercent("初始预付金率", false) ?? 0; #endregion #region 交易员支付信息读取 reader.SetTopType("交易员支付"); td.trade_swap.PayLongShort = reader.GetString("多头空头", false); if (!string.IsNullOrWhiteSpace(td.trade_swap.PayLongShort)) { td.trade_swap.IsPayFloatingProfit = true; td.trade_swap.PayUnderlyingCode = reader.GetString("标的代码", true); um = DataCacheProvider.GetUnderlyingDataSource().GetData(td.trade_swap.PayUnderlyingCode); if (um == null) { throw new ServiceException($"{td.trade_swap.PayUnderlyingCode} 不存在,请先新建标的再导入"); } td.trade_swap.PayUnderlyingId = um.id; td.UnderlyingId = um.id; td.UnderlyingCode = um.UnderlyingCode; td.UnderlyingName = um.UnderlyingName; td.UnderlyingInstrumentType = um.UnderlyingInstrumentType; if (!td.trade_swap.IsShare) { td.trade_swap.PayNotional = reader.GetDouble("份额/手数", true); td.trade_swap.PayTradeAmount = td.trade_swap.PayNotional / um.CountRatio; td.trade_swap.PayLot = td.trade_swap.PayNotional / um.ContractSize; } else { td.trade_swap.PayLot = reader.GetDouble("份额/手数", true); td.trade_swap.PayNotional = td.trade_swap.PayLot * um.ContractSize; td.trade_swap.PayTradeAmount = td.trade_swap.PayNotional / um.CountRatio; } td.Notional = td.trade_swap.PayNotional ?? 0; td.TradeAmount = td.trade_swap.PayTradeAmount ?? 0; td.Lots = td.trade_swap.PayLot; //判断是否组合标的 var synthetic = DataCacheProvider.GetUnderlyingDataSource().GetSyntheticUnderlying(um.UnderlyingCode); //读取组合标的 if (synthetic != null) { reader.SetTopType("组合标的"); var sulist = new List(4); var model = synthetic.GetSyntheticPriceModel(); var codeSet = model.SuList.Select(n => n.UnderlyingCode).ToHashSet(StringComparer.OrdinalIgnoreCase); for (var i = 1; i <= 4; i++) { var code = reader.GetString("标的" + i + "_代码"); if (string.IsNullOrWhiteSpace(code)) { continue; } if (!codeSet.Remove(code)) { throw new ServiceException($"[组合标的]标的{i}_代码 填写错误,组合标的中不存在此标的:{code}"); } model.SuList.First(n => n.UnderlyingCode.Equals(code, StringComparison.OrdinalIgnoreCase)).Price = reader.GetDouble("标的" + i + "_价格", true).Value; } if (codeSet.Any()) { throw new ServiceException("[组合标的]未填写完整"); } td.trade_swap.PaySpotPrice = model.Price = model.SuList.Sum(n => n.Coefficient * n.Price) + model.Constant; td.MetaDic["组合标的2"] = JsonHelper.ToJson(model); } else { td.trade_swap.PaySpotPrice = reader.GetDouble("期初标的价格", true); } td.SpotPrice = td.trade_swap.PaySpotPrice; } else { var str = reader.GetString("单位交易费用", true); if (!string.IsNullOrWhiteSpace(str)) { var percent = str.EndsWith("%"); if (percent) { var strTrim = str.TrimEnd('%'); if (double.TryParse(strTrim, out var num)) { td.trade_swap.PayUnAnnualRate = num / 100; } else { throw new ServiceException($"[交易员支付]单位交易费用 填写错误:{str}"); } } else { if (double.TryParse(str, out var num)) { singleFee = num; } else { throw new ServiceException($"[交易员支付]单位交易费用 填写错误:{str}"); } } } } td.trade_swap.PaySwapRate = reader.GetPercent("互换利率(年化)", false) ?? 0; if (td.trade_swap.PaySwapRate >= 0) { td.trade_swap.PaySwapTimeAndRate = td.ExerciseDate + ";" + td.trade_swap.PaySwapRate; } td.trade_swap.PayMarginRate = reader.GetPercent("初始预付金率", false) ?? 0; #endregion if ((string.IsNullOrWhiteSpace(td.trade_swap.GetLongShort) && string.IsNullOrWhiteSpace(td.trade_swap.PayLongShort))||(!string.IsNullOrWhiteSpace(td.trade_swap.GetLongShort) && !string.IsNullOrWhiteSpace(td.trade_swap.PayLongShort))) { throw new ServiceException($"注意“交易员收取”和“交易员支付”不可同时为浮动收益或利息收益!"); } var underlyingModel = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode); td.QuoteCurrency = DataCacheProvider.GetVarietyDataSource().GetData(underlyingModel.UnderlyingTypeId).QuoteCurrency; if (ExchangeRate != null) { if (td.QuoteCurrency != "CNY" && (!string.IsNullOrEmpty(td.QuoteCurrency))) { td.MetaDic.Add("ExchangeRate", ExchangeRate.ToString()); } } if (td.trade_swap.IsGetFloatingProfit) { if (!td.trade_swap.IsShare) { td.trade_swap.PaySingleFee = singleFee * um.ContractSize; } else { td.trade_swap.PaySingleFee = singleFee; } } else { if (!td.trade_swap.IsShare) { td.trade_swap.GetSingleFee = singleFee * um.ContractSize; } else { td.trade_swap.GetSingleFee = singleFee; } } var variety = DataCacheProvider.GetVariety(td.UnderlyingCode); if (variety == null) { throw new ServiceException($"该标的[{td.UnderlyingCode}]对应的品种在系统中不存在"); } else { if (string.IsNullOrWhiteSpace(variety.QuoteCurrency) && DbContext.currency.Any()) { throw new ServiceException($"标的代码[{td.UnderlyingCode}]对应的品种币种不能为空"); } else { td.QuoteCurrency = variety.QuoteCurrency; } var market = DataCacheProvider.GetMarketDataSource().AsQueryable().FirstOrDefault(x => x.MarketName == variety.TradingMarket); if (QdpCalendarHelper.IsHoliday((DateTime)td.TradeDate, string.IsNullOrWhiteSpace(market?.CalendarName) ? "chn" : market?.CalendarName)) { throw new ServiceException("成交日期:" + td.TradeDate + ",不能为节假日"); } if (QdpCalendarHelper.IsHoliday((DateTime)td.ExerciseDate, string.IsNullOrWhiteSpace(market?.CalendarName) ? "chn" : market?.CalendarName)) { throw new ServiceException("到期日期:" + td.ExerciseDate + ",不能为节假日"); } if (td.TradeDate > td.ExerciseDate) { throw new ServiceException("到期日期不能早于成交日期"); } } InnerSaveSwapTrade(td); #region 了结信息 reader.SetTopType("了结信息"); var action = reader.GetString("了结方式", false); if (!string.IsNullOrWhiteSpace(action)) { td.UnWindDate = reader.GetDate("了结日期", true); if (action == "互换" && td.UnWindDate != td.ExerciseDate) { throw new ServiceException($"只支持到期日互换,了结日期[{td.UnWindDate}]和到日期[{td.ExerciseDate}]不一致"); } td.FinalPrice = reader.GetDouble("了结标的价格", false); var eodpriceProvider = new YLErp.Modules.DataProviderModule.EodPriceProvider((DateTime)td.UnWindDate); if (td.FinalPrice == null) { td.FinalPrice = eodpriceProvider.GetPrice(td.UnderlyingCode, SettlementTypeEnum.ClosePrice);//平仓当天标的价格; } var toEndAmount = reader.GetDouble("了结总额", false); UnwindSwapTradeCashHandle(td, action, toEndAmount); } #endregion //记录审核日志 DbContext.TradeAuditLog.Add(new TradeAuditLog { TradeId = td.id, Changes = null, DataType = "00", OptId = UserId, OptName = UserName, OptDate = OptDate, OptType = "导入交易", AuditFlag = TradeAuditFlag.operation }); DbContext.SaveChanges(); return td; } private void UnwindSwapTradeCashHandle(trade td, string action, double? toEndAmount) { var client = DataCacheProvider.GetClientDataSource().GetData(td.ClientId); var um = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode); //增加现金交割交易记录 var tc = new trade_cash(); DbContext.trade_cash.Add(tc); tc.OptId = UserId; tc.OptName = UserName; tc.OptDate = OptDate; tc.ExceciseType = "现金"; tc.TradeType = td.BuySell; tc.CallPut = td.CallPut; tc.Notional = td.Notional; tc.TradeAmount = td.TradeAmount; tc.IsLastAction = true; tc.TradeId = td.id; tc.FinalPrice = td.FinalPrice; tc.UnwindType = "全部平仓"; tc.UnwindNotional = td.Notional; tc.UnwindTradeAmount = td.TradeAmount; tc.UnwindPercentRate = 1; tc.NotionalPercentRate = tc.UnwindPercentRate; var Amount = toEndAmount ?? 0; if (td.trade_swap.IsPayFloatingProfit) { var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, td.UnWindDate.Value, seekPreday: true, currencyRateType: Amount < 0 ? CurrencyRateType.Buy : CurrencyRateType.Sell); td.TradeStatus = td.UnWindDate.Value == td.ExerciseDate ? "已到期" : "已平仓"; td.StockEqvNotional = 0; td.Notional = 0; td.TradeAmount = 0; td.UnWindNotional = tc.UnwindNotional; tc.Amount = Amount; tc.QuoteAmount = Amount / currencyRate; tc.CurrencyRate = currencyRate; tc.Action = action == "平仓" ? ClientCashInCashOut.系统操作_平仓费 : ClientCashInCashOut.系统操作_互换; tc.IsLastAction = true; tc.Status = TradeCashStatusEnum.已执行; tc.ValueDate = td.UnWindDate.Value; tc.ValidState = "Valid"; tc.ExerciseWay = td.UnWindDate == td.ExerciseDate ? TradeCashExerciseWayEnum.到期行权 : TradeCashExerciseWayEnum.提前终止行权; DbContext.SaveChanges(); //增加出入金记录 new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(td, tc, tc.ValueDate); var trade_swap = DbContext.trade_swap.FirstOrDefault(x => x.TradeId == tc.TradeId); var trade_cash_swap = new trade_cash_swap(); trade_cash_swap.StartDate = td.StartDate.Value; trade_cash_swap.PayStartPrice = trade_swap.PayFinalPrice ?? trade_swap.PaySpotPrice; trade_cash_swap.PayFinalPrice = tc.FinalPrice; trade_cash_swap.PayInitialAmount = -Amount; trade_cash_swap.PayAmount = -Amount; trade_cash_swap.PaySwapRate = PayoffSwapCalcService.GetSwapRateByDate(td.trade_swap.PaySwapTimeAndRate, td.UnWindDate.Value); trade_cash_swap.GetExtraAmount = 0; trade_cash_swap.GetCostFee = 0; trade_cash_swap.GetAmount = 0; trade_cash_swap.GetSwapRate = PayoffSwapCalcService.GetSwapRateByDate(td.trade_swap.GetSwapTimeAndRate, td.UnWindDate.Value); trade_cash_swap.TradeId = tc.TradeId; trade_cash_swap.TradeCashId = tc.id; trade_cash_swap.OptId = tc.OptId; trade_cash_swap.OptName = tc.OptName; trade_cash_swap.OptDate = DateTime.Now; DbContext.trade_cash_swap.Add(trade_cash_swap); var tcdGet = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = 0, QuoteAmount = 0, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.利息.ToString() }; DbContext.trade_cash_detail.Add(tcdGet); var tcdCostFeeGet = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = 0, QuoteAmount = 0, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.了结手续费.ToString() }; DbContext.trade_cash_detail.Add(tcdCostFeeGet); var tcdCostTradePriceGet = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = 0, QuoteAmount = 0, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.开仓手续费.ToString() }; DbContext.trade_cash_detail.Add(tcdCostTradePriceGet); var tcdPay = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = Amount, QuoteAmount = Amount, ValueDate = tc.ValueDate, IsForGet = false, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.浮动收益.ToString() }; DbContext.trade_cash_detail.Add(tcdPay); } else { var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, td.UnWindDate.Value, seekPreday: true, currencyRateType: Amount < 0 ? CurrencyRateType.Buy : CurrencyRateType.Sell); tc.Amount = Amount; tc.QuoteAmount = Amount / currencyRate; tc.CurrencyRate = currencyRate; tc.Action = action == "平仓" ? ClientCashInCashOut.系统操作_平仓费 : ClientCashInCashOut.系统操作_互换; tc.IsLastAction = true; tc.Status = TradeCashStatusEnum.已执行; tc.ValueDate = td.UnWindDate.Value; tc.ValidState = "Valid"; tc.ExerciseWay = td.UnWindDate == td.ExerciseDate ? TradeCashExerciseWayEnum.到期行权 : TradeCashExerciseWayEnum.提前终止行权; DbContext.SaveChanges(); td.TradeStatus = tc.ValueDate == td.ExerciseDate ? "已到期" : "已平仓"; td.StockEqvNotional = 0; td.Notional = 0; td.TradeAmount = 0; td.UnWindNotional = tc.UnwindNotional; //增加出入金记录 new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(td, tc, tc.ValueDate); var trade_swap = DbContext.trade_swap.FirstOrDefault(x => x.TradeId == tc.TradeId); var trade_cash_swap = new trade_cash_swap(); trade_cash_swap.StartDate = td.StartDate.Value; trade_cash_swap.GetStartPrice = trade_swap.GetFinalPrice ?? trade_swap.GetSpotPrice; trade_cash_swap.GetFinalPrice = tc.FinalPrice; trade_cash_swap.GetInitialAmount = Amount; trade_cash_swap.GetAmount = Amount; trade_cash_swap.GetSwapRate = PayoffSwapCalcService.GetSwapRateByDate(td.trade_swap.GetSwapTimeAndRate, td.UnWindDate.Value); trade_cash_swap.PayExtraAmount = 0; trade_cash_swap.PayCostFee = 0; trade_cash_swap.PayAmount = 0; trade_cash_swap.PaySwapRate = PayoffSwapCalcService.GetSwapRateByDate(td.trade_swap.PaySwapTimeAndRate, td.UnWindDate.Value); trade_cash_swap.TradeId = tc.TradeId; trade_cash_swap.TradeCashId = tc.id; trade_cash_swap.OptId = tc.OptId; trade_cash_swap.OptName = tc.OptName; trade_cash_swap.OptDate = DateTime.Now; DbContext.trade_cash_swap.Add(trade_cash_swap); var tcdGet = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = 0, QuoteAmount = 0, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.利息.ToString() }; DbContext.trade_cash_detail.Add(tcdGet); var tcdCostFeeGet = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = 0, QuoteAmount = 0, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.了结手续费.ToString() }; DbContext.trade_cash_detail.Add(tcdCostFeeGet); var tcdCostTradePriceGet = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = 0, QuoteAmount = 0, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.开仓手续费.ToString() }; DbContext.trade_cash_detail.Add(tcdCostTradePriceGet); var tcdPay = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = Amount, QuoteAmount = Amount, ValueDate = tc.ValueDate, IsForGet = false, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.浮动收益.ToString() }; DbContext.trade_cash_detail.Add(tcdPay); } DbContext.SaveChanges(); } /// /// 导入了结历史数据交易 /// /// /// 当前文件中的目标期权总条数 /// 成功入库的数量 public void ImportToEndSwapTradeHistroyDataFromExcel(Stream streamIn, out int totalNum, out int successNum) { totalNum = 0; successNum = 0; var rowIndex = 0; try { var ds = Office.ExcelHelper.ReadExcelAsDataSet(streamIn, new[] { 0 }, 0); if (ds.Tables.Count < 1 || ds.Tables[0].Rows.Count < 2) { throw new ServiceException("读取导入数据失败:数据为空") { Tag = "111" }; } var table = ds.Tables[0]; var reader = new DataRowReader(table, 0); rowIndex = 1; totalNum = table.Rows.Count - rowIndex; foreach (var row in table.Rows.Cast().Skip(1)) { using (var trans = BeginTransaction()) { rowIndex++; if (row.ItemArray.All(n => string.IsNullOrWhiteSpace(n?.ToString()))) { totalNum--; continue; } reader.SetDataRow(row); HandleToEndSwapTradeHistroyData(reader); successNum++; trans.Commit(); } } } catch (ServiceException se) { if (se.Tag != null) { throw; } throw new ServiceException($"已成功导入{successNum}条;\n第{rowIndex}行,{se.Message}"); } catch (Exception ex) { LogFactory.GetLogger("导入收益互换了结").Error(ex); throw new ServiceException($"已成功导入{successNum}条,\n第{rowIndex}行,发生错误:{ex.Message}", ex); } } private trade HandleToEndSwapTradeHistroyData(DataRowReader reader) { var tradeNumnr = reader.GetString("交易编号", false); var td = new trade(); var tdTable = DbContext.trade.FirstOrDefault(a => a.TradeNumber == tradeNumnr); if (tdTable == null) { throw new ServiceException($"此交易编号[{td.TradeNumber}]不存在"); } if (tdTable.TradeStatus != ConsTrade.确认成交) { throw new ServiceException("只有交易状态为‘确认成交’,才能进行批量了结导入"); } td = tdTable; td.trade_cash = new trade_cash(); td.trade_cash.UnwindPercentRate = reader.GetPercent("了结比例", false) ?? 0; td.trade_cash.UnwindStockEqvNotional = reader.GetDouble("了结名义本金", false) ?? 0; if (td.trade_cash.UnwindStockEqvNotional == 0 && td.trade_cash.UnwindPercentRate == 0) { throw new ServiceException($"交易编号[{td.TradeNumber}]中,了结名义本金与了结比例,必须填写一个值"); } td.UnWindDate = reader.GetDate("了结日期", true); var variety = DataCacheProvider.GetVariety(td.UnderlyingCode); if (variety == null) { throw new ServiceException($"该标的[{td.UnderlyingCode}]对应的品种在系统中不存在"); } else { var market = DataCacheProvider.GetMarketDataSource().AsQueryable().FirstOrDefault(x => x.MarketName == variety.TradingMarket); if (QdpCalendarHelper.IsHoliday((DateTime)td.UnWindDate, string.IsNullOrWhiteSpace(market?.CalendarName) ? "chn" : market?.CalendarName)) { throw new ServiceException("了结日期:" + td.UnWindDate + ",不能为节假日"); } } if (td.TradeDate > td.UnWindDate) { throw new ServiceException("了结日期必须要大于或等于成交日期"); } if (td.UnWindDate > td.ExerciseDate) { throw new ServiceException("了结日期必须要小于或等于到期日期"); } if (td.UnWindDate > valuedateBLL.ValueDate) { throw new ServiceException("了结日期必须要小于或等于系统日期"); } td.FinalPrice = reader.GetDouble("了结标的价格", false); var toEndAmount = reader.GetDouble("了结总额", false); UnwindSwapTradeCashHandle(td, toEndAmount ?? 0); return td; } /// /// 了结导入数据处理 /// /// /// /// private void UnwindSwapTradeCashHandle(trade td, double Amount) { var client = DataCacheProvider.GetClientDataSource().GetData(td.ClientId); var um = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode); if (td.FinalPrice == null) { var eodpriceProvider = new YLErp.Modules.DataProviderModule.EodPriceProvider((DateTime)td.UnWindDate); td.FinalPrice = eodpriceProvider.GetPrice(td.UnderlyingCode, SettlementTypeEnum.ClosePrice);//平仓当天标的价格; } td.trade_swap = DbContext.trade_swap.FirstOrDefault(x => x.TradeId == td.id); var unwindType = "部分平仓"; //获取平仓比例 if (td.trade_cash.UnwindPercentRate == 0 || (td.trade_cash.UnwindStockEqvNotional != 0 && td.trade_cash.UnwindPercentRate != 0)) { if (td.StockEqvNotional <= td.trade_cash.UnwindStockEqvNotional + 1e-10) { td.trade_cash.UnwindStockEqvNotional = td.StockEqvNotional; unwindType = "全部平仓"; } td.trade_cash.UnwindPercentRate = td.trade_cash.UnwindStockEqvNotional / td.OriginalStockEqvNotional; } else if ((td.StockEqvNotional / td.OriginalStockEqvNotional) <= td.trade_cash.UnwindPercentRate + 1e-10) { td.trade_cash.UnwindPercentRate = td.StockEqvNotional / td.OriginalStockEqvNotional; td.trade_cash.UnwindStockEqvNotional = td.StockEqvNotional; unwindType = "全部平仓"; } if (td.trade_cash.UnwindStockEqvNotional == 0) { if ((td.StockEqvNotional / td.OriginalStockEqvNotional) <= td.trade_cash.UnwindPercentRate + 1e-10) { td.trade_cash.UnwindPercentRate = td.StockEqvNotional / td.OriginalStockEqvNotional; unwindType = "全部平仓"; } td.trade_cash.UnwindStockEqvNotional = (double)(td.OriginalStockEqvNotional * td.trade_cash.UnwindPercentRate); } else if (td.StockEqvNotional <= td.trade_cash.UnwindStockEqvNotional + 1e-10) { td.trade_cash.UnwindStockEqvNotional = td.StockEqvNotional; td.trade_cash.UnwindPercentRate = td.StockEqvNotional / td.OriginalStockEqvNotional; unwindType = "全部平仓"; } //增加现金交割交易记录 var tc = new trade_cash(); tc.OptId = UserId; tc.OptName = UserName; tc.OptDate = OptDate; tc.ExceciseType = "现金"; tc.TradeType = td.BuySell; tc.CallPut = td.CallPut; tc.IsLastAction = true; tc.TradeId = td.id; tc.FinalPrice = td.FinalPrice; tc.Notional = td.Notional; tc.TradeAmount = td.TradeAmount; tc.UnwindType = unwindType; tc.UnwindNotional = td.trade_cash.UnwindPercentRate * td.OriginalNotional; tc.UnwindTradeAmount = tc.UnwindNotional / um.CountRatio; tc.UnwindPercentRate = td.trade_cash.UnwindPercentRate; tc.NotionalPercentRate = tc.UnwindPercentRate; if (td.trade_swap.IsPayFloatingProfit) { var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, td.UnWindDate.Value, seekPreday: true, currencyRateType: Amount < 0 ? CurrencyRateType.Buy : CurrencyRateType.Sell); tc.CurrencyRate = currencyRate; tc.Action = ClientCashInCashOut.系统操作_平仓费; tc.IsLastAction = td.Notional <= 0 ? true : false; tc.Status = TradeCashStatusEnum.已执行; tc.ValueDate = td.UnWindDate.Value; tc.ValidState = "Valid"; tc.ExerciseWay = TradeCashExerciseWayEnum.提前终止行权; td.StockEqvNotional -= (double)(td.OriginalStockEqvNotional * tc.UnwindPercentRate); td.Notional -= (double)(td.OriginalNotional * tc.UnwindPercentRate); td.TradeAmount = td.Notional / um.CountRatio; td.UnWindNotional = tc.UnwindNotional; td.TradeStatus = tc.ValueDate == td.ExerciseDate ? "已到期" : (tc.UnwindType == "全部平仓" ? "已平仓" : td.TradeStatus); td.HasPartialUnWind = tc.UnwindType == "部分平仓" ? 1 : 0; DbContext.trade_cash.Add(tc); DbContext.SaveChanges(); var trade_swap = DbContext.trade_swap.FirstOrDefault(x => x.TradeId == tc.TradeId); var trade_cash_swap = new trade_cash_swap(); trade_cash_swap.StartDate = td.StartDate.Value; trade_cash_swap.PayStartPrice = trade_swap.PayFinalPrice ?? trade_swap.PaySpotPrice; trade_cash_swap.PayFinalPrice = tc.FinalPrice; trade_cash_swap.PayInitialAmount = -Amount; trade_cash_swap.PayAmount = -Amount; trade_cash_swap.PaySwapRate = PayoffSwapCalcService.GetSwapRateByDate(td.trade_swap.PaySwapTimeAndRate, td.UnWindDate.Value); trade_cash_swap.GetExtraAmount = 0; trade_cash_swap.GetCostFee = 0; trade_cash_swap.GetAmount = 0; trade_cash_swap.GetSwapRate = PayoffSwapCalcService.GetSwapRateByDate(td.trade_swap.GetSwapTimeAndRate, td.UnWindDate.Value); trade_cash_swap.TradeId = tc.TradeId; trade_cash_swap.TradeCashId = tc.id; trade_cash_swap.OptId = tc.OptId; trade_cash_swap.OptName = tc.OptName; trade_cash_swap.OptDate = DateTime.Now; tc.Amount = (trade_cash_swap.GetAmount ?? 0) - (trade_cash_swap.PayAmount ?? 0); tc.QuoteAmount = tc.Amount; DbContext.trade_cash_swap.Add(trade_cash_swap); DbContext.SaveChanges(); var tcdGet = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = 0, QuoteAmount = 0, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.利息.ToString() }; DbContext.trade_cash_detail.Add(tcdGet); var tcdCostFeeGet = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = 0, QuoteAmount = 0, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.了结手续费.ToString() }; DbContext.trade_cash_detail.Add(tcdCostFeeGet); var tcdCostTradePriceGet = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = 0, QuoteAmount = 0, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.开仓手续费.ToString() }; DbContext.trade_cash_detail.Add(tcdCostTradePriceGet); var tcdPay = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = Amount, QuoteAmount = Amount, ValueDate = tc.ValueDate, IsForGet = false, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.浮动收益.ToString() }; DbContext.trade_cash_detail.Add(tcdPay); } else { var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, td.UnWindDate.Value, seekPreday: true, currencyRateType: Amount < 0 ? CurrencyRateType.Buy : CurrencyRateType.Sell); tc.CurrencyRate = currencyRate; tc.Action = ClientCashInCashOut.系统操作_平仓费; tc.IsLastAction = td.Notional <= 0 ? true : false; tc.Status = TradeCashStatusEnum.已执行; tc.ValueDate = td.UnWindDate.Value; tc.ValidState = "Valid"; tc.ExerciseWay = TradeCashExerciseWayEnum.提前终止行权; td.StockEqvNotional -= (double)(td.OriginalStockEqvNotional * tc.UnwindPercentRate); td.Notional -= (double)(td.OriginalNotional * tc.UnwindPercentRate); td.TradeAmount = td.Notional / um.CountRatio; td.UnWindNotional = tc.UnwindNotional; td.TradeStatus = tc.ValueDate == td.ExerciseDate ? "已到期" : (tc.UnwindType == "全部平仓" ? "已平仓" : td.TradeStatus); td.HasPartialUnWind = tc.UnwindType == "部分平仓" ? 1 : 0; DbContext.trade_cash.Add(tc); DbContext.SaveChanges(); var trade_swap = DbContext.trade_swap.FirstOrDefault(x => x.TradeId == tc.TradeId); var trade_cash_swap = new trade_cash_swap(); trade_cash_swap.StartDate = td.StartDate.Value; trade_cash_swap.GetStartPrice = trade_swap.GetFinalPrice ?? trade_swap.GetSpotPrice; trade_cash_swap.GetFinalPrice = tc.FinalPrice; trade_cash_swap.GetInitialAmount = Amount; trade_cash_swap.GetAmount = Amount; trade_cash_swap.GetSwapRate = PayoffSwapCalcService.GetSwapRateByDate(td.trade_swap.GetSwapTimeAndRate, td.UnWindDate.Value); trade_cash_swap.PayExtraAmount = 0; trade_cash_swap.PayCostFee = 0; trade_cash_swap.PayAmount = 0; trade_cash_swap.PaySwapRate = PayoffSwapCalcService.GetSwapRateByDate(td.trade_swap.PaySwapTimeAndRate, td.UnWindDate.Value); trade_cash_swap.TradeId = tc.TradeId; trade_cash_swap.TradeCashId = tc.id; trade_cash_swap.OptId = tc.OptId; trade_cash_swap.OptName = tc.OptName; trade_cash_swap.OptDate = DateTime.Now; tc.Amount = (trade_cash_swap.GetAmount ?? 0) - (trade_cash_swap.PayAmount ?? 0); tc.QuoteAmount = tc.Amount; DbContext.trade_cash_swap.Add(trade_cash_swap); DbContext.SaveChanges(); var tcdGet = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = 0, QuoteAmount = 0, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.利息.ToString() }; DbContext.trade_cash_detail.Add(tcdGet); var tcdCostFeeGet = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = 0, QuoteAmount = 0, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.了结手续费.ToString() }; DbContext.trade_cash_detail.Add(tcdCostFeeGet); var tcdCostTradePriceGet = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = 0, QuoteAmount = 0, ValueDate = tc.ValueDate, IsForGet = true, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.开仓手续费.ToString() }; DbContext.trade_cash_detail.Add(tcdCostTradePriceGet); var tcdPay = new trade_cash_detail { TradeId = tc.TradeId, TradeCashId = tc.id, Action = tc.Action, Amount = Amount, QuoteAmount = Amount, ValueDate = tc.ValueDate, IsForGet = false, OptId = tc.OptId, OptName = tc.OptName, OptDate = DateTime.Now, TradeCashType = TradeCashTypeEnum.浮动收益.ToString() }; DbContext.trade_cash_detail.Add(tcdPay); } //增加出入金记录 new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(td, tc, tc.ValueDate); var auditLog = new TradeAuditLog { TradeId = tc.TradeId, Changes = null, DataType = "00", OptType = "批量了结-平仓", OptId = UserId, OptName = UserName, OptDate = OptDate, AuditFlag = TradeAuditFlag.operation }; //记录审核日志 DbContext.TradeAuditLog.Add(auditLog); DbContext.SaveChanges(); } #endregion #region 山西互换交易导入 private void ImportFromExcelSX(Stream streamIn, out int totalNum, out int successNum) { totalNum = 0; successNum = 0; var rowIndex = 0; var ds = Office.ExcelHelper.ReadExcelAsDataSet(streamIn, new[] { 0 }, 0); if (ds.Tables.Count < 1 || ds.Tables[0].Rows.Count < 2) { throw new ServiceException("读取导入数据失败:数据为空") { Tag = "111" }; } var table = ds.Tables[0]; var reader = new DataRowReader(table, 0); rowIndex = 1; totalNum = table.Rows.Count - rowIndex; foreach (var row in table.Rows.Cast().Skip(1)) { rowIndex++; reader.SetDataRow(row); if (row.ItemArray.All(n => string.IsNullOrWhiteSpace(n?.ToString()))) { totalNum--; continue; } var importSwap = MapSwap(reader, out var amount, out var getAmount, out var payAmount); var saveChecker = new OtcOptionSaveChecker(this).CheckBasic(importSwap); //簿记、交易员、交易对手 saveChecker.CheckAssetBook(importSwap).CheckTrader(importSwap); if (importSwap != null) { var saveTrade = new TradeSaveService(OptUser).SaveTrade(importSwap, TradeSourceEnum.导入交易); var trade = DbContext.trade.Find(importSwap.id); trade.trade_swap = saveTrade.trade_swap; trade.TradeOldStatus = trade.TradeStatus; trade.TradeStatus = ConsTrade.确认成交; new ClientCashInCashOutService(this).SaveClientCashInCashOut(trade); if (trade.UnWindDate == trade.SettlementDate) { trade.TradeStatus = ConsTrade.已到期; } else { trade.TradeStatus = ConsTrade.已平仓; } var tc = new trade_cash(); DbContext.trade_cash.Add(tc); tc.StartDate = trade.StartDate.Value; tc.ValidState = "Valid"; tc.OptId = UserId; tc.OptName = UserName; tc.OptDate = OptDate; tc.ExceciseType = "现金"; tc.TradeType = trade.BuySell; tc.CallPut = trade.CallPut; tc.Strike = trade.IsMoneynessOptionData ? (trade.Strike * trade.SpotPrice) : trade.Strike; tc.Notional = trade.Notional; tc.TradeAmount = trade.Amount; tc.IsLastAction = true; tc.Amount = amount; //1.远期存入平仓时算出的预付金占用成本 //2.部分行权时候的额外金额 tc.ExtraAmount = 0; tc.UnwindPrice = trade.FinalPrice; tc.TradeId = trade.id; tc.FinalPrice = trade.FinalPrice; tc.SpotPrice = trade.TradePrice; tc.HappenedDate = trade.UnWindDate; tc.UnwindType = "全部平仓"; tc.UnwindPercentRate = 1; tc.UnwindNotional = trade.Notional; tc.UnwindTradeAmount = trade.TradeAmount; tc.Action = ClientCashInCashOut.系统操作_平仓费; tc.Status = DBModels.Enums.TradeCashStatusEnum.已执行; tc.ValueDate = trade.UnWindDate.Value; DbContext.SaveChanges(); var ee = new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(trade, tc, tc.ValueDate); var swapTrade = trade.trade_swap; var trade_cash_swap = new trade_cash_swap() { GetAmount = getAmount, PayAmount = payAmount }; trade_cash_swap.StartDate = tc.StartDate; if (swapTrade.IsGetFloatingProfit) { trade_cash_swap.GetStartPrice = swapTrade.GetSpotPrice ?? 0; trade_cash_swap.GetFinalPrice = swapTrade.GetFinalPrice ?? 0; } if (swapTrade.IsPayFloatingProfit) { trade_cash_swap.PayStartPrice = swapTrade.PaySpotPrice ?? 0; trade_cash_swap.PayFinalPrice = swapTrade.PayFinalPrice ?? 0; } trade_cash_swap.TradeId = tc.TradeId; trade_cash_swap.TradeCashId = tc.id; trade_cash_swap.OptId = tc.OptId; trade_cash_swap.OptName = tc.OptName; trade_cash_swap.OptDate = DateTime.Now; DbContext.trade_cash_swap.Add(trade_cash_swap); //终止 trade_cash DbContext.SaveChanges(); successNum++; } //结算 trade cash } } private trade MapSwap(DataRowReader reader, out double winLoss, out double getAmount, out double payAmount) { var td = new trade(); //基本要素 td.TradeNumber = reader.GetString("交易确认书编号(双方约定)", false); td.AssetBookName = reader.GetString("簿记账户", true); td.TraderName = reader.GetString("交易员", true); td.TradeType = "收益互换"; td.ClientName = reader.GetString("交易对手方", true); if (string.IsNullOrWhiteSpace(td.ClientName)) { throw new ServiceException("交易对手方名称 必须填写"); } //交易日期 td.TradeDate = reader.GetDate("起始日", true); td.ExerciseDate = reader.GetDate("到期日", true); td.StartDate = td.TradeDate; //结算日期 td.SettlementDate = td.ExerciseDate; td.StockEqvNotional = reader.GetDouble("名义本金(人民币)", true) ?? 0; td.MarginTemplateName = null; td.UnWindDate = reader.GetDate("提前终止日/终止日", true); td.Comments = reader.GetString("备注", false); //td.UnderlyingName = reader.GetString("标的名称", true); td.UnderlyingCode = reader.GetString("标的代码", true); var clientQuery = DbContextFactory.GetClientDbContext(OptUser).client.AsQueryable(); if (!string.IsNullOrWhiteSpace(td.ClientName)) { clientQuery = clientQuery.Where(c => c.Name == td.ClientName); } var client = clientQuery.Select(n => new { n.id, n.DerivativesInvestmentVarieties }).FirstOrDefault(); if (client == null) { throw new ServiceException($"交易对手方不存在,ClientName:{td.ClientName}"); } else { if (!client.DerivativesInvestmentVarieties.Contains((int)DerivativesInvestmentVarietiesEnum.场外互换 + "")) { throw new ServiceException($"客户:{td.ClientName}未设置交易种类“场外互换”,无法生成互换交易!"); } } td.ClientId = client.id; var um = DataCacheProvider.GetUnderlyingDataSource().AsQueryable().FirstOrDefault(o => o.UnderlyingCode == td.UnderlyingCode); if (um == null) { throw new ServiceException($"{td.UnderlyingCode} 不存在,请先新建标的再导入"); } else { td.UnderlyingId = um.id; td.UnderlyingCode = um.UnderlyingCode; td.UnderlyingName = um.UnderlyingName; } //币种 var underlyingModel = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode); td.QuoteCurrency = DataCacheProvider.GetVarietyDataSource().GetData(underlyingModel.UnderlyingTypeId).QuoteCurrency; var ExchangeRate = reader.GetDouble("汇率", false); if (ExchangeRate != null) { if (td.QuoteCurrency != "CNY" && (!string.IsNullOrEmpty(td.QuoteCurrency))) { td.MetaDic.Add("ExchangeRate", ExchangeRate.ToString()); } } //td.MetaDic = new Dictionary() { // { "组合标的", "" }, // { "组合标的2", "" }, // { "交易场所", "" }, // { "清算机构", "" }, // { "主协议编号", "" }, // { "补充协议编号", "" }}; td.OpponentRole = "甲方"; double.TryParse(Regex.Match(reader.GetString("标的数量", false), @"^\d+((\,\d{3})+)?(\.\d+)?").Value, out var notional); double.TryParse(Regex.Match(reader.GetString("标的初始价格", false), @"^\d+((\,\d{3})+)?(\.\d+)?").Value, out var spotPrice); double.TryParse(Regex.Match(reader.GetString("标的提前终止日/终止日价格", false), @"^\d+((\,\d{3})+)?(\.\d+)?").Value, out var finalPrice); td.Notional = notional; td.TradeAmount = notional; td.OriginalNotional = notional; td.SpotPrice = spotPrice; td.FinalPrice = finalPrice; var swapTrade = new trade_swap(); swapTrade.RateCalcMode = GetRateCalcMode(reader.GetString("计息方式", true)); swapTrade.AnnualDays = reader.GetInt("年化天数", true); swapTrade.AnnualVarIncome = reader.GetString("浮动收益年化", true) == "是"; swapTrade.GetSwapRate = reader.GetPercent("我方收取互换利率(年化)", false) ?? 0; swapTrade.PaySwapRate = reader.GetPercent("我方支付互换利率(年化)", false) ?? 0; if (swapTrade.GetSwapRate >= 0) { swapTrade.GetSwapTimeAndRate = td.ExerciseDate + ";" + swapTrade.GetSwapRate; } if (swapTrade.PaySwapRate >= 0) { swapTrade.PaySwapTimeAndRate = td.ExerciseDate + ";" + swapTrade.PaySwapRate; } swapTrade.GetLongShort = reader.GetString("我方收取浮动收益", false); swapTrade.PayLongShort = reader.GetString("我方支付浮动收益", false); if (!string.IsNullOrWhiteSpace(swapTrade.GetLongShort)) { swapTrade.GetUnderlyingCode = td.UnderlyingCode; swapTrade.GetUnderlyingId = td.UnderlyingId; swapTrade.IsGetFloatingProfit = true; swapTrade.GetNotional = notional; swapTrade.GetTradeAmount = notional; swapTrade.GetSpotPrice = spotPrice; swapTrade.GetFinalPrice = finalPrice; } else { swapTrade.IsGetFloatingProfit = false; } if (!string.IsNullOrWhiteSpace(swapTrade.PayLongShort)) { swapTrade.PayUnderlyingCode = td.UnderlyingCode; swapTrade.PayUnderlyingId = td.UnderlyingId; swapTrade.IsPayFloatingProfit = true; swapTrade.PayNotional = notional; swapTrade.PayTradeAmount = notional; swapTrade.PaySpotPrice = spotPrice; swapTrade.PayFinalPrice = finalPrice; } else { swapTrade.IsPayFloatingProfit = false; } winLoss = reader.GetDouble("我方损益", true) ?? 0; getAmount = reader.GetDouble("我方收取现金流", false) ?? 0; payAmount = reader.GetDouble("我方支付现金流", false) ?? 0; //swapTrade. = winLoss >= 0 : /* data["UnderlyingId"] = data["trade_swap.PayUnderlyingId"]; data["UnderlyingCode"] = data["trade_swap.PayUnderlyingCode"]; */ td.trade_swap = swapTrade; return td; } #endregion public void UploadClientVarietyConfig(Stream streamIn, out int totalNum, out int successNum) { totalNum = 0; successNum = 0; var rowIndex = 0; var ds = Office.ExcelHelper.ReadExcelAsDataSet(streamIn, new[] { 0 }, 0); if (ds.Tables.Count < 1 || ds.Tables[0].Rows.Count < 2) { throw new ServiceException("读取导入数据失败:数据为空") { Tag = "111" }; } var table = ds.Tables[0]; var reader = new DataRowReader(table, 0); rowIndex = 1; totalNum = table.Rows.Count - rowIndex; foreach (var row in table.Rows.Cast().Skip(1)) { rowIndex++; reader.SetDataRow(row); if (row.ItemArray.All(n => string.IsNullOrWhiteSpace(n?.ToString()))) { totalNum--; continue; } var config = MapClientVarietyConfig(reader); if (config != null) { successNum++; } } } private client_variety_config MapClientVarietyConfig(DataRowReader reader) { var clientNumber = reader.GetString("客户编号", true); var varietyCode = reader.GetString("品种代码", true); var client = ClientModule.ClientDataQueryService.GetClientByNumber(clientNumber); if (client == null) { throw new ServiceException($"该客户[{clientNumber}]在系统中不存在"); } var variety = DbContext.variety.Where(o => o.VarietyCode == varietyCode || o.VarietyName == varietyCode).FirstOrDefault(); if (variety == null) { throw new ServiceException($"该品种[{varietyCode}]在系统中不存在"); } var config = new client_variety_config() { ValueDate = reader.GetDate("生效日期", true).Value, ClientId = client.id, VarietyId = variety.id, SingleFee = reader.GetDouble("按手数收费", false) ?? 0, UnAnnualRate = reader.GetPercent("按名义本金收费", false) ?? 0, AnnualRate = reader.GetPercent("年化利率", false) ?? 0, AnnualDays = reader.GetInt("年化天数", false) ?? 365, Multiple = reader.GetDouble("倍数", false) ?? 0, OptId = UserId, OptName = UserName, OptDate = DateTime.Now }; if (DbContext.client_variety_config.Any(x => x.ClientId == config.ClientId && x.VarietyId == config.VarietyId && x.ValueDate == config.ValueDate)) { throw new Exception($"同一客户[{clientNumber}]同一品种[{varietyCode}]不支持在同一天[{config.ValueDate.ToString("yyyy-MM-dd")}]有多条配置记录"); } if (config.SingleFee > 0 && config.UnAnnualRate > 0) { throw new Exception($"按手数收费和按名义本金收费不能同时设置"); } var types = new List(); if (config.SingleFee != 0) { types.Add(1); } if (config.AnnualRate != 0) { types.Add(2); } if (config.UnAnnualRate != 0) { types.Add(3); } config.FeeType = string.Join(",", types); DbContext.client_variety_config.Add(config); DbContext.SaveChanges(); return config; } #region---内部业务类---- class DataRowTopTypeReader { DataRow _row; readonly Dictionary _colMap; public DataRowTopTypeReader(DataTable table) { var colCount = table.Columns.Count; _colMap = new Dictionary(colCount, StringComparer.OrdinalIgnoreCase); var row1 = table.Rows[0]; var row2 = table.Rows[1]; var preCol1 = string.Empty; for (var index = 0; index < colCount; index++) { var col1 = row1[index]?.ToString()?.Trim(); var col2 = row2[index]?.ToString(); if (string.IsNullOrWhiteSpace(col2)) { continue; } if (!string.IsNullOrWhiteSpace(col1)) { preCol1 = col1; } col2 = col2.Replace("%", "").Trim(); _colMap[preCol1 + col2] = index; } } public string TopType { get; private set; } /// /// 设置datarow /// public void SetDataRow(DataRow row) { _row = row; TopType = "基本要素"; } /// /// 设置顶部类型 /// public void SetTopType(string type) { TopType = type; } public string GetString(string fieldName, bool required = false) { var str = _colMap.TryGetValue(TopType + fieldName, out var colIndex) ? _row[colIndex]?.ToString()?.Trim() : null; if (required && string.IsNullOrWhiteSpace(str)) { throw new ServiceException($"[{TopType}]{fieldName} 必须填写"); } return str; } public int? GetInt(string fieldName, bool required) { var str = GetString(fieldName, required); if (!required && string.IsNullOrEmpty(str)) { return null; } return int.TryParse(str, out var num) ? num : throw new ServiceException($"[{TopType}]{fieldName} 填写错误:{str}"); } public double? GetDoubleOrPercent(string fieldName, bool required, bool percent) { var str = GetString(fieldName, required); if (!required && string.IsNullOrWhiteSpace(str)) { return null; } if (percent && (percent = str.EndsWith("%"))) { str = str.TrimEnd('%'); } return double.TryParse(str, out var num) ? (percent ? num / 100 : num) : throw new ServiceException($"[{TopType}]{fieldName} 填写错误:{str}"); } public double? GetDouble(string fieldName, bool required = false) { var str = GetString(fieldName, required); if (!required && string.IsNullOrWhiteSpace(str)) { return null; } return double.TryParse(str, out var num) ? num : throw new ServiceException($"[{TopType}]{fieldName} 填写错误:{str}"); } //为了兼容模板修改导致的字段名称改变问题 public double? GetDouble(string fieldName, string fieldName2, bool required = false) { var str = GetString(fieldName, false) ?? GetString(fieldName2, false); if (string.IsNullOrWhiteSpace(str)) { return required ? throw new ServiceException($"[{TopType}]{fieldName} 必须填写") : (double?)null; } return double.TryParse(str, out var num) ? num : throw new ServiceException($"[{TopType}]{fieldName} 填写错误:{str}"); } public double? GetPercent(string fieldName, bool required = false) { var str = GetString(fieldName, required); if (!required && string.IsNullOrWhiteSpace(str)) { return null; } var percent = str.EndsWith("%"); if (percent) { str = str.TrimEnd('%'); } return double.TryParse(str, out var num) ? (percent ? num / 100 : num) : throw new ServiceException($"[{TopType}]{fieldName} 填写错误:{str}"); } /// /// 获取日期(不包括时间) /// public DateTime? GetDate(string fieldName, bool required = false) { var str = GetString(fieldName, required); if (!required && string.IsNullOrWhiteSpace(str)) { return null; } if (str.Length == 8 && Regex.IsMatch(str, @"^\d+$")) { return DateTime.TryParseExact(str, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt2) ? dt2 : throw new ServiceException($"{fieldName} 填写错误:{str}"); } return DateTime.TryParse(str, out var dt) ? dt.Date : throw new ServiceException($"[{TopType}]{fieldName} 填写错误:{str}"); } /// /// /// public int? GetInt32(string fieldName, bool required = false) { var str = GetString(fieldName, required); if (!required && string.IsNullOrWhiteSpace(str)) { return null; } return int.TryParse(str, out var num) ? num : throw new ServiceException($"[{TopType}]{fieldName} 填写错误:{str}"); } } #endregion } }