using FluentFTP.Helpers; using Microsoft.EntityFrameworkCore.Storage; using NPOI.SS.Formula.Functions; using Org.BouncyCastle.Ocsp; using Qdp.Pricing.Base.Utilities; using YLErp.BLL.Calculation; using YLErp.Configuration.Enums; using YLErp.Helpers; using YLErp.Model.Enum; using YLErp.Modules.CalculationModule; using YLErp.Modules.SystemModule; using YLErp.Modules.TagModule; using static iTextSharp.text.pdf.AcroFields; namespace YLErp.Modules.TradeModule.OrderModule { /// /// 衍生品交易录入服务 /// public class OtcTradeSaveService : YLBaseService { public OtcTradeSaveService(OptUserInfo userInfo) : base(userInfo) { } public OtcTradeSaveService(YLBaseService baseService) : base(baseService) { } /// /// 保存来自API或导入的交易 /// public trade SaveOptionTradeFromApiOrImport(OtcOptionTradeFullEx tdReq, TradeSourceEnum dataSource, IDbContextTransaction trans = null) { if (tdReq is null) { throw new ArgumentNullException(nameof(tdReq)); } if (dataSource != TradeSourceEnum.WebApiV2 && dataSource != TradeSourceEnum.导入交易) { throw new NotSupportedException("不支持:" + dataSource.ToString()); } if (tdReq._Update) { if (string.IsNullOrWhiteSpace(tdReq.TradeNumber)) { throw new ServiceException("交易编号 必须填写"); } var td = DbContext.trade.Where(n => n.TradeNumber == tdReq.TradeNumber) .Select(n => new { n.id, n.TradeType, n.IsGroup, n.TradeDate, n.ClientId, n.SpotPrice, n.TradePrice, n.ParentTradeId, n.StructureType, n.TradeStatus }).FirstOrDefault(); if (td == null) { throw new ServiceException("未找到对应的交易数据:" + tdReq.TradeNumber); } //国君需求,如果有更多update需求,更改tdReq._Update处理 if (td.TradeStatus != ConsTrade.新增待确认) { throw new ServiceException("交易状态必须是'新增待确认'状态才允许修改"); } tdReq.id = td.id; //扩展支持组合交易主交易的修改 if (td.IsGroup == 1 && td.TradeType == "结构化交易") { tdReq.IsGroup = 1; tdReq.TradeType = "结构化交易"; tdReq.ClientId = td.ClientId; tdReq.TradeDate = td.TradeDate; if (!tdReq.SpotPrice.HasValue) { tdReq.SpotPrice = td.SpotPrice; } tdReq.TradePrice = td.TradePrice; CheckApiOrImportSave(tdReq, dataSource); return new TradeSaveService(this).SaveGroupTrade(tdReq, null); } else if (td.IsGroup == 2 && td.ParentTradeId > 0) { tdReq.IsGroup = 2; tdReq.ParentTradeId = td.ParentTradeId; tdReq.StructureType = td.StructureType; } } else { tdReq.id = 0; } var saveExt = CheckApiOrImportSave(tdReq, dataSource); var dbtrade = TradeConverter.ConvertOptionTrade(tdReq); dbtrade.SaveExt = saveExt; return new TradeSaveService(this).SaveTrade(dbtrade, dataSource, trans); } /// /// 保存来自API或导入的结构化组合交易 /// public trade SaveStructureOptionTradeFromApiOrImport(StructureOrderRequest request, TradeSourceEnum dataSource, IDbContextTransaction trans = null) { if (request is null) { throw new ArgumentNullException(nameof(request)); } if (dataSource != TradeSourceEnum.WebApiV2 && dataSource != TradeSourceEnum.导入交易) { throw new NotSupportedException("不支持:" + dataSource.ToString()); } switch (request.StructureType) { case "牛市价差": case "熊市价差": case "亚式熊市价差": case "跨式组合": case "宽跨式组合": case "复制标的资产": case "蝶式组合": case "飞鹰式组合": case "比例价差": case "日历价差": case "箱式价差": case "风险逆转": case "三领口组合": case "结构化交易": break; default: if (string.IsNullOrWhiteSpace(request.StructureType)) { throw new ServiceException("结构化类型 必须填写"); } throw new ServiceException("结构化类型 不可辨识:" + request.StructureType); } if (request.Trades == null || request.Trades.Count() < 2) { throw new ServiceException("结构化交易组合必须有两条腿及以上"); } var dbtrades = request.Trades.Select(td => { td.id = 0; td.StructureType = request.StructureType; var saveExt = CheckApiOrImportSave(td, dataSource); var trad = TradeConverter.ConvertOptionTrade(td); trad.SaveExt = saveExt; return trad; }).ToArray(); var struTrade = dbtrades[0].Clone(); struTrade.TradeType = "结构化交易"; struTrade.SubTrades = dbtrades; return new TradeSaveService(OptUser).SaveTrade(struTrade, dataSource, trans); } /// /// 保存来自API或导入的组合交易 /// public trade SaveGroupOptionTradeFromApiOrImport(StructureOrderRequest request, TradeSourceEnum dataSource, IDbContextTransaction trans = null) { if (request is null) { throw new ArgumentNullException(nameof(request)); } if (dataSource != TradeSourceEnum.WebApiV2 && dataSource != TradeSourceEnum.导入交易) { throw new NotSupportedException("不支持:" + dataSource.ToString()); } if (request.Trades == null || request.Trades.Count() <= 2) { throw new ServiceException("组合交易必须有两条腿及以上"); } var dbtrades = request.Trades.Select(td => { td.id = 0; if (td.IsGroup == 1) { td.StructureType = td.TradeType; td.TradeType = "结构化交易"; } var saveExt = CheckApiOrImportSave(td, dataSource); var trad = TradeConverter.ConvertOptionTrade(td); trad.SaveExt = saveExt; return trad; }).ToArray(); var struTrade = dbtrades.FirstOrDefault(x => x.IsGroup == 1); if (struTrade == null) { throw new Exception("未找到组合主交易,组合子交易必须和主交易配对出现"); } struTrade.SubTrades = dbtrades.Where(x => x.IsGroup != 1); return new TradeSaveService(OptUser).SaveTrade(struTrade, dataSource, trans); } /// /// 保存组合报价的交易 /// public List SaveOptionTradesFromPricing(IEnumerable trades) { var tdGroups = trades.GroupBy(n => n.CalcId.Split('-')[0]).ToArray(); var dbTrades = new List(); var saveChecker = new OtcOptionSaveChecker(this); var tags = (trades != null && trades.Any()) ? trades.First().Tags : null; try { foreach (var tdGroup in tdGroups) { var tdList = tdGroup.Select(td => { saveChecker.CheckBasic(td).CheckAnnualizeFactorForEditSave(td); saveChecker.CheckPrincipalRate(td).CheckParticipationRate(td); saveChecker.CheckExoticFields(td); return TradeConverter.ConvertOptionTrade(td); }).ToArray(); var tdConv = tdList.Where(O => O.TradeType != "现金流交易").FirstOrDefault() ?? tdList[0]; if (tdList.Length > 1) { tdConv = tdConv.Clone(); tdConv.TradeType = "结构化交易"; tdConv.SubTrades = tdList; } if (PS.Config.Is浙期 && IsCodesExistsCommoditySpot(new string[] { tdConv.UnderlyingCode })) { tdConv.MetaDic["ActualExerciseDate"] = tdConv.ExerciseDate.Value.ToString("yyyy-MM-dd"); //没有录入 } var dbTrade = new TradeSaveService(this).SaveTrade(tdConv, TradeSourceEnum.系统交易); using (var tagService = new TagService(OptUser)) { tagService.SetTradeTagForTradeEdit(new TagModule.Dto.SetTradeTagForTradeEditRequest { TradeId = dbTrade.id, Tags = tags }); if ("结构化交易".Equals(dbTrade.TradeType)) { if (dbTrade.SubTrades != null && dbTrade.SubTrades.Any()) { dbTrade.SubTrades.ToList().ForEach(p => { if (p.id > 0) { tagService.SetTradeTagForTradeEdit(new TagModule.Dto.SetTradeTagForTradeEditRequest { TradeId = p.id, Tags = tags }); } }); } } } var variety = UnderlyingDataProvider.GetVariety(dbTrade.UnderlyingCode); if (variety != null) { dbTrade.QuoteUnit = variety.QuoteUnit; dbTrade.QuoteUnitSingle = variety.QuoteUnitSingle; } dbTrades.Add(dbTrade); } } catch { if (dbTrades.Count > 0) { var ids = dbTrades.Select(n => n.id.ToString()).ToArray(); var sql = "UPDATE trade SET ValidState='InValid', TradeNumber='XX', ClientId=0, ChangeMsg='因组合报价录入交易部分失败所以无效' WHERE id in ({0}) OR ParentTradeId in ({0});"; sql = string.Format(sql, string.Join(",", ids)); var changes = DbContext.Database.ExecuteSqlRaw(sql); } throw; } return dbTrades; } public trade SaveOptionForwardTradeFromApi(OtcTradeForward req, TradeSourceEnum dataSource) { req.TradeType = "远期"; var otcOptionTradeFullEx = new OtcOptionTradeFullEx { ClientName = req.ClientName }; new OtcOptionSaveChecker(this).CheckAssetBook(req).CheckTrader(req).CheckClient(otcOptionTradeFullEx); req.ClientId = otcOptionTradeFullEx.ClientId; var um = DataCacheProvider.GetUnderlyingDataSource().GetData(req.UnderlyingCode); if (um == null) { throw new ServiceException("系统中不存在,传入的标的1代码"); } else if (!string.IsNullOrEmpty(req.BasisUnderlyingCode)) { if (DataCacheProvider.GetUnderlyingDataSource().GetData(req.BasisUnderlyingCode) == null) { throw new ServiceException("系统中不存在,传入的标的2代码"); } } if (req.IsSupplyForwardValue == 0) { req.MetaDic["IsSupplyForwardValue"] = "0"; } else if (req.IsSupplyForwardValue == -1) { req.MetaDic["IsSupplyForwardValue"] = "-1"; } else if (req.IsSupplyForwardValue == 1) { req.MetaDic["IsSupplyForwardValue"] = "1"; } else { throw new ServiceException("是否补偿远期价值,传入值错误,只能为0,1,-1;"); } if (req.PriceModel == 0) { if ((PS.Config.ErpElement.ForwardTradePriceModel == ForwardTradePriceModel.STANDARD || PS.Config.ErpElement.ForwardTradePriceModel == ForwardTradePriceModel.HOOK_PRICE)) { if (PS.Config.ErpElement.ForwardTradePriceModel == ForwardTradePriceModel.STANDARD) { var spList = new List { req.SpotPrice.OtcFormatUmPrice() }; if (!string.IsNullOrEmpty(req.BasisUnderlyingCode)) { if (req.SpotPrice2 == null) { throw new ServiceException("填写标的2代码,则必须填写标的2期初价格"); } else { spList.Add((req.SpotPrice2 ?? 0).OtcFormatUmPrice()); } } else { throw new ServiceException("请填写标的2代码"); } req.SpotPrice = (double)((decimal)req.SpotPrice - (decimal)(req.SpotPrice2 ?? 0)); req.MetaDic["期初信息"] = spList.ToJson(); } new ForwardModule.ForwardTradeImportService(UserInfo).GetSpotPrice(req); } } else { var spList = new List { req.SpotPrice.OtcFormatUmPrice() }; req.MetaDic["期初信息"] = spList.ToJson(); } new ForwardModule.ForwardTradeImportService(UserInfo).GetCountRatio(req); new ForwardModule.ForwardTradeImportService(UserInfo).GetNotional_TradeAmount_Lots(req, um); new ForwardModule.ForwardTradeImportService(UserInfo).GetForwardValue(req); new ForwardModule.ForwardTradeImportService(UserInfo).GetOpenCommission_TradePrice(req, null, req.OpenCommission, req.TradePrice); var td = TradeConverter.ConvertForward(req); td = new TradeSaveService(this).SaveTrade(td, dataSource); var variety = UnderlyingDataProvider.GetVariety(td.UnderlyingCode); if (variety != null) { td.QuoteUnit = variety.QuoteUnit; td.QuoteUnitSingle = variety.QuoteUnitSingle; } return td; } /// /// 保存分组交易 /// public trade SaveGroupTradeFromPricing(trade trade, IEnumerable subTrades) { var saveChecker = new OtcOptionSaveChecker(this); subTrades.ToList().ForEach(x => { saveChecker.CheckBasic(x).CheckAnnualizeFactorForEditSave(x); saveChecker.CheckPrincipalRate(x).CheckParticipationRate(x); saveChecker.CheckExoticFields(x); }); trade.TradeType = "结构化交易"; trade.IsGroup = 1; trade.SubTrades = subTrades.Select(x => TradeConverter.ConvertOptionTrade(x)).ToArray(); trade.UnderlyingCode = trade.SubTrades.FirstOrDefault(x => !string.IsNullOrEmpty(x.UnderlyingCode)).UnderlyingCode; var dbTrade = new TradeSaveService(this).SaveTrade(trade, TradeSourceEnum.系统交易); using (var tagService = new TagService(OptUser)) { tagService.SetTradeTagForTradeEdit(new TagModule.Dto.SetTradeTagForTradeEditRequest { TradeId = dbTrade.id, Tags = trade.Tags }); } return dbTrade; } /// /// 保存交易编辑的交易 /// public trade SaveOptionTradesFromTradeEdit(OtcOptionTradeFull td) { var saveChecker = new OtcOptionSaveChecker(this); saveChecker.CheckBasic(td).CheckAnnualizeFactorForEditSave(td); saveChecker.CheckPrincipalRate(td).CheckParticipationRate(td); saveChecker.CheckExoticFields(td); var tdConv = TradeConverter.ConvertOptionTrade(td); var dbTrade = new TradeSaveService(this).SaveTrade(tdConv, TradeSourceEnum.系统交易); var variety = UnderlyingDataProvider.GetVariety(dbTrade.UnderlyingCode); if (variety != null) { dbTrade.QuoteUnit = variety.QuoteUnit; dbTrade.QuoteUnitSingle = variety.QuoteUnitSingle; } using (var tagService = new TagService(OptUser)) { tagService.SetTradeTagForTradeEdit(new TagModule.Dto.SetTradeTagForTradeEditRequest { TradeId = dbTrade.id, Tags = td.Tags }); } return dbTrade; } /// /// 判断标的是否为现货 /// /// /// public bool IsCodesExistsCommoditySpot(string[] codes) { return UndelyingHelper.IsCodesExistsCommoditySpot(codes, (um) => { return um.UnderlyingInstrumentType == "CommoditySpot" ? true : false; }); } //检查导入和API数据 private TradeSaveExt CheckApiOrImportSave(OtcOptionTradeFullEx td, TradeSourceEnum dataSource) { if (dataSource != TradeSourceEnum.WebApiV2 && dataSource != TradeSourceEnum.导入交易) { throw new NotSupportedException("不支持:" + dataSource.ToString()); } var saveChecker = new OtcOptionSaveChecker(this).CheckBasic(td); //簿记、交易员、交易对手 saveChecker.CheckAssetBook(td).CheckTrader(td).CheckClient(td); //年化系数 saveChecker.CheckAnnualizeFactorForApiOrImport(td); //保底收益率、参与率 saveChecker.CheckPrincipalRate(td).CheckParticipationRate(td); TradingPremiumFlag premiumFlag; var notionalFlag = TradingNotionalFlag.None; // 权利金单价/期权费率/权利金总额 // 20200622: 权利金总额/单价/费率、名义本金/份额导入时可能存在为负值的情况 // 20210425: 国海凤凰雪球期权资金记录方向问题,需要权利金可以为负 if (dataSource == TradeSourceEnum.导入交易) { if ((td.HasFlag & OtcOptionTradeFullHasFlag.TradeSinglePrice) > 0) { premiumFlag = TradingPremiumFlag.TradeSinglePrice; td.TradeSinglePrice = td.TradeSinglePrice.Value; } else if ((td.HasFlag & OtcOptionTradeFullHasFlag.TradePrice) > 0) { premiumFlag = TradingPremiumFlag.TradePrice; td.TradePrice = td.TradePrice.Value; } else if ((td.HasFlag & OtcOptionTradeFullHasFlag.PremiumRate) > 0) { premiumFlag = TradingPremiumFlag.PremiumRate; td.PremiumRate = td.PremiumRate.Value; } else { //暂时不支持小于0 td.TradeSinglePrice = td.PremiumRate = td.TradePrice = 0; premiumFlag = TradingPremiumFlag.AllZero; } } else { if (td.TradeSinglePrice.HasValue) { premiumFlag = TradingPremiumFlag.TradeSinglePrice; } else if (td.PremiumRate.HasValue) { premiumFlag = TradingPremiumFlag.PremiumRate; } else { //TradePrice在20200803被修改后一直有值,已经不具备优先使用的条件 premiumFlag = TradingPremiumFlag.TradePrice; } } // 有效名义本金/名义本金/交易份额/交易数量 if (notionalFlag == TradingNotionalFlag.None) { if (Math.Abs(td.Notional) > 0) { notionalFlag = TradingNotionalFlag.Notional; td.Notional = Math.Abs(td.Notional); } else if (Math.Abs(td.TradeAmount) > 0) { notionalFlag = TradingNotionalFlag.TradeAmount; td.TradeAmount = Math.Abs(td.TradeAmount); } else if (td.TradeType == "累计期权") { throw new ServiceException("有效交易份额/有效交易数量 两个字段中必须有一个值大于0"); } else if (Math.Abs(td.StockEqvNotional) > 0) { notionalFlag = TradingNotionalFlag.StockEqvNotional; td.StockEqvNotional = Math.Abs(td.StockEqvNotional); } else if (Math.Abs(td.StockEqvNotionalReal) > 0) { notionalFlag = TradingNotionalFlag.StockEqvNotionalReal; td.StockEqvNotionalReal = Math.Abs(td.StockEqvNotionalReal); } else { //交易数量 throw new ServiceException("有效交易份额/有效交易数量/有效名义本金/名义本金 四个字段中必须有一个值大于0"); } } if (!(td.TradeType == "结构化交易" && td.IsGroup == 1)) { td.IsUsePremiumRate = premiumFlag == TradingPremiumFlag.PremiumRate || premiumFlag == TradingPremiumFlag.TradePrice && (notionalFlag == TradingNotionalFlag.StockEqvNotionalReal || notionalFlag == TradingNotionalFlag.StockEqvNotional); } // 组合标的(使用最大名义本金) if (saveChecker.CheckSyntheticUnderlying(td, td.SyntheticPrices, out var maxSpotPrice) && maxSpotPrice > 0) { //暂时不再支持最大名义本金方式导入处理 //var spotPrice = Math.Abs(td.SpotPrice.Value); //var spotPriceRate = spotPrice / maxSpotPrice; //switch (notionalFlag) //{ // case TradingNotionalFlag.StockEqvNotionalReal: // td.StockEqvNotionalMax = TradeHelper.GetStockEqvNotional(td.StockEqvNotionalReal, td.ParticipationRate, td.AnnualizeFactor); // td.StockEqvNotionalReal *= spotPriceRate; // break; // case TradingNotionalFlag.StockEqvNotional: // td.StockEqvNotionalMax = td.StockEqvNotional; // td.StockEqvNotional *= spotPriceRate; // break; // case TradingNotionalFlag.Notional: // td.StockEqvNotionalMax = td.Notional * maxSpotPrice; // break; // case TradingNotionalFlag.TradeAmount: // //组合标的没有数量和份额的乘积因子 // td.StockEqvNotionalMax = td.TradeAmount * maxSpotPrice; // break; // default: // throw new SystemException("[NotionalFlag]系统程序错误"); //} } if (td.Comments?.Length > 100) { throw new ServiceException("交易备注 最多100个字"); } // 奇异期权检查 saveChecker.CheckExoticFields(td); return new TradeSaveExt { PremiumFlag = premiumFlag, NotionalFlag = notionalFlag }; } } class OtcOptionSaveChecker : YLBaseService { public OtcOptionSaveChecker(YLBaseService baseService) : base(baseService) { } /// /// 检查基础信息(结构类型/标的代码/初始预付金) /// public OtcOptionSaveChecker CheckBasic(OtcTradeBase td) { if (!string.IsNullOrEmpty(td.TradeNumber)) { var exclueStr = new List { "+","/","?","%","#","&","=" }; exclueStr.ForEach(x => { if (td.TradeNumber.Contains(x)) { throw new ServiceException("交易编号不合法,不能包含以下字符:"+ exclueStr.Join(",")); } }); } // 结构类型 if (string.IsNullOrWhiteSpace(td.TradeType)) { throw new ServiceException("结构类型 必须填写"); } if (td.TradeType == "现金流交易" || td.TradeType == "结构化交易" && td.IsGroup == 1) { return this; } // 标的代码 if (string.IsNullOrWhiteSpace(td.UnderlyingCode)) { throw new ServiceException("标的代码 必须填写"); } var um = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode); // 标的代码 if (um == null) { throw new ServiceException("标的代码 必须在系统中存在:" + td.UnderlyingCode); } if (um.UnderlyingInstrumentType == "Stock") { var limitStatus = new UnderlyingModule.StockBlackWhiteService(UserInfo).GetStockBlackWhiteList(Configuration.Enums.LimitRangeEnum.Option, out var Codes); if (Codes != null) { if (limitStatus == Codes.Contains(um.UnderlyingCode)) { if (limitStatus) { throw new ServiceException("标的代码 填写错误,存在于黑名单中,请求值:" + td.UnderlyingCode); } else { throw new ServiceException("标的代码 填写错误,不存在于白名单中,请求值:" + td.UnderlyingCode); } } } } return this; } /// /// 簿记账户 /// public OtcOptionSaveChecker CheckAssetBook(OtcTradeBase td) { if (td.AssetId > 0) { return this; } if (string.IsNullOrWhiteSpace(td.AssetBookName)) { throw new ServiceException("簿记账户 必须填写"); } var assetUnit = DbContext.assetunit.Where(au => au.Name == td.AssetBookName).Select(n => new { n.id }).FirstOrDefault(); if (assetUnit == null) { throw new ServiceException("簿记账户不存在:" + td.AssetBookName); } td.AssetId = assetUnit.id; return this; } /// /// 交易员赋值及验证逻辑 /// public OtcOptionSaveChecker CheckTrader(OtcTradeBase td) { if (td.TraderId > 0) { return this; } if (string.IsNullOrWhiteSpace(td.TraderName)) { throw new ServiceException("交易员名称 必须填写"); } using (var sysdb = DbContextFactory.GetErpBaseContext()) { var systemUser = sysdb.SystemUsers.Where(su => su.LoginName == td.TraderName) .Select(n => new { n.Id, n.Name }).FirstOrDefault() ?? sysdb.SystemUsers.Where(su => su.Name == td.TraderName) .Select(n => new { n.Id, n.Name }).FirstOrDefault(); if (systemUser == null) { throw new ServiceException("交易员不存在:" + td.TraderName); } else { td.TraderId = systemUser.Id; td.TraderName = systemUser.Name; } } return this; } /// /// 交易对手方 /// public OtcOptionSaveChecker CheckClient(OtcOptionTradeFullEx td) { if (td.ClientId > 0) { return this; } var clientQuery = DbContextFactory.GetClientDbContext(OptUser).client.AsQueryable(); if (!string.IsNullOrWhiteSpace(td.ClientNumber)) { clientQuery = clientQuery.Where(c => c.Number == td.ClientNumber); } else if (!string.IsNullOrWhiteSpace(td.ClientName)) { clientQuery = clientQuery.Where(c => c.Name == td.ClientName); } else { throw new ServiceException("交易对手方名称或编号 必须填写"); } var client = clientQuery.Select(n => new { n.id }).FirstOrDefault(); if (client == null) { throw new ServiceException($"交易对手方不存在,客户名称:[{td.ClientName}],交易对手方编号:[{td.ClientNumber}]"); } td.ClientId = client.id; if (!td.MetaDic.ContainsKey("主协议编号")) { var mainProtocal = ClientDataModelV1.getMainProtocols(td.ClientId).FirstOrDefault(); if (mainProtocal != null) { td.MetaDic.Add("主协议编号", mainProtocal.Value); } } if (!td.MetaDic.ContainsKey("补充协议编号") && td.MetaDic.ContainsKey("主协议编号")) { var sideProtocal = ClientDataModelV1.getSideProtocols(td.MetaDic["主协议编号"]).FirstOrDefault(); if (sideProtocal != null) { td.MetaDic.Add("补充协议编号", sideProtocal); } } return this; } /// /// 年化系数(API或导入的时候使用的是trade的字段所以雪球和凤凰需要转换为自己的字段) /// public OtcOptionSaveChecker CheckAnnualizeFactorForApiOrImport(OtcOptionTradeFull td) { //非期权年化 if (!td.IsAnnualized) { td.AnnualizeFactor = 1; if (td.TradeType == "雪球期权" || td.TradeType == "凤凰期权") { td.IsAnnualized2 = false; td.AnnualizeFactor2 = 1; } td.MetaDic[nameof(td.AnnualizeFactor)] = null; td.MetaDic[nameof(td.AnnualizeFactor2)] = null; return this; } //期权年化 if (!td.MetaDic.TryGetValue(nameof(td.AnnualizeFactor), out var str) || string.IsNullOrWhiteSpace(str)) { if (td.AnnualizeFactor > 0) { var daysInYear = CalculatorHelper.GetTradeDayCount().ToDayCountImpl().DaysInYear(); var ttmdays = td.AnnualizeFactor.Value * daysInYear; td.MetaDic[nameof(td.AnnualizeFactor)] = $"{ttmdays}/{daysInYear}"; } else { var un = UnderlyingDataProvider.GetUnderlying(td.UnderlyingCode); var ttmdays = TradeCalcHelper.CalculateTTMDays(td.TradeDate.Value, td.SettlementDate ?? td.ExerciseDate.Value, un?.UnderlyingTypeId ?? 0, false); var daysInYear = CalculatorHelper.GetTradeDayCount().ToDayCountImpl().DaysInYear(); td.AnnualizeFactor = ttmdays / daysInYear; td.MetaDic[nameof(td.AnnualizeFactor)] = $"{ttmdays}/{daysInYear}"; } } //雪球和凤凰使用自己的年化处理方式 if (td.TradeType == "雪球期权" || td.TradeType == "凤凰期权") { td.IsAnnualized2 = true; td.AnnualizeFactor2 = td.AnnualizeFactor; td.MetaDic[nameof(td.AnnualizeFactor2)] = td.MetaDic[nameof(td.AnnualizeFactor)]; td.IsAnnualized = false; td.AnnualizeFactor = 1; td.MetaDic[nameof(td.AnnualizeFactor)] = null; } return this; } /// /// 检查年化系数(组合报价或编辑保存时使用) /// public OtcOptionSaveChecker CheckAnnualizeFactorForEditSave(OtcOptionTradeFull td) { var IsAnnualized2 = td.TradeType == "雪球期权" || td.TradeType == "凤凰期权"; if (IsAnnualized2) { td.IsAnnualized = false; td.AnnualizeFactor = 1; td.MetaDic[nameof(td.AnnualizeFactor)] = null; if (!td.IsAnnualized2) { td.AnnualizeFactor2 = 1; td.MetaDic[nameof(td.AnnualizeFactor2)] = null; return this; } } else { td.IsAnnualized2 = false; td.AnnualizeFactor2 = 1; td.MetaDic[nameof(td.AnnualizeFactor2)] = null; if (!td.IsAnnualized) { td.AnnualizeFactor = 1; td.MetaDic[nameof(td.AnnualizeFactor)] = null; return this; } } var AnnualizeFactorField = IsAnnualized2 ? nameof(td.AnnualizeFactor2) : nameof(td.AnnualizeFactor); if (!td.MetaDic.TryGetValue(AnnualizeFactorField, out var str) || string.IsNullOrWhiteSpace(str)) { throw new ServiceException("年化系数 必须填写"); } var index = str.IndexOf('/'); if (index < 0) { throw new ServiceException("年化系数 填写错误,格式必须为:天数/年天数,填写值为:" + str); } if (!double.TryParse(str.Substring(0, index).Trim(), out var ttmDays)) { throw new ServiceException("年化系数 填写错误,天数解析失败,填写值为:" + str); } if (ttmDays < 0) { throw new ServiceException("年化系数 填写错误,格式必须为:天数/年天数,天数必须大于等于0,填写值为:" + str); } if (!double.TryParse(str.Substring(index + 1).Trim(), out var daysOfYear)) { throw new ServiceException("年化系数 填写错误,格式必须为:天数/年天数,年天数解析失败,填写值为:" + str); } if (daysOfYear < 1) { throw new ServiceException("年化系数 填写错误,格式必须为:天数/年天数,年天数必须大于0,填写值为:" + str); } if (IsAnnualized2) { td.AnnualizeFactor2 = ttmDays / daysOfYear; } else { td.AnnualizeFactor = ttmDays / daysOfYear; } return this; } /// /// 保底收益率 /// public OtcOptionSaveChecker CheckPrincipalRate(OtcTradeBase td) { if (!td.PrincipalRate.HasValue) { td.PrincipalRate = 0; } else if (td.PrincipalRate.Value < 0) { throw new ServiceException("保底收益率 填写错误,必须大于等于0,请求值:" + td.PrincipalRate.Value); } return this; } /// /// 参与率 /// public OtcOptionSaveChecker CheckParticipationRate(OtcTradeBase td) { if (!td.ParticipationRate.HasValue) { td.ParticipationRate = 1; } else if (td.ParticipationRate.Value < 0) { throw new ServiceException("参与率 填写错误,必须大于等于0,请求值:" + td.ParticipationRate.Value); } return this; } //组合标的 public bool CheckSyntheticUnderlying(OtcTradeBase td, IEnumerable syntheticPrices, out double maxSpotPrice) { maxSpotPrice = 0; var synthetic = DataCacheProvider.GetUnderlyingDataSource().GetSyntheticUnderlying(td.UnderlyingCode); if (synthetic == null) { return false; } if (syntheticPrices == null) { throw new ServiceException("[组合标的]必须填写每个组成标的的期初价格"); } var model = synthetic.GetSyntheticPriceModel(); var codeSet = model.SuList.Select(n => n.UnderlyingCode).ToHashSet(StringComparer.OrdinalIgnoreCase); foreach (var item in syntheticPrices) { if (string.IsNullOrWhiteSpace(item.UnderlyingCode)) { continue; } if (!codeSet.Remove(item.UnderlyingCode)) { throw new ServiceException($"[组合标的]填写错误,组合标的中不存在此标的:{item.UnderlyingCode}"); } model.SuList.First(n => n.UnderlyingCode.Equals(item.UnderlyingCode, StringComparison.OrdinalIgnoreCase)).Price = item.Price; } if (codeSet.Any()) { throw new ServiceException("[组合标的]标的价格未填写完整,缺少:" + string.Join(",", codeSet)); } //组合标的有独特的名义本金处理方式 maxSpotPrice = model.SuList.Max(n => Math.Abs(n.Coefficient * n.Price)); if (maxSpotPrice < 1e-6) { throw new ServiceException("[组合标的]标的价格 填写错误"); } td.SpotPrice = model.Price = model.SuList.Sum(n => n.Coefficient * n.Price) + model.Constant; td.MetaDic["组合标的"] = JsonHelper.ToJson(model); return true; } /// /// 奇异期权字段检查 /// public OtcOptionSaveChecker CheckExoticFields(OtcOptionTradeFull td) { switch (td.TradeType) { case "香草期权": case "自定义交易": case "结构化产品": case "合成价差期权": break; case "障碍期权": CheckBarrierTrade(td); break; case "Risky期权": CheckRiskyTrade(td); break; case "二元期权": CheckBinaryTrade(td); break; case "亚式期权": CheckAisanTrade(td); break; case "双鲨期权": CheckDoubleSharkTrade(td); break; case "区间累积期权": CheckRangeAccTrade(td); break; case "凤凰期权": CheckAutoCallTrade(td); break; case "雪球期权": CheckSnowballTrade(td); break; case "气囊结构": CheckAirbagTrade(td); break; case "收益增强结构": CheckUnderlyigEnhanceTrade(td); break; case "结构化交易": case "现金流交易": break; case "累计期权": CheckAccumulatorTrade(td); break; case "彩虹期权": case "价差期权": throw new ServiceException("暂不支持"); default: throw new ServiceException("不可辨识的结构类型:" + td.TradeType); } return this; } //障碍期权 private void CheckBarrierTrade(OtcOptionTradeFull td) { //--------------------------------------------- // 障碍类型 //--------------------------------------------- if (string.IsNullOrWhiteSpace(td.BarrierType)) { throw new ServiceException("[障碍期权]障碍类型 必须填写"); } switch (td.BarrierType) { default: throw new ServiceException("[障碍期权]障碍类型 不支持:" + td.BarrierType); case "上升敲入": case "上升敲出": case "下降敲入": case "下降敲出": case "双障碍敲入": case "双障碍敲出": break; } //障碍价格BarrierLow 高障碍价格BarrierHigh if (td.BarrierType.Contains("双障碍")) { if (!td.BarrierHigh.HasValue) { throw new ServiceException($"[障碍期权][障碍类型为{td.BarrierType}时]高障碍价格 必须填写"); } if (td.BarrierHigh < td.BarrierLow) { throw new ServiceException("[障碍期权]高障碍价格 必须大于障碍价格"); } } //--------------------------------------------- // 观察方式 //--------------------------------------------- if (string.IsNullOrWhiteSpace(td.MonitorType)) { td.MonitorType = "离散"; } else if (td.MonitorType != "离散" && td.MonitorType != "连续") { throw new ServiceException("[障碍期权]观察方式 填写错误,当前支持的类型为:离散、连续,请求值为:" + td.MonitorType); } //--------------------------------------------- // 补偿支付方式 //--------------------------------------------- if (string.IsNullOrWhiteSpace(td.RebateType)) { td.RebateType = "AtHit"; } else if (td.RebateType != "AtHit" && td.RebateType != "AtEnd") { throw new ServiceException("[障碍期权]补偿支付方式 填写错误,当前支持的类型为:AtHit、AtEnd,请求值为:" + td.RebateType); } //补偿金额(根据权利金数据格式进行处理) if (td.IsUsePremiumRate == true) { td.Rebate = 0; } else { td.RebateRate = 0; } //高障碍补偿金额(根据权利金数据格式进行处理) if (td.IsUsePremiumRate == true) { td.RebateHigh = 0; } else { td.RebateHighRate = 0; } //补偿金额是否年化选否的时候,清空DayCount if (!td.RebateAnnualizedAtKO) { td.RebateDayCount = ""; } //障碍偏移 if (!td.BarrierShift.HasValue) { td.BarrierShift = 0; } } //二元期权 private void CheckBinaryTrade(OtcOptionTradeFull td) { //--------------------------------------------- // 二元类型 //--------------------------------------------- if (string.IsNullOrWhiteSpace(td.PayoffType)) { throw new ServiceException("[二元期权]二元类型 必须填写"); } switch (td.PayoffType) { default: throw new ServiceException("[二元期权]二元类型 不支持:" + td.PayoffType); case "CashOrNothing": case "AssetOrNothing": if (td.ExerciseMode != "European") { throw new ServiceException("[二元期权]二元类型 填写错误,所选行权方式不支持:" + td.PayoffType); } break; case "UpOneTouch": case "DownOneTouch": case "UpNoTouch": case "DownNoTouch": case "DoubleOneTouch": case "DoubleNoTouch": if (td.ExerciseMode != "American") { throw new ServiceException("[二元期权]二元类型 填写错误,所选行权方式不支持:" + td.PayoffType); } break; } //补偿金额,高障碍补偿金额(根据权利金数据格式进行处理) if (td.IsUsePremiumRate == true) { td.CashOrNothingAmount = 0; td.CashOrNothingAmountHigh = 0; } else { td.CashOrNothingAmountRate = 0; td.CashOrNothingAmountHighRate = 0; } //补偿金额是否年化选否的时候,清空DayCount if (!td.RebateAnnualizedAtKO) { td.RebateDayCount = ""; } if (td.ExerciseMode == "American") { if (td.PayoffType.Contains("Double")) { if (!td.BarrierHigh.HasValue) { throw new ServiceException($"[二元期权][美式行权,二元类型为{td.PayoffType}时]高障碍价格 必须填写"); } //高障碍价格BarrierHigh } //--------------------------------------------- // 观察方式 //--------------------------------------------- if (string.IsNullOrWhiteSpace(td.MonitorType)) { td.MonitorType = "离散"; } else if (td.MonitorType != "离散" && td.MonitorType != "连续") { throw new ServiceException("[二元期权]观察方式 填写错误,当前支持的类型为:离散、连续,请求值为:" + td.MonitorType); } //--------------------------------------------- // 补偿支付方式 //--------------------------------------------- if (string.IsNullOrWhiteSpace(td.RebateType)) { td.RebateType = "AtHit"; } else if (td.RebateType != "AtHit" && td.RebateType != "AtEnd") { throw new ServiceException("[二元期权]补偿支付方式 填写错误,当前支持的类型为:AtHit、AtEnd,请求值为:" + td.RebateType); } } else { td.MonitorType = ""; td.RebateType = ""; } } //亚式期权 private void CheckAisanTrade(OtcOptionTradeFull td) { if (!td.AveragingPeriodStartDate.HasValue) { td.AveragingPeriodStartDate = td.TradeDate.Value; } else if (td.AveragingPeriodStartDate.Value < td.TradeDate.Value || td.AveragingPeriodStartDate >= td.ExerciseDate.Value) { throw new ServiceException("[亚式期权]均价起算日 必须在交易开始日和到期日之间"); } switch (td.PayoffType) { default: if (!string.IsNullOrWhiteSpace(td.PayoffType)) { throw new ServiceException("[亚式期权]均价计算方式 不支持:" + td.PayoffType); } td.PayoffType = td.StrikeType == "Segmented" ? "EnhancedArithmeticAverage" : "ArithmeticAverage"; break; case "ArithmeticAverage": case "GeometricAverage": case "DiscreteArithmeticAverage": case "EnhancedArithmeticAverage": break; } switch (td.StrikeType) { default: if (!string.IsNullOrWhiteSpace(td.StrikeType)) { throw new ServiceException("[亚式期权]行权价类型 不支持:" + td.StrikeType); } td.StrikeType = "Fixed"; break; case "Fixed": break; case "Segmented": if (td.PayoffType != "EnhancedArithmeticAverage") { throw new ServiceException("[亚式期权]行权价类型为'分段式'时,均价计算类型只支持'增强算术平均'"); } break; case "Floating": td.Strike = null; break; } if (!td.StrikeGearingFactor.HasValue) { td.StrikeGearingFactor = 1; } else if (td.StrikeGearingFactor < 0) { throw new ServiceException("[亚式期权]杠杆率 必须大于等于0"); } } //双鲨期权 private void CheckDoubleSharkTrade(OtcOptionTradeFull td) { //BarrierLow 障碍价格,BarrierHigh 高障碍价格 if (!td.BarrierHigh.HasValue) { throw new ServiceException("[双鲨期权]高障碍价格 必须填写"); } if (td.BarrierHigh <= td.BarrierLow) { throw new ServiceException("[双鲨期权]高障碍价格 必须大于障碍价格"); } //补偿金额+高障碍补偿金额(根据权利金数据格式进行处理) if (td.IsUsePremiumRate == true) { td.Rebate = 0; td.RebateHigh = 0; } else { td.RebateRate = 0; td.RebateHighRate = 0; } //--------------------------------------------- // 观察方式 //--------------------------------------------- if (string.IsNullOrWhiteSpace(td.MonitorType)) { td.MonitorType = "离散"; } else if (td.MonitorType != "离散" && td.MonitorType != "连续") { throw new ServiceException("[双鲨期权]观察方式 填写错误,当前支持的类型为:离散、连续,请求值为:" + td.MonitorType); } //--------------------------------------------- // 补偿支付方式 //--------------------------------------------- if (string.IsNullOrWhiteSpace(td.RebateType)) { td.RebateType = "AtHit"; } else if (td.RebateType != "AtHit" && td.RebateType != "AtEnd") { throw new ServiceException("[双鲨期权]补偿支付方式 填写错误,当前支持的类型为:AtHit、AtEnd,请求值为:" + td.RebateType); } } //区间累积期权 private void CheckRangeAccTrade(OtcOptionTradeFull td) { //区间下限 LowerRange,区间上限 UpperRange if (td.UpperRange < td.LowerRange) { throw new ServiceException("[区间累积期权]区间下限 必须小于 区间上限"); } if (td.BonusRate < 0) { throw new ServiceException("[区间累积期权]区间收益 必须大于等于0"); } } //凤凰期权 private void CheckAutoCallTrade(OtcOptionTradeFull td) { //敲出障碍价格 KOBarrier,敲入障碍价格:KIBarrier,派息障碍价格 CouponBarrier //敲入期权行权价2 SpreadStrike,敲入期权行权价1 SpreadStrike1 if (td.Coupon < 0) { throw new ServiceException("[凤凰期权]票息/敲出补偿 必须大于等于0"); } } //雪球期权 private void CheckSnowballTrade(OtcOptionTradeFull td) { if (td.KORebate < 0) { throw new ServiceException("[雪球期权]票息/敲出补偿 必须大于等于0"); } if (td.Coupon < 0) { throw new ServiceException("[雪球期权]未敲出补偿 必须大于等于0"); } if (!string.IsNullOrWhiteSpace(td.KOObservationDates) && !td.KOObservationDates.All(n => n == ';') && !td.KOObservationDates.Contains(td.ExerciseDate.Value.ToString("yyyy-MM-dd"))) { //throw new ServiceException($"[雪球期权]敲出观察频率 必须包含到期日({td.ExerciseDate.Value:yyyy-MM-dd})"); throw new ServiceException($"[雪球期权]到期日和最后一个敲出观察日不一致,请修改敲出观察日列表"); } } //气囊结构 private void CheckAirbagTrade(OtcOptionTradeFull td) { //敲入参与率 if (td.KIParticipationRate < 0) { throw new ServiceException("[气囊结构]敲入参与率 必须大于等于0"); } //障碍价格 BarrierLow,收益封顶价格 HighStrike } //收益增强结构 private void CheckUnderlyigEnhanceTrade(OtcOptionTradeFull td) { if (td.AnnualizedEnhanceRate < 0) { throw new ServiceException("[收益增强结构]年化增强收益 必须大于等于0"); } } //累计期权 private void CheckAccumulatorTrade(OtcOptionTradeFull td) { switch (td.PayoffType) { default: if (!string.IsNullOrWhiteSpace(td.PayoffType)) { throw new ServiceException("[累计期权]上端收益类型 不支持:" + td.PayoffType); } td.PayoffType = "浮动"; break; case "固定": case "浮动": break; } switch (td.SettlementMode) { default: if (!string.IsNullOrWhiteSpace(td.SettlementMode)) { throw new ServiceException("[累计期权]结算方式 不支持:" + td.SettlementMode); } td.SettlementMode = "现金当日"; break; case "现金当日": case "现金期末": case "实物交割": case "现金实物合并结算": break; case "现金结算(当日)": td.SettlementMode = "现金当日"; break; case "现金结算(期末)": td.SettlementMode = "现金期末"; break; } if (string.IsNullOrWhiteSpace(td.CouponDayCount)) { td.CouponDayCount = "Act365"; } else { var daycounts = Enum.GetNames(typeof(Qdp.Pricing.Base.Enums.DayCount)); if (!daycounts.Any(n => n.Equals(td.CouponDayCount, StringComparison.OrdinalIgnoreCase))) { throw new ServiceException("[累计期权]票息日历规则 不支持:" + td.CouponDayCount); } } if (td.AccumulatorStructureType== AccumulatorStructureTypeEnum.Segmented) { switch (td.SettlementMode2) { default: if (!string.IsNullOrWhiteSpace(td.SettlementMode2)) { throw new ServiceException("[累计期权]结算方式 不支持:" + td.SettlementMode2); } td.SettlementMode2 = "现金当日"; break; case "现金当日": case "现金期末": case "实物交割": break; case "现金实物合并结算": break; case "现金结算(当日)": td.SettlementMode2 = "现金当日"; break; } switch (td.SettlementMode3) { default: if (!string.IsNullOrWhiteSpace(td.SettlementMode3)) { throw new ServiceException("[累计期权]结算方式 不支持:" + td.SettlementMode3); } td.SettlementMode3 = "现金当日"; break; case "现金当日": case "现金期末": case "实物交割": break; case "现金实物合并结算": break; case "现金结算(当日)": td.SettlementMode3 = "现金当日"; break; } } } //Risky期权 private void CheckRiskyTrade(OtcOptionTradeFull td) { if (td.Strike1 == null) { throw new ServiceException("[Risky期权]执行价格1 必须填写"); } if (td.Strike2 == null) { throw new ServiceException("[Risky期权]执行价格2 必须填写"); } if (td.Strike3 == null) { throw new ServiceException("[Risky期权]执行价格3 必须填写"); } if (td.ParticipationRate1 == null) { throw new ServiceException("[Risky期权]参与率1 必须填写"); } if (td.ParticipationRate2 == null) { throw new ServiceException("[Risky期权]参与率2 必须填写"); } if (td.ParticipationRate3 == null) { throw new ServiceException("[Risky期权]参与率3 必须填写"); } if (td.Strike1 > td.Strike2) { throw new ServiceException("[Risky期权]执行价格1 必须小于等于执行价格2"); } if (td.Strike2 > td.Strike3) { throw new ServiceException("[Risky期权]执行价格2 必须小于等于执行价格3"); } } } /// /// 为了解决导入时某些可用null类型判断的属性被人为设定成必然不为null,比如TradePrice /// enum OtcOptionTradeFullHasFlag { None = 0, PremiumRate = 1 << 0, TradeSinglePrice = 1 << 1, TradePrice = 1 << 2 } /// /// 场外期权全量(API和导入用到) /// public class OtcOptionTradeFullEx : OtcOptionTradeFull { /// /// 是否更新已有数据 /// public bool _Update { get; set; } /// /// 了结信息 /// public TradeCloseRequestModel CloseRequest { get; set; } /// /// 组合标的价格列表 /// public IEnumerable SyntheticPrices { get; set; } /// /// HasFlag /// internal OtcOptionTradeFullHasFlag HasFlag { get; set; } } /// /// 组合交易下单请求 /// public class StructureOrderRequest { public string StructureType { get; set; } public IEnumerable Trades { get; set; } } } //if (td.IsUsePremiumRate == true) //{ // td.StockEqvNotionalMax = TradeHelper.GetStockEqvNotionalReal(td.StockEqvNotional, td.ParticipationRate, td.AnnualizeFactor); //} //else //{ // td.StockEqvNotionalReal = spotPrice * td.Notional; // td.StockEqvNotional = TradeHelper.GetStockEqvNotional(td.StockEqvNotionalReal, td.ParticipationRate, td.AnnualizeFactor); //}