using BaseOUDAL; using Qdp.Pricing.Base.Enums; using Qdp.Pricing.Base.Implementations; using System.Data; using System.Globalization; using System.Text.RegularExpressions; using YLErp.Commons; using YLErp.DBModels.Consts; using YLErp.DBModels.Enums; using YLErp.DBModels.Helpers; using YLErp.Enums; using YLErp.Helpers; using YLErp.Model.Enum; using YLErp.Models; using YLErp.Modules.DictionaryModule; using YLErp.Modules.SalesModule; using YLErp.Modules.StructureModule; using YLErp.Modules.TradeModule.DealModule; using YLErp.QdpModule; namespace YLErp.Modules.TradeModule.OrderModule { /// /// 场外交易导入服务 /// public class OtcTradeImportService : YLBaseService { public OtcTradeImportService(YLBaseService baseService) : base(baseService) { } public OtcTradeImportService(OptUserInfo userInfo) : base(userInfo) { } /// /// 从excel文件中导入场外期权交易 /// public void ImportOptionTradesFromExcel(Stream streamIn, out int totalNum, out int successNum) { new OtcOptionTradeImportService(this).ImportFromExcel(streamIn, out totalNum, out successNum); } /// /// 从excel文件中读取场外期权交易导入 /// public List ReadOptionTradesFromExcel(Stream streamIn) { return new OtcOptionTradeImportService(this).ReadFromExcel(streamIn); } } /// /// 场外期权交易导入服务 /// class OtcOptionTradeImportService : OtcTradeSaveService { public OtcOptionTradeImportService(OptUserInfo userInfo) : base(userInfo) { } public OtcOptionTradeImportService(YLBaseService baseService) : base(baseService) { } private readonly ErpBaseContext basedb = new ErpBaseContext(); /// /// 导入交易 /// /// /// 当前文件中的目标期权总条数 /// 成功入库的数量 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(stream: streamIn, includeSheetIndexs: new[] { 0 }, headerRowIndex: 0); if (ds.Tables.Count < 1 || ds.Tables[0].Rows.Count < 3) { throw new ServiceException("读取导入数据失败:数据为空") { Tag = "111" }; } if (ds.Tables[0].TableName != "交易导入模板V3") { throw new ServiceException("请使用最新交易导入模板V3") { Tag = "111" }; } var table = ds.Tables[0]; var reader = new DataRowReader(table); rowIndex = 2; totalNum = table.Rows.Count - rowIndex; foreach (var row in table.Rows.Cast().Skip(2)) { rowIndex++; if (row.ItemArray.All(n => string.IsNullOrWhiteSpace(n?.ToString()))) { totalNum--; continue; } reader.SetDataRow(row); //映射导入数据到交易对象 var importTrade = MapTrade(reader); if (groupTradeList.Count > 0 && importTrade.IsGroup != 2) { rowIndex -= 1; //save 方法里会清空列表 successNum += InnerSaveGroupTrade(groupTradeList); rowIndex += 1; } //保存结构化交易 if (structureTradeList.Count > 0 && (structureTradeList[0].StructureType != importTrade.StructureType || structureTradeList[0].CalcId != importTrade.CalcId)) { rowIndex -= 1; successNum += InnerSaveStructureTrade(structureTradeList); rowIndex += 1; } //当上一个组合保存完毕,接下来又遇到组合主交易时 if (importTrade.IsGroup == 1 || importTrade.IsGroup == 2) { groupTradeList.Add(importTrade); } //当上一个组合保存完毕,接下来遇到非组合主交易时 else { //结构化交易 if (!string.IsNullOrWhiteSpace(importTrade.StructureType) && importTrade.TradeType != "自定义交易") { structureTradeList.Add(importTrade); } //单笔交易 else { InnerSaveTrade(importTrade); successNum++; } } } } 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); } try { //保存组合交易 if (groupTradeList.Count > 0) { rowIndex -= (groupTradeList.Count - 1); successNum += InnerSaveGroupTrade(groupTradeList); } //保存结构化 if (structureTradeList.Count > 0) { rowIndex -= (structureTradeList.Count - 1); successNum += InnerSaveStructureTrade(structureTradeList); } } 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 List ReadFromExcel(Stream streamIn) { var rowIndex = 0; 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" }; } rowIndex = 2; var table = ds.Tables[0]; var reader = new DataRowReader(table); var list = new List(table.Rows.Count); foreach (var row in table.Rows.Cast().Skip(2)) { rowIndex++; if (row.ItemArray.All(n => string.IsNullOrWhiteSpace(n?.ToString()))) { continue; } reader.SetDataRow(row); //映射导入数据到交易对象 var importTrade = MapTrade(reader); list.Add(importTrade); } return list; } //保存交易 private void InnerSaveTrade(OtcOptionTradeFullEx importTrade) { using (var trans = DbContext.Database.BeginTransaction()) { var dbTrade = SaveOptionTradeFromApiOrImport(importTrade, TradeSourceEnum.导入交易, trans); if (importTrade.CloseRequest != null) { ExecuteClose(dbTrade, importTrade.CloseRequest); } trans.Commit(); } } //保存结构化交易 private int InnerSaveStructureTrade(List structureTradeList) { var count = structureTradeList.Count; if (count < 2) { throw new ServiceException("结构化交易组合必须有两条腿及以上"); } if (structureTradeList.Select(n => n.TradeNumber).Distinct().Count() > 1) { throw new ServiceException("结构化交易组合必须有相同的交易编号"); } //在底层服务已经做了验证:结构化交易组合必须有相同的交易对手方 using (var trans = DbContext.Database.BeginTransaction()) { var dbTrade = SaveStructureOptionTradeFromApiOrImport(new StructureOrderRequest { Trades = structureTradeList, StructureType = structureTradeList[0].StructureType }, TradeSourceEnum.导入交易, trans); var subTrades = dbTrade.SubTrades.ToArray(); DbContext.trade.Attach(dbTrade); for (var i = 0; i < count; i++) { DbContext.trade.Attach(subTrades[i]); ExecuteClose(subTrades[i], structureTradeList[i].CloseRequest); } structureTradeList.Clear(); trans.Commit(); } return count; } private int InnerSaveGroupTrade(List structureTradeList) { var count = structureTradeList.Count; if (count <= 2) { throw new ServiceException("组合交易必须有两条子交易及以上"); } //在底层服务已经做了验证:结构化交易组合必须有相同的交易对手方 using (var trans = DbContext.Database.BeginTransaction()) { var dbTrade = SaveGroupOptionTradeFromApiOrImport(new StructureOrderRequest { Trades = structureTradeList }, TradeSourceEnum.导入交易, trans); structureTradeList.Clear(); trans.Commit(); } return count; } private OtcOptionTradeFullEx MapTrade(DataRowReader reader) { var td = new OtcOptionTradeFullEx { //基本要素 TradeNumber = reader.GetString("交易编号"), AssetBookName = reader.GetString("簿记账户名称", true), TraderName = reader.GetString("交易员名称", true), TradeType = reader.GetString("结构类型", true), IsGroup = reader.GetString("组合交易类型") == "组合主交易" ? 1 : (reader.GetString("组合交易类型") == "组合子交易" ? 2 : 0), ClientNumber = reader.GetString("交易对手方编号"), ClientName = reader.GetString("交易对手方名称") }; if (string.IsNullOrWhiteSpace(td.ClientNumber) && string.IsNullOrWhiteSpace(td.ClientName)) { throw new ServiceException("交易对手方编号 或 交易对手方名称 必须填写其中一个"); } var tradeTypes = new StructureService(OptUser).QueryStructureInfo(td.IsGroup == 1 ? StructureRangeEnum.BALCK_TRADE : StructureRangeEnum.CUSTOM_TRADE).Select(O => O.StructureName); if (!ConsTrade.AllTradeTypes.Contains(td.TradeType) && td.TradeType != "累计期权三段式") { if (!tradeTypes.Contains(td.TradeType)) { throw new ServiceException($"交易类型{td.TradeType}不合法"); } if (td.IsGroup == 0) { td.StructureType = td.TradeType; td.TradeType = "自定义交易"; } } var required = td.TradeType != "自定义交易" && td.TradeType != "现金流交易" && td.IsGroup != 1; //交易方向 td.BuySell = reader.GetString("交易方向", true); switch (td.BuySell) { case "Buy": td.BuySell = "买入"; break; case "Sell": td.BuySell = "卖出"; break; case "买入": case "卖出": break; default: throw new ServiceException("交易方向 填写错误:" + td.BuySell); } //看涨看跌 td.OptionType = reader.GetString("看涨看跌", required); if (td.TradeType == "Risky期权" && td.OptionType != "看涨" && td.OptionType != "Call") { throw new ServiceException("Risky期权看涨看跌 必须为看涨"); } switch (td.OptionType) { case "看涨": case "看跌": break; case "Call": td.OptionType = "看涨"; break; case "Put": td.OptionType = "看跌"; break; default: if (!string.IsNullOrWhiteSpace(td.OptionType)) { throw new ServiceException("看涨看跌 填写错误:" + td.OptionType); } if (required) { throw new ServiceException("看涨看跌 必须填写"); } break; } //行权方式(需要转换预校验会用到) td.ExerciseMode = reader.GetString("行权方式", required); switch (td.ExerciseMode) { case "欧式": td.ExerciseMode = "European"; break; case "美式": td.ExerciseMode = "American"; break; case "European": case "American": break; default: if (!string.IsNullOrWhiteSpace(td.ExerciseMode)) { throw new ServiceException("行权方式 填写错误:" + td.ExerciseMode); } if (required) { throw new ServiceException("行权方式 必须填写"); } break; } //交易日期 td.TradeDate = reader.GetDate("交易日期", true); //标的代码 td.UnderlyingCode = reader.GetString("标的代码", td.TradeType != "现金流交易"); td.ExerciseDate = reader.GetDate("到期日期", true); //结算日期 td.SettlementDate = reader.GetDate("结算日期"); if (PS.Config.Is浙期) { var flag = false; var codes = new List(); codes.Add(td.UnderlyingCode); //判断标的是否为现货 flag = UndelyingHelper.IsCodesExistsCommoditySpot(codes, (um) => { return um.UnderlyingInstrumentType == "CommoditySpot" ? true : false; }); if (flag) { var dateStr = reader.GetDate("实际到期日期", false) ?? td.ExerciseDate; td.MetaDic["ActualExerciseDate"] = dateStr.Value.ToString("yyyy-MM-dd"); } } //判断是否组合标的 var synthetic = DataCacheProvider.GetUnderlyingDataSource().GetSyntheticUnderlying(td.UnderlyingCode); //标的价格(组合标的时忽略并由组合标的数据算出) if (synthetic == null) { td.SpotPrice = reader.GetDouble("标的价格", td.TradeType != "现金流交易"); } //相对行权价 td.IsMoneynessOption = reader.GetString("相对行权价"); //执行价格(亚式浮动行权价类型时非必填) td.Strike = reader.GetDoubleOrPercent("执行价格", false, td.IsMoneynessOption == "是"); double? dtemp; //权利金单价 td.TradeSinglePrice = dtemp = reader.GetDouble("权利金单价"); if (dtemp.HasValue) { td.HasFlag |= OtcOptionTradeFullHasFlag.TradeSinglePrice; } else { //权利金总额 td.TradePrice = dtemp = reader.GetDouble("权利金总额"); if (dtemp.HasValue) { td.HasFlag |= OtcOptionTradeFullHasFlag.TradePrice; } else { //期权费率 td.PremiumRate = dtemp = reader.GetPercent("期权费率"); if (dtemp.HasValue) { td.HasFlag |= OtcOptionTradeFullHasFlag.PremiumRate; } } } var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode); if (underlying == null) { throw new ServiceException($"标的{td.UnderlyingCode}信息不存在"); } var variety = DataCacheProvider.GetVarietyDataSource().GetData(underlying.UnderlyingTypeId); //币种 td.QuoteCurrency = variety?.QuoteCurrency; //汇率 var ExchangeRate = reader.GetDouble("汇率", false); if (ExchangeRate != null) { if (!string.IsNullOrEmpty(td.QuoteCurrency)) { td.MetaDic.Add("ExchangeRate", ExchangeRate.ToString()); } } //参与率(百分比格式) td.ParticipationRate = reader.GetPercent("参与率", td.TradeType == "Risky期权") ?? 1; if(td.ParticipationRate.HasValue && td.ParticipationRate.Value < 0) { throw new ServiceException("[基本要素]参与率 不能小于0,当前填写值:" + td.ParticipationRate); } //有效交易份额 td.Notional = reader.GetDouble("有效交易份额") ?? 0; //交易份额(虚拟) var notionalV = reader.GetDouble("交易份额"); if (notionalV.HasValue && notionalV.Value > 0) { var ttypes = new[] { "凤凰期权", "雪球期权" }; td.Notional = notionalV.Value * (ttypes.Contains(td.TradeType) ? 1 : td.AnnualizeFactor ?? 1) * (td.ParticipationRate ?? 1); } if (td.TradeType != "累计期权" && td.TradeType != "累计期权三段式") { //名义本金 td.StockEqvNotional = reader.GetDouble("名义本金") ?? 0; //有效名义本金 td.StockEqvNotionalReal = reader.GetDouble("有效名义本金") ?? reader.GetDouble("实际名义本金") ?? 0; } //期权年化 td.IsAnnualized = reader.GetString("期权年化") == "是"; if (td.IsAnnualized) { var annualizeFactor = reader.GetString("年化系数"); if (!string.IsNullOrEmpty(annualizeFactor)) { var index = annualizeFactor.IndexOf('/'); if (index < 0) { throw new ServiceException("[基本要素]年化系数 不是正确格式(正确格式样例:25/244),当前填写值:" + annualizeFactor); } if (!int.TryParse(annualizeFactor.Substring(0, index).TrimEnd(), out var ttmDays) || ttmDays < 0) { throw new ServiceException("[基本要素]年化系数 '/'左边不是正确的天数:" + annualizeFactor); } if (!int.TryParse(annualizeFactor.Substring(index + 1).TrimStart(), out var daysOfYear) || daysOfYear < 1) { throw new ServiceException("[基本要素]年化系数 '/'右边不是正确的年天数:" + annualizeFactor); } td.AnnualizeFactor = (double)ttmDays / daysOfYear; td.MetaDic[nameof(td.AnnualizeFactor)] = annualizeFactor; } } bool IsUsePremiumRate = false; if (td.HasFlag == OtcOptionTradeFullHasFlag.PremiumRate) { IsUsePremiumRate = true; } else if (td.HasFlag == OtcOptionTradeFullHasFlag.TradePrice) { IsUsePremiumRate = !(td.Notional > 0); } //保底收益 td.OriginalPrincipalSum = reader.GetDouble("保底收益总额") ?? 0; double stockEqvNotional = td.StockEqvNotional > 0 ? td.StockEqvNotional : td.StockEqvNotionalReal; if (IsUsePremiumRate) { td.PrincipalRateWrite = reader.GetPercent("保底收益") ?? 0; td.SinglePrincipalWrite = TradeHelper.GetSinglePrincipal(stockEqvNotional, td.PrincipalRateWrite, td.Notional, td.AnnualizeFactor); if (reader.GetDouble("保底收益总额") == null && reader.GetPercent("保底收益") != null) { td.OriginalPrincipalSum = TradeHelper.GetRateToPrincipalSum(stockEqvNotional, td.PrincipalRateWrite, td.AnnualizeFactor); } } else { td.SinglePrincipalWrite = reader.GetDouble("保底收益") ?? 0; td.PrincipalRateWrite = TradeHelper.GetPrincipalRate(stockEqvNotional, td.SinglePrincipalWrite, td.Notional, td.AnnualizeFactor); if (reader.GetDouble("保底收益总额") == null && reader.GetPercent("保底收益") != null) { td.OriginalPrincipalSum = TradeHelper.GetSingleToPrincipalSum(td.Notional, td.SinglePrincipalWrite); } } //无风险利率(百分比格式) td.NoRiskRate = reader.GetPercent("无风险利率"); //分红率(百分比格式) td.DividendRate = reader.GetPercent("分红率"); //销售员 提成方式 提成金额 var salesmen = reader.GetString("销售员"); if (PS.Config.ErpElement.SaleMode == YLErp.Configuration.Enums.SaleMode.CustomerManager) { //客户经理 提成方式 提成金额 salesmen = reader.GetString("客户经理"); } if (!string.IsNullOrWhiteSpace(salesmen)) { var salesCommission = new TradeSalesCommissionInfo { CommissionFixed = reader.GetString("提成方式") == "绝对值" }; var commission = reader.GetPercent("提成金额", false); if (commission < 0) { throw new ServiceException("销售佣金金额 不能小于0"); } //todo 多人 salesCommission.SalesIds = GetSalesIds(salesmen); salesCommission.Commission = commission; salesCommission.ClientId = GetClientId(td.ClientName); salesCommission.SaleMode = (int)PS.Config.ErpElement.SaleMode; td.SalesCommission = salesCommission; } //备注 td.Comments = reader.GetString("备注"); //成交波动率(必需&百分比格式) td.TradeOpenVolatility = reader.GetPercent("成交波动率", required); //目标波动率(必需&百分比格式) td.TradeCloseVolatility = reader.GetPercent("目标波动率", required); //平滑过渡天数 td.NumOfSmoothingDays = reader.GetInt32("平滑过渡天数"); //存续天数 td.DurationDays = (short?)reader.GetInt32("存续天数"); //期末费率 td.MetaDic["期末费率"] = reader.GetString("期末费率"); //初始预付金 td.InitialMargin = reader.GetDouble("初始预付金"); //计算标识 td.CalcFlag = reader.GetInt32("计算标识") ?? 0; //收益结算 var SettlementTypeStr = reader.GetString("收益结算"); switch (SettlementTypeStr) { case "收盘价": td.SettlementType = SettlementTypeEnum.ClosePrice; break; case "结算价": td.SettlementType = SettlementTypeEnum.SettlePrice; break; case "参考价": td.SettlementType = SettlementTypeEnum.ReferencePrice; break; default: if (!string.IsNullOrWhiteSpace(SettlementTypeStr)) { throw new ServiceException("收益结算 填写错误:" + SettlementTypeStr); } break; } //结构说明 td.StructureIntroduction = reader.GetString("结构说明"); //自定义保存 if (td.TradeType == "自定义交易" || (td.IsGroup == 1 && td.TradeType != "气囊结构")) { reader.SetTopType("自定义属性"); ReadCustomizedOption(reader, td); } else { if (reader.CustomField.Length > 0) { reader.SetTopType("自定义属性"); var propertys = new List(); foreach (var item in reader.CustomField) { var cusofield = reader.GetString(item); if (!string.IsNullOrWhiteSpace(cusofield)) { propertys.Add(new ExtendInfoModel() { name = item, value = cusofield }); } } td.MetaDic[ConsTradeMetaKey.tradeExtendInfo] = "自定义"; td.ExtendInfo = JsonHelper.Serialize(propertys); } reader.SetTopType(td.TradeType); switch (td.TradeType) { case "香草期权": case "合成价差期权": break; case "亚式期权": ReadAsiaOption(reader, td); break; case "障碍期权": ReadBarrierOption(reader, td); break; case "双鲨期权": ReadDoubleSharkOption(reader, td); break; case "二元期权": ReadBinaryOption(reader, td); break; case "区间累积期权": ReadRangeAccOption(reader, td); break; case "气囊结构": ReadAirBagOption(reader, td); break; case "收益增强结构": //年化增强收益(必需&百分比格式) td.AnnualizedEnhanceRate = reader.GetPercent("年化增强收益", true).Value; break; case "凤凰期权": ReadAutoCallOption(reader, td); break; case "雪球期权": ReadSnowBallOption(reader, td); break; case "累计期权": ReadAccumulatorOption(reader, td); break; case "累计期权三段式": ReadAccumulatorOptionSegmented(reader, td); break; case "现金流交易": ReadCashFlow(reader, td); break; case "Risky期权": ReadRisky(reader, td); break; default: throw new ServiceException("交易类型 填写错误,不支持:" + td.TradeType); } } if (!td.Strike.HasValue && TradeHelper.IsStrikeRequired(td)) { throw new ServiceException("[基本要素]执行价格 必须填写"); } //了结信息 td.CloseRequest = ReadCloseData(reader, td); if (td.CloseRequest != null && td.SalesCommission != null) { td.SalesCommission.IsValid = true; } if (td.TradeType != "自定义交易") { //结构化交易 reader.SetTopType("结构化交易"); td.StructureType = reader.GetString("结构化类型"); if (!string.IsNullOrWhiteSpace(td.StructureType)) { td.CalcId = reader.GetString("组合标识"); td.MetaDic["合约编号"] = reader.GetString("合约编号"); if (string.IsNullOrEmpty(td.CalcId)) { td.CalcId = td.TradeNumber.TrimToNull() ?? td.MetaDic["合约编号"]; } if (string.IsNullOrEmpty(td.CalcId)) { throw new ServiceException($"[结构化交易]组合标识 必须填写"); } } } //读取组合标的 if (synthetic != null) { reader.SetTopType("组合标的"); var sulist = new List(4); var codeSet = synthetic.GetUnderlyingCodes().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}"); } sulist.Add(new UnderlyingPriceModel { UnderlyingCode = code, Price = reader.GetDouble("标的" + i + "_价格", true).Value }); } if (codeSet.Any()) { throw new ServiceException("[组合标的]未填写完整"); } td.SyntheticPrices = sulist; } return td; } #region---读取奇异期权字段---- //亚式期权 private void ReadAsiaOption(DataRowReader reader, OtcOptionTradeFull td) { //均价起算日 td.AveragingPeriodStartDate = reader.GetDate("均价起算日"); //均价计算 td.PayoffType = reader.GetString("均价计算"); switch (td.PayoffType) { case "算术平均": td.PayoffType = "ArithmeticAverage"; break; case "几何平均": td.PayoffType = "GeometricAverage"; break; case "算术平均(离散)": case "算术平均(离散)": td.PayoffType = "DiscreteArithmeticAverage"; break; case "增强算术平均": td.PayoffType = "EnhancedArithmeticAverage"; break; case "ArithmeticAverage": case "GeometricAverage": case "DiscreteArithmeticAverage": case "EnhancedArithmeticAverage": default: td.PayoffType = string.IsNullOrWhiteSpace(td.PayoffType) ? "ArithmeticAverage" : throw new ServiceException("[亚式期权]均价计算 填写错误,不支持:" + td.PayoffType); break; } if (td.PayoffType == "EnhancedArithmeticAverage") { td.SettleMode = reader.GetString("结算类型"); switch (td.SettleMode) { case "递延": case "AtEnd": td.SettleMode = "AtEnd"; break; case "立即": case "AtHit": default: td.SettleMode = "AtHit"; break; } } //行权价类型 td.StrikeType = reader.GetString("行权价类型"); switch (td.StrikeType) { case "固定行权价": td.StrikeType = "Fixed"; break; case "浮动行权价": td.StrikeType = "Floating"; td.Strike = null; break; case "分段式": td.StrikeType = "Segmented"; break; case "Fixed": case "Segmented": break; case "Floating": td.Strike = null; break; default: td.StrikeType = string.IsNullOrWhiteSpace(td.StrikeType) ? "Fixed" : throw new ServiceException("[亚式期权]行权价类型 填写错误,不支持:" + td.StrikeType); break; } bool enhancedPriceRequired = td.PayoffType == "EnhancedArithmeticAverage" && td.StrikeType != "Floating"; td.EnhancedPrice = reader.GetDoubleOrPercent("增强价格", enhancedPriceRequired, td.IsMoneynessOptionData) ?? 0; //杠杆率(百分比格式) td.StrikeGearingFactor = reader.GetPercent("杠杆率"); } //障碍期权 private void ReadBarrierOption(DataRowReader reader, OtcOptionTradeFull td) { var ismoneyness = td.IsMoneynessOption == "是"; //障碍类型 td.BarrierType = reader.GetString("障碍类型", true); //障碍价格 td.BarrierLow = reader.GetDoubleOrPercent("障碍价格", true, ismoneyness).Value; //高障碍价格 td.BarrierHigh = reader.GetDoubleOrPercent("高障碍价格", false, ismoneyness) ?? 0; //补偿金额 td.Rebate = td.RebateRate = reader.GetDoubleOrPercent("补偿金额", false, td.IsUsePremiumRate == true); //高障碍补偿金额 td.RebateHigh = td.RebateHighRate = reader.GetDoubleOrPercent("高障碍补偿金额", false, td.IsUsePremiumRate == true); //障碍偏移 td.BarrierShift = reader.GetDouble("障碍偏移"); //观察方式 td.MonitorType = reader.GetString("观察方式"); //补偿按敲出日年化 td.RebateAnnualizedAtKO = reader.GetString("补偿按敲出日年化") == "是"; //补偿计息规则 td.RebateDayCount = reader.GetString("补偿计息规则") ?? ""; //补偿支付 ReadRebateType(reader, td); //观察频率 ReadObservationDates(reader, td); } //双鲨期权 private void ReadDoubleSharkOption(DataRowReader reader, OtcOptionTradeFull td) { var ismoneyness = td.IsMoneynessOption == "是"; var isPremiumRate = td.IsUsePremiumRate == true; //障碍价格 td.BarrierLow = reader.GetDoubleOrPercent("低障碍价格", true, ismoneyness).Value; //高障碍价格 td.BarrierHigh = reader.GetDoubleOrPercent("高障碍价格", false, ismoneyness) ?? 0; //高行权价 td.StrikeHigh = reader.GetDoubleOrPercent("高行权价", false, ismoneyness); //补偿金额 td.Rebate = td.RebateRate = reader.GetDoubleOrPercent("补偿金额", false, isPremiumRate); //高障碍补偿金额 td.RebateHigh = td.RebateHighRate = reader.GetDoubleOrPercent("高障碍补偿金额", false, isPremiumRate); //观察方式 td.MonitorType = reader.GetString("观察方式"); //高参与率(百分比格式) td.CallParticipationRate = reader.GetPercent("高参与率") ?? 0; if (td.CallParticipationRate.HasValue && td.CallParticipationRate.Value < 0) { throw new ServiceException("[双鲨期权]高参与率 不能小于0,当前填写值:" + td.CallParticipationRate); } //低参与率(百分比格式) td.PutParticipationRate = reader.GetPercent("低参与率") ?? 0; if (td.PutParticipationRate.HasValue && td.PutParticipationRate.Value < 0) { throw new ServiceException("[双鲨期权]低参与率 不能小于0,当前填写值:" + td.PutParticipationRate); } //补偿支付 ReadRebateType(reader, td); //观察频率 ReadObservationDates(reader, td); } //二元期权 private void ReadBinaryOption(DataRowReader reader, OtcOptionTradeFull td) { var isMoneyness = td.IsMoneynessOption == "是"; var isPremiumRate = td.IsUsePremiumRate == true; //二元类型 td.PayoffType = reader.GetString("二元类型", true); //补偿金额 td.CashOrNothingAmount = td.CashOrNothingAmountRate = reader.GetDoubleOrPercent("补偿金额", false, isPremiumRate); //高障碍价格 td.BarrierHigh = reader.GetDoubleOrPercent("高障碍价格", false, isMoneyness) ?? 0; //高障碍补偿金额 td.CashOrNothingAmountHigh = td.CashOrNothingAmountHighRate = reader.GetDoubleOrPercent("高障碍补偿金额", false, isPremiumRate); td.MonitorType = reader.GetString("观察方式"); //补偿按敲出日年化 td.RebateAnnualizedAtKO = reader.GetString("补偿按敲出日年化") == "是"; //补偿计息规则 td.RebateDayCount = reader.GetString("补偿计息规则") ?? ""; //补偿支付 ReadRebateType(reader, td); } //区间累积期权 private void ReadRangeAccOption(DataRowReader reader, OtcOptionTradeFull td) { var isMoneyness = td.IsMoneynessOption == "是"; //区间下限 td.LowerRange = reader.GetDoubleOrPercent("区间下限", true, isMoneyness).Value; //区间上限 td.UpperRange = reader.GetDoubleOrPercent("区间上限", true, isMoneyness).Value; //区间收益 td.BonusRate = reader.GetPercent("区间收益") ?? 0; //观察频率 ReadObservationDates(reader, td); } //气囊结构 private void ReadAirBagOption(DataRowReader reader, OtcOptionTradeFull td) { var isMoneyness = td.IsMoneynessOption == "是"; //障碍价格 td.BarrierLow = reader.GetDoubleOrPercent("障碍价格", true, isMoneyness).Value; //是否离散观察 td.IsDiscreteMonitored = reader.GetString("是否离散观察") == "是"; //敲入参与率(百分比格式) td.KIParticipationRate = reader.GetPercent("敲入参与率") ?? 0; if (td.KIParticipationRate < 0) { throw new ServiceException("[气囊结构]敲入参与率 不能小于0,当前填写值:" + td.KIParticipationRate); } //非敲入参与率 td.NotKIParticipationRate = reader.GetPercent("未敲入参与率") ?? 0; if (td.NotKIParticipationRate.HasValue && td.NotKIParticipationRate.Value < 0) { throw new ServiceException("[气囊结构]非敲入参与率 不能小于0,当前填写值:" + td.NotKIParticipationRate); } //收益封顶 td.HasPayoffLimit = reader.GetString("收益封顶") == "是"; if (td.HasPayoffLimit) { //收益封顶价格 td.HighStrike = reader.GetDoubleOrPercent("收益封顶价格", false, isMoneyness) ?? 0; } } //凤凰期权 private void ReadAutoCallOption(DataRowReader reader, OtcOptionTradeFull td) { var isMoneyness = td.IsMoneynessOption == "是"; //票息是否年化 td.IsFixedCoupon = reader.GetString("票息年化") != "是"; //票息率 td.Coupon = reader.GetPercent("票息率") ?? 0; //票息障碍价格 td.CouponBarrier = reader.GetDoubleOrPercent("票息障碍价格", false, isMoneyness) ?? 0; //票息日历规则 td.CouponDayCount = reader.GetString("票息日历规则"); //票息结算方式 var couponPayType = reader.GetString("票息结算方式"); switch (couponPayType) { case "产生时支付": td.CouponPayType = CouponPayTypeEnum.AtCreated; break; case "敲出时支付": td.CouponPayType = CouponPayTypeEnum.AtKnockout; break; case "期末支付": td.CouponPayType = CouponPayTypeEnum.AtMaturity; break; default: if (!string.IsNullOrEmpty(couponPayType)) { throw new ServiceException("[凤凰期权]票息结算方式 填写错误:" + couponPayType); } break; } td.CouponIncludeStartDate = reader.GetString("票息包含首日") == "是"; td.CouponUsePaymentDate = reader.GetString("使用支付日计息") == "是"; //敲出障碍价格 td.KOBarrier = reader.GetDoubleOrPercent("敲出障碍价格", true, isMoneyness) ?? 0; //敲入障碍价格 td.KIBarrier = reader.GetDoubleOrPercent("敲入障碍价格", false, isMoneyness) ?? 0; if (td.CouponPayType != CouponPayTypeEnum.AtMaturity && reader.GetString("敲入到期是否支付票息") == "否") { throw new ServiceException("只有期末支付时,【敲入到期是否支付票息】可以填否"); } //敲入到期是否支付票息(期末支付时可以选择否) td.IncludeCouponAfterKI = td.CouponPayType != CouponPayTypeEnum.AtMaturity || reader.GetString("敲入到期是否支付票息") == "是"; //敲入到期支付类别 var kiPayoffType = reader.GetString("敲入到期支付类别"); switch (kiPayoffType) { case "敲入转看跌": td.KIPayoffType = KIPayoffTypeEnum.ToPutOption; break; case "敲入转熊市价差": td.KIPayoffType = KIPayoffTypeEnum.ToPutSpreadOption; break; case "敲入转看涨": td.KIPayoffType = KIPayoffTypeEnum.ToCallOption; break; case "敲入转牛市价差": td.KIPayoffType = KIPayoffTypeEnum.ToCallSpreadOption; break; default: if (!string.IsNullOrEmpty(kiPayoffType)) { throw new ServiceException("[凤凰期权]敲入到期支付类别 填写错误:" + kiPayoffType); } break; } //敲入行权价 td.Strike = td.SpreadStrike1 = reader.GetDoubleOrPercent("敲入行权价", false, isMoneyness) ?? 0; //封顶/封底行权价 if (td.KIPayoffType == KIPayoffTypeEnum.ToPutSpreadOption || td.KIPayoffType == KIPayoffTypeEnum.ToCallSpreadOption) { td.SpreadStrike = reader.GetDoubleOrPercent("封顶/封底行权价", false, isMoneyness) ?? 0; } //敲出观察频率 ReadKOObservationDates(reader, td); //敲入观察频率 ReadObservationDates(reader, td); //年化系数在保存类中检查转换 } //雪球期权 private void ReadSnowBallOption(DataRowReader reader, OtcOptionTradeFull td) { var isMoneyness = td.IsMoneynessOption == "是"; //敲出障碍价格 td.KOBarrier = reader.GetDoubleOrPercent("敲出障碍价格", true, isMoneyness) ?? 0; //敲出赔付类别 var koPayoffType = reader.GetString("敲出赔付类别"); switch (koPayoffType) { case "票息补偿": td.KOPayoffType = KOPayoffTypeEnum.Rebate; td.IsFixedCoupon = reader.GetString("票息年化") != "是"; td.KORebate = reader.GetPercent("票息率") ?? 0; td.CouponDayCount = reader.GetString("票息日历规则"); td.AnnualizedPremiumRate = reader.GetPercent("年化期权费率"); td.CouponIncludeStartDate = reader.GetString("票息包含首日") == "是"; td.CouponUsePaymentDate = reader.GetString("使用支付日计息") == "是"; break; case "敲出转期权": td.KOPayoffType = KOPayoffTypeEnum.ToOption; td.SpreadStrikeAtKO1 = reader.GetDoubleOrPercent("敲出行权价1", true, isMoneyness) ?? 0; break; case "敲出转看涨": if (!ConsGlobal.CallPut.IsCall(td.CallPut)) { throw new ServiceException("[雪球期权]敲出赔付类别 看跌时不支持:敲出转看涨"); } td.KOPayoffType = KOPayoffTypeEnum.ToOption; td.SpreadStrikeAtKO1 = reader.GetDoubleOrPercent("敲出行权价1", true, isMoneyness) ?? 0; break; case "敲出转看跌": if (ConsGlobal.CallPut.IsCall(td.CallPut)) { throw new ServiceException("[雪球期权]敲出赔付类别 看涨时不支持:敲出转看跌"); } td.KOPayoffType = KOPayoffTypeEnum.ToOption; td.SpreadStrikeAtKO1 = reader.GetDoubleOrPercent("敲出行权价1", true, isMoneyness) ?? 0; break; case "敲出转价差期权": td.KOPayoffType = KOPayoffTypeEnum.ToSpreadOption; td.SpreadStrikeAtKO1 = reader.GetDoubleOrPercent("敲出行权价1", true, isMoneyness) ?? 0; td.SpreadStrikeAtKO = reader.GetDoubleOrPercent("敲出行权价2", true, isMoneyness) ?? 0; break; case "敲出转牛市价差": if (!ConsGlobal.CallPut.IsCall(td.CallPut)) { throw new ServiceException("[雪球期权]敲出赔付类别 看跌时不支持:敲出转牛市价差"); } td.KOPayoffType = KOPayoffTypeEnum.ToSpreadOption; td.SpreadStrikeAtKO1 = reader.GetDoubleOrPercent("敲出行权价1", true, isMoneyness) ?? 0; td.SpreadStrikeAtKO = reader.GetDoubleOrPercent("敲出行权价2", true, isMoneyness) ?? 0; break; case "敲出转熊市价差": if (ConsGlobal.CallPut.IsCall(td.CallPut)) { throw new ServiceException("[雪球期权]敲出赔付类别 看涨时不支持:敲出转熊市价差"); } td.KOPayoffType = KOPayoffTypeEnum.ToSpreadOption; td.SpreadStrikeAtKO1 = reader.GetDoubleOrPercent("敲出行权价1", true, isMoneyness) ?? 0; td.SpreadStrikeAtKO = reader.GetDoubleOrPercent("敲出行权价2", true, isMoneyness) ?? 0; break; default: if (!string.IsNullOrEmpty(koPayoffType)) { throw new ServiceException("[雪球期权]票息结算方式 填写错误:" + koPayoffType); } break; } //敲出支付方式 var koRebateType = reader.GetString("敲出支付方式"); switch (koRebateType) { case "立即支付": td.KORebateType = RebateTypeEnum.AtHit; break; case "递延至期末支付": td.KORebateType = RebateTypeEnum.AtEnd; break; default: if (!string.IsNullOrEmpty(koRebateType)) { throw new ServiceException("[雪球期权]敲出支付方式 填写错误:" + koRebateType); } break; } //票息支付日期 //td.KOObservationSettleDates = reader.GetString("票息支付日期"); //if (!string.IsNullOrWhiteSpace(td.KOObservationSettleDates)) //{ // td.KOObservationSettleDates = Regex.Replace(td.KOObservationSettleDates, "\\s0+:00:00", ""); //} //初始敲入状态 td.IsInitialKnockedIn = reader.GetString("初始敲入") == "是"; //敲入障碍价格 td.KIBarrier = reader.GetDoubleOrPercent("敲入障碍价格", false, isMoneyness) ?? 0; //敲入到期支付类别 var kiPayoffType = reader.GetString("敲入到期支付类别"); switch (kiPayoffType) { case "无": if (td.IsInitialKnockedIn) { throw new ServiceException("[雪球期权]敲入到期支付类别 初始敲入为是时不支持:无"); } td.KIPayoffType = KIPayoffTypeEnum.None; break; case "敲入转看跌": if (!ConsGlobal.CallPut.IsCall(td.CallPut)) { throw new ServiceException("[雪球期权]敲入到期支付类别 看跌时不支持:敲入转看跌"); } td.KIPayoffType = KIPayoffTypeEnum.ToPutOption; td.Strike = td.SpreadStrike1 = reader.GetDoubleOrPercent("敲入行权价", false, isMoneyness) ?? 0; break; case "敲入转熊市价差": if (!ConsGlobal.CallPut.IsCall(td.CallPut)) { throw new ServiceException("[雪球期权]敲入到期支付类别 看跌时不支持:敲入转熊市价差"); } td.KIPayoffType = KIPayoffTypeEnum.ToPutSpreadOption; td.Strike = td.SpreadStrike1 = reader.GetDoubleOrPercent("敲入行权价", false, isMoneyness) ?? 0; td.SpreadStrike = reader.GetDoubleOrPercent("封顶/封底行权价", false, isMoneyness) ?? 0; break; case "敲入转看涨": if (ConsGlobal.CallPut.IsCall(td.CallPut)) { throw new ServiceException("[雪球期权]敲入到期支付类别 看涨时不支持:敲入转看涨"); } td.KIPayoffType = KIPayoffTypeEnum.ToCallOption; td.Strike = td.SpreadStrike1 = reader.GetDoubleOrPercent("敲入行权价", false, isMoneyness) ?? 0; break; case "敲入转牛市价差": if (ConsGlobal.CallPut.IsCall(td.CallPut)) { throw new ServiceException("[雪球期权]敲入到期支付类别 看涨时不支持:敲入转牛市价差"); } td.KIPayoffType = KIPayoffTypeEnum.ToCallSpreadOption; td.Strike = td.SpreadStrike1 = reader.GetDoubleOrPercent("敲入行权价", false, isMoneyness) ?? 0; td.SpreadStrike = reader.GetDoubleOrPercent("封顶/封底行权价", false, isMoneyness) ?? 0; break; default: if (!string.IsNullOrEmpty(kiPayoffType)) { throw new ServiceException("[雪球期权]敲入到期支付类别 填写错误:" + kiPayoffType); } break; } //红利票息 td.Coupon = reader.GetDoubleOrPercent("红利票息", false, isMoneyness) ?? 0; //敲出观察频率 ReadKOObservationDates(reader, td); //敲入观察频率 ReadObservationDates(reader, td); //年化系数在保存类中检查转换 } //累计期权 三段式 private void ReadAccumulatorOptionSegmented(DataRowReader reader, OtcOptionTradeFull td) { var isMoneyness = td.IsMoneynessOption == "是"; td.AccumulatorStructureType = AccumulatorStructureTypeEnum.Segmented; td.AccumuTradeAmount = reader.GetDouble("沽购数量") ?? 0; //执行价2 td.Strike2 = reader.GetDoubleOrPercent("执行价2", true, isMoneyness) ?? 0; //执行价3 td.Strike3 = reader.GetDoubleOrPercent("执行价3", true, isMoneyness) ?? 0; //敲出障碍价格 td.KOBarrier = reader.GetDoubleOrPercent("敲出障碍价格", true, isMoneyness) ?? 0; //上端收益类型 var payType = reader.GetString("上端收益类型"); switch (payType) { case "浮动": td.PayoffType = "浮动"; break; case "固定(票息)": td.PayoffType = "固定"; break; default: if (!string.IsNullOrEmpty(payType)) { throw new ServiceException("[累计期权三段式]上端收益类型 填写错误:" + payType); } td.PayoffType = "浮动"; break; } if (td.PayoffType == "固定") { //票息率 td.Coupon = reader.TryGetDoubleOrPercent("票息1", true, out var percent) ?? 0; td.Coupon2 = reader.TryGetDoubleOrPercent("票息2", true, out var percent1) ?? 0; td.CouponPercent = percent; //票息是否年化 td.IsFixedCoupon = reader.GetString("票息年化") != "是"; //票息日历规则 td.CouponDayCount = reader.GetString("票息日历规则"); //乘数1 td.Multiplier = reader.GetDouble("乘数1", false) ?? 0; //乘数2 td.Multiplier2 = reader.GetDouble("乘数2", false) ?? 0; //乘数3 td.Multiplier3 = reader.GetDouble("乘数3", false) ?? 0; } else { //乘数1 td.Multiplier = reader.GetDouble("乘数1", false) ?? 1; if (td.Multiplier <= 0) { throw new ServiceException("[累计期权三段式]乘数1不能小于等于0 填写错误:" + td.Multiplier); } //乘数2 td.Multiplier2 = reader.GetDouble("乘数2", false) ?? 1; if (td.Multiplier2 <= 0) { throw new ServiceException("[累计期权三段式]乘数2不能小于等于0 填写错误:" + td.Multiplier2); } //乘数3 td.Multiplier3 = reader.GetDouble("乘数3", false) ?? 1; if (td.Multiplier3 <= 0) { throw new ServiceException("[累计期权三段式]乘数3不能小于等于0 填写错误:" + td.Multiplier3); } } //敲出是否终止 td.EarlyTerminate = reader.GetString("敲出是否终止") == "是"; //结算方式 var settlementMode = reader.GetString("结算方式1"); switch (settlementMode) { case "现金当日": case "现金结算(当日)": td.SettlementMode = "现金当日"; break; case "实物交割": td.SettlementMode = "实物交割"; break; case "现金实物合并结算": td.SettlementMode = "现金实物合并结算"; break; default: if (!string.IsNullOrEmpty(settlementMode)) { throw new ServiceException("[累计期权三段式]结算方式1 填写错误:" + settlementMode); } td.SettlementMode = "现金当日"; break; } DictionarySerivice dictionarySerivice = new DictionarySerivice(); var dic = dictionarySerivice.GetForwardDateDic(); if (td.SettlementMode == "实物交割" || td.SettlementMode == "现金实物合并结算") { td.ForwardPriceType = reader.GetString("远期期初价类型1"); td.ForwardDateType = reader.GetString("远期到期日类型1"); if (td.ForwardDateType != "标的到期日" && td.ForwardDateType != "期权到期日") { var dicModel = dic.FirstOrDefault(l => l.Value == td.ForwardDateType); if (dicModel.Value == null || dicModel.Value == "") { throw new ServiceException("[累计期权三段式]远期到期日类型1 系统中未找到:" + td.ForwardDateType); } } } var settlementMode2 = reader.GetString("结算方式2"); switch (settlementMode2) { case "现金当日": case "现金结算(当日)": td.SettlementMode2 = "现金当日"; break; case "实物交割": td.SettlementMode2 = "实物交割"; break; case "现金实物合并结算": td.SettlementMode2 = "现金实物合并结算"; break; default: if (!string.IsNullOrEmpty(settlementMode2)) { throw new ServiceException("[累计期权三段式]结算方式2 填写错误:" + settlementMode2); } td.SettlementMode2 = "现金当日"; break; } td.ForwardPriceType2 = reader.GetString("远期期初价类型2"); td.ForwardDateType2 = reader.GetString("远期到期日类型2"); if (td.SettlementMode2 == "实物交割" || td.SettlementMode2 == "现金实物合并结算") { if (td.ForwardDateType2 != "标的到期日" && td.ForwardDateType2 != "期权到期日") { var dicModel = dic.FirstOrDefault(l => l.Value == td.ForwardDateType2); if (dicModel.Value == null || dicModel.Value == "") { throw new ServiceException("[累计期权三段式]远期到期日类型2 系统中未找到:" + td.ForwardDateType2); } } } //结算方式 var settlementMode3 = reader.GetString("结算方式3"); switch (settlementMode3) { case "现金当日": case "现金结算(当日)": td.SettlementMode3 = "现金当日"; break; case "实物交割": td.SettlementMode3 = "实物交割"; break; case "现金实物合并结算": td.SettlementMode3 = "现金实物合并结算"; break; default: if (!string.IsNullOrEmpty(settlementMode3)) { throw new ServiceException("[累计期权三段式]结算方式3 填写错误:" + settlementMode3); } td.SettlementMode3 = "现金当日"; break; } if (td.SettlementMode3 == "实物交割" || td.SettlementMode3 == "现金实物合并结算") { td.ForwardPriceType3 = reader.GetString("远期期初价类型3"); td.ForwardDateType3 = reader.GetString("远期到期日类型3"); if (td.ForwardDateType3 != "标的到期日" && td.ForwardDateType3 != "期权到期日") { var dicModel = dic.FirstOrDefault(l => l.Value == td.ForwardDateType3); if (dicModel.Value == null || dicModel.Value == "") { throw new ServiceException("[累计期权三段式]远期到期日类型3 系统中未找到:" + td.ForwardDateType3); } } } //敲出观察频率 ReadKOObservationDates(reader, td); //保存 还是 用累计期权 保存 只不过 导入时取对应三段式列时需要判断 td.TradeType = "累计期权"; } //累计期权 private void ReadAccumulatorOption(DataRowReader reader, OtcOptionTradeFull td) { var isMoneyness = td.IsMoneynessOption == "是"; td.AccumulatorStructureType = AccumulatorStructureTypeEnum.Standard; td.AccumuTradeAmount = reader.GetDouble("沽购数量") ?? 0; //敲出障碍价格 td.KOBarrier = reader.GetDoubleOrPercent("敲出障碍价格", true, isMoneyness) ?? 0; //上端收益类型 var payType = reader.GetString("上端收益类型"); switch (payType) { case "浮动": td.PayoffType = "浮动"; break; case "固定(票息)": td.PayoffType = "固定"; break; default: if (!string.IsNullOrEmpty(payType)) { throw new ServiceException("[累计期权]上端收益类型 填写错误:" + payType); } td.PayoffType = "浮动"; break; } if (td.PayoffType == "固定") { //票息率 td.Coupon = reader.TryGetDoubleOrPercent("票息", true, out var percent) ?? 0; td.CouponPercent = percent; //票息是否年化 td.IsFixedCoupon = reader.GetString("票息年化") != "是"; //票息日历规则 td.CouponDayCount = reader.GetString("票息日历规则"); } //杠杆倍数 if (PS.Config.ErpElement.AccumulatorShowMultiplier1) { td.CallMultiplier = td.PutMultiplier = 1; var multiplier = reader.GetDouble("杠杆倍数", false) ?? 1; if (multiplier <= 0) { throw new ServiceException("[累计期权]杠杆倍数不能小于等于0 填写错误:" + multiplier); } if (ConsGlobal.CallPut.IsCall(td.CallPut)) { td.PutMultiplier = multiplier; } else { td.CallMultiplier = multiplier; } } else { //看涨乘数 td.CallMultiplier = reader.GetDouble("看涨乘数", false) ?? 1; if (td.CallMultiplier <= 0) { throw new ServiceException("[累计期权]看涨乘数不能小于等于0 填写错误:" + td.CallMultiplier); } //看跌乘数 td.PutMultiplier = reader.GetDouble("看跌乘数", false) ?? 1; if (td.PutMultiplier <= 0) { throw new ServiceException("[累计期权]看跌乘数不能小于等于0 填写错误:" + td.PutMultiplier); } } //结算方式 var settlementMode = reader.GetString("结算方式"); switch (settlementMode) { case "现金当日": case "现金结算(当日)": td.SettlementMode = "现金当日"; break; case "现金期末": case "现金结算(期末)": td.SettlementMode = "现金期末"; td.AccumuType = reader.GetString("累计类型") ?? ""; if (!string.IsNullOrWhiteSpace(td.AccumuType) && td.AccumuType != "子弹") { throw new ServiceException("[累计期权]结算方式 填写错误:" + td.AccumuType); } break; case "实物交割": td.SettlementMode = "实物交割"; td.AccumuType = reader.GetString("累计类型") ?? ""; if (!string.IsNullOrWhiteSpace(td.AccumuType) && td.AccumuType != "区间现金结算") { throw new ServiceException("[累计期权]结算方式 填写错误:" + td.AccumuType); } break; case "现金实物合并结算": td.SettlementMode = "现金实物合并结算"; td.AccumuType = reader.GetString("累计类型") ?? ""; if (!string.IsNullOrWhiteSpace(td.AccumuType) && td.AccumuType != "区间现金结算") { throw new ServiceException("[累计期权]结算方式 填写错误:" + td.AccumuType); } break; default: if (!string.IsNullOrEmpty(settlementMode)) { throw new ServiceException("[累计期权]结算方式 填写错误:" + settlementMode); } td.SettlementMode = "现金当日"; break; } if (td.SettlementMode == "实物交割" || td.SettlementMode == "现金实物合并结算") { DictionarySerivice dictionarySerivice = new DictionarySerivice(); var dic = dictionarySerivice.GetForwardDateDic(); td.ForwardPriceType = reader.GetString("远期期初价类型"); td.ForwardDateType = reader.GetString("远期到期日类型"); if (td.ForwardDateType != "标的到期日" && td.ForwardDateType != "期权到期日") { var dicModel = dic.FirstOrDefault(l => l.Value == td.ForwardDateType); if (dicModel.Value == null || dicModel.Value == "") { throw new ServiceException("[累计期权]远期到期日类型 系统中未找到:" + td.ForwardDateType); } } } //敲出是否终止 td.EarlyTerminate = reader.GetString("敲出是否终止") == "是"; //敲出观察频率 ReadKOObservationDates(reader, td); } //现金流交易 private void ReadCashFlow(DataRowReader reader, OtcOptionTradeFull td) { td.CouponDayCount = reader.GetString("计息日历规则"); td.ProfitRate = reader.GetPercent("利率") ?? 0; var rateType = reader.GetString("利率类型"); switch (rateType) { case "实际利率": td.RateType = CashFlowRateTypeEnum.实际利率; break; case "年化利率": td.RateType = CashFlowRateTypeEnum.年化利率; break; default: break; } td.PrepayRatio = reader.GetPercent("返还比例") ?? 0; var depositType = reader.GetString("资金类型"); switch (depositType) { case "成本摊还": td.DepositType = CashflowDepositTypeEnum.成本摊还; break; case "资金收益": td.DepositType = CashflowDepositTypeEnum.资金收益; break; default: break; } } //自定义交易 private void ReadCustomizedOption(DataRowReader reader, OtcOptionTradeFull td) { var dicInfo = new StructureService(OptUser).QueryStructureMap(td.IsGroup > 0 ? StructureRangeEnum.BALCK_TRADE : StructureRangeEnum.CUSTOM_TRADE); if (!dicInfo.TryGetValue(td.StructureType ?? td.TradeType, out var dictionaryNames)) { return; } var propertys = new List(); dictionaryNames.ForEach(x => { string value = null; switch (x.ColumnType) { case StructureColumnTypeEnum.NUMBER: value = reader.GetDouble(x.ColumnName)?.ToString("0.########"); break; case StructureColumnTypeEnum.DATE: value = reader.GetDate(x.ColumnName)?.ToString("yyyy-MM-dd"); break; case StructureColumnTypeEnum.COMBO_BOX_SINGLE: value = reader.GetString(x.ColumnName); break; case StructureColumnTypeEnum.TEXT: default: value = reader.GetString(x.ColumnName); break; } if (!string.IsNullOrWhiteSpace(value)) { propertys.Add(new ExtendInfoModel() { name = x.ColumnName, value = value }); } }); td.ExtendInfo = JsonHelper.Serialize(propertys); } //Risky private void ReadRisky(DataRowReader reader, OtcOptionTradeFull td) { //执行价格(亚式浮动行权价类型时非必填) td.Strike1 = td.Strike; td.Strike2 = reader.GetDoubleOrPercent("执行价格2", true, td.IsMoneynessOption == "是"); td.Strike3 = reader.GetDoubleOrPercent("执行价格3", true, td.IsMoneynessOption == "是"); td.ParticipationRate1 = td.ParticipationRate.Value; td.ParticipationRate2 = reader.GetPercent("参与率2", true) ?? 0; td.ParticipationRate3 = reader.GetPercent("参与率3", true) ?? 0; td.ParticipationRate = 1; td.PremiumPayDate = td.ExerciseDate; } //读取补偿支付 private void ReadRebateType(DataRowReader reader, OtcOptionTradeFull td) { td.RebateType = reader.GetString("补偿支付"); switch (td.RebateType) { case "立即": td.RebateType = "AtHit"; break; case "递延": td.RebateType = "AtEnd"; break; case "AtHit": case "AtEnd": break; default: td.RebateType = string.IsNullOrWhiteSpace(td.RebateType) ? "AtHit" : throw new ServiceException($"[{td.TradeType}]补偿支付 填写错误,不支持:{td.RebateType}"); break; } } //读取敲入观察频率 private void ReadObservationDates(DataRowReader reader, OtcOptionTradeFull td) { var topType = reader.TopType; reader.SetTopType("敲入观察日设置"); td.ObservationDates = reader.GetString("自定义观察日"); if (string.IsNullOrWhiteSpace(td.ObservationDates)) { var ruleStr = reader.GetString("使用规则生成观察日"); td.ObservationDates = GenObservationDatesByRule(td, ruleStr); } else { var observationDates = td.ObservationDates.Replace('/', '-').Split(',').Select(O => O.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)[0]); td.ObservationDates = string.Join(",", observationDates); if (td.ObservationDates.Split(',').Any(n => !DateTime.TryParse(n, out _))) { throw new ServiceException($"[{td.TradeType}][敲入观察日设置]自定义观察日 填写错误:不符合要求的格式"); } if (td.ObservationDates.Split(',').Any(n => { DateTime.TryParse(n, out var date); return QdpCalendarHelper.IsHoliday(date); })) { throw new ServiceException($"[{td.TradeType}][敲入观察日设置]自定义观察日 填写错误:观察日存在非交易日,导入失败"); } #region 导入时,写入敲入观察频率 string termStr = null; var ruleStr = reader.GetString("使用规则生成观察日"); if (!string.IsNullOrWhiteSpace(ruleStr)) { switch (ruleStr) { case "每日": termStr = "1D"; break; case "每周": termStr = "1W"; break; case "每月": termStr = "1M"; break; case "每年": termStr = "1Y"; break; } if (termStr == null) { var strs = ruleStr.Split(new[] { ',' }); termStr = strs[0].Trim(); } //敲入观察周期写入metadic中 td.MetaDic["敲入观察周期"] = termStr; } #endregion } reader.SetTopType(topType); } //读取观察日设置并生成观察日列表 private static string GenObservationDatesByRule(OtcOptionTradeFull td, string ruleStr, bool isKO = false) { if (string.IsNullOrWhiteSpace(ruleStr)) { return null; } string termStr = null; var dayConvention = BusinessDayConvention.Following; //假日调整 var alignEnd = true; //是否向到期日对齐 switch (ruleStr) { case "每日": termStr = "1D"; break; case "每周": termStr = "1W"; break; case "每月": termStr = "1M"; break; case "每年": termStr = "1Y"; break; } var kstr = isKO ? "敲出" : "敲入"; if (termStr == null) { var strs = ruleStr.Split(new[] { ',' }); termStr = strs[0].Trim(); if (!Term.IsTerm(termStr)) { throw new ServiceException($"[{td.TradeType}][{kstr}观察日设置]使用规则生成观察日 观察日间隔填写错误"); } //假日调整 if (strs.Length > 1) { var observationHolidayType = strs[1]; switch (observationHolidayType) { case "向后调整": dayConvention = BusinessDayConvention.Following; break; case "向前调整": dayConvention = BusinessDayConvention.Previous; break; case "不调整": dayConvention = BusinessDayConvention.None; break; default: dayConvention = string.IsNullOrWhiteSpace(observationHolidayType) ? BusinessDayConvention.Following : throw new ServiceException($"[{td.TradeType}][{kstr}观察日设置]使用规则生成观察日 假日调整填写错误,不支持:" + observationHolidayType); break; } } //对齐规则 if (strs.Length > 2) { var alignEndStr = strs[2]; switch (alignEndStr) { case "向开始日对齐": case "向到期日对齐": break; default: alignEndStr = string.IsNullOrWhiteSpace(alignEndStr) ? "向开始日对齐" : throw new ServiceException($"[{td.TradeType}][{kstr}观察日设置]使用规则生成观察日 对齐规则填写错误,不支持:" + alignEndStr); break; } alignEnd = alignEndStr == "向到期日对齐"; } } //敲入观察周期写入metadic中 if (!isKO) { td.MetaDic["敲入观察周期"] = termStr; } else if (isKO) { td.MetaDic["敲出观察周期"] = termStr; } return QdpObservationHelper.GetObservationDateString(td.TradeDate.Value, td.ExerciseDate.Value, termStr, dayConvention, alignEnd); } //读取敲出观察频率 private void ReadKOObservationDates(DataRowReader reader, OtcOptionTradeFull td) { var topType = reader.TopType; reader.SetTopType("敲出观察日设置"); var separators = new[] { ',', ';', ',', ';' }; List obDateList, stDateList; string[] barriePrices, coupons; //观察日 var obDatesStr = reader.GetString("观察日"); if (string.IsNullOrWhiteSpace(obDatesStr)) { var ruleStr = reader.GetString("敲出观察频率"); td.KOObservationDates = GenObservationDatesByRule(td, ruleStr, true); //结算日期 var stDatesStr = reader.GetString("结算日期"); if (string.IsNullOrWhiteSpace(stDatesStr)) { if (!string.IsNullOrEmpty(td.KOObservationDates)) { td.KOObservationSettleDates = td.KOObservationDates; } } if (!string.IsNullOrEmpty(td.KOObservationDates)) { var odNum = td.KOObservationDates.Split(",").Count(); var koBarrierStr = ""; var koKORebate = ""; for (var i = 0; i < odNum; i++) { koBarrierStr += td.KOBarrier + ","; koKORebate += "0,"; } if (!string.IsNullOrEmpty(koBarrierStr)) { koBarrierStr = koBarrierStr.Substring(0, koBarrierStr.Length - 1); } if (!string.IsNullOrEmpty(koKORebate) && !td.TradeType.Contains("累计")) { koKORebate = koKORebate.Substring(0, koKORebate.Length - 1); } td.KOObservationDates += ";" + koBarrierStr + (!td.TradeType.Contains("累计")?(";" + koKORebate):""); } } else { td.MetaDic["敲出观察周期"] = "1D"; var strArr = obDatesStr.Split(separators, StringSplitOptions.RemoveEmptyEntries); obDateList = new List(strArr.Length); foreach (var str in strArr) { if (DateTime.TryParse(str, out var dt)) { obDateList.Add(dt.ToString("yyyy-MM-dd")); if (dt < td.TradeDate.Value) { throw new ServiceException($"[{td.TradeType}][敲出观察日设置]观察日期 填写错误,观察日期'{str}'不能小于开始日期'{td.TradeDate.Value:yyyy-MM-dd}'"); } if (dt > td.ExerciseDate.Value) { throw new ServiceException($"[{td.TradeType}][敲出观察日设置]观察日期 填写错误,观察日期'{str}'不能大于到期日期'{td.ExerciseDate.Value:yyyy-MM-dd}'"); } if (QdpCalendarHelper.IsHoliday(dt)) { throw new ServiceException($"[{td.TradeType}][敲出观察日设置]观察日期 填写错误,存在非交易日'{str}'"); } } else { throw new ServiceException($"[{td.TradeType}][敲出观察日设置]观察日期 填写错误,日期'{str}'未能解析成功"); } } //结算日期 var stDatesStr = reader.GetString("结算日期"); if (string.IsNullOrWhiteSpace(stDatesStr)) { stDateList = obDateList; } else { strArr = stDatesStr.Split(separators, StringSplitOptions.RemoveEmptyEntries); stDateList = new List(strArr.Length); foreach (var str in strArr) { if (DateTime.TryParse(str, out var dt)) { stDateList.Add(dt.ToString("yyyy-MM-dd")); if (dt < td.TradeDate.Value) { throw new ServiceException($"[{td.TradeType}][敲出观察日设置]结算日期 填写错误,日期'{str}'不能小于开始日期'{td.TradeDate.Value:yyyy-MM-dd}'"); } } else { throw new ServiceException($"[{td.TradeType}][敲出观察日设置]结算日期 填写错误,日期'{str}'未能解析成功"); } } if (stDateList.Count != obDateList.Count) { throw new ServiceException($"[{td.TradeType}][敲出观察日设置]结算日期 列表数量应和观察日一致"); } } //障碍价格 var barriePricesStr = reader.GetString("障碍价格"); if (string.IsNullOrWhiteSpace(barriePricesStr)) { barriePrices = obDateList.Select(n => td.KOBarrier.HasValue ? td.KOBarrier.Value.ToString("0.######") : string.Empty).ToArray(); } else { if (td.TradeType == "凤凰期权") { barriePrices = barriePricesStr.Split(separators); } else { barriePrices = barriePricesStr.Split(separators, StringSplitOptions.RemoveEmptyEntries); } if (barriePrices.Length != obDateList.Count) { throw new ServiceException($"[{td.TradeType}][敲出观察日设置]障碍价格 列表数量应和观察日一致"); } var converts = barriePrices.Select(str => { var percent = str.EndsWith("%"); if (percent) { str = str.TrimEnd('%'); } if (td.TradeType == "凤凰期权") { return double.TryParse(str, out var num) ? (percent ? num / 100 : num).ToString() : ""; } else { return double.TryParse(str, out var num) ? (percent ? num / 100 : num).ToString() : throw new ServiceException($"[{td.TradeType}][敲出观察日设置]障碍价格 数字填写错误"); } }).ToArray(); barriePrices = converts.Select(n => string.IsNullOrEmpty(n) ? "" : Convert.ToDouble(n).ToString("0.######")).ToArray(); } //票息率 var couponsStr = reader.GetString("票息率"); if (string.IsNullOrWhiteSpace(couponsStr)) { var couponStr = (td.TradeType == "雪球期权" ? td.KORebate : td.Coupon).ToString("0.########"); if (couponStr.Length < 1) { couponStr = "0"; } coupons = obDateList.Select(n => couponStr).ToArray(); } else { coupons = couponsStr.Split(separators, StringSplitOptions.RemoveEmptyEntries); if (coupons.Length != obDateList.Count) { throw new ServiceException($"[{td.TradeType}][敲出观察日设置]票息率 列表数量应和观察日一致"); } var converts = coupons.Select(str => { var percent = str.EndsWith("%"); if (percent) { str = str.TrimEnd('%'); } return double.TryParse(str, out var num) ? (percent ? num / 100 : num) : throw new ServiceException($"[{td.TradeType}][敲出观察日设置]票息率 数字填写错误"); }).ToArray(); coupons = converts.Select(n => n.ToString("0.########")).ToArray(); } td.KOObservationDates = string.Join(";", new[] { string.Join(",",obDateList), string.Join(",",barriePrices), string.Join(",",coupons) }); td.KOObservationSettleDates = string.Join(",", stDateList); } reader.SetTopType(topType); } #endregion #region---读取交易了结---- //了结信息 private TradeCloseRequestModel ReadCloseData(DataRowReader reader, OtcOptionTradeFull td) { reader.SetTopType("了结信息"); //了结方式 var closeType = reader.GetString("了结方式"); switch (closeType) { case "平仓": break; case "行权": case "到期": case "提前行权": if (td.TradeType == "自定义交易") { throw new ServiceException("[了结信息]了结方式 填写错误,结构类型为'自定义交易'时仅支持'平仓',填写值为:" + closeType); } break; default: if (!string.IsNullOrWhiteSpace(closeType)) { throw new ServiceException("[了结信息]了结方式 填写错误,不支持:" + closeType); } return null; } var model = new TradeCloseRequestModel { CloseType = closeType }; //了结日期 model.CloseDate = model.CloseType == "到期" ? td.ExerciseDate.Value : reader.GetDate("了结日期") ?? td.ExerciseDate.Value; //了结标的价格 model.UnderlyingPrice = reader.GetDouble("了结标的价格", "了结时标的价格", true).Value; //行权标的均价 model.UnderlyingAvgPrice = reader.GetDouble("行权标的均价"); if (closeType == "平仓") { //平仓总额 model.UnwindTotalFee = reader.GetDouble("平仓总额"); if (!model.UnwindTotalFee.HasValue) { //平仓单价 model.UnwindPrice = reader.GetDouble("平仓单价"); //平仓单价比例 model.UnwindPriceRate = reader.GetDouble("平仓单价比例"); } //平仓波动率 model.UnwindVolatility = reader.GetPercent("平仓波动率"); } //导入了结方式为到期并且平仓总额不为空 if (closeType == "到期"&& reader.GetDouble("平仓总额")!=null) { //平仓总额 model.UnwindTotalFee = reader.GetDouble("平仓总额"); } return model; } // 执行了结 private void ExecuteClose(trade dbTrade, TradeCloseRequestModel req) { if (req == null) { return; } dbTrade.TradeStatus = "确认成交"; var dbTradeCashList = DbContext.trade_cash.Where(t => t.TradeId == dbTrade.id && !t.IsDeleted).ToList(); var dbClientCashInOutList = DbContext.ClientCashInCashOut.Where(t => dbTrade.id == t.TradeId.Value).ToList(); DbContext.trade_cash.RemoveRange(dbTradeCashList); var tradeCashIds = dbTradeCashList.Select(x => x.id); var tradeCashDetials = DbContext.trade_cash_detail.Where(x => tradeCashIds.Contains(x.TradeCashId)); DbContext.trade_cash_detail.RemoveRange(tradeCashDetials); DbContext.ClientCashInCashOut.RemoveRange(dbClientCashInOutList); var newTC = new trade_cash { ValidState = "Valid", OptId = UserId, OptName = UserName, OptDate = DateTime.Now, Action = ClientCashInCashOut.系统操作_期权费, ExceciseType = "现金", TradeId = dbTrade.id, ValueDate = dbTrade.TradeDate.Value, Strike = dbTrade.IsMoneynessOptionData ? dbTrade.Strike * (dbTrade.SpotPrice ?? 0.0) : dbTrade.Strike, Notional = dbTrade.Notional }; var CountRatio = UnderlyingDataProvider.GetCountRatio(dbTrade.UnderlyingCode); newTC.TradeAmount = newTC.Notional / CountRatio; newTC.Amount = ((dbTrade.BuySell == "买入" ? -1 : 1) * dbTrade.TradePrice) ?? 0; newTC.TradeType = dbTrade.BuySell; newTC.Comments = "导入交易了结添加期权费"; DbContext.trade_cash.Add(newTC); DbContext.SaveChanges(); if (dbTrade.PremiumPayDate.HasValue && dbTrade.PremiumPayDate > dbTrade.TradeDate) { newTC.Status = TradeCashStatusEnum.冻结; } else { var client = DataCacheProvider.GetClientDataSource().GetData(dbTrade.ClientId); var newCC = new ClientCashInCashOut { Number = UniqueTimeId.GetStr(), HappenDate = dbTrade.PremiumPayDate ?? dbTrade.TradeDate, Action = ClientCashInCashOut.系统操作_期权费, Direction = ClientCashInCashOut.应收, OptDate = DateTime.Now, OptId = UserId, OptName = UserName, CreateDate = DateTime.Now, CreatorId = UserId, CreatorName = UserName, ClientId = dbTrade.ClientId, ClientName = dbTrade.ClientName, ClientNumber = client.Number, TradeId = dbTrade.id, TradeCashId = newTC.id, Money = (dbTrade.BuySell == "买入" ? 1 : -1) * dbTrade.TradePrice, ValidState = "Valid", //权利金默认为应收 已执行 TradeNumber = dbTrade.TradeNumber, IsGroup = dbTrade.IsGroup }; //根据是否需要结算设置资金状态 newTC.Status = TradeCashStatusEnum.已执行; newTC.SettleDate = newTC.ValueDate; newCC.State = ClientCashInCashOut.已确认; newCC.SettleDate = newTC.ValueDate; DbContext.ClientCashInCashOut.Add(newCC); } DbContext.SaveChanges(); new TradeCashService(this).SaveTradeCashDetail(newTC); req.ImportFlag = "交易导入"; req.SkipWorkflow = true; req.TradeNumber = dbTrade.TradeNumber; req.CloseTradeAmountRate = 1; req.CloseTradeAmount = dbTrade.TradeAmount; new OtcTradeCloseService(this).ExecuteClose(req, true); } #endregion #region---内部业务类---- class DataRowReader { DataRow _row; readonly Dictionary _colMap; public DataRowReader(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; var customField = new List(); 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(); if (preCol1 == "自定义属性") { customField.Add(col2); } _colMap[preCol1 + col2] = index; } CustomField = customField.ToArray(); } public string TopType { get; private set; } /// /// 自定义字段名 /// public string[] CustomField { 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 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? TryGetDoubleOrPercent(string fieldName, bool required, out bool percent) { var str = GetString(fieldName, required); if (!required && string.IsNullOrWhiteSpace(str)) { percent = false; return null; } 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 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 /// /// 组合销售提成信息 /// private List GetSalesIds(string salesmenNames) { var salesNameArr = salesmenNames.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (!salesNameArr.Any()) { return null; } var salesDic = new SalesmenDataService(OptUser).GetSalesIdByNames(salesNameArr) .ToDictionary(n => n.Name, n => n.id); var results = new List(salesNameArr.Length); foreach (var name in salesNameArr) { if (!salesDic.TryGetValue(name, out var salesId)) { throw new ServiceException(PS.Config.ErpElement.SaleMode == YLErp.Configuration.Enums.SaleMode.CustomerManager ? $"客户经理'{name}'未找到" : $"销售员'{name}'未找到"); } results.Add(salesId); } return results; } private int GetClientId(string clientName) { if (string.IsNullOrWhiteSpace(clientName)) { return 0; } var client = DataCacheProvider.GetClientDataSource().AsQueryable().FirstOrDefault(p => p.Name == clientName); if (client != null) { return client.id; } return 0; } } }