using NPOI.Util;
using Qdp.Pricing.Base.Implementations;
using YLErp.Abstract;
using YLErp.Abstract.DataProviders;
using YLErp.BLL;
using YLErp.BLL.Calculation;
using YLErp.DBModels.Enums;
using YLErp.Enums;
using YLErp.Modules.CalculationModule.Abstract;
using YLErp.Modules.VolatilityModule;
using YLErp.QdpModule;
namespace YLErp.Modules.CalculationModule
{
///
/// 期权价值计算
///
public class TradeRiskValueCalc
{
readonly OtcTradeBase _trade;
readonly IOtcTradeValueCalcContext _context;
readonly IPriceProvider _underlyingPriceProvider;
readonly int _maturityShift;
underlying_manager _underlying;
underlying_manager[] _spreadUnderlyings;
readonly IOptionCalcDataProvider _dataProvider;
readonly IUnderlyingDataProvider _underlyingDataProvider;
readonly ITradeExtendDataProvider _tradeExtendDataProvider;
readonly ITradeKnockOutPayoffCalcService _tradeKnockOutPayoffCalcService;
public TradeRiskValueCalc(IOtcTradeValueCalcContext context, OtcTradeBase trade)
{
_trade = trade ?? throw new ArgumentNullException(nameof(trade));
_context = context ?? throw new ArgumentNullException(nameof(context));
_dataProvider = CalcCheckHelper.CheckOptionCalcDataProvider(context.DataProvider);
_underlyingDataProvider = _dataProvider.UnderlyingDataProvider;
_tradeExtendDataProvider = _dataProvider.TradeExtendDataProvider;
_underlyingPriceProvider = _dataProvider.UnderlyingPriceProvider;
_maturityShift = 0;
_tradeKnockOutPayoffCalcService = new TradeKnockOutPayoffCalcService(_tradeExtendDataProvider, _context.ValueDate);
}
///
/// 计算衍生品价值
///
public TradeValueResult GetTradeValue(out underlying_manager[] underlyingArr)
{
TradeValueResult result;
if (_trade.TradeType == "现金流交易")
{
result = InnerGetCashFlowTradeValue();
if (_context.CalcDeltaT1)
{
result.DeltaT1 = result.Delta;
}
}
else
{
result = InnerGetTradeValue(null);
}
underlyingArr = _spreadUnderlyings ?? Array.Empty();
Array.Resize(ref underlyingArr, underlyingArr.Length + 1);
underlyingArr[0] = _underlying;
return result;
}
///
/// 计算衍生品价值
///
private TradeValueResult InnerGetTradeValue(underlying_manager unly)
{
//underlying
_underlying = unly;
if (_underlying == null)
{
_underlying = _underlyingDataProvider.GetUnderlying(_trade.UnderlyingCode);
if (_underlying == null)
{
throw new TradeCalcExpception(_trade.id, $"未获取到标的数据:{_trade.UnderlyingCode}");
}
_underlying.QuotationDate = _context.ValueDate;
}
//结构化交易
if (_trade.TradeType == "结构化交易")
{
throw new ServiceException($"不支持'结构化交易'主交易的计算,tradeId:{_trade.id},tradeNumber:{_trade.TradeNumber}");
}
//准备计算
_spreadUnderlyings = null;
//stock MaturityDate
if (_underlying.UnderlyingInstrumentType == "Stock")
{
//股票默认到期日为行权日
if (_trade.ExerciseDate != null)
{
_trade.MaturityDate = _trade.ExerciseDate;
}
}
//标的即期价格
var blGetPrice = _underlyingPriceProvider.TryGetPrice(_trade.UnderlyingCode, out var spotPrice);
_underlying.Price = spotPrice;
//如果当天有维护过风险值,则直接获取风险值即可,未维护的风险值则通过系统计算
var result = TradeRiskCalcUtil.GetManualOptionValue(_context.ValueDate, (trade)_trade, spotPrice, 0, false, _context.CalcScenario, volType: _context.VolType, isSettle: _context.CalcScenario == Enums.CalcScenarioEnum.EodSettlement);
var optionValue = result.manual != null ? result.optionValue : null;
//没有自定义风险维护值 或者 自定风险维护没有涵盖PV和所有希腊值(部分维护场景)
//部分维护场景下,需要系统计算出未赋值的属性,进行合并返还
if (result.manual == null || result.manual != null && !result.manual.IsPVAndAllGreek)
{
//开始计算
switch (_trade.TradeType)
{
case "远期":
if (PS.Config.ErpElement.ForwardTradePriceModel == Configuration.Enums.ForwardTradePriceModel.STANDARD && !string.IsNullOrWhiteSpace(_trade.BasisUnderlyingCode))
{
_underlyingPriceProvider.TryGetPrice(_trade.BasisUnderlyingCode, out var BasiseodPrice);
spotPrice -= BasiseodPrice;
}
optionValue = ForwardradeCalcService.CalcValue(_trade, spotPrice);
break;
case "信用债":
case "商品期货":
case "股票":
{
var pv = spotPrice * _trade.Notional;
optionValue = new TradeValueResult { Pv = pv, Delta = _trade.Notional, DeltaCash = pv };
}
break;
case "自定义交易":
{
var positionVol = VolatilityHelper.GetTradeVol((trade)_trade, _context.ValueDate, _context.CalcScenario == CalcScenarioEnum.EodSettlement);
optionValue = TradeRiskCalcUtil.GetManualOptionValue(_context.ValueDate, (trade)_trade, spotPrice, positionVol, true, _context.CalcScenario, isSettle: _context.CalcScenario == CalcScenarioEnum.EodSettlement).optionValue;
}
break;
case "收益互换":
{
var trade_swap = _tradeExtendDataProvider.GetTrade_Swap(_trade.id);
optionValue = PayoffSwapCalcService.CalcValue(_trade, _context.ValueDate, _underlyingPriceProvider, false);
}
break;
}
//计算期权估值时包含了DeltaT1,所以不需要再次赋值
if (optionValue == null)
{
if (!blGetPrice)
{
if (_trade.TradeType == "场内期权")
{
throw new ServiceException($"场内期权计算失败,期权代码:{_trade.ExchangeOptionCode},错误信息:期权标的'{_trade.UnderlyingCode}'缺少价格");
}
else
{
throw new ServiceException($"{_trade.TradeType}计算失败,交易编号:{_trade.TradeNumber},标的:{_trade.UnderlyingCode},错误信息:缺少标的价格");
}
}
optionValue = InnerGetOptionValue(spotPrice);
}
else if (_context.CalcDeltaT1)
{
optionValue.DeltaT1 = optionValue.Delta;
}
}
//系统计算结果和手动维护值合并
TradeRiskCalcUtil.GetOptionValueWithManual(result.manual, optionValue);
if (optionValue != null)
{
optionValue.Strike = _trade.Strike ?? 0;
optionValue.SpotPrice = spotPrice;
optionValue.VegaCash = optionValue.VegaCash.IsNormalize() ? optionValue.VegaCash : (optionValue.Vega * optionValue.SpotPrice).Normalize();
optionValue.NPv = optionValue.Pv;
optionValue.NRoundedPv = optionValue.RoundedPv;
if (PS.Config.ErpElement.IsPVIncludePrincipal)
{
optionValue.Pv += _trade.PrincipalSum() * (_trade.BuySell == "卖出" ? -1 : 1);
optionValue.RoundedPv += _trade.PrincipalSum() * (_trade.BuySell == "卖出" ? -1 : 1);
}
}
return optionValue;
}
///
/// 获取场外期权价值计算结果
///
private TradeValueResult InnerGetOptionValue(double spotPrice)
{
var pricingRequest = _context.GetPricingRequest(_trade);
if (pricingRequest == PricingRequest.None)
{
return new TradeValueResult
{
TradeId = _trade.id,
UnderlyingCode = _trade.UnderlyingCode,
BuySell = _trade.BuySell,
UnderlyingId = _trade.UnderlyingId,
ErrorMessage = "PricingRequest.None"
};
}
var qdpTradeId = _context.MarketProxy.NextRequestId() + "_" +
(_trade.id > 0 ? _trade.id.ToString() : string.IsNullOrWhiteSpace(_trade.ExchangeOptionCode) ? _trade.UnderlyingCode : _trade.ExchangeOptionCode);
//准备波动率
if (!_context.PrepareVolatility(qdpTradeId, _trade, spotPrice, out var volsurfaceNames))
{
return new TradeValueResult(false)
{
UnderlyingCode = _underlying.UnderlyingCode,
ErrorMessage = "没有波动率",
FailReason = TradeValueFailReason.missingVol
};
}
TradeValueResult result = null;
var tpRequest = new OptionTradeParamRequest(_context.SysRiskFreeRate)
{
tradeId = qdpTradeId,
preciseTimeMode = _context.IsPreciseTimeMode,
fixings = null,
maturityShift = _maturityShift,
volSurfaceNames = volsurfaceNames,
hasNightMarket = false,
isEodCalc = _context.IsEodCalc,
timeToMaturityDays = double.NaN,
ParamOverride = p =>
{
p.riskFreeRate = _context.GetRiskFreeRate(_trade);
p.dividendRate = _context.GetDividendRate(_trade);
}
};
var nextDay = QdpCalendarHelper.BizDayShift(valuedateBLL.ValueDate, 1);
//象屿最后一个交易日实时计算时TTM需要和平仓时算法一致
if (PS.Config.Is厦门象屿
&& (_context.CalcScenario == CalcScenarioEnum.RealtimePosition
|| _context.CalcScenario == CalcScenarioEnum.RealtimeRisk)
&& _trade.SettlementType == SettlementTypeEnum.ReferencePrice)
{
tpRequest.timeToMaturityDays = TradeCalcHelper.CalculateTTMDays(valuedateBLL.ValueDate, _trade.ExerciseDate.Value, _underlying.UnderlyingTypeId, false);
}
var variety = new Variety();
if (_underlying.IsFutures())
{
variety = DataCacheProvider.GetVarietyDataSource().GetData(_underlying.UnderlyingTypeId);
tpRequest.hasNightMarket = variety != null && variety.HasNightMarket;
}
OptionCalcParam getOptionCalcParam(T tradeParam, double[] spotPrices = null) where T : OptionTradeParamBase
{
return new OptionCalcParam(tradeParam)
{
engineName = null,
pricingRequest = pricingRequest,
spotPrices = spotPrices ?? new[] { spotPrice },
calcScenario = _context.CalcScenario,
CalcDeltaT1 = _context.CalcDeltaT1
};
}
var marketProxy = _context.MarketProxy;
//东证润和是精确时间模式参与计算,收盘的话,刚好是整数天,所以不需要特殊处理
if (PS.Config.Is润和 && !tpRequest.isEodCalc && double.IsNaN(tpRequest.timeToMaturityDays))
{
tpRequest.timeToMaturityDays = TradeCalcHelper.CalculateTTMDays(marketProxy.ValueDate, _trade.ExerciseDate.Value, 0, false);
}
//开始计算
switch (_trade.TradeType)
{
case "香草期权":
{
var tradeParam = QdpTradeBuilder.GetVanillaOptionTradeParam(_trade, tpRequest, false);
result = TradeRiskCalcUtil.GetVanillaOptionValue(marketProxy, getOptionCalcParam(tradeParam));
}
break;
case "Risky期权":
{
var riskyOption = _tradeExtendDataProvider.GetTrade_Risky_Option(_trade.id);
result = GetOptionRisky(_trade, riskyOption, tpRequest, _underlying, marketProxy, pricingRequest, spotPrice, _context.CalcScenario, _context.CalcDeltaT1);
}
break;
case "彩虹期权":
{
var tradeParam = GetRainbowOptionTradeParam(tpRequest, out var spotPrices);
if (tradeParam == null)
{
return new TradeValueResult { UnderlyingCode = _underlying.UnderlyingCode };
}
result = TradeRiskCalcUtil.GetRainbowOptionValue(marketProxy, getOptionCalcParam(tradeParam, spotPrices));
}
break;
case "亚式期权":
{
if (!PS.Config.Is润和 && !tpRequest.isEodCalc && double.IsNaN(tpRequest.timeToMaturityDays))
{
tpRequest.timeToMaturityDays = TradeCalcHelper.CalculateTTMDays(marketProxy.ValueDate, _trade.ExerciseDate.Value, variety.id, false);
}
var tradeParam = GetAsianOptionCalcParam(tpRequest, spotPrice);
result = TradeRiskCalcUtil.GetAsianOptionValue(marketProxy, getOptionCalcParam(tradeParam));
}
break;
case "障碍期权":
{
var barrierOption = _tradeExtendDataProvider.GetTrade_Barrier_Option(_trade.id)
?? throw new TradeCalcExpception(_trade.id, "未获取到对应的障碍期权数据");
var tradeParam = QdpTradeBuilder.GetBarrierOptionTradeParam(_trade, barrierOption, tpRequest);
result = TradeRiskCalcUtil.GetBarrierOptionValue(marketProxy, getOptionCalcParam(tradeParam));
}
break;
case "二元期权":
{
var binaryOption = _tradeExtendDataProvider.GetTrade_Binary_Option(_trade.id)
?? throw new TradeCalcExpception(_trade.id, "未获取到对应的二元期权数据");
var tradeParam = QdpTradeBuilder.GetBinaryOptionTradeParam(_trade, binaryOption, tpRequest);
result = TradeRiskCalcUtil.GetBinaryOptionValue(marketProxy, getOptionCalcParam(tradeParam));
}
break;
case "价差期权":
{
var tradeParam = GetSpreadOptionCalcParam(tpRequest, out var spotPrices);
if (tradeParam == null)
{
return new TradeValueResult { UnderlyingCode = _underlying.UnderlyingCode };
}
result = TradeRiskCalcUtil.GetSpreadOptionValue(marketProxy, getOptionCalcParam(tradeParam, spotPrices));
}
break;
case "场内期权":
{
var tradeParam = QdpTradeBuilder.GetVanillaOptionTradeParam(_trade, tpRequest, true);
result = TradeRiskCalcUtil.GetVanillaOptionValue(marketProxy, getOptionCalcParam(tradeParam));
}
break;
case "合成价差期权":
{
var tradeParam = QdpTradeBuilder.GetSSpreadOptionTradeParam(_trade, tpRequest);
result = TradeRiskCalcUtil.GetSSpreadOptionValue(marketProxy, getOptionCalcParam(tradeParam));
}
break;
case "亚式合成价差期权":
{
var tradeParam = GetAsianOptionCalcParam(tpRequest, spotPrice);
result = TradeRiskCalcUtil.GetAsianSSpreadOptionValue(marketProxy, getOptionCalcParam(tradeParam));
}
break;
case "双鲨期权":
{
var dbsharkfinOption = _tradeExtendDataProvider.GetTrade_Double_SharkFin_Option(_trade.id)
?? throw new TradeCalcExpception(_trade.id, "未获取到对应的双鲨期权数据");
var tradeParam = QdpTradeBuilder.GetDoubleSharkFinOptionTradeParam(_trade, dbsharkfinOption, tpRequest);
result = TradeRiskCalcUtil.GetDoubleSharkFinOptionValue(marketProxy, getOptionCalcParam(tradeParam));
}
break;
case "凤凰期权":
{
var autocallOption = _tradeExtendDataProvider.GetTrade_Autocall_Option(_trade.id)
?? throw new TradeCalcExpception(_trade.id, "未获取到对应的凤凰期权数据");
autocallOption.HappenedObservations = _tradeExtendDataProvider.GetTrade_HappenedObservations(_trade.id);
var tradeParam = QdpTradeBuilder.GetAutocallOptionTradeParam(_trade, autocallOption, tpRequest);
try
{
result = TradeRiskCalcUtil.GetAutocallOptionValue(marketProxy, getOptionCalcParam(tradeParam));
}
catch (Exception ex)
{
if (string.IsNullOrEmpty(_trade.TradeNumber))
{
throw;
}
throw new Exception($"凤凰期权'{_trade.TradeNumber}'计算出错:{ex.Message}");
}
}
break;
case "雪球期权":
{
var snowball = _tradeExtendDataProvider.GetTrade_Snowball_Option(_trade.id)
?? throw new TradeCalcExpception(_trade.id, "未获取到对应的雪球期权数据");
if (snowball?.PrepaymentUsed ?? false)
{
var snowballSpecialistOptionCalculator = new SnowballSpecialistOptionCalculator();
var vol = marketProxy.QdpMarket.VolSurfaces[qdpTradeId].ValueOnGrids[0, 0];
var trade = (trade)_trade;
trade.trade_snowball = snowball;
if (snowball.PrepaymentRatio > 0)
{
var specialSnowballTrade = snowballSpecialistOptionCalculator.GetSpecialTrade(trade);
var specialSnowballResult = snowballSpecialistOptionCalculator.CalcOptionValue(marketProxy.ValueDate, spotPrice, vol, _context.CalcScenario, specialSnowballTrade);
var breakevenSnowballTrade = snowballSpecialistOptionCalculator.GetBreakevenTrade(trade);
var tradeParam = QdpTradeBuilder.GetSnowballTradeParam(breakevenSnowballTrade, breakevenSnowballTrade.trade_snowball, tpRequest);
//因为构建tradeParam的时候,会用全局变量_trade的NoRiskRate,所以这边需要覆盖一下
tradeParam.riskFreeRate = breakevenSnowballTrade.NoRiskRate ?? 0;
var breakevenSnowballResult = TradeRiskCalcUtil.GetSnowballOptionValue(marketProxy, getOptionCalcParam(tradeParam));
result = snowballSpecialistOptionCalculator.MergeTradeValueResult(specialSnowballResult, breakevenSnowballResult);
}
else
{
result = snowballSpecialistOptionCalculator.CalcOptionValue(marketProxy.ValueDate, spotPrice, vol, _context.CalcScenario, trade);
}
}
else
{
var tradeParam = QdpTradeBuilder.GetSnowballTradeParam(_trade, snowball, tpRequest);
result = TradeRiskCalcUtil.GetSnowballOptionValue(marketProxy, getOptionCalcParam(tradeParam));
}
}
break;
case "气囊结构":
{
var airbag = _tradeExtendDataProvider.GetTrade_Airbag(_trade.id)
?? throw new TradeCalcExpception(_trade.id, "未获取到对应的气囊结构期权数据");
var tradeParam = QdpTradeBuilder.GetAirbagOptionTradeParam(_trade, airbag, tpRequest);
result = TradeRiskCalcUtil.GetAirbagOptionValue(marketProxy, getOptionCalcParam(tradeParam));
}
break;
case "收益增强结构":
{
var underlyingEnhance = _tradeExtendDataProvider.GetTrade_UnderlyingEnhance(_trade.id)
?? throw new TradeCalcExpception(_trade.id, "未获取到对应的收益增强结构期权数据");
var tradeParam = QdpTradeBuilder.GetUnderlyingEnhanceTradeParam(_trade, underlyingEnhance, tpRequest);
result = TradeRiskCalcUtil.GetUnderlyingEnhanceValue(marketProxy, getOptionCalcParam(tradeParam));
}
break;
case "区间累积期权":
{
var rangeAccrual = _tradeExtendDataProvider.GetTrade_RangeAccrual(_trade.id)
?? throw new TradeCalcExpception(_trade.id, "未获取到对应的区间累积期权数据");
//有些时候会传入大于估值日期的票息(比如实时风险算昨日pv的时候)
rangeAccrual.HappenedObservations = _tradeExtendDataProvider.GetTrade_HappenedObservations(_trade.id)
?.Where(n => n.EndDate < _context.ValueDate)?.ToList();
var tradeParam = QdpTradeBuilder.GetRangeAccrualTradeParam(_trade, rangeAccrual, tpRequest);
result = TradeRiskCalcUtil.GetRangeAccrualValue(marketProxy, getOptionCalcParam(tradeParam));
}
break;
case "累计期权":
{
var accumulator = _tradeExtendDataProvider.GetTrade_Accumulator_Option(_trade.id)
?? throw new TradeCalcExpception(_trade.id, "未获取到对应的累计期权数据");
var tradeParam = QdpTradeBuilder.GetAccumulatorOptionTradeParam(_trade, accumulator, tpRequest);
result = TradeRiskCalcUtil.GetAccumulatorOptionValue(marketProxy, getOptionCalcParam(tradeParam), _trade.Notional);
}
break;
default:
result = new TradeValueResult(false) { ErrorMessage = "未知的期权结构" };
break;
}
if (result != null)
{
// 通过当前价格计算出来的交易是否敲出以及敲出payoff
var knockOutPayoffResult = _tradeKnockOutPayoffCalcService.GetKnockOutPayoff((trade)_trade, spotPrice);
if (knockOutPayoffResult != null)
{
result.IsKnockOut = knockOutPayoffResult.IsKnockOut;
result.KnockOutPayoff = knockOutPayoffResult.Payoff;
}
}
return result;
}
///
/// 获取现金流价值计算结果
///
private TradeValueResult InnerGetCashFlowTradeValue()
{
var qdpTradeId = _context.MarketProxy.NextRequestId() + "_" + (_trade.id > 0 ? _trade.id.ToString() : string.IsNullOrWhiteSpace(_trade.ExchangeOptionCode) ? _trade.UnderlyingCode : _trade.ExchangeOptionCode);
_context.MarketProxy.SetVolSurface(qdpTradeId, QdpModule.QdpVolHelper.GetDefaultVolatility(0.3));
var tpRequest = new OptionTradeParamRequest(_context.SysRiskFreeRate)
{
tradeId = qdpTradeId,
preciseTimeMode = _context.IsPreciseTimeMode,
fixings = null,
maturityShift = _maturityShift,
volSurfaceNames = new[] { qdpTradeId },
hasNightMarket = false,
timeToMaturityDays = double.NaN,
ParamOverride = p =>
{
p.riskFreeRate = _context.GetRiskFreeRate(_trade);
p.dividendRate = _context.GetDividendRate(_trade);
}
};
var rangeAccrual = _tradeExtendDataProvider.GetTrade_CashFlow(_trade.id) ?? throw new TradeCalcExpception(_trade.id, "未获取到对应的现金流交易数据");
var tradeParam = QdpTradeBuilder.GetCashFlowTradeParam(_trade, rangeAccrual, tpRequest);
var calcParam = new OptionCalcParam(tradeParam)
{
engineName = null,
pricingRequest = _context.GetPricingRequest(_trade),
calcScenario = _context.CalcScenario
};
return TradeRiskCalcUtil.GetCashFlowValue(_context.MarketProxy, calcParam, _trade.StockEqvNotional);
}
private static TradeValueResult GetOptionRisky(
OtcTradeBase td,
trade_risky_option risky_Option,
OptionTradeParamRequest request,
underlying_manager underlyings,
MarketProxy marketProxy,
PricingRequest pricingRequest,
double spotPrice,
CalcScenarioEnum CalcScenario,
bool calcDeltaT)
{
OptionCalcParam getOptionCalcParam(T tradeParam, double[] spotPrices = null) where T : OptionTradeParamBase
{
return new OptionCalcParam(tradeParam)
{
engineName = null,
pricingRequest = pricingRequest,
spotPrices = spotPrices ?? new[] { spotPrice },
calcScenario = CalcScenario,
CalcDeltaT1 = calcDeltaT
};
}
var result = new TradeValueResult();
var tradeclone = td.Copy();
tradeclone.TradeAmount = tradeclone.TradeAmount = TradeCalcHelper.GetTradeAmountV(td, td.TradeAmount, 1);
tradeclone.Notional = tradeclone.Notional = TradeCalcHelper.GetTradeAmountV(td, td.Notional, underlyings.CountRatio);
var td1 = tradeclone.Copy();
var td2 = tradeclone.Copy();
if (risky_Option.ParticipationRate2 != 0)
{
td2.Strike = risky_Option.Strike2;
td2.ParticipationRate = risky_Option.ParticipationRate2;
td2.TradeAmount = TradeCalcHelper.GetTradeAmount(td2, td2.TradeAmount, 1);
td2.Notional = TradeCalcHelper.GetTradeAmount(td2, td2.Notional, underlyings.CountRatio);
var tradeParam2 = QdpTradeBuilder.GetVanillaOptionTradeParam(td2, request, false);
result = TradeRiskCalcUtil.GetVanillaOptionValue(marketProxy, getOptionCalcParam(tradeParam2));
}
if (risky_Option.ParticipationRate1 != 0)
{
td1.Strike = risky_Option.Strike1;
td1.ParticipationRate = risky_Option.ParticipationRate1;
td1.TradeAmount = TradeCalcHelper.GetTradeAmount(td1, td1.TradeAmount, 1);
td1.Notional = TradeCalcHelper.GetTradeAmount(td1, td1.Notional, underlyings.CountRatio);
td1.OptionType = "看跌";
var tradeParam1 = QdpTradeBuilder.GetVanillaOptionTradeParam(td1, request, false);
var singleresult1 = TradeRiskCalcUtil.GetVanillaOptionValue(marketProxy, getOptionCalcParam(tradeParam1));
result.Pv -= singleresult1.Pv;
result.Delta -= singleresult1.Delta;
result.Gamma -= singleresult1.Gamma;
result.Vega -= singleresult1.Vega;
result.CalendarDayTheta -= singleresult1.CalendarDayTheta;
result.TradingDayTheta -= singleresult1.TradingDayTheta;
result.Rho -= singleresult1.Rho;
result.DeltaInLots -= singleresult1.DeltaInLots;
result.DeltaCash -= singleresult1.DeltaCash;
result.GammaCash -= singleresult1.GammaCash;
result.VegaCash -= singleresult1.VegaCash;
result.RoundedPv -= singleresult1.RoundedPv;
result.DDeltaDVol -= singleresult1.DDeltaDVol;
result.DDeltaDt -= singleresult1.DDeltaDt;
result.DVegaDVol -= singleresult1.DVegaDVol;
result.DVegaDt -= singleresult1.DVegaDt;
result.DeltaT1 -= singleresult1.DeltaT1;
result.ErrorMessage += singleresult1.ErrorMessage;
result.Vol = singleresult1.Vol;
result.Succeeded = singleresult1.Succeeded && result.Succeeded;
}
var td3 = tradeclone.Copy();
td3.Strike = risky_Option.Strike3;
//decimal 为了解决精度问题: 0.2-0.3=0.0999999999
var participationRate3 = (decimal)risky_Option.ParticipationRate3 - (decimal)risky_Option.ParticipationRate2;
if (participationRate3 != 0)
{
td3.ParticipationRate = (double?)Math.Abs(participationRate3);
td3.TradeAmount = TradeCalcHelper.GetTradeAmount(td3, td3.TradeAmount, 1);
td3.Notional = TradeCalcHelper.GetTradeAmount(td3, td3.Notional, underlyings.CountRatio);
var tradeParam3 = QdpTradeBuilder.GetVanillaOptionTradeParam(td3, request, false);
var singleresult3 = TradeRiskCalcUtil.GetVanillaOptionValue(marketProxy, getOptionCalcParam(tradeParam3));
if (participationRate3 < 0)
{
result.Pv -= singleresult3.Pv;
result.Delta -= singleresult3.Delta;
result.Gamma -= singleresult3.Gamma;
result.Vega -= singleresult3.Vega;
result.CalendarDayTheta -= singleresult3.CalendarDayTheta;
result.TradingDayTheta -= singleresult3.TradingDayTheta;
result.Rho -= singleresult3.Rho;
result.DeltaInLots -= singleresult3.DeltaInLots;
result.DeltaCash -= singleresult3.DeltaCash;
result.GammaCash -= singleresult3.GammaCash;
result.VegaCash -= singleresult3.VegaCash;
result.RoundedPv -= singleresult3.RoundedPv;
result.DDeltaDVol -= singleresult3.DDeltaDVol;
result.DDeltaDt -= singleresult3.DDeltaDt;
result.DVegaDVol -= singleresult3.DVegaDVol;
result.DVegaDt -= singleresult3.DVegaDt;
result.DeltaT1 -= singleresult3.DeltaT1;
result.ErrorMessage += singleresult3.ErrorMessage;
result.Vol = singleresult3.Vol;
}
else
{
result.Pv += singleresult3.Pv;
result.Delta += singleresult3.Delta;
result.Gamma += singleresult3.Gamma;
result.Vega += singleresult3.Vega;
result.CalendarDayTheta += singleresult3.CalendarDayTheta;
result.TradingDayTheta += singleresult3.TradingDayTheta;
result.Rho += singleresult3.Rho;
result.DeltaInLots += singleresult3.DeltaInLots;
result.DeltaCash += singleresult3.DeltaCash;
result.GammaCash += singleresult3.GammaCash;
result.VegaCash += singleresult3.VegaCash;
result.RoundedPv += singleresult3.RoundedPv;
result.DDeltaDVol += singleresult3.DDeltaDVol;
result.DDeltaDt += singleresult3.DDeltaDt;
result.DVegaDVol += singleresult3.DVegaDVol;
result.DVegaDt += singleresult3.DVegaDt;
result.DeltaT1 += singleresult3.DeltaT1;
result.ErrorMessage += singleresult3.ErrorMessage;
result.Vol = singleresult3.Vol;
}
result.Succeeded = singleresult3.Succeeded && result.Succeeded;
}
return result;
}
#region----期权计算参数----
///
/// 彩虹期权(如果准备波动率失败,返回null)
///
private RainbowOptionTradeParam GetRainbowOptionTradeParam(OptionTradeParamRequest request, out double[] spotPrices)
{
var rainbowOption = _tradeExtendDataProvider.GetTrade_Rainbow_Option(_trade.id)
?? throw new TradeCalcExpception(_trade.id, "未获取到对应的彩虹期权数据");
_underlyingPriceProvider.TryGetPrice(rainbowOption.UnderlyingAssetCode, out var price1);
_underlyingPriceProvider.TryGetPrice(rainbowOption.UnderlyingAssetCode2, out var price2);
spotPrices = new double[2] { price1, price2 };
//彩虹期权,需要两个标的的初始化
return QdpTradeBuilder.GetRainbowOptionTradeParam(_trade, rainbowOption, request);
}
///
/// 亚式期权
///
private AsianOptionTradeParam GetAsianOptionCalcParam(OptionTradeParamRequest request, double spotPrice)
{
var asianOption = _tradeExtendDataProvider.GetTrade_Asian_Option(_trade.id)
?? throw new TradeCalcExpception(_trade.id, "未获取到对应的亚式期权数据");
//主要是某些计算场景(比如实时)的fixing用了缓存提升性能所以才从context中获取
request.fixings = _context.GetFixingString(_trade, asianOption, spotPrice);
request.fixings = AsianOptionFixingService.CheckAsiaFixings(_trade, asianOption, request.fixings, spotPrice);
return QdpTradeBuilder.GetAsianOptionTradeParam(_trade, asianOption, request);
}
///
/// 价差期权
///
private SpreadOptionTradeParam GetSpreadOptionCalcParam(OptionTradeParamRequest request, out double[] spotPrices)
{
var spreadOption = _tradeExtendDataProvider.GetTrade_Spread_Option(_trade.id)
?? throw new TradeCalcExpception(_trade.id, "未获取到对应的价差期权数据");
var spreadOptionInput = GetMarketInputForSpreadOption(spreadOption);
spotPrices = spreadOptionInput.SpotPrices.ToArray();
var correlations = spreadOptionInput.Correlations.ToArray();
_spreadUnderlyings = spreadOptionInput.Underlyings;
return QdpTradeBuilder.GetSpreadOptionTradeParam(_trade, spreadOption, request, correlations);
}
#endregion
///
/// 为价差期权的多个标的获取现价和相关性数据
///
private SpreadOptionPricingInput GetMarketInputForSpreadOption(trade_spread_option spreadOption)
{
if (spreadOption == null)
{
throw new ArgumentNullException(nameof(spreadOption));
}
var unlyArr = new underlying_manager[4];
unlyArr[0] = _underlyingDataProvider.GetUnderlying(_trade.UnderlyingCode);
unlyArr[1] = _underlyingDataProvider.GetUnderlying(spreadOption.UnderlyingAssetCode2);
if (!string.IsNullOrEmpty(spreadOption.UnderlyingAssetCode3))
{
unlyArr[2] = _underlyingDataProvider.GetUnderlying(spreadOption.UnderlyingAssetCode3);
if (!string.IsNullOrEmpty(spreadOption.UnderlyingAssetCode4))
{
unlyArr[3] = _underlyingDataProvider.GetUnderlying(spreadOption.UnderlyingAssetCode4);
}
}
unlyArr = unlyArr.Where(n => n != null).ToArray();
var spotPrices = new List();
var correlations = new List();
for (var i = 0; i < unlyArr.Length; i++)
{
var unly = unlyArr[i];
if (unly == null)
{
break;
}
for (var j = 0; j < i; j++)
{
correlations.Add(_context.GetCorrelation(unlyArr[j].id, unly.id));
}
_underlyingPriceProvider.TryGetPrice(unly.UnderlyingCode, out var price);
spotPrices.Add(price);
unly.Price = price;
}
return new SpreadOptionPricingInput()
{
Underlyings = unlyArr,
SpotPrices = spotPrices.ToArray(),
Correlations = correlations.ToArray()
};
}
}
}