using Org.BouncyCastle.Ocsp; using Qdp.Pricing.Base.Implementations; using YLErp.Abstract; using YLErp.Abstract.DataProviders; using YLErp.BLL; using YLErp.Configuration; using YLErp.DBModels.Consts; using YLErp.DBModels.Enums; using YLErp.Enums; using YLErp.Model; using YLErp.Modules.ApiModule; using YLErp.Modules.DataProviderModule; using YLErp.Modules.VolatilityModule; using YLErp.QdpModule; namespace YLErp.Modules.CalculationModule { //这个类的主要目标是代替CalculatorHelper.CalculateRisksForTrades //并且不再支持日终结算的计算,不再支持场内期权 /// /// 简单场外衍生品交易风险计算 /// class SimpleOtcTradeRiskCalc { /// /// 波动率类型,默认:交易 /// public string VolType { get; set; } = "交易"; public SettlementTypeEnum SettlementType { get; set; } = SettlementTypeEnum.ClosePrice; /// /// 覆盖交易波动率 /// public Dictionary OverrideVolsForTrade { get; set; } /// /// 是否使用交易波动率,默认false /// public bool IsUseTradeVol { get; set; } /// /// 精确时间模式,默认true /// public bool PreciseTimeMode { get; set; } = true; /// /// 是否增加波动率为百分比格式 /// public bool IsAddVolPercent { get; set; } = true; /// /// 增加波动率 /// public Dictionary AddVolRateDic { get; set; } /// /// 计算指标枚举,默认只计算PV /// public PricingRequest PricingRequest { get; set; } = PricingRequest.Pv; /// /// 是否在进行预付金计算 /// public bool IsMarginCalc { get; set; } = false; /// /// 计算场景 /// public CalcScenarioEnum CalcScenario { get; set; } /// /// 是否使用手动维护的风险值 /// public bool CanUseManual { get; set; } = false; DateTime _valueDate; IPriceProvider _priceProvider; List _errorList; readonly UnderlyingDataProvider _umProvider; public SimpleOtcTradeRiskCalc() { _umProvider = new UnderlyingDataProvider(); } bool IsEodSettle => CalcScenario == CalcScenarioEnum.EodSettlement; bool IsInitialMargin => CalcScenario == CalcScenarioEnum.InitialMargin; private void AppendError(trade td, string errorMsg, Exception ex = null) { if (ex != null) { errorMsg = string.IsNullOrEmpty(errorMsg) ? ex.Messages() : errorMsg + "," + ex.Messages(); } errorMsg = $"[{td.TradeType},交易编号:{td.TradeNumber}]计算出错:{errorMsg}"; if (IsEodSettle) { throw new Exception(errorMsg, ex); } if (_errorList == null) { _errorList = new List(); } _errorList.Add(errorMsg); } public TradeRiskResult CalculateRisksForTrades(DateTime valueDate, IEnumerable tradeList, IPriceProvider priceProvider, CalcScenarioEnum calcScenario) { var results = new List(); var trResult = new TradeRiskResult { Results = results }; if (tradeList == null || !tradeList.Any()) { return trResult; } if (IsEodSettle) { PreciseTimeMode = false; } _valueDate = valueDate; _priceProvider = priceProvider ?? throw new ArgumentNullException(nameof(priceProvider)); var sysRiskFreeRate = (VolType == "光证" ? valuedateBLL.RiskFreeRateExtend : valuedateBLL.RiskFreeRate) / 100; var recordlist = DbContextFactory.GetYLDbContext().dividendrate_record.Where(x => valueDate >= x.ValueDate).ToList(); using (var marketProxy = new MarketProxy(_valueDate, sysRiskFreeRate)) { using var db = DbContextFactory.GetYLDbContext(); var tradeIds = tradeList.Select(x => x.id).ToArray(); var manualDicByTypes = db.eod_trade_risk_manual.Where(x => x.ValueDate == _valueDate && tradeIds.Contains(x.TradeId) && x.VolType == VolType && x.SettlementType == SettlementType).ToDictionary(x => x.TradeId); var manualDic = db.eod_trade_risk_manual.Where(x => x.ValueDate == _valueDate && tradeIds.Contains(x.TradeId) && string.IsNullOrEmpty(x.VolType)).ToDictionary(x => x.TradeId); foreach (var td in tradeList) { if (VolType == "光证") { td.NoRiskRate = sysRiskFreeRate; } //国元固收默认从全局配置里取分红率,如果TradeHisData里有分红率,则根据生效时间取最优 if (recordlist != null && recordlist.Any()) { var _record = recordlist.Where(x => x.UnderlyingCode.Split(',').Any(code => code == td.UnderlyingCode) && x.TradeType.Contains(td.TradeType) && (x.OptionType == td.OptionType || x.OptionType == "全部")).OrderByDescending(x => x.OptDate).OrderByDescending(x => x.ValueDate); if (_record.Any()) { td.DividendRate = _record.FirstOrDefault()?.DividendRate; } } td.DividendRate = td.DividendRate ?? td.NoRiskRate ?? sysRiskFreeRate; TradeRiskResultRecord record; if (IsMarginCalc && td.TradeType == "自定义交易") { record = new TradeRiskResultRecord { Trade = td, Underlyings = new[] { DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode) }, ValueResult = new TradeValueResult() //自定义交易的预付金不需要在这里处理 }; if (PS.Config.ErpElement.ExternalAPIForCustomCalcEnable) { td.StartDate = _valueDate; var tradeVol = IsUseTradeVol; IsUseTradeVol = false; var price = _priceProvider.GetPrice(td.UnderlyingCode); var um = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode); var consVol = GetConstVol(td, price, IsEodSettle, um?.UnderlyingTypeId ?? 0); //从风险对冲那边抄来的逻辑,波动率精度会导致计算结果和风险对冲有差异; var volValue = Commons.OtcFormatHelper.FormatValue(consVol, PS.Config.ErpElement.VolMoreAccurate ? 6 : 4); var apiValue = TradeCalcApiHelper.CalculateCustomizedTrade(_valueDate, td, price, volValue, CalcScenario == CalcScenarioEnum.EodSettlement, calcScenario, TradeCalcApiHelper.PV, TradeCalcApiHelper.DELTA, TradeCalcApiHelper.GAMMA, TradeCalcApiHelper.VEGA, TradeCalcApiHelper.THETA, TradeCalcApiHelper.RHO); if (apiValue.Success) { record.ValueResult = apiValue.Content; } //异常 else { throw new Exception(apiValue.Msg); } } } else if (td.TradeType != "场内期权" && ConsTrade.TradeTypesForHedge.Contains(td.TradeType)) { record = new TradeRiskResultRecord { Trade = td, Underlyings = new[] { DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode) }, ValueResult = new TradeValueResult() { Pv = td.Notional * _priceProvider.GetPrice(td.UnderlyingCode), Delta = td.Notional } }; } else { //分类维护的风险数据优先级高于普通的风险数据 manualDicByTypes.TryGetValue(td.id, out var manual); if (manual == null) { manualDic.TryGetValue(td.id, out manual); } else { //如果分类维护的数据不全,通过单一数据补充 manualDic.TryGetValue(td.id, out var singleManual); if (singleManual != null) { manual.Pv = manual.Pv ?? singleManual.Pv; manual.Delta = manual.Delta ?? singleManual.Delta; manual.DeltaCash = manual.DeltaCash ?? singleManual.DeltaCash; manual.Gamma = manual.Gamma ?? singleManual.Gamma; manual.GammaCash = manual.GammaCash ?? singleManual.GammaCash; manual.Theta = manual.Theta ?? singleManual.Theta; manual.Vega = manual.Vega ?? singleManual.Vega; manual.VegaCash = manual.VegaCash ?? singleManual.VegaCash; manual.Rho = manual.Rho ?? singleManual.Rho; } } record = CalcOtcTrade(marketProxy, td, CanUseManual ? manual : null); } if (record != null) { if (PS.Config.ErpElement.IsPVIncludePrincipal) { record.ValueResult.Pv = record.ValueResult.Pv + td.PrincipalSum() * (td.BuySell == "卖出" ? -1 : 1); record.ValueResult.RoundedPv = record.ValueResult.RoundedPv + td.PrincipalSum() * (td.BuySell == "卖出" ? -1 : 1); } results.Add(record); } } } return trResult; } //计算衍生品交易价值 private TradeRiskResultRecord CalcOtcTrade(MarketProxy mp, trade td, eod_trade_risk_manual manual) { var record = new TradeRiskResultRecord { Trade = td, ValueResult = null, Underlyings = null }; try { var um = _umProvider.GetUnderlying(td.UnderlyingCode); record.Underlyings = new[] { um }; if (um == null && td.HasUnderlying()) { AppendError(td, "未找到标的信息:" + td.UnderlyingCode); record.ValueResult = GetTradeValueResult(td, "未找到标的信息"); return record; } if (ConsTrade.TradeCompleteStatus.Contains(td.TradeStatus)) { record.ValueResult = GetTradeValueResult(td); return record; } TradeValueResult result = null; if (manual != null) { var spotPrice = GetSpotPrice(td); manual.DeltaCash = manual.Delta * spotPrice; manual.GammaCash = manual.Gamma * Math.Pow(spotPrice, 2) / 100; manual.VegaCash = manual.Vega * spotPrice; result = new TradeValueResult { Pv = manual.Pv ?? 0, RoundedPv = manual.Pv ?? 0, Delta = manual.Delta ?? 0, Gamma = manual.Gamma ?? 0, Vega = manual.Vega ?? 0, TradingDayTheta = manual.Theta ?? 0, CalendarDayTheta = manual.Theta ?? 0, Rho = manual.Rho ?? 0, DeltaCash = manual.DeltaCash ?? 0, GammaCash = manual.GammaCash ?? 0 }; } //没有自定义风险维护值 或者 自定风险维护没有涵盖PV和所有希腊值(部分维护场景) //部分维护场景下,需要系统计算出未赋值的属性,进行合并返还 if (manual == null || manual != null && !manual.IsPVAndAllGreek) { switch (td.TradeType) { case "彩虹期权": case "价差期权": case "结构化交易": AppendError(td, "不支持此交易类型的计算"); result = GetTradeValueResult(td, "不支持此交易类型的计算"); break; case ConsGlobal.TradeType.Custom: result = CalcCustom(td, IsEodSettle, um?.UnderlyingTypeId ?? 0); break; case ConsGlobal.TradeType.PayoffSwap: result = PayoffSwapCalcService.CalcValue(td, _valueDate, IsMarginCalc ? null : _priceProvider, IsEodSettle); break; case ConsGlobal.TradeType.Forward: { var spotPrice = GetSpotPrice(td); result = ForwardradeCalcService.CalcValue(td, spotPrice); } break; case "信用债": case "商品期货": _priceProvider.TryGetPrice(td.UnderlyingCode, out var spotPriceGet); result = new TradeValueResult() { Pv = td.Notional * spotPriceGet, }; break; case "场内期权": default: result = CalcOption(mp, td, IsEodSettle, IsInitialMargin, um?.UnderlyingTypeId ?? 0); //计算场外期权 break; } //系统计算结果和手动维护值合并 TradeRiskCalcUtil.GetOptionValueWithManual(manual, result); } else { if (td.TradeType != ConsGlobal.TradeType.CashFlow && result.Vol == 0) { result.Vol = GetConstVol(td, GetSpotPrice(td), IsEodSettle, um?.UnderlyingTypeId ?? 0); } } record.ValueResult = result; } catch (Exception ex) { AppendError(td, null, ex); record.ValueResult = GetTradeValueResult(td, ex.Message); } return record; } private TradeValueResult GetTradeValueResult(trade td, string errmsg = null) { return new TradeValueResult { TradeId = td.id, UnderlyingCode = td.UnderlyingCode, Strike = td.Strike, UnderlyingId = td.UnderlyingId, ErrorMessage = errmsg, Succeeded = string.IsNullOrEmpty(errmsg) }; } //获取标的现价 private double GetSpotPrice(trade td) { if (string.IsNullOrWhiteSpace(td.UnderlyingCode)) { return 0; } //20210706:支持参考价处理 -- 远期没有参考价 if (td.SettlementType == SettlementTypeEnum.ReferencePrice && _priceProvider.TryGetPrice(ConsGlobal.RefPricePrefix + td.UnderlyingCode, out var price)) { return price; } double BasiseodPrice = 0; if (PS.Config.ErpElement.ForwardTradePriceModel == Configuration.Enums.ForwardTradePriceModel.STANDARD && !string.IsNullOrWhiteSpace(td.BasisUnderlyingCode) && td.TradeType == ConsGlobal.TradeType.Forward) { _priceProvider.TryGetPrice(td.BasisUnderlyingCode, out BasiseodPrice); } if (_priceProvider.TryGetPrice(td.UnderlyingCode, out price)) { return price -= BasiseodPrice; } else { throw new Exception("未找到标的价格:" + td.UnderlyingCode); } } //计算自定义交易 private TradeValueResult CalcCustom(trade td, bool isEodCalc, int underlyingTypeId) { var spotPrice = GetSpotPrice(td); var volValue = GetConstVol(td, spotPrice, isEodCalc, underlyingTypeId); //从风险对冲那边抄来的逻辑,波动率精度会导致计算结果和风险对冲有差异; volValue = Commons.OtcFormatHelper.FormatValue(volValue, PS.Config.ErpElement.VolMoreAccurate ? 6 : 4); var (manual, optionValue) = TradeRiskCalcUtil.GetManualOptionValue(_valueDate, td, spotPrice, volValue, td.TradeType == "自定义交易", CalcScenario, VolType, SettlementType, isSettle: CalcScenario == CalcScenarioEnum.EodSettlement); //收盘时如果自定义交易还活着且没有维护当日风险,并且收的时系统日期当日的盘,抛出exception if (IsEodSettle && !ConsTrade.TradeCompleteStatus.Contains(td.TradeStatus) && _valueDate == valuedateBLL.ValueDate && !PS.Config.IsMustRiskManual) { if ((manual == null || manual.ValueDate != _valueDate) && !PS.Config.Is润和) { var error = $"[{td.TradeType}] 交易'{td.TradeNumber}'在{_valueDate:yyyy-MM-dd}需先进行交易风险维护"; throw new Exception(error); } } return optionValue; } //计算场外期权 private TradeValueResult CalcOption(MarketProxy mp, trade td, bool isEodCalc, bool isInitialMargin, int underlyingTypeId) { var spotPrice = GetSpotPrice(td); var req = new OptionValueCalcRequest(mp.RiskFreeRate) { correlations = null, //因为没有处理多标的,所以这里为null engineName = null, maturityShift = 0, preciseTimeMode = PreciseTimeMode, ParamOverride = null, pricingRequest = PricingRequest, spotPrices = new[] { spotPrice }, vols = new[] { 0d }, calcScenario = CalcScenario }; req.timeToMaturityDays = double.NaN; //象屿最后一个交易日实时计算时TTM需要和平仓时算法一致 if (PS.Config.Is厦门象屿 && (CalcScenario == CalcScenarioEnum.RealtimePosition || CalcScenario == CalcScenarioEnum.RealtimeRisk) && td.SettlementType == SettlementTypeEnum.ReferencePrice) { req.timeToMaturityDays = TradeCalcHelper.CalculateTTMDaysForXiangYu(valuedateBLL.ValueDate, td.ExerciseDate.Value, underlyingTypeId, false); //20210706:支持厦门象屿参考价相关交易(这类交易不需要支持精确模式) req.preciseTimeMode = false; } if (PS.Config.Is润和 && IsInitialMargin) { req.timeToMaturityDays = td.TTMDays; } if (td.TradeType != ConsGlobal.TradeType.CashFlow) { var consVol = GetConstVol(td, spotPrice, isEodCalc, underlyingTypeId); //从风险对冲那边抄来的逻辑,波动率精度会导致计算结果和风险对冲有差异; consVol = Commons.OtcFormatHelper.FormatValue(consVol, PS.Config.ErpElement.VolMoreAccurate ? 6 : 4); req.vols = new[] { consVol }; if (IsMarginCalc && CalcScenario == CalcScenarioEnum.EodSettlement) { LogFactory.GetLogger("日终预付金计算").Info($"{td.TradeType}'{td.TradeNumber}',波动率:{consVol},spotPrice:{spotPrice}"); } } req.isEodCalc = IsEodSettle; return OptionCalculatorV2.GetOptionValueResult(mp, td, req, out _); } #region----获取波动率---- /// /// /// /// /// /// /// 只适用于光证波动率 /// private double GetConstVol(trade td, double spotPrice, bool isEodCalc, int underlyingTypeId) { var vol = GetConstVolRaw(td, spotPrice, isEodCalc, underlyingTypeId); if (AddVolRateDic != null && AddVolRateDic.TryGetValue(td.id, out var addVolRate)) { vol += IsAddVolPercent ? addVolRate * vol : addVolRate; } return vol; } private double GetConstVolRaw(trade td, double spotPrice, bool isEodCalc, int underlyingTypeId) { if (td.TradeType == "场内期权") { var vol = new ExOptionSavedVolProvider(_valueDate).GetSavedVol(td.ExchangeOptionCode, _valueDate); if (vol != null) { return vol.Value; } } else { if (OverrideVolsForTrade != null && OverrideVolsForTrade.TryGetValue(td.id, out var overrideVol)) { return overrideVol; } if (IsUseTradeVol) { if (VolType == "交易" || VolType == "持仓") { return VolatilityHelper.GetTradeVol(td, _valueDate, IsEodSettle); } if (VolType == "对冲") { return TradeHedgeVolService.GetTradeHedgeVol(td, _valueDate); } } } if (VolType == "光证") { var va = DataCacheProvider.GetVarietyDataSource().GetData(underlyingTypeId) ?? throw new Exception("未找到光证波动率:" + td.UnderlyingCode); return VarietyVolService.GetVarietyVol(_valueDate, va.id) ?? throw new Exception("未找到光证波动率:" + td.UnderlyingCode); } if (VolType == "开仓") { return td.TradeOpenVolatility ?? 0; } var userGroup = string.Empty; if (ConsUserGroup.HasGroup) { userGroup = DataCacheProvider.GetAssetUnitDataSource().GetData(td.AssetId)?.UserGroup; if (string.IsNullOrEmpty(userGroup)) { throw new Exception($"取波动率时未能获取UserGroup"); } } if (VolType == "BidAskVol") { //结算时应以平仓的交易方向来选择曲面 //如果交易是买入,平仓时是卖出,则应该用Ask曲面 var tempVolType = td.BuySell == "买入" ? "报价Ask" : "报价Bid"; var bidAskVol = VolatilityHelper.GetVol(_valueDate, tempVolType, td.UnderlyingCode, userGroup) ?? throw new Exception($"未找到'{tempVolType}'波动率:{td.UnderlyingCode}"); return GetInterpolatedVol(td, bidAskVol, spotPrice, isEodCalc); } //当需要去波动率曲面中查询时,所有公司的VolType都应该是交易; //上面这句描述应该是错误,当曲面波动率模式时,应该用配置的结算波动率,todo string tempVolType2 = VolType; if (VolType == "对冲") { tempVolType2 = "交易"; } else if (VolType == "持仓" || string.IsNullOrEmpty(tempVolType2)) { tempVolType2 = IsUseTradeVol ? "交易" : valuedateBLL.SystemDate.EodSettleVolMode.TrimToNull() ?? "交易"; } var underlyingVols = VolatilityHelper.GetVol(_valueDate, tempVolType2, td.UnderlyingCode, userGroup) ?? throw new Exception($"未找到'{tempVolType2}'波动率:{td.UnderlyingCode}"); return GetInterpolatedVol(td, underlyingVols, spotPrice, isEodCalc); } private double GetInterpolatedVol(trade td, IVolatility vols, double spotPrice, bool isEodCalc) { return VolatilityHelper.GetInterpolatedVol( volConstructionType: PS.Config.ErpElement.SkewMapVolConstruction ? VolConstructionType.SkewMap : VolConstructionType.Normal, volSurface: vols, valueDate: _valueDate, underlyingCode: td.UnderlyingCode, exerciseDate: td.ExerciseDate.Value, strike: td.Strike ?? 0, isBuy: td.BuySell == "买入", isCall: td.CallPut == "Call", spotPrice: spotPrice, isMoneynessOption: td.IsMoneynessOption == "是", isEodCalc: isEodCalc); } #endregion } }