Files
zszq-trs/YLErpDAL/BLL/Calculation/V2/ValueCalculator.cs
T

2323 lines
111 KiB
C#

using Qdp.ComputeService.Data.CommonModels.ValuationParams.Equity;
using Qdp.ComputeService.Data.CommonModels.ValuationParams.FixedIncome;
using Qdp.ComputeServiceV2.Data.CommonModels.TradeInfos;
using Qdp.ComputeServiceV2.Data.CommonModels.TradeInfos.Options;
using Qdp.Foundation.Implementations;
using Qdp.Pricing.Base.Enums;
using Qdp.Pricing.Base.Implementations;
using Qdp.Pricing.Base.Interfaces;
using Qdp.Pricing.Library.Common.Base;
using Qdp.Pricing.Library.Common.Interfaces;
using Qdp.Pricing.Library.Common.Products.Rates.Engines;
using Qdp.Pricing.Library.Options.Products.Accumulator.Engines.Analytical;
using Qdp.Pricing.Library.Options.Products.Accumulator.Engines.Numerical;
using Qdp.Pricing.Library.Options.Products.Airbag.Engines.Analytical;
using Qdp.Pricing.Library.Options.Products.Asian;
using Qdp.Pricing.Library.Options.Products.AsianSyntheticSpread.Engines.Analytical;
using Qdp.Pricing.Library.Options.Products.Autocall.Phoenix;
using Qdp.Pricing.Library.Options.Products.Autocall.Phoenix.Numerical;
using Qdp.Pricing.Library.Options.Products.Autocall.Snowball;
using Qdp.Pricing.Library.Options.Products.Barrier;
using Qdp.Pricing.Library.Options.Products.Binary;
using Qdp.Pricing.Library.Options.Products.DoubleSharkFin;
using Qdp.Pricing.Library.Options.Products.PayoffEnhance.Engines.Analytical;
using Qdp.Pricing.Library.Options.Products.Rainbow;
using Qdp.Pricing.Library.Options.Products.RangeAccrual;
using Qdp.Pricing.Library.Options.Products.Spread;
using Qdp.Pricing.Library.Options.Products.Vanilla;
using YLErp.BLL.Calculation.Engine;
using YLErp.BLL.Calculation.V2.Parameter;
using YLErp.Commons;
using YLErp.Configuration;
using YLErp.Enums;
using YLErp.Modules;
using YLErp.Modules.CalculationLogModule;
using YLErp.Modules.CalculationModule;
using YLErp.QdpModule;
using YLErp.QdpModule.Constants;
namespace YLErp.BLL.Calculation.V2
{
public class ValueCalculator
{
/// <summary>
/// 计算金融产品及衍生品的价值
/// </summary>
/// <param name="userId">QdpMarketId</param>
/// <param name="trade">交易</param>
/// <param name="underlying">标的资产</param>
/// <param name="parameter">计算参数</param>
/// <returns></returns>
public static TradeValueResult CalculateTradeValue(
string userId,
trade trade,
underlying_manager underlying,
ParameterBase parameter,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS,
CalcScenarioEnum calcScenario = CalcScenarioEnum.Default,
bool quadratureFastMode = false)
{
underlying = underlying.Clone();
underlying.UnderlyingInstrumentType = ConsGlobal.InstrumentType.ConvertCalcType(underlying.UnderlyingInstrumentType);
switch (trade.TradeType)
{
case "香草期权":
case "场内期权":
return CalculateVanillaOption(userId, trade, underlying, parameter as VanillaOptionParameter, pricingRequest, calcScenario);
case "Risky期权":
return CalculateRiskyOption(userId, trade, underlying, parameter as VanillaOptionParameter, pricingRequest, calcScenario);
case "障碍期权":
return CalculateBarrierOption(userId, trade, underlying, parameter as BarrierOptionParameter, pricingRequest, calcScenario, quadratureFastMode: quadratureFastMode);
case "亚式期权":
return CalculateAsianOption(userId, trade, underlying, parameter as AsianOptionParameter, pricingRequest, calcScenario);
case "二元期权":
return CalculateBinaryOption(userId, trade, underlying, parameter as BinaryOptionParameter, pricingRequest, calcScenario);
case "彩虹期权":
return CalculateRainbowOption(userId, trade, underlying, parameter as RainbowOptionParameter, pricingRequest, calcScenario);
case "价差期权":
return CalculateSpreadOption(userId, trade, underlying, parameter as SpreadOptionParameter, pricingRequest, calcScenario);
case "合成价差期权":
return CalculateSyntheticNormalSpreadOption(userId, trade, underlying, parameter as SyntheticNormalSpreadOptionParameter, pricingRequest, calcScenario);
case "亚式合成价差期权":
return CalculateAsianSyntheticNormalSpreadOption(userId, trade, underlying, parameter as AsianSyntheticNormalSpreadOptionParameter, pricingRequest, calcScenario);
case "双鲨期权":
return CalculateDoubleSharkFinOption(userId, trade, underlying, parameter as DoubleSharkFinOptionParameter, pricingRequest, calcScenario, quadratureFastMode: quadratureFastMode);
case "凤凰期权":
return CalculateAutocall(userId, trade, underlying, parameter as AutocallParameter, pricingRequest, calcScenario, quadratureFastMode: quadratureFastMode);
case "雪球期权":
if (trade.IsSnowballSpecialist())
{
SnowballSpecialistOptionCalculator snowballSpecialistOptionCalculator = new SnowballSpecialistOptionCalculator();
if (parameter?.SpotPrices == null || !parameter.SpotPrices.TryGetValue(underlying.UnderlyingCode, out var spotPrice))
{
throw new Exception("[计算雪球期权交易]缺少SpotPrice");
}
if (trade.trade_snowball.PrepaymentRatio > 0)
{
var specialSnowballTrade = snowballSpecialistOptionCalculator.GetSpecialTrade(trade);
var specialSnowballResult = snowballSpecialistOptionCalculator.CalcOptionValue(parameter.ValueDate, spotPrice, (parameter as SnowballParameter).Volatility ?? 0, calcScenario, specialSnowballTrade);
var breakevenSnowballTrade = snowballSpecialistOptionCalculator.GetBreakevenTrade(trade);
parameter.RiskFreeRate = breakevenSnowballTrade.NoRiskRate;
var breakevenSnowballResult = CalculateSnowball(userId, breakevenSnowballTrade, underlying, parameter as SnowballParameter, pricingRequest, calcScenario, quadratureFastMode: quadratureFastMode);
return snowballSpecialistOptionCalculator.MergeTradeValueResult(specialSnowballResult, breakevenSnowballResult);
}
else
{
return snowballSpecialistOptionCalculator.CalcOptionValue(parameter.ValueDate, spotPrice, (parameter as SnowballParameter).Volatility ?? 0, calcScenario, trade);
}
}
return CalculateSnowball(userId, trade, underlying, parameter as SnowballParameter, pricingRequest, calcScenario, quadratureFastMode: quadratureFastMode);
case "区间累积期权":
return CalculateRangeAccrual(userId, trade, underlying, parameter as RangeAccrualParameter, pricingRequest, calcScenario);
case "气囊结构":
return CalculateAirbag(userId, trade, underlying, parameter as AirbagParameter, pricingRequest, calcScenario);
case "收益增强结构":
return CalculateUnderlyingEnhance(userId, trade, underlying, parameter as UnderlyingEnhanceParameter, pricingRequest, calcScenario);
case "累计期权":
return CalculateAccumulatorOption(userId, trade, underlying, parameter as AccumulatorOptionParameter, pricingRequest, calcScenario);
case "现金流交易":
return CalculateCashFlow(userId, trade, underlying, parameter as CashFlowTradeParameter, pricingRequest, calcScenario);
case "远期":
{
if (parameter?.SpotPrices == null || !parameter.SpotPrices.TryGetValue(underlying.UnderlyingCode, out var spotPrice))
{
throw new Exception("[计算远期交易]缺少SpotPrice");
}
return CalculateForward(trade.Strike ?? 0.0, spotPrice, trade.Notional, trade.CallPut);
}
case "信用债":
case "商品期货":
case "商品现货":
case "股票":
{
if (parameter?.SpotPrices == null || !parameter.SpotPrices.TryGetValue(underlying.UnderlyingCode, out var spotPrice))
{
throw new Exception($"[计算{trade.TradeType}交易]缺少SpotPrice");
}
var pv = spotPrice * trade.Notional;
return new TradeValueResult { Pv = pv, RoundedPv = pv, Delta = trade.Notional, DeltaCash = pv };
}
case "结构化产品":
var para = parameter as VanillaOptionParameter;
var isPreciseTimeMode = underlying.QuotationDate.Value.Date == valuedateBLL.ValueDate.Date;
var request = new OptionValueCalcRequest(valuedateBLL.SysRiskFreeRate())
{
vols = new[] { (double)para.Volatility },
spotPrices = new[] { parameter.SpotPrices[trade.UnderlyingCode] },
engineName = para.EngineName,
preciseTimeMode = isPreciseTimeMode,
pricingRequest = QdpPricingRequest.PRICE_GREEKS,
quadratureFastMode = true,
calcScenario = CalcScenarioEnum.Pricing,
fixings = null,
timeToMaturityDays = trade.TTMDays
};
return OptionCalculatorV2.GetOptionValueResult(para.ValueDate, trade, request, out _);
default:
break;
}
throw new Exception($"不支持的交易结构{trade.TradeType}的计算");
}
/// <summary>
/// 原OptionCalculator中的GetOptionValueResult,仅用于定价计算
/// </summary>
public static TradeValueResult GetOptionValueResultV2(
string userId,
underlying_manager underlying,
trade trade,
double[] vols,
double[] spotPrices = null,
string fixing = null,
bool preciseTimeMode = false,
double[] correlations = null,
string discountCurveName = null,
int maturityShift = 0,
double timeToMaturityDays = double.NaN,
string engineName = null,
PricingRequest request = QdpPricingRequest.BASIC_GREEKS,
CalcScenarioEnum calcScenario = CalcScenarioEnum.Default,
bool quadratureFastMode = false)
{
if (spotPrices is null)
{
throw new ArgumentNullException(nameof(spotPrices));
}
if (trade.TTMDays != null && timeToMaturityDays.Equals(double.NaN))
{
timeToMaturityDays = trade.TTMDays.Value;
}
//股票默认到期日为行权日
if (underlying.UnderlyingInstrumentType == "Stock" && trade.ExerciseDate != null)
{
trade.MaturityDate = trade.ExerciseDate;
}
var variety = DataCacheProvider.GetVarietyDataSource().GetData(underlying.UnderlyingTypeId);
var parameter = ParameterHelper.CreatePricingParameter(
underlying: underlying,
variety: variety,
trade: trade,
vols: vols,
spotPrices: spotPrices,
fixing: fixing,
preciseTimeMode: preciseTimeMode,
correlations: correlations ?? new double[0],
discountCurveName: discountCurveName,
timeToMaturityDays: timeToMaturityDays,
engineName: engineName);
parameter.maturityShift = maturityShift;
try
{
var result = CalculateTradeValue(userId, trade, underlying, parameter, request, calcScenario, quadratureFastMode: quadratureFastMode);
if (result != null)
{
result.Strike = trade.Strike ?? 0;
}
return result;
}
catch (Exception ex)
{
throw new ServiceException($"计算交易{trade.TradeNumber}估值报错,{ex.Message}", ex);
}
}
/// <summary>
/// 计算保本雪球的年化期权费率
/// </summary>
public static double CalculateSnowballAnnualPremium(
string userId,
underlying_manager underlying,
trade trade,
double[] vols,
double[] spotPrices = null,
bool preciseTimeMode = false,
string discountCurveName = null,
int maturityShift = 0,
double timeToMaturityDays = double.NaN,
string engineName = null,
bool quadratureFastMode = false)
{
if (spotPrices is null)
{
throw new ArgumentNullException(nameof(spotPrices));
}
if (trade.TTMDays != null && timeToMaturityDays.Equals(double.NaN))
{
timeToMaturityDays = trade.TTMDays.Value;
}
//股票默认到期日为行权日
if (underlying.UnderlyingInstrumentType == "Stock" && trade.ExerciseDate != null)
{
trade.MaturityDate = trade.ExerciseDate;
}
var variety = DataCacheProvider.GetVarietyDataSource().GetData(underlying.UnderlyingTypeId);
var parameter = ParameterHelper.CreatePricingParameter(
underlying,
variety,
trade,
vols,
spotPrices,
null,
preciseTimeMode,
null,
discountCurveName,
timeToMaturityDays,
engineName);
parameter.maturityShift = maturityShift;
return CalculateSnowballAnnualPremium(userId, trade, underlying, parameter as SnowballParameter, quadratureFastMode);
}
private static double CalculateSnowballAnnualPremium(string userId, trade trade, underlying_manager underlying, SnowballParameter parameter, bool quadratureFastMode)
{
var snowball = trade.trade_snowball;
snowball.AnnualizedPremiumRate = null;
var buySidePv = CalculateSnowball(userId, trade, underlying, parameter, PricingRequest.Pv, quadratureFastMode: quadratureFastMode).Pv;
buySidePv /= trade.Notional;
buySidePv = Math.Abs(buySidePv);
var sellSideProduct = QdpTradeBuilder.GetSnowballOptionTrade(new SnowballOptionTradeParam
{
tradeId = null,
volSurfaceNames = null,
tradeDate = parameter.ValueDate,
underlyingTickers = new[] { underlying.UnderlyingCode },
underlyingInstrumentType = underlying.UnderlyingInstrumentType,
startDate = trade.StartDate.Value,
endDate = trade.ExerciseDate.Value,
initialSpotPrice = trade.SpotPrice ?? 0.0,
isMoneynessOption = trade.IsMoneynessOptionData,
koBarrier = snowball.KOBarrier,
kiBarrier = snowball.KIBarrier,
koObservationDateStr = snowball.KOObservationDates,
couponPaymentDateStr = snowball.CouponUsePaymentDate == true ? snowball.KOObservationSettleDates : null,
observationDateStr = snowball.ObservationDates,
coupon = snowball.Coupon,
participationRate = trade.ParticipationRate ?? 1.0,
principalRate = trade.PrincipalRate ?? 0.0,
notional = 1.0,
buysell = "Buy",
exerciseDate = trade.ExerciseDate.Value,
annualizedFactor = snowball.AnnualizeFactor2 ?? 1.0,
callput = QdpConverter.ConvertOptionType(trade.CallPut),
strike = trade.Strike ?? 0.0,
spreadStrikeAtKO = -1,
spreadStrikeAtMaturity = -1,
isFixedCoupon = false,
koRebate = double.NaN,
koBarrierAdjustStep = snowball.KOBarrierAdjustStep,
//敲出转期权
useOptionPayoffAtKO = false,
//未敲出转期权
useOptionPayoffAtMaturity = false,
annualizedOptionPayoff = snowball.IsAnnualized2,
couponDayCount = snowball.CouponDayCount,
timeToMaturityDays = parameter.OverrideTTM ?? double.NaN,
riskFreeRate = parameter.RiskFreeRate ?? double.NaN,
dividendRate = parameter.DividendRate ?? double.NaN,
}, SnowBallOptionMode.SimpleSnowball);
var option = sellSideProduct.Instrument as SimpleSnowball;
var engine = new QuadAutoCallEngine();
return engine.CalcSnowballCouponRate(option, parameter.SpotPrices.Values.First(), parameter.RiskFreeRate.Value, parameter.DividendRate.Value, parameter.Volatility.Value, hasNkoNkiRebate: true, targetPv: buySidePv);
}
/// <summary>
/// 反算雪球票息
/// </summary>
public static double CalculateSnowballKORebateV2(
OtcTradeBase trade,
trade_snowball snowball,
DateTime valueDate,
double[] vols,
double[] spotPrices = null,
bool preciseTimeMode = false,
double timeToMaturityDays = double.NaN,
double initialMarginRate = 0.0)
{
if (spotPrices is null || !spotPrices.Any())
{
throw new ArgumentException("不能为空", nameof(spotPrices));
}
if (vols is null || !vols.Any())
{
throw new ArgumentException("不能为空", nameof(vols));
}
//股票默认到期日为行权日
if (ConsGlobal.InstrumentType.CalcTypeIsStock(trade.UnderlyingInstrumentType) && trade.ExerciseDate != null)
{
trade.MaturityDate = trade.ExerciseDate;
}
var targetPv = trade.TradeSinglePrice ?? 0.0;
snowball.AnnualizedPremiumRate = null;
var para = QdpTradeBuilder.GetSnowballTradeParam(trade, snowball, new OptionTradeParamRequest(valuedateBLL.SysRiskFreeRate()));
para.tradeId = null;
para.volSurfaceNames = null;
para.annualizedPremiumRate = null;
para.preciseTimeMode = preciseTimeMode;
para.tradeDate = valueDate;
para.notional = 1.0;
para.buysell = "Buy";
para.spreadStrikeAtKO = -1;
para.koRebate = double.NaN;
//敲出转期权
para.useOptionPayoffAtKO = false;
//未敲出转期权
para.timeToMaturityDays = timeToMaturityDays;
para.barrierStatus = null;
var optionTrade = QdpTradeBuilder.GetSnowballOptionTrade(para, SnowBallOptionMode.SimpleSnowball);
var option = optionTrade.Instrument as SimpleSnowball;
var engine = new QuadAutoCallEngine();
if (initialMarginRate <= 0.0) //无预付预付金,按一笔正常雪球反算票息
{
return engine.CalcSnowballCouponRate(
option,
spotPrices.First(),
para.riskFreeRate,
para.dividendRate,
vols[0],
hasNkoNkiRebate: snowball.Coupon > 0,
targetPv: targetPv);
}
else //有预付预付金
{
//计算方法:
//将有预付预付金的雪球看作两笔雪球A和B的组合,A是没有预付预付金的原雪球,B是与A期限、敲出观察、敲出障碍都一样的一笔保本雪球
//1. 如果预付预付金比例为100%,则B的票息就是无风险利率,直接计算pv
//2. 如果预付预付金比例小于100%(假设30%),则应该计算名义本金为30、票息为无风险利率的保本雪球pv
//
//最后用A和B的pv之和反算A的票息,即为所得结果
var paraB = QdpTradeBuilder.GetSnowballTradeParam(trade, snowball, new OptionTradeParamRequest(valuedateBLL.SysRiskFreeRate()));
paraB.notional = initialMarginRate; //部分预付金
paraB.useOptionPayoffAtMaturity = false; //保本雪球
paraB.koRebate = para.riskFreeRate;
paraB.coupon = para.riskFreeRate;
var optionTradeB = QdpTradeBuilder.GetSnowballOptionTrade(paraB, SnowBallOptionMode.SimpleSnowball);
var optionB = optionTradeB.Instrument as SimpleSnowball;
var pvB = engine.PriceSnowball(optionB, new Date(valueDate), spotPrices.First(), vols[0], paraB.dividendRate, paraB.riskFreeRate);
//因为A的pv为0,用B的pv反算A的票息
var koRebateA = engine.CalcSnowballCouponRate(
option,
spotPrices.First(),
para.riskFreeRate,
para.dividendRate,
vols[0],
hasNkoNkiRebate: snowball.Coupon > 0,
targetPv: pvB);
return koRebateA;
}
}
/// <summary>
/// 先保留这个方法,不一定适用于客户真实情况
/// 这个方法是假设带初始预付金的雪球的初始pv也为0
/// </summary>
private static double CalculateSnowballCouponRateWithInitialMargin(
SnowballOptionTradeParam para,
OtcTradeBase trade,
trade_snowball snowball,
SimpleSnowball option,
DateTime valueDate,
double[] vols,
double[] spotPrices = null,
double initialMarginRate = 0.0)
{
var engine = new QuadAutoCallEngine();
double koRebateB = 0.0, pvB = 0.0;
if (initialMarginRate == 1.0)
{
koRebateB = para.riskFreeRate;
var paraB = QdpTradeBuilder.GetSnowballTradeParam(trade, snowball, new OptionTradeParamRequest(valuedateBLL.SysRiskFreeRate()));
paraB.notional = 1.0;
paraB.useOptionPayoffAtMaturity = false; //保本雪球
paraB.koRebate = koRebateB;
paraB.coupon = koRebateB;
var optionTradeB = QdpTradeBuilder.GetSnowballOptionTrade(paraB, SnowBallOptionMode.SimpleSnowball);
var optionB = optionTradeB.Instrument as SimpleSnowball;
pvB = engine.PriceSnowball(optionB, new Date(valueDate), spotPrices.First(), vols[0], paraB.dividendRate, paraB.riskFreeRate);
}
else
{
var paraB = QdpTradeBuilder.GetSnowballTradeParam(trade, snowball, new OptionTradeParamRequest(valuedateBLL.SysRiskFreeRate()));
paraB.notional = initialMarginRate; //部分预付金
paraB.useOptionPayoffAtMaturity = false; //保本雪球
paraB.koRebate = para.riskFreeRate;
paraB.coupon = para.riskFreeRate;
var optionTradeB = QdpTradeBuilder.GetSnowballOptionTrade(paraB, SnowBallOptionMode.SimpleSnowball);
var optionB = optionTradeB.Instrument as SimpleSnowball;
pvB = engine.PriceSnowball(optionB, new Date(valueDate), spotPrices.First(), vols[0], paraB.dividendRate, paraB.riskFreeRate);
//再按全名义本金计算
paraB.notional = 1.0;
optionTradeB = QdpTradeBuilder.GetSnowballOptionTrade(paraB, SnowBallOptionMode.SimpleSnowball);
optionB = optionTradeB.Instrument as SimpleSnowball;
koRebateB = engine.CalcSnowballCouponRate(
optionB,
spotPrices.First(),
para.riskFreeRate,
para.dividendRate,
vols[0],
hasNkoNkiRebate: snowball.Coupon > 0,
targetPv: pvB);
}
//使A+B为0,用-pvB反算A的票息,再相加
var koRebateA = engine.CalcSnowballCouponRate(
option,
spotPrices.First(),
para.riskFreeRate,
para.dividendRate,
vols[0],
hasNkoNkiRebate: snowball.Coupon > 0,
targetPv: -pvB);
return koRebateA + koRebateB;
}
public static double CalculatePhoenixCouponRate(
underlying_manager underlying,
trade trade,
double[] vols,
double[] spotPrices = null,
bool preciseTimeMode = false,
string discountCurveName = null,
int maturityShift = 0,
double timeToMaturityDays = double.NaN,
string engineName = null)
{
if (spotPrices is null)
{
throw new ArgumentNullException(nameof(spotPrices));
}
if (trade.TTMDays != null && timeToMaturityDays.Equals(double.NaN))
{
timeToMaturityDays = trade.TTMDays.Value;
}
//股票默认到期日为行权日
if (underlying.UnderlyingInstrumentType == "Stock" && trade.ExerciseDate != null)
{
trade.MaturityDate = trade.ExerciseDate;
}
var variety = DataCacheProvider.GetVarietyDataSource().GetData(underlying.UnderlyingTypeId);
var parameter = ParameterHelper.CreatePricingParameter(
underlying,
variety,
trade,
vols,
spotPrices,
null,
preciseTimeMode,
null,
discountCurveName,
timeToMaturityDays,
engineName);
parameter.maturityShift = maturityShift;
return CalculatePhoenixCouponRate(trade, underlying, parameter as AutocallParameter);
}
private static double CalculatePhoenixCouponRate(trade trade, underlying_manager underlying, AutocallParameter parameter)
{
var targetPv = trade.TradeSinglePrice ?? 0.0;
var phoenix = trade.trade_autocall;
var optionTrade = QdpTradeBuilder.GetAutocallOptionTrade(new AutocallOptionTradeParam
{
tradeId = null,
volSurfaceNames = null,
tradeDate = parameter.ValueDate,
underlyingTickers = new[] { underlying.UnderlyingCode },
underlyingInstrumentType = underlying.UnderlyingInstrumentType,
startDate = trade.StartDate.Value,
endDate = trade.ExerciseDate.Value,
initialSpotPrice = trade.SpotPrice ?? 0.0,
isMoneynessOption = trade.IsMoneynessOptionData,
couponBarrier = phoenix.CouponBarrier,
koBarrier = phoenix.KOBarrier,
kiBarrier = phoenix.KIBarrier,
koObservationDateStr = phoenix.KOObservationDates,
observationDateStr = phoenix.ObservationDates,
coupon = phoenix.Coupon > 0 ? phoenix.Coupon : double.NaN,
participationRate = trade.ParticipationRate ?? 1.0,
principalRate = trade.PrincipalRate ?? 0.0,
notional = 1.0,
buysell = "Buy",
exerciseDate = trade.ExerciseDate.Value,
annualizedFactor = phoenix.AnnualizeFactor2 ?? 1.0,
callput = QdpConverter.ConvertCallPut(trade.CallPut),
kiOptionType = QdpConverter.ConvertOptionType(phoenix.KIPayoffType).ToString(),
strike = trade.Strike ?? 0.0,
spreadStrike = phoenix.SpreadStrike ?? -1,
isFixedCoupon = phoenix.IsFixedCoupon,
couponPayAtMaturity = phoenix.CouponPayType != CouponPayTypeEnum.AtCreated,
includeCouponAfterKI = phoenix.IncludeCouponAfterKI,
annualizedOptionPayoff = phoenix.IsAnnualized2,
couponDayCount = phoenix.CouponDayCount,
timeToMaturityDays = parameter.OverrideTTM ?? double.NaN,
riskFreeRate = parameter.RiskFreeRate ?? double.NaN,
dividendRate = parameter.DividendRate ?? double.NaN,
});
var option = optionTrade.Instrument as AutoCall;
var engine = new QuadAutoCallEngine();
return engine.CalcPhoenixCouponRate(
option,
parameter.SpotPrices.Values.First(),
parameter.RiskFreeRate.Value,
parameter.DividendRate.Value,
parameter.Volatility.Value,
targetPv: targetPv);
}
private static OptionTradeParamRequest GetOptionTradeParamRequest(ParameterBase parameter, params string[] volSurfaceNames)
{
return new OptionTradeParamRequest(valuedateBLL.SysRiskFreeRate())
{
volSurfaceNames = volSurfaceNames,
dividends = parameter.Dividends,
hasNightMarket = parameter.HasNightMarket,
preciseTimeMode = parameter.PreciseTimeMode,
timeToMaturityDays = parameter.OverrideTTM ?? double.NaN,
maturityShift = parameter.maturityShift,
ParamOverride = p =>
{
//这段逻辑应该不需要,先注释掉,OTC-13391
//p.tradeDate = valueDateStr;
//p.startDate = valueDateStr;
p.riskFreeRate = parameter.RiskFreeRate ?? double.NaN;
},
tradeId = null,
fixings = null,
};
}
/// <summary>
/// 计算香草期权
/// </summary>
private static TradeValueResult CalculateVanillaOption(
string userId,
trade trade,
underlying_manager underlying,
VanillaOptionParameter parameter,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS,
CalcScenarioEnum calcScenario = CalcScenarioEnum.Default)
{
var valueDateStr = parameter.ValueDate.ToString("yyyy-MM-dd");
var marketProxy = CreateMarketProxy(userId, valueDateStr);
var marketObjectName = PrepareOptionMarketObjects(marketProxy, valueDateStr, trade, underlying, parameter);
var valuationParameter = CreateQdpOptionValuationParameters(
underlying.UnderlyingInstrumentType, marketObjectName,
underlying.UnderlyingCode, parameter.IsForwardTrade);
var optionTrade = QdpTradeBuilder.GetVanillaOptionTrade(trade, GetOptionTradeParamRequest(parameter, marketObjectName.VolSurfaceName), false);
// 添加计算日志
AddCalculationLog(optionTrade, calcScenario, trade.TradeNumber);
var option = optionTrade.Instrument as VanillaOption;
var factory = OptionEngineFactory.GetEngineFactory("VanillaOption");
var engine = factory.GetEngine(parameter.EngineName, option.Exercise);
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDateStr), pricingRequest, engine, valuationParameter);
CleanupMarketObjects(parameter, marketProxy, valueDateStr, marketObjectName);
return ConvertValueResult(results, trade, underlying, parameter.SpotPrices[underlying.UnderlyingCode]);
}
/// <summary>
/// 计算Risky期权
/// </summary>
private static TradeValueResult CalculateRiskyOption(
string userId,
trade trade,
underlying_manager underlying,
VanillaOptionParameter parameter,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS,
CalcScenarioEnum calcScenario = CalcScenarioEnum.Default)
{
TradeValueResult result = new TradeValueResult();
var tradeclone = trade.Clone();
tradeclone.TradeAmount = tradeclone.TradeAmount = TradeCalcHelper.GetTradeAmountV(trade, trade.TradeAmount, 1);
tradeclone.Notional = tradeclone.Notional = TradeCalcHelper.GetTradeAmountV(trade, trade.Notional, underlying.CountRatio);
var td1 = tradeclone.Clone();
var td2 = tradeclone.Clone();
if (trade.trade_risky_option.ParticipationRate2 != 0)
{
td2.Strike = trade.trade_risky_option.Strike2;
td2.ParticipationRate = trade.trade_risky_option.ParticipationRate2;
td2.TradeAmount = TradeCalcHelper.GetTradeAmount(td2, td2.TradeAmount, 1);
td2.Notional = TradeCalcHelper.GetTradeAmount(td2, td2.Notional, underlying.CountRatio);
result = CalculateVanillaOption(userId, td2, underlying, parameter, pricingRequest, calcScenario);
}
if (trade.trade_risky_option.ParticipationRate1 != 0)
{
td1.Strike = trade.trade_risky_option.Strike1;
td1.ParticipationRate = trade.trade_risky_option.ParticipationRate1;
td1.TradeAmount = TradeCalcHelper.GetTradeAmount(td1, td1.TradeAmount, 1);
td1.Notional = TradeCalcHelper.GetTradeAmount(td1, td1.Notional, underlying.CountRatio);
td1.OptionType = "看跌";
var singleresult1 = CalculateVanillaOption(userId, td1, underlying, parameter, pricingRequest, calcScenario);
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.Vol = singleresult1.Vol;
}
var td3 = tradeclone.Clone();
td3.Strike = trade.trade_risky_option.Strike3;
//decimal 为了解决精度问题: 0.2-0.3=0.0999999999
var participationRate3 = (decimal)trade.trade_risky_option.ParticipationRate3 - (decimal)trade.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, underlying.CountRatio);
var singleresult3 = CalculateVanillaOption(userId, td3, underlying, parameter, pricingRequest, calcScenario);
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.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.Vol = singleresult3.Vol;
}
}
return result;
}
/// <summary>
/// 计算障碍期权
/// </summary>
private static TradeValueResult CalculateBarrierOption(
string userId,
trade trade,
underlying_manager underlying,
BarrierOptionParameter parameter,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS,
CalcScenarioEnum calcScenario = CalcScenarioEnum.Default,
bool quadratureFastMode = false)
{
var valueDateStr = parameter.ValueDate.ToString("yyyy-MM-dd");
var marketProxy = CreateMarketProxy(userId, valueDateStr);
var marketObjectName = PrepareOptionMarketObjects(marketProxy, valueDateStr, trade, underlying, parameter);
var valuationParameter = CreateQdpOptionValuationParameters(underlying.UnderlyingInstrumentType, marketObjectName, underlying.UnderlyingCode);
//如果用户自定义的观察日就是每天观察,则忽略自定义观察日
var observationDateStr = trade.trade_barrier_option.ObservationDates;
var observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(new Date(trade.StartDate.Value), new Date(trade.ExerciseDate.Value)).Union(new[] { new Date(trade.ExerciseDate.Value) }).ToArray();
var barrierObservationDates = QdpHelper.ParseObservationDate(observationDateStr);
if (barrierObservationDates != null && (barrierObservationDates.Count(d => d >= observationDates.First() && d <= observationDates.Last()) == observationDates.Length))
{
observationDateStr = null;
}
var tpReq = GetOptionTradeParamRequest(parameter, marketObjectName.VolSurfaceName);
var tParam = QdpTradeBuilder.GetBarrierOptionTradeParam(trade, trade.trade_barrier_option, tpReq);
tParam.observationDateStr = observationDateStr;
var optionTrade = QdpTradeBuilder.GetBarrierOptionTrade(tParam, valueDateStr);
// 添加计算日志
AddCalculationLog(optionTrade, calcScenario, trade.TradeNumber);
var option = optionTrade.Instrument as BarrierOption;
var factory = OptionEngineFactory.GetEngineFactory("BarrierOption");
var engine = factory.GetEngine(
parameter.EngineName,
option.Exercise,
option,
!string.IsNullOrWhiteSpace(observationDateStr),
quadratureFastMode);
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDateStr), pricingRequest, engine, valuationParameter);
CleanupMarketObjects(parameter, marketProxy, valueDateStr, marketObjectName);
return ConvertValueResult(results, trade, underlying, parameter.SpotPrices[underlying.UnderlyingCode]);
}
/// <summary>
/// 计算亚式期权
/// </summary>
private static TradeValueResult CalculateAsianOption(
string userId,
trade trade,
underlying_manager underlying,
AsianOptionParameter parameter,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS,
CalcScenarioEnum calcScenario = CalcScenarioEnum.Default)
{
var valueDateStr = parameter.ValueDate.ToString("yyyy-MM-dd");
var marketProxy = CreateMarketProxy(userId, valueDateStr);
var marketObjectName = PrepareOptionMarketObjects(marketProxy, valueDateStr, trade, underlying, parameter);
var valuationParameter = CreateQdpOptionValuationParameters(
underlying.UnderlyingInstrumentType,
marketObjectName,
underlying.UnderlyingCode);
trade.TradeDate = trade.StartDate = parameter.ValueDate;
var tpReq = GetOptionTradeParamRequest(parameter, marketObjectName.VolSurfaceName);
tpReq.fixings = AsianOptionFixingService.CheckAsiaFixings(trade, trade.trade_asian_option, parameter.Fixings, parameter.SpotPrices[underlying.UnderlyingCode]);
var optionTrade = QdpTradeBuilder.GetAsianOptionTrade(trade, trade.trade_asian_option, tpReq);
// 添加计算日志
AddCalculationLog(optionTrade, calcScenario, trade.TradeNumber);
var option = optionTrade.Instrument as AsianOption;
var factory = OptionEngineFactory.GetEngineFactory("AsianOption");
var engine = factory.GetEngine(parameter.EngineName, option.Exercise, trade.trade_asian_option.PayoffType, option, trade.trade_asian_option.StrikeType);
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDateStr), pricingRequest, engine, valuationParameter);
CleanupMarketObjects(parameter, marketProxy, valueDateStr, marketObjectName);
return ConvertValueResult(results, trade, underlying, parameter.SpotPrices[underlying.UnderlyingCode]);
}
/// <summary>
/// 计算二元期权
/// </summary>
private static TradeValueResult CalculateBinaryOption(
string userId,
trade trade,
underlying_manager underlying,
BinaryOptionParameter parameter,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS,
CalcScenarioEnum calcScenario = CalcScenarioEnum.Default)
{
var valueDateStr = parameter.ValueDate.ToString("yyyy-MM-dd");
var marketProxy = CreateMarketProxy(userId, valueDateStr);
var marketObjectName = PrepareOptionMarketObjects(marketProxy, valueDateStr, trade, underlying, parameter);
var valuationParameter = CreateQdpOptionValuationParameters(
underlying.UnderlyingInstrumentType,
marketObjectName,
underlying.UnderlyingCode);
var tpReq = GetOptionTradeParamRequest(parameter, marketObjectName.VolSurfaceName);
var optionTrade = QdpTradeBuilder.GetBinaryOptionTrade(trade, trade.trade_binary_option, tpReq);
var option = optionTrade.Instrument as BinaryOption;
// 添加计算日志
AddCalculationLog(optionTrade, calcScenario, trade.TradeNumber);
//如果是美式二元,则要判断是否有自定义观察日
//如果用户自定义的观察日就是每天观察,则忽略自定义观察日
string observationDateStr = null;
if (option.Exercise == OptionExercise.American)
{
observationDateStr = trade.trade_binary_option.ObservationDates;
var observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(new Date(trade.StartDate.Value), new Date(trade.ExerciseDate.Value)).Union(new[] { new Date(trade.ExerciseDate.Value) }).ToArray();
var barrierObservationDates = QdpHelper.ParseObservationDate(observationDateStr);
if (barrierObservationDates != null && (barrierObservationDates.Count(d => d >= observationDates.First() && d <= observationDates.Last()) == observationDates.Length))
{
observationDateStr = null;
}
}
var factory = OptionEngineFactory.GetEngineFactory("BinaryOption");
var engine = factory.GetEngine(
parameter.EngineName,
option.Exercise,
(parameter.ReplicationShiftSize <= 0.0) ? option.Strike * 0.01 : parameter.ReplicationShiftSize,
QdpConverter.ConvertReplicationStrategy(parameter.BinaryReplicationStrategy),
!string.IsNullOrWhiteSpace(observationDateStr),
option.RebateAnnualizedAtKO);
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDateStr), pricingRequest, engine, valuationParameter);
CleanupMarketObjects(parameter, marketProxy, valueDateStr, marketObjectName);
return ConvertValueResult(results, trade, underlying, parameter.SpotPrices[underlying.UnderlyingCode]);
}
/// <summary>
/// 计算彩虹期权
/// </summary>
private static TradeValueResult CalculateRainbowOption(
string userId,
trade trade,
underlying_manager underlying,
RainbowOptionParameter parameter,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS,
CalcScenarioEnum calcScenario = CalcScenarioEnum.Default)
{
var valueDateStr = parameter.ValueDate.ToString("yyyy-MM-dd");
var marketProxy = CreateMarketProxy(userId, valueDateStr);
var marketObjectName = PrepareRainbowOptionMarketObjects(marketProxy, valueDateStr, trade, underlying, parameter);
var valuationParameter = CreateQdpRainbowOptionValuationParameters(
underlying.UnderlyingInstrumentType,
marketObjectName,
trade.trade_rainbow_option.UnderlyingCodes);
var tpReq = GetOptionTradeParamRequest(parameter, marketObjectName.VolSurfaceNames);
var optionTrade = QdpTradeBuilder.GetRainbowOptionTrade(trade, trade.trade_rainbow_option, tpReq);
// 添加计算日志
AddCalculationLog(optionTrade, calcScenario, trade.TradeNumber);
var option = optionTrade.Instrument as RainbowOption;
var factory = OptionEngineFactory.GetEngineFactory("RainbowOption");
var engine = factory.GetEngine(parameter.EngineName, option.Exercise);
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDateStr), pricingRequest, engine, valuationParameter);
CleanupRainbowOptionMarketObjects(parameter, marketProxy, valueDateStr, marketObjectName);
return ConvertRainbowOptionValueResult(results, trade);
}
/// <summary>
/// 计算价差期权
/// </summary>
private static TradeValueResult CalculateSpreadOption(
string userId,
trade trade,
underlying_manager underlying,
SpreadOptionParameter parameter,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS,
CalcScenarioEnum calcScenario = CalcScenarioEnum.Default)
{
var valueDateStr = parameter.ValueDate.ToString("yyyy-MM-dd");
var marketProxy = CreateMarketProxy(userId, valueDateStr);
var marketObjectName = PrepareSpreadOptionMarketObjects(marketProxy, valueDateStr, trade, underlying, parameter);
var valuationParameter = CreateQdpSpreadOptionValuationParameters(
trade.trade_spread_option.UnderlyingAssetCodes(),
marketObjectName.DiscountCurveName,
marketObjectName.VolSurfaceNames);
var tpReq = GetOptionTradeParamRequest(parameter, marketObjectName.VolSurfaceNames);
var optionTrade = QdpTradeBuilder.GetSpreadOptionTrade(trade, trade.trade_spread_option, tpReq, correlations: parameter.Correlations);
// 添加计算日志
AddCalculationLog(optionTrade, calcScenario, trade.TradeNumber);
var option = optionTrade.Instrument as SpreadOption;
var factory = OptionEngineFactory.GetEngineFactory("SpreadOption");
var engine = factory.GetEngine(parameter.EngineName, option.Exercise);
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDateStr), pricingRequest, engine, valuationParameter);
CleanupSpreadOptionMarketObjects(parameter, marketProxy, valueDateStr, marketObjectName);
return ConvertSpreadOptionValueResult(results, trade);
}
/// <summary>
/// 计算合成价差期权
/// </summary>
private static TradeValueResult CalculateSyntheticNormalSpreadOption(
string userId,
trade trade,
underlying_manager underlying,
SyntheticNormalSpreadOptionParameter parameter,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS,
CalcScenarioEnum calcScenario = CalcScenarioEnum.Default)
{
var valueDateStr = parameter.ValueDate.ToString("yyyy-MM-dd");
var marketProxy = CreateMarketProxy(userId, valueDateStr);
var marketObjectName = PrepareOptionMarketObjects(marketProxy, valueDateStr, trade, underlying, parameter);
var valuationParameter = CreateQdpOptionValuationParameters(
underlying.UnderlyingInstrumentType,
marketObjectName,
underlying.UnderlyingCode,
parameter.IsForwardTrade);
var tpReq = GetOptionTradeParamRequest(parameter, marketObjectName.VolSurfaceName);
var optionTrade = QdpTradeBuilder.GetSSpreadOptionTrade(trade, tpReq);
// 添加计算日志
AddCalculationLog(optionTrade, calcScenario, trade.TradeNumber);
var option = optionTrade.Instrument as VanillaOption;
var factory = OptionEngineFactory.GetEngineFactory("SynthticNormalSpreadOption");
var engine = factory.GetEngine(parameter.EngineName, option.Exercise);
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDateStr), pricingRequest, engine, valuationParameter);
CleanupMarketObjects(parameter, marketProxy, valueDateStr, marketObjectName);
return ConvertValueResult(results, trade, underlying, parameter.SpotPrices[underlying.UnderlyingCode]);
}
/// <summary>
/// 计算亚式合成价差期权
/// </summary>
private static TradeValueResult CalculateAsianSyntheticNormalSpreadOption(
string userId,
trade trade,
underlying_manager underlying,
AsianSyntheticNormalSpreadOptionParameter parameter,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS,
CalcScenarioEnum calcScenario = CalcScenarioEnum.Default)
{
var valueDateStr = parameter.ValueDate.ToString("yyyy-MM-dd");
var marketProxy = CreateMarketProxy(userId, valueDateStr);
var marketObjectName = PrepareOptionMarketObjects(marketProxy, valueDateStr, trade, underlying, parameter);
var valuationParameter = CreateQdpOptionValuationParameters(
underlying.UnderlyingInstrumentType,
marketObjectName,
underlying.UnderlyingCode);
trade.TradeDate = trade.StartDate = parameter.ValueDate;
var tpReq = GetOptionTradeParamRequest(parameter, marketObjectName.VolSurfaceName);
tpReq.fixings = parameter.Fixings;
var optionTrade = QdpTradeBuilder.GetAsianSSpreadOptionTrade(trade, trade.trade_asian_option, tpReq);
// 添加计算日志
AddCalculationLog(optionTrade, calcScenario, trade.TradeNumber);
var engine = new AnalyticalAsianSyntheticNormalSpreadOptionEngine();
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDateStr), pricingRequest, engine, valuationParameter);
CleanupMarketObjects(parameter, marketProxy, valueDateStr, marketObjectName);
return ConvertValueResult(results, trade, underlying, parameter.SpotPrices[underlying.UnderlyingCode]);
}
/// <summary>
/// 计算双鲨期权
/// </summary>
private static TradeValueResult CalculateDoubleSharkFinOption(
string userId,
trade trade,
underlying_manager underlying,
DoubleSharkFinOptionParameter parameter,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS,
CalcScenarioEnum calcScenario = CalcScenarioEnum.Default,
bool quadratureFastMode = false)
{
var valueDateStr = parameter.ValueDate.ToString("yyyy-MM-dd");
var marketProxy = CreateMarketProxy(userId, valueDateStr);
var marketObjectName = PrepareOptionMarketObjects(marketProxy, valueDateStr, trade, underlying, parameter);
var valuationParameter = CreateQdpOptionValuationParameters(
underlying.UnderlyingInstrumentType,
marketObjectName,
underlying.UnderlyingCode);
var observationDateStr = trade.trade_double_sharkfin_option.ObservationDates;
//如果用户自定义的观察日就是每天观察,则忽略自定义观察日
var observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(new Date(trade.StartDate.Value), new Date(trade.ExerciseDate.Value)).Union(new[] { new Date(trade.ExerciseDate.Value) }).ToArray();
var barrierObservationDates = QdpHelper.ParseObservationDate(observationDateStr);
if (barrierObservationDates != null &&
(barrierObservationDates.Count(d => d >= observationDates.First() && d <= observationDates.Last()) == observationDates.Length))
{
observationDateStr = null;
}
var tpReq = GetOptionTradeParamRequest(parameter, marketObjectName.VolSurfaceName);
var tParam = QdpTradeBuilder.GetDoubleSharkFinOptionTradeParam(trade, trade.trade_double_sharkfin_option, tpReq);
tParam.observationDateStr = observationDateStr;
var optionTrade = QdpTradeBuilder.GetDoubleSharkFinOptionTrade(tParam);
// 添加计算日志
AddCalculationLog(optionTrade, calcScenario, trade.TradeNumber);
var option = optionTrade.Instrument as DoubleSharkFinOption;
var factory = OptionEngineFactory.GetEngineFactory("DoubleSharkFinOption");
var engine = factory.GetEngine(
parameter.EngineName,
option.Exercise,
option,
false,
!string.IsNullOrWhiteSpace(observationDateStr),
quadratureFastMode);
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDateStr), pricingRequest, engine, valuationParameter);
CleanupMarketObjects(parameter, marketProxy, valueDateStr, marketObjectName);
return ConvertValueResult(results, trade, underlying, parameter.SpotPrices[underlying.UnderlyingCode]);
}
/// <summary>
/// 计算远期交易(买方角度)
/// </summary>
public static TradeValueResult CalculateForward(double strike, double spotPrice, double notional, string callPut)
{
var pv = 0.0;
if (callPut == "Call")
{
pv = (spotPrice - strike) * notional;
}
else if (callPut == "Put")
{
pv = (strike - spotPrice) * notional;
}
return new TradeValueResult
{
Pv = pv,
Delta = callPut == "Call" ? notional : -notional,
DeltaCash = callPut == "Call" ? spotPrice * notional : -spotPrice * notional
};
}
private static TradeValueResult CalculateSnowball(
string userId,
trade trade,
underlying_manager underlying,
SnowballParameter parameter,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS,
CalcScenarioEnum calcScenario = CalcScenarioEnum.Default,
bool quadratureFastMode = false)
{
var snowball = trade.trade_snowball;
// 年化权利金模式的雪球期权
if (snowball.AnnualizedPremiumRate.HasValue && snowball.AnnualizedPremiumRate != 0)
{
return CalculateAnnualPremiumSnowball(userId, trade, underlying, parameter, pricingRequest, quadratureFastMode: quadratureFastMode);
}
var valueDateStr = parameter.ValueDate.ToString("yyyy-MM-dd");
var marketProxy = CreateMarketProxy(userId, valueDateStr);
var marketObjectName = PrepareOptionMarketObjects(marketProxy, valueDateStr, trade, underlying, parameter);
var valuationParameter = CreateQdpOptionValuationParameters(
underlying.UnderlyingInstrumentType,
marketObjectName,
underlying.UnderlyingCode);
var optionTrade = QdpTradeBuilder.GetSnowballOptionTrade(new SnowballOptionTradeParam
{
preciseTimeMode = parameter.PreciseTimeMode,
tradeId = null,
volSurfaceNames = new[] { marketObjectName.VolSurfaceName },
tradeDate = trade.TradeDate.Value,
underlyingTickers = new[] { underlying.UnderlyingCode },
underlyingInstrumentType = underlying.UnderlyingInstrumentType,
startDate = trade.StartDate.Value,
endDate = trade.ExerciseDate.Value,
initialSpotPrice = trade.SpotPrice ?? 0.0,
isMoneynessOption = trade.IsMoneynessOptionData,
koBarrier = snowball.KOBarrier,
kiBarrier = snowball.KIBarrier,
koObservationDateStr = snowball.KOObservationDates,
couponPaymentDateStr = snowball.CouponUsePaymentDate == true ? snowball.KOObservationSettleDates : null,
observationDateStr = snowball.ObservationDates,
coupon = snowball.Coupon,
participationRate = trade.ParticipationRate ?? 1.0,
principalRate = trade.PrincipalRate ?? 0.0,
notional = trade.Notional,
buysell = trade.BuySell,
exerciseDate = trade.ExerciseDate.Value,
annualizedFactor = snowball.AnnualizeFactor2 ?? 1.0,
callput = QdpConverter.ConvertCallPut(trade.CallPut),
strike = trade.Strike ?? 0.0,
spreadStrikeAtKO = snowball.SpreadStrikeAtKO ?? -1,
spreadStrikeAtMaturity = snowball.SpreadStrikeAtMaturity ?? -1,
isFixedCoupon = snowball.IsFixedCoupon,
koRebate = snowball.KORebate,
koBarrierAdjustStep = snowball.KOBarrierAdjustStep,
//敲出转期权
useOptionPayoffAtKO = snowball.KOPayoffType != KOPayoffTypeEnum.Rebate,
//未敲出转期权
useOptionPayoffAtMaturity = snowball.KIPayoffType != KIPayoffTypeEnum.None,
annualizedOptionPayoff = snowball.IsAnnualized2,
couponDayCount = snowball.CouponDayCount,
timeToMaturityDays = parameter.OverrideTTM ?? double.NaN,
riskFreeRate = parameter.RiskFreeRate ?? double.NaN,
dividendRate = parameter.DividendRate ?? double.NaN,
koStrike = snowball.KOPayoffType != KOPayoffTypeEnum.Rebate
? snowball.SpreadStrikeAtKO1.Value : double.NaN,
koOptionType = QdpConverter.ConvertAutocallKOOptionType(snowball.KOPayoffType, QdpConverter.ConvertCallPut(trade.CallPut)),
kiOptionType = QdpConverter.ConvertOptionType(snowball.KIPayoffType),
barrierStatus = snowball.KnockInOutStatus,
hasNightMarket=parameter.HasNightMarket
}, SnowBallOptionMode.SimpleSnowball);
// 添加计算日志
AddCalculationLog(optionTrade, calcScenario, trade.TradeNumber);
var option = optionTrade.Instrument as SimpleSnowball;
var factory = OptionEngineFactory.GetEngineFactory("SnowBall");
var engine = factory.GetEngine(parameter.EngineName, option.Exercise, quadratureFastMode);
IPricingResult results;
if (!(engine is QuadAutoCallEngine) || parameter.ValueDate == trade.ExerciseDate)
{
results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDateStr), pricingRequest, engine, valuationParameter);
}
else
{
//普通雪球期权可以采用加速方法定价
var market = SnowballTrade.ConvertToMarketCondition(optionTrade.TradeId, marketProxy.GetQdpMarket(valueDateStr), valuationParameter);
var gridSnapshot = new QuadSnowballGridSnapshot(option, market, (QuadAutoCallEngine)engine);
results = gridSnapshot.Calculate(
market.SpotPrices.Value.Values.First(),
option.BarrierStatus == BarrierStatus.KnockedIn,
pricingRequest,
rebuildGrid: false);
}
CleanupMarketObjects(parameter, marketProxy, valueDateStr, marketObjectName);
return ConvertValueResult(results, trade, underlying, parameter.SpotPrices[underlying.UnderlyingCode]);
}
/// <summary>
/// 计算年化权利金模式的保本雪球
/// </summary>
private static TradeValueResult CalculateAnnualPremiumSnowball(
string userId,
trade trade,
underlying_manager underlying,
SnowballParameter parameter,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS,
CalcScenarioEnum calcScenario = CalcScenarioEnum.Default,
bool quadratureFastMode = false)
{
var valueDateStr = parameter.ValueDate.ToString("yyyy-MM-dd");
var marketProxy = CreateMarketProxy(userId, valueDateStr);
var marketObjectName = PrepareOptionMarketObjects(marketProxy, valueDateStr, trade, underlying, parameter);
var valuationParameter = CreateQdpOptionValuationParameters(
underlying.UnderlyingInstrumentType,
marketObjectName,
underlying.UnderlyingCode);
var snowball = trade.trade_snowball;
var optionTrade = QdpTradeBuilder.GetSnowballOptionTrade(new SnowballOptionTradeParam
{
preciseTimeMode = parameter.PreciseTimeMode,
tradeId = null,
volSurfaceNames = new[] { marketObjectName.VolSurfaceName },
tradeDate = trade.TradeDate.Value,
underlyingTickers = new[] { underlying.UnderlyingCode },
underlyingInstrumentType = underlying.UnderlyingInstrumentType,
startDate = trade.StartDate.Value,
endDate = trade.ExerciseDate.Value,
initialSpotPrice = trade.SpotPrice ?? 0.0,
isMoneynessOption = trade.IsMoneynessOptionData,
koBarrier = snowball.KOBarrier,
kiBarrier = double.NaN,
barrierStatus = snowball.KnockInOutStatus,
koObservationDateStr = snowball.KOObservationDates,
couponPaymentDateStr = snowball.CouponUsePaymentDate == true ? snowball.KOObservationSettleDates : null,
observationDateStr = snowball.ObservationDates,
coupon = snowball.Coupon,
participationRate = trade.ParticipationRate ?? 1.0,
principalRate = trade.PrincipalRate ?? 0.0,
notional = trade.Notional,
buysell = trade.BuySell,
exerciseDate = trade.ExerciseDate.Value,
annualizedFactor = snowball.AnnualizeFactor2 ?? 1.0,
callput = QdpConverter.ConvertCallPut(trade.CallPut),
strike = 0,
spreadStrikeAtKO = -1,
spreadStrikeAtMaturity = -1,
isFixedCoupon = snowball.IsFixedCoupon,
koRebate = snowball.KORebate,
koBarrierAdjustStep = snowball.KOBarrierAdjustStep,
useOptionPayoffAtKO = snowball.KOPayoffType != KOPayoffTypeEnum.Rebate,
useOptionPayoffAtMaturity = false,
annualizedOptionPayoff = snowball.IsAnnualized2,
couponDayCount = snowball.CouponDayCount,
timeToMaturityDays = parameter.OverrideTTM ?? double.NaN,
riskFreeRate = parameter.RiskFreeRate ?? double.NaN,
dividendRate = parameter.DividendRate ?? double.NaN,
}, SnowBallOptionMode.AnnualizedPremiumSnowball);
// 添加计算日志
AddCalculationLog(optionTrade, calcScenario, trade.TradeNumber);
var option = optionTrade.Instrument as AnnualizedPremiumSnowball;
option.AnnualizedPremium = snowball.AnnualizedPremiumRate.Value;
var factory = OptionEngineFactory.GetEngineFactory("SnowBall");
var engine = factory.GetEngine(parameter.EngineName, option.Exercise, quadratureFastMode) as QuadAutoCallEngine;
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDateStr), pricingRequest, engine, valuationParameter);
CleanupMarketObjects(parameter, marketProxy, valueDateStr, marketObjectName);
return ConvertValueResult(results, trade, underlying, parameter.SpotPrices[underlying.UnderlyingCode]);
}
private static TradeValueResult CalculateRangeAccrual(
string userId,
trade trade,
underlying_manager underlying,
RangeAccrualParameter parameter,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS,
CalcScenarioEnum calcScenario = CalcScenarioEnum.Default)
{
var valueDateStr = parameter.ValueDate.ToString("yyyy-MM-dd");
var marketProxy = CreateMarketProxy(userId, valueDateStr);
var marketObjectName = PrepareOptionMarketObjects(marketProxy, valueDateStr, trade, underlying, parameter);
var valuationParameter = CreateQdpOptionValuationParameters(
underlying.UnderlyingInstrumentType,
marketObjectName,
underlying.UnderlyingCode);
var tpReq = GetOptionTradeParamRequest(parameter, marketObjectName.VolSurfaceName);
tpReq.fixings = parameter.Fixings;
var optionTrade = QdpTradeBuilder.GetRangeAccrualTrade(trade, trade.trade_rangeaccrual, tpReq);
// 添加计算日志
AddCalculationLog(optionTrade, calcScenario, trade.TradeNumber);
var option = optionTrade.Instrument as RangeAccrual;
var factory = OptionEngineFactory.GetEngineFactory("RangeAccrual");
var engine = factory.GetEngine(parameter.EngineName, option.Exercise);
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDateStr), pricingRequest, engine, valuationParameter);
CleanupMarketObjects(parameter, marketProxy, valueDateStr, marketObjectName);
return ConvertValueResult(results, trade, underlying, parameter.SpotPrices[underlying.UnderlyingCode]);
}
private static TradeValueResult CalculateAirbag(
string userId,
trade trade,
underlying_manager underlying,
AirbagParameter parameter,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS,
CalcScenarioEnum calcScenario = CalcScenarioEnum.Default)
{
var valueDateStr = parameter.ValueDate.ToString("yyyy-MM-dd");
var marketProxy = CreateMarketProxy(userId, valueDateStr);
var marketObjectName = PrepareOptionMarketObjects(marketProxy, valueDateStr, trade, underlying, parameter);
var valuationParameter = CreateQdpOptionValuationParameters(
underlying.UnderlyingInstrumentType,
marketObjectName,
underlying.UnderlyingCode);
var tpReq = GetOptionTradeParamRequest(parameter, marketObjectName.VolSurfaceName);
var optionTrade = QdpTradeBuilder.GetAirbagOptionTrade(trade, trade.trade_airbag, tpReq);
// 添加计算日志
AddCalculationLog(optionTrade, calcScenario, trade.TradeNumber);
var engine = new AnalyticalAirbagReplicationEngine();
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDateStr), pricingRequest, engine, valuationParameter);
CleanupMarketObjects(parameter, marketProxy, valueDateStr, marketObjectName);
return ConvertValueResult(results, trade, underlying, parameter.SpotPrices[underlying.UnderlyingCode]);
}
private static TradeValueResult CalculateUnderlyingEnhance(
string userId,
trade trade,
underlying_manager underlying,
UnderlyingEnhanceParameter parameter,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS,
CalcScenarioEnum calcScenario = CalcScenarioEnum.Default)
{
var valueDateStr = parameter.ValueDate.ToString("yyyy-MM-dd");
var marketProxy = CreateMarketProxy(userId, valueDateStr);
var marketObjectName = PrepareOptionMarketObjects(marketProxy, valueDateStr, trade, underlying, parameter);
var valuationParameter = CreateQdpOptionValuationParameters(
underlying.UnderlyingInstrumentType,
marketObjectName,
underlying.UnderlyingCode);
var tpReq = GetOptionTradeParamRequest(parameter, marketObjectName.VolSurfaceName);
var optionTrade = QdpTradeBuilder.GetUnderlyingEnhanceTrade(trade, trade.trade_underlying_enhance, tpReq);
// 添加计算日志
AddCalculationLog(optionTrade, calcScenario, trade.TradeNumber);
var engine = new AnalyticalUnderlyingPayoffEnhanceEngine(calcAsOptions: true);
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDateStr), pricingRequest, engine, valuationParameter);
CleanupMarketObjects(parameter, marketProxy, valueDateStr, marketObjectName);
return ConvertValueResult(results, trade, underlying, parameter.SpotPrices[underlying.UnderlyingCode]);
}
private static TradeValueResult CalculateAutocall(
string userId,
trade trade,
underlying_manager underlying,
AutocallParameter parameter,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS,
CalcScenarioEnum calcScenario = CalcScenarioEnum.Default,
bool quadratureFastMode = false)
{
var valueDateStr = parameter.ValueDate.ToString("yyyy-MM-dd");
var marketProxy = CreateMarketProxy(userId, valueDateStr);
var marketObjectName = PrepareOptionMarketObjects(marketProxy, valueDateStr, trade, underlying, parameter);
var valuationParameter = CreateQdpOptionValuationParameters(
underlying.UnderlyingInstrumentType,
marketObjectName,
underlying.UnderlyingCode);
var tpReq = GetOptionTradeParamRequest(parameter, marketObjectName.VolSurfaceName);
var optionTrade = QdpTradeBuilder.GetAutocallOptionTrade(trade, trade.trade_autocall, tpReq);
// 添加计算日志
AddCalculationLog(optionTrade, calcScenario, trade.TradeNumber);
var option = optionTrade.Instrument as AutoCall;
var factory = OptionEngineFactory.GetEngineFactory("AutoCall");
var engine = factory.GetEngine(parameter.EngineName, option.Exercise, quadratureFastMode);
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDateStr), pricingRequest, engine, valuationParameter);
CleanupMarketObjects(parameter, marketProxy, valueDateStr, marketObjectName);
return ConvertValueResult(results, trade, underlying, parameter.SpotPrices[underlying.UnderlyingCode]);
}
private static TradeValueResult CalculateAccumulatorOption(
string userId,
trade trade,
underlying_manager underlying,
AccumulatorOptionParameter parameter,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS,
CalcScenarioEnum calcScenario = CalcScenarioEnum.Default)
{
var valueDateStr = parameter.ValueDate.ToString("yyyy-MM-dd");
var marketProxy = CreateMarketProxy(userId, valueDateStr);
var marketObjectName = PrepareOptionMarketObjects(marketProxy, valueDateStr, trade, underlying, parameter);
var valuationParameter = CreateQdpOptionValuationParameters(underlying.UnderlyingInstrumentType, marketObjectName, underlying.UnderlyingCode);
//如果用户自定义的观察日就是每天观察,则忽略自定义观察日
var observationDateStr = trade.trade_accumulator_option.KOObservationDates;
var observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(new Date(trade.StartDate.Value), new Date(trade.ExerciseDate.Value)).Union(new[] { new Date(trade.ExerciseDate.Value) }).ToArray();
var barrierObservationDates = QdpHelper.ParseObservationDate(observationDateStr);
if (barrierObservationDates != null && (barrierObservationDates.Count(d => d >= observationDates.First() && d <= observationDates.Last()) == observationDates.Length))
{
observationDateStr = null;
}
var tpReq = GetOptionTradeParamRequest(parameter, marketObjectName.VolSurfaceName);
var tParam = QdpTradeBuilder.GetAccumulatorOptionTradeParam(trade, trade.trade_accumulator_option, tpReq);
tParam.KOObservationDates = observationDateStr;
//当到期日和定价日一样,并且当前时间大于等于3点,ttm传0;qdp里面 ttm等于0和不等于0逻辑处理不一样。
if (trade.ExerciseDate == parameter.ValueDate && DateTime.Now.Hour >= 15)
{
tParam.timeToMaturityDays = 0;
}
var optionTrade = QdpTradeBuilder.GetAccumulatorOptionTrade(tParam);
// 添加计算日志
AddCalculationLog(optionTrade, calcScenario, trade.TradeNumber);
IEngine engine;
if (optionTrade is SegmentedAccumulatorOptionTrade)
{
engine = new AnalyticalSegmentedAccumulatorOptionEngine();
}
else
{
if (tParam.EarlyTerminate)
{
engine = new QuadAccumulatorOptionEngine();
}
else
{
engine = new AnalyticalAccumulatorOptionEngine();
}
}
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDateStr), pricingRequest, engine, valuationParameter);
CleanupMarketObjects(parameter, marketProxy, valueDateStr, marketObjectName);
return ConvertValueResult(results, trade, underlying, parameter.SpotPrices[underlying.UnderlyingCode]);
}
private static TradeValueResult CalculateCashFlow(
string userId,
trade trade,
underlying_manager underlying,
CashFlowTradeParameter parameter,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS,
CalcScenarioEnum calcScenario = CalcScenarioEnum.Default)
{
var valueDateStr = parameter.ValueDate.ToString("yyyy-MM-dd");
var marketProxy = CreateMarketProxy(userId, valueDateStr);
var marketObjectName = PrepareOptionMarketObjects(marketProxy, valueDateStr, trade, underlying, parameter);
var valuationParameter = new SimpleCfValuationParameters(
marketObjectName.DiscountCurveName,
marketObjectName.DiscountCurveName,
marketObjectName.DiscountCurveName);
var tpReq = GetOptionTradeParamRequest(parameter, marketObjectName.VolSurfaceName);
var tParam = QdpTradeBuilder.GetCashFlowTradeParam(trade, trade.trade_cashflow, tpReq);
var optionTrade = QdpTradeBuilder.GetCashFlowTrade(tParam, trade.StockEqvNotional);
// 添加计算日志
AddCalculationLog(optionTrade, calcScenario, trade.TradeNumber);
IEngine engine = new DepositEngine();
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDateStr), pricingRequest, engine, valuationParameter);
CleanupMarketObjects(parameter, marketProxy, valueDateStr, marketObjectName);
return ConvertValueResult(results, trade, underlying, 0);
}
/// <summary>
/// 构造MarketProxy
/// </summary>
/// <param name="userId"></param>
/// <param name="valueDateStr"></param>
/// <returns></returns>
private static IQdpMarketProxy CreateMarketProxy(string userId, string valueDateStr)
{
var marketProxy = QdpMarketManager.Instance.GetPrebuiltMarketProxy(userId);
marketProxy.CreateMarket(valueDateStr);
return marketProxy;
}
/// <summary>
/// 准备标的价格数据
/// </summary>
/// <param name="marketProxy"></param>
/// <param name="valueDateStr"></param>
/// <param name="prices"></param>
/// <returns></returns>
private static bool PrepareSpotPrices(IQdpMarketProxy marketProxy, string valueDateStr, Dictionary<string, double> prices)
{
if (prices == null || prices.Count == 0)
{
return false;
}
foreach (var kv in prices)
{
marketProxy.AddStockPrice(kv.Key, valueDateStr, kv.Value);
}
return true;
}
/// <summary>
/// 准备波动率曲面
/// </summary>
/// <param name="marketProxy"></param>
/// <param name="valueDateStr"></param>
/// <param name="volSurfaceName"></param>
/// <param name="overrideVol"></param>
/// <returns></returns>
private static string PrepareVolSurface(IQdpMarketProxy marketProxy, string valueDateStr, string volSurfaceName, double? overrideVol)
{
if (overrideVol.HasValue)
{
volSurfaceName = CreateVolSurfaceFromVolValue(marketProxy, valueDateStr, overrideVol.Value);
}
return volSurfaceName;
}
/// <summary>
/// 准备折现曲线
/// </summary>
/// <param name="marketProxy"></param>
/// <param name="valueDateStr"></param>
/// <param name="discountCurveName"></param>
/// <param name="riskFreeRate"></param>
/// <returns></returns>
private static string PrepareDiscountCurve(IQdpMarketProxy marketProxy, string valueDateStr, string discountCurveName, double? riskFreeRate)
{
if (riskFreeRate.HasValue)
{
discountCurveName = Guid.NewGuid().ToString();
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, riskFreeRate.Value);
marketProxy.AddYieldCurve(discountCurveName, valueDateStr, discountCurve);
}
return discountCurveName;
}
/// <summary>
/// 准备分红曲线
/// </summary>
/// <param name="marketProxy"></param>
/// <param name="valueDateStr"></param>
/// <param name="dividendRate"></param>
/// <returns></returns>
private static string PrepareDividendCurve(IQdpMarketProxy marketProxy, string valueDateStr, double? dividendRate)
{
if (dividendRate.HasValue)
{
var dividendCurveName = Guid.NewGuid().ToString();
var dividendCurve = CalculatorHelper.CreateConstantRiskFreeCurve(dividendCurveName, dividendRate.Value);
marketProxy.AddYieldCurve(dividendCurveName, valueDateStr, dividendCurve);
return dividendCurveName;
}
else
{
return null;
}
}
/// <summary>
/// 为期权计算准备市场数据
/// </summary>
/// <param name="marketProxy"></param>
/// <param name="valueDateStr"></param>
/// <param name="trade"></param>
/// <param name="underlying"></param>
/// <param name="parameter"></param>
/// <returns></returns>
private static OptionMarketObjectName PrepareOptionMarketObjects(
IQdpMarketProxy marketProxy,
string valueDateStr,
trade trade,
underlying_manager underlying,
BaseOptionParameter parameter)
{
if (!PrepareSpotPrices(marketProxy, valueDateStr, parameter.SpotPrices) && trade.TradeType != "现金流交易")
{
throw new Exception($"交易计算未设置标的价格: {trade.id} {trade.TradeNumber} {underlying.UnderlyingCode}");
}
var volSurfaceName = PrepareVolSurface(marketProxy, valueDateStr, parameter.VolSurfaceName, parameter.Volatility);
if (string.IsNullOrWhiteSpace(volSurfaceName) && trade.TradeType != "现金流交易")
{
throw new Exception($"交易计算未设置波动率: {trade.id} {trade.TradeNumber} {underlying.UnderlyingCode}");
}
var discountCurveName = PrepareDiscountCurve(marketProxy, valueDateStr, parameter.DiscountCurveName, parameter.RiskFreeRate);
if (string.IsNullOrWhiteSpace(discountCurveName))
{
throw new Exception($"交易计算未设置无风险利率: {trade.id} {trade.TradeNumber}");
}
string dividendCurveName = null;
if (underlying.UnderlyingInstrumentType == "Stock")
{
dividendCurveName = PrepareDividendCurve(marketProxy, valueDateStr, parameter.DividendRate ?? 0);
if (string.IsNullOrWhiteSpace(dividendCurveName))
{
throw new Exception($"交易计算未设置分红率: {trade.id} {trade.TradeNumber}");
}
}
return new OptionMarketObjectName()
{
DiscountCurveName = discountCurveName,
DividendCurveName = dividendCurveName,
VolSurfaceName = volSurfaceName
};
}
/// <summary>
/// 为彩虹期权准备市场数据
/// </summary>
private static RainbowOptionMarketObjectName PrepareRainbowOptionMarketObjects(
IQdpMarketProxy marketProxy,
string valueDateStr,
trade trade,
underlying_manager underlying,
RainbowOptionParameter parameter)
{
if (!PrepareSpotPrices(marketProxy, valueDateStr, parameter.SpotPrices))
{
throw new Exception($"交易计算未设置标的价格: {trade.id} {trade.TradeNumber} {underlying.UnderlyingCode}");
}
foreach (var code in trade.trade_rainbow_option.UnderlyingCodes)
{
if (!parameter.SpotPrices.ContainsKey(code))
{
throw new Exception($"交易计算未设置标的价格: {trade.id} {trade.TradeNumber} {code}");
}
}
var volSurfaceName1 = PrepareVolSurface(marketProxy, valueDateStr, parameter.VolSurfaceName, parameter.Volatility);
if (string.IsNullOrWhiteSpace(volSurfaceName1))
{
throw new Exception($"交易计算未设置波动率: {trade.id} {trade.TradeNumber} {trade.trade_rainbow_option.UnderlyingCodes[0]}");
}
var volSurfaceName2 = PrepareVolSurface(marketProxy, valueDateStr, parameter.VolSurfaceName2, parameter.Volatility2);
if (string.IsNullOrWhiteSpace(volSurfaceName2))
{
throw new Exception($"交易计算未设置波动率: {trade.id} {trade.TradeNumber} {trade.trade_rainbow_option.UnderlyingCodes[1]}");
}
var discountCurveName = PrepareDiscountCurve(marketProxy, valueDateStr, parameter.DiscountCurveName, parameter.RiskFreeRate);
if (string.IsNullOrWhiteSpace(discountCurveName))
{
throw new Exception($"交易计算未设置无风险利率: {trade.id} {trade.TradeNumber}");
}
string dividendCurveName = null;
if (underlying.UnderlyingInstrumentType == "Stock")
{
dividendCurveName = PrepareDividendCurve(marketProxy, valueDateStr, parameter.DividendRate ?? 0);
if (string.IsNullOrWhiteSpace(dividendCurveName))
{
throw new Exception($"交易计算未设置分红率: {trade.id} {trade.TradeNumber}");
}
}
marketProxy.SaveCorrelationSurface(valueDateStr, trade.trade_rainbow_option.UnderlyingCodes, parameter.Correlation ?? 0);
return new RainbowOptionMarketObjectName()
{
DiscountCurveName = discountCurveName,
DividendCurveName = dividendCurveName,
VolSurfaceNames = new string[] { volSurfaceName1, volSurfaceName2 }
};
}
private static SpreadOptionMarketObjectName PrepareSpreadOptionMarketObjects(
IQdpMarketProxy marketProxy,
string valueDateStr,
trade trade,
underlying_manager underlying,
SpreadOptionParameter parameter)
{
if (!PrepareSpotPrices(marketProxy, valueDateStr, parameter.SpotPrices))
{
throw new Exception($"交易计算未设置标的价格: {trade.id} {trade.TradeNumber} {underlying.UnderlyingCode}");
}
var underlyingCodes = trade.trade_spread_option.UnderlyingAssetCodes();
if (parameter.VolSurfaceNames == null && (parameter.Volatilities == null || parameter.Volatilities.Length != underlyingCodes.Length)
|| parameter.Volatilities == null && (parameter.VolSurfaceNames == null || parameter.VolSurfaceNames.Length != underlyingCodes.Length))
{
throw new Exception($"交易计算未设置波动率: {trade.id} {trade.TradeNumber}");
}
var volSurfaceNames = new List<string>();
for (var i = 0; i < underlyingCodes.Length; ++i)
{
if (!parameter.SpotPrices.ContainsKey(underlyingCodes[i]))
{
throw new Exception($"交易计算未设置标的价格: {trade.id} {trade.TradeNumber} {underlyingCodes[i]}");
}
var volSurfaceName =
PrepareVolSurface(
marketProxy,
valueDateStr,
(parameter.VolSurfaceNames != null && parameter.VolSurfaceNames.Length > i) ? parameter.VolSurfaceNames[i] : null,
(parameter.Volatilities != null && parameter.Volatilities.Length > i) ? parameter.Volatilities[i] : null);
volSurfaceNames.Add(volSurfaceName);
}
var discountCurveName = PrepareDiscountCurve(marketProxy, valueDateStr, parameter.DiscountCurveName, parameter.RiskFreeRate);
if (string.IsNullOrWhiteSpace(discountCurveName))
{
throw new Exception($"交易计算未设置无风险利率: {trade.id} {trade.TradeNumber}");
}
marketProxy.SaveCorrelationSurface(valueDateStr, new string[] { underlyingCodes[0], underlyingCodes[1] }, parameter.Correlations[0]);
if (underlyingCodes.Length >= 3)
{
marketProxy.SaveCorrelationSurface(valueDateStr, new string[] { underlyingCodes[0], underlyingCodes[2] }, parameter.Correlations[1]);
marketProxy.SaveCorrelationSurface(valueDateStr, new string[] { underlyingCodes[1], underlyingCodes[2] }, parameter.Correlations[2]);
if (underlyingCodes.Length >= 4)
{
marketProxy.SaveCorrelationSurface(valueDateStr, new string[] { underlyingCodes[0], underlyingCodes[3] }, parameter.Correlations[3]);
marketProxy.SaveCorrelationSurface(valueDateStr, new string[] { underlyingCodes[2], underlyingCodes[3] }, parameter.Correlations[5]);
}
}
return new SpreadOptionMarketObjectName()
{
DiscountCurveName = discountCurveName,
VolSurfaceNames = volSurfaceNames.ToArray()
};
}
/// <summary>
/// 构造Qdp估值参数
/// </summary>
private static OptionValuationParameters CreateQdpOptionValuationParameters(
string underlyingInstrumentType,
OptionMarketObjectName marketObjectName,
string underlyingCode,
bool isForwardTrade = false)
{
return new OptionValuationParameters(
isForwardTrade ? MarketProxy.ConstantZeroCurve : marketObjectName.DiscountCurveName,
underlyingInstrumentType == "Stock" ? marketObjectName.DividendCurveName : MarketProxy.ConstantZeroCurve,
marketObjectName.VolSurfaceName,
underlyingCode);
}
/// <summary>
/// 构造彩虹期权的Qdp估值参数
/// </summary>
private static OptionValuationParameters CreateQdpRainbowOptionValuationParameters(
string underlyingInstrumentType,
RainbowOptionMarketObjectName marketObjectName,
string[] underlyingCodes)
{
return new OptionValuationParameters(
marketObjectName.DiscountCurveName,
(underlyingInstrumentType == "Stock" ?
new string[] { marketObjectName.DividendCurveName, marketObjectName.DividendCurveName } :
new string[] { MarketProxy.ConstantZeroCurve, MarketProxy.ConstantZeroCurve }),
marketObjectName.VolSurfaceNames,
new string[] { underlyingCodes[0] + "_" + underlyingCodes[1] },
null);
}
private static OptionValuationParameters CreateQdpSpreadOptionValuationParameters(
string[] underlyingTickers, string discountCurveName, string[] volSurfaceNames)
{
var dividendCurveNames = new string[] { MarketProxy.ConstantZeroCurve, MarketProxy.ConstantZeroCurve };
var corrSurfNames = new string[] { underlyingTickers[0] + "_" + underlyingTickers[1] };
// construct correlation surface names
// see SpreadOptionTrade.GenerateMarketConditionFromPrebuilt for the order
if (underlyingTickers.Length == 3)
{
dividendCurveNames = new string[] { MarketProxy.ConstantZeroCurve, MarketProxy.ConstantZeroCurve, MarketProxy.ConstantZeroCurve };
corrSurfNames = new string[] {
underlyingTickers[0] + "_" + underlyingTickers[1],
underlyingTickers[0] + "_" + underlyingTickers[2],
underlyingTickers[1] + "_" + underlyingTickers[2]
};
}
else if (underlyingTickers.Length == 4)
{
dividendCurveNames = new string[] { MarketProxy.ConstantZeroCurve, MarketProxy.ConstantZeroCurve, MarketProxy.ConstantZeroCurve, MarketProxy.ConstantZeroCurve };
corrSurfNames = new string[] {
underlyingTickers[0] + "_" + underlyingTickers[1],
underlyingTickers[0] + "_" + underlyingTickers[2],
underlyingTickers[1] + "_" + underlyingTickers[2],
underlyingTickers[0] + "_" + underlyingTickers[3],
underlyingTickers[1] + "_" + underlyingTickers[3],
underlyingTickers[2] + "_" + underlyingTickers[3],
};
}
return new OptionValuationParameters(
discountCurveName,
dividendCurveNames,
volSurfaceNames,
corrSurfNames,
null);
}
/// <summary>
/// 释放QdpMarket中的数据
/// </summary>
private static void CleanupMarketObjects(
BaseOptionParameter parameter,
IQdpMarketProxy marketProxy,
string valueDateStr,
OptionMarketObjectName marketObjectName)
{
if (parameter.Volatility.HasValue && !string.IsNullOrWhiteSpace(marketObjectName.VolSurfaceName))
{
marketProxy.RemoveVolSurface(valueDateStr, marketObjectName.VolSurfaceName);
}
if (parameter.RiskFreeRate.HasValue && !string.IsNullOrWhiteSpace(marketObjectName.DiscountCurveName) && marketObjectName.DiscountCurveName != MarketProxy.ConstantZeroCurve)
{
marketProxy.RemoveYieldCurve(marketObjectName.DiscountCurveName, valueDateStr);
}
if (!string.IsNullOrWhiteSpace(marketObjectName.DividendCurveName) && marketObjectName.DividendCurveName != MarketProxy.ConstantZeroCurve)
{
marketProxy.RemoveYieldCurve(marketObjectName.DividendCurveName, valueDateStr);
}
}
/// <summary>
/// 为彩虹期权释放QdpMarket中的数据
/// </summary>
private static void CleanupRainbowOptionMarketObjects(
RainbowOptionParameter parameter,
IQdpMarketProxy marketProxy,
string valueDateStr,
RainbowOptionMarketObjectName marketObjectName)
{
if (parameter.Volatility.HasValue && !string.IsNullOrWhiteSpace(marketObjectName.VolSurfaceNames[0]))
{
marketProxy.RemoveVolSurface(valueDateStr, marketObjectName.VolSurfaceNames[0]);
}
if (parameter.Volatility2.HasValue && !string.IsNullOrWhiteSpace(marketObjectName.VolSurfaceNames[1]))
{
marketProxy.RemoveVolSurface(valueDateStr, marketObjectName.VolSurfaceNames[1]);
}
if (parameter.RiskFreeRate.HasValue && !string.IsNullOrWhiteSpace(marketObjectName.DiscountCurveName) && marketObjectName.DiscountCurveName != MarketProxy.ConstantZeroCurve)
{
marketProxy.RemoveYieldCurve(marketObjectName.DiscountCurveName, valueDateStr);
}
if (!string.IsNullOrWhiteSpace(marketObjectName.DividendCurveName) && marketObjectName.DividendCurveName != MarketProxy.ConstantZeroCurve)
{
marketProxy.RemoveYieldCurve(marketObjectName.DividendCurveName, valueDateStr);
}
}
/// <summary>
/// 为价差期权释放QdpMarket中的数据
/// </summary>
private static void CleanupSpreadOptionMarketObjects(
SpreadOptionParameter parameter,
IQdpMarketProxy marketProxy,
string valueDateStr,
SpreadOptionMarketObjectName marketObjectName)
{
for (var i = 0; i < parameter.Volatilities.Length; ++i)
{
if (!string.IsNullOrWhiteSpace(marketObjectName.VolSurfaceNames[i]))
{
marketProxy.RemoveVolSurface(valueDateStr, marketObjectName.VolSurfaceNames[i]);
}
}
if (parameter.RiskFreeRate.HasValue && !string.IsNullOrWhiteSpace(marketObjectName.DiscountCurveName) && marketObjectName.DiscountCurveName != MarketProxy.ConstantZeroCurve)
{
marketProxy.RemoveYieldCurve(marketObjectName.DiscountCurveName, valueDateStr);
}
}
private static TradeValueResult ConvertValueResult(IPricingResult results, trade trade, underlying_manager underlying, double spotPrice)
{
if (results.Succeeded)
{
var ret = new TradeValueResult
{
UnderlyingCode = underlying.UnderlyingCode,
Pv = results.Pv,
Delta = results.Delta,
Gamma = results.Gamma,
Vega = results.Vega,
CalendarDayTheta = results.Theta,
TradingDayTheta = results.ThetaPnL,
Rho = results.Rho,
DeltaInLots = TradeLotsCalc.CalcDeltaInLots(results.Delta, null, underlying),
DeltaCash = results.DeltaCash,
//目前这个算法等价于 results.GammaCash/2*0.01 之所以没有这样算,是因为下面的算法是天风提供的;
GammaCash = PS.Config.Company == Configuration.CompanyEnum.天风 ? (0.5 * results.Gamma * Math.Pow(spotPrice * 0.01, 2)) : results.GammaCash,
VegaCash = results.VegaCash,
Vol = results.PricingVol,
//RoundedPv = OtcFormatHelper.FormatValue(results.Pv / trade.Notional, 2) * trade.Notional
};
if (trade.TradeType == "现金流交易" && trade.trade_cashflow?.DepositType == CashflowDepositTypeEnum.资金收益)
{
ret.Pv += trade.StockEqvNotional * trade.trade_cashflow.PrepayRatio * TradeCalcHelper.GetBuySellSign(trade.BuySell);
}
ret.RoundedPv = OtcFormatHelper.FormatValue(results.Pv / trade.Notional, 2) * trade.Notional;
ConvertTradeValueResultOfCompany(ret, trade.TradeType);
return ret;
}
else
{
return null;
}
}
/// <summary>
/// 按照各公司的配置调整估值结果
/// </summary>
/// <param name="ret"></param>
public static void ConvertTradeValueResultOfCompany(TradeValueResult ret, string tradeType)
{
switch (PS.Config.Company)
{
case Configuration.CompanyEnum.国元固收:
ret.Delta /= 10000;
ret.Delta2 /= 10000;
ret.Delta3 /= 10000;
ret.Delta4 /= 10000;
ret.DeltaCash /= 10000;
ret.DeltaCash2 /= 10000;
ret.DeltaCash3 /= 10000;
ret.DeltaCash4 /= 10000;
ret.DeltaInLots /= 10000;
ret.DeltaMax /= 10000;
ret.DeltaT1 /= 10000;
ret.DDeltaDt /= 10000;
ret.DDeltaDVol /= 10000;
ret.SA_Delta /= 10000;
ret.Gamma /= 100000000;
ret.Gamma2 /= 100000000;
ret.Gamma3 /= 100000000;
ret.Gamma4 /= 100000000;
ret.GammaCash /= 100000000;
ret.GammaCash2 /= 100000000;
ret.GammaCash3 /= 100000000;
ret.GammaCash4 /= 100000000;
ret.CrossGamma /= 100000000;
break;
case Configuration.CompanyEnum.招证:
if (tradeType == "现金流交易")
{
ret.Rho = 0;
}
break;
case Configuration.CompanyEnum.默认:
case Configuration.CompanyEnum.国泰君安:
case Configuration.CompanyEnum.申万:
case Configuration.CompanyEnum.渤海:
case Configuration.CompanyEnum.安粮:
case Configuration.CompanyEnum.上期资本:
case Configuration.CompanyEnum.格林大华:
case Configuration.CompanyEnum.方顿:
case Configuration.CompanyEnum.东兴:
case Configuration.CompanyEnum.光大光子:
case Configuration.CompanyEnum.长江:
case Configuration.CompanyEnum.海通:
case Configuration.CompanyEnum.茂川资本:
case Configuration.CompanyEnum.宏源:
case Configuration.CompanyEnum.伴兴:
case Configuration.CompanyEnum.中财资本:
case Configuration.CompanyEnum.瑞达:
case Configuration.CompanyEnum.兴证:
case Configuration.CompanyEnum.弘业:
case Configuration.CompanyEnum.东方财富:
case Configuration.CompanyEnum.国海:
case Configuration.CompanyEnum.山西:
case Configuration.CompanyEnum.天风:
case Configuration.CompanyEnum.广期资本:
case Configuration.CompanyEnum.厦门象屿:
case Configuration.CompanyEnum.润和:
case Configuration.CompanyEnum.兴业商贸:
case Configuration.CompanyEnum.天示:
case Configuration.CompanyEnum.中金:
case Configuration.CompanyEnum.中粮:
case Configuration.CompanyEnum.浙期:
case Configuration.CompanyEnum.中基石化:
case Configuration.CompanyEnum.国投:
case Configuration.CompanyEnum.华安:
case Configuration.CompanyEnum.国信金阳:
case Configuration.CompanyEnum.国贸启润:
case Configuration.CompanyEnum.东吴:
case Configuration.CompanyEnum.广发商贸:
case Configuration.CompanyEnum.湘财:
case Configuration.CompanyEnum.华西:
default:
break;
}
}
private static TradeValueResult ConvertRainbowOptionValueResult(IPricingResult results, trade trade)
{
if (results.Succeeded)
{
//var vol = 0.0;
var ret = new TradeValueResult
{
UnderlyingCode = trade.trade_rainbow_option.UnderlyingAssetCode,
Pv = results.Pv,
Delta = results.asset1Delta,
Gamma = results.asset1Gamma,
Vega = results.asset1Vega,
CalendarDayTheta = results.Theta,
TradingDayTheta = results.ThetaPnL,
Rho = results.Rho,
DeltaCash = results.DeltaCash,
GammaCash = results.GammaCash,
Vol = results.PricingVol,
UnderlyingCode2 = trade.trade_rainbow_option.UnderlyingAssetCode2,
Delta2 = results.asset2Delta,
Gamma2 = results.asset2Gamma,
DeltaCash2 = results.asset2DeltaCash,
GammaCash2 = results.asset2GammaCash,
Vega2 = results.asset2Vega,
CrossGamma = results.crossGamma,
CrossVogga = results.crossVomma,
CorrVega = results.correlationVega,
RoundedPv = OtcFormatHelper.FormatValue(results.Pv / trade.Notional, 2) * trade.Notional
};
ConvertTradeValueResultOfCompany(ret, trade.TradeType);
return ret;
}
else
{
return null;
}
}
private static TradeValueResult ConvertSpreadOptionValueResult(IPricingResult results, trade trade)
{
if (results.Succeeded)
{
var valueResult = new TradeValueResult
{
Pv = results.Pv,
Delta = results.asset1Delta,
Gamma = results.asset1Gamma,
Vega = results.asset1Vega,
CalendarDayTheta = results.Theta,
TradingDayTheta = results.ThetaPnL,
Rho = results.Rho,
DeltaCash = results.DeltaCash,
GammaCash = results.GammaCash,
Vol = results.PricingVol,
Delta2 = results.asset2Delta,
Gamma2 = results.asset2Gamma,
DeltaCash2 = results.asset2DeltaCash,
GammaCash2 = results.asset2GammaCash,
Vega2 = results.asset2Vega,
Delta3 = results.asset3Delta,
Gamma3 = results.asset3Gamma,
DeltaCash3 = results.asset3DeltaCash,
GammaCash3 = results.asset3GammaCash,
Vega3 = results.asset3Vega,
Delta4 = results.asset4Delta,
Gamma4 = results.asset4Gamma,
DeltaCash4 = results.asset4DeltaCash,
GammaCash4 = results.asset4GammaCash,
Vega4 = results.asset4Vega,
CrossGamma = results.crossGamma,
CrossVogga = results.crossVomma,
CorrVega = results.correlationVega,
RoundedPv = OtcFormatHelper.FormatValue(results.Pv / trade.Notional, 2) * trade.Notional
};
var underlyingCodes = trade.trade_spread_option.UnderlyingAssetCodes();
valueResult.UnderlyingCode = underlyingCodes[0];
valueResult.UnderlyingCode2 = underlyingCodes[1];
if (underlyingCodes.Length > 2)
{
valueResult.UnderlyingCode3 = underlyingCodes[2];
}
if (underlyingCodes.Length > 3)
{
valueResult.UnderlyingCode4 = underlyingCodes[3];
}
ConvertTradeValueResultOfCompany(valueResult, trade.TradeType);
return valueResult;
}
else
{
return null;
}
}
private static string CreateVolSurfaceFromVolValue(IQdpMarketProxy marketProxy, string valueDateStr, double vol)
{
var volPart = GetDefaultVol(vol);
volPart.QuotationDate = DateTime.Parse(valueDateStr);
return SaveVolSurfaceToMarketProxy(marketProxy, valueDateStr, volPart);
}
/// <summary>
/// 获取默认波动率
/// </summary>
private static VolatilityDefault GetDefaultVol(double vol)
{
vol = vol < 0 ? ConsVolInfos.defVol : vol;
var result = new VolatilityDefault
{
InterpolationMethod = ConsVolInfos.defInterpolationMethod,
VolSurfaceMode = ConsVolInfos.defVolMode,
VolType = ConsVolInfos.defVolType,
QuotationDate = DateTime.MinValue,
UnderlyingId = null,
ContractCode = null,
OptId = 0,
OptName = ConsVolInfos.defOptName,
OptDate = DateTime.Now
};
result.Data = QdpVolHelper.GenerateFlatSingleVols(vol).ToJson();
return result;
}
private static string SaveVolSurfaceToMarketProxy(IQdpMarketProxy marketProxy, string valueDateStr, volatility volatility)
{
var volSurfaceName = Guid.NewGuid().ToString();
var volSurfaceWrap = new VolSurfaceBuilder
{
volSurfaceName = volSurfaceName,
volSurfaceType = "MoneynessVol",
interpolation = "BiLinear"
}.SetVectors(volatility.VolTable).Build(valueDateStr);
marketProxy.SaveVolSurface(volSurfaceWrap);
return volSurfaceName;
}
/// <summary>
/// 计算一组相同标的的期权交易,使用同一个波动率曲面
/// </summary>
public static Dictionary<int, TradeValueResult> CalculateOptionsWithSharedVolSurface(
string userId,
IEnumerable<trade> trades,
underlying_manager underlying,
ParameterBase parameter,
volatility volatility,
DateTime exerciseDate)
{
if (PS.Config.Is润和)
{
parameter.OverrideTTM = TradeCalcHelper.CalculateTTMDays(parameter.ValueDate, exerciseDate, underlying?.UnderlyingTypeId ?? 0, false);
}
var valueDateStr = parameter.ValueDate.ToString("yyyy-MM-dd");
var marketProxy = QdpMarketManager.Instance.GetPrebuiltMarketProxy(userId);
marketProxy.CreateMarket(valueDateStr);
var volSurfaceName = SaveVolSurfaceToMarketProxy(marketProxy, valueDateStr, volatility);
var optionParameter = parameter as BaseOptionParameter;
optionParameter.VolSurfaceName = volSurfaceName;
var results = new Dictionary<int, TradeValueResult>();
foreach (var trade in trades)
{
var result = CalculateTradeValue(userId, trade, underlying, parameter);
results[trade.id] = result;
}
return results;
}
private static void AddCalculationLog(TradeBase trade, CalcScenarioEnum scenarioEnum, string tradeNumber)
{
try
{
var option = trade.Instrument as OptionBase;
// 添加计算日志
CalculationLog log = new CalculationLog()
{
CreateTime = DateTime.Now,
Scenario = scenarioEnum,
LogObject = trade,
TradeNumber = tradeNumber,
Notional = trade.Notional,
Exercise = option?.Exercise.ToString(),
OptionType = option?.OptionType.ToString(),
TradeDate = trade.TradeDate.ToString(),
MaturityDate = trade.MaturityDate?.ToString(),
InitialSpotPrice = option?.InitialSpotPrice.ToString(),
Strike = option?.Strike.ToString(),
};
ICalculationLogService calcLogService = new CalculationLogService();
calcLogService.AddLog(log, scenarioEnum);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
class OptionMarketObjectName
{
public string DiscountCurveName { get; set; }
public string DividendCurveName { get; set; }
public string VolSurfaceName { get; set; }
}
class RainbowOptionMarketObjectName : OptionMarketObjectName
{
public string[] VolSurfaceNames { get; set; }
}
class SpreadOptionMarketObjectName : OptionMarketObjectName
{
public string[] VolSurfaceNames { get; set; }
public string[] CorrelationSurfaceNames { get; set; }
}
}