579 lines
29 KiB
C#
579 lines
29 KiB
C#
using YLErp.BLL;
|
|
using YLErp.Enums;
|
|
using YLErp.Modules.CalculationModule.Abstract;
|
|
using YLErp.QdpModule;
|
|
using YLErp.ThirdParty.CaculatePrice.DongZheng;
|
|
using YLErp.ThirdParty.CaculatePrice.DongZheng.Dto;
|
|
|
|
namespace YLErp.Modules.CalculationModule
|
|
{
|
|
/// <summary>
|
|
/// 只用于计算期权PV
|
|
/// </summary>
|
|
public class OptionCalculatorV2
|
|
{
|
|
|
|
private static readonly IYcLogger log = LogFactory.GetLogger(nameof(OptionCalculatorV2));
|
|
|
|
/// <summary>
|
|
/// 只用于计算期权PV
|
|
/// </summary>
|
|
public static TradeValueResult GetOptionValueResult(DateTime valueDate, trade td, OptionValueCalcRequest request, out underlying_manager[] underlyings)
|
|
{
|
|
if (td is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(td));
|
|
}
|
|
|
|
if (request is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(request));
|
|
}
|
|
|
|
using var marketProxy = new MarketProxy(valueDate, request.sysRiskFreeRate);
|
|
return GetOptionValueResult(marketProxy, td, request, out underlyings);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 只用于计算期权PV
|
|
/// </summary>
|
|
public static TradeValueResult GetOptionValueResult(MarketProxy marketProxy, trade td, OptionValueCalcRequest request, out underlying_manager[] underlyings)
|
|
{
|
|
if (marketProxy is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(marketProxy));
|
|
}
|
|
|
|
if (td is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(td));
|
|
}
|
|
|
|
if (request is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(request));
|
|
}
|
|
|
|
if (request.spotPrices == null || !request.spotPrices.Any())
|
|
{
|
|
throw new Exception("期权计算缺少标的现价");
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(td.UnderlyingCode) && td.HasUnderlying())
|
|
{
|
|
throw new Exception("缺少标的代码");
|
|
}
|
|
|
|
TradeValueResult result;
|
|
|
|
//象屿最后一个交易日实时计算时TTM需要和平仓时算法一致
|
|
if (PS.Config.Is厦门象屿
|
|
&& (request.calcScenario == CalcScenarioEnum.RealtimePosition || request.calcScenario == CalcScenarioEnum.RealtimeRisk)
|
|
&& td.SettlementType == SettlementTypeEnum.ReferencePrice)
|
|
{
|
|
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode);
|
|
if (underlying != null)
|
|
{
|
|
request.timeToMaturityDays = TradeCalcHelper.CalculateTTMDaysForXiangYu(valuedateBLL.ValueDate, td.ExerciseDate.Value, underlying.UnderlyingTypeId, false);
|
|
//20210706:支持厦门象屿参考价相关交易(这类交易不需要支持精确模式)
|
|
request.preciseTimeMode = false;
|
|
}
|
|
}
|
|
|
|
//东证润和是精确时间模式参与计算,收盘的话,刚好是整数天,所以不需要特殊处理
|
|
|
|
if (PS.Config.Is润和 && !request.isEodCalc && (request.timeToMaturityDays == null || double.IsNaN(request.timeToMaturityDays.Value)))
|
|
{
|
|
request.timeToMaturityDays = TradeCalcHelper.CalculateTTMDays(marketProxy.ValueDate, td.ExerciseDate.Value, 0, false);
|
|
}
|
|
|
|
var tpReq = PrepareCalc(marketProxy, td, request, out underlyings, out var getAsianFixings);
|
|
|
|
switch (td.TradeType)
|
|
{
|
|
case "香草期权":
|
|
{
|
|
var tradeParam = QdpTradeBuilder.GetVanillaOptionTradeParam(td, tpReq, false);
|
|
result = TradeRiskCalcUtil.GetVanillaOptionValue(marketProxy, GetOptionCalcParam(tradeParam, request));
|
|
}
|
|
break;
|
|
case "Risky期权":
|
|
{
|
|
result = GetOptionRisky(td, request, underlyings[0], tpReq, marketProxy);
|
|
}
|
|
break;
|
|
case "场内期权":
|
|
{
|
|
var tradeParam = QdpTradeBuilder.GetVanillaOptionTradeParam(td, tpReq, true);
|
|
result = TradeRiskCalcUtil.GetVanillaOptionValue(marketProxy, GetOptionCalcParam(tradeParam, request));
|
|
}
|
|
break;
|
|
case "合成价差期权":
|
|
{
|
|
var tradeParam = QdpTradeBuilder.GetSSpreadOptionTradeParam(td, tpReq);
|
|
result = TradeRiskCalcUtil.GetSSpreadOptionValue(marketProxy, GetOptionCalcParam(tradeParam, request));
|
|
}
|
|
break;
|
|
case "亚式期权":
|
|
{
|
|
if (!PS.Config.Is润和 && !request.isEodCalc && (request.timeToMaturityDays == null || double.IsNaN(request.timeToMaturityDays.Value)))
|
|
{
|
|
tpReq.timeToMaturityDays = TradeCalcHelper.CalculateTTMDays(marketProxy.ValueDate, td.ExerciseDate.Value, 0, false);
|
|
}
|
|
tpReq.fixings = getAsianFixings();
|
|
var tradeParam = QdpTradeBuilder.GetAsianOptionTradeParam(td, td.trade_asian_option, tpReq);
|
|
result = TradeRiskCalcUtil.GetAsianOptionValue(marketProxy, GetOptionCalcParam(tradeParam, request));
|
|
}
|
|
break;
|
|
case "亚式合成价差期权":
|
|
{
|
|
tpReq.fixings = getAsianFixings();
|
|
var tradeParam = QdpTradeBuilder.GetAsianOptionTradeParam(td, td.trade_asian_option, tpReq);
|
|
result = TradeRiskCalcUtil.GetAsianSSpreadOptionValue(marketProxy, GetOptionCalcParam(tradeParam, request));
|
|
}
|
|
break;
|
|
case "障碍期权":
|
|
{
|
|
var tradeParam = QdpTradeBuilder.GetBarrierOptionTradeParam(td, td.trade_barrier_option, tpReq);
|
|
result = TradeRiskCalcUtil.GetBarrierOptionValue(marketProxy, GetOptionCalcParam(tradeParam, request));
|
|
}
|
|
break;
|
|
case "二元期权":
|
|
{
|
|
var tradeParam = QdpTradeBuilder.GetBinaryOptionTradeParam(td, td.trade_binary_option, tpReq);
|
|
result = TradeRiskCalcUtil.GetBinaryOptionValue(marketProxy, GetOptionCalcParam(tradeParam, request));
|
|
}
|
|
break;
|
|
case "双鲨期权":
|
|
{
|
|
var tradeParam = QdpTradeBuilder.GetDoubleSharkFinOptionTradeParam(td, td.trade_double_sharkfin_option, tpReq);
|
|
result = TradeRiskCalcUtil.GetDoubleSharkFinOptionValue(marketProxy, GetOptionCalcParam(tradeParam, request));
|
|
}
|
|
break;
|
|
case "凤凰期权":
|
|
{
|
|
if (td.trade_autocall == null)
|
|
{
|
|
throw new ServiceException("缺少奇异期权数据,交易编号:" + td.TradeNumber);
|
|
}
|
|
if (td.trade_autocall.HappenedObservations == null && td.id > 0)
|
|
{
|
|
td.trade_autocall.HappenedObservations = ObservationDataService.QueryDatas(td.id, marketProxy.ValueDate);
|
|
}
|
|
var tradeParam = QdpTradeBuilder.GetAutocallOptionTradeParam(td, td.trade_autocall, tpReq);
|
|
try
|
|
{
|
|
result = TradeRiskCalcUtil.GetAutocallOptionValue(marketProxy, GetOptionCalcParam(tradeParam, request));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (string.IsNullOrEmpty(td.TradeNumber))
|
|
{
|
|
throw;
|
|
}
|
|
throw new Exception($"凤凰期权'{td.TradeNumber}'计算出错:{ex.Message}");
|
|
}
|
|
}
|
|
break;
|
|
case "雪球期权":
|
|
{
|
|
if (td.IsSnowballSpecialist())
|
|
{
|
|
var snowballSpecialistOptionCalculator = new SnowballSpecialistOptionCalculator();
|
|
|
|
if (td.trade_snowball.PrepaymentRatio > 0)
|
|
{
|
|
var specialSnowballTrade = snowballSpecialistOptionCalculator.GetSpecialTrade(td);
|
|
var specialSnowballResult = snowballSpecialistOptionCalculator.CalcOptionValue(marketProxy.ValueDate, request.spotPrices[0], request.vols[0], request.calcScenario, specialSnowballTrade);
|
|
|
|
var breakevenSnowballTrade = snowballSpecialistOptionCalculator.GetBreakevenTrade(td);
|
|
var tradeParam = QdpTradeBuilder.GetSnowballTradeParam(breakevenSnowballTrade, breakevenSnowballTrade.trade_snowball, tpReq);
|
|
tradeParam.riskFreeRate = breakevenSnowballTrade.NoRiskRate ?? 0;
|
|
var breakevenSnowballResult = TradeRiskCalcUtil.GetSnowballOptionValue(marketProxy, GetOptionCalcParam(tradeParam, request));
|
|
|
|
result = snowballSpecialistOptionCalculator.MergeTradeValueResult(specialSnowballResult, breakevenSnowballResult);
|
|
}
|
|
else
|
|
{
|
|
result = snowballSpecialistOptionCalculator.CalcOptionValue(marketProxy.ValueDate, request.spotPrices[0], request.vols[0], request.calcScenario, td);
|
|
}
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
// 普通雪球
|
|
var tradeParam = QdpTradeBuilder.GetSnowballTradeParam(td, td.trade_snowball, tpReq);
|
|
result = TradeRiskCalcUtil.GetSnowballOptionValue(marketProxy, GetOptionCalcParam(tradeParam, request));
|
|
}
|
|
}
|
|
break;
|
|
case "区间累积期权":
|
|
{
|
|
if (td.trade_rangeaccrual == null)
|
|
{
|
|
throw new ServiceException("缺少奇异期权数据,交易编号:" + td.TradeNumber);
|
|
}
|
|
if (td.trade_rangeaccrual.HappenedObservations == null && td.id > 0)
|
|
{
|
|
td.trade_rangeaccrual.HappenedObservations = ObservationDataService.QueryDatas(td.id, marketProxy.ValueDate, includeValueDate: false);
|
|
}
|
|
tpReq.fixings = request.fixings;
|
|
if (string.IsNullOrWhiteSpace(tpReq.fixings)
|
|
&& (td.trade_rangeaccrual.HappenedObservations == null || td.trade_rangeaccrual.HappenedObservations.Count == 0))
|
|
{
|
|
//非日终时不将当天的价格加入fixing中
|
|
var valueDate = request.calcScenario == CalcScenarioEnum.EodMargin
|
|
|| request.calcScenario == CalcScenarioEnum.EodSettlement
|
|
|| request.calcScenario == CalcScenarioEnum.ScenarioCalc
|
|
? marketProxy.ValueDate : marketProxy.ValueDate.AddDays(-1);
|
|
tpReq.fixings = FixingService.GetFixingString(valueDate: valueDate, otcTrade: td, startDate: td.StartDate ?? td.TradeDate.Value, observationDates: td.trade_rangeaccrual.ObservationDates);
|
|
}
|
|
var tradeParam = QdpTradeBuilder.GetRangeAccrualTradeParam(td, td.trade_rangeaccrual, tpReq);
|
|
result = TradeRiskCalcUtil.GetRangeAccrualValue(marketProxy, GetOptionCalcParam(tradeParam, request));
|
|
}
|
|
break;
|
|
case "气囊结构":
|
|
{
|
|
var tradeParam = QdpTradeBuilder.GetAirbagOptionTradeParam(td, td.trade_airbag, tpReq);
|
|
result = TradeRiskCalcUtil.GetAirbagOptionValue(marketProxy, GetOptionCalcParam(tradeParam, request));
|
|
}
|
|
break;
|
|
case "收益增强结构":
|
|
{
|
|
var tradeParam = QdpTradeBuilder.GetUnderlyingEnhanceTradeParam(td, td.trade_underlying_enhance, tpReq);
|
|
result = TradeRiskCalcUtil.GetUnderlyingEnhanceValue(marketProxy, GetOptionCalcParam(tradeParam, request));
|
|
}
|
|
break;
|
|
case "累计期权":
|
|
{
|
|
var tradeParam = QdpTradeBuilder.GetAccumulatorOptionTradeParam(td, td.trade_accumulator_option, tpReq);
|
|
result = TradeRiskCalcUtil.GetAccumulatorOptionValue(marketProxy, GetOptionCalcParam(tradeParam, request), td.Notional);
|
|
}
|
|
break;
|
|
case "现金流交易":
|
|
{
|
|
var tradeParam = QdpTradeBuilder.GetCashFlowTradeParam(td, td.trade_cashflow, tpReq);
|
|
result = TradeRiskCalcUtil.GetCashFlowValue(marketProxy, GetOptionCalcParam(tradeParam, request), td.StockEqvNotional);
|
|
}
|
|
break;
|
|
case "结构化产品":
|
|
var structProductParm = QdpTradeBuilder.GetStructProductTradeParam(request, marketProxy, td);
|
|
StructureResult spResult;
|
|
log.Info("定价请求信息:" + JsonHelper.Serialize(structProductParm.Request));
|
|
if (DongZhengPriceApi.StructureProduct(structProductParm.Request, out spResult, structProductParm.VolSurface))
|
|
{
|
|
result = new TradeValueResult
|
|
{
|
|
Pv = spResult.pv,
|
|
Delta = spResult.delta,
|
|
Gamma = spResult.gamma,
|
|
Vega = spResult.vegaPercentage,
|
|
TradingDayTheta = spResult.thetaPerDay,
|
|
Rho = spResult.rhoPercentage
|
|
};
|
|
}
|
|
else
|
|
{
|
|
throw new Exception("东证结构化产品定价计算失败" + spResult.message);
|
|
}
|
|
log.Info("定价请求结果:" + JsonHelper.Serialize(spResult));
|
|
break;
|
|
default:
|
|
throw new Exception("不支持这种类型交易的期权计算:" + td.TradeType);
|
|
}
|
|
|
|
if (result != null)
|
|
{
|
|
result.Strike = td.Strike ?? 0;
|
|
result.SpotPrice = request.spotPrices[0];
|
|
result.VegaCash = result.VegaCash.IsNormalize() ? result.VegaCash : (result.Vega * result.SpotPrice).Normalize();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private static TradeValueResult GetOptionRisky(
|
|
trade td,
|
|
OptionValueCalcRequest request,
|
|
underlying_manager underlyings,
|
|
OptionTradeParamRequest tpReq,
|
|
MarketProxy marketProxy)
|
|
{
|
|
var result = new TradeValueResult();
|
|
var tradeclone = td.Clone();
|
|
tradeclone.TradeAmount = tradeclone.TradeAmount = TradeCalcHelper.GetTradeAmountV(td, td.TradeAmount, 1);
|
|
tradeclone.Notional = tradeclone.Notional = TradeCalcHelper.GetTradeAmountV(td, td.Notional, underlyings.CountRatio);
|
|
|
|
var td1 = tradeclone.Clone();
|
|
var td2 = tradeclone.Clone();
|
|
if (td.trade_risky_option.ParticipationRate2 != 0)
|
|
{
|
|
td2.Strike = td.trade_risky_option.Strike2;
|
|
td2.ParticipationRate = td.trade_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, tpReq, false);
|
|
result = TradeRiskCalcUtil.GetVanillaOptionValue(marketProxy, GetOptionCalcParam(tradeParam2, request));
|
|
}
|
|
|
|
if (td.trade_risky_option.ParticipationRate1 != 0)
|
|
{
|
|
td1.Strike = td.trade_risky_option.Strike1;
|
|
td1.ParticipationRate = td.trade_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, tpReq, false);
|
|
var singleresult1 = TradeRiskCalcUtil.GetVanillaOptionValue(marketProxy, GetOptionCalcParam(tradeParam1, request));
|
|
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.Clone();
|
|
td3.Strike = td.trade_risky_option.Strike3;
|
|
//decimal 为了解决精度问题: 0.2-0.3=0.0999999999
|
|
var participationRate3 = (decimal)td.trade_risky_option.ParticipationRate3 - (decimal)td.trade_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, tpReq, false);
|
|
var singleresult3 = TradeRiskCalcUtil.GetVanillaOptionValue(marketProxy, GetOptionCalcParam(tradeParam3, request));
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取期权计算参数
|
|
/// </summary>
|
|
private static OptionCalcParam<T> GetOptionCalcParam<T>(T tradeParam, OptionValueCalcRequest request) where T : OptionTradeParamBase
|
|
{
|
|
return new OptionCalcParam<T>(tradeParam)
|
|
{
|
|
engineName = request.engineName,
|
|
pricingRequest = request.pricingRequest,
|
|
spotPrices = request.spotPrices,
|
|
calcScenario = request.calcScenario,
|
|
quadratureFastMode = request.quadratureFastMode,
|
|
CalcDeltaT1 = request.calcDeltaT1
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// 准备计算
|
|
/// </summary>
|
|
private static OptionTradeParamRequest PrepareCalc(MarketProxy marketProxy, trade td
|
|
, OptionValueCalcRequest request, out underlying_manager[] underlyings, out Func<string> getAsianFixings)
|
|
{
|
|
getAsianFixings = new Func<string>(() =>
|
|
{
|
|
if (!string.IsNullOrEmpty(request.fixings))
|
|
{
|
|
return request.fixings;
|
|
}
|
|
var fixings = AsianOptionFixingService.GetFixingString(marketProxy.ValueDate, td);
|
|
fixings = FixingService.AddOrReplaceLastDateSpotPrice(fixings, marketProxy.ValueDate, request.spotPrices[0]);
|
|
|
|
if (PS.Config.Is润和 && DateTime.Now.Hour < 15)
|
|
{
|
|
var index = fixings.IndexOf(marketProxy.ValueDate.ToString("yyyy-MM-dd"));
|
|
if (index >= 0)
|
|
{
|
|
fixings = fixings.Remove(index).TrimEnd(';');
|
|
}
|
|
}
|
|
|
|
return AsianOptionFixingService.CheckAsiaFixings(td, td.trade_asian_option, fixings, request.spotPrices[0]);
|
|
});
|
|
|
|
var tpReq = new OptionTradeParamRequest(request.sysRiskFreeRate)
|
|
{
|
|
hasNightMarket = false,
|
|
maturityShift = request.maturityShift,
|
|
ParamOverride = request.ParamOverride,
|
|
preciseTimeMode = request.preciseTimeMode,
|
|
timeToMaturityDays = request.timeToMaturityDays ?? double.NaN,
|
|
tradeId = marketProxy.NextRequestId() + "_",
|
|
volSurfaceNames = null,
|
|
fixings = null
|
|
};
|
|
|
|
tradeBLL.SetFieldsByTradeType(td);
|
|
|
|
switch (td.TradeType)
|
|
{
|
|
case "彩虹期权":
|
|
//var underlyingCodes = new[] { trade.UnderlyingCode, trade.trade_rainbow_option.UnderlyingAssetCode2 };
|
|
//request.volSurfaceNames = PrepareVols(marketProxy, tradeId.ToString(), underlyingCodes, request.vols);
|
|
throw new Exception("不支持彩虹期权计算");
|
|
case "价差期权":
|
|
//var underlyingCodes = trade.trade_spread_option.UnderlyingAssetCodes();
|
|
//request.volSurfaceNames = PrepareVols(marketProxy, tradeId.ToString(), underlyingCodes, request.vols);
|
|
throw new Exception("不支持价差期权计算");
|
|
case ConsGlobal.TradeType.CashFlow:
|
|
break;
|
|
default:
|
|
tpReq.volSurfaceNames = PrepareVols(marketProxy, tpReq.tradeId, new string[] { td.UnderlyingCode }, request.vols);
|
|
if (td.StructureType == "亚式熊市价差")
|
|
{
|
|
tpReq.fixings = getAsianFixings();
|
|
}
|
|
break;
|
|
}
|
|
|
|
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode);
|
|
|
|
if (underlying == null)
|
|
{
|
|
if (td.HasUnderlying())
|
|
{
|
|
throw new TradeCalcExpception(td.id, "没有找到标的数据:" + td.UnderlyingCode);
|
|
}
|
|
}
|
|
else if (underlying.IsFutures())
|
|
{
|
|
var variety = DataCacheProvider.GetVarietyDataSource().GetData(underlying.UnderlyingTypeId);
|
|
tpReq.hasNightMarket = variety != null && variety.HasNightMarket;
|
|
}
|
|
|
|
underlyings = new[] { underlying ?? new underlying_manager() };
|
|
|
|
tpReq.tradeId += td.id > 0 ? td.id.ToString() : string.IsNullOrWhiteSpace(td.ExchangeOptionCode) ? td.UnderlyingCode : td.ExchangeOptionCode;
|
|
|
|
return tpReq;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 准备波动率
|
|
/// </summary>
|
|
private static string[] PrepareVols(MarketProxy marketProxy, string tradeId, string[] underlyingCodes, double[] vols)
|
|
{
|
|
if (string.IsNullOrEmpty(tradeId))
|
|
{
|
|
throw new ArgumentException($"“{nameof(tradeId)}”不能是 Null 或为空。", nameof(tradeId));
|
|
}
|
|
|
|
if (underlyingCodes == null || !underlyingCodes.Any())
|
|
{
|
|
throw new Exception("缺少标的代码");
|
|
}
|
|
|
|
if (vols == null || !vols.Any() || vols.Length < underlyingCodes.Length)
|
|
{
|
|
throw new Exception("缺少波动率");
|
|
}
|
|
|
|
var volSurfaceNames = new string[underlyingCodes.Length];
|
|
|
|
for (var i = 0; i < underlyingCodes.Length; i++)
|
|
{
|
|
var volatility = QdpVolHelper.GetDefaultVolatility(vols[i]);
|
|
volSurfaceNames[i] = QdpVolHelper.GetVolSurfaceName(tradeId, i > 0 ? underlyingCodes[i] : null);
|
|
marketProxy.SetVolSurface(volSurfaceNames[i], volatility);
|
|
}
|
|
|
|
return volSurfaceNames;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 计算交叉GAMMA
|
|
/// </summary>
|
|
public static double[] CalcSSpreadCrossGammas(DateTime valueDate, trade td, OptionValueCalcRequest request, double[] coefficients)
|
|
{
|
|
if (td is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(td));
|
|
}
|
|
|
|
if (request is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(request));
|
|
}
|
|
|
|
if (request.spotPrices == null || !request.spotPrices.Any())
|
|
{
|
|
throw new Exception("期权计算缺少标的现价");
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(td.UnderlyingCode) && td.HasUnderlying())
|
|
{
|
|
throw new Exception("缺少标的代码");
|
|
}
|
|
|
|
using var marketProxy = new MarketProxy(valueDate, request.sysRiskFreeRate);
|
|
var tpReq = PrepareCalc(marketProxy, td, request, out _, out _);
|
|
var tradeParam = QdpTradeBuilder.GetSSpreadOptionTradeParam(td, tpReq, coefficients);
|
|
return TradeRiskCalcUtil.CalcSSpreadCrossGammas(marketProxy, GetOptionCalcParam(tradeParam, request), coefficients);
|
|
}
|
|
}
|
|
}
|