using Qdp.ComputeServiceV2.Data.CommonModels.TradeInfos; using Qdp.Foundation.Utilities; using Qdp.Pricing.Library.Common.Interfaces; using Qdp.Pricing.Library.Options.Products.Asian; using System.Data; using System.Globalization; using YLErp.BLL; using YLErp.BLL.Eod; using YLErp.Commons; using YLErp.CustomizedBizLogic; using YLErp.DBModels.Consts; using YLErp.DBModels.Enums; using YLErp.DBModels.Helpers; using YLErp.Model.Enum; using YLErp.Modules.CalculationModule; using YLErp.Modules.ClientModule; namespace YLErp.Modules.TradeModule.DealModule { /// /// 交易了结处理服务 /// public class OtcTradeCloseService : TradeCashService { public OtcTradeCloseService(OptUserInfo userInfo) : base(userInfo) { } public OtcTradeCloseService(YLBaseService baseService) : base(baseService) { } /// /// 导入批量了结 /// /// /// 当前文件中的目标期权总条数 /// 成功入库的数量 public void BatchCloseByImport(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); totalNum = table.Rows.Count; rowIndex = 1; foreach (var row in table.Rows.Cast().Skip(1)) { rowIndex++; if (row.ItemArray.All(n => string.IsNullOrWhiteSpace(n?.ToString()))) { totalNum--; continue; } reader.SetDataRow(row); var model = new TradeCloseRequestModel { ImportFlag = "批量了结导入", SkipWorkflow = true, TradeNumber = reader.GetString("交易编号", true), CloseType = reader.GetString("了结方式", true), CloseDate = reader.GetDate("了结日期", true).Value, CloseTradeAmount = reader.GetDouble("了结数量"), CloseTradeAmountRate = reader.GetPercent("了结数量比例"), UnderlyingPrice = reader.GetDouble("了结标的价格", true).Value }; if (model.CloseType == "平仓") { model.UnwindTotalFee = reader.GetDouble("平仓总额", "了结总额"); model.UnwindPrice = reader.GetDouble("平仓单价", "了结单价"); model.UnwindPriceRate = reader.GetPercent("平仓单价比例", "了结单价比例"); model.UnwindVolatility = reader.GetPercent("平仓波动率"); } if (model.CloseType == "到期") { model.UnwindTotalFee = reader.GetDouble("平仓总额", "了结总额"); } ExecuteClose(model); successNum++; } } catch (ServiceException se) { if (se.Tag != null) { throw; } throw new ServiceException($"已成功了结{successNum}条,第{rowIndex}行,{se.Message}"); } catch (Exception ex) { LogFactory.GetLogger("批量了结导入").Error(ex); throw new ServiceException($"已成功了结{successNum}条,第{rowIndex}行,发生错误:{ex.Message}", ex); } } /// /// 执行了结(适用于交易导入、批量了结、api了结,如果用作其它用途需要注意IsApproval的赋值) /// public TradeCloseResult ExecuteClose(TradeCloseRequestModel req, bool isAveragePriceOverride = false) { if (req is null) { throw new ArgumentNullException(nameof(req)); } var dbTrade = DbContext.trade.FirstOrDefault(t => t.TradeNumber == req.TradeNumber && t.ValidState != "InValid"); if (dbTrade == null) { throw new ServiceException("交易数据 不存在,交易编号:" + req.TradeNumber); } CheckRequest(dbTrade, req); if (dbTrade.Warning) { new TradeDalModule.TradeDalService(OptUser).RollbackToBeforeSettle(dbTrade, req.CloseDate); } CalcForClose(dbTrade, req, isAveragePriceOverride, out var countRatio, out var closeNotional); var isPartialClose = Math.Abs(closeNotional - dbTrade.Notional) > 1e-4; if (isPartialClose && (req.CloseType == "行权" || req.CloseType == "到期")) { throw new ServiceException("了结方式为'行权'或'到期'时,了结数量必须等于持仓数量,交易编号:" + dbTrade.TradeNumber); } #region---添加trade_cash--- var tradeCash = new trade_cash { ValidState = ConsGlobal.Valid, OptId = OptUser.UserId, OptName = OptUser.UserName, OptDate = OptDate, ExceciseType = "现金", TradeType = dbTrade.BuySell, CallPut = dbTrade.CallPut, Strike = dbTrade.IsMoneynessOptionData ? dbTrade.Strike * (dbTrade.SpotPrice ?? 0.0) : dbTrade.Strike, Notional = dbTrade.Notional, Amount = req.UnwindTotalFee ?? 0, ExtraAmount = req.ExtraAmount, TradeId = dbTrade.id, FinalPrice = req.UnderlyingAvgPrice ?? req.UnderlyingPrice, VolType = "交易", TradeAmount = dbTrade.TradeAmount, Status = TradeCashStatusEnum.已执行, ValueDate = req.CloseDate, Comments = "导入了结数据", SpotPrice = req.UnderlyingPrice, ExerciseWay = "", UnwindType = "",//部分平仓/行权、全部平仓/行权/到期 Action = (req.CloseType == "行权" || req.CloseType == "到期") ? ClientCashInCashOut.系统操作_行权费 : ClientCashInCashOut.系统操作_平仓费 }; DbContext.trade_cash.Add(tradeCash); DbContext.SaveChanges(); tradeCash.UnwindNotional = closeNotional; tradeCash.UnwindTradeAmount = req.CloseTradeAmount; tradeCash.UnwindPercentRate = req.CloseTradeAmountRate; tradeCash.NotionalPercentRate = req.CloseTradeAmountRate; tradeCash.UnwindPrice = req.UnwindPrice; tradeCash.UnwindPricePercentRate = req.UnwindPriceRate; if (req.CloseType == "平仓") { tradeCash.UnwindVol = req.UnwindVolatility; tradeCash.UnwindType = isPartialClose ? "部分平仓" : "全部平仓"; tradeCash.Action = ClientCashInCashOut.系统操作_平仓费; tradeCash.ExerciseWay = TradeCashExerciseWayEnum.提前终止行权; tradeCash.IsLastAction = !isPartialClose; } else if (req.CloseType == "提前行权") { if (!PS.Config.TradeElement.IsSingleExecutionTemplate && dbTrade.ExerciseMode != "European") { tradeCash.ExerciseWay = TradeCashExerciseWayEnum.提前终止行权; } else { tradeCash.ExerciseWay = TradeCashExerciseWayEnum.到期行权; } if (isPartialClose) { tradeCash.UnwindType = "部分行权"; tradeCash.Action = ClientCashInCashOut.系统操作_平仓费; } else { tradeCash.UnwindType = "全部行权"; tradeCash.Action = ClientCashInCashOut.系统操作_行权费; } tradeCash.IsLastAction = !isPartialClose; } else { tradeCash.Action = ClientCashInCashOut.系统操作_行权费; tradeCash.ExerciseWay = TradeCashExerciseWayEnum.到期行权; tradeCash.UnwindType = req.CloseType == "到期" ? "到期" : "全部行权"; tradeCash.IsLastAction = true; } #endregion //审批流程 if (!req.SkipWorkflow && HasTradeProcess()) { var isUnwind = tradeCash.Action.Contains("平仓"); tradeCash.ValidState = ConsGlobal.InValid; dbTrade.TradeStatus = isUnwind ? ConsTrade.平仓待复核 : ConsTrade.行权待复核; dbTrade.CheckStatus = Convert.ToInt32(TradeCheckEnum.StatusOfOld); //检查是否有交易审批流程 if (valuedateBLL.SystemDate.CloseReApprove == 1) { // 如果有审批组 InitTradeProcessOrder(dbTrade, UserId); } AddTradeOperationHistoryAndSetParentTradeInfo(false, dbTrade, optType: isUnwind ? "平仓审核提交" : "行权审核提交", comments: req.ImportFlag); } else { //SaveTradeCashDetail(2021-11-23:如果进入审批流程则在审批后才增加tradecashDetail) SaveTradeCashDetail(tradeCash, false); #region---存入ClientCashInCashOut--- var cl = ClientDataQueryService.GetClient(dbTrade.ClientId, true); var ee = new ClientCashInCashOut { Direction = "应收", Number = UniqueTimeId.GetStr(), ClientId = cl.id, ClientNumber = cl.Number, ClientName = cl.Name, Money = tradeCash.Amount * -1, HappenDate = req.CloseDate, State = ClientCashInCashOut.已确认, OptId = tradeCash.OptId, OptName = tradeCash.OptName, OptDate = tradeCash.OptDate, CreatorId = tradeCash.OptId, CreatorName = tradeCash.OptName, CreateDate = tradeCash.OptDate, TradeId = tradeCash.TradeId, TradeCashId = tradeCash.id, Action = tradeCash.Action, TradeNumber = dbTrade.TradeNumber, IsGroup = dbTrade.IsGroup }; DbContext.ClientCashInCashOut.Add(ee); #endregion #region---更新trade--- dbTrade.UnWindNotional = tradeCash.UnwindNotional; if (req.CloseType == "平仓" || req.CloseType == "提前行权") { dbTrade.Notional -= tradeCash.UnwindNotional ?? 0; if (isPartialClose) { dbTrade.HasPartialUnWind = 1; } else { dbTrade.Notional = 0; if (req.CloseType == "平仓") { dbTrade.TradeStatus = ConsTrade.已平仓; } else { dbTrade.TradeStatus = ConsTrade.已执行; } } } else { dbTrade.Notional = 0; dbTrade.TradeStatus = req.CloseType == "到期" ? ConsTrade.已到期 : ConsTrade.已执行; } dbTrade.UnWindDate = req.CloseDate; dbTrade.TradeAmount = dbTrade.Notional / countRatio; if (dbTrade.TradeType == "远期" && PS.Config.ErpElement.ForwardTradePriceModel == 0 && !string.IsNullOrWhiteSpace(dbTrade.BasisUnderlyingCode)) { dbTrade.StockEqvNotional -= TradeHelper.GetStockEqvNotional(tradeCash.UnwindNotional / dbTrade.OriginalNotional * dbTrade.OriginalStockEqvNotional, 1, 1); } else { dbTrade.StockEqvNotional = TradeHelper.GetStockEqvNotional(dbTrade.Notional * (dbTrade.SpotPrice ?? 0.0), dbTrade.ParticipationRate, dbTrade.AnnualizeFactor); } dbTrade.FinalPrice = tradeCash.FinalPrice; #endregion //删除E/Bod数据 RemoveEodTradeAndFutureInfo(false, dbTrade.id, tradeCash.ValueDate); AddTradeOperationHistoryAndSetParentTradeInfo(false, dbTrade, optType: "批量了结-" + req.CloseType, comments: req.ImportFlag); //当前这个方法只会被导入了结交易和api了结调用,所以直接记录提成就行; if (PS.Config.SalesCommissionCalculation == "公式1") { //销售提成 new SalesModule.SalesCommissionDetailDataService(this).CalcuSalesCommissionDetail(tradeCash); } if (PS.Config.Company == Configuration.CompanyEnum.招证) { new BizLogicZhaoZheng().GenerateZhaoZhengDealNumber(dbTrade, tradeCash); } if (PS.Config.Company == Configuration.CompanyEnum.物产中大) { new BizLogicWCZD().GenerateWCZDNumber(DbContext, dbTrade, tradeCash.ValueDate, tradeCash.id); } } dbTrade.OptId = OptUser.UserId; dbTrade.OptName = OptUser.UserName; dbTrade.OptDate = OptDate; dbTrade.IsApproval = dbTrade.ParentTradeId > 0 && dbTrade.IsGroup == 2; //保存更改 DbContext.SaveChanges(); return new TradeCloseResult { Trade = dbTrade, TradeCash = tradeCash }; } //检查请求数据 private void CheckRequest(trade td, TradeCloseRequestModel req) { if (td.TradeType == "结构化交易") { throw new ServiceException("结构化交易主交易不允许了结操作,交易编号:" + req.TradeNumber); } if (td.TradeStatus != ConsTrade.确认成交 && td.TradeStatus != ConsTrade.提前终止拒绝) { throw new ServiceException("只有确认成交或者提前终止拒绝的交易可以导入,交易编号:" + req.TradeNumber); } //---------------------------------------------- // 了结方式 //---------------------------------------------- switch (req.CloseType) { case "平仓": break; case "行权": case "提前行权": case "到期": if (td.TradeType == "自定义交易") { throw new ServiceException("了结方式 填写错误,结构类型为'自定义交易'时仅支持'平仓',交易编号:" + td.TradeNumber); } break; default: if (string.IsNullOrWhiteSpace(req.CloseType)) { throw new ServiceException("了结方式 必须填写,交易编号:" + td.TradeNumber); } else { throw new ServiceException("了结方式 填写错误,不支持:" + req.CloseType + ",交易编号:" + td.TradeNumber); } } //---------------------------------------------- // 了结日期 //---------------------------------------------- if (req.CloseDate > SystemValueDate) { throw new ServiceException("了结日期 不能大于 系统日期,交易编号:" + td.TradeNumber); } if (req.CloseDate < td.TradeDate) { throw new ServiceException("了结日期不能早于交易日期,交易编号:" + td.TradeNumber); } if (req.CloseDate > td.ExerciseDate) { throw new ServiceException("了结日期不能大于到期日期,交易编号:" + td.TradeNumber); } if (td.ExerciseMode != "American" && req.CloseType == "提前行权") { throw new ServiceException("只有美式期权的了结方式可以填写'提前行权',交易编号:" + td.TradeNumber); } if ((req.CloseType == "到期" || req.CloseType == "行权") && req.CloseDate != td.ExerciseDate) { throw new ServiceException("了结日期应该为交易的到期日期,交易编号:" + td.TradeNumber); } var maxDate = DbContext.trade_cash.Where(n => n.TradeId == td.id && n.ValidState != ConsGlobal.InValid && !n.IsDeleted) .Max(n => (DateTime?)n.ValueDate); if (maxDate.HasValue && maxDate.Value > req.CloseDate) { throw new ServiceException($"了结日期 填写错误,因为{maxDate:yyyy-MM-dd}已存在了结操作,必须大于等于此日期,交易编号:{td.TradeNumber}"); } //---------------------------------------------- // 了结数量 //---------------------------------------------- if ((req.CloseTradeAmount ?? 0) <= 0 && td.IsUsePremiumRate == false) { throw new ServiceException("请填写了结数量,交易编号:" + td.TradeNumber); } if ((req.CloseTradeAmountRate ?? 0) <= 0 && td.IsUsePremiumRate == true) { throw new ServiceException("请填写了结数量比例,交易编号:" + td.TradeNumber); } //---------------------------------------------- // 了结金额 //---------------------------------------------- if (req.CloseType == "平仓") { if (td.IsUsePremiumRate == true) { if (!req.UnwindPriceRate.HasValue && !req.UnwindTotalFee.HasValue) { throw new ServiceException("名义本金成交方式时平仓总额和平仓单价比例至少有一个不能为空,交易编号:" + td.TradeNumber); } } else if (!req.UnwindPrice.HasValue && !req.UnwindTotalFee.HasValue) { throw new ServiceException("数量成交方式的交易了结单价和了结总额至少有一个不能为空,交易编号:" + td.TradeNumber); } //用户页面操作模式下平仓波动率不能为空 if (!req.UnwindVolatility.HasValue && string.IsNullOrEmpty(req.ImportFlag) && td.TradeType != "自定义交易") { throw new ServiceException("平仓波动率不能为空,交易编号:" + td.TradeNumber); } } } //互算加计算了结收益 private void CalcForClose(trade td, TradeCloseRequestModel req, bool isAveragePriceOverride, out int countRatio, out double closeNotional) { //------------------------------------- // 了结数量和了结数量比例互算 //------------------------------------- closeNotional = 0; countRatio = UnderlyingDataProvider.GetCountRatio(td.UnderlyingCode); if (td.IsUsePremiumRate == true || req.CloseTradeAmount == null) { closeNotional = (td.OriginalNotional * req.CloseTradeAmountRate) ?? 0; req.CloseTradeAmount = closeNotional / countRatio; } var diff = req.CloseTradeAmount.Value - td.TradeAmount; //比较了结数量和持仓数量 if (diff > 1e-4) { throw new ServiceException("了结数量超过了持仓数量,交易编号:" + td.TradeNumber); } else if (Math.Abs(diff) < 1e-4) { req.CloseTradeAmount = td.TradeAmount; } if (!td.IsUsePremiumRate.GetValueOrDefault()) { closeNotional = (req.CloseTradeAmount ?? 0) * countRatio; req.CloseTradeAmountRate = closeNotional / td.OriginalNotional; } //------------------------------------- // 了结单价和了结总额互算 //------------------------------------- if (req.CloseType == "平仓") { var StockEqvNotional = td.OriginalStockEqvNotional * req.CloseTradeAmountRate; if (req.UnwindTotalFee.HasValue) { req.UnwindTotalFee = (req.UnwindTotalFee ?? 0) * (valuedateBLL.SystemDate.UnwindAmountAngle == 1 ? 1 : EodOperationBase.GetSign(td.BuySell)); req.UnwindPriceRate = TradeHelper.GetPremiumRateByTradePrice(req.UnwindTotalFee, StockEqvNotional, td.ParticipationRate, td.PrincipalSum(), td.AnnualizeFactor, td.BuySell, td.TradeType, false); req.UnwindPrice = TradeHelper.GetTradeSinglePriceByTradePrice(req.UnwindTotalFee, closeNotional, td.PrincipalSum(), td.BuySell, td.TradeType, false); req.UnwindPriceRate = (req.UnwindPriceRate ?? 0) * (td.TradeType == "远期" || ConsTrade.HasMinusValueOptions.Contains(td.TradeType) ? 1 : EodOperationBase.GetSign(td.BuySell)); req.UnwindPrice = (req.UnwindPrice ?? 0) * (td.TradeType == "远期" || ConsTrade.HasMinusValueOptions.Contains(td.TradeType) ? 1 : EodOperationBase.GetSign(td.BuySell)); } else { bool ActualHasMinusValueOptions = valuedateBLL.SystemDate.UnwindSinglePriceAngle == 1 ? ConsTrade.HasMinusValueOptions.Contains(td.TradeType) : !ConsTrade.HasMinusValueOptions.Contains(td.TradeType); int UnwindpriceAndMoneyIsAgreement = td.BuySell == "买入" || valuedateBLL.SystemDate.UnwindSinglePriceAngle == 1 || ConsTrade.HasMinusValueOptions.Contains(td.TradeType) || td.TradeType == "远期" ? 1 : -1; if (td.IsUsePremiumRate == true) { req.UnwindPriceRate = (req.UnwindPriceRate ?? 0) * (td.TradeType == "远期" || ActualHasMinusValueOptions ? 1 : EodOperationBase.GetSign(td.BuySell)); req.UnwindPrice = TradeHelper.GetTradeSinglePriceByPremiumRate(req.UnwindPriceRate, td.SpotPrice); req.UnwindTotalFee = TradeHelper.GetTradePriceByPremiumRate(req.UnwindPriceRate * UnwindpriceAndMoneyIsAgreement, StockEqvNotional, td.ParticipationRate, td.PrincipalSum(), td.AnnualizeFactor, td.BuySell, td.TradeType, false) * UnwindpriceAndMoneyIsAgreement; } else { req.UnwindPrice = (req.UnwindPrice ?? 0) * (td.TradeType == "远期" || ActualHasMinusValueOptions ? 1 : EodOperationBase.GetSign(td.BuySell)); req.UnwindPriceRate = TradeHelper.GetPremiumRateByTradeSinglePrice(req.UnwindPrice, td.SpotPrice); req.UnwindTotalFee = TradeHelper.GetTradePriceBySinglePrice(req.UnwindPrice * UnwindpriceAndMoneyIsAgreement, closeNotional, td.PrincipalSum(), td.BuySell, td.TradeType, false) * UnwindpriceAndMoneyIsAgreement; } req.UnwindTotalFee = (req.UnwindTotalFee ?? 0) * (td.TradeType == "远期" || ConsTrade.HasMinusValueOptions.Contains(td.TradeType) ? 1 : EodOperationBase.GetSign(td.BuySell)); } } else { tradeBLL.SetFieldsByTradeType(td); if (td.TradeType == "亚式期权" && req.UnderlyingAvgPrice.HasValue) { var strike = td.Strike ?? 0; var finalPrice = req.UnderlyingPrice; if (td.trade_asian_option.StrikeType == "Floating") { strike = req.UnderlyingAvgPrice.Value; } else { finalPrice = req.UnderlyingAvgPrice.Value; } req.UnwindPrice = 0; if (td.OptionType == "看涨") { if (finalPrice > strike) { req.UnwindPrice = finalPrice - strike; } } else if (td.OptionType == "看跌") { if (finalPrice < strike) { req.UnwindPrice = strike - finalPrice; } } if (td.BuySell != "买入" && req.UnwindPrice > 0) { req.UnwindPrice = -req.UnwindPrice; } if (!req.UnwindTotalFee.HasValue) { if (td.IsUsePremiumRate == true) { req.UnwindTotalFee = TradeHelper.GetTradePriceBySinglePrice(req.UnwindPrice, td.OriginalNotional * req.CloseTradeAmountRate, td.PrincipalSum(), td.BuySell, td.TradeType, false); } else { req.UnwindTotalFee = TradeHelper.GetTradePriceBySinglePrice(req.UnwindPrice, closeNotional, td.PrincipalSum(), td.BuySell, td.TradeType, false); } } req.UnwindPriceRate = TradeHelper.GetPremiumRateByTradeSinglePrice(req.UnwindPrice, td.SpotPrice); } else if (td.TradeType == "Risky期权") { var finalPrice = req.UnderlyingPrice; var UnwindPrice = 0d; var UnwindTotalFee = 0d; var QdpTrades = GetToQdpOptionRisk(td, countRatio); foreach (var item in QdpTrades) { if (item != null && item.Instrument != null && item.Instrument is IOption optitem) { var cashFlows = optitem.GetPayoff(new double[] { finalPrice }); if (!(cashFlows != null && (cashFlows[0].PaymentAmount == 0 || double.IsNaN(cashFlows[0].PaymentAmount)))) { var perAmount = cashFlows[0].PaymentAmount / td.Notional; UnwindPrice += perAmount; UnwindTotalFee += OtcFormatHelper.FormatValue(cashFlows[0].PaymentAmount, 2); } } } req.UnwindPrice = UnwindPrice; //risky保底是一个总的值 var amount = TradeHelper.GetAmountByPaymentAmount(0, td.PrincipalSum(), td.BuySell); UnwindTotalFee += OtcFormatHelper.FormatValue(amount, 2); if (req.UnwindTotalFee != null) { req.ExtraAmount = req.UnwindTotalFee - UnwindTotalFee; } else { req.UnwindTotalFee = UnwindTotalFee; } req.UnwindPriceRate = TradeHelper.GetPremiumRateByTradeSinglePrice(req.UnwindPrice, td.SpotPrice); } else { var finalPrice = req.UnderlyingPrice; var QdpTrade = TradeCalcHelper.GetQdpTrade(td); if (QdpTrade != null && QdpTrade.Instrument != null && QdpTrade.Instrument is IOption opt) { if (td.TradeType == "亚式期权" && td.trade_asian_option != null) { if (isAveragePriceOverride && finalPrice > 0) { req.UnderlyingAvgPrice = finalPrice; } else { var asianOpt = opt as AsianOption; if (td.trade_asian_option.StrikeType != "Floating") { //分段式亚式期权,行权时的默认标的价格需要通过QDP计算得出,而不是标的现价,再根据该价格算出行权收益 req.UnderlyingAvgPrice = finalPrice = asianOpt.FinalPrice(); } else { req.UnderlyingAvgPrice = asianOpt.Strike; } } } var cashFlows = opt.GetPayoff(new double[] { finalPrice }); var perAmount = cashFlows[0].PaymentAmount / td.Notional; req.UnwindPrice = perAmount; } if (req.UnwindTotalFee != null) { if (td.IsUsePremiumRate == true) { var UnwindTotalFee = TradeHelper.GetTradePriceBySinglePrice(req.UnwindPrice, td.OriginalNotional * req.CloseTradeAmountRate, td.PrincipalSum(), td.BuySell, td.TradeType, false); req.ExtraAmount = req.UnwindTotalFee - UnwindTotalFee; } else { var UnwindTotalFee = TradeHelper.GetTradePriceBySinglePrice(req.UnwindPrice, closeNotional, td.PrincipalSum(), td.BuySell, td.TradeType, false); req.ExtraAmount = req.UnwindTotalFee - UnwindTotalFee; } } else { if (td.IsUsePremiumRate == true) { req.UnwindTotalFee = TradeHelper.GetTradePriceBySinglePrice(req.UnwindPrice, td.OriginalNotional * req.CloseTradeAmountRate, td.PrincipalSum(), td.BuySell, td.TradeType, false); } else { req.UnwindTotalFee = TradeHelper.GetTradePriceBySinglePrice(req.UnwindPrice, closeNotional, td.PrincipalSum(), td.BuySell, td.TradeType, false); } } req.UnwindPriceRate = TradeHelper.GetPremiumRateByTradeSinglePrice(req.UnwindPrice, td.SpotPrice); } } if (td.TradeType != "亚式期权") { req.UnderlyingAvgPrice = null; } } public static List GetToQdpOptionRisk(trade trade, int countRatio) { var options = new List(); var tradeclone = trade.Clone(); tradeclone.TradeAmount = tradeclone.TradeAmount = TradeCalcHelper.GetTradeAmountV(trade, trade.TradeAmount, 1); tradeclone.Notional = tradeclone.Notional = TradeCalcHelper.GetTradeAmountV(trade, trade.Notional, countRatio); var td1 = tradeclone.Clone(); td1.Strike = trade.trade_risky_option.Strike1; td1.ParticipationRate = trade.trade_risky_option.ParticipationRate1; td1.TradeAmount = TradeCalcHelper.GetTradeAmount(td1, td1.TradeAmount, 1); td1.Notional = TradeCalcHelper.GetTradeAmount(td1, td1.Notional, countRatio); td1.OptionType = "看跌"; td1.BuySell = trade.BuySell == "买入" ? "卖出" : "买入"; var option1 = TradeCalcHelper.GetQdpTrade(td1); if (option1 != null) { options.Add(option1); } var td2 = tradeclone.Clone(); td2.Strike = trade.trade_risky_option.Strike2; td2.ParticipationRate = trade.trade_risky_option.ParticipationRate2; td2.TradeAmount = TradeCalcHelper.GetTradeAmount(td2, td2.TradeAmount, 1); td2.Notional = TradeCalcHelper.GetTradeAmount(td2, td2.Notional, countRatio); var option2 = TradeCalcHelper.GetQdpTrade(td2); if (option2 != null) { options.Add(option2); } var td3 = tradeclone.Clone(); td3.Strike = trade.trade_risky_option.Strike3; //decimal 为了解决精度问题: 0.2-0.3=0.0999999999 var participationRate3 = (decimal)trade.trade_risky_option.ParticipationRate2 - (decimal)trade.trade_risky_option.ParticipationRate3; td3.ParticipationRate = (double?)Math.Abs(participationRate3); td3.TradeAmount = TradeCalcHelper.GetTradeAmount(td3, td3.TradeAmount, 1); td3.Notional = TradeCalcHelper.GetTradeAmount(td3, td3.Notional, countRatio); if (participationRate3 < 0) { td3.BuySell = trade.BuySell == "买入" ? "卖出" : "买入"; } var option3 = TradeCalcHelper.GetQdpTrade(td3); if (option3 != null) { options.Add(option3); } return options; } #region----DataRowReader---- class DataRowReader { DataRow _row; readonly Dictionary _colMap; public DataRowReader(DataTable table) { var colCount = table.Columns.Count; _colMap = new Dictionary(colCount, StringComparer.OrdinalIgnoreCase); var row = table.Rows[0]; for (var index = 0; index < colCount; index++) { var col = row[index]?.ToString()?.Trim(); if (string.IsNullOrEmpty(col)) { continue; } _colMap[col.Replace("%", "")] = index; } } /// /// 设置datarow /// public void SetDataRow(DataRow row) { _row = row; } public string GetString(string fieldName, bool required = false) { var str = _colMap.TryGetValue(fieldName, out var colIndex) ? _row[colIndex]?.ToString()?.Trim() : null; if (required && string.IsNullOrEmpty(str)) { throw new ServiceException($"{fieldName} 必须填写"); } return str; } public double? GetDouble(string fieldName, bool required = false) { var str = GetString(fieldName, required); if (!required && string.IsNullOrEmpty(str)) { return null; } return double.TryParse(str, out var num) ? num : throw new ServiceException($"{fieldName} 填写错误:{str}"); } //为了兼容模板修改导致的字段名称改变问题 public double? GetDouble(string fieldName, string fieldName2, bool required = false) { var str = GetString(fieldName, false) ?? GetString(fieldName2, false); if (string.IsNullOrEmpty(str)) { return required ? throw new ServiceException($"{fieldName} 必须填写") : (double?)null; } return double.TryParse(str, out var num) ? num : throw new ServiceException($"{fieldName} 填写错误:{str}"); } public double? GetPercent(string fieldName, bool required = false) { var str = GetString(fieldName, required); if (!required && string.IsNullOrEmpty(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($"{fieldName} 填写错误:{str}"); } //为了兼容模板修改导致的字段名称改变问题 public double? GetPercent(string fieldName, string fieldName2, bool required = false) { var str = GetString(fieldName, false) ?? GetString(fieldName2, false); if (string.IsNullOrEmpty(str)) { return required ? throw new ServiceException($"{fieldName} 必须填写") : (double?)null; } var percent = str.EndsWith("%"); if (percent) { str = str.TrimEnd('%'); } return double.TryParse(str, out var num) ? (percent ? num / 100 : num) : throw new ServiceException($"{fieldName} 填写错误:{str}"); } /// /// 获取日期(不包括时间) /// public DateTime? GetDate(string fieldName, bool required = false) { var str = GetString(fieldName, required); if (!required && string.IsNullOrEmpty(str)) { return null; } if (str.Length == 8) { 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($"{fieldName} 填写错误:{str}"); } public int? GetInt32(string fieldName, bool required = false) { var str = GetString(fieldName, required); if (!required && string.IsNullOrEmpty(str)) { return null; } return int.TryParse(str, out var num) ? num : throw new ServiceException($"{fieldName} 填写错误:{str}"); } } #endregion } /// /// 交易了结结果 /// public class TradeCloseResult { public trade Trade { get; set; } public trade_cash TradeCash { get; set; } } }