using BaseOUDAL; using YLErp.BLL; using YLErp.Configuration; using YLErp.Configuration.Enums; using YLErp.CustomizedBizLogic; using YLErp.DBModels.Consts; using YLErp.DBModels.Enums; using YLErp.DBModels.Helpers; using YLErp.Helpers; using YLErp.Model; using YLErp.Model.Enum; using YLErp.Modules.DataCacheModule; using YLErp.Modules.DataProviderModule; using YLErp.Modules.EodModule; using YLErp.Modules.UnderlyingModule; using YLErp.QdpModule; using static YLErp.DBModels.ConsTrade; namespace YLErp.Modules.TradeModule.OrderModule { /// /// 交易保存预处理 /// partial class TradeSavePreHandler : YLBaseService { AssetUnitDto _assetUnit; underlying_manager _underlying; readonly TradeRightChecker _tradeRightChecker; public TradeSavePreHandler(YLBaseService baseService) : base(baseService) { _tradeRightChecker = new TradeRightChecker(); } public bool isAddNew { get; private set; } /// /// 交易编号已自动生成 /// public bool TradeNumberGenerated { get; private set; } public trade Prepare(trade req, TradeSourceEnum dataSource) { if (string.IsNullOrWhiteSpace(req.TradeType)) { throw new ServiceException("结构类型 必须填写"); } if (ConsTrade.TradeTypesForHedge.Contains(req.TradeType)) { throw new ServiceException("程序错误,不再支持对冲类型的交易写入"); } if (req.UnWindDate != null && req.UnWindDate < req.StartDate) { throw new ServiceException("导入交易的平仓日期早于交易日期"); } if (req.TradeType != "现金流交易") { var um = DataCacheProvider.GetUnderlyingDataSource().GetData(req.UnderlyingCode); // 标的代码 if (um == null) { if (req.TradeType == "收益互换" && req.trade_swap.SwapType == "多空组合") { um = DataCacheProvider.GetUnderlyingDataSource().AsQueryable().FirstOrDefault(); if (um != null) { req.UnderlyingCode = um.UnderlyingCode; req.UnderlyingId = um.id; req.UnderlyingAssetClass = um.UnderlyingType; req.UnderlyingInstrumentType = um.UnderlyingInstrumentType; req.UnderlyingAssetName = um.UnderlyingName; } } else { throw new ServiceException("标的代码 必须在系统中存在"); } } if (um.UnderlyingInstrumentType == "Stock" && !um.IsCombined()) { var limit = req.TradeType == "收益互换" ? LimitRangeEnum.Swap : LimitRangeEnum.Option; var limitStatus = new StockBlackWhiteService(UserInfo).GetStockBlackWhiteList(limit, out var Codes); if (Codes != null) { if (limitStatus == Codes.Contains(um.UnderlyingCode)) { if (limitStatus) { throw new ServiceException("标的代码 填写错误,存在于黑名单中,请求值:" + req.UnderlyingCode); } else { throw new ServiceException("标的代码 填写错误,不存在于白名单中,请求值:" + req.UnderlyingCode); } } } } } if (req.TradeType == "收益互换") { req.InitialMargin = (req.trade_swap.GetMarginRate - req.trade_swap.PayMarginRate) * req.OriginalStockEqvNotional; var udm = DataCacheProvider.GetUnderlyingDataSource().GetData(req.UnderlyingCode); //价差组合初始履约预付金=Max{腿一组合系数×腿一价格×腿一预付金率×数量×合约乘数,abs(腿二组合系数)×腿二价格×腿二预付金率×数量×合约乘数} if (PS.Config.Company == CompanyEnum.华安 && udm.CommodityCode == "组合标的") { var synthetic = new SyntheticUnderlyingPriceService(this.OptUser).GetPriceModel(req.UnderlyingCode); var InitMarginRate = synthetic.SuList.Max(l => DataCacheProvider.GetUnderlyingDataSource().GetData(l.UnderlyingCode).MarginRate); var eodprice = new EodPriceProvider(req.TradeDate ?? DateTime.Now).GetPriceProvider(SettlementTypeEnum.ClosePrice); if (req.trade_swap.GetMarginRate == InitMarginRate) { req.InitialMargin = synthetic.SuList.Max(l => Math.Abs(l.Coefficient) * l.ContractSize * eodprice.GetPrice(l.UnderlyingCode) * DataCacheProvider.GetUnderlyingDataSource().GetData(l.UnderlyingCode).MarginRate * req.trade_swap.PayNotional); } } } isAddNew = req.id == 0; if (isAddNew) { SetDBModelCreator(req); req.TradeStatus = ConsTrade.新增待确认; req.TradeSource = dataSource.ToString(); if (string.IsNullOrWhiteSpace(req.TradeNumber) || (PS.Config.Company == CompanyEnum.光大光子 && (req.TradeNumber == "STG" || req.TradeNumber == "STD" || req.TradeNumber == "FWD" || req.TradeNumber == "SPD"))) { TradeNumberGenerated = true; if (req.UnderlyingCode != null && req.UnderlyingCode != "") { PrepareUnderlying(req, false); } req.TradeNumber = BizLogicSingleton.Instance.GenerateTradeNumberBeforeConfirm(req, DbContext); } else if (DbContext.trade.Any(n => n.TradeNumber == req.TradeNumber)) { throw new ServiceException($"交易编号'{req.TradeNumber}'已经存在"); } else if (PS.Config.ErpElement.UpperTradeNumber) { req.TradeNumber = req.TradeNumber.ToUpperInvariant(); } if (req.TradeType == "远期") { //添加拓展字段 是否补充远期价值,Type2:默认值 -1,其他默认值 0 if (!req.MetaDic.TryGetValue("IsSupplyForwardValue", out var metaDic)) { if (PS.Config.ErpElement.ForwardValueIsSupplyOrPay == YLErp.Configuration.Enums.ForwardValueIsSupplyOrPay.Type2) { req.MetaDic.Add("IsSupplyForwardValue", "-1"); } else { req.MetaDic.Add("IsSupplyForwardValue", "0"); } } } } else if (!string.IsNullOrWhiteSpace(req.TradeNumber)) { if (DbContext.trade.Any(n => n.id != req.id && n.TradeNumber == req.TradeNumber && n.ValidState != "InValid")) { throw new ServiceException($"交易编号'{req.TradeNumber}'已经存在"); } if (PS.Config.ErpElement.UpperTradeNumber) { req.TradeNumber = req.TradeNumber.ToUpperInvariant(); } } if (req.TradeType == "结构化交易") { if (!isAddNew) { throw new ServiceException("程序错误,结构化交易仅支持新增录入"); } PrepareStructureTrades(req, dataSource); if (req.IsGroup == 1) { PrepareOtcTradeCalc(req); } } else { if (req.ParentTradeId < 1 && req.TradeType != ConsGlobal.TradeType.Forward && req.TradeType != ConsGlobal.TradeType.Custom) { req.StructureType = null; } PrepareSingleTrade(req, dataSource); } req.PrincipalRateWrite = req.PrincipalRateWrite.IsNormalize() ? req.PrincipalRateWrite : null; return req; } //准备结构化交易 private void PrepareStructureTrades(trade req, TradeSourceEnum dataSource) { var subTrades = req.SubTrades?.ToList(); if (subTrades == null || !subTrades.Any()) { throw new ServiceException("结构化交易缺少子交易"); } if (subTrades.Count < 2) { throw new ServiceException("结构化交易需要最少两笔子交易"); } if (string.IsNullOrWhiteSpace(req.StructureType)) { throw new ServiceException("缺少期权结构类型"); } var option = StructureOption_Code.StructureOptions.FirstOrDefault(o => o.Name == req.StructureType || o.CnName == req.StructureType); if (option != null) { req.StructureType = option.CnName; } var index = 1; foreach (var subTrade in subTrades) { PrepareSingleTrade(subTrade, dataSource); if (req.IsGroup == 0) { subTrade.StructureType = req.StructureType; } if (PS.Config.Is海通) { subTrade.TradeNumber = req.TradeNumber.Substring(0, req.TradeNumber.Length - 2) + index.ToString("D2"); } else { subTrade.TradeNumber = BizLogicSingleton.Instance.GenerateSubTradeNumberBeforeConfirm(subTrade, req, index, DbContext); } index++; } if (subTrades.Select(n => n.ClientId).Distinct().Count() > 1) { throw new ServiceException("结构化交易组合必须有相同的交易对手方"); } if (subTrades.Select(n => n.AssetId).Distinct().Count() > 1) { throw new ServiceException("结构化交易组合必须有相同的簿记账户"); } } //准备单个交易 private trade PrepareSingleTrade(trade req, TradeSourceEnum dataSource) { //准备交易基础信息 PrepareBaseInfo(req); int? VarietyId2 = 0; if (req.TradeType == "远期") { var underlying = DataCacheManager.GetUnderlyingDataSource().GetData(req.BasisUnderlyingCode ?? ""); VarietyId2 = underlying?.UnderlyingTypeId; } //检查交易权限,req.VarietyId来自PrepareBaseInfo _tradeRightChecker.Check(_assetUnit, req.TraderId, req.ClientId, req.VarietyId ?? 0, VarietyId2 ?? 0, req.TradeType); if (req.TradeType == "收益互换") { PrepareSwapTrade(req, dataSource); } else { PrepareOtcTrade(req, dataSource); } if (req.TradeType == "现金流交易") {//现金流交易在平仓时的份额即名义本金,现金流没有份额的概念 req.Notional = req.StockEqvNotional; req.OriginalNotional = req.StockEqvNotional; req.TradeAmount = req.StockEqvNotional; if (req.trade_cashflow.DepositType != CashflowDepositTypeEnum.资金收益) { req.trade_cashflow.PrepayRatio = 0; } } req.OptId = UserId; req.OptName = UserName; req.OptDate = OptDate; //设置HasGeneratedConfirmBook为0 //req.HasGeneratedConfirmBook = 0; if (isAddNew) { req.ValidState = ConsGlobal.Valid; req.CreateDate = DateTime.Now; req.TradeSavedVol = PS.Config.Is厦门象屿 ? req.TradeCloseVolatility : req.TradeOpenVolatility; if (PS.Config.ErpElement.VolMode == VolModeEnum.VolSurface) { req.TradeSavedVol = req.Vol; } } req.UnderlyingCode = req.UnderlyingCode == null ? null : req.UnderlyingCode.ToUpperInvariant(); return req; } //准备交易标的信息 public underlying_manager PrepareUnderlying(trade req, bool prepareSynthetic) { var underlyingCode = req.UnderlyingCode ?? string.Empty; _underlying ??= UnderlyingDataProvider.GetUnderlying(underlyingCode); if (_underlying == null) { throw new ServiceException("标的信息不存在:" + req.UnderlyingCode); } if (_underlying.IsFutures() && _underlying.MaturityDate == null) { throw new ServiceException("标的到期日不存在:" + req.UnderlyingCode); } //组合标的缺少合成项的价格 if (prepareSynthetic) { var checkSynthetic = false; if (req.TradeType == "收益互换") { if (!req.trade_swap.IsGetFloatingProfit) { var gun = UnderlyingDataProvider.GetUnderlying(req.trade_swap.GetUnderlyingCode); checkSynthetic = gun != null && gun.IsSynthetic(); } if (!req.trade_swap.IsPayFloatingProfit) { var pun = UnderlyingDataProvider.GetUnderlying(req.trade_swap.PayUnderlyingCode); if (pun != null && pun.IsSynthetic() && (!req.MetaDic.TryGetValue("组合标的2", out var str) || string.IsNullOrWhiteSpace(str))) { throw new ServiceException("组合标的缺少组合价格:" + pun.UnderlyingCode); } } } else { checkSynthetic = _underlying.IsSynthetic(); } if (checkSynthetic) { if (!req.MetaDic.TryGetValue("组合标的", out var str) || string.IsNullOrWhiteSpace(str)) { throw new ServiceException("组合标的缺少组合价格:" + req.UnderlyingCode); } var saveExt = req.SaveExt; if (saveExt == null || !saveExt.MaxSpotPrice.HasValue || saveExt.MaxSpotPrice.Value < 1e-6) { var sulist = JsonHelper.Deserialize(str)?.SuList; var maxSpotPrice = sulist == null ? 0 : sulist.Max(n => Math.Abs(n.Coefficient * n.Price)); if (maxSpotPrice < 1e-6) { throw new ServiceException("[组合标的]标的价格 填写错误"); } if (saveExt == null) { req.SaveExt = new TradeSaveExt(); } req.SaveExt.MaxSpotPrice = maxSpotPrice; } } } req.UnderlyingId = _underlying.id; req.UnderlyingCode = _underlying.UnderlyingCode; req.VarietyId = _underlying.UnderlyingTypeId; req.UnderlyingAssetClass = _underlying.UnderlyingType; req.UnderlyingAssetName = _underlying.UnderlyingName; req.MaturityDate = _underlying.IsFutures() ? _underlying.MaturityDate : null; req.UnderlyingInstrumentType = _underlying.GetMainType(); req.CountRatio = _underlying.CountRatio; //权益类标的关联的交易如果没有设置分红率则取标的上的分红率,如果标的分红率为空则取0 if (!req.DividendRate.HasValue && _underlying.CalcTypeIsStock()) { req.DividendRate = _underlying.DividendRate ?? 0; } return _underlying; } //准备交易基础信息(标的、簿记、交易日期) private void PrepareBaseInfo(trade req) { if (!req.TradeDate.HasValue) { throw new ServiceException("交易日期 必须填写"); } if (req.TradeDate.Value.Year < 2000) { throw new ServiceException("交易日期 填写错误:" + req.TradeDate.Value.ToString("yyyy-MM-dd")); } //if (req.TradeDate > valuedateBLL.ValueDate) //{ // throw new ServiceException("交易日期不能是未来日期:" + req.TradeDate.Value.ToString("yyyy-MM-dd")); //} if (QdpCalendarHelper.IsHoliday(req.TradeDate.Value) && req.TradeType != "现金流交易") { throw new ServiceException("交易日期不能是假日:" + req.TradeDate.Value.ToString("yyyy-MM-dd")); } //如果起始日没有值,默认赋值交易日 if (!req.StartDate.HasValue) { req.StartDate = req.TradeDate; } //-------------------------------------- // 标的信息 //-------------------------------------- if (req.TradeType != "现金流交易") { PrepareUnderlying(req, true); } //-------------------------------------- // 簿记帐户 //-------------------------------------- if (req.AssetId < 1) { throw new ServiceException("请选择簿记账户"); } if (_assetUnit == null) { _assetUnit = DbContext.assetunit.Where(n => n.id == req.AssetId) .Select(n => new AssetUnitDto { id = n.id, Name = n.Name, TraderIds = n.TraderIds, UserGroup = n.UserGroup }).FirstOrDefault(); if (_assetUnit == null) { throw new ServiceException("簿记账户不存在"); } } else if (_assetUnit.id != req.AssetId) { throw new ServiceException("程序错误,多个簿记账户并存"); } req.AssetBookName = _assetUnit.Name; //-------------------------------------- // 交易员信息(如果没有交易员,查找簿记账户中的交易员) //-------------------------------------- if (string.IsNullOrWhiteSpace(req.TraderName)) { //有交易员id,通过id查名字 if (req.TraderId > 0) { req.TraderName = UserBLL.GetNameById(req.TraderId); } else { //当前登录用户作为交易员 req.TraderId = UserId; req.TraderName = UserName; } } if (req.TradeType != "现金流交易") { if (req.TradeType != "远期") { req.Lots = req.Notional / _underlying.ContractSize; } else if (req.Lots == null || req.Lots < 1e-6) { throw new ServiceException("成交手数 必须填写并大于0"); } } } //准备衍生品交易基础信息 private void PrepareOtcTrade(trade req, TradeSourceEnum dataSource) { var isForward = req.TradeType == "远期"; var isCustomTrad = req.TradeType == "自定义交易"; var flag = false; var codes = new List { req.UnderlyingCode }; if (isForward) { codes.Add(req.BasisUnderlyingCode); } //判断标的是否为现货 flag = UndelyingHelper.IsCodesExistsCommoditySpot(codes, (um) => { return um.UnderlyingInstrumentType == "CommoditySpot" ? true : false; }); //-------------------------------------- // 日期信息 //-------------------------------------- if (!req.ExerciseDate.HasValue) { throw new ServiceException("到期日期 必须填写"); } if (req.TradeDate.Value > req.ExerciseDate.Value) { throw new ServiceException("交易日应该在到期日之前"); } if (req.MaturityDate != null && req.ExerciseDate.Value > req.MaturityDate.Value && req.TradeType != "累计期权") { throw new ServiceException("到期日期 必须小于等于 标的到期日"); } if (flag && PS.Config.Is浙期 /*&& isForward*/) { if (!req.MetaDic.TryGetValue("ActualExerciseDate", out var ActualExerciseDateStr) && string.IsNullOrEmpty(ActualExerciseDateStr)) { throw new ServiceException("实际到期日期 必须填写"); } if (!DateTime.TryParse(ActualExerciseDateStr, out var ActualExerciseDate)) { throw new ServiceException("实际到期日期 必须是日期格式"); } if (req.TradeDate.Value > ActualExerciseDate) { throw new ServiceException("交易日应该在实际到期日之前"); } if (req.MaturityDate != null && ActualExerciseDate > req.MaturityDate.Value) { throw new ServiceException("实际到期日期 必须小于等于 标的到期日"); } } if (!req.SettlementDate.HasValue) { req.SettlementDate = req.ExerciseDate; } else if (req.SettlementDate < req.TradeDate) { throw new ServiceException("结算日期 必须大于等于 成交日期"); } if (!req.PremiumPayDate.HasValue || isForward) { req.PremiumPayDate = req.TradeDate; } //--------------------------------------------- // 交易方向 //--------------------------------------------- if (string.IsNullOrWhiteSpace(req.BuySell)) { throw new ServiceException("交易方向 必须填写"); } if (req.BuySell != "买入" && req.BuySell != "卖出") { throw new ServiceException("交易方向 填写错误:" + req.BuySell); } //--------------------------------------------- // 行权方式 //--------------------------------------------- CheckExerciseMode(req); //--------------------------------------------- // 看涨看跌 + 远期多空方向 //--------------------------------------------- CheckOptionType(req); //--------------------------------------------- // 标的价格 + 执行价格 //--------------------------------------------- if (!req.SpotPrice.HasValue && req.TradeType != "现金流交易") { throw new ServiceException("期初标的价格 必须填写"); } if (string.IsNullOrWhiteSpace(req.IsMoneynessOption)) { req.IsMoneynessOption = "否"; } else if (req.IsMoneynessOption != "是" && req.IsMoneynessOption != "否") { throw new ServiceException("是否相对行权价 填写错误:" + req.IsMoneynessOption); } if (!req.Strike.HasValue && !(req.TradeType == "自定义交易" || req.TradeType == "现金流交易" || (req.TradeType == "亚式期权" && req.trade_asian_option?.StrikeType == "Floating"))) { throw new ServiceException("执行价格 必须填写"); } if (req.TradeType == "亚式期权" && req.trade_asian_option != null) { if (req.trade_asian_option.PayoffType == AsianAverageType.EnhancedArithmeticAverage && req.trade_asian_option.StrikeType != "Floating") { if (req.trade_asian_option.EnhancedPrice == 0) { throw new ServiceException("增强价格 必须填写"); } } else { req.trade_asian_option.EnhancedPrice = 0; } } //-------------------------------------- // 客户信息 //-------------------------------------- PrepareOtcTradeClient(req, dataSource); //-------------------------------------- // 交易波动率、平滑过渡天数 //-------------------------------------- PrepareOtcTradeVol(req, dataSource); //-------------------------------------- // 奇异期权 //-------------------------------------- switch (req.TradeType) { case "凤凰期权": var autocall = req.trade_autocall; if (!autocall.IsAnnualized2 || !autocall.AnnualizeFactor2.HasValue) { autocall.AnnualizeFactor2 = 1; req.MetaDic[nameof(autocall.AnnualizeFactor2)] = "/"; } req.Strike = autocall.SpreadStrike1; break; case "雪球期权": CheckSnowball(req); break; case "亚式期权": if (req?.trade_asian_option?.StrikeType == "Floating") { req.Strike = null; } if (req?.trade_asian_option?.StrikeType == "Segmented" && req?.trade_asian_option?.PayoffType != "EnhancedArithmeticAverage") { throw new ServiceException("行权价类型为'分段式'时,均价计算类型必须为'增强算术平均'"); } if (req?.trade_asian_option?.PayoffType == "EnhancedArithmeticAverage" && req?.trade_asian_option?.StrikeType == "Floating") { throw new ServiceException("均价计算类型为'增强算术平均'时,行权价类型不能为'浮动行权价'"); } break; case "自定义交易": req.ExerciseMode = null; break; case "二元期权": if (req.ExerciseMode == "American") { var payoffType = req.trade_binary_option.PayoffType; if (payoffType.StartsWith("Down", StringComparison.OrdinalIgnoreCase)) { req.OptionType = ConsGlobal.CallPut.Put_CN; } else if (payoffType.StartsWith("Double", StringComparison.OrdinalIgnoreCase)) { req.OptionType = ""; } else { req.OptionType = ConsGlobal.CallPut.Call_CN; } } else { req.trade_binary_option.ObservationDates = null; } break; case "障碍期权": if (string.IsNullOrEmpty(req.trade_barrier_option.BarrierType)) { throw new ServiceException("障碍类型 必须填写"); } if (!req.trade_barrier_option.BarrierType.EndsWith("敲出")) { req.trade_barrier_option.RebateAnnualizedAtKO = false; } break; case "累计期权": if (req.trade_accumulator_option.AccumulatorStructureType != AccumulatorStructureTypeEnum.Segmented) { if (req.OptionType == "看涨") { if (req.Strike > req.trade_accumulator_option.KOBarrier) { throw new ServiceException("[累计期权]期权看涨时,执行价格必须小于等于障碍价格"); } } else if (req.Strike < req.trade_accumulator_option.KOBarrier) { throw new ServiceException("[累计期权]期权看跌时,执行价格必须大于等于障碍价格"); } if (req.trade_accumulator_option.EarlyTerminate && req.trade_accumulator_option.SettlementMode == "现金期末") { throw new ServiceException("[累计期权]结算方式为'现金结算(期末)'时,需要将'敲出是否终止'设为'否'"); } } else { if (req.trade_accumulator_option.Strike2 == null) { throw new ServiceException("[累计期权 三段式]执行价格2 必须填写"); } if (req.trade_accumulator_option.Strike3 == null) { throw new ServiceException("[累计期权 三段式]执行价格3 必须填写"); } if (req.trade_accumulator_option.KOBarrier == null) { throw new ServiceException("[累计期权 三段式]障碍价格 必须填写"); } if (req.trade_accumulator_option.Strike2 < req.Strike) { throw new ServiceException("[累计期权 三段式]执行价格2需要大于等于执行价格1"); } if (req.trade_accumulator_option.Strike3 < req.trade_accumulator_option.Strike2) { throw new ServiceException("[累计期权 三段式]执行价格3需要大于等于执行价格2"); } if (req.OptionType == "看涨") { if (req.trade_accumulator_option.Strike2 >= req.trade_accumulator_option.KOBarrier || req.trade_accumulator_option.Strike3 < req.trade_accumulator_option.KOBarrier) { throw new ServiceException("[累计期权 三段式]障碍价格需要大于执行价2小于等于执行价3"); } } else { if (req.Strike > req.trade_accumulator_option.KOBarrier || req.trade_accumulator_option.Strike2 <= req.trade_accumulator_option.KOBarrier) { throw new ServiceException("[累计期权 三段式]障碍价格需要大于等于执行价1小于执行价2"); } } if (string.IsNullOrWhiteSpace(req.trade_accumulator_option.SettlementMode2)) { req.trade_accumulator_option.SettlementMode2 = "现金当日"; } if (string.IsNullOrWhiteSpace(req.trade_accumulator_option.ForwardDateType2)) { req.trade_accumulator_option.ForwardDateType2 = PS.Config.Company == CompanyEnum.中粮 ? "标的到期日" : PS.Config.Is厦门象屿 ? "标的交割月前一交易日" : "期权到期日"; } if (string.IsNullOrWhiteSpace(req.trade_accumulator_option.ForwardPriceType2)) { req.trade_accumulator_option.ForwardPriceType2 = "期权行权价格"; } if (string.IsNullOrWhiteSpace(req.trade_accumulator_option.SettlementMode3)) { req.trade_accumulator_option.SettlementMode3 = "现金当日"; } if (string.IsNullOrWhiteSpace(req.trade_accumulator_option.ForwardDateType3)) { req.trade_accumulator_option.ForwardDateType3 = PS.Config.Company == CompanyEnum.中粮 ? "标的到期日" : PS.Config.Is厦门象屿 ? "标的交割月前一交易日" : "期权到期日"; } if (string.IsNullOrWhiteSpace(req.trade_accumulator_option.ForwardPriceType3)) { req.trade_accumulator_option.ForwardPriceType3 = "期权行权价格"; } } if (string.IsNullOrWhiteSpace(req.trade_accumulator_option.PayoffType)) { req.trade_accumulator_option.PayoffType = "固定"; } if (string.IsNullOrWhiteSpace(req.trade_accumulator_option.SettlementMode)) { req.trade_accumulator_option.SettlementMode = "现金当日"; } if (string.IsNullOrWhiteSpace(req.trade_accumulator_option.ForwardDateType)) { req.trade_accumulator_option.ForwardDateType = PS.Config.Company == CompanyEnum.中粮 ? "标的到期日" : PS.Config.Is厦门象屿 ? "标的交割月前一交易日" : "期权到期日"; } if (string.IsNullOrWhiteSpace(req.trade_accumulator_option.ForwardPriceType)) { req.trade_accumulator_option.ForwardPriceType = "期权行权价格"; } break; case "Risky期权": var risky = req.trade_risky_option; req.Strike = risky.Strike1; break; } //检查价差期权(暂时在外部处理) //检查互算是否正确(名义本金、权利金、交易总额、交易数量、交易份额) //-------------------------------------- // 其它处理 //-------------------------------------- if (isAddNew) { req.TradeStatus = ConsTrade.新增待确认; } if (!req.ParticipationRate.HasValue) { req.ParticipationRate = 1; } // 无风险利率 if (!req.NoRiskRate.HasValue) { req.NoRiskRate = valuedateBLL.SystemDate.RiskFreeRate / 100; } else if (req.NoRiskRate < 0) { throw new ServiceException("无风险利率 填写错误,必须大于等于0,请求值:" + req.NoRiskRate); } //分红率是可以为负值的 //-------------------------------------- // 名义本金或总额成交互算 -- 保底收益率 //-------------------------------------- PrepareOtcTradeCalc(req); if (req.SettlementType < 0 || req.SettlementType > SettlementTypeEnum.ReferencePrice) { throw new ServiceException("结算方式 填写错误,当前支持的类型为:0(收盘价)、1(结算价),、2(参考价),请求值为:" + (int)req.SettlementType); } //存续天数 if (!req.DurationDays.HasValue) { req.DurationDays = (short)(req.ExerciseDate.Value.Date.Subtract(req.TradeDate.Value.Date).TotalDays + 1); } } //准备互换交易 private void PrepareSwapTrade(trade req, TradeSourceEnum dataSource) { PrepareSwapTradeClient(req); if (!req.SettlementDate.HasValue) { req.SettlementDate = req.ExerciseDate; } req.OriginalNotional = req.Notional; req.BuySell = (req.trade_swap.GetTradePrice ?? 0) - (req.trade_swap.PayTradePrice ?? 0) == 0 ? (req.trade_swap.IsPayFloatingProfit ? "卖出" : "买入") : ((req.trade_swap.GetTradePrice ?? 0) - (req.trade_swap.PayTradePrice ?? 0) > 0 ? "卖出" : "买入"); req.Strike = null; req.OriginalStockEqvNotional = req.StockEqvNotional; req.StockEqvNotionalReal = req.StockEqvNotional; var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(req.QuoteCurrency, req.SettlementCurrency, req.TradeDate.Value , seekPreday: dataSource == TradeSourceEnum.系统交易); req.TradePrice = (req.trade_swap.IsTradePriceWhenOpen ? Math.Abs((req.trade_swap.GetTradePrice ?? 0) - (req.trade_swap.PayTradePrice ?? 0)) : 0) * currencyRate; } //场外期权交易波动率、平滑过渡天数 private static void PrepareOtcTradeVol(trade req, TradeSourceEnum dataSource) { if (!PS.Config.IsTradeVol) { //volsuface模式下也存在开仓波动率 req.TradeCloseVolatility = null; if (req.Vol.HasValue) { req.TradeOpenVolatility = req.Vol; } else { req.Vol = req.TradeOpenVolatility; //从webapi请求,填写的是TradeOpenVolatility } } else if (req.TradeType != "自定义交易" && req.TradeType != "远期" && req.TradeType != "收益互换" && req.TradeType != "现金流交易") { if (!req.TradeOpenVolatility.HasValue) { throw new ServiceException("成交波动率 必须填写"); } if (req.TradeOpenVolatility.Value < 0) { throw new ServiceException("成交波动率 必须大于等于0"); } if (!req.TradeCloseVolatility.HasValue) { throw new ServiceException("目标波动率 必须填写"); } if (req.TradeCloseVolatility.Value < 0) { throw new ServiceException("目标波动率 必须大于等于0"); } if (req.NumOfSmoothingDays.HasValue) { if (req.NumOfSmoothingDays.Value < 1) { throw new ServiceException("平滑过渡天数 必须大于0"); } var remianDays = QdpCalendarHelper.GetNonHolidayDaysBetween(req.TradeDate.Value, req.ExerciseDate.Value) + 1; if (req.NumOfSmoothingDays > remianDays) { throw new ServiceException($"平滑过渡天数{req.NumOfSmoothingDays}不能大于存续天数{remianDays}天"); } } else if (dataSource == TradeSourceEnum.导入交易) { req.NumOfSmoothingDays = QdpCalendarHelper.GetNonHolidayDaysBetween(req.TradeDate.Value, req.ExerciseDate.Value) + 1; } else { throw new ServiceException("平滑过渡天数 必须填写"); } } else if (req.TradeType == "自定义交易") { if (req.NumOfSmoothingDays.HasValue) { if (req.NumOfSmoothingDays.Value < 1) { throw new ServiceException("平滑过渡天数 必须大于0"); } var remianDays = QdpCalendarHelper.GetNonHolidayDaysBetween(req.TradeDate.Value, req.ExerciseDate.Value) + 1; if (req.NumOfSmoothingDays > remianDays) { throw new ServiceException($"平滑过渡天数{req.NumOfSmoothingDays}不能大于存续天数{remianDays}天"); } } } } //衍生品交易客户信息 private void PrepareOtcTradeClient(trade req, TradeSourceEnum dataSource) { var client = PrepareBaseTradeClient(req); var structureTypes = new List() { "牛市价差", "熊市价差" };//OTC-5843此两种组合特殊,去除客户可接受风险等级判断 if ((client.RiskServiceDegree == null || (client.RiskServiceDegree < 5 && !structureTypes.Contains(req.StructureType))) && req.BuySell == "买入") { throw new ServiceException($"客户'{client.Name}'为非高风险客户,没有卖出权限"); } if (_underlying != null && _underlying.CalcTypeIsStock() && ((client.TradingInstType & TradingInstTypeEnum.Equity) != TradingInstTypeEnum.Equity)) { throw new ServiceException($"客户'{client.Name}'的'交易资产'属性不包括'权益',不能进行权益类的期权交易"); } if (PS.Config.ErpElement.SecuritiesEnvironment) { if (client.AccessRule == AccessRuleEnum.MySideAccess.GetHashCode() && req.BuySell == "买入") { throw new ServiceException($"客户'{client.Name}'为我方准入,不能进行买入的期权交易"); } if (client.AccessRule == AccessRuleEnum.OpSideAccess.GetHashCode() && req.BuySell == "卖出") { throw new ServiceException($"客户'{client.Name}'为对手方准入,不能进行卖出的期权交易"); } } req.ClientName = client.Name; } //互换交易 private void PrepareSwapTradeClient(trade req) { var client = PrepareBaseTradeClient(req); if (_underlying.CalcTypeIsStock() && ((client.TradingInstType & TradingInstTypeEnum.Equity) != TradingInstTypeEnum.Equity)) { throw new ServiceException($"客户'{client.Name}'的'交易资产'属性不包括'权益',不能进行收益互换交易"); } req.ClientName = client.Name; req.SettlementCurrency = client.SettlementCurrency; } //客户信息 private Client PrepareBaseTradeClient(trade req) { if (req.ClientId < 1) { throw new ServiceException("客户信息不存在"); } var client = DbContextFactory.GetClientDbContext(OptUser).client.FirstOrDefault(n => n.id == req.ClientId); if (client == null) { throw new ServiceException("客户信息不存在"); } if (client.ProcessStatus == "已休眠" || client.ProcessStatus == "已销户") { if (!client.ProcessOptDate.HasValue || client.ProcessOptDate.Value < req.TradeDate.Value) { var tradeDateStr = req.TradeDate.Value.ToString("yyyy-MM-dd"); var processDateStr = client.ProcessOptDate?.ToString("yyyy-MM-dd HH:mm:ss"); throw new ServiceException($"客户'{client.Name}'{client.ProcessStatus}并且处理日期{processDateStr}早于交易日期'{tradeDateStr}',无法保存交易!"); } } else if (client.ProcessStatus != "已开户") { throw new ServiceException($"客户'{client.Name}'尚未完成开户!"); } return client; } //衍生品交易名义本金或总额成交互算 private void PrepareOtcTradeCalc(trade req) { if (!req.IsAnnualized || !req.AnnualizeFactor.HasValue) { req.AnnualizeFactor = 1; req.MetaDic[nameof(req.AnnualizeFactor)] = "/"; } var spotPrice = Math.Abs(req.SpotPrice == null ? 0 : req.SpotPrice.Value); var CountRatio = _underlying == null ? 0 : _underlying.CountRatio; var participationRate = req.ParticipationRate ?? 0; var saveExt = req.SaveExt; var annualizeFactor = req.AnnualizeFactor; req.TradePrice = Commons.OtcFormatHelper.GetTradePriceDouble(req.TradePrice ?? 0); //保存初始保底收益总额 实际保底收益率 //req.PrincipalSum = req.OriginalPrincipalSum; req.PrincipalRate = TradeHelper.GetPrincipalRateReal(req.StockEqvNotionalReal, req.OriginalPrincipalSum, annualizeFactor); if (req.TradeType == "现金流交易") { req.StockEqvNotionalReal = req.StockEqvNotional; req.Notional = 0; req.TradeSinglePrice = 0; } else if (req.TradeType == "远期") { req.Notional = req.TradeAmount * CountRatio; req.TradeSinglePrice = req.Notional > 0 ? req.TradePrice / req.Notional : 0; if (PS.Config.ErpElement.ForwardTradePriceModel == 0 && !string.IsNullOrWhiteSpace(req.BasisUnderlyingCode)) { var spotInfo = req.MetaDic.ContainsKey("期初信息") ? JsonHelper.Deserialize(req.MetaDic["期初信息"]) : null; if (spotInfo?.Length < 2) { throw new SystemException($"MetaDic标的价格有误"); } double.TryParse(spotInfo[0], out var price1); double.TryParse(spotInfo[1], out var price2); req.StockEqvNotional = price1 > price2 ? TradeHelper.GetStockEqvNotional(req.Notional * price1, 1, 1) : TradeHelper.GetStockEqvNotional(req.Notional * price2, 1, 1); } else { req.StockEqvNotional = TradeHelper.GetStockEqvNotional(req.Notional * spotPrice, 1, 1); } req.StockEqvNotionalReal = req.StockEqvNotional; } else { var isAutoCall = false; switch (req.TradeType) { case "凤凰期权": isAutoCall = true; annualizeFactor = req.trade_autocall.AnnualizeFactor2; break; case "雪球期权": isAutoCall = true; annualizeFactor = req.trade_snowball.AnnualizeFactor2; break; } if (saveExt != null && saveExt.NotionalFlag != TradingNotionalFlag.None) { if (req.TradeType == "累计期权") { var customObservDates = QdpHelper.ParseObservationDate(req.trade_accumulator_option.KOObservationDates); customObservDates ??= Qdp.Pricing.Base.Implementations.CalendarImpl.Get("chn") .BizDaysBetweenDatesExcluStartDay(req.TradeDate.Value, req.ExerciseDate.Value).ToArray(); req.TradeAmount = req.trade_accumulator_option.AccumuTradeAmount * customObservDates.Length; req.Notional = req.TradeAmount * CountRatio; req.StockEqvNotionalReal = req.Notional * spotPrice; req.StockEqvNotional = TradeHelper.GetStockEqvNotional(req.StockEqvNotionalReal, participationRate, annualizeFactor); req.IsTradePricePayType = saveExt.PremiumFlag == TradingPremiumFlag.TradePrice; if (req.IsTradePricePayType) { req.TradeSinglePrice = TradeHelper.GetTradeSinglePriceByTradePrice(req.TradePrice, req.Notional, req.OriginalPrincipalSum, req.BuySell, req.TradeType, true); req.PremiumRate = TradeHelper.GetPremiumRateByTradePrice(req.TradePrice, req.StockEqvNotional, participationRate, req.OriginalPrincipalSum, req.AnnualizeFactor, req.BuySell, req.TradeType, true); } else { req.TradePrice = TradeHelper.GetTradePriceBySinglePrice(req.TradeSinglePrice, req.Notional, req.OriginalPrincipalSum, req.BuySell, req.TradeType, true); req.PremiumRate = TradeHelper.GetPremiumRateByTradeSinglePrice(req.TradeSinglePrice, spotPrice); } } else { switch (saveExt.NotionalFlag) { case TradingNotionalFlag.StockEqvNotionalReal: { req.StockEqvNotional = TradeHelper.GetStockEqvNotional(req.StockEqvNotionalReal, participationRate, annualizeFactor); var notionalEqv = isAutoCall ? req.StockEqvNotional * participationRate : req.StockEqvNotionalReal; req.Notional = Math.Abs(spotPrice) > 0 ? notionalEqv / spotPrice : 0; req.TradeAmount = CountRatio == 0 ? 0 : req.Notional / CountRatio; } break; case TradingNotionalFlag.StockEqvNotional: { req.StockEqvNotionalReal = TradeHelper.GetStockEqvNotionalReal(req.StockEqvNotional, participationRate, annualizeFactor); var notionalEqv = isAutoCall ? req.StockEqvNotional * participationRate : req.StockEqvNotionalReal; req.Notional = Math.Abs(spotPrice) > 0 ? notionalEqv / spotPrice : 0; req.TradeAmount = CountRatio == 0 ? 0 : req.Notional / CountRatio; } break; case TradingNotionalFlag.Notional: req.TradeAmount = CountRatio == 0 ? 0 : req.Notional / CountRatio; if (isAutoCall) { req.StockEqvNotional = Math.Abs(participationRate) > 0 ? req.Notional * spotPrice / participationRate : 0; req.StockEqvNotionalReal = TradeHelper.GetStockEqvNotionalReal(req.StockEqvNotional, participationRate, annualizeFactor); } else { req.StockEqvNotionalReal = req.Notional * spotPrice; req.StockEqvNotional = TradeHelper.GetStockEqvNotional(req.StockEqvNotionalReal, participationRate, annualizeFactor); } break; case TradingNotionalFlag.TradeAmount: req.Notional = CountRatio == 0 ? 0 : req.TradeAmount * CountRatio; if (isAutoCall) { req.StockEqvNotional = Math.Abs(participationRate) > 0 ? req.Notional * spotPrice / participationRate : 0; req.StockEqvNotionalReal = TradeHelper.GetStockEqvNotionalReal(req.StockEqvNotional, participationRate, annualizeFactor); } else { req.StockEqvNotionalReal = req.Notional * spotPrice; req.StockEqvNotional = TradeHelper.GetStockEqvNotional(req.StockEqvNotionalReal, participationRate, annualizeFactor); } break; default: throw new SystemException($"[{nameof(TradeSavePreHandler)}.{nameof(PrepareOtcTradeCalc)}][NotionalFlag]系统程序错误"); } req.IsUsePremiumRate = false; req.IsTradePricePayType = false; switch (saveExt.PremiumFlag) { case TradingPremiumFlag.TradePrice: req.IsTradePricePayType = true; req.IsUsePremiumRate = saveExt.NotionalFlag == TradingNotionalFlag.StockEqvNotionalReal || saveExt.NotionalFlag == TradingNotionalFlag.StockEqvNotional; req.TradeSinglePrice = TradeHelper.GetTradeSinglePriceByTradePrice(req.TradePrice, req.Notional, req.OriginalPrincipalSum, req.BuySell, req.TradeType, true); req.PremiumRate = TradeHelper.GetPremiumRateByTradePrice(req.TradePrice, req.StockEqvNotional, participationRate, req.OriginalPrincipalSum, req.AnnualizeFactor, req.BuySell, req.TradeType, true); break; case TradingPremiumFlag.TradeSinglePrice: req.TradePrice = TradeHelper.GetTradePriceBySinglePrice(req.TradeSinglePrice, req.Notional, req.OriginalPrincipalSum, req.BuySell, req.TradeType, true); req.PremiumRate = TradeHelper.GetPremiumRateByTradeSinglePrice(req.TradeSinglePrice, spotPrice); break; case TradingPremiumFlag.PremiumRate: req.IsUsePremiumRate = true; req.TradeSinglePrice = TradeHelper.GetTradeSinglePriceByPremiumRate(req.PremiumRate, spotPrice); req.TradePrice = TradeHelper.GetTradePriceByPremiumRate(req.PremiumRate, req.StockEqvNotional, participationRate, req.OriginalPrincipalSum, req.AnnualizeFactor, req.BuySell, req.TradeType, true); break; case TradingPremiumFlag.AllZero: break; default: throw new SystemException($"[{nameof(TradeSavePreHandler)}.{nameof(PrepareOtcTradeCalc)}][PremiumFlag]系统程序错误"); } } } else { if (req.TradeType == "累计期权") { var customObservDates = QdpHelper.ParseObservationDate(req.trade_accumulator_option.KOObservationDates); customObservDates ??= Qdp.Pricing.Base.Implementations.CalendarImpl.Get("chn") .BizDaysBetweenDatesExcluStartDay(req.TradeDate.Value, req.ExerciseDate.Value).ToArray(); req.TradeAmount = req.trade_accumulator_option.AccumuTradeAmount * customObservDates.Length; req.Notional = req.TradeAmount * CountRatio; req.StockEqvNotionalReal = req.Notional * spotPrice; req.StockEqvNotional = TradeHelper.GetStockEqvNotional(req.StockEqvNotionalReal, participationRate, annualizeFactor); if (req.IsTradePricePayType) { req.TradeSinglePrice = TradeHelper.GetTradeSinglePriceByTradePrice(req.TradePrice, req.Notional, req.OriginalPrincipalSum, req.BuySell, req.TradeType, true); req.PremiumRate = TradeHelper.GetPremiumRateByTradePrice(req.TradePrice, req.StockEqvNotional, participationRate, req.OriginalPrincipalSum, req.AnnualizeFactor, req.BuySell, req.TradeType, true); } else { req.TradePrice = TradeHelper.GetTradePriceBySinglePrice(req.TradeSinglePrice, req.Notional, req.OriginalPrincipalSum, req.BuySell, req.TradeType, true); req.PremiumRate = TradeHelper.GetPremiumRateByTradeSinglePrice(req.TradeSinglePrice, spotPrice); } } else { if (req.IsUsePremiumRate == true) { if (req.IsTradePricePayType == true) { req.PremiumRate = TradeHelper.GetPremiumRateByTradePrice(req.TradePrice, req.StockEqvNotional, participationRate, req.OriginalPrincipalSum, req.AnnualizeFactor, req.BuySell, req.TradeType, true); } else { req.TradePrice = TradeHelper.GetTradePriceByPremiumRate(req.PremiumRate, req.StockEqvNotional, participationRate, req.OriginalPrincipalSum, req.AnnualizeFactor, req.BuySell, req.TradeType, true); } req.StockEqvNotionalReal = TradeHelper.GetStockEqvNotionalReal(req.StockEqvNotional, participationRate, annualizeFactor); if (isAutoCall) { req.Notional = Math.Abs(spotPrice) > 0 ? req.StockEqvNotional * participationRate / spotPrice : 0; } else { req.Notional = Math.Abs(spotPrice) > 0 ? req.StockEqvNotionalReal / spotPrice : 0; } req.TradeSinglePrice = TradeHelper.GetTradeSinglePriceByPremiumRate(req.PremiumRate, spotPrice); } else { if (isAutoCall) { req.StockEqvNotional = Math.Abs(participationRate) > 0 ? req.Notional * spotPrice / participationRate : 0; req.StockEqvNotionalReal = TradeHelper.GetStockEqvNotionalReal(req.StockEqvNotional, participationRate, annualizeFactor); } else { req.StockEqvNotionalReal = req.Notional * spotPrice; req.StockEqvNotional = TradeHelper.GetStockEqvNotional(req.StockEqvNotionalReal, participationRate, annualizeFactor); } if (req.IsTradePricePayType == true) { req.TradeSinglePrice = TradeHelper.GetTradeSinglePriceByTradePrice(req.TradePrice, req.Notional, req.OriginalPrincipalSum, req.BuySell, req.TradeType, true); req.PremiumRate = TradeHelper.GetPremiumRateByTradePrice(req.TradePrice, req.StockEqvNotional, participationRate, req.OriginalPrincipalSum, req.AnnualizeFactor, req.BuySell, req.TradeType, true); } else { req.TradePrice = TradeHelper.GetTradePriceBySinglePrice(req.TradeSinglePrice, req.Notional, req.OriginalPrincipalSum, req.BuySell, req.TradeType, true); req.PremiumRate = TradeHelper.GetPremiumRateByTradeSinglePrice(req.TradeSinglePrice, spotPrice); } } req.TradeAmount = CountRatio == 0 ? 0 : req.Notional / CountRatio; } } } req.TradePrice = Commons.OtcFormatHelper.GetTradePriceDouble(req.TradePrice ?? 0); //最大名义本金 if (saveExt != null && saveExt.MaxSpotPrice > 0) { req.StockEqvNotionalMax = TradeHelper.GetStockEqvNotional(saveExt.MaxSpotPrice.Value * req.Notional, participationRate, annualizeFactor); } else //if (!req.StockEqvNotionalMax.HasValue || req.StockEqvNotionalMax.Value < 1e-6) { req.StockEqvNotionalMax = req.StockEqvNotional; } req.OriginalNotional = req.Notional; req.OriginalStockEqvNotional = req.StockEqvNotional; } /// /// 检查交易权限 /// class TradeRightChecker { IEnumerable _allVarietyIds; readonly Dictionary> _checkDic; public TradeRightChecker() { _checkDic = new Dictionary>(); } /// /// 检查是否交易员 /// public void Check(AssetUnit assetUnit, int traderId, int? clientId, int varietyId, int varietyId2 = 0, string tradeType = null) { if (traderId < 1) { throw new ServiceException("请选择交易员"); } //交易用户组必须和簿记账户一致 if (ConsUserGroup.HasGroup) { var userGroup = UserBLL.GetUserGroup(traderId) ?? string.Empty; if (userGroup != (assetUnit.UserGroup ?? string.Empty)) { throw new ServiceException($"交易员所在用户组({userGroup})必须和簿记账户所在用户组({assetUnit.UserGroup})一致"); } } if (!_checkDic.TryGetValue("trader:" + traderId, out var userVarietyIds)) { if (!UserBLL.IsTradeOfCurrentLogin(traderId)) { throw new ServiceException("非交易员,不允许录入交易"); } if (!("," + assetUnit.TraderIds + ",").Contains("," + traderId + ",")) { throw new ServiceException("交易员不在所选簿记中,请重新选择簿记"); } var varietyIds = TradeRightProvider.GetUserVarietyIds(traderId); if (varietyIds == null) { _checkDic["trader:" + traderId] = userVarietyIds = null; } else { _checkDic["trader:" + traderId] = userVarietyIds = varietyIds.ToHashSet(); } } if (varietyId < 1 && userVarietyIds == null) { return; } if (userVarietyIds != null && !userVarietyIds.Contains(varietyId) && tradeType != "现金流交易" && varietyId != 0) { throw new ServiceException("该交易员没有该品种的交易权限,不能添加该品种交易"); } if (varietyId2 != 0) { if (userVarietyIds != null && !userVarietyIds.Contains(varietyId2)) { throw new ServiceException("该交易员没有该品种的交易权限,不能添加该品种交易"); } } if (!clientId.HasValue || clientId.Value < 1) { return; } HashSet clientVarietyidSet = null; if (PS.Config.ClientElement.CanSelectCreditVariety && !_checkDic.TryGetValue("client:" + clientId.Value, out clientVarietyidSet)) { var clientCredits = DbContextFactory.GetYLDbContext() .credit.Where(c => c.ClientId == clientId.Value && c.ProcessStatus == "已审批" && (c.Type == CreditTable.RoleType || (c.Type == CreditTable.ClientType && c.IsVariety == true))) .Select(n => new { n.VarietyId, IsFeelingWhiteList = n.Type == CreditTable.RoleType || n.IsWhitelist == 1, }).ToArray(); if (clientCredits.Any()) { clientVarietyidSet = new HashSet(); foreach (var c in clientCredits) { var VarietyIds = DataConvert.ConvertCommaValuesToInt32Array(c.VarietyId); //白名单、黑名单 if (c.IsFeelingWhiteList) { foreach (var v in VarietyIds) { clientVarietyidSet.Add(v); } } else { _allVarietyIds ??= VarietyBLL.GetAllvarietyModel().Select(v => v.id).ToArray(); foreach (var v in _allVarietyIds) { clientVarietyidSet.Add(v); } foreach (var v in VarietyIds) { clientVarietyidSet.Remove(v); } } } } _checkDic["client:" + clientId.Value] = clientVarietyidSet; } if (clientVarietyidSet != null && !clientVarietyidSet.Contains(varietyId)) { throw new ServiceException("该客户没有该品种的交易权限,不能添加该品种交易"); } if (varietyId2 != 0) { if (clientVarietyidSet != null && !clientVarietyidSet.Contains(varietyId2)) { throw new ServiceException("该客户没有该品种的交易权限,不能添加该品种交易"); } } } } /// /// 检查行权方式 /// static void CheckExerciseMode(trade req) { if (req.TradeType == "远期" || req.TradeType == "现金流交易") { req.ExerciseMode = string.Empty; } else { switch (req.ExerciseMode) { case "欧式": req.ExerciseMode = "European"; break; case "美式": req.ExerciseMode = "American"; break; case "European": case "American": break; default: if (!string.IsNullOrWhiteSpace(req.ExerciseMode)) { throw new ServiceException("行权方式 填写错误:" + req.ExerciseMode); } if (req.TradeType != "自定义交易") { throw new ServiceException("行权方式 必须填写"); } break; } if (req.ExerciseMode == "American") { switch (req.TradeType) { case "双鲨期权": case "凤凰期权": case "雪球期权": case "合成价差期权": case "区间累积期权": case "Risky期权": throw new ServiceException($"行权方式 填写错误:{req.TradeType}不支持美式行权"); case "亚式期权": if (req.trade_asian_option?.StrikeType == "Floating") { throw new ServiceException("行权方式 填写错误:亚式期权美式行权时不支持浮动行权价方式"); } else if (req.trade_asian_option?.StrikeType == "Segmented") { throw new ServiceException("行权方式 填写错误:亚式期权美式行权时不支持分段式行权价方式"); } break; case "二元期权": if (req.trade_binary_option?.RebateType == "AtHit" && req.trade_binary_option?.PayoffType == "DoubleNoTouch") { throw new ServiceException("行权方式 填写错误:二元美式行权并且二元类型为'DoubleNoTouch'时不支持'立即'补偿支付"); } break; } } else { switch (req.TradeType) { case "亚式期权": if (req.trade_asian_option?.StrikeType == "Segmented" && req.trade_asian_option.PayoffType != "EnhancedArithmeticAverage") { throw new ServiceException("行权价类型为'分段式'时,均价计算类型只支持'增强算术平均'"); } if (req.trade_asian_option?.StrikeType == "Floating" && req.trade_asian_option.PayoffType == "EnhancedArithmeticAverage") { throw new ServiceException("均价计算类型为'增强算术平均'时,行权价类型不能为'浮动行权价'"); } break; } } } } /// /// 检查看涨看跌和远期多空方向 /// static void CheckOptionType(OtcTradeBase req) { switch (req.OptionType) { case "看涨": case "看跌": break; case "Call": case "多头": req.OptionType = "看涨"; break; case "Put": case "空头": req.OptionType = "看跌"; break; default: if (!string.IsNullOrWhiteSpace(req.OptionType)) { throw new ServiceException((req.TradeType == "远期" ? "多空方向" : "看涨看跌") + " 填写错误:" + req.OptionType); } if (req.TradeType != "自定义交易" && req.TradeType != "现金流交易" && (req.TradeType != "二元期权" || req.ExerciseMode != ConsGlobal.ExerciseMode.American)) { throw new ServiceException((req.TradeType == "远期" ? "多空方向" : "看涨看跌") + " 必须填写"); } break; } if (req.TradeType == "Risky期权" && req.OptionType != "看涨") { throw new ServiceException($"看涨看跌 填写错误:{req.TradeType}不支持看跌"); } } } }