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.ComputeServiceV2.Data.CommonModels.TradeInfos.Rates;
using Qdp.Foundation.Implementations;
using Qdp.Pricing.Base.Enums;
using Qdp.Pricing.Base.Implementations;
using Qdp.Pricing.Base.Utilities;
using Qdp.Pricing.Library.Common.Interfaces;
using Qdp.Pricing.Library.Common.Products.Rates;
using Qdp.Pricing.Library.Common.Products.Rates.Engines;
using Qdp.Pricing.Library.Options.Products.Accumulator;
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;
using Qdp.Pricing.Library.Options.Products.Airbag.Engines.Analytical;
using Qdp.Pricing.Library.Options.Products.Asian;
using Qdp.Pricing.Library.Options.Products.AsianSyntheticSpread;
using Qdp.Pricing.Library.Options.Products.AsianSyntheticSpread.Engines.Analytical;
using Qdp.Pricing.Library.Options.Products.Autocall.Phoenix;
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;
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.SyntheticSpread;
using Qdp.Pricing.Library.Options.Products.Vanilla;
using Qdp.Pricing.Library.Options.Utilities;
using System.Runtime.CompilerServices;
using YLErp.BLL.Calculation;
using YLErp.BLL.Calculation.Engine;
using YLErp.Commons;
using YLErp.Modules.CalculationModule;
using YLErp.Modules.SkewMapVolModule;
using YLErp.QdpModule;
namespace YLErp.OldVersion
{
///
/// 期权计算类
///
static class OptionCalculatorV1
{
#region 香草期权
private static VanillaOptionTrade CreateVanillaOptionTrade(
string tradeId,
string volSurfaceName,
string tradeDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
double notional,
string tradeType,
string exerciseDate,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
Dictionary dividends = null,
bool hasNightMarket = false,
bool preciseTimeMode = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN)
//string optionDayCount = "Act365")
{
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
var exercise = QdpConverter.ConvertExerciseType(exerciseType);
var optionStartDate = startDate.ToDate();
var underlyingMaturityDate = string.IsNullOrWhiteSpace(endDate) ? null : endDate.ToDate();
var temp_exerciseDate = exerciseDate.ToDate();
//if (temp_exerciseDate < optionStartDate)
//{
// throw new Exception("到期日期不能小于起始日");
//}
Date[] exerciseDates;
Date[] observationDates;
if (exercise == OptionExercise.American)
{
exerciseDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray();
observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray();
}
else
{
exerciseDates = new Date[] { temp_exerciseDate };
observationDates = new Date[] { temp_exerciseDate };
}
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var pricingTOverride = double.IsNaN(timeToMaturityDays) ? double.NaN : QdpCalendarHelper.CalculateTFromDays(timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
var vanillaOption =
new VanillaOption(
startDate: optionStartDate,
maturityDate: underlyingMaturityDate,
exercise: exercise,
optionType: (OptionType)Enum.Parse(typeof(OptionType), optionType),
strike: strike,
underlyingInstrumentType: (InstrumentType)Enum.Parse(typeof(InstrumentType), underlyingInstrumentType),
calendar: CalendarImpl.Get("chn"),
dayCount: optionDayCount.ToDayCountImpl(),
payoffCcy: CurrencyCode.CNY,
settlementCcy: CurrencyCode.CNY,
exerciseDates: exerciseDates,
observationDates: observationDates,
notional: notional,
settlementGap: null,
optionPremiumPaymentDate: null,
optionPremium: 0.0,
isMoneynessOption: isMoneynessOption,
initialSpotPrice: initialSpotPrice,
dividends: dividends,
hasNightMarket: hasNightMarket,
commodityFuturesPreciseTimeMode: preciseTimeMode,
pricingToverride: pricingTOverride,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
participationRate: participationRate,
isAnnualized: isAnnualized,
annualizedFactor: annualizeFactor)
{
UnderlyingTickers = new string[] { underlyingTicker }
};
if (string.IsNullOrWhiteSpace(tradeId))
{
tradeId = Guid.NewGuid().ToString();
}
return new VanillaOptionTrade(
tradeId,
tradeDate.ToDate(),
vanillaOption.StartDate,
vanillaOption.ExerciseDates.Last(),
QdpConverter.ConvertTradeType(tradeType),
vanillaOption.Notional,
0.0,
vanillaOption)
{
ValuationParameters = new OptionValuationParameters("RiskFreeDiscountCurve", MarketProxy.ConstantZeroCurve, volSurfaceName, vanillaOption.UnderlyingTickers[0]),
};
}
///
/// 计算香草期权的估值与风险
///
/// MarketProxy对象
/// 估值日期
/// 标的代码
/// 标的类型
/// 行权价
/// 用户指定距离到期日的天数,可以为小数
private static TradeValueResult ValueVanillaOption(
IQdpMarketProxy marketProxy,
string valueDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
double spotPrice,
double notional,
string volSurfaceName,
double riskFreeRate,
string modelName,
string tradeType,
string exerciseDate,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
double dividendRate = 0.0,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
Dictionary dividends = null,
bool hasNightMarket = false,
bool preciseTimeMode = false,
string engineName = null,
string discountCurveName = null,
double timeToMaturityDays = double.NaN,
bool isForwardTrade = false,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
try
{
if (string.IsNullOrWhiteSpace(volSurfaceName))
{
volSurfaceName = underlyingTicker.GenerateVolSurfaceKey();
}
var optionTrade = CreateVanillaOptionTrade(
null,
volSurfaceName,
startDate,
underlyingTicker,
underlyingInstrumentType,
strike,
startDate,
endDate,
optionType,
exerciseType,
notional,
tradeType,
exerciseDate,
participationRate,
principalRate,
isAnnualized,
annualizeFactor,
isMoneynessOption,
initialSpotPrice,
dividends,
hasNightMarket: hasNightMarket,
preciseTimeMode: preciseTimeMode,
timeToMaturityDays: timeToMaturityDays,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride).CheckMaturityDate();
var market = marketProxy.GetQdpMarket(valueDate);
if (market == null)
{
return null;
}
var useLocalDiscountCurve = string.IsNullOrWhiteSpace(discountCurveName);
//设置DiscountCurve
if (useLocalDiscountCurve)
{
discountCurveName = Guid.NewGuid().ToString();
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, riskFreeRate);
marketProxy.AddYieldCurve(discountCurveName, valueDate, discountCurve);
}
//设置标的价格
marketProxy.AddStockPrice(underlyingTicker, valueDate, spotPrice);
OptionValuationParameters parameters = null;
string dividendCurveName = null;
if (underlyingInstrumentType == "Stock")
{
//设置DividendCurve
dividendCurveName = Guid.NewGuid().ToString();
var dividendCurve = CalculatorHelper.CreateConstantRiskFreeCurve(dividendCurveName, dividendRate);
marketProxy.AddYieldCurve(dividendCurveName, valueDate, dividendCurve);
parameters = new OptionValuationParameters(
isForwardTrade ? MarketProxy.ConstantZeroCurve : discountCurveName,
dividendCurveName,
volSurfaceName,
underlyingTicker);
}
else
{
parameters = new OptionValuationParameters(
isForwardTrade ? MarketProxy.ConstantZeroCurve : discountCurveName,
MarketProxy.ConstantZeroCurve,
volSurfaceName,
underlyingTicker);
}
var option = optionTrade.Instrument as VanillaOption;
var factory = OptionEngineFactory.GetEngineFactory("VanillaOption");
var engine = factory.GetEngine(engineName, option.Exercise);
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDate), pricingRequest, engine, parameters);
if (useLocalDiscountCurve)
{
marketProxy.RemoveYieldCurve(discountCurveName, valueDate);
}
if (!string.IsNullOrEmpty(dividendCurveName))
{
marketProxy.RemoveYieldCurve(dividendCurveName, valueDate);
}
if (results.Succeeded)
{
var ret = new TradeValueResult()
{
UnderlyingCode = underlyingTicker,
Pv = results.Pv,
Delta = results.Delta,
Gamma = results.Gamma,
Vega = results.Vega,
CalendarDayTheta = results.Theta,
TradingDayTheta = results.ThetaPnL,
Rho = results.Rho,
DeltaCash = results.DeltaCash,
GammaCash = results.GammaCash,
Vol = results.PricingVol,
RoundedPv = OtcFormatHelper.FormatValue(results.Pv / notional, 2) * notional
};
return ret;
}
else
{
return null;
}
}
catch (Exception ex)
{
LogFactory.GetLogger("期权计算").Error("ValueVanillaOption:" + underlyingTicker, ex);
throw new Exception($"香草期权处理失败[标的:{underlyingTicker},开始日:{startDate},结束日:{endDate},行权日:{exerciseDate}]", ex);
}
}
private static TradeValueResult ValueVanillaOptionTrade(
VolConstructionType volConstructionType,
IQdpMarketProxy marketProxy,
string valueDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
double spotPrice,
double notional,
string volSurfaceName,
double riskFreeRate,
string modelName,
string tradeType,
string exerciseDate,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
double dividendRate = 0.0,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
Dictionary dividends = null,
bool hasNightMarket = false,
bool preciseTimeMode = false,
string engineName = null,
string discountCurveName = null,
double timeToMaturityDays = double.NaN,
bool isForwardTrade = false,
bool ignoreSkewMap = false,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
if (volConstructionType == VolConstructionType.Normal || ignoreSkewMap)
{
return ValueVanillaOption(
marketProxy,
valueDate,
underlyingTicker,
underlyingInstrumentType,
strike,
startDate,
endDate,
optionType,
exerciseType,
spotPrice,
notional,
volSurfaceName,
riskFreeRate,
modelName,
tradeType,
exerciseDate,
participationRate,
principalRate,
isAnnualized,
annualizeFactor,
dividendRate,
isMoneynessOption,
initialSpotPrice,
dividends,
hasNightMarket,
preciseTimeMode,
engineName,
discountCurveName,
timeToMaturityDays,
isForwardTrade,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
pricingRequest: pricingRequest);
}
else // VolConstructionType.SkewMap
{
var tradeId = Guid.NewGuid().ToString();
SetSkewMapVol(
marketProxy,
valueDate,
underlyingTicker,
exerciseDate,
strike,
TradeCalcHelper.IsBuy(tradeType),
QdpHelper.IsCall(optionType),
tradeId,
spotPrice,
null,
timeToMaturityDays);
return ValueVanillaOption(
marketProxy,
valueDate,
underlyingTicker,
underlyingInstrumentType,
strike,
startDate,
endDate,
optionType,
exerciseType,
spotPrice,
notional,
tradeId.GenerateVolSurfaceKey(),
riskFreeRate,
modelName,
tradeType,
exerciseDate,
participationRate,
principalRate,
isAnnualized,
annualizeFactor,
dividendRate,
isMoneynessOption,
initialSpotPrice,
dividends,
hasNightMarket,
preciseTimeMode,
engineName,
discountCurveName,
timeToMaturityDays,
isForwardTrade,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
pricingRequest: pricingRequest);
}
}
///
/// 计算香草期权的估值与风险,本函数会根据是否为SkewMap波动率来调整波动率获取方式
///
private static TradeValueResult ValueVanillaOptionTrade(
IQdpMarketProxy marketProxy,
string valueDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
double spotPrice,
double notional,
string volSurfaceName,
double riskFreeRate,
string modelName,
string tradeType,
string exerciseDate,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
double dividendRate = 0.0,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
Dictionary dividends = null,
bool hasNightMarket = false,
bool preciseTimeMode = false,
string engineName = null,
string discountCurveName = null,
double timeToMaturityDays = double.NaN,
bool isForwardTrade = false,
bool ignoreSkewMap = false,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
return ValueVanillaOptionTrade(
VolSurfaceInitializerSingleton.VolConstructionType,
marketProxy,
valueDate,
underlyingTicker,
underlyingInstrumentType,
strike,
startDate,
endDate,
optionType,
exerciseType,
spotPrice,
notional,
volSurfaceName,
riskFreeRate,
modelName,
tradeType,
exerciseDate,
participationRate,
principalRate,
isAnnualized,
annualizeFactor,
dividendRate,
isMoneynessOption,
initialSpotPrice,
dividends,
hasNightMarket,
preciseTimeMode,
engineName,
discountCurveName,
timeToMaturityDays,
isForwardTrade,
ignoreSkewMap,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
pricingRequest: pricingRequest);
}
#endregion
#region 障碍期权 (单障碍和双障碍都支持)
private static BarrierOptionTrade CreateBarrierOptionTrade(
string tradeId,
string volSurfaceName,
string tradeDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
string barrierType,
double barrierPrice,
double upperBarrierPrice,
bool isDiscrete,
double rebate,
double notional,
string tradeType,
string exerciseDate,
string rebateType,
bool rebateAnnualizedAtKO,
string rebateDayCount,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
string observationDateStr = null,
Dictionary dividends = null,
string position = "Buy",
double barrierShift = 0.0,
string barrierStatus = "Monitoring",
bool hasNightMarket = false,
bool preciseTimeMode = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN)
{
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
var exercise = QdpConverter.ConvertExerciseType(exerciseType);
var optionStartDate = startDate.ToDate();
var underlyingMaturityDate = string.IsNullOrWhiteSpace(endDate) ? null : endDate.ToDate();
var temp_exerciseDate = exerciseDate.ToDate();
if (temp_exerciseDate < optionStartDate)
{
return null;
}
Date[] exerciseDates;
Date[] observationDates = null;
var barrierObservationDates = QdpHelper.ParseObservationDate(observationDateStr);
if (exercise == OptionExercise.American)
{
exerciseDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray();
observationDates ??= CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray();
}
else
{
exerciseDates = new Date[] { temp_exerciseDate };
observationDates ??= CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).Union(new[] { temp_exerciseDate }).ToArray();
}
//补丁
//if(string.IsNullOrEmpty(barrierType))
//{
// barrierType = "0";
//}
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var pricingTOverride = double.IsNaN(timeToMaturityDays) ? double.NaN : QdpCalendarHelper.CalculateTFromDays(timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
if (rebateAnnualizedAtKO)
{
var rebateDayCountImpl = string.IsNullOrWhiteSpace(rebateDayCount) ? new Act365() : rebateDayCount.ToDayCountImpl();
rebate /= rebateDayCountImpl.CalcDayCountFraction(optionStartDate, underlyingMaturityDate);
}
var barrierOption =
new BarrierOption(
startDate: optionStartDate,
maturityDate: underlyingMaturityDate,
exercise: exercise,
optionType: (OptionType)Enum.Parse(typeof(OptionType), optionType),
strike: strike,
rebate: rebate, //rebate
coupon: 0.0, //coupon
participationRate: participationRate,
barrierType: (BarrierType)Enum.Parse(typeof(BarrierType), barrierType),
lowerBarrier: barrierPrice, //lowerBarrier
upperBarrier: upperBarrierPrice, //upperBarrier
isDiscreteMonitored: isDiscrete,
underlyingType: (InstrumentType)Enum.Parse(typeof(InstrumentType), underlyingInstrumentType),
calendar: CalendarImpl.Get("chn"),
//new Act365(),
dayCount: optionDayCount.ToDayCountImpl(),
payoffCcy: CurrencyCode.CNY,
settlementCcy: CurrencyCode.CNY,
exerciseDates: exerciseDates,
observationDates: observationDates,
barrierObservationDates: barrierObservationDates,
highRebate: double.NaN,
notional: notional,
settlementGap: null,
optionPremiumPaymentDate: null,
optionPremium: 0.0,
isMoneynessOption: isMoneynessOption,
initialSpotPrice: initialSpotPrice,
dividends: dividends,
position: QdpConverter.ConvertPosition(position),
barrierShift: barrierShift,
rebateAnnualizedAtKO: rebateAnnualizedAtKO,
rebateDayCount: string.IsNullOrWhiteSpace(rebateDayCount) ? new Act365() : rebateDayCount.ToDayCountImpl(),
pricingToverride: pricingTOverride, //pricingToverride
rebateType: (string.IsNullOrEmpty(rebateType) ? "AtEnd" : rebateType).ToBinaryRebateType(),
barrierStatus: QdpConverter.ConvertBarrierStatus(barrierStatus),
hasNightMarket: hasNightMarket,
commodityFuturesPreciseTimeMode: preciseTimeMode,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
isAnnualized: isAnnualized,
annualizedFactor: annualizeFactor)
{
UnderlyingTickers = new string[] { underlyingTicker }
};
if (string.IsNullOrWhiteSpace(tradeId))
{
tradeId = Guid.NewGuid().ToString();
}
return new BarrierOptionTrade(
tradeId,
tradeDate.ToDate(),
barrierOption.StartDate,
barrierOption.ExerciseDates.Last(),
QdpConverter.ConvertTradeType(tradeType),
barrierOption.Notional,
0.0,
barrierOption)
{
ValuationParameters = new OptionValuationParameters("RiskFreeDiscountCurve", MarketProxy.ConstantZeroCurve, volSurfaceName, barrierOption.UnderlyingTickers[0]),
};
}
private static TradeValueResult ValueBarrierOptionTrade(
IQdpMarketProxy marketProxy,
string valueDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
string barrierType,
double barrierPrice,
double upperBarrierPrice,
bool isDiscrete,
double spotPrice,
double rebate,
double notional,
string volSurfaceName,
double riskFreeRate,
string tradeType,
string exerciseDate,
string rebateType,
bool rebateAnnualizedAtKO,
string rebateDayCount,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
double dividendRate = 0.0,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
string observationDateStr = null,
Dictionary dividends = null,
string position = "Buy",
double barrierShift = 0.0,
string barrierStatus = "Monitoring",
bool hasNightMarket = false,
bool preciseTimeMode = false,
string engineName = null,
string discountCurveName = null,
bool isForwardTrade = false,
bool ignoreSkewMap = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
return ValueBarrierOptionTrade(
VolSurfaceInitializerSingleton.VolConstructionType,
marketProxy,
valueDate,
underlyingTicker,
underlyingInstrumentType,
strike,
startDate,
endDate,
optionType,
exerciseType,
barrierType,
barrierPrice,
upperBarrierPrice,
isDiscrete,
spotPrice,
rebate,
notional,
volSurfaceName,
riskFreeRate,
tradeType,
exerciseDate,
rebateType,
rebateAnnualizedAtKO,
rebateDayCount,
participationRate,
principalRate,
isAnnualized,
annualizeFactor,
dividendRate,
isMoneynessOption,
initialSpotPrice,
observationDateStr,
dividends,
position,
barrierShift,
barrierStatus,
hasNightMarket,
preciseTimeMode,
engineName,
discountCurveName,
isForwardTrade,
ignoreSkewMap,
timeToMaturityDays: timeToMaturityDays,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
pricingRequest: pricingRequest);
}
private static TradeValueResult ValueBarrierOptionTrade(
VolConstructionType volConstructionType,
IQdpMarketProxy marketProxy,
string valueDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
string barrierType,
double barrierPrice,
double upperBarrierPrice,
bool isDiscrete,
double spotPrice,
double rebate,
double notional,
string volSurfaceName,
double riskFreeRate,
string tradeType,
string exerciseDate,
string rebateType,
bool rebateAnnualizedAtKO,
string rebateDayCount,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
double dividendRate = 0.0,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
string observationDateStr = null,
Dictionary dividends = null,
string position = "Buy",
double barrierShift = 0.0,
string barrierStatus = "Monitoring",
bool hasNightMarket = false,
bool preciseTimeMode = false,
string engineName = null,
string discountCurveName = null,
bool isForwardTrade = false,
bool ignoreSkewMap = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
if (volConstructionType == VolConstructionType.Normal || ignoreSkewMap)
{
return ValueBarrierOption(
marketProxy,
valueDate,
underlyingTicker,
underlyingInstrumentType,
strike,
startDate,
endDate,
optionType,
exerciseType,
barrierType,
barrierPrice,
upperBarrierPrice,
isDiscrete,
spotPrice,
rebate,
notional,
volSurfaceName,
riskFreeRate,
tradeType,
exerciseDate,
rebateType,
rebateAnnualizedAtKO,
rebateDayCount,
participationRate,
principalRate,
isAnnualized,
annualizeFactor,
dividendRate,
isMoneynessOption,
initialSpotPrice,
observationDateStr,
dividends,
position,
barrierShift,
barrierStatus,
hasNightMarket,
preciseTimeMode,
engineName,
discountCurveName,
isForwardTrade,
timeToMaturityDays: timeToMaturityDays,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
pricingRequest: pricingRequest);
}
else
{
var tradeId = Guid.NewGuid().ToString();
SetSkewMapVol(
marketProxy,
valueDate,
underlyingTicker,
exerciseDate,
strike,
TradeCalcHelper.IsBuy(tradeType),
QdpHelper.IsCall(optionType),
tradeId,
spotPrice);
return ValueBarrierOption(
marketProxy,
valueDate,
underlyingTicker,
underlyingInstrumentType,
strike,
startDate,
endDate,
optionType,
exerciseType,
barrierType,
barrierPrice,
upperBarrierPrice,
isDiscrete,
spotPrice,
rebate,
notional,
tradeId.GenerateVolSurfaceKey(),
riskFreeRate,
tradeType,
exerciseDate,
rebateType,
rebateAnnualizedAtKO,
rebateDayCount,
participationRate,
principalRate,
isAnnualized,
annualizeFactor,
dividendRate,
isMoneynessOption,
initialSpotPrice,
observationDateStr,
dividends,
position,
barrierShift,
barrierStatus,
hasNightMarket,
preciseTimeMode,
engineName,
discountCurveName,
isForwardTrade,
timeToMaturityDays: timeToMaturityDays,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
pricingRequest: pricingRequest);
}
}
private static TradeValueResult ValueBarrierOption(
IQdpMarketProxy marketProxy,
string valueDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
string barrierType,
double barrierPrice,
double upperBarrierPrice,
bool isDiscrete,
double spotPrice,
double rebate,
double notional,
string volSurfaceName,
double riskFreeRate,
string tradeType,
string exerciseDate,
string rebateType,
bool rebateAnnualizedAtKO,
string rebateDayCount,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
double dividendRate = 0.0,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
string observationDateStr = null,
Dictionary dividends = null,
string position = "Buy",
double barrierShift = 0.0,
string barrierStatus = "Monitoring",
bool hasNightMarket = false,
bool preciseTimeMode = false,
string engineName = null,
string discountCurveName = null,
bool isForwardTrade = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
if (string.IsNullOrWhiteSpace(volSurfaceName))
{
volSurfaceName = underlyingTicker.GenerateVolSurfaceKey();
}
//如果用户自定义的观察日就是每天观察,则忽略自定义观察日
var observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(startDate.ToDate(), exerciseDate.ToDate()).Union(new[] { exerciseDate.ToDate() }).ToArray();
var barrierObservationDates = QdpHelper.ParseObservationDate(observationDateStr);
if (barrierObservationDates != null &&
(barrierObservationDates.Count(d => d >= observationDates.First() && d <= observationDates.Last()) == observationDates.Length))
{
observationDateStr = null;
}
var optionTrade = CreateBarrierOptionTrade(
null,
volSurfaceName,
startDate,
underlyingTicker,
underlyingInstrumentType,
strike,
startDate,
endDate,
optionType,
exerciseType,
barrierType,
barrierPrice,
upperBarrierPrice,
isDiscrete,
rebate,
notional,
tradeType,
exerciseDate,
rebateType,
rebateAnnualizedAtKO,
rebateDayCount,
participationRate,
principalRate,
isAnnualized,
annualizeFactor,
isMoneynessOption,
initialSpotPrice,
observationDateStr,
dividends,
position,
barrierShift,
barrierStatus,
hasNightMarket: hasNightMarket,
preciseTimeMode: preciseTimeMode,
timeToMaturityDays: timeToMaturityDays,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride).CheckMaturityDate();
var market = marketProxy.GetQdpMarket(valueDate);
if (market == null)
{
return null;
}
var useLocalDiscountCurve = string.IsNullOrWhiteSpace(discountCurveName);
//设置DiscountCurve
if (useLocalDiscountCurve)
{
discountCurveName = Guid.NewGuid().ToString();
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, riskFreeRate);
marketProxy.AddYieldCurve(discountCurveName, valueDate, discountCurve);
}
//设置标的价格
marketProxy.AddStockPrice(underlyingTicker, valueDate, spotPrice);
OptionValuationParameters parameters = null;
string dividendCurveName = null;
if (underlyingInstrumentType == "Stock")
{
//设置DividendCurve
dividendCurveName = Guid.NewGuid().ToString();
var dividendCurve = CalculatorHelper.CreateConstantRiskFreeCurve(dividendCurveName, dividendRate);
marketProxy.AddYieldCurve(dividendCurveName, valueDate, dividendCurve);
parameters = new OptionValuationParameters(
isForwardTrade ? MarketProxy.ConstantZeroCurve : discountCurveName,
dividendCurveName,
volSurfaceName,
underlyingTicker);
}
else
{
parameters = new OptionValuationParameters(
isForwardTrade ? MarketProxy.ConstantZeroCurve : discountCurveName,
MarketProxy.ConstantZeroCurve,
volSurfaceName,
underlyingTicker);
}
var option = optionTrade.Instrument as BarrierOption;
var factory = OptionEngineFactory.GetEngineFactory("BarrierOption");
var engine = factory.GetEngine(
engineName,
option.Exercise,
option,
!string.IsNullOrWhiteSpace(observationDateStr));
var results = optionTrade.CalculateRisks(
marketProxy.GetQdpMarket(valueDate),
!string.IsNullOrWhiteSpace(observationDateStr) ? QdpPricingRequest.BASIC_GREEKS : pricingRequest,
engine,
parameters);
if (useLocalDiscountCurve)
{
marketProxy.RemoveYieldCurve(discountCurveName, valueDate);
}
if (!string.IsNullOrEmpty(dividendCurveName))
{
marketProxy.RemoveYieldCurve(dividendCurveName, valueDate);
}
if (results.Succeeded)
{
//double vol = marketProxy.GetVol(valueDate, underlyingTicker, strike, endDate, spotPrice);
//if (isMoneynessOption)
//{
// vol = marketProxy.GetVol(valueDate, underlyingTicker, strike / spotPrice, endDate);
//}
//else
//{
// vol = marketProxy.GetVol(valueDate, underlyingTicker, strike, endDate);
//}
return new TradeValueResult()
{
UnderlyingCode = underlyingTicker,
Pv = results.Pv,
Delta = results.Delta,
Gamma = results.Gamma,
Vega = results.Vega,
CalendarDayTheta = results.Theta,
TradingDayTheta = results.ThetaPnL,
Rho = results.Rho,
DeltaCash = results.DeltaCash,
GammaCash = results.GammaCash,
Vol = results.PricingVol,
DDeltaDVol = results.DDeltaDvol,
DDeltaDt = results.DDeltaDt,
DVegaDVol = results.DVegaDvol,
DVegaDt = results.DVegaDt,
StoppingTime = results.StoppingTime,
RoundedPv = OtcFormatHelper.FormatValue(results.Pv / notional, 2) * notional
};
}
else
{
return null;
}
}
#endregion
#region 二元期权
private static BinaryOptionTrade CreateBinaryOptionTrade(
string tradeId,
string volSurfaceName,
string tradeDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
string binaryOptionPayoffType,
double cashOrNothingAmount,
double notional,
string tradeType,
string exerciseDate,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
double upperBarrier = double.NaN,
double cashOrNothingAmountHigh = double.NaN,
bool isDiscreateMonitored = false,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
Dictionary dividends = null,
string binaryRebateType = "AtEnd",
bool hasNightMarket = false,
bool preciseTimeMode = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN)
{
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
var exercise = QdpConverter.ConvertExerciseType(exerciseType);
var optionStartDate = startDate.ToDate();
var underlyingMaturityDate = string.IsNullOrWhiteSpace(endDate) ? null : endDate.ToDate();
var rebateType = (string.IsNullOrEmpty(binaryRebateType) ? "AtEnd" : binaryRebateType).ToBinaryRebateType();
var temp_exerciseDate = exerciseDate.ToDate();
if (temp_exerciseDate < optionStartDate)
{
return null;
}
var exerciseDates = new Date[] { temp_exerciseDate };
var observationDates = new Date[] { temp_exerciseDate };
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var pricingTOverride = double.IsNaN(timeToMaturityDays) ? double.NaN : QdpCalendarHelper.CalculateTFromDays(timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
var binaryOption =
new BinaryOption(
optionStartDate,
underlyingMaturityDate,
exercise,
(OptionType)Enum.Parse(typeof(OptionType), optionType),
strike,
(InstrumentType)Enum.Parse(typeof(InstrumentType), underlyingInstrumentType),
(BinaryOptionPayoffType)Enum.Parse(typeof(BinaryOptionPayoffType), binaryOptionPayoffType),
cashOrNothingAmount,
CalendarImpl.Get("chn"),
optionDayCount.ToDayCountImpl(),
CurrencyCode.CNY,
CurrencyCode.CNY,
exerciseDates,
observationDates,
notional,
null,
null,
0.0,
isMoneynessOption,
initialSpotPrice,
upperBarrier,
cashOrNothingAmountHigh,
isDiscreteMonitored: isDiscreateMonitored,
dividends: dividends,
binaryRebateType: rebateType,
pricingToverride: pricingTOverride,
hasNightMarket: hasNightMarket,
commodityFuturesPreciseTimeMode: preciseTimeMode,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
participationRate: participationRate,
isAnnualized: isAnnualized,
annualizedFactor: annualizeFactor)
{
UnderlyingTickers = new string[] { underlyingTicker }
};
if (string.IsNullOrWhiteSpace(tradeId))
{
tradeId = Guid.NewGuid().ToString();
}
return new BinaryOptionTrade(
tradeId,
tradeDate.ToDate(),
binaryOption.StartDate,
binaryOption.ExerciseDates.Last(),
QdpConverter.ConvertTradeType(tradeType),
binaryOption.Notional,
0.0,
binaryOption)
{
ValuationParameters = new OptionValuationParameters("RiskFreeDiscountCurve", MarketProxy.ConstantZeroCurve, volSurfaceName, binaryOption.UnderlyingTickers[0]),
};
}
public static TradeValueResult ValueBinaryOptionTrade(
IQdpMarketProxy marketProxy,
string valueDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
string payoffType,
double cashOrNothingAmount,
double spotPrice,
double notional,
string volSurfaceName,
double riskFreeRate,
string tradeType,
string exerciseDate,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
double upperBarrier = double.NaN,
double cashOrNothingAmountHigh = double.NaN,
bool isDiscreteMonitored = false,
double dividendRate = 0.0,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
Dictionary dividends = null,
string binaryRebateType = "AtEnd",
string binaryOptionReplicationStrategy = "None",
double replicationShiftSize = 0.0,
bool hasNightMarket = false,
bool preciseTimeMode = false,
string engineName = null,
string discountCurveName = null,
bool isForwardTrade = false,
bool ignoreSkewMap = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
return ValueBinaryOptionTrade(
VolSurfaceInitializerSingleton.VolConstructionType,
marketProxy,
valueDate,
underlyingTicker,
underlyingInstrumentType,
strike,
startDate,
endDate,
optionType,
exerciseType,
payoffType,
cashOrNothingAmount,
spotPrice,
notional,
volSurfaceName,
riskFreeRate,
tradeType,
exerciseDate,
participationRate,
principalRate,
isAnnualized,
annualizeFactor,
upperBarrier,
cashOrNothingAmountHigh,
isDiscreteMonitored,
dividendRate,
isMoneynessOption,
initialSpotPrice,
dividends,
binaryRebateType,
binaryOptionReplicationStrategy,
replicationShiftSize,
hasNightMarket,
preciseTimeMode,
engineName,
discountCurveName,
isForwardTrade,
ignoreSkewMap,
timeToMaturityDays: timeToMaturityDays,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
pricingRequest: pricingRequest);
}
private static TradeValueResult ValueBinaryOptionTrade(
VolConstructionType volConstructionType,
IQdpMarketProxy marketProxy,
string valueDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
string payoffType,
double cashOrNothingAmount,
double spotPrice,
double notional,
string volSurfaceName,
double riskFreeRate,
string tradeType,
string exerciseDate,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
double upperBarrier = double.NaN,
double cashOrNothingAmountHigh = double.NaN,
bool isDiscreteMonitored = false,
double dividendRate = 0.0,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
Dictionary dividends = null,
string binaryRebateType = "AtEnd",
string binaryOptionReplicationStrategy = "None",
double replicationShiftSize = 0.0,
bool hasNightMarket = false,
bool preciseTimeMode = false,
string engineName = null,
string discountCurveName = null,
bool isForwardTrade = false,
bool ignoreSkewMap = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
if (volConstructionType == VolConstructionType.Normal || ignoreSkewMap)
{
return ValueBinaryOption(
marketProxy,
valueDate,
underlyingTicker,
underlyingInstrumentType,
strike,
startDate,
endDate,
optionType,
exerciseType,
payoffType,
cashOrNothingAmount,
spotPrice,
notional,
volSurfaceName,
riskFreeRate,
tradeType,
exerciseDate,
participationRate,
principalRate,
isAnnualized,
annualizeFactor,
upperBarrier,
cashOrNothingAmountHigh,
isDiscreteMonitored,
dividendRate,
isMoneynessOption,
initialSpotPrice,
dividends,
binaryRebateType,
binaryOptionReplicationStrategy,
replicationShiftSize,
hasNightMarket,
preciseTimeMode,
engineName,
discountCurveName,
isForwardTrade,
timeToMaturityDays: timeToMaturityDays,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
pricingRequest: pricingRequest);
}
else
{
var tradeId = Guid.NewGuid().ToString();
SetSkewMapVol(
marketProxy,
valueDate,
underlyingTicker,
exerciseDate,
strike,
TradeCalcHelper.IsBuy(tradeType),
QdpHelper.IsCall(optionType),
tradeId,
spotPrice);
return ValueBinaryOption(
marketProxy,
valueDate,
underlyingTicker,
underlyingInstrumentType,
strike,
startDate,
endDate,
optionType,
exerciseType,
payoffType,
cashOrNothingAmount,
spotPrice,
notional,
tradeId.GenerateVolSurfaceKey(),
riskFreeRate,
tradeType,
exerciseDate,
participationRate,
principalRate,
isAnnualized,
annualizeFactor,
upperBarrier,
cashOrNothingAmountHigh,
isDiscreteMonitored,
dividendRate,
isMoneynessOption,
initialSpotPrice,
dividends,
binaryRebateType,
binaryOptionReplicationStrategy,
replicationShiftSize,
hasNightMarket,
preciseTimeMode,
engineName,
discountCurveName,
isForwardTrade,
timeToMaturityDays: timeToMaturityDays,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
pricingRequest: pricingRequest);
}
}
private static TradeValueResult ValueBinaryOption(
IQdpMarketProxy marketProxy,
string valueDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
string binaryOptionPayoffType,
double cashOrNothingAmount,
double spotPrice,
double notional,
string volSurfaceName,
double riskFreeRate,
string tradeType,
string exerciseDate,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
double upperBarrier = double.NaN,
double cashOrNothingAmountHigh = double.NaN,
bool isDiscreteMonitored = false,
double dividendRate = 0.0,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
Dictionary dividends = null,
string binaryRebateType = "AtEnd",
string binaryOptionReplicationStrategy = "None",
double replicationShiftSize = 0.0,
bool hasNightMarket = false,
bool preciseTimeMode = false,
string engineName = null,
string discountCurveName = null,
bool isForwardTrade = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
if (string.IsNullOrWhiteSpace(volSurfaceName))
{
volSurfaceName = underlyingTicker.GenerateVolSurfaceKey();
}
var optionTrade = CreateBinaryOptionTrade(
null,
volSurfaceName,
startDate,
underlyingTicker,
underlyingInstrumentType,
strike,
startDate,
endDate,
optionType,
exerciseType,
binaryOptionPayoffType,
cashOrNothingAmount,
notional,
tradeType,
exerciseDate,
participationRate,
principalRate,
isAnnualized,
annualizeFactor,
upperBarrier,
cashOrNothingAmountHigh,
isDiscreteMonitored,
isMoneynessOption,
initialSpotPrice,
dividends,
binaryRebateType,
hasNightMarket,
preciseTimeMode,
timeToMaturityDays: timeToMaturityDays,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride).CheckMaturityDate();
var market = marketProxy.GetQdpMarket(valueDate);
if (market == null)
{
return null;
}
var replicationStrategy = QdpConverter.ConvertReplicationStrategy(binaryOptionReplicationStrategy);
var useLocalDiscountCurve = string.IsNullOrWhiteSpace(discountCurveName);
//设置DiscountCurve
if (useLocalDiscountCurve)
{
discountCurveName = Guid.NewGuid().ToString();
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, riskFreeRate);
marketProxy.AddYieldCurve(discountCurveName, valueDate, discountCurve);
}
//设置标的价格
marketProxy.AddStockPrice(underlyingTicker, valueDate, spotPrice);
OptionValuationParameters parameters;
string dividendCurveName = null;
if (underlyingInstrumentType == "Stock")
{
//设置DividendCurve
dividendCurveName = Guid.NewGuid().ToString();
var dividendCurve = CalculatorHelper.CreateConstantRiskFreeCurve(dividendCurveName, dividendRate);
marketProxy.AddYieldCurve(dividendCurveName, valueDate, dividendCurve);
parameters = new OptionValuationParameters(
isForwardTrade ? MarketProxy.ConstantZeroCurve : discountCurveName,
dividendCurveName,
volSurfaceName,
underlyingTicker);
}
else
{
parameters = new OptionValuationParameters(
isForwardTrade ? MarketProxy.ConstantZeroCurve : discountCurveName,
MarketProxy.ConstantZeroCurve,
volSurfaceName,
underlyingTicker);
}
var option = optionTrade.Instrument as BinaryOption;
var factory = OptionEngineFactory.GetEngineFactory("BinaryOption");
var engine = factory.GetEngine(
engineName,
option.Exercise,
(replicationShiftSize <= 0.0) ? option.Strike * 0.01 : replicationShiftSize,
replicationStrategy);
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDate), pricingRequest, engine, parameters);
if (useLocalDiscountCurve)
{
marketProxy.RemoveYieldCurve(discountCurveName, valueDate);
}
if (!string.IsNullOrEmpty(dividendCurveName))
{
marketProxy.RemoveYieldCurve(dividendCurveName, valueDate);
}
if (results.Succeeded)
{
//double vol = marketProxy.GetVol(valueDate, underlyingTicker, strike, endDate, spotPrice);
//if (isMoneynessOption)
//{
// vol = marketProxy.GetVol(valueDate, underlyingTicker, strike / spotPrice, endDate);
//}
//else
//{
// vol = marketProxy.GetVol(valueDate, underlyingTicker, strike, endDate);
//}
return new TradeValueResult()
{
UnderlyingCode = underlyingTicker,
Pv = results.Pv,
Delta = results.Delta,
Gamma = results.Gamma,
Vega = results.Vega,
CalendarDayTheta = results.Theta,
TradingDayTheta = results.ThetaPnL,
Rho = results.Rho,
DeltaCash = results.DeltaCash,
GammaCash = results.GammaCash,
Vol = results.PricingVol,
DDeltaDVol = results.DDeltaDvol,
DDeltaDt = results.DDeltaDt,
DVegaDVol = results.DVegaDvol,
DVegaDt = results.DVegaDt,
StoppingTime = results.StoppingTime,
RoundedPv = OtcFormatHelper.FormatValue(results.Pv / notional, 2) * notional
};
}
else
{
return null;
}
}
#endregion
#region 亚式期权
private static AsianOptionTrade CreateAsianOptionTrade(
string tradeId,
string volSurfaceName,
string tradeDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
string asianType,
string strikeStyle,
double notional,
string tradeType,
string exerciseDate,
bool isAnnualized,
double annualizeFactor,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
double participationRate = 1.0,
double principalRate = 0.0,
double strikeGearingFactor = 1.0,
Dictionary dividends = null,
string averagingPeriodStartDate = null,
string observationDateStr = null,
string fixings = null,
bool hasNightMarket = false,
bool preciseTimeMode = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN)
{
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
var exercise = QdpConverter.ConvertExerciseType(exerciseType);
var optionStartDate = startDate.ToDate();
var underlyingMaturityDate = string.IsNullOrWhiteSpace(endDate) ? null : endDate.ToDate();
var temp_exerciseDate = exerciseDate.ToDate();
if (temp_exerciseDate < optionStartDate)
{
return null;
}
Date[] exerciseDates;
Date[] observationDates;
var calendar = CalendarImpl.Get("chn");
if (exercise == OptionExercise.American)
{
exerciseDates = calendar.BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray();
}
else
{
exerciseDates = new Date[] { temp_exerciseDate };
}
if (!string.IsNullOrWhiteSpace(observationDateStr))
{
observationDates = QdpHelper.ParseObservationDate(observationDateStr);
}
else
{
var observationStartDate = string.IsNullOrWhiteSpace(averagingPeriodStartDate) ? optionStartDate : averagingPeriodStartDate.ToDate();
observationDates = calendar.BizDaysBetweenDatesExcluStartDay(observationStartDate, temp_exerciseDate).Union(new[] { temp_exerciseDate }).ToArray();
}
var fixingValues = QdpHelper.ParseFixingsFromString(fixings);
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var pricingTOverride = double.IsNaN(timeToMaturityDays) ? double.NaN : QdpCalendarHelper.CalculateTFromDays(timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
var asianOption =
new AsianOption(
optionStartDate,
underlyingMaturityDate,
(OptionExercise)Enum.Parse(typeof(OptionExercise), exerciseType),
(OptionType)Enum.Parse(typeof(OptionType), optionType),
(AsianType)Enum.Parse(typeof(AsianType), asianType),
(StrikeStyle)Enum.Parse(typeof(StrikeStyle), strikeStyle),
strike,
(InstrumentType)Enum.Parse(typeof(InstrumentType), underlyingInstrumentType),
CalendarImpl.Get("chn"),
optionDayCount.ToDayCountImpl(),
CurrencyCode.CNY,
CurrencyCode.CNY,
exerciseDates,
observationDates,
fixingValues,
strikeGearingFactor,
participationRate,
notional,
null,
null,
0,
isMoneynessOption,
initialSpotPrice,
dividends,
hasNightMarket: hasNightMarket,
commodityFuturesPreciseTimeMode: preciseTimeMode,
pricingToverride: pricingTOverride,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
isAnnualized: isAnnualized,
annualizedFactor: annualizeFactor)
{
UnderlyingTickers = new string[] { underlyingTicker }
};
if (string.IsNullOrWhiteSpace(tradeId))
{
tradeId = Guid.NewGuid().ToString();
}
return new AsianOptionTrade(
tradeId,
tradeDate.ToDate(),
asianOption.StartDate,
asianOption.ExerciseDates.Last(),
QdpConverter.ConvertTradeType(tradeType),
asianOption.Notional,
0.0,
asianOption)
{
ValuationParameters = new OptionValuationParameters("RiskFreeDiscountCurve", MarketProxy.ConstantZeroCurve, volSurfaceName, asianOption.UnderlyingTickers[0]),
};
}
public static TradeValueResult ValueAsianOptionTrade(
IQdpMarketProxy marketProxy,
string valueDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
string asianType,
string strikeStyle,
double spotPrice,
double notional,
string volSurfaceName,
double riskFreeRate,
string tradeType,
string exerciseDate,
bool isAnnualized,
double annualizeFactor,
double dividendRate = 0.0,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
double participationRate = 1.0,
double principalRate = 0.0,
double strikeGearingFactor = 1.0,
Dictionary dividends = null,
string fixings = null,
string averagingPeriodStartDate = null,
string observationDateStr = null,
bool hasNightMarket = false,
bool preciseTimeMode = false,
string engineName = null,
string discountCurveName = null,
bool isForwardTrade = false,
bool ignoreSkewMap = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
return ValueAsianOptionTrade(
VolSurfaceInitializerSingleton.VolConstructionType,
marketProxy,
valueDate,
underlyingTicker,
underlyingInstrumentType,
strike,
startDate,
endDate,
optionType,
exerciseType,
asianType,
strikeStyle,
spotPrice,
notional,
volSurfaceName,
riskFreeRate,
tradeType,
exerciseDate,
isAnnualized,
annualizeFactor,
dividendRate,
isMoneynessOption,
initialSpotPrice,
participationRate,
principalRate,
strikeGearingFactor,
dividends,
fixings,
averagingPeriodStartDate,
observationDateStr,
hasNightMarket,
preciseTimeMode,
engineName,
discountCurveName,
isForwardTrade,
ignoreSkewMap,
timeToMaturityDays: timeToMaturityDays,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
pricingRequest: pricingRequest);
}
private static TradeValueResult ValueAsianOptionTrade(
VolConstructionType volConstructionType,
IQdpMarketProxy marketProxy,
string valueDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
string asianType,
string strikeStyle,
double spotPrice,
double notional,
string volSurfaceName,
double riskFreeRate,
string tradeType,
string exerciseDate,
bool isAnnualized,
double annualizeFactor,
double dividendRate = 0.0,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
double participationRate = 1.0,
double principalRate = 0.0,
double strikeGearingFactor = 1.0,
Dictionary dividends = null,
string fixings = null,
string averagingPeriodStartDate = null,
string observationDateStr = null,
bool hasNightMarket = false,
bool preciseTimeMode = false,
string engineName = null,
string discountCurveName = null,
bool isForwardTrade = false,
bool ignoreSkewMap = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
if (volConstructionType == VolConstructionType.Normal || ignoreSkewMap)
{
return ValueAsianOption(
marketProxy,
valueDate,
underlyingTicker,
underlyingInstrumentType,
strike,
startDate,
endDate,
optionType,
exerciseType,
asianType,
strikeStyle,
spotPrice,
notional,
volSurfaceName,
riskFreeRate,
tradeType,
exerciseDate,
isAnnualized,
annualizeFactor,
dividendRate,
isMoneynessOption,
initialSpotPrice,
participationRate,
principalRate,
strikeGearingFactor,
dividends,
fixings,
averagingPeriodStartDate,
observationDateStr,
hasNightMarket,
preciseTimeMode,
engineName,
discountCurveName,
isForwardTrade,
timeToMaturityDays: timeToMaturityDays,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
pricingRequest: pricingRequest);
}
else
{
var tradeId = Guid.NewGuid().ToString();
SetSkewMapVol(
marketProxy,
valueDate,
underlyingTicker,
exerciseDate,
strike,
TradeCalcHelper.IsBuy(tradeType),
QdpHelper.IsCall(optionType),
tradeId,
spotPrice);
return ValueAsianOption(
marketProxy,
valueDate,
underlyingTicker,
underlyingInstrumentType,
strike,
startDate,
endDate,
optionType,
exerciseType,
asianType,
strikeStyle,
spotPrice,
notional,
tradeId.GenerateVolSurfaceKey(),
riskFreeRate,
tradeType,
exerciseDate,
isAnnualized,
annualizeFactor,
dividendRate,
isMoneynessOption,
initialSpotPrice,
participationRate,
principalRate,
strikeGearingFactor,
dividends,
fixings,
averagingPeriodStartDate,
observationDateStr,
hasNightMarket,
preciseTimeMode,
engineName,
discountCurveName,
isForwardTrade,
timeToMaturityDays: timeToMaturityDays,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
pricingRequest: pricingRequest);
}
}
private static TradeValueResult ValueAsianOption(
IQdpMarketProxy marketProxy,
string valueDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
string asianType,
string strikeStyle,
double spotPrice,
double notional,
string volSurfaceName,
double riskFreeRate,
string tradeType,
string exerciseDate,
bool isAnnualized,
double annualizeFactor,
double dividendRate = 0.0,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
double participationRate = 1.0,
double principalRate = 0.0,
double strikeGearingFactor = 1.0,
Dictionary dividends = null,
string fixings = null,
string averagingPeriodStartDate = null,
string observationDateStr = null,
bool hasNightMarket = false,
bool preciseTimeMode = false,
string engineName = null,
string discountCurveName = null,
bool isForwardTrade = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
if (string.IsNullOrWhiteSpace(volSurfaceName))
{
volSurfaceName = underlyingTicker.GenerateVolSurfaceKey();
}
var optionTrade = CreateAsianOptionTrade(
null,
volSurfaceName,
startDate,
underlyingTicker,
underlyingInstrumentType,
strike,
startDate,
endDate,
optionType,
exerciseType,
asianType,
strikeStyle,
notional,
tradeType,
exerciseDate,
isAnnualized,
annualizeFactor,
isMoneynessOption,
initialSpotPrice,
participationRate,
principalRate,
strikeGearingFactor,
dividends,
averagingPeriodStartDate,
observationDateStr,
(asianType == "EnhancedArithmeticAverage" && strikeStyle != "Segmented" && string.IsNullOrWhiteSpace(fixings)) ? $"{startDate},{spotPrice}" : fixings,
hasNightMarket: hasNightMarket,
preciseTimeMode: preciseTimeMode,
timeToMaturityDays: timeToMaturityDays,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride).CheckMaturityDate();
var market = marketProxy.GetQdpMarket(valueDate);
if (market == null)
{
return null;
}
var useLocalDiscountCurve = string.IsNullOrWhiteSpace(discountCurveName);
//设置DiscountCurve
if (useLocalDiscountCurve)
{
discountCurveName = Guid.NewGuid().ToString();
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, riskFreeRate);
marketProxy.AddYieldCurve(discountCurveName, valueDate, discountCurve);
}
//设置标的价格
marketProxy.AddStockPrice(underlyingTicker, valueDate, spotPrice);
OptionValuationParameters parameters;
string dividendCurveName = null;
if (underlyingInstrumentType == "Stock")
{
//设置DividendCurve
dividendCurveName = Guid.NewGuid().ToString();
var dividendCurve = CalculatorHelper.CreateConstantRiskFreeCurve(dividendCurveName, dividendRate);
marketProxy.AddYieldCurve(dividendCurveName, valueDate, dividendCurve);
parameters = new OptionValuationParameters(
isForwardTrade ? MarketProxy.ConstantZeroCurve : discountCurveName,
dividendCurveName,
volSurfaceName,
underlyingTicker);
}
else
{
parameters = new OptionValuationParameters(
isForwardTrade ? MarketProxy.ConstantZeroCurve : discountCurveName,
MarketProxy.ConstantZeroCurve,
volSurfaceName,
underlyingTicker);
}
var option = optionTrade.Instrument as AsianOption;
var factory = OptionEngineFactory.GetEngineFactory("AsianOption");
var engine = factory.GetEngine(engineName, option.Exercise, asianType, option, strikeStyle);
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDate), pricingRequest, engine, parameters);
if (useLocalDiscountCurve)
{
marketProxy.RemoveYieldCurve(discountCurveName, valueDate);
}
if (!string.IsNullOrEmpty(dividendCurveName))
{
marketProxy.RemoveYieldCurve(dividendCurveName, valueDate);
}
if (results.Succeeded)
{
//double vol = marketProxy.GetVol(valueDate, underlyingTicker, strike, endDate, spotPrice);
//if (isMoneynessOption)
//{
// vol = marketProxy.GetVol(valueDate, underlyingTicker, strike / spotPrice, endDate);
//}
//else
//{
// vol = marketProxy.GetVol(valueDate, underlyingTicker, strike, endDate);
//}
return new TradeValueResult()
{
UnderlyingCode = underlyingTicker,
Pv = results.Pv,
Delta = results.Delta,
Gamma = results.Gamma,
Vega = results.Vega,
CalendarDayTheta = results.Theta,
TradingDayTheta = results.ThetaPnL,
Rho = results.Rho,
DeltaCash = results.DeltaCash,
GammaCash = results.GammaCash,
Vol = results.PricingVol,
DDeltaDVol = results.DDeltaDvol,
DDeltaDt = results.DDeltaDt,
DVegaDVol = results.DVegaDvol,
DVegaDt = results.DVegaDt,
StoppingTime = results.StoppingTime,
RoundedPv = OtcFormatHelper.FormatValue(results.Pv / notional, 2) * notional,
SA_Delta = results.SA_Delta,
TimeValue = results.TimeValue
};
}
else
{
return null;
}
}
#endregion
#region 彩虹期权
private static RainbowOptionTrade CreateRainbowOptionTrade(
string tradeId,
string[] volSurfaceNames,
string tradeDate,
string underlyingInstrumentType,
string[] underlyingTickers,
double[] strikes,
string startDate,
string endDate,
string optionType,
string exerciseType,
string rainbowType,
double cashAmount,
double notional,
string tradeType,
string exerciseDate,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
bool hasNightMarket = false,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
bool preciseTimeMode = false)
{
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
var optionStartDate = startDate.ToDate();
var underlyingMaturityDate = string.IsNullOrWhiteSpace(endDate) ? null : endDate.ToDate();
var temp_exerciseDate = exerciseDate.ToDate();
if (temp_exerciseDate < optionStartDate)
{
return null;
}
var exerciseDates = new[] { temp_exerciseDate };
var calendar = CalendarImpl.Get("chn");
var observationDates = calendar.BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray();
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var rainbowOption = new RainbowOption(
optionStartDate,
underlyingMaturityDate,
(OptionExercise)Enum.Parse(typeof(OptionExercise), exerciseType),
(OptionType)Enum.Parse(typeof(OptionType), optionType),
(RainbowType)Enum.Parse(typeof(RainbowType), rainbowType),
strikes,
cashAmount,
(InstrumentType)Enum.Parse(typeof(InstrumentType), underlyingInstrumentType),
CalendarImpl.Get("chn"),
optionDayCount.ToDayCountImpl(),
CurrencyCode.CNY,
CurrencyCode.CNY,
exerciseDates,
observationDates,
underlyingTickers,
notional,
null,
null,
0,
hasNightMarket: hasNightMarket,
commodityFuturesPreciseTimeMode: preciseTimeMode,
participationRate: participationRate,
isAnnualized: isAnnualized,
annualizedFactor: annualizeFactor,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride)
{
UnderlyingTickers = underlyingTickers
};
if (string.IsNullOrWhiteSpace(tradeId))
{
tradeId = Guid.NewGuid().ToString();
}
return new RainbowOptionTrade(
tradeId,
tradeDate.ToDate(),
rainbowOption.StartDate,
rainbowOption.ExerciseDates.Last(),
QdpConverter.ConvertTradeType(tradeType),
rainbowOption.Notional,
0.0,
rainbowOption)
{
ValuationParameters = new OptionValuationParameters(
"RiskFreeDiscountCurve",
new string[] { MarketProxy.ConstantZeroCurve, MarketProxy.ConstantZeroCurve },
volSurfaceNames,
new string[] { underlyingTickers[0] + "_" + underlyingTickers[1] },
null),
};
}
public static TradeValueResult ValueRainbowOptionTrade(
IQdpMarketProxy marketProxy,
string valueDate,
string underlyingInstrumentType,
string[] underlyingTickers,
string[] volSurfaceNames,
double[] strikes,
string startDate,
string endDate,
string optionType,
string exerciseType,
string rainbowType,
double cashAmount,
double[] spotPrices,
double notional,
double riskFreeRate,
double correlation,
string tradeType,
string exerciseDate,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
double dividendRate = 0.0,
bool hasNightMarket = false,
bool preciseTimeMode = false,
string engineName = null,
string discountCurveName = null,
bool isForwardTrade = false,
bool ignoreSkewMap = false,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
return ValueRainbowOptionTrade(
VolSurfaceInitializerSingleton.VolConstructionType,
marketProxy,
valueDate,
underlyingInstrumentType,
underlyingTickers,
volSurfaceNames,
strikes,
startDate,
endDate,
optionType,
exerciseType,
rainbowType,
cashAmount,
spotPrices,
notional,
riskFreeRate,
correlation,
tradeType,
exerciseDate,
participationRate,
principalRate,
isAnnualized,
annualizeFactor,
dividendRate,
hasNightMarket,
preciseTimeMode,
engineName,
discountCurveName,
isForwardTrade,
ignoreSkewMap,
riskFreeRateOverride,
dividendRateOverride,
pricingRequest);
}
private static TradeValueResult ValueRainbowOptionTrade(
VolConstructionType volConstructionType,
IQdpMarketProxy marketProxy,
string valueDate,
string underlyingInstrumentType,
string[] underlyingTickers,
string[] volSurfaceNames,
double[] strikes,
string startDate,
string endDate,
string optionType,
string exerciseType,
string rainbowType,
double cashAmount,
double[] spotPrices,
double notional,
double riskFreeRate,
double correlation,
string tradeType,
string exerciseDate,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
double dividendRate = 0.0,
bool hasNightMarket = false,
bool preciseTimeMode = false,
string engineName = null,
string discountCurveName = null,
bool isForwardTrade = false,
bool ignoreSkewMap = false,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
if (volConstructionType == VolConstructionType.Normal || ignoreSkewMap)
{
return ValueRainbowOption(
marketProxy,
valueDate,
underlyingInstrumentType,
underlyingTickers,
volSurfaceNames,
strikes,
startDate,
endDate,
optionType,
exerciseType,
rainbowType,
cashAmount,
spotPrices,
notional,
riskFreeRate,
correlation,
tradeType,
exerciseDate,
participationRate,
principalRate,
isAnnualized,
annualizeFactor,
dividendRate,
hasNightMarket,
preciseTimeMode,
engineName,
discountCurveName,
isForwardTrade,
riskFreeRateOverride,
dividendRateOverride,
pricingRequest);
}
else
{
var tradeId1 = Guid.NewGuid().ToString();
SetSkewMapVol(
marketProxy,
valueDate,
underlyingTickers[0],
exerciseDate,
strikes[0],
TradeCalcHelper.IsBuy(tradeType),
QdpHelper.IsCall(optionType),
tradeId1,
spotPrices[0]);
var tradeId2 = Guid.NewGuid().ToString();
SetSkewMapVol(
marketProxy,
valueDate,
underlyingTickers[1],
exerciseDate,
strikes[1],
TradeCalcHelper.IsBuy(tradeType),
QdpHelper.IsCall(optionType),
tradeId2,
spotPrices[1]);
return ValueRainbowOption(
marketProxy,
valueDate,
underlyingInstrumentType,
underlyingTickers,
new string[] { tradeId1.GenerateVolSurfaceKey(), tradeId2.GenerateVolSurfaceKey() },
strikes,
startDate,
endDate,
optionType,
exerciseType,
rainbowType,
cashAmount,
spotPrices,
notional,
riskFreeRate,
correlation,
tradeType,
exerciseDate,
participationRate,
principalRate,
isAnnualized,
annualizeFactor,
dividendRate,
hasNightMarket,
preciseTimeMode,
engineName,
discountCurveName,
isForwardTrade,
riskFreeRateOverride,
dividendRateOverride,
pricingRequest);
}
}
private static TradeValueResult ValueRainbowOption(
IQdpMarketProxy marketProxy,
string valueDate,
string underlyingInstrumentType,
string[] underlyingTickers,
string[] volSurfaceNames,
double[] strikes,
string startDate,
string endDate,
string optionType,
string exerciseType,
string rainbowType,
double cashAmount,
double[] spotPrices,
double notional,
double riskFreeRate,
double correlation,
string tradeType,
string exerciseDate,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
double dividendRate = 0.0,
bool hasNightMarket = false,
bool preciseTimeMode = false,
string engineName = null,
string discountCurveName = null,
bool isForwardTrade = false,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
volSurfaceNames ??= underlyingTickers.Select(x => x.GenerateVolSurfaceKey()).ToArray();
var optionTrade = CreateRainbowOptionTrade(
null,
volSurfaceNames,
startDate,
underlyingInstrumentType,
underlyingTickers,
strikes,
startDate,
endDate,
optionType,
exerciseType,
rainbowType,
cashAmount,
notional,
tradeType,
exerciseDate,
participationRate,
principalRate,
isAnnualized,
annualizeFactor,
hasNightMarket: hasNightMarket,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
preciseTimeMode: preciseTimeMode).CheckMaturityDate();
var market = marketProxy.GetQdpMarket(valueDate);
if (market == null)
{
return null;
}
var useLocalDiscountCurve = string.IsNullOrWhiteSpace(discountCurveName);
//设置DiscountCurve
if (useLocalDiscountCurve)
{
discountCurveName = Guid.NewGuid().ToString();
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, riskFreeRate);
marketProxy.AddYieldCurve(discountCurveName, valueDate, discountCurve);
}
//设置标的价格
var count = Math.Min(underlyingTickers.Length, spotPrices.Length);
for (var i = 0; i < count; ++i)
{
marketProxy.AddStockPrice(underlyingTickers[i], valueDate, spotPrices[i]);
}
//设置correlation
marketProxy.SaveCorrelationSurface(valueDate, underlyingTickers, correlation);
OptionValuationParameters parameters = null;
string dividendCurveName = null;
if (underlyingInstrumentType == "Stock")
{
//设置DividendCurve
dividendCurveName = Guid.NewGuid().ToString();
var dividendCurve = CalculatorHelper.CreateConstantRiskFreeCurve(dividendCurveName, dividendRate);
marketProxy.AddYieldCurve(dividendCurveName, valueDate, dividendCurve);
parameters = new OptionValuationParameters(
isForwardTrade ? MarketProxy.ConstantZeroCurve : discountCurveName,
new string[] { dividendCurveName, dividendCurveName },
volSurfaceNames,
new string[] { underlyingTickers[0] + "_" + underlyingTickers[1] },
null);
}
else
{
parameters = new OptionValuationParameters(
isForwardTrade ? MarketProxy.ConstantZeroCurve : discountCurveName,
new string[] { MarketProxy.ConstantZeroCurve, MarketProxy.ConstantZeroCurve },
volSurfaceNames,
new string[] { underlyingTickers[0] + "_" + underlyingTickers[1] },
null);
}
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDate), pricingRequest, null, parameters);
if (useLocalDiscountCurve)
{
marketProxy.RemoveYieldCurve(discountCurveName, valueDate);
}
if (!string.IsNullOrEmpty(dividendCurveName))
{
marketProxy.RemoveYieldCurve(dividendCurveName, valueDate);
}
if (results.Succeeded)
{
//var vol = 0.0;
return new TradeValueResult()
{
UnderlyingCode = underlyingTickers[0],
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 = underlyingTickers[1],
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 / notional, 2) * notional
};
}
else
{
return null;
}
}
#endregion
#region 价差期权
private static SpreadOptionTrade CreateSpreadOptionTrade(
string tradeId,
string[] volSurfaceNames,
string tradeDate,
string underlyingInstrumentType,
string[] underlyingTickers,
double[] weights,
SpreadType spreadType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
double notional,
string tradeType,
string exerciseDate,
double participationRate,
bool isAnnualized,
double annualizeFactor,
bool hasNightMarket = false,
double riskFreeRateOverride = double.NaN,
bool preciseTimeMode = false)
{
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
var exercise = QdpConverter.ConvertExerciseType(exerciseType);
var optionStartDate = startDate.ToDate();
var underlyingMaturityDate = string.IsNullOrWhiteSpace(endDate) ? null : endDate.ToDate();
var temp_exerciseDate = exerciseDate.ToDate();
if (temp_exerciseDate < optionStartDate)
{
return null;
}
var exerciseDates = new[] { temp_exerciseDate };
var calendar = CalendarImpl.Get("chn");
var observationDates = calendar.BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray();
var normalizedWeight = new double[] { 1.0, 1.0, 1.0, 1.0 };
for (var i = 0; i < weights.Length; i++)
{
normalizedWeight[i] = weights[i];
}
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var spreadOption = new SpreadOption(
optionStartDate,
underlyingMaturityDate,
exercise,
(OptionType)Enum.Parse(typeof(OptionType), optionType),
spreadType,
normalizedWeight,
strike,
(InstrumentType)Enum.Parse(typeof(InstrumentType), underlyingInstrumentType),
CalendarImpl.Get("chn"),
optionDayCount.ToDayCountImpl(),
CurrencyCode.CNY,
CurrencyCode.CNY,
exerciseDates,
observationDates,
underlyingTickers,
notional,
null,
null,
0,
hasNightMarket: hasNightMarket,
commodityFuturesPreciseTimeMode: preciseTimeMode,
participationRate: participationRate,
isAnnualized: isAnnualized,
riskFreeRateOverride: riskFreeRateOverride,
annualizedFactor: annualizeFactor);
if (string.IsNullOrWhiteSpace(tradeId))
{
tradeId = Guid.NewGuid().ToString();
}
return new SpreadOptionTrade(
tradeId,
tradeDate.ToDate(),
spreadOption.StartDate,
spreadOption.ExerciseDates.Last(),
QdpConverter.ConvertTradeType(tradeType),
spreadOption.Notional,
0.0,
spreadOption)
{
ValuationParameters = createParametersForSpreadOption(
underlyingTickers,
"RiskFreeDiscountCurve",
volSurfaceNames)
};
}
public static TradeValueResult ValueSpreadOption(
IQdpMarketProxy marketProxy,
string valueDate,
string underlyingInstrumentType,
string[] underlyingTickers,
double[] weights,
SpreadType spreadType,
string[] volSurfaceNames,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
double[] spotPrices,
double notional,
double riskFreeRate,
double[] correlations,
string tradeType,
string exerciseDate,
double participationRate,
bool isAnnualized,
double annualizeFactor,
double[] dividendRates = null,
bool hasNightMarket = false,
bool preciseTimeMode = false,
string engineName = null,
string discountCurveName = null,
double riskFreeRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
volSurfaceNames ??= underlyingTickers.Select(x => x.GenerateVolSurfaceKey()).ToArray();
var optionTrade = CreateSpreadOptionTrade(
null,
volSurfaceNames,
startDate,
underlyingInstrumentType,
underlyingTickers,
weights,
spreadType,
strike,
startDate,
endDate,
optionType,
exerciseType,
notional,
tradeType,
exerciseDate,
participationRate,
isAnnualized,
annualizeFactor,
hasNightMarket: hasNightMarket,
riskFreeRateOverride: riskFreeRateOverride,
preciseTimeMode: preciseTimeMode).CheckMaturityDate();
var market = marketProxy.GetQdpMarket(valueDate);
if (market == null)
{
return null;
}
var useLocalDiscountCurve = string.IsNullOrWhiteSpace(discountCurveName);
//设置DiscountCurve
if (useLocalDiscountCurve)
{
discountCurveName = Guid.NewGuid().ToString();
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, riskFreeRate);
marketProxy.AddYieldCurve(discountCurveName, valueDate, discountCurve);
}
//设置标的价格
var count = Math.Min(underlyingTickers.Length, spotPrices.Length);
for (var i = 0; i < count; ++i)
{
marketProxy.AddStockPrice(underlyingTickers[i], valueDate, spotPrices[i]);
}
//设置correlation
marketProxy.SaveCorrelationSurface(valueDate, new string[] { underlyingTickers[0], underlyingTickers[1] }, correlations[0]);
if (underlyingTickers.Length >= 3)
{
marketProxy.SaveCorrelationSurface(valueDate, new string[] { underlyingTickers[0], underlyingTickers[2] }, correlations[1]);
marketProxy.SaveCorrelationSurface(valueDate, new string[] { underlyingTickers[1], underlyingTickers[2] }, correlations[2]);
if (underlyingTickers.Length >= 4)
{
marketProxy.SaveCorrelationSurface(valueDate, new string[] { underlyingTickers[0], underlyingTickers[3] }, correlations[3]);
marketProxy.SaveCorrelationSurface(valueDate, new string[] { underlyingTickers[1], underlyingTickers[3] }, correlations[4]);
marketProxy.SaveCorrelationSurface(valueDate, new string[] { underlyingTickers[2], underlyingTickers[3] }, correlations[5]);
}
}
var parameters = createParametersForSpreadOption(underlyingTickers, discountCurveName, volSurfaceNames);
var factory = OptionEngineFactory.GetEngineFactory("SpreadOption");
var engine = factory.GetEngine(engineName);
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDate), pricingRequest, engine, parameters);
if (useLocalDiscountCurve)
{
marketProxy.RemoveYieldCurve(discountCurveName, valueDate);
}
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 / notional, 2) * notional,
UnderlyingCode = underlyingTickers[0],
UnderlyingCode2 = underlyingTickers[1]
};
if (underlyingTickers.Length > 2)
{
valueResult.UnderlyingCode3 = underlyingTickers[2];
}
if (underlyingTickers.Length > 3)
{
valueResult.UnderlyingCode4 = underlyingTickers[3];
}
return valueResult;
}
else
{
return null;
}
}
private static OptionValuationParameters createParametersForSpreadOption(
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);
}
#endregion
#region 合成价差期权
public static double[] CalculateSyntheticNormalSpreadCrossGammas(
IQdpMarketProxy marketProxy,
string valueDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
double spotPrice,
double[] coefficients,
double notional,
string volSurfaceName,
double riskFreeRate,
string tradeType,
string exerciseDate,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
double dividendRate = 0.0,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
Dictionary dividends = null,
bool hasNightMarket = false,
bool preciseTimeMode = false,
string engineName = null,
string discountCurveName = null,
bool ignoreSkewMap = false,
bool isForwardTrade = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN)
{
if (coefficients == null || coefficients.Length == 1)
{
return null;
}
//处理光大光子的波动率模式
string tradeId = null;
if (VolSurfaceInitializerSingleton.VolConstructionType == VolConstructionType.SkewMap && !ignoreSkewMap)
{
tradeId = Guid.NewGuid().ToString();
SetSkewMapVol(
marketProxy,
valueDate,
underlyingTicker,
exerciseDate,
strike,
TradeCalcHelper.IsBuy(tradeType),
QdpHelper.IsCall(optionType),
tradeId,
spotPrice);
volSurfaceName = tradeId.GenerateVolSurfaceKey();
}
var trade = CreateSyntheticNormalSpreadOptionTrade(
tradeId,
volSurfaceName,
valueDate,
underlyingTicker,
underlyingInstrumentType,
strike,
startDate,
endDate,
optionType,
exerciseType,
notional,
tradeType,
exerciseDate,
participationRate,
principalRate,
isAnnualized,
annualizeFactor,
coefficients,
isMoneynessOption,
initialSpotPrice,
dividends,
hasNightMarket,
preciseTimeMode,
timeToMaturityDays,
riskFreeRateOverride,
dividendRateOverride);
var market = marketProxy.GetQdpMarket(valueDate);
if (market == null)
{
return null;
}
var useLocalDiscountCurve = string.IsNullOrWhiteSpace(discountCurveName);
//设置DiscountCurve
if (useLocalDiscountCurve)
{
discountCurveName = Guid.NewGuid().ToString();
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, riskFreeRate);
marketProxy.AddYieldCurve(discountCurveName, valueDate, discountCurve);
}
//设置标的价格
marketProxy.AddStockPrice(underlyingTicker, valueDate, spotPrice);
OptionValuationParameters parameters;
if (underlyingInstrumentType == "Stock")
{
//设置DividendCurve
var dividendCurveName = Guid.NewGuid().ToString();
var dividendCurve = CalculatorHelper.CreateConstantRiskFreeCurve(dividendCurveName, dividendRate);
marketProxy.AddYieldCurve(dividendCurveName, valueDate, dividendCurve);
parameters = new OptionValuationParameters(
isForwardTrade ? MarketProxy.ConstantZeroCurve : discountCurveName,
dividendCurveName,
volSurfaceName,
underlyingTicker);
}
else
{
parameters = new OptionValuationParameters(
isForwardTrade ? MarketProxy.ConstantZeroCurve : discountCurveName,
MarketProxy.ConstantZeroCurve,
volSurfaceName,
underlyingTicker);
}
var result = trade.CalcCrossGammas(marketProxy.GetQdpMarket(valueDate), parameters);
if (result == null)
{
return null;
}
var crossGammas = new List();
// 先获取返回的结果矩阵中的对角线上的元素,对应的是每个标的的Gamma
for (var i = 0; i < coefficients.Length; ++i)
{
crossGammas.Add(result[i, i]);
}
// 再获取两两对应的Cross Gamma
for (var i = 0; i < coefficients.Length - 1; ++i)
{
for (var j = i + 1; j < coefficients.Length; ++j)
{
crossGammas.Add(result[i, j]);
}
}
return crossGammas.ToArray();
}
public static TradeValueResult ValueSyntheticNormalSpreadOptionTrade(
IQdpMarketProxy marketProxy,
string valueDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
double spotPrice,
double notional,
string volSurfaceName,
double riskFreeRate,
string modelName,
string tradeType,
string exerciseDate,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
double dividendRate = 0.0,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
Dictionary dividends = null,
bool hasNightMarket = false,
bool preciseTimeMode = false,
string engineName = null,
string discountCurveName = null,
bool ignoreSkewMap = false,
bool isForwardTrade = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
return ValueSyntheticNormalSpreadOptionTrade(
VolSurfaceInitializerSingleton.VolConstructionType,
marketProxy,
valueDate,
underlyingTicker,
underlyingInstrumentType,
strike,
startDate,
endDate,
optionType,
exerciseType,
spotPrice,
notional,
volSurfaceName,
riskFreeRate,
modelName,
tradeType,
exerciseDate,
participationRate,
principalRate,
isAnnualized,
annualizeFactor,
dividendRate,
isMoneynessOption,
initialSpotPrice,
dividends,
hasNightMarket,
preciseTimeMode,
engineName,
discountCurveName,
ignoreSkewMap,
isForwardTrade,
timeToMaturityDays: timeToMaturityDays,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
pricingRequest: pricingRequest);
}
private static TradeValueResult ValueSyntheticNormalSpreadOptionTrade(
VolConstructionType volConstructionType,
IQdpMarketProxy marketProxy,
string valueDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
double spotPrice,
double notional,
string volSurfaceName,
double riskFreeRate,
string modelName,
string tradeType,
string exerciseDate,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
double dividendRate = 0.0,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
Dictionary dividends = null,
bool hasNightMarket = false,
bool preciseTimeMode = false,
string engineName = null,
string discountCurveName = null,
bool ignoreSkewMap = false,
bool isForwardTrade = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
if (volConstructionType == VolConstructionType.Normal || ignoreSkewMap)
{
return ValueSyntheticNormalSpreadOption(
marketProxy,
valueDate,
underlyingTicker,
underlyingInstrumentType,
strike,
startDate,
endDate,
optionType,
exerciseType,
spotPrice,
notional,
volSurfaceName,
riskFreeRate,
modelName,
tradeType,
exerciseDate,
participationRate,
principalRate,
isAnnualized,
annualizeFactor,
dividendRate,
isMoneynessOption,
initialSpotPrice,
dividends,
hasNightMarket,
preciseTimeMode,
engineName,
discountCurveName,
isForwardTrade,
timeToMaturityDays: timeToMaturityDays,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
pricingRequest: pricingRequest);
}
else
{
var tradeId = Guid.NewGuid().ToString();
SetSkewMapVol(
marketProxy,
valueDate,
underlyingTicker,
exerciseDate,
strike,
TradeCalcHelper.IsBuy(tradeType),
QdpHelper.IsCall(optionType),
tradeId,
spotPrice);
return ValueSyntheticNormalSpreadOption(
marketProxy,
valueDate,
underlyingTicker,
underlyingInstrumentType,
strike,
startDate,
endDate,
optionType,
exerciseType,
spotPrice,
notional,
tradeId.GenerateVolSurfaceKey(),
riskFreeRate,
modelName,
tradeType,
exerciseDate,
participationRate,
principalRate,
isAnnualized,
annualizeFactor,
dividendRate,
isMoneynessOption,
initialSpotPrice,
dividends,
hasNightMarket,
preciseTimeMode,
engineName,
discountCurveName,
isForwardTrade,
timeToMaturityDays: timeToMaturityDays,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
pricingRequest: pricingRequest);
}
}
private static TradeValueResult ValueSyntheticNormalSpreadOption(
IQdpMarketProxy marketProxy,
string valueDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
double spotPrice,
double notional,
string volSurfaceName,
double riskFreeRate,
string modelName,
string tradeType,
string exerciseDate,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
double dividendRate = 0.0,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
Dictionary dividends = null,
bool hasNightMarket = false,
bool preciseTimeMode = false,
string engineName = null,
string discountCurveName = null,
bool isForwardTrade = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
if (string.IsNullOrWhiteSpace(volSurfaceName))
{
volSurfaceName = underlyingTicker.GenerateVolSurfaceKey();
}
var optionTrade = CreateSyntheticNormalSpreadOptionTrade(
null,
volSurfaceName,
startDate,
underlyingTicker,
underlyingInstrumentType,
strike,
startDate,
endDate,
optionType,
exerciseType,
notional,
tradeType,
exerciseDate,
participationRate,
principalRate,
isAnnualized,
annualizeFactor,
null, // coefficients
isMoneynessOption,
initialSpotPrice,
dividends,
hasNightMarket: hasNightMarket,
preciseTimeMode: preciseTimeMode,
timeToMaturityDays: timeToMaturityDays,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride).CheckMaturityDate();
var market = marketProxy.GetQdpMarket(valueDate);
if (market == null)
{
return null;
}
var useLocalDiscountCurve = string.IsNullOrWhiteSpace(discountCurveName);
//设置DiscountCurve
if (useLocalDiscountCurve)
{
discountCurveName = Guid.NewGuid().ToString();
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, riskFreeRate);
marketProxy.AddYieldCurve(discountCurveName, valueDate, discountCurve);
}
//设置标的价格
marketProxy.AddStockPrice(underlyingTicker, valueDate, spotPrice);
OptionValuationParameters parameters;
string dividendCurveName = null;
if (underlyingInstrumentType == "Stock")
{
//设置DividendCurve
dividendCurveName = Guid.NewGuid().ToString();
var dividendCurve = CalculatorHelper.CreateConstantRiskFreeCurve(dividendCurveName, dividendRate);
marketProxy.AddYieldCurve(dividendCurveName, valueDate, dividendCurve);
parameters = new OptionValuationParameters(
isForwardTrade ? MarketProxy.ConstantZeroCurve : discountCurveName,
dividendCurveName,
volSurfaceName,
underlyingTicker);
}
else
{
parameters = new OptionValuationParameters(
isForwardTrade ? MarketProxy.ConstantZeroCurve : discountCurveName,
MarketProxy.ConstantZeroCurve,
volSurfaceName,
underlyingTicker);
}
try
{
var option = optionTrade.Instrument as VanillaOption;
var factory = OptionEngineFactory.GetEngineFactory("SynthticNormalSpreadOption");
var engine = factory.GetEngine(engineName, option.Exercise);
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDate), pricingRequest, engine, parameters);
if (useLocalDiscountCurve)
{
marketProxy.RemoveYieldCurve(discountCurveName, valueDate);
}
if (!string.IsNullOrEmpty(dividendCurveName))
{
marketProxy.RemoveYieldCurve(dividendCurveName, valueDate);
}
if (results.Succeeded)
{
//double vol = marketProxy.GetVol(valueDate, underlyingTicker, strike, endDate, spotPrice);
//if (isMoneynessOption)
//{
// vol = marketProxy.GetVol(valueDate, underlyingTicker, strike / spotPrice, endDate);
//}
//else
//{
// vol = marketProxy.GetVol(valueDate, underlyingTicker, strike, endDate);
//}
var ret = new TradeValueResult()
{
UnderlyingCode = underlyingTicker,
Pv = results.Pv,
Delta = results.Delta,
Gamma = results.Gamma,
Vega = results.Vega,
CalendarDayTheta = results.Theta,
TradingDayTheta = results.ThetaPnL,
Rho = results.Rho,
DeltaCash = results.DeltaCash,
GammaCash = results.GammaCash,
Vol = results.PricingVol,
RoundedPv = OtcFormatHelper.FormatValue(results.Pv / notional, 2) * notional
};
return ret;
}
else
{
return null;
}
}
catch
{
throw;
}
}
private static SyntheticNormalSpreadOptionTrade CreateSyntheticNormalSpreadOptionTrade(
string tradeId,
string volSurfaceName,
string tradeDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
double notional,
string tradeType,
string exerciseDate,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
double[] coefficients = null,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
Dictionary dividends = null,
bool hasNightMarket = false,
bool preciseTimeMode = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN)
{
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
var exercise = QdpConverter.ConvertExerciseType(exerciseType);
var optionStartDate = startDate.ToDate();
var temp_exerciseDate = exerciseDate.ToDate();
if (temp_exerciseDate < optionStartDate)
{
return null;
}
Date[] exerciseDates;
Date[] observationDates;
if (exercise == OptionExercise.American)
{
exerciseDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray();
observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray();
}
else
{
exerciseDates = new Date[] { temp_exerciseDate };
observationDates = new Date[] { temp_exerciseDate };
}
if (underlyingInstrumentType == null)
{
throw new Exception("标的资产类型不能为空");
}
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var pricingTOverride = double.IsNaN(timeToMaturityDays) ? double.NaN : QdpCalendarHelper.CalculateTFromDays(timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
var syntheticNormalSpreadOption =
new SyntheticNormalSpreadOption(
optionStartDate,
exercise,
(OptionType)Enum.Parse(typeof(OptionType), optionType),
strike,
(InstrumentType)Enum.Parse(typeof(InstrumentType), underlyingInstrumentType),
CalendarImpl.Get("chn"),
optionDayCount.ToDayCountImpl(),
CurrencyCode.CNY,
CurrencyCode.CNY,
exerciseDates,
observationDates,
coefficients, // coefficients
notional,
null,
null,
0.0,
isMoneynessOption,
initialSpotPrice,
dividends,
hasNightMarket: hasNightMarket,
commodityFuturesPreciseTimeMode: preciseTimeMode,
pricingToverride: pricingTOverride,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
participationRate: participationRate,
isAnnualized: isAnnualized,
annualizedFactor: annualizeFactor)
{
UnderlyingTickers = new string[] { underlyingTicker }
};
if (string.IsNullOrWhiteSpace(tradeId))
{
tradeId = Guid.NewGuid().ToString();
}
return new SyntheticNormalSpreadOptionTrade(
tradeId,
tradeDate.ToDate(),
syntheticNormalSpreadOption.StartDate,
syntheticNormalSpreadOption.ExerciseDates.Last(),
QdpConverter.ConvertTradeType(tradeType),
syntheticNormalSpreadOption.Notional,
0.0,
syntheticNormalSpreadOption)
{
ValuationParameters = new OptionValuationParameters("RiskFreeDiscountCurve", MarketProxy.ConstantZeroCurve, volSurfaceName, syntheticNormalSpreadOption.UnderlyingTickers[0]),
};
}
#endregion
#region 亚式合成价差期权
private static AsianSyntheticNormalSpreadOptionTrade CreateAsianSyntheticNormalSpreadOptionTrade(
string tradeId,
string volSurfaceName,
string tradeDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
string asianType,
string strikeStyle,
double notional,
string tradeType,
string exerciseDate,
bool isAnnualized,
double annualizeFactor,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
Dictionary dividends = null,
string averagingPeriodStartDate = null,
string fixings = null,
bool hasNightMarket = false,
bool preciseTimeMode = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN)
{
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
var exercise = QdpConverter.ConvertExerciseType(exerciseType);
var optionStartDate = startDate.ToDate();
var underlyingMaturityDate = string.IsNullOrWhiteSpace(endDate) ? null : endDate.ToDate();
var temp_exerciseDate = exerciseDate.ToDate();
if (temp_exerciseDate < optionStartDate)
{
return null;
}
Date[] exerciseDates = null;
Date[] observationDates = null;
var calendar = CalendarImpl.Get("chn");
if (exercise == OptionExercise.American)
{
exerciseDates = calendar.BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray();
}
else
{
exerciseDates = new Date[] { temp_exerciseDate };
}
observationDates = calendar.BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).Union(new[] { temp_exerciseDate }).ToArray();
if (!string.IsNullOrWhiteSpace(averagingPeriodStartDate))
{
observationDates = calendar.BizDaysBetweenDatesInclEndDay(averagingPeriodStartDate.ToDate(), exerciseDates.Last()).ToArray();
}
var fixingValues = string.IsNullOrWhiteSpace(fixings)
? new Dictionary() :
fixings.Split(QdpConsts.Semilicon)
.Select(x =>
{
var splits = x.Split(QdpConsts.Comma);
return Tuple.Create(splits[0].ToDate(), double.Parse(splits[1]));
}).ToDictionary(x => x.Item1, x => x.Item2);
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var pricingTOverride = double.IsNaN(timeToMaturityDays) ? double.NaN : QdpCalendarHelper.CalculateTFromDays(timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
var asianSyntheticNormalSpreadOption =
new AsianSyntheticNormalSpreadOption(
optionStartDate,
(OptionExercise)Enum.Parse(typeof(OptionExercise), exerciseType),
(OptionType)Enum.Parse(typeof(OptionType), optionType),
(AsianType)Enum.Parse(typeof(AsianType), asianType),
(StrikeStyle)Enum.Parse(typeof(StrikeStyle), strikeStyle),
strike,
(InstrumentType)Enum.Parse(typeof(InstrumentType), underlyingInstrumentType),
CalendarImpl.Get("chn"),
optionDayCount.ToDayCountImpl(),
CurrencyCode.CNY,
CurrencyCode.CNY,
exerciseDates,
observationDates,
fixingValues,
1.0, //StrikeGearingFator
1.0, //ParticipationRate
notional,
null,
null,
0,
isMoneynessOption,
initialSpotPrice,
dividends,
hasNightMarket: hasNightMarket,
commodityFuturesPreciseTimeMode: preciseTimeMode,
pricingToverride: pricingTOverride,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
isAnnualized: isAnnualized,
annualizedFactor: annualizeFactor)
{
UnderlyingTickers = new string[] { underlyingTicker }
};
if (string.IsNullOrWhiteSpace(tradeId))
{
tradeId = Guid.NewGuid().ToString();
}
return new AsianSyntheticNormalSpreadOptionTrade(
tradeId,
tradeDate.ToDate(),
asianSyntheticNormalSpreadOption.StartDate,
asianSyntheticNormalSpreadOption.ExerciseDates.Last(),
QdpConverter.ConvertTradeType(tradeType),
asianSyntheticNormalSpreadOption.Notional,
0.0,
asianSyntheticNormalSpreadOption)
{
ValuationParameters = new OptionValuationParameters("RiskFreeDiscountCurve", MarketProxy.ConstantZeroCurve, volSurfaceName, asianSyntheticNormalSpreadOption.UnderlyingTickers[0])
};
}
public static TradeValueResult ValueAsianSyntheticNormalSpreadOptionTrade(
IQdpMarketProxy marketProxy,
string valueDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
string asianType,
string strikeStyle,
double spotPrice,
double notional,
string volSurfaceName,
double riskFreeRate,
string tradeType,
string exerciseDate,
bool isAnnualized,
double annualizeFactor,
double dividendRate = 0.0,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
Dictionary dividends = null,
string fixings = null,
string averagingPeriodStartDate = null,
bool hasNightMarket = false,
bool preciseTimeMode = false,
string engineName = null,
string discountCurveName = null,
bool isForwardTrade = false,
bool ignoreSkewMap = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
return ValueAsianSyntheticNormalSpreadOptionTrade(
VolSurfaceInitializerSingleton.VolConstructionType,
marketProxy,
valueDate,
underlyingTicker,
underlyingInstrumentType,
strike,
startDate,
endDate,
optionType,
exerciseType,
asianType,
strikeStyle,
spotPrice,
notional,
volSurfaceName,
riskFreeRate,
tradeType,
exerciseDate,
isAnnualized,
annualizeFactor,
dividendRate,
isMoneynessOption,
initialSpotPrice,
dividends,
fixings,
averagingPeriodStartDate,
hasNightMarket,
preciseTimeMode,
engineName,
discountCurveName,
isForwardTrade,
ignoreSkewMap,
timeToMaturityDays: timeToMaturityDays,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
pricingRequest: pricingRequest);
}
private static TradeValueResult ValueAsianSyntheticNormalSpreadOptionTrade(
VolConstructionType volConstructionType,
IQdpMarketProxy marketProxy,
string valueDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
string asianType,
string strikeStyle,
double spotPrice,
double notional,
string volSurfaceName,
double riskFreeRate,
string tradeType,
string exerciseDate,
bool isAnnualized,
double annualizeFactor,
double dividendRate = 0.0,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
Dictionary dividends = null,
string fixings = null,
string averagingPeriodStartDate = null,
bool hasNightMarket = false,
bool preciseTimeMode = false,
string engineName = null,
string discountCurveName = null,
bool isForwardTrade = false,
bool ignoreSkewMap = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
if (volConstructionType == VolConstructionType.Normal || ignoreSkewMap)
{
return ValueAsianSyntheticNormalSpreadOption(
marketProxy,
valueDate,
underlyingTicker,
underlyingInstrumentType,
strike,
startDate,
endDate,
optionType,
exerciseType,
asianType,
strikeStyle,
spotPrice,
notional,
volSurfaceName,
riskFreeRate,
tradeType,
exerciseDate,
isAnnualized,
annualizeFactor,
dividendRate,
isMoneynessOption,
initialSpotPrice,
dividends,
fixings,
averagingPeriodStartDate,
hasNightMarket,
preciseTimeMode,
engineName,
discountCurveName,
isForwardTrade,
timeToMaturityDays: timeToMaturityDays,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
pricingRequest: pricingRequest);
}
else
{
var tradeId = Guid.NewGuid().ToString();
SetSkewMapVol(
marketProxy,
valueDate,
underlyingTicker,
exerciseDate,
strike,
TradeCalcHelper.IsBuy(tradeType),
QdpHelper.IsCall(optionType),
tradeId,
spotPrice);
return ValueAsianSyntheticNormalSpreadOption(
marketProxy,
valueDate,
underlyingTicker,
underlyingInstrumentType,
strike,
startDate,
endDate,
optionType,
exerciseType,
asianType,
strikeStyle,
spotPrice,
notional,
tradeId.GenerateVolSurfaceKey(),
riskFreeRate,
tradeType,
exerciseDate,
isAnnualized,
annualizeFactor,
dividendRate,
isMoneynessOption,
initialSpotPrice,
dividends,
fixings,
averagingPeriodStartDate,
hasNightMarket,
preciseTimeMode,
engineName,
discountCurveName,
isForwardTrade,
timeToMaturityDays: timeToMaturityDays,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
pricingRequest: pricingRequest);
}
}
private static TradeValueResult ValueAsianSyntheticNormalSpreadOption(
IQdpMarketProxy marketProxy,
string valueDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
string asianType,
string strikeStyle,
double spotPrice,
double notional,
string volSurfaceName,
double riskFreeRate,
string tradeType,
string exerciseDate,
bool isAnnualized,
double annualizeFactor,
double dividendRate = 0.0,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
Dictionary dividends = null,
string fixings = null,
string averagingPeriodStartDate = null,
bool hasNightMarket = false,
bool preciseTimeMode = false,
string engineName = null,
string discountCurveName = null,
bool isForwardTrade = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
if (string.IsNullOrWhiteSpace(volSurfaceName))
{
volSurfaceName = underlyingTicker.GenerateVolSurfaceKey();
}
var optionTrade = CreateAsianSyntheticNormalSpreadOptionTrade(
null,
volSurfaceName,
startDate,
underlyingTicker,
underlyingInstrumentType,
strike,
startDate,
endDate,
optionType,
exerciseType,
asianType,
strikeStyle,
notional,
tradeType,
exerciseDate,
isAnnualized,
annualizeFactor,
isMoneynessOption,
initialSpotPrice,
dividends,
averagingPeriodStartDate,
fixings,
hasNightMarket: hasNightMarket,
preciseTimeMode: preciseTimeMode,
timeToMaturityDays: timeToMaturityDays,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride).CheckMaturityDate();
var market = marketProxy.GetQdpMarket(valueDate);
if (market == null)
{
return null;
}
var useLocalDiscountCurve = string.IsNullOrWhiteSpace(discountCurveName);
//设置DiscountCurve
if (useLocalDiscountCurve)
{
discountCurveName = Guid.NewGuid().ToString();
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, riskFreeRate);
marketProxy.AddYieldCurve(discountCurveName, valueDate, discountCurve);
}
//设置标的价格
marketProxy.AddStockPrice(underlyingTicker, valueDate, spotPrice);
OptionValuationParameters parameters;
string dividendCurveName = null;
if (underlyingInstrumentType == "Stock")
{
//设置DividendCurve
dividendCurveName = Guid.NewGuid().ToString();
var dividendCurve = CalculatorHelper.CreateConstantRiskFreeCurve(dividendCurveName, dividendRate);
marketProxy.AddYieldCurve(dividendCurveName, valueDate, dividendCurve);
parameters = new OptionValuationParameters(
isForwardTrade ? MarketProxy.ConstantZeroCurve : discountCurveName,
dividendCurveName,
volSurfaceName,
underlyingTicker);
}
else
{
parameters = new OptionValuationParameters(
isForwardTrade ? MarketProxy.ConstantZeroCurve : discountCurveName,
MarketProxy.ConstantZeroCurve,
volSurfaceName,
underlyingTicker);
}
//var option = optionTrade.Instrument as AsianOption;
//var factory = OptionEngineFactory.GetEngineFactory("AsianOption");
var engine = new AnalyticalAsianSyntheticNormalSpreadOptionEngine();
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDate), pricingRequest, engine, parameters);
if (useLocalDiscountCurve)
{
marketProxy.RemoveYieldCurve(discountCurveName, valueDate);
}
if (!string.IsNullOrEmpty(dividendCurveName))
{
marketProxy.RemoveYieldCurve(dividendCurveName, valueDate);
}
if (results.Succeeded)
{
//double vol = marketProxy.GetVol(valueDate, underlyingTicker, strike, endDate, spotPrice);
//if (isMoneynessOption)
//{
// vol = marketProxy.GetVol(valueDate, underlyingTicker, strike / spotPrice, endDate);
//}
//else
//{
// vol = marketProxy.GetVol(valueDate, underlyingTicker, strike, endDate);
//}
return new TradeValueResult()
{
UnderlyingCode = underlyingTicker,
Pv = results.Pv,
Delta = results.Delta,
Gamma = results.Gamma,
Vega = results.Vega,
CalendarDayTheta = results.Theta,
TradingDayTheta = results.ThetaPnL,
Rho = results.Rho,
DeltaCash = results.DeltaCash,
GammaCash = results.GammaCash,
Vol = results.PricingVol,
DDeltaDVol = results.DDeltaDvol,
DDeltaDt = results.DDeltaDt,
DVegaDVol = results.DVegaDvol,
DVegaDt = results.DVegaDt,
StoppingTime = results.StoppingTime,
RoundedPv = OtcFormatHelper.FormatValue(results.Pv / notional, 2) * notional,
SA_Delta = results.SA_Delta,
TimeValue = results.TimeValue
};
}
else
{
return null;
}
}
#endregion
#region 双鲨期权
public static TradeValueResult ValueDoubleSharkFinOptionTrade(
PrebuiltQdpMarketProxy marketProxy,
string valueDate,
string underlyingCode,
string underlyingInstrumentType,
double strikeHigh,
double strikeLow,
string startDate,
string endDate,
string callPut,
string exerciseMode,
double barrierHigh,
double barrierLow,
bool isDiscrete,
double spotPrice,
double rebate,
double rebateHigh,
string rebateType,
double notional,
string volSurfaceName,
double riskFreeRate,
string buySell,
string exerciseDate,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
double callParticipationRate,
double putParticipationRate,
double dividendRate = 0.0,
bool isMoneynessOptionData = false,
double initialSpotPrice = 0.0,
string observationDates = null,
Dictionary dividends = null,
string knockInOutStatus = "Monitoring",
bool hasNightMarket = false,
bool preciseTimeMode = false,
string discountCurveName = null,
double timeToMaturityDays = double.NaN,
bool ignoreSkewMap = false,
string engineName = null,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
return ValueDoubleSharkFinOptionTrade(
VolSurfaceInitializerSingleton.VolConstructionType,
marketProxy,
valueDate,
underlyingCode,
underlyingInstrumentType,
strikeHigh,
strikeLow,
startDate,
endDate,
callPut,
exerciseMode,
barrierHigh,
barrierLow,
isDiscrete,
spotPrice,
rebate,
rebateHigh,
rebateType,
notional,
volSurfaceName,
riskFreeRate,
buySell,
exerciseDate,
dividendRate,
participationRate,
principalRate,
isAnnualized,
annualizeFactor,
callParticipationRate,
putParticipationRate,
isMoneynessOptionData,
initialSpotPrice,
observationDates,
dividends,
knockInOutStatus,
hasNightMarket,
preciseTimeMode,
discountCurveName,
timeToMaturityDays,
ignoreSkewMap,
engineName,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
pricingRequest: pricingRequest);
}
private static TradeValueResult ValueDoubleSharkFinOptionTrade(
VolConstructionType volConstructionType,
PrebuiltQdpMarketProxy marketProxy,
string valueDate,
string underlyingCode,
string underlyingInstrumentType,
double strikeHigh,
double strikeLow,
string startDate,
string endDate,
string callPut,
string exerciseMode,
double barrierHigh,
double barrierLow,
bool isDiscrete,
double spotPrice,
double rebate,
double rebateHigh,
string rebateType,
double notional,
string volSurfaceName,
double riskFreeRate,
string buySell,
string exerciseDate,
double dividendRate,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
double callParticipationRate,
double putParticipationRate,
bool isMoneynessOptionData,
double initialSpotPrice,
string observationDates,
Dictionary dividends,
string knockInOutStatus,
bool hasNightMarket,
bool preciseTimeMode,
string discountCurveName,
double timeToMaturityDays,
bool ignoreSkewMap,
string engineName,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
if (volConstructionType == VolConstructionType.Normal || ignoreSkewMap)
{
return ValueDoubleSharkFinOption(
marketProxy,
valueDate,
underlyingCode,
underlyingInstrumentType,
strikeHigh,
strikeLow,
startDate,
endDate,
callPut,
exerciseMode,
barrierHigh,
barrierLow,
isDiscrete,
spotPrice,
rebate,
rebateHigh,
rebateType,
notional,
volSurfaceName,
riskFreeRate,
buySell,
exerciseDate,
dividendRate,
participationRate,
principalRate,
isAnnualized,
annualizeFactor,
callParticipationRate,
putParticipationRate,
isMoneynessOptionData,
initialSpotPrice,
observationDates,
dividends,
knockInOutStatus,
hasNightMarket,
preciseTimeMode,
discountCurveName,
timeToMaturityDays,
engineName,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
pricingRequest: pricingRequest);
}
else
{
var tradeId = Guid.NewGuid().ToString();
SetSkewMapVol(
marketProxy,
valueDate,
underlyingCode,
exerciseDate,
(strikeHigh + strikeLow) / 2.0,
TradeCalcHelper.IsBuy(buySell),
QdpHelper.IsCall(callPut),
tradeId,
spotPrice);
return ValueDoubleSharkFinOption(
marketProxy,
valueDate,
underlyingCode,
underlyingInstrumentType,
strikeHigh,
strikeLow,
startDate,
endDate,
callPut,
exerciseMode,
barrierHigh,
barrierLow,
isDiscrete,
spotPrice,
rebate,
rebateHigh,
rebateType,
notional,
tradeId.GenerateVolSurfaceKey(),
riskFreeRate,
buySell,
exerciseDate,
dividendRate,
participationRate,
principalRate,
isAnnualized,
annualizeFactor,
callParticipationRate,
putParticipationRate,
isMoneynessOptionData,
initialSpotPrice,
observationDates,
dividends,
knockInOutStatus,
hasNightMarket,
preciseTimeMode,
discountCurveName,
timeToMaturityDays,
engineName,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
pricingRequest: pricingRequest);
}
}
private static DoubleSharkFinOptionTrade CreateDoubleSharkFinOptionTrade(
string tradeId,
string volSurfaceName,
string tradeDate,
string underlyingTicker,
string underlyingInstrumentType,
double strikeHigh,
double strikeLow,
string startDate,
string endDate,
string exerciseType,
double barrierHigh,
double barrierLow,
bool isDiscrete,
double rebate,
double rebateHigh,
string rebateType,
double notional,
string tradeType,
string exerciseDate,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
double callParticipationRate,
double putParticipationRate,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
string observationDateStr = null,
string barrierStatus = "Monitoring",
bool hasNightMarket = false,
bool preciseTimeMode = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN)
{
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
var exercise = QdpConverter.ConvertExerciseType(exerciseType);
var optionStartDate = startDate.ToDate();
var underlyingMaturityDate = string.IsNullOrWhiteSpace(endDate) ? null : endDate.ToDate();
var temp_exerciseDate = exerciseDate.ToDate();
if (temp_exerciseDate < optionStartDate)
{
return null;
}
Date[] exerciseDates;
var observationDates = QdpHelper.ParseObservationDate(observationDateStr);
if (exercise == OptionExercise.American)
{
exerciseDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray();
observationDates ??= CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray();
}
else
{
exerciseDates = new Date[] { temp_exerciseDate };
observationDates ??= CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).Union(new[] { temp_exerciseDate }).ToArray();
}
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var pricingTOverride = double.IsNaN(timeToMaturityDays) ? double.NaN : QdpCalendarHelper.CalculateTFromDays(timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
var lowRebateValue = double.IsNaN(rebate) ? 0.0 : rebate;
var highReateValue = double.IsNaN(rebateHigh) ? 0.0 : rebateHigh;
var doubleSharkFinOption =
new DoubleSharkFinOption(
strikeHigh,
strikeLow,
Enumerable.Repeat(barrierHigh, observationDates.Length).ToArray(),
Enumerable.Repeat(barrierLow, observationDates.Length).ToArray(),
Enumerable.Repeat(lowRebateValue, observationDates.Length).ToArray(),
Enumerable.Repeat(highReateValue, observationDates.Length).ToArray(),
(string.IsNullOrEmpty(rebateType) ? "AtEnd" : rebateType).ToBinaryRebateType(),
observationDates,
optionStartDate,
underlyingMaturityDate,
participationRate,
isDiscrete,
(InstrumentType)Enum.Parse(typeof(InstrumentType), underlyingInstrumentType),
CalendarImpl.Get("chn"),
optionDayCount.ToDayCountImpl(),
CurrencyCode.CNY,
CurrencyCode.CNY,
exerciseDates,
callParticipationRate,
putParticipationRate,
isMoneynessOption,
initialSpotPrice,
notional,
settlementGap: null,
optionPremiumPaymentDate: null,
optionPremium: 0,
pricingToverride: pricingTOverride,
barrierStatus: QdpConverter.ConvertBarrierStatus(barrierStatus),
hasNightMarket: hasNightMarket,
commodityFuturesPreciseTimeMode: preciseTimeMode,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
isAnnualized: isAnnualized,
annualizedFactor: annualizeFactor)
{
UnderlyingTickers = new string[] { underlyingTicker }
};
if (string.IsNullOrWhiteSpace(tradeId))
{
tradeId = Guid.NewGuid().ToString();
}
return new DoubleSharkFinOptionTrade(
tradeId,
tradeDate.ToDate(),
doubleSharkFinOption.StartDate,
doubleSharkFinOption.ExerciseDates.Last(),
QdpConverter.ConvertTradeType(tradeType),
doubleSharkFinOption.Notional,
0.0,
doubleSharkFinOption)
{
ValuationParameters = new OptionValuationParameters("RiskFreeDiscountCurve", MarketProxy.ConstantZeroCurve, volSurfaceName, doubleSharkFinOption.UnderlyingTickers[0]),
};
}
private static TradeValueResult ValueDoubleSharkFinOption(
PrebuiltQdpMarketProxy marketProxy,
string valueDate,
string underlyingCode,
string underlyingInstrumentType,
double strikeHigh,
double strikeLow,
string startDate,
string endDate,
string callPut,
string exerciseMode,
double barrierHigh,
double barrierLow,
bool isDiscrete,
double spotPrice,
double rebate,
double rebateHigh,
string rebateType,
double notional,
string volSurfaceName,
double riskFreeRate,
string buySell,
string exerciseDate,
double dividendRate,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
double callParticipationRate,
double putParticipationRate,
bool isMoneynessOptionData,
double initialSpotPrice,
string observationDateStr,
Dictionary dividends,
string knockInOutStatus,
bool hasNightMarket,
bool preciseTimeMode,
string discountCurveName,
double timeToMaturityDays,
string engineName,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
if (string.IsNullOrWhiteSpace(volSurfaceName))
{
volSurfaceName = underlyingCode.GenerateVolSurfaceKey();
}
//如果用户自定义的观察日就是每天观察,则忽略自定义观察日
var observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(startDate.ToDate(), exerciseDate.ToDate()).Union(new[] { exerciseDate.ToDate() }).ToArray();
var barrierObservationDates = QdpHelper.ParseObservationDate(observationDateStr);
if (barrierObservationDates != null &&
(barrierObservationDates.Count(d => d >= observationDates.First() && d <= observationDates.Last()) == observationDates.Length))
{
observationDateStr = null;
}
var optionTrade = CreateDoubleSharkFinOptionTrade(
null,
volSurfaceName,
startDate,
underlyingCode,
underlyingInstrumentType,
strikeHigh,
strikeLow,
startDate,
endDate,
exerciseMode,
barrierHigh,
barrierLow,
isDiscrete,
rebate,
rebateHigh,
rebateType,
notional,
buySell,
exerciseDate,
participationRate,
principalRate,
isAnnualized,
annualizeFactor,
callParticipationRate,
putParticipationRate,
isMoneynessOptionData,
initialSpotPrice,
observationDateStr,
knockInOutStatus,
hasNightMarket,
preciseTimeMode,
timeToMaturityDays,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride).CheckMaturityDate();
var market = marketProxy.GetQdpMarket(valueDate);
if (market == null)
{
return null;
}
var useLocalDiscountCurve = string.IsNullOrWhiteSpace(discountCurveName);
//设置DiscountCurve
if (useLocalDiscountCurve)
{
discountCurveName = Guid.NewGuid().ToString();
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, riskFreeRate);
marketProxy.AddYieldCurve(discountCurveName, valueDate, discountCurve);
}
//设置标的价格
marketProxy.AddStockPrice(underlyingCode, valueDate, spotPrice);
OptionValuationParameters parameters = null;
string dividendCurveName = null;
if (underlyingInstrumentType == "Stock")
{
//设置DividendCurve
dividendCurveName = Guid.NewGuid().ToString();
var dividendCurve = CalculatorHelper.CreateConstantRiskFreeCurve(dividendCurveName, dividendRate);
marketProxy.AddYieldCurve(dividendCurveName, valueDate, dividendCurve);
parameters = new OptionValuationParameters(
discountCurveName,
dividendCurveName,
volSurfaceName,
underlyingCode);
}
else
{
parameters = new OptionValuationParameters(
discountCurveName,
MarketProxy.ConstantZeroCurve,
volSurfaceName,
underlyingCode);
}
var option = optionTrade.Instrument as DoubleSharkFinOption;
var factory = OptionEngineFactory.GetEngineFactory("DoubleSharkFinOption");
var engine = factory.GetEngine(engineName, option.Exercise, option, false, !string.IsNullOrWhiteSpace(observationDateStr), true);
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDate), pricingRequest, engine, parameters);
if (useLocalDiscountCurve)
{
marketProxy.RemoveYieldCurve(discountCurveName, valueDate);
}
if (!string.IsNullOrEmpty(dividendCurveName))
{
marketProxy.RemoveYieldCurve(dividendCurveName, valueDate);
}
if (results.Succeeded)
{
//double vol = marketProxy.GetVol(valueDate, underlyingTicker, strike, endDate, spotPrice);
//if (isMoneynessOption)
//{
// vol = marketProxy.GetVol(valueDate, underlyingTicker, strike / spotPrice, endDate);
//}
//else
//{
// vol = marketProxy.GetVol(valueDate, underlyingTicker, strike, endDate);
//}
return new TradeValueResult()
{
UnderlyingCode = underlyingCode,
Pv = results.Pv,
Delta = results.Delta,
Gamma = results.Gamma,
Vega = results.Vega,
CalendarDayTheta = results.Theta,
TradingDayTheta = results.ThetaPnL,
Rho = results.Rho,
DeltaCash = results.DeltaCash,
GammaCash = results.GammaCash,
Vol = results.PricingVol,
DDeltaDVol = results.DDeltaDvol,
DDeltaDt = results.DDeltaDt,
DVegaDVol = results.DVegaDvol,
DVegaDt = results.DVegaDt,
StoppingTime = results.StoppingTime,
RoundedPv = OtcFormatHelper.FormatValue(results.Pv / notional, 2) * notional
};
}
else
{
return null;
}
}
#endregion
#region 凤凰期权
public static TradeValueResult ValueAutocallTrade(
PrebuiltQdpMarketProxy marketProxy,
string valueDate,
string underlyingCode,
string underlyingInstrumentType,
string startDate,
string endDate,
string callPut,
double koBarrier,
double kiBarrier,
double couponBarrier,
string koObservationDateStr,
string observationDateStr,
double coupon,
bool couponPayAtMaturity,
bool includeCouponAfterKI,
bool isFixedCoupon,
bool annualizedOptionPayoff,
double participationRate,
double principalRate,
double spotPrice,
double notional,
string volSurfaceName,
double riskFreeRate,
string buySell,
string exerciseDate,
double annualizeFactor,
double strike,
double spreadStrike,
string couponDayCount,
List happenedObservations,
string kiOptionType = "Put",
double dividendRate = 0.0,
bool isMoneynessOptionData = false,
double initialSpotPrice = 0.0,
string observationDates = null,
Dictionary dividends = null,
string knockInOutStatus = "Monitoring",
bool hasNightMarket = false,
bool preciseTimeMode = false,
string discountCurveName = null,
double timeToMaturityDays = double.NaN,
bool ignoreSkewMap = false,
string engineName = null,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN)
{
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
return ValueAutocallTrade(
VolSurfaceInitializerSingleton.VolConstructionType,
marketProxy: marketProxy,
valueDate: valueDate,
underlyingCode: underlyingCode,
underlyingInstrumentType: underlyingInstrumentType,
startDate: startDate,
endDate: endDate,
callPut: callPut,
koBarrier: koBarrier,
kiBarrier: kiBarrier,
couponBarrier: couponBarrier,
koObservationDateStr: koObservationDateStr,
observationDateStr: observationDateStr,
coupon: coupon,
couponPayAtMaturity: couponPayAtMaturity,
includeCouponAfterKI: includeCouponAfterKI,
isFixedCoupon: isFixedCoupon,
annualizedOptionPayoff: annualizedOptionPayoff,
participationRate: participationRate,
principalRate: principalRate,
spotPrice: spotPrice,
notional: notional,
volSurfaceName: volSurfaceName,
riskFreeRate: riskFreeRate,
buySell: buySell,
exerciseDate: exerciseDate,
annualizeFactor: annualizeFactor,
strike: strike,
spreadStrike: spreadStrike,
couponDayCount: couponDayCount,
happenedObservations: happenedObservations,
kiOptionType: kiOptionType,
dividendRate: dividendRate,
isMoneynessOptionData: isMoneynessOptionData,
initialSpotPrice: initialSpotPrice,
observationDates: observationDates,
dividends: dividends,
knockInOutStatus: knockInOutStatus,
hasNightMarket: hasNightMarket,
preciseTimeMode: preciseTimeMode,
discountCurveName: discountCurveName,
timeToMaturityDays: timeToMaturityDays,
ignoreSkewMap: ignoreSkewMap,
engineName: engineName,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride);
}
private static TradeValueResult ValueAutocallTrade(
VolConstructionType volConstructionType,
PrebuiltQdpMarketProxy marketProxy,
string valueDate,
string underlyingCode,
string underlyingInstrumentType,
string startDate,
string endDate,
string callPut,
double koBarrier,
double kiBarrier,
double couponBarrier,
string koObservationDateStr,
string observationDateStr,
double coupon,
bool couponPayAtMaturity,
bool includeCouponAfterKI,
bool isFixedCoupon,
bool annualizedOptionPayoff,
double participationRate,
double principalRate,
double spotPrice,
double notional,
string volSurfaceName,
double riskFreeRate,
string buySell,
string exerciseDate,
double annualizeFactor,
double strike,
double spreadStrike,
string couponDayCount,
List happenedObservations,
string kiOptionType = "Put",
double dividendRate = 0.0,
bool isMoneynessOptionData = false,
double initialSpotPrice = 0.0,
string observationDates = null,
Dictionary dividends = null,
string knockInOutStatus = "Monitoring",
bool hasNightMarket = false,
bool preciseTimeMode = false,
string discountCurveName = null,
double timeToMaturityDays = double.NaN,
bool ignoreSkewMap = false,
string engineName = null,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN)
{
if (volConstructionType == VolConstructionType.Normal || ignoreSkewMap)
{
return ValueAutocall(
marketProxy: marketProxy,
valueDate: valueDate,
underlyingCode: underlyingCode,
underlyingInstrumentType: underlyingInstrumentType,
startDate: startDate,
endDate: endDate,
callPut: callPut,
koBarrier: koBarrier,
kiBarrier: kiBarrier,
couponBarrier: couponBarrier,
koObservationDateStr: koObservationDateStr,
observationDateStr: observationDateStr,
coupon: coupon,
couponPayAtMaturity: couponPayAtMaturity,
includeCouponAfterKI: includeCouponAfterKI,
isFixedCoupon: isFixedCoupon,
annualizedOptionPayoff: annualizedOptionPayoff,
participationRate: participationRate,
principalRate: principalRate,
spotPrice: spotPrice,
notional: notional,
volSurfaceName: volSurfaceName,
riskFreeRate: riskFreeRate,
buySell: buySell,
exerciseDate: exerciseDate,
annualizeFactor: annualizeFactor,
strike: strike,
spreadStrike: spreadStrike,
couponDayCount: couponDayCount,
happenedObservations: happenedObservations,
kiOptionType: kiOptionType,
dividendRate: dividendRate,
isMoneynessOptionData: isMoneynessOptionData,
initialSpotPrice: initialSpotPrice,
dividends: dividends,
knockInOutStatus: knockInOutStatus,
hasNightMarket: hasNightMarket,
preciseTimeMode: preciseTimeMode,
discountCurveName: discountCurveName,
timeToMaturityDays: timeToMaturityDays,
ignoreSkewMap: ignoreSkewMap,
engineName: engineName,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride);
}
else
{
var tradeId = Guid.NewGuid().ToString();
SetSkewMapVol(
marketProxy,
valueDate,
underlyingCode,
exerciseDate,
koBarrier,
TradeCalcHelper.IsBuy(buySell),
QdpHelper.IsCall(callPut),
tradeId,
spotPrice);
return ValueAutocall(
marketProxy: marketProxy,
valueDate: valueDate,
underlyingCode: underlyingCode,
underlyingInstrumentType: underlyingInstrumentType,
startDate: startDate,
endDate: endDate,
callPut: callPut,
koBarrier: koBarrier,
kiBarrier: kiBarrier,
couponBarrier: couponBarrier,
koObservationDateStr: koObservationDateStr,
observationDateStr: observationDateStr,
coupon: coupon,
couponPayAtMaturity: couponPayAtMaturity,
includeCouponAfterKI: includeCouponAfterKI,
isFixedCoupon: isFixedCoupon,
annualizedOptionPayoff: annualizedOptionPayoff,
participationRate: participationRate,
principalRate: principalRate,
spotPrice: spotPrice,
notional: notional,
volSurfaceName: tradeId.GenerateVolSurfaceKey(),
riskFreeRate: riskFreeRate,
buySell: buySell,
exerciseDate: exerciseDate,
annualizeFactor: annualizeFactor,
strike: strike,
spreadStrike: spreadStrike,
couponDayCount: couponDayCount,
happenedObservations: happenedObservations,
kiOptionType: kiOptionType,
dividendRate: dividendRate,
isMoneynessOptionData: isMoneynessOptionData,
initialSpotPrice: initialSpotPrice,
dividends: dividends,
knockInOutStatus: knockInOutStatus,
hasNightMarket: hasNightMarket,
preciseTimeMode: preciseTimeMode,
discountCurveName: discountCurveName,
timeToMaturityDays: timeToMaturityDays,
ignoreSkewMap: ignoreSkewMap,
engineName: engineName,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride);
}
}
private static AutocallTrade CreateAutocallTrade(
string tradeId,
string volSurfaceName,
string tradeDate,
string underlyingTicker,
string underlyingInstrumentType,
string startDate,
string endDate,
double strike,
double initialSpotPrice,
bool isMoneynessOption,
double koBarrier,
double kiBarrier,
double couponBarrier,
string koObservationDateStr,
string observationDateStr,
double coupon,
bool couponPayAtMaturity,
bool includeCouponAfterKI,
bool isFixedCoupon,
bool annualizedOptionPayoff,
double spreadStrike,
double participationRate,
double principalRate,
double notional,
string tradeType,
string exerciseDate,
double annualizeFactor,
string couponDayCount,
List happenedObservations,
string kiOptionType = "Put",
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN)
{
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
var exercise = OptionExercise.European; // hard code
var optionStartDate = startDate.ToDate();
var underlyingMaturityDate = string.IsNullOrWhiteSpace(endDate) ? null : endDate.ToDate();
var temp_exerciseDate = exerciseDate.ToDate();
if (temp_exerciseDate < optionStartDate)
{
return null;
}
Date[] exerciseDates = null;
var observationDates = QdpHelper.ParseObservationDate(observationDateStr);
var customizedResults = QdpHelper.ParseAutocallCustomizedInfo(koObservationDateStr);
var koObservationDates = customizedResults.Item1;
var customizedKOBarriers = customizedResults.Item2;
var customizedCoupons = customizedResults.Item3;
//Date[] koObservationDates = QDPHelper.ParseObservationDate(koObservationDateStr);
if (exercise == OptionExercise.American)
{
exerciseDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray();
observationDates ??= CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray();
}
else
{
exerciseDates = new Date[] { temp_exerciseDate };
observationDates ??= CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).Union(new[] { temp_exerciseDate }).ToArray();
}
if (koObservationDates == null)
{
var monthlyDates = new List();
var oneMonth = new Term("1M");
var date = oneMonth.Next(optionStartDate);
var beforeExercise = oneMonth.Prev(temp_exerciseDate);
while (date < beforeExercise)
{
monthlyDates.Add(date);
date = oneMonth.Next(date);
}
monthlyDates.Add(temp_exerciseDate);
koObservationDates = monthlyDates.ToArray();
}
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var pricingTOverride = double.IsNaN(timeToMaturityDays) ? double.NaN : QdpCalendarHelper.CalculateTFromDays(timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
var autocall = new AutoCall(
startDate: optionStartDate,
maturityDate: underlyingMaturityDate,
koBarrier: koBarrier,
kiBarrier: kiBarrier,
couponBarrier: couponBarrier,
coupon: coupon,
couponPayAtMaturity: couponPayAtMaturity,
includeCouponAfterKI: includeCouponAfterKI,
optionParticipationRate: 1.0, // 在OTC系统增加“期权参与率”字段之前,请保留一直传1.0
koObsDates: koObservationDates,
kiObsDates: observationDates,
observationDates: null,
notional: notional,
initialSpotPrice: initialSpotPrice,
underlyingType: (InstrumentType)Enum.Parse(typeof(InstrumentType), underlyingInstrumentType),
calendar: CalendarImpl.Get("chn"),
dayCount: optionDayCount.ToDayCountImpl(),
payoffCcy: CurrencyCode.CNY,
settlementCcy: CurrencyCode.CNY,
exerciseDates: exerciseDates,
strike: strike,
spreadStrike: spreadStrike,
fixedCoupon: isFixedCoupon,
annualizedOptionPayoff: annualizedOptionPayoff,
customizedKOBarriers: customizedKOBarriers,
customizedCoupons: customizedCoupons,
isMoneynessOption: isMoneynessOption,
happenedObservations: happenedObservations?.Where(o => o != null).Select(o => new ObservationPayment()
{
StartDate = new Date(o.StartDate),
EndDate = new Date(o.EndDate),
CouponRate = o.CouponRate,
Notional = o.StockEqvNotional,
PaymentAmount = o.PaymentAmount,
PaymentDate = new Date(o.PaymentDate)
}).ToList(),
couponDayCount: couponDayCount.ToDayCountImpl(),
kiOptionType: kiOptionType.ToOptionType(),
pricingToverride: pricingTOverride,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
annualizedFactor: annualizeFactor)
{
UnderlyingTickers = new string[] { underlyingTicker }
};
if (string.IsNullOrWhiteSpace(tradeId))
{
tradeId = Guid.NewGuid().ToString();
}
return new AutocallTrade(
tradeId,
tradeDate.ToDate(),
autocall.StartDate,
autocall.ExerciseDates.Last(),
QdpConverter.ConvertTradeType(tradeType),
autocall.Notional,
0.0,
autocall)
{
ValuationParameters = new OptionValuationParameters("RiskFreeDiscountCurve", MarketProxy.ConstantZeroCurve, volSurfaceName, autocall.UnderlyingTickers[0]),
};
}
private static TradeValueResult ValueAutocall(
PrebuiltQdpMarketProxy marketProxy,
string valueDate,
string underlyingCode,
string underlyingInstrumentType,
string startDate,
string endDate,
string callPut,
double koBarrier,
double kiBarrier,
double couponBarrier,
string koObservationDateStr,
string observationDateStr,
double coupon,
bool couponPayAtMaturity,
bool includeCouponAfterKI,
bool isFixedCoupon,
bool annualizedOptionPayoff,
double participationRate,
double principalRate,
double spotPrice,
double notional,
string volSurfaceName,
double riskFreeRate,
string buySell,
string exerciseDate,
double annualizeFactor,
double strike,
double spreadStrike,
string couponDayCount,
List happenedObservations,
string kiOptionType = "Put",
double dividendRate = 0.0,
bool isMoneynessOptionData = false,
double initialSpotPrice = 0.0,
Dictionary dividends = null,
string knockInOutStatus = "Monitoring",
bool hasNightMarket = false,
bool preciseTimeMode = false,
string discountCurveName = null,
double timeToMaturityDays = double.NaN,
bool ignoreSkewMap = false,
string engineName = null,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
if (string.IsNullOrWhiteSpace(volSurfaceName))
{
volSurfaceName = underlyingCode.GenerateVolSurfaceKey();
}
var optionTrade = CreateAutocallTrade(
null,
volSurfaceName,
startDate,
underlyingCode,
underlyingInstrumentType,
startDate,
endDate,
strike,
initialSpotPrice,
isMoneynessOptionData,
koBarrier,
kiBarrier,
couponBarrier,
koObservationDateStr,
observationDateStr,
coupon,
couponPayAtMaturity,
includeCouponAfterKI,
isFixedCoupon,
annualizedOptionPayoff,
spreadStrike,
participationRate,
principalRate,
notional,
buySell,
exerciseDate,
annualizeFactor,
couponDayCount,
happenedObservations,
kiOptionType,
timeToMaturityDays: timeToMaturityDays,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride).CheckMaturityDate();
var market = marketProxy.GetQdpMarket(valueDate);
if (market == null)
{
return null;
}
var useLocalDiscountCurve = string.IsNullOrWhiteSpace(discountCurveName);
//设置DiscountCurve
if (useLocalDiscountCurve)
{
discountCurveName = Guid.NewGuid().ToString();
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, riskFreeRate);
marketProxy.AddYieldCurve(discountCurveName, valueDate, discountCurve);
}
//设置标的价格
marketProxy.AddStockPrice(underlyingCode, valueDate, spotPrice);
OptionValuationParameters parameters;
string dividendCurveName = null;
if (underlyingInstrumentType == "Stock")
{
//设置DividendCurve
dividendCurveName = Guid.NewGuid().ToString();
var dividendCurve = CalculatorHelper.CreateConstantRiskFreeCurve(dividendCurveName, dividendRate);
marketProxy.AddYieldCurve(dividendCurveName, valueDate, dividendCurve);
parameters = new OptionValuationParameters(
discountCurveName,
dividendCurveName,
volSurfaceName,
underlyingCode);
}
else
{
parameters = new OptionValuationParameters(
discountCurveName,
MarketProxy.ConstantZeroCurve,
volSurfaceName,
underlyingCode);
}
var option = optionTrade.Instrument as AutoCall;
var factory = OptionEngineFactory.GetEngineFactory("AutoCall");
var engine = factory.GetEngine(engineName, option.Exercise);
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDate), pricingRequest, engine, parameters);
if (useLocalDiscountCurve)
{
marketProxy.RemoveYieldCurve(discountCurveName, valueDate);
}
if (!string.IsNullOrEmpty(dividendCurveName))
{
marketProxy.RemoveYieldCurve(dividendCurveName, valueDate);
}
if (results.Succeeded)
{
return new TradeValueResult()
{
UnderlyingCode = underlyingCode,
Pv = results.Pv,
Delta = results.Delta,
Gamma = results.Gamma,
Vega = results.Vega,
CalendarDayTheta = results.Theta,
TradingDayTheta = results.ThetaPnL,
Rho = results.Rho,
DeltaCash = results.DeltaCash,
GammaCash = results.GammaCash,
Vol = results.PricingVol,
DDeltaDVol = results.DDeltaDvol,
DDeltaDt = results.DDeltaDt,
DVegaDVol = results.DVegaDvol,
DVegaDt = results.DVegaDt,
StoppingTime = results.StoppingTime,
RoundedPv = OtcFormatHelper.FormatValue(results.Pv / notional, 2) * notional
};
}
else
{
return null;
}
}
#endregion
#region 雪球期权
///
///
///
/// 是否构造年化权利金模式的保本雪球
///
public static SnowballTrade CreateSnowballTrade(
string tradeId,
string volSurfaceName,
string tradeDate,
string underlyingTicker,
string underlyingInstrumentType,
string startDate,
string endDate,
double initialSpotPrice,
bool isMoneynessOption,
double koBarrier,
double kiBarrier,
string koObservationDateStr,
string observationDateStr,
double coupon,
double participationRate,
double principalRate,
double notional,
string tradeType,
string exerciseDate,
double annualizeFactor,
string callput,
double strike,
double spreadStrikeAtKO,
double spreadStrikeAtMaturity,
bool isFixedCoupon,
double kORebate,
double kOBarrierAdjustStep,
bool useOptionPayoffAtKO,
bool useOptionPayoffAtMaturity,
bool annualizedOptionPayoff,
string couponDayCount,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
bool useNoKiSnowball = false,
double koStrike = double.NaN,
string koOptionType = "Call",
string kiOptionType = "Put")
{
callput = "Put"; //临时方案
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
var exercise = OptionExercise.European; // hard code
var optionStartDate = startDate.ToDate();
var underlyingMaturityDate = string.IsNullOrWhiteSpace(endDate) ? null : endDate.ToDate();
var temp_exerciseDate = exerciseDate.ToDate();
if (temp_exerciseDate < optionStartDate)
{
return null;
}
Date[] exerciseDates;
var observationDates = QdpHelper.ParseObservationDate(observationDateStr);
var customizedResults = QdpHelper.ParseAutocallCustomizedInfo(koObservationDateStr);
var koObservationDates = customizedResults.Item1;
var customizedKOBarriers = customizedResults.Item2;
var customizedCoupons = customizedResults.Item3;
if (exercise == OptionExercise.American)
{
exerciseDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray();
observationDates ??= CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray();
}
else
{
exerciseDates = new Date[] { temp_exerciseDate };
observationDates ??= CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).Union(new[] { temp_exerciseDate }).ToArray();
}
if (koObservationDates == null)
{
var monthlyDates = new List();
var oneMonth = new Term("1M");
var date = oneMonth.Next(optionStartDate);
var beforeExercise = oneMonth.Prev(temp_exerciseDate);
while (date < beforeExercise)
{
monthlyDates.Add(date);
date = oneMonth.Next(date);
}
monthlyDates.Add(temp_exerciseDate);
koObservationDates = monthlyDates.ToArray();
}
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var pricingTOverride = double.IsNaN(timeToMaturityDays) ? double.NaN : QdpCalendarHelper.CalculateTFromDays(timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
var snowball = useNoKiSnowball ?
new AnnualizedPremiumSnowball(
startDate: optionStartDate,
maturityDate: underlyingMaturityDate,
koBarrier: koBarrier,
kiBarrier: kiBarrier,
coupon: coupon,
optionParticipationRate: 1.0, // 在OTC系统增加“期权参与率”字段之前,请保留一直传1.0
koObsDates: koObservationDates,
kiObsDates: observationDates,
observationDates: null,
notional: notional,
initialSpotPrice: initialSpotPrice,
underlyingType: (InstrumentType)Enum.Parse(typeof(InstrumentType), underlyingInstrumentType),
calendar: CalendarImpl.Get("chn"),
dayCount: optionDayCount.ToDayCountImpl(),
payoffCcy: CurrencyCode.CNY,
settlementCcy: CurrencyCode.CNY,
exerciseDates: exerciseDates,
exercise: OptionExercise.European,
isMoneynessOption: isMoneynessOption,
optionType: callput.ToOptionType(),
strike: strike,
spreadStrikeAtKO: spreadStrikeAtKO,
spreadStrikeAtMaturity: spreadStrikeAtMaturity,
fixedCoupon: isFixedCoupon,
kORebate: kORebate,
kOBarrierAdjustStep: kOBarrierAdjustStep,
useOptionPayoffAtKO: useOptionPayoffAtKO,
useOptionPayoffAtMaturity: useOptionPayoffAtMaturity,
annualizedOptionPayoff: annualizedOptionPayoff,
customizedKOBarriers: customizedKOBarriers,
customizedCoupons: customizedCoupons,
pricingToverride: pricingTOverride,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
annualizedFactor: annualizeFactor,
koStrike: koStrike,
koOptionType: koOptionType.ToOptionType(),
kiOptionType: kiOptionType.ToOptionType(),
couponDayCount: couponDayCount.ToDayCountImpl()) :
new SimpleSnowball(
startDate: optionStartDate,
maturityDate: underlyingMaturityDate,
koBarrier: koBarrier,
kiBarrier: kiBarrier,
coupon: coupon,
optionParticipationRate: 1.0, // 在OTC系统增加“期权参与率”字段之前,请保留一直传1.0
koObsDates: koObservationDates,
kiObsDates: observationDates,
observationDates: null,
notional: notional,
initialSpotPrice: initialSpotPrice,
underlyingType: (InstrumentType)Enum.Parse(typeof(InstrumentType), underlyingInstrumentType),
calendar: CalendarImpl.Get("chn"),
dayCount: optionDayCount.ToDayCountImpl(),
payoffCcy: CurrencyCode.CNY,
settlementCcy: CurrencyCode.CNY,
exerciseDates: exerciseDates,
exercise: OptionExercise.European,
isMoneynessOption: isMoneynessOption,
optionType: callput.ToOptionType(),
strike: strike,
spreadStrikeAtKO: spreadStrikeAtKO,
spreadStrikeAtMaturity: spreadStrikeAtMaturity,
fixedCoupon: isFixedCoupon,
kORebate: kORebate,
kOBarrierAdjustStep: kOBarrierAdjustStep,
useOptionPayoffAtKO: useOptionPayoffAtKO,
useOptionPayoffAtMaturity: useOptionPayoffAtMaturity,
annualizedOptionPayoff: annualizedOptionPayoff,
customizedKOBarriers: customizedKOBarriers,
customizedCoupons: customizedCoupons,
pricingToverride: pricingTOverride,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
annualizedFactor: annualizeFactor,
koStrike: koStrike,
koOptionType: koOptionType.ToOptionType(),
kiOptionType: kiOptionType.ToOptionType(),
couponDayCount: couponDayCount.ToDayCountImpl());
snowball.UnderlyingTickers = new string[] { underlyingTicker };
if (string.IsNullOrWhiteSpace(tradeId))
{
tradeId = Guid.NewGuid().ToString();
}
return new SnowballTrade(
tradeId,
tradeDate.ToDate(),
snowball.StartDate,
snowball.ExerciseDates.Last(),
QdpConverter.ConvertTradeType(tradeType),
snowball.Notional,
0.0,
snowball)
{
ValuationParameters = new OptionValuationParameters("RiskFreeDiscountCurve", MarketProxy.ConstantZeroCurve, volSurfaceName, snowball.UnderlyingTickers[0]),
};
}
private static TradeValueResult ValueSnowball(
PrebuiltQdpMarketProxy marketProxy,
string valueDate,
string underlyingCode,
string underlyingInstrumentType,
string startDate,
string endDate,
string callPut,
double koBarrier,
double kiBarrier,
string koObservationDateStr,
string observationDateStr,
double coupon,
double participationRate,
double principalRate,
double spotPrice,
double notional,
string volSurfaceName,
double riskFreeRate,
string buySell,
string exerciseDate,
double annualizeFactor,
double strike,
double spreadStrikeAtKO,
double spreadStrikeAtMaturity,
bool isFixedCoupon,
double kORebate,
double kOBarrierAdjustStep,
bool useOptionPayoffAtKO,
bool useOptionPayoffAtMaturity,
bool annualizedOptionPayoff,
string couponDayCount,
double dividendRate = 0.0,
bool isMoneynessOptionData = false,
double initialSpotPrice = 0.0,
Dictionary dividends = null,
string knockInOutStatus = "Monitoring",
bool hasNightMarket = false,
bool preciseTimeMode = false,
string discountCurveName = null,
double timeToMaturityDays = double.NaN,
bool ignoreSkewMap = false,
string engineName = null,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
bool useNoKiSnowball = false,
double koStrike = double.NaN,
string koOptionType = "Call",
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
if (string.IsNullOrWhiteSpace(volSurfaceName))
{
volSurfaceName = underlyingCode.GenerateVolSurfaceKey();
}
var optionTrade = CreateSnowballTrade(
tradeId: null,
volSurfaceName: volSurfaceName,
tradeDate: startDate,
underlyingTicker: underlyingCode,
underlyingInstrumentType: underlyingInstrumentType,
startDate: startDate,
endDate: endDate,
initialSpotPrice: initialSpotPrice,
isMoneynessOption: isMoneynessOptionData,
koBarrier: koBarrier,
kiBarrier: kiBarrier,
koObservationDateStr: koObservationDateStr,
observationDateStr: observationDateStr,
coupon: coupon,
participationRate: participationRate,
principalRate: principalRate,
notional: notional,
tradeType: buySell,
exerciseDate: exerciseDate,
annualizeFactor: annualizeFactor,
callput: callPut,
strike: strike,
spreadStrikeAtKO: spreadStrikeAtKO,
spreadStrikeAtMaturity: spreadStrikeAtMaturity,
isFixedCoupon: isFixedCoupon,
kORebate: kORebate,
kOBarrierAdjustStep: kOBarrierAdjustStep,
useOptionPayoffAtKO: useOptionPayoffAtKO,
useOptionPayoffAtMaturity: useOptionPayoffAtMaturity,
annualizedOptionPayoff: annualizedOptionPayoff,
couponDayCount: couponDayCount,
timeToMaturityDays: timeToMaturityDays,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
useNoKiSnowball: useNoKiSnowball,
koStrike: koStrike,
koOptionType: koOptionType).CheckMaturityDate();
var market = marketProxy.GetQdpMarket(valueDate);
if (market == null)
{
return null;
}
var useLocalDiscountCurve = string.IsNullOrWhiteSpace(discountCurveName);
//设置DiscountCurve
if (useLocalDiscountCurve)
{
discountCurveName = Guid.NewGuid().ToString();
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, riskFreeRate);
marketProxy.AddYieldCurve(discountCurveName, valueDate, discountCurve);
}
//设置标的价格
marketProxy.AddStockPrice(underlyingCode, valueDate, spotPrice);
OptionValuationParameters parameters;
string dividendCurveName = null;
if (underlyingInstrumentType == "Stock")
{
//设置DividendCurve
dividendCurveName = Guid.NewGuid().ToString();
var dividendCurve = CalculatorHelper.CreateConstantRiskFreeCurve(dividendCurveName, dividendRate);
marketProxy.AddYieldCurve(dividendCurveName, valueDate, dividendCurve);
parameters = new OptionValuationParameters(
discountCurveName,
dividendCurveName,
volSurfaceName,
underlyingCode);
}
else
{
parameters = new OptionValuationParameters(
discountCurveName,
MarketProxy.ConstantZeroCurve,
volSurfaceName,
underlyingCode);
}
var option = optionTrade.Instrument as SimpleSnowball;
var factory = OptionEngineFactory.GetEngineFactory("Snowball");
var engine = factory.GetEngine(engineName, option.Exercise);
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDate), pricingRequest, engine, parameters);
if (useLocalDiscountCurve)
{
marketProxy.RemoveYieldCurve(discountCurveName, valueDate);
}
if (!string.IsNullOrEmpty(dividendCurveName))
{
marketProxy.RemoveYieldCurve(dividendCurveName, valueDate);
}
if (results.Succeeded)
{
return new TradeValueResult()
{
UnderlyingCode = underlyingCode,
Pv = results.Pv,
Delta = results.Delta,
Gamma = results.Gamma,
Vega = results.Vega,
CalendarDayTheta = results.Theta,
TradingDayTheta = results.ThetaPnL,
Rho = results.Rho,
DeltaCash = results.DeltaCash,
GammaCash = results.GammaCash,
Vol = results.PricingVol,
DDeltaDVol = results.DDeltaDvol,
DDeltaDt = results.DDeltaDt,
DVegaDVol = results.DVegaDvol,
DVegaDt = results.DVegaDt,
StoppingTime = results.StoppingTime,
RoundedPv = OtcFormatHelper.FormatValue(results.Pv / notional, 2) * notional
};
}
else
{
return null;
}
}
#endregion
#region 区间累积期权
private static TradeValueResult ValueRangeAccrual(
PrebuiltQdpMarketProxy marketProxy,
string valueDate,
string underlyingCode,
string underlyingInstrumentType,
string startDate,
string endDate,
double participationRate,
double principalRate,
double spotPrice,
double notional,
string volSurfaceName,
double riskFreeRate,
string buySell,
string exerciseDate,
double lowerRange,
double upperRange,
double bonusRate,
string observationDates,
string settlementDate,
double annualizeFactor,
double strike,
bool isAnnualized,
List happenedObservations,
double dividendRate = 0.0,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
bool hasNightMarket = false,
bool preciseTimeMode = false,
string engineName = null,
string discountCurveName = null,
double timeToMaturityDays = double.NaN,
bool ignoreSkewMap = false,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
try
{
if (string.IsNullOrWhiteSpace(volSurfaceName))
{
volSurfaceName = underlyingCode.GenerateVolSurfaceKey();
}
var optionTrade = CreateRangeAccrualTrade(
null,
volSurfaceName,
valueDate,
underlyingCode,
underlyingInstrumentType,
strike,
startDate,
endDate,
"Call",
"European",
notional,
buySell,
exerciseDate,
lowerRange,
upperRange,
bonusRate,
observationDates,
settlementDate,
participationRate,
principalRate,
isAnnualized,
annualizeFactor,
happenedObservations,
isMoneynessOption,
initialSpotPrice,
null,
fixings: null,
hasNightMarket: hasNightMarket,
preciseTimeMode: preciseTimeMode,
timeToMaturityDays: timeToMaturityDays,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride);
var market = marketProxy.GetQdpMarket(valueDate);
if (market == null)
{
return null;
}
var useLocalDiscountCurve = string.IsNullOrWhiteSpace(discountCurveName);
//设置DiscountCurve
if (useLocalDiscountCurve)
{
discountCurveName = Guid.NewGuid().ToString();
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, riskFreeRate);
marketProxy.AddYieldCurve(discountCurveName, valueDate, discountCurve);
}
//设置标的价格
marketProxy.AddStockPrice(underlyingCode, valueDate, spotPrice);
OptionValuationParameters parameters = null;
string dividendCurveName = null;
if (underlyingInstrumentType == "Stock")
{
//设置DividendCurve
dividendCurveName = Guid.NewGuid().ToString();
var dividendCurve = CalculatorHelper.CreateConstantRiskFreeCurve(dividendCurveName, dividendRate);
marketProxy.AddYieldCurve(dividendCurveName, valueDate, dividendCurve);
parameters = new OptionValuationParameters(
discountCurveName,
dividendCurveName,
volSurfaceName,
underlyingCode);
}
else
{
parameters = new OptionValuationParameters(
discountCurveName,
MarketProxy.ConstantZeroCurve,
volSurfaceName,
underlyingCode);
}
var option = optionTrade.Instrument as RangeAccrual;
var factory = OptionEngineFactory.GetEngineFactory("RangeAccrual");
var engine = factory.GetEngine(engineName, option.Exercise);
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDate), pricingRequest, engine, parameters);
if (useLocalDiscountCurve)
{
marketProxy.RemoveYieldCurve(discountCurveName, valueDate);
}
if (!string.IsNullOrEmpty(dividendCurveName))
{
marketProxy.RemoveYieldCurve(dividendCurveName, valueDate);
}
if (results.Succeeded)
{
var ret = new TradeValueResult()
{
UnderlyingCode = underlyingCode,
Pv = results.Pv,
Delta = results.Delta,
Gamma = results.Gamma,
Vega = results.Vega,
CalendarDayTheta = results.Theta,
TradingDayTheta = results.ThetaPnL,
Rho = results.Rho,
DeltaCash = results.DeltaCash,
GammaCash = results.GammaCash,
Vol = results.PricingVol,
RoundedPv = OtcFormatHelper.FormatValue(results.Pv / notional, 2) * notional
};
return ret;
}
else
{
return null;
}
}
catch (Exception ex)
{
LogFactory.GetLogger("期权计算").Error("ValueRangeAccrual:" + underlyingCode, ex);
throw new Exception($"区间累积期权处理失败[标的:{underlyingCode},开始日:{startDate},结束日:{endDate},行权日:{exerciseDate}]", ex);
}
}
private static RangeAccrualTrade CreateRangeAccrualTrade(
string tradeId,
string volSurfaceName,
string tradeDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
double notional,
string tradeType,
string exerciseDate,
double lowerRange,
double upperRange,
double bonusRate,
string observationDateStr,
string settlementDate,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
List happenedObservations,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
Dictionary dividends = null,
string fixings = null,
bool hasNightMarket = false,
bool preciseTimeMode = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN)
{
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
var exercise = QdpConverter.ConvertExerciseType(exerciseType);
var optionStartDate = startDate.ToDate();
var underlyingMaturityDate = string.IsNullOrWhiteSpace(endDate) ? null : endDate.ToDate();
var temp_exerciseDate = exerciseDate.ToDate();
if (temp_exerciseDate < optionStartDate)
{
throw new Exception("到期日期不能小于起始日");
}
Date[] exerciseDates = null;
var observationDates = QdpHelper.ParseObservationDate(observationDateStr);
if (exercise == OptionExercise.American)
{
exerciseDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray();
observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray();
}
else
{
exerciseDates = new Date[] { temp_exerciseDate };
//observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(optionStartDate, exerciseDates.Last()).ToArray();
observationDates ??= CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, exerciseDates.Last()).ToArray();
}
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var pricingTOverride = double.IsNaN(timeToMaturityDays) ? double.NaN : QdpCalendarHelper.CalculateTFromDays(timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
var fixingValues = QdpHelper.ParseFixingsFromString(fixings);
var bonusRateSettlementDate = string.IsNullOrWhiteSpace(settlementDate) ? temp_exerciseDate : new Date(DateTime.Parse(settlementDate));
if (bonusRateSettlementDate < optionStartDate)
{
bonusRateSettlementDate = temp_exerciseDate;
}
var rangeAccrual =
new RangeAccrual(
optionStartDate,
underlyingMaturityDate,
exercise,
(OptionType)Enum.Parse(typeof(OptionType), optionType),
strike,
new RangeDefinition[]
{
new RangeDefinition(lowerRange, upperRange, bonusRate, bonusRateSettlementDate, observationDates)
},
(InstrumentType)Enum.Parse(typeof(InstrumentType), underlyingInstrumentType),
CalendarImpl.Get("chn"),
optionDayCount.ToDayCountImpl(),
CurrencyCode.CNY,
CurrencyCode.CNY,
exerciseDates,
observationDates,
fixingValues,
happenedObservations?.Where(o => o != null).Select(o => new ObservationPayment()
{
StartDate = new Date(o.StartDate),
EndDate = new Date(o.EndDate),
CouponRate = o.CouponRate,
Notional = o.StockEqvNotional,
PaymentAmount = o.PaymentAmount,
PaymentDate = new Date(o.PaymentDate)
}).ToList(),
notional,
null,
null,
0.0,
isMoneynessOption,
initialSpotPrice,
hasNightMarket: hasNightMarket,
commodityFuturesPreciseTimeMode: preciseTimeMode,
pricingToverride: pricingTOverride,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
participationRate: participationRate,
isAnnualized: isAnnualized,
annualizedFactor: annualizeFactor)
{
UnderlyingTickers = new string[] { underlyingTicker }
};
if (string.IsNullOrWhiteSpace(tradeId))
{
tradeId = Guid.NewGuid().ToString();
}
return new RangeAccrualTrade(
tradeId,
tradeDate.ToDate(),
rangeAccrual.StartDate,
rangeAccrual.ExerciseDates.Last(),
QdpConverter.ConvertTradeType(tradeType),
rangeAccrual.Notional,
0.0,
rangeAccrual)
{
ValuationParameters = new OptionValuationParameters("RiskFreeDiscountCurve", MarketProxy.ConstantZeroCurve, volSurfaceName, rangeAccrual.UnderlyingTickers[0]),
};
}
#endregion
#region 气囊结构
private static TradeValueResult ValueAirbag(
PrebuiltQdpMarketProxy marketProxy,
string valueDate,
string underlyingCode,
string underlyingInstrumentType,
double strike,
double highStrike,
string startDate,
string endDate,
string optionType,
string exerciseType,
double barrier,
double participationRate,
double kiParticipationRate,
double principalRate,
bool isDiscrete,
bool hasPayoffLimit,
double spotPrice,
double notional,
double riskFreeRate,
double dividendRate,
string volSurfaceName,
string tradeType,
string exerciseDate,
bool isAnnualized,
double annualizeFactor,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
string observationDateStr = null,
Dictionary dividends = null,
string position = "Buy",
string engineName = null,
string discountCurveName = null,
string barrierStatus = "Monitoring",
bool hasNightMarket = false,
bool preciseTimeMode = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
try
{
if (string.IsNullOrWhiteSpace(volSurfaceName))
{
volSurfaceName = underlyingCode.GenerateVolSurfaceKey();
}
var optionTrade = CreateAirbagTrade(
null,
volSurfaceName,
valueDate,
underlyingCode,
underlyingInstrumentType,
strike,
highStrike,
startDate,
endDate,
optionType,
exerciseType,
barrier,
participationRate,
kiParticipationRate,
principalRate,
isDiscrete,
hasPayoffLimit,
notional,
position,
exerciseDate,
isAnnualized,
annualizeFactor,
isMoneynessOption,
initialSpotPrice,
observationDateStr,
dividends,
position,
barrierStatus,
hasNightMarket,
preciseTimeMode,
timeToMaturityDays,
riskFreeRateOverride,
dividendRateOverride);
var market = marketProxy.GetQdpMarket(valueDate);
if (market == null)
{
return null;
}
var useLocalDiscountCurve = string.IsNullOrWhiteSpace(discountCurveName);
//设置DiscountCurve
if (useLocalDiscountCurve)
{
discountCurveName = Guid.NewGuid().ToString();
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, riskFreeRate);
marketProxy.AddYieldCurve(discountCurveName, valueDate, discountCurve);
}
//设置标的价格
marketProxy.AddStockPrice(underlyingCode, valueDate, spotPrice);
OptionValuationParameters parameters = null;
string dividendCurveName = null;
if (underlyingInstrumentType == "Stock")
{
//设置DividendCurve
dividendCurveName = Guid.NewGuid().ToString();
var dividendCurve = CalculatorHelper.CreateConstantRiskFreeCurve(dividendCurveName, dividendRate);
marketProxy.AddYieldCurve(dividendCurveName, valueDate, dividendCurve);
parameters = new OptionValuationParameters(
discountCurveName,
dividendCurveName,
volSurfaceName,
underlyingCode);
}
else
{
parameters = new OptionValuationParameters(
discountCurveName,
MarketProxy.ConstantZeroCurve,
volSurfaceName,
underlyingCode);
}
var option = optionTrade.Instrument as Airbag;
//var factory = OptionEngineFactory.GetEngineFactory("Airbag");
//var engine = factory.GetEngine(engineName, option.Exercise);
var engine = new AnalyticalAirbagReplicationEngine();
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDate), pricingRequest, engine, parameters);
if (useLocalDiscountCurve)
{
marketProxy.RemoveYieldCurve(discountCurveName, valueDate);
}
if (!string.IsNullOrEmpty(dividendCurveName))
{
marketProxy.RemoveYieldCurve(dividendCurveName, valueDate);
}
if (results.Succeeded)
{
var ret = new TradeValueResult()
{
UnderlyingCode = underlyingCode,
Pv = results.Pv,
Delta = results.Delta,
Gamma = results.Gamma,
Vega = results.Vega,
CalendarDayTheta = results.Theta,
TradingDayTheta = results.ThetaPnL,
Rho = results.Rho,
DeltaCash = results.DeltaCash,
GammaCash = results.GammaCash,
Vol = results.PricingVol,
RoundedPv = OtcFormatHelper.FormatValue(results.Pv / notional, 2) * notional
};
return ret;
}
else
{
return null;
}
}
catch (Exception ex)
{
LogFactory.GetLogger("期权计算").Error("ValueAirbag:" + underlyingCode, ex);
throw new Exception($"气囊结构处理失败[标的:{underlyingCode},开始日:{startDate},结束日:{endDate},行权日:{exerciseDate}]", ex);
}
}
private static AirbagTrade CreateAirbagTrade(
string tradeId,
string volSurfaceName,
string tradeDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
double highStrike,
string startDate,
string endDate,
string optionType,
string exerciseType,
double barrier,
double participationRate,
double kiParticipationRate,
double principalRate,
bool isDiscrete,
bool hasPayoffLimit,
double notional,
string tradeType,
string exerciseDate,
bool isAnnualized,
double annualizeFactor,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
string observationDateStr = null,
Dictionary dividends = null,
string position = "Buy",
string barrierStatus = "Monitoring",
bool hasNightMarket = false,
bool preciseTimeMode = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN)
{
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
var exercise = QdpConverter.ConvertExerciseType(exerciseType);
var optionStartDate = startDate.ToDate();
var underlyingMaturityDate = string.IsNullOrWhiteSpace(endDate) ? null : endDate.ToDate();
var temp_exerciseDate = exerciseDate.ToDate();
if (temp_exerciseDate < optionStartDate)
{
return null;
}
Date[] exerciseDates;
Date[] observationDates = null;
if (exercise == OptionExercise.American)
{
exerciseDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray();
observationDates ??= CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray();
}
else
{
exerciseDates = new Date[] { temp_exerciseDate };
observationDates ??= CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).Union(new[] { temp_exerciseDate }).ToArray();
}
//补丁
//if(string.IsNullOrEmpty(barrierType))
//{
// barrierType = "0";
//}
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var pricingTOverride = double.IsNaN(timeToMaturityDays) ? double.NaN : QdpCalendarHelper.CalculateTFromDays(timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
var airbag =
new Airbag(
optionStartDate,
underlyingMaturityDate,
exercise,
(OptionType)Enum.Parse(typeof(OptionType), optionType),
strike,
highStrike,
barrier,
participationRate,
kiParticipationRate,
isDiscrete,
hasPayoffLimit,
(InstrumentType)Enum.Parse(typeof(InstrumentType), underlyingInstrumentType),
CalendarImpl.Get("chn"),
optionDayCount.ToDayCountImpl(),
CurrencyCode.CNY,
CurrencyCode.CNY,
exerciseDates,
observationDates,
notional,
null,
null,
0.0,
isMoneynessOption: isMoneynessOption,
initialSpotPrice: initialSpotPrice,
dividends: dividends,
hasNightMarket: hasNightMarket,
commodityFuturesPreciseTimeMode: preciseTimeMode,
pricingToverride: pricingTOverride, //pricingToverride
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
isAnnualized: isAnnualized,
annualizedFactor: annualizeFactor)
{
UnderlyingTickers = new string[] { underlyingTicker }
};
if (string.IsNullOrWhiteSpace(tradeId))
{
tradeId = Guid.NewGuid().ToString();
}
return new AirbagTrade(
tradeId,
tradeDate.ToDate(),
airbag.StartDate,
airbag.ExerciseDates.Last(),
QdpConverter.ConvertTradeType(tradeType),
airbag.Notional,
0.0,
airbag)
{
ValuationParameters = new OptionValuationParameters("RiskFreeDiscountCurve", MarketProxy.ConstantZeroCurve, volSurfaceName, airbag.UnderlyingTickers[0]),
};
}
#endregion
#region 收益增强结构
private static TradeValueResult ValueUnderlyingEnhance(
PrebuiltQdpMarketProxy marketProxy,
string valueDate,
string underlyingCode,
string underlyingInstrumentType,
double strike,
double annualizedEnhanceRate,
string startDate,
string endDate,
string optionType,
string exerciseType,
double participationRate,
double spotPrice,
double notional,
double riskFreeRate,
double dividendRate,
string volSurfaceName,
string tradeType,
string exerciseDate,
bool isAnnualized,
double annualizeFactor,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
string discountCurveName = null,
bool hasNightMarket = false,
bool preciseTimeMode = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
try
{
if (string.IsNullOrWhiteSpace(volSurfaceName))
{
volSurfaceName = underlyingCode.GenerateVolSurfaceKey();
}
var optionTrade = CreateUnderlyingEnhanceTrade(
null,
volSurfaceName,
valueDate,
underlyingCode,
underlyingInstrumentType,
strike,
startDate,
endDate,
optionType,
exerciseType,
annualizedEnhanceRate,
notional,
tradeType,
exerciseDate,
participationRate,
isAnnualized,
annualizeFactor,
isMoneynessOption,
initialSpotPrice,
null, // dividends
hasNightMarket,
preciseTimeMode,
timeToMaturityDays,
riskFreeRateOverride,
dividendRateOverride);
var market = marketProxy.GetQdpMarket(valueDate);
if (market == null)
{
return null;
}
var useLocalDiscountCurve = string.IsNullOrWhiteSpace(discountCurveName);
//设置DiscountCurve
if (useLocalDiscountCurve)
{
discountCurveName = Guid.NewGuid().ToString();
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, riskFreeRate);
marketProxy.AddYieldCurve(discountCurveName, valueDate, discountCurve);
}
//设置标的价格
marketProxy.AddStockPrice(underlyingCode, valueDate, spotPrice);
OptionValuationParameters parameters = null;
string dividendCurveName = null;
if (underlyingInstrumentType == "Stock")
{
//设置DividendCurve
dividendCurveName = Guid.NewGuid().ToString();
var dividendCurve = CalculatorHelper.CreateConstantRiskFreeCurve(dividendCurveName, dividendRate);
marketProxy.AddYieldCurve(dividendCurveName, valueDate, dividendCurve);
parameters = new OptionValuationParameters(
discountCurveName,
dividendCurveName,
volSurfaceName,
underlyingCode);
}
else
{
parameters = new OptionValuationParameters(
discountCurveName,
MarketProxy.ConstantZeroCurve,
volSurfaceName,
underlyingCode);
}
var option = optionTrade.Instrument as UnderlyingPayoffEnhance;
var engine = new AnalyticalUnderlyingPayoffEnhanceEngine();
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDate), pricingRequest, engine, parameters);
if (useLocalDiscountCurve)
{
marketProxy.RemoveYieldCurve(discountCurveName, valueDate);
}
if (!string.IsNullOrEmpty(dividendCurveName))
{
marketProxy.RemoveYieldCurve(dividendCurveName, valueDate);
}
if (results.Succeeded)
{
var ret = new TradeValueResult()
{
UnderlyingCode = underlyingCode,
Pv = results.Pv,
Delta = results.Delta,
Gamma = results.Gamma,
Vega = results.Vega,
CalendarDayTheta = results.Theta,
TradingDayTheta = results.ThetaPnL,
Rho = results.Rho,
DeltaCash = results.DeltaCash,
GammaCash = results.GammaCash,
Vol = results.PricingVol,
RoundedPv = OtcFormatHelper.FormatValue(results.Pv / notional, 2) * notional
};
return ret;
}
else
{
return null;
}
}
catch (Exception ex)
{
LogFactory.GetLogger("期权计算").Error("ValueUnderlyingEnhance:" + underlyingCode, ex);
throw new Exception($"收益增强结构处理失败[标的:{underlyingCode},开始日:{startDate},结束日:{endDate},行权日:{exerciseDate}]", ex);
}
}
private static UnderlyingPayoffEnhanceTrade CreateUnderlyingEnhanceTrade(string tradeId,
string volSurfaceName,
string tradeDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
double annualizedEnhanceRate,
double notional,
string tradeType,
string exerciseDate,
double participationRate,
bool isAnnualized,
double annualizeFactor,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
Dictionary dividends = null,
bool hasNightMarket = false,
bool preciseTimeMode = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN)
//string optionDayCount = "Act365")
{
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
var exercise = QdpConverter.ConvertExerciseType(exerciseType);
var optionStartDate = startDate.ToDate();
var underlyingMaturityDate = string.IsNullOrWhiteSpace(endDate) ? null : endDate.ToDate();
var temp_exerciseDate = exerciseDate.ToDate();
if (temp_exerciseDate < optionStartDate)
{
throw new Exception("到期日期不能小于起始日");
}
Date[] exerciseDates;
Date[] observationDates;
if (exercise == OptionExercise.American)
{
exerciseDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray();
observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray();
}
else
{
exerciseDates = new Date[] { temp_exerciseDate };
observationDates = new Date[] { temp_exerciseDate };
}
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var pricingTOverride = double.IsNaN(timeToMaturityDays) ? double.NaN : QdpCalendarHelper.CalculateTFromDays(timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
var underlyingEnhance =
new UnderlyingPayoffEnhance(
optionStartDate,
underlyingMaturityDate,
exercise,
(OptionType)Enum.Parse(typeof(OptionType), optionType),
strike,
(InstrumentType)Enum.Parse(typeof(InstrumentType), underlyingInstrumentType),
CalendarImpl.Get("chn"),
optionDayCount.ToDayCountImpl(),
new Act365(),
CurrencyCode.CNY,
CurrencyCode.CNY,
exerciseDates,
observationDates,
annualizedEnhanceRate,
notional,
null,
null,
0.0,
isMoneynessOption,
initialSpotPrice,
dividends,
hasNightMarket: hasNightMarket,
commodityFuturesPreciseTimeMode: preciseTimeMode,
pricingToverride: pricingTOverride,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
participationRate: participationRate,
isAnnualized: isAnnualized,
annualizedFactor: annualizeFactor)
{
UnderlyingTickers = new string[] { underlyingTicker }
};
if (string.IsNullOrWhiteSpace(tradeId))
{
tradeId = Guid.NewGuid().ToString();
}
return new UnderlyingPayoffEnhanceTrade(
tradeId,
tradeDate.ToDate(),
underlyingEnhance.StartDate,
underlyingEnhance.ExerciseDates.Last(),
QdpConverter.ConvertTradeType(tradeType),
underlyingEnhance.Notional,
0.0,
underlyingEnhance)
{
ValuationParameters = new OptionValuationParameters("RiskFreeDiscountCurve", MarketProxy.ConstantZeroCurve, volSurfaceName, underlyingEnhance.UnderlyingTickers[0])
};
}
#endregion
#region 累计期权
///
/// 累计期权
/// 暂不支持三段式,没有原因,就是没写逻辑
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
private static TradeValueResult ValueAccumulatorOptionTrade(
PrebuiltQdpMarketProxy marketProxy,
string valueDate,
string startDate,
string endDate,
string exerciseDate,
string optionType,
string underlyingCode,
string underlyingInstrumentType,
double strike,
string observationDates,
double barrier,
double coupon,
double callMultiplier,
double putMultiplier,
bool earlyTerminate,
bool payCoupon,
double participationRate,
double spotPrice,
double notional,
double riskFreeRate,
double dividendRate,
string volSurfaceName,
string buySell,
bool isAnnualized,
double annualizedFactor,
bool isMoneynessOption,
double initialSpotPrice,
string discountCurveName,
bool hasNightMarket,
bool preciseTimeMode,
double timeToMaturityDays,
double riskFreeRateOverride,
double dividendRateOverride,
PricingRequest pricingRequest)
{
try
{
if (string.IsNullOrWhiteSpace(volSurfaceName))
{
volSurfaceName = underlyingCode.GenerateVolSurfaceKey();
}
var optionTrade = CreateAccumulatorOptionTrade(
null,
volSurfaceName,
valueDate,
underlyingCode,
underlyingInstrumentType,
startDate,
endDate,
exerciseDate,
optionType,
strike,
observationDates,
barrier,
coupon,
callMultiplier,
putMultiplier,
earlyTerminate,
payCoupon,
participationRate,
spotPrice,
buySell,
notional,
isAnnualized,
annualizedFactor,
isMoneynessOption,
initialSpotPrice,
hasNightMarket: hasNightMarket,
preciseTimeMode: preciseTimeMode,
timeToMaturityDays: timeToMaturityDays,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride);
var market = marketProxy.GetQdpMarket(valueDate);
if (market == null)
{
return null;
}
var useLocalDiscountCurve = string.IsNullOrWhiteSpace(discountCurveName);
//设置DiscountCurve
if (useLocalDiscountCurve)
{
discountCurveName = Guid.NewGuid().ToString();
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, riskFreeRate);
marketProxy.AddYieldCurve(discountCurveName, valueDate, discountCurve);
}
//设置标的价格
marketProxy.AddStockPrice(underlyingCode, valueDate, spotPrice);
OptionValuationParameters parameters = null;
string dividendCurveName = null;
if (underlyingInstrumentType == "Stock")
{
//设置DividendCurve
dividendCurveName = Guid.NewGuid().ToString();
var dividendCurve = CalculatorHelper.CreateConstantRiskFreeCurve(dividendCurveName, dividendRate);
marketProxy.AddYieldCurve(dividendCurveName, valueDate, dividendCurve);
parameters = new OptionValuationParameters(
discountCurveName,
dividendCurveName,
volSurfaceName,
underlyingCode);
}
else
{
parameters = new OptionValuationParameters(
discountCurveName,
MarketProxy.ConstantZeroCurve,
volSurfaceName,
underlyingCode);
}
IEngine engine;
if (earlyTerminate)
{
engine = new QuadAccumulatorOptionEngine();
}
else
{
engine = new AnalyticalAccumulatorOptionEngine();
}
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDate), pricingRequest, engine, parameters);
if (useLocalDiscountCurve)
{
marketProxy.RemoveYieldCurve(discountCurveName, valueDate);
}
if (!string.IsNullOrEmpty(dividendCurveName))
{
marketProxy.RemoveYieldCurve(dividendCurveName, valueDate);
}
if (results.Succeeded)
{
var ret = new TradeValueResult()
{
UnderlyingCode = underlyingCode,
Pv = results.Pv,
Delta = results.Delta,
Gamma = results.Gamma,
Vega = results.Vega,
CalendarDayTheta = results.Theta,
TradingDayTheta = results.ThetaPnL,
Rho = results.Rho,
DeltaCash = results.DeltaCash,
GammaCash = results.GammaCash,
Vol = results.PricingVol,
RoundedPv = OtcFormatHelper.FormatValue(results.Pv / notional, 2) * notional
};
return ret;
}
else
{
return null;
}
}
catch (Exception ex)
{
LogFactory.GetLogger("期权计算").Error("ValueAccumulatorOptionTrade:" + underlyingCode, ex);
throw new Exception($"累计期权处理失败[标的:{underlyingCode},开始日:{startDate},结束日:{endDate},行权日:{exerciseDate}]", ex);
}
}
private static AccumulatorOptionTrade CreateAccumulatorOptionTrade(
string tradeId,
string volSurfaceName,
string valueDate,
string underlyingCode,
string underlyingInstrumentType,
string startDate,
string endDate,
string exerciseDate,
string optionType,
double strike,
string observationDateStr,
double barrier,
double coupon,
double callMultiplier,
double putMultiplier,
bool earlyTerminate,
bool payCoupon,
double participationRate,
double spotPrice,
string buySell,
double notional,
bool isAnnualized,
double annualizedFactor,
bool isMoneynessOption,
double initialSpotPrice,
bool hasNightMarket,
bool preciseTimeMode,
double timeToMaturityDays,
double riskFreeRateOverride,
double dividendRateOverride)
{
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
var exercise = OptionExercise.European;
var optionStartDate = startDate.ToDate();
var underlyingMaturityDate = string.IsNullOrWhiteSpace(endDate) ? null : endDate.ToDate();
var temp_exerciseDate = exerciseDate.ToDate();
if (temp_exerciseDate < optionStartDate)
{
return null;
}
Date[] exerciseDates;
Date[] observationDates = null;
if (exercise == OptionExercise.American)
{
exerciseDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray();
observationDates ??= CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray();
}
else
{
exerciseDates = new Date[] { temp_exerciseDate };
observationDates ??= CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).Union(new[] { temp_exerciseDate }).ToArray();
}
var customizedResults = QdpHelper.ParseAutocallCustomizedInfo(observationDateStr);
var koObservationDates = customizedResults.Item1;
var customizedKOBarriers = customizedResults.Item2;
var customizedCoupons = customizedResults.Item3;
if (koObservationDates == null || koObservationDates.Length == 0)
{
koObservationDates = observationDates;
}
if (customizedKOBarriers == null || customizedKOBarriers.Length == 0)
{
customizedKOBarriers = Enumerable.Repeat(barrier, koObservationDates.Length).ToArray();
}
//if (customizedCoupons == null || customizedCoupons.Length == 0)
//{
// customizedCoupons = Enumerable.Repeat(coupon, koObservationDates.Length).ToArray();
//}
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var pricingTOverride = double.IsNaN(timeToMaturityDays) ? double.NaN : QdpCalendarHelper.CalculateTFromDays(timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
var accumulatorOption =
new AccumulatorOption(
startDate: optionStartDate,
maturityDate: underlyingMaturityDate,
optionType: (OptionType)Enum.Parse(typeof(OptionType), optionType),
strike: strike,
barriers: customizedKOBarriers,
putMultiplier: putMultiplier,
callMultiplier: callMultiplier,
earlyTerminate: earlyTerminate,
underlyingInstrumentType: (InstrumentType)Enum.Parse(typeof(InstrumentType), underlyingInstrumentType),
calendar: CalendarImpl.Get("chn"),
dayCount: optionDayCount.ToDayCountImpl(),
payoffCcy: CurrencyCode.CNY,
settlementCcy: CurrencyCode.CNY,
exerciseDates: exerciseDates,
observationDates: koObservationDates,
//coupons: customizedCoupons,
coupon: coupon, // QDP改变了接口
payCoupon: payCoupon,
notional: notional,
settlementGap: null,
optionPremiumPaymentDate: null,
optionPremium: 0.0,
isMoneynessOption: isMoneynessOption,
initialSpotPrice: initialSpotPrice,
dividends: null,
pricingToverride: pricingTOverride,
hasNightMarket: hasNightMarket,
commodityFuturesPreciseTimeMode: preciseTimeMode,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
isAnnualized: isAnnualized,
annualizedFactor: annualizedFactor)
{
UnderlyingTickers = new string[] { underlyingCode }
};
if (string.IsNullOrWhiteSpace(tradeId))
{
tradeId = Guid.NewGuid().ToString();
}
return new AccumulatorOptionTrade(
tradeId,
valueDate.ToDate(),
accumulatorOption.StartDate,
accumulatorOption.ExerciseDates.Last(),
QdpConverter.ConvertTradeType(buySell),
accumulatorOption.Notional,
0.0,
accumulatorOption)
{
ValuationParameters = new OptionValuationParameters("RiskFreeDiscountCurve", MarketProxy.ConstantZeroCurve, volSurfaceName, accumulatorOption.UnderlyingTickers[0]),
};
}
#endregion
#region 现金流交易
private static TradeValueResult ValueCashFlowTrade(
PrebuiltQdpMarketProxy marketProxy,
string valueDate,
string startDate,
string endDate,
string underlyingCode,
double notional,
CashFlowRateTypeEnum rateType,
CashflowDepositTypeEnum depositType,
string buySell,
string volSurfaceName,
double profitRate,
double riskFreeRate,
string discountCurveName,
PricingRequest pricingRequest)
{
try
{
if (string.IsNullOrWhiteSpace(volSurfaceName))
{
volSurfaceName = underlyingCode.GenerateVolSurfaceKey();
}
var optionTrade = CreateCashFlowTrade(
null,
valueDate,
startDate,
endDate,
notional,
rateType,
depositType,
buySell,
profitRate);
var market = marketProxy.GetQdpMarket(valueDate);
if (market == null)
{
return null;
}
var useLocalDiscountCurve = string.IsNullOrWhiteSpace(discountCurveName);
//设置DiscountCurve
if (useLocalDiscountCurve)
{
discountCurveName = Guid.NewGuid().ToString();
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, riskFreeRate);
marketProxy.AddYieldCurve(discountCurveName, valueDate, discountCurve);
}
var parameters = new SimpleCfValuationParameters(discountCurveName, discountCurveName, discountCurveName);
IEngine engine = new DepositEngine();
var results = optionTrade.CalculateRisks(marketProxy.GetQdpMarket(valueDate), pricingRequest, engine, parameters);
if (useLocalDiscountCurve)
{
marketProxy.RemoveYieldCurve(discountCurveName, valueDate);
}
if (results.Succeeded)
{
var ret = new TradeValueResult()
{
UnderlyingCode = underlyingCode,
Pv = results.Pv,
Delta = results.Delta,
Gamma = results.Gamma,
Vega = results.Vega,
CalendarDayTheta = results.Theta,
TradingDayTheta = results.ThetaPnL,
Rho = results.Rho,
DeltaCash = results.DeltaCash,
GammaCash = results.GammaCash,
Vol = results.PricingVol,
RoundedPv = OtcFormatHelper.FormatValue(results.Pv / notional, 2) * notional
};
return ret;
}
else
{
return null;
}
}
catch (Exception ex)
{
LogFactory.GetLogger("期权计算").Error("ValueCashFlowTrade:" + underlyingCode, ex);
throw new Exception($"现金流交易处理失败[标的:{underlyingCode},开始日:{startDate},结束日:{endDate}]", ex);
}
}
private static DepositTrade CreateCashFlowTrade(
string tradeId,
string valueDate,
string startDate,
string endDate,
double notional,
CashFlowRateTypeEnum rateType,
CashflowDepositTypeEnum depositType,
string buySell,
double profitRate)
{
var optionStartDate = startDate.ToDate();
var underlyingMaturityDate = string.IsNullOrWhiteSpace(endDate) ? null : endDate.ToDate();
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var deposit = new Deposit(
startDate: optionStartDate,
maturityDate: underlyingMaturityDate,
depositRate: profitRate,
dayCount: optionDayCount.ToDayCountImpl(),
calendar: CalendarImpl.Get("chn"),
bda: BusinessDayConvention.None,
currency: CurrencyCode.CNY,
notional: notional,
annualizedRate: rateType == CashFlowRateTypeEnum.年化利率,
depositType: depositType == CashflowDepositTypeEnum.成本摊还 ? DepositType.CostRedemption : DepositType.Regular,
payCouponOnly: true
);
return new DepositTrade(
tradeId: tradeId,
tradeDate: valueDate.ToDate(),
startDate: startDate.ToDate(),
maturityDate: underlyingMaturityDate,
tradeType: QdpConverter.ConvertTradeType(buySell),
notional: notional,
price: 0.0,
instrument: deposit);
}
#endregion
///
/// 获取Qdp数据
///
///
///
///
///
///
/// 是否使用TradeVol模式计算
///
///
///
/// 是否使用用户自定义的波动率
/// 根据申万的要求:需要支持计算时在实际距离到期日天数的基础上,另外加减天数进行调整
/// 用户可以直接输入timeToMaturityDays来指定距离到期日的天数,指定该参数时,maturityShift和指定到期日都是无效的
///
public static TradeValueResult GetOptionValueResult(
string userId,
underlying_manager underlying,
trade trade,
double[] spotPrices = null,
string fixing = null,
bool useTradeVolMode = false,
bool preciseTimeMode = false,
double[] correlations = null,
string discountCurveName = null,
int maturityShift = 0,
double timeToMaturityDays = double.NaN,
string engineName = null,
PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
if (spotPrices is null || !spotPrices.Any())
{
throw new ArgumentException("缺少spotPrices", nameof(spotPrices));
}
var valueDate = underlying.QuotationDate.Value.ToString("yyyy-MM-dd");
if (trade.TTMDays != null && timeToMaturityDays.Equals(double.NaN))
{
timeToMaturityDays = trade.TTMDays.Value;
}
var marketProxy = QdpMarketManager.Instance.GetPrebuiltMarketProxy(userId);
var volSurfaceNames = GenerateVolSurfaceName(VolSurfaceInitializerSingleton.VolConstructionType, useTradeVolMode, underlying, trade, null);
var variety = VarietyBLL.GetAllvarietyModel().FirstOrDefault(v => v.id == underlying.UnderlyingTypeId);
TradeValueResult result = null;
if (trade.TradeType == "结构化交易")
{
throw new Exception("不支持结构化交易");
}
else if (trade.TradeType == "彩虹期权")
{
throw new Exception("不支持彩虹期权");
}
//股票默认到期日为行权日
if (underlying.IsStock() || underlying.IsCommoditySpot())
{
trade.MaturityDate = trade.ExerciseDate ?? DateTime.Today.AddYears(3);
}
else if (trade.MaturityDate == null)
{
trade.MaturityDate = underlying.MaturityDate ?? trade.ExerciseDate ?? DateTime.Today.AddYears(1);
}
var dayCount = CalculatorHelper.GetTradeDayCount();
//endDate必须有值否则QDP会报错
string endDate = null;
if (trade.MaturityDate != null)
{
endDate = QdpCalendarHelper.BizDayShift(trade.MaturityDate.Value).ToString("yyyy-MM-dd");
if (maturityShift != 0)
{
endDate = QdpCalendarHelper.ShiftDate(endDate, dayCount, maturityShift);
}
}
string exerciseDate = null;
if (trade.ExerciseDate != null)
{
exerciseDate = QdpCalendarHelper.BizDayShift(trade.ExerciseDate.Value).ToString("yyyy-MM-dd");
if (maturityShift != 0)
{
exerciseDate = QdpCalendarHelper.ShiftDate(exerciseDate, dayCount, maturityShift);
}
}
var tradeNotional = trade.Notional;
tradeBLL.SetFieldsByTradeType(trade);
#region 根据不同期权类型调用不同函数计算
var isForwardTrade = trade.Comments?.Trim() == "YQ";
var riskFreeRate = isForwardTrade ? 0 : (trade.NoRiskRate ?? 0);
switch (trade.TradeType)
{
case null:
case "":
case "香草期权":
result = ValueVanillaOptionTrade(
marketProxy,
valueDate,
underlying.UnderlyingCode,
trade.UnderlyingInstrumentType,
trade.Strike ?? 0,
trade.StartDate.Value.ToString("yyyy-MM-dd"),
endDate,
trade.CallPut, //put call
trade.ExerciseMode,
spotPrices[0],
tradeNotional, //数量
volSurfaceNames[0],
riskFreeRate,
null,
trade.BuySell,
exerciseDate,
trade.ParticipationRate ?? 1.0,
trade.PrincipalRate ?? 0.0,
trade.IsAnnualized,
trade.AnnualizeFactor ?? 1.0,
trade.DividendRate ?? 0,
trade.IsMoneynessOptionData,
trade.SpotPrice ?? 0,
hasNightMarket: variety != null && variety.HasNightMarket,
preciseTimeMode: preciseTimeMode,
discountCurveName: discountCurveName,
timeToMaturityDays: (!double.IsNaN(timeToMaturityDays) && timeToMaturityDays > 0) ? timeToMaturityDays : double.NaN,
ignoreSkewMap: useTradeVolMode,
isForwardTrade: isForwardTrade,
riskFreeRateOverride: trade.NoRiskRate ?? double.NaN,
dividendRateOverride: trade.DividendRate ?? double.NaN,
pricingRequest: pricingRequest);
break;
case "彩虹期权":
result = ValueRainbowOptionTrade(marketProxy,
valueDate,
trade.UnderlyingInstrumentType,
trade.trade_rainbow_option.UnderlyingCodes,
volSurfaceNames,
trade.trade_rainbow_option.Strikes,
trade.StartDate.Value.ToString("yyyy-MM-dd"),
endDate,
trade.CallPut, //put call
trade.ExerciseMode,// "European",
trade.trade_rainbow_option.RainbowType,
trade.trade_rainbow_option.CashAmount ?? 0,
spotPrices,
tradeNotional, //数量
trade.NoRiskRate ?? 0,
trade.trade_rainbow_option.CorRelation ?? 0,
trade.BuySell,
exerciseDate,
trade.ParticipationRate ?? 1.0,
trade.PrincipalRate ?? 0.0,
trade.IsAnnualized,
trade.AnnualizeFactor ?? 1.0,
trade.DividendRate ?? 0,
hasNightMarket: variety != null && variety.HasNightMarket,
preciseTimeMode: preciseTimeMode,
discountCurveName: discountCurveName,
ignoreSkewMap: useTradeVolMode,
engineName: engineName,
riskFreeRateOverride: trade.NoRiskRate ?? double.NaN,
dividendRateOverride: trade.DividendRate ?? double.NaN,
pricingRequest: pricingRequest);
break;
case "亚式期权":
var averagingPeriodStartDate = trade.trade_asian_option.AveragingPeriodStartDate.HasValue
? trade.trade_asian_option.AveragingPeriodStartDate.Value.ToString("yyyy-MM-dd")
: trade.TradeDate.Value.ToString("yyyy-MM-dd");
result = ValueAsianOptionTrade(
marketProxy,
valueDate,
underlying.UnderlyingCode,
trade.UnderlyingInstrumentType,
trade.Strike ?? 0,
trade.StartDate.Value.ToString("yyyy-MM-dd"),
endDate,
trade.CallPut, //put call
trade.ExerciseMode,//"European",
trade.trade_asian_option.PayoffType,
trade.trade_asian_option.StrikeType,
spotPrices[0],
tradeNotional, //数量
volSurfaceNames[0],
trade.NoRiskRate ?? 0,
trade.BuySell,
exerciseDate,
trade.IsAnnualized,
trade.AnnualizeFactor ?? 1.0,
trade.DividendRate ?? 0,
trade.IsMoneynessOptionData,
trade.SpotPrice ?? 0,
trade.ParticipationRate ?? 1.0,
trade.PrincipalRate ?? 0.0,
trade.trade_asian_option.StrikeGearingFactor ?? 1.0,
null,
fixing,
averagingPeriodStartDate,
trade.trade_asian_option.ObservationDates,
hasNightMarket: variety != null && variety.HasNightMarket,
preciseTimeMode: preciseTimeMode,
discountCurveName: discountCurveName,
ignoreSkewMap: useTradeVolMode,
engineName: engineName,
riskFreeRateOverride: trade.NoRiskRate ?? double.NaN,
dividendRateOverride: trade.DividendRate ?? double.NaN,
pricingRequest: pricingRequest);
break;
case "障碍期权":
result = ValueBarrierOptionTrade(
marketProxy,
valueDate,
underlying.UnderlyingCode,
trade.UnderlyingInstrumentType,
trade.Strike ?? 0,
trade.StartDate.Value.ToString("yyyy-MM-dd"),
endDate,
trade.CallPut, //put call
trade.ExerciseMode,// "European",
trade.trade_barrier_option.BarrierTypeEn,
trade.trade_barrier_option.BarrierPrice ?? 0,
trade.trade_barrier_option.UpperBarrierPrice ?? 0,
trade.trade_barrier_option.IsDiscrete,
spotPrices[0],
trade.IsUsePremiumRate.HasValue && trade.IsUsePremiumRate.Value ? ((trade.trade_barrier_option.RebateRate ?? 0) * (trade.SpotPrice ?? 0)) : (trade.trade_barrier_option.Rebate ?? 0),
tradeNotional, //数量
volSurfaceNames[0],
trade.NoRiskRate ?? 0,
trade.BuySell,
exerciseDate,
trade.trade_barrier_option.RebateType,
trade.trade_barrier_option.RebateAnnualizedAtKO,
trade.trade_barrier_option.RebateDayCount,
trade.ParticipationRate ?? 1.0,
trade.PrincipalRate ?? 0.0,
trade.IsAnnualized,
trade.AnnualizeFactor ?? 1.0,
trade.DividendRate ?? 0,
trade.IsMoneynessOptionData,
trade.SpotPrice ?? 0,
trade.trade_barrier_option.ObservationDates,
null, //dividends
trade.BuySell,
trade.trade_barrier_option.BarrierShift ?? 0.0,
trade.trade_barrier_option.KnockInOutStatus,
hasNightMarket: variety != null && variety.HasNightMarket,
preciseTimeMode: preciseTimeMode,
discountCurveName: discountCurveName,
ignoreSkewMap: useTradeVolMode,
engineName: engineName,
riskFreeRateOverride: trade.NoRiskRate ?? double.NaN,
dividendRateOverride: trade.DividendRate ?? double.NaN,
pricingRequest: pricingRequest);
break;
case "二元期权":
result = ValueBinaryOptionTrade(
marketProxy,
valueDate,
underlying.UnderlyingCode,
trade.UnderlyingInstrumentType,
trade.Strike ?? 0,
trade.StartDate.Value.ToString("yyyy-MM-dd"),
endDate,
trade.CallPut, //put call
trade.ExerciseMode,// "European",
trade.trade_binary_option.PayoffType,
trade.IsUsePremiumRate.HasValue && trade.IsUsePremiumRate.Value ? ((trade.trade_binary_option.CashOrNothingAmountRate ?? 0) * (trade.SpotPrice ?? 0)) : (trade.trade_binary_option.CashOrNothingAmount ?? 0),
spotPrices[0],
tradeNotional, //数量
volSurfaceNames[0],
trade.NoRiskRate ?? 0,
trade.BuySell,
exerciseDate,
trade.ParticipationRate ?? 1.0,
trade.PrincipalRate ?? 0.0,
trade.IsAnnualized,
trade.AnnualizeFactor ?? 1.0,
trade.trade_binary_option.UpperBarrier ?? double.NaN,
trade.IsUsePremiumRate.HasValue && trade.IsUsePremiumRate.Value ?
((trade.trade_binary_option.CashOrNothingAmountHighRate ?? 0) * (trade.SpotPrice ?? 0)) :
(trade.trade_binary_option.CashOrNothingAmountHigh ?? double.NaN),
trade.trade_binary_option.IsDiscreteMonitored,
trade.DividendRate ?? 0,
trade.IsMoneynessOptionData,
trade.SpotPrice ?? 0,
null,
trade.trade_binary_option.RebateType,
valuedateBLL.BinaryReplicationStrategy,
trade.trade_binary_option.Offset ?? (trade.Strike ?? 0) * 0.01,
hasNightMarket: variety != null && variety.HasNightMarket,
preciseTimeMode: preciseTimeMode,
discountCurveName: discountCurveName,
ignoreSkewMap: useTradeVolMode,
engineName: engineName,
riskFreeRateOverride: trade.NoRiskRate ?? double.NaN,
dividendRateOverride: trade.DividendRate ?? double.NaN,
pricingRequest: pricingRequest);
break;
case "价差期权":
var payoff = new SpreadOptionPayoff(trade.trade_spread_option.Payoff);
result = ValueSpreadOption(
marketProxy,
valueDate,
trade.UnderlyingInstrumentType,
trade.trade_spread_option.UnderlyingAssetCodes(),
payoff.Weights,
payoff.SpreadType,
trade.trade_spread_option.UnderlyingAssetCodes().Select(x => useTradeVolMode ? trade.GenerateVolSurfaceName(x) : x.GenerateVolSurfaceKey()).ToArray(),
trade.Strike ?? 0.0,
trade.StartDate.Value.ToString("yyyy-MM-dd"),
endDate,
trade.CallPut,
trade.ExerciseMode,
spotPrices,
tradeNotional,
trade.NoRiskRate ?? 0.0,
correlations,
trade.BuySell,
exerciseDate,
trade.ParticipationRate ?? 1.0,
trade.IsAnnualized,
trade.AnnualizeFactor ?? 1.0,
hasNightMarket: variety != null && variety.HasNightMarket,
preciseTimeMode: preciseTimeMode,
discountCurveName: discountCurveName,
engineName: engineName,
riskFreeRateOverride: trade.NoRiskRate ?? double.NaN,
pricingRequest: pricingRequest);
break;
case "亚式合成价差期权":
averagingPeriodStartDate = trade.trade_asian_option.AveragingPeriodStartDate.HasValue
? trade.trade_asian_option.AveragingPeriodStartDate.Value.ToString("yyyy-MM-dd")
: trade.TradeDate.Value.ToString("yyyy-MM-dd");
result = ValueAsianSyntheticNormalSpreadOptionTrade(
marketProxy,
valueDate,
underlying.UnderlyingCode,
trade.UnderlyingInstrumentType,
trade.Strike ?? 0,
trade.StartDate.Value.ToString("yyyy-MM-dd"),
endDate,
trade.CallPut, //put call
trade.ExerciseMode,//"European",
trade.trade_asian_option.PayoffType,
trade.trade_asian_option.StrikeType,
spotPrices[0],
tradeNotional, //数量
volSurfaceNames[0],
trade.NoRiskRate ?? 0,
trade.BuySell,
exerciseDate,
trade.IsAnnualized,
trade.AnnualizeFactor ?? 1.0,
trade.DividendRate ?? 0,
trade.IsMoneynessOptionData,
trade.SpotPrice ?? 0,
null,
fixing,
averagingPeriodStartDate,
hasNightMarket: variety != null && variety.HasNightMarket,
preciseTimeMode: preciseTimeMode,
discountCurveName: discountCurveName,
ignoreSkewMap: useTradeVolMode,
engineName: engineName,
riskFreeRateOverride: trade.NoRiskRate ?? double.NaN,
dividendRateOverride: trade.DividendRate ?? double.NaN,
pricingRequest: pricingRequest);
break;
case "远期":
result = ForwardradeCalcService.CalcValue(trade, spotPrices[0]);
break;
case "信用债":
case "商品期货":
case "商品现货":
case "股票":
var pv = spotPrices[0] * trade.Notional;
result = new TradeValueResult { Pv = pv, RoundedPv = pv, Delta = trade.Notional, DeltaCash = pv };
break;
case "场内期权":
result = ValueVanillaOptionTrade(
marketProxy,
valueDate,
underlying.UnderlyingCode,
trade.UnderlyingInstrumentType,
trade.Strike ?? 0,
(trade.StartDate ?? underlying.OpenDate.Value).ToString("yyyy-MM-dd"),
endDate,
trade.CallPut,
trade.ExerciseMode,
spotPrices[0],
tradeNotional, //数量
volSurfaceNames[0],
riskFreeRate,
null,
"Buy",
exerciseDate,
trade.ParticipationRate ?? 1.0,
trade.PrincipalRate ?? 0,
trade.IsAnnualized,
trade.AnnualizeFactor ?? 1.0,
trade.DividendRate ?? 0,
false,
trade.SpotPrice ?? 0,
hasNightMarket: variety != null && variety.HasNightMarket,
preciseTimeMode: preciseTimeMode,
discountCurveName: discountCurveName,
ignoreSkewMap: useTradeVolMode,
isForwardTrade: isForwardTrade,
riskFreeRateOverride: trade.NoRiskRate ?? double.NaN,
dividendRateOverride: trade.DividendRate ?? double.NaN,
pricingRequest: pricingRequest);
break;
case "合成价差期权":
result = ValueSyntheticNormalSpreadOptionTrade(
marketProxy,
valueDate,
underlying.UnderlyingCode,
trade.UnderlyingInstrumentType,
trade.Strike ?? 0,
trade.StartDate.Value.ToString("yyyy-MM-dd"),
endDate,
trade.CallPut, //put call
trade.ExerciseMode,
spotPrices[0],
tradeNotional, //数量
useTradeVolMode ? trade.GenerateVolSurfaceName() : underlying.GenerateVolSurfaceName(),
riskFreeRate,
null,
trade.BuySell,
exerciseDate,
trade.ParticipationRate ?? 1.0,
trade.PrincipalRate ?? 0.0,
trade.IsAnnualized,
trade.AnnualizeFactor ?? 1.0,
trade.DividendRate ?? 0,
trade.IsMoneynessOptionData,
trade.SpotPrice ?? 0,
hasNightMarket: variety != null && variety.HasNightMarket,
preciseTimeMode: preciseTimeMode,
discountCurveName: discountCurveName,
ignoreSkewMap: useTradeVolMode,
isForwardTrade: isForwardTrade,
engineName: engineName,
riskFreeRateOverride: trade.NoRiskRate ?? double.NaN,
dividendRateOverride: trade.DividendRate ?? double.NaN,
pricingRequest: pricingRequest);
break;
case "双鲨期权":
result = ValueDoubleSharkFinOptionTrade(
marketProxy,
valueDate,
underlying.UnderlyingCode,
trade.UnderlyingInstrumentType,
trade.trade_double_sharkfin_option.StrikeHigh ?? trade.Strike ?? 0.0,
trade.Strike ?? 0.0,
trade.StartDate.Value.ToString("yyyy-MM-dd"),
endDate,
trade.CallPut, //put call
trade.ExerciseMode,// "European",
trade.trade_double_sharkfin_option.BarrierHigh,
trade.trade_double_sharkfin_option.BarrierLow,
trade.trade_double_sharkfin_option.IsDiscrete,
spotPrices[0],
trade.IsUsePremiumRate.HasValue && trade.IsUsePremiumRate.Value ? ((trade.trade_double_sharkfin_option.RebateRate ?? 0) * (trade.SpotPrice ?? 0)) : (trade.trade_double_sharkfin_option.Rebate ?? 0),
trade.IsUsePremiumRate.HasValue && trade.IsUsePremiumRate.Value ? ((trade.trade_double_sharkfin_option.RebateHighRate ?? 0) * (trade.SpotPrice ?? 0)) : (trade.trade_double_sharkfin_option.RebateHigh ?? 0),
trade.trade_double_sharkfin_option.RebateType,
tradeNotional, //数量
volSurfaceNames[0],
trade.NoRiskRate ?? 0,
trade.BuySell,
exerciseDate,
trade.ParticipationRate ?? 1.0,
trade.PrincipalRate ?? 0.0,
trade.IsAnnualized,
trade.AnnualizeFactor ?? 1.0,
trade.trade_double_sharkfin_option.CallParticipationRate ?? 1.0,
trade.trade_double_sharkfin_option.PutParticipationRate ?? 1.0,
trade.DividendRate ?? 0,
trade.IsMoneynessOptionData,
trade.SpotPrice ?? 0,
trade.trade_double_sharkfin_option.ObservationDates,
null, //dividends
trade.trade_double_sharkfin_option.KnockInOutStatus,
hasNightMarket: variety != null && variety.HasNightMarket,
preciseTimeMode: preciseTimeMode,
discountCurveName: discountCurveName,
timeToMaturityDays: (!double.IsNaN(timeToMaturityDays) && timeToMaturityDays > 0) ? timeToMaturityDays : double.NaN,
ignoreSkewMap: useTradeVolMode,
engineName: engineName,
riskFreeRateOverride: trade.NoRiskRate ?? double.NaN,
dividendRateOverride: trade.DividendRate ?? double.NaN,
pricingRequest: pricingRequest);
break;
case "凤凰期权":
result = ValueAutocall(
marketProxy: marketProxy,
valueDate: valueDate,
underlyingCode: underlying.UnderlyingCode,
underlyingInstrumentType: trade.UnderlyingInstrumentType,
startDate: trade.StartDate.Value.ToString("yyyy-MM-dd"),
endDate: endDate,
callPut: trade.CallPut,
koBarrier: trade.trade_autocall.KOBarrier,
kiBarrier: trade.trade_autocall.KIBarrier,
couponBarrier: trade.trade_autocall.CouponBarrier,
koObservationDateStr: trade.trade_autocall.KOObservationDates,
observationDateStr: trade.trade_autocall.ObservationDates,
coupon: trade.trade_autocall.Coupon,
couponPayAtMaturity: trade.trade_autocall.CouponPayAtMaturity,
includeCouponAfterKI: trade.trade_autocall.IncludeCouponAfterKI,
isFixedCoupon: trade.trade_autocall.IsFixedCoupon,
annualizedOptionPayoff: trade.trade_autocall.IsAnnualized2,
participationRate: trade.ParticipationRate ?? 1.0,
principalRate: trade.PrincipalRate ?? 0.0,
spotPrice: spotPrices[0],
notional: tradeNotional,
volSurfaceName: volSurfaceNames[0],
riskFreeRate: trade.NoRiskRate ?? 0.0,
buySell: trade.BuySell,
exerciseDate: exerciseDate,
annualizeFactor: trade.trade_autocall.AnnualizeFactor2 ?? 1.0,
strike: trade.Strike ?? 0.0,
spreadStrike: trade.trade_autocall.SpreadStrike ?? -1,
couponDayCount: trade.trade_autocall.CouponDayCount,
happenedObservations: trade.trade_autocall.HappenedObservations,
dividendRate: trade.DividendRate ?? 0,
isMoneynessOptionData: trade.IsMoneynessOptionData,
initialSpotPrice: trade.SpotPrice ?? 0,
dividends: null,
knockInOutStatus: trade.trade_autocall.KnockInOutStatus,
hasNightMarket: variety != null && variety.HasNightMarket,
preciseTimeMode: preciseTimeMode,
discountCurveName: discountCurveName,
timeToMaturityDays: (!double.IsNaN(timeToMaturityDays) && timeToMaturityDays > 0) ? timeToMaturityDays : double.NaN,
ignoreSkewMap: useTradeVolMode,
engineName: engineName,
riskFreeRateOverride: trade.NoRiskRate ?? double.NaN,
dividendRateOverride: trade.DividendRate ?? double.NaN,
pricingRequest: pricingRequest);
break;
case "雪球期权":
var snowball = trade.trade_snowball;
result = ValueSnowball(
marketProxy: marketProxy,
valueDate: valueDate,
underlyingCode: underlying.UnderlyingCode,
underlyingInstrumentType: trade.UnderlyingInstrumentType,
startDate: trade.StartDate.Value.ToString("yyyy-MM-dd"),
endDate: endDate,
callPut: trade.CallPut,
koBarrier: snowball.KOBarrier,
kiBarrier: snowball.KIBarrier,
koObservationDateStr: snowball.KOObservationDates,
observationDateStr: snowball.ObservationDates,
coupon: snowball.Coupon,
participationRate: trade.ParticipationRate ?? 1.0,
principalRate: trade.PrincipalRate ?? 0.0,
spotPrice: spotPrices[0],
notional: tradeNotional,
volSurfaceName: volSurfaceNames[0],
riskFreeRate: trade.NoRiskRate ?? 0.0,
buySell: trade.BuySell,
exerciseDate: exerciseDate,
annualizeFactor: snowball.AnnualizeFactor2 ?? 1.0,
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,
dividendRate: trade.DividendRate ?? 0,
isMoneynessOptionData: trade.IsMoneynessOptionData,
initialSpotPrice: trade.SpotPrice ?? 0,
dividends: null,
knockInOutStatus: snowball.KnockInOutStatus,
hasNightMarket: variety != null && variety.HasNightMarket,
preciseTimeMode: preciseTimeMode,
discountCurveName: discountCurveName,
timeToMaturityDays: (!double.IsNaN(timeToMaturityDays) && timeToMaturityDays > 0) ? timeToMaturityDays : double.NaN,
ignoreSkewMap: useTradeVolMode,
engineName: engineName,
riskFreeRateOverride: trade.NoRiskRate ?? double.NaN,
dividendRateOverride: trade.DividendRate ?? double.NaN,
pricingRequest: pricingRequest);
break;
case "区间累积期权":
result = ValueRangeAccrual(
marketProxy,
valueDate,
underlying.UnderlyingCode,
trade.UnderlyingInstrumentType,
trade.StartDate.Value.ToString("yyyy-MM-dd"),
endDate,
trade.ParticipationRate ?? 1.0,
trade.PrincipalRate ?? 0.0,
spotPrices[0],
tradeNotional,
volSurfaceNames[0],
trade.NoRiskRate ?? 0.0,
trade.BuySell,
exerciseDate,
trade.trade_rangeaccrual.LowerRange,
trade.trade_rangeaccrual.UpperRange,
trade.trade_rangeaccrual.BonusRate,
trade.trade_rangeaccrual.ObservationDates,
trade.SettlementDate?.ToString("yyyy-MM-dd"),
trade.AnnualizeFactor ?? 1.0,
trade.Strike ?? 0.0,
trade.IsAnnualized,
trade.trade_rangeaccrual.HappenedObservations,
trade.DividendRate ?? 0,
trade.IsMoneynessOptionData,
trade.SpotPrice ?? 0,
hasNightMarket: variety != null && variety.HasNightMarket,
preciseTimeMode: preciseTimeMode,
discountCurveName: discountCurveName,
timeToMaturityDays: (!double.IsNaN(timeToMaturityDays) && timeToMaturityDays > 0) ? timeToMaturityDays : double.NaN,
ignoreSkewMap: useTradeVolMode,
engineName: engineName,
riskFreeRateOverride: trade.NoRiskRate ?? double.NaN,
dividendRateOverride: trade.DividendRate ?? double.NaN,
pricingRequest: pricingRequest);
break;
case "气囊结构":
result = ValueAirbag(
marketProxy,
valueDate,
underlying.UnderlyingCode,
trade.UnderlyingInstrumentType,
trade.Strike ?? 0.0,
trade.trade_airbag.HighStrike,
trade.StartDate.Value.ToString("yyyy-MM-dd"),
endDate,
trade.CallPut,
trade.ExerciseMode,
trade.trade_airbag.Barrier,
trade.ParticipationRate ?? 1.0,
trade.trade_airbag.KIParticipationRate,
trade.PrincipalRate ?? 0.0,
trade.trade_airbag.IsDiscreteMonitored,
trade.trade_airbag.HasPayoffLimit,
spotPrices[0],
tradeNotional,
trade.NoRiskRate ?? 0.0,
trade.DividendRate ?? 0,
volSurfaceNames[0],
trade.BuySell,
trade.ExerciseDate.Value.ToString("yyyy-MM-dd"),
trade.IsAnnualized,
trade.AnnualizeFactor ?? 1.0,
trade.IsMoneynessOptionData,
trade.SpotPrice ?? 0,
null,
null,
trade.BuySell,
barrierStatus: "Monitoring",
hasNightMarket: variety != null && variety.HasNightMarket,
preciseTimeMode: preciseTimeMode,
discountCurveName: discountCurveName,
timeToMaturityDays: (!double.IsNaN(timeToMaturityDays) && timeToMaturityDays > 0) ? timeToMaturityDays : double.NaN,
engineName: engineName,
riskFreeRateOverride: trade.NoRiskRate ?? double.NaN,
dividendRateOverride: trade.DividendRate ?? double.NaN,
pricingRequest: pricingRequest);
break;
case "收益增强结构":
result = ValueUnderlyingEnhance(
marketProxy,
valueDate,
underlying.UnderlyingCode,
trade.UnderlyingInstrumentType,
trade.Strike ?? 0.0,
trade.trade_underlying_enhance.AnnualizedEnhanceRate ?? 0.0,
trade.StartDate.Value.ToString("yyyy-MM-dd"),
endDate,
trade.CallPut,
trade.ExerciseMode,
trade.ParticipationRate ?? 1,
spotPrices[0],
tradeNotional,
trade.NoRiskRate ?? 0.0,
trade.DividendRate ?? 0,
volSurfaceNames[0],
trade.BuySell,
trade.ExerciseDate.Value.ToString("yyyy-MM-dd"),
trade.IsAnnualized,
trade.AnnualizeFactor ?? 1.0,
trade.IsMoneynessOptionData,
trade.SpotPrice ?? 0,
discountCurveName: discountCurveName,
hasNightMarket: variety != null && variety.HasNightMarket,
preciseTimeMode: preciseTimeMode,
timeToMaturityDays: (!double.IsNaN(timeToMaturityDays) && timeToMaturityDays > 0) ? timeToMaturityDays : double.NaN,
riskFreeRateOverride: trade.NoRiskRate ?? double.NaN,
dividendRateOverride: trade.DividendRate ?? double.NaN,
pricingRequest: pricingRequest);
break;
case "现金流交易":
result = ValueCashFlowTrade(
marketProxy,
valueDate,
trade.StartDate.Value.ToString("yyyy-MM-dd"),
endDate,
underlying.UnderlyingCode,
tradeNotional,
trade.trade_cashflow.RateType,
trade.trade_cashflow.DepositType,
trade.BuySell,
volSurfaceNames[0],
trade.NoRiskRate ?? 0.0,
trade.trade_cashflow.ProfitRate,
discountCurveName,
pricingRequest);
break;
default:
break;
}
#endregion
if (result != null)
{
result.Strike = trade.Strike ?? 0;
if (spotPrices != null && spotPrices.Any())
{
result.SpotPrice = spotPrices[0];
}
}
return result;
}
private static string[] GenerateVolSurfaceName(
VolConstructionType volConstructionType,
bool useTradeVolMode,
underlying_manager underlying,
trade trade,
string optionalVolKey = null)
{
if (volConstructionType == VolConstructionType.SkewMap || useTradeVolMode)
{
return new string[] { trade.GenerateVolSurfaceName() };
}
else if (!useTradeVolMode)
{
return new string[] { underlying.GenerateVolSurfaceName() };
}
else
{
switch (trade.TradeType)
{
case "彩虹期权":
var rainbowOptionVolKeys = new string[2];
rainbowOptionVolKeys[0] = trade.GenerateVolSurfaceName();
rainbowOptionVolKeys[1] = trade.GenerateVolSurfaceName(optionalVolKey);
return useTradeVolMode ? rainbowOptionVolKeys : trade.trade_rainbow_option.UnderlyingCodes.Select(x => x.GenerateVolSurfaceKey()).ToArray();
default:
return new string[] { useTradeVolMode ? trade.GenerateVolSurfaceName() : underlying.GenerateVolSurfaceName() };
}
}
}
private static double GetInterpolatedVolForSkewMapMode(
IQdpMarketProxy marketProxy,
string valueDate,
string underlyingCode,
string exerciseDate,
double strike,
bool isBuy,
bool isCall,
double spotPrice,
int? skewMapVolVar = null,
double timeToMaturityDays = double.NaN)
{
var baseVolSurface = marketProxy.GetSkewMapBaseVolSurface(valueDate, underlyingCode);
var skewMapVolSurface = new SkewMapVolSurface(baseVolSurface.BaseVol);
var underlying = underlying_managerBLL.GetByCode(underlyingCode);
//var calendar = CalendarImpl.Get("chn");
var t = double.IsNaN(timeToMaturityDays)
? TradeCalcHelper.CalculateTTMDays(
DateTime.Parse(valueDate),
DateTime.Parse(exerciseDate),
varietyid: underlying?.UnderlyingTypeId ?? 0,
precisionOfMinute: false) //calendar.NumberBizDaysBetweenDate(new Date(DateTime.Parse(valueDate)), new Date(DateTime.Parse(exerciseDate)), true)
: timeToMaturityDays;
var volVar = skewMapVolVar ?? (int)(isBuy ? baseVolSurface.BidVar : baseVolSurface.AskVar);
return skewMapVolSurface.GetVol(
t: Math.Ceiling(t), //不考虑日内精确时间
k: strike,
spot: spotPrice,
isCall: isCall,
isBuy: isBuy,
var: volVar);
}
private static void SetSkewMapVol(
IQdpMarketProxy marketProxy,
string valueDate,
string underlyingCode,
string exerciseDate,
double strike,
bool isBuy,
bool isCall,
string tradeId,
double spotPrice,
int? skewMapVolVar = null,
double timeToMaturityDays = double.NaN)
{
var vol = GetInterpolatedVolForSkewMapMode(
marketProxy,
valueDate,
underlyingCode,
exerciseDate,
strike,
isBuy,
isCall,
spotPrice,
skewMapVolVar,
timeToMaturityDays);
var underlying = underlying_managerBLL.GetByCode(underlyingCode);
//根据获取的Vol点,构造一个用于该trade的平的波动率曲面
var flatNormalVolSurface = VolatilityBuilder.CreateMoneynessVolBuilder(DateTime.Parse(valueDate))
.SetUnderlying(underlying).SetData(vol).Build();
//.GetDefault(DateTime.Parse(valueDate), underlying, defaultVol: vol);
var initParam = new VolSurfaceInitParamsBuilder()
.SetVolatility(flatNormalVolSurface).SetValueDate(valueDate).Build(tradeId);
VolSurfaceInitializerSingleton.GetInitializer(true).InitializeMarketProxy(marketProxy, initParam);
}
///
/// 期货期权卖方交易预付金的收取标准为下列两者中较大者:
///(1)权利金(期权合约结算价×标的期货合约交易单位)+标的期货合约交易预付金-期权合约虚值额的一半
///(2)权利金(期权合约结算价×标的期货合约交易单位)+标的期货合约交易预付金的一半
///看涨虚值额 = max(期权合约执行价格 - 标的期货合约当日结算价,0)*合约乘数;
///看跌虚值额=max(标的期货合约当日结算价 - 期权合约执行价格,0)*合约乘数。
///豆粕期权的合约乘数是10、标的期货合约的交易单位是10.
///所以以上2个公式可以结合成为一个公式:
///预付金=权利金+MAX(期货预付金-1/2虚值额,1/2期货预付金)
///
/// 权利金
/// 交易量
/// 行权价
/// 现价
/// 期货预付金率
/// 看涨看跌
///
public static double CommodityFutureOptionMargin(double premium, double amount, double strike, double spotPrice, double futureMarginRatio, string optionType = "Call")
{
var futureMargin = futureMarginRatio * amount * spotPrice;
var outTheMoney =
(optionType.ToUpper() == "PUT")
? Math.Max(spotPrice - strike, 0) * amount //看跌虚值额
: Math.Max(strike - spotPrice, 0) * amount; //看涨虚值额
return premium + Math.Max(futureMargin - (outTheMoney * 0.5), futureMargin * 0.5);
}
///
/// 股指期货类型转换为商品期货类型
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static string ConvertInstrumentType(string instrumentType)
{
return ConsGlobal.InstrumentType.IsStockIF(instrumentType) ? ConsGlobal.InstrumentType.CommodityFutures : instrumentType;
}
private static T CheckMaturityDate(this T trade) where T : TradeBase
{
if (trade?.MaturityDate.Year > 2030)
{
trade.MaturityDate = new Date(DateTime.Now.AddYears(3));
}
return trade;
}
}
}